1use crate::{traits::RandomSource, Result};
20
21#[derive(Debug, Clone, Copy, Default)]
29pub struct OsEntropy;
30
31impl RandomSource for OsEntropy {
32 fn fill(&mut self, out: &mut [u8]) -> Result<()> {
33 fill_impl(out)
34 }
35}
36
37pub fn fill(out: &mut [u8]) -> Result<()> {
39 fill_impl(out)
40}
41
42#[cfg(all(feature = "std", windows))]
43fn fill_impl(out: &mut [u8]) -> Result<()> {
44 const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x0000_0002;
48
49 #[link(name = "bcrypt")]
50 extern "system" {
51 fn BCryptGenRandom(
52 hAlgorithm: *mut core::ffi::c_void,
53 pbBuffer: *mut u8,
54 cbBuffer: u32,
55 dwFlags: u32,
56 ) -> i32;
57 }
58
59 for chunk in out.chunks_mut(u32::MAX as usize) {
61 if chunk.is_empty() {
62 continue;
63 }
64 let status = unsafe {
67 BCryptGenRandom(
68 core::ptr::null_mut(),
69 chunk.as_mut_ptr(),
70 chunk.len() as u32,
71 BCRYPT_USE_SYSTEM_PREFERRED_RNG,
72 )
73 };
74 if status != 0 {
75 return Err(crate::err!(EntropyFailure, "BCryptGenRandom"));
76 }
77 }
78 Ok(())
79}
80
81#[cfg(all(feature = "std", unix))]
82fn fill_impl(out: &mut [u8]) -> Result<()> {
83 use std::io::Read;
84
85 if out.is_empty() {
86 return Ok(());
87 }
88 let mut f = std::fs::File::open("/dev/urandom")
89 .map_err(|_| crate::err!(EntropyFailure, "/dev/urandom open"))?;
90 f.read_exact(out)
91 .map_err(|_| crate::err!(EntropyFailure, "/dev/urandom read"))?;
92 Ok(())
93}
94
95#[cfg(not(all(feature = "std", any(windows, unix))))]
96fn fill_impl(_out: &mut [u8]) -> Result<()> {
97 Err(crate::err!(
98 EntropyFailure,
99 "no OS entropy backend for this target; seed the DRBG manually"
100 ))
101}
102
103#[cfg(all(test, feature = "std", any(windows, unix)))]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn produces_distinct_nonzero_output() {
109 let mut a = [0u8; 32];
110 let mut b = [0u8; 32];
111 fill(&mut a).unwrap();
112 fill(&mut b).unwrap();
113 assert_ne!(a, [0u8; 32], "entropy source returned all zeroes");
114 assert_ne!(a, b, "entropy source repeated itself");
115 }
116
117 #[test]
118 fn empty_request_succeeds() {
119 fill(&mut []).unwrap();
120 }
121}