Skip to content

Commit

Permalink
Add in a RtlGenRandom fallback for non-UWP Windows
Browse files Browse the repository at this point in the history
In some instances BCryptRandom will fail when RtlGenRandom will work.
On UWP, we might be unable to actually use RtlGenRandom.

Thread the needle and use RtlGenRandom when we have to, when we're able.

See also rust-lang/rust#108060

Fixes #314
  • Loading branch information
chutten committed Feb 16, 2023
1 parent 7c83ea0 commit ab8c2d5
Showing 1 changed file with 32 additions and 10 deletions.
42 changes: 32 additions & 10 deletions src/windows.rs
Expand Up @@ -21,6 +21,12 @@ extern "system" {
) -> u32;
}

extern "system" {
// Forbidden when targetting UWP
#[link_name = "SystemFunction036"]
fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: u32) -> u8;
}

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) {
Expand All @@ -33,17 +39,33 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
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));
if ret > 0 {
// Failed. Try RtlGenRandom as a fallback.
let fallback_ret = fallback_rng(chunk);
// NTSTATUS codes use the two highest bits for severity status.
if fallback_ret > 0 {
// 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));
}
}
}
Ok(())
}

#[cfg(not(target_vendor = "uwp"))]
#[inline(never)]
fn fallback_rng(chunk: &mut [MaybeUninit<u8>]) -> u8 {
unsafe { RtlGenRandom(chunk.as_mut_ptr() as *mut u8, chunk.len() as u32) }
}

#[cfg(target_vendor = "uwp")]
#[inline(never)]
fn fallback_rng(_chunk: &mut [MaybeUninit<u8>]) -> u8 {
return Error::WINDOWS_RTL_GEN_RANDOM;
}

0 comments on commit ab8c2d5

Please sign in to comment.