pub fn fill(into: &mut [u8]) {
if into.is_empty() {
return;
}
imp::fill(into);
}
#[cfg(unix)]
mod imp {
use std::fs::File;
use std::io::Read;
pub fn fill(into: &mut [u8]) {
let mut file = File::open("/dev/urandom").expect("no /dev/urandom to take a password from");
let mut at = 0;
while at < into.len() {
let n = file
.read(&mut into[at..])
.expect("could not read /dev/urandom");
assert!(n != 0, "/dev/urandom stopped early");
at += n;
}
}
}
#[cfg(windows)]
mod imp {
const USE_SYSTEM_PREFERRED_RNG: u32 = 0x0000_0002;
#[link(name = "bcrypt")]
unsafe extern "system" {
fn BCryptGenRandom(
algorithm: *mut core::ffi::c_void,
buffer: *mut u8,
count: u32,
flags: u32,
) -> i32;
}
pub fn fill(into: &mut [u8]) {
for chunk in into.chunks_mut(u32::MAX as usize) {
let status = unsafe {
BCryptGenRandom(
core::ptr::null_mut(),
chunk.as_mut_ptr(),
chunk.len() as u32,
USE_SYSTEM_PREFERRED_RNG,
)
};
assert!(status == 0, "BCryptGenRandom failed with {status:#x}");
}
}
}
#[cfg(test)]
mod tests {
use super::fill;
#[test]
fn two_draws_are_not_the_same_bytes() {
let mut one = [0u8; 32];
let mut two = [0u8; 32];
fill(&mut one);
fill(&mut two);
assert_ne!(one, two);
assert_ne!(one, [0u8; 32]);
}
#[test]
fn every_length_up_to_a_block_comes_back_written() {
fill(&mut []);
for len in 1..=64 {
let mut buf = vec![0u8; len];
fill(&mut buf);
assert_eq!(buf.len(), len);
}
}
}