use crate::arch;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RandomError {
EntropyUnavailable,
InsufficientEntropy,
HardwareFailure,
}
pub struct CryptoRng {
entropy_pool: [u8; 32],
pool_position: usize,
reseed_counter: u64,
}
impl CryptoRng {
pub fn new() -> Result<Self, RandomError> {
let mut rng = Self {
entropy_pool: [0u8; 32],
pool_position: 32, reseed_counter: 0,
};
rng.reseed()?;
Ok(rng)
}
fn reseed(&mut self) -> Result<(), RandomError> {
#[cfg(target_os = "linux")]
{
if self.getrandom_syscall().is_ok() {
self.pool_position = 0;
self.reseed_counter += 1;
return Ok(());
}
}
if arch::has_rdrand() && arch::fill_hardware_entropy(&mut self.entropy_pool).is_ok() {
self.pool_position = 0;
self.reseed_counter += 1;
return Ok(());
}
crate::lcpfs_println!("[ RANDOM ] WARNING: Using insecure entropy source!");
self.timer_entropy_fallback();
self.pool_position = 0;
self.reseed_counter += 1;
Ok(())
}
pub fn fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RandomError> {
for byte in dest.iter_mut() {
if self.pool_position >= 32 || self.reseed_counter % 256 == 0 {
self.reseed()?;
}
*byte = self.entropy_pool[self.pool_position];
self.pool_position += 1;
}
Ok(())
}
pub fn next_u64(&mut self) -> Result<u64, RandomError> {
let mut bytes = [0u8; 8];
self.fill_bytes(&mut bytes)?;
Ok(u64::from_le_bytes(bytes))
}
pub fn next_u32(&mut self) -> Result<u32, RandomError> {
let mut bytes = [0u8; 4];
self.fill_bytes(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
}
#[cfg(target_os = "linux")]
fn getrandom_syscall(&mut self) -> Result<(), RandomError> {
#[cfg(target_arch = "x86_64")]
const SYS_GETRANDOM: i64 = 318;
#[cfg(target_arch = "aarch64")]
const SYS_GETRANDOM: i64 = 278;
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
const SYS_GETRANDOM: i64 = -1;
const GRND_NONBLOCK: i32 = 0x01;
if SYS_GETRANDOM < 0 {
return Err(RandomError::EntropyUnavailable);
}
unsafe {
let ret = arch::syscall3(
SYS_GETRANDOM,
self.entropy_pool.as_mut_ptr() as usize,
self.entropy_pool.len(),
GRND_NONBLOCK as usize,
);
if ret == self.entropy_pool.len() as i64 {
Ok(())
} else {
Err(RandomError::EntropyUnavailable)
}
}
}
fn timer_entropy_fallback(&mut self) {
let time = arch::get_timestamp();
let stack_addr = &self.entropy_pool as *const _ as u64;
let mut seed = time ^ stack_addr;
for byte in self.entropy_pool.iter_mut() {
seed = seed
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
*byte = (seed >> 56) as u8;
}
}
}
static mut GLOBAL_RNG: Option<CryptoRng> = None;
static mut RNG_INIT: bool = false;
pub fn init_global_rng() -> Result<(), RandomError> {
unsafe {
if !RNG_INIT {
GLOBAL_RNG = Some(CryptoRng::new()?);
RNG_INIT = true;
}
Ok(())
}
}
pub fn fill_random(buf: &mut [u8]) -> Result<(), RandomError> {
unsafe {
if !RNG_INIT {
init_global_rng()?;
}
if let Some(ref mut rng) = GLOBAL_RNG {
rng.fill_bytes(buf)
} else {
Err(RandomError::EntropyUnavailable)
}
}
}
pub fn generate_key() -> Result<[u8; 32], RandomError> {
let mut key = [0u8; 32];
fill_random(&mut key)?;
Ok(key)
}
pub fn generate_nonce() -> Result<[u8; 16], RandomError> {
let mut nonce = [0u8; 16];
fill_random(&mut nonce)?;
Ok(nonce)
}
pub fn generate_salt() -> Result<[u8; 32], RandomError> {
let mut salt = [0u8; 32];
fill_random(&mut salt)?;
Ok(salt)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rng_creation() {
let rng = CryptoRng::new();
assert!(rng.is_ok());
}
#[test]
fn test_fill_bytes() {
let mut rng = CryptoRng::new().expect("test: operation should succeed");
let mut buf1 = [0u8; 32];
let mut buf2 = [0u8; 32];
rng.fill_bytes(&mut buf1)
.expect("test: operation should succeed");
rng.fill_bytes(&mut buf2)
.expect("test: operation should succeed");
assert_ne!(buf1, buf2);
}
#[test]
fn test_generate_key() {
let key1 = generate_key().expect("test: operation should succeed");
let key2 = generate_key().expect("test: operation should succeed");
assert_ne!(key1, key2);
assert_ne!(key1, [0u8; 32]);
}
#[test]
fn test_generate_nonce() {
let nonce1 = generate_nonce().expect("test: operation should succeed");
let nonce2 = generate_nonce().expect("test: operation should succeed");
assert_ne!(nonce1, nonce2);
}
#[test]
fn test_next_u64() {
let mut rng = CryptoRng::new().expect("test: operation should succeed");
let val1 = rng.next_u64().expect("test: operation should succeed");
let val2 = rng.next_u64().expect("test: operation should succeed");
assert_ne!(val1, val2);
}
}