| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 | // 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;var DEFAULT_OPTS = {  size: 1024,  growthFactor: 8};// --- Helpersfunction merge(from, to) {  assert.ok(from);  assert.equal(typeof (from), 'object');  assert.ok(to);  assert.equal(typeof (to), 'object');  var keys = Object.getOwnPropertyNames(from);  keys.forEach(function (key) {    if (to[key])      return;    var value = Object.getOwnPropertyDescriptor(from, key);    Object.defineProperty(to, key, value);  });  return to;}// --- APIfunction Writer(options) {  options = merge(DEFAULT_OPTS, options || {});  this._buf = Buffer.alloc(options.size || 1024);  this._size = this._buf.length;  this._offset = 0;  this._options = options;  // A list of offsets in the buffer where we need to insert  // sequence tag/len pairs.  this._seq = [];}Object.defineProperty(Writer.prototype, 'buffer', {  get: function () {    if (this._seq.length)      throw newInvalidAsn1Error(this._seq.length + ' unended sequence(s)');    return (this._buf.slice(0, this._offset));  }});Writer.prototype.writeByte = function (b) {  if (typeof (b) !== 'number')    throw new TypeError('argument must be a Number');  this._ensure(1);  this._buf[this._offset++] = b;};Writer.prototype.writeInt = function (i, tag) {  if (typeof (i) !== 'number')    throw new TypeError('argument must be a Number');  if (typeof (tag) !== 'number')    tag = ASN1.Integer;  var sz = 4;  while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000 >> 0)) &&        (sz > 1)) {    sz--;    i <<= 8;  }  if (sz > 4)    throw newInvalidAsn1Error('BER ints cannot be > 0xffffffff');  this._ensure(2 + sz);  this._buf[this._offset++] = tag;  this._buf[this._offset++] = sz;  while (sz-- > 0) {    this._buf[this._offset++] = ((i & 0xff000000) >>> 24);    i <<= 8;  }};Writer.prototype.writeNull = function () {  this.writeByte(ASN1.Null);  this.writeByte(0x00);};Writer.prototype.writeEnumeration = function (i, tag) {  if (typeof (i) !== 'number')    throw new TypeError('argument must be a Number');  if (typeof (tag) !== 'number')    tag = ASN1.Enumeration;  return this.writeInt(i, tag);};Writer.prototype.writeBoolean = function (b, tag) {  if (typeof (b) !== 'boolean')    throw new TypeError('argument must be a Boolean');  if (typeof (tag) !== 'number')    tag = ASN1.Boolean;  this._ensure(3);  this._buf[this._offset++] = tag;  this._buf[this._offset++] = 0x01;  this._buf[this._offset++] = b ? 0xff : 0x00;};Writer.prototype.writeString = function (s, tag) {  if (typeof (s) !== 'string')    throw new TypeError('argument must be a string (was: ' + typeof (s) + ')');  if (typeof (tag) !== 'number')    tag = ASN1.OctetString;  var len = Buffer.byteLength(s);  this.writeByte(tag);  this.writeLength(len);  if (len) {    this._ensure(len);    this._buf.write(s, this._offset);    this._offset += len;  }};Writer.prototype.writeBuffer = function (buf, tag) {  if (typeof (tag) !== 'number')    throw new TypeError('tag must be a number');  if (!Buffer.isBuffer(buf))    throw new TypeError('argument must be a buffer');  this.writeByte(tag);  this.writeLength(buf.length);  this._ensure(buf.length);  buf.copy(this._buf, this._offset, 0, buf.length);  this._offset += buf.length;};Writer.prototype.writeStringArray = function (strings) {  if ((!strings instanceof Array))    throw new TypeError('argument must be an Array[String]');  var self = this;  strings.forEach(function (s) {    self.writeString(s);  });};// This is really to solve DER cases, but whatever for nowWriter.prototype.writeOID = function (s, tag) {  if (typeof (s) !== 'string')    throw new TypeError('argument must be a string');  if (typeof (tag) !== 'number')    tag = ASN1.OID;  if (!/^([0-9]+\.){3,}[0-9]+$/.test(s))    throw new Error('argument is not a valid OID string');  function encodeOctet(bytes, octet) {    if (octet < 128) {        bytes.push(octet);    } else if (octet < 16384) {        bytes.push((octet >>> 7) | 0x80);        bytes.push(octet & 0x7F);    } else if (octet < 2097152) {      bytes.push((octet >>> 14) | 0x80);      bytes.push(((octet >>> 7) | 0x80) & 0xFF);      bytes.push(octet & 0x7F);    } else if (octet < 268435456) {      bytes.push((octet >>> 21) | 0x80);      bytes.push(((octet >>> 14) | 0x80) & 0xFF);      bytes.push(((octet >>> 7) | 0x80) & 0xFF);      bytes.push(octet & 0x7F);    } else {      bytes.push(((octet >>> 28) | 0x80) & 0xFF);      bytes.push(((octet >>> 21) | 0x80) & 0xFF);      bytes.push(((octet >>> 14) | 0x80) & 0xFF);      bytes.push(((octet >>> 7) | 0x80) & 0xFF);      bytes.push(octet & 0x7F);    }  }  var tmp = s.split('.');  var bytes = [];  bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10));  tmp.slice(2).forEach(function (b) {    encodeOctet(bytes, parseInt(b, 10));  });  var self = this;  this._ensure(2 + bytes.length);  this.writeByte(tag);  this.writeLength(bytes.length);  bytes.forEach(function (b) {    self.writeByte(b);  });};Writer.prototype.writeLength = function (len) {  if (typeof (len) !== 'number')    throw new TypeError('argument must be a Number');  this._ensure(4);  if (len <= 0x7f) {    this._buf[this._offset++] = len;  } else if (len <= 0xff) {    this._buf[this._offset++] = 0x81;    this._buf[this._offset++] = len;  } else if (len <= 0xffff) {    this._buf[this._offset++] = 0x82;    this._buf[this._offset++] = len >> 8;    this._buf[this._offset++] = len;  } else if (len <= 0xffffff) {    this._buf[this._offset++] = 0x83;    this._buf[this._offset++] = len >> 16;    this._buf[this._offset++] = len >> 8;    this._buf[this._offset++] = len;  } else {    throw newInvalidAsn1Error('Length too long (> 4 bytes)');  }};Writer.prototype.startSequence = function (tag) {  if (typeof (tag) !== 'number')    tag = ASN1.Sequence | ASN1.Constructor;  this.writeByte(tag);  this._seq.push(this._offset);  this._ensure(3);  this._offset += 3;};Writer.prototype.endSequence = function () {  var seq = this._seq.pop();  var start = seq + 3;  var len = this._offset - start;  if (len <= 0x7f) {    this._shift(start, len, -2);    this._buf[seq] = len;  } else if (len <= 0xff) {    this._shift(start, len, -1);    this._buf[seq] = 0x81;    this._buf[seq + 1] = len;  } else if (len <= 0xffff) {    this._buf[seq] = 0x82;    this._buf[seq + 1] = len >> 8;    this._buf[seq + 2] = len;  } else if (len <= 0xffffff) {    this._shift(start, len, 1);    this._buf[seq] = 0x83;    this._buf[seq + 1] = len >> 16;    this._buf[seq + 2] = len >> 8;    this._buf[seq + 3] = len;  } else {    throw newInvalidAsn1Error('Sequence too long');  }};Writer.prototype._shift = function (start, len, shift) {  assert.ok(start !== undefined);  assert.ok(len !== undefined);  assert.ok(shift);  this._buf.copy(this._buf, start + shift, start, start + len);  this._offset += shift;};Writer.prototype._ensure = function (len) {  assert.ok(len);  if (this._size - this._offset < len) {    var sz = this._size * this._options.growthFactor;    if (sz - this._offset < len)      sz += len;    var buf = Buffer.alloc(sz);    this._buf.copy(buf, 0, 0, this._offset);    this._buf = buf;    this._size = sz;  }};// --- Exported APImodule.exports = Writer;
 |