6
6
// option. This file may not be copied, modified, or distributed
7
7
// except according to those terms.
8
8
9
+ // Implementation copied from rustc's `library/std/src/sys/windows/rand.rs`.
10
+ // Includes a fallback to `RtlGenRandom` in case `BCryptGenRandom` fails.
11
+
9
12
use crate :: Error ;
10
13
use core:: { ffi:: c_void, num:: NonZeroU32 , ptr} ;
14
+ use once_cell:: sync:: OnceCell ;
11
15
12
16
const BCRYPT_USE_SYSTEM_PREFERRED_RNG : u32 = 0x00000002 ;
13
17
18
+ /// The kinds of RNG that may be available
19
+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
20
+ enum Rng {
21
+ Preferred ,
22
+ Fallback ,
23
+ }
24
+
14
25
#[ link( name = "bcrypt" ) ]
15
26
extern "system" {
16
27
fn BCryptGenRandom (
@@ -21,7 +32,59 @@ extern "system" {
21
32
) -> u32 ;
22
33
}
23
34
35
+ #[ link( name = "advapi32" ) ]
36
+ extern "system" {
37
+ // Forbidden when targeting UWP
38
+ #[ link_name = "SystemFunction036" ]
39
+ pub fn RtlGenRandom ( RandomBuffer : * mut u8 , RandomBufferLength : u32 ) -> u8 ;
40
+ }
41
+
24
42
pub fn getrandom_inner ( dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
43
+ match get_rng ( ) {
44
+ Rng :: Preferred => {
45
+ preferred_rng ( dest)
46
+ }
47
+ Rng :: Fallback => {
48
+ fallback_rng ( dest)
49
+ }
50
+ }
51
+ }
52
+
53
+ /// Returns the RNG that should be used
54
+ ///
55
+ /// Panics if they are both broken
56
+ fn get_rng ( ) -> Rng {
57
+ // Assume that if the preferred RNG is broken the first time we use it, it likely means
58
+ // that: the DLL has failed to load, there is no point to calling it over-and-over again,
59
+ // and we should cache the result
60
+ static VALUE : OnceCell < Rng > = OnceCell :: new ( ) ;
61
+ * VALUE . get_or_init ( choose_rng)
62
+ }
63
+
64
+ /// Test whether we should use the preferred or fallback RNG
65
+ ///
66
+ /// If the preferred RNG is successful, we choose it. Otherwise, if the fallback RNG is successful,
67
+ /// we choose that
68
+ ///
69
+ /// Panics if both the preferred and the fallback RNG are both non-functional
70
+ fn choose_rng ( ) -> Rng {
71
+ let mut dest = [ 0 ; 1 ] ;
72
+
73
+ let preferred_error = match preferred_rng ( & mut dest) {
74
+ Ok ( _) => return Rng :: Preferred ,
75
+ Err ( e) => e,
76
+ } ;
77
+
78
+ match fallback_rng ( & mut dest) {
79
+ Ok ( _) => return Rng :: Fallback ,
80
+ Err ( fallback_error) => panic ! (
81
+ "preferred RNG broken: `{}`, fallback RNG broken: `{}`" ,
82
+ preferred_error, fallback_error
83
+ ) ,
84
+ }
85
+ }
86
+
87
+ fn preferred_rng ( dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
25
88
// Prevent overflow of u32
26
89
for chunk in dest. chunks_mut ( u32:: max_value ( ) as usize ) {
27
90
// BCryptGenRandom was introduced in Windows Vista
@@ -33,6 +96,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
33
96
BCRYPT_USE_SYSTEM_PREFERRED_RNG ,
34
97
)
35
98
} ;
99
+
36
100
// NTSTATUS codes use the two highest bits for severity status.
37
101
if ret >> 30 == 0b11 {
38
102
// We zeroize the highest bit, so the error code will reside
@@ -47,3 +111,22 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
47
111
}
48
112
Ok ( ( ) )
49
113
}
114
+
115
+ /// Generate random numbers using the fallback RNG function (RtlGenRandom)
116
+ #[ cfg( not( target_vendor = "uwp" ) ) ]
117
+ fn fallback_rng ( dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
118
+ // Prevent overflow of u32
119
+ for chunk in dest. chunks_mut ( u32:: max_value ( ) as usize ) {
120
+ let ret = unsafe { RtlGenRandom ( chunk. as_mut_ptr ( ) , chunk. len ( ) as u32 ) } ;
121
+ if ret == 0 {
122
+ return Err ( Error :: WINDOWS_RTL_GEN_RANDOM ) ;
123
+ }
124
+ }
125
+ Ok ( ( ) )
126
+ }
127
+
128
+ /// We can't use RtlGenRandom with UWP, so there is no fallback
129
+ #[ cfg( target_vendor = "uwp" ) ]
130
+ fn fallback_rng ( _dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
131
+ Err ( Error :: UNSUPPORTED )
132
+ }
0 commit comments