//! Implementation for WASM based on Web and Node.js usecrate::Error;
externcrate std; use std::{mem::MaybeUninit, thread_local};
use js_sys::{global, Function, Uint8Array}; use wasm_bindgen::{prelude::wasm_bindgen, JsCast, 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 WEB_CRYPTO_BUFFER_SIZE: usize = 256; // Node.js's crypto.randomFillSync requires the size to be less than 2**31. const NODE_MAX_BUFFER_SIZE: usize = (1 << 31) - 1;
// JsValues are always per-thread, so we initialize RngSource for each thread. // See: https://github.com/rustwasm/wasm-bindgen/pull/955
thread_local!( static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
);
match source {
RngSource::Node(n) => { for chunk in dest.chunks_mut(NODE_MAX_BUFFER_SIZE) { // SAFETY: chunk is never used directly, the memory is only // modified via the Uint8Array view, which is passed // directly to JavaScript. Also, crypto.randomFillSync does // not resize the buffer. We know the length is less than // u32::MAX because of the chunking above. // Note that this uses the fact that JavaScript doesn't // have a notion of "uninitialized memory", this is purely // a Rust/C/C++ concept. let res = n.random_fill_sync(unsafe {
Uint8Array::view_mut_raw(chunk.as_mut_ptr() as *mut u8, chunk.len())
}); if res.is_err() { return Err(Error::NODE_RANDOM_FILL_SYNC);
}
}
}
RngSource::Web(crypto, buf) => { // getRandomValues does not work with all types of WASM memory, // so we initially write to browser memory to avoid exceptions. for chunk in dest.chunks_mut(WEB_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() as u32);
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() as *mut u8) };
}
}
};
Ok(())
})
}
fn getrandom_init() -> Result<RngSource, Error> { let global: Global = global().unchecked_into();
// Get the Web Crypto interface if we are in a browser, Web Worker, Deno, // or another environment that supports the Web Cryptography API. This // also allows for user-provided polyfills in unsupported environments. let crypto = match global.crypto() { // Standard Web Crypto interface
c if c.is_object() => c, // Node.js CommonJS Crypto module
_ if is_node(&global) => { // If module.require isn't a valid function, we are in an ES module. match Module::require_fn().and_then(JsCast::dyn_into::<Function>) {
Ok(require_fn) => match require_fn.call1(&global, &JsValue::from_str("crypto")) {
Ok(n) => return Ok(RngSource::Node(n.unchecked_into())),
Err(_) => return Err(Error::NODE_CRYPTO),
},
Err(_) => return Err(Error::NODE_ES_MODULE),
}
} // IE 11 Workaround
_ => match global.ms_crypto() {
c if c.is_object() => c,
_ => return Err(Error::WEB_CRYPTO),
},
};
let buf = Uint8Array::new_with_length(WEB_CRYPTO_BUFFER_SIZE as u32);
Ok(RngSource::Web(crypto, buf))
}
// Taken from https://www.npmjs.com/package/browser-or-node fn is_node(global: &Global) -> bool { let process = global.process(); if process.is_object() { let versions = process.versions(); if versions.is_object() { return versions.node().is_string();
}
} false
}
#[wasm_bindgen] extern"C" { // Return type of js_sys::global() type Global;
// Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/) type WebCrypto; // Getters for the WebCrypto API #[wasm_bindgen(method, getter)] fn crypto(this: &Global) -> WebCrypto; #[wasm_bindgen(method, getter, js_name = msCrypto)] fn ms_crypto(this: &Global) -> WebCrypto; // Crypto.getRandomValues() #[wasm_bindgen(method, js_name = getRandomValues, catch)] fn get_random_values(this: &WebCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
// Ideally, we would just use `fn require(s: &str)` here. However, doing // this causes a Webpack warning. So we instead return the function itself // and manually invoke it using call1. This also lets us to check that the // function actually exists, allowing for better error messages. See: // https://github.com/rust-random/getrandom/issues/224 // https://github.com/rust-random/getrandom/issues/256 type Module; #[wasm_bindgen(getter, static_method_of = Module, js_class = module, js_name = require, catch)] fn require_fn() -> Result<JsValue, JsValue>;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.