Skip to content

Commit b7b7ee0

Browse files
committed
Move to using #[wasm_bindgen(module)]
Signed-off-by: Joe Richey <joerichey@google.com>
1 parent 63f861c commit b7b7ee0

File tree

3 files changed

+55
-64
lines changed

3 files changed

+55
-64
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ Cargo.lock
44
*.ts
55
*.js
66
*.wasm
7+
!src/*.js

src/js.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Node detection taken from https://www.npmjs.com/package/browser-or-node
2+
export const IS_NODE =
3+
typeof process !== "undefined" &&
4+
process.versions != null &&
5+
process.versions.node != null;
6+
7+
// TODO
8+
export function web_crypto() {
9+
return self.crypto || self.msCrypto;
10+
}
11+
12+
// TODO
13+
export var NODE_CRYPTO = await import("node:crypto").catch(_ => { })

src/js.rs

Lines changed: 41 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,39 @@ use crate::Error;
1010
extern crate std;
1111
use 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
1721
const BROWSER_CRYPTO_BUFFER_SIZE: usize = 256;
1822

1923
enum 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
2631
thread_local!(
27-
static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
32+
static RNG_SOURCE: RngSource = getrandom_init();
2833
);
2934

3035
pub(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]
9587
extern "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

Comments
 (0)