]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/windows/rand.rs
Auto merge of #99918 - WaffleLapkin:fnFnfun, r=estebank
[rust.git] / library / std / src / sys / windows / rand.rs
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
22 use crate::mem;
23 use crate::ptr;
24 use crate::sys::c;
25
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 pub fn hashmap_random_keys() -> (u64, u64) {
33     Rng::SYSTEM.gen_random_keys().unwrap_or_else(fallback_rng)
34 }
35
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     }
86
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     }
95 }
96
97 /// Generate random numbers using the fallback RNG function
98 #[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     }
106 }