diff --git a/README.md b/README.md index 63d39396..d40fa910 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ library like [`rand`]. [`rand`]: https://crates.io/crates/rand -## Usage +## Examples Add the `getrandom` dependency to your `Cargo.toml` file: @@ -38,6 +38,16 @@ fn get_random_u128() -> Result { } ``` +The crate also support direct generation of random `u32` and `u64` values: + +```rust +fn get_rand_u32_u64() -> Result<(u32, u64), getrandom::Error> { + let a = getrandom::u32()?; + let b = getrandom::u64()?; + Ok((a, b)) +} +``` + ## Supported targets | Target | Target Triple | Implementation @@ -106,6 +116,25 @@ WILL NOT have any effect on its downstream users. [`.cargo/config.toml`]: https://doc.rust-lang.org/cargo/reference/config.html +### "Insecure" functions + +Sometimes, early in the boot process, the OS has not collected enough +entropy to securely seed its RNG. This is especially common on virtual +machines, where standard "random" events are hard to come by. + +Some operating system interfaces always block until the RNG is securely +seeded, which can take anywhere from a few seconds to more than a minute. +Some platforms offer a choice between blocking and getting potentially less +secure randomness, i.e. generated data may be less difficult for an attacker +to predict. + +We expose this functionality through two sets of functions. The "secure" +functions ([`fill`], [`fill_uninit`], [`u32`], and [`u64`]) may block but +always return "secure" randomness suitable for cryptographic needs. +Meanwhile, their "insecure" counterparts ([`insecure_fill`], +[`insecure_fill_uninit`], [`insecure_u32`], and [`insecure_u64`]) may return +randomness of lesser quality but are less likely to block in entropy-starved scenarios. + ### WebAssembly support This crate fully supports the [WASI] and [Emscripten] targets. However, @@ -210,31 +239,6 @@ The fallback can be disabled by enabling the `linux_getrandom` opt-in backend. Note that doing so will bump minimum supported Linux kernel version to 3.17 and Android API level to 23 (Marshmallow). -### Early boot - -Sometimes, early in the boot process, the OS has not collected enough -entropy to securely seed its RNG. This is especially common on virtual -machines, where standard "random" events are hard to come by. - -Some operating system interfaces always block until the RNG is securely -seeded. This can take anywhere from a few seconds to more than a minute. -A few (Linux, NetBSD and Solaris) offer a choice between blocking and -getting an error; in these cases, we always choose to block. - -On Linux (when the `getrandom` system call is not available), reading from -`/dev/urandom` never blocks, even when the OS hasn't collected enough -entropy yet. To avoid returning low-entropy bytes, we first poll -`/dev/random` and only switch to `/dev/urandom` once this has succeeded. - -On OpenBSD, this kind of entropy accounting isn't available, and on -NetBSD, blocking on it is discouraged. On these platforms, nonblocking -interfaces are used, even when reliable entropy may not be available. -On the platforms where it is used, the reliability of entropy accounting -itself isn't free from controversy. This library provides randomness -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. - ## Error handling We always prioritize failure over returning known insecure "random" bytes. @@ -346,4 +350,11 @@ dual licensed as above, without any additional terms or conditions. [LICENSE-MIT]: https://github.com/rust-random/getrandom/blob/master/LICENSE-MIT [`Error::UNEXPECTED`]: https://docs.rs/getrandom/latest/getrandom/struct.Error.html#associatedconstant.UNEXPECTED +[`fill`]: https://docs.rs/getrandom/latest/getrandom/fn.fill.html [`fill_uninit`]: https://docs.rs/getrandom/latest/getrandom/fn.fill_uninit.html +[`u32`]: https://docs.rs/getrandom/latest/getrandom/fn.u32.html +[`u64`]: https://docs.rs/getrandom/latest/getrandom/fn.u64.html +[`insecure_fill`]: https://docs.rs/getrandom/latest/getrandom/fn.insecure_fill.html +[`insecure_fill_uninit`]: https://docs.rs/getrandom/latest/getrandom/fn.insecure_fill_uninit.html +[`insecure_u32`]: https://docs.rs/getrandom/latest/getrandom/fn.insecure_u32.html +[`insecure_u64`]: https://docs.rs/getrandom/latest/getrandom/fn.insecure_u64.html \ No newline at end of file diff --git a/src/backends.rs b/src/backends.rs index d5164173..98648189 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -1,10 +1,16 @@ //! System-specific implementations. //! -//! This module should provide `fill_inner` with the signature -//! `fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error>`. -//! The function MUST fully initialize `dest` when `Ok(())` is returned. -//! The function MUST NOT ever write uninitialized bytes into `dest`, -//! regardless of what value it returns. +//! This module should provide the following functions: +//! - `fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error>` +//! - `fn insecure_fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error>` +//! - `fn u32() -> Result` +//! - `fn insecure_u32() -> Result` +//! - `fn u64() -> Result` +//! - `fn insecure_u64() -> Result` +//! +//! `fill_uninit` and `insecure_fill_uninit` MUST fully initialize `dest` +//! when `Ok(())` is returned. The functions MUST NOT ever write uninitialized +//! bytes into `dest`, regardless of what value it returns. cfg_if! { if #[cfg(getrandom_backend = "custom")] { diff --git a/src/backends/apple_other.rs b/src/backends/apple_other.rs index 127a31e3..c0230f57 100644 --- a/src/backends/apple_other.rs +++ b/src/backends/apple_other.rs @@ -2,9 +2,9 @@ use crate::Error; use core::{ffi::c_void, mem::MaybeUninit}; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { let dst_ptr = dest.as_mut_ptr().cast::(); let ret = unsafe { libc::CCRandomGenerateBytes(dst_ptr, dest.len()) }; if ret == libc::kCCSuccess { diff --git a/src/backends/custom.rs b/src/backends/custom.rs index 0c482946..34f8f4c6 100644 --- a/src/backends/custom.rs +++ b/src/backends/custom.rs @@ -2,9 +2,9 @@ use crate::Error; use core::mem::MaybeUninit; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { extern "Rust" { fn __getrandom_v03_custom(dest: *mut u8, len: usize) -> Result<(), Error>; } diff --git a/src/backends/esp_idf.rs b/src/backends/esp_idf.rs index 0f0ff7f4..341bb5af 100644 --- a/src/backends/esp_idf.rs +++ b/src/backends/esp_idf.rs @@ -1,23 +1,36 @@ -//! Implementation for ESP-IDF +//! Implementation for ESP-IDF. +//! +//! Note that NOT enabling WiFi, BT, or the voltage noise entropy source +//! (via `bootloader_random_enable`) will cause ESP-IDF to return pseudo-random numbers based on +//! the voltage noise entropy, after the initial boot process: +//! https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html +//! +//! However tracking if some of these entropy sources is enabled is way too difficult +//! to implement here. use crate::Error; use core::{ffi::c_void, mem::MaybeUninit}; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_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; + fn esp_random() -> u32; + fn esp_fill_random(buf: *mut c_void, len: usize); } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { - // Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`) - // will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process: - // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html - // - // However tracking if some of these entropy sources is enabled is way too difficult to implement here - unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) }; +pub fn u32() -> Result { + Ok(unsafe { esp_random() }) +} +pub fn u64() -> Result { + let (a, b) = unsafe { (esp_random(), esp_random()) }; + let res = (u64::from(a) << 32) | u64::from(b); + Ok(res) +} + +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { + unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) }; Ok(()) } diff --git a/src/backends/fuchsia.rs b/src/backends/fuchsia.rs index 5edd210d..5396a9a2 100644 --- a/src/backends/fuchsia.rs +++ b/src/backends/fuchsia.rs @@ -2,14 +2,14 @@ use crate::Error; use core::mem::MaybeUninit; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[link(name = "zircon")] extern "C" { fn zx_cprng_draw(buffer: *mut u8, length: usize); } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { unsafe { zx_cprng_draw(dest.as_mut_ptr().cast::(), dest.len()) } Ok(()) } diff --git a/src/backends/getentropy.rs b/src/backends/getentropy.rs index e0b2d34c..24c7f36e 100644 --- a/src/backends/getentropy.rs +++ b/src/backends/getentropy.rs @@ -10,12 +10,12 @@ use crate::Error; use core::{ffi::c_void, mem::MaybeUninit}; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[path = "../util_libc.rs"] mod util_libc; -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { for chunk in dest.chunks_mut(256) { let ret = unsafe { libc::getentropy(chunk.as_mut_ptr().cast::(), chunk.len()) }; if ret != 0 { diff --git a/src/backends/getrandom.rs b/src/backends/getrandom.rs index a00829f7..24289d0e 100644 --- a/src/backends/getrandom.rs +++ b/src/backends/getrandom.rs @@ -18,12 +18,12 @@ use crate::Error; use core::{ffi::c_void, mem::MaybeUninit}; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[path = "../util_libc.rs"] mod util_libc; -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { util_libc::sys_fill_exact(dest, |buf| unsafe { libc::getrandom(buf.as_mut_ptr().cast::(), buf.len(), 0) }) diff --git a/src/backends/hermit.rs b/src/backends/hermit.rs index 11bfbf27..3eb04054 100644 --- a/src/backends/hermit.rs +++ b/src/backends/hermit.rs @@ -2,6 +2,8 @@ use crate::Error; use core::mem::MaybeUninit; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64}; + extern "C" { fn sys_read_entropy(buffer: *mut u8, length: usize, flags: u32) -> isize; // Note that `sys_secure_rand32/64` are implemented using `sys_read_entropy`: @@ -12,7 +14,7 @@ extern "C" { fn sys_secure_rand64(value: *mut u64) -> i32; } -pub fn inner_u32() -> Result { +pub fn u32() -> Result { let mut res = MaybeUninit::uninit(); let ret = unsafe { sys_secure_rand32(res.as_mut_ptr()) }; match ret { @@ -22,7 +24,7 @@ pub fn inner_u32() -> Result { } } -pub fn inner_u64() -> Result { +pub fn u64() -> Result { let mut res = MaybeUninit::uninit(); let ret = unsafe { sys_secure_rand64(res.as_mut_ptr()) }; match ret { @@ -32,7 +34,7 @@ pub fn inner_u64() -> Result { } } -pub fn fill_inner(mut dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(mut dest: &mut [MaybeUninit]) -> Result<(), Error> { while !dest.is_empty() { let res = unsafe { sys_read_entropy(dest.as_mut_ptr().cast::(), dest.len(), 0) }; match res { diff --git a/src/backends/linux_android.rs b/src/backends/linux_android.rs index 6c0b66ae..1ffd7e11 100644 --- a/src/backends/linux_android.rs +++ b/src/backends/linux_android.rs @@ -2,7 +2,7 @@ use crate::Error; use core::mem::MaybeUninit; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[path = "../util_libc.rs"] mod util_libc; @@ -10,7 +10,7 @@ mod util_libc; #[cfg(not(any(target_os = "android", target_os = "linux")))] compile_error!("`linux_getrandom` backend can be enabled only for Linux/Android targets!"); -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { util_libc::sys_fill_exact(dest, |buf| unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }) diff --git a/src/backends/linux_android_with_fallback.rs b/src/backends/linux_android_with_fallback.rs index cdfff980..71a62efb 100644 --- a/src/backends/linux_android_with_fallback.rs +++ b/src/backends/linux_android_with_fallback.rs @@ -9,7 +9,7 @@ use core::{ }; use use_file::util_libc; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t; @@ -56,10 +56,10 @@ fn init() -> NonNull { // prevent inlining of the fallback implementation #[inline(never)] fn use_file_fallback(dest: &mut [MaybeUninit]) -> Result<(), Error> { - use_file::fill_inner(dest) + use_file::fill_uninit(dest) } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { // Despite being only a single atomic variable, we still cannot always use // Ordering::Relaxed, as we need to make sure a successful call to `init` // is "ordered before" any data read through the returned pointer (which diff --git a/src/backends/linux_rustix.rs b/src/backends/linux_rustix.rs index d3bcce3e..e8848d88 100644 --- a/src/backends/linux_rustix.rs +++ b/src/backends/linux_rustix.rs @@ -2,12 +2,12 @@ use crate::{Error, MaybeUninit}; use rustix::rand::{getrandom_uninit, GetRandomFlags}; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[cfg(not(any(target_os = "android", target_os = "linux")))] compile_error!("`linux_rustix` backend can be enabled only for Linux/Android targets!"); -pub fn fill_inner(mut dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(mut dest: &mut [MaybeUninit]) -> Result<(), Error> { loop { let res = getrandom_uninit(dest, GetRandomFlags::empty()).map(|(res, _)| res.len()); match res { diff --git a/src/backends/netbsd.rs b/src/backends/netbsd.rs index 0e3268ef..44848bb9 100644 --- a/src/backends/netbsd.rs +++ b/src/backends/netbsd.rs @@ -12,7 +12,7 @@ use core::{ sync::atomic::{AtomicPtr, Ordering}, }; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[path = "../util_libc.rs"] mod util_libc; @@ -62,7 +62,7 @@ fn init() -> *mut c_void { ptr } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { // Despite being only a single atomic variable, we still cannot always use // Ordering::Relaxed, as we need to make sure a successful call to `init` // is "ordered before" any data read through the returned pointer (which diff --git a/src/backends/rdrand.rs b/src/backends/rdrand.rs index 19fa97ba..37aee518 100644 --- a/src/backends/rdrand.rs +++ b/src/backends/rdrand.rs @@ -2,6 +2,8 @@ use crate::{util::slice_as_uninit, Error}; use core::mem::{size_of, MaybeUninit}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64}; + #[path = "../lazy.rs"] mod lazy; @@ -147,7 +149,7 @@ unsafe fn rdrand_u64() -> Option { Some((u64::from(a) << 32) || u64::from(b)) } -pub fn inner_u32() -> Result { +pub fn u32() -> Result { if !RDRAND_GOOD.unsync_init(is_rdrand_good) { return Err(Error::NO_RDRAND); } @@ -155,7 +157,7 @@ pub fn inner_u32() -> Result { unsafe { rdrand_u32() }.ok_or(Error::FAILED_RDRAND) } -pub fn inner_u64() -> Result { +pub fn u64() -> Result { if !RDRAND_GOOD.unsync_init(is_rdrand_good) { return Err(Error::NO_RDRAND); } @@ -163,7 +165,7 @@ pub fn inner_u64() -> Result { unsafe { rdrand_u64() }.ok_or(Error::FAILED_RDRAND) } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { if !RDRAND_GOOD.unsync_init(is_rdrand_good) { return Err(Error::NO_RDRAND); } diff --git a/src/backends/rndr.rs b/src/backends/rndr.rs index bd2878f0..a39838a2 100644 --- a/src/backends/rndr.rs +++ b/src/backends/rndr.rs @@ -9,6 +9,8 @@ use crate::{ use core::arch::asm; use core::mem::{size_of, MaybeUninit}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64}; + #[cfg(not(target_arch = "aarch64"))] compile_error!("the `rndr` backend can be enabled only for AArch64 targets!"); @@ -104,7 +106,7 @@ fn is_rndr_available() -> bool { } } -pub fn inner_u32() -> Result { +pub fn u32() -> Result { if is_rndr_available() { // SAFETY: after this point, we know the `rand` target feature is enabled let res = unsafe { rndr() }; @@ -114,7 +116,7 @@ pub fn inner_u32() -> Result { } } -pub fn inner_u64() -> Result { +pub fn u64() -> Result { if is_rndr_available() { // SAFETY: after this point, we know the `rand` target feature is enabled let res = unsafe { rndr() }; @@ -124,7 +126,7 @@ pub fn inner_u64() -> Result { } } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { if is_rndr_available() { // SAFETY: after this point, we know the `rand` target feature is enabled unsafe { rndr_fill(dest).ok_or(Error::RNDR_FAILURE) } diff --git a/src/backends/solaris.rs b/src/backends/solaris.rs index ea5344fc..789b0d6c 100644 --- a/src/backends/solaris.rs +++ b/src/backends/solaris.rs @@ -15,14 +15,14 @@ use crate::Error; use core::{ffi::c_void, mem::MaybeUninit}; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[path = "../util_libc.rs"] mod util_libc; const MAX_BYTES: usize = 1024; -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { for chunk in dest.chunks_mut(MAX_BYTES) { let ptr = chunk.as_mut_ptr().cast::(); let ret = unsafe { libc::getrandom(ptr, chunk.len(), libc::GRND_RANDOM) }; diff --git a/src/backends/solid.rs b/src/backends/solid.rs index 6699e686..c2a91f52 100644 --- a/src/backends/solid.rs +++ b/src/backends/solid.rs @@ -2,13 +2,13 @@ use crate::Error; use core::mem::MaybeUninit; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; extern "C" { pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> i32; } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr().cast::(), dest.len()) }; if ret >= 0 { Ok(()) diff --git a/src/backends/use_file.rs b/src/backends/use_file.rs index ef12fca1..e08164c0 100644 --- a/src/backends/use_file.rs +++ b/src/backends/use_file.rs @@ -7,7 +7,7 @@ use core::{ }; #[cfg(not(any(target_os = "android", target_os = "linux")))] -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[path = "../util_libc.rs"] pub(super) mod util_libc; @@ -40,7 +40,7 @@ const FD_ONGOING_INIT: libc::c_int = -2; // `Ordering::Acquire` to synchronize with it. static FD: AtomicI32 = AtomicI32::new(FD_UNINIT); -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { let mut fd = FD.load(Ordering::Acquire); if fd == FD_UNINIT || fd == FD_ONGOING_INIT { fd = open_or_wait()?; diff --git a/src/backends/vxworks.rs b/src/backends/vxworks.rs index 51b7580e..4b76b9fe 100644 --- a/src/backends/vxworks.rs +++ b/src/backends/vxworks.rs @@ -9,9 +9,9 @@ use core::{ #[path = "../util_libc.rs"] mod util_libc; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { static RNG_INIT: AtomicBool = AtomicBool::new(false); while !RNG_INIT.load(Relaxed) { let ret = unsafe { libc::randSecure() }; diff --git a/src/backends/wasi_p1.rs b/src/backends/wasi_p1.rs index 6eefdee6..8caab153 100644 --- a/src/backends/wasi_p1.rs +++ b/src/backends/wasi_p1.rs @@ -2,7 +2,7 @@ use crate::Error; use core::mem::MaybeUninit; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; // This linking is vendored from the wasi crate: // https://docs.rs/wasi/0.11.0+wasi-snapshot-preview1/src/wasi/lib_generated.rs.html#2344-2350 @@ -11,7 +11,7 @@ extern "C" { fn random_get(arg0: i32, arg1: i32) -> i32; } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { // Based on the wasi code: // https://docs.rs/wasi/0.11.0+wasi-snapshot-preview1/src/wasi/lib_generated.rs.html#2046-2062 // Note that size of an allocated object can not be bigger than isize::MAX bytes. diff --git a/src/backends/wasi_p2.rs b/src/backends/wasi_p2.rs index 9d5d601d..022557b9 100644 --- a/src/backends/wasi_p2.rs +++ b/src/backends/wasi_p2.rs @@ -3,16 +3,18 @@ use crate::Error; use core::mem::MaybeUninit; use wasi::random::random::get_random_u64; -pub fn inner_u32() -> Result { +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64}; + +pub fn u32() -> Result { let val = get_random_u64(); Ok(crate::util::truncate(val)) } -pub fn inner_u64() -> Result { +pub fn u64() -> Result { Ok(get_random_u64()) } -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { use core::ptr::copy_nonoverlapping; use wasi::random::random::get_random_u64; diff --git a/src/backends/wasm_js.rs b/src/backends/wasm_js.rs index de2b4333..ed6d14e9 100644 --- a/src/backends/wasm_js.rs +++ b/src/backends/wasm_js.rs @@ -4,7 +4,7 @@ use crate::Error; extern crate std; use std::{mem::MaybeUninit, thread_local}; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; #[cfg(not(all( any(target_arch = "wasm32", target_arch = "wasm64"), @@ -32,7 +32,7 @@ thread_local!( static RNG_SOURCE: Result = getrandom_init(); ); -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { RNG_SOURCE.with(|result| { let source = result.as_ref().map_err(|&e| e)?; diff --git a/src/backends/windows.rs b/src/backends/windows.rs index 6c8e46b1..e530273e 100644 --- a/src/backends/windows.rs +++ b/src/backends/windows.rs @@ -23,7 +23,7 @@ use crate::Error; use core::mem::MaybeUninit; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; // Binding to the Windows.Win32.Security.Cryptography.ProcessPrng API. As // bcryptprimitives.dll lacks an import library, we use the windows-targets @@ -33,7 +33,7 @@ windows_targets::link!("bcryptprimitives.dll" "system" fn ProcessPrng(pbdata: *m pub type BOOL = i32; pub const TRUE: BOOL = 1i32; -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(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(()), diff --git a/src/backends/windows7.rs b/src/backends/windows7.rs index 2546719e..28c726c4 100644 --- a/src/backends/windows7.rs +++ b/src/backends/windows7.rs @@ -12,7 +12,7 @@ use crate::Error; use core::{ffi::c_void, mem::MaybeUninit}; -pub use crate::util::{inner_u32, inner_u64}; +pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64}; // Binding to the Windows.Win32.Security.Authentication.Identity.RtlGenRandom // API. Don't use windows-targets as it doesn't support Windows 7 targets. @@ -25,7 +25,7 @@ extern "system" { type BOOLEAN = u8; const TRUE: BOOLEAN = 1u8; -pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<(), Error> { // Prevent overflow of u32 let chunk_size = usize::try_from(i32::MAX).expect("Windows does not support 16-bit targets"); for chunk in dest.chunks_mut(chunk_size) { diff --git a/src/default_impls.rs b/src/default_impls.rs new file mode 100644 index 00000000..2b958e58 --- /dev/null +++ b/src/default_impls.rs @@ -0,0 +1,47 @@ +#![allow(dead_code)] +use crate::Error; +use core::{mem::MaybeUninit, slice}; + +#[inline(always)] +#[allow(unused_unsafe)] +unsafe fn default_impl(secure: bool) -> Result { + let mut res = MaybeUninit::::uninit(); + // SAFETY: the created slice has the same size as `res` + let dst = unsafe { + let p: *mut MaybeUninit = res.as_mut_ptr().cast(); + slice::from_raw_parts_mut(p, core::mem::size_of::()) + }; + if secure { + crate::fill_uninit(dst)?; + } else { + crate::insecure_fill_uninit(dst)?; + } + // SAFETY: `dst` has been fully initialized by `imp::fill_inner` + // since it returned `Ok`. + Ok(unsafe { res.assume_init() }) +} + +/// Default implementation of `inner_u32` on top of `getrandom::fill_uninit` +pub fn u32() -> Result { + unsafe { default_impl(true) } +} + +/// Default implementation of `inner_u64` on top of `getrandom::fill_uninit` +pub fn u64() -> Result { + unsafe { default_impl(true) } +} + +/// Default implementation of `insecure_fill_inner` on top of `getrandom::fill_uninit` +pub fn insecure_fill_uninit(dst: &mut [MaybeUninit]) -> Result<(), Error> { + crate::fill_uninit(dst).map(|_| ()) +} + +/// Default implementation of `inner_u32` on top of `getrandom::insecure_fill_uninit` +pub fn insecure_u32() -> Result { + unsafe { default_impl(false) } +} + +/// Default implementation of `inner_insecure_u64` on top of `getrandom::insecure_fill_uninit` +pub fn insecure_u64() -> Result { + unsafe { default_impl(false) } +} diff --git a/src/lib.rs b/src/lib.rs index 930b9452..ff6b20ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,13 @@ // Overwrite links to crate items with intra-crate links //! [`Error::UNEXPECTED`]: Error::UNEXPECTED +//! [`fill`]: fill //! [`fill_uninit`]: fill_uninit +//! [`u32`]: u32() +//! [`u64`]: u64() +//! [`insecure_fill`]: insecure_fill +//! [`insecure_fill_uninit`]: insecure_fill_uninit +//! [`insecure_u32`]: insecure_u32 +//! [`insecure_u64`]: insecure_u64 #![no_std] #![doc( @@ -34,6 +41,7 @@ extern crate cfg_if; use core::mem::MaybeUninit; mod backends; +mod default_impls; mod error; mod util; @@ -42,10 +50,10 @@ mod error_std_impls; pub use crate::error::Error; -/// Fill `dest` with random bytes from the system's preferred random number source. +/// Fill `dst` with random bytes from the system's entropy source. /// /// This function returns an error on any failure, including partial reads. We -/// make no guarantees regarding the contents of `dest` on error. If `dest` is +/// make no guarantees regarding the contents of `dst` on error. If `dst` is /// empty, `getrandom` immediately returns success, making no calls to the /// underlying operating system. /// @@ -56,7 +64,6 @@ pub use crate::error::Error; /// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html). /// /// # Examples -/// /// ``` /// # fn main() -> Result<(), getrandom::Error> { /// let mut buf = [0u8; 32]; @@ -64,63 +71,101 @@ pub use crate::error::Error; /// # Ok(()) } /// ``` #[inline] -pub fn fill(dest: &mut [u8]) -> Result<(), Error> { +pub fn fill(dst: &mut [u8]) -> Result<(), Error> { + // SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, + // and `fill_uninit` guarantees it will never de-initialize + // any part of `dst`. + fill_uninit(unsafe { util::slice_as_uninit_mut(dst) })?; + Ok(()) +} + +/// Fill `dst` with **potentially insecure** random bytes from the system's entropy source. +/// +/// See the ["insecure" functions][crate#insecure-functions] section for more information. +/// +/// # Examples +/// ``` +/// # fn main() -> Result<(), getrandom::Error> { +/// let mut buf = [0u8; 32]; +/// getrandom::insecure_fill(&mut buf)?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn insecure_fill(dst: &mut [u8]) -> Result<(), Error> { // SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, // and `fill_uninit` guarantees it will never de-initialize - // any part of `dest`. - fill_uninit(unsafe { util::slice_as_uninit_mut(dest) })?; + // any part of `dst`. + insecure_fill_uninit(unsafe { util::slice_as_uninit_mut(dst) })?; Ok(()) } -/// Fill potentially uninitialized buffer `dest` with random bytes from -/// the system's preferred random number source and return a mutable -/// reference to those bytes. +/// Fill potentially uninitialized buffer `dst` with random bytes from +/// the system's entropy source. /// /// On successful completion this function is guaranteed to return a slice -/// which points to the same memory as `dest` and has the same length. -/// In other words, it's safe to assume that `dest` is initialized after +/// which points to the same memory as `dst` and has the same length. +/// In other words, it's safe to assume that `dst` is initialized after /// this function has returned `Ok`. /// -/// No part of `dest` will ever be de-initialized at any point, regardless +/// No part of `dst` will ever be de-initialized at any point, regardless /// of what is returned. /// /// # Examples -/// /// ```ignore /// # // We ignore this test since `uninit_array` is unstable. /// #![feature(maybe_uninit_uninit_array)] /// # fn main() -> Result<(), getrandom::Error> { /// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>(); /// let buf: &mut [u8] = getrandom::fill_uninit(&mut buf)?; +/// assert_eq!(buf.len(), 1024); /// # Ok(()) } /// ``` #[inline] -pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<&mut [u8], Error> { - if !dest.is_empty() { - backends::fill_inner(dest)?; +pub fn fill_uninit(dst: &mut [MaybeUninit]) -> Result<&mut [u8], Error> { + if !dst.is_empty() { + backends::fill_uninit(dst)?; } - #[cfg(getrandom_sanitize)] - #[cfg(sanitize = "memory")] - extern "C" { - fn __msan_unpoison(a: *mut core::ffi::c_void, size: usize); - } + // SAFETY: `dst` has been fully initialized by `imp::fill_inner` since it returned `Ok` + Ok(unsafe { util::slice_assume_init_mut(dst) }) +} - // SAFETY: `dest` has been fully initialized by `imp::fill_inner` - // since it returned `Ok`. - Ok(unsafe { - #[cfg(getrandom_sanitize)] - #[cfg(sanitize = "memory")] - __msan_unpoison(dest.as_mut_ptr().cast(), dest.len()); +/// Fill potentially uninitialized buffer `dst` with **potentially insecure** random bytes +/// from the system's entropy source. +/// +/// On successful completion this function is guaranteed to return a slice +/// which points to the same memory as `dst` and has the same length. +/// In other words, it's safe to assume that `dst` is initialized after +/// this function has returned `Ok`. +/// +/// No part of `dst` will ever be de-initialized at any point, regardless +/// of what is returned. +/// +/// See the ["insecure" functions][crate#insecure-functions] section for more information. +/// +/// # Examples +/// ```ignore +/// # // We ignore this test since `uninit_array` is unstable. +/// #![feature(maybe_uninit_uninit_array)] +/// # fn main() -> Result<(), getrandom::Error> { +/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>(); +/// let buf: &mut [u8] = getrandom::insecure_fill_uninit(&mut buf)?; +/// assert_eq!(buf.len(), 1024); +/// # Ok(()) } +/// ``` +#[inline] +pub fn insecure_fill_uninit(dst: &mut [MaybeUninit]) -> Result<&mut [u8], Error> { + if !dst.is_empty() { + backends::insecure_fill_uninit(dst)?; + } - util::slice_assume_init_mut(dest) - }) + // SAFETY: `dst` has been fully initialized by `imp::fill_inner` since it returned `Ok` + Ok(unsafe { util::slice_assume_init_mut(dst) }) } -/// Get random `u32` from the system's preferred random number source. +/// Get random `u32` from the system's entropy source. /// /// # Examples -/// /// ``` /// # fn main() -> Result<(), getrandom::Error> { /// let rng_seed = getrandom::u32()?; @@ -128,13 +173,27 @@ pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<&mut [u8], Error> { /// ``` #[inline] pub fn u32() -> Result { - backends::inner_u32() + backends::u32() } -/// Get random `u64` from the system's preferred random number source. +/// Get **potentially insecure** random `u32` from the system's entropy source. +/// +/// See the ["insecure" functions][crate#insecure-functions] section for more information. /// /// # Examples +/// ``` +/// # fn main() -> Result<(), getrandom::Error> { +/// let rng_seed = getrandom::insecure_u32()?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn insecure_u32() -> Result { + backends::insecure_u32() +} + +/// Get random `u64` from the system's entropy source. /// +/// # Examples /// ``` /// # fn main() -> Result<(), getrandom::Error> { /// let rng_seed = getrandom::u64()?; @@ -142,5 +201,20 @@ pub fn u32() -> Result { /// ``` #[inline] pub fn u64() -> Result { - backends::inner_u64() + backends::u64() +} + +/// Get **potentially insecure** random `u64` from the system's entropy source. +/// +/// See the ["insecure" functions][crate#insecure-functions] section for more information. +/// +/// # Examples +/// ``` +/// # fn main() -> Result<(), getrandom::Error> { +/// let rng_seed = getrandom::insecure_u64()?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn insecure_u64() -> Result { + backends::insecure_u64() } diff --git a/src/util.rs b/src/util.rs index 4a55b0a5..2838afa9 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,14 +1,23 @@ #![allow(dead_code)] -use crate::Error; -use core::{mem::MaybeUninit, ptr, slice}; +use core::{mem::MaybeUninit, ptr}; /// Polyfill for `maybe_uninit_slice` feature's /// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have /// been initialized. #[inline(always)] #[allow(unused_unsafe)] // TODO(MSRV 1.65): Remove this. -pub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [T] { - let ptr = ptr_from_mut::<[MaybeUninit]>(slice) as *mut [T]; +pub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [u8] { + #[cfg(getrandom_sanitize)] + #[cfg(sanitize = "memory")] + extern "C" { + fn __msan_unpoison(a: *mut core::ffi::c_void, size: usize); + } + + #[cfg(getrandom_sanitize)] + #[cfg(sanitize = "memory")] + __msan_unpoison(slice.as_mut_ptr().cast(), slice.len()); + + let ptr = ptr_from_mut::<[MaybeUninit]>(slice) as *mut [u8]; // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. unsafe { &mut *ptr } } @@ -48,34 +57,6 @@ fn ptr_from_ref(r: &T) -> *const T { r } -/// Default implementation of `inner_u32` on top of `fill_uninit` -pub fn inner_u32() -> Result { - let mut res = MaybeUninit::::uninit(); - // SAFETY: the created slice has the same size as `res` - let dst = unsafe { - let p: *mut MaybeUninit = res.as_mut_ptr().cast(); - slice::from_raw_parts_mut(p, core::mem::size_of::()) - }; - crate::fill_uninit(dst)?; - // SAFETY: `dst` has been fully initialized by `imp::fill_inner` - // since it returned `Ok`. - Ok(unsafe { res.assume_init() }) -} - -/// Default implementation of `inner_u64` on top of `fill_uninit` -pub fn inner_u64() -> Result { - let mut res = MaybeUninit::::uninit(); - // SAFETY: the created slice has the same size as `res` - let dst = unsafe { - let p: *mut MaybeUninit = res.as_mut_ptr().cast(); - slice::from_raw_parts_mut(p, core::mem::size_of::()) - }; - crate::fill_uninit(dst)?; - // SAFETY: `dst` has been fully initialized by `imp::fill_inner` - // since it returned `Ok`. - Ok(unsafe { res.assume_init() }) -} - /// Truncates `u64` and returns the lower 32 bits as `u32` pub(crate) fn truncate(val: u64) -> u32 { u32::try_from(val & u64::from(u32::MAX)).expect("The higher 32 bits are masked")