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))
```
  • Loading branch information
flihp committed Jul 11, 2024
1 parent c39cea0 commit cea3967
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 16 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.

3 changes: 2 additions & 1 deletion app/lpc55xpresso/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ name = "drv-lpc55-rng"
priority = 3
uses = ["rng", "pmc"]
start = true
stacksize = 2600
stacksize = 3000
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 = 3000
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
10 changes: 9 additions & 1 deletion drv/lpc55-rng/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@ 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"
userlib = { path = "../../sys/userlib", features = ["panic-messages"] }

[build-dependencies]
idol = { workspace = true }
anyhow.workspace = true
build-util.path = "../../build/util"
idol.workspace = true
serde.workspace = true

[features]
no-ipc-counters = ["idol/no-counters"]
Expand Down
47 changes: 41 additions & 6 deletions drv/lpc55-rng/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,50 @@
// 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, Context, Result};
use idol::{server::ServerStyle, CounterSettings};
use std::{fs::File, io::Write};

mod config {
include!("src/config.rs");
}

use config::DataRegion;

const CFG_SRC: &str = "rng-config.rs";

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))?;

let out_dir = build_util::out_dir();
let dest_path = out_dir.join(CFG_SRC);
let mut out =
File::create(dest_path).context(format!("creating {}", CFG_SRC))?;

let data_regions = build_util::task_extern_regions::<DataRegion>()?;
if data_regions.is_empty() {
return Err(anyhow!("no data regions found"));
}

let region = data_regions
.get("dice_rng")
.ok_or_else(|| anyhow!("dice_rng data region not found"))?;
writeln!(
out,
r##"use crate::config::DataRegion;
pub const RNG_DATA: DataRegion = DataRegion {{
address: {:#x},
size: {:#x},
}};"##,
region.address, region.size
)?;

Ok(())
}
12 changes: 12 additions & 0 deletions drv/lpc55-rng/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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/.

use serde::Deserialize;

#[derive(Deserialize, Default, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct DataRegion {
pub address: u32,
pub size: u32,
}
90 changes: 83 additions & 7 deletions drv/lpc55-rng/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,48 @@
#![no_std]
#![no_main]

use core::{cmp, usize};
mod config;

use config::DataRegion;
use core::{cmp, slice, usize};
use drv_lpc55_syscon_api::Syscon;
use drv_rng_api::RngError;
use hubpack::SerializedSize;
use idol_runtime::{ClientError, NotificationHandler, RequestError};
use lib_dice::{RngData, RngSeed, SeedBuf};
use lib_lpc55_rng::Lpc55Rng;
use rand_chacha::ChaCha20Rng;
use rand_core::{impls, Error, RngCore, SeedableRng};
use ringbuf::{ringbuf, ringbuf_entry};
use serde::Deserialize;
use sha3::{
digest::crypto_common::{generic_array::GenericArray, OutputSizeUser},
digest::FixedOutputReset,
Digest, Sha3_256,
};
use stage0_handoff::{HandoffData, HandoffDataLoadError};
use userlib::*;
use zeroize::Zeroizing;

// 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::RNG_DATA;

task_slot!(SYSCON, syscon_driver);

#[derive(Copy, Clone, PartialEq)]
enum Trace {
HandoffError(HandoffDataLoadError),
None,
}

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 +67,35 @@ 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: RngSeed,
mut reseeder: R,
threshold: usize,
) -> Result<Self, Error> {
let threshold = if threshold == 0 {
usize::MAX
} else {
threshold
};

let mut mixer = H::default();
// mix platform unique seed drived 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 +157,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: RngSeed,
reseeder: Lpc55Rng,
threshold: usize,
) -> Result<Self, Error> {
Ok(Lpc55RngServer(ReseedingRng::new(
seed, reseeder, threshold,
)?))
}
}

Expand Down Expand Up @@ -156,12 +203,41 @@ 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 in None.
fn load_data_from_region<
T: for<'a> Deserialize<'a> + HandoffData + SerializedSize,
>(
region: &DataRegion,
) -> Option<T> {
// 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 kernel / build.
let data = unsafe {
slice::from_raw_parts(region.address as *mut u8, region.size as usize)
};

// this can be replaced w/ .ok() if we get rid of the ringbuf entry
match T::load_from_addr(data) {
Ok(d) => Some(d),
Err(e) => {
ringbuf_entry!(Trace::HandoffError(e));
None
}
}
}

#[export_name = "main"]
fn main() -> ! {
let seed = {
load_data_from_region::<RngData>(&RNG_DATA)
.unwrap_lite()
.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, rng, threshold)
.expect("Failed to create Lpc55RngServer");
let mut buffer = [0u8; idl::INCOMING_SIZE];

Expand Down

0 comments on commit cea3967

Please sign in to comment.