Skip to content

Commit

Permalink
std: switch to faster random sources on macOS and most BSDs
Browse files Browse the repository at this point in the history
  • Loading branch information
joboet committed Sep 23, 2024
1 parent a095a76 commit 849258c
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 102 deletions.
31 changes: 15 additions & 16 deletions std/src/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,34 @@ use crate::sys::random as sys;
/// 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&section=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;
Expand Down
21 changes: 7 additions & 14 deletions std/src/sys/random/apple.rs
Original file line number Diff line number Diff line change
@@ -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()) };
Expand Down
34 changes: 34 additions & 0 deletions std/src/sys/random/arc4random.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 17 additions & 0 deletions std/src/sys/random/getentropy.rs
Original file line number Diff line number Diff line change
@@ -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");
}
}
2 changes: 1 addition & 1 deletion std/src/sys/random/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
31 changes: 14 additions & 17 deletions std/src/sys/random/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,25 @@ cfg_if::cfg_if! {
} else if #[cfg(target_os = "windows")] {
mod windows;
pub use windows::fill_bytes;
} else if #[cfg(target_vendor = "apple")] {
mod apple;
pub use apple::fill_bytes;
// Others, in alphabetical ordering.
} else if #[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "macos",
all(target_os = "netbsd", netbsd10),
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
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")))] {
mod apple;
pub use apple::fill_bytes;
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;
Expand All @@ -34,7 +35,7 @@ cfg_if::cfg_if! {
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(
Expand All @@ -44,10 +45,6 @@ cfg_if::cfg_if! {
))] {
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;
Expand Down
19 changes: 0 additions & 19 deletions std/src/sys/random/netbsd.rs

This file was deleted.

33 changes: 0 additions & 33 deletions std/src/sys/random/unix.rs

This file was deleted.

4 changes: 2 additions & 2 deletions std/src/sys/random/unix_legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 849258c

Please sign in to comment.