diff --git a/components/script/dom/subtlecrypto.rs b/components/script/dom/subtlecrypto.rs index 3b8a5686c0b..d86e32841b9 100644 --- a/components/script/dom/subtlecrypto.rs +++ b/components/script/dom/subtlecrypto.rs @@ -20,8 +20,8 @@ use dom_struct::dom_struct; use js::conversions::ConversionResult; use js::jsapi::{JS_NewObject, JSObject}; use js::jsval::{ObjectValue, UndefinedValue}; -use js::rust::MutableHandleObject; use js::rust::wrappers::JS_ParseJSON; +use js::rust::{HandleValue, MutableHandleObject}; use js::typedarray::ArrayBufferU8; use servo_rand::{RngCore, ServoRng}; @@ -1403,15 +1403,103 @@ enum KeyWrapAlgorithm { AesGcm(SubtleAesGcmParams), } -macro_rules! value_from_js_object { - ($t: ty, $cx: ident, $value: ident) => {{ - let params_result = - <$t>::new($cx, $value.handle(), CanGc::note()).map_err(|_| Error::JSFailed)?; - let ConversionResult::Success(params) = params_result else { - return Err(Error::Syntax(None)); - }; - params - }}; +/// Helper to abstract the conversion process of a JS value into many different WebIDL dictionaries. +trait DictionaryFromJSVal: Sized { + fn create(cx: JSContext, value: HandleValue) -> Result, ()>; +} + +impl DictionaryFromJSVal for Algorithm { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + Self::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for AesDerivedKeyParams { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + Self::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for AesKeyGenParams { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + Self::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for RootedTraceableBox { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + HmacImportParams::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for RootedTraceableBox { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + HmacKeyGenParams::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for HmacKeyAlgorithm { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + Self::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for RootedTraceableBox { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + AesCbcParams::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for RootedTraceableBox { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + AesCtrParams::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for RootedTraceableBox { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + AesGcmParams::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for RootedTraceableBox { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + Pbkdf2Params::new(cx, value, CanGc::note()) + } +} + +impl DictionaryFromJSVal for RootedTraceableBox { + fn create(cx: JSContext, value: HandleValue) -> Result, ()> { + HkdfParams::new(cx, value, CanGc::note()) + } +} + +fn extract_native_dict(converted: Result, ()>) -> Fallible { + let params_result = converted.map_err(|_| Error::JSFailed)?; + let ConversionResult::Success(params) = params_result else { + return Err(Error::Syntax(None)); + }; + Ok(params) +} + +fn value_from_js_object(cx: JSContext, value: HandleValue) -> Fallible { + extract_native_dict(T::create(cx, value)) +} + +trait DictionaryFromJSValType: crate::JSTraceable {} +impl DictionaryFromJSValType for T where + RootedTraceableBox: DictionaryFromJSVal +{ +} + +fn boxed_value_from_js_object( + cx: JSContext, + value: HandleValue, +) -> Fallible> +where + RootedTraceableBox: DictionaryFromJSVal, +{ + extract_native_dict(>::create(cx, value)) } /// with operation `"get key length"` @@ -1422,17 +1510,17 @@ fn normalize_algorithm_for_get_key_length( match algorithm { AlgorithmIdentifier::Object(obj) => { rooted!(in(*cx) let value = ObjectValue(obj.get())); - let algorithm = value_from_js_object!(Algorithm, cx, value); + let algorithm = value_from_js_object::(cx, value.handle())?; 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) || name.eq_ignore_ascii_case(ALG_AES_GCM) { - let params = value_from_js_object!(AesDerivedKeyParams, cx, value); + let params = value_from_js_object::(cx, value.handle())?; GetKeyLengthAlgorithm::Aes(params.length) } else if name.eq_ignore_ascii_case(ALG_HMAC) { - let params = value_from_js_object!(HmacImportParams, cx, value); + let params = boxed_value_from_js_object::(cx, value.handle())?; let subtle_params = SubtleHmacImportParams::new(cx, params)?; return Ok(GetKeyLengthAlgorithm::Hmac(subtle_params)); } else { @@ -1456,7 +1544,7 @@ fn normalize_algorithm_for_digest( let name = match algorithm { AlgorithmIdentifier::Object(obj) => { rooted!(in(*cx) let value = ObjectValue(obj.get())); - let algorithm = value_from_js_object!(Algorithm, cx, value); + let algorithm = value_from_js_object::(cx, value.handle())?; algorithm.name.str().to_uppercase() }, @@ -1482,11 +1570,11 @@ fn normalize_algorithm_for_import_key( let name = match algorithm { AlgorithmIdentifier::Object(obj) => { rooted!(in(*cx) let value = ObjectValue(obj.get())); - let algorithm = value_from_js_object!(Algorithm, cx, value); + let algorithm = value_from_js_object::(cx, value.handle())?; let name = algorithm.name.str().to_uppercase(); if name == ALG_HMAC { - let params = value_from_js_object!(HmacImportParams, cx, value); + let params = boxed_value_from_js_object::(cx, value.handle())?; let subtle_params = SubtleHmacImportParams::new(cx, params)?; return Ok(ImportKeyAlgorithm::Hmac(subtle_params)); } @@ -1520,14 +1608,14 @@ fn normalize_algorithm_for_derive_bits( }; rooted!(in(*cx) let value = ObjectValue(obj.get())); - let algorithm = value_from_js_object!(Algorithm, cx, value); + let algorithm = value_from_js_object::(cx, value.handle())?; let normalized_algorithm = if algorithm.name.str().eq_ignore_ascii_case(ALG_PBKDF2) { - let params = value_from_js_object!(Pbkdf2Params, cx, value); + let params = boxed_value_from_js_object::(cx, value.handle())?; let subtle_params = SubtlePbkdf2Params::new(cx, params)?; DeriveBitsAlgorithm::Pbkdf2(subtle_params) } else if algorithm.name.str().eq_ignore_ascii_case(ALG_HKDF) { - let params = value_from_js_object!(HkdfParams, cx, value); + let params = boxed_value_from_js_object::(cx, value.handle())?; let subtle_params = SubtleHkdfParams::new(cx, params)?; DeriveBitsAlgorithm::Hkdf(subtle_params) } else { @@ -1548,17 +1636,17 @@ fn normalize_algorithm_for_encrypt_or_decrypt( }; rooted!(in(*cx) let value = ObjectValue(obj.get())); - let algorithm = value_from_js_object!(Algorithm, cx, value); + let algorithm = value_from_js_object::(cx, value.handle())?; 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); + let params = boxed_value_from_js_object::(cx, value.handle())?; EncryptionAlgorithm::AesCbc(params.into()) } else if name.eq_ignore_ascii_case(ALG_AES_CTR) { - let params = value_from_js_object!(AesCtrParams, cx, value); + let params = boxed_value_from_js_object::(cx, value.handle())?; EncryptionAlgorithm::AesCtr(params.into()) } else if name.eq_ignore_ascii_case(ALG_AES_GCM) { - let params = value_from_js_object!(AesGcmParams, cx, value); + let params = boxed_value_from_js_object::(cx, value.handle())?; EncryptionAlgorithm::AesGcm(params.into()) } else { return Err(Error::NotSupported); @@ -1576,7 +1664,7 @@ fn normalize_algorithm_for_sign_or_verify( let name = match algorithm { AlgorithmIdentifier::Object(obj) => { rooted!(in(*cx) let value = ObjectValue(obj.get())); - let algorithm = value_from_js_object!(Algorithm, cx, value); + let algorithm = value_from_js_object::(cx, value.handle())?; algorithm.name.str().to_uppercase() }, @@ -1602,7 +1690,7 @@ fn normalize_algorithm_for_generate_key( }; rooted!(in(*cx) let value = ObjectValue(obj.get())); - let algorithm = value_from_js_object!(Algorithm, cx, value); + let algorithm = value_from_js_object::(cx, value.handle())?; let name = algorithm.name.str(); let normalized_algorithm = if name.eq_ignore_ascii_case(ALG_AES_CBC) || @@ -1610,10 +1698,10 @@ fn normalize_algorithm_for_generate_key( name.eq_ignore_ascii_case(ALG_AES_KW) || name.eq_ignore_ascii_case(ALG_AES_GCM) { - let params = value_from_js_object!(AesKeyGenParams, cx, value); + let params = value_from_js_object::(cx, value.handle())?; KeyGenerationAlgorithm::Aes(params.into()) } else if name.eq_ignore_ascii_case(ALG_HMAC) { - let params = value_from_js_object!(HmacKeyGenParams, cx, value); + let params = boxed_value_from_js_object::(cx, value.handle())?; let subtle_params = SubtleHmacKeyGenParams::new(cx, params)?; KeyGenerationAlgorithm::Hmac(subtle_params) } else { @@ -1631,7 +1719,7 @@ fn normalize_algorithm_for_key_wrap( let name = match algorithm { AlgorithmIdentifier::Object(obj) => { rooted!(in(*cx) let value = ObjectValue(obj.get())); - let algorithm = value_from_js_object!(Algorithm, cx, value); + let algorithm = value_from_js_object::(cx, value.handle())?; algorithm.name.str().to_uppercase() }, @@ -1645,21 +1733,27 @@ fn normalize_algorithm_for_key_wrap( return Err(Error::Syntax(None)); }; rooted!(in(*cx) let value = ObjectValue(obj.get())); - KeyWrapAlgorithm::AesCbc(value_from_js_object!(AesCbcParams, cx, value).into()) + KeyWrapAlgorithm::AesCbc( + boxed_value_from_js_object::(cx, value.handle())?.into(), + ) }, ALG_AES_CTR => { let AlgorithmIdentifier::Object(obj) = algorithm else { return Err(Error::Syntax(None)); }; rooted!(in(*cx) let value = ObjectValue(obj.get())); - KeyWrapAlgorithm::AesCtr(value_from_js_object!(AesCtrParams, cx, value).into()) + KeyWrapAlgorithm::AesCtr( + boxed_value_from_js_object::(cx, value.handle())?.into(), + ) }, ALG_AES_GCM => { let AlgorithmIdentifier::Object(obj) = algorithm else { return Err(Error::Syntax(None)); }; rooted!(in(*cx) let value = ObjectValue(obj.get())); - KeyWrapAlgorithm::AesGcm(value_from_js_object!(AesGcmParams, cx, value).into()) + KeyWrapAlgorithm::AesGcm( + boxed_value_from_js_object::(cx, value.handle())?.into(), + ) }, _ => return Err(Error::NotSupported), }; @@ -3005,7 +3099,7 @@ fn sign_hmac(cx: JSContext, key: &CryptoKey, data: &[u8]) -> Result(cx, algorithm_slot.handle())?; let hash_algorithm = match params.hash.name.str() { ALG_SHA1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,