Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ pci-ids = { version = "0.2", optional = true }
scopeguard = { version = "1.1", default-features = false }
shell-words = { version = "1.1", default-features = false }
qemu-exit = "3.0"
rand_chacha = { version = "0.3", default-features = false }
futures-lite = { version = "1.11", default-features = false, optional = true }
async-task = { version = "4.3", default-features = false, optional = true }

Expand Down
6 changes: 1 addition & 5 deletions src/arch/aarch64/kernel/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ impl FPUState {
}
}

pub fn generate_random_number32() -> Option<u32> {
None
}

pub fn generate_random_number64() -> Option<u64> {
pub fn seed_entropy() -> Option<[u8; 32]> {
None
}

Expand Down
48 changes: 21 additions & 27 deletions src/arch/x86_64/kernel/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

use core::arch::asm;
use core::arch::x86_64::{
__rdtscp, _fxrstor, _fxsave, _mm_lfence, _rdrand32_step, _rdrand64_step, _rdtsc, _xrstor,
_xsave,
__rdtscp, _fxrstor, _fxsave, _mm_lfence, _rdseed64_step, _rdtsc, _xrstor, _xsave,
};
use core::convert::Infallible;
use core::hint::spin_loop;
Expand Down Expand Up @@ -49,7 +48,7 @@ struct Features {
linear_address_bits: u8,
supports_1gib_pages: bool,
supports_avx: bool,
supports_rdrand: bool,
supports_rdseed: bool,
supports_tsc_deadline: bool,
supports_x2apic: bool,
supports_xsave: bool,
Expand Down Expand Up @@ -79,7 +78,7 @@ static FEATURES: Lazy<Features> = Lazy::new(|| {
linear_address_bits: processor_capacity_info.linear_address_bits(),
supports_1gib_pages: extend_processor_identifiers.has_1gib_pages(),
supports_avx: feature_info.has_avx(),
supports_rdrand: feature_info.has_rdrand(),
supports_rdseed: extended_feature_info.has_rdseed(),
supports_tsc_deadline: feature_info.has_tsc_deadline(),
supports_x2apic: feature_info.has_x2apic(),
supports_xsave: feature_info.has_xsave(),
Expand Down Expand Up @@ -893,32 +892,27 @@ pub fn print_information() {
infofooter!();
}

pub fn generate_random_number32() -> Option<u32> {
unsafe {
if FEATURES.supports_rdrand {
let mut value: u32 = 0;

for _ in 0..RDRAND_RETRY_LIMIT {
if _rdrand32_step(&mut value) == 1 {
return Some(value);
}
pub fn seed_entropy() -> Option<[u8; 32]> {
let mut buf = [0; 32];
if FEATURES.supports_rdseed {
for word in buf.chunks_mut(8) {
let mut value = 0;

// Some RDRAND implementations on AMD CPUs have had bugs where the carry
// flag was incorrectly set without there actually being a random value
// available. Even though no bugs are known for RDSEED, we should not
// consider the default values random for extra security.
while unsafe { _rdseed64_step(&mut value) != 1 } || value == 0 || value == !0 {
// Spin as per the recommendation in the
// Intel® Digital Random Number Generator (DRNG) implementation guide
spin_loop();
}
}
None
}
}

pub fn generate_random_number64() -> Option<u64> {
unsafe {
if FEATURES.supports_rdrand {
let mut value: u64 = 0;

for _ in 0..RDRAND_RETRY_LIMIT {
if _rdrand64_step(&mut value) == 1 {
return Some(value);
}
}
word.copy_from_slice(&value.to_ne_bytes());
}

Some(buf)
} else {
None
}
}
Expand Down
53 changes: 53 additions & 0 deletions src/entropy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! Cryptographically secure random data generation.
//!
//! This currently uses a ChaCha-based generator (the same one Linux uses!) seeded
//! with random data provided by the processor.

use hermit_sync::InterruptTicketMutex;
use rand_chacha::rand_core::{RngCore, SeedableRng};
use rand_chacha::ChaCha20Rng;

use crate::arch::kernel::processor::{get_timer_ticks, seed_entropy};
use crate::errno::ENOSYS;

// Reseed every second for increased security while maintaining the performance of
// the PRNG.
const RESEED_INTERVAL: u64 = 1000000;

bitflags! {
pub struct Flags: u32 {}
}

struct Pool {
rng: ChaCha20Rng,
last_reseed: u64,
}

static POOL: InterruptTicketMutex<Option<Pool>> = InterruptTicketMutex::new(None);

/// Fills `buf` with random data, respecting the options in `flags`.
///
/// Returns the number of bytes written or `-ENOSYS` if the system does not support
/// random data generation.
pub fn read(buf: &mut [u8], _flags: Flags) -> isize {
let pool = &mut *POOL.lock();
let now = get_timer_ticks();
let pool = match pool {
Some(pool) if now.saturating_sub(pool.last_reseed) <= RESEED_INTERVAL => pool,
pool => {
if let Some(seed) = seed_entropy() {
pool.insert(Pool {
rng: ChaCha20Rng::from_seed(seed),
last_reseed: now,
})
} else {
return -ENOSYS as isize;
}
}
};

pool.rng.fill_bytes(buf);
// Slice lengths are always <= isize::MAX so this return value cannot conflict
// with error numbers.
buf.len() as isize
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ mod arch;
mod config;
mod console;
mod drivers;
mod entropy;
mod env;
pub mod errno;
pub(crate) mod fd;
Expand Down
12 changes: 0 additions & 12 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,6 @@ macro_rules! dbg {
};
}

macro_rules! try_sys {
($expr:expr $(,)?) => {
match $expr {
::core::result::Result::Ok(val) => val,
::core::result::Result::Err(err) => {
error!("{err}");
return -1;
}
}
};
}

/// Runs `f` on the kernel stack.
///
/// All arguments and return values have to fit into registers:
Expand Down
77 changes: 57 additions & 20 deletions src/syscalls/random.rs → src/syscalls/entropy.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use core::mem::size_of;
use core::slice;

use hermit_sync::TicketMutex;

use crate::arch;
use crate::entropy::{self, Flags};
use crate::errno::EINVAL;

static PARK_MILLER_LEHMER_SEED: TicketMutex<u32> = TicketMutex::new(0);
const RAND_MAX: u64 = 2_147_483_647;
Expand All @@ -12,42 +17,74 @@ fn generate_park_miller_lehmer_random_number() -> u32 {
random
}

unsafe extern "C" fn __sys_rand32(value: *mut u32) -> i32 {
let rand = try_sys!(arch::processor::generate_random_number32().ok_or("sys_rand32 failed"));
unsafe {
value.write(rand);
}
0
}
unsafe extern "C" fn __sys_read_entropy(buf: *mut u8, len: usize, flags: u32) -> isize {
let Some(flags) = Flags::from_bits(flags) else { return -EINVAL as isize };

unsafe extern "C" fn __sys_rand64(value: *mut u64) -> i32 {
let rand = try_sys!(arch::processor::generate_random_number64().ok_or("sys_rand64 failed"));
unsafe {
value.write(rand);
}
0
let buf = unsafe {
// Cap the number of bytes to be read at a time to isize::MAX to uphold
// the safety guarantees of `from_raw_parts`.
let len = usize::min(len, isize::MAX as usize);
buf.write_bytes(0, len);
slice::from_raw_parts_mut(buf, len)
};

entropy::read(buf, flags)
}

extern "C" fn __sys_rand() -> u32 {
generate_park_miller_lehmer_random_number()
/// Fill `len` bytes in `buf` with cryptographically secure random data.
///
/// Returns either the number of bytes written to buf (a positive value) or
/// * `-EINVAL` if `flags` contains unknown flags.
/// * `-ENOSYS` if the system does not support random data generation.
#[no_mangle]
pub unsafe extern "C" fn sys_read_entropy(buf: *mut u8, len: usize, flags: u32) -> isize {
kernel_function!(__sys_read_entropy(buf, len, flags))
}

/// Create a cryptographicly secure 32bit random number with the support of
/// the underlying hardware. If the required hardware isn't available,
/// the function returns `None`.
/// the function returns `-1`.
#[cfg(not(feature = "newlib"))]
#[no_mangle]
pub unsafe extern "C" fn sys_secure_rand32(value: *mut u32) -> i32 {
kernel_function!(__sys_rand32(value))
let mut buf = value.cast();
let mut len = size_of::<u32>();
while len != 0 {
let res = unsafe { sys_read_entropy(buf, len, 0) };
if res < 0 {
return -1;
}

buf = unsafe { buf.add(res as usize) };
len -= res as usize;
}

0
}

/// Create a cryptographicly secure 64bit random number with the support of
/// the underlying hardware. If the required hardware isn't available,
/// the function returns `None`.
/// the function returns -1.
#[cfg(not(feature = "newlib"))]
#[no_mangle]
pub unsafe extern "C" fn sys_secure_rand64(value: *mut u64) -> i32 {
kernel_function!(__sys_rand64(value))
let mut buf = value.cast();
let mut len = size_of::<u64>();
while len != 0 {
let res = unsafe { sys_read_entropy(buf, len, 0) };
if res < 0 {
return -1;
}

buf = unsafe { buf.add(res as usize) };
len -= res as usize;
}

0
}

extern "C" fn __sys_rand() -> u32 {
generate_park_miller_lehmer_random_number()
}

/// The function computes a sequence of pseudo-random integers
Expand All @@ -68,7 +105,7 @@ pub extern "C" fn sys_srand(seed: u32) {
kernel_function!(__sys_srand(seed))
}

pub(crate) fn random_init() {
pub(crate) fn init_entropy() {
let seed: u32 = arch::processor::get_timestamp() as u32;

*PARK_MILLER_LEHMER_SEED.lock() = seed;
Expand Down
6 changes: 3 additions & 3 deletions src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use hermit_sync::InterruptTicketMutex;
use hermit_sync::Lazy;

pub use self::condvar::*;
pub use self::entropy::*;
pub use self::futex::*;
pub use self::processor::*;
pub use self::random::*;
pub use self::recmutex::*;
pub use self::semaphore::*;
pub use self::spinlock::*;
Expand All @@ -21,6 +21,7 @@ use crate::syscalls::interfaces::SyscallInterface;
use crate::{__sys_free, __sys_malloc, __sys_realloc};

mod condvar;
mod entropy;
pub(crate) mod fs;
mod futex;
mod interfaces;
Expand All @@ -29,7 +30,6 @@ mod lwip;
#[cfg(all(feature = "tcp", not(feature = "newlib")))]
mod net;
mod processor;
mod random;
mod recmutex;
mod semaphore;
mod spinlock;
Expand Down Expand Up @@ -68,7 +68,7 @@ pub(crate) fn init() {
// Perform interface-specific initialization steps.
SYS.init();

random_init();
init_entropy();
#[cfg(feature = "newlib")]
sbrk_init();
}
Expand Down