forked from lowRISC/opentitan
-
Notifications
You must be signed in to change notification settings - Fork 0
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] Add an ownership transfer test
1. Create a library of helper functions for facilitating ownership transfer tests. 2. Create a basic ownership transfer test that transfers chip ownership from the default `fake` test owner to a `dummy` owner. Signed-off-by: Chris Frantz <cfrantz@google.com>
- Loading branch information
Showing
11 changed files
with
317 additions
and
8 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
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,51 @@ | ||
# 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", | ||
"fpga_params", | ||
"opentitan_test", | ||
) | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
opentitan_test( | ||
name = "ownership_transfer_test", | ||
srcs = ["//sw/device/silicon_creator/rom_ext/e2e/verified_boot:boot_test"], | ||
exec_env = { | ||
"//hw/top_earlgrey:fpga_hyper310_rom_ext": None, | ||
}, | ||
fpga = fpga_params( | ||
# This test doesn't change OTP, but it modifies the ownership INFO | ||
# pages, so we need to clear the bitstream after the test, which is | ||
# what the `changes_otp` parameter actually does. | ||
changes_otp = True, | ||
data = [ | ||
"//sw/device/silicon_creator/lib/ownership/keys/dummy:activate_key", | ||
"//sw/device/silicon_creator/lib/ownership/keys/dummy:app_prod_pub", | ||
"//sw/device/silicon_creator/lib/ownership/keys/dummy:owner_key", | ||
"//sw/device/silicon_creator/lib/ownership/keys/dummy:unlock_key", | ||
"//sw/device/silicon_creator/lib/ownership/keys/fake:unlock_key", | ||
], | ||
test_cmd = """ | ||
--clear-bitstream | ||
--bootstrap={firmware} | ||
--unlock-key=$(location //sw/device/silicon_creator/lib/ownership/keys/fake:unlock_key) | ||
--next-owner-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:owner_key) | ||
--next-unlock-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:unlock_key) | ||
--next-activate-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:activate_key) | ||
--next-application-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:app_prod_pub) | ||
""", | ||
test_harness = "//sw/host/tests/ownership:transfer_test", | ||
), | ||
rsa_key = { | ||
"//sw/device/silicon_creator/lib/ownership/keys/dummy:app_prod": "app_prod", | ||
}, | ||
deps = [ | ||
"//sw/device/lib/base:status", | ||
"//sw/device/lib/testing/test_framework:ottf_main", | ||
"//sw/device/silicon_creator/lib:boot_log", | ||
"//sw/device/silicon_creator/lib/drivers:retention_sram", | ||
], | ||
) |
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
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
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,33 @@ | ||
# 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_library( | ||
name = "transfer_lib", | ||
srcs = ["transfer_lib.rs"], | ||
deps = [ | ||
"//sw/host/opentitanlib", | ||
"@crate_index//:anyhow", | ||
"@crate_index//:log", | ||
], | ||
) | ||
|
||
rust_binary( | ||
name = "transfer_test", | ||
srcs = [ | ||
"transfer_test.rs", | ||
], | ||
deps = [ | ||
":transfer_lib", | ||
"//sw/host/opentitanlib", | ||
"@crate_index//:anyhow", | ||
"@crate_index//:clap", | ||
"@crate_index//:humantime", | ||
"@crate_index//:log", | ||
], | ||
) |
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,121 @@ | ||
// 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 opentitanlib::app::TransportWrapper; | ||
use opentitanlib::chip::boot_log::BootLog; | ||
use opentitanlib::chip::boot_svc::{Message, UnlockMode}; | ||
use opentitanlib::chip::helper::{OwnershipActivateParams, OwnershipUnlockParams}; | ||
use opentitanlib::chip::rom_error::RomError; | ||
use opentitanlib::crypto::ecdsa::EcdsaPrivateKey; | ||
use opentitanlib::crypto::rsa::RsaPublicKey; | ||
use opentitanlib::ownership::{ | ||
ApplicationKeyDomain, KeyMaterial, OwnerApplicationKey, OwnerBlock, OwnerConfigItem, | ||
OwnershipKeyAlg, | ||
}; | ||
use opentitanlib::rescue::serial::RescueSerial; | ||
|
||
use std::path::Path; | ||
|
||
/// Gets the BootLog. | ||
pub fn get_boot_log(transport: &TransportWrapper, rescue: &RescueSerial) -> Result<BootLog> { | ||
rescue.enter(transport, /*reset=*/ true)?; | ||
rescue.get_boot_log() | ||
} | ||
|
||
/// Prepares an UnlockOwnership command, sends it to the chip and gets the response. | ||
pub fn ownership_unlock( | ||
transport: &TransportWrapper, | ||
rescue: &RescueSerial, | ||
mode: UnlockMode, | ||
nonce: u64, | ||
unlock_key: &Path, | ||
next_owner: Option<&Path>, | ||
) -> Result<()> { | ||
let unlock = OwnershipUnlockParams { | ||
mode: Some(mode), | ||
nonce: Some(nonce), | ||
next_owner: next_owner.map(|p| p.into()), | ||
sign: Some(unlock_key.into()), | ||
..Default::default() | ||
} | ||
.apply_to(Option::<&mut std::fs::File>::None)?; | ||
|
||
rescue.enter(transport, /*reset=*/ true)?; | ||
rescue.ownership_unlock(unlock)?; | ||
rescue.enter(transport, /*reset=*/ false)?; | ||
let result = rescue.get_boot_svc()?; | ||
match &result.message { | ||
Message::OwnershipUnlockResponse(r) if r.status == RomError::Ok => Ok(()), | ||
_ => Err(anyhow!("Unexpected response: {result:x?}")), | ||
} | ||
} | ||
|
||
/// Prepares an UnlockOwnership command (with UnlockMode::Any), sends it to the chip and gets the response. | ||
pub fn ownership_unlock_any( | ||
transport: &TransportWrapper, | ||
rescue: &RescueSerial, | ||
nonce: u64, | ||
unlock_key: &Path, | ||
) -> Result<()> { | ||
ownership_unlock(transport, rescue, UnlockMode::Any, nonce, unlock_key, None) | ||
} | ||
|
||
/// Prepares an OwnershipActivate command, sends it to the chip and gets the response. | ||
pub fn ownership_activate( | ||
transport: &TransportWrapper, | ||
rescue: &RescueSerial, | ||
nonce: u64, | ||
activate_key: &Path, | ||
) -> Result<()> { | ||
let activate = OwnershipActivateParams { | ||
nonce: Some(nonce), | ||
sign: Some(activate_key.into()), | ||
..Default::default() | ||
} | ||
.apply_to(Option::<&mut std::fs::File>::None)?; | ||
|
||
rescue.enter(transport, /*reset=*/ true)?; | ||
rescue.ownership_activate(activate)?; | ||
rescue.enter(transport, /*reset=*/ false)?; | ||
let result = rescue.get_boot_svc()?; | ||
match &result.message { | ||
Message::OwnershipActivateResponse(r) if r.status == RomError::Ok => Ok(()), | ||
_ => Err(anyhow!("Unexpected response: {result:x?}")), | ||
} | ||
} | ||
|
||
/// Prepares an OwnerBlock and sends it to the chip. | ||
pub fn create_owner( | ||
transport: &TransportWrapper, | ||
rescue: &RescueSerial, | ||
owner_key: &Path, | ||
activate_key: &Path, | ||
unlock_key: &Path, | ||
app_key: &Path, | ||
) -> Result<()> { | ||
let owner_key = EcdsaPrivateKey::load(owner_key)?; | ||
let activate_key = EcdsaPrivateKey::load(activate_key)?; | ||
let unlock_key = EcdsaPrivateKey::load(unlock_key)?; | ||
let app_key = RsaPublicKey::from_pkcs1_der_file(app_key)?; | ||
let mut owner = OwnerBlock { | ||
owner_key: KeyMaterial::Ecdsa(owner_key.public_key().try_into()?), | ||
activate_key: KeyMaterial::Ecdsa(activate_key.public_key().try_into()?), | ||
unlock_key: KeyMaterial::Ecdsa(unlock_key.public_key().try_into()?), | ||
data: vec![OwnerConfigItem::ApplicationKey(OwnerApplicationKey { | ||
key_alg: OwnershipKeyAlg::Rsa, | ||
key_domain: ApplicationKeyDomain::Prod, | ||
key: KeyMaterial::Rsa(app_key.try_into()?), | ||
..Default::default() | ||
})], | ||
..Default::default() | ||
}; | ||
owner.sign(&owner_key)?; | ||
let mut owner_config = Vec::new(); | ||
owner.write(&mut owner_config)?; | ||
rescue.enter(transport, /*reset=*/ true)?; | ||
rescue.set_owner_config(&owner_config)?; | ||
Ok(()) | ||
} |
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,84 @@ | ||
// 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::Result; | ||
use clap::Parser; | ||
use std::path::PathBuf; | ||
use std::rc::Rc; | ||
use std::time::Duration; | ||
|
||
use opentitanlib::rescue::serial::RescueSerial; | ||
use opentitanlib::test_utils::init::InitializeTest; | ||
use opentitanlib::uart::console::UartConsole; | ||
|
||
#[derive(Debug, Parser)] | ||
struct Opts { | ||
#[command(flatten)] | ||
init: InitializeTest, | ||
|
||
/// Console receive timeout. | ||
#[arg(long, value_parser = humantime::parse_duration, default_value = "10s")] | ||
timeout: Duration, | ||
#[arg(long, help = "Unlock private key (ECDSA P256)")] | ||
unlock_key: PathBuf, | ||
#[arg(long, help = "Next Owner private key (ECDSA P256)")] | ||
next_owner_key: PathBuf, | ||
#[arg(long, help = "Next Owner activate private key (ECDSA P256)")] | ||
next_activate_key: PathBuf, | ||
#[arg(long, help = "Next Owner unlock private key (ECDSA P256)")] | ||
next_unlock_key: PathBuf, | ||
#[arg(long, help = "Next Owner's application public key (RSA3K)")] | ||
next_application_key: PathBuf, | ||
} | ||
|
||
fn main() -> Result<()> { | ||
let opts = Opts::parse(); | ||
opts.init.init_logging(); | ||
|
||
let transport = opts.init.init_target()?; | ||
let uart = transport.uart("console")?; | ||
let rescue = RescueSerial::new(Rc::clone(&uart)); | ||
|
||
let data = transfer_lib::get_boot_log(&transport, &rescue)?; | ||
transfer_lib::ownership_unlock_any(&transport, &rescue, data.rom_ext_nonce, &opts.unlock_key)?; | ||
|
||
transfer_lib::create_owner( | ||
&transport, | ||
&rescue, | ||
&opts.next_owner_key, | ||
&opts.next_activate_key, | ||
&opts.next_unlock_key, | ||
&opts.next_application_key, | ||
)?; | ||
|
||
// At this point, the device should be unlocked and should have accepted the owner | ||
// configuration. Owner code should run and report the state as `UANY`. | ||
transport.reset_target(Duration::from_millis(50), /*clear_uart=*/ true)?; | ||
let capture = UartConsole::wait_for( | ||
&*uart, | ||
r"(?msR)ownership_state = UANY$.*ownership_transfers = (\d+)$.*PASS!$", | ||
opts.timeout, | ||
)?; | ||
let transfers0 = capture[1].parse::<u32>()?; | ||
|
||
let data = transfer_lib::get_boot_log(&transport, &rescue)?; | ||
transfer_lib::ownership_activate( | ||
&transport, | ||
&rescue, | ||
data.rom_ext_nonce, | ||
&opts.next_activate_key, | ||
)?; | ||
|
||
// After the activate command, the device should report the ownership state as `OWND`. | ||
transport.reset_target(Duration::from_millis(50), /*clear_uart=*/ true)?; | ||
let capture = UartConsole::wait_for( | ||
&*uart, | ||
r"(?msR)ownership_state = OWND$.*ownership_transfers = (\d+)$.*PASS!$", | ||
opts.timeout, | ||
)?; | ||
let transfers1 = capture[1].parse::<u32>()?; | ||
assert_eq!(transfers0 + 1, transfers1); | ||
Ok(()) | ||
} |