66// option. This file may not be copied, modified, or distributed
77// except according to those terms.
88
9+ // Implementation copied from rustc's `library/std/src/sys/windows/rand.rs`.
10+ // Includes a fallback to `RtlGenRandom` in case `BCryptGenRandom` fails.
11+
912use crate :: Error ;
1013use core:: { ffi:: c_void, num:: NonZeroU32 , ptr} ;
14+ use once_cell:: sync:: OnceCell ;
1115
1216const BCRYPT_USE_SYSTEM_PREFERRED_RNG : u32 = 0x00000002 ;
1317
18+ /// The kinds of RNG that may be available
19+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
20+ enum Rng {
21+ Preferred ,
22+ Fallback ,
23+ }
24+
1425#[ link( name = "bcrypt" ) ]
1526extern "system" {
1627 fn BCryptGenRandom (
@@ -21,7 +32,59 @@ extern "system" {
2132 ) -> u32 ;
2233}
2334
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+
2442pub 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 > {
2588 // Prevent overflow of u32
2689 for chunk in dest. chunks_mut ( u32:: max_value ( ) as usize ) {
2790 // BCryptGenRandom was introduced in Windows Vista
@@ -33,6 +96,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
3396 BCRYPT_USE_SYSTEM_PREFERRED_RNG ,
3497 )
3598 } ;
99+
36100 // NTSTATUS codes use the two highest bits for severity status.
37101 if ret >> 30 == 0b11 {
38102 // We zeroize the highest bit, so the error code will reside
@@ -47,3 +111,22 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
47111 }
48112 Ok ( ( ) )
49113}
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