From 19c498af16fad0a2b12fc6c1424cd709fb638a9f Mon Sep 17 00:00:00 2001 From: Kingsley Yung Date: Thu, 2 Oct 2025 15:50:01 +0800 Subject: [PATCH] script: Migrate `digest` operation to use new normalization (#39477) Refactoring of the algorithm normalization in #39431 introduces a new algorithm normalization procedure to replace the existing one. This patch migrates the `digest` operation from using existing `normalize_algorithm_for_digest` function to using the new `normalize_algorithm` function. Note that the custom type `DigestAlgorithm` has not yet completely removed since other operations like `get key length` (not migrated yet) depend on it. It will be removed when those operations are also migrated. A minor bug (missing a step) in `normalize_algorithm` is also fixed. Testing: Refactoring. Existing WPT tests are enough. Fixes: Part of #39368 --------- Signed-off-by: Kingsley Yung --- components/script/dom/subtlecrypto.rs | 131 ++++++++++++++------------ 1 file changed, 73 insertions(+), 58 deletions(-) diff --git a/components/script/dom/subtlecrypto.rs b/components/script/dom/subtlecrypto.rs index 63620242991..f89f2f8317b 100644 --- a/components/script/dom/subtlecrypto.rs +++ b/components/script/dom/subtlecrypto.rs @@ -16,7 +16,7 @@ use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, StreamCipher}; use aes::{Aes128, Aes192, Aes256}; use aes_gcm::{AeadInPlace, AesGcm, KeyInit}; use aes_kw::{KekAes128, KekAes192, KekAes256}; -use aws_lc_rs::{digest, hkdf, hmac, pbkdf2}; +use aws_lc_rs::{hkdf, hmac, pbkdf2}; use base64::prelude::*; use cipher::consts::{U12, U16, U32}; use dom_struct::dom_struct; @@ -73,7 +73,6 @@ const ALG_RSA_PSS: &str = "RSA-PSS"; const ALG_ECDH: &str = "ECDH"; const ALG_ECDSA: &str = "ECDSA"; -#[allow(dead_code)] static SUPPORTED_ALGORITHMS: &[&str] = &[ ALG_AES_CBC, ALG_AES_CTR, @@ -100,7 +99,7 @@ const NAMED_CURVE_P521: &str = "P-521"; static SUPPORTED_CURVES: &[&str] = &[NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521]; /// -#[allow(unused)] +#[allow(dead_code)] enum Operation { Encrypt, Decrypt, @@ -154,30 +153,51 @@ impl SubtleCrypto { reflect_dom_object(Box::new(SubtleCrypto::new_inherited()), global, can_gc) } + /// Queue a global task on the crypto task source, given realm's global object, to resolve + /// promise with the result of creating an ArrayBuffer in realm, containing data. If it fails + /// to create buffer source, reject promise with a JSFailedError. + fn resolve_promise_with_data(&self, promise: Rc, data: Vec) { + let trusted_promise = TrustedPromise::new(promise); + self.global().task_manager().crypto_task_source().queue( + task!(resolve_data: move || { + let promise = trusted_promise.root(); + + let cx = GlobalScope::get_cx(); + rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::()); + match create_buffer_source::(cx, &data, array_buffer_ptr.handle_mut(), CanGc::note()) { + Ok(_) => promise.resolve_native(&*array_buffer_ptr, CanGc::note()), + Err(_) => promise.reject_error(Error::JSFailed, CanGc::note()), + } + }), + ); + } + /// Queue a global task on the crypto task source, given realm's global object, to resolve /// promise with a CryptoKey. fn resolve_promise_with_key(&self, promise: Rc, key: DomRoot) { let trusted_key = Trusted::new(&*key); let trusted_promise = TrustedPromise::new(promise); - self.global().task_manager().crypto_task_source().queue( - task!(generate_key_result: move || { + self.global() + .task_manager() + .crypto_task_source() + .queue(task!(resolve_key: move || { let key = trusted_key.root(); let promise = trusted_promise.root(); promise.resolve_native(&key, CanGc::note()); - }), - ); + })); } /// Queue a global task on the crypto task source, given realm's global object, to reject /// promise with an error. fn reject_promise_with_error(&self, promise: Rc, error: Error) { let trusted_promise = TrustedPromise::new(promise); - self.global().task_manager().crypto_task_source().queue( - task!(generate_key_result: move || { + self.global() + .task_manager() + .crypto_task_source() + .queue(task!(reject_error: move || { let promise = trusted_promise.root(); promise.reject_error(error, CanGc::note()); - }), - ); + })); } } @@ -487,6 +507,7 @@ impl SubtleCryptoMethods for SubtleCrypto { can_gc: CanGc, ) -> Rc { // Step 1. Let algorithm be the algorithm parameter passed to the digest() method. + // NOTE: We did that in method parameter. // Step 2. Let data be the result of getting a copy of the bytes held by the // data parameter passed to the digest() method. @@ -497,49 +518,52 @@ impl SubtleCryptoMethods for SubtleCrypto { // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, // with alg set to algorithm and op set to "digest". + // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm. let promise = Promise::new_in_current_realm(comp, can_gc); - let normalized_algorithm = match normalize_algorithm_for_digest(cx, &algorithm, can_gc) { - Ok(normalized_algorithm) => normalized_algorithm, - Err(e) => { - // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm. - promise.reject_error(e, can_gc); - return promise; - }, - }; + let normalized_algorithm = + match normalize_algorithm(cx, &Operation::Digest, &algorithm, can_gc) { + Ok(normalized_algorithm) => normalized_algorithm, + Err(error) => { + promise.reject_error(error, can_gc); + return promise; + }, + }; - // Step 5. Let promise be a new Promise. - // NOTE: We did that in preparation of Step 4. + // Step 5. Let realm be the relevant realm of this. + // Step 6. Let promise be a new Promise. + // NOTE: We did that in preparation of Step 3. - // Step 6. Return promise and perform the remaining steps in parallel. + // Step 7. Return promise and perform the remaining steps in parallel. + let this = Trusted::new(self); let trusted_promise = TrustedPromise::new(promise.clone()); - - self.global().task_manager().dom_manipulation_task_source().queue( - task!(generate_key: move || { - // Step 7. If the following steps or referenced procedures say to throw an error, reject promise - // with the returned error and then terminate the algorithm. + self.global() + .task_manager() + .dom_manipulation_task_source() + .queue(task!(generate_key: move || { + let subtle = this.root(); let promise = trusted_promise.root(); - // Step 8. Let result be the result of performing the digest operation specified by + // Step 8. If the following steps or referenced procedures say to throw an error, + // queue a global task on the crypto task source, given realm's global object, to + // reject promise with the returned error; and then terminate the algorithm. + + // Step 9. Let digest be the result of performing the digest operation specified by // normalizedAlgorithm using algorithm, with data as message. let digest = match normalized_algorithm.digest(&data) { Ok(digest) => digest, - Err(e) => { - promise.reject_error(e, CanGc::note()); + Err(error) => { + subtle.reject_promise_with_error(promise, error); return; } }; - let cx = GlobalScope::get_cx(); - rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::()); - create_buffer_source::(cx, digest.as_ref(), array_buffer_ptr.handle_mut(), CanGc::note()) - .expect("failed to create buffer source for exported key."); - - - // Step 9. Resolve promise with result. - promise.resolve_native(&*array_buffer_ptr, CanGc::note()); - }) - ); - + // 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 creating an ArrayBuffer in realm, + // containing digest. + // Step 12. Resolve promise with result. + subtle.resolve_promise_with_data(promise, digest); + })); promise } @@ -3438,16 +3462,6 @@ impl DigestAlgorithm { .into() } - fn digest(&self, data: &[u8]) -> Result, Error> { - let algorithm = match self { - Self::Sha1 => &digest::SHA1_FOR_LEGACY_USE_ONLY, - Self::Sha256 => &digest::SHA256, - Self::Sha384 => &digest::SHA384, - Self::Sha512 => &digest::SHA512, - }; - Ok(digest::digest(algorithm, data)) - } - fn block_size_in_bits(&self) -> usize { match self { Self::Sha1 => 160, @@ -3842,14 +3856,12 @@ impl JsonWebKeyExt for JsonWebKey { /// binding of) IDL dictionary types. /// /// -#[allow(unused)] enum NormalizedAlgorithm { Algorithm(SubtleAlgorithm), AesCtrParams(SubtleAesCtrParams), } /// -#[allow(unused)] fn normalize_algorithm( cx: JSContext, op: &Operation, @@ -3939,19 +3951,23 @@ fn normalize_algorithm( // (ALG_SHA1, Operation::Digest) => { - let params = value_from_js_object::(cx, value.handle(), can_gc)?; + let mut params = value_from_js_object::(cx, value.handle(), can_gc)?; + params.name = DOMString::from(alg_name); NormalizedAlgorithm::Algorithm(params.into()) }, (ALG_SHA256, Operation::Digest) => { - let params = value_from_js_object::(cx, value.handle(), can_gc)?; + let mut params = value_from_js_object::(cx, value.handle(), can_gc)?; + params.name = DOMString::from(alg_name); NormalizedAlgorithm::Algorithm(params.into()) }, (ALG_SHA384, Operation::Digest) => { - let params = value_from_js_object::(cx, value.handle(), can_gc)?; + let mut params = value_from_js_object::(cx, value.handle(), can_gc)?; + params.name = DOMString::from(alg_name); NormalizedAlgorithm::Algorithm(params.into()) }, (ALG_SHA512, Operation::Digest) => { - let params = value_from_js_object::(cx, value.handle(), can_gc)?; + let mut params = value_from_js_object::(cx, value.handle(), can_gc)?; + params.name = DOMString::from(alg_name); NormalizedAlgorithm::Algorithm(params.into()) }, _ => return Err(Error::NotSupported), @@ -3979,7 +3995,6 @@ impl NormalizedAlgorithm { // sign // verify - #[allow(unused)] fn digest(&self, message: &[u8]) -> Result, Error> { match self { NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() {