Skip to content

Commit 03abb05

Browse files
committed
Load DLL
Signed-off-by: Joe Richey <[email protected]>
1 parent 7a3e407 commit 03abb05

File tree

4 files changed

+50
-48
lines changed

4 files changed

+50
-48
lines changed

src/error.rs

+6
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ impl Error {
5353
/// Called from an ES module on Node.js. This is unsupported, see:
5454
/// <https://docs.rs/getrandom#nodejs-es-module-support>.
5555
pub const NODE_ES_MODULE: Error = internal_error(14);
56+
/// Unable to load Windows function from DLL.
57+
pub const WINDOWS_LOAD_DLL: Error = internal_error(15);
58+
/// Calling Windows ProcessPrng failed.
59+
pub const WINDOWS_PROCESS_PRNG: Error = internal_error(16);
5660

5761
/// Codes below this point represent OS Errors (i.e. positive i32 values).
5862
/// Codes at or above this point, but below [`Error::CUSTOM_START`] are
@@ -172,6 +176,8 @@ fn internal_desc(error: Error) -> Option<&'static str> {
172176
Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"),
173177
Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
174178
Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"),
179+
Error::WINDOWS_LOAD_DLL => Some("Unable to load function from Windows DLL"),
180+
Error::WINDOWS_PROCESS_PRNG => Some("ProcessPrng: Windows system function failure"),
175181
_ => None,
176182
}
177183
}

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ cfg_if! {
318318
#[path = "solid.rs"] mod imp;
319319
} else if #[cfg(target_os = "espidf")] {
320320
#[path = "espidf.rs"] mod imp;
321+
} else if #[cfg(all(windows, target_vendor = "win7"))] {
322+
#[path = "windows7.rs"] mod imp;
321323
} else if #[cfg(windows)] {
322324
#[path = "windows.rs"] mod imp;
323325
} else if #[cfg(all(target_os = "horizon", target_arch = "arm"))] {

src/windows.rs

+19-48
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,30 @@
11
//! Implementation for Windows
22
use crate::Error;
3-
use core::{ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr};
3+
use core::mem::MaybeUninit;
44

5-
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
5+
type HMODULE = isize;
6+
type BOOL = i32;
7+
const TRUE: BOOL = 1;
68

7-
#[link(name = "bcrypt")]
8-
extern "system" {
9-
fn BCryptGenRandom(
10-
hAlgorithm: *mut c_void,
11-
pBuffer: *mut u8,
12-
cbBuffer: u32,
13-
dwFlags: u32,
14-
) -> u32;
15-
}
9+
type ProcessPrng = unsafe extern "system" fn(*mut u8, usize) -> BOOL;
1610

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

2517
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
26-
// Prevent overflow of u32
27-
for chunk in dest.chunks_mut(u32::max_value() as usize) {
28-
// BCryptGenRandom was introduced in Windows Vista
29-
let ret = unsafe {
30-
BCryptGenRandom(
31-
ptr::null_mut(),
32-
chunk.as_mut_ptr() as *mut u8,
33-
chunk.len() as u32,
34-
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
35-
)
36-
};
37-
// NTSTATUS codes use the two highest bits for severity status.
38-
if ret >> 30 == 0b11 {
39-
// Failed. Try RtlGenRandom as a fallback.
40-
#[cfg(not(target_vendor = "uwp"))]
41-
{
42-
let ret =
43-
unsafe { RtlGenRandom(chunk.as_mut_ptr() as *mut c_void, chunk.len() as u32) };
44-
if ret != 0 {
45-
continue;
46-
}
47-
}
48-
// We zeroize the highest bit, so the error code will reside
49-
// inside the range designated for OS codes.
50-
let code = ret ^ (1 << 31);
51-
// SAFETY: the second highest bit is always equal to one,
52-
// so it's impossible to get zero. Unfortunately the type
53-
// system does not have a way to express this yet.
54-
let code = unsafe { NonZeroU32::new_unchecked(code) };
55-
return Err(Error::from(code));
56-
}
18+
let dll = unsafe { LoadLibraryA(b"bcryptprimitives.dll\0".as_ptr()) };
19+
if dll == 0 {
20+
return Err(Error::WINDOWS_LOAD_DLL);
21+
}
22+
let process_prng = match unsafe { GetProcAddress(dll, b"ProcessPrng\0".as_ptr()) } {
23+
Some(p) => p,
24+
None => return Err(Error::WINDOWS_LOAD_DLL),
25+
};
26+
match unsafe { process_prng(dest.as_mut_ptr() as *mut u8, dest.len()) } {
27+
TRUE => Ok(()),
28+
_ => Err(Error::WINDOWS_PROCESS_PRNG),
5729
}
58-
Ok(())
5930
}

src/windows7.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use crate::Error;
2+
use core::{ffi::c_void, mem::MaybeUninit};
3+
4+
type BOOLEAN = u8;
5+
type ULONG = u32;
6+
const TRUE: BOOLEAN = 1;
7+
8+
#[link(name = "advapi32")]
9+
extern "system" {
10+
#[link_name = "SystemFunction036"]
11+
fn RtlGenRandom(RandomBuffer: *mut c_void, RandomBufferLength: ULONG) -> BOOLEAN;
12+
}
13+
14+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
15+
// Prevent overflow of ULONG
16+
for chunk in dest.chunks_mut(u32::max_value() as usize) {
17+
let ret = unsafe { RtlGenRandom(chunk.as_mut_ptr() as *mut c_void, chunk.len() as u32) };
18+
if ret != TRUE {
19+
return Err(Error::WINDOWS_RTL_GEN_RANDOM);
20+
}
21+
}
22+
Ok(())
23+
}

0 commit comments

Comments
 (0)