mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
add production TRNG to cramium target for kernel
This commit is contained in:
parent
96102630b3
commit
5df2e27447
2 changed files with 64 additions and 89 deletions
|
|
@ -16,6 +16,7 @@ xous-kernel = { package = "xous", version = "0.9.57", features = [
|
|||
] }
|
||||
utralib = { version = "0.1.24", optional = true, default_features = false }
|
||||
rand_chacha = { version = "0.3.1", optional = true, default_features = false }
|
||||
cramium-hal = { path = "../libs/cramium-hal", optional = true }
|
||||
|
||||
[target.'cfg(any(windows,unix))'.dev-dependencies]
|
||||
xous-kernel = { package = "xous", version = "0.9.57", features = [
|
||||
|
|
@ -37,7 +38,7 @@ xous-kernel = { package = "xous", version = "0.9.57", features = ["v2p"] }
|
|||
critical-section = "1.1.1"
|
||||
|
||||
[features]
|
||||
cramium-soc = ["utralib/cramium-soc"]
|
||||
cramium-soc = ["utralib/cramium-soc", "cramium-hal", "rand_chacha"]
|
||||
cramium-fpga = ["utralib/cramium-fpga"]
|
||||
atsama5d27 = ["utralib/atsama5d27"]
|
||||
precursor = ["utralib/precursor"]
|
||||
|
|
@ -54,9 +55,6 @@ v2p = ["xous-kernel/v2p"]
|
|||
|
||||
# patches for simulation targets ONLY. Applying these flags will result in totally broken security.
|
||||
hwsim = []
|
||||
fake-rng = [
|
||||
"rand_chacha",
|
||||
] # works around lack of a real TRNG in the full-chip sim for now. This should go away for production.
|
||||
|
||||
# default = ["print-panics", "debug-print", "wrap-print"]
|
||||
default = ["print-panics"]
|
||||
|
|
|
|||
|
|
@ -2,88 +2,53 @@
|
|||
// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. <hello@foundationdevices.com>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
use utralib::generated::*;
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
use cramium_hal::sce;
|
||||
use rand_chacha::rand_core::RngCore;
|
||||
use rand_chacha::rand_core::SeedableRng;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use xous_kernel::{MemoryFlags, MemoryType, PID};
|
||||
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
use crate::mem::MemoryManager;
|
||||
|
||||
/// The manually chosen virtual address has to be in the top 4MiB as it is the
|
||||
/// only page shared among all processes.
|
||||
///
|
||||
/// See https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
pub const TRNG_KERNEL_ADDR: usize = 0xffce_0000;
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
pub static mut TRNG_KERNEL: Option<TrngKernel> = None;
|
||||
#[cfg(feature = "fake-rng")]
|
||||
pub static mut TRNG_KERNEL: Option<sce::trng::Trng> = None;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
#[cfg(feature = "fake-rng")]
|
||||
static LOCAL_RNG_STATE: AtomicU32 = AtomicU32::new(2);
|
||||
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
pub struct TrngKernel {
|
||||
pub trng_kernel_csr: CSR<u32>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
impl TrngKernel {
|
||||
pub fn new(addr: usize) -> TrngKernel { TrngKernel { trng_kernel_csr: CSR::new(addr as *mut u32) } }
|
||||
|
||||
pub fn init(&mut self) {
|
||||
if false {
|
||||
// raw random path - left in for debugging urandom, can strip out later
|
||||
while self.trng_kernel_csr.rf(utra::trng_kernel::STATUS_AVAIL) == 0 {}
|
||||
// discard the first entry, as it is always 0x0000_0000
|
||||
// this is because the read register is a pipeline stage behind the FIFO
|
||||
// once the pipeline has been filled, there is no need to prime it again.
|
||||
self.trng_kernel_csr.rf(utra::trng_kernel::DATA_DATA);
|
||||
} else {
|
||||
// urandom path (recommended)
|
||||
// simulations show this isn't strictly necessary, but I prefer to have it
|
||||
// just in case a subtle bug in the reset logic leaves something deterministic
|
||||
// in the connecting logic: the simulation coverage stops at the edge of the TRNG block.
|
||||
for _ in 0..4 {
|
||||
// wait until the urandom port is initialized
|
||||
while self.trng_kernel_csr.rf(utra::trng_kernel::URANDOM_VALID_URANDOM_VALID) == 0 {}
|
||||
// pull a dummy piece of data
|
||||
self.trng_kernel_csr.rf(utra::trng_kernel::URANDOM_URANDOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_u32(&mut self) -> u32 {
|
||||
if false {
|
||||
// raw random path
|
||||
while self.trng_kernel_csr.rf(utra::trng_kernel::STATUS_AVAIL) == 0 {}
|
||||
self.trng_kernel_csr.rf(utra::trng_kernel::DATA_DATA)
|
||||
} else {
|
||||
// urandom path (recommended)
|
||||
while self.trng_kernel_csr.rf(utra::trng_kernel::URANDOM_VALID_URANDOM_VALID) == 0 {}
|
||||
self.trng_kernel_csr.rf(utra::trng_kernel::URANDOM_URANDOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
// these values are overwritten on boot with something out of the TRNG.
|
||||
static LOCAL_RNG_STATE_LSB: AtomicU32 = AtomicU32::new(0);
|
||||
static LOCAL_RNG_STATE_MSB: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
/// Initialize TRNG driver.
|
||||
///
|
||||
/// Needed so that the kernel can allocate names.
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
/// This driver "owns" the hardware TRNG on the chip. Because there aren't two
|
||||
/// TRNG ports on the Cramium implementation, it means that userspace applications
|
||||
/// have to extract random numbers from the kernel through the SID request mechanism.
|
||||
/// This is ostensibly harmless, if everything is implemented well, but if there is
|
||||
/// a problem it does mean that the state of the kernel TRNG is disclosed to userspace.
|
||||
/// What can you do though, this chip just has one TRNG.
|
||||
///
|
||||
/// The final implementation for user applications is recommended to have a supplementary
|
||||
/// noise source in addition to the on-chip noise source, so that user applications
|
||||
/// combine both the feed from the kernel and the supplementary source to seed a CSPRNG.
|
||||
///
|
||||
/// This particular application doesn't trust the on-chip TRNG so much: it takes
|
||||
/// values from the TRNG and folds them into a seed pool that is then groomed by
|
||||
/// a ChaCha8 RNG CSPRNG. Every request for a random number folds another 32 bits
|
||||
/// from the TRNG into the CSPRNG pool, so even if the TRNG is fairly poor, quite
|
||||
/// rapidly the pool of numbers should diverge. However, from the kernel's perspective,
|
||||
/// it will function correctly even if the CSPRNG is fully deterministic, it just isn't
|
||||
/// secure against attackers trying to guess a server address.
|
||||
|
||||
pub fn init() {
|
||||
// Map the TRNG so that we can allocate names
|
||||
// hardware guarantees that:
|
||||
// - TRNG will automatically power on
|
||||
// - Both TRNGs are enabled, with conservative defaults
|
||||
// - Kernel FIFO will fill with TRNGs such that at least the next 512 calls to get_u32() will succeed
|
||||
// without delay
|
||||
// - The kernel will start a TRNG server
|
||||
// - All further security decisions and policies are 100% delegated to this new server.
|
||||
MemoryManager::with_mut(|memory_manager| {
|
||||
memory_manager
|
||||
.map_range(
|
||||
utra::trng_kernel::HW_TRNG_KERNEL_BASE as *mut u8,
|
||||
utralib::generated::HW_TRNG_BASE as *mut u8,
|
||||
(TRNG_KERNEL_ADDR & !4095) as *mut u8,
|
||||
4096,
|
||||
PID::new(1).unwrap(),
|
||||
|
|
@ -93,34 +58,46 @@ pub fn init() {
|
|||
.expect("unable to map TRNG_KERNEL")
|
||||
});
|
||||
|
||||
let mut trng_kernel = TrngKernel::new(TRNG_KERNEL_ADDR);
|
||||
trng_kernel.init();
|
||||
let mut trng_kernel = sce::trng::Trng::new(TRNG_KERNEL_ADDR);
|
||||
trng_kernel.setup_raw_generation(256);
|
||||
|
||||
// setup the initial seeds with stuff out of the TRNG.
|
||||
LOCAL_RNG_STATE_LSB.store(trng_kernel.get_u32().expect("TRNG error"), Ordering::SeqCst);
|
||||
LOCAL_RNG_STATE_MSB.store(trng_kernel.get_u32().expect("TRNG error"), Ordering::SeqCst);
|
||||
|
||||
// move the trng_kernel object into the static mut state
|
||||
unsafe {
|
||||
TRNG_KERNEL = Some(trng_kernel);
|
||||
}
|
||||
|
||||
// now run the CSPRNG a few cycles to fold more entropy into the pool before we start
|
||||
// to use it for reals. 16 iterations will extract 512 bits out of the TRNG, so even
|
||||
// if it is somewhat degraded, we'll be in good shape...
|
||||
for _ in 0..16 {
|
||||
let _ = get_u32();
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve random `u32`.
|
||||
#[cfg(not(feature = "fake-rng"))]
|
||||
pub fn get_u32() -> u32 {
|
||||
unsafe { TRNG_KERNEL.as_mut().expect("TRNG_KERNEL driver not initialized").get_u32() }
|
||||
}
|
||||
|
||||
/// Initialize TRNG driver.
|
||||
///
|
||||
/// Needed so that the kernel can allocate names.
|
||||
#[cfg(feature = "fake-rng")]
|
||||
pub fn init() {}
|
||||
|
||||
#[cfg(feature = "fake-rng")]
|
||||
pub fn get_u32() -> u32 {
|
||||
use rand_chacha::rand_core::RngCore;
|
||||
use rand_chacha::rand_core::SeedableRng;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
|
||||
let mut rng = ChaCha8Rng::seed_from_u64(LOCAL_RNG_STATE.load(Ordering::SeqCst) as u64);
|
||||
let r = rng.next_u32();
|
||||
LOCAL_RNG_STATE.store(rng.next_u64() as u32, Ordering::SeqCst);
|
||||
r
|
||||
let mut state = LOCAL_RNG_STATE_LSB.load(Ordering::SeqCst) as u64
|
||||
| (LOCAL_RNG_STATE_MSB.load(Ordering::SeqCst) as u64) << 32;
|
||||
|
||||
// XOR in 32 bits of entropy from the HW TRNG pool.
|
||||
state ^= unsafe {
|
||||
TRNG_KERNEL
|
||||
.as_mut()
|
||||
.expect("TRNG_KERNEL driver not initialized")
|
||||
.get_u32()
|
||||
.expect("Error in random number generation")
|
||||
} as u64;
|
||||
|
||||
let mut rng = ChaCha8Rng::seed_from_u64(state);
|
||||
let next_state = rng.next_u64();
|
||||
// next state is extracted before the returned value, to reduce state
|
||||
// leakage outside of the RNG.
|
||||
let ret_val = rng.next_u32();
|
||||
LOCAL_RNG_STATE_LSB.store(next_state as u32, Ordering::SeqCst);
|
||||
LOCAL_RNG_STATE_MSB.store((next_state >> 32) as u32, Ordering::SeqCst);
|
||||
ret_val
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue