diff options
Diffstat (limited to 'ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js')
-rw-r--r-- | ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js new file mode 100644 index 000000000..9ab811507 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js @@ -0,0 +1,386 @@ +// 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; +} |