Skip to content

Commit d233b32

Browse files
josephlrbadboy
authored andcommitted
windows: move BCryptGenRandom to helper function
This _is not_ a functional change. It just makes adding additional fallback implementations easier in the future. Note that the try_into().unwrap() is optimized away: https://godbolt.org/z/nbThsqsTE Signed-off-by: Joe Richey <[email protected]>
1 parent 7f73e3c commit d233b32

File tree

1 file changed

+33
-21
lines changed

1 file changed

+33
-21
lines changed

src/windows.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// except according to those terms.
88

99
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};
1111

1212
const 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+
2455
pub 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

Comments
 (0)