hermit/
entropy.rs

1//! Cryptographically secure random data generation.
2//!
3//! This currently uses a ChaCha-based generator (the same one Linux uses!) seeded
4//! with random data provided by the processor.
5
6use hermit_sync::InterruptTicketMutex;
7use rand_chacha::ChaCha20Rng;
8use rand_chacha::rand_core::{RngCore, SeedableRng};
9
10use crate::arch::kernel::processor::{get_timer_ticks, seed_entropy};
11use crate::errno::ENOSYS;
12
13// Reseed every second for increased security while maintaining the performance of
14// the PRNG.
15const RESEED_INTERVAL: u64 = 1_000_000;
16
17bitflags! {
18	pub struct Flags: u32 {}
19}
20
21struct Pool {
22	rng: ChaCha20Rng,
23	last_reseed: u64,
24}
25
26static POOL: InterruptTicketMutex<Option<Pool>> = InterruptTicketMutex::new(None);
27
28/// Fills `buf` with random data, respecting the options in `flags`.
29///
30/// Returns the number of bytes written or `-ENOSYS` if the system does not support
31/// random data generation.
32pub fn read(buf: &mut [u8], _flags: Flags) -> isize {
33	let pool = &mut *POOL.lock();
34	let now = get_timer_ticks();
35	let pool = match pool {
36		Some(pool) if now.saturating_sub(pool.last_reseed) <= RESEED_INTERVAL => pool,
37		pool => {
38			if let Some(seed) = seed_entropy() {
39				pool.insert(Pool {
40					rng: ChaCha20Rng::from_seed(seed),
41					last_reseed: now,
42				})
43			} else {
44				return -ENOSYS as isize;
45			}
46		}
47	};
48
49	pool.rng.fill_bytes(buf);
50	// Slice lengths are always <= isize::MAX so this return value cannot conflict
51	// with error numbers.
52	buf.len() as isize
53}