summaryrefslogtreecommitdiff
path: root/extensions/crypto/00_crypto.js
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2021-08-04 00:54:02 +0530
committerGitHub <noreply@github.com>2021-08-03 21:24:02 +0200
commit86f89f922296c3c9e082268d01044f2e04a2e210 (patch)
treedf754928b43318c22341d4f95b7aac21a62a7dc1 /extensions/crypto/00_crypto.js
parentd7d452efc158d7b1ea89b6f92237cea3b29f88db (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.js187
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,
);