use crate::error::{FAILED_RDRAND, NO_RDRAND};
#[cfg(not(target_feature = "rdrand"))]
use crate::util::LazyBool;
use crate::Error;
use core::arch::x86_64::_rdrand64_step;
use core::mem;
const RETRY_LIMIT: usize = 10;
const WORD_SIZE: usize = mem::size_of::<u64>();
#[target_feature(enable = "rdrand")]
unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> {
for _ in 0..RETRY_LIMIT {
let mut el = mem::zeroed();
if _rdrand64_step(&mut el) == 1 {
if el != 0 && el != !0 {
return Ok(el.to_ne_bytes());
}
error!("RDRAND returned {:X}, CPU RNG may be broken", el);
}
}
Err(FAILED_RDRAND)
}
#[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))]
compile_error!(
"SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd."
);
#[cfg(target_feature = "rdrand")]
fn is_rdrand_supported() -> bool {
true
}
#[cfg(not(target_feature = "rdrand"))]
fn is_rdrand_supported() -> bool {
use core::arch::x86_64::__cpuid;
const FLAG: u32 = 1 << 30;
static HAS_RDRAND: LazyBool = LazyBool::new();
HAS_RDRAND.unsync_init(|| unsafe { (__cpuid(1).ecx & FLAG) != 0 })
}
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
if !is_rdrand_supported() {
return Err(NO_RDRAND);
}
unsafe { rdrand_exact(dest) }
}
#[target_feature(enable = "rdrand")]
unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> {
let mut chunks = dest.chunks_exact_mut(WORD_SIZE);
for chunk in chunks.by_ref() {
chunk.copy_from_slice(&rdrand()?);
}
let tail = chunks.into_remainder();
let n = tail.len();
if n > 0 {
tail.copy_from_slice(&rdrand()?[..n]);
}
Ok(())
}