Skip to content

Commit

Permalink
std: implement the random feature
Browse files Browse the repository at this point in the history
Implements the ACP rust-lang/libs-team#393.
  • Loading branch information
joboet committed Sep 23, 2024
1 parent ba6158c commit a095a76
Show file tree
Hide file tree
Showing 43 changed files with 786 additions and 464 deletions.
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ pub mod panicking;
#[unstable(feature = "core_pattern_types", issue = "123646")]
pub mod pat;
pub mod pin;
#[unstable(feature = "random", issue = "none")]
pub mod random;
#[unstable(feature = "new_range_api", issue = "125687")]
pub mod range;
pub mod result;
Expand Down
62 changes: 62 additions & 0 deletions core/src/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//! Random value generation.
//!
//! The [`Random`] trait allows generating a random value for a type using a
//! given [`RandomSource`].
/// A source of randomness.
#[unstable(feature = "random", issue = "none")]
pub trait RandomSource {
/// Fills `bytes` with random bytes.
fn fill_bytes(&mut self, bytes: &mut [u8]);
}

/// A trait for getting a random value for a type.
///
/// **Warning:** Be careful when manipulating random values! The
/// [`random`](Random::random) method on integers samples them with a uniform
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
/// modulo operations, some of the resulting values can become more likely than
/// others. Use audited crates when in doubt.
#[unstable(feature = "random", issue = "none")]
pub trait Random: Sized {
/// Generates a random value.
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self;
}

impl Random for bool {
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
u8::random(source) & 1 == 1
}
}

macro_rules! impl_primitive {
($t:ty) => {
impl Random for $t {
/// Generates a random value.
///
/// **Warning:** Be careful when manipulating the resulting value! This
/// method samples according to a uniform distribution, so a value of 1 is
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
/// values can become more likely than others. Use audited crates when in
/// doubt.
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
let mut bytes = (0 as Self).to_ne_bytes();
source.fill_bytes(&mut bytes);
Self::from_ne_bytes(bytes)
}
}
};
}

impl_primitive!(u8);
impl_primitive!(i8);
impl_primitive!(u16);
impl_primitive!(i16);
impl_primitive!(u32);
impl_primitive!(i32);
impl_primitive!(u64);
impl_primitive!(i64);
impl_primitive!(u128);
impl_primitive!(i128);
impl_primitive!(usize);
impl_primitive!(isize);
5 changes: 3 additions & 2 deletions std/src/hash/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
#[allow(deprecated)]
use super::{BuildHasher, Hasher, SipHasher13};
use crate::cell::Cell;
use crate::{fmt, sys};
use crate::fmt;
use crate::sys::random::hashmap_random_keys;

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

KEYS.with(|keys| {
Expand Down
4 changes: 4 additions & 0 deletions std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@
//
// Library features (core):
// tidy-alphabetical-start
#![feature(array_chunks)]
#![feature(c_str_module)]
#![feature(char_internals)]
#![feature(clone_to_uninit)]
Expand Down Expand Up @@ -348,6 +349,7 @@
#![feature(prelude_2024)]
#![feature(ptr_as_uninit)]
#![feature(ptr_mask)]
#![feature(random)]
#![feature(slice_internals)]
#![feature(slice_ptr_get)]
#![feature(slice_range)]
Expand Down Expand Up @@ -595,6 +597,8 @@ pub mod path;
#[unstable(feature = "anonymous_pipe", issue = "127154")]
pub mod pipe;
pub mod process;
#[unstable(feature = "random", issue = "none")]
pub mod random;
pub mod sync;
pub mod time;

Expand Down
100 changes: 100 additions & 0 deletions std/src/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//! Random value generation.
//!
//! The [`Random`] trait allows generating a random value for a type using a
//! given [`RandomSource`].
#[unstable(feature = "random", issue = "none")]
pub use core::random::*;

use crate::sys::random as sys;

/// The default random source.
///
/// This asks the system for random data suitable for cryptographic purposes
/// such as key generation. If security is a concern, consult the platform
/// documentation below for the specific guarantees your target provides.
///
/// The high quality of randomness provided by this source means it can be quite
/// slow. If you need a large quantity of random numbers and security is not a
/// concern, consider using an alternative random number generator (potentially
/// seeded from this one).
///
/// # Underlying sources
///
/// 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`]
/// Hermit | `read_entropy`
/// Horizon | `getrandom` shim
/// Hurd, L4Re, QNX | `/dev/urandom`
/// NetBSD before 10.0 | [`kern.arandom`]
/// Redox | `/scheme/rand`
/// SGX | [`rdrand`]
/// SOLID | `SOLID_RNG_SampleRandomBytes`
/// TEEOS | `TEE_GenerateRandom`
/// UEFI | [`EFI_RNG_PROTOCOL`]
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
/// WASI | `random_get`
/// 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;

#[unstable(feature = "random", issue = "none")]
impl RandomSource for DefaultRandomSource {
fn fill_bytes(&mut self, bytes: &mut [u8]) {
sys::fill_bytes(bytes)
}
}

/// Generates a random value with the default random source.
///
/// This is a convenience function for `T::random(&mut DefaultRandomSource)` and
/// will sample according to the same distribution as the underlying [`Random`]
/// trait implementation.
///
/// **Warning:** Be careful when manipulating random values! The
/// [`random`](Random::random) method on integers samples them with a uniform
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
/// modulo operations, some of the resulting values can become more likely than
/// others. Use audited crates when in doubt.
///
/// # Examples
///
/// Generating a [version 4/variant 1 UUID] represented as text:
/// ```
/// #![feature(random)]
///
/// use std::random::random;
///
/// let bits = random::<u128>();
/// let g1 = (bits >> 96) as u32;
/// let g2 = (bits >> 80) as u16;
/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16;
/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16;
/// let g5 = (bits & 0xffffffffffff) as u64;
/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}");
/// println!("{uuid}");
/// ```
///
/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
pub fn random<T: Random>() -> T {
T::random(&mut DefaultRandomSource)
}
1 change: 1 addition & 0 deletions std/src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod dbg;
pub mod exit_guard;
pub mod os_str;
pub mod path;
pub mod random;
pub mod sync;
pub mod thread_local;

Expand Down
14 changes: 0 additions & 14 deletions std/src/sys/pal/hermit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,6 @@ pub fn abort_internal() -> ! {
unsafe { hermit_abi::abort() }
}

pub fn hashmap_random_keys() -> (u64, u64) {
let mut buf = [0; 16];
let mut slice = &mut buf[..];
while !slice.is_empty() {
let res = cvt(unsafe { hermit_abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) })
.expect("failed to generate random hashmap keys");
slice = &mut slice[res as usize..];
}

let key1 = buf[..8].try_into().unwrap();
let key2 = buf[8..].try_into().unwrap();
(u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
}

// This function is needed by the panic runtime. The symbol is named in
// pre-link args for the target specification, so keep that in sync.
#[cfg(not(test))]
Expand Down
4 changes: 2 additions & 2 deletions std/src/sys/pal/sgx/abi/usercalls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::cmp;
use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
use crate::sys::rand::rdrand64;
use crate::random::{DefaultRandomSource, Random};
use crate::time::{Duration, Instant};

pub(crate) mod alloc;
Expand Down Expand Up @@ -164,7 +164,7 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
// trusted to ensure accurate timeouts.
if let Ok(timeout_signed) = i64::try_from(timeout) {
let tenth = timeout_signed / 10;
let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
let deviation = i64::random(&mut DefaultRandomSource).checked_rem(tenth).unwrap_or(0);
timeout = timeout_signed.saturating_add(deviation) as _;
}
}
Expand Down
18 changes: 0 additions & 18 deletions std/src/sys/pal/sgx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,24 +132,6 @@ pub extern "C" fn __rust_abort() {
abort_internal();
}

pub mod rand {
pub fn rdrand64() -> u64 {
unsafe {
let mut ret: u64 = 0;
for _ in 0..10 {
if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
return ret;
}
}
rtabort!("Failed to obtain random data");
}
}
}

pub fn hashmap_random_keys() -> (u64, u64) {
(self::rand::rdrand64(), self::rand::rdrand64())
}

pub use crate::sys_common::{AsInner, FromInner, IntoInner};

pub trait TryIntoInner<Inner>: Sized {
Expand Down
10 changes: 0 additions & 10 deletions std/src/sys/pal/solid/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,3 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
pub fn abort_internal() -> ! {
unsafe { libc::abort() }
}

pub fn hashmap_random_keys() -> (u64, u64) {
unsafe {
let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit();
let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16);
assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}");
let [x1, x2] = out.assume_init();
(x1, x2)
}
}
3 changes: 0 additions & 3 deletions std/src/sys/pal/teeos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#![allow(unused_variables)]
#![allow(dead_code)]

pub use self::rand::hashmap_random_keys;

#[path = "../unsupported/args.rs"]
pub mod args;
#[path = "../unsupported/env.rs"]
Expand All @@ -23,7 +21,6 @@ pub mod os;
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
mod rand;
pub mod stdio;
pub mod thread;
#[allow(non_upper_case_globals)]
Expand Down
21 changes: 0 additions & 21 deletions std/src/sys/pal/teeos/rand.rs

This file was deleted.

33 changes: 0 additions & 33 deletions std/src/sys/pal/uefi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,39 +179,6 @@ pub extern "C" fn __rust_abort() {
abort_internal();
}

#[inline]
pub fn hashmap_random_keys() -> (u64, u64) {
get_random().unwrap()
}

fn get_random() -> Option<(u64, u64)> {
use r_efi::protocols::rng;

let mut buf = [0u8; 16];
let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?;
for handle in handles {
if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
let r = unsafe {
((*protocol.as_ptr()).get_rng)(
protocol.as_ptr(),
crate::ptr::null_mut(),
buf.len(),
buf.as_mut_ptr(),
)
};
if r.is_error() {
continue;
} else {
return Some((
u64::from_le_bytes(buf[..8].try_into().ok()?),
u64::from_le_bytes(buf[8..].try_into().ok()?),
));
}
}
}
None
}

/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled
extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) {
uefi::env::disable_boot_services();
Expand Down
2 changes: 0 additions & 2 deletions std/src/sys/pal/unix/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#![allow(missing_docs, nonstandard_style)]

pub use self::rand::hashmap_random_keys;
use crate::io::ErrorKind;

#[cfg(not(target_os = "espidf"))]
Expand All @@ -26,7 +25,6 @@ pub use self::l4re::net;
pub mod os;
pub mod pipe;
pub mod process;
pub mod rand;
pub mod stack_overflow;
pub mod stdio;
pub mod thread;
Expand Down
Loading

0 comments on commit a095a76

Please sign in to comment.