13
13
//! but significant number of users to experience panics caused by a failure of
14
14
//! this function. See [#94098].
15
15
//!
16
- //! The current version changes this to use the `BCRYPT_RNG_ALG_HANDLE`
17
- //! [Pseudo-handle], which gets the default RNG algorithm without querying the
18
- //! system preference thus hopefully avoiding the previous issue.
19
- //! This is only supported on Windows 10+ so a fallback is used for older versions.
16
+ //! The current version falls back to using `BCryptOpenAlgorithmProvider` if
17
+ //! `BCRYPT_USE_SYSTEM_PREFERRED_RNG` fails for any reason.
20
18
//!
21
19
//! [#94098]: https://github.com/rust-lang/rust/issues/94098
22
20
//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
23
21
//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
24
- //! [Pseudo-handle]: https://docs.microsoft.com/en-us/windows/win32/seccng/cng-algorithm-pseudo-handles
25
22
use crate :: mem;
26
23
use crate :: ptr;
27
24
use crate :: sys:: c;
@@ -33,37 +30,35 @@ use crate::sys::c;
33
30
/// [`HashMap`]: crate::collections::HashMap
34
31
/// [`RandomState`]: crate::collections::hash_map::RandomState
35
32
pub fn hashmap_random_keys ( ) -> ( u64 , u64 ) {
36
- Rng :: open ( ) . and_then ( |rng| rng . gen_random_keys ( ) ) . unwrap_or_else ( fallback_rng)
33
+ Rng :: SYSTEM . gen_random_keys ( ) . unwrap_or_else ( fallback_rng)
37
34
}
38
35
39
- struct Rng ( c:: BCRYPT_ALG_HANDLE ) ;
36
+ struct Rng {
37
+ algorithm : c:: BCRYPT_ALG_HANDLE ,
38
+ flags : u32 ,
39
+ }
40
40
impl Rng {
41
- #[ cfg( miri) ]
42
- fn open ( ) -> Result < Self , c:: NTSTATUS > {
43
- const BCRYPT_RNG_ALG_HANDLE : c:: BCRYPT_ALG_HANDLE = ptr:: invalid_mut ( 0x81 ) ;
44
- let _ = (
45
- c:: BCryptOpenAlgorithmProvider ,
46
- c:: BCryptCloseAlgorithmProvider ,
47
- c:: BCRYPT_RNG_ALGORITHM ,
48
- c:: STATUS_NOT_SUPPORTED ,
49
- ) ;
50
- Ok ( Self ( BCRYPT_RNG_ALG_HANDLE ) )
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 }
51
50
}
52
- # [ cfg ( not ( miri ) ) ]
53
- // Open a handle to the RNG algorithm.
51
+
52
+ /// Open a handle to the RNG algorithm.
54
53
fn open ( ) -> Result < Self , c:: NTSTATUS > {
55
54
use crate :: sync:: atomic:: AtomicPtr ;
56
55
use crate :: sync:: atomic:: Ordering :: { Acquire , Release } ;
57
- const ERROR_VALUE : c:: LPVOID = ptr:: invalid_mut ( usize:: MAX ) ;
58
56
59
57
// An atomic is used so we don't need to reopen the handle every time.
60
58
static HANDLE : AtomicPtr < crate :: ffi:: c_void > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
61
59
62
60
let mut handle = HANDLE . load ( Acquire ) ;
63
- // We use a sentinel value to designate an error occurred last time.
64
- if handle == ERROR_VALUE {
65
- Err ( c:: STATUS_NOT_SUPPORTED )
66
- } else if handle. is_null ( ) {
61
+ if handle. is_null ( ) {
67
62
let status = unsafe {
68
63
c:: BCryptOpenAlgorithmProvider (
69
64
& mut handle,
@@ -80,47 +75,32 @@ impl Rng {
80
75
unsafe { c:: BCryptCloseAlgorithmProvider ( handle, 0 ) } ;
81
76
handle = previous_handle;
82
77
}
83
- Ok ( Self ( handle) )
78
+ Ok ( unsafe { Self :: new ( handle, 0 ) } )
84
79
} else {
85
- HANDLE . store ( ERROR_VALUE , Release ) ;
86
80
Err ( status)
87
81
}
88
82
} else {
89
- Ok ( Self ( handle) )
83
+ Ok ( unsafe { Self :: new ( handle, 0 ) } )
90
84
}
91
85
}
92
86
93
87
fn gen_random_keys ( self ) -> Result < ( u64 , u64 ) , c:: NTSTATUS > {
94
88
let mut v = ( 0 , 0 ) ;
95
89
let status = unsafe {
96
90
let size = mem:: size_of_val ( & v) . try_into ( ) . unwrap ( ) ;
97
- c:: BCryptGenRandom ( self . 0 , ptr:: addr_of_mut!( v) . cast ( ) , size, 0 )
91
+ c:: BCryptGenRandom ( self . algorithm , ptr:: addr_of_mut!( v) . cast ( ) , size, self . flags )
98
92
} ;
99
93
if c:: nt_success ( status) { Ok ( v) } else { Err ( status) }
100
94
}
101
95
}
102
96
103
- /// Generate random numbers using the fallback RNG function (RtlGenRandom)
104
- #[ cfg( not( target_vendor = "uwp" ) ) ]
97
+ /// Generate random numbers using the fallback RNG function
105
98
#[ inline( never) ]
106
99
fn fallback_rng ( rng_status : c:: NTSTATUS ) -> ( u64 , u64 ) {
107
- let mut v = ( 0 , 0 ) ;
108
- let ret =
109
- unsafe { c:: RtlGenRandom ( & mut v as * mut _ as * mut u8 , mem:: size_of_val ( & v) as c:: ULONG ) } ;
110
-
111
- if ret != 0 {
112
- v
113
- } else {
114
- panic ! (
115
- "RNG broken: {rng_status:#x}, fallback RNG broken: {}" ,
116
- crate :: io:: Error :: last_os_error( )
117
- )
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
+ }
118
105
}
119
106
}
120
-
121
- /// We can't use RtlGenRandom with UWP, so there is no fallback
122
- #[ cfg( target_vendor = "uwp" ) ]
123
- #[ inline( never) ]
124
- fn fallback_rng ( rng_status : c:: NTSTATUS ) -> ( u64 , u64 ) {
125
- panic ! ( "RNG broken: {rng_status:#x} fallback RNG broken: RtlGenRandom() not supported on UWP" ) ;
126
- }
0 commit comments