mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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>
This commit is contained in:
parent
66bc430b24
commit
fc0d4d8157
82 changed files with 39536 additions and 557 deletions
|
@ -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,
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -218,4 +218,8 @@ DOMInterfaces = {
|
|||
'inRealms': ['RequestSession', 'SupportsSessionMode'],
|
||||
},
|
||||
|
||||
'SubtleCrypto': {
|
||||
'inRealms': ['GenerateKey', 'ExportKey']
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
140
components/script/dom/cryptokey.rs
Normal file
140
components/script/dom/cryptokey.rs
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
335
components/script/dom/subtlecrypto.rs
Normal file
335
components/script/dom/subtlecrypto.rs
Normal file
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
17
components/script/dom/webidls/CryptoKey.webidl
Normal file
17
components/script/dom/webidls/CryptoKey.webidl
Normal file
|
@ -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;
|
||||
};
|
87
components/script/dom/webidls/SubtleCrypto.webidl
Normal file
87
components/script/dom/webidls/SubtleCrypto.webidl
Normal file
|
@ -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;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue