1mod crc32;
4mod md5_hash;
5mod sha_hash;
6use std::slice;
7
8use once_cell::sync::Lazy;
9use rand::prelude::ThreadRng;
10use rand::Rng;
11use ring::rand::{SecureRandom, SystemRandom};
12use rquickjs::{
13 function::{Constructor, Opt},
14 module::{Declarations, Exports, ModuleDef},
15 prelude::{Func, Rest},
16 Class, Ctx, Exception, Function, IntoJs, Object, Result, Value,
17};
18
19use crate::{
20 modules::{
22 buffer::Buffer,
23 encoding::encoder::{bytes_to_b64_string, bytes_to_hex_string},
24 module::export_default,
25 uuid::uuidv4,
26 },
27 utils::{
28 class::get_class_name,
29 object::{bytes_to_typed_array, get_start_end_indexes, obj_to_array_buffer},
30 result::ResultExt,
31 },
32 };
34
35use self::{
36 crc32::{Crc32, Crc32c},
37 md5_hash::Md5,
38 sha_hash::{Hash, Hmac, ShaAlgorithm, ShaHash},
39};
40
41pub static SYSTEM_RANDOM: Lazy<SystemRandom> = Lazy::new(SystemRandom::new);
42
43fn encoded_bytes<'js>(ctx: Ctx<'js>, bytes: &[u8], encoding: &str) -> Result<Value<'js>> {
44 match encoding {
45 "hex" => {
46 let hex = bytes_to_hex_string(bytes);
47 let hex = rquickjs::String::from_str(ctx, &hex)?;
48 Ok(Value::from_string(hex))
49 }
50 "base64" => {
51 let b64 = bytes_to_b64_string(bytes);
52 let b64 = rquickjs::String::from_str(ctx, &b64)?;
53 Ok(Value::from_string(b64))
54 }
55 _ => bytes_to_typed_array(ctx, bytes),
56 }
57}
58
59#[inline]
60pub fn random_byte_array(length: usize) -> Vec<u8> {
61 let mut vec = vec![0; length];
62 SYSTEM_RANDOM.fill(&mut vec).unwrap();
63 vec
64}
65
66fn get_random_bytes(ctx: Ctx, length: usize) -> Result<Value> {
67 let random_bytes = random_byte_array(length);
68 Buffer(random_bytes).into_js(&ctx)
69}
70
71fn get_random_int(_ctx: Ctx, first: i64, second: Opt<i64>) -> Result<i64> {
72 let mut rng = ThreadRng::default();
73 let random_number = match second.0 {
74 Some(max) => rng.gen_range(first..max),
75 None => rng.gen_range(0..first),
76 };
77
78 Ok(random_number)
79}
80
81fn random_fill<'js>(ctx: Ctx<'js>, obj: Object<'js>, args: Rest<Value<'js>>) -> Result<()> {
82 let args_iter = args.0.into_iter();
83 let mut args_iter = args_iter.rev();
84
85 let callback: Function = args_iter
86 .next()
87 .and_then(|v| v.into_function())
88 .or_throw_msg(&ctx, "Callback required")?;
89 let size = args_iter
90 .next()
91 .and_then(|arg| arg.as_int())
92 .map(|i| i as usize);
93 let offset = args_iter
94 .next()
95 .and_then(|arg| arg.as_int())
96 .map(|i| i as usize);
97
98 todo!();
99 }
111
112fn random_fill_sync<'js>(
113 ctx: Ctx<'js>,
114 obj: Object<'js>,
115 offset: Opt<usize>,
116 size: Opt<usize>,
117) -> Result<Object<'js>> {
118 let offset = offset.unwrap_or(0);
119
120 if let Some((array_buffer, source_length, source_offset)) = obj_to_array_buffer(&obj)? {
121 let (start, end) = get_start_end_indexes(source_length, size.0, offset);
122
123 let raw = array_buffer
124 .as_raw()
125 .ok_or("ArrayBuffer is detached")
126 .or_throw(&ctx)?;
127 let bytes = unsafe { slice::from_raw_parts_mut(raw.ptr.as_ptr(), source_length) };
128
129 SYSTEM_RANDOM
130 .fill(&mut bytes[start + source_offset..end - source_offset])
131 .unwrap();
132 }
133
134 Ok(obj)
135}
136
137fn get_random_values<'js>(ctx: Ctx<'js>, obj: Object<'js>) -> Result<Object<'js>> {
138 if let Some((array_buffer, source_length, source_offset)) = obj_to_array_buffer(&obj)? {
139 let raw = array_buffer
140 .as_raw()
141 .ok_or("ArrayBuffer is detached")
142 .or_throw(&ctx)?;
143
144 if source_length > 0x10000 {
145 return Err(Exception::throw_message(
146 &ctx,
147 "QuotaExceededError: The requested length exceeds 65,536 bytes",
148 ));
149 }
150
151 let bytes = unsafe {
152 std::slice::from_raw_parts_mut(raw.ptr.as_ptr().add(source_offset), source_length)
153 };
154
155 match get_class_name(&obj)?.unwrap().as_str() {
156 "Int8Array" | "Uint8Array" | "Uint8ClampedArray" | "Int16Array" | "Uint16Array"
157 | "Int32Array" | "Uint32Array" | "BigInt64Array" | "BigUint64Array" => {
158 SYSTEM_RANDOM.fill(bytes).unwrap()
159 }
160 _ => return Err(Exception::throw_message(&ctx, "Unsupported TypedArray")),
161 }
162 }
163
164 Ok(obj)
165}
166
167pub fn init(ctx: &Ctx<'_>) -> Result<()> {
168 let globals = ctx.globals();
169
170 let crypto = Object::new(ctx.clone())?;
171
172 crypto.set("createHash", Func::from(Hash::new))?;
173 crypto.set("createHmac", Func::from(Hmac::new))?;
174 crypto.set("randomBytes", Func::from(get_random_bytes))?;
175 crypto.set("randomInt", Func::from(get_random_int))?;
176 crypto.set("randomUUID", Func::from(uuidv4))?;
177 crypto.set("randomFillSync", Func::from(random_fill_sync))?;
178 crypto.set("randomFill", Func::from(random_fill))?;
179 crypto.set("getRandomValues", Func::from(get_random_values))?;
180
181 globals.set("crypto", crypto)?;
182
183 Ok(())
184}
185
186pub struct CryptoModule;
187
188impl ModuleDef for CryptoModule {
189 fn declare(declare: &Declarations<'_>) -> Result<()> {
190 declare.declare("createHash")?;
191 declare.declare("createHmac")?;
192 declare.declare("Crc32")?;
193 declare.declare("Crc32c")?;
194 declare.declare("Md5")?;
195 declare.declare("randomBytes")?;
196 declare.declare("randomUUID")?;
197 declare.declare("randomInt")?;
198 declare.declare("randomFillSync")?;
199 declare.declare("randomFill")?;
200 declare.declare("getRandomValues")?;
201
202 for sha_algorithm in ShaAlgorithm::iterate() {
203 let class_name = sha_algorithm.class_name();
204 declare.declare(class_name)?;
205 }
206
207 declare.declare("default")?;
208
209 Ok(())
210 }
211
212 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
213 Class::<Hash>::register(ctx)?;
214 Class::<Hmac>::register(ctx)?;
215 Class::<ShaHash>::register(ctx)?;
216
217 export_default(ctx, exports, |default| {
218 for sha_algorithm in ShaAlgorithm::iterate() {
219 let class_name: &str = sha_algorithm.class_name();
220 let algo = sha_algorithm;
221
222 let ctor =
223 Constructor::new_class::<ShaHash, _, _>(ctx.clone(), move |ctx, secret| {
224 ShaHash::new(ctx, algo, secret)
225 })?;
226
227 default.set(class_name, ctor)?;
228 }
229
230 Class::<Md5>::define(default)?;
231 Class::<Crc32>::define(default)?;
232 Class::<Crc32c>::define(default)?;
233
234 default.set("createHash", Func::from(Hash::new))?;
235 default.set("createHmac", Func::from(Hmac::new))?;
236 default.set("randomBytes", Func::from(get_random_bytes))?;
237 default.set("randomInt", Func::from(get_random_int))?;
238 default.set("randomUUID", Func::from(uuidv4))?;
239 default.set("randomFillSync", Func::from(random_fill_sync))?;
240 default.set("randomFill", Func::from(random_fill))?;
241 default.set("getRandomValues", Func::from(get_random_values))?;
242 Ok(())
243 })?;
244
245 Ok(())
246 }
247}
248
249