summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tests/unit/webcrypto_test.ts54
-rw-r--r--ext/crypto/00_crypto.js159
-rw-r--r--ext/crypto/lib.deno_crypto.d.ts16
-rw-r--r--tools/wpt/expectation.json6
4 files changed, 229 insertions, 6 deletions
diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts
index 5087c7076..0660d9a7e 100644
--- a/cli/tests/unit/webcrypto_test.ts
+++ b/cli/tests/unit/webcrypto_test.ts
@@ -691,6 +691,60 @@ Deno.test(async function testAesKeyGen() {
assertEquals(algorithm.length, 256);
});
+Deno.test(async function testUnwrapKey() {
+ const subtle = crypto.subtle;
+
+ const AES_KEY: AesKeyAlgorithm & AesCbcParams = {
+ name: "AES-CBC",
+ length: 128,
+ iv: new Uint8Array(16),
+ };
+
+ const RSA_KEY: RsaHashedKeyGenParams & RsaOaepParams = {
+ name: "RSA-OAEP",
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([1, 0, 1]),
+ hash: "SHA-1",
+ };
+
+ const aesKey = await subtle.generateKey(AES_KEY, true, [
+ "encrypt",
+ "decrypt",
+ ]);
+
+ const rsaKeyPair = await subtle.generateKey(
+ {
+ name: "RSA-OAEP",
+ hash: "SHA-1",
+ publicExponent: new Uint8Array([1, 0, 1]),
+ modulusLength: 2048,
+ },
+ false,
+ ["wrapKey", "encrypt", "unwrapKey", "decrypt"],
+ );
+
+ const enc = await subtle.wrapKey(
+ "raw",
+ aesKey,
+ rsaKeyPair.publicKey,
+ RSA_KEY,
+ );
+ const unwrappedKey = await subtle.unwrapKey(
+ "raw",
+ enc,
+ rsaKeyPair.privateKey,
+ RSA_KEY,
+ AES_KEY,
+ false,
+ ["encrypt", "decrypt"],
+ );
+
+ assert(unwrappedKey instanceof CryptoKey);
+ assertEquals(unwrappedKey.type, "secret");
+ assertEquals(unwrappedKey.extractable, false);
+ assertEquals(unwrappedKey.usages, ["encrypt", "decrypt"]);
+});
+
Deno.test(async function testDecryptWithInvalidIntializationVector() {
const data = new Uint8Array([42, 42, 42, 42]);
const key = await crypto.subtle.generateKey(
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 311dcfbf1..2efe550e0 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -140,6 +140,10 @@
// TODO(@littledivy): Enable this once implemented.
// "AES-KW": "AesKeyWrapParams",
},
+ "unwrapKey": {
+ // TODO(@littledivy): Enable this once implemented.
+ // "AES-KW": "AesKeyWrapParams",
+ },
};
const aesJwkAlg = {
@@ -2070,6 +2074,161 @@
);
}
}
+ /**
+ * @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, SubtleCrypto);
+ 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",
+ });
+
+ // 2.
+ if (ArrayBufferIsView(wrappedKey)) {
+ wrappedKey = new Uint8Array(
+ wrappedKey.buffer,
+ wrappedKey.byteOffset,
+ wrappedKey.byteLength,
+ );
+ } else {
+ wrappedKey = new Uint8Array(wrappedKey);
+ }
+ wrappedKey = TypedArrayPrototypeSlice(wrappedKey);
+
+ let normalizedAlgorithm;
+
+ try {
+ // 3.
+ normalizedAlgorithm = normalizeAlgorithm(unwrapAlgorithm, "unwrapKey");
+ } catch (_) {
+ // 4.
+ normalizedAlgorithm = normalizeAlgorithm(unwrapAlgorithm, "decrypt");
+ }
+
+ // 6.
+ const normalizedKeyAlgorithm = normalizeAlgorithm(
+ unwrappedKeyAlgorithm,
+ "importKey",
+ );
+
+ // 11.
+ if (normalizedAlgorithm.name !== unwrappingKey[_algorithm].name) {
+ throw new DOMException(
+ "Unwrapping algorithm doesn't match key algorithm.",
+ "InvalidAccessError",
+ );
+ }
+
+ // 12.
+ if (!ArrayPrototypeIncludes(unwrappingKey[_usages], "unwrapKey")) {
+ throw new DOMException(
+ "Key does not support the 'unwrapKey' operation.",
+ "InvalidAccessError",
+ );
+ }
+
+ // 13.
+ let key;
+ if (
+ supportedAlgorithms["unwrapKey"][normalizedAlgorithm.name] !== undefined
+ ) {
+ // TODO(@littledivy): Implement this for AES-KW.
+ throw new DOMException(
+ "Not implemented",
+ "NotSupportedError",
+ );
+ } else if (
+ supportedAlgorithms["decrypt"][normalizedAlgorithm.name] !== undefined
+ ) {
+ key = await this.decrypt(
+ normalizedAlgorithm,
+ unwrappingKey,
+ wrappedKey,
+ );
+ } else {
+ throw new DOMException(
+ "Algorithm not supported",
+ "NotSupportedError",
+ );
+ }
+
+ // 14.
+ const bytes = key;
+ if (format == "jwk") {
+ // TODO(@littledivy): Implement JWK.
+ throw new DOMException(
+ "Not implemented",
+ "NotSupportedError",
+ );
+ }
+
+ // 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;
+ }
/**
* @param {string} algorithm
diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts
index 682296d3c..013f08bf8 100644
--- a/ext/crypto/lib.deno_crypto.d.ts
+++ b/ext/crypto/lib.deno_crypto.d.ts
@@ -257,6 +257,22 @@ interface SubtleCrypto {
wrappingKey: CryptoKey,
wrapAlgorithm: AlgorithmIdentifier | RsaOaepParams,
): Promise<ArrayBuffer>;
+ unwrapKey(
+ format: KeyFormat,
+ wrappedKey: BufferSource,
+ unwrappingKey: CryptoKey,
+ unwrapAlgorithm:
+ | AlgorithmIdentifier
+ | RsaOaepParams
+ | AesCbcParams,
+ unwrappedKeyAlgorithm:
+ | AlgorithmIdentifier
+ | RsaHashedImportParams
+ | HmacImportParams
+ | AesKeyAlgorithm,
+ extractable: boolean,
+ keyUsages: KeyUsage[],
+ ): Promise<CryptoKey>;
}
declare interface Crypto {
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index fed082ef8..b202dacaa 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -4151,15 +4151,9 @@
"historical.any.html": false,
"historical.any.worker.html": false,
"idlharness.https.any.html": [
- "SubtleCrypto interface: operation unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>)",
- "SubtleCrypto interface: crypto.subtle must inherit property \"unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>)\" with the proper type",
- "SubtleCrypto interface: calling unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>) on crypto.subtle with too few arguments must throw TypeError",
"Window interface: attribute crypto"
],
"idlharness.https.any.worker.html": [
- "SubtleCrypto interface: operation unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>)",
- "SubtleCrypto interface: crypto.subtle must inherit property \"unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>)\" with the proper type",
- "SubtleCrypto interface: calling unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>) on crypto.subtle with too few arguments must throw TypeError",
"WorkerGlobalScope interface: attribute crypto"
],
"import_export": {