diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 045a6bb9..93363ba0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -234,44 +234,56 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - name: Install precompiled wasm-pack - shell: bash - run: | - VERSION=v0.13.1 - URL=https://github.com/rustwasm/wasm-pack/releases/download/${VERSION}/wasm-pack-${VERSION}-x86_64-unknown-linux-musl.tar.gz - wget -O - $URL | tar -xz --strip-components=1 -C ~/.cargo/bin - wasm-pack --version + with: + targets: wasm32-unknown-unknown + # - name: Install precompiled wasm-bindgen + # shell: bash + # run: | + # VERSION=v0.2.97 + # URL=https://github.com/rustwasm/wasm-bindgen/releases/download/${VERSION}/wasm-bindgen-${VERSION}-x86_64-unknown-linux-musl.tar.gz + # wget -O - $URL | tar -xz --strip-components=1 -C ~/.cargo/bin + # wasm-bindgen --version + - uses: taiki-e/cache-cargo-install-action@v2 + with: + tool: wasm-bindgen-cli + git: https://github.com/daxpedda/wasm-bindgen + rev: 82fe725521ab1b95226408027530b3357919d6be - uses: Swatinem/rust-cache@v2 - name: Test (Node) env: RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" - run: wasm-pack test --node -- --features std + run: cargo test --target wasm32-unknown-unknown --features std - name: Test (Firefox) env: + GECKODRIVER: geckodriver WASM_BINDGEN_USE_BROWSER: 1 RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" - run: wasm-pack test --headless --firefox -- --features std + run: cargo test --target wasm32-unknown-unknown --features std - name: Test (Chrome) env: + CHROMEDRIVER: chromedriver WASM_BINDGEN_USE_BROWSER: 1 RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" - run: wasm-pack test --headless --chrome -- --features std + run: cargo test --target wasm32-unknown-unknown --features std - name: Test (dedicated worker) env: + GECKODRIVER: geckodriver WASM_BINDGEN_USE_DEDICATED_WORKER: 1 RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" - run: wasm-pack test --headless --firefox -- --features std + run: cargo test --target wasm32-unknown-unknown --features std - name: Test (shared worker) env: + GECKODRIVER: geckodriver WASM_BINDGEN_USE_SHARED_WORKER: 1 RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" - run: wasm-pack test --headless --firefox -- --features std + run: cargo test --target wasm32-unknown-unknown --features std - name: Test (service worker) env: + # Firefox doesn't support module service workers and therefor can't import scripts + CHROMEDRIVER: chromedriver WASM_BINDGEN_USE_SERVICE_WORKER: 1 RUSTFLAGS: -Dwarnings --cfg getrandom_backend="wasm_js" - # Firefox doesn't support module service workers and therefor can't import scripts - run: wasm-pack test --headless --chrome -- --features std + run: cargo test --target wasm32-unknown-unknown --features std wasi: name: WASI diff --git a/Cargo.toml b/Cargo.toml index 6e7f30d4..89837b8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,3 +99,8 @@ pre-build = [ "rm base.tar.xz", "rm -rf /tmp/netbsd", ] + +[patch.crates-io] +wasm-bindgen = { git = "https://github.com/daxpedda/wasm-bindgen", branch = "getrandom" } +js-sys = { git = "https://github.com/daxpedda/wasm-bindgen", branch = "getrandom" } +wasm-bindgen-test = { git = "https://github.com/daxpedda/wasm-bindgen", branch = "getrandom" } diff --git a/src/backends/wasm_js.rs b/src/backends/wasm_js.rs index 7753daf9..8837dab5 100644 --- a/src/backends/wasm_js.rs +++ b/src/backends/wasm_js.rs @@ -1,4 +1,5 @@ //! Implementation for WASM based on Web and Node.js + use crate::Error; use core::mem::MaybeUninit; @@ -7,52 +8,47 @@ pub use crate::util::{inner_u32, inner_u64}; #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] compile_error!("`wasm_js` backend can be enabled only for OS-less WASM targets!"); -use js_sys::{global, Uint8Array}; -use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; +use js_sys::Uint8Array; +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; // Size of our temporary Uint8Array buffer used with WebCrypto methods // Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues const CRYPTO_BUFFER_SIZE: u16 = 256; pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - let global: Global = global().unchecked_into(); - let crypto = global.crypto(); - - if !crypto.is_object() { - return Err(Error::WEB_CRYPTO); - } - - // getRandomValues does not work with all types of WASM memory, - // so we initially write to browser memory to avoid exceptions. - let buf = Uint8Array::new_with_length(CRYPTO_BUFFER_SIZE.into()); - for chunk in dest.chunks_mut(CRYPTO_BUFFER_SIZE.into()) { - let chunk_len: u32 = chunk - .len() - .try_into() - .expect("chunk length is bounded by CRYPTO_BUFFER_SIZE"); - // The chunk can be smaller than buf's length, so we call to - // JS to create a smaller view of buf without allocation. - let sub_buf = buf.subarray(0, chunk_len); - - if crypto.get_random_values(&sub_buf).is_err() { - return Err(Error::WEB_GET_RANDOM_VALUES); + CRYPTO.with(|crypto| { + let crypto = crypto.as_ref().ok_or(Error::WEB_CRYPTO)?; + + // getRandomValues does not work with all types of WASM memory, + // so we initially write to browser memory to avoid exceptions. + let buf = Uint8Array::new_with_length(CRYPTO_BUFFER_SIZE.into()); + for chunk in dest.chunks_mut(CRYPTO_BUFFER_SIZE.into()) { + let chunk_len: u32 = chunk + .len() + .try_into() + .expect("chunk length is bounded by CRYPTO_BUFFER_SIZE"); + // The chunk can be smaller than buf's length, so we call to + // JS to create a smaller view of buf without allocation. + let sub_buf = buf.subarray(0, chunk_len); + + if crypto.get_random_values(&sub_buf).is_err() { + return Err(Error::WEB_GET_RANDOM_VALUES); + } + + // SAFETY: `sub_buf`'s length is the same length as `chunk` + unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::()) }; } - - // SAFETY: `sub_buf`'s length is the same length as `chunk` - unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::()) }; - } - Ok(()) + Ok(()) + }) } #[wasm_bindgen] extern "C" { - // Return type of js_sys::global() - type Global; // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/) type Crypto; - // Getters for the Crypto API - #[wasm_bindgen(method, getter)] - fn crypto(this: &Global) -> Crypto; + // Holds the global `Crypto` object. + #[wasm_bindgen(thread_local_v2, js_name = crypto)] + static CRYPTO: Option; // Crypto.getRandomValues() #[wasm_bindgen(method, js_name = getRandomValues, catch)] fn get_random_values(this: &Crypto, buf: &Uint8Array) -> Result<(), JsValue>;