1 // Copyright 2018 Developers of the Rand project.
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
9 //! Implementation for WASM via wasm-bindgen
12 use core
::cell
::RefCell
;
14 use std
::thread_local
;
16 use wasm_bindgen
::prelude
::*;
18 use crate::error
::{BINDGEN_CRYPTO_UNDEF, BINDGEN_GRV_UNDEF}
;
21 #[derive(Clone, Debug)]
24 Browser(BrowserCrypto
),
27 // JsValues are always per-thread, so we initialize RngSource for each thread.
28 // See: https://github.com/rustwasm/wasm-bindgen/pull/955
30 static RNG_SOURCE
: RefCell
<Option
<RngSource
>> = RefCell
::new(None
);
33 pub fn getrandom_inner(dest
: &mut [u8]) -> Result
<(), Error
> {
34 assert_eq
!(mem
::size_of
::<usize>(), 4);
37 let mut source
= f
.borrow_mut();
39 *source
= Some(getrandom_init()?
);
42 match source
.as_ref().unwrap() {
43 RngSource
::Node(n
) => n
.random_fill_sync(dest
),
44 RngSource
::Browser(n
) => {
45 // see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
49 // > A QuotaExceededError DOMException is thrown if the
50 // > requested length is greater than 65536 bytes.
51 for chunk
in dest
.chunks_mut(65536) {
52 n
.get_random_values(chunk
)
60 fn getrandom_init() -> Result
<RngSource
, Error
> {
61 if let Ok(self_
) = Global
::get_self() {
62 // If `self` is defined then we're in a browser somehow (main window
63 // or web worker). Here we want to try to use
64 // `crypto.getRandomValues`, but if `crypto` isn't defined we assume
65 // we're in an older web browser and the OS RNG isn't available.
67 let crypto
= self_
.crypto();
68 if crypto
.is_undefined() {
69 return Err(BINDGEN_CRYPTO_UNDEF
);
72 // Test if `crypto.getRandomValues` is undefined as well
73 let crypto
: BrowserCrypto
= crypto
.into();
74 if crypto
.get_random_values_fn().is_undefined() {
75 return Err(BINDGEN_GRV_UNDEF
);
78 return Ok(RngSource
::Browser(crypto
));
81 return Ok(RngSource
::Node(node_require("crypto")));
87 #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)]
88 fn get_self() -> Result
<Self_
, JsValue
>;
91 #[wasm_bindgen(method, getter, structural)]
92 fn crypto(me
: &Self_
) -> JsValue
;
94 #[derive(Clone, Debug)]
97 // TODO: these `structural` annotations here ideally wouldn't be here to
98 // avoid a JS shim, but for now with feature detection they're
100 #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)]
101 fn get_random_values_fn(me
: &BrowserCrypto
) -> JsValue
;
102 #[wasm_bindgen(method, js_name = getRandomValues, structural)]
103 fn get_random_values(me
: &BrowserCrypto
, buf
: &mut [u8]);
105 #[wasm_bindgen(js_name = require)]
106 fn node_require(s
: &str) -> NodeCrypto
;
108 #[derive(Clone, Debug)]
111 #[wasm_bindgen(method, js_name = randomFillSync, structural)]
112 fn random_fill_sync(me
: &NodeCrypto
, buf
: &mut [u8]);