| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 | var inherits = require('inherits');var Buffer = require('buffer').Buffer;var asn1 = require('../../asn1');var base = asn1.base;// Import DER constantsvar der = asn1.constants.der;function DEREncoder(entity) {  this.enc = 'der';  this.name = entity.name;  this.entity = entity;  // Construct base tree  this.tree = new DERNode();  this.tree._init(entity.body);};module.exports = DEREncoder;DEREncoder.prototype.encode = function encode(data, reporter) {  return this.tree._encode(data, reporter).join();};// Tree methodsfunction DERNode(parent) {  base.Node.call(this, 'der', parent);}inherits(DERNode, base.Node);DERNode.prototype._encodeComposite = function encodeComposite(tag,                                                              primitive,                                                              cls,                                                              content) {  var encodedTag = encodeTag(tag, primitive, cls, this.reporter);  // Short form  if (content.length < 0x80) {    var header = new Buffer(2);    header[0] = encodedTag;    header[1] = content.length;    return this._createEncoderBuffer([ header, content ]);  }  // Long form  // Count octets required to store length  var lenOctets = 1;  for (var i = content.length; i >= 0x100; i >>= 8)    lenOctets++;  var header = new Buffer(1 + 1 + lenOctets);  header[0] = encodedTag;  header[1] = 0x80 | lenOctets;  for (var i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8)    header[i] = j & 0xff;  return this._createEncoderBuffer([ header, content ]);};DERNode.prototype._encodeStr = function encodeStr(str, tag) {  if (tag === 'bitstr') {    return this._createEncoderBuffer([ str.unused | 0, str.data ]);  } else if (tag === 'bmpstr') {    var buf = new Buffer(str.length * 2);    for (var i = 0; i < str.length; i++) {      buf.writeUInt16BE(str.charCodeAt(i), i * 2);    }    return this._createEncoderBuffer(buf);  } else if (tag === 'numstr') {    if (!this._isNumstr(str)) {      return this.reporter.error('Encoding of string type: numstr supports ' +                                 'only digits and space');    }    return this._createEncoderBuffer(str);  } else if (tag === 'printstr') {    if (!this._isPrintstr(str)) {      return this.reporter.error('Encoding of string type: printstr supports ' +                                 'only latin upper and lower case letters, ' +                                 'digits, space, apostrophe, left and rigth ' +                                 'parenthesis, plus sign, comma, hyphen, ' +                                 'dot, slash, colon, equal sign, ' +                                 'question mark');    }    return this._createEncoderBuffer(str);  } else if (/str$/.test(tag)) {    return this._createEncoderBuffer(str);  } else if (tag === 'objDesc') {    return this._createEncoderBuffer(str);  } else {    return this.reporter.error('Encoding of string type: ' + tag +                               ' unsupported');  }};DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) {  if (typeof id === 'string') {    if (!values)      return this.reporter.error('string objid given, but no values map found');    if (!values.hasOwnProperty(id))      return this.reporter.error('objid not found in values map');    id = values[id].split(/[\s\.]+/g);    for (var i = 0; i < id.length; i++)      id[i] |= 0;  } else if (Array.isArray(id)) {    id = id.slice();    for (var i = 0; i < id.length; i++)      id[i] |= 0;  }  if (!Array.isArray(id)) {    return this.reporter.error('objid() should be either array or string, ' +                               'got: ' + JSON.stringify(id));  }  if (!relative) {    if (id[1] >= 40)      return this.reporter.error('Second objid identifier OOB');    id.splice(0, 2, id[0] * 40 + id[1]);  }  // Count number of octets  var size = 0;  for (var i = 0; i < id.length; i++) {    var ident = id[i];    for (size++; ident >= 0x80; ident >>= 7)      size++;  }  var objid = new Buffer(size);  var offset = objid.length - 1;  for (var i = id.length - 1; i >= 0; i--) {    var ident = id[i];    objid[offset--] = ident & 0x7f;    while ((ident >>= 7) > 0)      objid[offset--] = 0x80 | (ident & 0x7f);  }  return this._createEncoderBuffer(objid);};function two(num) {  if (num < 10)    return '0' + num;  else    return num;}DERNode.prototype._encodeTime = function encodeTime(time, tag) {  var str;  var date = new Date(time);  if (tag === 'gentime') {    str = [      two(date.getFullYear()),      two(date.getUTCMonth() + 1),      two(date.getUTCDate()),      two(date.getUTCHours()),      two(date.getUTCMinutes()),      two(date.getUTCSeconds()),      'Z'    ].join('');  } else if (tag === 'utctime') {    str = [      two(date.getFullYear() % 100),      two(date.getUTCMonth() + 1),      two(date.getUTCDate()),      two(date.getUTCHours()),      two(date.getUTCMinutes()),      two(date.getUTCSeconds()),      'Z'    ].join('');  } else {    this.reporter.error('Encoding ' + tag + ' time is not supported yet');  }  return this._encodeStr(str, 'octstr');};DERNode.prototype._encodeNull = function encodeNull() {  return this._createEncoderBuffer('');};DERNode.prototype._encodeInt = function encodeInt(num, values) {  if (typeof num === 'string') {    if (!values)      return this.reporter.error('String int or enum given, but no values map');    if (!values.hasOwnProperty(num)) {      return this.reporter.error('Values map doesn\'t contain: ' +                                 JSON.stringify(num));    }    num = values[num];  }  // Bignum, assume big endian  if (typeof num !== 'number' && !Buffer.isBuffer(num)) {    var numArray = num.toArray();    if (!num.sign && numArray[0] & 0x80) {      numArray.unshift(0);    }    num = new Buffer(numArray);  }  if (Buffer.isBuffer(num)) {    var size = num.length;    if (num.length === 0)      size++;    var out = new Buffer(size);    num.copy(out);    if (num.length === 0)      out[0] = 0    return this._createEncoderBuffer(out);  }  if (num < 0x80)    return this._createEncoderBuffer(num);  if (num < 0x100)    return this._createEncoderBuffer([0, num]);  var size = 1;  for (var i = num; i >= 0x100; i >>= 8)    size++;  var out = new Array(size);  for (var i = out.length - 1; i >= 0; i--) {    out[i] = num & 0xff;    num >>= 8;  }  if(out[0] & 0x80) {    out.unshift(0);  }  return this._createEncoderBuffer(new Buffer(out));};DERNode.prototype._encodeBool = function encodeBool(value) {  return this._createEncoderBuffer(value ? 0xff : 0);};DERNode.prototype._use = function use(entity, obj) {  if (typeof entity === 'function')    entity = entity(obj);  return entity._getEncoder('der').tree;};DERNode.prototype._skipDefault = function skipDefault(dataBuffer, reporter, parent) {  var state = this._baseState;  var i;  if (state['default'] === null)    return false;  var data = dataBuffer.join();  if (state.defaultBuffer === undefined)    state.defaultBuffer = this._encodeValue(state['default'], reporter, parent).join();  if (data.length !== state.defaultBuffer.length)    return false;  for (i=0; i < data.length; i++)    if (data[i] !== state.defaultBuffer[i])      return false;  return true;};// Utility methodsfunction encodeTag(tag, primitive, cls, reporter) {  var res;  if (tag === 'seqof')    tag = 'seq';  else if (tag === 'setof')    tag = 'set';  if (der.tagByName.hasOwnProperty(tag))    res = der.tagByName[tag];  else if (typeof tag === 'number' && (tag | 0) === tag)    res = tag;  else    return reporter.error('Unknown tag: ' + tag);  if (res >= 0x1f)    return reporter.error('Multi-octet tag encoding unsupported');  if (!primitive)    res |= 0x20;  res |= (der.tagClassByName[cls || 'universal'] << 6);  return res;}
 |