Skip to content

Commit f4bec3b

Browse files
committed
bindgen: WIP
1 parent 208b318 commit f4bec3b

File tree

5 files changed

+48
-96
lines changed

5 files changed

+48
-96
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ wasi = "0.5"
3333

3434
[target.wasm32-unknown-unknown.dependencies]
3535
wasm-bindgen = { version = "0.2.29", optional = true }
36+
js-sys = { version = "0.3.27", optional = true }
37+
web-sys = { version = "0.3.27", optional = true, features = ["Window", "WorkerGlobalScope", "Crypto"] }
3638
stdweb = { version = "0.4.18", optional = true }
3739

3840
[features]
3941
std = []
4042
# enables dummy implementation for unsupported targets
4143
dummy = []
44+
bindgen = ["wasm-bindgen", "js-sys", "web-sys"]
4245
# Unstable feature to support being a libstd dependency
4346
rustc-dep-of-std = ["compiler_builtins", "core"]

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ reporting will be improved on some platforms.
5050
For the `wasm32-unknown-unknown` target, one of the following features should be
5151
enabled:
5252

53-
- [`wasm-bindgen`](https://crates.io/crates/wasm_bindgen)
53+
- [`bindgen`](https://crates.io/crates/wasm_bindgen)
5454
- [`stdweb`](https://crates.io/crates/stdweb)
5555

5656
By default, compiling `getrandom` for an unsupported target will result in

src/error.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,11 @@ pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3);
134134
pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4);
135135
pub(crate) const FAILED_RDRAND: Error = internal_error!(5);
136136
pub(crate) const NO_RDRAND: Error = internal_error!(6);
137-
pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7);
138-
pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8);
139-
pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9);
140-
pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10);
137+
pub(crate) const BINDGEN_WEB_CRYPTO: Error = internal_error!(7);
138+
pub(crate) const BINDGEN_WEB_FAILED: Error = internal_error!(8);
139+
pub(crate) const BINDGEN_NODE_FAILED: Error = internal_error!(9);
140+
pub(crate) const STDWEB_NO_RNG: Error = internal_error!(10);
141+
pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(11);
141142

142143
fn internal_desc(error: Error) -> Option<&'static str> {
143144
match error {
@@ -148,8 +149,9 @@ fn internal_desc(error: Error) -> Option<&'static str> {
148149
RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"),
149150
FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
150151
NO_RDRAND => Some("RDRAND: instruction not supported"),
151-
BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"),
152-
BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"),
152+
BINDGEN_WEB_CRYPTO => Some("wasm-bindgen: self.crypto is unavalible"),
153+
BINDGEN_WEB_FAILED => Some("wasm-bindgen: crypto.getRandomValues failed"),
154+
BINDGEN_NODE_FAILED => Some("wasm-bindgen: Node.js crypto.randomFillSync failed"),
153155
STDWEB_NO_RNG => Some("stdweb: no randomness source available"),
154156
STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"),
155157
_ => None,

src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
//! Getrandom also supports `wasm32-unknown-unknown` by directly calling
6262
//! JavaScript methods. Rust currently has two ways to do this: [bindgen] and
6363
//! [stdweb]. Getrandom supports using either one by enabling the
64-
//! `wasm-bindgen` or `stdweb` crate features. Note that if both features are
65-
//! enabled, `wasm-bindgen` will be used. If neither feature is enabled, calls
64+
//! `bindgen` or `stdweb` crate features. Note that if both features are
65+
//! enabled, `bindgen` will be used. If neither feature is enabled, calls
6666
//! to `getrandom` will always fail at runtime.
6767
//!
6868
//! [bindgen]: https://github.com/rust-lang/rust-bindgen
@@ -234,7 +234,7 @@ cfg_if! {
234234
#[path = "rdrand.rs"] mod imp;
235235
} else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
236236
cfg_if! {
237-
if #[cfg(feature = "wasm-bindgen")] {
237+
if #[cfg(feature = "bindgen")] {
238238
#[path = "wasm32_bindgen.rs"] mod imp;
239239
} else if #[cfg(feature = "stdweb")] {
240240
#[path = "wasm32_stdweb.rs"] mod imp;

src/wasm32_bindgen.rs

+33-86
Original file line numberDiff line numberDiff line change
@@ -7,107 +7,54 @@
77
// except according to those terms.
88

99
//! Implementation for WASM via wasm-bindgen
10-
extern crate std;
10+
use js_sys::global;
11+
use wasm_bindgen::{prelude::*, JsCast};
12+
use web_sys::{window, Crypto, WorkerGlobalScope};
1113

12-
use core::cell::RefCell;
13-
use core::mem;
14-
use std::thread_local;
15-
16-
use wasm_bindgen::prelude::*;
17-
18-
use crate::error::{BINDGEN_CRYPTO_UNDEF, BINDGEN_GRV_UNDEF};
19-
use crate::Error;
20-
21-
#[derive(Clone, Debug)]
22-
enum RngSource {
23-
Node(NodeCrypto),
24-
Browser(BrowserCrypto),
25-
}
26-
27-
// JsValues are always per-thread, so we initialize RngSource for each thread.
28-
// See: https://github.com/rustwasm/wasm-bindgen/pull/955
29-
thread_local!(
30-
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
31-
);
14+
use crate::util::LazyBool;
15+
use crate::{error, Error};
3216

3317
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
34-
assert_eq!(mem::size_of::<usize>(), 4);
35-
36-
RNG_SOURCE.with(|f| {
37-
let mut source = f.borrow_mut();
38-
if source.is_none() {
39-
*source = Some(getrandom_init()?);
18+
static IS_NODE: LazyBool = LazyBool::new();
19+
if IS_NODE.unsync_init(|| node_crypto().is_some()) {
20+
if node_crypto().unwrap().random_fill_sync(dest).is_err() {
21+
return Err(error::BINDGEN_NODE_FAILED);
4022
}
41-
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
46-
//
47-
// where it says:
48-
//
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)
53-
}
23+
} else {
24+
let crypto = browser_crypto().ok_or(error::BINDGEN_WEB_CRYPTO)?;
25+
// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
26+
// > A QuotaExceededError DOMException is thrown if the
27+
// > requested length is greater than 65536 bytes.
28+
for chunk in dest.chunks_mut(65536) {
29+
if crypto.get_random_values_with_u8_array(chunk).is_err() {
30+
return Err(error::BINDGEN_WEB_FAILED);
5431
}
55-
};
56-
Ok(())
57-
})
58-
}
59-
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.
66-
67-
let crypto = self_.crypto();
68-
if crypto.is_undefined() {
69-
return Err(BINDGEN_CRYPTO_UNDEF);
7032
}
33+
}
34+
Ok(())
35+
}
7136

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);
76-
}
37+
fn node_crypto() -> Option<NodeCrypto> {
38+
node_require("crypto").ok()
39+
}
7740

78-
return Ok(RngSource::Browser(crypto));
41+
fn browser_crypto() -> Option<Crypto> {
42+
// Support calling self.crypto in the main window or a Web Worker.
43+
if let Some(window) = window() {
44+
return window.crypto().ok();
7945
}
80-
81-
return Ok(RngSource::Node(node_require("crypto")));
46+
let worker = global().dyn_into::<WorkerGlobalScope>().ok()?;
47+
worker.crypto().ok()
8248
}
8349

8450
#[wasm_bindgen]
8551
extern "C" {
86-
type Global;
87-
#[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)]
88-
fn get_self() -> Result<Self_, JsValue>;
89-
90-
type Self_;
91-
#[wasm_bindgen(method, getter, structural)]
92-
fn crypto(me: &Self_) -> JsValue;
93-
94-
#[derive(Clone, Debug)]
95-
type BrowserCrypto;
96-
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
99-
// unavoidable.
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]);
104-
105-
#[wasm_bindgen(js_name = require)]
106-
fn node_require(s: &str) -> NodeCrypto;
52+
#[wasm_bindgen(catch, js_name = require)]
53+
fn node_require(s: &str) -> Result<NodeCrypto, JsValue>;
10754

10855
#[derive(Clone, Debug)]
10956
type NodeCrypto;
11057

111-
#[wasm_bindgen(method, js_name = randomFillSync, structural)]
112-
fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]);
58+
#[wasm_bindgen(catch, method, js_name = randomFillSync)]
59+
fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>;
11360
}

0 commit comments

Comments
 (0)