std: switch to faster random sources on macOS and most BSDs
This commit is contained in:
parent
5c1c725724
commit
b9d47cfa9b
@ -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;
|
||||
|
@ -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]: <https://support.apple.com/en-gb/guide/security/seca0c73a75b/web>
|
||||
//! 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()) };
|
||||
|
34
library/std/src/sys/random/arc4random.rs
Normal file
34
library/std/src/sys/random/arc4random.rs
Normal file
@ -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()) }
|
||||
}
|
17
library/std/src/sys/random/getentropy.rs
Normal file
17
library/std/src/sys/random/getentropy.rs
Normal file
@ -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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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 | <https://man.openbsd.org/getentropy.2>
|
||||
//! FreeBSD | 12.0 | <https://man.freebsd.org/cgi/man.cgi?query=getentropy&manpath=FreeBSD+15.0-CURRENT>
|
||||
//! macOS | 10.12 | <https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/man/man2/getentropy.2#L4>
|
||||
//! NetBSD | 10.0 | <https://man.netbsd.org/getentropy.3>
|
||||
//! DragonFly | 6.1 | <https://man.dragonflybsd.org/?command=getentropy§ion=3>
|
||||
//! Illumos | ? | <https://www.illumos.org/man/3C/getentropy>
|
||||
//! Solaris | ? | <https://docs.oracle.com/cd/E88353_01/html/E37841/getentropy-2.html>
|
||||
//!
|
||||
//! 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]: <https://man.netbsd.org/getrandom.2>
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user