diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2021-08-04 00:54:02 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-03 21:24:02 +0200 |
commit | 86f89f922296c3c9e082268d01044f2e04a2e210 (patch) | |
tree | df754928b43318c22341d4f95b7aac21a62a7dc1 /extensions/crypto/00_crypto.js | |
parent | d7d452efc158d7b1ea89b6f92237cea3b29f88db (diff) |
feat(extensions/crypto): implement importKey and exportKey for raw HMAC keys (#11367)
This commit introduces "SubtleCrypto.importKey()" and
"SubtleCrypto.exportKey()" APIs.
Diffstat (limited to 'extensions/crypto/00_crypto.js')
-rw-r--r-- | extensions/crypto/00_crypto.js | 187 |
1 files changed, 180 insertions, 7 deletions
diff --git a/extensions/crypto/00_crypto.js b/extensions/crypto/00_crypto.js index 97a8ed77e..a21469a81 100644 --- a/extensions/crypto/00_crypto.js +++ b/extensions/crypto/00_crypto.js @@ -38,6 +38,16 @@ // P-521 is not yet supported. const supportedNamedCurves = ["P-256", "P-384"]; + const recognisedUsages = [ + "encrypt", + "decrypt", + "sign", + "verify", + "deriveKey", + "deriveBits", + "wrapKey", + "unwrapKey", + ]; const simpleAlgorithmDictionaries = { RsaHashedKeyGenParams: { hash: "HashAlgorithmIdentifier" }, @@ -45,6 +55,7 @@ HmacKeyGenParams: { hash: "HashAlgorithmIdentifier" }, RsaPssParams: {}, EcdsaParams: { hash: "HashAlgorithmIdentifier" }, + HmacImportParams: { hash: "HashAlgorithmIdentifier" }, }; const supportedAlgorithms = { @@ -70,6 +81,9 @@ "RSASSA-PKCS1-v1_5": null, "RSA-PSS": "RsaPssParams", }, + "importKey": { + "HMAC": "HmacImportParams", + }, }; // See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm @@ -225,14 +239,13 @@ } // https://w3c.github.io/webcrypto/#concept-usage-intersection - // TODO(littledivy): When the need arises, make `b` a list. /** * @param {string[]} a - * @param {string} b + * @param {string[]} b * @returns */ function usageIntersection(a, b) { - return ArrayPrototypeIncludes(a, b) ? [b] : []; + return a.filter((i) => b.includes(i)); } // TODO(lucacasonato): this should be moved to rust @@ -416,6 +429,166 @@ } /** + * @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, SubtleCrypto); + 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(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", + }); + + const normalizedAlgorithm = normalizeAlgorithm(algorithm, "importKey"); + + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + switch (normalizedAlgorithm.name) { + // https://w3c.github.io/webcrypto/#hmac-operations + case "HMAC": { + switch (format) { + case "raw": { + const hash = normalizedAlgorithm.hash; + // 5. + let length = keyData.byteLength * 8; + // 6. + if (length === 0) { + throw new DOMException("Key length is zero", "DataError"); + } + if (normalizeAlgorithm.length) { + // 7. + if ( + normalizedAlgorithm.length > length || + normalizedAlgorithm.length <= (length - 8) + ) { + throw new DOMException( + "Key length is invalid", + "DataError", + ); + } + length = normalizeAlgorithm.length; + } + + if (keyUsages.length == 0) { + throw new DOMException("Key usage is empty", "SyntaxError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "raw", + data: keyData, + }); + + const algorithm = { + name: "HMAC", + length, + hash, + }; + + const key = constructKey( + "secret", + true, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + // TODO(@littledivy): jwk + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + // TODO(@littledivy): RSASSA-PKCS1-v1_5 + // TODO(@littledivy): RSA-PSS + // TODO(@littledivy): ECDSA + 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, SubtleCrypto); + 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 bits = WeakMapPrototypeGet(KEY_STORE, handle); + + switch (key[_algorithm].name) { + case "HMAC": { + if (bits == null) { + throw new DOMException("Key is not available", "OperationError"); + } + switch (format) { + // 3. + case "raw": { + for (let _i = 7 & (8 - bits.length % 8); _i > 0; _i--) { + bits.push(0); + } + // 4-5. + return bits.buffer; + } + // TODO(@littledivy): jwk + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + // TODO(@littledivy): RSASSA-PKCS1-v1_5 + // TODO(@littledivy): RSA-PSS + // TODO(@littledivy): ECDSA + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + + /** * @param {string} algorithm * @param {CryptoKey} key * @param {BufferSource} signature @@ -623,7 +796,7 @@ const publicKey = constructKey( "public", true, - usageIntersection(usages, "verify"), + usageIntersection(usages, ["verify"]), algorithm, handle, ); @@ -632,7 +805,7 @@ const privateKey = constructKey( "private", extractable, - usageIntersection(usages, "sign"), + usageIntersection(usages, ["sign"]), algorithm, handle, ); @@ -682,7 +855,7 @@ const publicKey = constructKey( "public", true, - usageIntersection(usages, "verify"), + usageIntersection(usages, ["verify"]), algorithm, handle, ); @@ -691,7 +864,7 @@ const privateKey = constructKey( "private", extractable, - usageIntersection(usages, "sign"), + usageIntersection(usages, ["sign"]), algorithm, handle, ); |