Skip to content

Commit 1d36931

Browse files
Rollup merge of #129201 - joboet:random_faster_sources, r=joshtriplett
std: implement the `random` feature (alternative version) Implements the ACP rust-lang/libs-team#393. This PR is an alternative version of #129120 that replaces `getentropy` with `CCRandomGenerateBytes` (on macOS) and `arc4random_buf` (other BSDs), since that function is not suited for generating large amounts of data and should only be used to seed other CPRNGs. `CCRandomGenerateBytes`/`arc4random_buf` on the other hand is (on modern platforms) just as secure and uses its own, very strong CPRNG (ChaCha20 on the BSDs, AES on macOS) periodically seeded with `getentropy`.
2 parents c22a421 + 3ff09a0 commit 1d36931

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+796
-464
lines changed

library/core/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ pub mod panicking;
394394
#[unstable(feature = "core_pattern_types", issue = "123646")]
395395
pub mod pat;
396396
pub mod pin;
397+
#[unstable(feature = "random", issue = "130703")]
398+
pub mod random;
397399
#[unstable(feature = "new_range_api", issue = "125687")]
398400
pub mod range;
399401
pub mod result;

library/core/src/random.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! Random value generation.
2+
//!
3+
//! The [`Random`] trait allows generating a random value for a type using a
4+
//! given [`RandomSource`].
5+
6+
/// A source of randomness.
7+
#[unstable(feature = "random", issue = "130703")]
8+
pub trait RandomSource {
9+
/// Fills `bytes` with random bytes.
10+
fn fill_bytes(&mut self, bytes: &mut [u8]);
11+
}
12+
13+
/// A trait for getting a random value for a type.
14+
///
15+
/// **Warning:** Be careful when manipulating random values! The
16+
/// [`random`](Random::random) method on integers samples them with a uniform
17+
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
18+
/// modulo operations, some of the resulting values can become more likely than
19+
/// others. Use audited crates when in doubt.
20+
#[unstable(feature = "random", issue = "130703")]
21+
pub trait Random: Sized {
22+
/// Generates a random value.
23+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self;
24+
}
25+
26+
impl Random for bool {
27+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
28+
u8::random(source) & 1 == 1
29+
}
30+
}
31+
32+
macro_rules! impl_primitive {
33+
($t:ty) => {
34+
impl Random for $t {
35+
/// Generates a random value.
36+
///
37+
/// **Warning:** Be careful when manipulating the resulting value! This
38+
/// method samples according to a uniform distribution, so a value of 1 is
39+
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
40+
/// values can become more likely than others. Use audited crates when in
41+
/// doubt.
42+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
43+
let mut bytes = (0 as Self).to_ne_bytes();
44+
source.fill_bytes(&mut bytes);
45+
Self::from_ne_bytes(bytes)
46+
}
47+
}
48+
};
49+
}
50+
51+
impl_primitive!(u8);
52+
impl_primitive!(i8);
53+
impl_primitive!(u16);
54+
impl_primitive!(i16);
55+
impl_primitive!(u32);
56+
impl_primitive!(i32);
57+
impl_primitive!(u64);
58+
impl_primitive!(i64);
59+
impl_primitive!(u128);
60+
impl_primitive!(i128);
61+
impl_primitive!(usize);
62+
impl_primitive!(isize);

library/std/src/hash/random.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
#[allow(deprecated)]
1111
use super::{BuildHasher, Hasher, SipHasher13};
1212
use crate::cell::Cell;
13-
use crate::{fmt, sys};
13+
use crate::fmt;
14+
use crate::sys::random::hashmap_random_keys;
1415

1516
/// `RandomState` is the default state for [`HashMap`] types.
1617
///
@@ -65,7 +66,7 @@ impl RandomState {
6566
// increment one of the seeds on every RandomState creation, giving
6667
// every corresponding HashMap a different iteration order.
6768
thread_local!(static KEYS: Cell<(u64, u64)> = {
68-
Cell::new(sys::hashmap_random_keys())
69+
Cell::new(hashmap_random_keys())
6970
});
7071

7172
KEYS.with(|keys| {

library/std/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@
318318
//
319319
// Library features (core):
320320
// tidy-alphabetical-start
321+
#![feature(array_chunks)]
321322
#![feature(c_str_module)]
322323
#![feature(char_internals)]
323324
#![feature(clone_to_uninit)]
@@ -348,6 +349,7 @@
348349
#![feature(prelude_2024)]
349350
#![feature(ptr_as_uninit)]
350351
#![feature(ptr_mask)]
352+
#![feature(random)]
351353
#![feature(slice_internals)]
352354
#![feature(slice_ptr_get)]
353355
#![feature(slice_range)]
@@ -595,6 +597,8 @@ pub mod path;
595597
#[unstable(feature = "anonymous_pipe", issue = "127154")]
596598
pub mod pipe;
597599
pub mod process;
600+
#[unstable(feature = "random", issue = "130703")]
601+
pub mod random;
598602
pub mod sync;
599603
pub mod time;
600604

library/std/src/random.rs

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//! Random value generation.
2+
//!
3+
//! The [`Random`] trait allows generating a random value for a type using a
4+
//! given [`RandomSource`].
5+
6+
#[unstable(feature = "random", issue = "130703")]
7+
pub use core::random::*;
8+
9+
use crate::sys::random as sys;
10+
11+
/// The default random source.
12+
///
13+
/// This asks the system for random data suitable for cryptographic purposes
14+
/// such as key generation. If security is a concern, consult the platform
15+
/// documentation below for the specific guarantees your target provides.
16+
///
17+
/// The high quality of randomness provided by this source means it can be quite
18+
/// slow on some targets. If you need a large quantity of random numbers and
19+
/// security is not a concern, consider using an alternative random number
20+
/// generator (potentially seeded from this one).
21+
///
22+
/// # Underlying sources
23+
///
24+
/// Platform | Source
25+
/// -----------------------|---------------------------------------------------------------
26+
/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
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)
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)
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`
39+
/// Hermit | `read_entropy`
40+
/// Horizon | `getrandom` shim
41+
/// Hurd, L4Re, QNX | `/dev/urandom`
42+
/// Redox | `/scheme/rand`
43+
/// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND)
44+
/// SOLID | `SOLID_RNG_SampleRandomBytes`
45+
/// TEEOS | `TEE_GenerateRandom`
46+
/// UEFI | [`EFI_RNG_PROTOCOL`](https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol)
47+
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
48+
/// WASI | [`random_get`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno)
49+
/// ZKVM | `sys_rand`
50+
///
51+
/// Note that the sources used might change over time.
52+
///
53+
/// Consult the documentation for the underlying operations on your supported
54+
/// targets to determine whether they provide any particular desired properties,
55+
/// such as support for reseeding on VM fork operations.
56+
///
57+
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
58+
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
59+
#[derive(Default, Debug, Clone, Copy)]
60+
#[unstable(feature = "random", issue = "130703")]
61+
pub struct DefaultRandomSource;
62+
63+
#[unstable(feature = "random", issue = "130703")]
64+
impl RandomSource for DefaultRandomSource {
65+
fn fill_bytes(&mut self, bytes: &mut [u8]) {
66+
sys::fill_bytes(bytes)
67+
}
68+
}
69+
70+
/// Generates a random value with the default random source.
71+
///
72+
/// This is a convenience function for `T::random(&mut DefaultRandomSource)` and
73+
/// will sample according to the same distribution as the underlying [`Random`]
74+
/// trait implementation.
75+
///
76+
/// **Warning:** Be careful when manipulating random values! The
77+
/// [`random`](Random::random) method on integers samples them with a uniform
78+
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
79+
/// modulo operations, some of the resulting values can become more likely than
80+
/// others. Use audited crates when in doubt.
81+
///
82+
/// # Examples
83+
///
84+
/// Generating a [version 4/variant 1 UUID] represented as text:
85+
/// ```
86+
/// #![feature(random)]
87+
///
88+
/// use std::random::random;
89+
///
90+
/// let bits: u128 = random();
91+
/// let g1 = (bits >> 96) as u32;
92+
/// let g2 = (bits >> 80) as u16;
93+
/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16;
94+
/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16;
95+
/// let g5 = (bits & 0xffffffffffff) as u64;
96+
/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}");
97+
/// println!("{uuid}");
98+
/// ```
99+
///
100+
/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
101+
#[unstable(feature = "random", issue = "130703")]
102+
pub fn random<T: Random>() -> T {
103+
T::random(&mut DefaultRandomSource)
104+
}

library/std/src/sys/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod dbg;
1515
pub mod exit_guard;
1616
pub mod os_str;
1717
pub mod path;
18+
pub mod random;
1819
pub mod sync;
1920
pub mod thread_local;
2021

library/std/src/sys/pal/hermit/mod.rs

-14
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,6 @@ pub fn abort_internal() -> ! {
5252
unsafe { hermit_abi::abort() }
5353
}
5454

55-
pub fn hashmap_random_keys() -> (u64, u64) {
56-
let mut buf = [0; 16];
57-
let mut slice = &mut buf[..];
58-
while !slice.is_empty() {
59-
let res = cvt(unsafe { hermit_abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) })
60-
.expect("failed to generate random hashmap keys");
61-
slice = &mut slice[res as usize..];
62-
}
63-
64-
let key1 = buf[..8].try_into().unwrap();
65-
let key2 = buf[8..].try_into().unwrap();
66-
(u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
67-
}
68-
6955
// This function is needed by the panic runtime. The symbol is named in
7056
// pre-link args for the target specification, so keep that in sync.
7157
#[cfg(not(test))]

library/std/src/sys/pal/sgx/abi/usercalls/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::cmp;
22
use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
3-
use crate::sys::rand::rdrand64;
3+
use crate::random::{DefaultRandomSource, Random};
44
use crate::time::{Duration, Instant};
55

66
pub(crate) mod alloc;
@@ -164,7 +164,7 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
164164
// trusted to ensure accurate timeouts.
165165
if let Ok(timeout_signed) = i64::try_from(timeout) {
166166
let tenth = timeout_signed / 10;
167-
let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
167+
let deviation = i64::random(&mut DefaultRandomSource).checked_rem(tenth).unwrap_or(0);
168168
timeout = timeout_signed.saturating_add(deviation) as _;
169169
}
170170
}

library/std/src/sys/pal/sgx/mod.rs

-18
Original file line numberDiff line numberDiff line change
@@ -132,24 +132,6 @@ pub extern "C" fn __rust_abort() {
132132
abort_internal();
133133
}
134134

135-
pub mod rand {
136-
pub fn rdrand64() -> u64 {
137-
unsafe {
138-
let mut ret: u64 = 0;
139-
for _ in 0..10 {
140-
if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
141-
return ret;
142-
}
143-
}
144-
rtabort!("Failed to obtain random data");
145-
}
146-
}
147-
}
148-
149-
pub fn hashmap_random_keys() -> (u64, u64) {
150-
(self::rand::rdrand64(), self::rand::rdrand64())
151-
}
152-
153135
pub use crate::sys_common::{AsInner, FromInner, IntoInner};
154136

155137
pub trait TryIntoInner<Inner>: Sized {

library/std/src/sys/pal/solid/mod.rs

-10
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,3 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
6262
pub fn abort_internal() -> ! {
6363
unsafe { libc::abort() }
6464
}
65-
66-
pub fn hashmap_random_keys() -> (u64, u64) {
67-
unsafe {
68-
let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit();
69-
let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16);
70-
assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}");
71-
let [x1, x2] = out.assume_init();
72-
(x1, x2)
73-
}
74-
}

library/std/src/sys/pal/teeos/mod.rs

-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
#![allow(unused_variables)]
77
#![allow(dead_code)]
88

9-
pub use self::rand::hashmap_random_keys;
10-
119
#[path = "../unsupported/args.rs"]
1210
pub mod args;
1311
#[path = "../unsupported/env.rs"]
@@ -23,7 +21,6 @@ pub mod os;
2321
pub mod pipe;
2422
#[path = "../unsupported/process.rs"]
2523
pub mod process;
26-
mod rand;
2724
pub mod stdio;
2825
pub mod thread;
2926
#[allow(non_upper_case_globals)]

library/std/src/sys/pal/teeos/rand.rs

-21
This file was deleted.

library/std/src/sys/pal/uefi/mod.rs

-33
Original file line numberDiff line numberDiff line change
@@ -179,39 +179,6 @@ pub extern "C" fn __rust_abort() {
179179
abort_internal();
180180
}
181181

182-
#[inline]
183-
pub fn hashmap_random_keys() -> (u64, u64) {
184-
get_random().unwrap()
185-
}
186-
187-
fn get_random() -> Option<(u64, u64)> {
188-
use r_efi::protocols::rng;
189-
190-
let mut buf = [0u8; 16];
191-
let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?;
192-
for handle in handles {
193-
if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
194-
let r = unsafe {
195-
((*protocol.as_ptr()).get_rng)(
196-
protocol.as_ptr(),
197-
crate::ptr::null_mut(),
198-
buf.len(),
199-
buf.as_mut_ptr(),
200-
)
201-
};
202-
if r.is_error() {
203-
continue;
204-
} else {
205-
return Some((
206-
u64::from_le_bytes(buf[..8].try_into().ok()?),
207-
u64::from_le_bytes(buf[8..].try_into().ok()?),
208-
));
209-
}
210-
}
211-
}
212-
None
213-
}
214-
215182
/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled
216183
extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) {
217184
uefi::env::disable_boot_services();

library/std/src/sys/pal/unix/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#![allow(missing_docs, nonstandard_style)]
22

3-
pub use self::rand::hashmap_random_keys;
43
use crate::io::ErrorKind;
54

65
#[cfg(not(target_os = "espidf"))]
@@ -26,7 +25,6 @@ pub use self::l4re::net;
2625
pub mod os;
2726
pub mod pipe;
2827
pub mod process;
29-
pub mod rand;
3028
pub mod stack_overflow;
3129
pub mod stdio;
3230
pub mod thread;

0 commit comments

Comments
 (0)