Skip to content

Commit

Permalink
Load DLL
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Richey <[email protected]>
  • Loading branch information
josephlr committed May 1, 2024
1 parent 7a3e407 commit 03abb05
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 48 deletions.
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ impl Error {
/// Called from an ES module on Node.js. This is unsupported, see:
/// <https://docs.rs/getrandom#nodejs-es-module-support>.
pub const NODE_ES_MODULE: Error = internal_error(14);
/// Unable to load Windows function from DLL.
pub const WINDOWS_LOAD_DLL: Error = internal_error(15);
/// Calling Windows ProcessPrng failed.
pub const WINDOWS_PROCESS_PRNG: Error = internal_error(16);

/// Codes below this point represent OS Errors (i.e. positive i32 values).
/// Codes at or above this point, but below [`Error::CUSTOM_START`] are
Expand Down Expand Up @@ -172,6 +176,8 @@ fn internal_desc(error: Error) -> Option<&'static str> {
Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"),
Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"),
Error::WINDOWS_LOAD_DLL => Some("Unable to load function from Windows DLL"),
Error::WINDOWS_PROCESS_PRNG => Some("ProcessPrng: Windows system function failure"),
_ => None,
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ cfg_if! {
#[path = "solid.rs"] mod imp;
} else if #[cfg(target_os = "espidf")] {
#[path = "espidf.rs"] mod imp;
} else if #[cfg(all(windows, target_vendor = "win7"))] {
#[path = "windows7.rs"] mod imp;
} else if #[cfg(windows)] {
#[path = "windows.rs"] mod imp;
} else if #[cfg(all(target_os = "horizon", target_arch = "arm"))] {
Expand Down
67 changes: 19 additions & 48 deletions src/windows.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,30 @@
//! Implementation for Windows
use crate::Error;
use core::{ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr};
use core::mem::MaybeUninit;

const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
type HMODULE = isize;
type BOOL = i32;
const TRUE: BOOL = 1;

#[link(name = "bcrypt")]
extern "system" {
fn BCryptGenRandom(
hAlgorithm: *mut c_void,
pBuffer: *mut u8,
cbBuffer: u32,
dwFlags: u32,
) -> u32;
}
type ProcessPrng = unsafe extern "system" fn(*mut u8, usize) -> BOOL;

// Forbidden when targetting UWP
#[cfg(not(target_vendor = "uwp"))]
#[link(name = "advapi32")]
#[link(name = "kernel32")]
extern "system" {
#[link_name = "SystemFunction036"]
fn RtlGenRandom(RandomBuffer: *mut c_void, RandomBufferLength: u32) -> u8;
fn LoadLibraryA(libfilename: *const u8) -> HMODULE;
fn GetProcAddress(hmodule: HMODULE, procname: *const u8) -> Option<ProcessPrng>;
}

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 {
// Failed. Try RtlGenRandom as a fallback.
#[cfg(not(target_vendor = "uwp"))]
{
let ret =
unsafe { RtlGenRandom(chunk.as_mut_ptr() as *mut c_void, chunk.len() as u32) };
if ret != 0 {
continue;
}
}
// 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));
}
let dll = unsafe { LoadLibraryA(b"bcryptprimitives.dll\0".as_ptr()) };
if dll == 0 {
return Err(Error::WINDOWS_LOAD_DLL);
}
let process_prng = match unsafe { GetProcAddress(dll, b"ProcessPrng\0".as_ptr()) } {
Some(p) => p,
None => return Err(Error::WINDOWS_LOAD_DLL),
};
match unsafe { process_prng(dest.as_mut_ptr() as *mut u8, dest.len()) } {
TRUE => Ok(()),
_ => Err(Error::WINDOWS_PROCESS_PRNG),
}
Ok(())
}
23 changes: 23 additions & 0 deletions src/windows7.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::Error;
use core::{ffi::c_void, mem::MaybeUninit};

type BOOLEAN = u8;
type ULONG = u32;
const TRUE: BOOLEAN = 1;

#[link(name = "advapi32")]
extern "system" {
#[link_name = "SystemFunction036"]
fn RtlGenRandom(RandomBuffer: *mut c_void, RandomBufferLength: ULONG) -> BOOLEAN;
}

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// Prevent overflow of ULONG
for chunk in dest.chunks_mut(u32::max_value() as usize) {
let ret = unsafe { RtlGenRandom(chunk.as_mut_ptr() as *mut c_void, chunk.len() as u32) };
if ret != TRUE {
return Err(Error::WINDOWS_RTL_GEN_RANDOM);
}
}
Ok(())
}

0 comments on commit 03abb05

Please sign in to comment.