Skip to content

Commit f08ebad

Browse files
committed
stage0: Use the dice library to implement minimal self-signed credentials.
This commit collects the DICE CDI from the NXP ROM, creates an ed25519 key pair from it (effectively the DeviceId) and uses this key to create a self-signed DeviceId cert. The hash of the hubris image that will be launched by stage0 is then hashed and the output used to generate the Alias credentials (used for attestation). Finally the keying material used to create the Alias key, and the cert chain from the Alias back to the DeviceId are written to memory for use in an attestation exchange by a future hubris task. If run on an lpc55 with DICE disabled (the default config) none of the DICE artifacts will be created and hubris will be booted as usual.
1 parent 1980749 commit f08ebad

File tree

6 files changed

+126
-7
lines changed

6 files changed

+126
-7
lines changed

Cargo.lock

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

app/lpc55xpresso/stage0.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ name = "lpc55xpresso"
22
target = "thumbv8m.main-none-eabihf"
33
board = "lpcxpresso55s69"
44
chip = "../../chips/lpc55"
5-
stacksize = 1024
65
secure-separation = true
76
image-names = ["stage0"]
87
external-images = ["a", "b"]
98

109
[kernel]
1110
name = "stage0"
12-
requires = {flash = 0x4000, ram = 4096}
13-
features = ["tz_support"]
11+
requires = {flash = 0x7000, ram = 8192}
12+
features = ["tz_support", "dice"]
13+
stacksize = 8000
1414

1515
[tasks.idle]
1616
name = "task-idle"

stage0/Cargo.toml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,34 @@ version = "0.1.0"
44
edition = "2018"
55

66
[features]
7+
dice = ["dice_crate", "digest", "salty", "sha3", "unwrap-lite"]
78
tz_support = []
89

910
[dependencies]
1011
cortex-m = {version = "0.7", features = ["inline-asm"]}
1112
cortex-m-rt = "0.6.12"
13+
digest = { version = "0.10", optional = true }
1214
panic-semihosting = "0.5.3"
1315
lpc55_romapi = { path = "../drv/lpc55-romapi" }
1416
panic-halt = "0.2.0"
1517
lpc55-pac = {version = "0.3.0", features = ["rt"]}
1618
ecdsa = { version = "0.12.4", default-features = false, features = ["der"] }
1719
p256 = { version = "0.9.0", default-features = false, features = ["ecdsa", "ecdsa-core"] }
1820
hmac = { version = "0.10.1", default-features = false }
19-
sha2 = { version = "0.9.2", default-features = false }
21+
salty = { version = "0.2", optional = true }
22+
sha3 = { version = "0.10", default-features = false, optional = true }
2023
zerocopy = "0.6.1"
21-
cfg-if = "1"
2224
abi = { path = "../sys/abi" }
25+
unwrap-lite = { path = "../lib/unwrap-lite", optional = true }
26+
nb = "1"
27+
28+
# features & deps can't have the same name, using this method from:
29+
# https://github.com/RustCrypto/RSA/pull/41/files
30+
[dependencies.dice_crate]
31+
package = "dice"
32+
path = "../lib/dice"
33+
default-features = false
34+
optional = true
2335

2436
[[bin]]
2537
name = "stage0"

stage0/src/dice.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
use crate::image_header::Image;
6+
use core::str::FromStr;
7+
use dice_crate::{
8+
AliasCert, AliasData, AliasOkm, Cdi, CdiL1, DeviceIdOkm, DeviceIdSelfCert,
9+
Handoff, SeedBuf, SerialNumber,
10+
};
11+
use lpc55_pac::Peripherals;
12+
use salty::signature::Keypair;
13+
use sha3::{Digest, Sha3_256};
14+
use unwrap_lite::UnwrapLite;
15+
16+
fn get_deviceid_keypair(cdi: &Cdi) -> Keypair {
17+
let devid_okm = DeviceIdOkm::from_cdi(cdi);
18+
19+
Keypair::from(devid_okm.as_bytes())
20+
}
21+
22+
// TODO: get the legit SN from somewhere
23+
// https://github.com/oxidecomputer/hubris/issues/734
24+
fn get_serial_number() -> SerialNumber {
25+
SerialNumber::from_str("0123456789ab").expect("SerialNumber::from_str")
26+
}
27+
28+
pub fn run(image: &Image) {
29+
// Turn on the memory we're using to handoff DICE artifacts and create
30+
// type to interact with said memory. We turn this on unconditionally
31+
// if DICE is enabled so that hubris tasks will always get valid memory
32+
// even if it's all 0's.
33+
let syscon = Peripherals::take().unwrap_lite().SYSCON;
34+
let handoff = Handoff::turn_on(&syscon);
35+
36+
let cdi = match Cdi::from_reg() {
37+
Some(cdi) => cdi,
38+
None => return,
39+
};
40+
41+
let dname_sn = get_serial_number();
42+
let deviceid_keypair = get_deviceid_keypair(&cdi);
43+
let mut cert_sn = 0;
44+
45+
let deviceid_cert =
46+
DeviceIdSelfCert::new(cert_sn, &dname_sn, &deviceid_keypair);
47+
cert_sn += 1;
48+
49+
// Collect hash(es) of TCB. The first TCB Component Identifier (TCI)
50+
// calculated is the Hubris image. The DICE specs call this collection
51+
// of TCIs the FWID. This hash is stored in keeys certified by the
52+
// DeviceId. This hash should be 'updated' with relevant configuration
53+
// and code as FWID for Hubris becomes known.
54+
// TODO: This is a particularly naive way to calculate the FWID:
55+
// https://github.com/oxidecomputer/hubris/issues/736
56+
let mut fwid = Sha3_256::new();
57+
fwid.update(image.as_bytes());
58+
let fwid = fwid.finalize();
59+
60+
// create CDI for layer 1 (L1) firmware (the hubris image we're booting)
61+
let cdi_l1 = CdiL1::new(&cdi, fwid.as_ref());
62+
63+
// derive alias key
64+
let alias_okm = AliasOkm::from_cdi(&cdi_l1);
65+
let alias_keypair = Keypair::from(alias_okm.as_bytes());
66+
67+
let alias_cert = AliasCert::new(
68+
cert_sn,
69+
&dname_sn,
70+
&alias_keypair.public,
71+
fwid.as_ref(),
72+
&deviceid_keypair,
73+
);
74+
75+
let alias_data = AliasData {
76+
seed: alias_okm,
77+
alias_cert,
78+
deviceid_cert,
79+
};
80+
81+
handoff.store(&alias_data);
82+
}

stage0/src/image_header.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ impl Image {
5656
self.0 as *const ImageVectors as u32
5757
}
5858

59+
#[cfg(feature = "dice")]
60+
fn get_img_size(&self) -> Option<usize> {
61+
use core::convert::TryFrom;
62+
63+
usize::try_from((unsafe { &*self.get_header() }).total_image_len).ok()
64+
}
65+
66+
#[cfg(feature = "dice")]
67+
pub fn as_bytes(&self) -> &[u8] {
68+
use unwrap_lite::UnwrapLite;
69+
70+
let img_ptr = self.get_img_start() as *const u8;
71+
let img_size = self.get_img_size().unwrap_lite();
72+
unsafe { core::slice::from_raw_parts(img_ptr, img_size) }
73+
}
74+
5975
fn get_header(&self) -> *const ImageHeader {
6076
// SAFETY: This generated by the linker script which we trust
6177
// Note that this is generated from _this_ image's linker script

stage0/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ extern crate panic_halt;
1515
use cortex_m::peripheral::Peripherals;
1616
use cortex_m_rt::entry;
1717

18+
#[cfg(feature = "dice")]
19+
mod dice;
1820
// FIXME Need to fixup the secure interface calls
1921
//mod hypo;
2022
mod image_header;
@@ -178,6 +180,9 @@ fn main() -> ! {
178180
None => panic!(),
179181
};
180182

183+
#[cfg(feature = "dice")]
184+
dice::run(&imagea);
185+
181186
unsafe {
182187
branch_to_image(imagea);
183188
}

0 commit comments

Comments
 (0)