| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 | // Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.var assert = require('assert');var Buffer = require('safer-buffer').Buffer;var ASN1 = require('./types');var errors = require('./errors');// --- Globalsvar newInvalidAsn1Error = errors.newInvalidAsn1Error;// --- APIfunction Reader(data) {  if (!data || !Buffer.isBuffer(data))    throw new TypeError('data must be a node Buffer');  this._buf = data;  this._size = data.length;  // These hold the "current" state  this._len = 0;  this._offset = 0;}Object.defineProperty(Reader.prototype, 'length', {  enumerable: true,  get: function () { return (this._len); }});Object.defineProperty(Reader.prototype, 'offset', {  enumerable: true,  get: function () { return (this._offset); }});Object.defineProperty(Reader.prototype, 'remain', {  get: function () { return (this._size - this._offset); }});Object.defineProperty(Reader.prototype, 'buffer', {  get: function () { return (this._buf.slice(this._offset)); }});/** * Reads a single byte and advances offset; you can pass in `true` to make this * a "peek" operation (i.e., get the byte, but don't advance the offset). * * @param {Boolean} peek true means don't move offset. * @return {Number} the next byte, null if not enough data. */Reader.prototype.readByte = function (peek) {  if (this._size - this._offset < 1)    return null;  var b = this._buf[this._offset] & 0xff;  if (!peek)    this._offset += 1;  return b;};Reader.prototype.peek = function () {  return this.readByte(true);};/** * Reads a (potentially) variable length off the BER buffer.  This call is * not really meant to be called directly, as callers have to manipulate * the internal buffer afterwards. * * As a result of this call, you can call `Reader.length`, until the * next thing called that does a readLength. * * @return {Number} the amount of offset to advance the buffer. * @throws {InvalidAsn1Error} on bad ASN.1 */Reader.prototype.readLength = function (offset) {  if (offset === undefined)    offset = this._offset;  if (offset >= this._size)    return null;  var lenB = this._buf[offset++] & 0xff;  if (lenB === null)    return null;  if ((lenB & 0x80) === 0x80) {    lenB &= 0x7f;    if (lenB === 0)      throw newInvalidAsn1Error('Indefinite length not supported');    if (lenB > 4)      throw newInvalidAsn1Error('encoding too long');    if (this._size - offset < lenB)      return null;    this._len = 0;    for (var i = 0; i < lenB; i++)      this._len = (this._len << 8) + (this._buf[offset++] & 0xff);  } else {    // Wasn't a variable length    this._len = lenB;  }  return offset;};/** * Parses the next sequence in this BER buffer. * * To get the length of the sequence, call `Reader.length`. * * @return {Number} the sequence's tag. */Reader.prototype.readSequence = function (tag) {  var seq = this.peek();  if (seq === null)    return null;  if (tag !== undefined && tag !== seq)    throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +                              ': got 0x' + seq.toString(16));  var o = this.readLength(this._offset + 1); // stored in `length`  if (o === null)    return null;  this._offset = o;  return seq;};Reader.prototype.readInt = function () {  return this._readTag(ASN1.Integer);};Reader.prototype.readBoolean = function () {  return (this._readTag(ASN1.Boolean) === 0 ? false : true);};Reader.prototype.readEnumeration = function () {  return this._readTag(ASN1.Enumeration);};Reader.prototype.readString = function (tag, retbuf) {  if (!tag)    tag = ASN1.OctetString;  var b = this.peek();  if (b === null)    return null;  if (b !== tag)    throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +                              ': got 0x' + b.toString(16));  var o = this.readLength(this._offset + 1); // stored in `length`  if (o === null)    return null;  if (this.length > this._size - o)    return null;  this._offset = o;  if (this.length === 0)    return retbuf ? Buffer.alloc(0) : '';  var str = this._buf.slice(this._offset, this._offset + this.length);  this._offset += this.length;  return retbuf ? str : str.toString('utf8');};Reader.prototype.readOID = function (tag) {  if (!tag)    tag = ASN1.OID;  var b = this.readString(tag, true);  if (b === null)    return null;  var values = [];  var value = 0;  for (var i = 0; i < b.length; i++) {    var byte = b[i] & 0xff;    value <<= 7;    value += byte & 0x7f;    if ((byte & 0x80) === 0) {      values.push(value);      value = 0;    }  }  value = values.shift();  values.unshift(value % 40);  values.unshift((value / 40) >> 0);  return values.join('.');};Reader.prototype._readTag = function (tag) {  assert.ok(tag !== undefined);  var b = this.peek();  if (b === null)    return null;  if (b !== tag)    throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +                              ': got 0x' + b.toString(16));  var o = this.readLength(this._offset + 1); // stored in `length`  if (o === null)    return null;  if (this.length > 4)    throw newInvalidAsn1Error('Integer too long: ' + this.length);  if (this.length > this._size - o)    return null;  this._offset = o;  var fb = this._buf[this._offset];  var value = 0;  for (var i = 0; i < this.length; i++) {    value <<= 8;    value |= (this._buf[this._offset++] & 0xff);  }  if ((fb & 0x80) === 0x80 && i !== 4)    value -= (1 << (i * 8));  return value >> 0;};// --- Exported APImodule.exports = Reader;
 |