7
7
// except according to those terms.
8
8
9
9
use 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} ;
11
11
12
12
const BCRYPT_USE_SYSTEM_PREFERRED_RNG : u32 = 0x00000002 ;
13
13
@@ -21,29 +21,41 @@ extern "system" {
21
21
) -> u32 ;
22
22
}
23
23
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
+
24
55
pub fn getrandom_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
25
56
// Prevent overflow of u32
26
57
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) ?;
47
59
}
48
60
Ok ( ( ) )
49
61
}
0 commit comments