Skip to content

Commit d2e99a0

Browse files
committed
rand: Rewrite OsRng in Rust for windows
This removes even more rust_builtin.c code, and allows us to more gracefully handle errors (not a process panic, but a task failure).
1 parent ab1dd09 commit d2e99a0

File tree

2 files changed

+112
-155
lines changed

2 files changed

+112
-155
lines changed

src/librand/os.rs

Lines changed: 112 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -11,124 +11,140 @@
1111
//! Interfaces to the operating system provided random number
1212
//! generators.
1313
14-
use Rng;
14+
pub use self::imp::OSRng;
1515

1616
#[cfg(unix)]
17-
use reader::ReaderRng;
18-
#[cfg(unix)]
19-
use std::io::File;
20-
21-
#[cfg(windows)]
22-
use std::cast;
23-
#[cfg(windows)]
24-
use std::libc::{c_long, DWORD, BYTE};
25-
#[cfg(windows)]
26-
type HCRYPTPROV = c_long;
27-
// the extern functions imported from the runtime on Windows are
28-
// implemented so that they either succeed or abort(), so we can just
29-
// assume they work when we call them.
30-
31-
/// A random number generator that retrieves randomness straight from
32-
/// the operating system. Platform sources:
33-
///
34-
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
35-
/// `/dev/urandom`.
36-
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
37-
/// service provider with the `PROV_RSA_FULL` type.
38-
///
39-
/// This does not block.
40-
#[cfg(unix)]
41-
pub struct OSRng {
42-
priv inner: ReaderRng<File>
43-
}
44-
/// A random number generator that retrieves randomness straight from
45-
/// the operating system. Platform sources:
46-
///
47-
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
48-
/// `/dev/urandom`.
49-
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
50-
/// service provider with the `PROV_RSA_FULL` type.
51-
///
52-
/// This does not block.
53-
#[cfg(windows)]
54-
pub struct OSRng {
55-
priv hcryptprov: HCRYPTPROV
56-
}
57-
58-
impl OSRng {
59-
/// Create a new `OSRng`.
17+
mod imp {
18+
use Rng;
19+
use reader::ReaderRng;
20+
use std::io::File;
21+
22+
/// A random number generator that retrieves randomness straight from
23+
/// the operating system. Platform sources:
24+
///
25+
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
26+
/// `/dev/urandom`.
27+
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
28+
/// service provider with the `PROV_RSA_FULL` type.
29+
///
30+
/// This does not block.
6031
#[cfg(unix)]
61-
pub fn new() -> OSRng {
62-
let reader = File::open(&Path::new("/dev/urandom"));
63-
let reader = reader.ok().expect("Error opening /dev/urandom");
64-
let reader_rng = ReaderRng::new(reader);
65-
66-
OSRng { inner: reader_rng }
32+
pub struct OSRng {
33+
priv inner: ReaderRng<File>
6734
}
6835

69-
/// Create a new `OSRng`.
70-
#[cfg(windows)]
71-
pub fn new() -> OSRng {
72-
extern { fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV); }
73-
74-
let mut hcp = 0;
75-
unsafe {rust_win32_rand_acquire(&mut hcp)};
36+
impl OSRng {
37+
/// Create a new `OSRng`.
38+
pub fn new() -> OSRng {
39+
let reader = File::open(&Path::new("/dev/urandom"));
40+
let reader = reader.ok().expect("Error opening /dev/urandom");
41+
let reader_rng = ReaderRng::new(reader);
7642

77-
OSRng { hcryptprov: hcp }
43+
OSRng { inner: reader_rng }
44+
}
7845
}
79-
}
8046

81-
#[cfg(unix)]
82-
impl Rng for OSRng {
83-
fn next_u32(&mut self) -> u32 {
84-
self.inner.next_u32()
85-
}
86-
fn next_u64(&mut self) -> u64 {
87-
self.inner.next_u64()
88-
}
89-
fn fill_bytes(&mut self, v: &mut [u8]) {
90-
self.inner.fill_bytes(v)
47+
impl Rng for OSRng {
48+
fn next_u32(&mut self) -> u32 {
49+
self.inner.next_u32()
50+
}
51+
fn next_u64(&mut self) -> u64 {
52+
self.inner.next_u64()
53+
}
54+
fn fill_bytes(&mut self, v: &mut [u8]) {
55+
self.inner.fill_bytes(v)
56+
}
9157
}
9258
}
9359

9460
#[cfg(windows)]
95-
impl Rng for OSRng {
96-
fn next_u32(&mut self) -> u32 {
97-
let mut v = [0u8, .. 4];
98-
self.fill_bytes(v);
99-
unsafe { cast::transmute(v) }
100-
}
101-
fn next_u64(&mut self) -> u64 {
102-
let mut v = [0u8, .. 8];
103-
self.fill_bytes(v);
104-
unsafe { cast::transmute(v) }
61+
mod imp {
62+
use Rng;
63+
use std::cast;
64+
use std::libc::{c_ulong, DWORD, BYTE, LPCSTR, BOOL};
65+
use std::os;
66+
67+
type HCRYPTPROV = c_ulong;
68+
69+
/// A random number generator that retrieves randomness straight from
70+
/// the operating system. Platform sources:
71+
///
72+
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
73+
/// `/dev/urandom`.
74+
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
75+
/// service provider with the `PROV_RSA_FULL` type.
76+
///
77+
/// This does not block.
78+
pub struct OSRng {
79+
priv hcryptprov: HCRYPTPROV
10580
}
106-
fn fill_bytes(&mut self, v: &mut [u8]) {
107-
extern {
108-
fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD,
109-
pbBuffer: *mut BYTE);
110-
}
11181

112-
unsafe {rust_win32_rand_gen(self.hcryptprov, v.len() as DWORD, v.as_mut_ptr())}
82+
static PROV_RSA_FULL: DWORD = 1;
83+
static CRYPT_SILENT: DWORD = 64;
84+
static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
85+
86+
extern "system" {
87+
fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
88+
pszContainer: LPCSTR,
89+
pszProvider: LPCSTR,
90+
dwProvType: DWORD,
91+
dwFlags: DWORD) -> BOOL;
92+
fn CryptGenRandom(hProv: HCRYPTPROV,
93+
dwLen: DWORD,
94+
pbBuffer: *mut BYTE) -> BOOL;
95+
fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
11396
}
114-
}
11597

116-
impl Drop for OSRng {
117-
#[cfg(unix)]
118-
fn drop(&mut self) {
119-
// ensure that OSRng is not implicitly copyable on all
120-
// platforms, for consistency.
98+
impl OSRng {
99+
/// Create a new `OSRng`.
100+
pub fn new() -> OSRng {
101+
let mut hcp = 0;
102+
let ret = unsafe {
103+
CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
104+
PROV_RSA_FULL,
105+
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
106+
};
107+
if ret == 0 {
108+
fail!("couldn't create context: {}", os::last_os_error());
109+
}
110+
OSRng { hcryptprov: hcp }
111+
}
121112
}
122113

123-
#[cfg(windows)]
124-
fn drop(&mut self) {
125-
extern { fn rust_win32_rand_release(hProv: HCRYPTPROV); }
114+
impl Rng for OSRng {
115+
fn next_u32(&mut self) -> u32 {
116+
let mut v = [0u8, .. 4];
117+
self.fill_bytes(v);
118+
unsafe { cast::transmute(v) }
119+
}
120+
fn next_u64(&mut self) -> u64 {
121+
let mut v = [0u8, .. 8];
122+
self.fill_bytes(v);
123+
unsafe { cast::transmute(v) }
124+
}
125+
fn fill_bytes(&mut self, v: &mut [u8]) {
126+
let ret = unsafe {
127+
CryptGenRandom(self.hcryptprov, v.len() as DWORD,
128+
v.as_mut_ptr())
129+
};
130+
if ret == 0 {
131+
fail!("couldn't generate random bytes: {}", os::last_os_error());
132+
}
133+
}
134+
}
126135

127-
unsafe {rust_win32_rand_release(self.hcryptprov)}
136+
impl Drop for OSRng {
137+
fn drop(&mut self) {
138+
let ret = unsafe {
139+
CryptReleaseContext(self.hcryptprov, 0)
140+
};
141+
if ret == 0 {
142+
fail!("couldn't release context: {}", os::last_os_error());
143+
}
144+
}
128145
}
129146
}
130147

131-
132148
#[cfg(test)]
133149
mod test {
134150
use super::OSRng;

src/rt/rust_builtin.c

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -387,65 +387,6 @@ rust_unset_sigprocmask() {
387387

388388
#endif
389389

390-
#if defined(__WIN32__)
391-
void
392-
win32_require(LPCTSTR fn, BOOL ok) {
393-
if (!ok) {
394-
LPTSTR buf;
395-
DWORD err = GetLastError();
396-
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
397-
FORMAT_MESSAGE_FROM_SYSTEM |
398-
FORMAT_MESSAGE_IGNORE_INSERTS,
399-
NULL, err,
400-
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
401-
(LPTSTR) &buf, 0, NULL );
402-
fprintf(stderr, "%s failed with error %ld: %s", fn, err, buf);
403-
LocalFree((HLOCAL)buf);
404-
abort();
405-
}
406-
}
407-
408-
void
409-
rust_win32_rand_acquire(HCRYPTPROV* phProv) {
410-
win32_require
411-
(_T("CryptAcquireContext"),
412-
// changes to the parameters here should be reflected in the docs of
413-
// rand::os::OSRng
414-
CryptAcquireContext(phProv, NULL, NULL, PROV_RSA_FULL,
415-
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
416-
417-
}
418-
void
419-
rust_win32_rand_gen(HCRYPTPROV hProv, DWORD dwLen, BYTE* pbBuffer) {
420-
win32_require
421-
(_T("CryptGenRandom"), CryptGenRandom(hProv, dwLen, pbBuffer));
422-
}
423-
void
424-
rust_win32_rand_release(HCRYPTPROV hProv) {
425-
win32_require
426-
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
427-
}
428-
429-
#else
430-
431-
// these symbols are listed in rustrt.def.in, so they need to exist; but they
432-
// should never be called.
433-
434-
void
435-
rust_win32_rand_acquire() {
436-
abort();
437-
}
438-
void
439-
rust_win32_rand_gen() {
440-
abort();
441-
}
442-
void
443-
rust_win32_rand_release() {
444-
abort();
445-
}
446-
447-
#endif
448-
449390
//
450391
// Local Variables:
451392
// mode: C++

0 commit comments

Comments
 (0)