Skip to main content

script_webcrypto/
crypto.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 dom_struct::dom_struct;
6use js::context::JSContext;
7use js::jsapi::Type;
8use js::rust::CustomAutoRooterGuard;
9use js::typedarray::{ArrayBufferView, HeapArrayBufferView};
10use jstraceable_derive::JSTraceable;
11use malloc_size_of_derive::MallocSizeOf;
12use script_bindings::DomTypes;
13use script_bindings::buffer_source::HeapBufferSource;
14use script_bindings::codegen::GenericBindings::CryptoBinding::{CryptoMethods, Wrap as CryptoWrap};
15use script_bindings::dom::MutNullableDom;
16use script_bindings::error::{Error, Fallible};
17use script_bindings::reflector::{DomGlobalGeneric, Reflector, reflect_dom_object_with_wrap};
18use script_bindings::root::DomRoot;
19use script_bindings::str::DOMString;
20use script_bindings::trace::RootedTraceableBox;
21use uuid::Uuid;
22
23use crate::subtlecrypto::SubtleCrypto;
24use crate::traits::Equivalence;
25
26/// <https://w3c.github.io/webcrypto/#crypto-interface>
27#[dom_struct]
28pub struct Crypto<D: DomTypes> {
29    reflector_: Reflector,
30    subtle: MutNullableDom<SubtleCrypto<D>>,
31}
32
33impl<D> Crypto<D>
34where
35    D: Equivalence,
36{
37    fn new_inherited() -> Crypto<D> {
38        Crypto {
39            reflector_: Reflector::new(),
40            subtle: MutNullableDom::default(),
41        }
42    }
43
44    pub fn new(cx: &mut JSContext, global: &D::GlobalScope) -> DomRoot<Crypto<D>> {
45        reflect_dom_object_with_wrap::<D, _, _>(
46            cx,
47            Box::new(Crypto::new_inherited()),
48            global,
49            CryptoWrap::<D>,
50        )
51    }
52}
53
54impl<D> CryptoMethods<D> for Crypto<D>
55where
56    D: Equivalence,
57    Self: DomGlobalGeneric<D>,
58{
59    /// <https://w3c.github.io/webcrypto/#dfn-Crypto-attribute-subtle>
60    fn Subtle(&self, cx: &mut JSContext) -> DomRoot<SubtleCrypto<D>> {
61        self.subtle
62            .or_init(|| SubtleCrypto::new(cx, &*self.global_from_reflector()))
63    }
64
65    /// <https://w3c.github.io/webcrypto/#Crypto-method-getRandomValues>
66    fn GetRandomValues(
67        &self,
68        cx: &mut JSContext,
69        mut array: CustomAutoRooterGuard<ArrayBufferView>,
70    ) -> Fallible<RootedTraceableBox<HeapArrayBufferView>> {
71        // Step 1. If array is not an Int8Array, Uint8Array, Uint8ClampedArray, Int16Array,
72        // Uint16Array, Int32Array, Uint32Array, BigInt64Array, or BigUint64Array, then throw a
73        // TypeMismatchError and terminate the algorithm.
74        if !matches!(
75            array.get_array_type(),
76            Type::Int8 |
77                Type::Uint8 |
78                Type::Uint8Clamped |
79                Type::Int16 |
80                Type::Uint16 |
81                Type::Int32 |
82                Type::Uint32 |
83                Type::BigInt64 |
84                Type::BigUint64
85        ) {
86            return Err(Error::TypeMismatch(Some(
87                "The array must be an integer-based TypedArray".into(),
88            )));
89        }
90
91        // Step 2. Let byteLength be the byte length of array.
92        // Step 3. If byteLength is greater than 65536, throw a QuotaExceededError and terminate the
93        // algorithm.
94        // Step 4. Let bytes be a byte sequence of length byteLength.
95        let bytes = array.as_mut_slice_safe(cx.no_gc()).unwrap_or_default();
96        let byte_length = bytes.len();
97        if byte_length > 65536 {
98            return Err(Error::QuotaExceeded {
99                quota: None,
100                requested: None,
101            });
102        }
103
104        // Step 5. Fill bytes with cryptographically secure random bytes.
105        // Step 6. Write bytes into array.
106        rand::fill(bytes);
107
108        // Step 7. Return array.
109        let array = HeapBufferSource::from_view(cx, array);
110        array.get_typed_array().map_err(|_| Error::JSFailed)
111    }
112
113    /// <https://w3c.github.io/webcrypto/#Crypto-method-randomUUID>
114    fn RandomUUID(&self) -> DOMString {
115        // Step 1. Let bytes be a byte sequence of length 16.
116        // Step 2. Fill bytes with cryptographically secure random bytes.
117        // Step 3. Set the 4 most significant bits of bytes[6], which represent the UUID version, to 0100.
118        // Step 4. Set the 2 most significant bits of bytes[8], which represent the UUID variant, to 10.
119        // Step 5.
120        // Return the string concatenation of «
121        //     hexadecimal representation of bytes[0], hexadecimal representation of bytes[1],
122        //     hexadecimal representation of bytes[2], hexadecimal representation of bytes[3],
123        //     "-",
124        //     hexadecimal representation of bytes[4], hexadecimal representation of bytes[5],
125        //     "-",
126        //     hexadecimal representation of bytes[6], hexadecimal representation of bytes[7],
127        //     "-",
128        //     hexadecimal representation of bytes[8], hexadecimal representation of bytes[9],
129        //     "-",
130        //     hexadecimal representation of bytes[10], hexadecimal representation of bytes[11],
131        //     hexadecimal representation of bytes[12], hexadecimal representation of bytes[13],
132        //     hexadecimal representation of bytes[14], hexadecimal representation of bytes[15]
133        // ».
134        let uuid = Uuid::new_v4();
135        uuid.hyphenated()
136            .encode_lower(&mut Uuid::encode_buffer())
137            .to_owned()
138            .into()
139    }
140}