#![allow(unsafe_code)]
#[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", 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 = "linux", windows))]
mod sysrand {
use {bssl, error};
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
for mut chunk in
dest.chunks_mut(super::CRYPTO_sysrand_chunk_max_len) {
try!(bssl::map_result(unsafe {
super::CRYPTO_sysrand_chunk(chunk.as_mut_ptr(), chunk.len())
}));
}
Ok(())
}
}
#[cfg(all(unix,
not(all(target_os = "linux",
not(feature = "dev_urandom_fallback")))))]
mod urandom {
extern crate std;
use error;
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
lazy_static! {
static ref FILE: Result<std::fs::File, std::io::Error> =
std::fs::File::open("/dev/urandom");
}
match *FILE {
Ok(ref file) => {
use self::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 {
extern crate std;
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::CRYPTO_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),
}
}
}
#[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 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 {
static CRYPTO_sysrand_chunk_max_len: c::size_t;
fn CRYPTO_sysrand_chunk(buf: *mut u8, len: c::size_t) -> c::int;
}
#[cfg(test)]
pub mod test_util {
use core;
use error;
use super::*;
pub struct FixedByteRandom {
pub byte: u8
}
impl SecureRandom for FixedByteRandom {
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
for d in dest {
*d = self.byte
}
Ok(())
}
}
pub struct FixedSliceRandom<'a> {
pub bytes: &'a [u8],
}
impl <'a> SecureRandom for FixedSliceRandom<'a> {
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
assert_eq!(dest.len(), self.bytes.len());
for i in 0..self.bytes.len() {
dest[i] = self.bytes[i];
}
Ok(())
}
}
pub struct FixedSliceSequenceRandom<'a> {
pub bytes: &'a [&'a [u8]],
pub current: core::cell::UnsafeCell<usize>,
}
impl <'a> SecureRandom for FixedSliceSequenceRandom<'a> {
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
let current = unsafe { *self.current.get() };
let bytes = self.bytes[current];
assert_eq!(dest.len(), bytes.len());
for i in 0..bytes.len() {
dest[i] = bytes[i];
}
unsafe { *self.current.get() += 1 };
Ok(())
}
}
impl <'a> Drop for FixedSliceSequenceRandom<'a> {
fn drop(&mut self) {
assert_eq!(unsafe { *self.current.get() }, self.bytes.len());
}
}
}
#[cfg(test)]
mod tests {
use rand;
extern crate std;
#[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));
}
if *len > 96 && *len - 96 > max_chunk_len() {
assert!(buf[max_chunk_len()..].iter().any(|x| *x != 0));
}
}
}
#[cfg(any(target_os = "linux", windows))]
fn max_chunk_len() -> usize { super::CRYPTO_sysrand_chunk_max_len }
#[cfg(not(any(target_os = "linux", windows)))]
fn max_chunk_len() -> usize {
use core;
core::usize::MAX
}
}