diff options
author | Simon Wülker <simon.wuelker@arcor.de> | 2024-11-09 17:43:47 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-09 16:43:47 +0000 |
commit | 67ac59b80994bb2b5ad11e54315165efcbabcaba (patch) | |
tree | 151ba151362dbc2b47745753754268621809a45b /components/script/dom | |
parent | da462d0fcda24f11ad2c67f5db7a2db0ea0f95e2 (diff) | |
download | servo-67ac59b80994bb2b5ad11e54315165efcbabcaba.tar.gz servo-67ac59b80994bb2b5ad11e54315165efcbabcaba.zip |
subtlecrypto: Replace `NormalizedAlgorithm` with specialized variants (#34193)
* Implement specialized normalize_algorithm for "get key length" operation
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Implement specialized normalize_algorithm for "digest"
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Implement specialized normalize_algorithm for "importKey"
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Implement specialized normalize_algorithm for "deriveBits"
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Implement specialized normalize_algorithm for "decrypt"
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Implement specialized normalize_algorithm for "generateKey"
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Implement specialized normalize_algorithm for "encrypt"
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Remove normalize_algorithm
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Update WPT expectations
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Replace obj.get_unsafe() with obj.get()
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Replace BitwiseOr with LogicalOr (typo)
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Update more WPT expectations outside of WebCryptoAPI
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
---------
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/subtlecrypto.rs | 608 |
1 files changed, 377 insertions, 231 deletions
diff --git a/components/script/dom/subtlecrypto.rs b/components/script/dom/subtlecrypto.rs index 64cdae30b8d..ef38190569c 100644 --- a/components/script/dom/subtlecrypto.rs +++ b/components/script/dom/subtlecrypto.rs @@ -26,8 +26,8 @@ use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{ CryptoKeyMethods, KeyType, KeyUsage, }; use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{ - AesCbcParams, AesCtrParams, AesKeyAlgorithm, AesKeyGenParams, Algorithm, AlgorithmIdentifier, - JsonWebKey, KeyAlgorithm, KeyFormat, Pbkdf2Params, SubtleCryptoMethods, + AesCbcParams, AesCtrParams, AesDerivedKeyParams, AesKeyAlgorithm, AesKeyGenParams, Algorithm, + AlgorithmIdentifier, JsonWebKey, KeyAlgorithm, KeyFormat, Pbkdf2Params, SubtleCryptoMethods, }; use crate::dom::bindings::codegen::UnionTypes::{ ArrayBufferViewOrArrayBuffer, ArrayBufferViewOrArrayBufferOrJsonWebKey, @@ -150,8 +150,15 @@ impl SubtleCryptoMethods for SubtleCrypto { comp: InRealm, can_gc: CanGc, ) -> Rc<Promise> { - let normalized_algorithm = normalize_algorithm(cx, &algorithm, "encrypt"); let promise = Promise::new_in_current_realm(comp, can_gc); + let normalized_algorithm = match normalize_algorithm_for_encrypt_or_decrypt(cx, &algorithm) + { + Ok(algorithm) => algorithm, + Err(e) => { + promise.reject_error(e); + return promise; + }, + }; let data = match data { ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(), ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(), @@ -161,7 +168,6 @@ impl SubtleCryptoMethods for SubtleCrypto { let this = Trusted::new(self); let trusted_promise = TrustedPromise::new(promise.clone()); let trusted_key = Trusted::new(key); - let alg = normalized_algorithm.clone(); let key_alg = key.algorithm(); let valid_usage = key.usages().contains(&KeyUsage::Encrypt); let _ = task_source.queue_with_canceller( @@ -169,39 +175,20 @@ impl SubtleCryptoMethods for SubtleCrypto { let subtle = this.root(); let promise = trusted_promise.root(); let key = trusted_key.root(); + + if !valid_usage || normalized_algorithm.name() != key_alg { + promise.reject_error(Error::InvalidAccess); + return; + } + let cx = GlobalScope::get_cx(); rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>()); - let text = match alg { - Ok(NormalizedAlgorithm::AesCbcParams(key_gen_params)) => { - if !valid_usage || key_gen_params.name != key_alg { - Err(Error::InvalidAccess) - } else { - match subtle.encrypt_aes_cbc( - key_gen_params, &key, &data, cx, array_buffer_ptr.handle_mut() - ) { - Ok(_) => Ok(array_buffer_ptr.handle()), - Err(e) => Err(e), - } - } - }, - Ok(NormalizedAlgorithm::AesCtrParams(key_gen_params)) => { - if !valid_usage || key_gen_params.name != key_alg { - Err(Error::InvalidAccess) - } else { - match subtle.encrypt_decrypt_aes_ctr( - key_gen_params, &key, &data, cx, array_buffer_ptr.handle_mut() - ) { - Ok(_) => Ok(array_buffer_ptr.handle()), - Err(e) => Err(e), - } - } - }, - _ => Err(Error::NotSupported), - }; - match text { - Ok(text) => promise.resolve_native(&*text), - Err(e) => promise.reject_error(e), + + if let Err(e) = normalized_algorithm.encrypt(&subtle, &key, &data, cx, array_buffer_ptr.handle_mut()) { + promise.reject_error(e); + return; } + promise.resolve_native(&*array_buffer_ptr.handle()); }), &canceller, ); @@ -219,8 +206,15 @@ impl SubtleCryptoMethods for SubtleCrypto { comp: InRealm, can_gc: CanGc, ) -> Rc<Promise> { - let normalized_algorithm = normalize_algorithm(cx, &algorithm, "decrypt"); let promise = Promise::new_in_current_realm(comp, can_gc); + let normalized_algorithm = match normalize_algorithm_for_encrypt_or_decrypt(cx, &algorithm) + { + Ok(algorithm) => algorithm, + Err(e) => { + promise.reject_error(e); + return promise; + }, + }; let data = match data { ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(), ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(), @@ -230,7 +224,6 @@ impl SubtleCryptoMethods for SubtleCrypto { let this = Trusted::new(self); let trusted_promise = TrustedPromise::new(promise.clone()); let trusted_key = Trusted::new(key); - let alg = normalized_algorithm.clone(); let key_alg = key.algorithm(); let valid_usage = key.usages().contains(&KeyUsage::Decrypt); let _ = task_source.queue_with_canceller( @@ -240,37 +233,18 @@ impl SubtleCryptoMethods for SubtleCrypto { let key = trusted_key.root(); let cx = GlobalScope::get_cx(); rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>()); - let text = match alg { - Ok(NormalizedAlgorithm::AesCbcParams(key_gen_params)) => { - if !valid_usage || key_gen_params.name != key_alg { - Err(Error::InvalidAccess) - } else { - match subtle.decrypt_aes_cbc( - key_gen_params, &key, &data, cx, array_buffer_ptr.handle_mut() - ) { - Ok(_) => Ok(array_buffer_ptr.handle()), - Err(e) => Err(e), - } - } - }, - Ok(NormalizedAlgorithm::AesCtrParams(key_gen_params)) => { - if !valid_usage || key_gen_params.name != key_alg { - Err(Error::InvalidAccess) - } else { - match subtle.encrypt_decrypt_aes_ctr( - key_gen_params, &key, &data, cx, array_buffer_ptr.handle_mut() - ) { - Ok(_) => Ok(array_buffer_ptr.handle()), - Err(e) => Err(e), - } - } - }, - _ => Err(Error::NotSupported), - }; - match text { - Ok(text) => promise.resolve_native(&*text), - Err(e) => promise.reject_error(e), + + if !valid_usage || normalized_algorithm.name() != key_alg { + promise.reject_error(Error::InvalidAccess); + return; } + + if let Err(e) = normalized_algorithm.decrypt(&subtle, &key, &data, cx, array_buffer_ptr.handle_mut()) { + promise.reject_error(e); + return; + } + + promise.resolve_native(&*array_buffer_ptr.handle()); }), &canceller, ); @@ -299,7 +273,7 @@ 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". let promise = Promise::new_in_current_realm(comp, can_gc); - let normalized_algorithm = match normalize_algorithm(cx, &algorithm, "digest") { + let normalized_algorithm = match normalize_algorithm_for_digest(cx, &algorithm) { Ok(normalized_algorithm) => normalized_algorithm, Err(e) => { // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm. @@ -314,7 +288,6 @@ impl SubtleCryptoMethods for SubtleCrypto { // Step 6. Return promise and perform the remaining steps in parallel. let (task_source, canceller) = self.task_source_with_canceller(); let trusted_promise = TrustedPromise::new(promise.clone()); - let alg = normalized_algorithm.clone(); let _ = task_source.queue_with_canceller( task!(generate_key: move || { @@ -324,7 +297,7 @@ impl SubtleCryptoMethods for SubtleCrypto { // Step 8. Let result be the result of performing the digest operation specified by // normalizedAlgorithm using algorithm, with data as message. - let digest = match alg.digest(&data) { + let digest = match normalized_algorithm.digest(&data) { Ok(digest) => digest, Err(e) => { promise.reject_error(e); @@ -357,27 +330,24 @@ impl SubtleCryptoMethods for SubtleCrypto { comp: InRealm, can_gc: CanGc, ) -> Rc<Promise> { - let normalized_algorithm = normalize_algorithm(cx, &algorithm, "generateKey"); let promise = Promise::new_in_current_realm(comp, can_gc); - if let Err(e) = normalized_algorithm { - promise.reject_error(e); - return promise; - } + let normalized_algorithm = match normalize_algorithm_for_generate_key(cx, &algorithm) { + Ok(algorithm) => algorithm, + Err(e) => { + promise.reject_error(e); + return promise; + }, + }; let (task_source, canceller) = self.task_source_with_canceller(); let this = Trusted::new(self); let trusted_promise = TrustedPromise::new(promise.clone()); - let alg = normalized_algorithm.clone(); let _ = task_source.queue_with_canceller( task!(generate_key: move || { let subtle = this.root(); let promise = trusted_promise.root(); - let key = match alg { - Ok(NormalizedAlgorithm::AesKeyGenParams(key_gen_params)) => { - subtle.generate_key_aes(key_usages, key_gen_params, extractable) - }, - _ => Err(Error::NotSupported), - }; + let key = normalized_algorithm.generate_key(&subtle, key_usages, extractable); + match key { Ok(key) => promise.resolve_native(&key), Err(e) => promise.reject_error(e), @@ -407,7 +377,7 @@ impl SubtleCryptoMethods for SubtleCrypto { // Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm // and op set to "deriveBits". let promise = Promise::new_in_current_realm(comp, can_gc); - let normalized_algorithm = match normalize_algorithm(cx, &algorithm, "deriveBits") { + let normalized_algorithm = match normalize_algorithm_for_derive_bits(cx, &algorithm) { Ok(algorithm) => algorithm, Err(e) => { // Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm. @@ -419,7 +389,7 @@ impl SubtleCryptoMethods for SubtleCrypto { // Step 4. Let normalizedDerivedKeyAlgorithmImport be the result of normalizing an algorithm, // with alg set to derivedKeyType and op set to "importKey". let normalized_derived_key_algorithm_import = - match normalize_algorithm(cx, &derived_key_type, "importKey") { + match normalize_algorithm_for_import_key(cx, &derived_key_type) { Ok(algorithm) => algorithm, Err(e) => { // Step 5. If an error occurred, return a Promise rejected with normalizedDerivedKeyAlgorithmImport. @@ -431,7 +401,7 @@ impl SubtleCryptoMethods for SubtleCrypto { // Step 6. Let normalizedDerivedKeyAlgorithmLength be the result of normalizing an algorithm, with alg set // to derivedKeyType and op set to "get key length". let normalized_derived_key_algorithm_length = - match normalize_algorithm(cx, &derived_key_type, "get key length") { + match normalize_algorithm_for_get_key_length(cx, &derived_key_type) { Ok(algorithm) => algorithm, Err(e) => { // Step 7. If an error occurred, return a Promise rejected with normalizedDerivedKeyAlgorithmLength. @@ -536,7 +506,7 @@ impl SubtleCryptoMethods for SubtleCrypto { // Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, // with alg set to algorithm and op set to "deriveBits". let promise = Promise::new_in_current_realm(comp, can_gc); - let normalized_algorithm = match normalize_algorithm(cx, &algorithm, "deriveBits") { + let normalized_algorithm = match normalize_algorithm_for_derive_bits(cx, &algorithm) { Ok(algorithm) => algorithm, Err(e) => { // Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm. @@ -607,7 +577,7 @@ impl SubtleCryptoMethods for SubtleCrypto { can_gc: CanGc, ) -> Rc<Promise> { let promise = Promise::new_in_current_realm(comp, can_gc); - let normalized_algorithm = match normalize_algorithm(cx, &algorithm, "importKey") { + let normalized_algorithm = match normalize_algorithm_for_import_key(cx, &algorithm) { Ok(algorithm) => algorithm, Err(e) => { promise.reject_error(e); @@ -718,28 +688,6 @@ impl SubtleCryptoMethods for SubtleCrypto { } } -#[derive(Clone, Debug)] -pub enum NormalizedAlgorithm { - #[allow(dead_code)] - Algorithm(SubtleAlgorithm), - AesCbcParams(SubtleAesCbcParams), - AesCtrParams(SubtleAesCtrParams), - AesKeyGenParams(SubtleAesKeyGenParams), - Pbkdf2Params(SubtlePbkdf2Params), - - /// <https://w3c.github.io/webcrypto/#sha> - Sha1, - - /// <https://w3c.github.io/webcrypto/#sha> - Sha256, - - /// <https://w3c.github.io/webcrypto/#sha> - Sha384, - - /// <https://w3c.github.io/webcrypto/#sha> - Sha512, -} - // These "subtle" structs are proxies for the codegen'd dicts which don't hold a DOMString // so they can be sent safely when running steps in parallel. @@ -823,7 +771,7 @@ pub struct SubtlePbkdf2Params { iterations: u32, /// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-hash> - hash: Box<NormalizedAlgorithm>, + hash: DigestAlgorithm, } impl SubtlePbkdf2Params { @@ -836,99 +784,249 @@ impl SubtlePbkdf2Params { let params = Self { salt, iterations: params.iterations, - hash: Box::new(normalize_algorithm(cx, ¶ms.hash, "digest")?), + hash: normalize_algorithm_for_digest(cx, ¶ms.hash)?, }; Ok(params) } } -/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> -#[allow(unsafe_code)] -fn normalize_algorithm( +enum GetKeyLengthAlgorithm { + Aes(u16), +} + +#[derive(Clone, Copy, Debug)] +enum DigestAlgorithm { + /// <https://w3c.github.io/webcrypto/#sha> + Sha1, + + /// <https://w3c.github.io/webcrypto/#sha> + Sha256, + + /// <https://w3c.github.io/webcrypto/#sha> + Sha384, + + /// <https://w3c.github.io/webcrypto/#sha> + Sha512, +} + +/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"importKey"` +/// +/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm +enum ImportKeyAlgorithm { + AesCbc, + AesCtr, + Pbkdf2, +} + +/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"deriveBits"` +/// +/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm +enum DeriveBitsAlgorithm { + Pbkdf2(SubtlePbkdf2Params), +} + +/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"encrypt"` or `"decrypt"` +/// +/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm +enum EncryptionAlgorithm { + AesCbc(SubtleAesCbcParams), + AesCtr(SubtleAesCtrParams), +} + +/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"generateKey"` +/// +/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm +enum KeyGenerationAlgorithm { + Aes(SubtleAesKeyGenParams), +} + +macro_rules! value_from_js_object { + ($t: ty, $cx: ident, $value: ident) => {{ + let params_result = <$t>::new($cx, $value.handle()).map_err(|_| Error::Operation)?; + let ConversionResult::Success(params) = params_result else { + return Err(Error::Syntax); + }; + params + }}; +} + +/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"get key length"` +fn normalize_algorithm_for_get_key_length( cx: JSContext, algorithm: &AlgorithmIdentifier, - operation: &str, -) -> Result<NormalizedAlgorithm, Error> { +) -> Result<GetKeyLengthAlgorithm, Error> { match algorithm { - AlgorithmIdentifier::String(name) => { - Ok(NormalizedAlgorithm::Algorithm(name.clone().into())) - }, AlgorithmIdentifier::Object(obj) => { - rooted!(in(*cx) let value = ObjectValue(unsafe { *obj.get_unsafe() })); + rooted!(in(*cx) let value = ObjectValue(obj.get())); let Ok(ConversionResult::Success(algorithm)) = Algorithm::new(cx, value.handle()) else { return Err(Error::Syntax); }; - let normalized_name = algorithm.name.str().to_uppercase(); - - // This implements the table from https://w3c.github.io/webcrypto/#algorithm-overview - let normalized_algorithm = match (normalized_name.as_str(), operation) { - (ALG_AES_CBC, "encrypt") | (ALG_AES_CBC, "decrypt") => { - let params_result = - AesCbcParams::new(cx, value.handle()).map_err(|_| Error::Operation)?; - let ConversionResult::Success(params) = params_result else { - return Err(Error::Syntax); - }; - NormalizedAlgorithm::AesCbcParams(params.into()) - }, - (ALG_AES_CTR, "encrypt") | (ALG_AES_CTR, "decrypt") => { - let params_result = - AesCtrParams::new(cx, value.handle()).map_err(|_| Error::Operation)?; - let ConversionResult::Success(params) = params_result else { - return Err(Error::Syntax); - }; - NormalizedAlgorithm::AesCtrParams(params.into()) - }, - (ALG_AES_CBC, "generateKey") | (ALG_AES_CTR, "generateKey") => { - let params_result = - AesKeyGenParams::new(cx, value.handle()).map_err(|_| Error::Operation)?; - let ConversionResult::Success(params) = params_result else { - return Err(Error::Syntax); - }; - NormalizedAlgorithm::AesKeyGenParams(params.into()) - }, - (ALG_ECDSA, "deriveBits") => NormalizedAlgorithm::Algorithm(SubtleAlgorithm { - name: ALG_ECDSA.to_string(), - }), - (ALG_HKDF, "deriveBits") => NormalizedAlgorithm::Algorithm(SubtleAlgorithm { - name: ALG_HKDF.to_string(), - }), - (ALG_PBKDF2, "deriveBits") => { - let params_result = - Pbkdf2Params::new(cx, value.handle()).map_err(|_| Error::Operation)?; - let ConversionResult::Success(params) = params_result else { - return Err(Error::Syntax); - }; - let subtle_params = SubtlePbkdf2Params::new(cx, params)?; - NormalizedAlgorithm::Pbkdf2Params(subtle_params) - }, - (ALG_AES_CBC, "importKey") => NormalizedAlgorithm::Algorithm(SubtleAlgorithm { - name: ALG_AES_CBC.to_string(), - }), - (ALG_AES_CTR, "importKey") => NormalizedAlgorithm::Algorithm(SubtleAlgorithm { - name: ALG_AES_CTR.to_string(), - }), - (ALG_PBKDF2, "importKey") => NormalizedAlgorithm::Algorithm(SubtleAlgorithm { - name: ALG_PBKDF2.to_string(), - }), - (ALG_SHA1, "digest") => NormalizedAlgorithm::Sha1, - (ALG_SHA256, "digest") => NormalizedAlgorithm::Sha256, - (ALG_SHA384, "digest") => NormalizedAlgorithm::Sha384, - (ALG_SHA512, "digest") => NormalizedAlgorithm::Sha512, - _ => return Err(Error::NotSupported), + + let name = algorithm.name.str(); + let normalized_algorithm = if name.eq_ignore_ascii_case(ALG_AES_CBC) || + name.eq_ignore_ascii_case(ALG_AES_CTR) + { + let params = value_from_js_object!(AesDerivedKeyParams, cx, value); + GetKeyLengthAlgorithm::Aes(params.length) + } else { + return Err(Error::NotSupported); }; Ok(normalized_algorithm) }, + AlgorithmIdentifier::String(_) => { + // All algorithms that support "get key length" require additional parameters + Err(Error::NotSupported) + }, } } +/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"digest"` +fn normalize_algorithm_for_digest( + cx: JSContext, + algorithm: &AlgorithmIdentifier, +) -> Result<DigestAlgorithm, Error> { + let name = match algorithm { + AlgorithmIdentifier::Object(obj) => { + rooted!(in(*cx) let value = ObjectValue(obj.get())); + let Ok(ConversionResult::Success(algorithm)) = Algorithm::new(cx, value.handle()) + else { + return Err(Error::Syntax); + }; + + algorithm.name.str().to_uppercase() + }, + AlgorithmIdentifier::String(name) => name.str().to_uppercase(), + }; + + let normalized_algorithm = match name.as_str() { + ALG_SHA1 => DigestAlgorithm::Sha1, + ALG_SHA256 => DigestAlgorithm::Sha256, + ALG_SHA384 => DigestAlgorithm::Sha384, + ALG_SHA512 => DigestAlgorithm::Sha512, + _ => return Err(Error::NotSupported), + }; + + Ok(normalized_algorithm) +} + +/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"importKey"` +fn normalize_algorithm_for_import_key( + cx: JSContext, + algorithm: &AlgorithmIdentifier, +) -> Result<ImportKeyAlgorithm, Error> { + let name = match algorithm { + AlgorithmIdentifier::Object(obj) => { + rooted!(in(*cx) let value = ObjectValue(obj.get())); + let Ok(ConversionResult::Success(algorithm)) = Algorithm::new(cx, value.handle()) + else { + return Err(Error::Syntax); + }; + + algorithm.name.str().to_uppercase() + }, + AlgorithmIdentifier::String(name) => name.str().to_uppercase(), + }; + + let normalized_algorithm = match name.as_str() { + ALG_AES_CBC => ImportKeyAlgorithm::AesCbc, + ALG_AES_CTR => ImportKeyAlgorithm::AesCtr, + ALG_PBKDF2 => ImportKeyAlgorithm::Pbkdf2, + _ => return Err(Error::NotSupported), + }; + + Ok(normalized_algorithm) +} + +/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"deriveBits"` +fn normalize_algorithm_for_derive_bits( + cx: JSContext, + algorithm: &AlgorithmIdentifier, +) -> Result<DeriveBitsAlgorithm, Error> { + let AlgorithmIdentifier::Object(obj) = algorithm else { + // All algorithms that support "deriveBits" require additional parameters + return Err(Error::NotSupported); + }; + + rooted!(in(*cx) let value = ObjectValue(obj.get())); + let Ok(ConversionResult::Success(algorithm)) = Algorithm::new(cx, value.handle()) else { + return Err(Error::Syntax); + }; + + let normalized_algorithm = if algorithm.name.str().eq_ignore_ascii_case(ALG_PBKDF2) { + let params = value_from_js_object!(Pbkdf2Params, cx, value); + let subtle_params = SubtlePbkdf2Params::new(cx, params)?; + DeriveBitsAlgorithm::Pbkdf2(subtle_params) + } else { + return Err(Error::NotSupported); + }; + + Ok(normalized_algorithm) +} + +/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"deriveBits"` +fn normalize_algorithm_for_encrypt_or_decrypt( + cx: JSContext, + algorithm: &AlgorithmIdentifier, +) -> Result<EncryptionAlgorithm, Error> { + let AlgorithmIdentifier::Object(obj) = algorithm else { + // All algorithms that support "encrypt" or "decrypt" require additional parameters + return Err(Error::NotSupported); + }; + + rooted!(in(*cx) let value = ObjectValue(obj.get())); + let Ok(ConversionResult::Success(algorithm)) = Algorithm::new(cx, value.handle()) else { + return Err(Error::Syntax); + }; + + let name = algorithm.name.str(); + let normalized_algorithm = if name.eq_ignore_ascii_case(ALG_AES_CBC) { + let params = value_from_js_object!(AesCbcParams, cx, value); + EncryptionAlgorithm::AesCbc(params.into()) + } else if name.eq_ignore_ascii_case(ALG_AES_CTR) { + let params = value_from_js_object!(AesCtrParams, cx, value); + EncryptionAlgorithm::AesCtr(params.into()) + } else { + return Err(Error::NotSupported); + }; + + Ok(normalized_algorithm) +} + +/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"generateKey"` +fn normalize_algorithm_for_generate_key( + cx: JSContext, + algorithm: &AlgorithmIdentifier, +) -> Result<KeyGenerationAlgorithm, Error> { + let AlgorithmIdentifier::Object(obj) = algorithm else { + // All algorithms that support "generateKey" require additional parameters + return Err(Error::NotSupported); + }; + + rooted!(in(*cx) let value = ObjectValue(obj.get())); + let Ok(ConversionResult::Success(algorithm)) = Algorithm::new(cx, value.handle()) else { + return Err(Error::Syntax); + }; + + let name = algorithm.name.str(); + let normalized_algorithm = + if name.eq_ignore_ascii_case(ALG_AES_CBC) || name.eq_ignore_ascii_case(ALG_AES_CTR) { + let params = value_from_js_object!(AesKeyGenParams, cx, value); + KeyGenerationAlgorithm::Aes(params.into()) + } else { + return Err(Error::NotSupported); + }; + + Ok(normalized_algorithm) +} + impl SubtleCrypto { /// <https://w3c.github.io/webcrypto/#aes-cbc-operations> fn encrypt_aes_cbc( &self, - params: SubtleAesCbcParams, + params: &SubtleAesCbcParams, key: &CryptoKey, data: &[u8], cx: JSContext, @@ -966,7 +1064,7 @@ impl SubtleCrypto { /// <https://w3c.github.io/webcrypto/#aes-cbc-operations> fn decrypt_aes_cbc( &self, - params: SubtleAesCbcParams, + params: &SubtleAesCbcParams, key: &CryptoKey, data: &[u8], cx: JSContext, @@ -1010,7 +1108,7 @@ impl SubtleCrypto { /// <https://w3c.github.io/webcrypto/#aes-ctr-operations> fn encrypt_decrypt_aes_ctr( &self, - params: SubtleAesCtrParams, + params: &SubtleAesCtrParams, key: &CryptoKey, data: &[u8], cx: JSContext, @@ -1051,7 +1149,7 @@ impl SubtleCrypto { fn generate_key_aes( &self, usages: Vec<KeyUsage>, - key_gen_params: SubtleAesKeyGenParams, + key_gen_params: &SubtleAesKeyGenParams, extractable: bool, ) -> Result<DomRoot<CryptoKey>, Error> { let mut rand = vec![0; key_gen_params.length as usize]; @@ -1323,16 +1421,11 @@ impl SubtlePbkdf2Params { // Step 3. Let prf be the MAC Generation function described in Section 4 of [FIPS-198-1] // using the hash function described by the hash member of normalizedAlgorithm. - let NormalizedAlgorithm::Algorithm(alg) = &*self.hash else { - return Err(Error::NotSupported); - }; - - let prf = match alg.name.as_str() { - ALG_SHA1 => pbkdf2::PBKDF2_HMAC_SHA1, - ALG_SHA256 => pbkdf2::PBKDF2_HMAC_SHA256, - ALG_SHA384 => pbkdf2::PBKDF2_HMAC_SHA384, - ALG_SHA512 => pbkdf2::PBKDF2_HMAC_SHA512, - _ => return Err(Error::NotSupported), + let prf = match self.hash { + DigestAlgorithm::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1, + DigestAlgorithm::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256, + DigestAlgorithm::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384, + DigestAlgorithm::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512, }; // Step 4. Let result be the result of performing the PBKDF2 operation defined in Section 5.2 of [RFC8018] using @@ -1358,23 +1451,39 @@ impl SubtlePbkdf2Params { } } -impl NormalizedAlgorithm { - fn derive_bits(&self, key: &CryptoKey, length: Option<u32>) -> Result<Vec<u8>, Error> { - match self { - Self::Pbkdf2Params(pbkdf2_params) => pbkdf2_params.derive_bits(key, length), - _ => Err(Error::NotSupported), - } +/// <https://w3c.github.io/webcrypto/#aes-ctr-operations> +fn get_key_length_for_aes(length: u16) -> Result<u16, Error> { + // Step 1. If the length member of normalizedDerivedKeyAlgorithm is not 128, 192 or 256, + // then throw an OperationError. + if !matches!(length, 128 | 192 | 256) { + return Err(Error::Operation); } + // Step 2. Return the length member of normalizedDerivedKeyAlgorithm. + Ok(length) +} + +impl GetKeyLengthAlgorithm { fn get_key_length(&self) -> Result<u16, Error> { match self { - Self::AesCtrParams(aes_ctr_params) => { - get_key_length_for_aes(aes_ctr_params.length as u16) - }, - _ => Err(Error::NotSupported), + Self::Aes(length) => get_key_length_for_aes(*length), } } +} +impl DigestAlgorithm { + 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)) + } +} + +impl ImportKeyAlgorithm { fn import_key( &self, subtle: &SubtleCrypto, @@ -1383,47 +1492,84 @@ impl NormalizedAlgorithm { extractable: bool, key_usages: Vec<KeyUsage>, ) -> Result<DomRoot<CryptoKey>, Error> { - let alg = match self { - Self::Algorithm(name) => name, - _ => { - return Err(Error::NotSupported); - }, - }; - - match alg.name.as_str() { - ALG_AES_CBC => { + match self { + Self::AesCbc => { subtle.import_key_aes(format, secret, extractable, key_usages, ALG_AES_CBC) }, - ALG_AES_CTR => { + Self::AesCtr => { subtle.import_key_aes(format, secret, extractable, key_usages, ALG_AES_CTR) }, - ALG_PBKDF2 => subtle.import_key_pbkdf2(format, secret, extractable, key_usages), - _ => Err(Error::NotSupported), + Self::Pbkdf2 => subtle.import_key_pbkdf2(format, secret, extractable, key_usages), } } +} - 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, - _ => { - return Err(Error::NotSupported); - }, - }; - Ok(digest::digest(algorithm, data)) +impl DeriveBitsAlgorithm { + fn derive_bits(&self, key: &CryptoKey, length: Option<u32>) -> Result<Vec<u8>, Error> { + match self { + Self::Pbkdf2(pbkdf2_params) => pbkdf2_params.derive_bits(key, length), + } } } -/// <https://w3c.github.io/webcrypto/#aes-ctr-operations> -fn get_key_length_for_aes(length: u16) -> Result<u16, Error> { - // Step 1. If the length member of normalizedDerivedKeyAlgorithm is not 128, 192 or 256, - // then throw an OperationError. - if !matches!(length, 128 | 192 | 256) { - return Err(Error::Operation); +impl EncryptionAlgorithm { + /// <https://w3c.github.io/webcrypto/#dom-algorithm-name> + fn name(&self) -> &str { + match self { + Self::AesCbc(key_gen_params) => &key_gen_params.name, + Self::AesCtr(key_gen_params) => &key_gen_params.name, + } } - // Step 2. Return the length member of normalizedDerivedKeyAlgorithm. - Ok(length) + // FIXME: This doesn't really need the "SubtleCrypto" argument + fn encrypt( + &self, + subtle: &SubtleCrypto, + key: &CryptoKey, + data: &[u8], + cx: JSContext, + result: MutableHandleObject, + ) -> Result<(), Error> { + match self { + Self::AesCbc(key_gen_params) => { + subtle.encrypt_aes_cbc(key_gen_params, key, data, cx, result) + }, + Self::AesCtr(key_gen_params) => { + subtle.encrypt_decrypt_aes_ctr(key_gen_params, key, data, cx, result) + }, + } + } + + // FIXME: This doesn't really need the "SubtleCrypto" argument + fn decrypt( + &self, + subtle: &SubtleCrypto, + key: &CryptoKey, + data: &[u8], + cx: JSContext, + result: MutableHandleObject, + ) -> Result<(), Error> { + match self { + Self::AesCbc(key_gen_params) => { + subtle.decrypt_aes_cbc(key_gen_params, key, data, cx, result) + }, + Self::AesCtr(key_gen_params) => { + subtle.encrypt_decrypt_aes_ctr(key_gen_params, key, data, cx, result) + }, + } + } +} + +impl KeyGenerationAlgorithm { + // FIXME: This doesn't really need the "SubtleCrypto" argument + fn generate_key( + &self, + subtle: &SubtleCrypto, + usages: Vec<KeyUsage>, + extractable: bool, + ) -> Result<DomRoot<CryptoKey>, Error> { + match self { + Self::Aes(params) => subtle.generate_key_aes(usages, params, extractable), + } + } } |