Skip to content

Commit 0500cf9

Browse files
committed
Remove static dependency on advapi.dll, impl random generation fallbacks
Down to Win95IE3.02/Win95OSR2/NT4 we get a somewhat reasonable implementation based on CryptGenRandom, before that we fall back to a non-cryptographic PRNG instead.
1 parent f568acb commit 0500cf9

File tree

5 files changed

+141
-3
lines changed

5 files changed

+141
-3
lines changed

library/std/src/sys/pal/windows/c.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ if #[cfg(not(target_vendor = "uwp"))] {
111111
}
112112

113113
// Use raw-dylib to import ProcessPrng as we can't rely on there being an import library.
114-
#[cfg(not(target_vendor = "win7"))]
114+
#[cfg(not(any(target_vendor = "win7", target_vendor = "rust9x")))]
115115
#[cfg_attr(
116116
target_arch = "x86",
117117
link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated")
@@ -468,3 +468,36 @@ compat_fn_with_fallback! {
468468
FALSE
469469
}
470470
}
471+
472+
#[cfg(target_vendor = "rust9x")]
473+
compat_fn_with_fallback! {
474+
pub static advapi32: &CStr = c"advapi32" => { load: true, unicows: false };
475+
// >= XP / Server 2003
476+
// https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
477+
pub fn SystemFunction036(
478+
randombuffer: *mut core::ffi::c_void,
479+
randombufferlength: u32
480+
) -> BOOLEAN {
481+
unimplemented!()
482+
}
483+
484+
// >= NT 4.0 / Windows 95 OSR2 / Windows 95 with IE 3.02
485+
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta
486+
pub fn CryptAcquireContextA(
487+
phprov: *mut usize,
488+
szcontainer: PCSTR,
489+
szprovider: PCSTR,
490+
dwprovtype: u32,
491+
dwflags: u32
492+
) -> BOOL {
493+
unimplemented!()
494+
}
495+
pub fn CryptReleaseContext(hprov: usize, dwflags: u32) -> BOOL {
496+
unimplemented!()
497+
}
498+
pub fn CryptGenRandom(hprov: usize, dwlen: u32, pbbuffer: *mut u8) -> BOOL {
499+
unimplemented!()
500+
}
501+
}
502+
#[cfg(target_vendor = "rust9x")]
503+
pub use self::SystemFunction036 as RtlGenRandom;

library/std/src/sys/pal/windows/c/bindings.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,6 +2188,11 @@ Windows.Win32.Networking.WinSock.WSATRY_AGAIN
21882188
Windows.Win32.Networking.WinSock.WSATYPE_NOT_FOUND
21892189
Windows.Win32.Networking.WinSock.WSAVERNOTSUPPORTED
21902190
Windows.Win32.Security.Authentication.Identity.RtlGenRandom
2191+
Windows.Win32.Security.Cryptography.CRYPT_VERIFYCONTEXT
2192+
Windows.Win32.Security.Cryptography.CryptAcquireContextA
2193+
Windows.Win32.Security.Cryptography.CryptGenRandom
2194+
Windows.Win32.Security.Cryptography.CryptReleaseContext
2195+
Windows.Win32.Security.Cryptography.PROV_RSA_FULL
21912196
Windows.Win32.Security.SECURITY_ATTRIBUTES
21922197
Windows.Win32.Security.TOKEN_ACCESS_MASK
21932198
Windows.Win32.Security.TOKEN_ACCESS_PSEUDO_HANDLE
@@ -2497,6 +2502,7 @@ Windows.Win32.System.SystemInformation.GetSystemInfo
24972502
Windows.Win32.System.SystemInformation.GetSystemTime
24982503
Windows.Win32.System.SystemInformation.GetSystemTimeAsFileTime
24992504
Windows.Win32.System.SystemInformation.GetSystemTimePreciseAsFileTime
2505+
Windows.Win32.System.SystemInformation.GetTickCount
25002506
Windows.Win32.System.SystemInformation.GetVersion
25012507
Windows.Win32.System.SystemInformation.GetWindowsDirectoryW
25022508
Windows.Win32.System.SystemInformation.PROCESSOR_ARCHITECTURE

library/std/src/sys/pal/windows/c/windows_sys.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Bindings generated by `windows-bindgen` 0.58.0
22

33
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
4+
windows_targets::link!("advapi32.dll" "system" fn CryptAcquireContextA(phprov : *mut usize, szcontainer : PCSTR, szprovider : PCSTR, dwprovtype : u32, dwflags : u32) -> BOOL);
5+
windows_targets::link!("advapi32.dll" "system" fn CryptGenRandom(hprov : usize, dwlen : u32, pbbuffer : *mut u8) -> BOOL);
6+
windows_targets::link!("advapi32.dll" "system" fn CryptReleaseContext(hprov : usize, dwflags : u32) -> BOOL);
47
windows_targets::link!("advapi32.dll" "system" fn OpenProcessToken(processhandle : HANDLE, desiredaccess : TOKEN_ACCESS_MASK, tokenhandle : *mut HANDLE) -> BOOL);
58
windows_targets::link!("advapi32.dll" "system" "SystemFunction036" fn RtlGenRandom(randombuffer : *mut core::ffi::c_void, randombufferlength : u32) -> BOOLEAN);
69
windows_targets::link!("kernel32.dll" "system" fn AcquireSRWLockExclusive(srwlock : *mut SRWLOCK));
@@ -65,6 +68,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetSystemTime(lpsystemtime : *
6568
windows_targets::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME));
6669
windows_targets::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME));
6770
windows_targets::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32);
71+
windows_targets::link!("kernel32.dll" "system" fn GetTickCount() -> u32);
6872
windows_targets::link!("kernel32.dll" "system" fn GetVersion() -> u32);
6973
windows_targets::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32);
7074
windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL);
@@ -436,6 +440,7 @@ pub struct CRITICAL_SECTION_DEBUG {
436440
pub CreatorBackTraceIndexHigh: u16,
437441
pub Identifier: u16,
438442
}
443+
pub const CRYPT_VERIFYCONTEXT: u32 = 4026531840u32;
439444
pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32;
440445
pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32;
441446
pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32;
@@ -2918,6 +2923,7 @@ pub const PROFILE_KERNEL: PROCESS_CREATION_FLAGS = 536870912u32;
29182923
pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32;
29192924
pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32;
29202925
pub const PROGRESS_CONTINUE: u32 = 0u32;
2926+
pub const PROV_RSA_FULL: u32 = 1u32;
29212927
pub type PSTR = *mut u8;
29222928
pub type PTIMERAPCROUTINE = Option<
29232929
unsafe extern "system" fn(

library/std/src/sys/random/windows.rs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::sys::c;
22

3-
#[cfg(not(target_vendor = "win7"))]
3+
#[cfg(not(any(target_vendor = "win7", target_vendor = "rust9x")))]
44
#[inline]
55
pub fn fill_bytes(bytes: &mut [u8]) {
66
let ret = unsafe { c::ProcessPrng(bytes.as_mut_ptr(), bytes.len()) };
@@ -18,3 +18,96 @@ pub fn fill_bytes(mut bytes: &mut [u8]) {
1818
bytes = &mut bytes[len as usize..];
1919
}
2020
}
21+
22+
#[cfg(target_vendor = "rust9x")]
23+
mod rust9x {
24+
use super::*;
25+
use crate::sys::sync::OnceBox;
26+
27+
pub fn fill_bytes(mut bytes: &mut [u8]) {
28+
if let Some(f) = c::RtlGenRandom::available() {
29+
while !bytes.is_empty() {
30+
let len = bytes.len().try_into().unwrap_or(u32::MAX);
31+
let ret = unsafe { f(bytes.as_mut_ptr().cast(), len) };
32+
assert_ne!(ret, 0, "failed to generate random data");
33+
bytes = &mut bytes[len as usize..];
34+
}
35+
} else if let Some(f) = c::CryptGenRandom::available() {
36+
let ctx = CRYPT_CONTEXT.get_or_init(init_crypt_context);
37+
while !bytes.is_empty() {
38+
let len = bytes.len().try_into().unwrap_or(u32::MAX);
39+
let ret = unsafe { f(ctx.0, len, bytes.as_mut_ptr().cast()) };
40+
assert_ne!(ret, 0, "failed to generate random data");
41+
bytes = &mut bytes[len as usize..];
42+
}
43+
} else {
44+
// well, we tried, fall back to a non-cryptographically-secure PRNG
45+
// for NT <4.0 and 95 without IE3.02 or higher.
46+
47+
// seed with stack address and tick count
48+
let mut state: [u32; 2] = [unsafe { c::GetTickCount() }, 0];
49+
state[1] = (&raw const state) as u32;
50+
51+
let mut chunks = bytes.chunks_exact_mut(4);
52+
for chunk in &mut chunks {
53+
let [a, b, c, d] = xoroshiro64_star_star(&mut state).to_ne_bytes();
54+
chunk[0] = a;
55+
chunk[1] = b;
56+
chunk[2] = c;
57+
chunk[3] = d;
58+
}
59+
60+
let remainder = chunks.into_remainder();
61+
if remainder.is_empty() {
62+
return;
63+
}
64+
65+
for (rem, val) in
66+
remainder.iter_mut().zip(xoroshiro64_star_star(&mut state).to_ne_bytes())
67+
{
68+
*rem = val;
69+
}
70+
}
71+
}
72+
73+
static CRYPT_CONTEXT: OnceBox<HCryptProvider> = OnceBox::new();
74+
75+
struct HCryptProvider(usize);
76+
impl Drop for HCryptProvider {
77+
fn drop(&mut self) {
78+
unsafe {
79+
c::CryptReleaseContext(self.0, 0);
80+
}
81+
}
82+
}
83+
84+
fn init_crypt_context() -> Box<HCryptProvider> {
85+
let mut crypt_context = 0;
86+
unsafe {
87+
let ret = c::CryptAcquireContextA(
88+
&mut crypt_context,
89+
core::ptr::null(),
90+
core::ptr::null(),
91+
c::PROV_RSA_FULL,
92+
c::CRYPT_VERIFYCONTEXT,
93+
);
94+
assert_ne!(ret, c::FALSE, "failed to acquire crypt context: {:#X}", c::GetLastError());
95+
};
96+
Box::new(HCryptProvider(crypt_context))
97+
}
98+
99+
// xoroshiro64**
100+
// 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
101+
// https://prng.di.unimi.it/xoroshiro64starstar.c
102+
fn xoroshiro64_star_star(state: &mut [u32; 2]) -> u32 {
103+
let result = state[0].wrapping_mul(0x9E3779BB).rotate_left(5).wrapping_mul(5);
104+
state[1] ^= state[0];
105+
state[0] = state[0].rotate_left(26) ^ state[1] ^ (state[1] << 9);
106+
state[1] = state[1].rotate_left(13);
107+
108+
result
109+
}
110+
}
111+
112+
#[cfg(target_vendor = "rust9x")]
113+
pub use rust9x::fill_bytes;

library/std/src/sys/sync/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ pub use condvar::Condvar;
99
pub use mutex::Mutex;
1010
pub use once::{Once, OnceState};
1111
#[allow(unused)] // Only used on some platforms.
12-
use once_box::OnceBox;
12+
pub(crate) use once_box::OnceBox;
1313
pub use rwlock::RwLock;
1414
pub use thread_parking::Parker;

0 commit comments

Comments
 (0)