Skip to content

Commit

Permalink
lpc55-rng: Include 32 bytes from the last PRNG instance when reseeding.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
flihp committed Sep 17, 2024
1 parent f76e271 commit c16168f
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 17 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

3 changes: 1 addition & 2 deletions app/lpc55xpresso/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
3 changes: 1 addition & 2 deletions app/rot-carrier/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 2 additions & 0 deletions drv/lpc55-rng/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
63 changes: 50 additions & 13 deletions drv/lpc55-rng/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: SeedableRng, R: RngCore> {
struct ReseedingRng<T: SeedableRng, R: RngCore, H: Digest> {
inner: T,
reseeder: R,
threshold: usize,
bytes_until_reseed: usize,
mixer: H,
}

impl<T, R> ReseedingRng<T, R>
impl<T, R, H> ReseedingRng<T, R, H>
where
T: SeedableRng,
T: SeedableRng<Seed = [u8; 32]> + RngCore,
R: RngCore,
H: FixedOutputReset + Default + Digest,
[u8; 32]: From<GenericArray<u8, <H as OutputSizeUser>::OutputSize>>,
{
fn new(mut reseeder: R, threshold: usize) -> Result<Self, Error> {
let threshold = if threshold == 0 {
Expand All @@ -45,14 +54,17 @@ where
reseeder,
threshold,
bytes_until_reseed: threshold,
mixer: H::default(),
})
}
}

impl<T, R> RngCore for ReseedingRng<T, R>
impl<T, R, H> RngCore for ReseedingRng<T, R, H>
where
T: SeedableRng + RngCore,
T: SeedableRng<Seed = [u8; 32]> + RngCore,
R: RngCore,
H: FixedOutputReset + Default + Digest,
[u8; 32]: From<GenericArray<u8, <H as OutputSizeUser>::OutputSize>>,
{
fn next_u32(&mut self) -> u32 {
impls::next_u32_via_fill(self)
Expand All @@ -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<ChaCha20Rng, Lpc55Rng>);
struct Lpc55RngServer(ReseedingRng<ChaCha20Rng, Lpc55Rng, Sha3_256>);

impl Lpc55RngServer {
fn new(reseeder: Lpc55Rng, threshold: usize) -> Result<Self, Error> {
Expand Down

0 comments on commit c16168f

Please sign in to comment.