Files
servo/components/script/dom/subtlecrypto.rs
Kingsley Yung 889749643f script: Implement encrypt and decrypt operations of AES-OCB (#41829)
Finish on adding AES-OCB support to WebCrypto API.

This patch implements the encrypt and decrypt operations of AES-OCB,
with the `ocb3` crate. The "get key length" operation of AES-OCB is also
implemented for internal use.

Specification:
-
<https://wicg.github.io/webcrypto-modern-algos/#aes-ocb-operations-encrypt>
-
<https://wicg.github.io/webcrypto-modern-algos/#aes-ocb-operations-decrypt>
-
<https://wicg.github.io/webcrypto-modern-algos/#aes-ocb-operations-get-key-length>

Testing: Pass some WPT tests that were expected to fail.
Fixes: Part of #41762

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
2026-01-11 05:06:10 +00:00

4558 lines
197 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_common;
mod aes_ocb_operation;
mod aes_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(), "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_operation::encrypt_aes_ctr(algo, key, plaintext)
},
(ALG_AES_CBC, NormalizedAlgorithm::AesCbcParams(algo)) => {
aes_operation::encrypt_aes_cbc(algo, key, plaintext)
},
(ALG_AES_GCM, NormalizedAlgorithm::AesGcmParams(algo)) => {
aes_operation::encrypt_aes_gcm(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_operation::decrypt_aes_ctr(algo, key, ciphertext)
},
(ALG_AES_CBC, NormalizedAlgorithm::AesCbcParams(algo)) => {
aes_operation::decrypt_aes_cbc(algo, key, ciphertext)
},
(ALG_AES_GCM, NormalizedAlgorithm::AesGcmParams(algo)) => {
aes_operation::decrypt_aes_gcm(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_operation::generate_key_aes_ctr(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(ALG_AES_CBC, NormalizedAlgorithm::AesKeyGenParams(algo)) => {
aes_operation::generate_key_aes_cbc(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(ALG_AES_GCM, NormalizedAlgorithm::AesKeyGenParams(algo)) => {
aes_operation::generate_key_aes_gcm(global, algo, extractable, usages, can_gc)
.map(CryptoKeyOrCryptoKeyPair::CryptoKey)
},
(ALG_AES_KW, NormalizedAlgorithm::AesKeyGenParams(algo)) => {
aes_operation::generate_key_aes_kw(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_operation::import_key_aes_ctr(
global,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_AES_CBC, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_operation::import_key_aes_cbc(
global,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_AES_GCM, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_operation::import_key_aes_gcm(
global,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_AES_KW, NormalizedAlgorithm::Algorithm(_algo)) => {
aes_operation::import_key_aes_kw(
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_operation::wrap_key_aes_kw(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_operation::unwrap_key_aes_kw(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_operation::get_key_length_aes_ctr(algo)
},
(ALG_AES_CBC, NormalizedAlgorithm::AesDerivedKeyParams(algo)) => {
aes_operation::get_key_length_aes_cbc(algo)
},
(ALG_AES_GCM, NormalizedAlgorithm::AesDerivedKeyParams(algo)) => {
aes_operation::get_key_length_aes_gcm(algo)
},
(ALG_AES_KW, NormalizedAlgorithm::AesDerivedKeyParams(algo)) => {
aes_operation::get_key_length_aes_kw(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_operation::export_key_aes_ctr(format, key),
ALG_AES_CBC => aes_operation::export_key_aes_cbc(format, key),
ALG_AES_GCM => aes_operation::export_key_aes_gcm(format, key),
ALG_AES_KW => aes_operation::export_key_aes_kw(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)),
}
}