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
						
					
					
				'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;
 | 
						|
}
 |