Skip to main content

script/dom/webcrypto/
cryptokey.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6use std::ptr::NonNull;
7
8use dom_struct::dom_struct;
9use js::jsapi::{Heap, JSObject, Value};
10use malloc_size_of::MallocSizeOf;
11use script_bindings::cell::DomRefCell;
12use script_bindings::conversions::SafeToJSValConvertible;
13use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
14use zeroize::Zeroizing;
15
16use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
17    CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
18};
19use crate::dom::bindings::root::DomRoot;
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::subtlecrypto::KeyAlgorithmAndDerivatives;
22use crate::script_runtime::{CanGc, JSContext};
23
24pub(crate) enum CryptoKeyOrCryptoKeyPair {
25    CryptoKey(DomRoot<CryptoKey>),
26    CryptoKeyPair(CryptoKeyPair),
27}
28
29/// The underlying cryptographic data this key represents.
30///
31/// Please make sure the inner types for secret variants implement the `zeroize::ZeroizeOnDrop`
32/// trait, which signifies that the type will call `Zeroize::zeroize` on `Drop` to securely erase
33/// the secret from memory.
34pub(crate) enum Handle {
35    RsaPrivateKey(rsa::RsaPrivateKey),
36    RsaPublicKey(rsa::RsaPublicKey),
37    P256PrivateKey(p256::SecretKey),
38    P384PrivateKey(p384::SecretKey),
39    P521PrivateKey(p521::SecretKey),
40    P256PublicKey(p256::PublicKey),
41    P384PublicKey(p384::PublicKey),
42    P521PublicKey(p521::PublicKey),
43    Ed25519PrivateKey(Zeroizing<Vec<u8>>),
44    Ed25519PublicKey(Vec<u8>),
45    X25519PrivateKey(x25519_dalek::StaticSecret),
46    X25519PublicKey(x25519_dalek::PublicKey),
47    Aes128Key(aes::cipher::crypto_common::Key<aes::Aes128>),
48    Aes192Key(aes::cipher::crypto_common::Key<aes::Aes192>),
49    Aes256Key(aes::cipher::crypto_common::Key<aes::Aes256>),
50    HkdfSecret(Zeroizing<Vec<u8>>),
51    Pbkdf2(Zeroizing<Vec<u8>>),
52    Hmac(Zeroizing<Vec<u8>>),
53    MlKem512PrivateKey((ml_kem::B32, ml_kem::B32)),
54    MlKem768PrivateKey((ml_kem::B32, ml_kem::B32)),
55    MlKem1024PrivateKey((ml_kem::B32, ml_kem::B32)),
56    MlKem512PublicKey(Box<ml_kem::Encoded<ml_kem::kem::EncapsulationKey<ml_kem::MlKem512Params>>>),
57    MlKem768PublicKey(Box<ml_kem::Encoded<ml_kem::kem::EncapsulationKey<ml_kem::MlKem768Params>>>),
58    MlKem1024PublicKey(
59        Box<ml_kem::Encoded<ml_kem::kem::EncapsulationKey<ml_kem::MlKem1024Params>>>,
60    ),
61    MlDsa44PrivateKey(ml_dsa::B32),
62    MlDsa65PrivateKey(ml_dsa::B32),
63    MlDsa87PrivateKey(ml_dsa::B32),
64    MlDsa44PublicKey(Box<ml_dsa::EncodedVerifyingKey<ml_dsa::MlDsa44>>),
65    MlDsa65PublicKey(Box<ml_dsa::EncodedVerifyingKey<ml_dsa::MlDsa65>>),
66    MlDsa87PublicKey(Box<ml_dsa::EncodedVerifyingKey<ml_dsa::MlDsa87>>),
67    ChaCha20Poly1305Key(chacha20poly1305::Key),
68    Argon2Password(Zeroizing<Vec<u8>>),
69}
70
71/// <https://w3c.github.io/webcrypto/#cryptokey-interface>
72#[dom_struct]
73pub(crate) struct CryptoKey {
74    reflector_: Reflector,
75
76    /// <https://w3c.github.io/webcrypto/#dfn-CryptoKey-slot-type>
77    key_type: KeyType,
78
79    /// <https://w3c.github.io/webcrypto/#dfn-CryptoKey-slot-extractable>
80    extractable: Cell<bool>,
81
82    /// <https://w3c.github.io/webcrypto/#dfn-CryptoKey-slot-algorithm>
83    ///
84    /// The contents of the [[algorithm]] internal slot shall be, or be derived from, a
85    /// KeyAlgorithm.
86    #[no_trace]
87    algorithm: KeyAlgorithmAndDerivatives,
88
89    /// <https://w3c.github.io/webcrypto/#dfn-CryptoKey-slot-algorithm_cached>
90    #[ignore_malloc_size_of = "Defined in mozjs"]
91    algorithm_cached: Heap<*mut JSObject>,
92
93    /// <https://w3c.github.io/webcrypto/#dfn-CryptoKey-slot-usages>
94    ///
95    /// The contents of the [[usages]] internal slot shall be of type Sequence<KeyUsage>.
96    usages: DomRefCell<Vec<KeyUsage>>,
97
98    /// <https://w3c.github.io/webcrypto/#dfn-CryptoKey-slot-usages_cached>
99    #[ignore_malloc_size_of = "Defined in mozjs"]
100    usages_cached: Heap<*mut JSObject>,
101
102    /// <https://w3c.github.io/webcrypto/#dfn-CryptoKey-slot-handle>
103    #[no_trace]
104    handle: Handle,
105}
106
107impl CryptoKey {
108    fn new_inherited(
109        key_type: KeyType,
110        extractable: bool,
111        algorithm: KeyAlgorithmAndDerivatives,
112        usages: Vec<KeyUsage>,
113        handle: Handle,
114    ) -> CryptoKey {
115        CryptoKey {
116            reflector_: Reflector::new(),
117            key_type,
118            extractable: Cell::new(extractable),
119            algorithm,
120            algorithm_cached: Heap::default(),
121            usages: DomRefCell::new(usages),
122            usages_cached: Heap::default(),
123            handle,
124        }
125    }
126
127    pub(crate) fn new(
128        cx: &mut js::context::JSContext,
129        global: &GlobalScope,
130        key_type: KeyType,
131        extractable: bool,
132        algorithm: KeyAlgorithmAndDerivatives,
133        usages: Vec<KeyUsage>,
134        handle: Handle,
135    ) -> DomRoot<CryptoKey> {
136        let crypto_key = reflect_dom_object_with_cx(
137            Box::new(CryptoKey::new_inherited(
138                key_type,
139                extractable,
140                algorithm.clone(),
141                usages.clone(),
142                handle,
143            )),
144            global,
145            cx,
146        );
147
148        // Create and store a cached object of algorithm
149        rooted!(&in(cx) let mut algorithm_object_value: Value);
150        algorithm.safe_to_jsval(
151            cx.into(),
152            algorithm_object_value.handle_mut(),
153            CanGc::from_cx(cx),
154        );
155        crypto_key
156            .algorithm_cached
157            .set(algorithm_object_value.to_object());
158
159        // Create and store a cached object of usages
160        rooted!(&in(cx) let mut usages_object_value: Value);
161        usages.safe_to_jsval(
162            cx.into(),
163            usages_object_value.handle_mut(),
164            CanGc::from_cx(cx),
165        );
166        crypto_key
167            .usages_cached
168            .set(usages_object_value.to_object());
169
170        crypto_key
171    }
172
173    pub(crate) fn algorithm(&self) -> &KeyAlgorithmAndDerivatives {
174        &self.algorithm
175    }
176
177    pub(crate) fn usages(&self) -> Vec<KeyUsage> {
178        self.usages.borrow().clone()
179    }
180
181    pub(crate) fn handle(&self) -> &Handle {
182        &self.handle
183    }
184
185    pub(crate) fn set_extractable(&self, extractable: bool) {
186        self.extractable.set(extractable);
187    }
188
189    pub(crate) fn set_usages(&self, cx: &mut js::context::JSContext, usages: &[KeyUsage]) {
190        *self.usages.borrow_mut() = usages.to_owned();
191
192        // Create and store a cached object of usages
193        rooted!(&in(cx) let mut usages_object_value: Value);
194        usages.safe_to_jsval(
195            cx.into(),
196            usages_object_value.handle_mut(),
197            CanGc::from_cx(cx),
198        );
199        self.usages_cached.set(usages_object_value.to_object());
200    }
201}
202
203impl CryptoKeyMethods<crate::DomTypeHolder> for CryptoKey {
204    /// <https://w3c.github.io/webcrypto/#dom-cryptokey-type>
205    fn Type(&self) -> KeyType {
206        // Reflects the [[type]] internal slot, which contains the type of the underlying key.
207        self.key_type
208    }
209
210    /// <https://w3c.github.io/webcrypto/#dom-cryptokey-extractable>
211    fn Extractable(&self) -> bool {
212        // Reflects the [[extractable]] internal slot, which indicates whether or not the raw
213        // keying material may be exported by the application.
214        self.extractable.get()
215    }
216
217    /// <https://w3c.github.io/webcrypto/#dom-cryptokey-algorithm>
218    fn Algorithm(&self, _cx: JSContext) -> NonNull<JSObject> {
219        // Returns the cached ECMAScript object associated with the [[algorithm]] internal slot.
220        NonNull::new(self.algorithm_cached.get()).unwrap()
221    }
222
223    /// <https://w3c.github.io/webcrypto/#dom-cryptokey-usages>
224    fn Usages(&self, _cx: JSContext) -> NonNull<JSObject> {
225        // Returns the cached ECMAScript object associated with the [[usages]] internal slot, which
226        // indicates which cryptographic operations are permissible to be used with this key.
227        NonNull::new(self.usages_cached.get()).unwrap()
228    }
229}
230
231impl Handle {
232    pub(crate) fn as_bytes(&self) -> &[u8] {
233        match self {
234            Self::Pbkdf2(bytes) => bytes,
235            Self::Hmac(bytes) => bytes,
236            Self::Ed25519PrivateKey(bytes) => bytes,
237            Self::Ed25519PublicKey(bytes) => bytes,
238            _ => unreachable!(),
239        }
240    }
241}
242
243impl MallocSizeOf for Handle {
244    fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
245        match self {
246            Handle::RsaPrivateKey(private_key) => private_key.size_of(ops),
247            Handle::RsaPublicKey(public_key) => public_key.size_of(ops),
248            Handle::P256PrivateKey(private_key) => private_key.size_of(ops),
249            Handle::P384PrivateKey(private_key) => private_key.size_of(ops),
250            Handle::P521PrivateKey(private_key) => private_key.size_of(ops),
251            Handle::P256PublicKey(public_key) => public_key.size_of(ops),
252            Handle::P384PublicKey(public_key) => public_key.size_of(ops),
253            Handle::P521PublicKey(public_key) => public_key.size_of(ops),
254            Handle::Ed25519PrivateKey(bytes) => bytes.size_of(ops),
255            Handle::Ed25519PublicKey(bytes) => bytes.size_of(ops),
256            Handle::X25519PrivateKey(private_key) => private_key.size_of(ops),
257            Handle::X25519PublicKey(public_key) => public_key.size_of(ops),
258            Handle::Aes128Key(key) => key.size_of(ops),
259            Handle::Aes192Key(key) => key.size_of(ops),
260            Handle::Aes256Key(key) => key.size_of(ops),
261            Handle::HkdfSecret(secret) => secret.size_of(ops),
262            Handle::Pbkdf2(bytes) => bytes.size_of(ops),
263            Handle::Hmac(bytes) => bytes.size_of(ops),
264            Handle::MlKem512PrivateKey(seed) => seed.0.size_of(ops) + seed.1.size_of(ops),
265            Handle::MlKem768PrivateKey(seed) => seed.0.size_of(ops) + seed.1.size_of(ops),
266            Handle::MlKem1024PrivateKey(seed) => seed.0.size_of(ops) + seed.1.size_of(ops),
267            Handle::MlKem512PublicKey(public_key) => public_key.size_of(ops),
268            Handle::MlKem768PublicKey(public_key) => public_key.size_of(ops),
269            Handle::MlKem1024PublicKey(public_key) => public_key.size_of(ops),
270            Handle::MlDsa44PrivateKey(seed) => seed.size_of(ops),
271            Handle::MlDsa65PrivateKey(seed) => seed.size_of(ops),
272            Handle::MlDsa87PrivateKey(seed) => seed.size_of(ops),
273            Handle::MlDsa44PublicKey(public_key) => public_key.size_of(ops),
274            Handle::MlDsa65PublicKey(public_key) => public_key.size_of(ops),
275            Handle::MlDsa87PublicKey(public_key) => public_key.size_of(ops),
276            Handle::ChaCha20Poly1305Key(key) => key.size_of(ops),
277            Handle::Argon2Password(password) => password.size_of(ops),
278        }
279    }
280}