diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f2e0c480..5c340c33 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -86,6 +86,7 @@ jobs: armv6k-nintendo-3ds, armv7-sony-vita-newlibeabihf, i686-unknown-hurd-gnu, + riscv32imc-esp-espidf, x86_64-unknown-hermit, x86_64-wrs-vxworks, x86_64-unknown-dragonfly, @@ -207,19 +208,6 @@ jobs: RUSTFLAGS: -Dwarnings --cfg getrandom_backend="rndr" run: cargo build --target=aarch64-unknown-linux-gnu --features std - esp-idf: - name: ESP-IDF - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly # Required to build libcore - with: - components: rust-src - - uses: Swatinem/rust-cache@v2 - - env: - RUSTFLAGS: -Dwarnings --cfg getrandom_backend="esp_idf" - run: cargo build -Z build-std=core --target=riscv32imc-esp-espidf - no-atomics: name: No Atomics runs-on: ubuntu-24.04 diff --git a/Cargo.toml b/Cargo.toml index 639c906f..4c795c23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,14 +74,14 @@ windows-targets = "0.52" [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 } [target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"), target_feature = "atomics"))'.dependencies] -js-sys = { version = "0.3.75", default-features = false, optional = true } +js-sys = { version = "0.3.77", default-features = false, optional = true } [target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))'.dev-dependencies] wasm-bindgen-test = "0.3" [lints.rust.unexpected_cfgs] level = "warn" check-cfg = [ - 'cfg(getrandom_backend, values("custom", "rdrand", "rndr", "linux_getrandom", "wasm_js", "esp_idf"))', + 'cfg(getrandom_backend, values("custom", "rdrand", "rndr", "linux_getrandom", "wasm_js"))', 'cfg(getrandom_msan)', 'cfg(getrandom_test_linux_fallback)', 'cfg(getrandom_test_netbsd_fallback)', diff --git a/README.md b/README.md index 46c9a2c9..6e7e3e28 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ fn get_random_u128() -> Result { | WASI 0.2 | `wasm32‑wasip2` | [`get-random-u64`] | SOLID | `*-kmc-solid_*` | `SOLID_RNG_SampleRandomBytes` | Nintendo 3DS | `*-nintendo-3ds` | [`getrandom`][18] +| ESP-IDF | `*‑espidf` | [`esp_fill_random`] WARNING: see "Early Boot" section below | PS Vita | `*-vita-*` | [`getentropy`][19] | QNX Neutrino | `*‑nto-qnx*` | [`/dev/urandom`][14] (identical to `/dev/random`) | AIX | `*-ibm-aix` | [`/dev/urandom`][15] @@ -81,7 +82,6 @@ of randomness based on their specific needs: | `linux_getrandom` | Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call (without `/dev/urandom` fallback). Bumps minimum supported Linux kernel version to 3.17 and Android API level to 23 (Marshmallow). | `rdrand` | x86, x86-64 | `x86_64-*`, `i686-*` | [`RDRAND`] instruction | `rndr` | AArch64 | `aarch64-*` | [`RNDR`] register -| `esp_idf` | ESP-IDF | `*‑espidf` | [`esp_fill_random`]. WARNING: can return low-quality entropy without proper hardware configuration! | `wasm_js` | Web Browser, Node.js | `wasm32‑unknown‑unknown`, `wasm32v1-none` | [`Crypto.getRandomValues`]. Requires feature `js`. | `custom` | All targets | `*` | User-provided custom implementation (see [custom backend]) @@ -248,6 +248,13 @@ sourced according to the platform's best practices, but each platform has its own limits on the grade of randomness it can promise in environments with few sources of entropy. +On ESP-IDF, if `esp_fill_random` is used before enabling WiFi, BT, or the +voltage noise entropy source (SAR ADC), the Hardware RNG will only be seeded +via RC_FAST_CLK. This can occur during early boot unless +`bootloader_random_enable()` is called. For more information see the +[ESP-IDF RNG Docs][esp-idf-rng] or the +[RNG section of the ESP32 Technical Reference Manual][esp-trng-docs]. + ## Error handling We always prioritize failure over returning known insecure "random" bytes. @@ -336,7 +343,9 @@ dual licensed as above, without any additional terms or conditions. [`RNDR`]: https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/RNDR--Random-Number [`CCRandomGenerateBytes`]: https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html [`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw -[`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t +[`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html#functions +[esp-idf-rng]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html +[esp-trng-docs]: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#rng [`random_get`]: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno [`get-random-u64`]: https://github.com/WebAssembly/WASI/blob/v0.2.1/wasip2/random/random.wit#L23-L28 [configuration flags]: #configuration-flags diff --git a/src/backends.rs b/src/backends.rs index 10141521..7abb8d3f 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -22,7 +22,7 @@ cfg_if! { } else if #[cfg(all(feature = "js", getrandom_backend = "wasm_js"))] { mod wasm_js; pub use wasm_js::*; - } else if #[cfg(getrandom_backend = "esp_idf")] { + } else if #[cfg(target_os = "espidf")] { mod esp_idf; pub use esp_idf::*; } else if #[cfg(any( diff --git a/src/backends/esp_idf.rs b/src/backends/esp_idf.rs index 0f0ff7f4..7ee391ab 100644 --- a/src/backends/esp_idf.rs +++ b/src/backends/esp_idf.rs @@ -4,9 +4,6 @@ use core::{ffi::c_void, mem::MaybeUninit}; pub use crate::util::{inner_u32, inner_u64}; -#[cfg(not(target_os = "espidf"))] -compile_error!("`esp_idf` backend can be enabled only for ESP-IDF targets!"); - extern "C" { fn esp_fill_random(buf: *mut c_void, len: usize) -> u32; } diff --git a/src/backends/wasm_js.rs b/src/backends/wasm_js.rs index c4943652..fe8f1a6e 100644 --- a/src/backends/wasm_js.rs +++ b/src/backends/wasm_js.rs @@ -49,8 +49,7 @@ pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { return Err(Error::WEB_CRYPTO); } - // SAFETY: `sub_buf`'s length is the same length as `chunk` - unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr().cast::()) }; + sub_buf.copy_to_uninit(chunk); } Ok(()) } diff --git a/src/backends/windows.rs b/src/backends/windows.rs index 76435943..e6e12e18 100644 --- a/src/backends/windows.rs +++ b/src/backends/windows.rs @@ -1,7 +1,7 @@ //! Implementation for Windows 10 and later //! //! On Windows 10 and later, ProcessPrng "is the primary interface to the -//! user-mode per-processer PRNGs" and only requires bcryptprimitives.dll, +//! user-mode per-processor PRNGs" and only requires bcryptprimitives.dll, //! making it a better option than the other Windows RNG APIs: //! - BCryptGenRandom: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom //! - Requires bcrypt.dll (which loads bcryptprimitives.dll anyway) @@ -28,20 +28,19 @@ pub use crate::util::{inner_u32, inner_u64}; // Binding to the Windows.Win32.Security.Cryptography.ProcessPrng API. As // bcryptprimitives.dll lacks an import library, we use the windows-targets // crate to link to it. -windows_targets::link!("bcryptprimitives.dll" "system" fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL); -#[allow(clippy::upper_case_acronyms)] -pub type BOOL = i32; -pub const TRUE: BOOL = 1i32; +// +// TODO(MSRV 1.71): Migrate to linking as raw-dylib directly. +// https://github.com/joboet/rust/blob/5c1c72572479afe98734d5f78fa862abe662c41a/library/std/src/sys/pal/windows/c.rs#L119 +// https://github.com/microsoft/windows-rs/blob/0.60.0/crates/libs/targets/src/lib.rs +windows_targets::link!("bcryptprimitives.dll" "system" fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> i32); pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - // ProcessPrng should always return TRUE, but we check just in case. - match unsafe { ProcessPrng(dest.as_mut_ptr().cast::(), dest.len()) } { - TRUE => Ok(()), - _ => Err(Error::WINDOWS_PROCESS_PRNG), - } -} - -impl Error { - /// Calling Windows ProcessPrng failed. - pub(crate) const WINDOWS_PROCESS_PRNG: Error = Self::new_internal(10); + let result = unsafe { ProcessPrng(dest.as_mut_ptr().cast::(), dest.len()) }; + // Since Windows 10, calls to the user-mode RNG are guaranteed to never + // fail during runtime (rare windows W); `ProcessPrng` will only ever + // return 1 (which is how windows represents TRUE). + // See the bottom of page 6 of the aforementioned Windows RNG + // whitepaper for more information. + debug_assert!(result == 1); + Ok(()) } diff --git a/src/error.rs b/src/error.rs index aafff56c..d94ca2a4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -114,8 +114,6 @@ impl Error { target_os = "tvos", ))] Error::IOS_RANDOM_GEN => "SecRandomCopyBytes: iOS Security framework failure", - #[cfg(all(windows, not(target_vendor = "win7")))] - Error::WINDOWS_PROCESS_PRNG => "ProcessPrng: Windows system function failure", #[cfg(all(windows, target_vendor = "win7"))] Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure", #[cfg(all(feature = "js", getrandom_backend = "wasm_js"))]