use crate::{traits::RandomSource, Result};
#[derive(Debug, Clone, Copy, Default)]
pub struct OsEntropy;
impl RandomSource for OsEntropy {
fn fill(&mut self, out: &mut [u8]) -> Result<()> {
fill_impl(out)
}
}
pub fn fill(out: &mut [u8]) -> Result<()> {
fill_impl(out)
}
#[cfg(all(feature = "std", windows))]
fn fill_impl(out: &mut [u8]) -> Result<()> {
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x0000_0002;
#[link(name = "bcrypt")]
extern "system" {
fn BCryptGenRandom(
hAlgorithm: *mut core::ffi::c_void,
pbBuffer: *mut u8,
cbBuffer: u32,
dwFlags: u32,
) -> i32;
}
for chunk in out.chunks_mut(u32::MAX as usize) {
if chunk.is_empty() {
continue;
}
let status = unsafe {
BCryptGenRandom(
core::ptr::null_mut(),
chunk.as_mut_ptr(),
chunk.len() as u32,
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};
if status != 0 {
return Err(crate::err!(EntropyFailure, "BCryptGenRandom"));
}
}
Ok(())
}
#[cfg(all(feature = "std", unix))]
fn fill_impl(out: &mut [u8]) -> Result<()> {
use std::io::Read;
if out.is_empty() {
return Ok(());
}
let mut f = std::fs::File::open("/dev/urandom")
.map_err(|_| crate::err!(EntropyFailure, "/dev/urandom open"))?;
f.read_exact(out)
.map_err(|_| crate::err!(EntropyFailure, "/dev/urandom read"))?;
Ok(())
}
#[cfg(not(all(feature = "std", any(windows, unix))))]
fn fill_impl(_out: &mut [u8]) -> Result<()> {
Err(crate::err!(
EntropyFailure,
"no OS entropy backend for this target; seed the DRBG manually"
))
}
#[cfg(all(test, feature = "std", any(windows, unix)))]
mod tests {
use super::*;
#[test]
fn produces_distinct_nonzero_output() {
let mut a = [0u8; 32];
let mut b = [0u8; 32];
fill(&mut a).unwrap();
fill(&mut b).unwrap();
assert_ne!(a, [0u8; 32], "entropy source returned all zeroes");
assert_ne!(a, b, "entropy source repeated itself");
}
#[test]
fn empty_request_succeeds() {
fill(&mut []).unwrap();
}
}