// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // Copyright 2017 Fedor Indutny. All rights reserved. MIT license. import bignum from "internal:deno_node/polyfills/_crypto/crypto_browserify/bn.js/bn.js"; import { DecoderBuffer } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/buffer.js"; import { Node } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js"; import * as der from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/constants/der.js"; export 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); } 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 = Object.create(Node.prototype, { constructor: { value: DERNode, enumerable: false, writable: true, configurable: true, }, }); 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; }