std::rand: make the windows OSRng more correct, remove some C++.
This lets the C++ code in the rt handle the (slightly) tricky parts of random number generation: e.g. error detection/handling, and using the values of the `#define`d options to the various functions.
This commit is contained in:
parent
fb9706338d
commit
29e3b33a09
1
mk/rt.mk
1
mk/rt.mk
@ -86,7 +86,6 @@ RUNTIME_CXXS_$(1)_$(2) := \
|
||||
rt/sync/lock_and_signal.cpp \
|
||||
rt/sync/rust_thread.cpp \
|
||||
rt/rust_builtin.cpp \
|
||||
rt/rust_rng.cpp \
|
||||
rt/rust_upcall.cpp \
|
||||
rt/rust_uv.cpp \
|
||||
rt/miniz.cpp \
|
||||
|
@ -19,12 +19,8 @@ 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
|
||||
@ -40,9 +36,6 @@ pub struct OSRng {
|
||||
/// `/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
|
||||
@ -60,12 +53,10 @@ impl OSRng {
|
||||
|
||||
/// Create a new `OSRng`.
|
||||
#[cfg(windows)]
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
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()})
|
||||
}
|
||||
let mut hcp = 0;
|
||||
unsafe {raw::rust_win32_rand_acquire(&mut hcp)};
|
||||
|
||||
OSRng { hcryptprov: hcp }
|
||||
}
|
||||
@ -96,9 +87,12 @@ impl Rng for OSRng {
|
||||
self.fill_bytes(v);
|
||||
unsafe { cast::transmute(v) }
|
||||
}
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
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()})
|
||||
use libc::DWORD;
|
||||
|
||||
do v.as_mut_buf |ptr, len| {
|
||||
unsafe {raw::rust_win32_rand_gen(self.hcryptprov, len as DWORD, ptr)}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -111,27 +105,24 @@ impl Drop for OSRng {
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn drop(&mut self) {
|
||||
// TODO this 0 means?
|
||||
if unsafe { raw::CryptReleaseContext(self.hcryptprov, 0)} == FALSE {
|
||||
fail!("CryptReleaseContext failed with error %u", unsafe {GetLastError()})
|
||||
}
|
||||
unsafe {raw::rust_win32_rand_release(self.hcryptprov)}
|
||||
}
|
||||
}
|
||||
|
||||
#[abi = "cdecl"]
|
||||
#[cfg(windows)]
|
||||
mod raw {
|
||||
use libc::{LPCTSTR, DWORD, BOOL, BYTE};
|
||||
use libc::{c_long, DWORD, BYTE};
|
||||
|
||||
enum HCRYPTPROV_opaque {}
|
||||
pub type HCRYPTPROV = *CRYPTPROV;
|
||||
pub type HCRYPTPROV = c_long;
|
||||
|
||||
// these functions are implemented so that they either succeed or
|
||||
// abort(), so we can just assume they work when we call them.
|
||||
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;
|
||||
pub fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV);
|
||||
pub fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE);
|
||||
pub fn rust_win32_rand_release(hProv: HCRYPTPROV);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,15 +8,28 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use option::{Some, None};
|
||||
use rt::io::Reader;
|
||||
use rt::io::ReaderByteConversions;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
/// An RNG that reads random bytes straight from a `Reader`. This will
|
||||
/// work best with an infinite reader, but this is not required. The
|
||||
/// semantics of reading past the end of the reader are the same as
|
||||
/// those of the `read` method of the inner `Reader`.
|
||||
/// work best with an infinite reader, but this is not required.
|
||||
///
|
||||
/// It will fail if it there is insufficient data to fulfill a request.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::rand::reader;
|
||||
/// use std::rt::io::mem;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut rng = reader::ReaderRng::new(mem::MemReader::new(~[1,2,3,4,5,6,7,8]));
|
||||
/// println!("{}", rng.gen::<uint>());
|
||||
/// }
|
||||
/// ```
|
||||
pub struct ReaderRng<R> {
|
||||
priv reader: R
|
||||
}
|
||||
@ -32,8 +45,6 @@ impl<R: Reader> ReaderRng<R> {
|
||||
|
||||
impl<R: Reader> Rng for ReaderRng<R> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
// XXX which is better: consistency between big/little-endian
|
||||
// platforms, or speed.
|
||||
if cfg!(target_endian="little") {
|
||||
self.reader.read_le_u32_()
|
||||
} else {
|
||||
@ -48,8 +59,13 @@ impl<R: Reader> Rng for ReaderRng<R> {
|
||||
}
|
||||
}
|
||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||
// XXX: check that we filled `v``
|
||||
let _n = self.reader.read(v);
|
||||
if v.len() == 0 { return }
|
||||
match self.reader.read(v) {
|
||||
Some(n) if n == v.len() => return,
|
||||
Some(n) => fail2!("ReaderRng.fill_bytes could not fill buffer: \
|
||||
read {} out of {} bytes.", n, v.len()),
|
||||
None => fail2!("ReaderRng.fill_bytes reached eof.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,4 +107,12 @@ mod test {
|
||||
|
||||
assert_eq!(v, w);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_reader_rng_insufficient_bytes() {
|
||||
let mut rng = ReaderRng::new(MemReader::new(~[]));
|
||||
let mut v = [0u8, .. 3];
|
||||
rng.fill_bytes(v);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ use rt::local::Local;
|
||||
use rt::rtio::{RemoteCallback, PausibleIdleCallback};
|
||||
use borrow::{to_uint};
|
||||
use cell::Cell;
|
||||
use rand::{SeedableRng, XorShiftRng, Rng, Rand};
|
||||
use rand::{XorShiftRng, Rng, Rand};
|
||||
use iter::range;
|
||||
use vec::{OwnedVector};
|
||||
|
||||
@ -862,6 +862,7 @@ fn new_sched_rng() -> XorShiftRng {
|
||||
use ptr::RawPtr;
|
||||
use vec::MutableVector;
|
||||
use iter::Iterator;
|
||||
use rand::SeedableRng;
|
||||
|
||||
// XXX: this could use io::native::file, when it works.
|
||||
let file = do "/dev/urandom".with_c_str |name| {
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "sync/lock_and_signal.h"
|
||||
#include "memory_region.h"
|
||||
#include "boxed_region.h"
|
||||
#include "rust_rng.h"
|
||||
#include "vg/valgrind.h"
|
||||
#include "sp.h"
|
||||
|
||||
@ -69,11 +68,6 @@ rust_env_pairs() {
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" CDECL void
|
||||
rand_gen_seed(uint8_t* dest, size_t size) {
|
||||
rng_gen_seed(dest, size);
|
||||
}
|
||||
|
||||
extern "C" CDECL char*
|
||||
#if defined(__WIN32__)
|
||||
rust_list_dir_val(WIN32_FIND_DATA* entry_ptr) {
|
||||
@ -654,6 +648,62 @@ rust_unset_sigprocmask() {
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__WIN32__)
|
||||
void
|
||||
win32_require(LPCTSTR fn, BOOL ok) {
|
||||
if (!ok) {
|
||||
LPTSTR buf;
|
||||
DWORD err = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &buf, 0, NULL );
|
||||
fprintf(stderr, "%s failed with error %ld: %s", fn, err, buf);
|
||||
LocalFree((HLOCAL)buf);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
rust_win32_rand_acquire(HCRYPTPROV* phProv) {
|
||||
win32_require
|
||||
(_T("CryptAcquireContext"),
|
||||
CryptAcquireContext(phProv, NULL, NULL, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
|
||||
|
||||
}
|
||||
extern "C" CDECL void
|
||||
rust_win32_rand_gen(HCRYPTPROV hProv, DWORD dwLen, BYTE* pbBuffer) {
|
||||
win32_require
|
||||
(_T("CryptGenRandom"), CryptGenRandom(hProv, dwLen, pbBuffer));
|
||||
}
|
||||
extern "C" CDECL void
|
||||
rust_win32_rand_release(HCRYPTPROV hProv) {
|
||||
win32_require
|
||||
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// these symbols are listed in rustrt.def.in, so they need to exist; but they
|
||||
// should never be called.
|
||||
|
||||
extern "C" CDECL void
|
||||
rust_win32_rand_acquire() {
|
||||
abort();
|
||||
}
|
||||
extern "C" CDECL void
|
||||
rust_win32_rand_gen() {
|
||||
abort();
|
||||
}
|
||||
extern "C" CDECL void
|
||||
rust_win32_rand_release() {
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
|
@ -1,83 +0,0 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
#include "rust_globals.h"
|
||||
#include "rust_rng.h"
|
||||
#include "rust_util.h"
|
||||
|
||||
|
||||
#ifdef __WIN32__
|
||||
void
|
||||
win32_require(LPCTSTR fn, BOOL ok) {
|
||||
if (!ok) {
|
||||
LPTSTR buf;
|
||||
DWORD err = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &buf, 0, NULL );
|
||||
fprintf(stderr, "%s failed with error %ld: %s", fn, err, buf);
|
||||
LocalFree((HLOCAL)buf);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
rng_gen_seed(uint8_t* dest, size_t size) {
|
||||
#ifdef __WIN32__
|
||||
HCRYPTPROV hProv;
|
||||
win32_require
|
||||
(_T("CryptAcquireContext"),
|
||||
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
|
||||
win32_require
|
||||
(_T("CryptGenRandom"), CryptGenRandom(hProv, size, (BYTE*) dest));
|
||||
win32_require
|
||||
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
|
||||
#else
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "error opening /dev/urandom: %s", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
size_t amount = 0;
|
||||
do {
|
||||
ssize_t ret = read(fd, dest+amount, size-amount);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "error reading /dev/urandom: %s", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
else if (ret == 0) {
|
||||
fprintf(stderr, "somehow hit eof reading from /dev/urandom");
|
||||
abort();
|
||||
}
|
||||
amount += (size_t)ret;
|
||||
} while (amount < size);
|
||||
int ret = close(fd);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "error closing /dev/urandom: %s", strerror(errno));
|
||||
// FIXME #3697: Why does this fail sometimes?
|
||||
// abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// End:
|
||||
//
|
@ -1,26 +0,0 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
#ifndef RUST_RNG_H
|
||||
#define RUST_RNG_H
|
||||
|
||||
void rng_gen_seed(uint8_t* dest, size_t size);
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// End:
|
||||
//
|
||||
|
||||
#endif
|
@ -9,7 +9,6 @@ rust_localtime
|
||||
rust_timegm
|
||||
rust_mktime
|
||||
precise_time_ns
|
||||
rand_gen_seed
|
||||
rust_path_is_dir
|
||||
rust_path_exists
|
||||
rust_get_stdin
|
||||
@ -23,6 +22,9 @@ rust_log_console_off
|
||||
rust_should_log_console
|
||||
rust_unset_sigprocmask
|
||||
rust_env_pairs
|
||||
rust_win32_rand_acquire
|
||||
rust_win32_rand_gen
|
||||
rust_win32_rand_release
|
||||
upcall_rust_personality
|
||||
upcall_call_shim_on_c_stack
|
||||
upcall_call_shim_on_rust_stack
|
||||
|
Loading…
x
Reference in New Issue
Block a user