Skip to content

Commit 120a1d7

Browse files
authored
Imporve detection for Node JS (#215)
This uses `process.versions.node` to see if we are in Node.js rather than using `global.self` to check if we are in a Browser. This change also makes some minor `wasm_bindgen` improvements: - Use `js_sys::global()` to get the global object - Bind the node require as `module.require` - Improving error messages Signed-off-by: Joe Richey <joerichey@google.com>
1 parent 922d1de commit 120a1d7

File tree

2 files changed

+49
-34
lines changed

2 files changed

+49
-34
lines changed

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ fn internal_desc(error: Error) -> Option<&'static str> {
162162
Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
163163
Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
164164
Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
165-
Error::WEB_CRYPTO => Some("Web API self.crypto is unavailable"),
165+
Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"),
166166
Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"),
167167
Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
168168
Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"),

src/js.rs

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extern crate std;
1111
use std::thread_local;
1212

1313
use js_sys::Uint8Array;
14-
use wasm_bindgen::prelude::*;
14+
use wasm_bindgen::{prelude::*, JsCast};
1515

1616
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
1717
const BROWSER_CRYPTO_BUFFER_SIZE: usize = 256;
@@ -57,50 +57,65 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
5757
}
5858

5959
fn getrandom_init() -> Result<RngSource, Error> {
60-
if let Ok(self_) = Global::get_self() {
61-
// If `self` is defined then we're in a browser somehow (main window
62-
// or web worker). We get `self.crypto` (called `msCrypto` on IE), so we
63-
// can call `crypto.getRandomValues`. If `crypto` isn't defined, we
64-
// assume we're in an older web browser and the OS RNG isn't available.
65-
66-
let crypto: BrowserCrypto = match (self_.crypto(), self_.ms_crypto()) {
67-
(crypto, _) if !crypto.is_undefined() => crypto,
68-
(_, crypto) if !crypto.is_undefined() => crypto,
69-
_ => return Err(Error::WEB_CRYPTO),
70-
};
71-
72-
let buf = Uint8Array::new_with_length(BROWSER_CRYPTO_BUFFER_SIZE as u32);
73-
return Ok(RngSource::Browser(crypto, buf));
60+
let global: Global = js_sys::global().unchecked_into();
61+
if is_node(&global) {
62+
let crypto = require("crypto").map_err(|_| Error::NODE_CRYPTO)?;
63+
return Ok(RngSource::Node(crypto));
7464
}
7565

76-
let crypto = MODULE.require("crypto").map_err(|_| Error::NODE_CRYPTO)?;
77-
Ok(RngSource::Node(crypto))
66+
// Assume we are in some Web environment (browser or web worker). We get
67+
// `self.crypto` (called `msCrypto` on IE), so we can call
68+
// `crypto.getRandomValues`. If `crypto` isn't defined, we assume that
69+
// we are in an older web browser and the OS RNG isn't available.
70+
let crypto = match (global.crypto(), global.ms_crypto()) {
71+
(c, _) if c.is_object() => c,
72+
(_, c) if c.is_object() => c,
73+
_ => return Err(Error::WEB_CRYPTO),
74+
};
75+
76+
let buf = Uint8Array::new_with_length(BROWSER_CRYPTO_BUFFER_SIZE as u32);
77+
Ok(RngSource::Browser(crypto, buf))
78+
}
79+
80+
// Taken from https://www.npmjs.com/package/browser-or-node
81+
fn is_node(global: &Global) -> bool {
82+
let process = global.process();
83+
if process.is_object() {
84+
let versions = process.versions();
85+
if versions.is_object() {
86+
return versions.node().is_string();
87+
}
88+
}
89+
false
7890
}
7991

8092
#[wasm_bindgen]
8193
extern "C" {
82-
type Global;
83-
#[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)]
84-
fn get_self() -> Result<Self_, JsValue>;
94+
type Global; // Return type of js_sys::global()
8595

86-
type Self_;
96+
// Web Crypto API (https://www.w3.org/TR/WebCryptoAPI/)
8797
#[wasm_bindgen(method, getter, js_name = "msCrypto")]
88-
fn ms_crypto(me: &Self_) -> BrowserCrypto;
98+
fn ms_crypto(this: &Global) -> BrowserCrypto;
8999
#[wasm_bindgen(method, getter)]
90-
fn crypto(me: &Self_) -> BrowserCrypto;
91-
100+
fn crypto(this: &Global) -> BrowserCrypto;
92101
type BrowserCrypto;
93102
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
94-
fn get_random_values(me: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
95-
96-
#[wasm_bindgen(js_name = module)]
97-
static MODULE: NodeModule;
98-
99-
type NodeModule;
100-
#[wasm_bindgen(method, catch)]
101-
fn require(this: &NodeModule, s: &str) -> Result<NodeCrypto, JsValue>;
103+
fn get_random_values(this: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
102104

105+
// Node JS crypto module (https://nodejs.org/api/crypto.html)
106+
#[wasm_bindgen(catch, js_name = "module.require")]
107+
fn require(s: &str) -> Result<NodeCrypto, JsValue>;
103108
type NodeCrypto;
104109
#[wasm_bindgen(method, js_name = randomFillSync, catch)]
105-
fn random_fill_sync(crypto: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>;
110+
fn random_fill_sync(this: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>;
111+
112+
// Node JS process Object (https://nodejs.org/api/process.html)
113+
#[wasm_bindgen(method, getter)]
114+
fn process(this: &Global) -> Process;
115+
type Process;
116+
#[wasm_bindgen(method, getter)]
117+
fn versions(this: &Process) -> Versions;
118+
type Versions;
119+
#[wasm_bindgen(method, getter)]
120+
fn node(this: &Versions) -> JsValue;
106121
}

0 commit comments

Comments
 (0)