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