#![cfg_attr(not(feature = "std"), no_std)]
pub mod changelog;
mod errors;
pub use errors::ErrorCode;
use rand_core::{CryptoRng, Error, RngCore};
#[cold]
#[inline(never)]
pub(crate) fn busy_loop_fail(code: ErrorCode) -> ! {
panic!("{}", code);
}
#[derive(Clone, Copy)]
pub struct RdRand(());
#[derive(Clone, Copy)]
pub struct RdSeed(());
impl CryptoRng for RdRand {}
impl CryptoRng for RdSeed {}
mod arch {
#[cfg(target_arch = "x86")]
pub use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
pub use core::arch::x86_64::*;
#[cfg(target_arch = "x86")]
pub(crate) unsafe fn _rdrand64_step(dest: &mut u64) -> i32 {
let mut ret1: u32 = 0;
let mut ret2: u32 = 0;
let ok = _rdrand32_step(&mut ret1) & _rdrand32_step(&mut ret2);
*dest = (ret1 as u64) << 32 | (ret2 as u64);
ok
}
#[cfg(target_arch = "x86")]
pub(crate) unsafe fn _rdseed64_step(dest: &mut u64) -> i32 {
let mut ret1: u32 = 0;
let mut ret2: u32 = 0;
let ok = _rdseed32_step(&mut ret1) & _rdseed32_step(&mut ret2);
*dest = (ret1 as u64) << 32 | (ret2 as u64);
ok
}
}
macro_rules! loop_rand {
("rdrand", $el: ty, $step: path) => {{
let mut idx = 0;
loop {
let mut el: $el = 0;
if $step(&mut el) != 0 {
break Ok(el);
} else if idx == 10 {
break Err(ErrorCode::HardwareFailure);
}
idx += 1;
}
}};
("rdseed", $el: ty, $step: path) => {{
let mut idx = 0;
loop {
let mut el: $el = 0;
if $step(&mut el) != 0 {
break Ok(el);
} else if idx == 127 {
break Err(ErrorCode::HardwareFailure);
}
idx += 1;
arch::_mm_pause();
}
}};
}
macro_rules! is_available {
("rdrand") => {{
const FLAG: u32 = 1 << 30;
let cpuid0 = unsafe { arch::__cpuid(0) };
if let (0x68747541, 0x444D4163, 0x69746E65) = (cpuid0.ebx, cpuid0.ecx, cpuid0.edx) {
let cpuid1 = unsafe { arch::__cpuid(1) };
let family = ((cpuid1.eax >> 8) & 0xF) + ((cpuid1.eax >> 20) & 0xFF);
(cpuid1.ecx & FLAG == FLAG) && (family >= 0x17)
} else {
cfg!(target_feature="rdrand") || unsafe { arch::__cpuid(1).ecx & FLAG == FLAG }
}
}};
("rdseed") => {{
const FLAG : u32 = 1 << 18;
let cpuid0 = unsafe { arch::__cpuid(0) };
if let (0x68747541, 0x444D4163, 0x69746E65) = (cpuid0.ebx, cpuid0.ecx, cpuid0.edx) {
let cpuid1 = unsafe { arch::__cpuid(1) };
let family = ((cpuid1.eax >> 8) & 0xF) + ((cpuid1.eax >> 20) & 0xFF);
(family >= 0x17) && unsafe { arch::__cpuid(7).ebx & FLAG == FLAG }
} else {
cfg!(target_feature="rdrand") || unsafe { arch::__cpuid(7).ebx & FLAG == FLAG }
}
}};
}
macro_rules! impl_rand {
($gen:ident, $feat:tt, $step16: path, $step32:path, $step64:path,
maxstep = $maxstep:path, maxty = $maxty: ty) => {
impl $gen {
pub fn new() -> Result<Self, ErrorCode> {
if cfg!(target_env="sgx") {
if cfg!(target_feature=$feat) {
Ok($gen(()))
} else {
Err(ErrorCode::UnsupportedInstruction)
}
} else if is_available!($feat) {
Ok($gen(()))
} else {
Err(ErrorCode::UnsupportedInstruction)
}
}
#[inline(always)]
pub fn try_next_u16(&self) -> Result<u16, ErrorCode> {
#[target_feature(enable = $feat)]
unsafe fn imp() -> Result<u16, ErrorCode> {
loop_rand!($feat, u16, $step16)
}
unsafe { imp() }
}
#[inline(always)]
pub fn try_next_u32(&self) -> Result<u32, ErrorCode> {
#[target_feature(enable = $feat)]
unsafe fn imp() -> Result<u32, ErrorCode> {
loop_rand!($feat, u32, $step32)
}
unsafe { imp() }
}
#[inline(always)]
pub fn try_next_u64(&self) -> Result<u64, ErrorCode> {
#[target_feature(enable = $feat)]
unsafe fn imp() -> Result<u64, ErrorCode> {
loop_rand!($feat, u64, $step64)
}
unsafe { imp() }
}
#[inline(always)]
pub fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ErrorCode> {
#[target_feature(enable = $feat)]
unsafe fn imp(dest: &mut [u8]) -> Result<(), ErrorCode> {
fn slow_fill_bytes<'a>(
mut left: &'a mut [u8],
mut right: &'a mut [u8],
) -> Result<(), ErrorCode> {
let mut word;
let mut buffer: &[u8] = &[];
while !left.is_empty() {
if buffer.is_empty() {
word =
unsafe { loop_rand!($feat, $maxty, $maxstep) }?.to_ne_bytes();
buffer = &word[..];
}
let len = left.len().min(buffer.len());
let (copy_src, leftover) = buffer.split_at(len);
let (copy_dest, dest_leftover) = { left }.split_at_mut(len);
buffer = leftover;
left = dest_leftover;
copy_dest.copy_from_slice(copy_src);
if left.is_empty() {
::core::mem::swap(&mut left, &mut right);
}
}
Ok(())
}
let destlen = dest.len();
if destlen > ::core::mem::size_of::<$maxty>() {
let (left, mid, right) = dest.align_to_mut();
for el in mid {
*el = loop_rand!($feat, $maxty, $maxstep)?;
}
slow_fill_bytes(left, right)
} else {
slow_fill_bytes(dest, &mut [])
}
}
unsafe { imp(dest) }
}
}
impl RngCore for $gen {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
match self.try_next_u32() {
Ok(result) => result,
Err(c) => busy_loop_fail(c),
}
}
#[inline(always)]
fn next_u64(&mut self) -> u64 {
match self.try_next_u64() {
Ok(result) => result,
Err(c) => busy_loop_fail(c),
}
}
#[inline(always)]
fn fill_bytes(&mut self, dest: &mut [u8]) {
match self.try_fill_bytes(dest) {
Ok(result) => result,
Err(c) => busy_loop_fail(c),
}
}
#[inline(always)]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.try_fill_bytes(dest).map_err(Into::into)
}
}
};
}
#[cfg(target_arch = "x86_64")]
impl_rand!(
RdRand,
"rdrand",
arch::_rdrand16_step,
arch::_rdrand32_step,
arch::_rdrand64_step,
maxstep = arch::_rdrand64_step,
maxty = u64
);
#[cfg(target_arch = "x86_64")]
impl_rand!(
RdSeed,
"rdseed",
arch::_rdseed16_step,
arch::_rdseed32_step,
arch::_rdseed64_step,
maxstep = arch::_rdseed64_step,
maxty = u64
);
#[cfg(target_arch = "x86")]
impl_rand!(
RdRand,
"rdrand",
arch::_rdrand16_step,
arch::_rdrand32_step,
arch::_rdrand64_step,
maxstep = arch::_rdrand32_step,
maxty = u32
);
#[cfg(target_arch = "x86")]
impl_rand!(
RdSeed,
"rdseed",
arch::_rdseed16_step,
arch::_rdseed32_step,
arch::_rdseed64_step,
maxstep = arch::_rdseed32_step,
maxty = u32
);
#[cfg(test)]
mod test {
use super::{RdRand, RdSeed};
use rand_core::RngCore;
#[test]
fn rdrand_works() {
let _ = RdRand::new().map(|mut r| {
r.next_u32();
r.next_u64();
});
}
#[test]
fn fill_fills_all_bytes() {
let _ = RdRand::new().map(|mut r| {
let mut peach;
let mut banana;
let mut start = 0;
let mut end = 128;
'outer: while start < end {
banana = [0; 128];
for _ in 0..512 {
peach = [0; 128];
r.fill_bytes(&mut peach[start..end]);
for (b, p) in banana.iter_mut().zip(peach.iter()) {
*b = *b | *p;
}
if (&banana[start..end]).iter().all(|x| *x != 0) {
assert!(
banana[..start].iter().all(|x| *x == 0),
"all other values must be 0"
);
assert!(
banana[end..].iter().all(|x| *x == 0),
"all other values must be 0"
);
if start < 17 {
start += 1;
} else {
end -= 3;
}
continue 'outer;
}
}
panic!("wow, we broke it? {} {} {:?}", start, end, &banana[..])
}
});
}
#[test]
fn rdseed_works() {
let _ = RdSeed::new().map(|mut r| {
r.next_u32();
r.next_u64();
});
}
}