forked from rust-random/getrandom
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwasm_js.rs
More file actions
77 lines (67 loc) · 2.84 KB
/
wasm_js.rs
File metadata and controls
77 lines (67 loc) · 2.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! Implementation for WASM based on Web and Node.js
use crate::Error;
use core::mem::MaybeUninit;
pub use crate::util::{inner_u32, inner_u64};
#[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))]
compile_error!("`wasm_js` backend can be enabled only for OS-less WASM targets!");
#[cfg(target_feature = "atomics")]
use js_sys::Uint8Array;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
// Maximum buffer size allowed in `Crypto.getRandomValuesSize` is 65536 bytes.
// See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
const MAX_BUFFER_SIZE: u16 = 256;
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
CRYPTO.with(|crypto| {
let crypto = crypto.as_ref().ok_or(Error::WEB_CRYPTO)?;
inner(crypto, dest)
})
}
#[cfg(not(target_feature = "atomics"))]
fn inner(crypto: &Crypto, dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
for chunk in dest.chunks_mut(MAX_BUFFER_SIZE.into()) {
if crypto.get_random_values(chunk).is_err() {
return Err(Error::WEB_GET_RANDOM_VALUES);
}
}
Ok(())
}
#[cfg(target_feature = "atomics")]
fn inner(crypto: &Crypto, dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getRandomValues does not work with all types of WASM memory,
// so we initially write to browser memory to avoid exceptions.
let buf = Uint8Array::new_with_length(MAX_BUFFER_SIZE.into());
for chunk in dest.chunks_mut(MAX_BUFFER_SIZE.into()) {
let chunk_len: u32 = chunk
.len()
.try_into()
.expect("chunk length is bounded by MAX_BUFFER_SIZE");
// The chunk can be smaller than buf's length, so we call to
// JS to create a smaller view of buf without allocation.
let sub_buf = if chunk_len == u32::from(MAX_BUFFER_SIZE) {
buf.clone()
} else {
buf.subarray(0, chunk_len)
};
if crypto.get_random_values(&sub_buf).is_err() {
return Err(Error::WEB_GET_RANDOM_VALUES);
}
// SAFETY: `sub_buf`'s length is the same length as `chunk`
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::<u8>()) };
}
Ok(())
}
#[wasm_bindgen]
extern "C" {
// Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
type Crypto;
// Holds the global `Crypto` object.
#[wasm_bindgen(thread_local_v2, js_name = crypto)]
static CRYPTO: Option<Crypto>;
// Crypto.getRandomValues()
#[cfg(not(target_feature = "atomics"))]
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
fn get_random_values(this: &Crypto, buf: &mut [MaybeUninit<u8>]) -> Result<(), JsValue>;
#[cfg(target_feature = "atomics")]
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
fn get_random_values(this: &Crypto, buf: &Uint8Array) -> Result<(), JsValue>;
}