servo/components/script/dom/cryptokey.rs
Kingsley Yung f3d5617349
script: Check whether the generated crypto key has empty usages (#39328)
The WebCryptoAPI spec requires that when we generate crypto keys by the
generateKey method of SubtleCrypto interface we have to check whether
the usages is empty. If the usages is empty, throw a SyntaxError.

FYI, Step 9 of
https://w3c.github.io/webcrypto/#SubtleCrypto-method-generateKey

We have not yet implemented this logic, and this patch implements it.

Testing: Pass WPT tests that were expected to fail.

---------

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
2025-09-16 17:23:42 +00:00

171 lines
4.8 KiB
Rust

/* 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::{Heap, JSObject, Value};
use js::rust::HandleObject;
use script_bindings::conversions::SafeToJSValConvertible;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
CryptoKeyMethods, KeyType, KeyUsage,
};
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::{CanGc, JSContext};
pub(crate) enum CryptoKeyOrCryptoKeyPair {
CryptoKey(DomRoot<CryptoKey>),
// TODO: CryptoKeyPair(CryptoKeyPair),
}
/// The underlying cryptographic data this key represents
#[allow(dead_code)]
#[derive(MallocSizeOf)]
pub(crate) enum Handle {
Aes128(Vec<u8>),
Aes192(Vec<u8>),
Aes256(Vec<u8>),
Pbkdf2(Vec<u8>),
Hkdf(Vec<u8>),
Hmac(Vec<u8>),
}
/// <https://w3c.github.io/webcrypto/#cryptokey-interface>
#[dom_struct]
pub(crate) struct CryptoKey {
reflector_: Reflector,
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-type>
key_type: KeyType,
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-extractable>
extractable: Cell<bool>,
/// The name of the algorithm used
///
/// This is always the same as the `name` of the
/// [`[[algorithm]]`](https://w3c.github.io/webcrypto/#dom-cryptokey-algorithm)
/// internal slot, but we store it here again for convenience
algorithm: DOMString,
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-algorithm>
#[ignore_malloc_size_of = "Defined in mozjs"]
algorithm_object: Heap<*mut JSObject>,
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-usages>
usages: DomRefCell<Vec<KeyUsage>>,
#[no_trace]
handle: Handle,
}
impl CryptoKey {
fn new_inherited(
key_type: KeyType,
extractable: bool,
usages: Vec<KeyUsage>,
algorithm: DOMString,
handle: Handle,
) -> CryptoKey {
CryptoKey {
reflector_: Reflector::new(),
key_type,
extractable: Cell::new(extractable),
algorithm,
algorithm_object: Heap::default(),
usages: DomRefCell::new(usages),
handle,
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
global: &GlobalScope,
key_type: KeyType,
extractable: bool,
algorithm: DOMString,
algorithm_object: HandleObject,
usages: Vec<KeyUsage>,
handle: Handle,
can_gc: CanGc,
) -> DomRoot<CryptoKey> {
let object = reflect_dom_object(
Box::new(CryptoKey::new_inherited(
key_type,
extractable,
usages,
algorithm,
handle,
)),
global,
can_gc,
);
object.algorithm_object.set(algorithm_object.get());
object
}
pub(crate) fn algorithm(&self) -> String {
self.algorithm.to_string()
}
pub(crate) fn usages(&self) -> Vec<KeyUsage> {
self.usages.borrow().clone()
}
pub(crate) fn handle(&self) -> &Handle {
&self.handle
}
pub(crate) fn set_extractable(&self, extractable: bool) {
self.extractable.set(extractable);
}
pub(crate) fn set_usages(&self, usages: &[KeyUsage]) {
*self.usages.borrow_mut() = usages.to_owned();
}
}
impl CryptoKeyMethods<crate::DomTypeHolder> for CryptoKey {
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-type>
fn Type(&self) -> KeyType {
self.key_type
}
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-extractable>
fn Extractable(&self) -> bool {
self.extractable.get()
}
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-algorithm>
fn Algorithm(&self, _cx: JSContext) -> NonNull<JSObject> {
NonNull::new(self.algorithm_object.get()).unwrap()
}
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-usages>
fn Usages(&self, cx: JSContext) -> NonNull<JSObject> {
rooted!(in(*cx) let mut usages: Value);
self.usages.borrow().safe_to_jsval(cx, usages.handle_mut());
NonNull::new(usages.to_object()).unwrap()
}
}
impl Handle {
pub(crate) fn as_bytes(&self) -> &[u8] {
match self {
Self::Aes128(bytes) => bytes,
Self::Aes192(bytes) => bytes,
Self::Aes256(bytes) => bytes,
Self::Pbkdf2(bytes) => bytes,
Self::Hkdf(bytes) => bytes,
Self::Hmac(bytes) => bytes,
}
}
}