Skip to content

Commit f4eeb60

Browse files
committed
std: implement io::Entropy, refactor random data generation
1 parent f2d9a3d commit f4eeb60

File tree

24 files changed

+744
-410
lines changed

24 files changed

+744
-410
lines changed

library/std/src/collections/hash/map.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use crate::error::Error;
1313
use crate::fmt::{self, Debug};
1414
#[allow(deprecated)]
1515
use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13};
16+
use crate::io::{entropy, Read};
1617
use crate::iter::FusedIterator;
1718
use crate::ops::Index;
18-
use crate::sys;
1919

2020
/// A [hash map] implemented with quadratic probing and SIMD lookup.
2121
///
@@ -3122,7 +3122,17 @@ impl RandomState {
31223122
// increment one of the seeds on every RandomState creation, giving
31233123
// every corresponding HashMap a different iteration order.
31243124
thread_local!(static KEYS: Cell<(u64, u64)> = {
3125-
Cell::new(sys::hashmap_random_keys())
3125+
if crate::sys::entropy::INSECURE_HASHMAP {
3126+
Cell::new((1, 2))
3127+
} else {
3128+
let mut v = [0u8; 16];
3129+
let mut entropy = entropy();
3130+
entropy.set_insecure(true);
3131+
entropy.read_exact(&mut v).expect("failed to generate random keys for hashmap");
3132+
let key1 = v[..8].try_into().unwrap();
3133+
let key2 = v[8..].try_into().unwrap();
3134+
Cell::new((u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)))
3135+
}
31263136
});
31273137

31283138
KEYS.with(|keys| {

library/std/src/io/entropy.rs

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use super::{BorrowedBuf, BorrowedCursor, Read, Result};
2+
use crate::sys::entropy as sys;
3+
4+
/// A reader which returns random bytes from the system entropy source.
5+
///
6+
/// This struct is generally created by calling [`entropy()`]. Please
7+
/// see the documentation of [`entropy()`] for more details.
8+
#[derive(Debug)]
9+
#[unstable(feature = "io_entropy", issue = "none")]
10+
pub struct Entropy {
11+
insecure: bool,
12+
}
13+
14+
impl Entropy {
15+
pub(crate) fn set_insecure(&mut self, insecure: bool) {
16+
self.insecure = insecure;
17+
}
18+
}
19+
20+
#[unstable(feature = "io_entropy", issue = "none")]
21+
impl Read for Entropy {
22+
#[inline]
23+
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
24+
sys::Entropy { insecure: self.insecure }.read(buf)
25+
}
26+
27+
#[inline]
28+
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
29+
let mut buf = BorrowedBuf::from(buf);
30+
self.read_buf_exact(buf.unfilled())
31+
}
32+
33+
#[inline]
34+
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
35+
sys::Entropy { insecure: self.insecure }.read_buf(buf)
36+
}
37+
38+
#[inline]
39+
fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
40+
sys::Entropy { insecure: self.insecure }.read_buf_exact(buf)
41+
}
42+
}
43+
44+
/// Constructs a new handle to the system entropy source.
45+
///
46+
/// Reads from the resulting reader will return high-quality random data that
47+
/// is suited for cryptographic purposes (by the standard of the platform defaults).
48+
///
49+
/// Be aware that, because the data is of very high quality, reading high amounts
50+
/// of data can be very slow, and potentially slow down other processes requiring
51+
/// random data. Use a pseudo-random number generator if speed is important.
52+
///
53+
/// # Platform sources
54+
///
55+
/// | OS | Source
56+
/// |------------------|--------
57+
/// | Linux, Android | [`getrandom`][1] if available, otherwise [`/dev/urandom`][2] after successfully polling [`/dev/random`][2]
58+
/// | Windows | [`BCryptGenRandom`][3], falling back to [`RtlGenRandom`][4]
59+
/// | macOS | [`getentropy`][5] if available, falling back to [`/dev/urandom`][6]
60+
/// | OpenBSD | [`getentropy`][7]
61+
/// | iOS, watchOS | [`SecRandomCopyBytes`][8]
62+
/// | FreeBSD | [`kern.arandom`][9]
63+
/// | NetBSD | [`kern.arandom`][10]
64+
/// | Fuchsia | [`zx_cprng_draw`][11]
65+
/// | WASM | *Unsupported*
66+
/// | WASI | [`random_get`][12]
67+
/// | Emscripten | [`getentropy`][7]
68+
/// | Redox | `rand:`
69+
/// | VxWorks | `randABytes` after checking entropy pool initialization with `randSecure`
70+
/// | Haiku | `/dev/urandom`
71+
/// | ESP-IDF, Horizon | [`getrandom`][1]
72+
/// | Other UNIXes | `/dev/random`
73+
/// | Hermit | [`read_entropy`][13]
74+
/// | SGX | [`RDRAND`][14]
75+
/// | SOLID | `SOLID_RNG_SampleRandomBytes`
76+
///
77+
/// [1]: https://man7.org/linux/man-pages/man2/getrandom.2.html
78+
/// [2]: https://man7.org/linux/man-pages/man7/random.7.html
79+
/// [3]: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
80+
/// [4]: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
81+
/// [5]: https://www.unix.com/man-page/mojave/2/getentropy/
82+
/// [6]: https://www.unix.com/man-page/mojave/4/random/
83+
/// [7]: https://man.openbsd.org/getentropy.2
84+
/// [8]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
85+
/// [9]: https://man.freebsd.org/cgi/man.cgi?query=random&sektion=4&manpath=FreeBSD+13.1-RELEASE+and+Ports
86+
/// [10]: https://man.netbsd.org/rnd.4
87+
/// [11]: https://fuchsia.dev/fuchsia-src/reference/syscalls/cprng_draw
88+
/// [12]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md
89+
/// [13]: https://docs.rs/hermit-abi/latest/hermit_abi/fn.read_entropy.html
90+
/// [14]: https://www.intel.com/content/www/us/en/developer/articles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html
91+
///
92+
/// # Examples
93+
///
94+
/// Generating a seed for a random number generator:
95+
///
96+
/// ```rust
97+
/// #![feature(io_entropy)]
98+
///
99+
/// # use std::io::Result;
100+
/// # fn main() -> Result<()> {
101+
/// use std::io::{entropy, Read};
102+
///
103+
/// let mut seed = [0u8; 32];
104+
/// entropy().read_exact(&mut seed)?;
105+
/// println!("seed: {seed:?}");
106+
/// # Ok(())
107+
/// # }
108+
/// ```
109+
///
110+
/// Implementing your very own `/dev/random`:
111+
///
112+
/// ```rust, no_run
113+
/// #![feature(io_entropy)]
114+
///
115+
/// use std::io::{copy, entropy, stdout};
116+
///
117+
/// fn main() {
118+
/// let _ = copy(&mut entropy(), &mut stdout());
119+
/// }
120+
/// ```
121+
#[inline]
122+
#[unstable(feature = "io_entropy", issue = "none")]
123+
pub fn entropy() -> Entropy {
124+
Entropy { insecure: false }
125+
}

library/std/src/io/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ use crate::sys_common::memchr;
262262

263263
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
264264
pub use self::buffered::WriterPanicked;
265+
#[unstable(feature = "io_entropy", issue = "none")]
266+
pub use self::entropy::{entropy, Entropy};
265267
#[unstable(feature = "raw_os_error_ty", issue = "107792")]
266268
pub use self::error::RawOsError;
267269
pub(crate) use self::stdio::attempt_print_to_stderr;
@@ -289,6 +291,7 @@ pub(crate) use error::const_io_error;
289291
mod buffered;
290292
pub(crate) mod copy;
291293
mod cursor;
294+
mod entropy;
292295
mod error;
293296
mod impls;
294297
pub mod prelude;
@@ -351,6 +354,13 @@ where
351354
}
352355
}
353356

357+
/// Implement the `read` method by forwarding to `read_buf`.
358+
// FIXME(joboet): remove once #106643 is merged.
359+
pub(crate) fn default_read<R: Read + ?Sized>(r: &mut R, buf: &mut [u8]) -> Result<usize> {
360+
let mut buf = BorrowedBuf::from(buf);
361+
r.read_buf(buf.unfilled()).map(|()| buf.len())
362+
}
363+
354364
// This uses an adaptive system to extend the vector when it fills. We want to
355365
// avoid paying to allocate and zero a huge chunk of memory if the reader only
356366
// has 4 bytes while still making large reads if the reader does have a ton

library/std/src/io/readbuf.rs

+14
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,20 @@ impl<'a> BorrowedCursor<'a> {
230230
&mut self.buf.buf[self.buf.filled..]
231231
}
232232

233+
/// Returns a pointer to the current position of the cursor, with provenance to the
234+
/// rest of the buffer.
235+
///
236+
/// The resulting pointer may not be used to deinitialize any bytes in the initialized
237+
/// section of the buffer.
238+
// FIXME(joboet): Maybe make this public?
239+
#[allow(unused)]
240+
pub(crate) fn as_ptr(&mut self) -> *mut u8 {
241+
// SAFETY: deinitialization requires `unsafe` in the caller, where they have to make
242+
// sure not to deinitialize any bytes. The burden of prove is moved to the pointer
243+
// operation. Just returning a pointer is sound.
244+
unsafe { self.as_mut().as_mut_ptr().cast() }
245+
}
246+
233247
/// Advance the cursor by asserting that `n` bytes have been filled.
234248
///
235249
/// After advancing, the `n` bytes are no longer accessible via the cursor and can only be

library/std/src/sys/hermit/entropy.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use super::{abi, cvt};
2+
use crate::io::{default_read, BorrowedCursor, Read, Result};
3+
4+
pub const INSECURE_HASHMAP: bool = false;
5+
6+
pub struct Entropy {
7+
pub insecure: bool,
8+
}
9+
10+
impl Read for Entropy {
11+
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
12+
default_read(self, buf)
13+
}
14+
15+
fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> {
16+
unsafe {
17+
let len = cvt(abi::read_entropy(buf.as_ptr(), buf.capacity(), 0))?;
18+
buf.advance(len as usize);
19+
Ok(())
20+
}
21+
}
22+
}

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

+1-14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod alloc;
2121
pub mod args;
2222
#[path = "../unix/cmath.rs"]
2323
pub mod cmath;
24+
pub mod entropy;
2425
pub mod env;
2526
pub mod fd;
2627
pub mod fs;
@@ -75,20 +76,6 @@ pub fn abort_internal() -> ! {
7576
}
7677
}
7778

78-
pub fn hashmap_random_keys() -> (u64, u64) {
79-
let mut buf = [0; 16];
80-
let mut slice = &mut buf[..];
81-
while !slice.is_empty() {
82-
let res = cvt(unsafe { abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) })
83-
.expect("failed to generate random hashmap keys");
84-
slice = &mut slice[res as usize..];
85-
}
86-
87-
let key1 = buf[..8].try_into().unwrap();
88-
let key2 = buf[8..].try_into().unwrap();
89-
(u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
90-
}
91-
9279
// This function is needed by the panic runtime. The symbol is named in
9380
// pre-link args for the target specification, so keep that in sync.
9481
#[cfg(not(test))]

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

+3-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::sys::entropy::rdrand64;
44
use crate::time::{Duration, Instant};
55

66
pub(crate) mod alloc;
@@ -164,7 +164,8 @@ 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 rand = rdrand64().unwrap_or_else(|| rtabort!("Failed to obtain random data"));
168+
let deviation = (rand as i64).checked_rem(tenth).unwrap_or(0);
168169
timeout = timeout_signed.saturating_add(deviation) as _;
169170
}
170171
}

library/std/src/sys/sgx/entropy.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use crate::io::{const_io_error, default_read, BorrowedCursor, ErrorKind, Read, Result};
2+
3+
pub const INSECURE_HASHMAP: bool = false;
4+
5+
pub struct Entropy {
6+
pub insecure: bool,
7+
}
8+
9+
impl Read for Entropy {
10+
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
11+
default_read(self, buf)
12+
}
13+
14+
fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> {
15+
if buf.capacity() != 0 {
16+
let rand = rdrand64()
17+
.ok_or(const_io_error!(ErrorKind::WouldBlock, "no random data available"))?;
18+
buf.append(&rand.to_ne_bytes()[..usize::min(buf.capacity(), 8)]);
19+
Ok(())
20+
} else {
21+
Ok(())
22+
}
23+
}
24+
}
25+
26+
pub(super) fn rdrand64() -> Option<u64> {
27+
unsafe {
28+
let mut ret: u64 = 0;
29+
for _ in 0..10 {
30+
if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
31+
return Some(ret);
32+
}
33+
}
34+
None
35+
}
36+
}

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

+1-18
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod alloc;
1515
pub mod args;
1616
#[path = "../unix/cmath.rs"]
1717
pub mod cmath;
18+
pub mod entropy;
1819
pub mod env;
1920
pub mod fd;
2021
#[path = "../unsupported/fs.rs"]
@@ -144,24 +145,6 @@ pub extern "C" fn __rust_abort() {
144145
abort_internal();
145146
}
146147

147-
pub mod rand {
148-
pub fn rdrand64() -> u64 {
149-
unsafe {
150-
let mut ret: u64 = 0;
151-
for _ in 0..10 {
152-
if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
153-
return ret;
154-
}
155-
}
156-
rtabort!("Failed to obtain random data");
157-
}
158-
}
159-
}
160-
161-
pub fn hashmap_random_keys() -> (u64, u64) {
162-
(self::rand::rdrand64(), self::rand::rdrand64())
163-
}
164-
165148
pub use crate::sys_common::{AsInner, FromInner, IntoInner};
166149

167150
pub trait TryIntoInner<Inner>: Sized {

library/std/src/sys/solid/entropy.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use super::abi;
2+
use super::error::SolidError;
3+
use crate::io::{default_read, BorrowedCursor, Read, Result};
4+
5+
pub const INSECURE_HASHMAP: bool = false;
6+
7+
pub struct Entropy {
8+
pub insecure: bool,
9+
}
10+
11+
impl Read for Entropy {
12+
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
13+
default_read(self, buf)
14+
}
15+
16+
fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> {
17+
SolidError::err_if_negative(unsafe {
18+
abi::SOLID_RNG_SampleRandomBytes(buf.as_mut().as_mut_ptr().cast(), buf.capacity())
19+
})
20+
.map_err(|e| e.as_io_error())?;
21+
22+
unsafe {
23+
buf.advance(buf.capacity());
24+
Ok(())
25+
}
26+
}
27+
28+
fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
29+
self.read_buf(buf)
30+
}
31+
}

0 commit comments

Comments
 (0)