about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorjoboet <jonasboettiger@icloud.com>2024-08-17 16:54:01 +0200
committerjoboet <jonasboettiger@icloud.com>2024-09-23 10:36:16 +0200
commitb9d47cfa9b8f0805bfab2c254a02e598a906f102 (patch)
treee4c681065c6a064adba9ba0d784db8b59a8ad182 /library/std/src
parent5c1c72572479afe98734d5f78fa862abe662c41a (diff)
downloadrust-b9d47cfa9b8f0805bfab2c254a02e598a906f102.tar.gz
rust-b9d47cfa9b8f0805bfab2c254a02e598a906f102.zip
std: switch to faster random sources on macOS and most BSDs
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/random.rs31
-rw-r--r--library/std/src/sys/random/apple.rs21
-rw-r--r--library/std/src/sys/random/arc4random.rs34
-rw-r--r--library/std/src/sys/random/getentropy.rs17
-rw-r--r--library/std/src/sys/random/linux.rs2
-rw-r--r--library/std/src/sys/random/mod.rs31
-rw-r--r--library/std/src/sys/random/netbsd.rs19
-rw-r--r--library/std/src/sys/random/unix.rs33
-rw-r--r--library/std/src/sys/random/unix_legacy.rs4
9 files changed, 90 insertions, 102 deletions
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 @@ 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;
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]: <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()) };
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 @@ 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;
@@ -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(
@@ -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;
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     | <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&section=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");
-    }
-}
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;