Skip to content

Commit

Permalink
lpc55-rot-startup: Use a better method to generate an FWID.
Browse files Browse the repository at this point in the history
The current implementation captures all of the RoT software. It does not
however include other pages in the RoT image flash range. Expanding our
definition for the RoT FWID as not only the expected image, but also any
other programmed flash pages in the range of possible pages mitigates a
potential persistence mechanism for an attacker.

This commit:
- adds a new environment variable HUBRIS_FLASH_OUTPUT that holds all
  config::Output structures for the flash regions assigned to the image
  being built
- generates a Range for each flash region in HUBRIS_FLASH_OUTPUTS for
  use by the lpc55-rot-startup crate
- uses the A & B Ranges to walk through all possible flash pages for the
  given image including each page that's been programmed in the FWID
  • Loading branch information
flihp committed Aug 25, 2023
1 parent 7a1bc24 commit cc131b7
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 31 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion app/lpc55xpresso/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ image-names = ["a", "b"]
[kernel]
name = "lpc55xpresso"
features = ["dump", "dice-self"]
requires = {flash = 54016, ram = 4096}
requires = {flash = 54116, ram = 4096}

[caboose]
region = "flash"
Expand Down
7 changes: 7 additions & 0 deletions build/xtask/src/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1005,12 +1005,19 @@ fn build_kernel(

let image_id = image_id.finish();

let flash_outputs = if let Some(o) = cfg.toml.outputs.get("flash") {
ron::ser::to_string(o)?
} else {
bail!("no 'flash' output regions defined in config toml");
};

// Build the kernel.
let build_config = cfg.toml.kernel_build_config(
cfg.verbose,
&[
("HUBRIS_KCONFIG", &kconfig),
("HUBRIS_IMAGE_ID", &format!("{}", image_id)),
("HUBRIS_FLASH_OUTPUTS", &flash_outputs),
],
Some(&cfg.sysroot),
);
Expand Down
1 change: 1 addition & 0 deletions lib/lpc55-rot-startup/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ unwrap-lite = { path = "../unwrap-lite" }

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

Expand Down
50 changes: 38 additions & 12 deletions lib/lpc55-rot-startup/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,37 @@
// 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;
use std::fs::File;
use std::io::Write;

fn main() -> Result<(), Box<dyn std::error::Error>> {
let out = build_util::out_dir();
gen_image_flash_range(&out)?;

#[cfg(feature = "dice-mfg")]
{
let out = build_util::out_dir();
gen_memory_range(&out)?;
}

Ok(())
}

#[derive(Deserialize, Debug)]
struct Region {
pub name: String,
pub address: u32,
pub size: u32,
}

#[cfg(feature = "dice-mfg")]
fn gen_memory_range(
out_dir: &std::path::PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
use serde::Deserialize;
use std::fs::File;
use std::io::Write;

#[derive(Deserialize, Debug)]
struct DiceMfgRegion {
pub address: u32,
pub size: u32,
}

let toml = build_util::env_var("HUBRIS_DICE_MFG")?;
let region: DiceMfgRegion = toml::from_str(&toml)?;
let region: Region = toml::from_str(&toml)?;
let mut dice_mfg = File::create(out_dir.join("dice-mfg.rs")).unwrap();

writeln!(
dice_mfg,
"use core::ops::Range;\n\n\
Expand All @@ -39,3 +43,25 @@ fn gen_memory_range(

Ok(())
}

fn gen_image_flash_range(
out_dir: &std::path::PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
let outputs = build_util::env_var("HUBRIS_FLASH_OUTPUTS")?;
let outputs: Vec<Region> = ron::de::from_str(&outputs)?;

let mut cfg = File::create(out_dir.join("config.rs")).unwrap();

writeln!(cfg, "use core::ops::Range;\n")?;
for region in outputs {
writeln!(
cfg,
"#[allow(dead_code)]\npub const FLASH_{}: Range<u32> = {:#x}..{:#x};",
region.name.replace("-", "_").to_uppercase(),
region.address,
region.address + region.size
)?;
}

Ok(())
}
53 changes: 35 additions & 18 deletions lib/lpc55-rot-startup/src/images.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use unwrap_lite::UnwrapLite;
pub fn get_image_b() -> Option<Image> {
let imageb = unsafe { &__IMAGE_B_BASE };

let img = Image(imageb);
let img = Image {
flash: FLASH_B,
vector: imageb,
};

if img.validate() {
Some(img)
Expand All @@ -23,7 +26,10 @@ pub fn get_image_b() -> Option<Image> {
pub fn get_image_a() -> Option<Image> {
let imagea = unsafe { &__IMAGE_A_BASE };

let img = Image(imagea);
let img = Image {
flash: FLASH_A,
vector: imagea,
};

if img.validate() {
Some(img)
Expand All @@ -49,18 +55,21 @@ extern "C" {
// to do the u32 change everywhere
const PAGE_SIZE: u32 = FLASH_PAGE_SIZE as u32;

pub struct Image(&'static ImageVectors);
pub struct Image {
flash: Range<u32>,
vector: &'static ImageVectors,
}

pub fn image_details(img: Image) -> RotImageDetails {
RotImageDetails {
digest: img.get_hash(),
digest: img.get_fwid(),
version: img.get_image_version(),
}
}

impl Image {
fn get_img_start(&self) -> u32 {
self.0 as *const ImageVectors as u32
self.vector as *const ImageVectors as u32
}

fn get_img_size(&self) -> Option<usize> {
Expand Down Expand Up @@ -120,20 +129,26 @@ impl Image {
return true;
}

// TODO: This is a particularly naive way to calculate the hash of the
// hubris image: https://github.com/oxidecomputer/hubris/issues/736
pub fn get_hash(&self) -> [u8; 32] {
let img_ptr = self.get_img_start() as *const u8;
// The MPU requires 32 byte alignment and so the compiler pads the
// image accordingly. The length field from the image header does not
// (and should not) account for this padding so we must do that here.
let img_size = self.get_img_size().unwrap_lite() + 31 & !31;
let image = unsafe { core::slice::from_raw_parts(img_ptr, img_size) };

let mut img_hash = Sha3_256::new();
img_hash.update(image);
pub fn get_fwid(&self) -> [u8; 32] {
let mut hash = Sha3_256::new();

for start in self.flash.clone().step_by(FLASH_PAGE_SIZE) {
if lpc55_romapi::validate_programmed(start, PAGE_SIZE) {
// SAFETY: The addresses used in this unsafe code are all
// generated by build.rs from data in the build environment.
// The safety of this code is an extension of our trust in
// the build.
let page = unsafe {
core::slice::from_raw_parts(
start as *const u8,
FLASH_PAGE_SIZE,
)
};
hash.update(page);
}
}

img_hash.finalize().try_into().unwrap_lite()
hash.finalize().try_into().unwrap_lite()
}

pub fn get_image_version(&self) -> ImageVersion {
Expand Down Expand Up @@ -163,3 +178,5 @@ impl Image {
self.pointer_range().contains(&address)
}
}

include!(concat!(env!("OUT_DIR"), "/config.rs"));

0 comments on commit cc131b7

Please sign in to comment.