Skip to content

Commit

Permalink
Implement FIPS commands for ROM and invoke SELF-TEST from Runtime. (#693
Browse files Browse the repository at this point in the history
)

* Decouple dependency of Kat execution from Rom

* Decouple verification environment from ROM dependency

* Move fips-self-test test to test crate

* Use conditional compilation to build caliptra-runtime as ROM image

* Postpone self test execution

* Fix 1600 byte ROM size regression in #684

When callers from separate crates call a large generic function like
verify_lms_signature_cfi(), rustc 1.70 may build multiple versions
(depending on optimizer heuristics), even when all the generic
parameters are identical. This is bad, as it can bloat the binary and
the second copy violates the FIPS requirements that the same machine
code be used for the KAT as the actual implementation. To defend against
it, we provide a non-generic function that production firmware should
call instead.

* Rename goto_idle to enter_idle

* Fix typo

* Renamed SELF_TEST

* START/GET_PROGRESS commands

* kats executes before image verification

* Remove persistent_data from venv

* Implement FIPS commands in ROM

VERSION
SELF_TEST
SHUTDOWN

* Fix unassigned error codes

* Address feedback

- Remove mut from fips create_slice
- Use copy_bytes_to_mbox in write_response
- Restore idevid tests mistakenly removed
- Add bounds checking on fmc and rt sizes

* Rom itegrity test call from Runtime.

* Fixes for gate

---------

Co-authored-by: Anthony Rocha <[email protected]>
Co-authored-by: Kor Nielsen <[email protected]>
  • Loading branch information
3 people authored Sep 8, 2023
1 parent 28e8146 commit ab9a518
Show file tree
Hide file tree
Showing 32 changed files with 774 additions and 171 deletions.
3 changes: 3 additions & 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 builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub const FMC_FAKE_WITH_UART: FwId = FwId {
pub const APP_WITH_UART: FwId = FwId {
crate_name: "caliptra-runtime",
bin_name: "caliptra-runtime",
features: &["emu", "test_only_commands"],
features: &["emu", "test_only_commands", "fips_self_test"],
workspace_dir: None,
};

Expand Down
3 changes: 2 additions & 1 deletion common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ bitfield.workspace = true
bitflags.workspace = true
caliptra-drivers.workspace = true
caliptra-image-types = { workspace = true, default-features = false }
caliptra-image-verify.workspace = true
caliptra-registers.workspace = true
ufmt.workspace = true
zerocopy.workspace = true

[features]
default = ["std"]
emu = ["caliptra-drivers/emu"]
std = []
std = []
25 changes: 25 additions & 0 deletions common/src/fips.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed under the Apache-2.0 license

use crate::cprintln;
use crate::mailbox_api::{FipsVersionResp, MailboxResp, MailboxRespHeader};
use caliptra_drivers::CaliptraResult;
use caliptra_drivers::SocIfc;

pub struct FipsVersionCmd;
impl FipsVersionCmd {
pub const NAME: [u8; 12] = *b"Caliptra RTM";
pub const MODE: u32 = 0x46495053;

pub fn execute(soc_ifc: &SocIfc) -> CaliptraResult<MailboxResp> {
cprintln!("[rt] FIPS Version");

let resp = FipsVersionResp {
hdr: MailboxRespHeader::default(),
mode: Self::MODE,
fips_rev: soc_ifc.get_version(),
name: Self::NAME,
};

Ok(MailboxResp::FipsVersion(resp))
}
}
2 changes: 2 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ pub mod capabilities;
pub mod checksum;
pub mod crypto;
pub mod dice;
pub mod fips;
pub mod keyids;
pub mod mailbox_api;
pub mod verifier;
pub mod wdt;

///merge imports
Expand Down
4 changes: 3 additions & 1 deletion common/src/mailbox_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ impl CommandId {
/// The status command.
pub const VERSION: Self = Self(0x4650_5652); // "FPVR"
/// The self-test command.
pub const SELF_TEST: Self = Self(0x4650_4C54); // "FPST"
pub const SELF_TEST_START: Self = Self(0x4650_4C54); // "FPST"
/// The self-test get results.
pub const SELF_TEST_GET_RESULTS: Self = Self(0x4650_4C67); // "FPGR"
/// The shutdown command.
pub const SHUTDOWN: Self = Self(0x4650_5344); // "FPSD"

Expand Down
23 changes: 11 additions & 12 deletions rom/dev/src/verifier.rs → common/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,20 @@ use caliptra_image_types::*;
use caliptra_image_verify::ImageVerificationEnv;
use core::ops::Range;

use crate::rom_env::RomEnv;
use caliptra_drivers::memory_layout::ICCM_RANGE;

/// ROM Verification Environemnt
pub(crate) struct RomImageVerificationEnv<'a> {
pub(crate) sha256: &'a mut Sha256,
pub(crate) sha384: &'a mut Sha384,
pub(crate) sha384_acc: &'a mut Sha384Acc,
pub(crate) soc_ifc: &'a mut SocIfc,
pub(crate) ecc384: &'a mut Ecc384,
pub(crate) data_vault: &'a mut DataVault,
pub(crate) pcr_bank: &'a mut PcrBank,
pub(crate) persistent_data: &'a mut PersistentDataAccessor,
pub struct FirmwareImageVerificationEnv<'a> {
pub sha256: &'a mut Sha256,
pub sha384: &'a mut Sha384,
pub sha384_acc: &'a mut Sha384Acc,
pub soc_ifc: &'a mut SocIfc,
pub ecc384: &'a mut Ecc384,
pub data_vault: &'a mut DataVault,
pub pcr_bank: &'a mut PcrBank,
}

impl<'a> ImageVerificationEnv for &mut RomImageVerificationEnv<'a> {
impl<'a> ImageVerificationEnv for &mut FirmwareImageVerificationEnv<'a> {
/// Calculate Digest using SHA-384 Accelerator
fn sha384_digest(&mut self, offset: u32, len: u32) -> CaliptraResult<ImageDigest> {
loop {
Expand Down Expand Up @@ -134,7 +133,7 @@ impl<'a> ImageVerificationEnv for &mut RomImageVerificationEnv<'a> {
}

fn iccm_range(&self) -> Range<u32> {
RomEnv::ICCM_RANGE
ICCM_RANGE
}

fn lms_verify_enabled(&self) -> bool {
Expand Down
36 changes: 28 additions & 8 deletions drivers/src/hand_off.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,29 @@ pub enum DataVaultRegister {
impl TryInto<DataStore> for HandOffDataHandle {
type Error = CaliptraError;
fn try_into(self) -> Result<DataStore, Self::Error> {
let vault = Vault::try_from(self.vault())
.unwrap_or_else(|_| report_handoff_error_and_halt("Invalid Vault", 0xbadbad));
let vault = Vault::try_from(self.vault()).unwrap_or_else(|_| {
report_handoff_error_and_halt(
"Invalid Vault",
CaliptraError::DRIVER_HANDOFF_INVALID_VAULT.into(),
)
});
match vault {
Vault::KeyVault => Ok(DataStore::KeyVaultSlot(
KeyId::try_from(self.reg_num() as u8)
.unwrap_or_else(|_| report_handoff_error_and_halt("Invalid KeyId", 0xbadbad)),
KeyId::try_from(self.reg_num() as u8).unwrap_or_else(|_| {
report_handoff_error_and_halt(
"Invalid KeyId",
CaliptraError::DRIVER_HANDOFF_INVALID_KEY_ID.into(),
)
}),
)),
Vault::DataVault => match self.reg_type() {
1 => {
let entry = DataStore::DataVaultSticky4(
ColdResetEntry4::try_from(self.reg_num() as u8).unwrap_or_else(|_| {
report_handoff_error_and_halt("Invalid ColdResetEntry4", 0xbadbad)
report_handoff_error_and_halt(
"Invalid ColdResetEntry4",
CaliptraError::DRIVER_HANDOFF_INVALID_COLD_RESET_ENTRY4.into(),
)
}),
);
Ok(entry)
Expand All @@ -109,7 +120,10 @@ impl TryInto<DataStore> for HandOffDataHandle {
2 => {
let entry = DataStore::DataVaultSticky48(
ColdResetEntry48::try_from(self.reg_num() as u8).unwrap_or_else(|_| {
report_handoff_error_and_halt("Invalid ColdResetEntry48", 0xbadbad)
report_handoff_error_and_halt(
"Invalid ColdResetEntry48",
CaliptraError::DRIVER_HANDOFF_INVALID_COLD_RESET_ENTRY48.into(),
)
}),
);
Ok(entry)
Expand All @@ -118,7 +132,10 @@ impl TryInto<DataStore> for HandOffDataHandle {
3 => {
let entry =
WarmResetEntry4::try_from(self.reg_num() as u8).unwrap_or_else(|_| {
report_handoff_error_and_halt("Invalid WarmResetEntry4", 0xbadbad)
report_handoff_error_and_halt(
"Invalid WarmResetEntry4",
CaliptraError::DRIVER_HANDOFF_INVALID_WARM_RESET_ENTRY4.into(),
)
});

let ds = DataStore::DataVaultNonSticky4(entry);
Expand All @@ -128,7 +145,10 @@ impl TryInto<DataStore> for HandOffDataHandle {
4 => {
let entry = DataStore::DataVaultNonSticky48(
WarmResetEntry48::try_from(self.reg_num() as u8).unwrap_or_else(|_| {
report_handoff_error_and_halt("Invalid WarmResetEntry48", 0xbadbad)
report_handoff_error_and_halt(
"Invalid WarmResetEntry48",
CaliptraError::DRIVER_HANDOFF_INVALID_WARM_RESET_ENTRY48.into(),
)
}),
);
Ok(entry)
Expand Down
51 changes: 47 additions & 4 deletions drivers/src/lms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,12 +379,12 @@ impl Lms {

/// Note: Use this function only if glitch protection is not needed.
/// If glitch protection is needed, use `verify_lms_signature_cfi` instead.
pub fn verify_lms_signature<const N: usize, const P: usize, const H: usize>(
pub fn verify_lms_signature(
&self,
sha256_driver: &mut Sha256,
input_string: &[u8],
lms_public_key: &LmsPublicKey<N>,
lms_sig: &LmsSignature<N, P, H>,
lms_public_key: &LmsPublicKey<6>,
lms_sig: &LmsSignature<6, 51, 15>,
) -> CaliptraResult<LmsResult> {
let mut candidate_key =
self.verify_lms_signature_cfi(sha256_driver, input_string, lms_public_key, lms_sig)?;
Expand All @@ -397,7 +397,50 @@ impl Lms {
result
}

pub fn verify_lms_signature_cfi<const N: usize, const P: usize, const H: usize>(
/// Note: Use this function only if glitch protection is not needed.
/// If glitch protection is needed, use `verify_lms_signature_cfi_generic` instead.
pub fn verify_lms_signature_generic<const N: usize, const P: usize, const H: usize>(
&self,
sha256_driver: &mut Sha256,
input_string: &[u8],
lms_public_key: &LmsPublicKey<N>,
lms_sig: &LmsSignature<N, P, H>,
) -> CaliptraResult<LmsResult> {
let mut candidate_key = self.verify_lms_signature_cfi_generic(
sha256_driver,
input_string,
lms_public_key,
lms_sig,
)?;
let result = if candidate_key != HashValue::from(lms_public_key.digest) {
Ok(LmsResult::SigVerifyFailed)
} else {
Ok(LmsResult::Success)
};
candidate_key.0.fill(0);
result
}

// When callers from separate crates call a function like
// verify_lms_signature_cfi_generic(), Rustc 1.70
// may build multiple versions (depending on optimizer heuristics), even when all the
// generic parameters are identical. This is bad, as it can bloat the binary and the
// second copy violates the FIPS requirements that the same machine code be used for the
// KAT as the actual implementation. To defend against it, we provide this non-generic
// function that production firmware should call instead.
#[inline(never)]
pub fn verify_lms_signature_cfi(
&self,
sha256_driver: &mut Sha256,
input_string: &[u8],
lms_public_key: &LmsPublicKey<6>,
lms_sig: &LmsSignature<6, 51, 15>,
) -> CaliptraResult<HashValue<6>> {
self.verify_lms_signature_cfi_generic(sha256_driver, input_string, lms_public_key, lms_sig)
}

#[inline(always)]
pub fn verify_lms_signature_cfi_generic<const N: usize, const P: usize, const H: usize>(
&self,
sha256_driver: &mut Sha256,
input_string: &[u8],
Expand Down
5 changes: 5 additions & 0 deletions drivers/src/memory_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ pub const STACK_SIZE: u32 = 22 * 1024;
pub const ESTACK_SIZE: u32 = 1024;
pub const NSTACK_SIZE: u32 = 1024;

pub const ICCM_RANGE: core::ops::Range<u32> = core::ops::Range {
start: ICCM_ORG,
end: ICCM_ORG + ICCM_SIZE,
};

#[test]
#[allow(clippy::assertions_on_constants)]
fn mem_layout_test_manifest() {
Expand Down
8 changes: 8 additions & 0 deletions drivers/src/soc_ifc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,14 @@ impl SocIfc {
let soc_ifc_regs = self.soc_ifc.regs_mut();
soc_ifc_regs.cptra_fw_rev_id().at(1).write(|_| rt_version);
}

pub fn get_version(&self) -> [u32; 3] {
[
u32::from(self.soc_ifc.regs().cptra_hw_rev_id().read()),
self.soc_ifc.regs().cptra_fw_rev_id().at(0).read(),
self.soc_ifc.regs().cptra_fw_rev_id().at(1).read(),
]
}
}

bitflags::bitflags! {
Expand Down
6 changes: 3 additions & 3 deletions drivers/test-fw/src/bin/lms_32_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,12 +551,12 @@ fn test_lms_lower_32() {
};

let final_result = Lms::default()
.verify_lms_signature(&mut sha256, &MESSAGE, &LMS_PUBLIC_KEY, &FINAL_LMS_SIG)
.verify_lms_signature_generic(&mut sha256, &MESSAGE, &LMS_PUBLIC_KEY, &FINAL_LMS_SIG)
.unwrap();
assert_eq!(final_result, LmsResult::Success);

let candidate_key = Lms::default()
.verify_lms_signature_cfi(&mut sha256, &MESSAGE, &LMS_PUBLIC_KEY, &FINAL_LMS_SIG)
.verify_lms_signature_cfi_generic(&mut sha256, &MESSAGE, &LMS_PUBLIC_KEY, &FINAL_LMS_SIG)
.unwrap();
assert_eq!(candidate_key, HashValue::from(LMS_PUBLIC_KEY.digest));
}
Expand Down Expand Up @@ -815,7 +815,7 @@ fn test_hss_upper_32() {
};

let result = Lms::default()
.verify_lms_signature(
.verify_lms_signature_generic(
&mut sha256,
&PUBLIC_BUFFER,
&HSS_PUBLIC_KEY,
Expand Down
2 changes: 1 addition & 1 deletion drivers/test-fw/src/bin/negative_tests_lms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ fn test_failures_lms_24() {
);

assert_eq!(
Lms::default().verify_lms_signature(
Lms::default().verify_lms_signature_generic(
&mut sha256,
&MESSAGE,
&LmsPublicKey {
Expand Down
15 changes: 15 additions & 0 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,17 @@ impl CaliptraError {
pub const DRIVER_CSRNG_ADAPTP_HEALTH_CHECK_FAILED: CaliptraError =
CaliptraError::new_const(0x000d0008);

pub const DRIVER_HANDOFF_INVALID_VAULT: CaliptraError = CaliptraError::new_const(0x000D100);
pub const DRIVER_HANDOFF_INVALID_KEY_ID: CaliptraError = CaliptraError::new_const(0x000D101);
pub const DRIVER_HANDOFF_INVALID_COLD_RESET_ENTRY4: CaliptraError =
CaliptraError::new_const(0x000D102);
pub const DRIVER_HANDOFF_INVALID_COLD_RESET_ENTRY48: CaliptraError =
CaliptraError::new_const(0x000D103);
pub const DRIVER_HANDOFF_INVALID_WARM_RESET_ENTRY4: CaliptraError =
CaliptraError::new_const(0x000D104);
pub const DRIVER_HANDOFF_INVALID_WARM_RESET_ENTRY48: CaliptraError =
CaliptraError::new_const(0x000D104);

/// Runtime Errors
pub const RUNTIME_INTERNAL: CaliptraError = CaliptraError::new_const(0x000E0001);
pub const RUNTIME_UNIMPLEMENTED_COMMAND: CaliptraError = CaliptraError::new_const(0x000E0002);
Expand All @@ -312,6 +323,10 @@ impl CaliptraError {
pub const RUNTIME_GET_DEVID_CERT_FAILED: CaliptraError = CaliptraError::new_const(0x000E0013);
pub const RUNTIME_CERT_CHAIN_CREATION_FAILED: CaliptraError =
CaliptraError::new_const(0x000E0014);
pub const RUNTIME_SELF_TEST_IN_PROGRESS: CaliptraError = CaliptraError::new_const(0x000E0015);
pub const RUNTIME_SELF_TEST_NOT_STARTED: CaliptraError = CaliptraError::new_const(0x000E0016);
pub const RUNTIME_INVALID_FMC_SIZE: CaliptraError = CaliptraError::new_const(0x000E0017);
pub const RUNTIME_INVALID_RUNTIME_SIZE: CaliptraError = CaliptraError::new_const(0x000E0018);

/// FMC Errors
pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
Expand Down
3 changes: 2 additions & 1 deletion kat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ doctest = false
[dependencies]
caliptra-drivers.workspace = true
caliptra-lms-types.workspace = true
zerocopy.workspace = true
zerocopy.workspace = true
ufmt.workspace = true
Loading

0 comments on commit ab9a518

Please sign in to comment.