Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81159 views
1
// Copyright 2011 Mark Cavage <[email protected]> All rights reserved.
2
3
var assert = require('assert');
4
5
var ASN1 = require('./types');
6
var errors = require('./errors');
7
8
9
///--- Globals
10
11
var newInvalidAsn1Error = errors.newInvalidAsn1Error;
12
13
14
15
///--- API
16
17
function Reader(data) {
18
if (!data || !Buffer.isBuffer(data))
19
throw new TypeError('data must be a node Buffer');
20
21
this._buf = data;
22
this._size = data.length;
23
24
// These hold the "current" state
25
this._len = 0;
26
this._offset = 0;
27
28
var self = this;
29
this.__defineGetter__('length', function() { return self._len; });
30
this.__defineGetter__('offset', function() { return self._offset; });
31
this.__defineGetter__('remain', function() {
32
return self._size - self._offset;
33
});
34
this.__defineGetter__('buffer', function() {
35
return self._buf.slice(self._offset);
36
});
37
}
38
39
40
/**
41
* Reads a single byte and advances offset; you can pass in `true` to make this
42
* a "peek" operation (i.e., get the byte, but don't advance the offset).
43
*
44
* @param {Boolean} peek true means don't move offset.
45
* @return {Number} the next byte, null if not enough data.
46
*/
47
Reader.prototype.readByte = function(peek) {
48
if (this._size - this._offset < 1)
49
return null;
50
51
var b = this._buf[this._offset] & 0xff;
52
53
if (!peek)
54
this._offset += 1;
55
56
return b;
57
};
58
59
60
Reader.prototype.peek = function() {
61
return this.readByte(true);
62
};
63
64
65
/**
66
* Reads a (potentially) variable length off the BER buffer. This call is
67
* not really meant to be called directly, as callers have to manipulate
68
* the internal buffer afterwards.
69
*
70
* As a result of this call, you can call `Reader.length`, until the
71
* next thing called that does a readLength.
72
*
73
* @return {Number} the amount of offset to advance the buffer.
74
* @throws {InvalidAsn1Error} on bad ASN.1
75
*/
76
Reader.prototype.readLength = function(offset) {
77
if (offset === undefined)
78
offset = this._offset;
79
80
if (offset >= this._size)
81
return null;
82
83
var lenB = this._buf[offset++] & 0xff;
84
if (lenB === null)
85
return null;
86
87
if ((lenB & 0x80) == 0x80) {
88
lenB &= 0x7f;
89
90
if (lenB == 0)
91
throw newInvalidAsn1Error('Indefinite length not supported');
92
93
if (lenB > 4)
94
throw newInvalidAsn1Error('encoding too long');
95
96
if (this._size - offset < lenB)
97
return null;
98
99
this._len = 0;
100
for (var i = 0; i < lenB; i++)
101
this._len = (this._len << 8) + (this._buf[offset++] & 0xff);
102
103
} else {
104
// Wasn't a variable length
105
this._len = lenB;
106
}
107
108
return offset;
109
};
110
111
112
/**
113
* Parses the next sequence in this BER buffer.
114
*
115
* To get the length of the sequence, call `Reader.length`.
116
*
117
* @return {Number} the sequence's tag.
118
*/
119
Reader.prototype.readSequence = function(tag) {
120
var seq = this.peek();
121
if (seq === null)
122
return null;
123
if (tag !== undefined && tag !== seq)
124
throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
125
': got 0x' + seq.toString(16));
126
127
var o = this.readLength(this._offset + 1); // stored in `length`
128
if (o === null)
129
return null;
130
131
this._offset = o;
132
return seq;
133
};
134
135
136
Reader.prototype.readInt = function() {
137
return this._readTag(ASN1.Integer);
138
};
139
140
141
Reader.prototype.readBoolean = function() {
142
return (this._readTag(ASN1.Boolean) === 0 ? false : true);
143
};
144
145
146
Reader.prototype.readEnumeration = function() {
147
return this._readTag(ASN1.Enumeration);
148
};
149
150
151
Reader.prototype.readString = function(tag, retbuf) {
152
if (!tag)
153
tag = ASN1.OctetString;
154
155
var b = this.peek();
156
if (b === null)
157
return null;
158
159
if (b !== tag)
160
throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
161
': got 0x' + b.toString(16));
162
163
var o = this.readLength(this._offset + 1); // stored in `length`
164
165
if (o === null)
166
return null;
167
168
if (this.length > this._size - o)
169
return null;
170
171
this._offset = o;
172
173
if (this.length === 0)
174
return '';
175
176
var str = this._buf.slice(this._offset, this._offset + this.length);
177
this._offset += this.length;
178
179
return retbuf ? str : str.toString('utf8');
180
};
181
182
Reader.prototype.readOID = function(tag) {
183
if (!tag)
184
tag = ASN1.OID;
185
186
var b = this.peek();
187
if (b === null)
188
return null;
189
190
if (b !== tag)
191
throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
192
': got 0x' + b.toString(16));
193
194
var o = this.readLength(this._offset + 1); // stored in `length`
195
if (o === null)
196
return null;
197
198
if (this.length > this._size - o)
199
return null;
200
201
this._offset = o;
202
203
var values = [];
204
var value = 0;
205
206
for (var i = 0; i < this.length; i++) {
207
var byte = this._buf[this._offset++] & 0xff;
208
209
value <<= 7;
210
value += byte & 0x7f;
211
if ((byte & 0x80) == 0) {
212
values.push(value);
213
value = 0;
214
}
215
}
216
217
value = values.shift();
218
values.unshift(value % 40);
219
values.unshift((value / 40) >> 0);
220
221
return values.join('.');
222
};
223
224
225
Reader.prototype._readTag = function(tag) {
226
assert.ok(tag !== undefined);
227
228
var b = this.peek();
229
230
if (b === null)
231
return null;
232
233
if (b !== tag)
234
throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
235
': got 0x' + b.toString(16));
236
237
var o = this.readLength(this._offset + 1); // stored in `length`
238
if (o === null)
239
return null;
240
241
if (this.length > 4)
242
throw newInvalidAsn1Error('Integer too long: ' + this.length);
243
244
if (this.length > this._size - o)
245
return null;
246
this._offset = o;
247
248
var fb = this._buf[this._offset++];
249
var value = 0;
250
251
value = fb & 0x7F;
252
for (var i = 1; i < this.length; i++) {
253
value <<= 8;
254
value |= (this._buf[this._offset++] & 0xff);
255
}
256
257
if ((fb & 0x80) == 0x80)
258
value = -value;
259
260
return value;
261
};
262
263
264
265
///--- Exported API
266
267
module.exports = Reader;
268
269