diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2023-02-14 17:38:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-14 17:38:45 +0100 |
commit | d47147fb6ad229b1c039aff9d0959b6e281f4df5 (patch) | |
tree | 6e9e790f2b9bc71b5f0c9c7e64b95cae31579d58 /ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders | |
parent | 1d00bbe47e2ca14e2d2151518e02b2324461a065 (diff) |
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is
transpiled and snapshotted during the build process.
During the first pass a minimal amount of work was done to create the
snapshot, a lot of code in "ext/node" depends on presence of "Deno"
global. This code will be gradually fixed in the follow up PRs to migrate
it to import relevant APIs from "internal:" modules.
Currently the code from snapshot is not used in any way, and all
Node/npm compatibility still uses code from
"https://deno.land/std/node" (or from the location specified by
"DENO_NODE_COMPAT_URL"). This will also be handled in a follow
up PRs.
---------
Co-authored-by: crowlkats <crowlkats@toaxl.com>
Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
Diffstat (limited to 'ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders')
-rw-r--r-- | ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js | 386 | ||||
-rw-r--r-- | ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/pem.js | 63 |
2 files changed, 449 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; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/pem.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/pem.js new file mode 100644 index 000000000..3dedfb293 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/pem.js @@ -0,0 +1,63 @@ +// 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 { DERDecoder } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js"; + +export function PEMDecoder(entity) { + DERDecoder.call(this, entity); + this.enc = "pem"; +} +// inherits(PEMDecoder, DERDecoder); +PEMDecoder.prototype = Object.create(DERDecoder.prototype, { + constructor: { + value: PEMDecoder, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +PEMDecoder.prototype.decode = function decode(data, options) { + const lines = data.toString().split(/[\r\n]+/g); + + const label = options.label.toUpperCase(); + + const re = /^-----(BEGIN|END) ([^-]+)-----$/; + let start = -1; + let end = -1; + for (let i = 0; i < lines.length; i++) { + const match = lines[i].match(re); + if (match === null) { + continue; + } + + if (match[2] !== label) { + continue; + } + + if (start === -1) { + if (match[1] !== "BEGIN") { + break; + } + start = i; + } else { + if (match[1] !== "END") { + break; + } + end = i; + break; + } + } + if (start === -1 || end === -1) { + throw new Error("PEM section not found for: " + label); + } + + const base64 = lines.slice(start + 1, end).join(""); + // Remove excessive symbols + base64.replace(/[^a-z0-9+/=]+/gi, ""); + + const input = Buffer.from(base64, "base64"); + return DERDecoder.prototype.decode.call(this, input, options); +}; |