mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-04-18 06:08:09 -04:00
69
Cargo.lock
generated
69
Cargo.lock
generated
@@ -58,6 +58,24 @@ version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
@@ -126,6 +144,16 @@ version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.100"
|
||||
@@ -176,6 +204,17 @@ dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
@@ -305,6 +344,16 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getargs"
|
||||
version = "0.5.0"
|
||||
@@ -492,6 +541,8 @@ dependencies = [
|
||||
"arm-pl011-uart",
|
||||
"async-trait",
|
||||
"bitflags 2.11.0",
|
||||
"blake2",
|
||||
"chacha20",
|
||||
"fdt-parser",
|
||||
"futures",
|
||||
"getargs",
|
||||
@@ -815,6 +866,12 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
@@ -911,6 +968,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
@@ -933,6 +996,12 @@ dependencies = [
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-drivers"
|
||||
version = "0.12.0"
|
||||
|
||||
@@ -42,6 +42,8 @@ rustc-hash = { version = "2.1", default-features = false }
|
||||
smoltcp = { version = "0.12.0", default-features = false, features = ["alloc", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-ipv6", "socket-tcp", "socket-udp"] }
|
||||
tock-registers = "0.10.1"
|
||||
virtio-drivers = "0.12.0"
|
||||
blake2 = { version = "0.10.6", default-features = false }
|
||||
chacha20 = { version = "0.10.0", default-features = false, features = ["rng"] }
|
||||
|
||||
[build-dependencies]
|
||||
time = { version = "0.3.47", features = ["formatting", "macros"] } # For build timestamping via build.rs
|
||||
|
||||
@@ -23,6 +23,10 @@ pub enum ProbeError {
|
||||
// Driver probing should be tried again after other probes have succeeded.
|
||||
#[error("Driver probing deferred for other dependencies")]
|
||||
Deferred,
|
||||
|
||||
// Device inspected but not a match for this driver; skip silently.
|
||||
#[error("Device not matched by driver")]
|
||||
NoMatch,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq, Clone)]
|
||||
|
||||
@@ -41,9 +41,12 @@ default_args = {
|
||||
"-nographic": None,
|
||||
"-s": None,
|
||||
"-kernel": bin_executable_location,
|
||||
"-append": f"{append_args} --rootfs=ext4fs --automount=/dev,devfs --automount=/tmp,tmpfs --automount=/proc,procfs --automount=/sys,sysfs"
|
||||
"-append": f"{append_args} --rootfs=ext4fs --automount=/dev,devfs --automount=/tmp,tmpfs --automount=/proc,procfs --automount=/sys,sysfs",
|
||||
}
|
||||
|
||||
# Arguments that can appear multiple times (e.g. -device)
|
||||
extra_args = ["-device", "virtio-rng-device"]
|
||||
|
||||
if args.debug:
|
||||
default_args["-S"] = None
|
||||
|
||||
@@ -53,7 +56,7 @@ if args.display:
|
||||
default_args["-nic"] = "none"
|
||||
# Add uart
|
||||
default_args["-serial"] = "stdio"
|
||||
default_args["-device"] = "virtio-gpu-device"
|
||||
extra_args += ["-device", "virtio-gpu-device"]
|
||||
|
||||
qemu_command = ["qemu-system-aarch64"]
|
||||
|
||||
@@ -62,4 +65,6 @@ for key, value in default_args.items():
|
||||
if value is not None:
|
||||
qemu_command.append(value)
|
||||
|
||||
qemu_command += extra_args
|
||||
|
||||
subprocess.run(qemu_command, check=True)
|
||||
|
||||
@@ -3,6 +3,7 @@ use super::meta::VSUSP;
|
||||
use super::{TtyInputHandler, meta::*};
|
||||
use crate::console::Console;
|
||||
use crate::kernel::kpipe::KPipe;
|
||||
use crate::kernel::rand::entropy_pool;
|
||||
use crate::process::thread_group::Pgid;
|
||||
use crate::process::thread_group::signal::SigId;
|
||||
use crate::process::thread_group::signal::kill::send_signal_to_pg;
|
||||
@@ -52,6 +53,11 @@ impl TtyInputHandler for SpinLock<TtyInputCooker> {
|
||||
..
|
||||
} = &mut *this;
|
||||
|
||||
// Seed the entropy pool.
|
||||
//
|
||||
// SAFETY: A console interrupt isn't periodic.
|
||||
entropy_pool().add_temporal_entropy();
|
||||
|
||||
// Handle signal-generating control characters
|
||||
if termios.c_lflag.contains(TermiosLocalFlags::ISIG) {
|
||||
let intr_char = termios.c_cc[VINTR];
|
||||
|
||||
@@ -7,10 +7,11 @@ use crate::{
|
||||
fops::FileOps,
|
||||
open_file::{FileCtx, OpenFile},
|
||||
},
|
||||
kernel::rand::sys_getrandom,
|
||||
kernel::rand::fill_random_bytes,
|
||||
kernel_driver,
|
||||
memory::uaccess::copy_to_user_slice,
|
||||
};
|
||||
use alloc::{boxed::Box, string::ToString, sync::Arc};
|
||||
use alloc::{boxed::Box, string::ToString, sync::Arc, vec};
|
||||
use async_trait::async_trait;
|
||||
use core::{future::Future, pin::Pin};
|
||||
use libkernel::{
|
||||
@@ -34,7 +35,12 @@ impl FileOps for RandomFileOps {
|
||||
}
|
||||
|
||||
async fn readat(&mut self, buf: UA, count: usize, _offset: u64) -> Result<usize> {
|
||||
sys_getrandom(buf.cast(), count as _, 0).await
|
||||
// TODO: Add an implementation of `/dev/urandom` which doesn't block if
|
||||
// the entropy pool hasn't yet been seeded.
|
||||
let mut kbuf = vec![0u8; count];
|
||||
fill_random_bytes(&mut kbuf).await;
|
||||
copy_to_user_slice(&kbuf, buf).await?;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn poll_read_ready(&self) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
|
||||
|
||||
@@ -127,15 +127,15 @@ fn virtio_gpu_probe(_dm: &mut DriverManager, d: DeviceDescriptor) -> Result<Arc<
|
||||
|
||||
// Construct the transport first; it can report its device type.
|
||||
let transport = unsafe {
|
||||
MmioTransport::new(header, size).map_err(|e| {
|
||||
log::error!("Failed to initialize virtio-mmio transport: {e}");
|
||||
KernelError::Other("virtio-mmio transport init failed")
|
||||
})?
|
||||
match MmioTransport::new(header, size) {
|
||||
Ok(t) => t,
|
||||
Err(_) => return Err(KernelError::Probe(ProbeError::NoMatch)),
|
||||
}
|
||||
};
|
||||
|
||||
// Only bind to GPU here; other virtio-mmio devices should be handled by their own drivers.
|
||||
if !matches!(transport.device_type(), DeviceType::GPU) {
|
||||
return Err(KernelError::Probe(ProbeError::Deferred));
|
||||
return Err(KernelError::Probe(ProbeError::NoMatch));
|
||||
}
|
||||
|
||||
if mapped != VA::from_value(0xffffd00000f92e00) {
|
||||
|
||||
@@ -73,6 +73,9 @@ pub fn probe_for_fdt_devices() {
|
||||
Err(KernelError::Probe(ProbeError::Deferred)) => {
|
||||
deferred_list.push(desc);
|
||||
}
|
||||
Err(KernelError::Probe(ProbeError::NoMatch)) => {
|
||||
// Driver inspected the device but it's not a match; skip silently.
|
||||
}
|
||||
Ok(None) => {
|
||||
// No driver found for this compatible string. Not an error, just ignore.
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ pub mod fs;
|
||||
pub mod init;
|
||||
pub mod interrupts;
|
||||
pub mod probe;
|
||||
pub mod rng;
|
||||
pub mod timer;
|
||||
pub mod uart;
|
||||
mod virtio_hal;
|
||||
|
||||
1
src/drivers/rng/mod.rs
Normal file
1
src/drivers/rng/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod virtio;
|
||||
115
src/drivers/rng/virtio.rs
Normal file
115
src/drivers/rng/virtio.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use crate::drivers::virtio_hal::VirtioHal;
|
||||
use crate::sync::SpinLock;
|
||||
use crate::{
|
||||
arch::ArchImpl,
|
||||
drivers::{
|
||||
Driver, DriverManager,
|
||||
init::PlatformBus,
|
||||
probe::{DeviceDescriptor, DeviceMatchType},
|
||||
},
|
||||
kernel::rand::{EntropySource, register_entropy_source},
|
||||
kernel_driver,
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use core::ptr::NonNull;
|
||||
use libkernel::{
|
||||
KernAddressSpace, VirtualMemory,
|
||||
error::{KernelError, ProbeError, Result},
|
||||
memory::{
|
||||
address::{PA, VA},
|
||||
region::PhysMemoryRegion,
|
||||
},
|
||||
};
|
||||
use log::info;
|
||||
use virtio_drivers::{
|
||||
device::rng::VirtIORng,
|
||||
transport::{
|
||||
DeviceType, Transport,
|
||||
mmio::{MmioTransport, VirtIOHeader},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct VirtioRngDriver {
|
||||
rng: SpinLock<VirtIORng<VirtioHal, MmioTransport<'static>>>,
|
||||
}
|
||||
|
||||
impl Driver for VirtioRngDriver {
|
||||
fn name(&self) -> &'static str {
|
||||
"virtio-rng"
|
||||
}
|
||||
}
|
||||
|
||||
impl EntropySource for VirtioRngDriver {
|
||||
fn get_entropy(&self, buf: &mut [u8]) -> (usize, usize) {
|
||||
let mut rng = self.rng.lock_save_irq();
|
||||
match rng.request_entropy(buf) {
|
||||
Ok(n) => (n, n * 8),
|
||||
Err(_) => (0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn virtio_rng_probe(_dm: &mut DriverManager, d: DeviceDescriptor) -> Result<Arc<dyn Driver>> {
|
||||
match d {
|
||||
DeviceDescriptor::Fdt(fdt_node, _flags) => {
|
||||
let region = fdt_node
|
||||
.reg()
|
||||
.ok_or(ProbeError::NoReg)?
|
||||
.next()
|
||||
.ok_or(ProbeError::NoReg)?;
|
||||
|
||||
let size = region.size.ok_or(ProbeError::NoRegSize)?;
|
||||
|
||||
let mapped: VA =
|
||||
ArchImpl::kern_address_space()
|
||||
.lock_save_irq()
|
||||
.map_mmio(PhysMemoryRegion::new(
|
||||
PA::from_value(region.address as usize),
|
||||
size,
|
||||
))?;
|
||||
|
||||
let header = NonNull::new(mapped.value() as *mut VirtIOHeader)
|
||||
.ok_or(KernelError::InvalidValue)?;
|
||||
|
||||
let transport = unsafe {
|
||||
match MmioTransport::new(header, size) {
|
||||
Ok(t) => t,
|
||||
Err(_) => return Err(KernelError::Probe(ProbeError::NoMatch)),
|
||||
}
|
||||
};
|
||||
|
||||
if !matches!(transport.device_type(), DeviceType::EntropySource) {
|
||||
return Err(KernelError::Probe(ProbeError::NoMatch));
|
||||
}
|
||||
|
||||
info!("virtio-rng found (node {})", fdt_node.name);
|
||||
|
||||
let rng = VirtIORng::<VirtioHal, _>::new(transport)
|
||||
.map_err(|_| KernelError::Other("virtio-rng init failed"))?;
|
||||
|
||||
let driver = Arc::new(VirtioRngDriver {
|
||||
rng: SpinLock::new(rng),
|
||||
});
|
||||
|
||||
register_entropy_source(driver.clone());
|
||||
|
||||
Ok(driver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn virtio_rng_init(bus: &mut PlatformBus, _dm: &mut DriverManager) -> Result<()> {
|
||||
bus.register_platform_driver(
|
||||
DeviceMatchType::FdtCompatible("virtio,mmio"),
|
||||
Box::new(virtio_rng_probe),
|
||||
);
|
||||
|
||||
bus.register_platform_driver(
|
||||
DeviceMatchType::FdtCompatible("virtio-mmio"),
|
||||
Box::new(virtio_rng_probe),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
kernel_driver!(virtio_rng_init);
|
||||
@@ -1,39 +1,227 @@
|
||||
// TODO: generate a pool of entropy.
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
|
||||
use crate::{
|
||||
drivers::timer::uptime,
|
||||
memory::uaccess::copy_to_user_slice,
|
||||
sync::{OnceLock, SpinLock},
|
||||
per_cpu_private,
|
||||
sync::{CondVar, OnceLock, SpinLock},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use libkernel::error::Result;
|
||||
use blake2::{Blake2s256, Digest};
|
||||
use chacha20::ChaCha20Rng;
|
||||
use libkernel::memory::address::TUA;
|
||||
use rand::{Rng, SeedableRng, rngs::SmallRng};
|
||||
use libkernel::{error::Result, sync::condvar::WakeupType};
|
||||
use rand::{Rng, SeedableRng};
|
||||
|
||||
pub async fn sys_getrandom(ubuf: TUA<u8>, size: isize, _flags: u32) -> Result<usize> {
|
||||
let buf = {
|
||||
let mut rng = ENTROPY_POOL
|
||||
.get_or_init(|| {
|
||||
let now = uptime();
|
||||
|
||||
SpinLock::new(SmallRng::seed_from_u64(
|
||||
(now.as_micros() & 0xffffffff_ffffffff) as u64,
|
||||
))
|
||||
})
|
||||
.lock_save_irq();
|
||||
|
||||
let mut buf = Vec::with_capacity(size as usize);
|
||||
|
||||
for _ in 0..size {
|
||||
buf.push((rng.next_u32() & 0xff) as u8);
|
||||
}
|
||||
|
||||
buf
|
||||
};
|
||||
|
||||
copy_to_user_slice(&buf, ubuf.to_untyped()).await?;
|
||||
|
||||
Ok(size as _)
|
||||
/// 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);
|
||||
}
|
||||
|
||||
static ENTROPY_POOL: OnceLock<SpinLock<SmallRng>> = OnceLock::new();
|
||||
/// Number of bytes generated before a per-CPU RNG reseeds from the entropy pool.
|
||||
const RESEED_BYTES: usize = 1024 * 1024;
|
||||
|
||||
pub struct EntropyPool {
|
||||
state: SpinLock<Blake2s256>,
|
||||
pool_waiters: CondVar<bool>,
|
||||
pool_bits: AtomicUsize,
|
||||
sources: SpinLock<Vec<Arc<dyn EntropySource>>>,
|
||||
}
|
||||
|
||||
impl EntropyPool {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
state: SpinLock::new(Blake2s256::default()),
|
||||
pool_waiters: CondVar::new(false),
|
||||
pool_bits: AtomicUsize::new(0),
|
||||
sources: SpinLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_entropy(&self, entropy: &[u8], approx_bits: usize) {
|
||||
self.state.lock_save_irq().update(entropy);
|
||||
|
||||
let old_val = self.pool_bits.fetch_add(approx_bits, Ordering::Relaxed);
|
||||
|
||||
if old_val + approx_bits >= 256 && old_val < 256 {
|
||||
// Pool was initialised. Wake up any waiters.
|
||||
self.pool_waiters.update(|s| {
|
||||
*s = true;
|
||||
WakeupType::All
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Add entropy into this pool based upon the uptime of the system.
|
||||
///
|
||||
/// WARNING: This method should *not* be called for predictable or periodic
|
||||
/// events.
|
||||
pub fn add_temporal_entropy(&self) {
|
||||
let uptime = uptime();
|
||||
|
||||
self.add_entropy(
|
||||
&uptime.subsec_micros().to_le_bytes(),
|
||||
1_000_000_usize.ilog2() as usize,
|
||||
);
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
Some(self.extract_seed_inner())
|
||||
}
|
||||
|
||||
fn extract_seed_inner(&self) -> [u8; 32] {
|
||||
let mut state = self.state.lock_save_irq();
|
||||
|
||||
let hash = (*state).clone().finalize();
|
||||
|
||||
let mut seed = [0u8; 32];
|
||||
seed.copy_from_slice(&hash);
|
||||
|
||||
// Feed the extracted hash back so the pool state diverges from the
|
||||
// output (forward secrecy).
|
||||
state.update(seed);
|
||||
|
||||
seed
|
||||
}
|
||||
}
|
||||
|
||||
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<dyn EntropySource>) {
|
||||
entropy_pool().sources.lock_save_irq().push(source);
|
||||
}
|
||||
|
||||
static ENTROPY_POOL: OnceLock<EntropyPool> = OnceLock::new();
|
||||
|
||||
struct CpuRng {
|
||||
rng: ChaCha20Rng,
|
||||
seeded: bool,
|
||||
bytes_since_reseed: usize,
|
||||
}
|
||||
|
||||
unsafe impl Send for CpuRng {}
|
||||
|
||||
impl CpuRng {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
rng: ChaCha20Rng::from_seed([0u8; 32]),
|
||||
seeded: false,
|
||||
bytes_since_reseed: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_seed(&mut self, seed: [u8; 32]) {
|
||||
self.rng = ChaCha20Rng::from_seed(seed);
|
||||
self.seeded = true;
|
||||
self.bytes_since_reseed = 0;
|
||||
}
|
||||
|
||||
/// Reseed by XOR-ing a fresh BLAKE2 seed with 32 bytes of our own output,
|
||||
/// then constructing a new ChaCha20 instance from the combined material.
|
||||
fn reseed_with_blake(&mut self, blake_seed: [u8; 32]) {
|
||||
let mut self_bytes = [0u8; 32];
|
||||
self.rng.fill_bytes(&mut self_bytes);
|
||||
|
||||
let mut new_seed = [0u8; 32];
|
||||
|
||||
for i in 0..32 {
|
||||
new_seed[i] = blake_seed[i] ^ self_bytes[i];
|
||||
}
|
||||
|
||||
self.rng = ChaCha20Rng::from_seed(new_seed);
|
||||
self.bytes_since_reseed = 0;
|
||||
}
|
||||
|
||||
fn fill(&mut self, buf: &mut [u8]) {
|
||||
self.rng.fill_bytes(buf);
|
||||
self.bytes_since_reseed += buf.len();
|
||||
}
|
||||
}
|
||||
|
||||
per_cpu_private! {
|
||||
static CPU_RNG: CpuRng = CpuRng::new;
|
||||
}
|
||||
|
||||
/// Fill `buf` with cryptographically-strong random bytes.
|
||||
///
|
||||
/// On the first invocation per CPU the call blocks until the global entropy
|
||||
/// pool has been seeded (>= 256 bits). Subsequent calls are non-blocking;
|
||||
/// periodic reseeding from the pool happens inline.
|
||||
pub async fn fill_random_bytes(buf: &mut [u8]) {
|
||||
// Ensure the per-CPU RNG has been seeded at least once.
|
||||
let seeded = CPU_RNG.borrow().seeded;
|
||||
|
||||
if !seeded {
|
||||
let seed = entropy_pool().extract_seed().await;
|
||||
CPU_RNG.borrow_mut().apply_seed(seed);
|
||||
}
|
||||
|
||||
// Reseed from the entropy pool if we have generated enough bytes.
|
||||
let needs_reseed = CPU_RNG.borrow().bytes_since_reseed >= RESEED_BYTES;
|
||||
if needs_reseed && let Some(blake_seed) = entropy_pool().try_extract_seed() {
|
||||
CPU_RNG.borrow_mut().reseed_with_blake(blake_seed);
|
||||
}
|
||||
|
||||
CPU_RNG.borrow_mut().fill(buf);
|
||||
}
|
||||
|
||||
const GETRANDOM_CHUNK: usize = 256;
|
||||
|
||||
pub async fn sys_getrandom(ubuf: TUA<u8>, size: isize, _flags: u32) -> Result<usize> {
|
||||
let total = size as usize;
|
||||
let mut buf = [0u8; GETRANDOM_CHUNK];
|
||||
let mut offset = 0;
|
||||
|
||||
while offset < total {
|
||||
let n = (total - offset).min(GETRANDOM_CHUNK);
|
||||
let chunk = &mut buf[..n];
|
||||
|
||||
fill_random_bytes(chunk).await;
|
||||
|
||||
copy_to_user_slice(chunk, ubuf.to_untyped().add_bytes(offset)).await?;
|
||||
|
||||
offset += n;
|
||||
}
|
||||
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user