Skip to content

Commit

Permalink
lpc55-rng: Include DICE derived seed in initial PRNG seed.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
flihp committed Sep 17, 2024
1 parent c16168f commit 39767db
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 15 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion app/lpc55xpresso/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,13 @@ task-slots = ["gpio_driver", "syscon_driver"]

[tasks.rng_driver]
name = "drv-lpc55-rng"
features = ["dice-seed"]
priority = 3
uses = ["rng", "pmc"]
start = true
stacksize = 2600
stacksize = 2704
task-slots = ["syscon_driver"]
extern-regions = ["dice_rng"]

[tasks.pong]
name = "task-pong"
Expand Down
3 changes: 2 additions & 1 deletion app/rot-carrier/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 16 additions & 0 deletions chips/lpc55/memory.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 11 additions & 1 deletion drv/lpc55-rng/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,33 @@ 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]
dice-seed = ["stage0-handoff"]
no-ipc-counters = ["idol/no-counters"]

# This section is here to discourage RLS/rust-analyzer from doing test builds,
Expand Down
59 changes: 53 additions & 6 deletions drv/lpc55-rng/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn std::error::Error + Send + Sync>> {
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::<DataRegion>()?;
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(())
}
11 changes: 11 additions & 0 deletions drv/lpc55-rng/src/config.rs
Original file line number Diff line number Diff line change
@@ -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,
}
119 changes: 113 additions & 6 deletions drv/lpc55-rng/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<T: SeedableRng, R: RngCore, H: Digest> {
inner: T,
Expand All @@ -42,19 +77,37 @@ where
H: FixedOutputReset + Default + Digest,
[u8; 32]: From<GenericArray<u8, <H as OutputSizeUser>::OutputSize>>,
{
fn new(mut reseeder: R, threshold: usize) -> Result<Self, Error> {
fn new(
seed: Option<&RngSeed>,
mut reseeder: R,
threshold: usize,
) -> Result<Self, Error> {
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,
})
}
}
Expand Down Expand Up @@ -116,8 +169,14 @@ where
struct Lpc55RngServer(ReseedingRng<ChaCha20Rng, Lpc55Rng, Sha3_256>);

impl Lpc55RngServer {
fn new(reseeder: Lpc55Rng, threshold: usize) -> Result<Self, Error> {
Ok(Lpc55RngServer(ReseedingRng::new(reseeder, threshold)?))
fn new(
seed: Option<&RngSeed>,
reseeder: Lpc55Rng,
threshold: usize,
) -> Result<Self, Error> {
Ok(Lpc55RngServer(ReseedingRng::new(
seed, reseeder, threshold,
)?))
}
}

Expand Down Expand Up @@ -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<T> {
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<RngSeed> {
cfg_if::cfg_if! {
if #[cfg(feature = "dice-seed")] {
match load_data_from_region::<RngData>(&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];

Expand Down

0 comments on commit 39767db

Please sign in to comment.