Skip to content

Commit c39cea0

Browse files
committed
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.
1 parent d18e856 commit c39cea0

File tree

5 files changed

+56
-17
lines changed

5 files changed

+56
-17
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lpc55xpresso/app.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,9 @@ task-slots = ["gpio_driver", "syscon_driver"]
113113
[tasks.rng_driver]
114114
name = "drv-lpc55-rng"
115115
priority = 3
116-
max-sizes = {flash = 16384, ram = 4096}
117116
uses = ["rng", "pmc"]
118117
start = true
119-
stacksize = 2200
118+
stacksize = 2600
120119
task-slots = ["syscon_driver"]
121120

122121
[tasks.pong]

app/rot-carrier/app.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,9 @@ pins = [
9999
[tasks.rng_driver]
100100
name = "drv-lpc55-rng"
101101
priority = 5
102-
max-sizes = {flash = 16384, ram = 4096}
103102
uses = ["rng", "pmc"]
104103
start = true
105-
stacksize = 2200
104+
stacksize = 2600
106105
task-slots = ["syscon_driver"]
107106

108107
[tasks.sprot]

drv/lpc55-rng/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ idol-runtime = { workspace = true }
99
num-traits = { workspace = true }
1010
rand_chacha = { workspace = true }
1111
rand_core = { workspace = true }
12+
sha3.workspace = true
1213
zerocopy = { workspace = true }
14+
zeroize.workspace = true
1315

1416
drv-lpc55-syscon-api = { path = "../lpc55-syscon-api" }
1517
drv-rng-api = { path = "../rng-api" }

drv/lpc55-rng/src/main.rs

+50-13
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,31 @@ use idol_runtime::{ClientError, NotificationHandler, RequestError};
1616
use lib_lpc55_rng::Lpc55Rng;
1717
use rand_chacha::ChaCha20Rng;
1818
use rand_core::{impls, Error, RngCore, SeedableRng};
19+
use sha3::{
20+
digest::crypto_common::{generic_array::GenericArray, OutputSizeUser},
21+
digest::FixedOutputReset,
22+
Digest, Sha3_256,
23+
};
1924
use userlib::*;
25+
use zeroize::Zeroizing;
2026

2127
task_slot!(SYSCON, syscon_driver);
2228

2329
// low-budget rand::rngs::adapter::ReseedingRng w/o fork stuff
24-
struct ReseedingRng<T: SeedableRng, R: RngCore> {
30+
struct ReseedingRng<T: SeedableRng, R: RngCore, H: Digest> {
2531
inner: T,
2632
reseeder: R,
2733
threshold: usize,
2834
bytes_until_reseed: usize,
35+
mixer: H,
2936
}
3037

31-
impl<T, R> ReseedingRng<T, R>
38+
impl<T, R, H> ReseedingRng<T, R, H>
3239
where
33-
T: SeedableRng,
40+
T: SeedableRng<Seed = [u8; 32]> + RngCore,
3441
R: RngCore,
42+
H: FixedOutputReset + Default + Digest,
43+
[u8; 32]: From<GenericArray<u8, <H as OutputSizeUser>::OutputSize>>,
3544
{
3645
fn new(mut reseeder: R, threshold: usize) -> Result<Self, Error> {
3746
let threshold = if threshold == 0 {
@@ -45,14 +54,17 @@ where
4554
reseeder,
4655
threshold,
4756
bytes_until_reseed: threshold,
57+
mixer: H::default(),
4858
})
4959
}
5060
}
5161

52-
impl<T, R> RngCore for ReseedingRng<T, R>
62+
impl<T, R, H> RngCore for ReseedingRng<T, R, H>
5363
where
54-
T: SeedableRng + RngCore,
64+
T: SeedableRng<Seed = [u8; 32]> + RngCore,
5565
R: RngCore,
66+
H: FixedOutputReset + Default + Digest,
67+
[u8; 32]: From<GenericArray<u8, <H as OutputSizeUser>::OutputSize>>,
5668
{
5769
fn next_u32(&mut self) -> u32 {
5870
impls::next_u32_via_fill(self)
@@ -65,18 +77,43 @@ where
6577
.expect("Failed to get entropy from RNG.")
6678
}
6779
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
68-
let num_bytes = dest.len();
69-
if num_bytes >= self.bytes_until_reseed || num_bytes >= self.threshold {
70-
self.inner = T::from_rng(&mut self.reseeder)?;
71-
self.bytes_until_reseed = self.threshold;
72-
} else {
73-
self.bytes_until_reseed -= num_bytes;
80+
let mut filled = 0;
81+
82+
while filled < dest.len() {
83+
if self.bytes_until_reseed > 0 {
84+
// fill dest as much as we can
85+
let len =
86+
cmp::min(dest.len() - filled, self.bytes_until_reseed);
87+
self.inner.try_fill_bytes(&mut dest[filled..filled + len])?;
88+
89+
filled += len;
90+
self.bytes_until_reseed -= len;
91+
} else {
92+
// create seed for next PRNG & reset mixer
93+
let mut buf = Zeroizing::new(T::Seed::default());
94+
95+
// mix 32 bytes from current PRNG instance
96+
self.inner.try_fill_bytes(buf.as_mut())?;
97+
Digest::update(&mut self.mixer, buf.as_mut());
98+
99+
// w/ 32 bytes from HRNG
100+
self.reseeder.try_fill_bytes(buf.as_mut())?;
101+
Digest::update(&mut self.mixer, buf.as_mut());
102+
103+
// seed new RNG instance & reset mixer
104+
self.inner =
105+
T::from_seed(self.mixer.finalize_fixed_reset().into());
106+
107+
// reset reseed countdown
108+
self.bytes_until_reseed = self.threshold;
109+
}
74110
}
75-
self.inner.try_fill_bytes(dest)
111+
112+
Ok(())
76113
}
77114
}
78115

79-
struct Lpc55RngServer(ReseedingRng<ChaCha20Rng, Lpc55Rng>);
116+
struct Lpc55RngServer(ReseedingRng<ChaCha20Rng, Lpc55Rng, Sha3_256>);
80117

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

0 commit comments

Comments
 (0)