// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // Copyright 2017 Fedor Indutny. All rights reserved. MIT license. import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; import { Node } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js"; // Import DER constants import * as der from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/constants/der.js"; export function DEREncoder(entity) { this.enc = "der"; this.name = entity.name; this.entity = entity; // Construct base tree this.tree = new DERNode(); this.tree._init(entity.body); } DEREncoder.prototype.encode = function encode(data, reporter) { return this.tree._encode(data, reporter).join(); }; // 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._encodeComposite = function encodeComposite( tag, primitive, cls, content, ) { const encodedTag = encodeTag(tag, primitive, cls, this.reporter); // Short form if (content.length < 0x80) { const header = Buffer.alloc(2); header[0] = encodedTag; header[1] = content.length; return this._createEncoderBuffer([header, content]); } // Long form // Count octets required to store length let lenOctets = 1; for (let i = content.length; i >= 0x100; i >>= 8) { lenOctets++; } const header = Buffer.alloc(1 + 1 + lenOctets); header[0] = encodedTag; header[1] = 0x80 | lenOctets; for (let i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8) { header[i] = j & 0xff; } return this._createEncoderBuffer([header, content]); }; DERNode.prototype._encodeStr = function encodeStr(str, tag) { if (tag === "bitstr") { return this._createEncoderBuffer([str.unused | 0, str.data]); } else if (tag === "bmpstr") { const buf = Buffer.alloc(str.length * 2); for (let i = 0; i < str.length; i++) { buf.writeUInt16BE(str.charCodeAt(i), i * 2); } return this._createEncoderBuffer(buf); } else if (tag === "numstr") { if (!this._isNumstr(str)) { return this.reporter.error( "Encoding of string type: numstr supports " + "only digits and space", ); } return this._createEncoderBuffer(str); } else if (tag === "printstr") { if (!this._isPrintstr(str)) { return this.reporter.error( "Encoding of string type: printstr supports " + "only latin upper and lower case letters, " + "digits, space, apostrophe, left and rigth " + "parenthesis, plus sign, comma, hyphen, " + "dot, slash, colon, equal sign, " + "question mark", ); } return this._createEncoderBuffer(str); } else if (/str$/.test(tag)) { return this._createEncoderBuffer(str); } else if (tag === "objDesc") { return this._createEncoderBuffer(str); } else { return this.reporter.error( "Encoding of string type: " + tag + " unsupported", ); } }; DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) { if (typeof id === "string") { if (!values) { return this.reporter.error("string objid given, but no values map found"); } // deno-lint-ignore no-prototype-builtins if (!values.hasOwnProperty(id)) { return this.reporter.error("objid not found in values map"); } id = values[id].split(/[\s.]+/g); for (let i = 0; i < id.length; i++) { id[i] |= 0; } } else if (Array.isArray(id)) { id = id.slice(); for (let i = 0; i < id.length; i++) { id[i] |= 0; } } if (!Array.isArray(id)) { return this.reporter.error( "objid() should be either array or string, " + "got: " + JSON.stringify(id), ); } if (!relative) { if (id[1] >= 40) { return this.reporter.error("Second objid identifier OOB"); } id.splice(0, 2, id[0] * 40 + id[1]); } // Count number of octets let size = 0; for (let i = 0; i < id.length; i++) { let ident = id[i]; for (size++; ident >= 0x80; ident >>= 7) { size++; } } const objid = Buffer.alloc(size); let offset = objid.length - 1; for (let i = id.length - 1; i >= 0; i--) { let ident = id[i]; objid[offset--] = ident & 0x7f; while ((ident >>= 7) > 0) { objid[offset--] = 0x80 | (ident & 0x7f); } } return this._createEncoderBuffer(objid); }; function two(num) { if (num < 10) { return "0" + num; } else { return num; } } DERNode.prototype._encodeTime = function encodeTime(time, tag) { let str; const date = new Date(time); if (tag === "gentime") { str = [ two(date.getUTCFullYear()), two(date.getUTCMonth() + 1), two(date.getUTCDate()), two(date.getUTCHours()), two(date.getUTCMinutes()), two(date.getUTCSeconds()), "Z", ].join(""); } else if (tag === "utctime") { str = [ two(date.getUTCFullYear() % 100), two(date.getUTCMonth() + 1), two(date.getUTCDate()), two(date.getUTCHours()), two(date.getUTCMinutes()), two(date.getUTCSeconds()), "Z", ].join(""); } else { this.reporter.error("Encoding " + tag + " time is not supported yet"); } return this._encodeStr(str, "octstr"); }; DERNode.prototype._encodeNull = function encodeNull() { return this._createEncoderBuffer(""); }; DERNode.prototype._encodeInt = function encodeInt(num, values) { if (typeof num === "string") { if (!values) { return this.reporter.error("String int or enum given, but no values map"); } // deno-lint-ignore no-prototype-builtins if (!values.hasOwnProperty(num)) { return this.reporter.error( "Values map doesn't contain: " + JSON.stringify(num), ); } num = values[num]; } // Bignum, assume big endian if (typeof num !== "number" && !Buffer.isBuffer(num)) { const numArray = num.toArray(); if (!num.sign && numArray[0] & 0x80) { numArray.unshift(0); } num = Buffer.from(numArray); } if (Buffer.isBuffer(num)) { let size = num.length; if (num.length === 0) { size++; } const out = Buffer.alloc(size); num.copy(out); if (num.length === 0) { out[0] = 0; } return this._createEncoderBuffer(out); } if (num < 0x80) { return this._createEncoderBuffer(num); } if (num < 0x100) { return this._createEncoderBuffer([0, num]); } let size = 1; for (let i = num; i >= 0x100; i >>= 8) { size++; } const out = new Array(size); for (let i = out.length - 1; i >= 0; i--) { out[i] = num & 0xff; num >>= 8; } if (out[0] & 0x80) { out.unshift(0); } return this._createEncoderBuffer(Buffer.from(out)); }; DERNode.prototype._encodeBool = function encodeBool(value) { return this._createEncoderBuffer(value ? 0xff : 0); }; DERNode.prototype._use = function use(entity, obj) { if (typeof entity === "function") { entity = entity(obj); } return entity._getEncoder("der").tree; }; DERNode.prototype._skipDefault = function skipDefault( dataBuffer, reporter, parent, ) { const state = this._baseState; let i; if (state["default"] === null) { return false; } const data = dataBuffer.join(); if (state.defaultBuffer === undefined) { state.defaultBuffer = this._encodeValue(state["default"], reporter, parent) .join(); } if (data.length !== state.defaultBuffer.length) { return false; } for (i = 0; i < data.length; i++) { if (data[i] !== state.defaultBuffer[i]) { return false; } } return true; }; // Utility methods function encodeTag(tag, primitive, cls, reporter) { let res; if (tag === "seqof") { tag = "seq"; } else if (tag === "setof") { tag = "set"; } // deno-lint-ignore no-prototype-builtins if (der.tagByName.hasOwnProperty(tag)) { res = der.tagByName[tag]; } else if (typeof tag === "number" && (tag | 0) === tag) { res = tag; } else { return reporter.error("Unknown tag: " + tag); } if (res >= 0x1f) { return reporter.error("Multi-octet tag encoding unsupported"); } if (!primitive) { res |= 0x20; } res |= der.tagClassByName[cls || "universal"] << 6; return res; }