Skip to content

Commit 849258c

Browse files
committed
std: switch to faster random sources on macOS and most BSDs
1 parent a095a76 commit 849258c

File tree

9 files changed

+90
-102
lines changed

9 files changed

+90
-102
lines changed

std/src/random.rs

+15-16
Original file line numberDiff line numberDiff line change
@@ -24,35 +24,34 @@ use crate::sys::random as sys;
2424
/// Platform | Source
2525
/// -----------------------|---------------------------------------------------------------
2626
/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
27-
/// Windows | [`ProcessPrng`]
28-
/// macOS and other UNIXes | [`getentropy`]
29-
/// other Apple platforms | `CCRandomGenerateBytes`
30-
/// ESP-IDF | [`esp_fill_random`]
31-
/// Fuchsia | [`cprng_draw`]
27+
/// Windows | [`ProcessPrng`](https://learn.microsoft.com/en-us/windows/win32/seccng/processprng)
28+
/// Apple | `CCRandomGenerateBytes`
29+
/// DragonFly | [`arc4random_buf`](https://man.dragonflybsd.org/?command=arc4random&section=ANY)
30+
/// ESP-IDF | [`esp_fill_random`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t)
31+
/// 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)
32+
/// Fuchsia | [`cprng_draw`](https://fuchsia.dev/reference/syscalls/cprng_draw)
33+
/// Haiku | `arc4random_buf`
34+
/// Illumos | [`arc4random_buf`](https://www.illumos.org/man/3C/arc4random)
35+
/// NetBSD | [`arc4random_buf`](https://man.netbsd.org/arc4random.3)
36+
/// OpenBSD | [`arc4random_buf`](https://man.openbsd.org/arc4random.3)
37+
/// Solaris | [`arc4random_buf`](https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html)
38+
/// Vita | `arc4random_buf`
3239
/// Hermit | `read_entropy`
3340
/// Horizon | `getrandom` shim
3441
/// Hurd, L4Re, QNX | `/dev/urandom`
35-
/// NetBSD before 10.0 | [`kern.arandom`]
3642
/// Redox | `/scheme/rand`
37-
/// SGX | [`rdrand`]
43+
/// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND)
3844
/// SOLID | `SOLID_RNG_SampleRandomBytes`
3945
/// TEEOS | `TEE_GenerateRandom`
40-
/// UEFI | [`EFI_RNG_PROTOCOL`]
46+
/// UEFI | [`EFI_RNG_PROTOCOL`](https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol)
4147
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
42-
/// WASI | `random_get`
48+
/// WASI | [`random_get`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno)
4349
/// ZKVM | `sys_rand`
4450
///
4551
/// **Disclaimer:** The sources used might change over time.
4652
///
4753
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
4854
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
49-
/// [`ProcessPrng`]: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
50-
/// [`getentropy`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html
51-
/// [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t
52-
/// [`cprng_draw`]: https://fuchsia.dev/reference/syscalls/cprng_draw
53-
/// [`kern.arandom`]: https://man.netbsd.org/rnd.4
54-
/// [`rdrand`]: https://en.wikipedia.org/wiki/RDRAND
55-
/// [`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol
5655
#[derive(Default, Debug, Clone, Copy)]
5756
#[unstable(feature = "random", issue = "none")]
5857
pub struct DefaultRandomSource;

std/src/sys/random/apple.rs

+7-14
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
1-
//! Random data on non-macOS Apple platforms.
1+
//! Random data on Apple platforms.
22
//!
3-
//! Apple recommends the usage of `getentropy` in their security documentation[^1]
4-
//! and mark it as being available in iOS 10.0, but we cannot use it on non-macOS
5-
//! platforms as Apple in their *infinite wisdom* decided to consider this API
6-
//! private, meaning its use will lead to App Store rejections (see #102643).
7-
//!
8-
//! Thus, we need to do the next best thing:
9-
//!
10-
//! Both `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply call into
11-
//! `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` manages a
12-
//! CSPRNG which is seeded from the kernel's CSPRNG and which runs on its own
13-
//! thread accessed via GCD (this is so wasteful...). Both are available on
14-
//! iOS, but we use `CCRandomGenerateBytes` because it is accessible via
3+
//! `CCRandomGenerateBytes` calls into `CCRandomCopyBytes` with `kCCRandomDefault`.
4+
//! `CCRandomCopyBytes` manages a CSPRNG which is seeded from the kernel's CSPRNG.
5+
//! We use `CCRandomGenerateBytes` instead of `SecCopyBytes` because it is accessible via
156
//! `libSystem` (libc) while the other needs to link to `Security.framework`.
167
//!
17-
//! [^1]: <https://support.apple.com/en-gb/guide/security/seca0c73a75b/web>
8+
//! Note that technically, `arc4random_buf` is available as well, but that calls
9+
//! into the same system service anyway, and `CCRandomGenerateBytes` has been
10+
//! proven to be App Store-compatible.
1811
1912
pub fn fill_bytes(bytes: &mut [u8]) {
2013
let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) };

std/src/sys/random/arc4random.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! Random data generation with `arc4random_buf`.
2+
//!
3+
//! Contrary to its name, `arc4random` doesn't actually use the horribly-broken
4+
//! RC4 cypher anymore, at least not on modern systems, but rather something
5+
//! like ChaCha20 with continual reseeding from the OS. That makes it an ideal
6+
//! source of large quantities of cryptographically secure data, which is exactly
7+
//! what we need for `DefaultRandomSource`. Unfortunately, it's not available
8+
//! on all UNIX systems, most notably Linux (until recently, but it's just a
9+
//! wrapper for `getrandom`. Since we need to hook into `getrandom` directly
10+
//! for `HashMap` keys anyway, we just keep our version).
11+
12+
#[cfg(not(any(
13+
target_os = "haiku",
14+
target_os = "illumos",
15+
target_os = "solaris",
16+
target_os = "vita",
17+
)))]
18+
use libc::arc4random_buf;
19+
20+
// FIXME: move these to libc (except Haiku, that one needs to link to libbsd.so).
21+
#[cfg(any(
22+
target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h
23+
target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random
24+
target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html
25+
target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74
26+
))]
27+
#[cfg_attr(target_os = "haiku", link(name = "bsd"))]
28+
extern "C" {
29+
fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t);
30+
}
31+
32+
pub fn fill_bytes(bytes: &mut [u8]) {
33+
unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) }
34+
}

std/src/sys/random/getentropy.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//! Random data generation through `getentropy`.
2+
//!
3+
//! Since issue 8 (2024), the POSIX specification mandates the existence of the
4+
//! `getentropy` function, which fills a slice of up to `GETENTROPY_MAX` bytes
5+
//! (256 on all known platforms) with random data. Unfortunately, it's only
6+
//! meant to be used to seed other CPRNGs, which we don't have, so we only use
7+
//! it where `arc4random_buf` and friends aren't available or secure (currently
8+
//! that's only the case on Emscripten).
9+
10+
pub fn fill_bytes(bytes: &mut [u8]) {
11+
// GETENTROPY_MAX isn't defined yet on most platforms, but it's mandated
12+
// to be at least 256, so just use that as limit.
13+
for chunk in bytes.chunks_mut(256) {
14+
let r = unsafe { libc::getentropy(chunk.as_mut_ptr().cast(), chunk.len()) };
15+
assert_ne!(r, -1, "failed to generate random data");
16+
}
17+
}

std/src/sys/random/linux.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@
6363
use crate::fs::File;
6464
use crate::io::Read;
6565
use crate::os::fd::AsRawFd;
66+
use crate::sync::OnceLock;
6667
use crate::sync::atomic::AtomicBool;
6768
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
68-
use crate::sync::OnceLock;
6969
use crate::sys::pal::os::errno;
7070
use crate::sys::pal::weak::syscall;
7171

std/src/sys/random/mod.rs

+14-17
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,25 @@ cfg_if::cfg_if! {
66
} else if #[cfg(target_os = "windows")] {
77
mod windows;
88
pub use windows::fill_bytes;
9+
} else if #[cfg(target_vendor = "apple")] {
10+
mod apple;
11+
pub use apple::fill_bytes;
12+
// Others, in alphabetical ordering.
913
} else if #[cfg(any(
10-
target_os = "openbsd",
11-
target_os = "freebsd",
12-
target_os = "macos",
13-
all(target_os = "netbsd", netbsd10),
1414
target_os = "dragonfly",
15+
target_os = "freebsd",
16+
target_os = "haiku",
1517
target_os = "illumos",
18+
target_os = "netbsd",
19+
target_os = "openbsd",
1620
target_os = "solaris",
17-
target_os = "emscripten",
1821
target_os = "vita",
19-
target_os = "haiku",
2022
))] {
21-
mod unix;
22-
pub use unix::fill_bytes;
23-
// Others, in alphabetical ordering.
24-
} else if #[cfg(all(target_vendor = "apple", not(target_os = "macos")))] {
25-
mod apple;
26-
pub use apple::fill_bytes;
23+
mod arc4random;
24+
pub use arc4random::fill_bytes;
25+
} else if #[cfg(target_os = "emscripten")] {
26+
mod getentropy;
27+
pub use getentropy::fill_bytes;
2728
} else if #[cfg(target_os = "espidf")] {
2829
mod espidf;
2930
pub use espidf::fill_bytes;
@@ -34,7 +35,7 @@ cfg_if::cfg_if! {
3435
mod hermit;
3536
pub use hermit::fill_bytes;
3637
} else if #[cfg(target_os = "horizon")] {
37-
// FIXME: add getentropy to shim-3ds
38+
// FIXME: add arc4random_buf to shim-3ds
3839
mod horizon;
3940
pub use horizon::fill_bytes;
4041
} else if #[cfg(any(
@@ -44,10 +45,6 @@ cfg_if::cfg_if! {
4445
))] {
4546
mod unix_legacy;
4647
pub use unix_legacy::fill_bytes;
47-
} else if #[cfg(all(target_os = "netbsd", not(netbsd10)))] {
48-
// FIXME: remove once NetBSD 10 is the minimum
49-
mod netbsd;
50-
pub use netbsd::fill_bytes;
5148
} else if #[cfg(target_os = "redox")] {
5249
mod redox;
5350
pub use redox::fill_bytes;

std/src/sys/random/netbsd.rs

-19
This file was deleted.

std/src/sys/random/unix.rs

-33
This file was deleted.

std/src/sys/random/unix_legacy.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
//! Before `getentropy` was standardized in 2024, UNIX didn't have a standardized
44
//! way of getting random data, so systems just followed the precedent set by
55
//! Linux and exposed random devices at `/dev/random` and `/dev/urandom`. Thus,
6-
//! for the few systems that do not support `getentropy` yet, we just read from
7-
//! the file.
6+
//! for the few systems that support neither `arc4random_buf` nor `getentropy`
7+
//! yet, we just read from the file.
88
99
use crate::fs::File;
1010
use crate::io::Read;

0 commit comments

Comments
 (0)