UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

643 lines (565 loc) 16.3 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/crypto/webidl.js import { makeException, createEnumConverter, } from "nstdlib/lib/internal/webidl"; import { kEmptyObject, setOwnProperty } from "nstdlib/lib/internal/util"; import { CryptoKey } from "nstdlib/lib/internal/crypto/webcrypto"; import { getDataViewOrTypedArrayBuffer } from "nstdlib/lib/internal/crypto/util"; // Adapted from the following sources // - https://github.com/jsdom/webidl-conversions // Copyright Domenic Denicola. Licensed under BSD-2-Clause License. // Original license at https://github.com/jsdom/webidl-conversions/blob/master/LICENSE.md. // - https://github.com/denoland/deno // Copyright Deno authors. Licensed under MIT License. // Original license at https://github.com/denoland/deno/blob/main/LICENSE.md. // Changes include using primordials and stripping the code down to only what // WebCryptoAPI needs. // https://tc39.es/ecma262/#sec-tonumber function toNumber(value, opts = kEmptyObject) { switch (typeof value) { case "number": return value; case "bigint": throw makeException( "is a BigInt and cannot be converted to a number.", opts, ); case "symbol": throw makeException( "is a Symbol and cannot be converted to a number.", opts, ); default: return Number(value); } } function type(V) { if (V === null) return "Null"; switch (typeof V) { case "undefined": return "Undefined"; case "boolean": return "Boolean"; case "number": return "Number"; case "string": return "String"; case "symbol": return "Symbol"; case "bigint": return "BigInt"; case "object": // Fall through case "function": // Fall through default: // Per ES spec, typeof returns an implemention-defined value that is not // any of the existing ones for uncallable non-standard exotic objects. // Yet Type() which the Web IDL spec depends on returns Object for such // cases. So treat the default case as an object. return "Object"; } } const integerPart = MathTrunc; // This was updated to only consider bitlength up to 32 used by WebCryptoAPI function createIntegerConversion(bitLength) { const lowerBound = 0; const upperBound = MathPow(2, bitLength) - 1; const twoToTheBitLength = MathPow(2, bitLength); return (V, opts = kEmptyObject) => { let x = toNumber(V, opts); if (opts.enforceRange) { if (!Number.isFinite(x)) { throw makeException("is not a finite number.", opts); } x = integerPart(x); if (x < lowerBound || x > upperBound) { throw makeException( `is outside the expected range of ${lowerBound} to ${upperBound}.`, { __proto__: null, ...opts, code: "ERR_OUT_OF_RANGE" }, ); } return x; } if (!Number.isFinite(x) || x === 0) { return 0; } x = integerPart(x); if (x >= lowerBound && x <= upperBound) { return x; } x = x % twoToTheBitLength; return x; }; } const converters = {}; converters.boolean = (val) => !!val; converters.octet = createIntegerConversion(8); converters["unsigned short"] = createIntegerConversion(16); converters["unsigned long"] = createIntegerConversion(32); converters.DOMString = function (V, opts = kEmptyObject) { if (typeof V === "string") { return V; } else if (typeof V === "symbol") { throw makeException( "is a Symbol and cannot be converted to a string.", opts, ); } return String(V); }; converters.object = (V, opts) => { if (type(V) !== "Object") { throw makeException("is not an object.", opts); } return V; }; function isNonSharedArrayBuffer(V) { return Object.prototype.isPrototypeOf.call(ArrayBufferPrototype, V); } function isSharedArrayBuffer(V) { // SharedArrayBuffers can be disabled with --no-harmony-sharedarraybuffer. if (SharedArrayBuffer !== undefined) return Object.prototype.isPrototypeOf.call(SharedArrayBuffer.prototype, V); return false; } converters.Uint8Array = (V, opts = kEmptyObject) => { if ( !ArrayBuffer.isView(V) || TypedArrayPrototypeGetSymbolToStringTag(V) !== "Uint8Array" ) { throw makeException("is not an Uint8Array object.", opts); } if (isSharedArrayBuffer(TypedArrayPrototypeGetBuffer(V))) { throw makeException( "is a view on a SharedArrayBuffer, which is not allowed.", opts, ); } return V; }; converters.BufferSource = (V, opts = kEmptyObject) => { if (ArrayBuffer.isView(V)) { if (isSharedArrayBuffer(getDataViewOrTypedArrayBuffer(V))) { throw makeException( "is a view on a SharedArrayBuffer, which is not allowed.", opts, ); } return V; } if (!isNonSharedArrayBuffer(V)) { throw makeException( "is not instance of ArrayBuffer, Buffer, TypedArray, or DataView.", opts, ); } return V; }; converters["sequence<DOMString>"] = createSequenceConverter( converters.DOMString, ); function requiredArguments(length, required, opts = kEmptyObject) { if (length < required) { throw makeException( `${required} argument${ required === 1 ? "" : "s" } required, but only ${length} present.`, { __proto__: null, ...opts, context: "", code: "ERR_MISSING_ARGS" }, ); } } function createDictionaryConverter(name, dictionaries) { let hasRequiredKey = false; const allMembers = []; for (let i = 0; i < dictionaries.length; i++) { const member = dictionaries[i]; if (member.required) { hasRequiredKey = true; } Array.prototype.push.call(allMembers, member); } Array.prototype.sort.call(allMembers, (a, b) => { if (a.key === b.key) { return 0; } return a.key < b.key ? -1 : 1; }); return function (V, opts = kEmptyObject) { const typeV = type(V); switch (typeV) { case "Undefined": case "Null": case "Object": break; default: throw makeException("can not be converted to a dictionary", opts); } const esDict = V; const idlDict = {}; // Fast path null and undefined. if (V == null && !hasRequiredKey) { return idlDict; } for (const member of new (Array.prototype[Symbol.iterator]())(allMembers)) { const key = member.key; let esMemberValue; if (typeV === "Undefined" || typeV === "Null") { esMemberValue = undefined; } else { esMemberValue = esDict[key]; } if (esMemberValue !== undefined) { const context = `'${key}' of '${name}'${ opts.context ? ` (${opts.context})` : "" }`; const converter = member.converter; const idlMemberValue = converter(esMemberValue, { __proto__: null, ...opts, context, }); setOwnProperty(idlDict, key, idlMemberValue); } else if (member.required) { throw makeException( `can not be converted to '${name}' because '${key}' is required in '${name}'.`, { __proto__: null, ...opts, code: "ERR_MISSING_OPTION" }, ); } } return idlDict; }; } function createSequenceConverter(converter) { return function (V, opts = kEmptyObject) { if (type(V) !== "Object") { throw makeException("can not be converted to sequence.", opts); } const iter = V?.[Symbol.iterator]?.(); if (iter === undefined) { throw makeException("can not be converted to sequence.", opts); } const array = []; while (true) { const res = iter?.next?.(); if (res === undefined) { throw makeException("can not be converted to sequence.", opts); } if (res.done === true) break; const val = converter(res.value, { __proto__: null, ...opts, context: `${opts.context}, index ${array.length}`, }); Array.prototype.push.call(array, val); } return array; }; } function createInterfaceConverter(name, prototype) { return (V, opts) => { if (!Object.prototype.isPrototypeOf.call(prototype, V)) { throw makeException(`is not of type ${name}.`, opts); } return V; }; } converters.AlgorithmIdentifier = (V, opts) => { // Union for (object or DOMString) if (type(V) === "Object") { return converters.object(V, opts); } return converters.DOMString(V, opts); }; converters.KeyFormat = createEnumConverter("KeyFormat", [ "raw", "pkcs8", "spki", "jwk", ]); converters.KeyUsage = createEnumConverter("KeyUsage", [ "encrypt", "decrypt", "sign", "verify", "deriveKey", "deriveBits", "wrapKey", "unwrapKey", ]); converters["sequence<KeyUsage>"] = createSequenceConverter(converters.KeyUsage); converters.HashAlgorithmIdentifier = converters.AlgorithmIdentifier; const dictAlgorithm = [ { key: "name", converter: converters.DOMString, required: true, }, ]; converters.Algorithm = createDictionaryConverter("Algorithm", dictAlgorithm); converters.BigInteger = converters.Uint8Array; const dictRsaKeyGenParams = [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "modulusLength", converter: (V, opts) => converters["unsigned long"](V, { ...opts, enforceRange: true }), required: true, }, { key: "publicExponent", converter: converters.BigInteger, required: true, }, ]; converters.RsaKeyGenParams = createDictionaryConverter( "RsaKeyGenParams", dictRsaKeyGenParams, ); converters.RsaHashedKeyGenParams = createDictionaryConverter( "RsaHashedKeyGenParams", [ ...new (Array.prototype[Symbol.iterator]())(dictRsaKeyGenParams), { key: "hash", converter: converters.HashAlgorithmIdentifier, required: true, }, ], ); converters.RsaHashedImportParams = createDictionaryConverter( "RsaHashedImportParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "hash", converter: converters.HashAlgorithmIdentifier, required: true, }, ], ); converters.NamedCurve = converters.DOMString; converters.EcKeyImportParams = createDictionaryConverter("EcKeyImportParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "namedCurve", converter: converters.NamedCurve, required: true, }, ]); converters.EcKeyGenParams = createDictionaryConverter("EcKeyGenParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "namedCurve", converter: converters.NamedCurve, required: true, }, ]); converters.AesKeyGenParams = createDictionaryConverter("AesKeyGenParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "length", converter: (V, opts) => converters["unsigned short"](V, { ...opts, enforceRange: true }), required: true, }, ]); converters.HmacKeyGenParams = createDictionaryConverter("HmacKeyGenParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "hash", converter: converters.HashAlgorithmIdentifier, required: true, }, { key: "length", converter: (V, opts) => converters["unsigned long"](V, { ...opts, enforceRange: true }), }, ]); converters.RsaPssParams = createDictionaryConverter("RsaPssParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "saltLength", converter: (V, opts) => converters["unsigned long"](V, { ...opts, enforceRange: true }), required: true, }, ]); converters.RsaOaepParams = createDictionaryConverter("RsaOaepParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "label", converter: converters.BufferSource, }, ]); converters.EcdsaParams = createDictionaryConverter("EcdsaParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "hash", converter: converters.HashAlgorithmIdentifier, required: true, }, ]); converters.HmacImportParams = createDictionaryConverter("HmacImportParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "hash", converter: converters.HashAlgorithmIdentifier, required: true, }, { key: "length", converter: (V, opts) => converters["unsigned long"](V, { ...opts, enforceRange: true }), }, ]); const simpleDomStringKey = (key) => ({ key, converter: converters.DOMString }); converters.RsaOtherPrimesInfo = createDictionaryConverter( "RsaOtherPrimesInfo", [simpleDomStringKey("r"), simpleDomStringKey("d"), simpleDomStringKey("t")], ); converters["sequence<RsaOtherPrimesInfo>"] = createSequenceConverter( converters.RsaOtherPrimesInfo, ); converters.JsonWebKey = createDictionaryConverter("JsonWebKey", [ simpleDomStringKey("kty"), simpleDomStringKey("use"), { key: "key_ops", converter: converters["sequence<DOMString>"], }, simpleDomStringKey("alg"), { key: "ext", converter: converters.boolean, }, simpleDomStringKey("crv"), simpleDomStringKey("x"), simpleDomStringKey("y"), simpleDomStringKey("d"), simpleDomStringKey("n"), simpleDomStringKey("e"), simpleDomStringKey("p"), simpleDomStringKey("q"), simpleDomStringKey("dp"), simpleDomStringKey("dq"), simpleDomStringKey("qi"), { key: "oth", converter: converters["sequence<RsaOtherPrimesInfo>"], }, simpleDomStringKey("k"), ]); converters.HkdfParams = createDictionaryConverter("HkdfParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "hash", converter: converters.HashAlgorithmIdentifier, required: true, }, { key: "salt", converter: converters.BufferSource, required: true, }, { key: "info", converter: converters.BufferSource, required: true, }, ]); converters.Pbkdf2Params = createDictionaryConverter("Pbkdf2Params", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "hash", converter: converters.HashAlgorithmIdentifier, required: true, }, { key: "iterations", converter: (V, opts) => converters["unsigned long"](V, { ...opts, enforceRange: true }), required: true, }, { key: "salt", converter: converters.BufferSource, required: true, }, ]); converters.AesDerivedKeyParams = createDictionaryConverter( "AesDerivedKeyParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "length", converter: (V, opts) => converters["unsigned short"](V, { ...opts, enforceRange: true }), required: true, }, ], ); converters.AesCbcParams = createDictionaryConverter("AesCbcParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "iv", converter: converters.BufferSource, required: true, }, ]); converters.AesGcmParams = createDictionaryConverter("AesGcmParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "iv", converter: converters.BufferSource, required: true, }, { key: "tagLength", converter: (V, opts) => converters.octet(V, { ...opts, enforceRange: true }), }, { key: "additionalData", converter: converters.BufferSource, }, ]); converters.AesCtrParams = createDictionaryConverter("AesCtrParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "counter", converter: converters.BufferSource, required: true, }, { key: "length", converter: (V, opts) => converters.octet(V, { ...opts, enforceRange: true }), required: true, }, ]); converters.CryptoKey = createInterfaceConverter( "CryptoKey", CryptoKey.prototype, ); converters.EcdhKeyDeriveParams = createDictionaryConverter( "EcdhKeyDeriveParams", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "public", converter: converters.CryptoKey, required: true, }, ], ); converters.Ed448Params = createDictionaryConverter("Ed448Params", [ ...new (Array.prototype[Symbol.iterator]())(dictAlgorithm), { key: "context", converter: converters.BufferSource, required: false, }, ]); export { converters }; export { requiredArguments };