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