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