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/base/node.js | |
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/base/node.js')
-rw-r--r-- | ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js | 734 |
1 files changed, 734 insertions, 0 deletions
diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js new file mode 100644 index 000000000..027778155 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js @@ -0,0 +1,734 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +import { Reporter } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/reporter.js"; +import { + DecoderBuffer, + EncoderBuffer, +} from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/buffer.js"; +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; + +// Supported tags +const tags = [ + "seq", + "seqof", + "set", + "setof", + "objid", + "bool", + "gentime", + "utctime", + "null_", + "enum", + "int", + "objDesc", + "bitstr", + "bmpstr", + "charstr", + "genstr", + "graphstr", + "ia5str", + "iso646str", + "numstr", + "octstr", + "printstr", + "t61str", + "unistr", + "utf8str", + "videostr", +]; + +// Public methods list +const methods = [ + "key", + "obj", + "use", + "optional", + "explicit", + "implicit", + "def", + "choice", + "any", + "contains", +].concat(tags); + +// Overrided methods list +const overrided = [ + "_peekTag", + "_decodeTag", + "_use", + "_decodeStr", + "_decodeObjid", + "_decodeTime", + "_decodeNull", + "_decodeInt", + "_decodeBool", + "_decodeList", + + "_encodeComposite", + "_encodeStr", + "_encodeObjid", + "_encodeTime", + "_encodeNull", + "_encodeInt", + "_encodeBool", +]; + +export function Node(enc, parent, name) { + const state = {}; + this._baseState = state; + + state.name = name; + state.enc = enc; + + state.parent = parent || null; + state.children = null; + + // State + state.tag = null; + state.args = null; + state.reverseArgs = null; + state.choice = null; + state.optional = false; + state.any = false; + state.obj = false; + state.use = null; + state.useDecoder = null; + state.key = null; + state["default"] = null; + state.explicit = null; + state.implicit = null; + state.contains = null; + + // Should create new instance on each method + if (!state.parent) { + state.children = []; + this._wrap(); + } +} + +const stateProps = [ + "enc", + "parent", + "children", + "tag", + "args", + "reverseArgs", + "choice", + "optional", + "any", + "obj", + "use", + "alteredUse", + "key", + "default", + "explicit", + "implicit", + "contains", +]; + +Node.prototype.clone = function clone() { + const state = this._baseState; + const cstate = {}; + stateProps.forEach(function (prop) { + cstate[prop] = state[prop]; + }); + const res = new this.constructor(cstate.parent); + res._baseState = cstate; + return res; +}; + +Node.prototype._wrap = function wrap() { + const state = this._baseState; + methods.forEach(function (method) { + this[method] = function _wrappedMethod() { + const clone = new this.constructor(this); + state.children.push(clone); + return clone[method].apply(clone, arguments); + }; + }, this); +}; + +Node.prototype._init = function init(body) { + const state = this._baseState; + + assert(state.parent === null); + body.call(this); + + // Filter children + state.children = state.children.filter(function (child) { + return child._baseState.parent === this; + }, this); + assert(state.children.length === 1, "Root node can have only one child"); +}; + +Node.prototype._useArgs = function useArgs(args) { + const state = this._baseState; + + // Filter children and args + const children = args.filter(function (arg) { + return arg instanceof this.constructor; + }, this); + args = args.filter(function (arg) { + return !(arg instanceof this.constructor); + }, this); + + if (children.length !== 0) { + assert(state.children === null); + state.children = children; + + // Replace parent to maintain backward link + children.forEach(function (child) { + child._baseState.parent = this; + }, this); + } + if (args.length !== 0) { + assert(state.args === null); + state.args = args; + state.reverseArgs = args.map(function (arg) { + if (typeof arg !== "object" || arg.constructor !== Object) { + return arg; + } + + const res = {}; + Object.keys(arg).forEach(function (key) { + if (key == (key | 0)) { + key |= 0; + } + const value = arg[key]; + res[value] = key; + }); + return res; + }); + } +}; + +// +// Overrided methods +// + +overrided.forEach(function (method) { + Node.prototype[method] = function _overrided() { + const state = this._baseState; + throw new Error(method + " not implemented for encoding: " + state.enc); + }; +}); + +// +// Public methods +// + +tags.forEach(function (tag) { + Node.prototype[tag] = function _tagMethod() { + const state = this._baseState; + const args = Array.prototype.slice.call(arguments); + + assert(state.tag === null); + state.tag = tag; + + this._useArgs(args); + + return this; + }; +}); + +Node.prototype.use = function use(item) { + assert(item); + const state = this._baseState; + + assert(state.use === null); + state.use = item; + + return this; +}; + +Node.prototype.optional = function optional() { + const state = this._baseState; + + state.optional = true; + + return this; +}; + +Node.prototype.def = function def(val) { + const state = this._baseState; + + assert(state["default"] === null); + state["default"] = val; + state.optional = true; + + return this; +}; + +Node.prototype.explicit = function explicit(num) { + const state = this._baseState; + + assert(state.explicit === null && state.implicit === null); + state.explicit = num; + + return this; +}; + +Node.prototype.implicit = function implicit(num) { + const state = this._baseState; + + assert(state.explicit === null && state.implicit === null); + state.implicit = num; + + return this; +}; + +Node.prototype.obj = function obj() { + const state = this._baseState; + const args = Array.prototype.slice.call(arguments); + + state.obj = true; + + if (args.length !== 0) { + this._useArgs(args); + } + + return this; +}; + +Node.prototype.key = function key(newKey) { + const state = this._baseState; + + assert(state.key === null); + state.key = newKey; + + return this; +}; + +Node.prototype.any = function any() { + const state = this._baseState; + + state.any = true; + + return this; +}; + +Node.prototype.choice = function choice(obj) { + const state = this._baseState; + + assert(state.choice === null); + state.choice = obj; + this._useArgs( + Object.keys(obj).map(function (key) { + return obj[key]; + }), + ); + + return this; +}; + +Node.prototype.contains = function contains(item) { + const state = this._baseState; + + assert(state.use === null); + state.contains = item; + + return this; +}; + +// +// Decoding +// + +Node.prototype._decode = function decode(input, options) { + const state = this._baseState; + + // Decode root node + if (state.parent === null) { + return input.wrapResult(state.children[0]._decode(input, options)); + } + + let result = state["default"]; + let present = true; + + let prevKey = null; + if (state.key !== null) { + prevKey = input.enterKey(state.key); + } + + // Check if tag is there + if (state.optional) { + let tag = null; + if (state.explicit !== null) { + tag = state.explicit; + } else if (state.implicit !== null) { + tag = state.implicit; + } else if (state.tag !== null) { + tag = state.tag; + } + + if (tag === null && !state.any) { + // Trial and Error + const save = input.save(); + try { + if (state.choice === null) { + this._decodeGeneric(state.tag, input, options); + } else { + this._decodeChoice(input, options); + } + present = true; + } catch (_e) { + present = false; + } + input.restore(save); + } else { + present = this._peekTag(input, tag, state.any); + + if (input.isError(present)) { + return present; + } + } + } + + // Push object on stack + let prevObj; + if (state.obj && present) { + prevObj = input.enterObject(); + } + + if (present) { + // Unwrap explicit values + if (state.explicit !== null) { + const explicit = this._decodeTag(input, state.explicit); + if (input.isError(explicit)) { + return explicit; + } + input = explicit; + } + + const start = input.offset; + + // Unwrap implicit and normal values + if (state.use === null && state.choice === null) { + let save; + if (state.any) { + save = input.save(); + } + const body = this._decodeTag( + input, + state.implicit !== null ? state.implicit : state.tag, + state.any, + ); + if (input.isError(body)) { + return body; + } + + if (state.any) { + result = input.raw(save); + } else { + input = body; + } + } + + if (options && options.track && state.tag !== null) { + options.track(input.path(), start, input.length, "tagged"); + } + + if (options && options.track && state.tag !== null) { + options.track(input.path(), input.offset, input.length, "content"); + } + + // Select proper method for tag + if (state.any) { + // no-op + } else if (state.choice === null) { + result = this._decodeGeneric(state.tag, input, options); + } else { + result = this._decodeChoice(input, options); + } + + if (input.isError(result)) { + return result; + } + + // Decode children + if (!state.any && state.choice === null && state.children !== null) { + state.children.forEach(function decodeChildren(child) { + // NOTE: We are ignoring errors here, to let parser continue with other + // parts of encoded data + child._decode(input, options); + }); + } + + // Decode contained/encoded by schema, only in bit or octet strings + if (state.contains && (state.tag === "octstr" || state.tag === "bitstr")) { + const data = new DecoderBuffer(result); + result = this._getUse(state.contains, input._reporterState.obj) + ._decode(data, options); + } + } + + // Pop object + if (state.obj && present) { + result = input.leaveObject(prevObj); + } + + // Set key + if (state.key !== null && (result !== null || present === true)) { + input.leaveKey(prevKey, state.key, result); + } else if (prevKey !== null) { + input.exitKey(prevKey); + } + + return result; +}; + +Node.prototype._decodeGeneric = function decodeGeneric(tag, input, options) { + const state = this._baseState; + + if (tag === "seq" || tag === "set") { + return null; + } + if (tag === "seqof" || tag === "setof") { + return this._decodeList(input, tag, state.args[0], options); + } else if (/str$/.test(tag)) { + return this._decodeStr(input, tag, options); + } else if (tag === "objid" && state.args) { + return this._decodeObjid(input, state.args[0], state.args[1], options); + } else if (tag === "objid") { + return this._decodeObjid(input, null, null, options); + } else if (tag === "gentime" || tag === "utctime") { + return this._decodeTime(input, tag, options); + } else if (tag === "null_") { + return this._decodeNull(input, options); + } else if (tag === "bool") { + return this._decodeBool(input, options); + } else if (tag === "objDesc") { + return this._decodeStr(input, tag, options); + } else if (tag === "int" || tag === "enum") { + return this._decodeInt(input, state.args && state.args[0], options); + } + + if (state.use !== null) { + return this._getUse(state.use, input._reporterState.obj) + ._decode(input, options); + } else { + return input.error("unknown tag: " + tag); + } +}; + +Node.prototype._getUse = function _getUse(entity, obj) { + const state = this._baseState; + // Create altered use decoder if implicit is set + state.useDecoder = this._use(entity, obj); + assert(state.useDecoder._baseState.parent === null); + state.useDecoder = state.useDecoder._baseState.children[0]; + if (state.implicit !== state.useDecoder._baseState.implicit) { + state.useDecoder = state.useDecoder.clone(); + state.useDecoder._baseState.implicit = state.implicit; + } + return state.useDecoder; +}; + +Node.prototype._decodeChoice = function decodeChoice(input, options) { + const state = this._baseState; + let result = null; + let match = false; + + Object.keys(state.choice).some(function (key) { + const save = input.save(); + const node = state.choice[key]; + try { + const value = node._decode(input, options); + if (input.isError(value)) { + return false; + } + + result = { type: key, value: value }; + match = true; + } catch (_e) { + input.restore(save); + return false; + } + return true; + }, this); + + if (!match) { + return input.error("Choice not matched"); + } + + return result; +}; + +// +// Encoding +// + +Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) { + return new EncoderBuffer(data, this.reporter); +}; + +Node.prototype._encode = function encode(data, reporter, parent) { + const state = this._baseState; + if (state["default"] !== null && state["default"] === data) { + return; + } + + const result = this._encodeValue(data, reporter, parent); + if (result === undefined) { + return; + } + + if (this._skipDefault(result, reporter, parent)) { + return; + } + + return result; +}; + +Node.prototype._encodeValue = function encode(data, reporter, parent) { + const state = this._baseState; + + // Decode root node + if (state.parent === null) { + return state.children[0]._encode(data, reporter || new Reporter()); + } + + let result = null; + + // Set reporter to share it with a child class + this.reporter = reporter; + + // Check if data is there + if (state.optional && data === undefined) { + if (state["default"] !== null) { + data = state["default"]; + } else { + return; + } + } + + // Encode children first + let content = null; + let primitive = false; + if (state.any) { + // Anything that was given is translated to buffer + result = this._createEncoderBuffer(data); + } else if (state.choice) { + result = this._encodeChoice(data, reporter); + } else if (state.contains) { + content = this._getUse(state.contains, parent)._encode(data, reporter); + primitive = true; + } else if (state.children) { + content = state.children.map(function (child) { + if (child._baseState.tag === "null_") { + return child._encode(null, reporter, data); + } + + if (child._baseState.key === null) { + return reporter.error("Child should have a key"); + } + const prevKey = reporter.enterKey(child._baseState.key); + + if (typeof data !== "object") { + return reporter.error("Child expected, but input is not object"); + } + + const res = child._encode(data[child._baseState.key], reporter, data); + reporter.leaveKey(prevKey); + + return res; + }, this).filter(function (child) { + return child; + }); + content = this._createEncoderBuffer(content); + } else { + if (state.tag === "seqof" || state.tag === "setof") { + // TODO(indutny): this should be thrown on DSL level + if (!(state.args && state.args.length === 1)) { + return reporter.error("Too many args for : " + state.tag); + } + + if (!Array.isArray(data)) { + return reporter.error("seqof/setof, but data is not Array"); + } + + const child = this.clone(); + child._baseState.implicit = null; + content = this._createEncoderBuffer(data.map(function (item) { + const state = this._baseState; + + return this._getUse(state.args[0], data)._encode(item, reporter); + }, child)); + } else if (state.use !== null) { + result = this._getUse(state.use, parent)._encode(data, reporter); + } else { + content = this._encodePrimitive(state.tag, data); + primitive = true; + } + } + + // Encode data itself + if (!state.any && state.choice === null) { + const tag = state.implicit !== null ? state.implicit : state.tag; + const cls = state.implicit === null ? "universal" : "context"; + + if (tag === null) { + if (state.use === null) { + reporter.error("Tag could be omitted only for .use()"); + } + } else { + if (state.use === null) { + result = this._encodeComposite(tag, primitive, cls, content); + } + } + } + + // Wrap in explicit + if (state.explicit !== null) { + result = this._encodeComposite(state.explicit, false, "context", result); + } + + return result; +}; + +Node.prototype._encodeChoice = function encodeChoice(data, reporter) { + const state = this._baseState; + + const node = state.choice[data.type]; + if (!node) { + assert( + false, + data.type + " not found in " + + JSON.stringify(Object.keys(state.choice)), + ); + } + return node._encode(data.value, reporter); +}; + +Node.prototype._encodePrimitive = function encodePrimitive(tag, data) { + const state = this._baseState; + + if (/str$/.test(tag)) { + return this._encodeStr(data, tag); + } else if (tag === "objid" && state.args) { + return this._encodeObjid(data, state.reverseArgs[0], state.args[1]); + } else if (tag === "objid") { + return this._encodeObjid(data, null, null); + } else if (tag === "gentime" || tag === "utctime") { + return this._encodeTime(data, tag); + } else if (tag === "null_") { + return this._encodeNull(); + } else if (tag === "int" || tag === "enum") { + return this._encodeInt(data, state.args && state.reverseArgs[0]); + } else if (tag === "bool") { + return this._encodeBool(data); + } else if (tag === "objDesc") { + return this._encodeStr(data, tag); + } else { + throw new Error("Unsupported tag: " + tag); + } +}; + +Node.prototype._isNumstr = function isNumstr(str) { + return /^[0-9 ]*$/.test(str); +}; + +Node.prototype._isPrintstr = function isPrintstr(str) { + return /^[A-Za-z0-9 '()+,-./:=?]*$/.test(str); +}; |