rust/src/librand/os.rs

183 lines
4.7 KiB
Rust
Raw Normal View History

// 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 Rng;
#[cfg(unix)]
use reader::ReaderRng;
#[cfg(unix)]
use std::io::File;
#[cfg(windows)]
use std::cast;
#[cfg(windows)]
use std::libc::{c_long, DWORD, BYTE};
#[cfg(windows)]
type HCRYPTPROV = c_long;
// the extern functions imported from the runtime on Windows are
// implemented so that they either succeed or abort(), so we can just
// assume they work when we call them.
/// A random number generator that retrieves randomness straight from
/// the operating system. Platform sources:
///
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
/// `/dev/urandom`.
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
/// service provider with the `PROV_RSA_FULL` type.
///
/// This does not block.
#[cfg(unix)]
pub struct OSRng {
priv inner: ReaderRng<File>
}
/// A random number generator that retrieves randomness straight from
/// the operating system. Platform sources:
///
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
/// `/dev/urandom`.
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
/// service provider with the `PROV_RSA_FULL` type.
///
/// This does not block.
#[cfg(windows)]
pub struct OSRng {
priv hcryptprov: HCRYPTPROV
}
impl OSRng {
/// Create a new `OSRng`.
#[cfg(unix)]
pub fn new() -> OSRng {
let reader = File::open(&Path::new("/dev/urandom"));
let reader = reader.ok().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 {
extern { fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV); }
let mut hcp = 0;
unsafe {rust_win32_rand_acquire(&mut hcp)};
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]) {
extern {
fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD,
pbBuffer: *mut BYTE);
}
unsafe {rust_win32_rand_gen(self.hcryptprov, v.len() as DWORD, v.as_mut_ptr())}
}
}
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) {
extern { fn rust_win32_rand_release(hProv: HCRYPTPROV); }
unsafe {rust_win32_rand_release(self.hcryptprov)}
}
}
#[cfg(test)]
mod test {
use super::OSRng;
use Rng;
use std::task;
#[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() {
let mut txs = ~[];
for _ in range(0, 20) {
let (tx, rx) = channel();
txs.push(tx);
task::spawn(proc() {
// wait until all the tasks are ready to go.
rx.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 tx in txs.iter() {
tx.send(())
}
}
}