From f1e3162c6b37901a43afdd407c46bbf8bebc563d Mon Sep 17 00:00:00 2001 From: Tim Lander <5424257+VirtualTim@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:24:42 +0800 Subject: [PATCH 1/2] Fix bug in canOnlySignOrVerify logic. Current version checks for sign|verify, but not exclusively those flags. --- builtins/web/crypto/crypto-key.h | 5 ++++- tests/integration/crypto/crypto.js | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/builtins/web/crypto/crypto-key.h b/builtins/web/crypto/crypto-key.h index 58187885..d0d90675 100644 --- a/builtins/web/crypto/crypto-key.h +++ b/builtins/web/crypto/crypto-key.h @@ -57,7 +57,10 @@ class CryptoKeyUsages { [[nodiscard]] bool canOnlyDecrypt() const { return this->mask == decrypt_flag; }; [[nodiscard]] bool canOnlySign() const { return this->mask == sign_flag; }; [[nodiscard]] bool canOnlyVerify() const { return this->mask == verify_flag; }; - [[nodiscard]] bool canOnlySignOrVerify() const { return (this->mask & (sign_flag | verify_flag)) != 0; }; + [[nodiscard]] bool canOnlySignOrVerify() const { + return this->mask == sign_flag || this->mask == verify_flag || + this->mask == (sign_flag | verify_flag); + }; [[nodiscard]] bool canOnlyDeriveKey() const { return this->mask == derive_key_flag; }; [[nodiscard]] bool canOnlyDeriveBits() const { return this->mask == derive_bits_flag; }; [[nodiscard]] bool canOnlyWrapKey() const { return this->mask == wrap_key_flag; }; diff --git a/tests/integration/crypto/crypto.js b/tests/integration/crypto/crypto.js index cd152ecb..09a9d975 100644 --- a/tests/integration/crypto/crypto.js +++ b/tests/integration/crypto/crypto.js @@ -1249,6 +1249,19 @@ export const handler = serveTest(async (t) => { deepStrictEqual(key.usages, ["sign", "verify"], `key.usages`); } }); + await t.test("subtle.importKey.HMAC-non-sign-or-verify-usage", async () => { + for (const usages of [[], ["encrypt"], ["sign", "encrypt"], ["verify", "deriveBits"]]) { + await rejects(async () => { + await crypto.subtle.importKey( + "raw", + new Uint8Array([1, 0, 1]), + { name: "HMAC", hash: "SHA-256" }, + false, + usages, + ); + }, DOMException, undefined, "SyntaxError"); + } + }); await t.test("subtle.importKey.JWK-HS256-Public", async () => { const key = await crypto.subtle.importKey( "jwk", From 304c25172f11157f7d543f74dd5746abd9b51530 Mon Sep 17 00:00:00 2001 From: Tim Lander <5424257+VirtualTim@users.noreply.github.com> Date: Thu, 22 Oct 2026 08:58:04 +0800 Subject: [PATCH 2/2] Regenerate test expectations. Not strictly needed here, but will get the build passing in CI. --- ..._importKey_failures_Ed25519.https.any.js.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/wpt-harness/expectations/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js.json b/tests/wpt-harness/expectations/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js.json index b09ab922..19e96014 100644 --- a/tests/wpt-harness/expectations/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js.json +++ b/tests/wpt-harness/expectations/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js.json @@ -2136,16 +2136,16 @@ "status": "PASS" }, "Missing JWK 'kty' parameter: importKey(jwk(private), Ed25519, true, [sign])": { - "status": "FAIL" + "status": "PASS" }, "Missing JWK 'kty' parameter: importKey(jwk(private), Ed25519, false, [sign])": { - "status": "FAIL" + "status": "PASS" }, "Missing JWK 'kty' parameter: importKey(jwk(private), Ed25519, true, [sign, sign])": { - "status": "FAIL" + "status": "PASS" }, "Missing JWK 'kty' parameter: importKey(jwk(private), Ed25519, false, [sign, sign])": { - "status": "FAIL" + "status": "PASS" }, "Missing JWK 'crv' parameter: importKey(jwk (public) , Ed25519, true, [verify])": { "status": "PASS" @@ -2172,16 +2172,16 @@ "status": "PASS" }, "Missing JWK 'kty' parameter: importKey(jwk(private), {name: Ed25519}, true, [sign])": { - "status": "FAIL" + "status": "PASS" }, "Missing JWK 'kty' parameter: importKey(jwk(private), {name: Ed25519}, false, [sign])": { - "status": "FAIL" + "status": "PASS" }, "Missing JWK 'kty' parameter: importKey(jwk(private), {name: Ed25519}, true, [sign, sign])": { - "status": "FAIL" + "status": "PASS" }, "Missing JWK 'kty' parameter: importKey(jwk(private), {name: Ed25519}, false, [sign, sign])": { - "status": "FAIL" + "status": "PASS" }, "Missing JWK 'crv' parameter: importKey(jwk (public) , {name: Ed25519}, true, [verify])": { "status": "PASS"