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}