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