From 5a6ce9e3a1a1585e0b6cfd8dd380baf7a63d4219 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 27 Jun 2024 08:29:44 -0700 Subject: [PATCH 01/16] lpc55-rng: Move Lpc55Core and Lpc55Rng to a library. This is intended to enable testing the HRNG on the LPC55 independet of the rng_driver task. --- Cargo.lock | 12 +++++- drv/lpc55-rng/Cargo.toml | 2 +- drv/lpc55-rng/src/main.rs | 74 ++---------------------------------- lib/lpc55-rng/Cargo.toml | 16 ++++++++ lib/lpc55-rng/src/lib.rs | 79 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 73 deletions(-) create mode 100644 lib/lpc55-rng/Cargo.toml create mode 100644 lib/lpc55-rng/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4eb6d6b39d..c006119cac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1294,7 +1294,7 @@ dependencies = [ "drv-rng-api", "idol", "idol-runtime", - "lpc55-pac", + "lib-lpc55-rng", "num-traits", "rand_chacha", "rand_core", @@ -3069,6 +3069,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "lib-lpc55-rng" +version = "0.1.0" +dependencies = [ + "drv-lpc55-syscon-api", + "drv-rng-api", + "lpc55-pac", + "rand_core", +] + [[package]] name = "lib-lpc55-usart" version = "0.1.0" diff --git a/drv/lpc55-rng/Cargo.toml b/drv/lpc55-rng/Cargo.toml index 7704265cb6..b52c2fc962 100644 --- a/drv/lpc55-rng/Cargo.toml +++ b/drv/lpc55-rng/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] cfg-if = { workspace = true } idol-runtime = { workspace = true } -lpc55-pac = { workspace = true } num-traits = { workspace = true } rand_chacha = { workspace = true } rand_core = { workspace = true } @@ -14,6 +13,7 @@ zerocopy = { workspace = true } drv-lpc55-syscon-api = { path = "../lpc55-syscon-api" } drv-rng-api = { path = "../rng-api" } +lib-lpc55-rng.path = "../../lib/lpc55-rng" userlib = { path = "../../sys/userlib", features = ["panic-messages"] } [build-dependencies] diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index e71e9e7591..1b829fe04a 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -10,84 +10,16 @@ #![no_main] use core::mem::size_of; -use drv_lpc55_syscon_api::{Peripheral, Syscon}; +use drv_lpc55_syscon_api::Syscon; use drv_rng_api::RngError; use idol_runtime::{ClientError, NotificationHandler, RequestError}; +use lib_lpc55_rng::Lpc55Rng; use rand_chacha::ChaCha20Rng; -use rand_core::block::{BlockRng, BlockRngCore}; use rand_core::{impls, Error, RngCore, SeedableRng}; use userlib::task_slot; -use lpc55_pac as device; - task_slot!(SYSCON, syscon_driver); -struct Lpc55Core { - pmc: &'static lpc55_pac::pmc::RegisterBlock, - rng: &'static lpc55_pac::rng::RegisterBlock, - syscon: Syscon, -} - -impl Lpc55Core { - fn new() -> Self { - let syscon = SYSCON.get_task_id(); - Lpc55Core { - pmc: unsafe { &*device::PMC::ptr() }, - rng: unsafe { &*device::RNG::ptr() }, - syscon: Syscon::from(syscon), - } - } -} - -impl BlockRngCore for Lpc55Core { - type Item = u32; - type Results = [u32; 1]; - - fn generate(&mut self, results: &mut Self::Results) { - results[0] = self.rng.random_number.read().bits(); - } -} - -struct Lpc55Rng(BlockRng); - -impl Lpc55Rng { - fn new() -> Self { - Lpc55Rng(BlockRng::new(Lpc55Core::new())) - } - - fn init(&self) { - self.0 - .core - .pmc - .pdruncfg0 - .modify(|_, w| w.pden_rng().poweredon()); - - self.0.core.syscon.enable_clock(Peripheral::Rng); - - self.0.core.syscon.enter_reset(Peripheral::Rng); - self.0.core.syscon.leave_reset(Peripheral::Rng); - } -} - -impl RngCore for Lpc55Rng { - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.0.fill_bytes(bytes) - } - fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { - if self.0.core.pmc.pdruncfg0.read().pden_rng().bits() { - return Err(RngError::PoweredOff.into()); - } - - self.0.try_fill_bytes(bytes) - } -} - // low-budget rand::rngs::adapter::ReseedingRng w/o fork stuff struct ReseedingRng { inner: T, @@ -199,7 +131,7 @@ impl NotificationHandler for Lpc55RngServer { #[export_name = "main"] fn main() -> ! { - let rng = Lpc55Rng::new(); + let rng = Lpc55Rng::new(Syscon::from(SYSCON.get_task_id())); rng.init(); let threshold = 0x100000; // 1 MiB diff --git a/lib/lpc55-rng/Cargo.toml b/lib/lpc55-rng/Cargo.toml new file mode 100644 index 0000000000..dc635d92d3 --- /dev/null +++ b/lib/lpc55-rng/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "lib-lpc55-rng" +version = "0.1.0" +edition = "2021" + +[dependencies] +lpc55-pac.workspace = true +rand_core.workspace = true + +drv-lpc55-syscon-api.path = "../../drv/lpc55-syscon-api" +drv-rng-api.path = "../../drv/rng-api" + +[lib] +test = false +doctest = false +bench = false diff --git a/lib/lpc55-rng/src/lib.rs b/lib/lpc55-rng/src/lib.rs new file mode 100644 index 0000000000..efc6107c05 --- /dev/null +++ b/lib/lpc55-rng/src/lib.rs @@ -0,0 +1,79 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#![no_std] +#![no_main] + +use drv_lpc55_syscon_api::{Peripheral, Syscon}; +use drv_rng_api::RngError; +use lpc55_pac::{pmc, rng, PMC, RNG}; +use rand_core::{ + block::{BlockRng, BlockRngCore}, + Error, RngCore, +}; + +struct Lpc55Core { + pmc: &'static pmc::RegisterBlock, + rng: &'static rng::RegisterBlock, + syscon: Syscon, +} + +impl Lpc55Core { + fn new(syscon: Syscon) -> Self { + Lpc55Core { + pmc: unsafe { &*PMC::ptr() }, + rng: unsafe { &*RNG::ptr() }, + syscon, + } + } +} + +impl BlockRngCore for Lpc55Core { + type Item = u32; + type Results = [u32; 1]; + + fn generate(&mut self, results: &mut Self::Results) { + results[0] = self.rng.random_number.read().bits(); + } +} + +pub struct Lpc55Rng(BlockRng); + +impl Lpc55Rng { + pub fn new(syscon: Syscon) -> Self { + Lpc55Rng(BlockRng::new(Lpc55Core::new(syscon))) + } + + pub fn init(&self) { + self.0 + .core + .pmc + .pdruncfg0 + .modify(|_, w| w.pden_rng().poweredon()); + + self.0.core.syscon.enable_clock(Peripheral::Rng); + + self.0.core.syscon.enter_reset(Peripheral::Rng); + self.0.core.syscon.leave_reset(Peripheral::Rng); + } +} + +impl RngCore for Lpc55Rng { + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } + fn fill_bytes(&mut self, bytes: &mut [u8]) { + self.0.fill_bytes(bytes) + } + fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + if self.0.core.pmc.pdruncfg0.read().pden_rng().bits() { + return Err(RngError::PoweredOff.into()); + } + + self.0.try_fill_bytes(bytes) + } +} From 6551ef33789ec2a162f81f1daef0d37990dcb332 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 27 Jun 2024 08:30:50 -0700 Subject: [PATCH 02/16] lpc55-rng: Combine Lpc55Core and Lpc55Rng. Implementing `BlockRngCore` for `Lpc55Core` got us an implementation of RngCore for free. Unfortunately it cost us the ability to return errors. This wasn't a good tradeoff. Better to implement `RngCore` directly. --- drv/lpc55-rng/src/main.rs | 3 +- lib/lpc55-rng/src/lib.rs | 93 ++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 51 deletions(-) diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 1b829fe04a..9ecab798c5 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -131,8 +131,7 @@ impl NotificationHandler for Lpc55RngServer { #[export_name = "main"] fn main() -> ! { - let rng = Lpc55Rng::new(Syscon::from(SYSCON.get_task_id())); - rng.init(); + let rng = Lpc55Rng::new(&Syscon::from(SYSCON.get_task_id())); let threshold = 0x100000; // 1 MiB let mut rng = Lpc55RngServer::new(rng, threshold) diff --git a/lib/lpc55-rng/src/lib.rs b/lib/lpc55-rng/src/lib.rs index efc6107c05..15ede6712a 100644 --- a/lib/lpc55-rng/src/lib.rs +++ b/lib/lpc55-rng/src/lib.rs @@ -5,75 +5,70 @@ #![no_std] #![no_main] +use core::{cmp, mem}; use drv_lpc55_syscon_api::{Peripheral, Syscon}; use drv_rng_api::RngError; use lpc55_pac::{pmc, rng, PMC, RNG}; -use rand_core::{ - block::{BlockRng, BlockRngCore}, - Error, RngCore, -}; +use rand_core::{impls, Error, RngCore}; -struct Lpc55Core { - pmc: &'static pmc::RegisterBlock, - rng: &'static rng::RegisterBlock, - syscon: Syscon, +/// The Lpc55Rng is a thin wrapper around the LPC55 hardware random number +/// generator (HRNG). +pub struct Lpc55Rng { + pub pmc: &'static pmc::RegisterBlock, + pub rng: &'static rng::RegisterBlock, } -impl Lpc55Core { - fn new(syscon: Syscon) -> Self { - Lpc55Core { - pmc: unsafe { &*PMC::ptr() }, - rng: unsafe { &*RNG::ptr() }, - syscon, - } - } -} - -impl BlockRngCore for Lpc55Core { - type Item = u32; - type Results = [u32; 1]; - - fn generate(&mut self, results: &mut Self::Results) { - results[0] = self.rng.random_number.read().bits(); - } -} - -pub struct Lpc55Rng(BlockRng); - impl Lpc55Rng { - pub fn new(syscon: Syscon) -> Self { - Lpc55Rng(BlockRng::new(Lpc55Core::new(syscon))) - } - - pub fn init(&self) { - self.0 - .core - .pmc - .pdruncfg0 - .modify(|_, w| w.pden_rng().poweredon()); + /// Create a new Lpc55Rng instance after powering on, enabling the clocks + /// and reseting the underlying HRNG. + pub fn new(syscon: &Syscon) -> Self { + let pmc = unsafe { &*PMC::ptr() }; + pmc.pdruncfg0.modify(|_, w| w.pden_rng().poweredon()); - self.0.core.syscon.enable_clock(Peripheral::Rng); + syscon.enable_clock(Peripheral::Rng); + syscon.enter_reset(Peripheral::Rng); + syscon.leave_reset(Peripheral::Rng); - self.0.core.syscon.enter_reset(Peripheral::Rng); - self.0.core.syscon.leave_reset(Peripheral::Rng); + Lpc55Rng { + pmc, + rng: unsafe { &*RNG::ptr() }, + } } } impl RngCore for Lpc55Rng { + /// Get the next 4 bytes from the HRNG. fn next_u32(&mut self) -> u32 { - self.0.next_u32() + impls::next_u32_via_fill(self) } + + /// Get the next 8 bytes from the HRNG. fn next_u64(&mut self) -> u64 { - self.0.next_u64() + impls::next_u64_via_fill(self) } + + /// Fill the provided buffer with output from the HRNG. fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.0.fill_bytes(bytes) + self.try_fill_bytes(bytes).expect("fill_bytes") } - fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { - if self.0.core.pmc.pdruncfg0.read().pden_rng().bits() { - return Err(RngError::PoweredOff.into()); + + /// Fill the provided buffer with output from the HRNG. If the HRNG + /// can't service the request an error is returned. + fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Error> { + let mut filled = 0; + while filled < dst.len() { + if self.pmc.pdruncfg0.read().pden_rng().bits() { + return Err(RngError::PoweredOff.into()); + } + + let src = self.rng.random_number.read().bits(); + let len = cmp::min(mem::size_of_val(&src), dst[filled..].len()); + + dst[filled..filled + len] + .copy_from_slice(&src.to_le_bytes()[..len]); + filled += len; } - self.0.try_fill_bytes(bytes) + Ok(()) } } From 64270062457b13e9ed1abaf68485b5091d8c0e10 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 27 Jun 2024 08:32:44 -0700 Subject: [PATCH 03/16] lpc55-rng: cleanup some nonsense Not sure what I was thinking here. --- drv/lpc55-rng/src/main.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 9ecab798c5..c05c21c4d3 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -9,7 +9,7 @@ #![no_std] #![no_main] -use core::mem::size_of; +use core::{mem::size_of, usize}; use drv_lpc55_syscon_api::Syscon; use drv_rng_api::RngError; use idol_runtime::{ClientError, NotificationHandler, RequestError}; @@ -33,17 +33,14 @@ where T: SeedableRng, { fn new(mut reseeder: Lpc55Rng, threshold: usize) -> Result { - use ::core::usize::MAX; - - let threshold = if threshold == 0 { MAX } else { threshold }; - - // try_trait_v2 is still experimental - let inner = match T::from_rng(&mut reseeder) { - Ok(rng) => rng, - Err(err) => return Err(err), + let threshold = if threshold == 0 { + usize::MAX + } else { + threshold }; + Ok(ReseedingRng { - inner, + inner: T::from_rng(&mut reseeder)?, reseeder, threshold, bytes_until_reseed: threshold, @@ -68,11 +65,7 @@ where 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 { - // try_trait_v2 is still experimental - self.inner = match T::from_rng(&mut self.reseeder) { - Ok(rng) => rng, - Err(e) => return Err(e), - }; + self.inner = T::from_rng(&mut self.reseeder)?; self.bytes_until_reseed = self.threshold; } else { self.bytes_until_reseed -= num_bytes; From dd1dbc00e45ae8717e61d26d3b9b496648214876 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 27 Jun 2024 10:43:38 -0700 Subject: [PATCH 04/16] lpc55-rng: Streamline the `fill` loop & increase the read buffer size. The `ReseedingRng` can handle us pulling more than 4 bytes out at a time. --- drv/lpc55-rng/src/main.rs | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index c05c21c4d3..1558272160 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -9,7 +9,7 @@ #![no_std] #![no_main] -use core::{mem::size_of, usize}; +use core::{cmp, usize}; use drv_lpc55_syscon_api::Syscon; use drv_rng_api::RngError; use idol_runtime::{ClientError, NotificationHandler, RequestError}; @@ -89,24 +89,19 @@ impl idl::InOrderRngImpl for Lpc55RngServer { dest: idol_runtime::Leased, ) -> Result> { let mut cnt = 0; - const STEP: usize = size_of::(); - let mut buf = [0u8; STEP]; - // fill in multiples of STEP / RNG register size - for _ in 0..(dest.len() / STEP) { - self.0.try_fill_bytes(&mut buf).map_err(RngError::from)?; - dest.write_range(cnt..cnt + STEP, &buf) + let mut buf = [0u8; 32]; + while cnt < dest.len() { + let len = cmp::min(buf.len(), dest.len() - cnt); + + self.0 + .try_fill_bytes(&mut buf[..len]) + .map_err(RngError::from)?; + dest.write_range(cnt..cnt + len, &buf[..len]) .map_err(|_| RequestError::Fail(ClientError::WentAway))?; - cnt += STEP; - } - // fill in remaining - let remain = dest.len() - cnt; - assert!(remain < STEP); - if remain > 0 { - self.0.try_fill_bytes(&mut buf).map_err(RngError::from)?; - dest.write_range(dest.len() - remain..dest.len(), &buf) - .map_err(|_| RequestError::Fail(ClientError::WentAway))?; - cnt += remain; + + cnt += len; } + Ok(cnt) } } From f76e2719b2bba6b21f76520ab13d0cfe7f2d248b Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 27 Jun 2024 08:33:16 -0700 Subject: [PATCH 05/16] lpc55-rng: Make ReseedingRng generic over the reseeder. --- drv/lpc55-rng/src/main.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 1558272160..bcf445c8d8 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -21,18 +21,19 @@ use userlib::task_slot; task_slot!(SYSCON, syscon_driver); // low-budget rand::rngs::adapter::ReseedingRng w/o fork stuff -struct ReseedingRng { +struct ReseedingRng { inner: T, - reseeder: Lpc55Rng, + reseeder: R, threshold: usize, bytes_until_reseed: usize, } -impl ReseedingRng +impl ReseedingRng where T: SeedableRng, + R: RngCore, { - fn new(mut reseeder: Lpc55Rng, threshold: usize) -> Result { + fn new(mut reseeder: R, threshold: usize) -> Result { let threshold = if threshold == 0 { usize::MAX } else { @@ -48,9 +49,10 @@ where } } -impl RngCore for ReseedingRng +impl RngCore for ReseedingRng where T: SeedableRng + RngCore, + R: RngCore, { fn next_u32(&mut self) -> u32 { impls::next_u32_via_fill(self) @@ -74,7 +76,7 @@ where } } -struct Lpc55RngServer(ReseedingRng); +struct Lpc55RngServer(ReseedingRng); impl Lpc55RngServer { fn new(reseeder: Lpc55Rng, threshold: usize) -> Result { From c16168fc1294cde2d70a4384945f981571509d01 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Wed, 22 May 2024 13:05:18 -0700 Subject: [PATCH 06/16] 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 c006119cac..ddd20d7098 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1298,8 +1298,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 91f91becd1..633df61984 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 ec85ba8bb8..54ff7aa7f8 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 b52c2fc962..f8eb2858b3 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 bcf445c8d8..f980a8f9c6 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::task_slot; +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 { From 67828cf5b4938be9f221fd2600a0c253c409d0bd Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Wed, 22 May 2024 13:05:18 -0700 Subject: [PATCH 07/16] lpc55-rng: Include DICE derived seed in initial PRNG seed. Previously we constructed the initial seed as 32 bytes from the hardware RNG: ``` SEED_0 = HRNG(32) ``` This commit includes a seed value constructed by the measured boot implementation from the DICE CDI. This is passed through to the RNG task using the `stage0-handoff` mechanism. This 32 byte value is now extracted and mixed with 32 bytes from the HRNG to construct SEED_0: ``` SEED_0 = sha3_256(DICE_SEED | HRNG(32)) ``` Use of this feature is gated by the `dice-seed` feature. --- Cargo.lock | 7 +++ app/lpc55xpresso/app.toml | 3 +- app/rot-carrier/app.toml | 3 +- chips/lpc55/memory.toml | 16 +++++ drv/lpc55-rng/Cargo.toml | 13 +++- drv/lpc55-rng/build.rs | 59 ++++++++++++++++-- drv/lpc55-rng/src/config.rs | 11 ++++ drv/lpc55-rng/src/main.rs | 119 ++++++++++++++++++++++++++++++++++-- 8 files changed, 216 insertions(+), 15 deletions(-) create mode 100644 drv/lpc55-rng/src/config.rs diff --git a/Cargo.lock b/Cargo.lock index ddd20d7098..59ac921387 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1289,16 +1289,23 @@ dependencies = [ name = "drv-lpc55-rng" version = "0.1.0" dependencies = [ + "anyhow", + "build-util", "cfg-if", "drv-lpc55-syscon-api", "drv-rng-api", + "hubpack", "idol", "idol-runtime", + "lib-dice", "lib-lpc55-rng", "num-traits", "rand_chacha", "rand_core", + "ringbuf", + "serde", "sha3", + "stage0-handoff", "userlib", "zerocopy 0.6.6", "zeroize", diff --git a/app/lpc55xpresso/app.toml b/app/lpc55xpresso/app.toml index 633df61984..079ca3af27 100644 --- a/app/lpc55xpresso/app.toml +++ b/app/lpc55xpresso/app.toml @@ -115,8 +115,9 @@ name = "drv-lpc55-rng" priority = 3 uses = ["rng", "pmc"] start = true -stacksize = 2600 +stacksize = 2704 task-slots = ["syscon_driver"] +extern-regions = ["dice_rng"] [tasks.pong] name = "task-pong" diff --git a/app/rot-carrier/app.toml b/app/rot-carrier/app.toml index 54ff7aa7f8..016f4cfe85 100644 --- a/app/rot-carrier/app.toml +++ b/app/rot-carrier/app.toml @@ -101,8 +101,9 @@ name = "drv-lpc55-rng" priority = 5 uses = ["rng", "pmc"] start = true -stacksize = 2600 +stacksize = 2704 task-slots = ["syscon_driver"] +extern-regions = ["dice_rng"] [tasks.sprot] name = "drv-lpc55-sprot-server" diff --git a/chips/lpc55/memory.toml b/chips/lpc55/memory.toml index 47be0adc47..1d8573a246 100644 --- a/chips/lpc55/memory.toml +++ b/chips/lpc55/memory.toml @@ -156,3 +156,19 @@ size = 0x800 read = true write = true execute = false + +[[dice_rng]] +name = "a" +address =0x40101a00 +size = 0x100 +read = true +write = true +execute = false + +[[dice_rng]] +name = "b" +address =0x40101a00 +size = 0x100 +read = true +write = true +execute = false diff --git a/drv/lpc55-rng/Cargo.toml b/drv/lpc55-rng/Cargo.toml index f8eb2858b3..e834d1c8e7 100644 --- a/drv/lpc55-rng/Cargo.toml +++ b/drv/lpc55-rng/Cargo.toml @@ -5,23 +5,34 @@ edition = "2021" [dependencies] cfg-if = { workspace = true } +hubpack.workspace = true idol-runtime = { workspace = true } num-traits = { workspace = true } rand_chacha = { workspace = true } rand_core = { workspace = true } +serde.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" } +lib-dice.path = "../../lib/dice" lib-lpc55-rng.path = "../../lib/lpc55-rng" +ringbuf.path = "../../lib/ringbuf" +stage0-handoff = { path = "../../lib/stage0-handoff", optional = true } userlib = { path = "../../sys/userlib", features = ["panic-messages"] } [build-dependencies] -idol = { workspace = true } +anyhow.workspace = true +build-util.path = "../../build/util" +cfg-if.workspace = true +idol.workspace = true +serde.workspace = true [features] +default = ["dice-seed"] +dice-seed = ["stage0-handoff"] no-ipc-counters = ["idol/no-counters"] # This section is here to discourage RLS/rust-analyzer from doing test builds, diff --git a/drv/lpc55-rng/build.rs b/drv/lpc55-rng/build.rs index 4f53fdc481..8fb4688da5 100644 --- a/drv/lpc55-rng/build.rs +++ b/drv/lpc55-rng/build.rs @@ -2,15 +2,62 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -fn main() -> Result<(), Box> { +use anyhow::{anyhow, Result}; +use idol::{server::ServerStyle, CounterSettings}; + +cfg_if::cfg_if! { + if #[cfg(feature = "dice-seed")] { + mod config { + include!("src/config.rs"); + } + use anyhow::Context; + use config::DataRegion; + use std::{fs::File, io::Write}; + + const CFG_SRC: &str = "rng-config.rs"; + } +} + +#[cfg(feature = "dice-seed")] +fn extern_regions_to_cfg(path: &str) -> Result<()> { + let out_dir = build_util::out_dir(); + let dest_path = out_dir.join(path); + let mut out = + File::create(dest_path).context(format!("creating {}", path))?; + + let data_regions = build_util::task_extern_regions::()?; + if data_regions.is_empty() { + return Err(anyhow!("no data regions found")); + } + + writeln!(out, "use crate::config::DataRegion;\n\n")?; + + let region = data_regions + .get("dice_rng") + .ok_or_else(|| anyhow::anyhow!("dice_certs data region not found"))?; + + Ok(writeln!( + out, + r##"pub const DICE_RNG: DataRegion = DataRegion {{ + address: {:#x}, + size: {:#x}, +}};"##, + region.address, region.size + )?) +} + +fn main() -> Result<()> { idol::Generator::new() - .with_counters( - idol::CounterSettings::default().with_server_counters(false), - ) + .with_counters(CounterSettings::default().with_server_counters(false)) .build_server_support( "../../idl/rng.idol", "server_stub.rs", - idol::server::ServerStyle::InOrder, - )?; + ServerStyle::InOrder, + ) + .map_err(|e| anyhow!(e))?; + + #[cfg(feature = "dice-seed")] + extern_regions_to_cfg(CFG_SRC)?; + Ok(()) } diff --git a/drv/lpc55-rng/src/config.rs b/drv/lpc55-rng/src/config.rs new file mode 100644 index 0000000000..519a285eee --- /dev/null +++ b/drv/lpc55-rng/src/config.rs @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#[derive(serde::Deserialize, Default, Debug)] +#[serde(rename_all = "kebab-case")] +#[cfg(feature = "dice-seed")] +pub struct DataRegion { + pub address: usize, + pub size: usize, +} diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index f980a8f9c6..05c0a55c53 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -9,13 +9,17 @@ #![no_std] #![no_main] +mod config; + use core::{cmp, usize}; use drv_lpc55_syscon_api::Syscon; use drv_rng_api::RngError; use idol_runtime::{ClientError, NotificationHandler, RequestError}; +use lib_dice::{RngSeed, SeedBuf}; use lib_lpc55_rng::Lpc55Rng; use rand_chacha::ChaCha20Rng; use rand_core::{impls, Error, RngCore, SeedableRng}; +use ringbuf::ringbuf; use sha3::{ digest::crypto_common::{generic_array::GenericArray, OutputSizeUser}, digest::FixedOutputReset, @@ -24,8 +28,39 @@ use sha3::{ use userlib::task_slot; use zeroize::Zeroizing; +cfg_if::cfg_if! { + if #[cfg(any(feature = "dice-seed"))] { + use config::DataRegion; + use hubpack::SerializedSize; + use lib_dice::RngData; + use ringbuf::ringbuf_entry; + use serde::Deserialize; + use stage0_handoff::{HandoffData, HandoffDataLoadError}; + + // This file is generated by the crate build.rs. It contains instances + // of config::DataRegion structs describing regions of memory + // configured & exposed to this task by the hubris build. + mod build { + include!(concat!(env!("OUT_DIR"), "/rng-config.rs")); + } + + use build::DICE_RNG; + } +} + task_slot!(SYSCON, syscon_driver); +#[derive(Copy, Clone, PartialEq)] +enum Trace { + None, + #[cfg(feature = "dice-seed")] + NoDiceSeed, + #[cfg(feature = "dice-seed")] + HandoffError(HandoffDataLoadError), +} + +ringbuf!(Trace, 16, Trace::None); + // low-budget rand::rngs::adapter::ReseedingRng w/o fork stuff struct ReseedingRng { inner: T, @@ -42,19 +77,37 @@ where H: FixedOutputReset + Default + Digest, [u8; 32]: From::OutputSize>>, { - fn new(mut reseeder: R, threshold: usize) -> Result { + fn new( + seed: Option<&RngSeed>, + mut reseeder: R, + threshold: usize, + ) -> Result { let threshold = if threshold == 0 { usize::MAX } else { threshold }; + let mut mixer = H::default(); + if let Some(seed) = seed { + // mix platform unique seed derived by measured boot + Digest::update(&mut mixer, seed.as_bytes()); + } + + // w/ 32 bytes from HRNG + let mut buf = Zeroizing::new(T::Seed::default()); + reseeder.try_fill_bytes(buf.as_mut())?; + Digest::update(&mut mixer, buf.as_ref()); + + // create initial instance of the SeedableRng from the seed + let inner = T::from_seed(mixer.finalize_fixed_reset().into()); + Ok(ReseedingRng { - inner: T::from_rng(&mut reseeder)?, + inner, reseeder, threshold, bytes_until_reseed: threshold, - mixer: H::default(), + mixer, }) } } @@ -116,8 +169,14 @@ where struct Lpc55RngServer(ReseedingRng); impl Lpc55RngServer { - fn new(reseeder: Lpc55Rng, threshold: usize) -> Result { - Ok(Lpc55RngServer(ReseedingRng::new(reseeder, threshold)?)) + fn new( + seed: Option<&RngSeed>, + reseeder: Lpc55Rng, + threshold: usize, + ) -> Result { + Ok(Lpc55RngServer(ReseedingRng::new( + seed, reseeder, threshold, + )?)) } } @@ -156,12 +215,60 @@ impl NotificationHandler for Lpc55RngServer { } } +/// Load a type implementing HandoffData (and others) from a config::DataRegion. +/// Errors will be reported in the ringbuf and will return None. +#[cfg(feature = "dice-seed")] +fn load_data_from_region< + T: for<'a> Deserialize<'a> + HandoffData + SerializedSize, +>( + region: &DataRegion, +) -> Option { + use core::slice; + + // Safety: This memory is setup by code executed before hubris and + // exposed using the kernel `extern-regions` mechanism. The safety of + // this code is an extension of our trust in the hubris pre-main, kernel, + // and build process. + let data = unsafe { + slice::from_raw_parts(region.address as *mut u8, region.size) + }; + + T::load_from_addr(data) + .inspect_err(|e| ringbuf_entry!(Trace::HandoffError(*e))) + .ok() +} + +/// Get the seed derived by the lpc55-rot-startup and passed to us through +/// the stage0-handoff memory region. +/// +/// If use of DICE seed in seeding the PRNG is not enabled then this function +/// will just return None. Otherwise it will attempt to get the seed from the +/// dice-rng region of the stage0-handoff memory. If it's not able to get +/// the seed it will put an entry in the ringbuf and panic. +pub fn get_dice_seed() -> Option { + cfg_if::cfg_if! { + if #[cfg(feature = "dice-seed")] { + match load_data_from_region::(&DICE_RNG) { + Some(rng_data) => Some(rng_data.seed), + _ => { + ringbuf_entry!(Trace::NoDiceSeed); + panic!(); + }, + } + } else { + None + } + } +} + #[export_name = "main"] fn main() -> ! { + let seed = get_dice_seed(); + let rng = Lpc55Rng::new(&Syscon::from(SYSCON.get_task_id())); let threshold = 0x100000; // 1 MiB - let mut rng = Lpc55RngServer::new(rng, threshold) + let mut rng = Lpc55RngServer::new(seed.as_ref(), rng, threshold) .expect("Failed to create Lpc55RngServer"); let mut buffer = [0u8; idl::INCOMING_SIZE]; From 0c8a4be9886a1861caf7163f9c0ddda47fdeb653 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Fri, 14 Jun 2024 21:57:33 +0000 Subject: [PATCH 08/16] lpc55-rng: Include SN from platform id cert in initial PRNG seed. Platforms assigned a unique serial number can include this string in the initial seed to ensure uniqueness in the bit stream produced by the RNG. We now construct the intial seed as: ``` SEED_0 = sha3_256(DICE_SEED | SN | HRNG(32)) ``` Extracting the Platform Id / serial number from the platform identity cert required exposing the relevant module from the lib-dice crate. We also add additional constants to the template module that are required to know the length of the platform id string at compile time. Finally this feature is gated by the same `dice-seed` feature used for the seed derived by measured boot for simplicity. --- Cargo.lock | 1 + app/lpc55xpresso/app.toml | 4 +-- app/rot-carrier/app.toml | 4 +-- drv/lpc55-rng/Cargo.toml | 3 +- drv/lpc55-rng/build.rs | 37 +++++++++++++------- drv/lpc55-rng/src/main.rs | 54 +++++++++++++++++++++++++---- lib/dice/src/lib.rs | 2 +- lib/dice/src/persistid_cert_tmpl.rs | 5 ++- 8 files changed, 84 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59ac921387..3c7ed3c0e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1297,6 +1297,7 @@ dependencies = [ "hubpack", "idol", "idol-runtime", + "indexmap 1.9.1", "lib-dice", "lib-lpc55-rng", "num-traits", diff --git a/app/lpc55xpresso/app.toml b/app/lpc55xpresso/app.toml index 079ca3af27..980a14c43e 100644 --- a/app/lpc55xpresso/app.toml +++ b/app/lpc55xpresso/app.toml @@ -115,9 +115,9 @@ name = "drv-lpc55-rng" priority = 3 uses = ["rng", "pmc"] start = true -stacksize = 2704 +stacksize = 4200 task-slots = ["syscon_driver"] -extern-regions = ["dice_rng"] +extern-regions = ["dice_certs", "dice_rng"] [tasks.pong] name = "task-pong" diff --git a/app/rot-carrier/app.toml b/app/rot-carrier/app.toml index 016f4cfe85..c22f06be85 100644 --- a/app/rot-carrier/app.toml +++ b/app/rot-carrier/app.toml @@ -101,9 +101,9 @@ name = "drv-lpc55-rng" priority = 5 uses = ["rng", "pmc"] start = true -stacksize = 2704 +stacksize = 4200 task-slots = ["syscon_driver"] -extern-regions = ["dice_rng"] +extern-regions = ["dice_certs", "dice_rng"] [tasks.sprot] name = "drv-lpc55-sprot-server" diff --git a/drv/lpc55-rng/Cargo.toml b/drv/lpc55-rng/Cargo.toml index e834d1c8e7..81d9e5e0dc 100644 --- a/drv/lpc55-rng/Cargo.toml +++ b/drv/lpc55-rng/Cargo.toml @@ -28,11 +28,12 @@ anyhow.workspace = true build-util.path = "../../build/util" cfg-if.workspace = true idol.workspace = true +indexmap = { workspace = true, optional = true } serde.workspace = true [features] default = ["dice-seed"] -dice-seed = ["stage0-handoff"] +dice-seed = ["indexmap", "stage0-handoff"] no-ipc-counters = ["idol/no-counters"] # This section is here to discourage RLS/rust-analyzer from doing test builds, diff --git a/drv/lpc55-rng/build.rs b/drv/lpc55-rng/build.rs index 8fb4688da5..1b3cae546b 100644 --- a/drv/lpc55-rng/build.rs +++ b/drv/lpc55-rng/build.rs @@ -12,12 +12,35 @@ cfg_if::cfg_if! { } use anyhow::Context; use config::DataRegion; + use indexmap::IndexMap; use std::{fs::File, io::Write}; const CFG_SRC: &str = "rng-config.rs"; } } +#[cfg(feature = "dice-seed")] +fn extern_region_to_cfg( + out: &mut W, + data_regions: &IndexMap, + name: &str, +) -> Result<()> { + let region = data_regions + .get(name) + .ok_or_else(|| anyhow::anyhow!("dice_certs data region not found"))?; + + Ok(writeln!( + out, + r##"pub const {}: DataRegion = DataRegion {{ + address: {:#x}, + size: {:#x}, +}};"##, + name.to_uppercase(), + region.address, + region.size + )?) +} + #[cfg(feature = "dice-seed")] fn extern_regions_to_cfg(path: &str) -> Result<()> { let out_dir = build_util::out_dir(); @@ -32,18 +55,8 @@ fn extern_regions_to_cfg(path: &str) -> Result<()> { writeln!(out, "use crate::config::DataRegion;\n\n")?; - let region = data_regions - .get("dice_rng") - .ok_or_else(|| anyhow::anyhow!("dice_certs data region not found"))?; - - Ok(writeln!( - out, - r##"pub const DICE_RNG: DataRegion = DataRegion {{ - address: {:#x}, - size: {:#x}, -}};"##, - region.address, region.size - )?) + extern_region_to_cfg(&mut out, &data_regions, "dice_certs")?; + extern_region_to_cfg(&mut out, &data_regions, "dice_rng") } fn main() -> Result<()> { diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 05c0a55c53..139655ebe2 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -15,7 +15,7 @@ use core::{cmp, usize}; use drv_lpc55_syscon_api::Syscon; use drv_rng_api::RngError; use idol_runtime::{ClientError, NotificationHandler, RequestError}; -use lib_dice::{RngSeed, SeedBuf}; +use lib_dice::{persistid_cert_tmpl::SUBJECT_CN_LENGTH, RngSeed, SeedBuf}; use lib_lpc55_rng::Lpc55Rng; use rand_chacha::ChaCha20Rng; use rand_core::{impls, Error, RngCore, SeedableRng}; @@ -29,13 +29,14 @@ use userlib::task_slot; use zeroize::Zeroizing; cfg_if::cfg_if! { - if #[cfg(any(feature = "dice-seed"))] { + if #[cfg(feature = "dice-seed")] { use config::DataRegion; use hubpack::SerializedSize; - use lib_dice::RngData; + use lib_dice::{persistid_cert_tmpl::SUBJECT_CN_RANGE, CertData, RngData}; use ringbuf::ringbuf_entry; use serde::Deserialize; use stage0_handoff::{HandoffData, HandoffDataLoadError}; + use userlib::UnwrapLite; // This file is generated by the crate build.rs. It contains instances // of config::DataRegion structs describing regions of memory @@ -44,7 +45,7 @@ cfg_if::cfg_if! { include!(concat!(env!("OUT_DIR"), "/rng-config.rs")); } - use build::DICE_RNG; + use build::{DICE_CERTS, DICE_RNG}; } } @@ -57,6 +58,8 @@ enum Trace { NoDiceSeed, #[cfg(feature = "dice-seed")] HandoffError(HandoffDataLoadError), + #[cfg(feature = "dice-seed")] + NoSeedPersonalization, } ringbuf!(Trace, 16, Trace::None); @@ -80,6 +83,7 @@ where fn new( seed: Option<&RngSeed>, mut reseeder: R, + pid: Option<&[u8; SUBJECT_CN_LENGTH]>, threshold: usize, ) -> Result { let threshold = if threshold == 0 { @@ -94,6 +98,11 @@ where Digest::update(&mut mixer, seed.as_bytes()); } + if let Some(pid) = pid { + // mix in unique platform id + Digest::update(&mut mixer, pid); + } + // w/ 32 bytes from HRNG let mut buf = Zeroizing::new(T::Seed::default()); reseeder.try_fill_bytes(buf.as_mut())?; @@ -172,10 +181,11 @@ impl Lpc55RngServer { fn new( seed: Option<&RngSeed>, reseeder: Lpc55Rng, + pid: Option<&[u8; SUBJECT_CN_LENGTH]>, threshold: usize, ) -> Result { Ok(Lpc55RngServer(ReseedingRng::new( - seed, reseeder, threshold, + seed, reseeder, pid, threshold, )?)) } } @@ -261,15 +271,45 @@ pub fn get_dice_seed() -> Option { } } +/// Get the platform identifier / barcode string from the platform identity +/// cert passed to hubris by the lpc55-rot-startup through the stage0-handoff +/// memory region. +/// +/// If use of the platform identifier string is not enabled then this function +/// will return `None`. Otherwise it will try to get the platform identity +/// string from the stage0-handoff region. If it's unable to get this data it +/// will put an entry into the ringbuf and panic. +pub fn get_seed_personalization() -> Option<[u8; SUBJECT_CN_LENGTH]> { + cfg_if::cfg_if! { + if #[cfg(feature = "dice-seed")] { + match load_data_from_region::(&DICE_CERTS) { + Some(cert_data) => Some( + cert_data.persistid_cert.0.as_bytes()[SUBJECT_CN_RANGE] + .try_into() + .unwrap_lite(), + ), + _ => { + ringbuf_entry!(Trace::NoSeedPersonalization); + panic!(); + }, + } + } else { + None + } + } +} + #[export_name = "main"] fn main() -> ! { let seed = get_dice_seed(); + let pid = get_seed_personalization(); let rng = Lpc55Rng::new(&Syscon::from(SYSCON.get_task_id())); let threshold = 0x100000; // 1 MiB - let mut rng = Lpc55RngServer::new(seed.as_ref(), rng, threshold) - .expect("Failed to create Lpc55RngServer"); + let mut rng = + Lpc55RngServer::new(seed.as_ref(), rng, pid.as_ref(), threshold) + .expect("Failed to create Lpc55RngServer"); let mut buffer = [0u8; idl::INCOMING_SIZE]; loop { diff --git a/lib/dice/src/lib.rs b/lib/dice/src/lib.rs index f0d556296d..06cfcf2f6c 100644 --- a/lib/dice/src/lib.rs +++ b/lib/dice/src/lib.rs @@ -30,7 +30,7 @@ mod alias_cert_tmpl; mod deviceid_cert_tmpl; mod handoff; mod mfg; -mod persistid_cert_tmpl; +pub mod persistid_cert_tmpl; mod persistid_csr_tmpl; pub use crate::mfg::{ DiceMfg, DiceMfgState, PersistIdSeed, SelfMfg, SerialMfg, diff --git a/lib/dice/src/persistid_cert_tmpl.rs b/lib/dice/src/persistid_cert_tmpl.rs index d4c03afb08..68140acfd1 100644 --- a/lib/dice/src/persistid_cert_tmpl.rs +++ b/lib/dice/src/persistid_cert_tmpl.rs @@ -12,7 +12,10 @@ use core::ops::Range; pub const SIZE: usize = 441; pub const SERIAL_NUMBER_RANGE: Range = 15..16; pub const ISSUER_CN_RANGE: Range = 82..114; -pub const SUBJECT_CN_RANGE: Range = 207..239; +pub const SUBJECT_CN_START: usize = 207; +pub const SUBJECT_CN_END: usize = 239; +pub const SUBJECT_CN_RANGE: Range = SUBJECT_CN_START..SUBJECT_CN_END; +pub const SUBJECT_CN_LENGTH: usize = SUBJECT_CN_END - SUBJECT_CN_START; pub const PUB_RANGE: Range = 251..283; pub const SIG_RANGE: Range = 377..441; pub const SIGNDATA_RANGE: Range = 4..367; From 6036d0eb122bb05f3d01cba36056b41019dffae3 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Tue, 23 Jul 2024 04:41:09 +0000 Subject: [PATCH 09/16] oxide-rot-1: Add rng task to app-dev.toml. --- app/oxide-rot-1/app-dev.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/oxide-rot-1/app-dev.toml b/app/oxide-rot-1/app-dev.toml index 5a50d25b94..5f8662e60a 100644 --- a/app/oxide-rot-1/app-dev.toml +++ b/app/oxide-rot-1/app-dev.toml @@ -165,6 +165,15 @@ stacksize = 12304 start = true extern-regions = ["dice_alias", "dice_certs"] +[tasks.rng_driver] +name = "drv-lpc55-rng" +priority = 6 +uses = ["rng", "pmc"] +start = true +stacksize = 4200 +task-slots = ["syscon_driver"] +extern-regions = ["dice_certs", "dice_rng"] + [signing.certs] signing-certs = ["../../support/fake_certs/fake_certificate.der.crt"] root-certs = ["../../support/fake_certs/fake_certificate.der.crt"] From bce388971fce1d15496ef0bc7174bc4f4033a5e5 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Tue, 24 Sep 2024 00:29:48 +0000 Subject: [PATCH 10/16] Pass lpc55 PMC & RNG devices as parameters to Lpc55Rng 'new'. Not sure if having the server take ownership of the PMC & RNG instances is optimal but it seems to most accurately reflect our intent. This resolves: - https://github.com/oxidecomputer/hubris/pull/1820#discussion_r1772082662 - https://github.com/oxidecomputer/hubris/pull/1820#discussion_r1772082772 --- Cargo.lock | 1 + drv/lpc55-rng/Cargo.toml | 1 + drv/lpc55-rng/src/main.rs | 8 +++++++- lib/lpc55-rng/src/lib.rs | 14 +++++--------- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c7ed3c0e5..04a13dfa5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1300,6 +1300,7 @@ dependencies = [ "indexmap 1.9.1", "lib-dice", "lib-lpc55-rng", + "lpc55-pac", "num-traits", "rand_chacha", "rand_core", diff --git a/drv/lpc55-rng/Cargo.toml b/drv/lpc55-rng/Cargo.toml index 81d9e5e0dc..6408cab2cf 100644 --- a/drv/lpc55-rng/Cargo.toml +++ b/drv/lpc55-rng/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" cfg-if = { workspace = true } hubpack.workspace = true idol-runtime = { workspace = true } +lpc55-pac.workspace = true num-traits = { workspace = true } rand_chacha = { workspace = true } rand_core = { workspace = true } diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 139655ebe2..fb403874b0 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -17,6 +17,7 @@ use drv_rng_api::RngError; use idol_runtime::{ClientError, NotificationHandler, RequestError}; use lib_dice::{persistid_cert_tmpl::SUBJECT_CN_LENGTH, RngSeed, SeedBuf}; use lib_lpc55_rng::Lpc55Rng; +use lpc55_pac::Peripherals; use rand_chacha::ChaCha20Rng; use rand_core::{impls, Error, RngCore, SeedableRng}; use ringbuf::ringbuf; @@ -303,8 +304,13 @@ pub fn get_seed_personalization() -> Option<[u8; SUBJECT_CN_LENGTH]> { fn main() -> ! { let seed = get_dice_seed(); let pid = get_seed_personalization(); + let peripherals = Peripherals::take().unwrap(); - let rng = Lpc55Rng::new(&Syscon::from(SYSCON.get_task_id())); + let rng = Lpc55Rng::new( + peripherals.PMC, + peripherals.RNG, + &Syscon::from(SYSCON.get_task_id()), + ); let threshold = 0x100000; // 1 MiB let mut rng = diff --git a/lib/lpc55-rng/src/lib.rs b/lib/lpc55-rng/src/lib.rs index 15ede6712a..fcf4c743cf 100644 --- a/lib/lpc55-rng/src/lib.rs +++ b/lib/lpc55-rng/src/lib.rs @@ -8,31 +8,27 @@ use core::{cmp, mem}; use drv_lpc55_syscon_api::{Peripheral, Syscon}; use drv_rng_api::RngError; -use lpc55_pac::{pmc, rng, PMC, RNG}; +use lpc55_pac::{PMC, RNG}; use rand_core::{impls, Error, RngCore}; /// The Lpc55Rng is a thin wrapper around the LPC55 hardware random number /// generator (HRNG). pub struct Lpc55Rng { - pub pmc: &'static pmc::RegisterBlock, - pub rng: &'static rng::RegisterBlock, + pub pmc: PMC, + pub rng: RNG, } impl Lpc55Rng { /// Create a new Lpc55Rng instance after powering on, enabling the clocks /// and reseting the underlying HRNG. - pub fn new(syscon: &Syscon) -> Self { - let pmc = unsafe { &*PMC::ptr() }; + pub fn new(pmc: PMC, rng: RNG, syscon: &Syscon) -> Self { pmc.pdruncfg0.modify(|_, w| w.pden_rng().poweredon()); syscon.enable_clock(Peripheral::Rng); syscon.enter_reset(Peripheral::Rng); syscon.leave_reset(Peripheral::Rng); - Lpc55Rng { - pmc, - rng: unsafe { &*RNG::ptr() }, - } + Lpc55Rng { pmc, rng } } } From c29dffad19ac1a26bcfe4a058c66efe5e016bc83 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Tue, 24 Sep 2024 02:42:45 +0000 Subject: [PATCH 11/16] Panic if RNG is powered off after Lpc55Rng instantiation. This resolves: - https://github.com/oxidecomputer/hubris/pull/1820#discussion_r1772084771 --- drv/rng-api/src/lib.rs | 3 +-- lib/lpc55-rng/src/lib.rs | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drv/rng-api/src/lib.rs b/drv/rng-api/src/lib.rs index e89f9a4d5d..ec733005a8 100644 --- a/drv/rng-api/src/lib.rs +++ b/drv/rng-api/src/lib.rs @@ -17,8 +17,7 @@ use userlib::{sys_send, FromPrimitive}; Copy, Clone, Debug, FromPrimitive, Eq, PartialEq, IdolError, counters::Count, )] pub enum RngError { - PoweredOff = 1, - NoData, + NoData = 1, ClockError, SeedError, UnknownRngError, diff --git a/lib/lpc55-rng/src/lib.rs b/lib/lpc55-rng/src/lib.rs index fcf4c743cf..26669166bf 100644 --- a/lib/lpc55-rng/src/lib.rs +++ b/lib/lpc55-rng/src/lib.rs @@ -7,7 +7,6 @@ use core::{cmp, mem}; use drv_lpc55_syscon_api::{Peripheral, Syscon}; -use drv_rng_api::RngError; use lpc55_pac::{PMC, RNG}; use rand_core::{impls, Error, RngCore}; @@ -53,8 +52,10 @@ impl RngCore for Lpc55Rng { fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Error> { let mut filled = 0; while filled < dst.len() { + // `new` takes ownership of the PMC & RNG before powering on the + // RNG. If it gets turned off between then and now it's a bug. if self.pmc.pdruncfg0.read().pden_rng().bits() { - return Err(RngError::PoweredOff.into()); + panic!(); } let src = self.rng.random_number.read().bits(); From cf2704fae6fab72f6178fb6ca768fc171c7820d0 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Tue, 24 Sep 2024 21:55:02 +0000 Subject: [PATCH 12/16] Make safety comment on code loading data from handoff region true. This prevents the caller from attempting to deserialize data from arbitrary memory regions. --- drv/lpc55-rng/build.rs | 16 +++---- drv/lpc55-rng/src/config.rs | 54 ++++++++++++++++++--- drv/lpc55-rng/src/data-region.rs | 10 ++++ drv/lpc55-rng/src/main.rs | 80 ++++++++++---------------------- 4 files changed, 91 insertions(+), 69 deletions(-) create mode 100644 drv/lpc55-rng/src/data-region.rs diff --git a/drv/lpc55-rng/build.rs b/drv/lpc55-rng/build.rs index 1b3cae546b..740f75941d 100644 --- a/drv/lpc55-rng/build.rs +++ b/drv/lpc55-rng/build.rs @@ -7,11 +7,11 @@ use idol::{server::ServerStyle, CounterSettings}; cfg_if::cfg_if! { if #[cfg(feature = "dice-seed")] { - mod config { - include!("src/config.rs"); + mod data_region { + include!("src/data-region.rs"); } use anyhow::Context; - use config::DataRegion; + use data_region::DataRegion; use indexmap::IndexMap; use std::{fs::File, io::Write}; @@ -25,13 +25,13 @@ fn extern_region_to_cfg( data_regions: &IndexMap, name: &str, ) -> Result<()> { - let region = data_regions - .get(name) - .ok_or_else(|| anyhow::anyhow!("dice_certs data region not found"))?; + let region = data_regions.get(name).ok_or_else(|| { + anyhow::anyhow!(format!("external region not found: {}", name)) + })?; Ok(writeln!( out, - r##"pub const {}: DataRegion = DataRegion {{ + r##"pub const {}_REGION: DataRegion = DataRegion {{ address: {:#x}, size: {:#x}, }};"##, @@ -53,7 +53,7 @@ fn extern_regions_to_cfg(path: &str) -> Result<()> { return Err(anyhow!("no data regions found")); } - writeln!(out, "use crate::config::DataRegion;\n\n")?; + writeln!(out, "use crate::data_region::DataRegion;\n\n")?; extern_region_to_cfg(&mut out, &data_regions, "dice_certs")?; extern_region_to_cfg(&mut out, &data_regions, "dice_rng") diff --git a/drv/lpc55-rng/src/config.rs b/drv/lpc55-rng/src/config.rs index 519a285eee..420787f1b4 100644 --- a/drv/lpc55-rng/src/config.rs +++ b/drv/lpc55-rng/src/config.rs @@ -2,10 +2,52 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#[derive(serde::Deserialize, Default, Debug)] -#[serde(rename_all = "kebab-case")] -#[cfg(feature = "dice-seed")] -pub struct DataRegion { - pub address: usize, - pub size: usize, +use crate::data_region::DataRegion; +use hubpack::SerializedSize; +use serde::Deserialize; +use stage0_handoff::{HandoffData, HandoffDataLoadError}; + +pub enum HandoffDataRegion { + DiceCerts, + DiceRng, +} + +pub const DICE_CERTS: HandoffDataRegion = HandoffDataRegion::DiceCerts; +pub const DICE_RNG: HandoffDataRegion = HandoffDataRegion::DiceRng; + +// This file is generated by the crate build.rs. +mod build { + include!(concat!(env!("OUT_DIR"), "/rng-config.rs")); +} + +use build::{DICE_CERTS_REGION, DICE_RNG_REGION}; + +impl HandoffDataRegion { + pub fn data_region(&self) -> DataRegion { + match self { + Self::DiceCerts => DICE_CERTS_REGION, + Self::DiceRng => DICE_RNG_REGION, + } + } + + /// Load a type implementing HandoffData (and others) from a config::DataRegion. + /// Errors will be reported in the ringbuf and will return None. + pub fn load_data< + T: for<'a> Deserialize<'a> + HandoffData + SerializedSize, + >( + &self, + ) -> Result { + use core::slice; + + let region = self.data_region(); + // Safety: This memory is setup by code executed before hubris and + // exposed using the kernel `extern-regions` mechanism. The safety of + // this code is an extension of our trust in the hubris pre-main, kernel, + // and build process. + let data = unsafe { + slice::from_raw_parts(region.address as *mut u8, region.size) + }; + + T::load_from_addr(data) + } } diff --git a/drv/lpc55-rng/src/data-region.rs b/drv/lpc55-rng/src/data-region.rs new file mode 100644 index 0000000000..fbd20f716d --- /dev/null +++ b/drv/lpc55-rng/src/data-region.rs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#[derive(serde::Deserialize, Default, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct DataRegion { + pub address: usize, + pub size: usize, +} diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index fb403874b0..79a4f37908 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -9,8 +9,6 @@ #![no_std] #![no_main] -mod config; - use core::{cmp, usize}; use drv_lpc55_syscon_api::Syscon; use drv_rng_api::RngError; @@ -31,22 +29,13 @@ use zeroize::Zeroizing; cfg_if::cfg_if! { if #[cfg(feature = "dice-seed")] { - use config::DataRegion; - use hubpack::SerializedSize; - use lib_dice::{persistid_cert_tmpl::SUBJECT_CN_RANGE, CertData, RngData}; + mod config; + #[path="data-region.rs"] + mod data_region; + use ringbuf::ringbuf_entry; - use serde::Deserialize; - use stage0_handoff::{HandoffData, HandoffDataLoadError}; + use stage0_handoff::HandoffDataLoadError; use userlib::UnwrapLite; - - // This file is generated by the crate build.rs. It contains instances - // of config::DataRegion structs describing regions of memory - // configured & exposed to this task by the hubris build. - mod build { - include!(concat!(env!("OUT_DIR"), "/rng-config.rs")); - } - - use build::{DICE_CERTS, DICE_RNG}; } } @@ -56,11 +45,9 @@ task_slot!(SYSCON, syscon_driver); enum Trace { None, #[cfg(feature = "dice-seed")] - NoDiceSeed, + NoDiceSeed(HandoffDataLoadError), #[cfg(feature = "dice-seed")] - HandoffError(HandoffDataLoadError), - #[cfg(feature = "dice-seed")] - NoSeedPersonalization, + NoSeedPersonalization(HandoffDataLoadError), } ringbuf!(Trace, 16, Trace::None); @@ -226,29 +213,6 @@ impl NotificationHandler for Lpc55RngServer { } } -/// Load a type implementing HandoffData (and others) from a config::DataRegion. -/// Errors will be reported in the ringbuf and will return None. -#[cfg(feature = "dice-seed")] -fn load_data_from_region< - T: for<'a> Deserialize<'a> + HandoffData + SerializedSize, ->( - region: &DataRegion, -) -> Option { - use core::slice; - - // Safety: This memory is setup by code executed before hubris and - // exposed using the kernel `extern-regions` mechanism. The safety of - // this code is an extension of our trust in the hubris pre-main, kernel, - // and build process. - let data = unsafe { - slice::from_raw_parts(region.address as *mut u8, region.size) - }; - - T::load_from_addr(data) - .inspect_err(|e| ringbuf_entry!(Trace::HandoffError(*e))) - .ok() -} - /// Get the seed derived by the lpc55-rot-startup and passed to us through /// the stage0-handoff memory region. /// @@ -259,10 +223,13 @@ fn load_data_from_region< pub fn get_dice_seed() -> Option { cfg_if::cfg_if! { if #[cfg(feature = "dice-seed")] { - match load_data_from_region::(&DICE_RNG) { - Some(rng_data) => Some(rng_data.seed), - _ => { - ringbuf_entry!(Trace::NoDiceSeed); + use config::DICE_RNG; + use lib_dice::RngData; + + match DICE_RNG.load_data::() { + Ok(rng_data) => Some(rng_data.seed), + Err(e) => { + ringbuf_entry!(Trace::NoDiceSeed(e)); panic!(); }, } @@ -283,14 +250,17 @@ pub fn get_dice_seed() -> Option { pub fn get_seed_personalization() -> Option<[u8; SUBJECT_CN_LENGTH]> { cfg_if::cfg_if! { if #[cfg(feature = "dice-seed")] { - match load_data_from_region::(&DICE_CERTS) { - Some(cert_data) => Some( - cert_data.persistid_cert.0.as_bytes()[SUBJECT_CN_RANGE] - .try_into() - .unwrap_lite(), - ), - _ => { - ringbuf_entry!(Trace::NoSeedPersonalization); + use config::DICE_CERTS; + use lib_dice::{persistid_cert_tmpl::SUBJECT_CN_RANGE, CertData}; + + match DICE_CERTS.load_data::() { + Ok(cert_data) => Some( + cert_data.persistid_cert.0.as_bytes()[SUBJECT_CN_RANGE] + .try_into() + .unwrap_lite(), + ), + Err(e) => { + ringbuf_entry!(Trace::NoSeedPersonalization(e)); panic!(); }, } From bf092d3ff4dbdfa4cd6a1f12b53eb74d2c9dc0b5 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 26 Sep 2024 01:05:59 +0000 Subject: [PATCH 13/16] Undo increased stack usage from cf2704fae6. Decorating `HandoffDataRegion::load_data` with the `#[inline(always])` attribute mitigates the increase in stack usage from cf2704fae6 --- app/lpc55xpresso/app.toml | 2 +- drv/lpc55-rng/src/config.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/lpc55xpresso/app.toml b/app/lpc55xpresso/app.toml index 980a14c43e..844d6edf51 100644 --- a/app/lpc55xpresso/app.toml +++ b/app/lpc55xpresso/app.toml @@ -115,7 +115,7 @@ name = "drv-lpc55-rng" priority = 3 uses = ["rng", "pmc"] start = true -stacksize = 4200 +stacksize = 4000 task-slots = ["syscon_driver"] extern-regions = ["dice_certs", "dice_rng"] diff --git a/drv/lpc55-rng/src/config.rs b/drv/lpc55-rng/src/config.rs index 420787f1b4..1a7176f0a5 100644 --- a/drv/lpc55-rng/src/config.rs +++ b/drv/lpc55-rng/src/config.rs @@ -32,6 +32,7 @@ impl HandoffDataRegion { /// Load a type implementing HandoffData (and others) from a config::DataRegion. /// Errors will be reported in the ringbuf and will return None. + #[inline(always)] pub fn load_data< T: for<'a> Deserialize<'a> + HandoffData + SerializedSize, >( From 11f962abae014e57369efc371118bde35ffd74d0 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 26 Sep 2024 01:14:10 +0000 Subject: [PATCH 14/16] Create inputs to `Lpc55Rng::new` in dedicated scope. This reduces stack usage by ~74 bytes. --- app/lpc55xpresso/app.toml | 2 +- drv/lpc55-rng/src/main.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/lpc55xpresso/app.toml b/app/lpc55xpresso/app.toml index 844d6edf51..d8cfe695f6 100644 --- a/app/lpc55xpresso/app.toml +++ b/app/lpc55xpresso/app.toml @@ -115,7 +115,7 @@ name = "drv-lpc55-rng" priority = 3 uses = ["rng", "pmc"] start = true -stacksize = 4000 +stacksize = 3904 task-slots = ["syscon_driver"] extern-regions = ["dice_certs", "dice_rng"] diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 79a4f37908..2878b8b2d5 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -272,16 +272,18 @@ pub fn get_seed_personalization() -> Option<[u8; SUBJECT_CN_LENGTH]> { #[export_name = "main"] fn main() -> ! { - let seed = get_dice_seed(); - let pid = get_seed_personalization(); - let peripherals = Peripherals::take().unwrap(); + let rng = { + let peripherals = Peripherals::take().unwrap(); - let rng = Lpc55Rng::new( - peripherals.PMC, - peripherals.RNG, - &Syscon::from(SYSCON.get_task_id()), - ); + Lpc55Rng::new( + peripherals.PMC, + peripherals.RNG, + &Syscon::from(SYSCON.get_task_id()), + ) + }; + let seed = get_dice_seed(); + let pid = get_seed_personalization(); let threshold = 0x100000; // 1 MiB let mut rng = Lpc55RngServer::new(seed.as_ref(), rng, pid.as_ref(), threshold) From de27347784874bced0ab60eb19e6e07fd1b251be Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Wed, 25 Sep 2024 18:25:54 +0000 Subject: [PATCH 15/16] Restructure the construction of the server. We do this to move the parameterization of the ReseedingRng to the top level / `main` and for simplification. Our stack usage after this commit is unchanged as we explicitly create the inputs to the `ReseedingRng` in a dedicated scope. --- drv/lpc55-rng/src/main.rs | 51 +++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 2878b8b2d5..8db45523c6 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -163,22 +163,17 @@ where } } -struct Lpc55RngServer(ReseedingRng); +struct Lpc55RngServer( + ReseedingRng, +); -impl Lpc55RngServer { - fn new( - seed: Option<&RngSeed>, - reseeder: Lpc55Rng, - pid: Option<&[u8; SUBJECT_CN_LENGTH]>, - threshold: usize, - ) -> Result { - Ok(Lpc55RngServer(ReseedingRng::new( - seed, reseeder, pid, threshold, - )?)) - } -} - -impl idl::InOrderRngImpl for Lpc55RngServer { +impl idl::InOrderRngImpl for Lpc55RngServer +where + T: SeedableRng + RngCore, + R: RngCore, + H: FixedOutputReset + Default + Digest, + [u8; 32]: From::OutputSize>>, +{ fn fill( &mut self, _: &userlib::RecvMessage, @@ -202,7 +197,13 @@ impl idl::InOrderRngImpl for Lpc55RngServer { } } -impl NotificationHandler for Lpc55RngServer { +impl NotificationHandler for Lpc55RngServer +where + T: SeedableRng + RngCore, + R: RngCore, + H: FixedOutputReset + Default + Digest, + [u8; 32]: From::OutputSize>>, +{ fn current_notification_mask(&self) -> u32 { // We don't use notifications, don't listen for any. 0 @@ -282,16 +283,20 @@ fn main() -> ! { ) }; - let seed = get_dice_seed(); - let pid = get_seed_personalization(); - let threshold = 0x100000; // 1 MiB - let mut rng = - Lpc55RngServer::new(seed.as_ref(), rng, pid.as_ref(), threshold) - .expect("Failed to create Lpc55RngServer"); + let reseeding_rng: ReseedingRng = { + let seed = get_dice_seed(); + let pid = get_seed_personalization(); + let threshold = 0x100000; // 1 MiB + + ReseedingRng::new(seed.as_ref(), rng, pid.as_ref(), threshold) + .unwrap_lite() + }; + + let mut server = Lpc55RngServer(reseeding_rng); let mut buffer = [0u8; idl::INCOMING_SIZE]; loop { - idol_runtime::dispatch(&mut buffer, &mut rng); + idol_runtime::dispatch(&mut buffer, &mut server); } } From b43295cc9a8bf2f7668a17916d6c86d7d40becfc Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 26 Sep 2024 18:49:30 +0000 Subject: [PATCH 16/16] Use `mutable-static` to create hash instance. This requires restructuring the ReseedingRng constructor a bit but it saves us 400 bytes of stack. --- Cargo.lock | 1 + app/lpc55xpresso/app.toml | 2 +- drv/lpc55-rng/Cargo.toml | 1 + drv/lpc55-rng/src/main.rs | 34 ++++++++++++++++++++++------------ 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04a13dfa5c..bf05728b33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1301,6 +1301,7 @@ dependencies = [ "lib-dice", "lib-lpc55-rng", "lpc55-pac", + "mutable-statics", "num-traits", "rand_chacha", "rand_core", diff --git a/app/lpc55xpresso/app.toml b/app/lpc55xpresso/app.toml index d8cfe695f6..4c9aecab75 100644 --- a/app/lpc55xpresso/app.toml +++ b/app/lpc55xpresso/app.toml @@ -115,7 +115,7 @@ name = "drv-lpc55-rng" priority = 3 uses = ["rng", "pmc"] start = true -stacksize = 3904 +stacksize = 3504 task-slots = ["syscon_driver"] extern-regions = ["dice_certs", "dice_rng"] diff --git a/drv/lpc55-rng/Cargo.toml b/drv/lpc55-rng/Cargo.toml index 6408cab2cf..34de7db84c 100644 --- a/drv/lpc55-rng/Cargo.toml +++ b/drv/lpc55-rng/Cargo.toml @@ -20,6 +20,7 @@ drv-lpc55-syscon-api = { path = "../lpc55-syscon-api" } drv-rng-api = { path = "../rng-api" } lib-dice.path = "../../lib/dice" lib-lpc55-rng.path = "../../lib/lpc55-rng" +mutable-statics.path = "../../lib/mutable-statics" ringbuf.path = "../../lib/ringbuf" stage0-handoff = { path = "../../lib/stage0-handoff", optional = true } userlib = { path = "../../sys/userlib", features = ["panic-messages"] } diff --git a/drv/lpc55-rng/src/main.rs b/drv/lpc55-rng/src/main.rs index 8db45523c6..6c26aa201e 100644 --- a/drv/lpc55-rng/src/main.rs +++ b/drv/lpc55-rng/src/main.rs @@ -16,6 +16,7 @@ use idol_runtime::{ClientError, NotificationHandler, RequestError}; use lib_dice::{persistid_cert_tmpl::SUBJECT_CN_LENGTH, RngSeed, SeedBuf}; use lib_lpc55_rng::Lpc55Rng; use lpc55_pac::Peripherals; +use mutable_statics::mutable_statics; use rand_chacha::ChaCha20Rng; use rand_core::{impls, Error, RngCore, SeedableRng}; use ringbuf::ringbuf; @@ -53,12 +54,12 @@ enum Trace { ringbuf!(Trace, 16, Trace::None); // 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, + mixer: &'static mut H, } impl ReseedingRng @@ -73,6 +74,7 @@ where mut reseeder: R, pid: Option<&[u8; SUBJECT_CN_LENGTH]>, threshold: usize, + mixer: &'static mut H, ) -> Result { let threshold = if threshold == 0 { usize::MAX @@ -80,21 +82,20 @@ where threshold }; - let mut mixer = H::default(); if let Some(seed) = seed { // mix platform unique seed derived by measured boot - Digest::update(&mut mixer, seed.as_bytes()); + Digest::update(mixer, seed.as_bytes()); } if let Some(pid) = pid { // mix in unique platform id - Digest::update(&mut mixer, pid); + Digest::update(mixer, pid); } // w/ 32 bytes from HRNG let mut buf = Zeroizing::new(T::Seed::default()); reseeder.try_fill_bytes(buf.as_mut())?; - Digest::update(&mut mixer, buf.as_ref()); + Digest::update(mixer, buf.as_ref()); // create initial instance of the SeedableRng from the seed let inner = T::from_seed(mixer.finalize_fixed_reset().into()); @@ -144,11 +145,11 @@ where // mix 32 bytes from current PRNG instance self.inner.try_fill_bytes(buf.as_mut())?; - Digest::update(&mut self.mixer, buf.as_mut()); + Digest::update(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()); + Digest::update(self.mixer, buf.as_mut()); // seed new RNG instance & reset mixer self.inner = @@ -163,7 +164,7 @@ where } } -struct Lpc55RngServer( +struct Lpc55RngServer( ReseedingRng, ); @@ -283,13 +284,22 @@ fn main() -> ! { ) }; + let mixer = mutable_statics! { + static mut MIXER: [Sha3_256; 1] = [Sha3_256::new; _]; + }; + let reseeding_rng: ReseedingRng = { let seed = get_dice_seed(); let pid = get_seed_personalization(); let threshold = 0x100000; // 1 MiB - - ReseedingRng::new(seed.as_ref(), rng, pid.as_ref(), threshold) - .unwrap_lite() + ReseedingRng::new( + seed.as_ref(), + rng, + pid.as_ref(), + threshold, + &mut mixer[0], + ) + .unwrap_lite() }; let mut server = Lpc55RngServer(reseeding_rng);