Skip to content

Commit c3ca962

Browse files
committed
std: switch to faster random sources on macOS and most BSDs
1 parent 791244e commit c3ca962

File tree

8 files changed

+86
-98
lines changed

8 files changed

+86
-98
lines changed

library/std/src/random.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,25 @@ use crate::sys::random as sys;
4242
/// Platform | Source
4343
/// -----------------------|---------------------------------------------------------------
4444
/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
45-
/// Windows | [`ProcessPrng`]
46-
/// macOS and other UNIXes | [`getentropy`]
47-
/// other Apple platforms | `CCRandomGenerateBytes`
48-
/// Fuchsia | [`cprng_draw`]
45+
/// Windows | [`ProcessPrng`](https://learn.microsoft.com/en-us/windows/win32/seccng/processprng)
46+
/// Apple | `CCRandomGenerateBytes`
47+
/// DragonFly | [`arc4random_buf`](https://man.dragonflybsd.org/?command=arc4random&section=ANY)
48+
/// 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)
49+
/// Fuchsia | [`cprng_draw`](https://fuchsia.dev/reference/syscalls/cprng_draw)
50+
/// Haiku | `arc4random_buf`
51+
/// Illumos | [`arc4random_buf`](https://www.illumos.org/man/3C/arc4random)
52+
/// NetBSD | [`arc4random_buf`](https://man.netbsd.org/arc4random.3)
53+
/// OpenBSD | [`arc4random_buf`](https://man.openbsd.org/arc4random.3)
54+
/// Solaris | [`arc4random_buf`](https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html)
55+
/// Vita | `arc4random_buf`
4956
/// Hermit | `read_entropy`
5057
/// Horizon | `getrandom` shim
5158
/// Hurd, L4Re, QNX | `/dev/urandom`
52-
/// NetBSD before 10.0 | [`kern.arandom`]
5359
/// Redox | `/scheme/rand`
54-
/// SGX | [`rdrand`]
60+
/// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND)
5561
/// SOLID | `SOLID_RNG_SampleRandomBytes`
5662
/// TEEOS | `TEE_GenerateRandom`
57-
/// UEFI | [`EFI_RNG_PROTOCOL`]
63+
/// UEFI | [`EFI_RNG_PROTOCOL`](https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol)
5864
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
5965
/// WASI | `random_get`
6066
/// ZKVM | `sys_rand`
@@ -63,12 +69,6 @@ use crate::sys::random as sys;
6369
///
6470
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
6571
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
66-
/// [`ProcessPrng`]: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
67-
/// [`getentropy`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html
68-
/// [`cprng_draw`]: https://fuchsia.dev/reference/syscalls/cprng_draw
69-
/// [`kern.arandom`]: https://man.netbsd.org/rnd.4
70-
/// [`rdrand`]: https://en.wikipedia.org/wiki/RDRAND
71-
/// [`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol
7272
#[derive(Default, Debug, Clone, Copy)]
7373
#[unstable(feature = "random", issue = "none")]
7474
pub struct DefaultRandomSource;

library/std/src/sys/random/apple.rs

Lines changed: 7 additions & 14 deletions
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], _best_effort: bool) {
2013
let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) };
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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, and that implementation
9+
//! is broken).
10+
11+
#[cfg(not(any(
12+
target_os = "haiku",
13+
target_os = "illumos",
14+
target_os = "solaris",
15+
target_os = "vita",
16+
)))]
17+
use libc::arc4random_buf;
18+
19+
// FIXME: move these to libc (except Haiku, that one needs to link to libbsd.so).
20+
#[cfg(any(
21+
target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h
22+
target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random
23+
target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html
24+
target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74
25+
))]
26+
#[cfg_attr(target_os = "haiku", link(name = "bsd"))]
27+
extern "C" {
28+
fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t);
29+
}
30+
31+
pub fn fill_bytes(bytes: &mut [u8], _best_effort: bool) {
32+
unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) }
33+
}
Lines changed: 17 additions & 0 deletions
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], _best_effort: bool) {
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+
}

library/std/src/sys/random/mod.rs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,33 @@ 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 = "fuchsia")] {
2829
mod fuchsia;
2930
pub use fuchsia::fill_bytes;
3031
} else if #[cfg(target_os = "hermit")] {
3132
mod hermit;
3233
pub use hermit::fill_bytes;
3334
} else if #[cfg(target_os = "horizon")] {
34-
// FIXME: add getentropy to shim-3ds
35+
// FIXME: add arc4random_buf to shim-3ds
3536
mod horizon;
3637
pub use horizon::fill_bytes;
3738
} else if #[cfg(any(
@@ -41,10 +42,6 @@ cfg_if::cfg_if! {
4142
))] {
4243
mod unix_legacy;
4344
pub use unix_legacy::fill_bytes;
44-
} else if #[cfg(all(target_os = "netbsd", not(netbsd10)))] {
45-
// FIXME: remove once NetBSD 10 is the minimum
46-
mod netbsd;
47-
pub use netbsd::fill_bytes;
4845
} else if #[cfg(target_os = "redox")] {
4946
mod redox;
5047
pub use redox::fill_bytes;

library/std/src/sys/random/netbsd.rs

Lines changed: 0 additions & 19 deletions
This file was deleted.

library/std/src/sys/random/unix.rs

Lines changed: 0 additions & 33 deletions
This file was deleted.

library/std/src/sys/random/unix_legacy.rs

Lines changed: 2 additions & 2 deletions
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)