Files
servo/components/script/dom/subtlecrypto.rs
Sam 2503cc2ab7 script: Pass &CStr insted of &str in {set,get}_dictionary_property (#42125)
It's stupid to create rust strings in the first place to only convert
them into C-style for SM anyway. Furthermore most of those strings are
const literals (comp time strings), so now we will just store additional
null byte within them and able to avoid any runtime conversions.
We should do this in more places.

Testing: Just refactor.

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2026-01-24 13:03:41 +00:00

4533 lines
196 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/. */
mod aes_cbc_operation;
mod aes_common;
mod aes_ctr_operation;
mod aes_gcm_operation;
mod aes_kw_operation;
mod aes_ocb_operation;
mod argon2_operation;
mod chacha20_poly1305_operation;
mod cshake_operation;
mod ecdh_operation;
mod ecdsa_operation;
mod ed25519_operation;
mod hkdf_operation;
mod hmac_operation;
mod ml_dsa_operation;
mod ml_kem_operation;
mod pbkdf2_operation;
mod rsa_common;
mod rsa_oaep_operation;
mod rsa_pss_operation;
mod rsassa_pkcs1_v1_5_operation;
mod sha3_operation;
mod sha_operation;
mod x25519_operation;
use std::fmt::Display;
use std::ptr;
use std::rc::Rc;
use std::str::FromStr;
use base64ct::{Base64UrlUnpadded, Encoding};
use dom_struct::dom_struct;
use js::conversions::ConversionResult;
use js::jsapi::{Heap, JSObject};
use js::jsval::{ObjectValue, UndefinedValue};
use js::rust::wrappers::JS_ParseJSON;
use js::rust::{HandleValue, MutableHandleValue};
use js::typedarray::ArrayBufferU8;
use crate::dom::bindings::buffer_source::create_buffer_source;
use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
};
use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
AeadParams, AesCbcParams, AesCtrParams, AesDerivedKeyParams, AesGcmParams, AesKeyAlgorithm,
AesKeyGenParams, Algorithm, AlgorithmIdentifier, Argon2Params, CShakeParams, ContextParams,
EcKeyAlgorithm, EcKeyGenParams, EcKeyImportParams, EcdhKeyDeriveParams, EcdsaParams,
EncapsulatedBits, EncapsulatedKey, HkdfParams, HmacImportParams, HmacKeyAlgorithm,
HmacKeyGenParams, JsonWebKey, KeyAlgorithm, KeyFormat, Pbkdf2Params, RsaHashedImportParams,
RsaHashedKeyAlgorithm, RsaHashedKeyGenParams, RsaKeyAlgorithm, RsaOaepParams, RsaPssParams,
SubtleCryptoMethods,
};
use crate::dom::bindings::codegen::UnionTypes::{
ArrayBufferViewOrArrayBuffer, ArrayBufferViewOrArrayBufferOrJsonWebKey, ObjectOrString,
};
use crate::dom::bindings::conversions::{SafeFromJSValConvertible, SafeToJSValConvertible};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{DOMString, serialize_jsval_to_json_utf8};
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::set_dictionary_property;
use crate::dom::cryptokey::{CryptoKey, CryptoKeyOrCryptoKeyPair};
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, JSContext};
// Regconized algorithm name from <https://w3c.github.io/webcrypto/>
const ALG_RSASSA_PKCS1_V1_5: &str = "RSASSA-PKCS1-v1_5";
const ALG_RSA_PSS: &str = "RSA-PSS";
const ALG_RSA_OAEP: &str = "RSA-OAEP";
const ALG_ECDSA: &str = "ECDSA";
const ALG_ECDH: &str = "ECDH";
const ALG_ED25519: &str = "Ed25519";
const ALG_X25519: &str = "X25519";
const ALG_AES_CTR: &str = "AES-CTR";
const ALG_AES_CBC: &str = "AES-CBC";
const ALG_AES_GCM: &str = "AES-GCM";
const ALG_AES_KW: &str = "AES-KW";
const ALG_HMAC: &str = "HMAC";
const ALG_SHA1: &str = "SHA-1";
const ALG_SHA256: &str = "SHA-256";
const ALG_SHA384: &str = "SHA-384";
const ALG_SHA512: &str = "SHA-512";
const ALG_HKDF: &str = "HKDF";
const ALG_PBKDF2: &str = "PBKDF2";
// Regconized algorithm name from <https://wicg.github.io/webcrypto-modern-algos/>
const ALG_ML_KEM_512: &str = "ML-KEM-512";
const ALG_ML_KEM_768: &str = "ML-KEM-768";
const ALG_ML_KEM_1024: &str = "ML-KEM-1024";
const ALG_ML_DSA_44: &str = "ML-DSA-44";
const ALG_ML_DSA_65: &str = "ML-DSA-65";
const ALG_ML_DSA_87: &str = "ML-DSA-87";
const ALG_AES_OCB: &str = "AES-OCB";
const ALG_CHACHA20_POLY1305: &str = "ChaCha20-Poly1305";
const ALG_SHA3_256: &str = "SHA3-256";
const ALG_SHA3_384: &str = "SHA3-384";
const ALG_SHA3_512: &str = "SHA3-512";
const ALG_CSHAKE_128: &str = "cSHAKE128";
const ALG_CSHAKE_256: &str = "cSHAKE256";
const ALG_ARGON2D: &str = "Argon2d";
const ALG_ARGON2I: &str = "Argon2i";
const ALG_ARGON2ID: &str = "Argon2id";
static SUPPORTED_ALGORITHMS: &[&str] = &[
ALG_RSASSA_PKCS1_V1_5,
ALG_RSA_PSS,
ALG_RSA_OAEP,
ALG_ECDSA,
ALG_ECDH,
ALG_ED25519,
ALG_X25519,
ALG_AES_CTR,
ALG_AES_CBC,
ALG_AES_GCM,
ALG_AES_KW,
ALG_HMAC,
ALG_SHA1,
ALG_SHA256,
ALG_SHA384,
ALG_SHA512,
ALG_HKDF,
ALG_PBKDF2,
ALG_ML_KEM_512,
ALG_ML_KEM_768,
ALG_ML_KEM_1024,
ALG_ML_DSA_44,
ALG_ML_DSA_65,
ALG_ML_DSA_87,
ALG_AES_OCB,
ALG_CHACHA20_POLY1305,
ALG_SHA3_256,
ALG_SHA3_384,
ALG_SHA3_512,
ALG_CSHAKE_128,
ALG_CSHAKE_256,
ALG_ARGON2D,
ALG_ARGON2I,
ALG_ARGON2ID,
];
// Named elliptic curves
const NAMED_CURVE_P256: &str = "P-256";
const NAMED_CURVE_P384: &str = "P-384";
const NAMED_CURVE_P521: &str = "P-521";
static SUPPORTED_CURVES: &[&str] = &[NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521];
/// <https://w3c.github.io/webcrypto/#supported-operation>
enum Operation {
Encrypt,
Decrypt,
Sign,
Verify,
Digest,
DeriveBits,
WrapKey,
UnwrapKey,
GenerateKey,
ImportKey,
ExportKey,
GetKeyLength,
Encapsulate,
Decapsulate,
}
impl Operation {
fn as_str(&self) -> &'static str {
match self {
Operation::Encrypt => "encrypt",
Operation::Decrypt => "decrypt",
Operation::Sign => "sign",
Operation::Verify => "verify",
Operation::Digest => "digest",
Operation::DeriveBits => "deriveBits",
Operation::WrapKey => "wrapKey",
Operation::UnwrapKey => "unwrapKey",
Operation::GenerateKey => "generateKey",
Operation::ImportKey => "importKey",
Operation::ExportKey => "exportKey",
Operation::GetKeyLength => "get key length",
Operation::Encapsulate => "encapsulate",
Operation::Decapsulate => "decapsulate",
}
}
}
#[dom_struct]
pub(crate) struct SubtleCrypto {
reflector_: Reflector,
}
impl SubtleCrypto {
fn new_inherited() -> SubtleCrypto {
SubtleCrypto {
reflector_: Reflector::new(),
}
}
pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<SubtleCrypto> {
reflect_dom_object(Box::new(SubtleCrypto::new_inherited()), global, can_gc)
}
/// Queue a global task on the crypto task source, given realm's global object, to resolve
/// promise with the result of creating an ArrayBuffer in realm, containing data. If it fails
/// to create buffer source, reject promise with a JSFailedError.
fn resolve_promise_with_data(&self, promise: Rc<Promise>, data: Vec<u8>) {
let trusted_promise = TrustedPromise::new(promise);
self.global().task_manager().crypto_task_source().queue(
task!(resolve_data: move || {
let promise = trusted_promise.root();
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
match create_buffer_source::<ArrayBufferU8>(cx, &data, array_buffer_ptr.handle_mut(), CanGc::note()) {
Ok(_) => promise.resolve_native(&*array_buffer_ptr, CanGc::note()),
Err(_) => promise.reject_error(Error::JSFailed, CanGc::note()),
}
}),
);
}
/// Queue a global task on the crypto task source, given realm's global object, to resolve
/// promise with the result of converting a JsonWebKey dictionary to an ECMAScript Object in
/// realm, as defined by [WebIDL].
fn resolve_promise_with_jwk(&self, promise: Rc<Promise>, jwk: Box<JsonWebKey>) {
// NOTE: Serialize the JsonWebKey dictionary by stringifying it, in order to pass it to
// other threads.
let cx = GlobalScope::get_cx();
let stringified_jwk = match jwk.stringify(cx) {
Ok(stringified_jwk) => stringified_jwk.to_string(),
Err(error) => {
self.reject_promise_with_error(promise, error);
return;
},
};
let trusted_subtle = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise);
self.global()
.task_manager()
.crypto_task_source()
.queue(task!(resolve_jwk: move || {
let subtle = trusted_subtle.root();
let promise = trusted_promise.root();
let cx = GlobalScope::get_cx();
match JsonWebKey::parse(cx, stringified_jwk.as_bytes()) {
Ok(jwk) => {
rooted!(in(*cx) let mut rval = UndefinedValue());
jwk.safe_to_jsval(cx, rval.handle_mut(), CanGc::note());
rooted!(in(*cx) let mut object = rval.to_object());
promise.resolve_native(&*object, CanGc::note());
},
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
}
}));
}
/// Queue a global task on the crypto task source, given realm's global object, to resolve
/// promise with a CryptoKey.
fn resolve_promise_with_key(&self, promise: Rc<Promise>, key: DomRoot<CryptoKey>) {
let trusted_key = Trusted::new(&*key);
let trusted_promise = TrustedPromise::new(promise);
self.global()
.task_manager()
.crypto_task_source()
.queue(task!(resolve_key: move || {
let key = trusted_key.root();
let promise = trusted_promise.root();
promise.resolve_native(&key, CanGc::note());
}));
}
/// Queue a global task on the crypto task source, given realm's global object, to resolve
/// promise with a CryptoKeyPair.
fn resolve_promise_with_key_pair(&self, promise: Rc<Promise>, key_pair: CryptoKeyPair) {
let trusted_private_key = key_pair.privateKey.map(|key| Trusted::new(&*key));
let trusted_public_key = key_pair.publicKey.map(|key| Trusted::new(&*key));
let trusted_promise = TrustedPromise::new(promise);
self.global()
.task_manager()
.crypto_task_source()
.queue(task!(resolve_key: move || {
let key_pair = CryptoKeyPair {
privateKey: trusted_private_key.map(|trusted_key| trusted_key.root()),
publicKey: trusted_public_key.map(|trusted_key| trusted_key.root()),
};
let promise = trusted_promise.root();
promise.resolve_native(&key_pair, CanGc::note());
}));
}
/// Queue a global task on the crypto task source, given realm's global object, to resolve
/// promise with a bool value.
fn resolve_promise_with_bool(&self, promise: Rc<Promise>, result: bool) {
let trusted_promise = TrustedPromise::new(promise);
self.global().task_manager().crypto_task_source().queue(
task!(generate_key_result: move || {
let promise = trusted_promise.root();
promise.resolve_native(&result, CanGc::note());
}),
);
}
/// Queue a global task on the crypto task source, given realm's global object, to reject
/// promise with an error.
fn reject_promise_with_error(&self, promise: Rc<Promise>, error: Error) {
let trusted_promise = TrustedPromise::new(promise);
self.global()
.task_manager()
.crypto_task_source()
.queue(task!(reject_error: move || {
let promise = trusted_promise.root();
promise.reject_error(error, CanGc::note());
}));
}
/// Queue a global task on the crypto task source, given realm's global object, to resolve
/// promise with the result of converting EncapsulatedKey to an ECMAScript Object in realm, as
/// defined by [WebIDL].
fn resolve_promise_with_encapsulated_key(
&self,
promise: Rc<Promise>,
encapsulated_key: SubtleEncapsulatedKey,
) {
let trusted_promise = TrustedPromise::new(promise);
self.global().task_manager().crypto_task_source().queue(
task!(resolve_encapsulated_key: move || {
let promise = trusted_promise.root();
promise.resolve_native(&encapsulated_key, CanGc::note());
}),
);
}
/// Queue a global task on the crypto task source, given realm's global object, to resolve
/// promise with the result of converting EncapsulateBits to an ECMAScript Object in realm, as
/// defined by [WebIDL].
fn resolve_promise_with_encapsulated_bits(
&self,
promise: Rc<Promise>,
encapsulated_bits: SubtleEncapsulatedBits,
) {
let trusted_promise = TrustedPromise::new(promise);
self.global().task_manager().crypto_task_source().queue(
task!(resolve_encapsulated_bits: move || {
let promise = trusted_promise.root();
promise.resolve_native(&encapsulated_bits, CanGc::note());
}),
);
}
}
impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-encrypt>
fn Encrypt(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
key: &CryptoKey,
data: ArrayBufferViewOrArrayBuffer,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm and key be the algorithm and key parameters passed to the
// encrypt() method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let data be the result of getting a copy of the bytes held by the data parameter
// passed to the encrypt() method.
let data = match data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "encrypt".
// Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm =
match normalize_algorithm(cx, Operation::Encrypt, &algorithm, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 5. Let realm be the relevant realm of this.
// Step 6. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 4.
// Step 7. Return promise and perform the remaining steps in parallel.
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(encrypt: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let key = trusted_key.root();
// Step 8. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 9. If the name member of normalizedAlgorithm is not equal to the name
// attribute of the [[algorithm]] internal slot of key then throw an
// InvalidAccessError.
if normalized_algorithm.name() != key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 10. If the [[usages]] internal slot of key does not contain an entry that
// is "encrypt", then throw an InvalidAccessError.
if !key.usages().contains(&KeyUsage::Encrypt) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 11. Let ciphertext be the result of performing the encrypt operation
// specified by normalizedAlgorithm using algorithm and key and with data as
// plaintext.
let ciphertext = match normalized_algorithm.encrypt(&key, &data) {
Ok(ciphertext) => ciphertext,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 12. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 13. Let result be the result of creating an ArrayBuffer in realm,
// containing ciphertext.
// Step 14. Resolve promise with result.
subtle.resolve_promise_with_data(promise, ciphertext);
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-decrypt>
fn Decrypt(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
key: &CryptoKey,
data: ArrayBufferViewOrArrayBuffer,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm and key be the algorithm and key parameters passed to the
// decrypt() method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let data be the result of getting a copy of the bytes held by the data parameter
// passed to the decrypt() method.
let data = match data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "decrypt".
// Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm =
match normalize_algorithm(cx, Operation::Decrypt, &algorithm, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 5. Let realm be the relevant realm of this.
// Step 6. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 4.
// Step 7. Return promise and perform the remaining steps in parallel.
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(decrypt: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let key = trusted_key.root();
// Step 8. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 9. If the name member of normalizedAlgorithm is not equal to the name
// attribute of the [[algorithm]] internal slot of key then throw an
// InvalidAccessError.
if normalized_algorithm.name() != key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 10. If the [[usages]] internal slot of key does not contain an entry that
// is "decrypt", then throw an InvalidAccessError.
if !key.usages().contains(&KeyUsage::Decrypt) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 11. Let plaintext be the result of performing the decrypt operation
// specified by normalizedAlgorithm using key and algorithm and with data as
// ciphertext.
let plaintext = match normalized_algorithm.decrypt(&key, &data) {
Ok(plaintext) => plaintext,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 12. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 13. Let result be the result of creating an ArrayBuffer in realm,
// containing plaintext.
// Step 14. Resolve promise with result.
subtle.resolve_promise_with_data(promise, plaintext);
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-sign>
fn Sign(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
key: &CryptoKey,
data: ArrayBufferViewOrArrayBuffer,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm and key be the algorithm and key parameters passed to the sign()
// method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let data be the result of getting a copy of the bytes held by the data parameter
// passed to the sign() method.
let data = match &data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "sign".
// Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm =
match normalize_algorithm(cx, Operation::Sign, &algorithm, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 5. Let realm be the relevant realm of this.
// Step 6. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 4.
// Step 7. Return promise and perform the remaining steps in parallel.
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(sign: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let key = trusted_key.root();
// Step 8. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 9. If the name member of normalizedAlgorithm is not equal to the name
// attribute of the [[algorithm]] internal slot of key then throw an
// InvalidAccessError.
if normalized_algorithm.name() != key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 10. If the [[usages]] internal slot of key does not contain an entry that
// is "sign", then throw an InvalidAccessError.
if !key.usages().contains(&KeyUsage::Sign) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 11. Let signature be the result of performing the sign operation specified
// by normalizedAlgorithm using key and algorithm and with data as message.
let signature = match normalized_algorithm.sign(&key, &data) {
Ok(signature) => signature,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 12. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 13. Let result be the result of creating an ArrayBuffer in realm,
// containing signature.
// Step 14. Resolve promise with result.
subtle.resolve_promise_with_data(promise, signature);
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-verify>
fn Verify(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
key: &CryptoKey,
signature: ArrayBufferViewOrArrayBuffer,
data: ArrayBufferViewOrArrayBuffer,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm and key be the algorithm and key parameters passed to the verify()
// method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let signature be the result of getting a copy of the bytes held by the signature
// parameter passed to the verify() method.
let signature = match &signature {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 3. Let data be the result of getting a copy of the bytes held by the data parameter
// passed to the verify() method.
let data = match &data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 4. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to
// algorithm and op set to "verify".
// Step 5. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm =
match normalize_algorithm(cx, Operation::Verify, &algorithm, can_gc) {
Ok(algorithm) => algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 6. Let realm be the relevant realm of this.
// Step 7. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 5.
// Step 8. Return promise and perform the remaining steps in parallel.
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(sign: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let key = trusted_key.root();
// Step 9. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 10. If the name member of normalizedAlgorithm is not equal to the name
// attribute of the [[algorithm]] internal slot of key then throw an
// InvalidAccessError.
if normalized_algorithm.name() != key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 11. If the [[usages]] internal slot of key does not contain an entry that
// is "verify", then throw an InvalidAccessError.
if !key.usages().contains(&KeyUsage::Verify) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 12. Let result be the result of performing the verify operation specified
// by normalizedAlgorithm using key, algorithm and signature and with data as
// message.
let result = match normalized_algorithm.verify(&key, &data, &signature) {
Ok(result) => result,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 13. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 14. Resolve promise with result.
subtle.resolve_promise_with_bool(promise, result);
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-digest>
fn Digest(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
data: ArrayBufferViewOrArrayBuffer,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm be the algorithm parameter passed to the digest() method.
// NOTE: We did that in method parameter.
// Step 2. Let data be the result of getting a copy of the bytes held by the
// data parameter passed to the digest() method.
let data = match data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm,
// with alg set to algorithm and op set to "digest".
// Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm =
match normalize_algorithm(cx, Operation::Digest, &algorithm, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 5. Let realm be the relevant realm of this.
// Step 6. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 3.
// Step 7. Return promise and perform the remaining steps in parallel.
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(generate_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
// Step 8. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 9. Let digest be the result of performing the digest operation specified by
// normalizedAlgorithm using algorithm, with data as message.
let digest = match normalized_algorithm.digest(&data) {
Ok(digest) => digest,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
}
};
// Step 10. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 11. Let result be the result of creating an ArrayBuffer in realm,
// containing digest.
// Step 12. Resolve promise with result.
subtle.resolve_promise_with_data(promise, digest);
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-generateKey>
fn GenerateKey(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
extractable: bool,
key_usages: Vec<KeyUsage>,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm, extractable and usages be the algorithm, extractable and
// keyUsages parameters passed to the generateKey() method, respectively.
// Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "generateKey".
// Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm =
match normalize_algorithm(cx, Operation::GenerateKey, &algorithm, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 4. Let realm be the relevant realm of this.
// Step 5. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 3.
// Step 6. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(generate_key: move || {
let subtle = trusted_subtle.root();
let promise = trusted_promise.root();
// Step 7. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 8. Let result be the result of performing the generate key operation
// specified by normalizedAlgorithm using algorithm, extractable and usages.
let result = match normalized_algorithm.generate_key(
&subtle.global(),
extractable,
key_usages,
CanGc::note(),
) {
Ok(result) => result,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
}
};
// Step 9.
// If result is a CryptoKey object:
// If the [[type]] internal slot of result is "secret" or "private" and usages
// is empty, then throw a SyntaxError.
// If result is a CryptoKeyPair object:
// If the [[usages]] internal slot of the privateKey attribute of result is the
// empty sequence, then throw a SyntaxError.
// TODO: Implement CryptoKeyPair case
match &result {
CryptoKeyOrCryptoKeyPair::CryptoKey(crpyto_key) => {
if matches!(crpyto_key.Type(), KeyType::Secret | KeyType::Private)
&& crpyto_key.usages().is_empty()
{
subtle.reject_promise_with_error(promise, Error::Syntax(None));
return;
}
},
CryptoKeyOrCryptoKeyPair::CryptoKeyPair(crypto_key_pair) => {
if crypto_key_pair.privateKey.as_ref().is_none_or(|private_key| private_key.usages().is_empty()) {
subtle.reject_promise_with_error(promise, Error::Syntax(None));
return;
}
}
};
// Step 10. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 11. Let result be the result of converting result to an ECMAScript Object
// in realm, as defined by [WebIDL].
// Step 12. Resolve promise with result.
match result {
CryptoKeyOrCryptoKeyPair::CryptoKey(key) => {
subtle.resolve_promise_with_key(promise, key);
},
CryptoKeyOrCryptoKeyPair::CryptoKeyPair(key_pair) => {
subtle.resolve_promise_with_key_pair(promise, key_pair);
},
}
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-deriveKey>
fn DeriveKey(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
base_key: &CryptoKey,
derived_key_type: AlgorithmIdentifier,
extractable: bool,
usages: Vec<KeyUsage>,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm, baseKey, derivedKeyType, extractable and usages be the algorithm,
// baseKey, derivedKeyType, extractable and keyUsages parameters passed to the deriveKey()
// method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "deriveBits".
// Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm =
match normalize_algorithm(cx, Operation::DeriveBits, &algorithm, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 4. Let normalizedDerivedKeyAlgorithmImport be the result of normalizing an
// algorithm, with alg set to derivedKeyType and op set to "importKey".
// Step 5. If an error occurred, return a Promise rejected with
// normalizedDerivedKeyAlgorithmImport.
let normalized_derived_key_algorithm_import =
match normalize_algorithm(cx, Operation::ImportKey, &derived_key_type, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 6. Let normalizedDerivedKeyAlgorithmLength be the result of normalizing an
// algorithm, with alg set to derivedKeyType and op set to "get key length".
// Step 7. If an error occurred, return a Promise rejected with
// normalizedDerivedKeyAlgorithmLength.
let normalized_derived_key_algorithm_length =
match normalize_algorithm(cx, Operation::GetKeyLength, &derived_key_type, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 8. Let realm be the relevant realm of this.
// Step 9. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 3.
// Step 10. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_base_key = Trusted::new(base_key);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global().task_manager().dom_manipulation_task_source().queue(
task!(derive_key: move || {
let subtle = trusted_subtle.root();
let base_key = trusted_base_key.root();
let promise = trusted_promise.root();
// Step 11. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 12. If the name member of normalizedAlgorithm is not equal to the name
// attribute of the [[algorithm]] internal slot of baseKey then throw an
// InvalidAccessError.
if normalized_algorithm.name() != base_key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 13. If the [[usages]] internal slot of baseKey does not contain an entry
// that is "deriveKey", then throw an InvalidAccessError.
if !base_key.usages().contains(&KeyUsage::DeriveKey) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 14. Let length be the result of performing the get key length algorithm
// specified by normalizedDerivedKeyAlgorithmLength using derivedKeyType.
let length = match normalized_derived_key_algorithm_length.get_key_length() {
Ok(length) => length,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
}
};
// Step 15. Let secret be the result of performing the derive bits operation
// specified by normalizedAlgorithm using key, algorithm and length.
let secret = match normalized_algorithm.derive_bits(&base_key, length) {
Ok(secret) => secret,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
}
};
// Step 16. Let result be the result of performing the import key operation
// specified by normalizedDerivedKeyAlgorithmImport using "raw" as format, secret
// as keyData, derivedKeyType as algorithm and using extractable and usages.
// NOTE: Use "raw-secret" instead, according to
// <https://wicg.github.io/webcrypto-modern-algos/#subtlecrypto-interface-keyformat>.
let result = match normalized_derived_key_algorithm_import.import_key(
&subtle.global(),
KeyFormat::Raw_secret,
&secret,
extractable,
usages.clone(),
CanGc::note(),
) {
Ok(algorithm) => algorithm,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 17. If the [[type]] internal slot of result is "secret" or "private" and
// usages is empty, then throw a SyntaxError.
if matches!(result.Type(), KeyType::Secret | KeyType::Private) && usages.is_empty() {
subtle.reject_promise_with_error(promise, Error::Syntax(None));
return;
}
// Step 18. Set the [[extractable]] internal slot of result to extractable.
// Step 19. Set the [[usages]] internal slot of result to the normalized value of
// usages.
// NOTE: Done by normalized_derived_key_algorithm_import.import_key in Step 16.
// Step 20. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 20. Let result be the result of converting result to an ECMAScript Object
// in realm, as defined by [WebIDL].
// Step 20. Resolve promise with result.
subtle.resolve_promise_with_key(promise, result);
}),
);
promise
}
/// <https://w3c.github.io/webcrypto/#dfn-SubtleCrypto-method-deriveBits>
fn DeriveBits(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
base_key: &CryptoKey,
length: Option<u32>,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm, baseKey and length, be the algorithm, baseKey and length
// parameters passed to the deriveBits() method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "deriveBits".
// Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm =
match normalize_algorithm(cx, Operation::DeriveBits, &algorithm, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 4. Let realm be the relevant realm of this.
// Step 5. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 3.
// Step 5. Return promise and perform the remaining steps in parallel.
let trsuted_subtle = Trusted::new(self);
let trusted_base_key = Trusted::new(base_key);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(import_key: move || {
let subtle = trsuted_subtle.root();
let base_key = trusted_base_key.root();
let promise = trusted_promise.root();
// Step 7. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 8. If the name member of normalizedAlgorithm is not equal to the name
// attribute of the [[algorithm]] internal slot of baseKey then throw an
// InvalidAccessError.
if normalized_algorithm.name() != base_key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 9. If the [[usages]] internal slot of baseKey does not contain an entry
// that is "deriveBits", then throw an InvalidAccessError.
if !base_key.usages().contains(&KeyUsage::DeriveBits) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 10. Let bits be the result of performing the derive bits operation
// specified by normalizedAlgorithm using baseKey, algorithm and length.
let bits = match normalized_algorithm.derive_bits(&base_key, length) {
Ok(bits) => bits,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
}
};
// Step 11. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 12. Let result be the result of creating an ArrayBuffer in realm,
// containing bits.
// Step 13. Resolve promise with result.
subtle.resolve_promise_with_data(promise, bits);
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-importKey>
fn ImportKey(
&self,
cx: JSContext,
format: KeyFormat,
key_data: ArrayBufferViewOrArrayBufferOrJsonWebKey,
algorithm: AlgorithmIdentifier,
extractable: bool,
key_usages: Vec<KeyUsage>,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let format, algorithm, extractable and usages, be the format, algorithm,
// extractable and keyUsages parameters passed to the importKey() method, respectively.
// Step 5. Let realm be the relevant realm of this.
// Step 6. Let promise be a new Promise.
let promise = Promise::new_in_current_realm(comp, can_gc);
// Step 2.
let key_data = match format {
// If format is equal to the string "jwk":
KeyFormat::Jwk => {
match key_data {
ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBufferView(_) |
ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBuffer(_) => {
// Step 2.1. If the keyData parameter passed to the importKey() method is
// not a JsonWebKey dictionary, throw a TypeError.
promise.reject_error(
Error::Type("The keyData type does not match the format".to_string()),
can_gc,
);
return promise;
},
ArrayBufferViewOrArrayBufferOrJsonWebKey::JsonWebKey(jwk) => {
// Step 2.2. Let keyData be the keyData parameter passed to the importKey() method.
//
// NOTE: Serialize JsonWebKey throught stringifying it.
// JsonWebKey::stringify internally relies on ToJSON, so it will raise an
// exception when a JS error is thrown. When this happens, we report the
// error.
match jwk.stringify(cx) {
Ok(stringified) => stringified.as_bytes().to_vec(),
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
}
},
}
},
// Otherwise:
_ => {
match key_data {
// Step 2.1. If the keyData parameter passed to the importKey() method is a
// JsonWebKey dictionary, throw a TypeError.
ArrayBufferViewOrArrayBufferOrJsonWebKey::JsonWebKey(_) => {
promise.reject_error(
Error::Type("The keyData type does not match the format".to_string()),
can_gc,
);
return promise;
},
// Step 2.2. Let keyData be the result of getting a copy of the bytes held by
// the keyData parameter passed to the importKey() method.
ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBufferView(view) => {
view.to_vec()
},
ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBuffer(buffer) => {
buffer.to_vec()
},
}
},
};
// Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "importKey".
// Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let normalized_algorithm =
match normalize_algorithm(cx, Operation::ImportKey, &algorithm, can_gc) {
Ok(algorithm) => algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 7. Return promise and perform the remaining steps in parallel.
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(import_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
// Step 8. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 9. Let result be the CryptoKey object that results from performing the
// import key operation specified by normalizedAlgorithm using keyData, algorithm,
// format, extractable and usages.
let result = match normalized_algorithm.import_key(
&subtle.global(),
format,
&key_data,
extractable,
key_usages.clone(),
CanGc::note()
) {
Ok(key) => key,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 10. If the [[type]] internal slot of result is "secret" or "private" and
// usages is empty, then throw a SyntaxError.
if matches!(result.Type(), KeyType::Secret | KeyType::Private) && key_usages.is_empty() {
subtle.reject_promise_with_error(promise, Error::Syntax(None));
return;
}
// Step 11. Set the [[extractable]] internal slot of result to extractable.
result.set_extractable(extractable);
// Step 12. Set the [[usages]] internal slot of result to the normalized value of usages.
result.set_usages(&key_usages);
// Step 13. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 14. Let result be the result of converting result to an ECMAScript Object
// in realm, as defined by [WebIDL].
// Step 15. Resolve promise with result.
subtle.resolve_promise_with_key(promise, result);
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-exportKey>
fn ExportKey(
&self,
format: KeyFormat,
key: &CryptoKey,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let format and key be the format and key parameters passed to the exportKey()
// method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let realm be the relevant realm of this.
// Step 3. Let promise be a new Promise.
let promise = Promise::new_in_current_realm(comp, can_gc);
// Step 4. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(export_key: move || {
let subtle = trusted_subtle.root();
let promise = trusted_promise.root();
let key = trusted_key.root();
// Step 5. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 6. If the name member of the [[algorithm]] internal slot of key does not
// identify a registered algorithm that supports the export key operation, then
// throw a NotSupportedError.
let registered_algorithm = match SupportedAlgorithm::try_from(key.algorithm().name()) {
Ok(registered_algorithm) => registered_algorithm,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
if registered_algorithm.support(Operation::ExportKey).is_err() {
subtle.reject_promise_with_error(promise, Error::NotSupported(None));
return;
}
// Step 7. If the [[extractable]] internal slot of key is false, then throw an
// InvalidAccessError.
if !key.Extractable() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 8. Let result be the result of performing the export key operation
// specified by the [[algorithm]] internal slot of key using key and format.
let result = match perform_export_key_operation(format, &key) {
Ok(exported_key) => exported_key,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 9. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 10.
// If format is equal to the string "jwk":
// Let result be the result of converting result to an ECMAScript Object in
// realm, as defined by [WebIDL].
// Otherwise:
// Let result be the result of creating an ArrayBuffer in realm, containing
// result.
// Step 11. Resolve promise with result.
// NOTE: We determine the format by pattern matching on result, which is an
// ExportedKey enum.
match result {
ExportedKey::Bytes(bytes) => {
subtle.resolve_promise_with_data(promise, bytes);
},
ExportedKey::Jwk(jwk) => {
subtle.resolve_promise_with_jwk(promise, jwk);
},
}
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-wrapKey>
fn WrapKey(
&self,
cx: JSContext,
format: KeyFormat,
key: &CryptoKey,
wrapping_key: &CryptoKey,
algorithm: AlgorithmIdentifier,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let format, key, wrappingKey and algorithm be the format, key, wrappingKey and
// wrapAlgorithm parameters passed to the wrapKey() method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "wrapKey".
let mut normalized_algorithm_result =
normalize_algorithm(cx, Operation::WrapKey, &algorithm, can_gc);
// Step 3. If an error occurred, let normalizedAlgorithm be the result of normalizing an
// algorithm, with alg set to algorithm and op set to "encrypt".
if normalized_algorithm_result.is_err() {
normalized_algorithm_result =
normalize_algorithm(cx, Operation::Encrypt, &algorithm, can_gc);
}
// Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm = match normalized_algorithm_result {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 5. Let realm be the relevant realm of this.
// Step 6. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 4.
// Step 7. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_key = Trusted::new(key);
let trusted_wrapping_key = Trusted::new(wrapping_key);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(wrap_key: move || {
let subtle = trusted_subtle.root();
let key = trusted_key.root();
let wrapping_key = trusted_wrapping_key.root();
let promise = trusted_promise.root();
// Step 8. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 9. If the name member of normalizedAlgorithm is not equal to the name
// attribute of the [[algorithm]] internal slot of wrappingKey then throw an
// InvalidAccessError.
if normalized_algorithm.name() != wrapping_key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 10. If the [[usages]] internal slot of wrappingKey does not contain an
// entry that is "wrapKey", then throw an InvalidAccessError.
if !wrapping_key.usages().contains(&KeyUsage::WrapKey) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 11. If the algorithm identified by the [[algorithm]] internal slot of key
// does not support the export key operation, then throw a NotSupportedError.
let registered_algorithm = match SupportedAlgorithm::try_from(key.algorithm().name()) {
Ok(registered_algorithm) => registered_algorithm,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
if registered_algorithm.support(Operation::ExportKey).is_err() {
subtle.reject_promise_with_error(promise, Error::NotSupported(None));
return;
}
// Step 12. If the [[extractable]] internal slot of key is false, then throw an
// InvalidAccessError.
if !key.Extractable() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 13. Let exportedKey be the result of performing the export key operation
// specified by the [[algorithm]] internal slot of key using key and format.
let exported_key = match perform_export_key_operation(format, &key) {
Ok(exported_key) => exported_key,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 14.
// If format is equal to the string "jwk":
// Step 14.1. Let json be the result of representing exportedKey as a UTF-16
// string conforming to the JSON grammar; for example, by executing the
// JSON.stringify algorithm specified in [ECMA-262] in the context of a new
// global object.
// Step 14.2. Let bytes be the result of UTF-8 encoding json.
// Otherwise:
// Let bytes be exportedKey.
// NOTE: We determine the format by pattern matching on result, which is an
// ExportedKey enum.
let cx = GlobalScope::get_cx();
let bytes = match exported_key {
ExportedKey::Bytes(bytes) => bytes,
ExportedKey::Jwk(jwk) => match jwk.stringify(cx) {
Ok(stringified_jwk) => stringified_jwk.as_bytes().to_vec(),
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
},
};
// Step 15.
// If normalizedAlgorithm supports the wrap key operation:
// Let result be the result of performing the wrap key operation specified by
// normalizedAlgorithm using algorithm, wrappingKey as key and bytes as
// plaintext.
// Otherwise, if normalizedAlgorithm supports the encrypt operation:
// Let result be the result of performing the encrypt operation specified by
// normalizedAlgorithm using algorithm, wrappingKey as key and bytes as
// plaintext.
// Otherwise:
// throw a NotSupportedError.
let mut result = normalized_algorithm.wrap_key(&wrapping_key, &bytes);
if result.is_err() {
result = normalized_algorithm.encrypt(&wrapping_key, &bytes);
}
let result = match result {
Ok(result) => result,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 16. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 17. Let result be the result of creating an ArrayBuffer in realm,
// containing result.
// Step 18. Resolve promise with result.
subtle.resolve_promise_with_data(promise, result);
}));
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-unwrapKey>
fn UnwrapKey(
&self,
cx: JSContext,
format: KeyFormat,
wrapped_key: ArrayBufferViewOrArrayBuffer,
unwrapping_key: &CryptoKey,
algorithm: AlgorithmIdentifier,
unwrapped_key_algorithm: AlgorithmIdentifier,
extractable: bool,
usages: Vec<KeyUsage>,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let format, unwrappingKey, algorithm, unwrappedKeyAlgorithm, extractable and
// usages, be the format, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm,
// extractable and keyUsages parameters passed to the unwrapKey() method, respectively.
// NOTE: We did that in method parameter.
// Step 2. Let wrappedKey be the result of getting a copy of the bytes held by the
// wrappedKey parameter passed to the unwrapKey() method.
let wrapped_key = match wrapped_key {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
// to algorithm and op set to "unwrapKey".
let mut normalized_algorithm =
normalize_algorithm(cx, Operation::UnwrapKey, &algorithm, can_gc);
// Step 4. If an error occurred, let normalizedAlgorithm be the result of normalizing an
// algorithm, with alg set to algorithm and op set to "decrypt".
if normalized_algorithm.is_err() {
normalized_algorithm = normalize_algorithm(cx, Operation::Decrypt, &algorithm, can_gc);
}
// Step 5. If an error occurred, return a Promise rejected with normalizedAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm = match normalized_algorithm {
Ok(algorithm) => algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 6. Let normalizedKeyAlgorithm be the result of normalizing an algorithm, with alg
// set to unwrappedKeyAlgorithm and op set to "importKey".
// Step 7. If an error occurred, return a Promise rejected with normalizedKeyAlgorithm.
let normalized_key_algorithm =
match normalize_algorithm(cx, Operation::ImportKey, &unwrapped_key_algorithm, can_gc) {
Ok(algorithm) => algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 8. Let realm be the relevant realm of this.
// Step 9. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 5.
// Step 10. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_unwrapping_key = Trusted::new(unwrapping_key);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global().task_manager().dom_manipulation_task_source().queue(
task!(unwrap_key: move || {
let subtle = trusted_subtle.root();
let unwrapping_key = trusted_unwrapping_key.root();
let promise = trusted_promise.root();
// Step 11. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 12. If the name member of normalizedAlgorithm is not equal to the name
// attribute of the [[algorithm]] internal slot of unwrappingKey then throw an
// InvalidAccessError.
if normalized_algorithm.name() != unwrapping_key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 13. If the [[usages]] internal slot of unwrappingKey does not contain an
// entry that is "unwrapKey", then throw an InvalidAccessError.
if !unwrapping_key.usages().contains(&KeyUsage::UnwrapKey) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(None));
return;
}
// Step 14.
// If normalizedAlgorithm supports an unwrap key operation:
// Let bytes be the result of performing the unwrap key operation specified by
// normalizedAlgorithm using algorithm, unwrappingKey as key and wrappedKey as
// ciphertext.
// Otherwise, if normalizedAlgorithm supports a decrypt operation:
// Let bytes be the result of performing the decrypt operation specified by
// normalizedAlgorithm using algorithm, unwrappingKey as key and wrappedKey as
// ciphertext.
// Otherwise:
// throw a NotSupportedError.
let mut bytes = normalized_algorithm.unwrap_key(&unwrapping_key, &wrapped_key);
if bytes.is_err() {
bytes = normalized_algorithm.decrypt(&unwrapping_key, &wrapped_key);
}
let bytes = match bytes {
Ok(bytes) => bytes,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 15.
// If format is equal to the string "jwk":
// Let key be the result of executing the parse a JWK algorithm, with bytes as
// the data to be parsed.
// NOTE: We only parse bytes by executing the parse a JWK algorithm, but keep
// it as raw bytes for later steps, instead of converting it to a JsonWebKey
// dictionary.
//
// Otherwise:
// Let key be bytes.
let cx = GlobalScope::get_cx();
if format == KeyFormat::Jwk {
if let Err(error) = JsonWebKey::parse(cx, &bytes) {
subtle.reject_promise_with_error(promise, error);
return;
}
}
let key = bytes;
// Step 16. Let result be the result of performing the import key operation
// specified by normalizedKeyAlgorithm using unwrappedKeyAlgorithm as algorithm,
// format, usages and extractable and with key as keyData.
let result = match normalized_key_algorithm.import_key(
&subtle.global(),
format,
&key,
extractable,
usages.clone(),
CanGc::note(),
) {
Ok(result) => result,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 17. If the [[type]] internal slot of result is "secret" or "private" and
// usages is empty, then throw a SyntaxError.
if matches!(result.Type(), KeyType::Secret | KeyType::Private) && usages.is_empty() {
subtle.reject_promise_with_error(promise, Error::Syntax(None));
return;
}
// Step 18. Set the [[extractable]] internal slot of result to extractable.
// Step 19. Set the [[usages]] internal slot of result to the normalized value of
// usages.
// NOTE: Done by normalized_algorithm.import_key in Step 16.
// Step 20. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 21. Let result be the result of converting result to an ECMAScript Object
// in realm, as defined by [WebIDL].
// Step 22. Resolve promise with result.
subtle.resolve_promise_with_key(promise, result);
}),
);
promise
}
/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-encapsulateKey>
fn EncapsulateKey(
&self,
cx: JSContext,
encapsulation_algorithm: AlgorithmIdentifier,
encapsulation_key: &CryptoKey,
shared_key_algorithm: AlgorithmIdentifier,
extractable: bool,
usages: Vec<KeyUsage>,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let encapsulationAlgorithm, encapsulationKey, sharedKeyAlgorithm, extractable
// and usages be the encapsulationAlgorithm, encapsulationKey, sharedKeyAlgorithm,
// extractable and keyUsages parameters passed to the encapsulateKey() method,
// respectively.
// Step 2. Let normalizedEncapsulationAlgorithm be the result of normalizing an algorithm,
// with alg set to encapsulationAlgorithm and op set to "encapsulate".
// Step 3. If an error occurred, return a Promise rejected with
// normalizedEncapsulationAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_encapsulation_algorithm =
match normalize_algorithm(cx, Operation::Encapsulate, &encapsulation_algorithm, can_gc)
{
Ok(algorithm) => algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 4. Let normalizedSharedKeyAlgorithm be the result of normalizing an algorithm, with
// alg set to sharedKeyAlgorithm and op set to "importKey".
// Step 5. If an error occurred, return a Promise rejected with
// normalizedSharedKeyAlgorithm.
let normalized_shared_key_algorithm =
match normalize_algorithm(cx, Operation::ImportKey, &shared_key_algorithm, can_gc) {
Ok(algorithm) => algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 6. Let realm be the relevant realm of this.
// Step 7. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 3.
// Step 8. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_encapsulated_key = Trusted::new(encapsulation_key);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global().task_manager().dom_manipulation_task_source().queue(
task!(encapsulate_keys: move || {
let subtle = trusted_subtle.root();
let encapsulation_key = trusted_encapsulated_key.root();
let promise = trusted_promise.root();
// Step 9. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 10. If the name member of normalizedEncapsulationAlgorithm is not equal to
// the name attribute of the [[algorithm]] internal slot of encapsulationKey then
// throw an InvalidAccessError.
if normalized_encapsulation_algorithm.name() != encapsulation_key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(Some(
"[[algorithm]] internal slot of encapsulationKey is not equal to \
normalizedEncapsulationAlgorithm".to_string(),
)));
return;
}
// Step 11. If the [[usages]] internal slot of encapsulationKey does not contain an
// entry that is "encapsulateKey", then throw an InvalidAccessError.
if !encapsulation_key.usages().contains(&KeyUsage::EncapsulateKey) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(Some(
"[[usages]] internal slot of encapsulationKey does not contain an \
entry that is \"encapsulateBits\"".to_string(),
)));
return;
}
// Step 12. Let encapsulatedBits be the result of performing the encapsulate
// operation specified by the [[algorithm]] internal slot of encapsulationKey using
// encapsulationKey.
// NOTE: Step 10 guarantees normalizedEncapsulationAlgorithm specifies the same
// algorithm as the [[algorithm]] internal slot of encapsulationKey.
let encapsulated_bits_result =
normalized_encapsulation_algorithm.encapsulate(&encapsulation_key);
let encapsulated_bits = match encapsulated_bits_result {
Ok(encapsulated_bits) => encapsulated_bits,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 13. Let sharedKey be the result of performing the import key operation
// specified by normalizedSharedKeyAlgorithm using "raw-secret" as format, the
// sharedKey field of encapsulatedBits as keyData, sharedKeyAlgorithm as algorithm
// and using extractable and usages.
// Step 14. Set the [[extractable]] internal slot of sharedKey to extractable.
// Step 15. Set the [[usages]] internal slot of sharedKey to the normalized value
// of usages.
let encapsulated_shared_key = match &encapsulated_bits.shared_key {
Some(shared_key) => shared_key,
None => {
subtle.reject_promise_with_error(promise, Error::Operation(Some(
"Shared key is missing in the result of the encapsulate operation"
.to_string())));
return;
},
};
let shared_key_result = normalized_shared_key_algorithm.import_key(
&subtle.global(),
KeyFormat::Raw_secret,
encapsulated_shared_key,
extractable,
usages.clone(),
CanGc::note(),
);
let shared_key = match shared_key_result {
Ok(shared_key) => shared_key,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 16. Let encapsulatedKey be a new EncapsulatedKey dictionary with sharedKey
// set to sharedKey and ciphertext set to the ciphertext field of encapsulatedBits.
let encapsulated_key = SubtleEncapsulatedKey {
shared_key: Some(Trusted::new(&shared_key)),
ciphertext:encapsulated_bits.ciphertext,
};
// Step 17. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 18. Let result be the result of converting encapsulatedKey to an ECMAScript
// Object in realm, as defined by [WebIDL].
// Step 19. Resolve promise with result.
subtle.resolve_promise_with_encapsulated_key(promise, encapsulated_key);
})
);
promise
}
/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-encapsulateBits>
fn EncapsulateBits(
&self,
cx: JSContext,
encapsulation_algorithm: AlgorithmIdentifier,
encapsulation_key: &CryptoKey,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let encapsulationAlgorithm and encapsulationKey be the encapsulationAlgorithm
// and encapsulationKey parameters passed to the encapsulateBits() method, respectively.
// Step 2. Let normalizedEncapsulationAlgorithm be the result of normalizing an algorithm,
// with alg set to encapsulationAlgorithm and op set to "encapsulate".
// Step 3. If an error occurred, return a Promise rejected with
// normalizedEncapsulationAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_encapsulation_algorithm =
match normalize_algorithm(cx, Operation::Encapsulate, &encapsulation_algorithm, can_gc)
{
Ok(algorithm) => algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 4. Let realm be the relevant realm of this.
// Step 5. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 3.
// Step 6. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_encapsulation_key = Trusted::new(encapsulation_key);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global().task_manager().dom_manipulation_task_source().queue(
task!(derive_key: move || {
let subtle = trusted_subtle.root();
let encapsulation_key = trusted_encapsulation_key.root();
let promise = trusted_promise.root();
// Step 7. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 8. If the name member of normalizedEncapsulationAlgorithm is not equal to
// the name attribute of the [[algorithm]] internal slot of encapsulationKey then
// throw an InvalidAccessError.
if normalized_encapsulation_algorithm.name() != encapsulation_key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(Some(
"[[algorithm]] internal slot of encapsulationKey is not equal to \
normalizedEncapsulationAlgorithm".to_string(),
)));
return;
}
// Step 9. If the [[usages]] internal slot of encapsulationKey does not contain an
// entry that is "encapsulateBits", then throw an InvalidAccessError.
if !encapsulation_key.usages().contains(&KeyUsage::EncapsulateBits) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(Some(
"[[usages]] internal slot of encapsulationKey does not contain an \
entry that is \"encapsulateBits\"".to_string(),
)));
return;
}
// Step 10. Let encapsulatedBits be the result of performing the encapsulate
// operation specified by the [[algorithm]] internal slot of encapsulationKey using
// encapsulationKey.
// NOTE: Step 8 guarantees normalizedEncapsulationAlgorithm specifies the same
// algorithm as the [[algorithm]] internal slot of encapsulationKey.
let encapsulated_bits = match normalized_encapsulation_algorithm.encapsulate(&encapsulation_key) {
Ok(encapsulated_bits) => encapsulated_bits,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 11. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 12. Let result be the result of converting encapsulatedBits to an
// ECMAScript Object in realm, as defined by [WebIDL].
// Step 13. Resolve promise with result.
subtle.resolve_promise_with_encapsulated_bits(promise, encapsulated_bits);
}),
);
promise
}
/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-decapsulateKey>
fn DecapsulateKey(
&self,
cx: JSContext,
decapsulation_algorithm: AlgorithmIdentifier,
decapsulation_key: &CryptoKey,
ciphertext: ArrayBufferViewOrArrayBuffer,
shared_key_algorithm: AlgorithmIdentifier,
extractable: bool,
usages: Vec<KeyUsage>,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let decapsulationAlgorithm, decapsulationKey, sharedKeyAlgorithm, extractable
// and usages be the decapsulationAlgorithm, decapsulationKey, sharedKeyAlgorithm,
// extractable and keyUsages parameters passed to the decapsulateKey() method,
// respectively.
// Step 2. Let ciphertext be the result of getting a copy of the bytes held by the
// ciphertext parameter passed to the decapsulateKey() method.
let ciphertext = match ciphertext {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 3. Let normalizedDecapsulationAlgorithm be the result of normalizing an algorithm,
// with alg set to decapsulationAlgorithm and op set to "decapsulate".
// Step 4. If an error occurred, return a Promise rejected with
// normalizedDecapsulationAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_decapsulation_algorithm =
match normalize_algorithm(cx, Operation::Decapsulate, &decapsulation_algorithm, can_gc)
{
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 5. Let normalizedSharedKeyAlgorithm be the result of normalizing an algorithm, with
// alg set to sharedKeyAlgorithm and op set to "importKey".
// Step 6. If an error occurred, return a Promise rejected with
// normalizedSharedKeyAlgorithm.
let normalized_shared_key_algorithm =
match normalize_algorithm(cx, Operation::ImportKey, &shared_key_algorithm, can_gc) {
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 7. Let realm be the relevant realm of this.
// Step 8. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 4.
// Step 9. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_decapsulation_key = Trusted::new(decapsulation_key);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(decapsulate_key: move || {
let subtle = trusted_subtle.root();
let promise = trusted_promise.root();
let decapsulation_key = trusted_decapsulation_key.root();
// Step 10. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 11. If the name member of normalizedDecapsulationAlgorithm is not equal to
// the name attribute of the [[algorithm]] internal slot of decapsulationKey then
// throw an InvalidAccessError.
if normalized_decapsulation_algorithm.name() != decapsulation_key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(Some(
"[[algorithm]] internal slot of decapsulationKey is not equal to \
normalizedDecapsulationAlgorithm".to_string()
)));
return;
}
// Step 12. If the [[usages]] internal slot of decapsulationKey does not contain an
// entry that is "decapsulateKey", then throw an InvalidAccessError.
if !decapsulation_key.usages().contains(&KeyUsage::DecapsulateKey) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(Some(
"[[usages]] internal slot of decapsulationKey does not contain an \
entry that is \"decapsulateBits\"".to_string(),
)));
return;
}
// Step 13. Let decapsulatedBits be the result of performing the decapsulate
// operation specified by the [[algorithm]] internal slot of decapsulationKey using
// decapsulationKey and ciphertext.
// NOTE: Step 11 guarantees normalizedDecapsulationAlgorithm specifies the same
// algorithm as the [[algorithm]] internal slot of decapsulationKey.
let decapsulated_bits_result =
normalized_decapsulation_algorithm.decapsulate(&decapsulation_key, &ciphertext);
let decapsulated_bits = match decapsulated_bits_result {
Ok(decapsulated_bits) => decapsulated_bits,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 14. Let sharedKey be the result of performing the import key operation
// specified by normalizedSharedKeyAlgorithm using "raw-secret" as format, the
// decapsulatedBits as keyData, sharedKeyAlgorithm as algorithm and using
// extractable and usages.
// Step 15. Set the [[extractable]] internal slot of sharedKey to extractable.
// Step 16. Set the [[usages]] internal slot of sharedKey to the normalized value
// of usages.
let shared_key_result = normalized_shared_key_algorithm.import_key(
&subtle.global(),
KeyFormat::Raw_secret,
&decapsulated_bits,
extractable,
usages.clone(),
CanGc::note(),
);
let shared_key = match shared_key_result {
Ok(shared_key) => shared_key,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 17. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 18. Let result be the result of converting sharedKey to an ECMAScript
// Object in realm, as defined by [WebIDL].
// Step 19. Resolve promise with result.
subtle.resolve_promise_with_key(promise, shared_key);
}));
promise
}
/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-decapsulateBits>
fn DecapsulateBits(
&self,
cx: JSContext,
decapsulation_algorithm: AlgorithmIdentifier,
decapsulation_key: &CryptoKey,
ciphertext: ArrayBufferViewOrArrayBuffer,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let decapsulationAlgorithm and decapsulationKey be the decapsulationAlgorithm
// and decapsulationKey parameters passed to the decapsulateBits() method, respectively.
// Step 2. Let ciphertext be the result of getting a copy of the bytes held by the
// ciphertext parameter passed to the decapsulateBits() method.
let ciphertext = match ciphertext {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
// Step 3. Let normalizedDecapsulationAlgorithm be the result of normalizing an algorithm,
// with alg set to decapsulationAlgorithm and op set to "decapsulate".
// Step 4. If an error occurred, return a Promise rejected with
// normalizedDecapsulationAlgorithm.
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_decapsulation_algorithm =
match normalize_algorithm(cx, Operation::Decapsulate, &decapsulation_algorithm, can_gc)
{
Ok(normalized_algorithm) => normalized_algorithm,
Err(error) => {
promise.reject_error(error, can_gc);
return promise;
},
};
// Step 5. Let realm be the relevant realm of this.
// Step 6. Let promise be a new Promise.
// NOTE: We did that in preparation of Step 4.
// Step 7. Return promise and perform the remaining steps in parallel.
let trusted_subtle = Trusted::new(self);
let trusted_decapsulation_key = Trusted::new(decapsulation_key);
let trusted_promise = TrustedPromise::new(promise.clone());
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(decapsulate_bits: move || {
let subtle = trusted_subtle.root();
let promise = trusted_promise.root();
let decapsulation_key = trusted_decapsulation_key.root();
// Step 8. If the following steps or referenced procedures say to throw an error,
// queue a global task on the crypto task source, given realm's global object, to
// reject promise with the returned error; and then terminate the algorithm.
// Step 9. If the name member of normalizedDecapsulationAlgorithm is not equal to
// the name attribute of the [[algorithm]] internal slot of decapsulationKey then
// throw an InvalidAccessError.
if normalized_decapsulation_algorithm.name() != decapsulation_key.algorithm().name() {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(Some(
"[[algorithm]] internal slot of decapsulationKey is not equal to \
normalizedDecapsulationAlgorithm".to_string()
)));
return;
}
// Step 10. If the [[usages]] internal slot of decapsulationKey does not contain an
// entry that is "decapsulateBits", then throw an InvalidAccessError.
if !decapsulation_key.usages().contains(&KeyUsage::DecapsulateBits) {
subtle.reject_promise_with_error(promise, Error::InvalidAccess(Some(
"[[usages]] internal slot of decapsulationKey does not contain an \
entry that is \"decapsulateBits\"".to_string(),
)));
return;
}
// Step 11. Let decapsulatedBits be the result of performing the decapsulate
// operation specified by the [[algorithm]] internal slot of decapsulationKey using
// decapsulationKey and ciphertext.
// NOTE: Step 9 guarantees normalizedDecapsulationAlgorithm specifies the same
// algorithm as the [[algorithm]] internal slot of decapsulationKey.
let decapsulated_bits_result =
normalized_decapsulation_algorithm.decapsulate(&decapsulation_key, &ciphertext);
let decapsulated_bits = match decapsulated_bits_result {
Ok(decapsulated_bits) => decapsulated_bits,
Err(error) => {
subtle.reject_promise_with_error(promise, error);
return;
},
};
// Step 12. Queue a global task on the crypto task source, given realm's global
// object, to perform the remaining steps.
// Step 13. Let result be the result of creating an ArrayBuffer in realm,
// containing decapsulatedBits.
// Step 14. Resolve promise with result.
subtle.resolve_promise_with_data(promise, decapsulated_bits);
}));
promise
}
}
// 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.
/// <https://w3c.github.io/webcrypto/#dfn-Algorithm>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleAlgorithm {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
}
impl From<Algorithm> for SubtleAlgorithm {
fn from(params: Algorithm) -> Self {
SubtleAlgorithm {
name: params.name.to_string(),
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-KeyAlgorithm>
#[derive(Clone, Debug, MallocSizeOf)]
pub(crate) struct SubtleKeyAlgorithm {
/// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
name: String,
}
impl From<NormalizedAlgorithm> for SubtleKeyAlgorithm {
fn from(value: NormalizedAlgorithm) -> Self {
SubtleKeyAlgorithm {
name: value.name().to_string(),
}
}
}
impl SafeToJSValConvertible for SubtleKeyAlgorithm {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
let dictionary = KeyAlgorithm {
name: self.name.clone().into(),
};
dictionary.safe_to_jsval(cx, rval, can_gc);
}
}
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedKeyGenParams>
#[derive(Clone, MallocSizeOf)]
pub(crate) struct SubtleRsaHashedKeyGenParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-RsaKeyGenParams-modulusLength>
modulus_length: u32,
/// <https://w3c.github.io/webcrypto/#dfn-RsaKeyGenParams-publicExponent>
public_exponent: Vec<u8>,
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedKeyGenParams-hash>
hash: Box<NormalizedAlgorithm>,
}
impl TryFrom<RootedTraceableBox<RsaHashedKeyGenParams>> for SubtleRsaHashedKeyGenParams {
type Error = Error;
fn try_from(value: RootedTraceableBox<RsaHashedKeyGenParams>) -> Result<Self, Self::Error> {
let cx = GlobalScope::get_cx();
Ok(SubtleRsaHashedKeyGenParams {
name: value.parent.parent.name.to_string(),
modulus_length: value.parent.modulusLength,
public_exponent: value.parent.publicExponent.to_vec(),
hash: Box::new(normalize_algorithm(
cx,
Operation::Digest,
&value.hash,
CanGc::note(),
)?),
})
}
}
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedKeyAlgorithm>
#[derive(Clone, MallocSizeOf)]
pub(crate) struct SubtleRsaHashedKeyAlgorithm {
/// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-RsaKeyAlgorithm-modulusLength>
modulus_length: u32,
/// <https://w3c.github.io/webcrypto/#dfn-RsaKeyAlgorithm-publicExponent>
public_exponent: Vec<u8>,
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedKeyAlgorithm-hash>
hash: Box<NormalizedAlgorithm>,
}
impl SafeToJSValConvertible for SubtleRsaHashedKeyAlgorithm {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
let public_exponent =
create_buffer_source(cx, &self.public_exponent, js_object.handle_mut(), can_gc)
.expect("Fail to convert publicExponent to Uint8Array");
let key_algorithm = KeyAlgorithm {
name: self.name.clone().into(),
};
let rsa_key_algorithm = RootedTraceableBox::new(RsaKeyAlgorithm {
parent: key_algorithm,
modulusLength: self.modulus_length,
publicExponent: public_exponent,
});
let rsa_hashed_key_algorithm = RootedTraceableBox::new(RsaHashedKeyAlgorithm {
parent: rsa_key_algorithm,
hash: KeyAlgorithm {
name: self.hash.name().into(),
},
});
rsa_hashed_key_algorithm.safe_to_jsval(cx, rval, can_gc);
}
}
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedImportParams>
#[derive(Clone, MallocSizeOf)]
struct SubtleRsaHashedImportParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedImportParams-hash>
hash: Box<NormalizedAlgorithm>,
}
impl TryFrom<RootedTraceableBox<RsaHashedImportParams>> for SubtleRsaHashedImportParams {
type Error = Error;
fn try_from(value: RootedTraceableBox<RsaHashedImportParams>) -> Result<Self, Self::Error> {
let cx = GlobalScope::get_cx();
Ok(SubtleRsaHashedImportParams {
name: value.parent.name.to_string(),
hash: Box::new(normalize_algorithm(
cx,
Operation::Digest,
&value.hash,
CanGc::note(),
)?),
})
}
}
/// <https://w3c.github.io/webcrypto/#dfn-RsaPssParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleRsaPssParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-RsaPssParams-saltLength>
salt_length: u32,
}
impl From<RsaPssParams> for SubtleRsaPssParams {
fn from(value: RsaPssParams) -> Self {
SubtleRsaPssParams {
name: value.parent.name.to_string(),
salt_length: value.saltLength,
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-RsaOaepParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleRsaOaepParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-RsaOaepParams-label>
label: Option<Vec<u8>>,
}
impl From<RootedTraceableBox<RsaOaepParams>> for SubtleRsaOaepParams {
fn from(value: RootedTraceableBox<RsaOaepParams>) -> Self {
let label = value.label.as_ref().map(|label| match label {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
});
SubtleRsaOaepParams {
name: value.parent.name.to_string(),
label,
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-EcdsaParams>
#[derive(Clone, MallocSizeOf)]
struct SubtleEcdsaParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-EcdsaParams-hash>
hash: Box<NormalizedAlgorithm>,
}
impl TryFrom<RootedTraceableBox<EcdsaParams>> for SubtleEcdsaParams {
type Error = Error;
fn try_from(value: RootedTraceableBox<EcdsaParams>) -> Result<Self, Error> {
let cx = GlobalScope::get_cx();
let hash = normalize_algorithm(cx, Operation::Digest, &value.hash, CanGc::note())?;
Ok(SubtleEcdsaParams {
name: value.parent.name.to_string(),
hash: Box::new(hash),
})
}
}
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyGenParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleEcKeyGenParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyGenParams-namedCurve>
named_curve: String,
}
impl From<EcKeyGenParams> for SubtleEcKeyGenParams {
fn from(value: EcKeyGenParams) -> Self {
SubtleEcKeyGenParams {
name: value.parent.name.to_string(),
named_curve: value.namedCurve.to_string(),
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyAlgorithm>
#[derive(Clone, Debug, MallocSizeOf)]
pub(crate) struct SubtleEcKeyAlgorithm {
/// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyAlgorithm-namedCurve>
named_curve: String,
}
impl SafeToJSValConvertible for SubtleEcKeyAlgorithm {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
let parent = KeyAlgorithm {
name: self.name.clone().into(),
};
let dictionary = EcKeyAlgorithm {
parent,
namedCurve: self.named_curve.clone().into(),
};
dictionary.safe_to_jsval(cx, rval, can_gc);
}
}
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyImportParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleEcKeyImportParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyImportParams-namedCurve>
named_curve: String,
}
impl From<EcKeyImportParams> for SubtleEcKeyImportParams {
fn from(value: EcKeyImportParams) -> Self {
SubtleEcKeyImportParams {
name: value.parent.name.to_string(),
named_curve: value.namedCurve.to_string(),
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-EcdhKeyDeriveParams>
#[derive(Clone, MallocSizeOf)]
struct SubtleEcdhKeyDeriveParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-EcdhKeyDeriveParams-public>
public: Trusted<CryptoKey>,
}
impl From<EcdhKeyDeriveParams> for SubtleEcdhKeyDeriveParams {
fn from(value: EcdhKeyDeriveParams) -> Self {
SubtleEcdhKeyDeriveParams {
name: value.parent.name.to_string(),
public: Trusted::new(&value.public),
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-AesCtrParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleAesCtrParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-AesCtrParams-counter>
counter: Vec<u8>,
/// <https://w3c.github.io/webcrypto/#dfn-AesCtrParams-length>
length: u8,
}
impl From<RootedTraceableBox<AesCtrParams>> for SubtleAesCtrParams {
fn from(params: RootedTraceableBox<AesCtrParams>) -> Self {
let counter = match &params.counter {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
SubtleAesCtrParams {
name: params.parent.name.to_string(),
counter,
length: params.length,
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-AesKeyAlgorithm>
#[derive(Clone, Debug, MallocSizeOf)]
pub(crate) struct SubtleAesKeyAlgorithm {
/// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-AesKeyAlgorithm-length>
length: u16,
}
impl SafeToJSValConvertible for SubtleAesKeyAlgorithm {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
let parent = KeyAlgorithm {
name: self.name.clone().into(),
};
let dictionary = AesKeyAlgorithm {
parent,
length: self.length,
};
dictionary.safe_to_jsval(cx, rval, can_gc);
}
}
/// <https://w3c.github.io/webcrypto/#dfn-AesKeyGenParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleAesKeyGenParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-AesKeyGenParams-length>
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/#dfn-AesDerivedKeyParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleAesDerivedKeyParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-AesDerivedKeyParams-length>
length: u16,
}
impl From<AesDerivedKeyParams> for SubtleAesDerivedKeyParams {
fn from(params: AesDerivedKeyParams) -> Self {
SubtleAesDerivedKeyParams {
name: params.parent.name.to_string(),
length: params.length,
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-AesCbcParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleAesCbcParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-AesCbcParams-iv>
iv: Vec<u8>,
}
impl From<RootedTraceableBox<AesCbcParams>> for SubtleAesCbcParams {
fn from(params: RootedTraceableBox<AesCbcParams>) -> Self {
let iv = match &params.iv {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
SubtleAesCbcParams {
name: params.parent.name.to_string(),
iv,
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-AesGcmParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleAesGcmParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-AesGcmParams-iv>
iv: Vec<u8>,
/// <https://w3c.github.io/webcrypto/#dfn-AesGcmParams-additionalData>
additional_data: Option<Vec<u8>>,
/// <https://w3c.github.io/webcrypto/#dfn-AesGcmParams-tagLength>
tag_length: Option<u8>,
}
impl From<RootedTraceableBox<AesGcmParams>> for SubtleAesGcmParams {
fn from(params: RootedTraceableBox<AesGcmParams>) -> Self {
let iv = match &params.iv {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let additional_data = params.additionalData.as_ref().map(|data| match data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
});
SubtleAesGcmParams {
name: params.parent.name.to_string(),
iv,
additional_data,
tag_length: params.tagLength,
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-HmacImportParams>
#[derive(Clone, MallocSizeOf)]
struct SubtleHmacImportParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-HmacImportParams-hash>
hash: Box<NormalizedAlgorithm>,
/// <https://w3c.github.io/webcrypto/#dfn-HmacImportParams-length>
length: Option<u32>,
}
impl TryFrom<RootedTraceableBox<HmacImportParams>> for SubtleHmacImportParams {
type Error = Error;
fn try_from(params: RootedTraceableBox<HmacImportParams>) -> Result<Self, Error> {
let cx = GlobalScope::get_cx();
let hash = normalize_algorithm(cx, Operation::Digest, &params.hash, CanGc::note())?;
Ok(SubtleHmacImportParams {
name: params.parent.name.to_string(),
hash: Box::new(hash),
length: params.length,
})
}
}
/// <https://w3c.github.io/webcrypto/#dfn-HmacKeyAlgorithm>
#[derive(Clone, Debug, MallocSizeOf)]
pub(crate) struct SubtleHmacKeyAlgorithm {
/// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-HmacKeyAlgorithm-hash>
hash: SubtleKeyAlgorithm,
/// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-length>
length: u32,
}
impl SafeToJSValConvertible for SubtleHmacKeyAlgorithm {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
let parent = KeyAlgorithm {
name: self.name.clone().into(),
};
let hash = KeyAlgorithm {
name: self.hash.name.clone().into(),
};
let dictionary = HmacKeyAlgorithm {
parent,
hash,
length: self.length,
};
dictionary.safe_to_jsval(cx, rval, can_gc);
}
}
/// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams>
#[derive(Clone, MallocSizeOf)]
struct SubtleHmacKeyGenParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-hash>
hash: Box<NormalizedAlgorithm>,
/// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-length>
length: Option<u32>,
}
impl TryFrom<RootedTraceableBox<HmacKeyGenParams>> for SubtleHmacKeyGenParams {
type Error = Error;
fn try_from(params: RootedTraceableBox<HmacKeyGenParams>) -> Result<Self, Error> {
let cx = GlobalScope::get_cx();
let hash = normalize_algorithm(cx, Operation::Digest, &params.hash, CanGc::note())?;
Ok(SubtleHmacKeyGenParams {
name: params.parent.name.to_string(),
hash: Box::new(hash),
length: params.length,
})
}
}
/// <https://w3c.github.io/webcrypto/#dfn-HkdfParams>
#[derive(Clone, MallocSizeOf)]
pub(crate) struct SubtleHkdfParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-hash>
hash: Box<NormalizedAlgorithm>,
/// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-salt>
salt: Vec<u8>,
/// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-info>
info: Vec<u8>,
}
impl TryFrom<RootedTraceableBox<HkdfParams>> for SubtleHkdfParams {
type Error = Error;
fn try_from(params: RootedTraceableBox<HkdfParams>) -> Result<Self, Error> {
let cx = GlobalScope::get_cx();
let hash = normalize_algorithm(cx, Operation::Digest, &params.hash, CanGc::note())?;
let salt = match &params.salt {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let info = match &params.info {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
Ok(SubtleHkdfParams {
name: params.parent.name.to_string(),
hash: Box::new(hash),
salt,
info,
})
}
}
/// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params>
#[derive(Clone, MallocSizeOf)]
pub(crate) struct SubtlePbkdf2Params {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-salt>
salt: Vec<u8>,
/// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-iterations>
iterations: u32,
/// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-hash>
hash: Box<NormalizedAlgorithm>,
}
impl TryFrom<RootedTraceableBox<Pbkdf2Params>> for SubtlePbkdf2Params {
type Error = Error;
fn try_from(params: RootedTraceableBox<Pbkdf2Params>) -> Result<Self, Error> {
let cx = GlobalScope::get_cx();
let salt = match &params.salt {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let hash = normalize_algorithm(cx, Operation::Digest, &params.hash, CanGc::note())?;
Ok(SubtlePbkdf2Params {
name: params.parent.name.to_string(),
salt,
iterations: params.iterations,
hash: Box::new(hash),
})
}
}
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-ContextParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleContextParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-ContextParams-context>
context: Option<Vec<u8>>,
}
impl From<RootedTraceableBox<ContextParams>> for SubtleContextParams {
fn from(value: RootedTraceableBox<ContextParams>) -> Self {
let context = value.context.as_ref().map(|context| match context {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
});
SubtleContextParams {
name: value.parent.name.to_string(),
context,
}
}
}
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-AeadParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleAeadParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-AeadParams-iv>
iv: Vec<u8>,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-AeadParams-additionalData>
additional_data: Option<Vec<u8>>,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-AeadParams-tagLength>
tag_length: Option<u8>,
}
impl From<RootedTraceableBox<AeadParams>> for SubtleAeadParams {
fn from(value: RootedTraceableBox<AeadParams>) -> Self {
let iv = match &value.iv {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let additional_data = value.additionalData.as_ref().map(|data| match data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
});
SubtleAeadParams {
name: value.parent.name.to_string(),
iv,
additional_data,
tag_length: value.tagLength,
}
}
}
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-CShakeParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleCShakeParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-CShakeParams-length>
length: u32,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-CShakeParams-functionName>
function_name: Option<Vec<u8>>,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-CShakeParams-customization>
customization: Option<Vec<u8>>,
}
impl From<RootedTraceableBox<CShakeParams>> for SubtleCShakeParams {
fn from(value: RootedTraceableBox<CShakeParams>) -> Self {
let function_name = value
.functionName
.as_ref()
.map(|function_name| match function_name {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
});
let customization = value
.customization
.as_ref()
.map(|customization| match customization {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
});
SubtleCShakeParams {
name: value.parent.name.to_string(),
length: value.length,
function_name,
customization,
}
}
}
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-Argon2Params>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleArgon2Params {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-Argon2Params-nonce>
nonce: Vec<u8>,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-Argon2Params-parallelism>
parallelism: u32,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-Argon2Params-memory>
memory: u32,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-Argon2Params-passes>
passes: u32,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-Argon2Params-version>
version: Option<u8>,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-Argon2Params-secretValue>
secret_value: Option<Vec<u8>>,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-Argon2Params-associatedData>
associated_data: Option<Vec<u8>>,
}
impl From<RootedTraceableBox<Argon2Params>> for SubtleArgon2Params {
fn from(value: RootedTraceableBox<Argon2Params>) -> Self {
let nonce = match &value.nonce {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let secret_value = value
.secretValue
.as_ref()
.map(|secret_value| match secret_value {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
});
let associated_data =
value
.associatedData
.as_ref()
.map(|associated_data| match associated_data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
});
SubtleArgon2Params {
name: value.parent.name.to_string(),
nonce,
parallelism: value.parallelism,
memory: value.memory,
passes: value.passes,
version: value.version,
secret_value,
associated_data,
}
}
}
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-EncapsulatedKey>
struct SubtleEncapsulatedKey {
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-EncapsulatedKey-sharedKey>
shared_key: Option<Trusted<CryptoKey>>,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-EncapsulatedKey-ciphertext>
ciphertext: Option<Vec<u8>>,
}
impl SafeToJSValConvertible for SubtleEncapsulatedKey {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
let shared_key = self.shared_key.as_ref().map(|shared_key| shared_key.root());
let ciphertext = self.ciphertext.as_ref().map(|data| {
rooted!(in(*cx) let mut ciphertext_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<ArrayBufferU8>(cx, data, ciphertext_ptr.handle_mut(), can_gc)
.expect("Failed to convert ciphertext to ArrayBufferU8")
});
let encapsulated_key = RootedTraceableBox::new(EncapsulatedKey {
sharedKey: shared_key,
ciphertext,
});
encapsulated_key.safe_to_jsval(cx, rval, can_gc);
}
}
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-EncapsulatedBits>
struct SubtleEncapsulatedBits {
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-EncapsulatedBits-sharedKey>
shared_key: Option<Vec<u8>>,
/// <https://wicg.github.io/webcrypto-modern-algos/#dfn-EncapsulatedBits-ciphertext>
ciphertext: Option<Vec<u8>>,
}
impl SafeToJSValConvertible for SubtleEncapsulatedBits {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
let shared_key = self.shared_key.as_ref().map(|data| {
rooted!(in(*cx) let mut shared_key_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<ArrayBufferU8>(cx, data, shared_key_ptr.handle_mut(), can_gc)
.expect("Failed to convert shared key to ArrayBufferU8")
});
let ciphertext = self.ciphertext.as_ref().map(|data| {
rooted!(in(*cx) let mut ciphertext_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<ArrayBufferU8>(cx, data, ciphertext_ptr.handle_mut(), can_gc)
.expect("Failed to convert ciphertext to ArrayBufferU8")
});
let encapsulated_bits = RootedTraceableBox::new(EncapsulatedBits {
sharedKey: shared_key,
ciphertext,
});
encapsulated_bits.safe_to_jsval(cx, rval, can_gc);
}
}
/// Helper to abstract the conversion process of a JS value into many different WebIDL dictionaries.
fn dictionary_from_jsval<T>(cx: JSContext, value: HandleValue, can_gc: CanGc) -> Fallible<T>
where
T: SafeFromJSValConvertible<Config = ()>,
{
let conversion = T::safe_from_jsval(cx, value, (), can_gc).map_err(|_| Error::JSFailed)?;
match conversion {
ConversionResult::Success(dictionary) => Ok(dictionary),
ConversionResult::Failure(error) => Err(Error::Type(error.into())),
}
}
/// The returned type of the successful export key operation. `Bytes` should be used when the key
/// is exported in "raw", "spki" or "pkcs8" format. `Jwk` should be used when the key is exported
/// in "jwk" format.
enum ExportedKey {
Bytes(Vec<u8>),
Jwk(Box<JsonWebKey>),
}
/// Union type of KeyAlgorithm and IDL dictionary types derived from it. Note that we actually use
/// our "subtle" structs of the corresponding IDL dictionary types so that they can be easily
/// passed to another threads.
#[derive(Clone, MallocSizeOf)]
#[expect(clippy::enum_variant_names)]
pub(crate) enum KeyAlgorithmAndDerivatives {
KeyAlgorithm(SubtleKeyAlgorithm),
RsaHashedKeyAlgorithm(SubtleRsaHashedKeyAlgorithm),
EcKeyAlgorithm(SubtleEcKeyAlgorithm),
AesKeyAlgorithm(SubtleAesKeyAlgorithm),
HmacKeyAlgorithm(SubtleHmacKeyAlgorithm),
}
impl KeyAlgorithmAndDerivatives {
fn name(&self) -> &str {
match self {
KeyAlgorithmAndDerivatives::KeyAlgorithm(algo) => &algo.name,
KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algo) => &algo.name,
KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algo) => &algo.name,
KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algo) => &algo.name,
KeyAlgorithmAndDerivatives::HmacKeyAlgorithm(algo) => &algo.name,
}
}
}
impl From<NormalizedAlgorithm> for KeyAlgorithmAndDerivatives {
fn from(value: NormalizedAlgorithm) -> Self {
KeyAlgorithmAndDerivatives::KeyAlgorithm(SubtleKeyAlgorithm {
name: value.name().to_string(),
})
}
}
impl SafeToJSValConvertible for KeyAlgorithmAndDerivatives {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
match self {
KeyAlgorithmAndDerivatives::KeyAlgorithm(algo) => algo.safe_to_jsval(cx, rval, can_gc),
KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algo) => {
algo.safe_to_jsval(cx, rval, can_gc)
},
KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algo) => {
algo.safe_to_jsval(cx, rval, can_gc)
},
KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algo) => {
algo.safe_to_jsval(cx, rval, can_gc)
},
KeyAlgorithmAndDerivatives::HmacKeyAlgorithm(algo) => {
algo.safe_to_jsval(cx, rval, can_gc)
},
}
}
}
#[derive(Clone, Copy)]
enum JwkStringField {
X,
Y,
D,
N,
E,
P,
Q,
DP,
DQ,
QI,
K,
Priv,
Pub,
}
impl Display for JwkStringField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let field_name = match self {
JwkStringField::X => "x",
JwkStringField::Y => "y",
JwkStringField::D => "d",
JwkStringField::N => "n",
JwkStringField::E => "e",
JwkStringField::P => "q",
JwkStringField::Q => "q",
JwkStringField::DP => "dp",
JwkStringField::DQ => "dq",
JwkStringField::QI => "qi",
JwkStringField::K => "k",
JwkStringField::Priv => "priv",
JwkStringField::Pub => "pub",
};
write!(f, "{}", field_name)
}
}
trait JsonWebKeyExt {
fn parse(cx: JSContext, data: &[u8]) -> Result<JsonWebKey, Error>;
fn stringify(&self, cx: JSContext) -> Result<DOMString, Error>;
fn get_usages_from_key_ops(&self) -> Result<Vec<KeyUsage>, Error>;
fn check_key_ops(&self, specified_usages: &[KeyUsage]) -> Result<(), Error>;
fn set_key_ops(&mut self, usages: Vec<KeyUsage>);
fn encode_string_field(&mut self, field: JwkStringField, data: &[u8]);
fn decode_optional_string_field(&self, field: JwkStringField)
-> Result<Option<Vec<u8>>, Error>;
fn decode_required_string_field(&self, field: JwkStringField) -> Result<Vec<u8>, Error>;
fn decode_primes_from_oth_field(&self, primes: &mut Vec<Vec<u8>>) -> Result<(), Error>;
}
impl JsonWebKeyExt for JsonWebKey {
/// <https://w3c.github.io/webcrypto/#concept-parse-a-jwk>
#[expect(unsafe_code)]
fn parse(cx: JSContext, data: &[u8]) -> Result<JsonWebKey, Error> {
// Step 1. Let data be the sequence of bytes to be parsed.
// (It is given as a method paramter.)
// Step 2. Let json be the Unicode string that results from interpreting data according to UTF-8.
let json = String::from_utf8_lossy(data);
// Step 3. Convert json to UTF-16.
let json: Vec<_> = json.encode_utf16().collect();
// Step 4. Let result be the object literal that results from executing the JSON.parse
// internal function in the context of a new global object, with text argument set to a
// JavaScript String containing json.
rooted!(in(*cx) let mut result = UndefinedValue());
unsafe {
if !JS_ParseJSON(*cx, json.as_ptr(), json.len() as u32, result.handle_mut()) {
return Err(Error::JSFailed);
}
}
// Step 5. Let key be the result of converting result to the IDL dictionary type of JsonWebKey.
let key = match JsonWebKey::new(cx, result.handle(), CanGc::note()) {
Ok(ConversionResult::Success(key)) => key,
Ok(ConversionResult::Failure(error)) => {
return Err(Error::Type(error.to_string()));
},
Err(()) => {
return Err(Error::JSFailed);
},
};
// Step 6. If the kty field of key is not defined, then throw a DataError.
if key.kty.is_none() {
return Err(Error::Data(None));
}
// Step 7. Result key.
Ok(key)
}
/// Convert a JsonWebKey value to DOMString. We first convert the JsonWebKey value to
/// JavaScript value, and then serialize it by performing steps in
/// <https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string>. This acts
/// like the opposite of JsonWebKey::parse if you further convert the stringified result to
/// bytes.
fn stringify(&self, cx: JSContext) -> Result<DOMString, Error> {
rooted!(in(*cx) let mut data = UndefinedValue());
self.safe_to_jsval(cx, data.handle_mut(), CanGc::note());
serialize_jsval_to_json_utf8(cx, data.handle())
}
fn get_usages_from_key_ops(&self) -> Result<Vec<KeyUsage>, Error> {
let mut usages = vec![];
for op in self.key_ops.as_ref().ok_or(Error::Data(None))? {
usages.push(KeyUsage::from_str(&op.str()).map_err(|_| Error::Data(None))?);
}
Ok(usages)
}
/// If the key_ops field of jwk is present, and is invalid according to the requirements of
/// JSON Web Key [JWK] or does not contain all of the specified usages values, then throw a
/// DataError.
fn check_key_ops(&self, specified_usages: &[KeyUsage]) -> Result<(), Error> {
// If the key_ops field of jwk is present,
if let Some(ref key_ops) = self.key_ops {
// and is invalid according to the requirements of JSON Web Key [JWK]:
// 1. Duplicate key operation values MUST NOT be present in the array.
if key_ops
.iter()
.collect::<std::collections::HashSet<_>>()
.len() <
key_ops.len()
{
return Err(Error::Data(None));
}
// 2. The "use" and "key_ops" JWK members SHOULD NOT be used together; however, if both
// are used, the information they convey MUST be consistent.
if let Some(ref use_) = self.use_ {
if key_ops.iter().any(|op| op != use_) {
return Err(Error::Data(None));
}
}
// or does not contain all of the specified usages values
let key_ops_as_usages = self.get_usages_from_key_ops()?;
if !specified_usages
.iter()
.all(|specified_usage| key_ops_as_usages.contains(specified_usage))
{
return Err(Error::Data(None));
}
}
Ok(())
}
// Set the key_ops attribute of jwk to equal the given usages.
fn set_key_ops(&mut self, usages: Vec<KeyUsage>) {
self.key_ops = Some(
usages
.into_iter()
.map(|usage| DOMString::from(usage.as_str()))
.collect(),
);
}
// Encode a byte sequence to a base64url-encoded string, and set the field to the encoded
// string.
fn encode_string_field(&mut self, field: JwkStringField, data: &[u8]) {
let encoded_data = DOMString::from(Base64UrlUnpadded::encode_string(data));
match field {
JwkStringField::X => self.x = Some(encoded_data),
JwkStringField::Y => self.y = Some(encoded_data),
JwkStringField::D => self.d = Some(encoded_data),
JwkStringField::N => self.n = Some(encoded_data),
JwkStringField::E => self.e = Some(encoded_data),
JwkStringField::P => self.p = Some(encoded_data),
JwkStringField::Q => self.q = Some(encoded_data),
JwkStringField::DP => self.dp = Some(encoded_data),
JwkStringField::DQ => self.dq = Some(encoded_data),
JwkStringField::QI => self.qi = Some(encoded_data),
JwkStringField::K => self.k = Some(encoded_data),
JwkStringField::Priv => self.priv_ = Some(encoded_data),
JwkStringField::Pub => self.pub_ = Some(encoded_data),
}
}
// Decode a field from a base64url-encoded string to a byte sequence. If the field is not a
// valid base64url-encoded string, then throw a DataError.
fn decode_optional_string_field(
&self,
field: JwkStringField,
) -> Result<Option<Vec<u8>>, Error> {
let field_string = match field {
JwkStringField::X => &self.x,
JwkStringField::Y => &self.y,
JwkStringField::D => &self.d,
JwkStringField::N => &self.n,
JwkStringField::E => &self.e,
JwkStringField::P => &self.p,
JwkStringField::Q => &self.q,
JwkStringField::DP => &self.dp,
JwkStringField::DQ => &self.dq,
JwkStringField::QI => &self.qi,
JwkStringField::K => &self.k,
JwkStringField::Priv => &self.priv_,
JwkStringField::Pub => &self.pub_,
};
field_string
.as_ref()
.map(|field_string| Base64UrlUnpadded::decode_vec(&field_string.str()))
.transpose()
.map_err(|_| Error::Data(Some(format!("Failed to decode {} field in jwk", field))))
}
// Decode a field from a base64url-encoded string to a byte sequence. If the field is not
// present or it is not a valid base64url-encoded string, then throw a DataError.
fn decode_required_string_field(&self, field: JwkStringField) -> Result<Vec<u8>, Error> {
self.decode_optional_string_field(field)?
.ok_or(Error::Data(Some(format!(
"The {} field is not present in jwk",
field
))))
}
// Decode the "r", "d" and "t" field of each entry in the "oth" array, from a base64url-encoded
// string to a byte sequence, and append the decoded "r" field to the `primes` list, in the
// order of presence in the "oth" array.
//
// If the "oth" field is present and any of the "p", "q", "dp", "dq" or "qi" field is not
// present, then throw a DataError. For each entry in the "oth" array, if any of the "r", "d"
// and "t" field is not present or it is not a valid base64url-encoded string, then throw a
// DataError.
fn decode_primes_from_oth_field(&self, primes: &mut Vec<Vec<u8>>) -> Result<(), Error> {
if self.oth.is_some() &&
(self.p.is_none() ||
self.q.is_none() ||
self.dp.is_none() ||
self.dq.is_none() ||
self.qi.is_none())
{
return Err(Error::Data(Some(
"The oth field is present while at least one of p, q, dp, dq, qi is missing, in jwk".to_string()
)));
}
for rsa_other_prime_info in self.oth.as_ref().unwrap_or(&Vec::new()) {
let r = Base64UrlUnpadded::decode_vec(
&rsa_other_prime_info
.r
.as_ref()
.ok_or(Error::Data(Some(
"The r field is not present in one of the entry of oth field in jwk"
.to_string(),
)))?
.str(),
)
.map_err(|_| {
Error::Data(Some(
"Fail to decode r field in one of the entry of oth field in jwk".to_string(),
))
})?;
primes.push(r);
let _d = Base64UrlUnpadded::decode_vec(
&rsa_other_prime_info
.d
.as_ref()
.ok_or(Error::Data(Some(
"The d field is not present in one of the entry of oth field in jwk"
.to_string(),
)))?
.str(),
)
.map_err(|_| {
Error::Data(Some(
"Fail to decode d field in one of the entry of oth field in jwk".to_string(),
))
})?;
let _t = Base64UrlUnpadded::decode_vec(
&rsa_other_prime_info
.t
.as_ref()
.ok_or(Error::Data(Some(
"The t field is not present in one of the entry of oth field in jwk"
.to_string(),
)))?
.str(),
)
.map_err(|_| {
Error::Data(Some(
"Fail to decode t field in one of the entry of oth field in jwk".to_string(),
))
})?;
}
Ok(())
}
}
/// Inner type of <https://w3c.github.io/webcrypto/#dfn-supportedAlgorithms> for SHA
enum ShaAlgorithm {
Sha1,
Sha256,
Sha384,
Sha512,
}
impl ShaAlgorithm {
fn as_str(&self) -> &'static str {
match self {
ShaAlgorithm::Sha1 => ALG_SHA1,
ShaAlgorithm::Sha256 => ALG_SHA256,
ShaAlgorithm::Sha384 => ALG_SHA384,
ShaAlgorithm::Sha512 => ALG_SHA512,
}
}
}
/// Inner type of <https://w3c.github.io/webcrypto/#dfn-supportedAlgorithms> for ML-KEM
enum MlKemAlgorithm {
MlKem512,
MlKem768,
MlKem1024,
}
impl MlKemAlgorithm {
fn as_str(&self) -> &'static str {
match self {
MlKemAlgorithm::MlKem512 => ALG_ML_KEM_512,
MlKemAlgorithm::MlKem768 => ALG_ML_KEM_768,
MlKemAlgorithm::MlKem1024 => ALG_ML_KEM_1024,
}
}
}
/// Inner type of <https://w3c.github.io/webcrypto/#dfn-supportedAlgorithms> for ML-DSA
enum MlDsaAlgorithm {
MlDsa44,
MlDsa65,
MlDsa87,
}
impl MlDsaAlgorithm {
fn as_str(&self) -> &'static str {
match self {
MlDsaAlgorithm::MlDsa44 => ALG_ML_DSA_44,
MlDsaAlgorithm::MlDsa65 => ALG_ML_DSA_65,
MlDsaAlgorithm::MlDsa87 => ALG_ML_DSA_87,
}
}
}
/// Inner type of <https://w3c.github.io/webcrypto/#dfn-supportedAlgorithms> for SHA3
enum Sha3Algorithm {
Sha3_256,
Sha3_384,
Sha3_512,
}
impl Sha3Algorithm {
fn as_str(&self) -> &'static str {
match self {
Sha3Algorithm::Sha3_256 => ALG_SHA3_256,
Sha3Algorithm::Sha3_384 => ALG_SHA3_384,
Sha3Algorithm::Sha3_512 => ALG_SHA3_512,
}
}
}
/// Inner type of <https://w3c.github.io/webcrypto/#dfn-supportedAlgorithms> for cSHAKE
enum CShakeAlgorithm {
CShake128,
CShake256,
}
impl CShakeAlgorithm {
fn as_str(&self) -> &'static str {
match self {
CShakeAlgorithm::CShake128 => ALG_CSHAKE_128,
CShakeAlgorithm::CShake256 => ALG_CSHAKE_256,
}
}
}
/// Inner type of <https://w3c.github.io/webcrypto/#dfn-supportedAlgorithms> for Argon2
enum Argon2Algorithm {
Argon2D,
Argon2I,
Argon2ID,
}
impl Argon2Algorithm {
fn as_str(&self) -> &'static str {
match self {
Argon2Algorithm::Argon2D => ALG_ARGON2D,
Argon2Algorithm::Argon2I => ALG_ARGON2I,
Argon2Algorithm::Argon2ID => ALG_ARGON2ID,
}
}
}
/// <https://w3c.github.io/webcrypto/#dfn-supportedAlgorithms>
enum SupportedAlgorithm {
RsassaPkcs1V1_5,
RsaPss,
RsaOaep,
Ecdsa,
Ecdh,
Ed25519,
X25519,
AesCtr,
AesCbc,
AesGcm,
AesKw,
Hmac,
Sha(ShaAlgorithm),
Hkdf,
Pbkdf2,
MlKem(MlKemAlgorithm),
MlDsa(MlDsaAlgorithm),
AesOcb,
ChaCha20Poly1305,
Sha3(Sha3Algorithm),
CShake(CShakeAlgorithm),
Argon2(Argon2Algorithm),
}
impl SupportedAlgorithm {
fn as_str(&self) -> &'static str {
match self {
SupportedAlgorithm::RsassaPkcs1V1_5 => ALG_RSASSA_PKCS1_V1_5,
SupportedAlgorithm::RsaPss => ALG_RSA_PSS,
SupportedAlgorithm::RsaOaep => ALG_RSA_OAEP,
SupportedAlgorithm::Ecdsa => ALG_ECDSA,
SupportedAlgorithm::Ecdh => ALG_ECDH,
SupportedAlgorithm::Ed25519 => ALG_ED25519,
SupportedAlgorithm::X25519 => ALG_X25519,
SupportedAlgorithm::AesCtr => ALG_AES_CTR,
SupportedAlgorithm::AesCbc => ALG_AES_CBC,
SupportedAlgorithm::AesGcm => ALG_AES_GCM,
SupportedAlgorithm::AesKw => ALG_AES_KW,
SupportedAlgorithm::Hmac => ALG_HMAC,
SupportedAlgorithm::Sha(sha_algorithm) => sha_algorithm.as_str(),
SupportedAlgorithm::Hkdf => ALG_HKDF,
SupportedAlgorithm::Pbkdf2 => ALG_PBKDF2,
SupportedAlgorithm::MlKem(ml_kem_algorithm) => ml_kem_algorithm.as_str(),
SupportedAlgorithm::MlDsa(ml_dsa_algorithm) => ml_dsa_algorithm.as_str(),
SupportedAlgorithm::AesOcb => ALG_AES_OCB,
SupportedAlgorithm::ChaCha20Poly1305 => ALG_CHACHA20_POLY1305,
SupportedAlgorithm::Sha3(sha3_algorithm) => sha3_algorithm.as_str(),
SupportedAlgorithm::CShake(cshake_algorithm) => cshake_algorithm.as_str(),
SupportedAlgorithm::Argon2(argon2_algorithm) => argon2_algorithm.as_str(),
}
}
fn from_ignore_case(alg_name: &str) -> Result<SupportedAlgorithm, Error> {
let Some(&alg_name) = SUPPORTED_ALGORITHMS
.iter()
.find(|supported_algorithm| supported_algorithm.eq_ignore_ascii_case(alg_name))
else {
return Err(Error::NotSupported(Some(format!(
"Unsupported algorithm: {}",
alg_name
))));
};
SupportedAlgorithm::try_from(alg_name)
}
/// Check whether the cryptographic algorithm supports the specified operation. If the
/// algorithm supports the operation, then return the desired IDL dictionary type for the
/// operation of the algorithm. Otherwise, throw a NotSupportedError.
///
/// This function is also used as the "define an algorithm" algorithm, by adding algorithms,
/// operations and desired IDL dictionary types, to the `match` block.
/// <https://w3c.github.io/webcrypto/#concept-define-an-algorithm>
fn support(&self, op: Operation) -> Result<ParameterType, Error> {
let desired_type = match (self, &op) {
// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-registration>
(Self::RsassaPkcs1V1_5, Operation::Sign) => ParameterType::None,
(Self::RsassaPkcs1V1_5, Operation::Verify) => ParameterType::None,
(Self::RsassaPkcs1V1_5, Operation::GenerateKey) => ParameterType::RsaHashedKeyGenParams,
(Self::RsassaPkcs1V1_5, Operation::ImportKey) => ParameterType::RsaHashedImportParams,
(Self::RsassaPkcs1V1_5, Operation::ExportKey) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#rsa-pss-registration>
(Self::RsaPss, Operation::Sign) => ParameterType::RsaPssParams,
(Self::RsaPss, Operation::Verify) => ParameterType::RsaPssParams,
(Self::RsaPss, Operation::GenerateKey) => ParameterType::RsaHashedKeyGenParams,
(Self::RsaPss, Operation::ImportKey) => ParameterType::RsaHashedImportParams,
(Self::RsaPss, Operation::ExportKey) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#rsa-oaep-registration>
(Self::RsaOaep, Operation::Encrypt) => ParameterType::RsaOaepParams,
(Self::RsaOaep, Operation::Decrypt) => ParameterType::RsaOaepParams,
(Self::RsaOaep, Operation::GenerateKey) => ParameterType::RsaHashedKeyGenParams,
(Self::RsaOaep, Operation::ImportKey) => ParameterType::RsaHashedImportParams,
(Self::RsaOaep, Operation::ExportKey) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#ecdsa-registration>
(Self::Ecdsa, Operation::Sign) => ParameterType::EcdsaParams,
(Self::Ecdsa, Operation::Verify) => ParameterType::EcdsaParams,
(Self::Ecdsa, Operation::GenerateKey) => ParameterType::EcKeyGenParams,
(Self::Ecdsa, Operation::ImportKey) => ParameterType::EcKeyImportParams,
(Self::Ecdsa, Operation::ExportKey) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#ecdh-registration>
(Self::Ecdh, Operation::GenerateKey) => ParameterType::EcKeyGenParams,
(Self::Ecdh, Operation::DeriveBits) => ParameterType::EcdhKeyDeriveParams,
(Self::Ecdh, Operation::ImportKey) => ParameterType::EcKeyImportParams,
(Self::Ecdh, Operation::ExportKey) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#ed25519-registration>
(Self::Ed25519, Operation::Sign) => ParameterType::None,
(Self::Ed25519, Operation::Verify) => ParameterType::None,
(Self::Ed25519, Operation::GenerateKey) => ParameterType::None,
(Self::Ed25519, Operation::ImportKey) => ParameterType::None,
(Self::Ed25519, Operation::ExportKey) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#x25519-registration>
(Self::X25519, Operation::DeriveBits) => ParameterType::EcdhKeyDeriveParams,
(Self::X25519, Operation::GenerateKey) => ParameterType::None,
(Self::X25519, Operation::ImportKey) => ParameterType::None,
(Self::X25519, Operation::ExportKey) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#aes-ctr-registration>
(Self::AesCtr, Operation::Encrypt) => ParameterType::AesCtrParams,
(Self::AesCtr, Operation::Decrypt) => ParameterType::AesCtrParams,
(Self::AesCtr, Operation::GenerateKey) => ParameterType::AesKeyGenParams,
(Self::AesCtr, Operation::ImportKey) => ParameterType::None,
(Self::AesCtr, Operation::ExportKey) => ParameterType::None,
(Self::AesCtr, Operation::GetKeyLength) => ParameterType::AesDerivedKeyParams,
// <https://w3c.github.io/webcrypto/#aes-cbc-registration>
(Self::AesCbc, Operation::Encrypt) => ParameterType::AesCbcParams,
(Self::AesCbc, Operation::Decrypt) => ParameterType::AesCbcParams,
(Self::AesCbc, Operation::GenerateKey) => ParameterType::AesKeyGenParams,
(Self::AesCbc, Operation::ImportKey) => ParameterType::None,
(Self::AesCbc, Operation::ExportKey) => ParameterType::None,
(Self::AesCbc, Operation::GetKeyLength) => ParameterType::AesDerivedKeyParams,
// <https://w3c.github.io/webcrypto/#aes-gcm-registration>
(Self::AesGcm, Operation::Encrypt) => ParameterType::AesGcmParams,
(Self::AesGcm, Operation::Decrypt) => ParameterType::AesGcmParams,
(Self::AesGcm, Operation::GenerateKey) => ParameterType::AesKeyGenParams,
(Self::AesGcm, Operation::ImportKey) => ParameterType::None,
(Self::AesGcm, Operation::ExportKey) => ParameterType::None,
(Self::AesGcm, Operation::GetKeyLength) => ParameterType::AesDerivedKeyParams,
// <https://w3c.github.io/webcrypto/#aes-kw-registration>
(Self::AesKw, Operation::WrapKey) => ParameterType::None,
(Self::AesKw, Operation::UnwrapKey) => ParameterType::None,
(Self::AesKw, Operation::GenerateKey) => ParameterType::AesKeyGenParams,
(Self::AesKw, Operation::ImportKey) => ParameterType::None,
(Self::AesKw, Operation::ExportKey) => ParameterType::None,
(Self::AesKw, Operation::GetKeyLength) => ParameterType::AesDerivedKeyParams,
// <https://w3c.github.io/webcrypto/#hmac-registration>
(Self::Hmac, Operation::Sign) => ParameterType::None,
(Self::Hmac, Operation::Verify) => ParameterType::None,
(Self::Hmac, Operation::GenerateKey) => ParameterType::HmacKeyGenParams,
(Self::Hmac, Operation::ImportKey) => ParameterType::HmacImportParams,
(Self::Hmac, Operation::ExportKey) => ParameterType::None,
(Self::Hmac, Operation::GetKeyLength) => ParameterType::HmacImportParams,
// <https://w3c.github.io/webcrypto/#sha-registration>
(Self::Sha(_), Operation::Digest) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#hkdf-registration>
(Self::Hkdf, Operation::DeriveBits) => ParameterType::HkdfParams,
(Self::Hkdf, Operation::ImportKey) => ParameterType::None,
(Self::Hkdf, Operation::GetKeyLength) => ParameterType::None,
// <https://w3c.github.io/webcrypto/#pbkdf2-registration>
(Self::Pbkdf2, Operation::DeriveBits) => ParameterType::Pbkdf2Params,
(Self::Pbkdf2, Operation::ImportKey) => ParameterType::None,
(Self::Pbkdf2, Operation::GetKeyLength) => ParameterType::None,
// <https://wicg.github.io/webcrypto-modern-algos/#ml-kem-registration>
(Self::MlKem(_), Operation::Encapsulate) => ParameterType::None,
(Self::MlKem(_), Operation::Decapsulate) => ParameterType::None,
(Self::MlKem(_), Operation::GenerateKey) => ParameterType::None,
(Self::MlKem(_), Operation::ImportKey) => ParameterType::None,
(Self::MlKem(_), Operation::ExportKey) => ParameterType::None,
// <https://wicg.github.io/webcrypto-modern-algos/#ml-dsa-registration>
(Self::MlDsa(_), Operation::Sign) => ParameterType::ContextParams,
(Self::MlDsa(_), Operation::Verify) => ParameterType::ContextParams,
(Self::MlDsa(_), Operation::GenerateKey) => ParameterType::None,
(Self::MlDsa(_), Operation::ImportKey) => ParameterType::None,
(Self::MlDsa(_), Operation::ExportKey) => ParameterType::None,
// <https://wicg.github.io/webcrypto-modern-algos/#aes-ocb-registration>
(Self::AesOcb, Operation::Encrypt) => ParameterType::AeadParams,
(Self::AesOcb, Operation::Decrypt) => ParameterType::AeadParams,
(Self::AesOcb, Operation::GenerateKey) => ParameterType::AesKeyGenParams,
(Self::AesOcb, Operation::ImportKey) => ParameterType::None,
(Self::AesOcb, Operation::ExportKey) => ParameterType::None,
(Self::AesOcb, Operation::GetKeyLength) => ParameterType::AesDerivedKeyParams,
// <https://wicg.github.io/webcrypto-modern-algos/#chacha20-poly1305-registration>
(Self::ChaCha20Poly1305, Operation::Encrypt) => ParameterType::AeadParams,
(Self::ChaCha20Poly1305, Operation::Decrypt) => ParameterType::AeadParams,
(Self::ChaCha20Poly1305, Operation::GenerateKey) => ParameterType::None,
(Self::ChaCha20Poly1305, Operation::ImportKey) => ParameterType::None,
(Self::ChaCha20Poly1305, Operation::ExportKey) => ParameterType::None,
(Self::ChaCha20Poly1305, Operation::GetKeyLength) => ParameterType::None,
// <https://wicg.github.io/webcrypto-modern-algos/#sha3-registration>
(Self::Sha3(_), Operation::Digest) => ParameterType::None,
// <https://wicg.github.io/webcrypto-modern-algos/#cshake-registration>
(Self::CShake(_), Operation::Digest) => ParameterType::CShakeParams,
// <https://wicg.github.io/webcrypto-modern-algos/#argon2-registration>
(Self::Argon2(_), Operation::DeriveBits) => ParameterType::Argon2Params,
(Self::Argon2(_), Operation::ImportKey) => ParameterType::None,
(Self::Argon2(_), Operation::GetKeyLength) => ParameterType::None,
_ => {
return Err(Error::NotSupported(Some(format!(
"{} does not support {} operation",
self.as_str(),
op.as_str()
))));
},
};
Ok(desired_type)
}
}
impl TryFrom<&str> for SupportedAlgorithm {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
ALG_RSASSA_PKCS1_V1_5 => Ok(SupportedAlgorithm::RsassaPkcs1V1_5),
ALG_RSA_PSS => Ok(SupportedAlgorithm::RsaPss),
ALG_RSA_OAEP => Ok(SupportedAlgorithm::RsaOaep),
ALG_ECDSA => Ok(SupportedAlgorithm::Ecdsa),
ALG_ECDH => Ok(SupportedAlgorithm::Ecdh),
ALG_ED25519 => Ok(SupportedAlgorithm::Ed25519),
ALG_X25519 => Ok(SupportedAlgorithm::X25519),
ALG_AES_CTR => Ok(SupportedAlgorithm::AesCtr),
ALG_AES_CBC => Ok(SupportedAlgorithm::AesCbc),
ALG_AES_GCM => Ok(SupportedAlgorithm::AesGcm),
ALG_AES_KW => Ok(SupportedAlgorithm::AesKw),
ALG_HMAC => Ok(SupportedAlgorithm::Hmac),
ALG_SHA1 => Ok(SupportedAlgorithm::Sha(ShaAlgorithm::Sha1)),
ALG_SHA256 => Ok(SupportedAlgorithm::Sha(ShaAlgorithm::Sha256)),
ALG_SHA384 => Ok(SupportedAlgorithm::Sha(ShaAlgorithm::Sha384)),
ALG_SHA512 => Ok(SupportedAlgorithm::Sha(ShaAlgorithm::Sha512)),
ALG_HKDF => Ok(SupportedAlgorithm::Hkdf),
ALG_PBKDF2 => Ok(SupportedAlgorithm::Pbkdf2),
ALG_ML_KEM_512 => Ok(SupportedAlgorithm::MlKem(MlKemAlgorithm::MlKem512)),
ALG_ML_KEM_768 => Ok(SupportedAlgorithm::MlKem(MlKemAlgorithm::MlKem768)),
ALG_ML_KEM_1024 => Ok(SupportedAlgorithm::MlKem(MlKemAlgorithm::MlKem1024)),
ALG_ML_DSA_44 => Ok(SupportedAlgorithm::MlDsa(MlDsaAlgorithm::MlDsa44)),
ALG_ML_DSA_65 => Ok(SupportedAlgorithm::MlDsa(MlDsaAlgorithm::MlDsa65)),
ALG_ML_DSA_87 => Ok(SupportedAlgorithm::MlDsa(MlDsaAlgorithm::MlDsa87)),
ALG_AES_OCB => Ok(SupportedAlgorithm::AesOcb),
ALG_CHACHA20_POLY1305 => Ok(SupportedAlgorithm::ChaCha20Poly1305),
ALG_SHA3_256 => Ok(SupportedAlgorithm::Sha3(Sha3Algorithm::Sha3_256)),
ALG_SHA3_384 => Ok(SupportedAlgorithm::Sha3(Sha3Algorithm::Sha3_384)),
ALG_SHA3_512 => Ok(SupportedAlgorithm::Sha3(Sha3Algorithm::Sha3_512)),
ALG_CSHAKE_128 => Ok(SupportedAlgorithm::CShake(CShakeAlgorithm::CShake128)),
ALG_CSHAKE_256 => Ok(SupportedAlgorithm::CShake(CShakeAlgorithm::CShake256)),
ALG_ARGON2D => Ok(SupportedAlgorithm::Argon2(Argon2Algorithm::Argon2D)),
ALG_ARGON2I => Ok(SupportedAlgorithm::Argon2(Argon2Algorithm::Argon2I)),
ALG_ARGON2ID => Ok(SupportedAlgorithm::Argon2(Argon2Algorithm::Argon2ID)),
_ => Err(Error::NotSupported(Some(format!(
"Unsupported algorithm: {}",
value
)))),
}
}
}
enum ParameterType {
None,
RsaHashedKeyGenParams,
RsaHashedImportParams,
RsaPssParams,
RsaOaepParams,
EcdsaParams,
EcKeyGenParams,
EcKeyImportParams,
EcdhKeyDeriveParams,
AesCtrParams,
AesKeyGenParams,
AesDerivedKeyParams,
AesCbcParams,
AesGcmParams,
HmacImportParams,
HmacKeyGenParams,
HkdfParams,
Pbkdf2Params,
ContextParams,
AeadParams,
CShakeParams,
Argon2Params,
}
/// The successful output of [`normalize_algorithm`], in form of an union type of (our "subtle"
/// binding of) IDL dictionary types.
///
/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm>
#[derive(Clone, MallocSizeOf)]
enum NormalizedAlgorithm {
Algorithm(SubtleAlgorithm),
RsaHashedKeyGenParams(SubtleRsaHashedKeyGenParams),
RsaHashedImportParams(SubtleRsaHashedImportParams),
RsaPssParams(SubtleRsaPssParams),
RsaOaepParams(SubtleRsaOaepParams),
EcdsaParams(SubtleEcdsaParams),
EcKeyGenParams(SubtleEcKeyGenParams),
EcKeyImportParams(SubtleEcKeyImportParams),
EcdhKeyDeriveParams(SubtleEcdhKeyDeriveParams),
AesCtrParams(SubtleAesCtrParams),
AesKeyGenParams(SubtleAesKeyGenParams),
AesDerivedKeyParams(SubtleAesDerivedKeyParams),
AesCbcParams(SubtleAesCbcParams),
AesGcmParams(SubtleAesGcmParams),
HmacImportParams(SubtleHmacImportParams),
HmacKeyGenParams(SubtleHmacKeyGenParams),
HkdfParams(SubtleHkdfParams),
Pbkdf2Params(SubtlePbkdf2Params),
ContextParams(SubtleContextParams),
AeadParams(SubtleAeadParams),
CShakeParams(SubtleCShakeParams),
Argon2Params(SubtleArgon2Params),
}
/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm>
fn normalize_algorithm(
cx: JSContext,
op: Operation,
alg: &AlgorithmIdentifier,
can_gc: CanGc,
) -> Result<NormalizedAlgorithm, Error> {
match alg {
// If alg is an instance of a DOMString:
ObjectOrString::String(name) => {
// Return the result of running the normalize an algorithm algorithm, with the alg set
// to a new Algorithm dictionary whose name attribute is alg, and with the op set to
// op.
let alg = Algorithm {
name: name.to_owned(),
};
rooted!(in(*cx) let mut alg_value = UndefinedValue());
alg.safe_to_jsval(cx, alg_value.handle_mut(), CanGc::note());
let alg_obj = RootedTraceableBox::new(Heap::default());
alg_obj.set(alg_value.to_object());
normalize_algorithm(cx, op, &ObjectOrString::Object(alg_obj), can_gc)
},
// If alg is an object:
ObjectOrString::Object(obj) => {
// Step 1. Let registeredAlgorithms be the associative container stored at the op key
// of supportedAlgorithms.
// NOTE: The supportedAlgorithms and registeredAlgorithms are expressed as match arms
// in Step 5.2 - Step 10.
// Stpe 2. Let initialAlg be the result of converting the ECMAScript object represented
// by alg to the IDL dictionary type Algorithm, as defined by [WebIDL].
// Step 3. If an error occurred, return the error and terminate this algorithm.
rooted!(in(*cx) let value = ObjectValue(obj.get()));
let initial_alg = dictionary_from_jsval::<Algorithm>(cx, value.handle(), can_gc)?;
// Step 4. Let algName be the value of the name attribute of initialAlg.
// Step 5.
// If registeredAlgorithms contains a key that is a case-insensitive string match
// for algName:
// Step 5.1. Set algName to the value of the matching key.
// Step 5.2. Let desiredType be the IDL dictionary type stored at algName in
// registeredAlgorithms.
// Otherwise:
// Return a new NotSupportedError and terminate this algorithm.
let alg_name = SupportedAlgorithm::from_ignore_case(&initial_alg.name.str())?;
let desired_type = alg_name.support(op)?;
// Step 6. Let normalizedAlgorithm be the result of converting the ECMAScript object
// represented by alg to the IDL dictionary type desiredType, as defined by [WebIDL].
// Step 7. Set the name attribute of normalizedAlgorithm to algName.
// Step 8. If an error occurred, return the error and terminate this algorithm.
// Step 9. Let dictionaries be a list consisting of the IDL dictionary type desiredType
// and all of desiredType's inherited dictionaries, in order from least to most
// derived.
// Step 10. For each dictionary dictionary in dictionaries:
// Step 10.1. For each dictionary member member declared on dictionary, in order:
// Step 10.1.1. Let key be the identifier of member.
// Step 10.1.2. Let idlValue be the value of the dictionary member with key
// name of key on normalizedAlgorithm.
// Step 10.1.3.
// If member is of the type BufferSource and is present:
// Set the dictionary member on normalizedAlgorithm with key name key
// to the result of getting a copy of the bytes held by idlValue,
// replacing the current value.
// If member is of the type HashAlgorithmIdentifier:
// Set the dictionary member on normalizedAlgorithm with key name key
// to the result of normalizing an algorithm, with the alg set to
// idlValue and the op set to "digest".
// If member is of the type AlgorithmIdentifier:
// Set the dictionary member on normalizedAlgorithm with key name key
// to the result of normalizing an algorithm, with the alg set to
// idlValue and the op set to the operation defined by the
// specification that defines the algorithm identified by algName.
//
// NOTE: We do Step 7 first, by setting algName to the name attribute of the JS object
// before IDL dictionary conversion, in order to simplify our implementation.
rooted!(in(*cx) let mut alg_name_ptr = UndefinedValue());
alg_name
.as_str()
.safe_to_jsval(cx, alg_name_ptr.handle_mut(), can_gc);
set_dictionary_property(cx, obj.handle(), c"name", alg_name_ptr.handle())
.map_err(|_| Error::JSFailed)?;
let normalized_algorithm =
NormalizedAlgorithm::from_object_value(cx, value.handle(), desired_type, can_gc)?;
// Step 11. Return normalizedAlgorithm.
Ok(normalized_algorithm)
},
}
}
impl NormalizedAlgorithm {
/// Step 6, 8-10 of the "normalize an algorithm" algorithm
/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm>. This
/// function converts the ECMAScript object represented to a normalized algorithm of the IDL
/// dictionary type desiredType.
///
/// Step 6 and Step 8 is done by `dictionary_from_jsval`.
///
/// Step 9 and Step 10 is done by the `From` and `TryFrom` trait implementations of the inner
/// types (the structs with prefix "Subtle" in their name) of NormalizedAlgorithm.
fn from_object_value(
cx: JSContext,
value: HandleValue,
desired_type: ParameterType,
can_gc: CanGc,
) -> Result<NormalizedAlgorithm, Error> {
let normalized_algorithm = match desired_type {
ParameterType::None => NormalizedAlgorithm::Algorithm(
dictionary_from_jsval::<Algorithm>(cx, value, can_gc)?.into(),
),
ParameterType::RsaHashedKeyGenParams => NormalizedAlgorithm::RsaHashedKeyGenParams(
dictionary_from_jsval::<RootedTraceableBox<RsaHashedKeyGenParams>>(
cx, value, can_gc,
)?
.try_into()?,
),
ParameterType::RsaHashedImportParams => NormalizedAlgorithm::RsaHashedImportParams(
dictionary_from_jsval::<RootedTraceableBox<RsaHashedImportParams>>(
cx, value, can_gc,
)?
.try_into()?,
),
ParameterType::RsaPssParams => NormalizedAlgorithm::RsaPssParams(
dictionary_from_jsval::<RsaPssParams>(cx, value, can_gc)?.into(),
),
ParameterType::RsaOaepParams => NormalizedAlgorithm::RsaOaepParams(
dictionary_from_jsval::<RootedTraceableBox<RsaOaepParams>>(cx, value, can_gc)?
.into(),
),
ParameterType::EcdsaParams => NormalizedAlgorithm::EcdsaParams(
dictionary_from_jsval::<RootedTraceableBox<EcdsaParams>>(cx, value, can_gc)?
.try_into()?,
),
ParameterType::EcKeyGenParams => NormalizedAlgorithm::EcKeyGenParams(
dictionary_from_jsval::<EcKeyGenParams>(cx, value, can_gc)?.into(),
),
ParameterType::EcKeyImportParams => NormalizedAlgorithm::EcKeyImportParams(
dictionary_from_jsval::<EcKeyImportParams>(cx, value, can_gc)?.into(),
),
ParameterType::EcdhKeyDeriveParams => NormalizedAlgorithm::EcdhKeyDeriveParams(
dictionary_from_jsval::<EcdhKeyDeriveParams>(cx, value, can_gc)?.into(),
),
ParameterType::AesCtrParams => NormalizedAlgorithm::AesCtrParams(
dictionary_from_jsval::<RootedTraceableBox<AesCtrParams>>(cx, value, can_gc)?
.into(),
),
ParameterType::AesKeyGenParams => NormalizedAlgorithm::AesKeyGenParams(
dictionary_from_jsval::<AesKeyGenParams>(cx, value, can_gc)?.into(),
),
ParameterType::AesDerivedKeyParams => NormalizedAlgorithm::AesDerivedKeyParams(
dictionary_from_jsval::<AesDerivedKeyParams>(cx, value, can_gc)?.into(),
),
ParameterType::AesCbcParams => NormalizedAlgorithm::AesCbcParams(
dictionary_from_jsval::<RootedTraceableBox<AesCbcParams>>(cx, value, can_gc)?
.into(),
),
ParameterType::AesGcmParams => NormalizedAlgorithm::AesGcmParams(
dictionary_from_jsval::<RootedTraceableBox<AesGcmParams>>(cx, value, can_gc)?
.into(),
),
ParameterType::HmacImportParams => NormalizedAlgorithm::HmacImportParams(
dictionary_from_jsval::<RootedTraceableBox<HmacImportParams>>(cx, value, can_gc)?
.try_into()?,
),
ParameterType::HmacKeyGenParams => NormalizedAlgorithm::HmacKeyGenParams(
dictionary_from_jsval::<RootedTraceableBox<HmacKeyGenParams>>(cx, value, can_gc)?
.try_into()?,
),
ParameterType::HkdfParams => NormalizedAlgorithm::HkdfParams(
dictionary_from_jsval::<RootedTraceableBox<HkdfParams>>(cx, value, can_gc)?
.try_into()?,
),
ParameterType::Pbkdf2Params => NormalizedAlgorithm::Pbkdf2Params(
dictionary_from_jsval::<RootedTraceableBox<Pbkdf2Params>>(cx, value, can_gc)?
.try_into()?,
),
ParameterType::ContextParams => NormalizedAlgorithm::ContextParams(
dictionary_from_jsval::<RootedTraceableBox<ContextParams>>(cx, value, can_gc)?
.into(),
),
ParameterType::AeadParams => NormalizedAlgorithm::AeadParams(
dictionary_from_jsval::<RootedTraceableBox<AeadParams>>(cx, value, can_gc)?.into(),
),
ParameterType::CShakeParams => NormalizedAlgorithm::CShakeParams(
dictionary_from_jsval::<RootedTraceableBox<CShakeParams>>(cx, value, can_gc)?
.into(),
),
ParameterType::Argon2Params => NormalizedAlgorithm::Argon2Params(
dictionary_from_jsval::<RootedTraceableBox<Argon2Params>>(cx, value, can_gc)?
.into(),
),
};
Ok(normalized_algorithm)
}
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
fn name(&self) -> &str {
match self {
NormalizedAlgorithm::Algorithm(algo) => &algo.name,
NormalizedAlgorithm::RsaHashedKeyGenParams(algo) => &algo.name,
NormalizedAlgorithm::RsaHashedImportParams(algo) => &algo.name,
NormalizedAlgorithm::RsaPssParams(algo) => &algo.name,
NormalizedAlgorithm::RsaOaepParams(algo) => &algo.name,
NormalizedAlgorithm::EcdsaParams(algo) => &algo.name,
NormalizedAlgorithm::EcKeyGenParams(algo) => &algo.name,
NormalizedAlgorithm::EcKeyImportParams(algo) => &algo.name,
NormalizedAlgorithm::EcdhKeyDeriveParams(algo) => &algo.name,
NormalizedAlgorithm::AesCtrParams(algo) => &algo.name,
NormalizedAlgorithm::AesKeyGenParams(algo) => &algo.name,
NormalizedAlgorithm::AesDerivedKeyParams(algo) => &algo.name,
NormalizedAlgorithm::AesCbcParams(algo) => &algo.name,
NormalizedAlgorithm::AesGcmParams(algo) => &algo.name,
NormalizedAlgorithm::HmacImportParams(algo) => &algo.name,
NormalizedAlgorithm::HmacKeyGenParams(algo) => &algo.name,
NormalizedAlgorithm::HkdfParams(algo) => &algo.name,
NormalizedAlgorithm::Pbkdf2Params(algo) => &algo.name,
NormalizedAlgorithm::ContextParams(algo) => &algo.name,
NormalizedAlgorithm::AeadParams(algo) => &algo.name,
NormalizedAlgorithm::CShakeParams(algo) => &algo.name,
NormalizedAlgorithm::Argon2Params(algo) => &algo.name,
}
}
fn encrypt(&self, key: &CryptoKey, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
match (self.name(), self) {
(ALG_RSA_OAEP, NormalizedAlgorithm::RsaOaepParams(algo)) => {
rsa_oaep_operation::encrypt(algo, key, plaintext)
},
(ALG_AES_CTR, NormalizedAlgorithm::AesCtrParams(algo)) => {
aes_ctr_operation::encrypt(algo, key, plaintext)
},
(ALG_AES_CBC, NormalizedAlgorithm::AesCbcParams(algo)) => {
aes_cbc_operation::encrypt(algo, key, plaintext)
},
(ALG_AES_GCM, NormalizedAlgorithm::AesGcmParams(algo)) => {
aes_gcm_operation::encrypt(algo, key, plaintext)
},
(ALG_AES_OCB, NormalizedAlgorithm::AeadParams(algo)) => {
aes_ocb_operation::encrypt(algo, key, plaintext)
},
(ALG_CHACHA20_POLY1305, NormalizedAlgorithm::AeadParams(algo)) => {
chacha20_poly1305_operation::encrypt(algo, key, plaintext)
},
_ => Err(Error::NotSupported(None)),
}
}
fn decrypt(&self, key: &CryptoKey, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
match (self.name(), self) {
(ALG_RSA_OAEP, NormalizedAlgorithm::RsaOaepParams(algo)) => {
rsa_oaep_operation::decrypt(algo, key, ciphertext)
},
(ALG_AES_CTR, NormalizedAlgorithm::AesCtrParams(algo)) => {
aes_ctr_operation::decrypt(algo, key, ciphertext)
},
(ALG_AES_CBC, NormalizedAlgorithm::AesCbcParams(algo)) => {
aes_cbc_operation::decrypt(algo, key, ciphertext)
},
(ALG_AES_GCM, NormalizedAlgorithm::AesGcmParams(algo)) => {
aes_gcm_operation::decrypt(algo, key, ciphertext)
},
(ALG_AES_OCB, NormalizedAlgorithm::AeadParams(algo)) => {
aes_ocb_operation::decrypt(algo, key, ciphertext)
},
(ALG_CHACHA20_POLY1305, NormalizedAlgorithm::AeadParams(algo)) => {
chacha20_poly1305_operation::decrypt(algo, key, ciphertext)
},
_ => Err(Error::NotSupported(None)),
}
}
fn sign(&self, key: &CryptoKey, message: &[u8]) -> Result<Vec<u8>, Error> {
match (self.name(), self) {
(ALG_RSASSA_PKCS1_V1_5, NormalizedAlgorithm::Algorithm(_algo)) => {
rsassa_pkcs1_v1_5_operation::sign(key, message)
},
(ALG_RSA_PSS, NormalizedAlgorithm::RsaPssParams(algo)) => {
rsa_pss_operation::sign(algo, key, message)
},
(ALG_ECDSA, NormalizedAlgorithm::EcdsaParams(algo)) => {
ecdsa_operation::sign(algo, key, message)
},
(ALG_ED25519, NormalizedAlgorithm::Algorithm(_algo)) => {
ed25519_operation::sign(key, message)
},
(ALG_HMAC, NormalizedAlgorithm::Algorithm(_algo)) => hmac_operation::sign(key, message),
(
ALG_ML_DSA_44 | ALG_ML_DSA_65 | ALG_ML_DSA_87,
NormalizedAlgorithm::ContextParams(algo),
) => ml_dsa_operation::sign(algo, key, message),
_ => Err(Error::NotSupported(None)),
}
}
fn verify(&self, key: &CryptoKey, message: &[u8], signature: &[u8]) -> Result<bool, Error> {
match (self.name(), self) {
(ALG_RSASSA_PKCS1_V1_5, NormalizedAlgorithm::Algorithm(_algo)) => {
rsassa_pkcs1_v1_5_operation::verify(key, message, signature)
},
(ALG_RSA_PSS, NormalizedAlgorithm::RsaPssParams(algo)) => {
rsa_pss_operation::verify(algo, key, message, signature)
},
(ALG_ECDSA, NormalizedAlgorithm::EcdsaParams(algo)) => {
ecdsa_operation::verify(algo, key, message, signature)
},
(ALG_ED25519, NormalizedAlgorithm::Algorithm(_algo)) => {
ed25519_operation::verify(key, message, signature)
},
(ALG_HMAC, NormalizedAlgorithm::Algorithm(_algo)) => {
hmac_operation::verify(key, message, signature)
},
(
ALG_ML_DSA_44 | ALG_ML_DSA_65 | ALG_ML_DSA_87,
NormalizedAlgorithm::ContextParams(algo),
) => ml_dsa_operation::verify(algo, key, message, signature),
_ => Err(Error::NotSupported(None)),
}
}
fn digest(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
match (self.name(), self) {
(ALG_SHA1, NormalizedAlgorithm::Algorithm(algo)) => {
sha_operation::digest(algo, message)
},
(ALG_SHA256, NormalizedAlgorithm::Algorithm(algo)) => {
sha_operation::digest(algo, message)
},
(ALG_SHA384, NormalizedAlgorithm::Algorithm(algo)) => {
sha_operation::digest(algo, message)
},
(ALG_SHA512, NormalizedAlgorithm::Algorithm(algo)) => {
sha_operation::digest(algo, message)
},
(ALG_SHA3_256, NormalizedAlgorithm::Algorithm(algo)) => {
sha3_operation::digest(algo, message)
},
(ALG_SHA3_384, NormalizedAlgorithm::Algorithm(algo)) => {
sha3_operation::digest(algo, message)
},
(ALG_SHA3_512, NormalizedAlgorithm::Algorithm(algo)) => {
sha3_operation::digest(algo, message)
},
(ALG_CSHAKE_128, NormalizedAlgorithm::CShakeParams(algo)) => {
cshake_operation::digest(algo, message)
},
(ALG_CSHAKE_256, NormalizedAlgorithm::CShakeParams(algo)) => {
cshake_operation::digest(algo, message)
},
_ => Err(Error::NotSupported(None)),
}
}
fn generate_key(
&self,
global: &GlobalScope,
extractable: bool,
usages: Vec<KeyUsage>,
can_gc: CanGc,
) -> Result<CryptoKeyOrCryptoKeyPair, Error> {
match (self.name(), self) {
(ALG_RSASSA_PKCS1_V1_5, NormalizedAlgorithm::RsaHashedKeyGenParams(algo)) => {
rsassa_pkcs1_v1_5_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair)
},
(ALG_RSA_PSS, NormalizedAlgorithm::RsaHashedKeyGenParams(algo)) => {
rsa_pss_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair)
},
(ALG_RSA_OAEP, NormalizedAlgorithm::RsaHashedKeyGenParams(algo)) => {
rsa_oaep_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair)
},
(ALG_ECDSA, NormalizedAlgorithm::EcKeyGenParams(algo)) => {
ecdsa_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair)
},
(ALG_ECDH, NormalizedAlgorithm::EcKeyGenParams(algo)) => {
ecdh_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair)
},
(ALG_ED25519, NormalizedAlgorithm::Algorithm(_algo)) => {
ed25519_operation::generate_key(global, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair)
},
(ALG_X25519, NormalizedAlgorithm::Algorithm(_algo)) => {
x25519_operation::generate_key(global, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair)
},
(ALG_AES_CTR, NormalizedAlgorithm::AesKeyGenParams(algo)) => {
aes_ctr_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(ALG_AES_CBC, NormalizedAlgorithm::AesKeyGenParams(algo)) => {
aes_cbc_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(ALG_AES_GCM, NormalizedAlgorithm::AesKeyGenParams(algo)) => {
aes_gcm_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(ALG_AES_KW, NormalizedAlgorithm::AesKeyGenParams(algo)) => {
aes_kw_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(ALG_HMAC, NormalizedAlgorithm::HmacKeyGenParams(algo)) => {
hmac_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(
ALG_ML_KEM_512 | ALG_ML_KEM_768 | ALG_ML_KEM_1024,
NormalizedAlgorithm::Algorithm(algo),
) => ml_kem_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair),
(
ALG_ML_DSA_44 | ALG_ML_DSA_65 | ALG_ML_DSA_87,
NormalizedAlgorithm::Algorithm(algo),
) => ml_dsa_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKeyPair),
(ALG_AES_OCB, NormalizedAlgorithm::AesKeyGenParams(algo)) => {
aes_ocb_operation::generate_key(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(ALG_CHACHA20_POLY1305, NormalizedAlgorithm::Algorithm(_algo)) => {
chacha20_poly1305_operation::generate_key(global, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
_ => Err(Error::NotSupported(None)),
}
}
fn derive_bits(&self, key: &CryptoKey, length: Option<u32>) -> Result<Vec<u8>, Error> {
match (self.name(), self) {
(ALG_ECDH, NormalizedAlgorithm::EcdhKeyDeriveParams(algo)) => {
ecdh_operation::derive_bits(algo, key, length)
},
(ALG_X25519, NormalizedAlgorithm::EcdhKeyDeriveParams(algo)) => {
x25519_operation::derive_bits(algo, key, length)
},
(ALG_HKDF, NormalizedAlgorithm::HkdfParams(algo)) => {
hkdf_operation::derive_bits(algo, key, length)
},
(ALG_PBKDF2, NormalizedAlgorithm::Pbkdf2Params(algo)) => {
pbkdf2_operation::derive_bits(algo, key, length)
},
(ALG_ARGON2D, NormalizedAlgorithm::Argon2Params(algo)) => {
argon2_operation::derive_bits(algo, key, length)
},
(ALG_ARGON2I, NormalizedAlgorithm::Argon2Params(algo)) => {
argon2_operation::derive_bits(algo, key, length)
},
(ALG_ARGON2ID, NormalizedAlgorithm::Argon2Params(algo)) => {
argon2_operation::derive_bits(algo, key, length)
},
_ => Err(Error::NotSupported(None)),
}
}
fn import_key(
&self,
global: &GlobalScope,
format: KeyFormat,
key_data: &[u8],
extractable: bool,
usages: Vec<KeyUsage>,
can_gc: CanGc,
) -> Result<DomRoot<CryptoKey>, Error> {
match (self.name(), self) {
(ALG_RSASSA_PKCS1_V1_5, NormalizedAlgorithm::RsaHashedImportParams(algo)) => {
rsassa_pkcs1_v1_5_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_RSA_PSS, NormalizedAlgorithm::RsaHashedImportParams(algo)) => {
rsa_pss_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_RSA_OAEP, NormalizedAlgorithm::RsaHashedImportParams(algo)) => {
rsa_oaep_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_ECDSA, NormalizedAlgorithm::EcKeyImportParams(algo)) => {
ecdsa_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_ECDH, NormalizedAlgorithm::EcKeyImportParams(algo)) => ecdh_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
),
(ALG_ED25519, NormalizedAlgorithm::Algorithm(_algo)) => {
ed25519_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(ALG_X25519, NormalizedAlgorithm::Algorithm(_algo)) => {
x25519_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(ALG_AES_CTR, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_ctr_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(ALG_AES_CBC, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_cbc_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(ALG_AES_GCM, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_gcm_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(ALG_AES_KW, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_kw_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(ALG_HMAC, NormalizedAlgorithm::HmacImportParams(algo)) => hmac_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
),
(ALG_HKDF, NormalizedAlgorithm::Algorithm(_algo)) => {
hkdf_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(ALG_PBKDF2, NormalizedAlgorithm::Algorithm(_algo)) => {
pbkdf2_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(
ALG_ML_KEM_512 | ALG_ML_KEM_768 | ALG_ML_KEM_1024,
NormalizedAlgorithm::Algorithm(algo),
) => ml_kem_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
),
(
ALG_ML_DSA_44 | ALG_ML_DSA_65 | ALG_ML_DSA_87,
NormalizedAlgorithm::Algorithm(algo),
) => ml_dsa_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
),
(ALG_AES_OCB, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_ocb_operation::import_key(global, format, key_data, extractable, usages, can_gc)
},
(ALG_CHACHA20_POLY1305, NormalizedAlgorithm::Algorithm(_algo)) => {
chacha20_poly1305_operation::import_key(
global,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_ARGON2D, NormalizedAlgorithm::Algorithm(algo)) => argon2_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
),
(ALG_ARGON2I, NormalizedAlgorithm::Algorithm(algo)) => argon2_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
),
(ALG_ARGON2ID, NormalizedAlgorithm::Algorithm(algo)) => argon2_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
),
_ => Err(Error::NotSupported(None)),
}
}
fn wrap_key(&self, key: &CryptoKey, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
match (self.name(), self) {
(ALG_AES_KW, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_kw_operation::wrap_key(key, plaintext)
},
_ => Err(Error::NotSupported(None)),
}
}
fn unwrap_key(&self, key: &CryptoKey, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
match (self.name(), self) {
(ALG_AES_KW, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_kw_operation::unwrap_key(key, ciphertext)
},
_ => Err(Error::NotSupported(None)),
}
}
fn get_key_length(&self) -> Result<Option<u32>, Error> {
match (self.name(), self) {
(ALG_AES_CTR, NormalizedAlgorithm::AesDerivedKeyParams(algo)) => {
aes_ctr_operation::get_key_length(algo)
},
(ALG_AES_CBC, NormalizedAlgorithm::AesDerivedKeyParams(algo)) => {
aes_cbc_operation::get_key_length(algo)
},
(ALG_AES_GCM, NormalizedAlgorithm::AesDerivedKeyParams(algo)) => {
aes_gcm_operation::get_key_length(algo)
},
(ALG_AES_KW, NormalizedAlgorithm::AesDerivedKeyParams(algo)) => {
aes_kw_operation::get_key_length(algo)
},
(ALG_HMAC, NormalizedAlgorithm::HmacImportParams(algo)) => {
hmac_operation::get_key_length(algo)
},
(ALG_HKDF, NormalizedAlgorithm::Algorithm(_algo)) => hkdf_operation::get_key_length(),
(ALG_PBKDF2, NormalizedAlgorithm::Algorithm(_algo)) => {
pbkdf2_operation::get_key_length()
},
(ALG_AES_OCB, NormalizedAlgorithm::AesDerivedKeyParams(algo)) => {
aes_ocb_operation::get_key_length(algo)
},
(ALG_CHACHA20_POLY1305, NormalizedAlgorithm::Algorithm(_algo)) => {
chacha20_poly1305_operation::get_key_length()
},
(ALG_ARGON2D, NormalizedAlgorithm::Algorithm(_algo)) => {
argon2_operation::get_key_length()
},
(ALG_ARGON2I, NormalizedAlgorithm::Algorithm(_algo)) => {
argon2_operation::get_key_length()
},
(ALG_ARGON2ID, NormalizedAlgorithm::Algorithm(_algo)) => {
argon2_operation::get_key_length()
},
_ => Err(Error::NotSupported(None)),
}
}
fn encapsulate(&self, key: &CryptoKey) -> Result<SubtleEncapsulatedBits, Error> {
match (self.name(), self) {
(
ALG_ML_KEM_512 | ALG_ML_KEM_768 | ALG_ML_KEM_1024,
NormalizedAlgorithm::Algorithm(algo),
) => ml_kem_operation::encapsulate(algo, key),
_ => Err(Error::NotSupported(None)),
}
}
fn decapsulate(&self, key: &CryptoKey, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
match (self.name(), self) {
(
ALG_ML_KEM_512 | ALG_ML_KEM_768 | ALG_ML_KEM_1024,
NormalizedAlgorithm::Algorithm(algo),
) => ml_kem_operation::decapsulate(algo, key, ciphertext),
_ => Err(Error::NotSupported(None)),
}
}
}
/// Return the result of performing the export key operation specified by the [[algorithm]]
/// internal slot of key using key and format.
///
/// According to the WebCrypto API spec, the export key operation does not rely on the algorithm
/// normalization, We create this helper function to minic the functions of NormalizedAlgorithm
/// for export key operation.
fn perform_export_key_operation(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
match key.algorithm().name() {
ALG_RSASSA_PKCS1_V1_5 => rsassa_pkcs1_v1_5_operation::export_key(format, key),
ALG_RSA_PSS => rsa_pss_operation::export_key(format, key),
ALG_RSA_OAEP => rsa_oaep_operation::export_key(format, key),
ALG_ECDSA => ecdsa_operation::export_key(format, key),
ALG_ECDH => ecdh_operation::export_key(format, key),
ALG_ED25519 => ed25519_operation::export_key(format, key),
ALG_X25519 => x25519_operation::export_key(format, key),
ALG_AES_CTR => aes_ctr_operation::export_key(format, key),
ALG_AES_CBC => aes_cbc_operation::export_key(format, key),
ALG_AES_GCM => aes_gcm_operation::export_key(format, key),
ALG_AES_KW => aes_kw_operation::export_key(format, key),
ALG_HMAC => hmac_operation::export_key(format, key),
ALG_ML_KEM_512 | ALG_ML_KEM_768 | ALG_ML_KEM_1024 => {
ml_kem_operation::export_key(format, key)
},
ALG_ML_DSA_44 | ALG_ML_DSA_65 | ALG_ML_DSA_87 => ml_dsa_operation::export_key(format, key),
ALG_AES_OCB => aes_ocb_operation::export_key(format, key),
ALG_CHACHA20_POLY1305 => chacha20_poly1305_operation::export_key(format, key),
_ => Err(Error::NotSupported(None)),
}
}