#[cfg(any(target_os = "linux",
windows,
test))]
use c;
#[cfg(test)]
use core;
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 SystemRandom {
#[inline(always)]
pub fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
fill_impl(dest)
}
}
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(any(target_os = "linux", windows))]
mod sysrand {
use error;
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
let mut read_len = 0;
while read_len < dest.len() {
let r = unsafe {
super::GFp_sysrand_chunk(dest[read_len..].as_mut_ptr(),
dest.len() - read_len)
};
if r < 0 {
return Err(error::Unspecified);
}
read_len += r as usize;
}
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 unsafe {
super::GFp_sysrand_chunk(dummy.as_mut_ptr(),
dummy.len()) } == -1 {
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;
}
}
#[allow(non_snake_case)]
#[doc(hidden)]
pub struct RAND<'a> {
pub rng: &'a SecureRandom,
}
impl<'a> RAND<'a> {
pub fn new(rng: &'a SecureRandom) -> RAND<'a> { RAND { rng: rng } }
}
#[cfg(test)]
#[allow(non_snake_case)]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern fn GFp_RAND_bytes(rng: *mut RAND, dest: *mut u8,
dest_len: c::size_t) -> c::int {
let dest: &mut [u8] = core::slice::from_raw_parts_mut(dest, dest_len);
match (*(*rng).rng).fill(dest) {
Ok(()) => 1,
_ => 0,
}
}
#[cfg(any(target_os = "linux", windows))]
extern {
fn GFp_sysrand_chunk(buf: *mut u8, len: c::size_t) -> c::long;
}
#[cfg(test)]
mod tests {
use rand;
#[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));
}
}
}
}