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 <kingsley@kkoyung.dev>
This commit is contained in:
Kingsley Yung 2025-10-02 15:50:01 +08:00 committed by GitHub
parent 5687be385e
commit 19c498af16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -16,7 +16,7 @@ use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, StreamCipher};
use aes::{Aes128, Aes192, Aes256}; use aes::{Aes128, Aes192, Aes256};
use aes_gcm::{AeadInPlace, AesGcm, KeyInit}; use aes_gcm::{AeadInPlace, AesGcm, KeyInit};
use aes_kw::{KekAes128, KekAes192, KekAes256}; 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 base64::prelude::*;
use cipher::consts::{U12, U16, U32}; use cipher::consts::{U12, U16, U32};
use dom_struct::dom_struct; use dom_struct::dom_struct;
@ -73,7 +73,6 @@ const ALG_RSA_PSS: &str = "RSA-PSS";
const ALG_ECDH: &str = "ECDH"; const ALG_ECDH: &str = "ECDH";
const ALG_ECDSA: &str = "ECDSA"; const ALG_ECDSA: &str = "ECDSA";
#[allow(dead_code)]
static SUPPORTED_ALGORITHMS: &[&str] = &[ static SUPPORTED_ALGORITHMS: &[&str] = &[
ALG_AES_CBC, ALG_AES_CBC,
ALG_AES_CTR, 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]; static SUPPORTED_CURVES: &[&str] = &[NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521];
/// <https://w3c.github.io/webcrypto/#supported-operation> /// <https://w3c.github.io/webcrypto/#supported-operation>
#[allow(unused)] #[allow(dead_code)]
enum Operation { enum Operation {
Encrypt, Encrypt,
Decrypt, Decrypt,
@ -154,30 +153,51 @@ impl SubtleCrypto {
reflect_dom_object(Box::new(SubtleCrypto::new_inherited()), global, can_gc) 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<Promise>, data: Vec<u8>) {
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::<JSObject>());
match create_buffer_source::<ArrayBufferU8>(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 /// Queue a global task on the crypto task source, given realm's global object, to resolve
/// promise with a CryptoKey. /// promise with a CryptoKey.
fn resolve_promise_with_key(&self, promise: Rc<Promise>, key: DomRoot<CryptoKey>) { fn resolve_promise_with_key(&self, promise: Rc<Promise>, key: DomRoot<CryptoKey>) {
let trusted_key = Trusted::new(&*key); let trusted_key = Trusted::new(&*key);
let trusted_promise = TrustedPromise::new(promise); let trusted_promise = TrustedPromise::new(promise);
self.global().task_manager().crypto_task_source().queue( self.global()
task!(generate_key_result: move || { .task_manager()
.crypto_task_source()
.queue(task!(resolve_key: move || {
let key = trusted_key.root(); let key = trusted_key.root();
let promise = trusted_promise.root(); let promise = trusted_promise.root();
promise.resolve_native(&key, CanGc::note()); promise.resolve_native(&key, CanGc::note());
}), }));
);
} }
/// Queue a global task on the crypto task source, given realm's global object, to reject /// Queue a global task on the crypto task source, given realm's global object, to reject
/// promise with an error. /// promise with an error.
fn reject_promise_with_error(&self, promise: Rc<Promise>, error: Error) { fn reject_promise_with_error(&self, promise: Rc<Promise>, error: Error) {
let trusted_promise = TrustedPromise::new(promise); let trusted_promise = TrustedPromise::new(promise);
self.global().task_manager().crypto_task_source().queue( self.global()
task!(generate_key_result: move || { .task_manager()
.crypto_task_source()
.queue(task!(reject_error: move || {
let promise = trusted_promise.root(); let promise = trusted_promise.root();
promise.reject_error(error, CanGc::note()); promise.reject_error(error, CanGc::note());
}), }));
);
} }
} }
@ -487,6 +507,7 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
can_gc: CanGc, can_gc: CanGc,
) -> Rc<Promise> { ) -> Rc<Promise> {
// Step 1. Let algorithm be the algorithm parameter passed to the digest() method. // 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 // Step 2. Let data be the result of getting a copy of the bytes held by the
// data parameter passed to the digest() method. // data parameter passed to the digest() method.
@ -497,49 +518,52 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
// Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm,
// with alg set to algorithm and op set to "digest". // 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 promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm = match normalize_algorithm_for_digest(cx, &algorithm, can_gc) { let normalized_algorithm =
Ok(normalized_algorithm) => normalized_algorithm, match normalize_algorithm(cx, &Operation::Digest, &algorithm, can_gc) {
Err(e) => { Ok(normalized_algorithm) => normalized_algorithm,
// Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm. Err(error) => {
promise.reject_error(e, can_gc); promise.reject_error(error, can_gc);
return promise; return promise;
}, },
}; };
// Step 5. Let promise be a new Promise. // Step 5. Let realm be the relevant realm of this.
// NOTE: We did that in preparation of Step 4. // 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()); let trusted_promise = TrustedPromise::new(promise.clone());
self.global()
self.global().task_manager().dom_manipulation_task_source().queue( .task_manager()
task!(generate_key: move || { .dom_manipulation_task_source()
// Step 7. If the following steps or referenced procedures say to throw an error, reject promise .queue(task!(generate_key: move || {
// with the returned error and then terminate the algorithm. let subtle = this.root();
let promise = trusted_promise.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. // normalizedAlgorithm using algorithm, with data as message.
let digest = match normalized_algorithm.digest(&data) { let digest = match normalized_algorithm.digest(&data) {
Ok(digest) => digest, Ok(digest) => digest,
Err(e) => { Err(error) => {
promise.reject_error(e, CanGc::note()); subtle.reject_promise_with_error(promise, error);
return; return;
} }
}; };
let cx = GlobalScope::get_cx(); // Step 10. Queue a global task on the crypto task source, given realm's global
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>()); // object, to perform the remaining steps.
create_buffer_source::<ArrayBufferU8>(cx, digest.as_ref(), array_buffer_ptr.handle_mut(), CanGc::note()) // Step 11. Let result be the result of creating an ArrayBuffer in realm,
.expect("failed to create buffer source for exported key."); // containing digest.
// Step 12. Resolve promise with result.
subtle.resolve_promise_with_data(promise, digest);
// Step 9. Resolve promise with result. }));
promise.resolve_native(&*array_buffer_ptr, CanGc::note());
})
);
promise promise
} }
@ -3438,16 +3462,6 @@ impl DigestAlgorithm {
.into() .into()
} }
fn digest(&self, data: &[u8]) -> Result<impl AsRef<[u8]>, 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 { fn block_size_in_bits(&self) -> usize {
match self { match self {
Self::Sha1 => 160, Self::Sha1 => 160,
@ -3842,14 +3856,12 @@ impl JsonWebKeyExt for JsonWebKey {
/// binding of) IDL dictionary types. /// binding of) IDL dictionary types.
/// ///
/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> /// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm>
#[allow(unused)]
enum NormalizedAlgorithm { enum NormalizedAlgorithm {
Algorithm(SubtleAlgorithm), Algorithm(SubtleAlgorithm),
AesCtrParams(SubtleAesCtrParams), AesCtrParams(SubtleAesCtrParams),
} }
/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> /// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm>
#[allow(unused)]
fn normalize_algorithm( fn normalize_algorithm(
cx: JSContext, cx: JSContext,
op: &Operation, op: &Operation,
@ -3939,19 +3951,23 @@ fn normalize_algorithm(
// <https://w3c.github.io/webcrypto/#sha-registration> // <https://w3c.github.io/webcrypto/#sha-registration>
(ALG_SHA1, Operation::Digest) => { (ALG_SHA1, Operation::Digest) => {
let params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?; let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
params.name = DOMString::from(alg_name);
NormalizedAlgorithm::Algorithm(params.into()) NormalizedAlgorithm::Algorithm(params.into())
}, },
(ALG_SHA256, Operation::Digest) => { (ALG_SHA256, Operation::Digest) => {
let params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?; let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
params.name = DOMString::from(alg_name);
NormalizedAlgorithm::Algorithm(params.into()) NormalizedAlgorithm::Algorithm(params.into())
}, },
(ALG_SHA384, Operation::Digest) => { (ALG_SHA384, Operation::Digest) => {
let params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?; let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
params.name = DOMString::from(alg_name);
NormalizedAlgorithm::Algorithm(params.into()) NormalizedAlgorithm::Algorithm(params.into())
}, },
(ALG_SHA512, Operation::Digest) => { (ALG_SHA512, Operation::Digest) => {
let params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?; let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
params.name = DOMString::from(alg_name);
NormalizedAlgorithm::Algorithm(params.into()) NormalizedAlgorithm::Algorithm(params.into())
}, },
_ => return Err(Error::NotSupported), _ => return Err(Error::NotSupported),
@ -3979,7 +3995,6 @@ impl NormalizedAlgorithm {
// sign // sign
// verify // verify
#[allow(unused)]
fn digest(&self, message: &[u8]) -> Result<Vec<u8>, Error> { fn digest(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
match self { match self {
NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() { NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() {