diff options
| author | Huon Wilson <dbau.pp+github@gmail.com> | 2013-09-22 20:51:57 +1000 |
|---|---|---|
| committer | Huon Wilson <dbau.pp+github@gmail.com> | 2013-10-09 22:22:42 +1100 |
| commit | 39a69d323da95ce642ea7fe8d40eb8bdd6a277c8 (patch) | |
| tree | 00c17e0dc840f82fe747dfd1265282209e0005f0 /src/libstd/rand/os.rs | |
| parent | a2b509656ac9c0f98d89fe4ea9d2f64a6ec7047a (diff) | |
std::rand: Add OSRng, ReaderRng wrappers around the OS RNG & generic Readers respectively.
The former reads from e.g. /dev/urandom, the latter just wraps any std::rt::io::Reader into an interface that implements Rng. This also adds Rng.fill_bytes for efficient implementations of the above (reading 8 bytes at a time is inefficient when you can read 1000), and removes the dependence on src/rt (i.e. rand_gen_seed) although this last one requires implementing hand-seeding of the XorShiftRng used in the scheduler on Linux/unixes, since OSRng relies on a scheduler existing to be able to read from /dev/urandom.
Diffstat (limited to 'src/libstd/rand/os.rs')
| -rw-r--r-- | src/libstd/rand/os.rs | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs new file mode 100644 index 00000000000..0e6472b4d37 --- /dev/null +++ b/src/libstd/rand/os.rs @@ -0,0 +1,193 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Interfaces to the operating system provided random number +//! generators. + +use rand::Rng; +use ops::Drop; + +#[cfg(unix)] +use rand::reader::ReaderRng; +#[cfg(unix)] +use rt::io::{file, Open, Read}; + +#[cfg(windows)] +use ptr; +#[cfg(windows)] +use cast; +#[cfg(windows)] +use libc::{GetLastError, FALSE}; + +/// A random number generator that retrieves randomness straight from +/// the operating system. On Unix-like systems this reads from +/// `/dev/urandom`, on Windows this uses `CryptGenRandom`. +/// +/// This does not block. +#[cfg(unix)] +pub struct OSRng { + priv inner: ReaderRng<file::FileStream> +} +/// A random number generator that retrieves randomness straight from +/// the operating system. On Unix-like systems this reads from +/// `/dev/urandom`, on Windows this uses `CryptGenRandom`. +/// +/// This does not block. +/// +/// XXX: it is unlikely that this is threadsafe with the use of +/// GetLastError. +#[cfg(windows)] +pub struct OSRng { + priv hcryptprov: raw::HCRYPTPROV +} + +impl OSRng { + /// Create a new `OSRng`. + #[cfg(unix)] + pub fn new() -> OSRng { + let reader = file::open(& &"/dev/urandom", Open, Read).expect("Error opening /dev/urandom"); + let reader_rng = ReaderRng::new(reader); + + OSRng { inner: reader_rng } + } + + /// Create a new `OSRng`. + #[cfg(windows)] + pub fn new() -> OSRng { + let hcp = ptr::mut_null(); + // TODO these two 0 constants are incorrect! + if unsafe { raw::CryptAcquireContext(hcp, ptr::null(), ptr::null(), 0, 0); } == FALSE { + fail!("CryptAcquireContext failed with error %u", unsafe {GetLastError()}) + } + + OSRng { hcryptprov: hcp } + } +} + +#[cfg(unix)] +impl Rng for OSRng { + fn next_u32(&mut self) -> u32 { + self.inner.next_u32() + } + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + fn fill_bytes(&mut self, v: &mut [u8]) { + self.inner.fill_bytes(v) + } +} + +#[cfg(windows)] +impl Rng for OSRng { + fn next_u32(&mut self) -> u32 { + let mut v = [0u8, .. 4]; + self.fill_bytes(v); + unsafe { cast::transmute(v) } + } + fn next_u64(&mut self) -> u64 { + let mut v = [0u8, .. 8]; + self.fill_bytes(v); + unsafe { cast::transmute(v) } + } + fn fill_bytes(&mut self, v: &mut [u8]) { + if unsafe { raw::CryptGenRandom(self.hcryptprov, v.len(), v.unsafe_mut_ref(0)) } == FALSE { + fail!("CryptGenRandom failed with error %u", unsafe {GetLastError()}) + } + } +} + +impl Drop for OSRng { + #[cfg(unix)] + fn drop(&mut self) { + // ensure that OSRng is not implicitly copyable on all + // platforms, for consistency. + } + + #[cfg(windows)] + fn drop(&mut self) { + // TODO this 0 means? + if unsafe { raw::CryptReleaseContext(self.hcryptprov, 0)} == FALSE { + fail!("CryptReleaseContext failed with error %u", unsafe {GetLastError()}) + } + } +} + +#[abi = "cdecl"] +#[cfg(windows)] +mod raw { + use libc::{LPCTSTR, DWORD, BOOL, BYTE}; + + enum HCRYPTPROV_opaque {} + pub type HCRYPTPROV = *CRYPTPROV; + extern { + pub fn CryptAcquireContext(phProv: *mut HCRYPTPROV, + pszContainer: LPCTSTR, pszProvider: LPCTSTR, + dwProvType: DWORD, dwFlags: DWORD) -> BOOL; + pub fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE) -> BOOL; + pub fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL; + } +} + +#[cfg(test)] +mod test { + use super::*; + use rand::Rng; + + #[test] + fn test_os_rng() { + let mut r = OSRng::new(); + + r.next_u32(); + r.next_u64(); + + let mut v = [0u8, .. 1000]; + r.fill_bytes(v); + } + + #[test] + fn test_os_rng_tasks() { + use task; + use comm; + use comm::{GenericChan, GenericPort}; + use option::{None, Some}; + use iter::{Iterator, range}; + use vec::{ImmutableVector, OwnedVector}; + + let mut chans = ~[]; + for _ in range(0, 20) { + let (p, c) = comm::stream(); + chans.push(c); + do task::spawn_with(p) |p| { + // wait until all the tasks are ready to go. + p.recv(); + + // deschedule to attempt to interleave things as much + // as possible (XXX: is this a good test?) + let mut r = OSRng::new(); + task::deschedule(); + let mut v = [0u8, .. 1000]; + + for _ in range(0, 100) { + r.next_u32(); + task::deschedule(); + r.next_u64(); + task::deschedule(); + r.fill_bytes(v); + task::deschedule(); + } + } + } + + // start all the tasks + for c in chans.iter() { + c.send(()) + } + } +} |
