mod hmac_drbg;
#[cfg(all(feature = "linux-getrandom", target_os = "linux"))]
mod linux_getrandom;
pub use hmac_drbg::HmacDrbg;
pub trait RngCore {
fn fill_bytes(&mut self, dest: &mut [u8]);
#[inline]
fn next_u32(&mut self) -> u32 {
let mut b = [0u8; 4];
self.fill_bytes(&mut b);
u32::from_le_bytes(b)
}
#[inline]
fn next_u64(&mut self) -> u64 {
let mut b = [0u8; 8];
self.fill_bytes(&mut b);
u64::from_le_bytes(b)
}
}
pub trait CryptoRng {}
#[cfg(all(feature = "std", unix))]
#[derive(Debug, Clone, Copy, Default)]
pub struct OsRng;
#[cfg(all(feature = "std", unix, not(target_vendor = "apple")))]
std::thread_local! {
static URANDOM: core::cell::RefCell<Option<std::fs::File>> =
const { core::cell::RefCell::new(None) };
}
#[cfg(all(feature = "std", unix, target_vendor = "apple"))]
mod os_apple {
#![allow(unsafe_code)]
unsafe extern "C" {
pub(super) fn arc4random_buf(buf: *mut core::ffi::c_void, len: usize);
}
}
#[cfg(all(feature = "std", unix))]
impl RngCore for OsRng {
fn fill_bytes(&mut self, dest: &mut [u8]) {
if dest.is_empty() {
return;
}
#[cfg(target_vendor = "apple")]
{
#[allow(unsafe_code)]
unsafe {
os_apple::arc4random_buf(dest.as_mut_ptr() as *mut core::ffi::c_void, dest.len());
}
}
#[cfg(not(target_vendor = "apple"))]
{
#[cfg(all(feature = "linux-getrandom", target_os = "linux"))]
match linux_getrandom::try_getrandom(dest) {
Ok(()) => return,
Err(linux_getrandom::Error::NotImplemented) => {} Err(linux_getrandom::Error::Other(e)) => {
panic!("getrandom(2) failed with errno {e}");
}
}
urandom_fill(dest);
}
}
}
#[cfg(all(feature = "std", unix, not(target_vendor = "apple")))]
fn urandom_fill(dest: &mut [u8]) {
use std::io::Read;
URANDOM.with(|cell| {
let mut slot = cell.borrow_mut();
if slot.is_none() {
*slot = Some(std::fs::File::open("/dev/urandom").expect("failed to open /dev/urandom"));
}
slot.as_mut()
.unwrap()
.read_exact(dest)
.expect("failed to read entropy from /dev/urandom");
});
}
#[cfg(all(feature = "std", unix))]
impl CryptoRng for OsRng {}
#[cfg(all(feature = "std", windows))]
mod os_windows {
#![allow(unsafe_code)]
use super::{CryptoRng, RngCore};
#[link(name = "bcryptprimitives", kind = "raw-dylib")]
unsafe extern "system" {
fn ProcessPrng(data: *mut u8, len: usize) -> i32;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct OsRng;
impl RngCore for OsRng {
fn fill_bytes(&mut self, dest: &mut [u8]) {
if dest.is_empty() {
return;
}
let ok = unsafe { ProcessPrng(dest.as_mut_ptr(), dest.len()) };
assert!(ok != 0, "ProcessPrng failed to produce entropy");
}
}
impl CryptoRng for OsRng {}
}
#[cfg(all(feature = "std", windows))]
pub use os_windows::OsRng;
#[cfg(all(test, feature = "std", any(unix, windows)))]
mod tests {
use super::*;
#[test]
fn os_rng_fills_and_varies() {
let mut rng = OsRng;
let mut a = [0u8; 32];
let mut b = [0u8; 32];
rng.fill_bytes(&mut a);
rng.fill_bytes(&mut b);
assert_ne!(a, [0u8; 32]);
assert_ne!(a, b);
}
}