From b3cea335937e7a481b9aa9f4437383b2df2c832e Mon Sep 17 00:00:00 2001 From: Chris H-C Date: Thu, 16 Feb 2023 15:10:04 -0500 Subject: [PATCH] Add in a RtlGenRandom fallback for non-UWP Windows 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 --- src/windows.rs | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index e5a626c0..1282a92b 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -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]) -> Result<(), Error> { // Prevent overflow of u32 for chunk in dest.chunks_mut(u32::max_value() as usize) { @@ -33,17 +39,33 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> 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 ret = fallback_rng(chunk); + // 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)); + } } } Ok(()) } + +#[cfg(not(target_vendor = "uwp"))] +#[inline(never)] +fn fallback_rng(chunk: &mut [MaybeUninit]) -> 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 { + return Error::WINDOWS_RTL_GEN_RANDOM; +}