diff --git a/library/std/src/random.rs b/library/std/src/random.rs index 24d1dac4dae..48b55a55b2f 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -24,35 +24,34 @@ /// Platform | Source /// -----------------------|--------------------------------------------------------------- /// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random` -/// Windows | [`ProcessPrng`] -/// macOS and other UNIXes | [`getentropy`] -/// other Apple platforms | `CCRandomGenerateBytes` -/// ESP-IDF | [`esp_fill_random`] -/// Fuchsia | [`cprng_draw`] +/// Windows | [`ProcessPrng`](https://learn.microsoft.com/en-us/windows/win32/seccng/processprng) +/// Apple | `CCRandomGenerateBytes` +/// DragonFly | [`arc4random_buf`](https://man.dragonflybsd.org/?command=arc4random§ion=ANY) +/// ESP-IDF | [`esp_fill_random`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t) +/// FreeBSD | [`arc4random_buf`](https://man.freebsd.org/cgi/man.cgi?query=arc4random&apropos=0&sektion=0&manpath=FreeBSD+15.0-CURRENT&arch=default&format=html) +/// Fuchsia | [`cprng_draw`](https://fuchsia.dev/reference/syscalls/cprng_draw) +/// Haiku | `arc4random_buf` +/// Illumos | [`arc4random_buf`](https://www.illumos.org/man/3C/arc4random) +/// NetBSD | [`arc4random_buf`](https://man.netbsd.org/arc4random.3) +/// OpenBSD | [`arc4random_buf`](https://man.openbsd.org/arc4random.3) +/// Solaris | [`arc4random_buf`](https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html) +/// Vita | `arc4random_buf` /// Hermit | `read_entropy` /// Horizon | `getrandom` shim /// Hurd, L4Re, QNX | `/dev/urandom` -/// NetBSD before 10.0 | [`kern.arandom`] /// Redox | `/scheme/rand` -/// SGX | [`rdrand`] +/// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND) /// SOLID | `SOLID_RNG_SampleRandomBytes` /// TEEOS | `TEE_GenerateRandom` -/// UEFI | [`EFI_RNG_PROTOCOL`] +/// UEFI | [`EFI_RNG_PROTOCOL`](https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol) /// VxWorks | `randABytes` after waiting for `randSecure` to become ready -/// WASI | `random_get` +/// WASI | [`random_get`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno) /// ZKVM | `sys_rand` /// /// **Disclaimer:** The sources used might change over time. /// /// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html /// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html -/// [`ProcessPrng`]: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng -/// [`getentropy`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html -/// [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t -/// [`cprng_draw`]: https://fuchsia.dev/reference/syscalls/cprng_draw -/// [`kern.arandom`]: https://man.netbsd.org/rnd.4 -/// [`rdrand`]: https://en.wikipedia.org/wiki/RDRAND -/// [`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol #[derive(Default, Debug, Clone, Copy)] #[unstable(feature = "random", issue = "none")] pub struct DefaultRandomSource; diff --git a/library/std/src/sys/random/apple.rs b/library/std/src/sys/random/apple.rs index 09b6d0d51ab..417198c9d85 100644 --- a/library/std/src/sys/random/apple.rs +++ b/library/std/src/sys/random/apple.rs @@ -1,20 +1,13 @@ -//! Random data on non-macOS Apple platforms. +//! Random data on Apple platforms. //! -//! Apple recommends the usage of `getentropy` in their security documentation[^1] -//! and mark it as being available in iOS 10.0, but we cannot use it on non-macOS -//! platforms as Apple in their *infinite wisdom* decided to consider this API -//! private, meaning its use will lead to App Store rejections (see #102643). -//! -//! Thus, we need to do the next best thing: -//! -//! Both `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply call into -//! `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` manages a -//! CSPRNG which is seeded from the kernel's CSPRNG and which runs on its own -//! thread accessed via GCD (this is so wasteful...). Both are available on -//! iOS, but we use `CCRandomGenerateBytes` because it is accessible via +//! `CCRandomGenerateBytes` calls into `CCRandomCopyBytes` with `kCCRandomDefault`. +//! `CCRandomCopyBytes` manages a CSPRNG which is seeded from the kernel's CSPRNG. +//! We use `CCRandomGenerateBytes` instead of `SecCopyBytes` because it is accessible via //! `libSystem` (libc) while the other needs to link to `Security.framework`. //! -//! [^1]: +//! Note that technically, `arc4random_buf` is available as well, but that calls +//! into the same system service anyway, and `CCRandomGenerateBytes` has been +//! proven to be App Store-compatible. pub fn fill_bytes(bytes: &mut [u8]) { let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) }; diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs new file mode 100644 index 00000000000..32467e9ebaa --- /dev/null +++ b/library/std/src/sys/random/arc4random.rs @@ -0,0 +1,34 @@ +//! Random data generation with `arc4random_buf`. +//! +//! Contrary to its name, `arc4random` doesn't actually use the horribly-broken +//! RC4 cypher anymore, at least not on modern systems, but rather something +//! like ChaCha20 with continual reseeding from the OS. That makes it an ideal +//! source of large quantities of cryptographically secure data, which is exactly +//! what we need for `DefaultRandomSource`. Unfortunately, it's not available +//! on all UNIX systems, most notably Linux (until recently, but it's just a +//! wrapper for `getrandom`. Since we need to hook into `getrandom` directly +//! for `HashMap` keys anyway, we just keep our version). + +#[cfg(not(any( + target_os = "haiku", + target_os = "illumos", + target_os = "solaris", + target_os = "vita", +)))] +use libc::arc4random_buf; + +// FIXME: move these to libc (except Haiku, that one needs to link to libbsd.so). +#[cfg(any( + target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h + target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random + target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html + target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74 +))] +#[cfg_attr(target_os = "haiku", link(name = "bsd"))] +extern "C" { + fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) } +} diff --git a/library/std/src/sys/random/getentropy.rs b/library/std/src/sys/random/getentropy.rs new file mode 100644 index 00000000000..110ac134c1f --- /dev/null +++ b/library/std/src/sys/random/getentropy.rs @@ -0,0 +1,17 @@ +//! Random data generation through `getentropy`. +//! +//! Since issue 8 (2024), the POSIX specification mandates the existence of the +//! `getentropy` function, which fills a slice of up to `GETENTROPY_MAX` bytes +//! (256 on all known platforms) with random data. Unfortunately, it's only +//! meant to be used to seed other CPRNGs, which we don't have, so we only use +//! it where `arc4random_buf` and friends aren't available or secure (currently +//! that's only the case on Emscripten). + +pub fn fill_bytes(bytes: &mut [u8]) { + // GETENTROPY_MAX isn't defined yet on most platforms, but it's mandated + // to be at least 256, so just use that as limit. + for chunk in bytes.chunks_mut(256) { + let r = unsafe { libc::getentropy(chunk.as_mut_ptr().cast(), chunk.len()) }; + assert_ne!(r, -1, "failed to generate random data"); + } +} diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs index 4ede0af6494..073fdc45e61 100644 --- a/library/std/src/sys/random/linux.rs +++ b/library/std/src/sys/random/linux.rs @@ -63,9 +63,9 @@ use crate::fs::File; use crate::io::Read; use crate::os::fd::AsRawFd; +use crate::sync::OnceLock; use crate::sync::atomic::AtomicBool; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::OnceLock; use crate::sys::pal::os::errno; use crate::sys::pal::weak::syscall; diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index d39e78bb7b3..16fb8c64c9b 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -6,24 +6,25 @@ } else if #[cfg(target_os = "windows")] { mod windows; pub use windows::fill_bytes; - } else if #[cfg(any( - target_os = "openbsd", - target_os = "freebsd", - target_os = "macos", - all(target_os = "netbsd", netbsd10), - target_os = "dragonfly", - target_os = "illumos", - target_os = "solaris", - target_os = "emscripten", - target_os = "vita", - target_os = "haiku", - ))] { - mod unix; - pub use unix::fill_bytes; - // Others, in alphabetical ordering. - } else if #[cfg(all(target_vendor = "apple", not(target_os = "macos")))] { + } else if #[cfg(target_vendor = "apple")] { mod apple; pub use apple::fill_bytes; + // Others, in alphabetical ordering. + } else if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "haiku", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "vita", + ))] { + mod arc4random; + pub use arc4random::fill_bytes; + } else if #[cfg(target_os = "emscripten")] { + mod getentropy; + pub use getentropy::fill_bytes; } else if #[cfg(target_os = "espidf")] { mod espidf; pub use espidf::fill_bytes; @@ -34,7 +35,7 @@ mod hermit; pub use hermit::fill_bytes; } else if #[cfg(target_os = "horizon")] { - // FIXME: add getentropy to shim-3ds + // FIXME: add arc4random_buf to shim-3ds mod horizon; pub use horizon::fill_bytes; } else if #[cfg(any( @@ -44,10 +45,6 @@ ))] { mod unix_legacy; pub use unix_legacy::fill_bytes; - } else if #[cfg(all(target_os = "netbsd", not(netbsd10)))] { - // FIXME: remove once NetBSD 10 is the minimum - mod netbsd; - pub use netbsd::fill_bytes; } else if #[cfg(target_os = "redox")] { mod redox; pub use redox::fill_bytes; diff --git a/library/std/src/sys/random/netbsd.rs b/library/std/src/sys/random/netbsd.rs deleted file mode 100644 index 2c5d9c72f30..00000000000 --- a/library/std/src/sys/random/netbsd.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::ptr; - -pub fn fill_bytes(bytes: &mut [u8]) { - let mib = [libc::CTL_KERN, libc::KERN_ARND]; - for chunk in bytes.chunks_mut(256) { - let mut len = chunk.len(); - let ret = unsafe { - libc::sysctl( - mib.as_ptr(), - mib.len() as libc::c_uint, - chunk.as_mut_ptr().cast(), - &mut len, - ptr::null(), - 0, - ) - }; - assert!(ret != -1 && len == chunk.len(), "failed to generate random data"); - } -} diff --git a/library/std/src/sys/random/unix.rs b/library/std/src/sys/random/unix.rs deleted file mode 100644 index a56847e5541..00000000000 --- a/library/std/src/sys/random/unix.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Random data generation through `getentropy`. -//! -//! Since issue 8 (2024), the POSIX specification mandates the existence of the -//! `getentropy` function, which fills a slice of up to `GETENTROPY_MAX` bytes -//! (256 on all known platforms) with random data. Luckily, this function has -//! already been available on quite some BSDs before that, having appeared with -//! OpenBSD 5.7 and spread from there: -//! -//! platform | version | man-page -//! -----------|---------|---------- -//! OpenBSD | 5.6 | -//! FreeBSD | 12.0 | -//! macOS | 10.12 | -//! NetBSD | 10.0 | -//! DragonFly | 6.1 | -//! Illumos | ? | -//! Solaris | ? | -//! -//! As it is standardized we use it whereever possible, even when `getrandom` is -//! also available. NetBSD even warns that "Applications should avoid getrandom -//! and use getentropy(2) instead; getrandom may be removed from a later -//! release."[^1]. -//! -//! [^1]: - -pub fn fill_bytes(bytes: &mut [u8]) { - // GETENTROPY_MAX isn't defined yet on most platforms, but it's mandated - // to be at least 256, so just use that as limit. - for chunk in bytes.chunks_mut(256) { - let r = unsafe { libc::getentropy(chunk.as_mut_ptr().cast(), chunk.len()) }; - assert_ne!(r, -1, "failed to generate random data"); - } -} diff --git a/library/std/src/sys/random/unix_legacy.rs b/library/std/src/sys/random/unix_legacy.rs index dd6be43c173..587068b0d66 100644 --- a/library/std/src/sys/random/unix_legacy.rs +++ b/library/std/src/sys/random/unix_legacy.rs @@ -3,8 +3,8 @@ //! Before `getentropy` was standardized in 2024, UNIX didn't have a standardized //! way of getting random data, so systems just followed the precedent set by //! Linux and exposed random devices at `/dev/random` and `/dev/urandom`. Thus, -//! for the few systems that do not support `getentropy` yet, we just read from -//! the file. +//! for the few systems that support neither `arc4random_buf` nor `getentropy` +//! yet, we just read from the file. use crate::fs::File; use crate::io::Read;