77// except according to those terms.
88
99use crate :: Error ;
10- use core:: { ffi:: c_void, mem:: MaybeUninit , num:: NonZeroU32 , ptr} ;
10+ use core:: { convert :: TryInto , ffi:: c_void, mem:: MaybeUninit , num:: NonZeroU32 , ptr} ;
1111
1212const BCRYPT_USE_SYSTEM_PREFERRED_RNG : u32 = 0x00000002 ;
1313
@@ -21,29 +21,41 @@ extern "system" {
2121 ) -> u32 ;
2222}
2323
24+ // BCryptGenRandom was introduced in Windows Vista. However, CNG Algorithm
25+ // Pseudo-handles (specifically BCRYPT_RNG_ALG_HANDLE) weren't introduced
26+ // until Windows 10, so we cannot use them yet. Note that on older systems
27+ // these Pseudo-handles are interpreted as pointers, causing crashes if used.
28+ fn bcrypt_random ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
29+ // Will always succeed given the chunking in getrandom_inner().
30+ let len: u32 = dest. len ( ) . try_into ( ) . unwrap ( ) ;
31+ // SAFETY: dest is valid, writable buffer of length len
32+ let ret = unsafe {
33+ BCryptGenRandom (
34+ ptr:: null_mut ( ) ,
35+ dest. as_mut_ptr ( ) as * mut u8 ,
36+ len,
37+ BCRYPT_USE_SYSTEM_PREFERRED_RNG ,
38+ )
39+ } ;
40+
41+ // NTSTATUS codes use the two highest bits for severity status.
42+ if ret >> 30 != 0b11 {
43+ return Ok ( ( ) ) ;
44+ }
45+ // We zeroize the highest bit, so the error code will reside
46+ // inside the range designated for OS codes.
47+ let code = ret ^ ( 1 << 31 ) ;
48+ // SAFETY: the second highest bit is always equal to one,
49+ // so it's impossible to get zero. Unfortunately the type
50+ // system does not have a way to express this yet.
51+ let code = unsafe { NonZeroU32 :: new_unchecked ( code) } ;
52+ Err ( Error :: from ( code) )
53+ }
54+
2455pub fn getrandom_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
2556 // Prevent overflow of u32
2657 for chunk in dest. chunks_mut ( u32:: max_value ( ) as usize ) {
27- // BCryptGenRandom was introduced in Windows Vista
28- let ret = unsafe {
29- BCryptGenRandom (
30- ptr:: null_mut ( ) ,
31- chunk. as_mut_ptr ( ) as * mut u8 ,
32- chunk. len ( ) as u32 ,
33- BCRYPT_USE_SYSTEM_PREFERRED_RNG ,
34- )
35- } ;
36- // NTSTATUS codes use the two highest bits for severity status.
37- if ret >> 30 == 0b11 {
38- // We zeroize the highest bit, so the error code will reside
39- // inside the range designated for OS codes.
40- let code = ret ^ ( 1 << 31 ) ;
41- // SAFETY: the second highest bit is always equal to one,
42- // so it's impossible to get zero. Unfortunately the type
43- // system does not have a way to express this yet.
44- let code = unsafe { NonZeroU32 :: new_unchecked ( code) } ;
45- return Err ( Error :: from ( code) ) ;
46- }
58+ bcrypt_random ( chunk) ?;
4759 }
4860 Ok ( ( ) )
4961}
0 commit comments