aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorDaniel Adams <70986246+msub2@users.noreply.github.com>2024-10-08 03:51:08 +0000
committerGitHub <noreply@github.com>2024-10-08 03:51:08 +0000
commitfc0d4d8157c71c512817886bc3b8d1adad5d46a9 (patch)
tree0def1a423dd597400abee4906940d375c3b6ca6f /components
parent66bc430b24a0cb1fe2d51e2b5983ea8833ba22b9 (diff)
downloadservo-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.rs5
-rw-r--r--components/rand/Cargo.toml2
-rw-r--r--components/rand/lib.rs3
-rw-r--r--components/script/Cargo.toml1
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf4
-rw-r--r--components/script/dom/crypto.rs13
-rw-r--r--components/script/dom/cryptokey.rs140
-rw-r--r--components/script/dom/mod.rs2
-rw-r--r--components/script/dom/subtlecrypto.rs335
-rw-r--r--components/script/dom/webidls/Crypto.webidl2
-rw-r--r--components/script/dom/webidls/CryptoKey.webidl17
-rw-r--r--components/script/dom/webidls/SubtleCrypto.webidl87
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;
+};