use super::ArchError;
pub fn fill_hardware_entropy(buf: &mut [u8]) -> Result<(), ArchError> {
for chunk in buf.chunks_mut(8) {
let mut value: u64 = 0;
let mut success: u8 = 0;
for _ in 0..10 {
unsafe {
core::arch::asm!(
"rdrand {0}",
"setc {1}",
out(reg) value,
out(reg_byte) success,
options(nomem, nostack),
);
}
if success != 0 {
let bytes = value.to_le_bytes();
let len = chunk.len().min(8);
chunk[..len].copy_from_slice(&bytes[..len]);
break;
}
}
if success == 0 {
return Err(ArchError::HardwareUnavailable);
}
}
Ok(())
}
pub fn fill_seed_entropy(buf: &mut [u8]) -> Result<(), ArchError> {
for chunk in buf.chunks_mut(8) {
let mut value: u64 = 0;
let mut success: u8 = 0;
for _ in 0..100 {
unsafe {
core::arch::asm!(
"rdseed {0}",
"setc {1}",
out(reg) value,
out(reg_byte) success,
options(nomem, nostack),
);
}
if success != 0 {
let bytes = value.to_le_bytes();
let len = chunk.len().min(8);
chunk[..len].copy_from_slice(&bytes[..len]);
break;
}
core::hint::spin_loop();
}
if success == 0 {
return Err(ArchError::HardwareUnavailable);
}
}
Ok(())
}
#[inline]
pub fn get_timestamp() -> u64 {
let low: u32;
let high: u32;
unsafe {
core::arch::asm!(
"rdtsc",
out("eax") low,
out("edx") high,
options(nomem, nostack, preserves_flags),
);
}
((high as u64) << 32) | (low as u64)
}
#[cfg(target_os = "linux")]
pub unsafe fn syscall3(num: i64, arg1: usize, arg2: usize, arg3: usize) -> i64 {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") num,
in("rdi") arg1,
in("rsi") arg2,
in("rdx") arg3,
out("rcx") _,
out("r11") _,
lateout("rax") ret,
options(nostack, preserves_flags),
);
ret
}
#[inline]
pub fn has_rdrand() -> bool {
let ecx: u32;
unsafe {
core::arch::asm!(
"push rbx", "mov eax, 1",
"cpuid",
"pop rbx", out("ecx") ecx,
out("eax") _,
out("edx") _,
options(nomem, preserves_flags),
);
}
(ecx & (1 << 30)) != 0
}
#[inline]
pub fn has_aesni() -> bool {
let ecx: u32;
unsafe {
core::arch::asm!(
"push rbx", "mov eax, 1",
"cpuid",
"pop rbx", out("ecx") ecx,
out("eax") _,
out("edx") _,
options(nomem, preserves_flags),
);
}
(ecx & (1 << 25)) != 0
}
#[inline]
pub fn has_pclmulqdq() -> bool {
let ecx: u32;
unsafe {
core::arch::asm!(
"push rbx",
"mov eax, 1",
"cpuid",
"pop rbx",
out("ecx") ecx,
out("eax") _,
out("edx") _,
options(nomem, preserves_flags),
);
}
(ecx & (1 << 1)) != 0
}
#[inline]
pub fn has_rdseed() -> bool {
let ebx: u32;
unsafe {
core::arch::asm!(
"push rbx", "mov eax, 7",
"xor ecx, ecx",
"cpuid",
"mov {0:e}, ebx", "pop rbx", out(reg) ebx,
out("eax") _,
out("ecx") _,
out("edx") _,
options(nomem, preserves_flags),
);
}
(ebx & (1 << 18)) != 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rdrand_available() {
let _available = has_rdrand();
}
#[test]
fn test_rdseed_available() {
let _available = has_rdseed();
}
#[test]
fn test_fill_entropy() {
if has_rdrand() {
let mut buf = [0u8; 32];
let result = fill_hardware_entropy(&mut buf);
assert!(result.is_ok());
assert_ne!(buf, [0u8; 32]); }
}
#[test]
fn test_timestamp() {
let t1 = get_timestamp();
let t2 = get_timestamp();
assert!(t2 >= t1); }
#[test]
fn test_entropy_uniqueness() {
if has_rdrand() {
let mut buf1 = [0u8; 32];
let mut buf2 = [0u8; 32];
fill_hardware_entropy(&mut buf1).unwrap();
fill_hardware_entropy(&mut buf2).unwrap();
assert_ne!(buf1, buf2); }
}
}