From f3d56173494180bdee1980e385e490887ed0c188 Mon Sep 17 00:00:00 2001 From: Kingsley Yung Date: Wed, 17 Sep 2025 01:23:42 +0800 Subject: [PATCH] script: Check whether the generated crypto key has empty usages (#39328) The WebCryptoAPI spec requires that when we generate crypto keys by the generateKey method of SubtleCrypto interface we have to check whether the usages is empty. If the usages is empty, throw a SyntaxError. FYI, Step 9 of https://w3c.github.io/webcrypto/#SubtleCrypto-method-generateKey We have not yet implemented this logic, and this patch implements it. Testing: Pass WPT tests that were expected to fail. --------- Signed-off-by: Kingsley Yung --- components/script/dom/cryptokey.rs | 5 + components/script/dom/subtlecrypto.rs | 67 +++++++++++-- .../script_bindings/webidls/CryptoKey.webidl | 7 ++ .../failures_HMAC.https.any.js.ini | 98 ------------------- 4 files changed, 69 insertions(+), 108 deletions(-) delete mode 100644 tests/wpt/meta/WebCryptoAPI/generateKey/failures_HMAC.https.any.js.ini diff --git a/components/script/dom/cryptokey.rs b/components/script/dom/cryptokey.rs index 6d9a6c562be..498199c586e 100644 --- a/components/script/dom/cryptokey.rs +++ b/components/script/dom/cryptokey.rs @@ -20,6 +20,11 @@ use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; use crate::script_runtime::{CanGc, JSContext}; +pub(crate) enum CryptoKeyOrCryptoKeyPair { + CryptoKey(DomRoot), + // TODO: CryptoKeyPair(CryptoKeyPair), +} + /// The underlying cryptographic data this key represents #[allow(dead_code)] #[derive(MallocSizeOf)] diff --git a/components/script/dom/subtlecrypto.rs b/components/script/dom/subtlecrypto.rs index 12753a77c62..cbf624758cd 100644 --- a/components/script/dom/subtlecrypto.rs +++ b/components/script/dom/subtlecrypto.rs @@ -46,7 +46,7 @@ use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::{DOMString, serialize_jsval_to_json_utf8}; use crate::dom::bindings::trace::RootedTraceableBox; -use crate::dom::cryptokey::{CryptoKey, Handle}; +use crate::dom::cryptokey::{CryptoKey, CryptoKeyOrCryptoKeyPair, Handle}; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::realms::InRealm; @@ -524,12 +524,49 @@ impl SubtleCryptoMethods for SubtleCrypto { .queue(task!(generate_key: move || { let subtle = this.root(); let promise = trusted_promise.root(); - let key = normalized_algorithm.generate_key(&subtle, key_usages, extractable, CanGc::note()); + // Step 8. Let result be the result of performing the generate key operation + // specified by normalizedAlgorithm using algorithm, extractable and usages. + let key = match normalized_algorithm + .generate_key(&subtle, key_usages, extractable, CanGc::note()) + { + Ok(key) => key, + Err(error) => { + promise.reject_error(error, CanGc::note()); + return; + } + }; + + // Step 9. + // If result is a CryptoKey object: + // If the [[type]] internal slot of result is "secret" or "private" and usages + // is empty, then throw a SyntaxError. + // If result is a CryptoKeyPair object: + // If the [[usages]] internal slot of the privateKey attribute of result is the + // empty sequence, then throw a SyntaxError. + // TODO: Implement CryptoKeyPair case + match &key { + CryptoKeyOrCryptoKeyPair::CryptoKey(crpyto_key) => { + if matches!(crpyto_key.Type(), KeyType::Secret | KeyType::Private) + && crpyto_key.usages().is_empty() + { + promise.reject_error(Error::Syntax(None), CanGc::note()); + return; + } + }, + }; + + // TODO: Step 10. Queue a global task on the crypto task source, given realm's + // global object, to perform the remaining steps. + + // Step 11. Let result be the result of converting result to an ECMAScript Object + // in realm, as defined by [WebIDL]. + // Step 12. Resolve promise with result. + // TODO: Implement CryptoKeyPair case match key { - Ok(key) => promise.resolve_native(&key, CanGc::note()), - Err(e) => promise.reject_error(e, CanGc::note()), - } + CryptoKeyOrCryptoKeyPair::CryptoKey(crypto_key) => + promise.resolve_native(&crypto_key, CanGc::note()), + }; })); promise @@ -3531,11 +3568,21 @@ impl KeyGenerationAlgorithm { usages: Vec, extractable: bool, can_gc: CanGc, - ) -> Result, Error> { - match self { - Self::Aes(params) => subtle.generate_key_aes(usages, params, extractable, can_gc), - Self::Hmac(params) => subtle.generate_key_hmac(usages, params, extractable, can_gc), - } + ) -> Result { + let key_or_key_pair = + match self { + Self::Aes(params) => CryptoKeyOrCryptoKeyPair::CryptoKey(subtle.generate_key_aes( + usages, + params, + extractable, + can_gc, + )?), + Self::Hmac(params) => CryptoKeyOrCryptoKeyPair::CryptoKey( + subtle.generate_key_hmac(usages, params, extractable, can_gc)?, + ), + }; + + Ok(key_or_key_pair) } } diff --git a/components/script_bindings/webidls/CryptoKey.webidl b/components/script_bindings/webidls/CryptoKey.webidl index 3540a2e9db7..09dc13fb88f 100644 --- a/components/script_bindings/webidls/CryptoKey.webidl +++ b/components/script_bindings/webidls/CryptoKey.webidl @@ -15,3 +15,10 @@ interface CryptoKey { readonly attribute object algorithm; readonly attribute object usages; }; + +// https://w3c.github.io/webcrypto/#dfn-CryptoKeyPair + +dictionary CryptoKeyPair { + CryptoKey publicKey; + CryptoKey privateKey; +}; diff --git a/tests/wpt/meta/WebCryptoAPI/generateKey/failures_HMAC.https.any.js.ini b/tests/wpt/meta/WebCryptoAPI/generateKey/failures_HMAC.https.any.js.ini deleted file mode 100644 index b479ecbe79b..00000000000 --- a/tests/wpt/meta/WebCryptoAPI/generateKey/failures_HMAC.https.any.js.ini +++ /dev/null @@ -1,98 +0,0 @@ -[failures_HMAC.https.any.worker.html] - [Empty usages: generateKey({hash: SHA-1, length: 160, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-1, length: 160, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-256, length: 256, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-256, length: 256, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-384, length: 384, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-384, length: 384, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-512, length: 512, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-512, length: 512, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-1, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-1, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-256, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-256, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-384, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-384, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-512, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-512, name: HMAC}, true, [\])] - expected: FAIL - - -[failures_HMAC.https.any.html] - [Empty usages: generateKey({hash: SHA-1, length: 160, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-1, length: 160, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-256, length: 256, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-256, length: 256, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-384, length: 384, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-384, length: 384, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-512, length: 512, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-512, length: 512, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-1, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-1, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-256, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-256, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-384, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-384, name: HMAC}, true, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-512, name: HMAC}, false, [\])] - expected: FAIL - - [Empty usages: generateKey({hash: SHA-512, name: HMAC}, true, [\])] - expected: FAIL