diff --git a/.gitignore b/.gitignore index 51778e4a6..0f082cdee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/*.rs.bk nopanic_check/Cargo.lock nopanic_check/target/ +.idea/ diff --git a/Cargo.toml b/Cargo.toml index 3037a4454..31398d5d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,9 @@ libc = { version = "0.2.154", default-features = false } [target.'cfg(all(target_arch = "wasm32", target_os = "wasi", target_env = "p2"))'.dependencies] wasip2 = { version = "1", default-features = false } +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown", getrandom_backend = "ic"))'.dependencies] +ic-cdk = { version = "0.19.0", default-features = false } + # wasm_js [target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))'.dependencies] wasm-bindgen = { version = "0.2.98", default-features = false, optional = true } @@ -84,7 +87,7 @@ wasm-bindgen-test = "0.3" [lints.rust.unexpected_cfgs] level = "warn" check-cfg = [ - 'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "wasm_js", "windows_legacy", "unsupported"))', + 'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "wasm_js", "windows_legacy", "unsupported", "ic"))', 'cfg(getrandom_msan)', 'cfg(getrandom_test_linux_fallback)', 'cfg(getrandom_test_linux_without_fallback)', diff --git a/src/backends.rs b/src/backends.rs index 991d413b5..8d27a4beb 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -11,6 +11,9 @@ cfg_if! { if #[cfg(getrandom_backend = "custom")] { mod custom; pub use custom::*; + } else if #[cfg(getrandom_backend = "ic")] { + mod internet_computer; + pub use internet_computer::*; } else if #[cfg(getrandom_backend = "linux_getrandom")] { mod getrandom; mod sanitizer; diff --git a/src/backends/internet_computer.rs b/src/backends/internet_computer.rs new file mode 100644 index 000000000..55ba2a4f7 --- /dev/null +++ b/src/backends/internet_computer.rs @@ -0,0 +1,67 @@ +//! Backend for Internet Computer (IC) WASM canisters +//! +//! # ⚠️ WARNING: NOT CRYPTOGRAPHICALLY SECURE +//! +//! This backend provides **deterministic pseudo-random bytes** seeded from +//! canister execution state. It exists solely to satisfy dependencies that +//! require `getrandom` for non-security purposes (e.g., ML inference, +//! hash table initialization). +//! +//! **DO NOT USE** for key generation, nonces, tokens, or any security-sensitive purpose. +//! +//! IC's `raw_rand()` is async-only and cannot be called from synchronous `getrandom`. + +use crate::Error; +use core::mem::MaybeUninit; + +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +use ic_cdk::api::{instruction_counter, time}; + +#[inline] +fn splitmix64_next(x: &mut u64) -> u64 { + *x = x.wrapping_add(0x9E3779B97F4A7C15); + let mut z = *x; + z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9); + z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB); + z ^ (z >> 31) +} + +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let len = dest.len(); + if len == 0 { + return Ok(()); + } + + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + let mut state = time() ^ instruction_counter() ^ (len as u64).wrapping_mul(0x9E3779B97F4A7C15); + + #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] + let mut state = (len as u64).wrapping_mul(0x9E3779B97F4A7C15); + + let mut i = 0; + while i < len { + let word = splitmix64_next(&mut state).to_le_bytes(); + let n = core::cmp::min(8, len - i); + for j in 0..n { + dest[i + j].write(word[j]); + } + i += n; + } + Ok(()) +} + +pub fn inner_u32() -> Result { + let mut buf = [MaybeUninit::::uninit(); 4]; + fill_inner(&mut buf)?; + Ok(u32::from_ne_bytes(core::array::from_fn(|i| unsafe { + buf[i].assume_init() + }))) +} + +pub fn inner_u64() -> Result { + let mut buf = [MaybeUninit::::uninit(); 8]; + fill_inner(&mut buf)?; + Ok(u64::from_ne_bytes(core::array::from_fn(|i| unsafe { + buf[i].assume_init() + }))) +}