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