You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					336 lines
				
				7.9 KiB
			
		
		
			
		
	
	
					336 lines
				
				7.9 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const inherits = require('inherits');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const bignum = require('bn.js');
							 | 
						||
| 
								 | 
							
								const DecoderBuffer = require('../base/buffer').DecoderBuffer;
							 | 
						||
| 
								 | 
							
								const Node = require('../base/node');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Import DER constants
							 | 
						||
| 
								 | 
							
								const der = require('../constants/der');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function DERDecoder(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 = DERDecoder;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERDecoder.prototype.decode = function decode(data, options) {
							 | 
						||
| 
								 | 
							
								  if (!DecoderBuffer.isDecoderBuffer(data)) {
							 | 
						||
| 
								 | 
							
								    data = new DecoderBuffer(data, options);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this.tree._decode(data, options);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Tree methods
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function DERNode(parent) {
							 | 
						||
| 
								 | 
							
								  Node.call(this, 'der', parent);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								inherits(DERNode, Node);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._peekTag = function peekTag(buffer, tag, any) {
							 | 
						||
| 
								 | 
							
								  if (buffer.isEmpty())
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const state = buffer.save();
							 | 
						||
| 
								 | 
							
								  const decodedTag = derDecodeTag(buffer, 'Failed to peek tag: "' + tag + '"');
							 | 
						||
| 
								 | 
							
								  if (buffer.isError(decodedTag))
							 | 
						||
| 
								 | 
							
								    return decodedTag;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  buffer.restore(state);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return decodedTag.tag === tag || decodedTag.tagStr === tag ||
							 | 
						||
| 
								 | 
							
								    (decodedTag.tagStr + 'of') === tag || any;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) {
							 | 
						||
| 
								 | 
							
								  const decodedTag = derDecodeTag(buffer,
							 | 
						||
| 
								 | 
							
								    'Failed to decode tag of "' + tag + '"');
							 | 
						||
| 
								 | 
							
								  if (buffer.isError(decodedTag))
							 | 
						||
| 
								 | 
							
								    return decodedTag;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let len = derDecodeLen(buffer,
							 | 
						||
| 
								 | 
							
								    decodedTag.primitive,
							 | 
						||
| 
								 | 
							
								    'Failed to get length of "' + tag + '"');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Failure
							 | 
						||
| 
								 | 
							
								  if (buffer.isError(len))
							 | 
						||
| 
								 | 
							
								    return len;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!any &&
							 | 
						||
| 
								 | 
							
								      decodedTag.tag !== tag &&
							 | 
						||
| 
								 | 
							
								      decodedTag.tagStr !== tag &&
							 | 
						||
| 
								 | 
							
								      decodedTag.tagStr + 'of' !== tag) {
							 | 
						||
| 
								 | 
							
								    return buffer.error('Failed to match tag: "' + tag + '"');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (decodedTag.primitive || len !== null)
							 | 
						||
| 
								 | 
							
								    return buffer.skip(len, 'Failed to match body of: "' + tag + '"');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Indefinite length... find END tag
							 | 
						||
| 
								 | 
							
								  const state = buffer.save();
							 | 
						||
| 
								 | 
							
								  const res = this._skipUntilEnd(
							 | 
						||
| 
								 | 
							
								    buffer,
							 | 
						||
| 
								 | 
							
								    'Failed to skip indefinite length body: "' + this.tag + '"');
							 | 
						||
| 
								 | 
							
								  if (buffer.isError(res))
							 | 
						||
| 
								 | 
							
								    return res;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  len = buffer.offset - state.offset;
							 | 
						||
| 
								 | 
							
								  buffer.restore(state);
							 | 
						||
| 
								 | 
							
								  return buffer.skip(len, 'Failed to match body of: "' + tag + '"');
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer, fail) {
							 | 
						||
| 
								 | 
							
								  for (;;) {
							 | 
						||
| 
								 | 
							
								    const tag = derDecodeTag(buffer, fail);
							 | 
						||
| 
								 | 
							
								    if (buffer.isError(tag))
							 | 
						||
| 
								 | 
							
								      return tag;
							 | 
						||
| 
								 | 
							
								    const len = derDecodeLen(buffer, tag.primitive, fail);
							 | 
						||
| 
								 | 
							
								    if (buffer.isError(len))
							 | 
						||
| 
								 | 
							
								      return len;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let res;
							 | 
						||
| 
								 | 
							
								    if (tag.primitive || len !== null)
							 | 
						||
| 
								 | 
							
								      res = buffer.skip(len);
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      res = this._skipUntilEnd(buffer, fail);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Failure
							 | 
						||
| 
								 | 
							
								    if (buffer.isError(res))
							 | 
						||
| 
								 | 
							
								      return res;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (tag.tagStr === 'end')
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._decodeList = function decodeList(buffer, tag, decoder,
							 | 
						||
| 
								 | 
							
								  options) {
							 | 
						||
| 
								 | 
							
								  const result = [];
							 | 
						||
| 
								 | 
							
								  while (!buffer.isEmpty()) {
							 | 
						||
| 
								 | 
							
								    const possibleEnd = this._peekTag(buffer, 'end');
							 | 
						||
| 
								 | 
							
								    if (buffer.isError(possibleEnd))
							 | 
						||
| 
								 | 
							
								      return possibleEnd;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const res = decoder.decode(buffer, 'der', options);
							 | 
						||
| 
								 | 
							
								    if (buffer.isError(res) && possibleEnd)
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    result.push(res);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return result;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._decodeStr = function decodeStr(buffer, tag) {
							 | 
						||
| 
								 | 
							
								  if (tag === 'bitstr') {
							 | 
						||
| 
								 | 
							
								    const unused = buffer.readUInt8();
							 | 
						||
| 
								 | 
							
								    if (buffer.isError(unused))
							 | 
						||
| 
								 | 
							
								      return unused;
							 | 
						||
| 
								 | 
							
								    return { unused: unused, data: buffer.raw() };
							 | 
						||
| 
								 | 
							
								  } else if (tag === 'bmpstr') {
							 | 
						||
| 
								 | 
							
								    const raw = buffer.raw();
							 | 
						||
| 
								 | 
							
								    if (raw.length % 2 === 1)
							 | 
						||
| 
								 | 
							
								      return buffer.error('Decoding of string type: bmpstr length mismatch');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let str = '';
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < raw.length / 2; i++) {
							 | 
						||
| 
								 | 
							
								      str += String.fromCharCode(raw.readUInt16BE(i * 2));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return str;
							 | 
						||
| 
								 | 
							
								  } else if (tag === 'numstr') {
							 | 
						||
| 
								 | 
							
								    const numstr = buffer.raw().toString('ascii');
							 | 
						||
| 
								 | 
							
								    if (!this._isNumstr(numstr)) {
							 | 
						||
| 
								 | 
							
								      return buffer.error('Decoding of string type: ' +
							 | 
						||
| 
								 | 
							
								                          'numstr unsupported characters');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return numstr;
							 | 
						||
| 
								 | 
							
								  } else if (tag === 'octstr') {
							 | 
						||
| 
								 | 
							
								    return buffer.raw();
							 | 
						||
| 
								 | 
							
								  } else if (tag === 'objDesc') {
							 | 
						||
| 
								 | 
							
								    return buffer.raw();
							 | 
						||
| 
								 | 
							
								  } else if (tag === 'printstr') {
							 | 
						||
| 
								 | 
							
								    const printstr = buffer.raw().toString('ascii');
							 | 
						||
| 
								 | 
							
								    if (!this._isPrintstr(printstr)) {
							 | 
						||
| 
								 | 
							
								      return buffer.error('Decoding of string type: ' +
							 | 
						||
| 
								 | 
							
								                          'printstr unsupported characters');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return printstr;
							 | 
						||
| 
								 | 
							
								  } else if (/str$/.test(tag)) {
							 | 
						||
| 
								 | 
							
								    return buffer.raw().toString();
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    return buffer.error('Decoding of string type: ' + tag + ' unsupported');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._decodeObjid = function decodeObjid(buffer, values, relative) {
							 | 
						||
| 
								 | 
							
								  let result;
							 | 
						||
| 
								 | 
							
								  const identifiers = [];
							 | 
						||
| 
								 | 
							
								  let ident = 0;
							 | 
						||
| 
								 | 
							
								  let subident = 0;
							 | 
						||
| 
								 | 
							
								  while (!buffer.isEmpty()) {
							 | 
						||
| 
								 | 
							
								    subident = buffer.readUInt8();
							 | 
						||
| 
								 | 
							
								    ident <<= 7;
							 | 
						||
| 
								 | 
							
								    ident |= subident & 0x7f;
							 | 
						||
| 
								 | 
							
								    if ((subident & 0x80) === 0) {
							 | 
						||
| 
								 | 
							
								      identifiers.push(ident);
							 | 
						||
| 
								 | 
							
								      ident = 0;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (subident & 0x80)
							 | 
						||
| 
								 | 
							
								    identifiers.push(ident);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const first = (identifiers[0] / 40) | 0;
							 | 
						||
| 
								 | 
							
								  const second = identifiers[0] % 40;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (relative)
							 | 
						||
| 
								 | 
							
								    result = identifiers;
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    result = [first, second].concat(identifiers.slice(1));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (values) {
							 | 
						||
| 
								 | 
							
								    let tmp = values[result.join(' ')];
							 | 
						||
| 
								 | 
							
								    if (tmp === undefined)
							 | 
						||
| 
								 | 
							
								      tmp = values[result.join('.')];
							 | 
						||
| 
								 | 
							
								    if (tmp !== undefined)
							 | 
						||
| 
								 | 
							
								      result = tmp;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return result;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._decodeTime = function decodeTime(buffer, tag) {
							 | 
						||
| 
								 | 
							
								  const str = buffer.raw().toString();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let year;
							 | 
						||
| 
								 | 
							
								  let mon;
							 | 
						||
| 
								 | 
							
								  let day;
							 | 
						||
| 
								 | 
							
								  let hour;
							 | 
						||
| 
								 | 
							
								  let min;
							 | 
						||
| 
								 | 
							
								  let sec;
							 | 
						||
| 
								 | 
							
								  if (tag === 'gentime') {
							 | 
						||
| 
								 | 
							
								    year = str.slice(0, 4) | 0;
							 | 
						||
| 
								 | 
							
								    mon = str.slice(4, 6) | 0;
							 | 
						||
| 
								 | 
							
								    day = str.slice(6, 8) | 0;
							 | 
						||
| 
								 | 
							
								    hour = str.slice(8, 10) | 0;
							 | 
						||
| 
								 | 
							
								    min = str.slice(10, 12) | 0;
							 | 
						||
| 
								 | 
							
								    sec = str.slice(12, 14) | 0;
							 | 
						||
| 
								 | 
							
								  } else if (tag === 'utctime') {
							 | 
						||
| 
								 | 
							
								    year = str.slice(0, 2) | 0;
							 | 
						||
| 
								 | 
							
								    mon = str.slice(2, 4) | 0;
							 | 
						||
| 
								 | 
							
								    day = str.slice(4, 6) | 0;
							 | 
						||
| 
								 | 
							
								    hour = str.slice(6, 8) | 0;
							 | 
						||
| 
								 | 
							
								    min = str.slice(8, 10) | 0;
							 | 
						||
| 
								 | 
							
								    sec = str.slice(10, 12) | 0;
							 | 
						||
| 
								 | 
							
								    if (year < 70)
							 | 
						||
| 
								 | 
							
								      year = 2000 + year;
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      year = 1900 + year;
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    return buffer.error('Decoding ' + tag + ' time is not supported yet');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return Date.UTC(year, mon - 1, day, hour, min, sec, 0);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._decodeNull = function decodeNull() {
							 | 
						||
| 
								 | 
							
								  return null;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._decodeBool = function decodeBool(buffer) {
							 | 
						||
| 
								 | 
							
								  const res = buffer.readUInt8();
							 | 
						||
| 
								 | 
							
								  if (buffer.isError(res))
							 | 
						||
| 
								 | 
							
								    return res;
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    return res !== 0;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._decodeInt = function decodeInt(buffer, values) {
							 | 
						||
| 
								 | 
							
								  // Bigint, return as it is (assume big endian)
							 | 
						||
| 
								 | 
							
								  const raw = buffer.raw();
							 | 
						||
| 
								 | 
							
								  let res = new bignum(raw);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (values)
							 | 
						||
| 
								 | 
							
								    res = values[res.toString(10)] || res;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return res;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DERNode.prototype._use = function use(entity, obj) {
							 | 
						||
| 
								 | 
							
								  if (typeof entity === 'function')
							 | 
						||
| 
								 | 
							
								    entity = entity(obj);
							 | 
						||
| 
								 | 
							
								  return entity._getDecoder('der').tree;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Utility methods
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function derDecodeTag(buf, fail) {
							 | 
						||
| 
								 | 
							
								  let tag = buf.readUInt8(fail);
							 | 
						||
| 
								 | 
							
								  if (buf.isError(tag))
							 | 
						||
| 
								 | 
							
								    return tag;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const cls = der.tagClass[tag >> 6];
							 | 
						||
| 
								 | 
							
								  const primitive = (tag & 0x20) === 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Multi-octet tag - load
							 | 
						||
| 
								 | 
							
								  if ((tag & 0x1f) === 0x1f) {
							 | 
						||
| 
								 | 
							
								    let oct = tag;
							 | 
						||
| 
								 | 
							
								    tag = 0;
							 | 
						||
| 
								 | 
							
								    while ((oct & 0x80) === 0x80) {
							 | 
						||
| 
								 | 
							
								      oct = buf.readUInt8(fail);
							 | 
						||
| 
								 | 
							
								      if (buf.isError(oct))
							 | 
						||
| 
								 | 
							
								        return oct;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      tag <<= 7;
							 | 
						||
| 
								 | 
							
								      tag |= oct & 0x7f;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    tag &= 0x1f;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  const tagStr = der.tag[tag];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    cls: cls,
							 | 
						||
| 
								 | 
							
								    primitive: primitive,
							 | 
						||
| 
								 | 
							
								    tag: tag,
							 | 
						||
| 
								 | 
							
								    tagStr: tagStr
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function derDecodeLen(buf, primitive, fail) {
							 | 
						||
| 
								 | 
							
								  let len = buf.readUInt8(fail);
							 | 
						||
| 
								 | 
							
								  if (buf.isError(len))
							 | 
						||
| 
								 | 
							
								    return len;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Indefinite form
							 | 
						||
| 
								 | 
							
								  if (!primitive && len === 0x80)
							 | 
						||
| 
								 | 
							
								    return null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Definite form
							 | 
						||
| 
								 | 
							
								  if ((len & 0x80) === 0) {
							 | 
						||
| 
								 | 
							
								    // Short form
							 | 
						||
| 
								 | 
							
								    return len;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Long form
							 | 
						||
| 
								 | 
							
								  const num = len & 0x7f;
							 | 
						||
| 
								 | 
							
								  if (num > 4)
							 | 
						||
| 
								 | 
							
								    return buf.error('length octect is too long');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  len = 0;
							 | 
						||
| 
								 | 
							
								  for (let i = 0; i < num; i++) {
							 | 
						||
| 
								 | 
							
								    len <<= 8;
							 | 
						||
| 
								 | 
							
								    const j = buf.readUInt8(fail);
							 | 
						||
| 
								 | 
							
								    if (buf.isError(j))
							 | 
						||
| 
								 | 
							
								      return j;
							 | 
						||
| 
								 | 
							
								    len |= j;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return len;
							 | 
						||
| 
								 | 
							
								}
							 |