diff options
author | Daniel Adams <70986246+msub2@users.noreply.github.com> | 2024-10-08 03:51:08 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-08 03:51:08 +0000 |
commit | fc0d4d8157c71c512817886bc3b8d1adad5d46a9 (patch) | |
tree | 0def1a423dd597400abee4906940d375c3b6ca6f /components | |
parent | 66bc430b24a0cb1fe2d51e2b5983ea8833ba22b9 (diff) | |
download | servo-fc0d4d8157c71c512817886bc3b8d1adad5d46a9.tar.gz servo-fc0d4d8157c71c512817886bc3b8d1adad5d46a9.zip |
crypto: Begin SubtleCrypto implementation (#33628)
* Update IDLs and Bindings conf
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Add AES crate
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Implement DOM interfaces
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* IDL tidy
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Remove deriveKey from inRealms for now until implemented
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Fix CryptoKey rustdoc comments
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Move string constants to top of file
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Use properly rooted CryptoKey
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Code clarity
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Rework NormalizedAlgorithm to not hold a DOMString
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Add Rustdoc for CryptoKey interface
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Move ignore mallocsizeof to rand crate, remove from crypto
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update cargo lock
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Fix key handling, implement exportKey with JWK TODO
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Add missing spec link
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Use create_buffer_source, remove aes dep from libservo
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Fix crash when running in worker
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update expectations
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* fmt
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Move CryptoKey and SubtleCrypto behind pref for now
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update expectations
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Readd timeout expectation
Signed-off-by: Daniel Adams <msub2official@gmail.com>
---------
Signed-off-by: Daniel Adams <msub2official@gmail.com>
Diffstat (limited to 'components')
-rw-r--r-- | components/config/prefs.rs | 5 | ||||
-rw-r--r-- | components/rand/Cargo.toml | 2 | ||||
-rw-r--r-- | components/rand/lib.rs | 3 | ||||
-rw-r--r-- | components/script/Cargo.toml | 1 | ||||
-rw-r--r-- | components/script/dom/bindings/codegen/Bindings.conf | 4 | ||||
-rw-r--r-- | components/script/dom/crypto.rs | 13 | ||||
-rw-r--r-- | components/script/dom/cryptokey.rs | 140 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 2 | ||||
-rw-r--r-- | components/script/dom/subtlecrypto.rs | 335 | ||||
-rw-r--r-- | components/script/dom/webidls/Crypto.webidl | 2 | ||||
-rw-r--r-- | components/script/dom/webidls/CryptoKey.webidl | 17 | ||||
-rw-r--r-- | components/script/dom/webidls/SubtleCrypto.webidl | 87 |
12 files changed, 607 insertions, 4 deletions
diff --git a/components/config/prefs.rs b/components/config/prefs.rs index d6da7d0e5d9..fe169317341 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -249,6 +249,11 @@ mod gen { #[serde(rename = "dom.compositionevent.enabled")] enabled: bool, }, + crypto: { + subtle: { + enabled: bool, + } + }, custom_elements: { #[serde(rename = "dom.customelements.enabled")] enabled: bool, diff --git a/components/rand/Cargo.toml b/components/rand/Cargo.toml index 44606ff218d..46910f5e3f1 100644 --- a/components/rand/Cargo.toml +++ b/components/rand/Cargo.toml @@ -13,6 +13,8 @@ path = "lib.rs" [dependencies] log = { workspace = true } +malloc_size_of = { workspace = true } +malloc_size_of_derive = { workspace = true } rand = { workspace = true } rand_core = { workspace = true } rand_isaac = { workspace = true } diff --git a/components/rand/lib.rs b/components/rand/lib.rs index c5669a8cc4b..89af575c0f3 100644 --- a/components/rand/lib.rs +++ b/components/rand/lib.rs @@ -7,6 +7,7 @@ use std::rc::Rc; use std::sync::Mutex; use log::trace; +use malloc_size_of_derive::MallocSizeOf; /// A random number generator which shares one instance of an `OsRng`. /// /// A problem with `OsRng`, which is inherited by `StdRng` and so @@ -31,7 +32,9 @@ static OS_RNG: Mutex<OsRng> = Mutex::new(OsRng); const RESEED_THRESHOLD: u64 = 32_768; // An in-memory RNG that only uses the shared file descriptor for seeding and reseeding. +#[derive(MallocSizeOf)] pub struct ServoRng { + #[ignore_malloc_size_of = "Defined in rand"] rng: ReseedingRng<IsaacCore, ServoReseeder>, } diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index cc731066180..c5b64d5a675 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -26,6 +26,7 @@ phf_shared = "0.11" serde_json = { workspace = true } [dependencies] +aes = { workspace = true } accountable-refcell = { workspace = true, optional = true } app_units = { workspace = true } arrayvec = { workspace = true } diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf index fa5b0755e79..2fee063b753 100644 --- a/components/script/dom/bindings/codegen/Bindings.conf +++ b/components/script/dom/bindings/codegen/Bindings.conf @@ -218,4 +218,8 @@ DOMInterfaces = { 'inRealms': ['RequestSession', 'SupportsSessionMode'], }, +'SubtleCrypto': { + 'inRealms': ['GenerateKey', 'ExportKey'] +} + } diff --git a/components/script/dom/crypto.rs b/components/script/dom/crypto.rs index 8a2fa81fd36..4d1ead530c2 100644 --- a/components/script/dom/crypto.rs +++ b/components/script/dom/crypto.rs @@ -12,19 +12,20 @@ use uuid::Uuid; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::CryptoBinding::CryptoMethods; use crate::dom::bindings::error::{Error, Fallible}; -use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; -use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::str::USVString; use crate::dom::globalscope::GlobalScope; +use crate::dom::subtlecrypto::SubtleCrypto; use crate::script_runtime::JSContext; // https://developer.mozilla.org/en-US/docs/Web/API/Crypto #[dom_struct] pub struct Crypto { reflector_: Reflector, - #[ignore_malloc_size_of = "Defined in rand"] #[no_trace] rng: DomRefCell<ServoRng>, + subtle: MutNullableDom<SubtleCrypto>, } impl Crypto { @@ -32,6 +33,7 @@ impl Crypto { Crypto { reflector_: Reflector::new(), rng: DomRefCell::new(ServoRng::default()), + subtle: MutNullableDom::default(), } } @@ -41,6 +43,11 @@ impl Crypto { } impl CryptoMethods for Crypto { + /// <https://w3c.github.io/webcrypto/#dfn-Crypto-attribute-subtle> + fn Subtle(&self) -> DomRoot<SubtleCrypto> { + self.subtle.or_init(|| SubtleCrypto::new(&self.global())) + } + #[allow(unsafe_code)] // https://w3c.github.io/webcrypto/#Crypto-method-getRandomValues fn GetRandomValues( diff --git a/components/script/dom/cryptokey.rs b/components/script/dom/cryptokey.rs new file mode 100644 index 00000000000..a9fd0471301 --- /dev/null +++ b/components/script/dom/cryptokey.rs @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::cell::Cell; +use std::ptr::NonNull; + +use dom_struct::dom_struct; +use js::jsapi::{JSObject, Value}; + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{ + CryptoKeyMethods, KeyType, KeyUsage, +}; +use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{AesKeyAlgorithm, KeyAlgorithm}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use crate::js::conversions::ToJSValConvertible; +use crate::script_runtime::JSContext; + +/// The underlying cryptographic data this key represents +#[allow(dead_code)] +pub enum Handle { + Aes128(Vec<u8>), + Aes192(Vec<u8>), + Aes256(Vec<u8>), +} + +/// <https://w3c.github.io/webcrypto/#cryptokey-interface> +#[dom_struct] +pub struct CryptoKey { + reflector_: Reflector, + key_type: KeyType, + extractable: Cell<bool>, + // This would normally be KeyAlgorithm but we cannot Send DOMString, which + // is a member of Algorithm + algorithm: DomRefCell<String>, + usages: Vec<KeyUsage>, + #[ignore_malloc_size_of = "Defined in external cryptography crates"] + #[no_trace] + handle: Handle, +} + +impl CryptoKey { + fn new_inherited( + key_type: KeyType, + extractable: bool, + algorithm: KeyAlgorithm, + usages: Vec<KeyUsage>, + handle: Handle, + ) -> CryptoKey { + CryptoKey { + reflector_: Reflector::new(), + key_type, + extractable: Cell::new(extractable), + algorithm: DomRefCell::new(algorithm.name.to_string()), + usages, + handle, + } + } + + pub fn new( + global: &GlobalScope, + key_type: KeyType, + extractable: bool, + algorithm: KeyAlgorithm, + usages: Vec<KeyUsage>, + handle: Handle, + ) -> DomRoot<CryptoKey> { + reflect_dom_object( + Box::new(CryptoKey::new_inherited( + key_type, + extractable, + algorithm, + usages, + handle, + )), + global, + ) + } + + pub fn algorithm(&self) -> String { + self.algorithm.borrow().to_string() + } + + pub fn handle(&self) -> &Handle { + &self.handle + } +} + +impl CryptoKeyMethods for CryptoKey { + /// <https://w3c.github.io/webcrypto/#cryptokey-interface-members> + fn Type(&self) -> KeyType { + self.key_type.clone() + } + + /// <https://w3c.github.io/webcrypto/#cryptokey-interface-members> + fn Extractable(&self) -> bool { + self.extractable.get() + } + + #[allow(unsafe_code)] + /// <https://w3c.github.io/webcrypto/#cryptokey-interface-members> + fn Algorithm(&self, cx: JSContext) -> NonNull<JSObject> { + let parent = KeyAlgorithm { + name: DOMString::from_string(self.algorithm()), + }; + let algorithm = match self.handle() { + Handle::Aes128(_) => AesKeyAlgorithm { + parent, + length: 128, + }, + Handle::Aes192(_) => AesKeyAlgorithm { + parent, + length: 192, + }, + Handle::Aes256(_) => AesKeyAlgorithm { + parent, + length: 256, + }, + }; + unsafe { + rooted!(in(*cx) let mut alg: Value); + algorithm.to_jsval(*cx, alg.handle_mut()); + NonNull::new(alg.to_object()).unwrap() + } + } + + #[allow(unsafe_code)] + /// <https://w3c.github.io/webcrypto/#cryptokey-interface-members> + fn Usages(&self, cx: JSContext) -> NonNull<JSObject> { + unsafe { + rooted!(in(*cx) let mut usages: Value); + self.usages.to_jsval(*cx, usages.handle_mut()); + NonNull::new(usages.to_object()).unwrap() + } + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index cc5a60c7ef9..059f706b373 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -257,6 +257,7 @@ pub mod console; pub mod constantsourcenode; mod create; pub mod crypto; +pub mod cryptokey; pub mod css; pub mod cssconditionrule; pub mod cssfontfacerule; @@ -543,6 +544,7 @@ pub mod stylepropertymapreadonly; pub mod stylesheet; pub mod stylesheetlist; pub mod submitevent; +pub mod subtlecrypto; pub mod svgelement; pub mod svggraphicselement; pub mod svgsvgelement; diff --git a/components/script/dom/subtlecrypto.rs b/components/script/dom/subtlecrypto.rs new file mode 100644 index 00000000000..81291df87f4 --- /dev/null +++ b/components/script/dom/subtlecrypto.rs @@ -0,0 +1,335 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::ptr; +use std::rc::Rc; + +use dom_struct::dom_struct; +use js::conversions::ConversionResult; +use js::jsapi::JSObject; +use js::jsval::ObjectValue; +use js::typedarray::ArrayBufferU8; +use servo_rand::{RngCore, ServoRng}; + +use crate::dom::bindings::buffer_source::create_buffer_source; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{ + CryptoKeyMethods, KeyType, KeyUsage, +}; +use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{ + AesKeyGenParams, Algorithm, AlgorithmIdentifier, KeyAlgorithm, KeyFormat, SubtleCryptoMethods, +}; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::cryptokey::{CryptoKey, Handle}; +use crate::dom::globalscope::GlobalScope; +use crate::dom::promise::Promise; +use crate::dom::window::Window; +use crate::dom::workerglobalscope::WorkerGlobalScope; +use crate::realms::InRealm; +use crate::script_runtime::JSContext; +use crate::task::TaskCanceller; +use crate::task_source::dom_manipulation::DOMManipulationTaskSource; +use crate::task_source::TaskSource; + +// String constants for algorithms/curves +const ALG_AES_CBC: &str = "AES-CBC"; +const ALG_AES_CTR: &str = "AES-CTR"; +const ALG_AES_GCM: &str = "AES-GCM"; +const ALG_AES_KW: &str = "AES-KW"; +const ALG_SHA1: &str = "SHA1"; +const ALG_SHA256: &str = "SHA256"; +const ALG_SHA384: &str = "SHA384"; +const ALG_SHA512: &str = "SHA512"; +const ALG_HMAC: &str = "HMAC"; +const ALG_HKDF: &str = "HKDF"; +const ALG_PBKDF2: &str = "PBKDF2"; +const ALG_RSASSA_PKCS1: &str = "RSASSA-PKCS1-v1_5"; +const ALG_RSA_OAEP: &str = "RSA-OAEP"; +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, + ALG_AES_GCM, + ALG_AES_KW, + ALG_SHA1, + ALG_SHA256, + ALG_SHA384, + ALG_SHA512, + ALG_HMAC, + ALG_HKDF, + ALG_PBKDF2, + ALG_RSASSA_PKCS1, + ALG_RSA_OAEP, + ALG_RSA_PSS, + ALG_ECDH, + ALG_ECDSA, +]; + +const NAMED_CURVE_P256: &str = "P-256"; +const NAMED_CURVE_P384: &str = "P-384"; +const NAMED_CURVE_P521: &str = "P-521"; +#[allow(dead_code)] +static SUPPORTED_CURVES: &[&str] = &[NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521]; + +#[dom_struct] +pub struct SubtleCrypto { + reflector_: Reflector, + #[no_trace] + rng: DomRefCell<ServoRng>, +} + +impl SubtleCrypto { + fn new_inherited() -> SubtleCrypto { + SubtleCrypto { + reflector_: Reflector::new(), + rng: DomRefCell::new(ServoRng::default()), + } + } + + pub(crate) fn new(global: &GlobalScope) -> DomRoot<SubtleCrypto> { + reflect_dom_object(Box::new(SubtleCrypto::new_inherited()), global) + } + + fn task_source_with_canceller(&self) -> (DOMManipulationTaskSource, TaskCanceller) { + if let Some(window) = self.global().downcast::<Window>() { + window + .task_manager() + .dom_manipulation_task_source_with_canceller() + } else if let Some(worker_global) = self.global().downcast::<WorkerGlobalScope>() { + let task_source = worker_global.dom_manipulation_task_source(); + let canceller = worker_global.task_canceller(); + (task_source, canceller) + } else { + unreachable!("Couldn't downcast to Window or WorkerGlobalScope!"); + } + } +} + +impl SubtleCryptoMethods for SubtleCrypto { + /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-generateKey> + fn GenerateKey( + &self, + cx: JSContext, + algorithm: AlgorithmIdentifier, + extractable: bool, + key_usages: Vec<KeyUsage>, + comp: InRealm, + ) -> Rc<Promise> { + let normalized_algorithm = normalize_algorithm(cx, algorithm, "generateKey"); + let promise = Promise::new_in_current_realm(comp); + if let Err(e) = normalized_algorithm { + 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_cbc(key_usages, key_gen_params, extractable) + }, + _ => Err(Error::NotSupported), + }; + match key { + Ok(key) => promise.resolve_native(&key), + Err(e) => promise.reject_error(e), + } + }), + &canceller, + ); + + promise + } + + /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-exportKey> + #[allow(unsafe_code)] + fn ExportKey(&self, format: KeyFormat, key: &CryptoKey, comp: InRealm) -> Rc<Promise> { + let promise = Promise::new_in_current_realm(comp); + + let (task_source, canceller) = self.task_source_with_canceller(); + let this = Trusted::new(self); + let trusted_key = Trusted::new(key); + let trusted_promise = TrustedPromise::new(promise.clone()); + let _ = task_source.queue_with_canceller( + task!(export_key: move || { + let subtle = this.root(); + let promise = trusted_promise.root(); + let key = trusted_key.root(); + let alg_name = key.algorithm(); + if matches!( + alg_name.as_str(), ALG_SHA1 | ALG_SHA256 | ALG_SHA384 | ALG_SHA512 | ALG_HKDF | ALG_PBKDF2 + ) { + promise.reject_error(Error::NotSupported); + return; + } + if !key.Extractable() { + promise.reject_error(Error::InvalidAccess); + return; + } + let exported_key = match alg_name.as_str() { + ALG_AES_CBC => subtle.export_key_aes_cbc(format, &*key), + _ => Err(Error::NotSupported), + }; + match exported_key { + Ok(k) => { + let cx = GlobalScope::get_cx(); + rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>()); + create_buffer_source::<ArrayBufferU8>(cx, &k, array_buffer_ptr.handle_mut()) + .expect("failed to create buffer source for exported key."); + promise.resolve_native(&array_buffer_ptr.get()) + }, + Err(e) => promise.reject_error(e), + } + }), + &canceller, + ); + + promise + } +} + +#[derive(Clone)] +pub enum NormalizedAlgorithm { + #[allow(dead_code)] + Algorithm(SubtleAlgorithm), + AesKeyGenParams(SubtleAesKeyGenParams), +} + +// 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. + +#[derive(Clone)] +pub struct SubtleAlgorithm { + #[allow(dead_code)] + pub name: String, +} + +impl From<DOMString> for SubtleAlgorithm { + fn from(name: DOMString) -> Self { + SubtleAlgorithm { + name: name.to_string(), + } + } +} + +#[derive(Clone)] +pub struct SubtleAesKeyGenParams { + #[allow(dead_code)] + pub name: String, + pub length: u16, +} + +impl From<AesKeyGenParams> for SubtleAesKeyGenParams { + fn from(params: AesKeyGenParams) -> Self { + SubtleAesKeyGenParams { + name: params.parent.name.to_string(), + length: params.length, + } + } +} + +/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> +#[allow(unsafe_code)] +fn normalize_algorithm( + cx: JSContext, + algorithm: AlgorithmIdentifier, + operation: &str, +) -> Result<NormalizedAlgorithm, Error> { + match algorithm { + AlgorithmIdentifier::String(name) => Ok(NormalizedAlgorithm::Algorithm(name.into())), + AlgorithmIdentifier::Object(obj) => { + rooted!(in(*cx) let value = ObjectValue(unsafe { *obj.get_unsafe() })); + let Ok(ConversionResult::Success(algorithm)) = Algorithm::new(cx, value.handle()) + else { + return Err(Error::Syntax); + }; + match (algorithm.name.str().to_uppercase().as_str(), operation) { + (ALG_AES_CBC, "generateKey") => { + let params_result = + AesKeyGenParams::new(cx, value.handle()).map_err(|_| Error::Operation)?; + let ConversionResult::Success(params) = params_result else { + return Err(Error::Syntax); + }; + Ok(NormalizedAlgorithm::AesKeyGenParams(params.into())) + }, + _ => return Err(Error::NotSupported), + } + }, + } +} + +impl SubtleCrypto { + /// <https://w3c.github.io/webcrypto/#aes-cbc-operations> + fn generate_key_aes_cbc( + &self, + usages: Vec<KeyUsage>, + key_gen_params: SubtleAesKeyGenParams, + extractable: bool, + ) -> Result<DomRoot<CryptoKey>, Error> { + if !matches!(key_gen_params.length, 128 | 192 | 256) { + return Err(Error::Operation); + } + + if usages.iter().any(|usage| { + !matches!( + usage, + KeyUsage::Encrypt | KeyUsage::Decrypt | KeyUsage::WrapKey | KeyUsage::UnwrapKey + ) + }) || usages.is_empty() + { + return Err(Error::Syntax); + } + + let mut rand = Vec::new(); + rand.resize(key_gen_params.length as usize, 0); + self.rng.borrow_mut().fill_bytes(&mut rand); + let handle = match key_gen_params.length { + 128 => Handle::Aes128(rand), + 192 => Handle::Aes192(rand), + 256 => Handle::Aes256(rand), + _ => return Err(Error::Operation), + }; + + Ok(CryptoKey::new( + &self.global(), + KeyType::Secret, + extractable, + KeyAlgorithm { + name: DOMString::from(ALG_AES_CBC), + }, + usages, + handle, + )) + } + + /// <https://w3c.github.io/webcrypto/#aes-cbc-operations> + fn export_key_aes_cbc(&self, format: KeyFormat, key: &CryptoKey) -> Result<Vec<u8>, Error> { + match format { + KeyFormat::Raw => match key.handle() { + Handle::Aes128(key) => Ok(key.as_slice().to_vec()), + Handle::Aes192(key) => Ok(key.as_slice().to_vec()), + Handle::Aes256(key) => Ok(key.as_slice().to_vec()), + }, + KeyFormat::Jwk => { + // TODO: Support jwk + Err(Error::NotSupported) + }, + _ => Err(Error::NotSupported), + } + } +} diff --git a/components/script/dom/webidls/Crypto.webidl b/components/script/dom/webidls/Crypto.webidl index 84b14ff1d0c..0138e33a14f 100644 --- a/components/script/dom/webidls/Crypto.webidl +++ b/components/script/dom/webidls/Crypto.webidl @@ -17,7 +17,7 @@ WorkerGlobalScope includes GlobalCrypto; [Exposed=(Window,Worker)] interface Crypto { - //readonly attribute SubtleCrypto subtle; + [SecureContext] readonly attribute SubtleCrypto subtle; [Throws] ArrayBufferView getRandomValues(ArrayBufferView array); diff --git a/components/script/dom/webidls/CryptoKey.webidl b/components/script/dom/webidls/CryptoKey.webidl new file mode 100644 index 00000000000..1cad0097df2 --- /dev/null +++ b/components/script/dom/webidls/CryptoKey.webidl @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/webcrypto/#cryptokey-interface + +enum KeyType { "public", "private", "secret" }; + +enum KeyUsage { "encrypt", "decrypt", "sign", "verify", "deriveKey", "deriveBits", "wrapKey", "unwrapKey" }; + +[SecureContext, Exposed=(Window,Worker), Serializable, Pref="dom.crypto.subtle.enabled"] +interface CryptoKey { + readonly attribute KeyType type; + readonly attribute boolean extractable; + readonly attribute object algorithm; + readonly attribute object usages; +}; diff --git a/components/script/dom/webidls/SubtleCrypto.webidl b/components/script/dom/webidls/SubtleCrypto.webidl new file mode 100644 index 00000000000..05e2df29ec3 --- /dev/null +++ b/components/script/dom/webidls/SubtleCrypto.webidl @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/webcrypto/#subtlecrypto-interface + +typedef (object or DOMString) AlgorithmIdentifier; + +typedef AlgorithmIdentifier HashAlgorithmIdentifier; + +dictionary Algorithm { + required DOMString name; +}; + +dictionary KeyAlgorithm { + required DOMString name; +}; + +enum KeyFormat { "raw", "spki", "pkcs8", "jwk" }; + +[SecureContext,Exposed=(Window,Worker),Pref="dom.crypto.subtle.enabled"] +interface SubtleCrypto { + // Promise<any> encrypt(AlgorithmIdentifier algorithm, + // CryptoKey key, + // BufferSource data); + // Promise<any> decrypt(AlgorithmIdentifier algorithm, + // CryptoKey key, + // BufferSource data); + // Promise<any> sign(AlgorithmIdentifier algorithm, + // CryptoKey key, + // BufferSource data); + // Promise<any> verify(AlgorithmIdentifier algorithm, + // CryptoKey key, + // BufferSource signature, + // BufferSource data); + // Promise<any> digest(AlgorithmIdentifier algorithm, + // BufferSource data); + + Promise<any> generateKey(AlgorithmIdentifier algorithm, + boolean extractable, + sequence<KeyUsage> keyUsages ); + // Promise<any> deriveKey(AlgorithmIdentifier algorithm, + // CryptoKey baseKey, + // AlgorithmIdentifier derivedKeyType, + // boolean extractable, + // sequence<KeyUsage> keyUsages ); + // Promise<ArrayBuffer> deriveBits(AlgorithmIdentifier algorithm, + // CryptoKey baseKey, + // optional unsigned long? length = null); + + // Promise<CryptoKey> importKey(KeyFormat format, + // (BufferSource or JsonWebKey) keyData, + // AlgorithmIdentifier algorithm, + // boolean extractable, + // sequence<KeyUsage> keyUsages ); + Promise<any> exportKey(KeyFormat format, CryptoKey key); + + // Promise<any> wrapKey(KeyFormat format, + // CryptoKey key, + // CryptoKey wrappingKey, + // AlgorithmIdentifier wrapAlgorithm); + // Promise<CryptoKey> unwrapKey(KeyFormat format, + // BufferSource wrappedKey, + // CryptoKey unwrappingKey, + // AlgorithmIdentifier unwrapAlgorithm, + // AlgorithmIdentifier unwrappedKeyAlgorithm, + // boolean extractable, + // sequence<KeyUsage> keyUsages ); +}; + +// AES shared +dictionary AesKeyAlgorithm : KeyAlgorithm { + required unsigned short length; +}; + +dictionary AesKeyGenParams : Algorithm { + required [EnforceRange] unsigned short length; +}; + +dictionary AesDerivedKeyParams : Algorithm { + required [EnforceRange] unsigned short length; +}; + +// AES_CBC +dictionary AesCbcParams : Algorithm { + required BufferSource iv; +}; |