From 70ce893dafc0e376d976b37d7db7fde0fa934d79 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Mon, 2 Mar 2026 21:56:58 +0000 Subject: [PATCH] rand: add `EntropySource` Add an `EntropySource` trait which allows the `EntropyPool` to pull entropy from various sources. --- src/kernel/rand.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/kernel/rand.rs b/src/kernel/rand.rs index dc0bd97..3044e04 100644 --- a/src/kernel/rand.rs +++ b/src/kernel/rand.rs @@ -1,5 +1,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; +use alloc::{sync::Arc, vec::Vec}; + use crate::{ drivers::timer::uptime, memory::uaccess::copy_to_user_slice, @@ -12,6 +14,13 @@ use libkernel::memory::address::TUA; use libkernel::{error::Result, sync::condvar::WakeupType}; use rand::{Rng, SeedableRng}; +/// A hardware or software source of entropy that the pool can query. +pub trait EntropySource: Send + Sync { + /// Pull up to `buf.len()` bytes of entropy from this source. + /// Returns `(bytes_written, approx_entropy_bits)`. + fn get_entropy(&self, buf: &mut [u8]) -> (usize, usize); +} + /// Number of bytes generated before a per-CPU RNG reseeds from the entropy pool. const RESEED_BYTES: usize = 1024 * 1024; @@ -19,6 +28,7 @@ pub struct EntropyPool { state: SpinLock, pool_waiters: CondVar, pool_bits: AtomicUsize, + sources: SpinLock>>, } impl EntropyPool { @@ -27,6 +37,7 @@ impl EntropyPool { state: SpinLock::new(Blake2s256::default()), pool_waiters: CondVar::new(false), pool_bits: AtomicUsize::new(0), + sources: SpinLock::new(Vec::new()), } } @@ -57,19 +68,37 @@ impl EntropyPool { ); } + /// Poll all registered entropy sources and feed their output into the pool. + fn poll_sources(&self) { + let sources = self.sources.lock_save_irq(); + let mut buf = [0u8; 64]; + + for source in sources.iter() { + let (written, bits) = source.get_entropy(&mut buf); + if written > 0 && bits > 0 { + self.add_entropy(&buf[..written], bits); + } + } + } + /// Block until the pool has accumulated at least 256 bits of entropy, then /// return a 32-byte seed derived from the pool state. pub async fn extract_seed(&self) -> [u8; 32] { + self.poll_sources(); + self.pool_waiters .wait_until(|s| if *s { Some(()) } else { None }) .await; + self.poll_sources(); self.extract_seed_inner() } /// Non-blocking seed extraction. Returns `None` if the pool has not yet /// accumulated 256 bits of entropy. pub fn try_extract_seed(&self) -> Option<[u8; 32]> { + self.poll_sources(); + if self.pool_bits.load(Ordering::Relaxed) < 256 { return None; } @@ -97,6 +126,11 @@ pub fn entropy_pool() -> &'static EntropyPool { ENTROPY_POOL.get_or_init(EntropyPool::new) } +/// Register a new entropy source that the pool can pull from. +pub fn register_entropy_source(source: Arc) { + entropy_pool().sources.lock_save_irq().push(source); +} + static ENTROPY_POOL: OnceLock = OnceLock::new(); struct CpuRng {