diff options
author | Leo Kettmeir <crowlkats@toaxl.com> | 2023-02-07 20:22:46 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-07 20:22:46 +0100 |
commit | b4aa1530970f7b9cc4e6f2f27e077852c4e178d3 (patch) | |
tree | 3d008912affe8550692183bd2697a386db5e3c79 /ext/crypto/00_crypto.js | |
parent | 65500f36e870b4ada3996b06aa287e30177d21a3 (diff) |
refactor: Use ES modules for internal runtime code (#17648)
This PR refactors all internal js files (except core) to be written as
ES modules.
`__bootstrap`has been mostly replaced with static imports in form in
`internal:[path to file from repo root]`.
To specify if files are ESM, an `esm` method has been added to
`Extension`, similar to the `js` method.
A new ModuleLoader called `InternalModuleLoader` has been added to
enable the loading of internal specifiers, which is used in all
situations except when a snapshot is only loaded, and not a new one is
created from it.
---------
Co-authored-by: Bartek IwaĆczuk <biwanczuk@gmail.com>
Diffstat (limited to 'ext/crypto/00_crypto.js')
-rw-r--r-- | ext/crypto/00_crypto.js | 7914 |
1 files changed, 3953 insertions, 3961 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 7d30dcccf..c79a1e4e8 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -6,2394 +6,2534 @@ /// <reference path="../webidl/internal.d.ts" /> /// <reference path="../web/lib.deno_web.d.ts" /> -"use strict"; - -((window) => { - const core = window.Deno.core; - const ops = core.ops; - const webidl = window.__bootstrap.webidl; - const { DOMException } = window.__bootstrap.domException; - - const { - ArrayBufferPrototype, - ArrayBufferIsView, - ArrayPrototypeEvery, - ArrayPrototypeFind, - ArrayPrototypeIncludes, - BigInt64ArrayPrototype, - BigUint64ArrayPrototype, - Int16ArrayPrototype, - Int32ArrayPrototype, - Int8ArrayPrototype, - JSONParse, - JSONStringify, - MathCeil, - ObjectAssign, - ObjectPrototypeHasOwnProperty, - ObjectPrototypeIsPrototypeOf, - StringPrototypeToLowerCase, - StringPrototypeToUpperCase, - StringPrototypeCharCodeAt, - StringFromCharCode, - SafeArrayIterator, - Symbol, - SymbolFor, - SyntaxError, - TypedArrayPrototypeSlice, - TypeError, - Uint16ArrayPrototype, - Uint32ArrayPrototype, - Uint8Array, - Uint8ArrayPrototype, - Uint8ClampedArrayPrototype, - WeakMap, - WeakMapPrototypeGet, - WeakMapPrototypeSet, - } = window.__bootstrap.primordials; - - // P-521 is not yet supported. - const supportedNamedCurves = ["P-256", "P-384"]; - const recognisedUsages = [ - "encrypt", - "decrypt", - "sign", - "verify", - "deriveKey", - "deriveBits", - "wrapKey", - "unwrapKey", - ]; - - const simpleAlgorithmDictionaries = { - AesGcmParams: { iv: "BufferSource", additionalData: "BufferSource" }, - RsaHashedKeyGenParams: { hash: "HashAlgorithmIdentifier" }, - EcKeyGenParams: {}, - HmacKeyGenParams: { hash: "HashAlgorithmIdentifier" }, - RsaPssParams: {}, - EcdsaParams: { hash: "HashAlgorithmIdentifier" }, - HmacImportParams: { hash: "HashAlgorithmIdentifier" }, - HkdfParams: { - hash: "HashAlgorithmIdentifier", - salt: "BufferSource", - info: "BufferSource", - }, - Pbkdf2Params: { hash: "HashAlgorithmIdentifier", salt: "BufferSource" }, - RsaOaepParams: { label: "BufferSource" }, - RsaHashedImportParams: { hash: "HashAlgorithmIdentifier" }, - EcKeyImportParams: {}, - }; - - const supportedAlgorithms = { - "digest": { - "SHA-1": null, - "SHA-256": null, - "SHA-384": null, - "SHA-512": null, - }, - "generateKey": { - "RSASSA-PKCS1-v1_5": "RsaHashedKeyGenParams", - "RSA-PSS": "RsaHashedKeyGenParams", - "RSA-OAEP": "RsaHashedKeyGenParams", - "ECDSA": "EcKeyGenParams", - "ECDH": "EcKeyGenParams", - "AES-CTR": "AesKeyGenParams", - "AES-CBC": "AesKeyGenParams", - "AES-GCM": "AesKeyGenParams", - "AES-KW": "AesKeyGenParams", - "HMAC": "HmacKeyGenParams", - "X25519": null, - "Ed25519": null, - }, - "sign": { - "RSASSA-PKCS1-v1_5": null, - "RSA-PSS": "RsaPssParams", - "ECDSA": "EcdsaParams", - "HMAC": null, - "Ed25519": null, - }, - "verify": { - "RSASSA-PKCS1-v1_5": null, - "RSA-PSS": "RsaPssParams", - "ECDSA": "EcdsaParams", - "HMAC": null, - "Ed25519": null, - }, - "importKey": { - "RSASSA-PKCS1-v1_5": "RsaHashedImportParams", - "RSA-PSS": "RsaHashedImportParams", - "RSA-OAEP": "RsaHashedImportParams", - "ECDSA": "EcKeyImportParams", - "ECDH": "EcKeyImportParams", - "HMAC": "HmacImportParams", - "HKDF": null, - "PBKDF2": null, - "AES-CTR": null, - "AES-CBC": null, - "AES-GCM": null, - "AES-KW": null, - "Ed25519": null, - "X25519": null, - }, - "deriveBits": { - "HKDF": "HkdfParams", - "PBKDF2": "Pbkdf2Params", - "ECDH": "EcdhKeyDeriveParams", - "X25519": "EcdhKeyDeriveParams", - }, - "encrypt": { - "RSA-OAEP": "RsaOaepParams", - "AES-CBC": "AesCbcParams", - "AES-GCM": "AesGcmParams", - "AES-CTR": "AesCtrParams", - }, - "decrypt": { - "RSA-OAEP": "RsaOaepParams", - "AES-CBC": "AesCbcParams", - "AES-GCM": "AesGcmParams", - "AES-CTR": "AesCtrParams", - }, - "get key length": { - "AES-CBC": "AesDerivedKeyParams", - "AES-CTR": "AesDerivedKeyParams", - "AES-GCM": "AesDerivedKeyParams", - "AES-KW": "AesDerivedKeyParams", - "HMAC": "HmacImportParams", - "HKDF": null, - "PBKDF2": null, - }, - "wrapKey": { - "AES-KW": null, - }, - "unwrapKey": { - "AES-KW": null, - }, - }; - - const aesJwkAlg = { - "AES-CTR": { - 128: "A128CTR", - 192: "A192CTR", - 256: "A256CTR", - }, - "AES-CBC": { - 128: "A128CBC", - 192: "A192CBC", - 256: "A256CBC", - }, - "AES-GCM": { - 128: "A128GCM", - 192: "A192GCM", - 256: "A256GCM", - }, - "AES-KW": { - 128: "A128KW", - 192: "A192KW", - 256: "A256KW", - }, - }; +const core = globalThis.Deno.core; +const ops = core.ops; +const primordials = globalThis.__bootstrap.primordials; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import DOMException from "internal:ext/web/01_dom_exception.js"; +const { + ArrayBufferPrototype, + ArrayBufferIsView, + ArrayPrototypeEvery, + ArrayPrototypeFind, + ArrayPrototypeIncludes, + BigInt64ArrayPrototype, + BigUint64ArrayPrototype, + Int16ArrayPrototype, + Int32ArrayPrototype, + Int8ArrayPrototype, + JSONParse, + JSONStringify, + MathCeil, + ObjectAssign, + ObjectPrototypeHasOwnProperty, + ObjectPrototypeIsPrototypeOf, + StringPrototypeToLowerCase, + StringPrototypeToUpperCase, + StringPrototypeCharCodeAt, + StringFromCharCode, + SafeArrayIterator, + Symbol, + SymbolFor, + SyntaxError, + TypedArrayPrototypeSlice, + TypeError, + Uint16ArrayPrototype, + Uint32ArrayPrototype, + Uint8Array, + Uint8ArrayPrototype, + Uint8ClampedArrayPrototype, + WeakMap, + WeakMapPrototypeGet, + WeakMapPrototypeSet, +} = primordials; + +// P-521 is not yet supported. +const supportedNamedCurves = ["P-256", "P-384"]; +const recognisedUsages = [ + "encrypt", + "decrypt", + "sign", + "verify", + "deriveKey", + "deriveBits", + "wrapKey", + "unwrapKey", +]; + +const simpleAlgorithmDictionaries = { + AesGcmParams: { iv: "BufferSource", additionalData: "BufferSource" }, + RsaHashedKeyGenParams: { hash: "HashAlgorithmIdentifier" }, + EcKeyGenParams: {}, + HmacKeyGenParams: { hash: "HashAlgorithmIdentifier" }, + RsaPssParams: {}, + EcdsaParams: { hash: "HashAlgorithmIdentifier" }, + HmacImportParams: { hash: "HashAlgorithmIdentifier" }, + HkdfParams: { + hash: "HashAlgorithmIdentifier", + salt: "BufferSource", + info: "BufferSource", + }, + Pbkdf2Params: { hash: "HashAlgorithmIdentifier", salt: "BufferSource" }, + RsaOaepParams: { label: "BufferSource" }, + RsaHashedImportParams: { hash: "HashAlgorithmIdentifier" }, + EcKeyImportParams: {}, +}; + +const supportedAlgorithms = { + "digest": { + "SHA-1": null, + "SHA-256": null, + "SHA-384": null, + "SHA-512": null, + }, + "generateKey": { + "RSASSA-PKCS1-v1_5": "RsaHashedKeyGenParams", + "RSA-PSS": "RsaHashedKeyGenParams", + "RSA-OAEP": "RsaHashedKeyGenParams", + "ECDSA": "EcKeyGenParams", + "ECDH": "EcKeyGenParams", + "AES-CTR": "AesKeyGenParams", + "AES-CBC": "AesKeyGenParams", + "AES-GCM": "AesKeyGenParams", + "AES-KW": "AesKeyGenParams", + "HMAC": "HmacKeyGenParams", + "X25519": null, + "Ed25519": null, + }, + "sign": { + "RSASSA-PKCS1-v1_5": null, + "RSA-PSS": "RsaPssParams", + "ECDSA": "EcdsaParams", + "HMAC": null, + "Ed25519": null, + }, + "verify": { + "RSASSA-PKCS1-v1_5": null, + "RSA-PSS": "RsaPssParams", + "ECDSA": "EcdsaParams", + "HMAC": null, + "Ed25519": null, + }, + "importKey": { + "RSASSA-PKCS1-v1_5": "RsaHashedImportParams", + "RSA-PSS": "RsaHashedImportParams", + "RSA-OAEP": "RsaHashedImportParams", + "ECDSA": "EcKeyImportParams", + "ECDH": "EcKeyImportParams", + "HMAC": "HmacImportParams", + "HKDF": null, + "PBKDF2": null, + "AES-CTR": null, + "AES-CBC": null, + "AES-GCM": null, + "AES-KW": null, + "Ed25519": null, + "X25519": null, + }, + "deriveBits": { + "HKDF": "HkdfParams", + "PBKDF2": "Pbkdf2Params", + "ECDH": "EcdhKeyDeriveParams", + "X25519": "EcdhKeyDeriveParams", + }, + "encrypt": { + "RSA-OAEP": "RsaOaepParams", + "AES-CBC": "AesCbcParams", + "AES-GCM": "AesGcmParams", + "AES-CTR": "AesCtrParams", + }, + "decrypt": { + "RSA-OAEP": "RsaOaepParams", + "AES-CBC": "AesCbcParams", + "AES-GCM": "AesGcmParams", + "AES-CTR": "AesCtrParams", + }, + "get key length": { + "AES-CBC": "AesDerivedKeyParams", + "AES-CTR": "AesDerivedKeyParams", + "AES-GCM": "AesDerivedKeyParams", + "AES-KW": "AesDerivedKeyParams", + "HMAC": "HmacImportParams", + "HKDF": null, + "PBKDF2": null, + }, + "wrapKey": { + "AES-KW": null, + }, + "unwrapKey": { + "AES-KW": null, + }, +}; + +const aesJwkAlg = { + "AES-CTR": { + 128: "A128CTR", + 192: "A192CTR", + 256: "A256CTR", + }, + "AES-CBC": { + 128: "A128CBC", + 192: "A192CBC", + 256: "A256CBC", + }, + "AES-GCM": { + 128: "A128GCM", + 192: "A192GCM", + 256: "A256GCM", + }, + "AES-KW": { + 128: "A128KW", + 192: "A192KW", + 256: "A256KW", + }, +}; + +// See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm +// 18.4.4 +function normalizeAlgorithm(algorithm, op) { + if (typeof algorithm == "string") { + return normalizeAlgorithm({ name: algorithm }, op); + } - // See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm - // 18.4.4 - function normalizeAlgorithm(algorithm, op) { - if (typeof algorithm == "string") { - return normalizeAlgorithm({ name: algorithm }, op); + // 1. + const registeredAlgorithms = supportedAlgorithms[op]; + // 2. 3. + const initialAlg = webidl.converters.Algorithm(algorithm, { + prefix: "Failed to normalize algorithm", + context: "passed algorithm", + }); + // 4. + let algName = initialAlg.name; + + // 5. + let desiredType = undefined; + for (const key in registeredAlgorithms) { + if (!ObjectPrototypeHasOwnProperty(registeredAlgorithms, key)) { + continue; } + if ( + StringPrototypeToUpperCase(key) === StringPrototypeToUpperCase(algName) + ) { + algName = key; + desiredType = registeredAlgorithms[key]; + } + } + if (desiredType === undefined) { + throw new DOMException( + "Unrecognized algorithm name", + "NotSupportedError", + ); + } - // 1. - const registeredAlgorithms = supportedAlgorithms[op]; - // 2. 3. - const initialAlg = webidl.converters.Algorithm(algorithm, { - prefix: "Failed to normalize algorithm", - context: "passed algorithm", - }); - // 4. - let algName = initialAlg.name; + // Fast path everything below if the registered dictionary is "None". + if (desiredType === null) { + return { name: algName }; + } - // 5. - let desiredType = undefined; - for (const key in registeredAlgorithms) { - if (!ObjectPrototypeHasOwnProperty(registeredAlgorithms, key)) { - continue; - } - if ( - StringPrototypeToUpperCase(key) === StringPrototypeToUpperCase(algName) - ) { - algName = key; - desiredType = registeredAlgorithms[key]; - } + // 6. + const normalizedAlgorithm = webidl.converters[desiredType](algorithm, { + prefix: "Failed to normalize algorithm", + context: "passed algorithm", + }); + // 7. + normalizedAlgorithm.name = algName; + + // 9. + const dict = simpleAlgorithmDictionaries[desiredType]; + // 10. + for (const member in dict) { + if (!ObjectPrototypeHasOwnProperty(dict, member)) { + continue; } - if (desiredType === undefined) { - throw new DOMException( - "Unrecognized algorithm name", - "NotSupportedError", + const idlType = dict[member]; + const idlValue = normalizedAlgorithm[member]; + // 3. + if (idlType === "BufferSource" && idlValue) { + normalizedAlgorithm[member] = TypedArrayPrototypeSlice( + new Uint8Array( + ArrayBufferIsView(idlValue) ? idlValue.buffer : idlValue, + idlValue.byteOffset ?? 0, + idlValue.byteLength, + ), ); + } else if (idlType === "HashAlgorithmIdentifier") { + normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, "digest"); + } else if (idlType === "AlgorithmIdentifier") { + // TODO(lucacasonato): implement + throw new TypeError("unimplemented"); } + } - // Fast path everything below if the registered dictionary is "None". - if (desiredType === null) { - return { name: algName }; - } - - // 6. - const normalizedAlgorithm = webidl.converters[desiredType](algorithm, { - prefix: "Failed to normalize algorithm", - context: "passed algorithm", - }); - // 7. - normalizedAlgorithm.name = algName; + return normalizedAlgorithm; +} + +/** + * @param {ArrayBufferView | ArrayBuffer} input + * @returns {Uint8Array} + */ +function copyBuffer(input) { + return TypedArrayPrototypeSlice( + ArrayBufferIsView(input) + ? new Uint8Array(input.buffer, input.byteOffset, input.byteLength) + : new Uint8Array(input), + ); +} + +const _handle = Symbol("[[handle]]"); +const _algorithm = Symbol("[[algorithm]]"); +const _extractable = Symbol("[[extractable]]"); +const _usages = Symbol("[[usages]]"); +const _type = Symbol("[[type]]"); + +class CryptoKey { + /** @type {string} */ + [_type]; + /** @type {boolean} */ + [_extractable]; + /** @type {object} */ + [_algorithm]; + /** @type {string[]} */ + [_usages]; + /** @type {object} */ + [_handle]; + + constructor() { + webidl.illegalConstructor(); + } - // 9. - const dict = simpleAlgorithmDictionaries[desiredType]; - // 10. - for (const member in dict) { - if (!ObjectPrototypeHasOwnProperty(dict, member)) { - continue; - } - const idlType = dict[member]; - const idlValue = normalizedAlgorithm[member]; - // 3. - if (idlType === "BufferSource" && idlValue) { - normalizedAlgorithm[member] = TypedArrayPrototypeSlice( - new Uint8Array( - ArrayBufferIsView(idlValue) ? idlValue.buffer : idlValue, - idlValue.byteOffset ?? 0, - idlValue.byteLength, - ), - ); - } else if (idlType === "HashAlgorithmIdentifier") { - normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, "digest"); - } else if (idlType === "AlgorithmIdentifier") { - // TODO(lucacasonato): implement - throw new TypeError("unimplemented"); - } - } + /** @returns {string} */ + get type() { + webidl.assertBranded(this, CryptoKeyPrototype); + return this[_type]; + } - return normalizedAlgorithm; + /** @returns {boolean} */ + get extractable() { + webidl.assertBranded(this, CryptoKeyPrototype); + return this[_extractable]; } - /** - * @param {ArrayBufferView | ArrayBuffer} input - * @returns {Uint8Array} - */ - function copyBuffer(input) { - return TypedArrayPrototypeSlice( - ArrayBufferIsView(input) - ? new Uint8Array(input.buffer, input.byteOffset, input.byteLength) - : new Uint8Array(input), - ); + /** @returns {string[]} */ + get usages() { + webidl.assertBranded(this, CryptoKeyPrototype); + // TODO(lucacasonato): return a SameObject copy + return this[_usages]; } - const _handle = Symbol("[[handle]]"); - const _algorithm = Symbol("[[algorithm]]"); - const _extractable = Symbol("[[extractable]]"); - const _usages = Symbol("[[usages]]"); - const _type = Symbol("[[type]]"); - - class CryptoKey { - /** @type {string} */ - [_type]; - /** @type {boolean} */ - [_extractable]; - /** @type {object} */ - [_algorithm]; - /** @type {string[]} */ - [_usages]; - /** @type {object} */ - [_handle]; - - constructor() { - webidl.illegalConstructor(); - } + /** @returns {object} */ + get algorithm() { + webidl.assertBranded(this, CryptoKeyPrototype); + // TODO(lucacasonato): return a SameObject copy + return this[_algorithm]; + } - /** @returns {string} */ - get type() { - webidl.assertBranded(this, CryptoKeyPrototype); - return this[_type]; - } + [SymbolFor("Deno.customInspect")](inspect) { + return `${this.constructor.name} ${ + inspect({ + type: this.type, + extractable: this.extractable, + algorithm: this.algorithm, + usages: this.usages, + }) + }`; + } +} + +webidl.configurePrototype(CryptoKey); +const CryptoKeyPrototype = CryptoKey.prototype; + +/** + * @param {string} type + * @param {boolean} extractable + * @param {string[]} usages + * @param {object} algorithm + * @param {object} handle + * @returns + */ +function constructKey(type, extractable, usages, algorithm, handle) { + const key = webidl.createBranded(CryptoKey); + key[_type] = type; + key[_extractable] = extractable; + key[_usages] = usages; + key[_algorithm] = algorithm; + key[_handle] = handle; + return key; +} + +// https://w3c.github.io/webcrypto/#concept-usage-intersection +/** + * @param {string[]} a + * @param {string[]} b + * @returns + */ +function usageIntersection(a, b) { + return a.filter((i) => b.includes(i)); +} + +// TODO(lucacasonato): this should be moved to rust +/** @type {WeakMap<object, object>} */ +const KEY_STORE = new WeakMap(); + +function getKeyLength(algorithm) { + switch (algorithm.name) { + case "AES-CBC": + case "AES-CTR": + case "AES-GCM": + case "AES-KW": { + // 1. + if (!ArrayPrototypeIncludes([128, 192, 256], algorithm.length)) { + throw new DOMException( + "length must be 128, 192, or 256", + "OperationError", + ); + } - /** @returns {boolean} */ - get extractable() { - webidl.assertBranded(this, CryptoKeyPrototype); - return this[_extractable]; + // 2. + return algorithm.length; } + case "HMAC": { + // 1. + let length; + if (algorithm.length === undefined) { + switch (algorithm.hash.name) { + case "SHA-1": + length = 512; + break; + case "SHA-256": + length = 512; + break; + case "SHA-384": + length = 1024; + break; + case "SHA-512": + length = 1024; + break; + default: + throw new DOMException( + "Unrecognized hash algorithm", + "NotSupportedError", + ); + } + } else if (algorithm.length !== 0) { + length = algorithm.length; + } else { + throw new TypeError("Invalid length."); + } - /** @returns {string[]} */ - get usages() { - webidl.assertBranded(this, CryptoKeyPrototype); - // TODO(lucacasonato): return a SameObject copy - return this[_usages]; + // 2. + return length; } - - /** @returns {object} */ - get algorithm() { - webidl.assertBranded(this, CryptoKeyPrototype); - // TODO(lucacasonato): return a SameObject copy - return this[_algorithm]; + case "HKDF": { + // 1. + return null; } - - [SymbolFor("Deno.customInspect")](inspect) { - return `${this.constructor.name} ${ - inspect({ - type: this.type, - extractable: this.extractable, - algorithm: this.algorithm, - usages: this.usages, - }) - }`; + case "PBKDF2": { + // 1. + return null; } + default: + throw new TypeError("unreachable"); } +} - webidl.configurePrototype(CryptoKey); - const CryptoKeyPrototype = CryptoKey.prototype; - - /** - * @param {string} type - * @param {boolean} extractable - * @param {string[]} usages - * @param {object} algorithm - * @param {object} handle - * @returns - */ - function constructKey(type, extractable, usages, algorithm, handle) { - const key = webidl.createBranded(CryptoKey); - key[_type] = type; - key[_extractable] = extractable; - key[_usages] = usages; - key[_algorithm] = algorithm; - key[_handle] = handle; - return key; +class SubtleCrypto { + constructor() { + webidl.illegalConstructor(); } - // https://w3c.github.io/webcrypto/#concept-usage-intersection /** - * @param {string[]} a - * @param {string[]} b - * @returns + * @param {string} algorithm + * @param {BufferSource} data + * @returns {Promise<Uint8Array>} */ - function usageIntersection(a, b) { - return a.filter((i) => b.includes(i)); - } + async digest(algorithm, data) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'digest' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 2, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: "Argument 2", + }); - // TODO(lucacasonato): this should be moved to rust - /** @type {WeakMap<object, object>} */ - const KEY_STORE = new WeakMap(); + data = copyBuffer(data); - function getKeyLength(algorithm) { - switch (algorithm.name) { - case "AES-CBC": - case "AES-CTR": - case "AES-GCM": - case "AES-KW": { - // 1. - if (!ArrayPrototypeIncludes([128, 192, 256], algorithm.length)) { - throw new DOMException( - "length must be 128, 192, or 256", - "OperationError", - ); - } + algorithm = normalizeAlgorithm(algorithm, "digest"); - // 2. - return algorithm.length; - } - case "HMAC": { - // 1. - let length; - if (algorithm.length === undefined) { - switch (algorithm.hash.name) { - case "SHA-1": - length = 512; - break; - case "SHA-256": - length = 512; - break; - case "SHA-384": - length = 1024; - break; - case "SHA-512": - length = 1024; - break; - default: - throw new DOMException( - "Unrecognized hash algorithm", - "NotSupportedError", - ); - } - } else if (algorithm.length !== 0) { - length = algorithm.length; - } else { - throw new TypeError("Invalid length."); - } + const result = await core.opAsync( + "op_crypto_subtle_digest", + algorithm.name, + data, + ); - // 2. - return length; - } - case "HKDF": { - // 1. - return null; - } - case "PBKDF2": { - // 1. - return null; - } - default: - throw new TypeError("unreachable"); - } + return result.buffer; } - class SubtleCrypto { - constructor() { - webidl.illegalConstructor(); - } - - /** - * @param {string} algorithm - * @param {BufferSource} data - * @returns {Promise<Uint8Array>} - */ - async digest(algorithm, data) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'digest' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 2, { prefix }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 1", - }); - data = webidl.converters.BufferSource(data, { - prefix, - context: "Argument 2", - }); + /** + * @param {string} algorithm + * @param {CryptoKey} key + * @param {BufferSource} data + * @returns {Promise<any>} + */ + async encrypt(algorithm, key, data) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'encrypt' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: "Argument 2", + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: "Argument 3", + }); - data = copyBuffer(data); + // 2. + data = copyBuffer(data); - algorithm = normalizeAlgorithm(algorithm, "digest"); + // 3. + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "encrypt"); - const result = await core.opAsync( - "op_crypto_subtle_digest", - algorithm.name, - data, + // 8. + if (normalizedAlgorithm.name !== key[_algorithm].name) { + throw new DOMException( + "Encryption algorithm doesn't match key algorithm.", + "InvalidAccessError", ); + } - return result.buffer; + // 9. + if (!ArrayPrototypeIncludes(key[_usages], "encrypt")) { + throw new DOMException( + "Key does not support the 'encrypt' operation.", + "InvalidAccessError", + ); } - /** - * @param {string} algorithm - * @param {CryptoKey} key - * @param {BufferSource} data - * @returns {Promise<any>} - */ - async encrypt(algorithm, key, data) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'encrypt' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 3, { prefix }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 1", - }); - key = webidl.converters.CryptoKey(key, { - prefix, - context: "Argument 2", - }); - data = webidl.converters.BufferSource(data, { - prefix, - context: "Argument 3", - }); + return await encrypt(normalizedAlgorithm, key, data); + } - // 2. - data = copyBuffer(data); + /** + * @param {string} algorithm + * @param {CryptoKey} key + * @param {BufferSource} data + * @returns {Promise<any>} + */ + async decrypt(algorithm, key, data) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'decrypt' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: "Argument 2", + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: "Argument 3", + }); - // 3. - const normalizedAlgorithm = normalizeAlgorithm(algorithm, "encrypt"); + // 2. + data = copyBuffer(data); - // 8. - if (normalizedAlgorithm.name !== key[_algorithm].name) { - throw new DOMException( - "Encryption algorithm doesn't match key algorithm.", - "InvalidAccessError", - ); - } + // 3. + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "decrypt"); - // 9. - if (!ArrayPrototypeIncludes(key[_usages], "encrypt")) { - throw new DOMException( - "Key does not support the 'encrypt' operation.", - "InvalidAccessError", - ); - } + // 8. + if (normalizedAlgorithm.name !== key[_algorithm].name) { + throw new DOMException( + "Decryption algorithm doesn't match key algorithm.", + "OperationError", + ); + } - return await encrypt(normalizedAlgorithm, key, data); + // 9. + if (!ArrayPrototypeIncludes(key[_usages], "decrypt")) { + throw new DOMException( + "Key does not support the 'decrypt' operation.", + "InvalidAccessError", + ); } - /** - * @param {string} algorithm - * @param {CryptoKey} key - * @param {BufferSource} data - * @returns {Promise<any>} - */ - async decrypt(algorithm, key, data) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'decrypt' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 3, { prefix }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 1", - }); - key = webidl.converters.CryptoKey(key, { - prefix, - context: "Argument 2", - }); - data = webidl.converters.BufferSource(data, { - prefix, - context: "Argument 3", - }); + const handle = key[_handle]; + const keyData = WeakMapPrototypeGet(KEY_STORE, handle); - // 2. - data = copyBuffer(data); + switch (normalizedAlgorithm.name) { + case "RSA-OAEP": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } - // 3. - const normalizedAlgorithm = normalizeAlgorithm(algorithm, "decrypt"); + // 2. + if (normalizedAlgorithm.label) { + normalizedAlgorithm.label = copyBuffer(normalizedAlgorithm.label); + } else { + normalizedAlgorithm.label = new Uint8Array(); + } - // 8. - if (normalizedAlgorithm.name !== key[_algorithm].name) { - throw new DOMException( - "Decryption algorithm doesn't match key algorithm.", - "OperationError", - ); - } + // 3-5. + const hashAlgorithm = key[_algorithm].hash.name; + const plainText = await core.opAsync("op_crypto_decrypt", { + key: keyData, + algorithm: "RSA-OAEP", + hash: hashAlgorithm, + label: normalizedAlgorithm.label, + }, data); - // 9. - if (!ArrayPrototypeIncludes(key[_usages], "decrypt")) { - throw new DOMException( - "Key does not support the 'decrypt' operation.", - "InvalidAccessError", - ); + // 6. + return plainText.buffer; } + case "AES-CBC": { + normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); - const handle = key[_handle]; - const keyData = WeakMapPrototypeGet(KEY_STORE, handle); - - switch (normalizedAlgorithm.name) { - case "RSA-OAEP": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } + // 1. + if (normalizedAlgorithm.iv.byteLength !== 16) { + throw new DOMException( + "Counter must be 16 bytes", + "OperationError", + ); + } - // 2. - if (normalizedAlgorithm.label) { - normalizedAlgorithm.label = copyBuffer(normalizedAlgorithm.label); - } else { - normalizedAlgorithm.label = new Uint8Array(); - } + const plainText = await core.opAsync("op_crypto_decrypt", { + key: keyData, + algorithm: "AES-CBC", + iv: normalizedAlgorithm.iv, + length: key[_algorithm].length, + }, data); - // 3-5. - const hashAlgorithm = key[_algorithm].hash.name; - const plainText = await core.opAsync("op_crypto_decrypt", { - key: keyData, - algorithm: "RSA-OAEP", - hash: hashAlgorithm, - label: normalizedAlgorithm.label, - }, data); + // 6. + return plainText.buffer; + } + case "AES-CTR": { + normalizedAlgorithm.counter = copyBuffer(normalizedAlgorithm.counter); - // 6. - return plainText.buffer; + // 1. + if (normalizedAlgorithm.counter.byteLength !== 16) { + throw new DOMException( + "Counter vector must be 16 bytes", + "OperationError", + ); } - case "AES-CBC": { - normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); - - // 1. - if (normalizedAlgorithm.iv.byteLength !== 16) { - throw new DOMException( - "Counter must be 16 bytes", - "OperationError", - ); - } - const plainText = await core.opAsync("op_crypto_decrypt", { - key: keyData, - algorithm: "AES-CBC", - iv: normalizedAlgorithm.iv, - length: key[_algorithm].length, - }, data); - - // 6. - return plainText.buffer; + // 2. + if ( + normalizedAlgorithm.length === 0 || normalizedAlgorithm.length > 128 + ) { + throw new DOMException( + "Counter length must not be 0 or greater than 128", + "OperationError", + ); } - case "AES-CTR": { - normalizedAlgorithm.counter = copyBuffer(normalizedAlgorithm.counter); - // 1. - if (normalizedAlgorithm.counter.byteLength !== 16) { - throw new DOMException( - "Counter vector must be 16 bytes", - "OperationError", - ); - } + // 3. + const cipherText = await core.opAsync("op_crypto_decrypt", { + key: keyData, + algorithm: "AES-CTR", + keyLength: key[_algorithm].length, + counter: normalizedAlgorithm.counter, + ctrLength: normalizedAlgorithm.length, + }, data); - // 2. - if ( - normalizedAlgorithm.length === 0 || normalizedAlgorithm.length > 128 - ) { - throw new DOMException( - "Counter length must not be 0 or greater than 128", - "OperationError", - ); - } + // 4. + return cipherText.buffer; + } + case "AES-GCM": { + normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); - // 3. - const cipherText = await core.opAsync("op_crypto_decrypt", { - key: keyData, - algorithm: "AES-CTR", - keyLength: key[_algorithm].length, - counter: normalizedAlgorithm.counter, - ctrLength: normalizedAlgorithm.length, - }, data); + // 1. + if (normalizedAlgorithm.tagLength === undefined) { + normalizedAlgorithm.tagLength = 128; + } else if ( + !ArrayPrototypeIncludes( + [32, 64, 96, 104, 112, 120, 128], + normalizedAlgorithm.tagLength, + ) + ) { + throw new DOMException( + "Invalid tag length", + "OperationError", + ); + } - // 4. - return cipherText.buffer; + // 2. + if (data.byteLength < normalizedAlgorithm.tagLength / 8) { + throw new DOMException( + "Tag length overflows ciphertext", + "OperationError", + ); } - case "AES-GCM": { - normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); - // 1. - if (normalizedAlgorithm.tagLength === undefined) { - normalizedAlgorithm.tagLength = 128; - } else if ( - !ArrayPrototypeIncludes( - [32, 64, 96, 104, 112, 120, 128], - normalizedAlgorithm.tagLength, - ) - ) { - throw new DOMException( - "Invalid tag length", - "OperationError", - ); - } + // 3. We only support 96-bit and 128-bit nonce. + if ( + ArrayPrototypeIncludes( + [12, 16], + normalizedAlgorithm.iv.byteLength, + ) === undefined + ) { + throw new DOMException( + "Initialization vector length not supported", + "NotSupportedError", + ); + } - // 2. - if (data.byteLength < normalizedAlgorithm.tagLength / 8) { + // 4. + if (normalizedAlgorithm.additionalData !== undefined) { + if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) { throw new DOMException( - "Tag length overflows ciphertext", + "Additional data too large", "OperationError", ); } + normalizedAlgorithm.additionalData = copyBuffer( + normalizedAlgorithm.additionalData, + ); + } - // 3. We only support 96-bit and 128-bit nonce. - if ( - ArrayPrototypeIncludes( - [12, 16], - normalizedAlgorithm.iv.byteLength, - ) === undefined - ) { - throw new DOMException( - "Initialization vector length not supported", - "NotSupportedError", - ); - } - - // 4. - if (normalizedAlgorithm.additionalData !== undefined) { - if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) { - throw new DOMException( - "Additional data too large", - "OperationError", - ); - } - normalizedAlgorithm.additionalData = copyBuffer( - normalizedAlgorithm.additionalData, - ); - } + // 5-8. + const plaintext = await core.opAsync("op_crypto_decrypt", { + key: keyData, + algorithm: "AES-GCM", + length: key[_algorithm].length, + iv: normalizedAlgorithm.iv, + additionalData: normalizedAlgorithm.additionalData || + null, + tagLength: normalizedAlgorithm.tagLength, + }, data); - // 5-8. - const plaintext = await core.opAsync("op_crypto_decrypt", { - key: keyData, - algorithm: "AES-GCM", - length: key[_algorithm].length, - iv: normalizedAlgorithm.iv, - additionalData: normalizedAlgorithm.additionalData || - null, - tagLength: normalizedAlgorithm.tagLength, - }, data); - - // 9. - return plaintext.buffer; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); + // 9. + return plaintext.buffer; } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } + } - /** - * @param {string} algorithm - * @param {CryptoKey} key - * @param {BufferSource} data - * @returns {Promise<any>} - */ - async sign(algorithm, key, data) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'sign' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 3, { prefix }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 1", - }); - key = webidl.converters.CryptoKey(key, { - prefix, - context: "Argument 2", - }); - data = webidl.converters.BufferSource(data, { - prefix, - context: "Argument 3", - }); + /** + * @param {string} algorithm + * @param {CryptoKey} key + * @param {BufferSource} data + * @returns {Promise<any>} + */ + async sign(algorithm, key, data) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'sign' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: "Argument 2", + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: "Argument 3", + }); - // 1. - data = copyBuffer(data); + // 1. + data = copyBuffer(data); - // 2. - const normalizedAlgorithm = normalizeAlgorithm(algorithm, "sign"); + // 2. + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "sign"); - const handle = key[_handle]; - const keyData = WeakMapPrototypeGet(KEY_STORE, handle); + const handle = key[_handle]; + const keyData = WeakMapPrototypeGet(KEY_STORE, handle); - // 8. - if (normalizedAlgorithm.name !== key[_algorithm].name) { - throw new DOMException( - "Signing algorithm doesn't match key algorithm.", - "InvalidAccessError", - ); - } + // 8. + if (normalizedAlgorithm.name !== key[_algorithm].name) { + throw new DOMException( + "Signing algorithm doesn't match key algorithm.", + "InvalidAccessError", + ); + } - // 9. - if (!ArrayPrototypeIncludes(key[_usages], "sign")) { - throw new DOMException( - "Key does not support the 'sign' operation.", - "InvalidAccessError", - ); - } + // 9. + if (!ArrayPrototypeIncludes(key[_usages], "sign")) { + throw new DOMException( + "Key does not support the 'sign' operation.", + "InvalidAccessError", + ); + } - switch (normalizedAlgorithm.name) { - case "RSASSA-PKCS1-v1_5": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } + switch (normalizedAlgorithm.name) { + case "RSASSA-PKCS1-v1_5": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } - // 2. - const hashAlgorithm = key[_algorithm].hash.name; - const signature = await core.opAsync("op_crypto_sign_key", { - key: keyData, - algorithm: "RSASSA-PKCS1-v1_5", - hash: hashAlgorithm, - }, data); + // 2. + const hashAlgorithm = key[_algorithm].hash.name; + const signature = await core.opAsync("op_crypto_sign_key", { + key: keyData, + algorithm: "RSASSA-PKCS1-v1_5", + hash: hashAlgorithm, + }, data); - return signature.buffer; + return signature.buffer; + } + case "RSA-PSS": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); } - case "RSA-PSS": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } - // 2. - const hashAlgorithm = key[_algorithm].hash.name; - const signature = await core.opAsync("op_crypto_sign_key", { - key: keyData, - algorithm: "RSA-PSS", - hash: hashAlgorithm, - saltLength: normalizedAlgorithm.saltLength, - }, data); + // 2. + const hashAlgorithm = key[_algorithm].hash.name; + const signature = await core.opAsync("op_crypto_sign_key", { + key: keyData, + algorithm: "RSA-PSS", + hash: hashAlgorithm, + saltLength: normalizedAlgorithm.saltLength, + }, data); - return signature.buffer; + return signature.buffer; + } + case "ECDSA": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); } - case "ECDSA": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } - // 2. - const hashAlgorithm = normalizedAlgorithm.hash.name; - const namedCurve = key[_algorithm].namedCurve; - if (!ArrayPrototypeIncludes(supportedNamedCurves, namedCurve)) { - throw new DOMException("Curve not supported", "NotSupportedError"); - } + // 2. + const hashAlgorithm = normalizedAlgorithm.hash.name; + const namedCurve = key[_algorithm].namedCurve; + if (!ArrayPrototypeIncludes(supportedNamedCurves, namedCurve)) { + throw new DOMException("Curve not supported", "NotSupportedError"); + } - const signature = await core.opAsync("op_crypto_sign_key", { - key: keyData, - algorithm: "ECDSA", - hash: hashAlgorithm, - namedCurve, - }, data); + const signature = await core.opAsync("op_crypto_sign_key", { + key: keyData, + algorithm: "ECDSA", + hash: hashAlgorithm, + namedCurve, + }, data); - return signature.buffer; - } - case "HMAC": { - const hashAlgorithm = key[_algorithm].hash.name; + return signature.buffer; + } + case "HMAC": { + const hashAlgorithm = key[_algorithm].hash.name; - const signature = await core.opAsync("op_crypto_sign_key", { - key: keyData, - algorithm: "HMAC", - hash: hashAlgorithm, - }, data); + const signature = await core.opAsync("op_crypto_sign_key", { + key: keyData, + algorithm: "HMAC", + hash: hashAlgorithm, + }, data); - return signature.buffer; + return signature.buffer; + } + case "Ed25519": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); } - case "Ed25519": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } - // https://briansmith.org/rustdoc/src/ring/ec/curve25519/ed25519/signing.rs.html#260 - const SIGNATURE_LEN = 32 * 2; // ELEM_LEN + SCALAR_LEN - const signature = new Uint8Array(SIGNATURE_LEN); - if (!ops.op_sign_ed25519(keyData, data, signature)) { - throw new DOMException( - "Failed to sign", - "OperationError", - ); - } - return signature.buffer; + // https://briansmith.org/rustdoc/src/ring/ec/curve25519/ed25519/signing.rs.html#260 + const SIGNATURE_LEN = 32 * 2; // ELEM_LEN + SCALAR_LEN + const signature = new Uint8Array(SIGNATURE_LEN); + if (!ops.op_sign_ed25519(keyData, data, signature)) { + throw new DOMException( + "Failed to sign", + "OperationError", + ); } + return signature.buffer; } - - throw new TypeError("unreachable"); } - /** - * @param {string} format - * @param {BufferSource} keyData - * @param {string} algorithm - * @param {boolean} extractable - * @param {KeyUsages[]} keyUsages - * @returns {Promise<any>} - */ - // deno-lint-ignore require-await - async importKey(format, keyData, algorithm, extractable, keyUsages) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'importKey' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 4, { prefix }); - format = webidl.converters.KeyFormat(format, { - prefix, - context: "Argument 1", - }); - keyData = webidl.converters["BufferSource or JsonWebKey"](keyData, { - prefix, - context: "Argument 2", - }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 3", - }); - extractable = webidl.converters.boolean(extractable, { - prefix, - context: "Argument 4", - }); - keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, { - prefix, - context: "Argument 5", - }); + throw new TypeError("unreachable"); + } - // 2. - if (format !== "jwk") { - if ( - ArrayBufferIsView(keyData) || - ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, keyData) - ) { - keyData = copyBuffer(keyData); - } else { - throw new TypeError("keyData is a JsonWebKey"); - } + /** + * @param {string} format + * @param {BufferSource} keyData + * @param {string} algorithm + * @param {boolean} extractable + * @param {KeyUsages[]} keyUsages + * @returns {Promise<any>} + */ + // deno-lint-ignore require-await + async importKey(format, keyData, algorithm, extractable, keyUsages) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'importKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 4, { prefix }); + format = webidl.converters.KeyFormat(format, { + prefix, + context: "Argument 1", + }); + keyData = webidl.converters["BufferSource or JsonWebKey"](keyData, { + prefix, + context: "Argument 2", + }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 3", + }); + extractable = webidl.converters.boolean(extractable, { + prefix, + context: "Argument 4", + }); + keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, { + prefix, + context: "Argument 5", + }); + + // 2. + if (format !== "jwk") { + if ( + ArrayBufferIsView(keyData) || + ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, keyData) + ) { + keyData = copyBuffer(keyData); } else { - if ( - ArrayBufferIsView(keyData) || - ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, keyData) - ) { - throw new TypeError("keyData is not a JsonWebKey"); - } + throw new TypeError("keyData is a JsonWebKey"); + } + } else { + if ( + ArrayBufferIsView(keyData) || + ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, keyData) + ) { + throw new TypeError("keyData is not a JsonWebKey"); } + } - const normalizedAlgorithm = normalizeAlgorithm(algorithm, "importKey"); + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "importKey"); - const algorithmName = normalizedAlgorithm.name; + const algorithmName = normalizedAlgorithm.name; - switch (algorithmName) { - case "HMAC": { - return importKeyHMAC( - format, - normalizedAlgorithm, - keyData, - extractable, - keyUsages, - ); - } - case "ECDH": - case "ECDSA": { - return importKeyEC( - format, - normalizedAlgorithm, - keyData, - extractable, - keyUsages, - ); - } - case "RSASSA-PKCS1-v1_5": - case "RSA-PSS": - case "RSA-OAEP": { - return importKeyRSA( - format, - normalizedAlgorithm, - keyData, - extractable, - keyUsages, - ); - } - case "HKDF": { - return importKeyHKDF(format, keyData, extractable, keyUsages); - } - case "PBKDF2": { - return importKeyPBKDF2(format, keyData, extractable, keyUsages); - } - case "AES-CTR": - case "AES-CBC": - case "AES-GCM": { - return importKeyAES( - format, - normalizedAlgorithm, - keyData, - extractable, - keyUsages, - ["encrypt", "decrypt", "wrapKey", "unwrapKey"], - ); - } - case "AES-KW": { - return importKeyAES( - format, - normalizedAlgorithm, - keyData, - extractable, - keyUsages, - ["wrapKey", "unwrapKey"], - ); - } - case "X25519": { - return importKeyX25519( - format, - keyData, - extractable, - keyUsages, - ); - } - case "Ed25519": { - return importKeyEd25519( - format, - keyData, - extractable, - keyUsages, - ); - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); + switch (algorithmName) { + case "HMAC": { + return importKeyHMAC( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, + ); + } + case "ECDH": + case "ECDSA": { + return importKeyEC( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, + ); + } + case "RSASSA-PKCS1-v1_5": + case "RSA-PSS": + case "RSA-OAEP": { + return importKeyRSA( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, + ); + } + case "HKDF": { + return importKeyHKDF(format, keyData, extractable, keyUsages); + } + case "PBKDF2": { + return importKeyPBKDF2(format, keyData, extractable, keyUsages); + } + case "AES-CTR": + case "AES-CBC": + case "AES-GCM": { + return importKeyAES( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, + ["encrypt", "decrypt", "wrapKey", "unwrapKey"], + ); + } + case "AES-KW": { + return importKeyAES( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, + ["wrapKey", "unwrapKey"], + ); + } + case "X25519": { + return importKeyX25519( + format, + keyData, + extractable, + keyUsages, + ); + } + case "Ed25519": { + return importKeyEd25519( + format, + keyData, + extractable, + keyUsages, + ); } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } + } - /** - * @param {string} format - * @param {CryptoKey} key - * @returns {Promise<any>} - */ - // deno-lint-ignore require-await - async exportKey(format, key) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'exportKey' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 2, { prefix }); - format = webidl.converters.KeyFormat(format, { - prefix, - context: "Argument 1", - }); - key = webidl.converters.CryptoKey(key, { - prefix, - context: "Argument 2", - }); + /** + * @param {string} format + * @param {CryptoKey} key + * @returns {Promise<any>} + */ + // deno-lint-ignore require-await + async exportKey(format, key) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'exportKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 2, { prefix }); + format = webidl.converters.KeyFormat(format, { + prefix, + context: "Argument 1", + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: "Argument 2", + }); - const handle = key[_handle]; - // 2. - const innerKey = WeakMapPrototypeGet(KEY_STORE, handle); + const handle = key[_handle]; + // 2. + const innerKey = WeakMapPrototypeGet(KEY_STORE, handle); - const algorithmName = key[_algorithm].name; + const algorithmName = key[_algorithm].name; - let result; + let result; - switch (algorithmName) { - case "HMAC": { - result = exportKeyHMAC(format, key, innerKey); - break; - } - case "RSASSA-PKCS1-v1_5": - case "RSA-PSS": - case "RSA-OAEP": { - result = exportKeyRSA(format, key, innerKey); - break; - } - case "ECDH": - case "ECDSA": { - result = exportKeyEC(format, key, innerKey); - break; - } - case "Ed25519": { - result = exportKeyEd25519(format, key, innerKey); - break; - } - case "X25519": { - result = exportKeyX25519(format, key, innerKey); - break; - } - case "AES-CTR": - case "AES-CBC": - case "AES-GCM": - case "AES-KW": { - result = exportKeyAES(format, key, innerKey); - break; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); + switch (algorithmName) { + case "HMAC": { + result = exportKeyHMAC(format, key, innerKey); + break; } - - if (key.extractable === false) { - throw new DOMException( - "Key is not extractable", - "InvalidAccessError", - ); + case "RSASSA-PKCS1-v1_5": + case "RSA-PSS": + case "RSA-OAEP": { + result = exportKeyRSA(format, key, innerKey); + break; + } + case "ECDH": + case "ECDSA": { + result = exportKeyEC(format, key, innerKey); + break; + } + case "Ed25519": { + result = exportKeyEd25519(format, key, innerKey); + break; + } + case "X25519": { + result = exportKeyX25519(format, key, innerKey); + break; + } + case "AES-CTR": + case "AES-CBC": + case "AES-GCM": + case "AES-KW": { + result = exportKeyAES(format, key, innerKey); + break; } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } - return result; + if (key.extractable === false) { + throw new DOMException( + "Key is not extractable", + "InvalidAccessError", + ); } - /** - * @param {AlgorithmIdentifier} algorithm - * @param {CryptoKey} baseKey - * @param {number | null} length - * @returns {Promise<ArrayBuffer>} - */ - async deriveBits(algorithm, baseKey, length) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'deriveBits' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 3, { prefix }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 1", - }); - baseKey = webidl.converters.CryptoKey(baseKey, { + return result; + } + + /** + * @param {AlgorithmIdentifier} algorithm + * @param {CryptoKey} baseKey + * @param {number | null} length + * @returns {Promise<ArrayBuffer>} + */ + async deriveBits(algorithm, baseKey, length) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'deriveBits' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + baseKey = webidl.converters.CryptoKey(baseKey, { + prefix, + context: "Argument 2", + }); + if (length !== null) { + length = webidl.converters["unsigned long"](length, { prefix, - context: "Argument 2", + context: "Argument 3", }); - if (length !== null) { - length = webidl.converters["unsigned long"](length, { - prefix, - context: "Argument 3", - }); - } + } - // 2. - const normalizedAlgorithm = normalizeAlgorithm(algorithm, "deriveBits"); - // 4-6. - const result = await deriveBits(normalizedAlgorithm, baseKey, length); - // 7. - if (normalizedAlgorithm.name !== baseKey[_algorithm].name) { - throw new DOMException("Invalid algorithm name", "InvalidAccessError"); - } - // 8. - if (!ArrayPrototypeIncludes(baseKey[_usages], "deriveBits")) { - throw new DOMException( - "baseKey usages does not contain `deriveBits`", - "InvalidAccessError", - ); - } - // 9-10. - return result; + // 2. + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "deriveBits"); + // 4-6. + const result = await deriveBits(normalizedAlgorithm, baseKey, length); + // 7. + if (normalizedAlgorithm.name !== baseKey[_algorithm].name) { + throw new DOMException("Invalid algorithm name", "InvalidAccessError"); } + // 8. + if (!ArrayPrototypeIncludes(baseKey[_usages], "deriveBits")) { + throw new DOMException( + "baseKey usages does not contain `deriveBits`", + "InvalidAccessError", + ); + } + // 9-10. + return result; + } - /** - * @param {AlgorithmIdentifier} algorithm - * @param {CryptoKey} baseKey - * @param {number} length - * @returns {Promise<ArrayBuffer>} - */ - async deriveKey( - algorithm, - baseKey, + /** + * @param {AlgorithmIdentifier} algorithm + * @param {CryptoKey} baseKey + * @param {number} length + * @returns {Promise<ArrayBuffer>} + */ + async deriveKey( + algorithm, + baseKey, + derivedKeyType, + extractable, + keyUsages, + ) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'deriveKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 5, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + baseKey = webidl.converters.CryptoKey(baseKey, { + prefix, + context: "Argument 2", + }); + derivedKeyType = webidl.converters.AlgorithmIdentifier(derivedKeyType, { + prefix, + context: "Argument 3", + }); + extractable = webidl.converters["boolean"](extractable, { + prefix, + context: "Argument 4", + }); + keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, { + prefix, + context: "Argument 5", + }); + + // 2-3. + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "deriveBits"); + + // 4-5. + const normalizedDerivedKeyAlgorithmImport = normalizeAlgorithm( derivedKeyType, + "importKey", + ); + + // 6-7. + const normalizedDerivedKeyAlgorithmLength = normalizeAlgorithm( + derivedKeyType, + "get key length", + ); + + // 8-10. + + // 11. + if (normalizedAlgorithm.name !== baseKey[_algorithm].name) { + throw new DOMException( + "Invalid algorithm name", + "InvalidAccessError", + ); + } + + // 12. + if (!ArrayPrototypeIncludes(baseKey[_usages], "deriveKey")) { + throw new DOMException( + "baseKey usages does not contain `deriveKey`", + "InvalidAccessError", + ); + } + + // 13. + const length = getKeyLength(normalizedDerivedKeyAlgorithmLength); + + // 14. + const secret = await this.deriveBits( + normalizedAlgorithm, + baseKey, + length, + ); + + // 15. + const result = await this.importKey( + "raw", + secret, + normalizedDerivedKeyAlgorithmImport, extractable, keyUsages, + ); + + // 16. + if ( + ArrayPrototypeIncludes(["private", "secret"], result[_type]) && + keyUsages.length == 0 ) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'deriveKey' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 5, { prefix }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 1", - }); - baseKey = webidl.converters.CryptoKey(baseKey, { - prefix, - context: "Argument 2", - }); - derivedKeyType = webidl.converters.AlgorithmIdentifier(derivedKeyType, { - prefix, - context: "Argument 3", - }); - extractable = webidl.converters["boolean"](extractable, { - prefix, - context: "Argument 4", - }); - keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, { - prefix, - context: "Argument 5", - }); + throw new SyntaxError("Invalid key usages"); + } + // 17. + return result; + } - // 2-3. - const normalizedAlgorithm = normalizeAlgorithm(algorithm, "deriveBits"); + /** + * @param {string} algorithm + * @param {CryptoKey} key + * @param {BufferSource} signature + * @param {BufferSource} data + * @returns {Promise<boolean>} + */ + async verify(algorithm, key, signature, data) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'verify' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 4, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: "Argument 2", + }); + signature = webidl.converters.BufferSource(signature, { + prefix, + context: "Argument 3", + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: "Argument 4", + }); - // 4-5. - const normalizedDerivedKeyAlgorithmImport = normalizeAlgorithm( - derivedKeyType, - "importKey", + // 2. + signature = copyBuffer(signature); + + // 3. + data = copyBuffer(data); + + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "verify"); + + const handle = key[_handle]; + const keyData = WeakMapPrototypeGet(KEY_STORE, handle); + + if (normalizedAlgorithm.name !== key[_algorithm].name) { + throw new DOMException( + "Verifying algorithm doesn't match key algorithm.", + "InvalidAccessError", ); + } - // 6-7. - const normalizedDerivedKeyAlgorithmLength = normalizeAlgorithm( - derivedKeyType, - "get key length", + if (!ArrayPrototypeIncludes(key[_usages], "verify")) { + throw new DOMException( + "Key does not support the 'verify' operation.", + "InvalidAccessError", ); + } - // 8-10. + switch (normalizedAlgorithm.name) { + case "RSASSA-PKCS1-v1_5": { + if (key[_type] !== "public") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } - // 11. - if (normalizedAlgorithm.name !== baseKey[_algorithm].name) { - throw new DOMException( - "Invalid algorithm name", - "InvalidAccessError", - ); + const hashAlgorithm = key[_algorithm].hash.name; + return await core.opAsync("op_crypto_verify_key", { + key: keyData, + algorithm: "RSASSA-PKCS1-v1_5", + hash: hashAlgorithm, + signature, + }, data); } + case "RSA-PSS": { + if (key[_type] !== "public") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } - // 12. - if (!ArrayPrototypeIncludes(baseKey[_usages], "deriveKey")) { - throw new DOMException( - "baseKey usages does not contain `deriveKey`", - "InvalidAccessError", - ); + const hashAlgorithm = key[_algorithm].hash.name; + return await core.opAsync("op_crypto_verify_key", { + key: keyData, + algorithm: "RSA-PSS", + hash: hashAlgorithm, + signature, + }, data); } + case "HMAC": { + const hash = key[_algorithm].hash.name; + return await core.opAsync("op_crypto_verify_key", { + key: keyData, + algorithm: "HMAC", + hash, + signature, + }, data); + } + case "ECDSA": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } + // 2. + const hash = normalizedAlgorithm.hash.name; - // 13. - const length = getKeyLength(normalizedDerivedKeyAlgorithmLength); - - // 14. - const secret = await this.deriveBits( - normalizedAlgorithm, - baseKey, - length, - ); - - // 15. - const result = await this.importKey( - "raw", - secret, - normalizedDerivedKeyAlgorithmImport, - extractable, - keyUsages, - ); + // 3-8. + return await core.opAsync("op_crypto_verify_key", { + key: keyData, + algorithm: "ECDSA", + hash, + signature, + namedCurve: key[_algorithm].namedCurve, + }, data); + } + case "Ed25519": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } - // 16. - if ( - ArrayPrototypeIncludes(["private", "secret"], result[_type]) && - keyUsages.length == 0 - ) { - throw new SyntaxError("Invalid key usages"); + return ops.op_verify_ed25519(keyData, data, signature); } - // 17. - return result; } - /** - * @param {string} algorithm - * @param {CryptoKey} key - * @param {BufferSource} signature - * @param {BufferSource} data - * @returns {Promise<boolean>} - */ - async verify(algorithm, key, signature, data) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'verify' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 4, { prefix }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 1", - }); - key = webidl.converters.CryptoKey(key, { - prefix, - context: "Argument 2", - }); - signature = webidl.converters.BufferSource(signature, { - prefix, - context: "Argument 3", - }); - data = webidl.converters.BufferSource(data, { - prefix, - context: "Argument 4", - }); + throw new TypeError("unreachable"); + } - // 2. - signature = copyBuffer(signature); + /** + * @param {string} algorithm + * @param {boolean} extractable + * @param {KeyUsage[]} keyUsages + * @returns {Promise<any>} + */ + async wrapKey(format, key, wrappingKey, wrapAlgorithm) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'wrapKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 4, { prefix }); + format = webidl.converters.KeyFormat(format, { + prefix, + context: "Argument 1", + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: "Argument 2", + }); + wrappingKey = webidl.converters.CryptoKey(wrappingKey, { + prefix, + context: "Argument 3", + }); + wrapAlgorithm = webidl.converters.AlgorithmIdentifier(wrapAlgorithm, { + prefix, + context: "Argument 4", + }); + + let normalizedAlgorithm; + try { + // 2. + normalizedAlgorithm = normalizeAlgorithm(wrapAlgorithm, "wrapKey"); + } catch (_) { // 3. - data = copyBuffer(data); + normalizedAlgorithm = normalizeAlgorithm(wrapAlgorithm, "encrypt"); + } - const normalizedAlgorithm = normalizeAlgorithm(algorithm, "verify"); + // 8. + if (normalizedAlgorithm.name !== wrappingKey[_algorithm].name) { + throw new DOMException( + "Wrapping algorithm doesn't match key algorithm.", + "InvalidAccessError", + ); + } - const handle = key[_handle]; - const keyData = WeakMapPrototypeGet(KEY_STORE, handle); + // 9. + if (!ArrayPrototypeIncludes(wrappingKey[_usages], "wrapKey")) { + throw new DOMException( + "Key does not support the 'wrapKey' operation.", + "InvalidAccessError", + ); + } - if (normalizedAlgorithm.name !== key[_algorithm].name) { - throw new DOMException( - "Verifying algorithm doesn't match key algorithm.", - "InvalidAccessError", - ); - } + // 10. NotSupportedError will be thrown in step 12. + // 11. + if (key[_extractable] === false) { + throw new DOMException( + "Key is not extractable", + "InvalidAccessError", + ); + } - if (!ArrayPrototypeIncludes(key[_usages], "verify")) { - throw new DOMException( - "Key does not support the 'verify' operation.", - "InvalidAccessError", - ); + // 12. + const exportedKey = await this.exportKey(format, key); + + let bytes; + // 13. + if (format !== "jwk") { + bytes = new Uint8Array(exportedKey); + } else { + const jwk = JSONStringify(exportedKey); + const ret = new Uint8Array(jwk.length); + for (let i = 0; i < jwk.length; i++) { + ret[i] = StringPrototypeCharCodeAt(jwk, i); } + bytes = ret; + } - switch (normalizedAlgorithm.name) { - case "RSASSA-PKCS1-v1_5": { - if (key[_type] !== "public") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } + // 14-15. + if ( + supportedAlgorithms["wrapKey"][normalizedAlgorithm.name] !== undefined + ) { + const handle = wrappingKey[_handle]; + const keyData = WeakMapPrototypeGet(KEY_STORE, handle); - const hashAlgorithm = key[_algorithm].hash.name; - return await core.opAsync("op_crypto_verify_key", { + switch (normalizedAlgorithm.name) { + case "AES-KW": { + const cipherText = await ops.op_crypto_wrap_key({ key: keyData, - algorithm: "RSASSA-PKCS1-v1_5", - hash: hashAlgorithm, - signature, - }, data); - } - case "RSA-PSS": { - if (key[_type] !== "public") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } + algorithm: normalizedAlgorithm.name, + }, bytes); - const hashAlgorithm = key[_algorithm].hash.name; - return await core.opAsync("op_crypto_verify_key", { - key: keyData, - algorithm: "RSA-PSS", - hash: hashAlgorithm, - signature, - }, data); + // 4. + return cipherText.buffer; } - case "HMAC": { - const hash = key[_algorithm].hash.name; - return await core.opAsync("op_crypto_verify_key", { - key: keyData, - algorithm: "HMAC", - hash, - signature, - }, data); + default: { + throw new DOMException( + "Not implemented", + "NotSupportedError", + ); } - case "ECDSA": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } - // 2. - const hash = normalizedAlgorithm.hash.name; + } + } else if ( + supportedAlgorithms["encrypt"][normalizedAlgorithm.name] !== undefined + ) { + // must construct a new key, since keyUsages is ["wrapKey"] and not ["encrypt"] + return await encrypt( + normalizedAlgorithm, + constructKey( + wrappingKey[_type], + wrappingKey[_extractable], + ["encrypt"], + wrappingKey[_algorithm], + wrappingKey[_handle], + ), + bytes, + ); + } else { + throw new DOMException( + "Algorithm not supported", + "NotSupportedError", + ); + } + } + /** + * @param {string} format + * @param {BufferSource} wrappedKey + * @param {CryptoKey} unwrappingKey + * @param {AlgorithmIdentifier} unwrapAlgorithm + * @param {AlgorithmIdentifier} unwrappedKeyAlgorithm + * @param {boolean} extractable + * @param {KeyUsage[]} keyUsages + * @returns {Promise<CryptoKey>} + */ + async unwrapKey( + format, + wrappedKey, + unwrappingKey, + unwrapAlgorithm, + unwrappedKeyAlgorithm, + extractable, + keyUsages, + ) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'unwrapKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 7, { prefix }); + format = webidl.converters.KeyFormat(format, { + prefix, + context: "Argument 1", + }); + wrappedKey = webidl.converters.BufferSource(wrappedKey, { + prefix, + context: "Argument 2", + }); + unwrappingKey = webidl.converters.CryptoKey(unwrappingKey, { + prefix, + context: "Argument 3", + }); + unwrapAlgorithm = webidl.converters.AlgorithmIdentifier(unwrapAlgorithm, { + prefix, + context: "Argument 4", + }); + unwrappedKeyAlgorithm = webidl.converters.AlgorithmIdentifier( + unwrappedKeyAlgorithm, + { + prefix, + context: "Argument 5", + }, + ); + extractable = webidl.converters.boolean(extractable, { + prefix, + context: "Argument 6", + }); + keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, { + prefix, + context: "Argument 7", + }); - // 3-8. - return await core.opAsync("op_crypto_verify_key", { - key: keyData, - algorithm: "ECDSA", - hash, - signature, - namedCurve: key[_algorithm].namedCurve, - }, data); - } - case "Ed25519": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } + // 2. + wrappedKey = copyBuffer(wrappedKey); - return ops.op_verify_ed25519(keyData, data, signature); - } - } + let normalizedAlgorithm; - throw new TypeError("unreachable"); + try { + // 3. + normalizedAlgorithm = normalizeAlgorithm(unwrapAlgorithm, "unwrapKey"); + } catch (_) { + // 4. + normalizedAlgorithm = normalizeAlgorithm(unwrapAlgorithm, "decrypt"); } - /** - * @param {string} algorithm - * @param {boolean} extractable - * @param {KeyUsage[]} keyUsages - * @returns {Promise<any>} - */ - async wrapKey(format, key, wrappingKey, wrapAlgorithm) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'wrapKey' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 4, { prefix }); - format = webidl.converters.KeyFormat(format, { - prefix, - context: "Argument 1", - }); - key = webidl.converters.CryptoKey(key, { - prefix, - context: "Argument 2", - }); - wrappingKey = webidl.converters.CryptoKey(wrappingKey, { - prefix, - context: "Argument 3", - }); - wrapAlgorithm = webidl.converters.AlgorithmIdentifier(wrapAlgorithm, { - prefix, - context: "Argument 4", - }); + // 6. + const normalizedKeyAlgorithm = normalizeAlgorithm( + unwrappedKeyAlgorithm, + "importKey", + ); - let normalizedAlgorithm; + // 11. + if (normalizedAlgorithm.name !== unwrappingKey[_algorithm].name) { + throw new DOMException( + "Unwrapping algorithm doesn't match key algorithm.", + "InvalidAccessError", + ); + } - try { - // 2. - normalizedAlgorithm = normalizeAlgorithm(wrapAlgorithm, "wrapKey"); - } catch (_) { - // 3. - normalizedAlgorithm = normalizeAlgorithm(wrapAlgorithm, "encrypt"); - } + // 12. + if (!ArrayPrototypeIncludes(unwrappingKey[_usages], "unwrapKey")) { + throw new DOMException( + "Key does not support the 'unwrapKey' operation.", + "InvalidAccessError", + ); + } - // 8. - if (normalizedAlgorithm.name !== wrappingKey[_algorithm].name) { - throw new DOMException( - "Wrapping algorithm doesn't match key algorithm.", - "InvalidAccessError", - ); - } + // 13. + let key; + if ( + supportedAlgorithms["unwrapKey"][normalizedAlgorithm.name] !== undefined + ) { + const handle = unwrappingKey[_handle]; + const keyData = WeakMapPrototypeGet(KEY_STORE, handle); - // 9. - if (!ArrayPrototypeIncludes(wrappingKey[_usages], "wrapKey")) { - throw new DOMException( - "Key does not support the 'wrapKey' operation.", - "InvalidAccessError", - ); + switch (normalizedAlgorithm.name) { + case "AES-KW": { + const plainText = await ops.op_crypto_unwrap_key({ + key: keyData, + algorithm: normalizedAlgorithm.name, + }, wrappedKey); + + // 4. + key = plainText.buffer; + break; + } + default: { + throw new DOMException( + "Not implemented", + "NotSupportedError", + ); + } } + } else if ( + supportedAlgorithms["decrypt"][normalizedAlgorithm.name] !== undefined + ) { + // must construct a new key, since keyUsages is ["unwrapKey"] and not ["decrypt"] + key = await this.decrypt( + normalizedAlgorithm, + constructKey( + unwrappingKey[_type], + unwrappingKey[_extractable], + ["decrypt"], + unwrappingKey[_algorithm], + unwrappingKey[_handle], + ), + wrappedKey, + ); + } else { + throw new DOMException( + "Algorithm not supported", + "NotSupportedError", + ); + } - // 10. NotSupportedError will be thrown in step 12. - // 11. - if (key[_extractable] === false) { - throw new DOMException( - "Key is not extractable", - "InvalidAccessError", - ); + let bytes; + // 14. + if (format !== "jwk") { + bytes = key; + } else { + const k = new Uint8Array(key); + let str = ""; + for (let i = 0; i < k.length; i++) { + str += StringFromCharCode(k[i]); } + bytes = JSONParse(str); + } - // 12. - const exportedKey = await this.exportKey(format, key); + // 15. + const result = await this.importKey( + format, + bytes, + normalizedKeyAlgorithm, + extractable, + keyUsages, + ); + // 16. + if ( + (result[_type] == "secret" || result[_type] == "private") && + keyUsages.length == 0 + ) { + throw new SyntaxError("Invalid key type."); + } + // 17. + result[_extractable] = extractable; + // 18. + result[_usages] = usageIntersection(keyUsages, recognisedUsages); + // 19. + return result; + } - let bytes; - // 13. - if (format !== "jwk") { - bytes = new Uint8Array(exportedKey); - } else { - const jwk = JSONStringify(exportedKey); - const ret = new Uint8Array(jwk.length); - for (let i = 0; i < jwk.length; i++) { - ret[i] = StringPrototypeCharCodeAt(jwk, i); - } - bytes = ret; + /** + * @param {string} algorithm + * @param {boolean} extractable + * @param {KeyUsage[]} keyUsages + * @returns {Promise<any>} + */ + async generateKey(algorithm, extractable, keyUsages) { + webidl.assertBranded(this, SubtleCryptoPrototype); + const prefix = "Failed to execute 'generateKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + extractable = webidl.converters["boolean"](extractable, { + prefix, + context: "Argument 2", + }); + keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, { + prefix, + context: "Argument 3", + }); + + const usages = keyUsages; + + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "generateKey"); + const result = await generateKey( + normalizedAlgorithm, + extractable, + usages, + ); + + if (ObjectPrototypeIsPrototypeOf(CryptoKeyPrototype, result)) { + const type = result[_type]; + if ((type === "secret" || type === "private") && usages.length === 0) { + throw new DOMException("Invalid key usages", "SyntaxError"); } + } else if ( + ObjectPrototypeIsPrototypeOf(CryptoKeyPrototype, result.privateKey) + ) { + if (result.privateKey[_usages].length === 0) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + } - // 14-15. - if ( - supportedAlgorithms["wrapKey"][normalizedAlgorithm.name] !== undefined - ) { - const handle = wrappingKey[_handle]; - const keyData = WeakMapPrototypeGet(KEY_STORE, handle); + return result; + } +} +const SubtleCryptoPrototype = SubtleCrypto.prototype; - switch (normalizedAlgorithm.name) { - case "AES-KW": { - const cipherText = await ops.op_crypto_wrap_key({ - key: keyData, - algorithm: normalizedAlgorithm.name, - }, bytes); +async function generateKey(normalizedAlgorithm, extractable, usages) { + const algorithmName = normalizedAlgorithm.name; - // 4. - return cipherText.buffer; - } - default: { - throw new DOMException( - "Not implemented", - "NotSupportedError", - ); - } - } - } else if ( - supportedAlgorithms["encrypt"][normalizedAlgorithm.name] !== undefined + switch (algorithmName) { + case "RSASSA-PKCS1-v1_5": + case "RSA-PSS": { + // 1. + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), + ) !== undefined ) { - // must construct a new key, since keyUsages is ["wrapKey"] and not ["encrypt"] - return await encrypt( - normalizedAlgorithm, - constructKey( - wrappingKey[_type], - wrappingKey[_extractable], - ["encrypt"], - wrappingKey[_algorithm], - wrappingKey[_handle], - ), - bytes, - ); - } else { - throw new DOMException( - "Algorithm not supported", - "NotSupportedError", - ); + throw new DOMException("Invalid key usages", "SyntaxError"); } - } - /** - * @param {string} format - * @param {BufferSource} wrappedKey - * @param {CryptoKey} unwrappingKey - * @param {AlgorithmIdentifier} unwrapAlgorithm - * @param {AlgorithmIdentifier} unwrappedKeyAlgorithm - * @param {boolean} extractable - * @param {KeyUsage[]} keyUsages - * @returns {Promise<CryptoKey>} - */ - async unwrapKey( - format, - wrappedKey, - unwrappingKey, - unwrapAlgorithm, - unwrappedKeyAlgorithm, - extractable, - keyUsages, - ) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'unwrapKey' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 7, { prefix }); - format = webidl.converters.KeyFormat(format, { - prefix, - context: "Argument 1", - }); - wrappedKey = webidl.converters.BufferSource(wrappedKey, { - prefix, - context: "Argument 2", - }); - unwrappingKey = webidl.converters.CryptoKey(unwrappingKey, { - prefix, - context: "Argument 3", - }); - unwrapAlgorithm = webidl.converters.AlgorithmIdentifier(unwrapAlgorithm, { - prefix, - context: "Argument 4", - }); - unwrappedKeyAlgorithm = webidl.converters.AlgorithmIdentifier( - unwrappedKeyAlgorithm, + + // 2. + const keyData = await core.opAsync( + "op_crypto_generate_key", { - prefix, - context: "Argument 5", + algorithm: "RSA", + modulusLength: normalizedAlgorithm.modulusLength, + publicExponent: normalizedAlgorithm.publicExponent, }, ); - extractable = webidl.converters.boolean(extractable, { - prefix, - context: "Argument 6", - }); - keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, { - prefix, - context: "Argument 7", + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "private", + data: keyData, }); - // 2. - wrappedKey = copyBuffer(wrappedKey); + // 4-8. + const algorithm = { + name: algorithmName, + modulusLength: normalizedAlgorithm.modulusLength, + publicExponent: normalizedAlgorithm.publicExponent, + hash: normalizedAlgorithm.hash, + }; + + // 9-13. + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, ["verify"]), + algorithm, + handle, + ); - let normalizedAlgorithm; + // 14-18. + const privateKey = constructKey( + "private", + extractable, + usageIntersection(usages, ["sign"]), + algorithm, + handle, + ); - try { - // 3. - normalizedAlgorithm = normalizeAlgorithm(unwrapAlgorithm, "unwrapKey"); - } catch (_) { - // 4. - normalizedAlgorithm = normalizeAlgorithm(unwrapAlgorithm, "decrypt"); + // 19-22. + return { publicKey, privateKey }; + } + case "RSA-OAEP": { + if ( + ArrayPrototypeFind( + usages, + (u) => + !ArrayPrototypeIncludes([ + "encrypt", + "decrypt", + "wrapKey", + "unwrapKey", + ], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); } - // 6. - const normalizedKeyAlgorithm = normalizeAlgorithm( - unwrappedKeyAlgorithm, - "importKey", + // 2. + const keyData = await core.opAsync( + "op_crypto_generate_key", + { + algorithm: "RSA", + modulusLength: normalizedAlgorithm.modulusLength, + publicExponent: normalizedAlgorithm.publicExponent, + }, ); + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "private", + data: keyData, + }); - // 11. - if (normalizedAlgorithm.name !== unwrappingKey[_algorithm].name) { - throw new DOMException( - "Unwrapping algorithm doesn't match key algorithm.", - "InvalidAccessError", - ); - } + // 4-8. + const algorithm = { + name: algorithmName, + modulusLength: normalizedAlgorithm.modulusLength, + publicExponent: normalizedAlgorithm.publicExponent, + hash: normalizedAlgorithm.hash, + }; + + // 9-13. + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, ["encrypt", "wrapKey"]), + algorithm, + handle, + ); - // 12. - if (!ArrayPrototypeIncludes(unwrappingKey[_usages], "unwrapKey")) { - throw new DOMException( - "Key does not support the 'unwrapKey' operation.", - "InvalidAccessError", - ); + // 14-18. + const privateKey = constructKey( + "private", + extractable, + usageIntersection(usages, ["decrypt", "unwrapKey"]), + algorithm, + handle, + ); + + // 19-22. + return { publicKey, privateKey }; + } + case "ECDSA": { + const namedCurve = normalizedAlgorithm.namedCurve; + + // 1. + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); } - // 13. - let key; + // 2-3. + const handle = {}; if ( - supportedAlgorithms["unwrapKey"][normalizedAlgorithm.name] !== undefined + ArrayPrototypeIncludes( + supportedNamedCurves, + namedCurve, + ) ) { - const handle = unwrappingKey[_handle]; - const keyData = WeakMapPrototypeGet(KEY_STORE, handle); + const keyData = await core.opAsync("op_crypto_generate_key", { + algorithm: "EC", + namedCurve, + }); + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "private", + data: keyData, + }); + } else { + throw new DOMException("Curve not supported", "NotSupportedError"); + } - switch (normalizedAlgorithm.name) { - case "AES-KW": { - const plainText = await ops.op_crypto_unwrap_key({ - key: keyData, - algorithm: normalizedAlgorithm.name, - }, wrappedKey); + // 4-6. + const algorithm = { + name: algorithmName, + namedCurve, + }; + + // 7-11. + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, ["verify"]), + algorithm, + handle, + ); - // 4. - key = plainText.buffer; - break; - } - default: { - throw new DOMException( - "Not implemented", - "NotSupportedError", - ); - } - } - } else if ( - supportedAlgorithms["decrypt"][normalizedAlgorithm.name] !== undefined + // 12-16. + const privateKey = constructKey( + "private", + extractable, + usageIntersection(usages, ["sign"]), + algorithm, + handle, + ); + + // 17-20. + return { publicKey, privateKey }; + } + case "ECDH": { + const namedCurve = normalizedAlgorithm.namedCurve; + + // 1. + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined ) { - // must construct a new key, since keyUsages is ["unwrapKey"] and not ["decrypt"] - key = await this.decrypt( - normalizedAlgorithm, - constructKey( - unwrappingKey[_type], - unwrappingKey[_extractable], - ["decrypt"], - unwrappingKey[_algorithm], - unwrappingKey[_handle], - ), - wrappedKey, - ); - } else { - throw new DOMException( - "Algorithm not supported", - "NotSupportedError", - ); + throw new DOMException("Invalid key usages", "SyntaxError"); } - let bytes; - // 14. - if (format !== "jwk") { - bytes = key; + // 2-3. + const handle = {}; + if ( + ArrayPrototypeIncludes( + supportedNamedCurves, + namedCurve, + ) + ) { + const keyData = await core.opAsync("op_crypto_generate_key", { + algorithm: "EC", + namedCurve, + }); + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "private", + data: keyData, + }); } else { - const k = new Uint8Array(key); - let str = ""; - for (let i = 0; i < k.length; i++) { - str += StringFromCharCode(k[i]); - } - bytes = JSONParse(str); + throw new DOMException("Curve not supported", "NotSupportedError"); } - // 15. - const result = await this.importKey( - format, - bytes, - normalizedKeyAlgorithm, + // 4-6. + const algorithm = { + name: algorithmName, + namedCurve, + }; + + // 7-11. + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, []), + algorithm, + handle, + ); + + // 12-16. + const privateKey = constructKey( + "private", extractable, - keyUsages, + usageIntersection(usages, ["deriveKey", "deriveBits"]), + algorithm, + handle, ); - // 16. + + // 17-20. + return { publicKey, privateKey }; + } + case "AES-CTR": + case "AES-CBC": + case "AES-GCM": { + // 1. if ( - (result[_type] == "secret" || result[_type] == "private") && - keyUsages.length == 0 + ArrayPrototypeFind( + usages, + (u) => + !ArrayPrototypeIncludes([ + "encrypt", + "decrypt", + "wrapKey", + "unwrapKey", + ], u), + ) !== undefined ) { - throw new SyntaxError("Invalid key type."); - } - // 17. - result[_extractable] = extractable; - // 18. - result[_usages] = usageIntersection(keyUsages, recognisedUsages); - // 19. - return result; - } + throw new DOMException("Invalid key usages", "SyntaxError"); + } - /** - * @param {string} algorithm - * @param {boolean} extractable - * @param {KeyUsage[]} keyUsages - * @returns {Promise<any>} - */ - async generateKey(algorithm, extractable, keyUsages) { - webidl.assertBranded(this, SubtleCryptoPrototype); - const prefix = "Failed to execute 'generateKey' on 'SubtleCrypto'"; - webidl.requiredArguments(arguments.length, 3, { prefix }); - algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { - prefix, - context: "Argument 1", - }); - extractable = webidl.converters["boolean"](extractable, { - prefix, - context: "Argument 2", - }); - keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, { - prefix, - context: "Argument 3", - }); + return generateKeyAES(normalizedAlgorithm, extractable, usages); + } + case "AES-KW": { + // 1. + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["wrapKey", "unwrapKey"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - const usages = keyUsages; + return generateKeyAES(normalizedAlgorithm, extractable, usages); + } + case "X25519": { + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + const privateKeyData = new Uint8Array(32); + const publicKeyData = new Uint8Array(32); + ops.op_generate_x25519_keypair(privateKeyData, publicKeyData); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const publicHandle = {}; + WeakMapPrototypeSet(KEY_STORE, publicHandle, publicKeyData); + + const algorithm = { + name: algorithmName, + }; + + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, []), + algorithm, + publicHandle, + ); - const normalizedAlgorithm = normalizeAlgorithm(algorithm, "generateKey"); - const result = await generateKey( - normalizedAlgorithm, + const privateKey = constructKey( + "private", extractable, - usages, + usageIntersection(usages, ["deriveKey", "deriveBits"]), + algorithm, + handle, ); - if (ObjectPrototypeIsPrototypeOf(CryptoKeyPrototype, result)) { - const type = result[_type]; - if ((type === "secret" || type === "private") && usages.length === 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - } else if ( - ObjectPrototypeIsPrototypeOf(CryptoKeyPrototype, result.privateKey) + return { publicKey, privateKey }; + } + case "Ed25519": { + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), + ) !== undefined ) { - if (result.privateKey[_usages].length === 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + throw new DOMException("Invalid key usages", "SyntaxError"); } - return result; - } - } - const SubtleCryptoPrototype = SubtleCrypto.prototype; + const ED25519_SEED_LEN = 32; + const ED25519_PUBLIC_KEY_LEN = 32; + const privateKeyData = new Uint8Array(ED25519_SEED_LEN); + const publicKeyData = new Uint8Array(ED25519_PUBLIC_KEY_LEN); + if ( + !ops.op_generate_ed25519_keypair(privateKeyData, publicKeyData) + ) { + throw new DOMException("Failed to generate key", "OperationError"); + } - async function generateKey(normalizedAlgorithm, extractable, usages) { - const algorithmName = normalizedAlgorithm.name; + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); - switch (algorithmName) { - case "RSASSA-PKCS1-v1_5": - case "RSA-PSS": { - // 1. - if ( - ArrayPrototypeFind( - usages, - (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + const publicHandle = {}; + WeakMapPrototypeSet(KEY_STORE, publicHandle, publicKeyData); - // 2. - const keyData = await core.opAsync( - "op_crypto_generate_key", - { - algorithm: "RSA", - modulusLength: normalizedAlgorithm.modulusLength, - publicExponent: normalizedAlgorithm.publicExponent, - }, - ); - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "private", - data: keyData, - }); - - // 4-8. - const algorithm = { - name: algorithmName, - modulusLength: normalizedAlgorithm.modulusLength, - publicExponent: normalizedAlgorithm.publicExponent, - hash: normalizedAlgorithm.hash, - }; + const algorithm = { + name: algorithmName, + }; - // 9-13. - const publicKey = constructKey( - "public", - true, - usageIntersection(usages, ["verify"]), - algorithm, - handle, - ); + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, ["verify"]), + algorithm, + publicHandle, + ); - // 14-18. - const privateKey = constructKey( - "private", - extractable, - usageIntersection(usages, ["sign"]), - algorithm, - handle, - ); + const privateKey = constructKey( + "private", + extractable, + usageIntersection(usages, ["sign"]), + algorithm, + handle, + ); - // 19-22. - return { publicKey, privateKey }; + return { publicKey, privateKey }; + } + case "HMAC": { + // 1. + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); } - case "RSA-OAEP": { - if ( - ArrayPrototypeFind( - usages, - (u) => - !ArrayPrototypeIncludes([ - "encrypt", - "decrypt", - "wrapKey", - "unwrapKey", - ], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - // 2. - const keyData = await core.opAsync( - "op_crypto_generate_key", - { - algorithm: "RSA", - modulusLength: normalizedAlgorithm.modulusLength, - publicExponent: normalizedAlgorithm.publicExponent, - }, - ); - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "private", - data: keyData, - }); + // 2. + let length; + if (normalizedAlgorithm.length === undefined) { + length = null; + } else if (normalizedAlgorithm.length !== 0) { + length = normalizedAlgorithm.length; + } else { + throw new DOMException("Invalid length", "OperationError"); + } - // 4-8. - const algorithm = { - name: algorithmName, - modulusLength: normalizedAlgorithm.modulusLength, - publicExponent: normalizedAlgorithm.publicExponent, - hash: normalizedAlgorithm.hash, - }; + // 3-4. + const keyData = await core.opAsync("op_crypto_generate_key", { + algorithm: "HMAC", + hash: normalizedAlgorithm.hash.name, + length, + }); + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "secret", + data: keyData, + }); - // 9-13. - const publicKey = constructKey( - "public", - true, - usageIntersection(usages, ["encrypt", "wrapKey"]), - algorithm, - handle, - ); + // 6-10. + const algorithm = { + name: algorithmName, + hash: { + name: normalizedAlgorithm.hash.name, + }, + length: keyData.byteLength * 8, + }; - // 14-18. - const privateKey = constructKey( - "private", - extractable, - usageIntersection(usages, ["decrypt", "unwrapKey"]), - algorithm, - handle, - ); + // 5, 11-13. + const key = constructKey( + "secret", + extractable, + usages, + algorithm, + handle, + ); - // 19-22. - return { publicKey, privateKey }; + // 14. + return key; + } + } +} + +function importKeyEd25519( + format, + keyData, + extractable, + keyUsages, +) { + switch (format) { + case "raw": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["verify"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); } - case "ECDSA": { - const namedCurve = normalizedAlgorithm.namedCurve; - // 1. - if ( - ArrayPrototypeFind( - usages, - (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, keyData); - // 2-3. - const handle = {}; - if ( - ArrayPrototypeIncludes( - supportedNamedCurves, - namedCurve, - ) - ) { - const keyData = await core.opAsync("op_crypto_generate_key", { - algorithm: "EC", - namedCurve, - }); - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "private", - data: keyData, - }); - } else { - throw new DOMException("Curve not supported", "NotSupportedError"); - } + // 2-3. + const algorithm = { + name: "Ed25519", + }; - // 4-6. - const algorithm = { - name: algorithmName, - namedCurve, - }; + // 4-6. + return constructKey( + "public", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + } + case "spki": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["verify"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - // 7-11. - const publicKey = constructKey( - "public", - true, - usageIntersection(usages, ["verify"]), - algorithm, - handle, - ); + const publicKeyData = new Uint8Array(32); + if (!ops.op_import_spki_ed25519(keyData, publicKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } - // 12-16. - const privateKey = constructKey( - "private", - extractable, - usageIntersection(usages, ["sign"]), - algorithm, - handle, - ); + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); - // 17-20. - return { publicKey, privateKey }; + const algorithm = { + name: "Ed25519", + }; + + return constructKey( + "public", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + } + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["sign"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); } - case "ECDH": { - const namedCurve = normalizedAlgorithm.namedCurve; - // 1. - if ( - ArrayPrototypeFind( - usages, - (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + const privateKeyData = new Uint8Array(32); + if (!ops.op_import_pkcs8_ed25519(keyData, privateKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } - // 2-3. - const handle = {}; - if ( - ArrayPrototypeIncludes( - supportedNamedCurves, - namedCurve, - ) - ) { - const keyData = await core.opAsync("op_crypto_generate_key", { - algorithm: "EC", - namedCurve, - }); - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "private", - data: keyData, - }); - } else { - throw new DOMException("Curve not supported", "NotSupportedError"); - } + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); - // 4-6. - const algorithm = { - name: algorithmName, - namedCurve, - }; + const algorithm = { + name: "Ed25519", + }; - // 7-11. - const publicKey = constructKey( - "public", - true, - usageIntersection(usages, []), - algorithm, - handle, - ); - - // 12-16. - const privateKey = constructKey( - "private", - extractable, - usageIntersection(usages, ["deriveKey", "deriveBits"]), - algorithm, - handle, - ); + return constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + } + case "jwk": { + // 1. + const jwk = keyData; - // 17-20. - return { publicKey, privateKey }; - } - case "AES-CTR": - case "AES-CBC": - case "AES-GCM": { - // 1. + // 2. + if (jwk.d !== undefined) { if ( ArrayPrototypeFind( - usages, + keyUsages, (u) => - !ArrayPrototypeIncludes([ - "encrypt", - "decrypt", - "wrapKey", - "unwrapKey", - ], u), + !ArrayPrototypeIncludes( + ["sign"], + u, + ), ) !== undefined ) { throw new DOMException("Invalid key usages", "SyntaxError"); } - - return generateKeyAES(normalizedAlgorithm, extractable, usages); - } - case "AES-KW": { - // 1. + } else { if ( ArrayPrototypeFind( - usages, - (u) => !ArrayPrototypeIncludes(["wrapKey", "unwrapKey"], u), + keyUsages, + (u) => + !ArrayPrototypeIncludes( + ["verify"], + u, + ), ) !== undefined ) { throw new DOMException("Invalid key usages", "SyntaxError"); } - - return generateKeyAES(normalizedAlgorithm, extractable, usages); } - case "X25519": { - if ( - ArrayPrototypeFind( - usages, - (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - const privateKeyData = new Uint8Array(32); - const publicKeyData = new Uint8Array(32); - ops.op_generate_x25519_keypair(privateKeyData, publicKeyData); - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); - - const publicHandle = {}; - WeakMapPrototypeSet(KEY_STORE, publicHandle, publicKeyData); - - const algorithm = { - name: algorithmName, - }; + // 3. + if (jwk.kty !== "OKP") { + throw new DOMException("Invalid key type", "DataError"); + } - const publicKey = constructKey( - "public", - true, - usageIntersection(usages, []), - algorithm, - publicHandle, - ); + // 4. + if (jwk.crv !== "Ed25519") { + throw new DOMException("Invalid curve", "DataError"); + } - const privateKey = constructKey( - "private", - extractable, - usageIntersection(usages, ["deriveKey", "deriveBits"]), - algorithm, - handle, - ); + // 5. + if (jwk.alg !== undefined && jwk.alg !== "EdDSA") { + throw new DOMException("Invalid algorithm", "DataError"); + } - return { publicKey, privateKey }; + // 6. + if ( + keyUsages.length > 0 && jwk.use !== undefined && jwk.use !== "sig" + ) { + throw new DOMException("Invalid key usage", "DataError"); } - case "Ed25519": { + + // 7. + if (jwk.key_ops !== undefined) { if ( ArrayPrototypeFind( - usages, - (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); } - const ED25519_SEED_LEN = 32; - const ED25519_PUBLIC_KEY_LEN = 32; - const privateKeyData = new Uint8Array(ED25519_SEED_LEN); - const publicKeyData = new Uint8Array(ED25519_PUBLIC_KEY_LEN); if ( - !ops.op_generate_ed25519_keypair(privateKeyData, publicKeyData) + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) ) { - throw new DOMException("Failed to generate key", "OperationError"); + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); } + } + + // 8. + if (jwk.ext !== undefined && jwk.ext === false && extractable) { + throw new DOMException("Invalid key extractability", "DataError"); + } + + // 9. + if (jwk.d !== undefined) { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const privateKeyData = ops.op_crypto_base64url_decode(jwk.d); const handle = {}; WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); - const publicHandle = {}; - WeakMapPrototypeSet(KEY_STORE, publicHandle, publicKeyData); - const algorithm = { - name: algorithmName, + name: "Ed25519", }; - const publicKey = constructKey( - "public", - true, - usageIntersection(usages, ["verify"]), - algorithm, - publicHandle, - ); - - const privateKey = constructKey( + return constructKey( "private", extractable, - usageIntersection(usages, ["sign"]), + usageIntersection(keyUsages, recognisedUsages), algorithm, handle, ); + } else { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const publicKeyData = ops.op_crypto_base64url_decode(jwk.x); - return { publicKey, privateKey }; - } - case "HMAC": { - // 1. - if ( - ArrayPrototypeFind( - usages, - (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - - // 2. - let length; - if (normalizedAlgorithm.length === undefined) { - length = null; - } else if (normalizedAlgorithm.length !== 0) { - length = normalizedAlgorithm.length; - } else { - throw new DOMException("Invalid length", "OperationError"); - } - - // 3-4. - const keyData = await core.opAsync("op_crypto_generate_key", { - algorithm: "HMAC", - hash: normalizedAlgorithm.hash.name, - length, - }); const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "secret", - data: keyData, - }); + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); - // 6-10. const algorithm = { - name: algorithmName, - hash: { - name: normalizedAlgorithm.hash.name, - }, - length: keyData.byteLength * 8, + name: "Ed25519", }; - // 5, 11-13. - const key = constructKey( - "secret", + return constructKey( + "public", extractable, - usages, + usageIntersection(keyUsages, recognisedUsages), algorithm, handle, ); - - // 14. - return key; } } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } +} + +function importKeyX25519( + format, + keyData, + extractable, + keyUsages, +) { + switch (format) { + case "raw": { + // 1. + if (keyUsages.length > 0) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - function importKeyEd25519( - format, - keyData, - extractable, - keyUsages, - ) { - switch (format) { - case "raw": { - // 1. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => !ArrayPrototypeIncludes(["verify"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, keyData); - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, keyData); + // 2-3. + const algorithm = { + name: "X25519", + }; - // 2-3. - const algorithm = { - name: "Ed25519", - }; + // 4-6. + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "spki": { + // 1. + if (keyUsages.length > 0) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - // 4-6. - return constructKey( - "public", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); + const publicKeyData = new Uint8Array(32); + if (!ops.op_import_spki_x25519(keyData, publicKeyData)) { + throw new DOMException("Invalid key data", "DataError"); } - case "spki": { - // 1. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => !ArrayPrototypeIncludes(["verify"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - const publicKeyData = new Uint8Array(32); - if (!ops.op_import_spki_ed25519(keyData, publicKeyData)) { - throw new DOMException("Invalid key data", "DataError"); - } + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + const algorithm = { + name: "X25519", + }; - const algorithm = { - name: "Ed25519", - }; + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - return constructKey( - "public", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); + const privateKeyData = new Uint8Array(32); + if (!ops.op_import_pkcs8_x25519(keyData, privateKeyData)) { + throw new DOMException("Invalid key data", "DataError"); } - case "pkcs8": { - // 1. + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "X25519", + }; + + return constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + } + case "jwk": { + // 1. + const jwk = keyData; + + // 2. + if (jwk.d !== undefined) { if ( ArrayPrototypeFind( keyUsages, - (u) => !ArrayPrototypeIncludes(["sign"], u), + (u) => + !ArrayPrototypeIncludes( + ["deriveKey", "deriveBits"], + u, + ), ) !== undefined ) { throw new DOMException("Invalid key usages", "SyntaxError"); } - - const privateKeyData = new Uint8Array(32); - if (!ops.op_import_pkcs8_ed25519(keyData, privateKeyData)) { - throw new DOMException("Invalid key data", "DataError"); - } - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); - - const algorithm = { - name: "Ed25519", - }; - - return constructKey( - "private", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); } - case "jwk": { - // 1. - const jwk = keyData; - // 2. - if (jwk.d !== undefined) { - if ( - ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - ["sign"], - u, - ), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - } else { - if ( - ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - ["verify"], - u, - ), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - } + // 3. + if (jwk.d === undefined && keyUsages.length > 0) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - // 3. - if (jwk.kty !== "OKP") { - throw new DOMException("Invalid key type", "DataError"); - } + // 4. + if (jwk.kty !== "OKP") { + throw new DOMException("Invalid key type", "DataError"); + } - // 4. - if (jwk.crv !== "Ed25519") { - throw new DOMException("Invalid curve", "DataError"); - } + // 5. + if (jwk.crv !== "X25519") { + throw new DOMException("Invalid curve", "DataError"); + } - // 5. - if (jwk.alg !== undefined && jwk.alg !== "EdDSA") { - throw new DOMException("Invalid algorithm", "DataError"); + // 6. + if (keyUsages.length > 0 && jwk.use !== undefined) { + if (jwk.use !== "enc") { + throw new DOMException("Invalid key use", "DataError"); } + } - // 6. + // 7. + if (jwk.key_ops !== undefined) { if ( - keyUsages.length > 0 && jwk.use !== undefined && jwk.use !== "sig" + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined ) { - throw new DOMException("Invalid key usage", "DataError"); - } - - // 7. - if (jwk.key_ops !== undefined) { - if ( - ArrayPrototypeFind( - jwk.key_ops, - (u) => !ArrayPrototypeIncludes(recognisedUsages, u), - ) !== undefined - ) { - throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", - "DataError", - ); - } - - if ( - !ArrayPrototypeEvery( - jwk.key_ops, - (u) => ArrayPrototypeIncludes(keyUsages, u), - ) - ) { - throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", - "DataError", - ); - } - } - - // 8. - if (jwk.ext !== undefined && jwk.ext === false && extractable) { - throw new DOMException("Invalid key extractability", "DataError"); + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); } - // 9. - if (jwk.d !== undefined) { - // https://www.rfc-editor.org/rfc/rfc8037#section-2 - const privateKeyData = ops.op_crypto_base64url_decode(jwk.d); - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); - - const algorithm = { - name: "Ed25519", - }; - - return constructKey( - "private", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); - } else { - // https://www.rfc-editor.org/rfc/rfc8037#section-2 - const publicKeyData = ops.op_crypto_base64url_decode(jwk.x); - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); - - const algorithm = { - name: "Ed25519", - }; - - return constructKey( - "public", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", ); } } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - } - function importKeyX25519( - format, - keyData, - extractable, - keyUsages, - ) { - switch (format) { - case "raw": { - // 1. - if (keyUsages.length > 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + // 8. + if (jwk.ext !== undefined && jwk.ext === false && extractable) { + throw new DOMException("Invalid key extractability", "DataError"); + } + + // 9. + if (jwk.d !== undefined) { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const privateKeyData = ops.op_crypto_base64url_decode(jwk.d); const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, keyData); + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); - // 2-3. const algorithm = { name: "X25519", }; - // 4-6. return constructKey( - "public", + "private", extractable, - [], + usageIntersection(keyUsages, ["deriveKey", "deriveBits"]), algorithm, handle, ); - } - case "spki": { - // 1. - if (keyUsages.length > 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - - const publicKeyData = new Uint8Array(32); - if (!ops.op_import_spki_x25519(keyData, publicKeyData)) { - throw new DOMException("Invalid key data", "DataError"); - } + } else { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const publicKeyData = ops.op_crypto_base64url_decode(jwk.x); const handle = {}; WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); @@ -2410,600 +2550,538 @@ handle, ); } - case "pkcs8": { - // 1. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} + +function exportKeyAES( + format, + key, + innerKey, +) { + switch (format) { + // 2. + case "raw": { + // 1. + const data = innerKey.data; + // 2. + return data.buffer; + } + case "jwk": { + // 1-2. + const jwk = { + kty: "oct", + }; - const privateKeyData = new Uint8Array(32); - if (!ops.op_import_pkcs8_x25519(keyData, privateKeyData)) { - throw new DOMException("Invalid key data", "DataError"); - } + // 3. + const data = ops.op_crypto_export_key({ + format: "jwksecret", + algorithm: "AES", + }, innerKey); + ObjectAssign(jwk, data); + + // 4. + const algorithm = key[_algorithm]; + switch (algorithm.length) { + case 128: + jwk.alg = aesJwkAlg[algorithm.name][128]; + break; + case 192: + jwk.alg = aesJwkAlg[algorithm.name][192]; + break; + case 256: + jwk.alg = aesJwkAlg[algorithm.name][256]; + break; + default: + throw new DOMException( + "Invalid key length", + "NotSupportedError", + ); + } - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + // 5. + jwk.key_ops = key.usages; - const algorithm = { - name: "X25519", - }; + // 6. + jwk.ext = key[_extractable]; - return constructKey( - "private", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); + // 7. + return jwk; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} + +function importKeyAES( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, + supportedKeyUsages, +) { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(supportedKeyUsages, u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + const algorithmName = normalizedAlgorithm.name; + + // 2. + let data = keyData; + + switch (format) { + case "raw": { + // 2. + if ( + !ArrayPrototypeIncludes([128, 192, 256], keyData.byteLength * 8) + ) { + throw new DOMException("Invalid key length", "Datarror"); } - case "jwk": { - // 1. - const jwk = keyData; - // 2. - if (jwk.d !== undefined) { - if ( - ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - ["deriveKey", "deriveBits"], - u, - ), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - } + break; + } + case "jwk": { + // 1. + const jwk = keyData; - // 3. - if (jwk.d === undefined && keyUsages.length > 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + // 2. + if (jwk.kty !== "oct") { + throw new DOMException( + "'kty' property of JsonWebKey must be 'oct'", + "DataError", + ); + } - // 4. - if (jwk.kty !== "OKP") { - throw new DOMException("Invalid key type", "DataError"); - } + // Section 6.4.1 of RFC7518 + if (jwk.k === undefined) { + throw new DOMException( + "'k' property of JsonWebKey must be present", + "DataError", + ); + } - // 5. - if (jwk.crv !== "X25519") { - throw new DOMException("Invalid curve", "DataError"); - } + // 4. + const { rawData } = ops.op_crypto_import_key( + { algorithm: "AES" }, + { jwkSecret: jwk }, + ); + data = rawData.data; - // 6. - if (keyUsages.length > 0 && jwk.use !== undefined) { - if (jwk.use !== "enc") { - throw new DOMException("Invalid key use", "DataError"); + // 5. + switch (data.byteLength * 8) { + case 128: + if ( + jwk.alg !== undefined && + jwk.alg !== aesJwkAlg[algorithmName][128] + ) { + throw new DOMException("Invalid algorithm", "DataError"); } - } - - // 7. - if (jwk.key_ops !== undefined) { + break; + case 192: if ( - ArrayPrototypeFind( - jwk.key_ops, - (u) => !ArrayPrototypeIncludes(recognisedUsages, u), - ) !== undefined + jwk.alg !== undefined && + jwk.alg !== aesJwkAlg[algorithmName][192] ) { - throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", - "DataError", - ); + throw new DOMException("Invalid algorithm", "DataError"); } - + break; + case 256: if ( - !ArrayPrototypeEvery( - jwk.key_ops, - (u) => ArrayPrototypeIncludes(keyUsages, u), - ) + jwk.alg !== undefined && + jwk.alg !== aesJwkAlg[algorithmName][256] ) { - throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", - "DataError", - ); + throw new DOMException("Invalid algorithm", "DataError"); } - } - - // 8. - if (jwk.ext !== undefined && jwk.ext === false && extractable) { - throw new DOMException("Invalid key extractability", "DataError"); - } - - // 9. - if (jwk.d !== undefined) { - // https://www.rfc-editor.org/rfc/rfc8037#section-2 - const privateKeyData = ops.op_crypto_base64url_decode(jwk.d); - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); - - const algorithm = { - name: "X25519", - }; - - return constructKey( - "private", - extractable, - usageIntersection(keyUsages, ["deriveKey", "deriveBits"]), - algorithm, - handle, - ); - } else { - // https://www.rfc-editor.org/rfc/rfc8037#section-2 - const publicKeyData = ops.op_crypto_base64url_decode(jwk.x); - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); - - const algorithm = { - name: "X25519", - }; - - return constructKey( - "public", - extractable, - [], - algorithm, - handle, + break; + default: + throw new DOMException( + "Invalid key length", + "DataError", ); - } } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - } - function exportKeyAES( - format, - key, - innerKey, - ) { - switch (format) { - // 2. - case "raw": { - // 1. - const data = innerKey.data; - // 2. - return data.buffer; + // 6. + if ( + keyUsages.length > 0 && jwk.use !== undefined && jwk.use !== "enc" + ) { + throw new DOMException("Invalid key usages", "DataError"); } - case "jwk": { - // 1-2. - const jwk = { - kty: "oct", - }; - - // 3. - const data = ops.op_crypto_export_key({ - format: "jwksecret", - algorithm: "AES", - }, innerKey); - ObjectAssign(jwk, data); - // 4. - const algorithm = key[_algorithm]; - switch (algorithm.length) { - case 128: - jwk.alg = aesJwkAlg[algorithm.name][128]; - break; - case 192: - jwk.alg = aesJwkAlg[algorithm.name][192]; - break; - case 256: - jwk.alg = aesJwkAlg[algorithm.name][256]; - break; - default: - throw new DOMException( - "Invalid key length", - "NotSupportedError", - ); + // 7. + // Section 4.3 of RFC7517 + if (jwk.key_ops !== undefined) { + if ( + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); } - // 5. - jwk.key_ops = key.usages; - - // 6. - jwk.ext = key[_extractable]; + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + } - // 7. - return jwk; + // 8. + if (jwk.ext === false && extractable === true) { + throw new DOMException( + "'ext' property of JsonWebKey must not be false if extractable is true", + "DataError", + ); } - default: - throw new DOMException("Not implemented", "NotSupportedError"); + + break; } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } - function importKeyAES( - format, - normalizedAlgorithm, - keyData, + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "secret", + data, + }); + + // 4-7. + const algorithm = { + name: algorithmName, + length: data.byteLength * 8, + }; + + const key = constructKey( + "secret", extractable, - keyUsages, - supportedKeyUsages, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + // 8. + return key; +} + +function importKeyHMAC( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, +) { + // 2. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), + ) !== undefined ) { - // 1. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => !ArrayPrototypeIncludes(supportedKeyUsages, u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - - const algorithmName = normalizedAlgorithm.name; + throw new DOMException("Invalid key usages", "SyntaxError"); + } - // 2. - let data = keyData; + // 3. + let hash; + let data; - switch (format) { - case "raw": { - // 2. - if ( - !ArrayPrototypeIncludes([128, 192, 256], keyData.byteLength * 8) - ) { - throw new DOMException("Invalid key length", "Datarror"); - } + // 4. https://w3c.github.io/webcrypto/#hmac-operations + switch (format) { + case "raw": { + data = keyData; + hash = normalizedAlgorithm.hash; + break; + } + case "jwk": { + const jwk = keyData; - break; + // 2. + if (jwk.kty !== "oct") { + throw new DOMException( + "'kty' property of JsonWebKey must be 'oct'", + "DataError", + ); } - case "jwk": { - // 1. - const jwk = keyData; - // 2. - if (jwk.kty !== "oct") { - throw new DOMException( - "'kty' property of JsonWebKey must be 'oct'", - "DataError", - ); - } + // Section 6.4.1 of RFC7518 + if (jwk.k === undefined) { + throw new DOMException( + "'k' property of JsonWebKey must be present", + "DataError", + ); + } - // Section 6.4.1 of RFC7518 - if (jwk.k === undefined) { - throw new DOMException( - "'k' property of JsonWebKey must be present", - "DataError", - ); - } + // 4. + const { rawData } = ops.op_crypto_import_key( + { algorithm: "HMAC" }, + { jwkSecret: jwk }, + ); + data = rawData.data; - // 4. - const { rawData } = ops.op_crypto_import_key( - { algorithm: "AES" }, - { jwkSecret: jwk }, - ); - data = rawData.data; + // 5. + hash = normalizedAlgorithm.hash; - // 5. - switch (data.byteLength * 8) { - case 128: - if ( - jwk.alg !== undefined && - jwk.alg !== aesJwkAlg[algorithmName][128] - ) { - throw new DOMException("Invalid algorithm", "DataError"); - } - break; - case 192: - if ( - jwk.alg !== undefined && - jwk.alg !== aesJwkAlg[algorithmName][192] - ) { - throw new DOMException("Invalid algorithm", "DataError"); - } - break; - case 256: - if ( - jwk.alg !== undefined && - jwk.alg !== aesJwkAlg[algorithmName][256] - ) { - throw new DOMException("Invalid algorithm", "DataError"); - } - break; - default: + // 6. + switch (hash.name) { + case "SHA-1": { + if (jwk.alg !== undefined && jwk.alg !== "HS1") { throw new DOMException( - "Invalid key length", + "'alg' property of JsonWebKey must be 'HS1'", "DataError", ); + } + break; } - - // 6. - if ( - keyUsages.length > 0 && jwk.use !== undefined && jwk.use !== "enc" - ) { - throw new DOMException("Invalid key usages", "DataError"); - } - - // 7. - // Section 4.3 of RFC7517 - if (jwk.key_ops !== undefined) { - if ( - ArrayPrototypeFind( - jwk.key_ops, - (u) => !ArrayPrototypeIncludes(recognisedUsages, u), - ) !== undefined - ) { + case "SHA-256": { + if (jwk.alg !== undefined && jwk.alg !== "HS256") { throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", + "'alg' property of JsonWebKey must be 'HS256'", "DataError", ); } - - if ( - !ArrayPrototypeEvery( - jwk.key_ops, - (u) => ArrayPrototypeIncludes(keyUsages, u), - ) - ) { + break; + } + case "SHA-384": { + if (jwk.alg !== undefined && jwk.alg !== "HS384") { throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", + "'alg' property of JsonWebKey must be 'HS384'", "DataError", ); } + break; } - - // 8. - if (jwk.ext === false && extractable === true) { - throw new DOMException( - "'ext' property of JsonWebKey must not be false if extractable is true", - "DataError", - ); + case "SHA-512": { + if (jwk.alg !== undefined && jwk.alg !== "HS512") { + throw new DOMException( + "'alg' property of JsonWebKey must be 'HS512'", + "DataError", + ); + } + break; } - - break; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "secret", - data, - }); - - // 4-7. - const algorithm = { - name: algorithmName, - length: data.byteLength * 8, - }; - - const key = constructKey( - "secret", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); - - // 8. - return key; - } - - function importKeyHMAC( - format, - normalizedAlgorithm, - keyData, - extractable, - keyUsages, - ) { - // 2. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - - // 3. - let hash; - let data; - - // 4. https://w3c.github.io/webcrypto/#hmac-operations - switch (format) { - case "raw": { - data = keyData; - hash = normalizedAlgorithm.hash; - break; + default: + throw new TypeError("unreachable"); } - case "jwk": { - const jwk = keyData; - - // 2. - if (jwk.kty !== "oct") { - throw new DOMException( - "'kty' property of JsonWebKey must be 'oct'", - "DataError", - ); - } - - // Section 6.4.1 of RFC7518 - if (jwk.k === undefined) { - throw new DOMException( - "'k' property of JsonWebKey must be present", - "DataError", - ); - } - // 4. - const { rawData } = ops.op_crypto_import_key( - { algorithm: "HMAC" }, - { jwkSecret: jwk }, + // 7. + if ( + keyUsages.length > 0 && jwk.use !== undefined && jwk.use !== "sig" + ) { + throw new DOMException( + "'use' property of JsonWebKey must be 'sig'", + "DataError", ); - data = rawData.data; - - // 5. - hash = normalizedAlgorithm.hash; - - // 6. - switch (hash.name) { - case "SHA-1": { - if (jwk.alg !== undefined && jwk.alg !== "HS1") { - throw new DOMException( - "'alg' property of JsonWebKey must be 'HS1'", - "DataError", - ); - } - break; - } - case "SHA-256": { - if (jwk.alg !== undefined && jwk.alg !== "HS256") { - throw new DOMException( - "'alg' property of JsonWebKey must be 'HS256'", - "DataError", - ); - } - break; - } - case "SHA-384": { - if (jwk.alg !== undefined && jwk.alg !== "HS384") { - throw new DOMException( - "'alg' property of JsonWebKey must be 'HS384'", - "DataError", - ); - } - break; - } - case "SHA-512": { - if (jwk.alg !== undefined && jwk.alg !== "HS512") { - throw new DOMException( - "'alg' property of JsonWebKey must be 'HS512'", - "DataError", - ); - } - break; - } - default: - throw new TypeError("unreachable"); - } + } - // 7. + // 8. + // Section 4.3 of RFC7517 + if (jwk.key_ops !== undefined) { if ( - keyUsages.length > 0 && jwk.use !== undefined && jwk.use !== "sig" + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined ) { throw new DOMException( - "'use' property of JsonWebKey must be 'sig'", + "'key_ops' property of JsonWebKey is invalid", "DataError", ); } - // 8. - // Section 4.3 of RFC7517 - if (jwk.key_ops !== undefined) { - if ( - ArrayPrototypeFind( - jwk.key_ops, - (u) => !ArrayPrototypeIncludes(recognisedUsages, u), - ) !== undefined - ) { - throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", - "DataError", - ); - } - - if ( - !ArrayPrototypeEvery( - jwk.key_ops, - (u) => ArrayPrototypeIncludes(keyUsages, u), - ) - ) { - throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", - "DataError", - ); - } - } - - // 9. - if (jwk.ext === false && extractable === true) { + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { throw new DOMException( - "'ext' property of JsonWebKey must not be false if extractable is true", + "'key_ops' property of JsonWebKey is invalid", "DataError", ); } + } - break; + // 9. + if (jwk.ext === false && extractable === true) { + throw new DOMException( + "'ext' property of JsonWebKey must not be false if extractable is true", + "DataError", + ); } - default: - throw new DOMException("Not implemented", "NotSupportedError"); + + break; } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } - // 5. - let length = data.byteLength * 8; - // 6. - if (length === 0) { - throw new DOMException("Key length is zero", "DataError"); + // 5. + let length = data.byteLength * 8; + // 6. + if (length === 0) { + throw new DOMException("Key length is zero", "DataError"); + } + // 7. + if (normalizedAlgorithm.length !== undefined) { + if ( + normalizedAlgorithm.length > length || + normalizedAlgorithm.length <= (length - 8) + ) { + throw new DOMException( + "Key length is invalid", + "DataError", + ); } - // 7. - if (normalizedAlgorithm.length !== undefined) { + length = normalizedAlgorithm.length; + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "secret", + data, + }); + + const algorithm = { + name: "HMAC", + length, + hash, + }; + + const key = constructKey( + "secret", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; +} + +function importKeyEC( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, +) { + const supportedUsages = SUPPORTED_KEY_USAGES[normalizedAlgorithm.name]; + + switch (format) { + case "raw": { + // 1. if ( - normalizedAlgorithm.length > length || - normalizedAlgorithm.length <= (length - 8) + !ArrayPrototypeIncludes( + supportedNamedCurves, + normalizedAlgorithm.namedCurve, + ) ) { throw new DOMException( - "Key length is invalid", + "Invalid namedCurve", "DataError", ); } - length = normalizedAlgorithm.length; - } - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "secret", - data, - }); + // 2. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].public, + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - const algorithm = { - name: "HMAC", - length, - hash, - }; + // 3. + const { rawData } = ops.op_crypto_import_key({ + algorithm: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }, { raw: keyData }); - const key = constructKey( - "secret", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); - return key; - } + // 4-5. + const algorithm = { + name: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }; + + // 6-8. + const key = constructKey( + "public", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); - function importKeyEC( - format, - normalizedAlgorithm, - keyData, - extractable, - keyUsages, - ) { - const supportedUsages = SUPPORTED_KEY_USAGES[normalizedAlgorithm.name]; + return key; + } + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].private, + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - switch (format) { - case "raw": { - // 1. - if ( - !ArrayPrototypeIncludes( - supportedNamedCurves, - normalizedAlgorithm.namedCurve, - ) - ) { - throw new DOMException( - "Invalid namedCurve", - "DataError", - ); - } + // 2-9. + const { rawData } = ops.op_crypto_import_key({ + algorithm: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }, { pkcs8: keyData }); - // 2. + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); + + const algorithm = { + name: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }; + + const key = constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + case "spki": { + // 1. + if (normalizedAlgorithm.name == "ECDSA") { if ( ArrayPrototypeFind( keyUsages, @@ -3016,53 +3094,159 @@ ) { throw new DOMException("Invalid key usages", "SyntaxError"); } + } else if (keyUsages.length != 0) { + throw new DOMException("Key usage must be empty", "SyntaxError"); + } - // 3. - const { rawData } = ops.op_crypto_import_key({ - algorithm: normalizedAlgorithm.name, - namedCurve: normalizedAlgorithm.namedCurve, - }, { raw: keyData }); + // 2-12 + const { rawData } = ops.op_crypto_import_key({ + algorithm: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }, { spki: keyData }); - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, rawData); + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); - // 4-5. - const algorithm = { - name: normalizedAlgorithm.name, - namedCurve: normalizedAlgorithm.namedCurve, - }; + const algorithm = { + name: normalizedAlgorithm.name, + namedCurve: normalizedAlgorithm.namedCurve, + }; - // 6-8. - const key = constructKey( - "public", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, + // 6-8. + const key = constructKey( + "public", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + case "jwk": { + const jwk = keyData; + + const keyType = (jwk.d !== undefined) ? "private" : "public"; + + // 2. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(supportedUsages[keyType], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + // 3. + if (jwk.kty !== "EC") { + throw new DOMException( + "'kty' property of JsonWebKey must be 'EC'", + "DataError", ); + } - return key; + // 4. + if ( + keyUsages.length > 0 && jwk.use !== undefined && + jwk.use !== supportedUsages.jwkUse + ) { + throw new DOMException( + `'use' property of JsonWebKey must be '${supportedUsages.jwkUse}'`, + "DataError", + ); } - case "pkcs8": { - // 1. + + // 5. + // Section 4.3 of RFC7517 + if (jwk.key_ops !== undefined) { if ( ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].private, - u, - ), + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException( + "'key_ops' member of JsonWebKey is invalid", + "DataError", + ); + } + + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "'key_ops' member of JsonWebKey is invalid", + "DataError", + ); + } + } + + // 6. + if (jwk.ext === false && extractable === true) { + throw new DOMException( + "'ext' property of JsonWebKey must not be false if extractable is true", + "DataError", + ); + } + + // 9. + if (jwk.alg !== undefined && normalizedAlgorithm.name == "ECDSA") { + let algNamedCurve; + + switch (jwk.alg) { + case "ES256": { + algNamedCurve = "P-256"; + break; + } + case "ES384": { + algNamedCurve = "P-384"; + break; + } + case "ES512": { + algNamedCurve = "P-521"; + break; + } + default: + throw new DOMException( + "Curve algorithm not supported", + "DataError", + ); } - // 2-9. + if (algNamedCurve) { + if (algNamedCurve !== normalizedAlgorithm.namedCurve) { + throw new DOMException( + "Mismatched curve algorithm", + "DataError", + ); + } + } + } + + // Validate that this is a valid public key. + if (jwk.x === undefined) { + throw new DOMException( + "'x' property of JsonWebKey is required for EC keys", + "DataError", + ); + } + if (jwk.y === undefined) { + throw new DOMException( + "'y' property of JsonWebKey is required for EC keys", + "DataError", + ); + } + + if (jwk.d !== undefined) { + // it's also a Private key const { rawData } = ops.op_crypto_import_key({ algorithm: normalizedAlgorithm.name, namedCurve: normalizedAlgorithm.namedCurve, - }, { pkcs8: keyData }); + }, { jwkPrivateEc: jwk }); const handle = {}; WeakMapPrototypeSet(KEY_STORE, handle, rawData); @@ -3081,31 +3265,11 @@ ); return key; - } - case "spki": { - // 1. - if (normalizedAlgorithm.name == "ECDSA") { - if ( - ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].public, - u, - ), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - } else if (keyUsages.length != 0) { - throw new DOMException("Key usage must be empty", "SyntaxError"); - } - - // 2-12 + } else { const { rawData } = ops.op_crypto_import_key({ algorithm: normalizedAlgorithm.name, namedCurve: normalizedAlgorithm.namedCurve, - }, { spki: keyData }); + }, { jwkPublicEc: jwk }); const handle = {}; WeakMapPrototypeSet(KEY_STORE, handle, rawData); @@ -3115,7 +3279,6 @@ namedCurve: normalizedAlgorithm.namedCurve, }; - // 6-8. const key = constructKey( "public", extractable, @@ -3126,238 +3289,367 @@ return key; } - case "jwk": { - const jwk = keyData; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} + +const SUPPORTED_KEY_USAGES = { + "RSASSA-PKCS1-v1_5": { + public: ["verify"], + private: ["sign"], + jwkUse: "sig", + }, + "RSA-PSS": { + public: ["verify"], + private: ["sign"], + jwkUse: "sig", + }, + "RSA-OAEP": { + public: ["encrypt", "wrapKey"], + private: ["decrypt", "unwrapKey"], + jwkUse: "enc", + }, + "ECDSA": { + public: ["verify"], + private: ["sign"], + jwkUse: "sig", + }, + "ECDH": { + public: [], + private: ["deriveKey", "deriveBits"], + jwkUse: "enc", + }, +}; + +function importKeyRSA( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, +) { + switch (format) { + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].private, + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - const keyType = (jwk.d !== undefined) ? "private" : "public"; + // 2-9. + const { modulusLength, publicExponent, rawData } = ops + .op_crypto_import_key( + { + algorithm: normalizedAlgorithm.name, + // Needed to perform step 7 without normalization. + hash: normalizedAlgorithm.hash.name, + }, + { pkcs8: keyData }, + ); - // 2. + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); + + const algorithm = { + name: normalizedAlgorithm.name, + modulusLength, + publicExponent, + hash: normalizedAlgorithm.hash, + }; + + const key = constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + case "spki": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].public, + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + // 2-9. + const { modulusLength, publicExponent, rawData } = ops + .op_crypto_import_key( + { + algorithm: normalizedAlgorithm.name, + // Needed to perform step 7 without normalization. + hash: normalizedAlgorithm.hash.name, + }, + { spki: keyData }, + ); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, rawData); + + const algorithm = { + name: normalizedAlgorithm.name, + modulusLength, + publicExponent, + hash: normalizedAlgorithm.hash, + }; + + const key = constructKey( + "public", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + case "jwk": { + // 1. + const jwk = keyData; + + // 2. + if (jwk.d !== undefined) { if ( ArrayPrototypeFind( keyUsages, - (u) => !ArrayPrototypeIncludes(supportedUsages[keyType], u), + (u) => + !ArrayPrototypeIncludes( + SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].private, + u, + ), ) !== undefined ) { throw new DOMException("Invalid key usages", "SyntaxError"); } + } else if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].public, + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - // 3. - if (jwk.kty !== "EC") { + // 3. + if (StringPrototypeToUpperCase(jwk.kty) !== "RSA") { + throw new DOMException( + "'kty' property of JsonWebKey must be 'RSA'", + "DataError", + ); + } + + // 4. + if ( + keyUsages.length > 0 && jwk.use !== undefined && + StringPrototypeToLowerCase(jwk.use) !== + SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].jwkUse + ) { + throw new DOMException( + `'use' property of JsonWebKey must be '${ + SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].jwkUse + }'`, + "DataError", + ); + } + + // 5. + if (jwk.key_ops !== undefined) { + if ( + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined + ) { throw new DOMException( - "'kty' property of JsonWebKey must be 'EC'", + "'key_ops' property of JsonWebKey is invalid", "DataError", ); } - // 4. if ( - keyUsages.length > 0 && jwk.use !== undefined && - jwk.use !== supportedUsages.jwkUse + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) ) { throw new DOMException( - `'use' property of JsonWebKey must be '${supportedUsages.jwkUse}'`, + "'key_ops' property of JsonWebKey is invalid", "DataError", ); } + } - // 5. - // Section 4.3 of RFC7517 - if (jwk.key_ops !== undefined) { - if ( - ArrayPrototypeFind( - jwk.key_ops, - (u) => !ArrayPrototypeIncludes(recognisedUsages, u), - ) !== undefined - ) { + if (jwk.ext === false && extractable === true) { + throw new DOMException( + "'ext' property of JsonWebKey must not be false if extractable is true", + "DataError", + ); + } + + // 7. + let hash; + + // 8. + if (normalizedAlgorithm.name === "RSASSA-PKCS1-v1_5") { + switch (jwk.alg) { + case undefined: + hash = undefined; + break; + case "RS1": + hash = "SHA-1"; + break; + case "RS256": + hash = "SHA-256"; + break; + case "RS384": + hash = "SHA-384"; + break; + case "RS512": + hash = "SHA-512"; + break; + default: throw new DOMException( - "'key_ops' member of JsonWebKey is invalid", + `'alg' property of JsonWebKey must be one of 'RS1', 'RS256', 'RS384', 'RS512'`, "DataError", ); - } - - if ( - !ArrayPrototypeEvery( - jwk.key_ops, - (u) => ArrayPrototypeIncludes(keyUsages, u), - ) - ) { + } + } else if (normalizedAlgorithm.name === "RSA-PSS") { + switch (jwk.alg) { + case undefined: + hash = undefined; + break; + case "PS1": + hash = "SHA-1"; + break; + case "PS256": + hash = "SHA-256"; + break; + case "PS384": + hash = "SHA-384"; + break; + case "PS512": + hash = "SHA-512"; + break; + default: throw new DOMException( - "'key_ops' member of JsonWebKey is invalid", + `'alg' property of JsonWebKey must be one of 'PS1', 'PS256', 'PS384', 'PS512'`, "DataError", ); - } } - - // 6. - if (jwk.ext === false && extractable === true) { - throw new DOMException( - "'ext' property of JsonWebKey must not be false if extractable is true", - "DataError", - ); + } else { + switch (jwk.alg) { + case undefined: + hash = undefined; + break; + case "RSA-OAEP": + hash = "SHA-1"; + break; + case "RSA-OAEP-256": + hash = "SHA-256"; + break; + case "RSA-OAEP-384": + hash = "SHA-384"; + break; + case "RSA-OAEP-512": + hash = "SHA-512"; + break; + default: + throw new DOMException( + `'alg' property of JsonWebKey must be one of 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', or 'RSA-OAEP-512'`, + "DataError", + ); } + } - // 9. - if (jwk.alg !== undefined && normalizedAlgorithm.name == "ECDSA") { - let algNamedCurve; - - switch (jwk.alg) { - case "ES256": { - algNamedCurve = "P-256"; - break; - } - case "ES384": { - algNamedCurve = "P-384"; - break; - } - case "ES512": { - algNamedCurve = "P-521"; - break; - } - default: - throw new DOMException( - "Curve algorithm not supported", - "DataError", - ); - } - - if (algNamedCurve) { - if (algNamedCurve !== normalizedAlgorithm.namedCurve) { - throw new DOMException( - "Mismatched curve algorithm", - "DataError", - ); - } - } - } + // 9. + if (hash !== undefined) { + // 9.1. + const normalizedHash = normalizeAlgorithm(hash, "digest"); - // Validate that this is a valid public key. - if (jwk.x === undefined) { - throw new DOMException( - "'x' property of JsonWebKey is required for EC keys", - "DataError", - ); - } - if (jwk.y === undefined) { + // 9.2. + if (normalizedHash.name !== normalizedAlgorithm.hash.name) { throw new DOMException( - "'y' property of JsonWebKey is required for EC keys", + `'alg' property of JsonWebKey must be '${normalizedAlgorithm.name}'`, "DataError", ); } + } - if (jwk.d !== undefined) { - // it's also a Private key - const { rawData } = ops.op_crypto_import_key({ - algorithm: normalizedAlgorithm.name, - namedCurve: normalizedAlgorithm.namedCurve, - }, { jwkPrivateEc: jwk }); - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, rawData); - - const algorithm = { - name: normalizedAlgorithm.name, - namedCurve: normalizedAlgorithm.namedCurve, - }; - - const key = constructKey( - "private", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); - - return key; + // 10. + if (jwk.d !== undefined) { + // Private key + const optimizationsPresent = jwk.p !== undefined || + jwk.q !== undefined || jwk.dp !== undefined || + jwk.dq !== undefined || jwk.qi !== undefined; + if (optimizationsPresent) { + if (jwk.q === undefined) { + throw new DOMException( + "'q' property of JsonWebKey is required for private keys", + "DataError", + ); + } + if (jwk.dp === undefined) { + throw new DOMException( + "'dp' property of JsonWebKey is required for private keys", + "DataError", + ); + } + if (jwk.dq === undefined) { + throw new DOMException( + "'dq' property of JsonWebKey is required for private keys", + "DataError", + ); + } + if (jwk.qi === undefined) { + throw new DOMException( + "'qi' property of JsonWebKey is required for private keys", + "DataError", + ); + } + if (jwk.oth !== undefined) { + throw new DOMException( + "'oth' property of JsonWebKey is not supported", + "NotSupportedError", + ); + } } else { - const { rawData } = ops.op_crypto_import_key({ - algorithm: normalizedAlgorithm.name, - namedCurve: normalizedAlgorithm.namedCurve, - }, { jwkPublicEc: jwk }); - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, rawData); - - const algorithm = { - name: normalizedAlgorithm.name, - namedCurve: normalizedAlgorithm.namedCurve, - }; - - const key = constructKey( - "public", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, + throw new DOMException( + "only optimized private keys are supported", + "NotSupportedError", ); - - return key; - } - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - } - - const SUPPORTED_KEY_USAGES = { - "RSASSA-PKCS1-v1_5": { - public: ["verify"], - private: ["sign"], - jwkUse: "sig", - }, - "RSA-PSS": { - public: ["verify"], - private: ["sign"], - jwkUse: "sig", - }, - "RSA-OAEP": { - public: ["encrypt", "wrapKey"], - private: ["decrypt", "unwrapKey"], - jwkUse: "enc", - }, - "ECDSA": { - public: ["verify"], - private: ["sign"], - jwkUse: "sig", - }, - "ECDH": { - public: [], - private: ["deriveKey", "deriveBits"], - jwkUse: "enc", - }, - }; - - function importKeyRSA( - format, - normalizedAlgorithm, - keyData, - extractable, - keyUsages, - ) { - switch (format) { - case "pkcs8": { - // 1. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].private, - u, - ), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); } - // 2-9. const { modulusLength, publicExponent, rawData } = ops .op_crypto_import_key( { algorithm: normalizedAlgorithm.name, - // Needed to perform step 7 without normalization. hash: normalizedAlgorithm.hash.name, }, - { pkcs8: keyData }, + { jwkPrivateRsa: jwk }, ); const handle = {}; @@ -3379,31 +3671,28 @@ ); return key; - } - case "spki": { - // 1. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].public, - u, - ), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + } else { + // Validate that this is a valid public key. + if (jwk.n === undefined) { + throw new DOMException( + "'n' property of JsonWebKey is required for public keys", + "DataError", + ); + } + if (jwk.e === undefined) { + throw new DOMException( + "'e' property of JsonWebKey is required for public keys", + "DataError", + ); } - // 2-9. const { modulusLength, publicExponent, rawData } = ops .op_crypto_import_key( { algorithm: normalizedAlgorithm.name, - // Needed to perform step 7 without normalization. hash: normalizedAlgorithm.hash.name, }, - { spki: keyData }, + { jwkPublicRsa: jwk }, ); const handle = {}; @@ -3426,453 +3715,241 @@ return key; } - case "jwk": { - // 1. - const jwk = keyData; - - // 2. - if (jwk.d !== undefined) { - if ( - ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].private, - u, - ), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - } else if ( - ArrayPrototypeFind( - keyUsages, - (u) => - !ArrayPrototypeIncludes( - SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].public, - u, - ), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } - - // 3. - if (StringPrototypeToUpperCase(jwk.kty) !== "RSA") { - throw new DOMException( - "'kty' property of JsonWebKey must be 'RSA'", - "DataError", - ); - } - - // 4. - if ( - keyUsages.length > 0 && jwk.use !== undefined && - StringPrototypeToLowerCase(jwk.use) !== - SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].jwkUse - ) { - throw new DOMException( - `'use' property of JsonWebKey must be '${ - SUPPORTED_KEY_USAGES[normalizedAlgorithm.name].jwkUse - }'`, - "DataError", - ); - } - - // 5. - if (jwk.key_ops !== undefined) { - if ( - ArrayPrototypeFind( - jwk.key_ops, - (u) => !ArrayPrototypeIncludes(recognisedUsages, u), - ) !== undefined - ) { - throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", - "DataError", - ); - } - - if ( - !ArrayPrototypeEvery( - jwk.key_ops, - (u) => ArrayPrototypeIncludes(keyUsages, u), - ) - ) { - throw new DOMException( - "'key_ops' property of JsonWebKey is invalid", - "DataError", - ); - } - } - - if (jwk.ext === false && extractable === true) { - throw new DOMException( - "'ext' property of JsonWebKey must not be false if extractable is true", - "DataError", - ); - } - - // 7. - let hash; - - // 8. - if (normalizedAlgorithm.name === "RSASSA-PKCS1-v1_5") { - switch (jwk.alg) { - case undefined: - hash = undefined; - break; - case "RS1": - hash = "SHA-1"; - break; - case "RS256": - hash = "SHA-256"; - break; - case "RS384": - hash = "SHA-384"; - break; - case "RS512": - hash = "SHA-512"; - break; - default: - throw new DOMException( - `'alg' property of JsonWebKey must be one of 'RS1', 'RS256', 'RS384', 'RS512'`, - "DataError", - ); - } - } else if (normalizedAlgorithm.name === "RSA-PSS") { - switch (jwk.alg) { - case undefined: - hash = undefined; - break; - case "PS1": - hash = "SHA-1"; - break; - case "PS256": - hash = "SHA-256"; - break; - case "PS384": - hash = "SHA-384"; - break; - case "PS512": - hash = "SHA-512"; - break; - default: - throw new DOMException( - `'alg' property of JsonWebKey must be one of 'PS1', 'PS256', 'PS384', 'PS512'`, - "DataError", - ); - } - } else { - switch (jwk.alg) { - case undefined: - hash = undefined; - break; - case "RSA-OAEP": - hash = "SHA-1"; - break; - case "RSA-OAEP-256": - hash = "SHA-256"; - break; - case "RSA-OAEP-384": - hash = "SHA-384"; - break; - case "RSA-OAEP-512": - hash = "SHA-512"; - break; - default: - throw new DOMException( - `'alg' property of JsonWebKey must be one of 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', or 'RSA-OAEP-512'`, - "DataError", - ); - } - } - - // 9. - if (hash !== undefined) { - // 9.1. - const normalizedHash = normalizeAlgorithm(hash, "digest"); - - // 9.2. - if (normalizedHash.name !== normalizedAlgorithm.hash.name) { - throw new DOMException( - `'alg' property of JsonWebKey must be '${normalizedAlgorithm.name}'`, - "DataError", - ); - } - } - - // 10. - if (jwk.d !== undefined) { - // Private key - const optimizationsPresent = jwk.p !== undefined || - jwk.q !== undefined || jwk.dp !== undefined || - jwk.dq !== undefined || jwk.qi !== undefined; - if (optimizationsPresent) { - if (jwk.q === undefined) { - throw new DOMException( - "'q' property of JsonWebKey is required for private keys", - "DataError", - ); - } - if (jwk.dp === undefined) { - throw new DOMException( - "'dp' property of JsonWebKey is required for private keys", - "DataError", - ); - } - if (jwk.dq === undefined) { - throw new DOMException( - "'dq' property of JsonWebKey is required for private keys", - "DataError", - ); - } - if (jwk.qi === undefined) { - throw new DOMException( - "'qi' property of JsonWebKey is required for private keys", - "DataError", - ); - } - if (jwk.oth !== undefined) { - throw new DOMException( - "'oth' property of JsonWebKey is not supported", - "NotSupportedError", - ); - } - } else { - throw new DOMException( - "only optimized private keys are supported", - "NotSupportedError", - ); - } - - const { modulusLength, publicExponent, rawData } = ops - .op_crypto_import_key( - { - algorithm: normalizedAlgorithm.name, - hash: normalizedAlgorithm.hash.name, - }, - { jwkPrivateRsa: jwk }, - ); - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, rawData); - - const algorithm = { - name: normalizedAlgorithm.name, - modulusLength, - publicExponent, - hash: normalizedAlgorithm.hash, - }; - - const key = constructKey( - "private", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); - - return key; - } else { - // Validate that this is a valid public key. - if (jwk.n === undefined) { - throw new DOMException( - "'n' property of JsonWebKey is required for public keys", - "DataError", - ); - } - if (jwk.e === undefined) { - throw new DOMException( - "'e' property of JsonWebKey is required for public keys", - "DataError", - ); - } - - const { modulusLength, publicExponent, rawData } = ops - .op_crypto_import_key( - { - algorithm: normalizedAlgorithm.name, - hash: normalizedAlgorithm.hash.name, - }, - { jwkPublicRsa: jwk }, - ); - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, rawData); - - const algorithm = { - name: normalizedAlgorithm.name, - modulusLength, - publicExponent, - hash: normalizedAlgorithm.hash, - }; - - const key = constructKey( - "public", - extractable, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); - - return key; - } - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} + +function importKeyHKDF( + format, + keyData, + extractable, + keyUsages, +) { + if (format !== "raw") { + throw new DOMException("Format not supported", "NotSupportedError"); } - function importKeyHKDF( - format, - keyData, - extractable, - keyUsages, + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined ) { - if (format !== "raw") { - throw new DOMException("Format not supported", "NotSupportedError"); - } + throw new DOMException("Invalid key usages", "SyntaxError"); + } - // 1. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + // 2. + if (extractable !== false) { + throw new DOMException( + "Key must not be extractable", + "SyntaxError", + ); + } - // 2. - if (extractable !== false) { - throw new DOMException( - "Key must not be extractable", - "SyntaxError", - ); - } + // 3. + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "secret", + data: keyData, + }); - // 3. - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "secret", - data: keyData, - }); + // 4-8. + const algorithm = { + name: "HKDF", + }; + const key = constructKey( + "secret", + false, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + // 9. + return key; +} + +function importKeyPBKDF2( + format, + keyData, + extractable, + keyUsages, +) { + // 1. + if (format !== "raw") { + throw new DOMException("Format not supported", "NotSupportedError"); + } - // 4-8. - const algorithm = { - name: "HKDF", - }; - const key = constructKey( - "secret", - false, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); + // 2. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } - // 9. - return key; + // 3. + if (extractable !== false) { + throw new DOMException( + "Key must not be extractable", + "SyntaxError", + ); } - function importKeyPBKDF2( - format, - keyData, - extractable, - keyUsages, - ) { - // 1. - if (format !== "raw") { - throw new DOMException("Format not supported", "NotSupportedError"); - } + // 4. + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "secret", + data: keyData, + }); - // 2. - if ( - ArrayPrototypeFind( - keyUsages, - (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), - ) !== undefined - ) { - throw new DOMException("Invalid key usages", "SyntaxError"); - } + // 5-9. + const algorithm = { + name: "PBKDF2", + }; + const key = constructKey( + "secret", + false, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + // 10. + return key; +} + +function exportKeyHMAC(format, key, innerKey) { + // 1. + if (innerKey == null) { + throw new DOMException("Key is not available", "OperationError"); + } + switch (format) { // 3. - if (extractable !== false) { - throw new DOMException( - "Key must not be extractable", - "SyntaxError", - ); + case "raw": { + const bits = innerKey.data; + for (let _i = 7 & (8 - bits.length % 8); _i > 0; _i--) { + bits.push(0); + } + // 4-5. + return bits.buffer; } + case "jwk": { + // 1-2. + const jwk = { + kty: "oct", + }; - // 4. - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "secret", - data: keyData, - }); + // 3. + const data = ops.op_crypto_export_key({ + format: "jwksecret", + algorithm: key[_algorithm].name, + }, innerKey); + jwk.k = data.k; + + // 4. + const algorithm = key[_algorithm]; + // 5. + const hash = algorithm.hash; + // 6. + switch (hash.name) { + case "SHA-1": + jwk.alg = "HS1"; + break; + case "SHA-256": + jwk.alg = "HS256"; + break; + case "SHA-384": + jwk.alg = "HS384"; + break; + case "SHA-512": + jwk.alg = "HS512"; + break; + default: + throw new DOMException( + "Hash algorithm not supported", + "NotSupportedError", + ); + } + // 7. + jwk.key_ops = key.usages; + // 8. + jwk.ext = key[_extractable]; + // 9. + return jwk; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} - // 5-9. - const algorithm = { - name: "PBKDF2", - }; - const key = constructKey( - "secret", - false, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, - ); +function exportKeyRSA(format, key, innerKey) { + switch (format) { + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a private key", + "InvalidAccessError", + ); + } - // 10. - return key; - } + // 2. + const data = ops.op_crypto_export_key({ + algorithm: key[_algorithm].name, + format: "pkcs8", + }, innerKey); - function exportKeyHMAC(format, key, innerKey) { - // 1. - if (innerKey == null) { - throw new DOMException("Key is not available", "OperationError"); + // 3. + return data.buffer; } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + // 2. + const data = ops.op_crypto_export_key({ + algorithm: key[_algorithm].name, + format: "spki", + }, innerKey); - switch (format) { // 3. - case "raw": { - const bits = innerKey.data; - for (let _i = 7 & (8 - bits.length % 8); _i > 0; _i--) { - bits.push(0); - } - // 4-5. - return bits.buffer; - } - case "jwk": { - // 1-2. - const jwk = { - kty: "oct", - }; + return data.buffer; + } + case "jwk": { + // 1-2. + const jwk = { + kty: "RSA", + }; - // 3. - const data = ops.op_crypto_export_key({ - format: "jwksecret", - algorithm: key[_algorithm].name, - }, innerKey); - jwk.k = data.k; + // 3. + const hash = key[_algorithm].hash.name; - // 4. - const algorithm = key[_algorithm]; - // 5. - const hash = algorithm.hash; - // 6. - switch (hash.name) { + // 4. + if (key[_algorithm].name === "RSASSA-PKCS1-v1_5") { + switch (hash) { case "SHA-1": - jwk.alg = "HS1"; + jwk.alg = "RS1"; break; case "SHA-256": - jwk.alg = "HS256"; + jwk.alg = "RS256"; break; case "SHA-384": - jwk.alg = "HS384"; + jwk.alg = "RS384"; break; case "SHA-512": - jwk.alg = "HS512"; + jwk.alg = "RS512"; break; default: throw new DOMException( @@ -3880,848 +3957,763 @@ "NotSupportedError", ); } - // 7. - jwk.key_ops = key.usages; - // 8. - jwk.ext = key[_extractable]; - // 9. - return jwk; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - } - - function exportKeyRSA(format, key, innerKey) { - switch (format) { - case "pkcs8": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key is not a private key", - "InvalidAccessError", - ); + } else if (key[_algorithm].name === "RSA-PSS") { + switch (hash) { + case "SHA-1": + jwk.alg = "PS1"; + break; + case "SHA-256": + jwk.alg = "PS256"; + break; + case "SHA-384": + jwk.alg = "PS384"; + break; + case "SHA-512": + jwk.alg = "PS512"; + break; + default: + throw new DOMException( + "Hash algorithm not supported", + "NotSupportedError", + ); } - - // 2. - const data = ops.op_crypto_export_key({ - algorithm: key[_algorithm].name, - format: "pkcs8", - }, innerKey); - - // 3. - return data.buffer; - } - case "spki": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); + } else { + switch (hash) { + case "SHA-1": + jwk.alg = "RSA-OAEP"; + break; + case "SHA-256": + jwk.alg = "RSA-OAEP-256"; + break; + case "SHA-384": + jwk.alg = "RSA-OAEP-384"; + break; + case "SHA-512": + jwk.alg = "RSA-OAEP-512"; + break; + default: + throw new DOMException( + "Hash algorithm not supported", + "NotSupportedError", + ); } - - // 2. - const data = ops.op_crypto_export_key({ - algorithm: key[_algorithm].name, - format: "spki", - }, innerKey); - - // 3. - return data.buffer; } - case "jwk": { - // 1-2. - const jwk = { - kty: "RSA", - }; - // 3. - const hash = key[_algorithm].hash.name; - - // 4. - if (key[_algorithm].name === "RSASSA-PKCS1-v1_5") { - switch (hash) { - case "SHA-1": - jwk.alg = "RS1"; - break; - case "SHA-256": - jwk.alg = "RS256"; - break; - case "SHA-384": - jwk.alg = "RS384"; - break; - case "SHA-512": - jwk.alg = "RS512"; - break; - default: - throw new DOMException( - "Hash algorithm not supported", - "NotSupportedError", - ); - } - } else if (key[_algorithm].name === "RSA-PSS") { - switch (hash) { - case "SHA-1": - jwk.alg = "PS1"; - break; - case "SHA-256": - jwk.alg = "PS256"; - break; - case "SHA-384": - jwk.alg = "PS384"; - break; - case "SHA-512": - jwk.alg = "PS512"; - break; - default: - throw new DOMException( - "Hash algorithm not supported", - "NotSupportedError", - ); - } - } else { - switch (hash) { - case "SHA-1": - jwk.alg = "RSA-OAEP"; - break; - case "SHA-256": - jwk.alg = "RSA-OAEP-256"; - break; - case "SHA-384": - jwk.alg = "RSA-OAEP-384"; - break; - case "SHA-512": - jwk.alg = "RSA-OAEP-512"; - break; - default: - throw new DOMException( - "Hash algorithm not supported", - "NotSupportedError", - ); - } - } - - // 5-6. - const data = ops.op_crypto_export_key({ - format: key[_type] === "private" ? "jwkprivate" : "jwkpublic", - algorithm: key[_algorithm].name, - }, innerKey); - ObjectAssign(jwk, data); + // 5-6. + const data = ops.op_crypto_export_key({ + format: key[_type] === "private" ? "jwkprivate" : "jwkpublic", + algorithm: key[_algorithm].name, + }, innerKey); + ObjectAssign(jwk, data); - // 7. - jwk.key_ops = key.usages; + // 7. + jwk.key_ops = key.usages; - // 8. - jwk.ext = key[_extractable]; + // 8. + jwk.ext = key[_extractable]; - return jwk; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); + return jwk; } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } +} - function exportKeyEd25519(format, key, innerKey) { - switch (format) { - case "raw": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); - } - - // 2-3. - return innerKey.buffer; +function exportKeyEd25519(format, key, innerKey) { + switch (format) { + case "raw": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); } - case "spki": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); - } - const spkiDer = ops.op_export_spki_ed25519(innerKey); - return spkiDer.buffer; + // 2-3. + return innerKey.buffer; + } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); } - case "pkcs8": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); - } - const pkcs8Der = ops.op_export_pkcs8_ed25519( - new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]), + const spkiDer = ops.op_export_spki_ed25519(innerKey); + return spkiDer.buffer; + } + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", ); - pkcs8Der[15] = 0x20; - return pkcs8Der.buffer; } - case "jwk": { - const x = key[_type] === "private" - ? ops.op_jwk_x_ed25519(innerKey) - : ops.op_crypto_base64url_encode(innerKey); - const jwk = { - kty: "OKP", - alg: "EdDSA", - crv: "Ed25519", - x, - "key_ops": key.usages, - ext: key[_extractable], - }; - if (key[_type] === "private") { - jwk.d = ops.op_crypto_base64url_encode(innerKey); - } - return jwk; + + const pkcs8Der = ops.op_export_pkcs8_ed25519( + new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]), + ); + pkcs8Der[15] = 0x20; + return pkcs8Der.buffer; + } + case "jwk": { + const x = key[_type] === "private" + ? ops.op_jwk_x_ed25519(innerKey) + : ops.op_crypto_base64url_encode(innerKey); + const jwk = { + kty: "OKP", + alg: "EdDSA", + crv: "Ed25519", + x, + "key_ops": key.usages, + ext: key[_extractable], + }; + if (key[_type] === "private") { + jwk.d = ops.op_crypto_base64url_encode(innerKey); } - default: - throw new DOMException("Not implemented", "NotSupportedError"); + return jwk; } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } +} - function exportKeyX25519(format, key, innerKey) { - switch (format) { - case "raw": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); - } - - // 2-3. - return innerKey.buffer; +function exportKeyX25519(format, key, innerKey) { + switch (format) { + case "raw": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); } - case "spki": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); - } - const spkiDer = ops.op_export_spki_x25519(innerKey); - return spkiDer.buffer; + // 2-3. + return innerKey.buffer; + } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); } - case "pkcs8": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); - } - const pkcs8Der = ops.op_export_pkcs8_x25519( - new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]), + const spkiDer = ops.op_export_spki_x25519(innerKey); + return spkiDer.buffer; + } + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", ); - pkcs8Der[15] = 0x20; - return pkcs8Der.buffer; - } - case "jwk": { - if (key[_type] === "private") { - throw new DOMException("Not implemented", "NotSupportedError"); - } - const x = ops.op_crypto_base64url_encode(innerKey); - const jwk = { - kty: "OKP", - crv: "X25519", - x, - "key_ops": key.usages, - ext: key[_extractable], - }; - return jwk; } - default: + + const pkcs8Der = ops.op_export_pkcs8_x25519( + new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]), + ); + pkcs8Der[15] = 0x20; + return pkcs8Der.buffer; + } + case "jwk": { + if (key[_type] === "private") { throw new DOMException("Not implemented", "NotSupportedError"); + } + const x = ops.op_crypto_base64url_encode(innerKey); + const jwk = { + kty: "OKP", + crv: "X25519", + x, + "key_ops": key.usages, + ext: key[_extractable], + }; + return jwk; } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } +} - function exportKeyEC(format, key, innerKey) { - switch (format) { - case "raw": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); - } +function exportKeyEC(format, key, innerKey) { + switch (format) { + case "raw": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } - // 2. - const data = ops.op_crypto_export_key({ - algorithm: key[_algorithm].name, - namedCurve: key[_algorithm].namedCurve, - format: "raw", - }, innerKey); + // 2. + const data = ops.op_crypto_export_key({ + algorithm: key[_algorithm].name, + namedCurve: key[_algorithm].namedCurve, + format: "raw", + }, innerKey); - return data.buffer; + return data.buffer; + } + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a private key", + "InvalidAccessError", + ); } - case "pkcs8": { - // 1. - if (key[_type] !== "private") { - throw new DOMException( - "Key is not a private key", - "InvalidAccessError", - ); - } - // 2. - const data = ops.op_crypto_export_key({ - algorithm: key[_algorithm].name, - namedCurve: key[_algorithm].namedCurve, - format: "pkcs8", - }, innerKey); + // 2. + const data = ops.op_crypto_export_key({ + algorithm: key[_algorithm].name, + namedCurve: key[_algorithm].namedCurve, + format: "pkcs8", + }, innerKey); - return data.buffer; + return data.buffer; + } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); } - case "spki": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key is not a public key", - "InvalidAccessError", - ); + + // 2. + const data = ops.op_crypto_export_key({ + algorithm: key[_algorithm].name, + namedCurve: key[_algorithm].namedCurve, + format: "spki", + }, innerKey); + + return data.buffer; + } + case "jwk": { + if (key[_algorithm].name == "ECDSA") { + // 1-2. + const jwk = { + kty: "EC", + }; + + // 3.1 + jwk.crv = key[_algorithm].namedCurve; + + // Missing from spec + let algNamedCurve; + + switch (key[_algorithm].namedCurve) { + case "P-256": { + algNamedCurve = "ES256"; + break; + } + case "P-384": { + algNamedCurve = "ES384"; + break; + } + case "P-521": { + algNamedCurve = "ES512"; + break; + } + default: + throw new DOMException( + "Curve algorithm not supported", + "DataError", + ); } - // 2. + jwk.alg = algNamedCurve; + + // 3.2 - 3.4. const data = ops.op_crypto_export_key({ + format: key[_type] === "private" ? "jwkprivate" : "jwkpublic", algorithm: key[_algorithm].name, namedCurve: key[_algorithm].namedCurve, - format: "spki", }, innerKey); + ObjectAssign(jwk, data); - return data.buffer; - } - case "jwk": { - if (key[_algorithm].name == "ECDSA") { - // 1-2. - const jwk = { - kty: "EC", - }; - - // 3.1 - jwk.crv = key[_algorithm].namedCurve; - - // Missing from spec - let algNamedCurve; - - switch (key[_algorithm].namedCurve) { - case "P-256": { - algNamedCurve = "ES256"; - break; - } - case "P-384": { - algNamedCurve = "ES384"; - break; - } - case "P-521": { - algNamedCurve = "ES512"; - break; - } - default: - throw new DOMException( - "Curve algorithm not supported", - "DataError", - ); - } - - jwk.alg = algNamedCurve; - - // 3.2 - 3.4. - const data = ops.op_crypto_export_key({ - format: key[_type] === "private" ? "jwkprivate" : "jwkpublic", - algorithm: key[_algorithm].name, - namedCurve: key[_algorithm].namedCurve, - }, innerKey); - ObjectAssign(jwk, data); - - // 4. - jwk.key_ops = key.usages; + // 4. + jwk.key_ops = key.usages; - // 5. - jwk.ext = key[_extractable]; + // 5. + jwk.ext = key[_extractable]; - return jwk; - } else { // ECDH - // 1-2. - const jwk = { - kty: "EC", - }; + return jwk; + } else { // ECDH + // 1-2. + const jwk = { + kty: "EC", + }; - // missing step from spec - jwk.alg = "ECDH"; + // missing step from spec + jwk.alg = "ECDH"; - // 3.1 - jwk.crv = key[_algorithm].namedCurve; + // 3.1 + jwk.crv = key[_algorithm].namedCurve; - // 3.2 - 3.4 - const data = ops.op_crypto_export_key({ - format: key[_type] === "private" ? "jwkprivate" : "jwkpublic", - algorithm: key[_algorithm].name, - namedCurve: key[_algorithm].namedCurve, - }, innerKey); - ObjectAssign(jwk, data); + // 3.2 - 3.4 + const data = ops.op_crypto_export_key({ + format: key[_type] === "private" ? "jwkprivate" : "jwkpublic", + algorithm: key[_algorithm].name, + namedCurve: key[_algorithm].namedCurve, + }, innerKey); + ObjectAssign(jwk, data); - // 4. - jwk.key_ops = key.usages; + // 4. + jwk.key_ops = key.usages; - // 5. - jwk.ext = key[_extractable]; + // 5. + jwk.ext = key[_extractable]; - return jwk; - } + return jwk; } - default: - throw new DOMException("Not implemented", "NotSupportedError"); } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } +} - async function generateKeyAES(normalizedAlgorithm, extractable, usages) { - const algorithmName = normalizedAlgorithm.name; - - // 2. - if (!ArrayPrototypeIncludes([128, 192, 256], normalizedAlgorithm.length)) { - throw new DOMException("Invalid key length", "OperationError"); - } +async function generateKeyAES(normalizedAlgorithm, extractable, usages) { + const algorithmName = normalizedAlgorithm.name; - // 3. - const keyData = await core.opAsync("op_crypto_generate_key", { - algorithm: "AES", - length: normalizedAlgorithm.length, - }); - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "secret", - data: keyData, - }); + // 2. + if (!ArrayPrototypeIncludes([128, 192, 256], normalizedAlgorithm.length)) { + throw new DOMException("Invalid key length", "OperationError"); + } - // 6-8. - const algorithm = { - name: algorithmName, - length: normalizedAlgorithm.length, - }; + // 3. + const keyData = await core.opAsync("op_crypto_generate_key", { + algorithm: "AES", + length: normalizedAlgorithm.length, + }); + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "secret", + data: keyData, + }); + + // 6-8. + const algorithm = { + name: algorithmName, + length: normalizedAlgorithm.length, + }; - // 9-11. - const key = constructKey( - "secret", - extractable, - usages, - algorithm, - handle, - ); + // 9-11. + const key = constructKey( + "secret", + extractable, + usages, + algorithm, + handle, + ); + + // 12. + return key; +} + +async function deriveBits(normalizedAlgorithm, baseKey, length) { + switch (normalizedAlgorithm.name) { + case "PBKDF2": { + // 1. + if (length == null || length == 0 || length % 8 !== 0) { + throw new DOMException("Invalid length", "OperationError"); + } - // 12. - return key; - } + if (normalizedAlgorithm.iterations == 0) { + throw new DOMException( + "iterations must not be zero", + "OperationError", + ); + } - async function deriveBits(normalizedAlgorithm, baseKey, length) { - switch (normalizedAlgorithm.name) { - case "PBKDF2": { - // 1. - if (length == null || length == 0 || length % 8 !== 0) { - throw new DOMException("Invalid length", "OperationError"); - } + const handle = baseKey[_handle]; + const keyData = WeakMapPrototypeGet(KEY_STORE, handle); - if (normalizedAlgorithm.iterations == 0) { - throw new DOMException( - "iterations must not be zero", - "OperationError", - ); - } + normalizedAlgorithm.salt = copyBuffer(normalizedAlgorithm.salt); - const handle = baseKey[_handle]; - const keyData = WeakMapPrototypeGet(KEY_STORE, handle); + const buf = await core.opAsync("op_crypto_derive_bits", { + key: keyData, + algorithm: "PBKDF2", + hash: normalizedAlgorithm.hash.name, + iterations: normalizedAlgorithm.iterations, + length, + }, normalizedAlgorithm.salt); - normalizedAlgorithm.salt = copyBuffer(normalizedAlgorithm.salt); + return buf.buffer; + } + case "ECDH": { + // 1. + if (baseKey[_type] !== "private") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 2. + const publicKey = normalizedAlgorithm.public; + // 3. + if (publicKey[_type] !== "public") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 4. + if (publicKey[_algorithm].name !== baseKey[_algorithm].name) { + throw new DOMException( + "Algorithm mismatch", + "InvalidAccessError", + ); + } + // 5. + if ( + publicKey[_algorithm].namedCurve !== baseKey[_algorithm].namedCurve + ) { + throw new DOMException( + "namedCurve mismatch", + "InvalidAccessError", + ); + } + // 6. + if ( + ArrayPrototypeIncludes( + supportedNamedCurves, + publicKey[_algorithm].namedCurve, + ) + ) { + const baseKeyhandle = baseKey[_handle]; + const baseKeyData = WeakMapPrototypeGet(KEY_STORE, baseKeyhandle); + const publicKeyhandle = publicKey[_handle]; + const publicKeyData = WeakMapPrototypeGet(KEY_STORE, publicKeyhandle); const buf = await core.opAsync("op_crypto_derive_bits", { - key: keyData, - algorithm: "PBKDF2", - hash: normalizedAlgorithm.hash.name, - iterations: normalizedAlgorithm.iterations, + key: baseKeyData, + publicKey: publicKeyData, + algorithm: "ECDH", + namedCurve: publicKey[_algorithm].namedCurve, length, - }, normalizedAlgorithm.salt); + }); - return buf.buffer; - } - case "ECDH": { - // 1. - if (baseKey[_type] !== "private") { - throw new DOMException("Invalid key type", "InvalidAccessError"); - } - // 2. - const publicKey = normalizedAlgorithm.public; - // 3. - if (publicKey[_type] !== "public") { - throw new DOMException("Invalid key type", "InvalidAccessError"); - } - // 4. - if (publicKey[_algorithm].name !== baseKey[_algorithm].name) { - throw new DOMException( - "Algorithm mismatch", - "InvalidAccessError", - ); - } - // 5. - if ( - publicKey[_algorithm].namedCurve !== baseKey[_algorithm].namedCurve - ) { - throw new DOMException( - "namedCurve mismatch", - "InvalidAccessError", - ); - } - // 6. - if ( - ArrayPrototypeIncludes( - supportedNamedCurves, - publicKey[_algorithm].namedCurve, - ) - ) { - const baseKeyhandle = baseKey[_handle]; - const baseKeyData = WeakMapPrototypeGet(KEY_STORE, baseKeyhandle); - const publicKeyhandle = publicKey[_handle]; - const publicKeyData = WeakMapPrototypeGet(KEY_STORE, publicKeyhandle); - - const buf = await core.opAsync("op_crypto_derive_bits", { - key: baseKeyData, - publicKey: publicKeyData, - algorithm: "ECDH", - namedCurve: publicKey[_algorithm].namedCurve, - length, - }); - - // 8. - if (length === null) { - return buf.buffer; - } else if (buf.buffer.byteLength * 8 < length) { - throw new DOMException("Invalid length", "OperationError"); - } else { - return buf.buffer.slice(0, MathCeil(length / 8)); - } + // 8. + if (length === null) { + return buf.buffer; + } else if (buf.buffer.byteLength * 8 < length) { + throw new DOMException("Invalid length", "OperationError"); } else { - throw new DOMException("Not implemented", "NotSupportedError"); + return buf.buffer.slice(0, MathCeil(length / 8)); } + } else { + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + case "HKDF": { + // 1. + if (length === null || length === 0 || length % 8 !== 0) { + throw new DOMException("Invalid length", "OperationError"); } - case "HKDF": { - // 1. - if (length === null || length === 0 || length % 8 !== 0) { - throw new DOMException("Invalid length", "OperationError"); - } - const handle = baseKey[_handle]; - const keyDerivationKey = WeakMapPrototypeGet(KEY_STORE, handle); + const handle = baseKey[_handle]; + const keyDerivationKey = WeakMapPrototypeGet(KEY_STORE, handle); - normalizedAlgorithm.salt = copyBuffer(normalizedAlgorithm.salt); + normalizedAlgorithm.salt = copyBuffer(normalizedAlgorithm.salt); - normalizedAlgorithm.info = copyBuffer(normalizedAlgorithm.info); + normalizedAlgorithm.info = copyBuffer(normalizedAlgorithm.info); - const buf = await core.opAsync("op_crypto_derive_bits", { - key: keyDerivationKey, - algorithm: "HKDF", - hash: normalizedAlgorithm.hash.name, - info: normalizedAlgorithm.info, - length, - }, normalizedAlgorithm.salt); + const buf = await core.opAsync("op_crypto_derive_bits", { + key: keyDerivationKey, + algorithm: "HKDF", + hash: normalizedAlgorithm.hash.name, + info: normalizedAlgorithm.info, + length, + }, normalizedAlgorithm.salt); - return buf.buffer; + return buf.buffer; + } + case "X25519": { + // 1. + if (baseKey[_type] !== "private") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 2. + const publicKey = normalizedAlgorithm.public; + // 3. + if (publicKey[_type] !== "public") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 4. + if (publicKey[_algorithm].name !== baseKey[_algorithm].name) { + throw new DOMException( + "Algorithm mismatch", + "InvalidAccessError", + ); } - case "X25519": { - // 1. - if (baseKey[_type] !== "private") { - throw new DOMException("Invalid key type", "InvalidAccessError"); - } - // 2. - const publicKey = normalizedAlgorithm.public; - // 3. - if (publicKey[_type] !== "public") { - throw new DOMException("Invalid key type", "InvalidAccessError"); - } - // 4. - if (publicKey[_algorithm].name !== baseKey[_algorithm].name) { - throw new DOMException( - "Algorithm mismatch", - "InvalidAccessError", - ); - } - // 5. - const kHandle = baseKey[_handle]; - const k = WeakMapPrototypeGet(KEY_STORE, kHandle); + // 5. + const kHandle = baseKey[_handle]; + const k = WeakMapPrototypeGet(KEY_STORE, kHandle); - const uHandle = publicKey[_handle]; - const u = WeakMapPrototypeGet(KEY_STORE, uHandle); + const uHandle = publicKey[_handle]; + const u = WeakMapPrototypeGet(KEY_STORE, uHandle); - const secret = new Uint8Array(32); - const isIdentity = ops.op_derive_bits_x25519(k, u, secret); + const secret = new Uint8Array(32); + const isIdentity = ops.op_derive_bits_x25519(k, u, secret); - // 6. - if (isIdentity) { - throw new DOMException("Invalid key", "OperationError"); - } + // 6. + if (isIdentity) { + throw new DOMException("Invalid key", "OperationError"); + } - // 7. - if (length === null) { - return secret.buffer; - } else if ( - secret.buffer.byteLength * 8 < length - ) { - throw new DOMException("Invalid length", "OperationError"); - } else { - return secret.buffer.slice(0, MathCeil(length / 8)); - } + // 7. + if (length === null) { + return secret.buffer; + } else if ( + secret.buffer.byteLength * 8 < length + ) { + throw new DOMException("Invalid length", "OperationError"); + } else { + return secret.buffer.slice(0, MathCeil(length / 8)); } - default: - throw new DOMException("Not implemented", "NotSupportedError"); } + default: + throw new DOMException("Not implemented", "NotSupportedError"); } +} - async function encrypt(normalizedAlgorithm, key, data) { - const handle = key[_handle]; - const keyData = WeakMapPrototypeGet(KEY_STORE, handle); - - switch (normalizedAlgorithm.name) { - case "RSA-OAEP": { - // 1. - if (key[_type] !== "public") { - throw new DOMException( - "Key type not supported", - "InvalidAccessError", - ); - } - - // 2. - if (normalizedAlgorithm.label) { - normalizedAlgorithm.label = copyBuffer(normalizedAlgorithm.label); - } else { - normalizedAlgorithm.label = new Uint8Array(); - } +async function encrypt(normalizedAlgorithm, key, data) { + const handle = key[_handle]; + const keyData = WeakMapPrototypeGet(KEY_STORE, handle); - // 3-5. - const hashAlgorithm = key[_algorithm].hash.name; - const cipherText = await core.opAsync("op_crypto_encrypt", { - key: keyData, - algorithm: "RSA-OAEP", - hash: hashAlgorithm, - label: normalizedAlgorithm.label, - }, data); + switch (normalizedAlgorithm.name) { + case "RSA-OAEP": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } - // 6. - return cipherText.buffer; + // 2. + if (normalizedAlgorithm.label) { + normalizedAlgorithm.label = copyBuffer(normalizedAlgorithm.label); + } else { + normalizedAlgorithm.label = new Uint8Array(); } - case "AES-CBC": { - normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); - // 1. - if (normalizedAlgorithm.iv.byteLength !== 16) { - throw new DOMException( - "Initialization vector must be 16 bytes", - "OperationError", - ); - } + // 3-5. + const hashAlgorithm = key[_algorithm].hash.name; + const cipherText = await core.opAsync("op_crypto_encrypt", { + key: keyData, + algorithm: "RSA-OAEP", + hash: hashAlgorithm, + label: normalizedAlgorithm.label, + }, data); - // 2. - const cipherText = await core.opAsync("op_crypto_encrypt", { - key: keyData, - algorithm: "AES-CBC", - length: key[_algorithm].length, - iv: normalizedAlgorithm.iv, - }, data); + // 6. + return cipherText.buffer; + } + case "AES-CBC": { + normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); - // 4. - return cipherText.buffer; + // 1. + if (normalizedAlgorithm.iv.byteLength !== 16) { + throw new DOMException( + "Initialization vector must be 16 bytes", + "OperationError", + ); } - case "AES-CTR": { - normalizedAlgorithm.counter = copyBuffer(normalizedAlgorithm.counter); - // 1. - if (normalizedAlgorithm.counter.byteLength !== 16) { - throw new DOMException( - "Counter vector must be 16 bytes", - "OperationError", - ); - } - - // 2. - if ( - normalizedAlgorithm.length == 0 || normalizedAlgorithm.length > 128 - ) { - throw new DOMException( - "Counter length must not be 0 or greater than 128", - "OperationError", - ); - } + // 2. + const cipherText = await core.opAsync("op_crypto_encrypt", { + key: keyData, + algorithm: "AES-CBC", + length: key[_algorithm].length, + iv: normalizedAlgorithm.iv, + }, data); + + // 4. + return cipherText.buffer; + } + case "AES-CTR": { + normalizedAlgorithm.counter = copyBuffer(normalizedAlgorithm.counter); - // 3. - const cipherText = await core.opAsync("op_crypto_encrypt", { - key: keyData, - algorithm: "AES-CTR", - keyLength: key[_algorithm].length, - counter: normalizedAlgorithm.counter, - ctrLength: normalizedAlgorithm.length, - }, data); + // 1. + if (normalizedAlgorithm.counter.byteLength !== 16) { + throw new DOMException( + "Counter vector must be 16 bytes", + "OperationError", + ); + } - // 4. - return cipherText.buffer; + // 2. + if ( + normalizedAlgorithm.length == 0 || normalizedAlgorithm.length > 128 + ) { + throw new DOMException( + "Counter length must not be 0 or greater than 128", + "OperationError", + ); } - case "AES-GCM": { - normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); - // 1. - if (data.byteLength > (2 ** 39) - 256) { - throw new DOMException( - "Plaintext too large", - "OperationError", - ); - } + // 3. + const cipherText = await core.opAsync("op_crypto_encrypt", { + key: keyData, + algorithm: "AES-CTR", + keyLength: key[_algorithm].length, + counter: normalizedAlgorithm.counter, + ctrLength: normalizedAlgorithm.length, + }, data); + + // 4. + return cipherText.buffer; + } + case "AES-GCM": { + normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); - // 2. - // We only support 96-bit and 128-bit nonce. - if ( - ArrayPrototypeIncludes( - [12, 16], - normalizedAlgorithm.iv.byteLength, - ) === undefined - ) { - throw new DOMException( - "Initialization vector length not supported", - "NotSupportedError", - ); - } + // 1. + if (data.byteLength > (2 ** 39) - 256) { + throw new DOMException( + "Plaintext too large", + "OperationError", + ); + } - // 3. - if (normalizedAlgorithm.additionalData !== undefined) { - if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) { - throw new DOMException( - "Additional data too large", - "OperationError", - ); - } - } + // 2. + // We only support 96-bit and 128-bit nonce. + if ( + ArrayPrototypeIncludes( + [12, 16], + normalizedAlgorithm.iv.byteLength, + ) === undefined + ) { + throw new DOMException( + "Initialization vector length not supported", + "NotSupportedError", + ); + } - // 4. - if (normalizedAlgorithm.tagLength == undefined) { - normalizedAlgorithm.tagLength = 128; - } else if ( - !ArrayPrototypeIncludes( - [32, 64, 96, 104, 112, 120, 128], - normalizedAlgorithm.tagLength, - ) - ) { + // 3. + if (normalizedAlgorithm.additionalData !== undefined) { + if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) { throw new DOMException( - "Invalid tag length", + "Additional data too large", "OperationError", ); } - // 5. - if (normalizedAlgorithm.additionalData) { - normalizedAlgorithm.additionalData = copyBuffer( - normalizedAlgorithm.additionalData, - ); - } - // 6-7. - const cipherText = await core.opAsync("op_crypto_encrypt", { - key: keyData, - algorithm: "AES-GCM", - length: key[_algorithm].length, - iv: normalizedAlgorithm.iv, - additionalData: normalizedAlgorithm.additionalData || null, - tagLength: normalizedAlgorithm.tagLength, - }, data); - - // 8. - return cipherText.buffer; } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - } - webidl.configurePrototype(SubtleCrypto); - const subtle = webidl.createBranded(SubtleCrypto); - - class Crypto { - constructor() { - webidl.illegalConstructor(); - } - - getRandomValues(arrayBufferView) { - webidl.assertBranded(this, CryptoPrototype); - const prefix = "Failed to execute 'getRandomValues' on 'Crypto'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - // Fast path for Uint8Array - if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, arrayBufferView)) { - ops.op_crypto_get_random_values(arrayBufferView); - return arrayBufferView; - } - arrayBufferView = webidl.converters.ArrayBufferView(arrayBufferView, { - prefix, - context: "Argument 1", - }); - if ( - !( - ObjectPrototypeIsPrototypeOf(Int8ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf( - Uint8ClampedArrayPrototype, - arrayBufferView, - ) || - ObjectPrototypeIsPrototypeOf(Int16ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf(Uint16ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf(Int32ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf(Uint32ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf( - BigInt64ArrayPrototype, - arrayBufferView, - ) || - ObjectPrototypeIsPrototypeOf(BigUint64ArrayPrototype, arrayBufferView) + // 4. + if (normalizedAlgorithm.tagLength == undefined) { + normalizedAlgorithm.tagLength = 128; + } else if ( + !ArrayPrototypeIncludes( + [32, 64, 96, 104, 112, 120, 128], + normalizedAlgorithm.tagLength, ) ) { throw new DOMException( - "The provided ArrayBufferView is not an integer array type", - "TypeMismatchError", + "Invalid tag length", + "OperationError", ); } - const ui8 = new Uint8Array( - arrayBufferView.buffer, - arrayBufferView.byteOffset, - arrayBufferView.byteLength, - ); - ops.op_crypto_get_random_values(ui8); - return arrayBufferView; - } + // 5. + if (normalizedAlgorithm.additionalData) { + normalizedAlgorithm.additionalData = copyBuffer( + normalizedAlgorithm.additionalData, + ); + } + // 6-7. + const cipherText = await core.opAsync("op_crypto_encrypt", { + key: keyData, + algorithm: "AES-GCM", + length: key[_algorithm].length, + iv: normalizedAlgorithm.iv, + additionalData: normalizedAlgorithm.additionalData || null, + tagLength: normalizedAlgorithm.tagLength, + }, data); - randomUUID() { - webidl.assertBranded(this, CryptoPrototype); - return ops.op_crypto_random_uuid(); + // 8. + return cipherText.buffer; } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} - get subtle() { - webidl.assertBranded(this, CryptoPrototype); - return subtle; - } +webidl.configurePrototype(SubtleCrypto); +const subtle = webidl.createBranded(SubtleCrypto); - [SymbolFor("Deno.customInspect")](inspect) { - return `${this.constructor.name} ${inspect({})}`; +class Crypto { + constructor() { + webidl.illegalConstructor(); + } + + getRandomValues(arrayBufferView) { + webidl.assertBranded(this, CryptoPrototype); + const prefix = "Failed to execute 'getRandomValues' on 'Crypto'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + // Fast path for Uint8Array + if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, arrayBufferView)) { + ops.op_crypto_get_random_values(arrayBufferView); + return arrayBufferView; } + arrayBufferView = webidl.converters.ArrayBufferView(arrayBufferView, { + prefix, + context: "Argument 1", + }); + if ( + !( + ObjectPrototypeIsPrototypeOf(Int8ArrayPrototype, arrayBufferView) || + ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, arrayBufferView) || + ObjectPrototypeIsPrototypeOf( + Uint8ClampedArrayPrototype, + arrayBufferView, + ) || + ObjectPrototypeIsPrototypeOf(Int16ArrayPrototype, arrayBufferView) || + ObjectPrototypeIsPrototypeOf(Uint16ArrayPrototype, arrayBufferView) || + ObjectPrototypeIsPrototypeOf(Int32ArrayPrototype, arrayBufferView) || + ObjectPrototypeIsPrototypeOf(Uint32ArrayPrototype, arrayBufferView) || + ObjectPrototypeIsPrototypeOf( + BigInt64ArrayPrototype, + arrayBufferView, + ) || + ObjectPrototypeIsPrototypeOf(BigUint64ArrayPrototype, arrayBufferView) + ) + ) { + throw new DOMException( + "The provided ArrayBufferView is not an integer array type", + "TypeMismatchError", + ); + } + const ui8 = new Uint8Array( + arrayBufferView.buffer, + arrayBufferView.byteOffset, + arrayBufferView.byteLength, + ); + ops.op_crypto_get_random_values(ui8); + return arrayBufferView; } - webidl.configurePrototype(Crypto); - const CryptoPrototype = Crypto.prototype; + randomUUID() { + webidl.assertBranded(this, CryptoPrototype); + return ops.op_crypto_random_uuid(); + } - window.__bootstrap.crypto = { - SubtleCrypto, - crypto: webidl.createBranded(Crypto), - Crypto, - CryptoKey, - }; -})(this); + get subtle() { + webidl.assertBranded(this, CryptoPrototype); + return subtle; + } + + [SymbolFor("Deno.customInspect")](inspect) { + return `${this.constructor.name} ${inspect({})}`; + } +} + +webidl.configurePrototype(Crypto); +const CryptoPrototype = Crypto.prototype; + +const crypto = webidl.createBranded(Crypto); +export { Crypto, crypto, CryptoKey, SubtleCrypto }; |