Rollup merge of #108060 - ChrisDenton:rtlgenrandom, r=thomcc
Revert to using `RtlGenRandom` as a fallback This is required due to `BCryptGenRandom` failing to load a dll it depends on. Fixes #108059
This commit is contained in:
commit
ef6de70c77
2 changed files with 34 additions and 106 deletions
|
@ -295,8 +295,6 @@ pub fn nt_success(status: NTSTATUS) -> bool {
|
||||||
status >= 0
|
status >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// "RNG\0"
|
|
||||||
pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0];
|
|
||||||
pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002;
|
pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -834,6 +832,10 @@ if #[cfg(not(target_vendor = "uwp"))] {
|
||||||
|
|
||||||
#[link(name = "advapi32")]
|
#[link(name = "advapi32")]
|
||||||
extern "system" {
|
extern "system" {
|
||||||
|
// Forbidden when targeting UWP
|
||||||
|
#[link_name = "SystemFunction036"]
|
||||||
|
pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN;
|
||||||
|
|
||||||
// Allowed but unused by UWP
|
// Allowed but unused by UWP
|
||||||
pub fn OpenProcessToken(
|
pub fn OpenProcessToken(
|
||||||
ProcessHandle: HANDLE,
|
ProcessHandle: HANDLE,
|
||||||
|
@ -1258,13 +1260,6 @@ extern "system" {
|
||||||
cbBuffer: ULONG,
|
cbBuffer: ULONG,
|
||||||
dwFlags: ULONG,
|
dwFlags: ULONG,
|
||||||
) -> NTSTATUS;
|
) -> NTSTATUS;
|
||||||
pub fn BCryptOpenAlgorithmProvider(
|
|
||||||
phalgorithm: *mut BCRYPT_ALG_HANDLE,
|
|
||||||
pszAlgId: LPCWSTR,
|
|
||||||
pszimplementation: LPCWSTR,
|
|
||||||
dwflags: ULONG,
|
|
||||||
) -> NTSTATUS;
|
|
||||||
pub fn BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE, dwFlags: ULONG) -> NTSTATUS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions that aren't available on every version of Windows that we support,
|
// Functions that aren't available on every version of Windows that we support,
|
||||||
|
|
|
@ -1,106 +1,39 @@
|
||||||
//! # Random key generation
|
use crate::io;
|
||||||
//!
|
|
||||||
//! This module wraps the RNG provided by the OS. There are a few different
|
|
||||||
//! ways to interface with the OS RNG so it's worth exploring each of the options.
|
|
||||||
//! Note that at the time of writing these all go through the (undocumented)
|
|
||||||
//! `bcryptPrimitives.dll` but they use different route to get there.
|
|
||||||
//!
|
|
||||||
//! Originally we were using [`RtlGenRandom`], however that function is
|
|
||||||
//! deprecated and warns it "may be altered or unavailable in subsequent versions".
|
|
||||||
//!
|
|
||||||
//! So we switched to [`BCryptGenRandom`] with the `BCRYPT_USE_SYSTEM_PREFERRED_RNG`
|
|
||||||
//! flag to query and find the system configured RNG. However, this change caused a small
|
|
||||||
//! but significant number of users to experience panics caused by a failure of
|
|
||||||
//! this function. See [#94098].
|
|
||||||
//!
|
|
||||||
//! The current version falls back to using `BCryptOpenAlgorithmProvider` if
|
|
||||||
//! `BCRYPT_USE_SYSTEM_PREFERRED_RNG` fails for any reason.
|
|
||||||
//!
|
|
||||||
//! [#94098]: https://github.com/rust-lang/rust/issues/94098
|
|
||||||
//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
|
|
||||||
//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
|
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::ptr;
|
use crate::ptr;
|
||||||
use crate::sys::c;
|
use crate::sys::c;
|
||||||
|
|
||||||
/// Generates high quality secure random keys for use by [`HashMap`].
|
|
||||||
///
|
|
||||||
/// This is used to seed the default [`RandomState`].
|
|
||||||
///
|
|
||||||
/// [`HashMap`]: crate::collections::HashMap
|
|
||||||
/// [`RandomState`]: crate::collections::hash_map::RandomState
|
|
||||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||||
Rng::SYSTEM.gen_random_keys().unwrap_or_else(fallback_rng)
|
let mut v = (0, 0);
|
||||||
}
|
let ret = unsafe {
|
||||||
|
c::BCryptGenRandom(
|
||||||
struct Rng {
|
ptr::null_mut(),
|
||||||
algorithm: c::BCRYPT_ALG_HANDLE,
|
&mut v as *mut _ as *mut u8,
|
||||||
flags: u32,
|
mem::size_of_val(&v) as c::ULONG,
|
||||||
}
|
c::BCRYPT_USE_SYSTEM_PREFERRED_RNG,
|
||||||
impl Rng {
|
|
||||||
const SYSTEM: Self = unsafe { Self::new(ptr::null_mut(), c::BCRYPT_USE_SYSTEM_PREFERRED_RNG) };
|
|
||||||
|
|
||||||
/// Create the RNG from an existing algorithm handle.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The handle must either be null or a valid algorithm handle.
|
|
||||||
const unsafe fn new(algorithm: c::BCRYPT_ALG_HANDLE, flags: u32) -> Self {
|
|
||||||
Self { algorithm, flags }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open a handle to the RNG algorithm.
|
|
||||||
fn open() -> Result<Self, c::NTSTATUS> {
|
|
||||||
use crate::sync::atomic::AtomicPtr;
|
|
||||||
use crate::sync::atomic::Ordering::{Acquire, Release};
|
|
||||||
|
|
||||||
// An atomic is used so we don't need to reopen the handle every time.
|
|
||||||
static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(ptr::null_mut());
|
|
||||||
|
|
||||||
let mut handle = HANDLE.load(Acquire);
|
|
||||||
if handle.is_null() {
|
|
||||||
let status = unsafe {
|
|
||||||
c::BCryptOpenAlgorithmProvider(
|
|
||||||
&mut handle,
|
|
||||||
c::BCRYPT_RNG_ALGORITHM.as_ptr(),
|
|
||||||
ptr::null(),
|
|
||||||
0,
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if c::nt_success(status) {
|
if c::nt_success(ret) { v } else { fallback_rng() }
|
||||||
// If another thread opens a handle first then use that handle instead.
|
|
||||||
let result = HANDLE.compare_exchange(ptr::null_mut(), handle, Release, Acquire);
|
|
||||||
if let Err(previous_handle) = result {
|
|
||||||
// Close our handle and return the previous one.
|
|
||||||
unsafe { c::BCryptCloseAlgorithmProvider(handle, 0) };
|
|
||||||
handle = previous_handle;
|
|
||||||
}
|
|
||||||
Ok(unsafe { Self::new(handle, 0) })
|
|
||||||
} else {
|
|
||||||
Err(status)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(unsafe { Self::new(handle, 0) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_random_keys(self) -> Result<(u64, u64), c::NTSTATUS> {
|
|
||||||
let mut v = (0, 0);
|
|
||||||
let status = unsafe {
|
|
||||||
let size = mem::size_of_val(&v).try_into().unwrap();
|
|
||||||
c::BCryptGenRandom(self.algorithm, ptr::addr_of_mut!(v).cast(), size, self.flags)
|
|
||||||
};
|
|
||||||
if c::nt_success(status) { Ok(v) } else { Err(status) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate random numbers using the fallback RNG function
|
/// Generate random numbers using the fallback RNG function (RtlGenRandom)
|
||||||
|
///
|
||||||
|
/// This is necessary because of a failure to load the SysWOW64 variant of the
|
||||||
|
/// bcryptprimitives.dll library from code that lives in bcrypt.dll
|
||||||
|
/// See <https://bugzilla.mozilla.org/show_bug.cgi?id=1788004#c9>
|
||||||
|
#[cfg(not(target_vendor = "uwp"))]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) {
|
fn fallback_rng() -> (u64, u64) {
|
||||||
match Rng::open().and_then(|rng| rng.gen_random_keys()) {
|
let mut v = (0, 0);
|
||||||
Ok(keys) => keys,
|
let ret =
|
||||||
Err(status) => {
|
unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) };
|
||||||
panic!("RNG broken: {rng_status:#x}, fallback RNG broken: {status:#x}")
|
|
||||||
}
|
if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We can't use RtlGenRandom with UWP, so there is no fallback
|
||||||
|
#[cfg(target_vendor = "uwp")]
|
||||||
|
#[inline(never)]
|
||||||
|
fn fallback_rng() -> (u64, u64) {
|
||||||
|
panic!("fallback RNG broken: RtlGenRandom() not supported on UWP");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue