Skip to content

Commit 2d0a30b

Browse files
committed
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.
1 parent 83f79ce commit 2d0a30b

File tree

8 files changed

+221
-15
lines changed

8 files changed

+221
-15
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lpc55xpresso/app.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,13 @@ task-slots = ["gpio_driver", "syscon_driver"]
112112

113113
[tasks.rng_driver]
114114
name = "drv-lpc55-rng"
115+
features = ["dice-seed"]
115116
priority = 3
116117
uses = ["rng", "pmc"]
117118
start = true
118-
stacksize = 2600
119+
stacksize = 2704
119120
task-slots = ["syscon_driver"]
121+
extern-regions = ["dice_rng"]
120122

121123
[tasks.pong]
122124
name = "task-pong"

app/rot-carrier/app.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ name = "drv-lpc55-rng"
101101
priority = 5
102102
uses = ["rng", "pmc"]
103103
start = true
104-
stacksize = 2600
104+
stacksize = 2704
105105
task-slots = ["syscon_driver"]
106+
extern-regions = ["dice_rng"]
106107

107108
[tasks.sprot]
108109
name = "drv-lpc55-sprot-server"

chips/lpc55/memory.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,19 @@ size = 0x800
156156
read = true
157157
write = true
158158
execute = false
159+
160+
[[dice_rng]]
161+
name = "a"
162+
address =0x40101a00
163+
size = 0x100
164+
read = true
165+
write = true
166+
execute = false
167+
168+
[[dice_rng]]
169+
name = "b"
170+
address =0x40101a00
171+
size = 0x100
172+
read = true
173+
write = true
174+
execute = false

drv/lpc55-rng/Cargo.toml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,33 @@ edition = "2021"
55

66
[dependencies]
77
cfg-if = { workspace = true }
8+
hubpack.workspace = true
89
idol-runtime = { workspace = true }
910
num-traits = { workspace = true }
1011
rand_chacha = { workspace = true }
1112
rand_core = { workspace = true }
13+
serde.workspace = true
1214
sha3.workspace = true
1315
zerocopy = { workspace = true }
1416
zeroize.workspace = true
1517

1618
drv-lpc55-syscon-api = { path = "../lpc55-syscon-api" }
1719
drv-rng-api = { path = "../rng-api" }
20+
lib-dice.path = "../../lib/dice"
1821
lib-lpc55-rng.path = "../../lib/lpc55-rng"
22+
ringbuf.path = "../../lib/ringbuf"
23+
stage0-handoff = { path = "../../lib/stage0-handoff", optional = true }
1924
userlib = { path = "../../sys/userlib", features = ["panic-messages"] }
2025

2126
[build-dependencies]
22-
idol = { workspace = true }
27+
anyhow.workspace = true
28+
build-util.path = "../../build/util"
29+
cfg-if.workspace = true
30+
idol.workspace = true
31+
serde.workspace = true
2332

2433
[features]
34+
dice-seed = ["stage0-handoff"]
2535
no-ipc-counters = ["idol/no-counters"]
2636

2737
# This section is here to discourage RLS/rust-analyzer from doing test builds,

drv/lpc55-rng/build.rs

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,62 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
5+
use anyhow::{anyhow, Result};
6+
use idol::{server::ServerStyle, CounterSettings};
7+
8+
cfg_if::cfg_if! {
9+
if #[cfg(feature = "dice-seed")] {
10+
mod config {
11+
include!("src/config.rs");
12+
}
13+
use anyhow::Context;
14+
use config::DataRegion;
15+
use std::{fs::File, io::Write};
16+
17+
const CFG_SRC: &str = "rng-config.rs";
18+
}
19+
}
20+
21+
#[cfg(feature = "dice-seed")]
22+
fn extern_regions_to_cfg(path: &str) -> Result<()> {
23+
let out_dir = build_util::out_dir();
24+
let dest_path = out_dir.join(path);
25+
let mut out =
26+
File::create(dest_path).context(format!("creating {}", path))?;
27+
28+
let data_regions = build_util::task_extern_regions::<DataRegion>()?;
29+
if data_regions.is_empty() {
30+
return Err(anyhow!("no data regions found"));
31+
}
32+
33+
writeln!(out, "use crate::config::DataRegion;\n\n")?;
34+
35+
let region = data_regions
36+
.get("dice_rng")
37+
.ok_or_else(|| anyhow::anyhow!("dice_certs data region not found"))?;
38+
39+
Ok(writeln!(
40+
out,
41+
r##"pub const DICE_RNG: DataRegion = DataRegion {{
42+
address: {:#x},
43+
size: {:#x},
44+
}};"##,
45+
region.address, region.size
46+
)?)
47+
}
48+
49+
fn main() -> Result<()> {
650
idol::Generator::new()
7-
.with_counters(
8-
idol::CounterSettings::default().with_server_counters(false),
9-
)
51+
.with_counters(CounterSettings::default().with_server_counters(false))
1052
.build_server_support(
1153
"../../idl/rng.idol",
1254
"server_stub.rs",
13-
idol::server::ServerStyle::InOrder,
14-
)?;
55+
ServerStyle::InOrder,
56+
)
57+
.map_err(|e| anyhow!(e))?;
58+
59+
#[cfg(feature = "dice-seed")]
60+
extern_regions_to_cfg(CFG_SRC)?;
61+
1562
Ok(())
1663
}

drv/lpc55-rng/src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
#[derive(serde::Deserialize, Default, Debug)]
6+
#[serde(rename_all = "kebab-case")]
7+
#[cfg(feature = "dice-seed")]
8+
pub struct DataRegion {
9+
pub address: usize,
10+
pub size: usize,
11+
}

drv/lpc55-rng/src/main.rs

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@
99
#![no_std]
1010
#![no_main]
1111

12+
mod config;
13+
1214
use core::{cmp, usize};
1315
use drv_lpc55_syscon_api::Syscon;
1416
use drv_rng_api::RngError;
1517
use idol_runtime::{ClientError, NotificationHandler, RequestError};
18+
use lib_dice::{RngSeed, SeedBuf};
1619
use lib_lpc55_rng::Lpc55Rng;
1720
use rand_chacha::ChaCha20Rng;
1821
use rand_core::{impls, Error, RngCore, SeedableRng};
22+
use ringbuf::ringbuf;
1923
use sha3::{
2024
digest::crypto_common::{generic_array::GenericArray, OutputSizeUser},
2125
digest::FixedOutputReset,
@@ -24,8 +28,39 @@ use sha3::{
2428
use userlib::*;
2529
use zeroize::Zeroizing;
2630

31+
cfg_if::cfg_if! {
32+
if #[cfg(any(feature = "dice-seed"))] {
33+
use config::DataRegion;
34+
use hubpack::SerializedSize;
35+
use lib_dice::RngData;
36+
use ringbuf::ringbuf_entry;
37+
use serde::Deserialize;
38+
use stage0_handoff::{HandoffData, HandoffDataLoadError};
39+
40+
// This file is generated by the crate build.rs. It contains instances
41+
// of config::DataRegion structs describing regions of memory
42+
// configured & exposed to this task by the hubris build.
43+
mod build {
44+
include!(concat!(env!("OUT_DIR"), "/rng-config.rs"));
45+
}
46+
47+
use build::DICE_RNG;
48+
}
49+
}
50+
2751
task_slot!(SYSCON, syscon_driver);
2852

53+
#[derive(Copy, Clone, PartialEq)]
54+
enum Trace {
55+
#[cfg(feature = "dice-seed")]
56+
NoDiceSeed,
57+
#[cfg(feature = "dice-seed")]
58+
HandoffError(HandoffDataLoadError),
59+
None,
60+
}
61+
62+
ringbuf!(Trace, 16, Trace::None);
63+
2964
// low-budget rand::rngs::adapter::ReseedingRng w/o fork stuff
3065
struct ReseedingRng<T: SeedableRng, R: RngCore, H: Digest> {
3166
inner: T,
@@ -42,19 +77,37 @@ where
4277
H: FixedOutputReset + Default + Digest,
4378
[u8; 32]: From<GenericArray<u8, <H as OutputSizeUser>::OutputSize>>,
4479
{
45-
fn new(mut reseeder: R, threshold: usize) -> Result<Self, Error> {
80+
fn new(
81+
seed: Option<&RngSeed>,
82+
mut reseeder: R,
83+
threshold: usize,
84+
) -> Result<Self, Error> {
4685
let threshold = if threshold == 0 {
4786
usize::MAX
4887
} else {
4988
threshold
5089
};
5190

91+
let mut mixer = H::default();
92+
if let Some(seed) = seed {
93+
// mix platform unique seed drived by measured boot
94+
Digest::update(&mut mixer, seed.as_bytes());
95+
}
96+
97+
// w/ 32 bytes from HRNG
98+
let mut buf = Zeroizing::new(T::Seed::default());
99+
reseeder.try_fill_bytes(buf.as_mut())?;
100+
Digest::update(&mut mixer, buf.as_ref());
101+
102+
// create initial instance of the SeedableRng from the seed
103+
let inner = T::from_seed(mixer.finalize_fixed_reset().into());
104+
52105
Ok(ReseedingRng {
53-
inner: T::from_rng(&mut reseeder)?,
106+
inner,
54107
reseeder,
55108
threshold,
56109
bytes_until_reseed: threshold,
57-
mixer: H::default(),
110+
mixer,
58111
})
59112
}
60113
}
@@ -116,8 +169,14 @@ where
116169
struct Lpc55RngServer(ReseedingRng<ChaCha20Rng, Lpc55Rng, Sha3_256>);
117170

118171
impl Lpc55RngServer {
119-
fn new(reseeder: Lpc55Rng, threshold: usize) -> Result<Self, Error> {
120-
Ok(Lpc55RngServer(ReseedingRng::new(reseeder, threshold)?))
172+
fn new(
173+
seed: Option<&RngSeed>,
174+
reseeder: Lpc55Rng,
175+
threshold: usize,
176+
) -> Result<Self, Error> {
177+
Ok(Lpc55RngServer(ReseedingRng::new(
178+
seed, reseeder, threshold,
179+
)?))
121180
}
122181
}
123182

@@ -156,12 +215,65 @@ impl NotificationHandler for Lpc55RngServer {
156215
}
157216
}
158217

218+
/// Load a type implementing HandoffData (and others) from a config::DataRegion.
219+
/// Errors will be reported in the ringbuf and will return None.
220+
#[cfg(feature = "dice-seed")]
221+
fn load_data_from_region<
222+
T: for<'a> Deserialize<'a> + HandoffData + SerializedSize,
223+
>(
224+
region: &DataRegion,
225+
) -> Option<T> {
226+
use core::slice;
227+
228+
// Safety: This memory is setup by code executed before hubris and
229+
// exposed using the kernel `extern-regions` mechanism. The safety of
230+
// this code is an extension of our trust in the hubris pre-main, kernel,
231+
// and build process.
232+
let data = unsafe {
233+
slice::from_raw_parts(region.address as *mut u8, region.size as usize)
234+
};
235+
236+
// this can be replaced w/ .ok() if we get rid of the ringbuf entry
237+
match T::load_from_addr(data) {
238+
Ok(d) => Some(d),
239+
Err(e) => {
240+
ringbuf_entry!(Trace::HandoffError(e));
241+
None
242+
}
243+
}
244+
}
245+
246+
/// Get the seed derived by the lpc55-rot-startup and passed to us through
247+
/// the stage0-handoff memory region.
248+
///
249+
/// If use of DICE seed in seeding the PRNG is not enabled then this function
250+
/// will just return None. Otherwise it will attempt to get the seed from the
251+
/// dice-rng region of the stage0-handoff memory. If it's not able to get
252+
/// the seed it will put an entry in the ringbuf and panic.
253+
pub fn get_dice_seed() -> Option<RngSeed> {
254+
cfg_if::cfg_if! {
255+
if #[cfg(feature = "dice-seed")] {
256+
match load_data_from_region::<RngData>(&DICE_RNG) {
257+
Some(rng_data) => Some(rng_data.seed),
258+
_ => {
259+
ringbuf_entry!(Trace::NoDiceSeed);
260+
panic!();
261+
},
262+
}
263+
} else {
264+
None
265+
}
266+
}
267+
}
268+
159269
#[export_name = "main"]
160270
fn main() -> ! {
271+
let seed = get_dice_seed();
272+
161273
let rng = Lpc55Rng::new(&Syscon::from(SYSCON.get_task_id()));
162274

163275
let threshold = 0x100000; // 1 MiB
164-
let mut rng = Lpc55RngServer::new(rng, threshold)
276+
let mut rng = Lpc55RngServer::new(seed.as_ref(), rng, threshold)
165277
.expect("Failed to create Lpc55RngServer");
166278
let mut buffer = [0u8; idl::INCOMING_SIZE];
167279

0 commit comments

Comments
 (0)