Skip to content

Commit

Permalink
Fips test hooks support (#1391)
Browse files Browse the repository at this point in the history
  • Loading branch information
nquarton authored Jul 15, 2024
1 parent 8a429a5 commit 7d9d89c
Show file tree
Hide file tree
Showing 17 changed files with 365 additions and 5 deletions.
7 changes: 7 additions & 0 deletions builder/src/firmware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ pub const ROM_FAKE_WITH_UART: FwId = FwId {
features: &["emu", "fake-rom"],
};

pub const ROM_WITH_UART_FIPS_TEST_HOOKS: FwId = FwId {
crate_name: "caliptra-rom",
bin_name: "caliptra-rom",
features: &["emu", "fips-test-hooks"],
};

pub const FMC_WITH_UART: FwId = FwId {
crate_name: "caliptra-fmc",
bin_name: "caliptra-fmc",
Expand Down Expand Up @@ -368,6 +374,7 @@ pub const REGISTERED_FW: &[&FwId] = &[
&ROM,
&ROM_WITH_UART,
&ROM_FAKE_WITH_UART,
&ROM_WITH_UART_FIPS_TEST_HOOKS,
&FMC_WITH_UART,
&FMC_FAKE_WITH_UART,
&APP,
Expand Down
1 change: 1 addition & 0 deletions drivers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ itrng = ["caliptra-hw-model/itrng"]
verilator = ["caliptra-hw-model/verilator"]
no-cfi = []
"hw-1.0" = ["caliptra-builder/hw-1.0", "caliptra-registers/hw-1.0"]
fips-test-hooks = []

[dev-dependencies]
caliptra-builder.workspace = true
Expand Down
82 changes: 82 additions & 0 deletions drivers/src/fips_test_hooks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed under the Apache-2.0 license

use caliptra_registers::soc_ifc::SocIfcReg;

pub struct FipsTestHook;

impl FipsTestHook {
pub const RSVD: u8 = 0x0;
// Set by Caliptra
pub const COMPLETE: u8 = 0x1;
// Set by external test
pub const CONTINUE: u8 = 0x10;
pub const HALT_SELF_TESTS: u8 = 0x21;
pub const SHA384_ERROR: u8 = 0x22;
pub const LMS_ERROR: u8 = 0x23;

/// # Safety
///
/// This function interrupts normal flow and halts operation of the ROM or FW
/// (Only when the hook_cmd matches the value from get_fips_test_hook_code)
pub unsafe fn halt_if_hook_set(hook_cmd: u8) {
if get_fips_test_hook_code() == hook_cmd {
// Report that we've reached this point
set_fips_test_hook_code(FipsTestHook::COMPLETE);

// Wait for the CONTINUE command
while get_fips_test_hook_code() != FipsTestHook::CONTINUE {}

// Write COMPLETE
set_fips_test_hook_code(FipsTestHook::COMPLETE);
}
}

/// # Safety
///
/// This function returns an intentionally corrupted version of the data provided
/// (Only when the hook_cmd matches the value from get_fips_test_hook_code)
pub unsafe fn corrupt_data_if_hook_set<T: core::marker::Copy>(hook_cmd: u8, data: &T) -> T {
if get_fips_test_hook_code() == hook_cmd {
let mut mut_data = *data;
let ptr_t = &mut mut_data as *mut T;
let mut_u8 = ptr_t as *mut u8;
let byte_0 = unsafe { &mut *mut_u8 };

// Corrupt (invert) the first byte
*byte_0 = !*byte_0;

return mut_data;
}

*data
}
}

/// # Safety
///
/// Temporarily creates a new instance of SocIfcReg instead of following the
/// normal convention of sharing one instance
unsafe fn get_fips_test_hook_code() -> u8 {
// Bits 23:16 indicate the 8 bit code for the enabled FIPS test hook
const CODE_MASK: u32 = 0x00FF0000;
const CODE_OFFSET: u32 = 16;
let soc_ifc = unsafe { SocIfcReg::new() };
let soc_ifc_regs = soc_ifc.regs();
let val = soc_ifc_regs.cptra_dbg_manuf_service_reg().read();
((val & CODE_MASK) >> CODE_OFFSET) as u8
}

/// # Safety
///
/// Temporarily creates a new instance of SocIfcReg instead of following the
/// normal convention of sharing one instance
unsafe fn set_fips_test_hook_code(code: u8) {
// Bits 23:16 indicate the 8 bit code for the enabled FIPS test hook
const CODE_MASK: u32 = 0x00FF0000;
const CODE_OFFSET: u32 = 16;
let mut soc_ifc = unsafe { SocIfcReg::new() };
let soc_ifc_regs = soc_ifc.regs_mut();
let val = (soc_ifc_regs.cptra_dbg_manuf_service_reg().read() & !(CODE_MASK))
| ((code as u32) << CODE_OFFSET);
soc_ifc_regs.cptra_dbg_manuf_service_reg().write(|_| val);
}
4 changes: 4 additions & 0 deletions drivers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ mod doe;
mod ecc384;
mod error_reporter;
mod exit_ctrl;
#[cfg(feature = "fips-test-hooks")]
pub mod fips_test_hooks;
mod fuse_bank;
pub mod fuse_log;
pub mod hand_off;
Expand Down Expand Up @@ -65,6 +67,8 @@ pub use ecc384::{
};
pub use error_reporter::{report_fw_error_fatal, report_fw_error_non_fatal};
pub use exit_ctrl::ExitCtrl;
#[cfg(feature = "fips-test-hooks")]
pub use fips_test_hooks::FipsTestHook;
pub use fuse_bank::{
FuseBank, IdevidCertAttr, RomVerifyConfig, VendorPubKeyRevocation, X509KeyIdAlgo,
};
Expand Down
9 changes: 9 additions & 0 deletions drivers/src/lms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,17 @@ impl Lms {
lms_public_key: &LmsPublicKey<6>,
lms_sig: &LmsSignature<6, 51, 15>,
) -> CaliptraResult<LmsResult> {
#[cfg(feature = "fips-test-hooks")]
let input_string = unsafe {
crate::FipsTestHook::corrupt_data_if_hook_set(
crate::FipsTestHook::LMS_ERROR,
&input_string,
)
};

let mut candidate_key =
self.verify_lms_signature_cfi(sha256_driver, input_string, lms_public_key, lms_sig)?;

let result = if candidate_key != HashValue::from(lms_public_key.digest) {
Ok(LmsResult::SigVerifyFailed)
} else {
Expand Down
8 changes: 8 additions & 0 deletions drivers/src/sha384.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ impl Sha384 {
}
let digest = self.read_digest();

#[cfg(feature = "fips-test-hooks")]
let digest = unsafe {
crate::FipsTestHook::corrupt_data_if_hook_set(
crate::FipsTestHook::SHA384_ERROR,
&digest,
)
};

self.zeroize_internal();

Ok(digest)
Expand Down
4 changes: 3 additions & 1 deletion error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,9 @@ impl CaliptraError {
pub const ROM_GLOBAL_MEASUREMENT_LOG_EXHAUSTED: CaliptraError =
CaliptraError::new_const(0x0105000D);

/// KAT Errors
pub const ROM_GLOBAL_FIPS_HOOKS_ROM_EXIT: CaliptraError = CaliptraError::new_const(0x0105000F);

/// ROM KAT Errors
pub const KAT_SHA256_DIGEST_FAILURE: CaliptraError = CaliptraError::new_const(0x90010001);
pub const KAT_SHA256_DIGEST_MISMATCH: CaliptraError = CaliptraError::new_const(0x90010002);

Expand Down
9 changes: 9 additions & 0 deletions hw-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ pub enum ModelError {
actual: u32,
},
MailboxRespInvalidFipsStatus(u32),
MailboxTimeout,
}
impl Error for ModelError {}
impl Display for ModelError {
Expand Down Expand Up @@ -361,6 +362,9 @@ impl Display for ModelError {
"Mailbox response had non-success FIPS status: 0x{status:x}"
)
}
ModelError::MailboxTimeout => {
write!(f, "Mailbox timed out in busy state")
}
}
}
}
Expand Down Expand Up @@ -971,8 +975,13 @@ pub trait HwModel {
/// Wait for the response to a previous call to `start_mailbox_execute()`.
fn finish_mailbox_execute(&mut self) -> std::result::Result<Option<Vec<u8>>, ModelError> {
// Wait for the microcontroller to finish executing
let mut timeout_cycles = 40000000; // 100ms @400MHz
while self.soc_mbox().status().read().status().cmd_busy() {
self.step();
timeout_cycles -= 1;
if timeout_cycles == 0 {
return Err(ModelError::MailboxTimeout);
}
}
let status = self.soc_mbox().status().read().status();
if status.cmd_failure() {
Expand Down
1 change: 1 addition & 0 deletions rom/dev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ fake-rom = []
no-cfi = ["caliptra-image-verify/no-cfi", "caliptra-drivers/no-cfi"]
slow_tests = []
"hw-1.0" = ["caliptra-builder/hw-1.0", "caliptra-drivers/hw-1.0", "caliptra-registers/hw-1.0", "caliptra-hw-model/hw-1.0"]
fips-test-hooks = ["caliptra-drivers/fips-test-hooks"]

[[bin]]
name = "asm_tests"
Expand Down
14 changes: 13 additions & 1 deletion rom/dev/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Abstract:
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), no_main)]
#![cfg_attr(feature = "fake-rom", allow(unused_imports))]
#![cfg_attr(feature = "fips-test-hooks", allow(dead_code))]

use crate::{lock::lock_registers, print::HexBytes};
use caliptra_cfi_lib::{cfi_assert_eq, CfiCounter};
Expand Down Expand Up @@ -187,7 +188,11 @@ pub extern "C" fn rom_entry() -> ! {
CfiCounter::corrupt();
}

#[cfg(not(feature = "no-fmc"))]
// FIPS test hooks mode does not allow handoff to FMC to prevent incorrect/accidental usage
#[cfg(feature = "fips-test-hooks")]
handle_fatal_error(CaliptraError::ROM_GLOBAL_FIPS_HOOKS_ROM_EXIT.into());

#[cfg(not(any(feature = "no-fmc", feature = "fips-test-hooks")))]
launch_fmc(&mut env);

#[cfg(feature = "no-fmc")]
Expand All @@ -200,6 +205,13 @@ fn run_fips_tests(env: &mut KatsEnv) -> CaliptraResult<()> {
cprintln!("[kat] SHA2-256");
Sha256Kat::default().execute(env.sha256)?;

#[cfg(feature = "fips-test-hooks")]
unsafe {
caliptra_drivers::FipsTestHook::halt_if_hook_set(
caliptra_drivers::FipsTestHook::HALT_SELF_TESTS,
)
};

// ROM integrity check needs SHA2-256 KAT to be executed first per FIPS requirement AS10.20.
let rom_info = unsafe { &CALIPTRA_ROM_INFO };
rom_integrity_test(env, &rom_info.sha256_digest)?;
Expand Down
1 change: 1 addition & 0 deletions rom/dev/tests/rom_integration_tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod test_cfi;
mod test_cpu_fault;
mod test_dice_derivations;
mod test_fake_rom;
mod test_fips_hooks;
mod test_fmcalias_derivation;
mod test_idevid_derivation;
mod test_image_validation;
Expand Down
41 changes: 41 additions & 0 deletions rom/dev/tests/rom_integration_tests/test_fips_hooks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed under the Apache-2.0 license

use caliptra_builder::firmware::{APP_WITH_UART, FMC_WITH_UART, ROM_WITH_UART_FIPS_TEST_HOOKS};
use caliptra_builder::ImageOptions;
use caliptra_drivers::CaliptraError;
use caliptra_hw_model::{BootParams, HwModel, InitParams};

#[test]
fn test_fips_hook_exit() {
let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART_FIPS_TEST_HOOKS).unwrap();

let image_bundle = caliptra_builder::build_and_sign_image(
&FMC_WITH_UART,
&APP_WITH_UART,
ImageOptions::default(),
)
.unwrap()
.to_bytes()
.unwrap();

let init_params = InitParams {
rom: &rom,
..Default::default()
};

let boot_params = BootParams {
fw_image: Some(&image_bundle),
..Default::default()
};

let mut hw = caliptra_hw_model::new(init_params, boot_params).unwrap();

// Wait for fatal error
hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() != 0);

// Verify fatal code is correct
assert_eq!(
hw.soc_ifc().cptra_fw_error_fatal().read(),
u32::from(CaliptraError::ROM_GLOBAL_FIPS_HOOKS_ROM_EXIT)
);
}
2 changes: 1 addition & 1 deletion test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ asn1.workspace = true
caliptra-builder.workspace = true
caliptra_common = { workspace = true, default-features = false }
caliptra-coverage.workspace = true
caliptra-drivers.workspace = true
caliptra-drivers = { workspace = true, features = ["fips-test-hooks"] }
caliptra-hw-model-types.workspace = true
caliptra-image-types.workspace = true
caliptra-runtime = { workspace = true, default-features = false }
Expand Down
17 changes: 16 additions & 1 deletion test/tests/fips_test_suite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,19 @@ These can be enabled using the --features argument for rust like:

## Additional Environments

Support for additional environments can be done by creating new implementations/interfaces for the HW model at hw-model/src. See model_fpga_realtime.rs as an example. This implementation needs to be able to access the APB bus, control the input signals to caliptra, and, if possible, control ROM.
Support for additional environments can be done by creating new implementations/interfaces for the HW model at hw-model/src. See model_fpga_realtime.rs as an example. This implementation needs to be able to access the APB bus, control the input signals to Caliptra, and, if possible, control ROM.

## Test Hooks

Certain tests require "hooks" into the ROM or FW to cause operation to deviate from the normal flow (ie. injecting errors or halting execution at specific points). This functionality is enabled using a build option called "fips-test-hooks". Then, the specific command codes are written and read from the DBG_MANUF_SERVICE_REG. The ROM/FW can respond back with a status code written to the same field if applicable. See command codes in drivers\src\fips_test_hooks.rs for more details.

Test hooks are needed to meet the following FIPS 140-3 test requirements:
TE03.07.02
TE03.07.04
TE04.29.01
TE10.07.03
TE10.08.03
TE10.09.03
TE10.10.01
TE10.10.02
TE10.35.04
Loading

0 comments on commit 7d9d89c

Please sign in to comment.