summaryrefslogtreecommitdiff
path: root/ext/crypto/00_crypto.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/crypto/00_crypto.js')
-rw-r--r--ext/crypto/00_crypto.js7914
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 };