From c39cea05fcf1107ae2ca230b9498e04af95b1f55 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Wed, 22 May 2024 13:05:18 -0700 Subject: [PATCH] lpc55-rng: Include 32 bytes from the last PRNG instance when reseeding. where: - N > 0 - `HRNG_N(count)` represents count bytes taken from the hardware RNG - `PRNG_N(count)` represents count bytes taken from the Nth generation of the PRNG This commit changes our algorithm for constructing the seed `SEED_N` for the PRNG instance `PRNG_N` from: ``` SEED_N = HRNG(32) ``` to: ``` SEED_N = sha3_256(PRNG_N-1(32) | HRNG(32)) ``` We use `sha3_256` as a mixing function to combine these two components of the seed though the implementation is generic over the digest w/ constraints on the length. --- Cargo.lock | 2 ++ app/lpc55xpresso/app.toml | 3 +- app/rot-carrier/app.toml | 3 +- drv/lpc55-rng/Cargo.toml | 2 ++ drv/lpc55-rng/src/main.rs | 63 +++++++++++++++++++++++++++++++-------- 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a25b61a5..593fa9119 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1202,8 +1202,10 @@ dependencies = [ "num-traits", "rand_chacha", "rand_core", + "sha3", "userlib", "zerocopy 0.6.6", + "zeroize", ] [[package]] diff --git a/app/lpc55xpresso/app.toml b/app/lpc55xpresso/app.toml index 91f91becd..633df6198 100644 --- a/app/lpc55xpresso/app.toml +++ b/app/lpc55xpresso/app.toml @@ -113,10 +113,9 @@ task-slots = ["gpio_driver", "syscon_driver"] [tasks.rng_driver] name = "drv-lpc55-rng" priority = 3 -max-sizes = {flash = 16384, ram = 4096} uses = ["rng", "pmc"] start = true -stacksize = 2200 +stacksize = 2600 task-slots = ["syscon_driver"] [tasks.pong] diff --git a/app/rot-carrier/app.toml b/app/rot-carrier/app.toml index ec85ba8bb..54ff7aa7f 100644 --- a/app/rot-carrier/app.toml +++ b/app/rot-carrier/app.toml @@ -99,10 +99,9 @@ pins = [ [tasks.rng_driver] name = "drv-lpc55-rng" priority = 5 -max-sizes = {flash = 16384, ram = 4096} uses = ["rng", "pmc"] start = true -stacksize = 2200 +stacksize = 2600 task-slots = ["syscon_driver"] [tasks.sprot] diff --git a/drv/lpc55-rng/Cargo.toml b/drv/lpc55-rng/Cargo.toml index b52c2fc96..f8eb2858b 100644 --- a/drv/lpc55-rng/Cargo.toml +++ b/drv/lpc55-rng/Cargo.toml @@ -9,7 +9,9 @@ idol-runtime = { workspace = true } num-traits = { workspace = true } rand_chacha = { workspace = true } rand_core = { workspace = true } +sha3.workspace = true zerocopy = { workspace = true } +zeroize.workspace = true drv-lpc55-syscon-api = { path = "../lpc55-syscon-api" } drv-rng-api = { path = "../rng-api" } diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 3e2f88bcd..828082e87 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -16,22 +16,31 @@ use idol_runtime::{ClientError, NotificationHandler, RequestError}; use lib_lpc55_rng::Lpc55Rng; use rand_chacha::ChaCha20Rng; use rand_core::{impls, Error, RngCore, SeedableRng}; +use sha3::{ + digest::crypto_common::{generic_array::GenericArray, OutputSizeUser}, + digest::FixedOutputReset, + Digest, Sha3_256, +}; use userlib::*; +use zeroize::Zeroizing; task_slot!(SYSCON, syscon_driver); // low-budget rand::rngs::adapter::ReseedingRng w/o fork stuff -struct ReseedingRng { +struct ReseedingRng { inner: T, reseeder: R, threshold: usize, bytes_until_reseed: usize, + mixer: H, } -impl ReseedingRng +impl ReseedingRng where - T: SeedableRng, + T: SeedableRng + RngCore, R: RngCore, + H: FixedOutputReset + Default + Digest, + [u8; 32]: From::OutputSize>>, { fn new(mut reseeder: R, threshold: usize) -> Result { let threshold = if threshold == 0 { @@ -45,14 +54,17 @@ where reseeder, threshold, bytes_until_reseed: threshold, + mixer: H::default(), }) } } -impl RngCore for ReseedingRng +impl RngCore for ReseedingRng where - T: SeedableRng + RngCore, + T: SeedableRng + RngCore, R: RngCore, + H: FixedOutputReset + Default + Digest, + [u8; 32]: From::OutputSize>>, { fn next_u32(&mut self) -> u32 { impls::next_u32_via_fill(self) @@ -65,18 +77,43 @@ where .expect("Failed to get entropy from RNG.") } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let num_bytes = dest.len(); - if num_bytes >= self.bytes_until_reseed || num_bytes >= self.threshold { - self.inner = T::from_rng(&mut self.reseeder)?; - self.bytes_until_reseed = self.threshold; - } else { - self.bytes_until_reseed -= num_bytes; + let mut filled = 0; + + while filled < dest.len() { + if self.bytes_until_reseed > 0 { + // fill dest as much as we can + let len = + cmp::min(dest.len() - filled, self.bytes_until_reseed); + self.inner.try_fill_bytes(&mut dest[filled..filled + len])?; + + filled += len; + self.bytes_until_reseed -= len; + } else { + // create seed for next PRNG & reset mixer + let mut buf = Zeroizing::new(T::Seed::default()); + + // mix 32 bytes from current PRNG instance + self.inner.try_fill_bytes(buf.as_mut())?; + Digest::update(&mut self.mixer, buf.as_mut()); + + // w/ 32 bytes from HRNG + self.reseeder.try_fill_bytes(buf.as_mut())?; + Digest::update(&mut self.mixer, buf.as_mut()); + + // seed new RNG instance & reset mixer + self.inner = + T::from_seed(self.mixer.finalize_fixed_reset().into()); + + // reset reseed countdown + self.bytes_until_reseed = self.threshold; + } } - self.inner.try_fill_bytes(dest) + + Ok(()) } } -struct Lpc55RngServer(ReseedingRng); +struct Lpc55RngServer(ReseedingRng); impl Lpc55RngServer { fn new(reseeder: Lpc55Rng, threshold: usize) -> Result {