-
Notifications
You must be signed in to change notification settings - Fork 771
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[rom_ext_e2e] Check the attestation certificates
Examine the attestation certificates and check the measurements published in the DiceTcbInfo extensions. Signed-off-by: Chris Frantz <[email protected]>
- Loading branch information
Showing
6 changed files
with
294 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Copyright lowRISC contributors (OpenTitan project). | ||
# Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
load( | ||
"//rules/opentitan:defs.bzl", | ||
"CLEAR_KEY_SET", | ||
"DEFAULT_TEST_FAILURE_MSG", | ||
"DEFAULT_TEST_SUCCESS_MSG", | ||
"EARLGREY_TEST_ENVS", | ||
"fpga_params", | ||
"opentitan_test", | ||
) | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
opentitan_test( | ||
name = "print_certs_test", | ||
srcs = ["print_certs.c"], | ||
exec_env = { | ||
"//hw/top_earlgrey:fpga_hyper310_rom_ext": None, | ||
}, | ||
fpga = fpga_params( | ||
test_cmd = """ | ||
--bootstrap={firmware} | ||
""", | ||
test_harness = "//sw/host/tests/attestation:attestation_test", | ||
), | ||
deps = [ | ||
"//sw/device/lib/base:status", | ||
"//sw/device/lib/runtime:log", | ||
"//sw/device/lib/runtime:print", | ||
"//sw/device/lib/testing/test_framework:ottf_main", | ||
"//sw/device/silicon_creator/lib/drivers:flash_ctrl", | ||
], | ||
) |
82 changes: 82 additions & 0 deletions
82
sw/device/silicon_creator/rom_ext/e2e/attestation/print_certs.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright lowRISC contributors (OpenTitan project). | ||
// Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#include "sw/device/lib/base/status.h" | ||
#include "sw/device/lib/runtime/log.h" | ||
#include "sw/device/lib/runtime/print.h" | ||
#include "sw/device/lib/testing/test_framework/ottf_main.h" | ||
#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h" | ||
|
||
OTTF_DEFINE_TEST_CONFIG(); | ||
|
||
const char base64[] = | ||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
|
||
void base64_encode(char *dest, const uint8_t *data, int32_t len) { | ||
*dest = '\0'; | ||
for (int32_t i = 0; len > 0; i += 3, len -= 3) { | ||
// clang-format off | ||
uint32_t val = (uint32_t)(data[i] << 16 | | ||
(len > 1 ? data[i + 1] << 8 : 0) | | ||
(len > 2 ? data[i + 2] : 0)); | ||
// clang-format on | ||
*dest++ = base64[(val >> 18) & 0x3f]; | ||
*dest++ = base64[(val >> 12) & 0x3f]; | ||
*dest++ = len > 1 ? base64[(val >> 6) & 0x3f] : '='; | ||
*dest++ = len > 2 ? base64[(val >> 0) & 0x3f] : '='; | ||
*dest = '\0'; | ||
} | ||
} | ||
|
||
status_t print_cert(char *dest, const flash_ctrl_info_page_t *info_page) { | ||
uint8_t data[2048]; | ||
TRY(flash_ctrl_info_read(info_page, 0, sizeof(data) / sizeof(uint32_t), | ||
data)); | ||
|
||
int16_t len = (int16_t)(data[2] << 8 | data[3]); | ||
if (len == -1) { | ||
*dest = '\0'; | ||
return OK_STATUS(); | ||
} | ||
len += 4; | ||
base64_encode(dest, data, len); | ||
return OK_STATUS(); | ||
} | ||
|
||
status_t print_owner_block(char *dest, | ||
const flash_ctrl_info_page_t *info_page) { | ||
uint8_t data[2048]; | ||
TRY(flash_ctrl_info_read(info_page, 0, sizeof(data) / sizeof(uint32_t), | ||
data)); | ||
base64_encode(dest, data, sizeof(data)); | ||
return OK_STATUS(); | ||
} | ||
|
||
status_t print_certs(void) { | ||
char buf[3072]; | ||
TRY(print_cert(buf, &kFlashCtrlInfoPageUdsCertificate)); | ||
LOG_INFO("UDS: %s", buf); | ||
|
||
TRY(print_cert(buf, &kFlashCtrlInfoPageCdi0Certificate)); | ||
LOG_INFO("CDI_0: %s", buf); | ||
|
||
TRY(print_cert(buf, &kFlashCtrlInfoPageCdi1Certificate)); | ||
LOG_INFO("CDI_1: %s", buf); | ||
|
||
TRY(print_owner_block(buf, &kFlashCtrlInfoPageOwnerSlot0)); | ||
LOG_INFO("OWNER_PAGE_0: %s", buf); | ||
|
||
TRY(print_owner_block(buf, &kFlashCtrlInfoPageOwnerSlot1)); | ||
LOG_INFO("OWNER_PAGE_1: %s", buf); | ||
|
||
return OK_STATUS(); | ||
} | ||
|
||
bool test_main(void) { | ||
status_t sts = print_certs(); | ||
if (status_err(sts)) { | ||
LOG_ERROR("print_certs: %r", sts); | ||
} | ||
return status_ok(sts); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Copyright lowRISC contributors (OpenTitan project). | ||
# Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library") | ||
load("//rules:ujson.bzl", "ujson_rust") | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
rust_binary( | ||
name = "attestation_test", | ||
srcs = ["attestation_test.rs"], | ||
deps = [ | ||
"//sw/host/opentitanlib", | ||
"//sw/host/ot_certs", | ||
"@crate_index//:anyhow", | ||
"@crate_index//:base64ct", | ||
"@crate_index//:clap", | ||
"@crate_index//:humantime", | ||
"@crate_index//:log", | ||
"@crate_index//:num-bigint-dig", | ||
"@crate_index//:regex", | ||
"@crate_index//:serde_json", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Copyright lowRISC contributors (OpenTitan project). | ||
// Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#![allow(clippy::bool_assert_comparison)] | ||
use anyhow::{anyhow, Result}; | ||
use base64ct::{Base64, Decoder}; | ||
use clap::Parser; | ||
use num_bigint_dig::BigUint; | ||
use regex::Regex; | ||
use std::time::Duration; | ||
|
||
use opentitanlib::app::TransportWrapper; | ||
use opentitanlib::crypto::sha256; | ||
use opentitanlib::image::image::Image; | ||
use opentitanlib::ownership::OwnerBlock; | ||
use opentitanlib::test_utils::init::InitializeTest; | ||
use opentitanlib::uart::console::UartConsole; | ||
use opentitanlib::util::file::FromReader; | ||
|
||
use ot_certs::template::{CertificateExtension, Value}; | ||
use ot_certs::x509; | ||
|
||
#[derive(Debug, Parser)] | ||
struct Opts { | ||
#[command(flatten)] | ||
init: InitializeTest, | ||
|
||
/// Console receive timeout. | ||
#[arg(long, value_parser = humantime::parse_duration, default_value = "10s")] | ||
timeout: Duration, | ||
} | ||
|
||
// A helper trait for extracting data out of the `Value` type. | ||
trait GetValue<T> { | ||
fn get_value(&self) -> &T; | ||
} | ||
|
||
impl<T: std::fmt::Debug> GetValue<T> for Value<T> { | ||
fn get_value(&self) -> &T { | ||
match self { | ||
Value::Variable(_) => panic!("Not expecting a Variable: {self:?}"), | ||
Value::Literal(x) => x, | ||
} | ||
} | ||
} | ||
|
||
impl<T: std::fmt::Debug> GetValue<T> for Option<Value<T>> { | ||
fn get_value(&self) -> &T { | ||
match self { | ||
None => panic!("Not expecting Option::None"), | ||
Some(Value::Variable(_)) => panic!("Not expecting a Variable: {self:?}"), | ||
Some(Value::Literal(x)) => x, | ||
} | ||
} | ||
} | ||
|
||
fn get_base64_blob(haystack: &str, rx: &str) -> Result<Vec<u8>> { | ||
let rx = Regex::new(rx)?; | ||
let encoded = rx | ||
.captures(haystack) | ||
.ok_or(anyhow!("Encoded certificate not found"))?; | ||
let mut bin = Vec::new(); | ||
if !encoded[1].is_empty() { | ||
let mut decoder = Decoder::<Base64>::new(encoded[1].as_bytes())?; | ||
decoder.decode_to_end(&mut bin)?; | ||
} | ||
Ok(bin) | ||
} | ||
|
||
fn attestation_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { | ||
let uart = transport.uart("console")?; | ||
let capture = UartConsole::wait_for( | ||
&*uart, | ||
r"(?msR)Running.*PASS!$|FAIL!$|BFV:([0-9A-Fa-f]{8})$", | ||
opts.timeout, | ||
)?; | ||
|
||
let _uds_bin = get_base64_blob(&capture[0], r"(?msR)UDS: (.*?)$")?; | ||
let cdi0_bin = get_base64_blob(&capture[0], r"(?msR)CDI_0: (.*?)$")?; | ||
let cdi1_bin = get_base64_blob(&capture[0], r"(?msR)CDI_1: (.*?)$")?; | ||
let owner_page_0 = get_base64_blob(&capture[0], r"(?msR)OWNER_PAGE_0: (.*?)$")?; | ||
let owner_page_1 = get_base64_blob(&capture[0], r"(?msR)OWNER_PAGE_1: (.*?)$")?; | ||
|
||
// TODO: check UDS certificate. | ||
let cdi0 = x509::parse_certificate(&cdi0_bin)?; | ||
let cdi1 = x509::parse_certificate(&cdi1_bin)?; | ||
|
||
// TODO: verify signature chain from CDI_1 to CDI_0 to UDS. | ||
|
||
let image = Image::read_from_file(opts.init.bootstrap.bootstrap.as_deref().unwrap())?; | ||
|
||
// TODO: determine the correct endianness for expressing these measurements. | ||
let measurements = image | ||
.subimages()? | ||
.iter() | ||
.map(|s| s.compute_digest().unwrap().to_le_bytes()) | ||
.collect::<Vec<_>>(); | ||
let owner_measurements = [ | ||
// The owner page digests should not include the signature or seal fields. | ||
sha256::sha256(&owner_page_0[0..OwnerBlock::SIGNATURE_OFFSET]).to_le_bytes(), | ||
sha256::sha256(&owner_page_1[0..OwnerBlock::SIGNATURE_OFFSET]).to_le_bytes(), | ||
]; | ||
|
||
let CertificateExtension::DiceTcbInfo(dice) = &cdi0.private_extensions[0]; | ||
log::info!("Checking CDI_0 (ROM_EXT) DICE Extension: {dice:#?}"); | ||
assert_eq!(dice.model.get_value(), "ROM_EXT"); | ||
assert_eq!(dice.vendor.get_value(), "OpenTitan"); | ||
assert_eq!(dice.layer.get_value(), &BigUint::from(1u8)); | ||
let fw_ids = dice.fw_ids.as_ref().expect("list of fw_ids"); | ||
assert_eq!(fw_ids.len(), 1); | ||
assert_eq!(fw_ids[0].digest.get_value(), &measurements[0]); | ||
|
||
let CertificateExtension::DiceTcbInfo(dice) = &cdi1.private_extensions[0]; | ||
log::info!("Checking CDI_1 (Owner) DICE Extension: {dice:#?}"); | ||
assert_eq!(dice.model.get_value(), "Owner"); | ||
assert_eq!(dice.vendor.get_value(), "OpenTitan"); | ||
assert_eq!(dice.layer.get_value(), &BigUint::from(2u8)); | ||
let fw_ids = dice.fw_ids.as_ref().expect("list of fw_ids"); | ||
assert_eq!(fw_ids.len(), 2); | ||
assert_eq!(fw_ids[0].digest.get_value(), &measurements[1]); | ||
assert_eq!(fw_ids[1].digest.get_value(), &owner_measurements[0]); | ||
Ok(()) | ||
} | ||
|
||
fn main() -> Result<()> { | ||
let opts = Opts::parse(); | ||
opts.init.init_logging(); | ||
let transport = opts.init.init_target()?; | ||
attestation_test(&opts, &transport)?; | ||
Ok(()) | ||
} |