#![allow(unsafe_code)]
#[cfg(feature = "cpu-rng-reseed")]
const RETRIES: usize = 16;
#[cfg(feature = "cpu-rng-reseed")]
pub(crate) fn cpu_rng_fill(out: &mut [u8]) -> bool {
use zeroize::Zeroize as _;
if imp::fill(out) && !stuck_output(out) {
return true;
}
out.zeroize();
false
}
#[cfg(feature = "cpu-rng-reseed")]
fn stuck_output(buf: &[u8]) -> bool {
let mut outer = 0_usize;
while outer + 8 <= buf.len() {
let word = &buf[outer..outer + 8];
let mut inner = outer + 8;
while inner + 8 <= buf.len() {
if &buf[inner..inner + 8] == word {
return true;
}
inner += 8;
}
outer += 8;
}
false
}
#[cfg(all(test, feature = "cpu-rng-reseed"))]
mod tests {
use super::stuck_output;
#[test]
fn flags_constant_byte_sources() {
assert!(stuck_output(&[0x00_u8; 48]));
assert!(stuck_output(&[0xff_u8; 48]));
assert!(stuck_output(&[0xfe_u8; 48]));
}
#[test]
fn flags_constant_word_sources() {
let word = 0x0123_4567_89ab_cdef_u64.to_le_bytes();
let mut buf = [0_u8; 48];
for chunk in buf.chunks_exact_mut(8) {
chunk.copy_from_slice(&word);
}
assert!(stuck_output(&buf));
}
#[test]
fn flags_any_duplicate_word() {
let mut buf = [0_u8; 48];
for (i, chunk) in buf.chunks_exact_mut(8).enumerate() {
chunk.copy_from_slice(&(i as u64).to_le_bytes());
}
buf[40..48].copy_from_slice(&0_u64.to_le_bytes()); assert!(stuck_output(&buf));
}
#[test]
fn passes_varied_output() {
let mut buf = [0_u8; 48];
for (i, chunk) in buf.chunks_exact_mut(8).enumerate() {
let word = 0x1111_1111_1111_1111_u64.wrapping_mul(i as u64 + 1);
chunk.copy_from_slice(&word.to_le_bytes());
}
assert!(!stuck_output(&buf));
}
}
#[cfg(not(feature = "cpu-rng-reseed"))]
pub(crate) fn cpu_rng_fill(_out: &mut [u8]) -> bool {
false
}
#[must_use]
pub fn cpu_rng_available() -> bool {
#[cfg(feature = "cpu-rng-reseed")]
{
imp::available()
}
#[cfg(not(feature = "cpu-rng-reseed"))]
{
false
}
}
#[cfg(all(feature = "cpu-rng-reseed", target_arch = "x86_64"))]
mod imp {
use core::arch::x86_64::_rdseed64_step;
pub(super) fn available() -> bool {
std::arch::is_x86_feature_detected!("rdseed")
}
pub(super) fn fill(out: &mut [u8]) -> bool {
if !available() {
return false;
}
unsafe { fill_rdseed(out) }
}
#[target_feature(enable = "rdseed")]
unsafe fn fill_rdseed(out: &mut [u8]) -> bool {
for chunk in out.chunks_mut(8) {
let mut value = 0_u64;
let mut ready = false;
for _ in 0..super::RETRIES {
if _rdseed64_step(&mut value) == 1 {
ready = true;
break;
}
core::hint::spin_loop();
}
if !ready {
return false;
}
let bytes = value.to_le_bytes();
chunk.copy_from_slice(&bytes[..chunk.len()]);
}
true
}
}
#[cfg(all(feature = "cpu-rng-reseed", target_arch = "aarch64"))]
mod imp {
pub(super) fn available() -> bool {
std::arch::is_aarch64_feature_detected!("rand")
}
pub(super) fn fill(out: &mut [u8]) -> bool {
if !available() {
return false;
}
unsafe { fill_rndrrs(out) }
}
#[target_feature(enable = "rand")]
unsafe fn fill_rndrrs(out: &mut [u8]) -> bool {
for chunk in out.chunks_mut(8) {
let mut value: u64 = 0;
let mut ready = false;
for _ in 0..super::RETRIES {
let ok: u64;
unsafe {
core::arch::asm!(
"mrs {value}, S3_3_C2_C4_1",
"cset {ok}, ne",
value = out(reg) value,
ok = out(reg) ok,
options(nostack, nomem),
);
}
if ok != 0 {
ready = true;
break;
}
core::hint::spin_loop();
}
if !ready {
return false;
}
let bytes = value.to_le_bytes();
chunk.copy_from_slice(&bytes[..chunk.len()]);
}
true
}
}
#[cfg(all(
feature = "cpu-rng-reseed",
not(any(target_arch = "x86_64", target_arch = "aarch64"))
))]
mod imp {
pub(super) fn available() -> bool {
false
}
pub(super) fn fill(_out: &mut [u8]) -> bool {
false
}
}