@@ -10,34 +10,39 @@ use crate::Error;
1010extern crate std;
1111use std:: thread_local;
1212
13- use js_sys:: { global , Uint8Array } ;
14- use wasm_bindgen:: { prelude:: wasm_bindgen, JsCast , JsValue } ;
13+ use js_sys:: Uint8Array ;
14+ use wasm_bindgen:: { prelude:: wasm_bindgen, JsValue } ;
1515
16- // Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
16+ // Maximum length for Node's crypto.getRandomValues
17+ const NODE_MAX_BUFFER_SIZE : usize = ( 1 << 31 ) - 1 ;
18+
19+ // Maximum length Web's crypto. is 65536 bytes, see:
20+ // https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
1721const BROWSER_CRYPTO_BUFFER_SIZE : usize = 256 ;
1822
1923enum RngSource {
20- Node ( NodeCrypto ) ,
21- Browser ( BrowserCrypto , Uint8Array ) ,
24+ Node ,
25+ Web ( WebCrypto , Uint8Array ) ,
26+ Failed ( Error ) ,
2227}
2328
2429// JsValues are always per-thread, so we initialize RngSource for each thread.
2530// See: https://github.com/rustwasm/wasm-bindgen/pull/955
2631thread_local ! (
27- static RNG_SOURCE : Result < RngSource , Error > = getrandom_init( ) ;
32+ static RNG_SOURCE : RngSource = getrandom_init( ) ;
2833) ;
2934
3035pub ( crate ) fn getrandom_inner ( dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
31- RNG_SOURCE . with ( |result| {
32- let source = result. as_ref ( ) . map_err ( |& e| e) ?;
33-
36+ RNG_SOURCE . with ( |source| {
3437 match source {
35- RngSource :: Node ( n) => {
36- if n. random_fill_sync ( dest) . is_err ( ) {
37- return Err ( Error :: NODE_RANDOM_FILL_SYNC ) ;
38+ RngSource :: Node => {
39+ for chunk in dest. chunks_mut ( NODE_MAX_BUFFER_SIZE ) {
40+ if NODE_CRYPTO . random_fill_sync ( chunk) . is_err ( ) {
41+ return Err ( Error :: NODE_RANDOM_FILL_SYNC ) ;
42+ }
3843 }
3944 }
40- RngSource :: Browser ( crypto, buf) => {
45+ RngSource :: Web ( crypto, buf) => {
4146 // getRandomValues does not work with all types of WASM memory,
4247 // so we initially write to browser memory to avoid exceptions.
4348 for chunk in dest. chunks_mut ( BROWSER_CRYPTO_BUFFER_SIZE ) {
@@ -51,79 +56,51 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
5156 sub_buf. copy_to ( chunk) ;
5257 }
5358 }
54- } ;
59+ RngSource :: Failed ( err) => return Err ( * err) ,
60+ }
5561 Ok ( ( ) )
5662 } )
5763}
5864
59- fn getrandom_init ( ) -> Result < RngSource , Error > {
60- let global: Global = global ( ) . unchecked_into ( ) ;
61- if is_node ( & global) {
62- let crypto = NODE_MODULE
63- . require ( "crypto" )
64- . map_err ( |_| Error :: NODE_CRYPTO ) ?;
65- return Ok ( RngSource :: Node ( crypto) ) ;
65+ fn getrandom_init ( ) -> RngSource {
66+ if * IS_NODE {
67+ if !NODE_CRYPTO . is_object ( ) {
68+ return RngSource :: Failed ( Error :: NODE_CRYPTO ) ;
69+ }
70+ return RngSource :: Node ;
6671 }
6772
6873 // Assume we are in some Web environment (browser or web worker). We get
6974 // `self.crypto` (called `msCrypto` on IE), so we can call
7075 // `crypto.getRandomValues`. If `crypto` isn't defined, we assume that
7176 // we are in an older web browser and the OS RNG isn't available.
72- let crypto = match ( global. crypto ( ) , global. ms_crypto ( ) ) {
73- ( c, _) if c. is_object ( ) => c,
74- ( _, c) if c. is_object ( ) => c,
75- _ => return Err ( Error :: WEB_CRYPTO ) ,
76- } ;
77-
78- let buf = Uint8Array :: new_with_length ( BROWSER_CRYPTO_BUFFER_SIZE as u32 ) ;
79- Ok ( RngSource :: Browser ( crypto, buf) )
80- }
81-
82- // Taken from https://www.npmjs.com/package/browser-or-node
83- fn is_node ( global : & Global ) -> bool {
84- let process = global. process ( ) ;
85- if process. is_object ( ) {
86- let versions = process. versions ( ) ;
87- if versions. is_object ( ) {
88- return versions. node ( ) . is_string ( ) ;
77+ match web_crypto ( ) {
78+ Ok ( crypto) if crypto. is_object ( ) => {
79+ let buf = Uint8Array :: new_with_length ( BROWSER_CRYPTO_BUFFER_SIZE as u32 ) ;
80+ RngSource :: Web ( crypto, buf)
8981 }
82+ _ => RngSource :: Failed ( Error :: WEB_CRYPTO ) ,
9083 }
91- false
9284}
9385
9486#[ wasm_bindgen]
9587extern "C" {
96- type Global ; // Return type of js_sys::global()
97-
9888 // Web Crypto API (https://www.w3.org/TR/WebCryptoAPI/)
99- #[ wasm_bindgen( method, getter, js_name = "msCrypto" ) ]
100- fn ms_crypto ( this : & Global ) -> BrowserCrypto ;
101- #[ wasm_bindgen( method, getter) ]
102- fn crypto ( this : & Global ) -> BrowserCrypto ;
103- type BrowserCrypto ;
89+ type WebCrypto ;
10490 #[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
105- fn get_random_values ( this : & BrowserCrypto , buf : & Uint8Array ) -> Result < ( ) , JsValue > ;
91+ fn get_random_values ( this : & WebCrypto , buf : & Uint8Array ) -> Result < ( ) , JsValue > ;
10692
107- // We use a "module" object here instead of just annotating require() with
108- // js_name = "module.require", so that Webpack doesn't give a warning. See:
109- // https://github.com/rust-random/getrandom/issues/224
110- type NodeModule ;
111- #[ wasm_bindgen( js_name = module) ]
112- static NODE_MODULE : NodeModule ;
11393 // Node JS crypto module (https://nodejs.org/api/crypto.html)
114- #[ wasm_bindgen( method, catch) ]
115- fn require ( this : & NodeModule , s : & str ) -> Result < NodeCrypto , JsValue > ;
11694 type NodeCrypto ;
11795 #[ wasm_bindgen( method, js_name = randomFillSync, catch) ]
11896 fn random_fill_sync ( this : & NodeCrypto , buf : & mut [ u8 ] ) -> Result < ( ) , JsValue > ;
97+ }
98+
99+ #[ wasm_bindgen( module = "/src/js.js" ) ]
100+ extern "C" {
101+ static IS_NODE : bool ;
102+ static NODE_CRYPTO : NodeCrypto ;
119103
120- // Node JS process Object (https://nodejs.org/api/process.html)
121- #[ wasm_bindgen( method, getter) ]
122- fn process ( this : & Global ) -> Process ;
123- type Process ;
124- #[ wasm_bindgen( method, getter) ]
125- fn versions ( this : & Process ) -> Versions ;
126- type Versions ;
127- #[ wasm_bindgen( method, getter) ]
128- fn node ( this : & Versions ) -> JsValue ;
104+ #[ wasm_bindgen( catch) ]
105+ fn web_crypto ( ) -> Result < WebCrypto , JsValue > ;
129106}
0 commit comments