| use crate::io; |
| use crate::mem; |
| use crate::sync; |
| use crate::sys::c; |
| |
| /// The kinds of HashMap RNG that may be available |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| enum HashMapRng { |
| Preferred, |
| Fallback, |
| } |
| |
| pub fn hashmap_random_keys() -> (u64, u64) { |
| match get_hashmap_rng() { |
| HashMapRng::Preferred => { |
| preferred_rng().expect("couldn't generate random bytes with preferred RNG") |
| } |
| HashMapRng::Fallback => { |
| fallback_rng().expect("couldn't generate random bytes with fallback RNG") |
| } |
| } |
| } |
| |
| /// Returns the HashMap RNG that should be used |
| /// |
| /// Panics if they are both broken |
| fn get_hashmap_rng() -> HashMapRng { |
| // Assume that if the preferred RNG is broken the first time we use it, it likely means |
| // that: the DLL has failed to load, there is no point to calling it over-and-over again, |
| // and we should cache the result |
| static VALUE: sync::OnceLock<HashMapRng> = sync::OnceLock::new(); |
| *VALUE.get_or_init(choose_hashmap_rng) |
| } |
| |
| /// Test whether we should use the preferred or fallback RNG |
| /// |
| /// If the preferred RNG is successful, we choose it. Otherwise, if the fallback RNG is successful, |
| /// we choose that |
| /// |
| /// Panics if both the preferred and the fallback RNG are both non-functional |
| fn choose_hashmap_rng() -> HashMapRng { |
| let preferred_error = match preferred_rng() { |
| Ok(_) => return HashMapRng::Preferred, |
| Err(e) => e, |
| }; |
| |
| match fallback_rng() { |
| Ok(_) => return HashMapRng::Fallback, |
| Err(fallback_error) => panic!( |
| "preferred RNG broken: `{}`, fallback RNG broken: `{}`", |
| preferred_error, fallback_error |
| ), |
| } |
| } |
| |
| /// Generate random numbers using the preferred RNG function (BCryptGenRandom) |
| fn preferred_rng() -> Result<(u64, u64), io::Error> { |
| use crate::ptr; |
| |
| let mut v = (0, 0); |
| let ret = unsafe { |
| c::BCryptGenRandom( |
| ptr::null_mut(), |
| &mut v as *mut _ as *mut u8, |
| mem::size_of_val(&v) as c::ULONG, |
| c::BCRYPT_USE_SYSTEM_PREFERRED_RNG, |
| ) |
| }; |
| |
| if ret == 0 { Ok(v) } else { Err(io::Error::last_os_error()) } |
| } |
| |
| /// Generate random numbers using the fallback RNG function (RtlGenRandom) |
| #[cfg(not(target_vendor = "uwp"))] |
| fn fallback_rng() -> Result<(u64, u64), io::Error> { |
| let mut v = (0, 0); |
| let ret = |
| unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) }; |
| |
| if ret != 0 { Ok(v) } else { Err(io::Error::last_os_error()) } |
| } |
| |
| /// We can't use RtlGenRandom with UWP, so there is no fallback |
| #[cfg(target_vendor = "uwp")] |
| fn fallback_rng() -> Result<(u64, u64), io::Error> { |
| Err(io::const_io_error!(io::ErrorKind::Unsupported, "RtlGenRandom() not supported on UWP")) |
| } |