std::rand::OsRng: Use getrandom
syscall on Linux
`getrandom(2)` system call [1] has been added on Linux 3.17. This patch makes `OsRng` use `getrandom` if available, and use traditional `/dev/urandom` fallback if not. [1]: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c6e9d6f38894798696f23c8084ca7edbf16ee895
This commit is contained in:
parent
14cd5c590e
commit
fdf5195f58
@ -45,8 +45,12 @@
|
||||
//! so the "quality" of `/dev/random` is not better than `/dev/urandom` in most cases.
|
||||
//! However, this means that `/dev/urandom` can yield somewhat predictable randomness
|
||||
//! if the entropy pool is very small, such as immediately after first booting.
|
||||
//! If an application likely to be run soon after first booting, or on a system with very
|
||||
//! few entropy sources, one should consider using `/dev/random` via `ReaderRng`.
|
||||
//! Linux 3,17 added `getrandom(2)` system call which solves the issue: it blocks if entropy
|
||||
//! pool is not initialized yet, but it does not block once initialized.
|
||||
//! `OsRng` tries to use `getrandom(2)` if available, and use `/dev/urandom` fallback if not.
|
||||
//! If an application does not have `getrandom` and likely to be run soon after first booting,
|
||||
//! or on a system with very few entropy sources, one should consider using `/dev/random` via
|
||||
//! `ReaderRng`.
|
||||
//! - On some systems (e.g. FreeBSD, OpenBSD and Mac OS X) there is no difference
|
||||
//! between the two sources. (Also note that, on some systems e.g. FreeBSD, both `/dev/random`
|
||||
//! and `/dev/urandom` may block once if the CSPRNG has not seeded yet.)
|
||||
|
@ -15,45 +15,149 @@ pub use self::imp::OsRng;
|
||||
|
||||
#[cfg(all(unix, not(target_os = "ios")))]
|
||||
mod imp {
|
||||
extern crate libc;
|
||||
|
||||
use io::{IoResult, File};
|
||||
use path::Path;
|
||||
use rand::Rng;
|
||||
use rand::reader::ReaderRng;
|
||||
use result::{Ok, Err};
|
||||
use slice::{ImmutableSlice, MutableSlice};
|
||||
use mem;
|
||||
use os::errno;
|
||||
|
||||
#[cfg(all(target_os = "linux",
|
||||
any(target_arch = "x86_64", target_arch = "x86", target_arch = "arm")))]
|
||||
fn getrandom(buf: &mut [u8]) -> libc::c_long {
|
||||
extern "C" {
|
||||
fn syscall(number: libc::c_long, ...) -> libc::c_long;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const NR_GETRANDOM: libc::c_long = 318;
|
||||
#[cfg(target_arch = "x86")]
|
||||
const NR_GETRANDOM: libc::c_long = 355;
|
||||
#[cfg(target_arch = "arm")]
|
||||
const NR_GETRANDOM: libc::c_long = 384;
|
||||
|
||||
unsafe {
|
||||
syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_os = "linux",
|
||||
any(target_arch = "x86_64", target_arch = "x86", target_arch = "arm"))))]
|
||||
fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
|
||||
|
||||
fn getrandom_fill_bytes(v: &mut [u8]) {
|
||||
let mut read = 0;
|
||||
let len = v.len();
|
||||
while read < len {
|
||||
let result = getrandom(v[mut read..]);
|
||||
if result == -1 {
|
||||
let err = errno() as libc::c_int;
|
||||
if err == libc::EINTR {
|
||||
continue;
|
||||
} else {
|
||||
panic!("unexpected getrandom error: {}", err);
|
||||
}
|
||||
} else {
|
||||
read += result as uint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn getrandom_next_u32() -> u32 {
|
||||
let mut buf: [u8, ..4] = [0u8, ..4];
|
||||
getrandom_fill_bytes(&mut buf);
|
||||
unsafe { mem::transmute::<[u8, ..4], u32>(buf) }
|
||||
}
|
||||
|
||||
fn getrandom_next_u64() -> u64 {
|
||||
let mut buf: [u8, ..8] = [0u8, ..8];
|
||||
getrandom_fill_bytes(&mut buf);
|
||||
unsafe { mem::transmute::<[u8, ..8], u64>(buf) }
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux",
|
||||
any(target_arch = "x86_64", target_arch = "x86", target_arch = "arm")))]
|
||||
fn is_getrandom_available() -> bool {
|
||||
use sync::atomic::{AtomicBool, INIT_ATOMIC_BOOL, Relaxed};
|
||||
|
||||
static GETRANDOM_CHECKED: AtomicBool = INIT_ATOMIC_BOOL;
|
||||
static GETRANDOM_AVAILABLE: AtomicBool = INIT_ATOMIC_BOOL;
|
||||
|
||||
if !GETRANDOM_CHECKED.load(Relaxed) {
|
||||
let mut buf: [u8, ..0] = [];
|
||||
let result = getrandom(&mut buf);
|
||||
let available = if result == -1 {
|
||||
let err = errno() as libc::c_int;
|
||||
err != libc::ENOSYS
|
||||
} else {
|
||||
true
|
||||
};
|
||||
GETRANDOM_AVAILABLE.store(available, Relaxed);
|
||||
GETRANDOM_CHECKED.store(true, Relaxed);
|
||||
available
|
||||
} else {
|
||||
GETRANDOM_AVAILABLE.load(Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_os = "linux",
|
||||
any(target_arch = "x86_64", target_arch = "x86", target_arch = "arm"))))]
|
||||
fn is_getrandom_available() -> bool { false }
|
||||
|
||||
/// 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`.
|
||||
/// `/dev/urandom`, or from `getrandom(2)` system call if available.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
/// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed
|
||||
/// This does not block.
|
||||
#[cfg(unix)]
|
||||
pub struct OsRng {
|
||||
inner: ReaderRng<File>
|
||||
inner: OsRngInner,
|
||||
}
|
||||
|
||||
enum OsRngInner {
|
||||
OsGetrandomRng,
|
||||
OsReaderRng(ReaderRng<File>),
|
||||
}
|
||||
|
||||
impl OsRng {
|
||||
/// Create a new `OsRng`.
|
||||
pub fn new() -> IoResult<OsRng> {
|
||||
if is_getrandom_available() {
|
||||
return Ok(OsRng { inner: OsGetrandomRng });
|
||||
}
|
||||
|
||||
let reader = try!(File::open(&Path::new("/dev/urandom")));
|
||||
let reader_rng = ReaderRng::new(reader);
|
||||
|
||||
Ok(OsRng { inner: reader_rng })
|
||||
Ok(OsRng { inner: OsReaderRng(reader_rng) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Rng for OsRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.inner.next_u32()
|
||||
match self.inner {
|
||||
OsGetrandomRng => getrandom_next_u32(),
|
||||
OsReaderRng(ref mut rng) => rng.next_u32(),
|
||||
}
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.inner.next_u64()
|
||||
match self.inner {
|
||||
OsGetrandomRng => getrandom_next_u64(),
|
||||
OsReaderRng(ref mut rng) => rng.next_u64(),
|
||||
}
|
||||
}
|
||||
fn fill_bytes(&mut self, v: &mut [u8]) {
|
||||
self.inner.fill_bytes(v)
|
||||
match self.inner {
|
||||
OsGetrandomRng => getrandom_fill_bytes(v),
|
||||
OsReaderRng(ref mut rng) => rng.fill_bytes(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,7 +179,7 @@ mod imp {
|
||||
/// the operating system. Platform sources:
|
||||
///
|
||||
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
|
||||
/// `/dev/urandom`.
|
||||
/// `/dev/urandom`, or from `getrandom(2)` system call if available.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
/// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed
|
||||
@ -145,10 +249,10 @@ mod imp {
|
||||
/// the operating system. Platform sources:
|
||||
///
|
||||
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
|
||||
/// `/dev/urandom`.
|
||||
/// `/dev/urandom`, or from `getrandom(2)` system call if available.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
///
|
||||
/// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed
|
||||
/// This does not block.
|
||||
pub struct OsRng {
|
||||
hcryptprov: HCRYPTPROV
|
||||
|
Loading…
Reference in New Issue
Block a user