@@ -10,15 +10,16 @@ use crate::Error;
10
10
extern crate std;
11
11
use std:: thread_local;
12
12
13
- use js_sys:: { global, Uint8Array } ;
13
+ use js_sys:: { global, Function , Uint8Array } ;
14
14
use wasm_bindgen:: { prelude:: wasm_bindgen, JsCast , JsValue } ;
15
15
16
+ // Size of our temporary Uint8Array buffer used with WebCrypto methods
16
17
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
17
- const BROWSER_CRYPTO_BUFFER_SIZE : usize = 256 ;
18
+ const WEB_CRYPTO_BUFFER_SIZE : usize = 256 ;
18
19
19
20
enum RngSource {
20
21
Node ( NodeCrypto ) ,
21
- Browser ( BrowserCrypto , Uint8Array ) ,
22
+ Web ( WebCrypto , Uint8Array ) ,
22
23
}
23
24
24
25
// JsValues are always per-thread, so we initialize RngSource for each thread.
@@ -37,10 +38,10 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
37
38
return Err ( Error :: NODE_RANDOM_FILL_SYNC ) ;
38
39
}
39
40
}
40
- RngSource :: Browser ( crypto, buf) => {
41
+ RngSource :: Web ( crypto, buf) => {
41
42
// getRandomValues does not work with all types of WASM memory,
42
43
// so we initially write to browser memory to avoid exceptions.
43
- for chunk in dest. chunks_mut ( BROWSER_CRYPTO_BUFFER_SIZE ) {
44
+ for chunk in dest. chunks_mut ( WEB_CRYPTO_BUFFER_SIZE ) {
44
45
// The chunk can be smaller than buf's length, so we call to
45
46
// JS to create a smaller view of buf without allocation.
46
47
let sub_buf = buf. subarray ( 0 , chunk. len ( ) as u32 ) ;
@@ -58,25 +59,33 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
58
59
59
60
fn getrandom_init ( ) -> Result < RngSource , Error > {
60
61
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) ) ;
66
- }
67
62
68
- // Assume we are in some Web environment (browser or web worker). We get
69
- // `self.crypto` (called `msCrypto` on IE), so we can call
70
- // `crypto.getRandomValues`. If `crypto` isn't defined, we assume that
71
- // 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 ) ,
63
+ // Get the Web Crypto interface if we are in a browser, Web Worker, Deno,
64
+ // or another environment that supports the Web Cryptography API. This
65
+ // also allows for user-provided polyfills in unsupported environments.
66
+ let crypto = match global. crypto ( ) {
67
+ // Standard Web Crypto interface
68
+ c if c. is_object ( ) => c,
69
+ // Node.js CommonJS Crypto module
70
+ _ if is_node ( & global) => {
71
+ // If module.require isn't a valid function, we are in an ES module.
72
+ match Module :: require_fn ( ) . and_then ( JsCast :: dyn_into :: < Function > ) {
73
+ Ok ( require_fn) => match require_fn. call1 ( & global, & JsValue :: from_str ( "crypto" ) ) {
74
+ Ok ( n) => return Ok ( RngSource :: Node ( n. unchecked_into ( ) ) ) ,
75
+ Err ( _) => return Err ( Error :: NODE_CRYPTO ) ,
76
+ } ,
77
+ Err ( _) => return Err ( Error :: NODE_ES_MODULE ) ,
78
+ }
79
+ }
80
+ // IE 11 Workaround
81
+ _ => match global. ms_crypto ( ) {
82
+ c if c. is_object ( ) => c,
83
+ _ => return Err ( Error :: WEB_CRYPTO ) ,
84
+ } ,
76
85
} ;
77
86
78
- let buf = Uint8Array :: new_with_length ( BROWSER_CRYPTO_BUFFER_SIZE as u32 ) ;
79
- Ok ( RngSource :: Browser ( crypto, buf) )
87
+ let buf = Uint8Array :: new_with_length ( WEB_CRYPTO_BUFFER_SIZE as u32 ) ;
88
+ Ok ( RngSource :: Web ( crypto, buf) )
80
89
}
81
90
82
91
// Taken from https://www.npmjs.com/package/browser-or-node
@@ -93,30 +102,36 @@ fn is_node(global: &Global) -> bool {
93
102
94
103
#[ wasm_bindgen]
95
104
extern "C" {
96
- type Global ; // Return type of js_sys::global()
105
+ // Return type of js_sys::global()
106
+ type Global ;
97
107
98
- // Web Crypto API (https://www.w3.org/TR/WebCryptoAPI/)
99
- # [ wasm_bindgen ( method , getter , js_name = "msCrypto" ) ]
100
- fn ms_crypto ( this : & Global ) -> BrowserCrypto ;
108
+ // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
109
+ type WebCrypto ;
110
+ // Getters for the WebCrypto API
101
111
#[ wasm_bindgen( method, getter) ]
102
- fn crypto ( this : & Global ) -> BrowserCrypto ;
103
- type BrowserCrypto ;
112
+ fn crypto ( this : & Global ) -> WebCrypto ;
113
+ #[ wasm_bindgen( method, getter, js_name = msCrypto) ]
114
+ fn ms_crypto ( this : & Global ) -> WebCrypto ;
115
+ // Crypto.getRandomValues()
104
116
#[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
105
- fn get_random_values ( this : & BrowserCrypto , buf : & Uint8Array ) -> Result < ( ) , JsValue > ;
117
+ fn get_random_values ( this : & WebCrypto , buf : & Uint8Array ) -> Result < ( ) , JsValue > ;
106
118
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 ;
113
119
// 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 > ;
116
120
type NodeCrypto ;
121
+ // crypto.randomFillSync()
117
122
#[ wasm_bindgen( method, js_name = randomFillSync, catch) ]
118
123
fn random_fill_sync ( this : & NodeCrypto , buf : & mut [ u8 ] ) -> Result < ( ) , JsValue > ;
119
124
125
+ // Ideally, we would just use `fn require(s: &str)` here. However, doing
126
+ // this causes a Webpack warning. So we instead return the function itself
127
+ // and manually invoke it using call1. This also lets us to check that the
128
+ // function actually exists, allowing for better error messages. See:
129
+ // https://github.com/rust-random/getrandom/issues/224
130
+ // https://github.com/rust-random/getrandom/issues/256
131
+ type Module ;
132
+ #[ wasm_bindgen( getter, static_method_of = Module , js_class = module, js_name = require, catch) ]
133
+ fn require_fn ( ) -> Result < JsValue , JsValue > ;
134
+
120
135
// Node JS process Object (https://nodejs.org/api/process.html)
121
136
#[ wasm_bindgen( method, getter) ]
122
137
fn process ( this : & Global ) -> Process ;
0 commit comments