|  | 
| 1 |  | -//! # Random key generation | 
| 2 |  | -//! | 
| 3 |  | -//! This module wraps the RNG provided by the OS. There are a few different | 
| 4 |  | -//! ways to interface with the OS RNG so it's worth exploring each of the options. | 
| 5 |  | -//! Note that at the time of writing these all go through the (undocumented) | 
| 6 |  | -//! `bcryptPrimitives.dll` but they use different route to get there. | 
| 7 |  | -//! | 
| 8 |  | -//! Originally we were using [`RtlGenRandom`], however that function is | 
| 9 |  | -//! deprecated and warns it "may be altered or unavailable in subsequent versions". | 
| 10 |  | -//! | 
| 11 |  | -//! So we switched to [`BCryptGenRandom`] with the `BCRYPT_USE_SYSTEM_PREFERRED_RNG` | 
| 12 |  | -//! flag to query and find the system configured RNG. However, this change caused a small | 
| 13 |  | -//! but significant number of users to experience panics caused by a failure of | 
| 14 |  | -//! this function. See [#94098]. | 
| 15 |  | -//! | 
| 16 |  | -//! The current version falls back to using `BCryptOpenAlgorithmProvider` if | 
| 17 |  | -//! `BCRYPT_USE_SYSTEM_PREFERRED_RNG` fails for any reason. | 
| 18 |  | -//! | 
| 19 |  | -//! [#94098]: https://github.com/rust-lang/rust/issues/94098 | 
| 20 |  | -//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom | 
| 21 |  | -//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom | 
|  | 1 | +use crate::io; | 
| 22 | 2 | use crate::mem; | 
| 23 | 3 | use crate::ptr; | 
| 24 | 4 | use crate::sys::c; | 
| 25 | 5 | 
 | 
| 26 |  | -/// Generates high quality secure random keys for use by [`HashMap`]. | 
| 27 |  | -/// | 
| 28 |  | -/// This is used to seed the default [`RandomState`]. | 
| 29 |  | -/// | 
| 30 |  | -/// [`HashMap`]: crate::collections::HashMap | 
| 31 |  | -/// [`RandomState`]: crate::collections::hash_map::RandomState | 
| 32 | 6 | pub fn hashmap_random_keys() -> (u64, u64) { | 
| 33 |  | -    Rng::SYSTEM.gen_random_keys().unwrap_or_else(fallback_rng) | 
|  | 7 | +    let mut v = (0, 0); | 
|  | 8 | +    let ret = unsafe { | 
|  | 9 | +        c::BCryptGenRandom( | 
|  | 10 | +            ptr::null_mut(), | 
|  | 11 | +            &mut v as *mut _ as *mut u8, | 
|  | 12 | +            mem::size_of_val(&v) as c::ULONG, | 
|  | 13 | +            c::BCRYPT_USE_SYSTEM_PREFERRED_RNG, | 
|  | 14 | +        ) | 
|  | 15 | +    }; | 
|  | 16 | +    if c::nt_success(ret) { v } else { fallback_rng() } | 
| 34 | 17 | } | 
| 35 | 18 | 
 | 
| 36 |  | -struct Rng { | 
| 37 |  | -    algorithm: c::BCRYPT_ALG_HANDLE, | 
| 38 |  | -    flags: u32, | 
| 39 |  | -} | 
| 40 |  | -impl Rng { | 
| 41 |  | -    const SYSTEM: Self = unsafe { Self::new(ptr::null_mut(), c::BCRYPT_USE_SYSTEM_PREFERRED_RNG) }; | 
| 42 |  | - | 
| 43 |  | -    /// Create the RNG from an existing algorithm handle. | 
| 44 |  | -    /// | 
| 45 |  | -    /// # Safety | 
| 46 |  | -    /// | 
| 47 |  | -    /// The handle must either be null or a valid algorithm handle. | 
| 48 |  | -    const unsafe fn new(algorithm: c::BCRYPT_ALG_HANDLE, flags: u32) -> Self { | 
| 49 |  | -        Self { algorithm, flags } | 
| 50 |  | -    } | 
| 51 |  | - | 
| 52 |  | -    /// Open a handle to the RNG algorithm. | 
| 53 |  | -    fn open() -> Result<Self, c::NTSTATUS> { | 
| 54 |  | -        use crate::sync::atomic::AtomicPtr; | 
| 55 |  | -        use crate::sync::atomic::Ordering::{Acquire, Release}; | 
| 56 |  | - | 
| 57 |  | -        // An atomic is used so we don't need to reopen the handle every time. | 
| 58 |  | -        static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(ptr::null_mut()); | 
| 59 |  | - | 
| 60 |  | -        let mut handle = HANDLE.load(Acquire); | 
| 61 |  | -        if handle.is_null() { | 
| 62 |  | -            let status = unsafe { | 
| 63 |  | -                c::BCryptOpenAlgorithmProvider( | 
| 64 |  | -                    &mut handle, | 
| 65 |  | -                    c::BCRYPT_RNG_ALGORITHM.as_ptr(), | 
| 66 |  | -                    ptr::null(), | 
| 67 |  | -                    0, | 
| 68 |  | -                ) | 
| 69 |  | -            }; | 
| 70 |  | -            if c::nt_success(status) { | 
| 71 |  | -                // If another thread opens a handle first then use that handle instead. | 
| 72 |  | -                let result = HANDLE.compare_exchange(ptr::null_mut(), handle, Release, Acquire); | 
| 73 |  | -                if let Err(previous_handle) = result { | 
| 74 |  | -                    // Close our handle and return the previous one. | 
| 75 |  | -                    unsafe { c::BCryptCloseAlgorithmProvider(handle, 0) }; | 
| 76 |  | -                    handle = previous_handle; | 
| 77 |  | -                } | 
| 78 |  | -                Ok(unsafe { Self::new(handle, 0) }) | 
| 79 |  | -            } else { | 
| 80 |  | -                Err(status) | 
| 81 |  | -            } | 
| 82 |  | -        } else { | 
| 83 |  | -            Ok(unsafe { Self::new(handle, 0) }) | 
| 84 |  | -        } | 
| 85 |  | -    } | 
|  | 19 | +/// Generate random numbers using the fallback RNG function (RtlGenRandom) | 
|  | 20 | +/// | 
|  | 21 | +/// This is necessary because of a failure to load the SysWOW64 variant of the | 
|  | 22 | +/// bcryptprimitives.dll library from code that lives in bcrypt.dll | 
|  | 23 | +/// See <https://bugzilla.mozilla.org/show_bug.cgi?id=1788004#c9> | 
|  | 24 | +#[cfg(not(target_vendor = "uwp"))] | 
|  | 25 | +#[inline(never)] | 
|  | 26 | +fn fallback_rng() -> (u64, u64) { | 
|  | 27 | +    let mut v = (0, 0); | 
|  | 28 | +    let ret = | 
|  | 29 | +        unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) }; | 
| 86 | 30 | 
 | 
| 87 |  | -    fn gen_random_keys(self) -> Result<(u64, u64), c::NTSTATUS> { | 
| 88 |  | -        let mut v = (0, 0); | 
| 89 |  | -        let status = unsafe { | 
| 90 |  | -            let size = mem::size_of_val(&v).try_into().unwrap(); | 
| 91 |  | -            c::BCryptGenRandom(self.algorithm, ptr::addr_of_mut!(v).cast(), size, self.flags) | 
| 92 |  | -        }; | 
| 93 |  | -        if c::nt_success(status) { Ok(v) } else { Err(status) } | 
| 94 |  | -    } | 
|  | 31 | +    if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) } | 
| 95 | 32 | } | 
| 96 | 33 | 
 | 
| 97 |  | -/// Generate random numbers using the fallback RNG function | 
|  | 34 | +/// We can't use RtlGenRandom with UWP, so there is no fallback | 
|  | 35 | +#[cfg(target_vendor = "uwp")] | 
| 98 | 36 | #[inline(never)] | 
| 99 |  | -fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) { | 
| 100 |  | -    match Rng::open().and_then(|rng| rng.gen_random_keys()) { | 
| 101 |  | -        Ok(keys) => keys, | 
| 102 |  | -        Err(status) => { | 
| 103 |  | -            panic!("RNG broken: {rng_status:#x}, fallback RNG broken: {status:#x}") | 
| 104 |  | -        } | 
| 105 |  | -    } | 
|  | 37 | +fn fallback_rng() -> (u64, u64) { | 
|  | 38 | +    panic!("fallback RNG broken: RtlGenRandom() not supported on UWP"); | 
| 106 | 39 | } | 
0 commit comments