Skip to main content

hermit/syscalls/
entropy.rs

1use core::slice;
2
3use hermit_sync::TicketMutex;
4
5use crate::arch;
6use crate::entropy::{self, Flags};
7use crate::errno::Errno;
8
9static PARK_MILLER_LEHMER_SEED: TicketMutex<u32> = TicketMutex::new(0);
10const RAND_MAX: u64 = 0x7fff_ffff;
11
12fn generate_park_miller_lehmer_random_number() -> u32 {
13	let mut seed = PARK_MILLER_LEHMER_SEED.lock();
14	let random = ((u64::from(*seed) * 48271) % RAND_MAX) as u32;
15	*seed = random;
16	random
17}
18
19unsafe fn read_entropy(buf: *mut u8, len: usize, flags: u32) -> isize {
20	let Some(flags) = Flags::from_bits(flags) else {
21		return -i32::from(Errno::Inval) as isize;
22	};
23
24	let buf = unsafe {
25		// Cap the number of bytes to be read at a time to isize::MAX to uphold
26		// the safety guarantees of `from_raw_parts`.
27		let len = usize::min(len, isize::MAX as usize);
28		buf.write_bytes(0, len);
29		slice::from_raw_parts_mut(buf, len)
30	};
31
32	let ret = entropy::read(buf, flags);
33	if ret < 0 {
34		warn!("Unable to read entropy! Fallback to a naive implementation!");
35		for byte in &mut *buf {
36			*byte = (generate_park_miller_lehmer_random_number() & 0xff)
37				.try_into()
38				.unwrap();
39		}
40		buf.len().try_into().unwrap()
41	} else {
42		ret
43	}
44}
45
46/// Fill `len` bytes in `buf` with cryptographically secure random data.
47///
48/// Returns either the number of bytes written to buf (a positive value) or
49/// * `-EINVAL` if `flags` contains unknown flags.
50/// * `-ENOSYS` if the system does not support random data generation.
51#[hermit_macro::system]
52#[unsafe(no_mangle)]
53pub unsafe extern "C" fn sys_read_entropy(buf: *mut u8, len: usize, flags: u32) -> isize {
54	unsafe { read_entropy(buf, len, flags) }
55}
56
57/// Create a cryptographicly secure 32bit random number with the support of
58/// the underlying hardware. If the required hardware isn't available,
59/// the function returns `-1`.
60#[cfg(not(feature = "newlib"))]
61#[hermit_macro::system]
62#[unsafe(no_mangle)]
63pub unsafe extern "C" fn sys_secure_rand32(value: *mut u32) -> i32 {
64	let mut buf = value.cast();
65	let mut len = size_of::<u32>();
66	while len != 0 {
67		let res = unsafe { read_entropy(buf, len, 0) };
68		if res < 0 {
69			return -1;
70		}
71
72		buf = unsafe { buf.add(res as usize) };
73		len -= res as usize;
74	}
75
76	0
77}
78
79/// Create a cryptographicly secure 64bit random number with the support of
80/// the underlying hardware. If the required hardware isn't available,
81/// the function returns -1.
82#[cfg(not(feature = "newlib"))]
83#[hermit_macro::system]
84#[unsafe(no_mangle)]
85pub unsafe extern "C" fn sys_secure_rand64(value: *mut u64) -> i32 {
86	let mut buf = value.cast();
87	let mut len = size_of::<u64>();
88	while len != 0 {
89		let res = unsafe { read_entropy(buf, len, 0) };
90		if res < 0 {
91			return -1;
92		}
93
94		buf = unsafe { buf.add(res as usize) };
95		len -= res as usize;
96	}
97
98	0
99}
100
101/// The function computes a sequence of pseudo-random integers
102/// in the range of 0 to RAND_MAX
103#[hermit_macro::system]
104#[unsafe(no_mangle)]
105pub extern "C" fn sys_rand() -> u32 {
106	generate_park_miller_lehmer_random_number()
107}
108
109/// The function sets its argument as the seed for a new sequence
110/// of pseudo-random numbers to be returned by rand()
111#[hermit_macro::system]
112#[unsafe(no_mangle)]
113pub extern "C" fn sys_srand(seed: u32) {
114	*(PARK_MILLER_LEHMER_SEED.lock()) = seed;
115}
116
117pub(crate) fn init_entropy() {
118	let seed: u32 = arch::processor::get_timestamp() as u32;
119
120	*PARK_MILLER_LEHMER_SEED.lock() = seed;
121}