use error;
pub trait SecureRandom {
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
}
pub struct SystemRandom;
impl SystemRandom {
#[inline(always)]
pub fn new() -> SystemRandom { SystemRandom }
}
impl SecureRandom for SystemRandom {
#[inline(always)]
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
fill_impl(dest)
}
}
#[cfg(not(any(target_os = "linux",
target_os = "macos",
target_os = "ios",
windows)))]
use self::urandom::fill as fill_impl;
#[cfg(any(all(target_os = "linux", not(feature = "dev_urandom_fallback")),
windows))]
use self::sysrand::fill as fill_impl;
#[cfg(all(target_os = "linux", feature = "dev_urandom_fallback"))]
use self::sysrand_or_urandom::fill as fill_impl;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use self::darwin::fill as fill_impl;
#[cfg(target_os = "linux")]
mod sysrand_chunk {
use {c, error};
use libc;
extern {
static GFp_SYS_GETRANDOM: c::long;
}
#[inline]
pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
let chunk_len: c::size_t = dest.len();
let flags: c::uint = 0;
let r = unsafe {
libc::syscall(GFp_SYS_GETRANDOM, dest.as_mut_ptr(), chunk_len, flags)
};
if r < 0 {
if unsafe { *libc::__errno_location() } == libc::EINTR {
return Ok(0);
}
return Err(error::Unspecified);
}
Ok(r as usize)
}
}
#[cfg(windows)]
mod sysrand_chunk {
use core;
use {c, error};
#[link(name = "Advapi32")]
extern "system" {
#[link_name = "SystemFunction036"]
fn RtlGenRandom(random_buffer: *mut u8,
random_buffer_length: c::win32::ULONG)
-> c::win32::BOOLEAN;
}
#[inline]
pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
assert!(core::mem::size_of::<usize>() >=
core::mem::size_of::<c::win32::ULONG>());
let max_chunk_len = c::win32::ULONG::from(0u32).wrapping_sub(1) as usize;
assert_eq!(max_chunk_len, 0xffff_ffff);
let len = core::cmp::min(dest.len(), max_chunk_len);
if unsafe { RtlGenRandom(dest.as_mut_ptr(), len as c::win32::ULONG) }
== 0 {
return Err(error::Unspecified);
}
Ok(len)
}
}
#[cfg(any(target_os = "linux", windows))]
mod sysrand {
use error;
use super::sysrand_chunk::chunk;
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
let mut read_len = 0;
while read_len < dest.len() {
let chunk_len = try!(chunk(&mut dest[read_len..]));
read_len += chunk_len;
}
Ok(())
}
}
#[cfg(all(any(target_os = "redox", unix),
not(any(target_os = "macos", target_os = "ios")),
not(all(target_os = "linux",
not(feature = "dev_urandom_fallback")))))]
mod urandom {
use std;
use error;
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
#[cfg(target_os = "redox")]
static RANDOM_PATH: &'static str = "rand:";
#[cfg(unix)]
static RANDOM_PATH: &'static str = "/dev/urandom";
lazy_static! {
static ref FILE: Result<std::fs::File, std::io::Error> =
std::fs::File::open(RANDOM_PATH);
}
match *FILE {
Ok(ref file) => {
use std::io::Read;
(&*file).read_exact(dest).map_err(|_| error::Unspecified)
},
Err(_) => Err(error::Unspecified),
}
}
}
#[cfg(all(target_os = "linux", feature = "dev_urandom_fallback"))]
mod sysrand_or_urandom {
use error;
enum Mechanism {
Sysrand,
DevURandom,
}
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
lazy_static! {
static ref MECHANISM: Mechanism = {
let mut dummy = [0u8; 1];
if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
Mechanism::DevURandom
} else {
Mechanism::Sysrand
}
};
}
match *MECHANISM {
Mechanism::Sysrand => super::sysrand::fill(dest),
Mechanism::DevURandom => super::urandom::fill(dest),
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
mod darwin {
use c;
use error;
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
let r = unsafe {
SecRandomCopyBytes(kSecRandomDefault, dest.len(),
dest.as_mut_ptr())
};
match r {
0 => Ok(()),
_ => Err(error::Unspecified),
}
}
#[repr(C)]
struct SecRandomRef([u8; 0]);
#[link(name = "Security", kind = "framework")]
extern {
static kSecRandomDefault: &'static SecRandomRef;
fn SecRandomCopyBytes(rnd: &'static SecRandomRef, count: c::size_t,
bytes: *mut u8) -> c::int;
}
}
#[cfg(test)]
mod tests {
use rand;
use rand::SecureRandom;
#[test]
fn test_system_random_lengths() {
let lengths = [0, 1, 2, 3, 96, 255, 256, 257, 511, 512, 513, 4096];
for len in lengths.iter() {
let mut buf = vec![0; *len];
let rng = rand::SystemRandom::new();
assert!(rng.fill(&mut buf).is_ok());
if *len >= 96 {
assert!(buf.iter().any(|x| *x != 0));
}
}
}
}