Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

windows: move BCryptGenRandom to helper function #318

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 33 additions & 21 deletions src/windows.rs
Expand Up @@ -7,7 +7,7 @@
// except according to those terms.

use crate::Error;
use core::{ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr};
use core::{convert::TryInto, ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr};

const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;

Expand All @@ -21,29 +21,41 @@ extern "system" {
) -> u32;
}

// BCryptGenRandom was introduced in Windows Vista. However, CNG Algorithm
// Pseudo-handles (specifically BCRYPT_RNG_ALG_HANDLE) weren't introduced
// until Windows 10, so we cannot use them yet. Note that on older systems
// these Pseudo-handles are interpreted as pointers, causing crashes if used.
fn bcrypt_random(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// Will always succeed given the chunking in getrandom_inner().
let len: u32 = dest.len().try_into().unwrap();
// SAFETY: dest is valid, writable buffer of length len
let ret = unsafe {
BCryptGenRandom(
ptr::null_mut(),
dest.as_mut_ptr() as *mut u8,
len,
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};

// NTSTATUS codes use the two highest bits for severity status.
if ret >> 30 != 0b11 {
return Ok(());
}
// We zeroize the highest bit, so the error code will reside
// inside the range designated for OS codes.
let code = ret ^ (1 << 31);
// SAFETY: the second highest bit is always equal to one,
// so it's impossible to get zero. Unfortunately the type
// system does not have a way to express this yet.
let code = unsafe { NonZeroU32::new_unchecked(code) };
Err(Error::from(code))
}

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// Prevent overflow of u32
for chunk in dest.chunks_mut(u32::max_value() as usize) {
// BCryptGenRandom was introduced in Windows Vista
let ret = unsafe {
BCryptGenRandom(
ptr::null_mut(),
chunk.as_mut_ptr() as *mut u8,
chunk.len() as u32,
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};
// NTSTATUS codes use the two highest bits for severity status.
if ret >> 30 == 0b11 {
// We zeroize the highest bit, so the error code will reside
// inside the range designated for OS codes.
let code = ret ^ (1 << 31);
// SAFETY: the second highest bit is always equal to one,
// so it's impossible to get zero. Unfortunately the type
// system does not have a way to express this yet.
let code = unsafe { NonZeroU32::new_unchecked(code) };
return Err(Error::from(code));
}
bcrypt_random(chunk)?;
}
Ok(())
}