Skip to content

Commit

Permalink
Cache global Crypto object
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda committed Dec 6, 2024
1 parent ae0c807 commit aee9640
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 47 deletions.
40 changes: 26 additions & 14 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
62 changes: 29 additions & 33 deletions src/backends/wasm_js.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Implementation for WASM based on Web and Node.js
use crate::Error;
use core::mem::MaybeUninit;

Expand All @@ -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<u8>]) -> 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::<u8>()) };
}

// SAFETY: `sub_buf`'s length is the same length as `chunk`
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::<u8>()) };
}
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>;
// Crypto.getRandomValues()
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
fn get_random_values(this: &Crypto, buf: &Uint8Array) -> Result<(), JsValue>;
Expand Down

0 comments on commit aee9640

Please sign in to comment.