//! Cryptographically secure random bytes. //! //! Uses the best available platform source: //! - OpenBSD/macOS: arc4random_buf(3) //! - Linux/FreeBSD: getrandom(2) //! - Fallback: /dev/urandom /// Fill buffer with cryptographically secure random /// bytes. pub fn random_bytes(buf: &mut [u8]) { platform::fill(buf); } #[cfg(any(target_os = "openbsd", target_os = "macos"))] mod platform { use std::ffi::c_void; // SAFETY: arc4random_buf always fills the entire // buffer. Pointer valid from mutable slice. unsafe extern "C" { fn arc4random_buf(buf: *mut c_void, nbytes: usize); } pub fn fill(buf: &mut [u8]) { unsafe { arc4random_buf(buf.as_mut_ptr() as *mut c_void, buf.len()); } } } #[cfg(target_os = "linux")] mod platform { pub fn fill(buf: &mut [u8]) { // getrandom(2) — available since Linux 3.17 // Flags: 0 = block until entropy available let ret = unsafe { libc_getrandom( buf.as_mut_ptr() as *mut std::ffi::c_void, buf.len(), 0, ) }; if ret < 0 { // Fallback to /dev/urandom urandom_fill(buf); } } unsafe extern "C" { fn getrandom( buf: *mut std::ffi::c_void, buflen: usize, flags: std::ffi::c_uint, ) -> isize; } // Rename to avoid conflict with the syscall unsafe fn libc_getrandom( buf: *mut std::ffi::c_void, buflen: usize, flags: std::ffi::c_uint, ) -> isize { getrandom(buf, buflen, flags) } fn urandom_fill(buf: &mut [u8]) { use std::io::Read; let mut f = std::fs::File::open("/dev/urandom").expect( "FATAL: cannot open /dev/urandom — no secure randomness available", ); f.read_exact(buf).expect("FATAL: cannot read /dev/urandom"); } } #[cfg(target_os = "freebsd")] mod platform { use std::ffi::c_void; unsafe extern "C" { fn arc4random_buf(buf: *mut c_void, nbytes: usize); } pub fn fill(buf: &mut [u8]) { unsafe { arc4random_buf(buf.as_mut_ptr() as *mut c_void, buf.len()); } } } #[cfg(not(any( target_os = "openbsd", target_os = "macos", target_os = "linux", target_os = "freebsd" )))] mod platform { pub fn fill(buf: &mut [u8]) { use std::io::Read; let mut f = std::fs::File::open("/dev/urandom").expect( "FATAL: cannot open /dev/urandom — no secure randomness available", ); f.read_exact(buf).expect("FATAL: cannot read /dev/urandom"); } } #[cfg(test)] mod tests { use super::*; #[test] fn random_bytes_not_zero() { let mut buf = [0u8; 32]; random_bytes(&mut buf); // Probability of all zeros: 2^-256 assert!(buf.iter().any(|&b| b != 0)); } #[test] fn random_bytes_different_calls() { let mut a = [0u8; 32]; let mut b = [0u8; 32]; random_bytes(&mut a); random_bytes(&mut b); assert_ne!(a, b); } }