Repository: chipsalliance/caliptra-sw
Pull request #196 (2023-04-24 18:25:20)

[feat] Store TBS in DCCM
This change stores the "To be signed" portions of ldevid and fmclias certificates stored in the DCCM on a cold boot. The change also adds a test to generate the idevid, ldevid and fmcalias certificates using these tbs and validates the certificate chain. 


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index a20f8e22..78eab196 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -406,6 +406,8 @@ dependencies = [
  "caliptra-x509",
  "caliptra_common",
  "cfg-if",
+ "hex",
+ "openssl",
  "ufmt",
  "zerocopy",
 ]
@@ -414,7 +416,9 @@ dependencies = [
 name = "caliptra-rom-test-fmc"
 version = "0.1.0"
 dependencies = [
+ "caliptra-cpu",
  "caliptra-drivers",
+ "caliptra-x509",
  "caliptra_common",
  "cfg-if",
  "ufmt",
@@ -425,6 +429,7 @@ dependencies = [
 name = "caliptra-rom-test-rt"
 version = "0.1.0"
 dependencies = [
+ "caliptra-cpu",
  "caliptra-drivers",
  "cfg-if",
  "ufmt",
diff --git a/rom/dev/Cargo.toml b/rom/dev/Cargo.toml
index 2907eb25..5bade5d7 100644
--- a/rom/dev/Cargo.toml
+++ b/rom/dev/Cargo.toml
@@ -15,8 +15,11 @@ caliptra_common = { path = "../../common", default-features = false }
 ufmt = "0.2.0"
 zerocopy = "0.6.1"
 
+
 [build-dependencies]
 cfg-if = "1.0.0"
+hex = "0.4.3"
+
 
 [dev-dependencies]
 caliptra-builder = { path = "../../builder" }
@@ -26,6 +29,8 @@ caliptra-image-openssl = { path = "../../image/openssl" }
 caliptra-image-elf = { path = "../../image/elf" }
 caliptra-image-fake-keys = { path = "../../image/fake-keys" }
 caliptra-image-types = { path = "../../image/types" }
+hex = "0.4.3"
+openssl = "0.10"
 
 [features]
 riscv = []
diff --git a/rom/dev/src/flow/cold_reset/fmc_alias.rs b/rom/dev/src/flow/cold_reset/fmc_alias.rs
index cdff8009..7a591dc2 100644
--- a/rom/dev/src/flow/cold_reset/fmc_alias.rs
+++ b/rom/dev/src/flow/cold_reset/fmc_alias.rs
@@ -18,6 +18,7 @@ use core::mem::ManuallyDrop;
 use super::crypto::{Crypto, Ecc384KeyPair};
 use super::dice::{DiceInput, DiceLayer, DiceOutput};
 use super::x509::X509;
+use crate::flow::cold_reset::{copy_tbs, TbsType};
 use crate::verifier::RomImageVerificationEnv;
 use crate::{cprint, cprint_slice, cprintln, pcr};
 use crate::{rom_env::RomEnv, rom_err_def};
@@ -410,6 +411,9 @@ impl FmcAliasLayer {
         // Lock the FMC Public key in the data vault until next boot
         env.data_vault().map(|d| d.set_fmc_pub_key(pub_key));
 
+        //  Copy TBS to DCCM.
+        copy_tbs(tbs.tbs(), TbsType::FmcaliasTbs)?;
+
         Ok(())
     }
 }
diff --git a/rom/dev/src/flow/cold_reset/idev_id.rs b/rom/dev/src/flow/cold_reset/idev_id.rs
index ba74f57e..eede098a 100644
--- a/rom/dev/src/flow/cold_reset/idev_id.rs
+++ b/rom/dev/src/flow/cold_reset/idev_id.rs
@@ -242,10 +242,12 @@ impl InitDevIdLayer {
             raise_err!(CsrVerify);
         }
 
-        let _pub_x: [u8; 48] = key_pair.pub_key.x.into();
-        let _pub_y: [u8; 48] = key_pair.pub_key.y.into();
-        cprint_slice!("[idev] PUB.X", _pub_x);
-        cprint_slice!("[idev] PUB.Y", _pub_y);
+        // [TODO] Due to printing of the CSR, rom sections are hitting max limits.
+        // Add this back when CSR printing is removed from here and added to test cases.
+        // let _pub_x: [u8; 48] = key_pair.pub_key.x.into();
+        // let _pub_y: [u8; 48] = key_pair.pub_key.y.into();
+        // cprint_slice!("[idev] PUB.X", _pub_x);
+        // cprint_slice!("[idev] PUB.Y", _pub_y);
 
         let _sig_r: [u8; 48] = sig.r.into();
         let _sig_s: [u8; 48] = sig.s.into();
@@ -257,6 +259,7 @@ impl InitDevIdLayer {
         let csr_bldr =
             Ecdsa384CsrBuilder::new(tbs.tbs(), &sig.to_ecdsa()).ok_or(err_u32!(CsrBuilderInit))?;
         let csr_len = csr_bldr.build(&mut csr).ok_or(err_u32!(CsrBuilderBuild))?;
+        cprint_slice!("[idev] CSR", csr);
 
         // Execute Send CSR Flow
         Self::send_csr(env, InitDevIdCsr::new(&csr, csr_len))
diff --git a/rom/dev/src/flow/cold_reset/ldev_id.rs b/rom/dev/src/flow/cold_reset/ldev_id.rs
index 387b0660..090c918e 100644
--- a/rom/dev/src/flow/cold_reset/ldev_id.rs
+++ b/rom/dev/src/flow/cold_reset/ldev_id.rs
@@ -18,6 +18,8 @@ use super::dice::*;
 use super::x509::*;
 use crate::cprint_slice;
 use crate::cprintln;
+use crate::flow::cold_reset::copy_tbs;
+use crate::flow::cold_reset::TbsType;
 use crate::rom_env::RomEnv;
 use crate::rom_err_def;
 use caliptra_drivers::*;
@@ -189,6 +191,9 @@ impl LocalDevIdLayer {
         // cold reset
         env.data_vault().map(|d| d.set_ldev_dice_pub_key(pub_key));
 
+        //  Copy TBS to DCCM.
+        copy_tbs(tbs.tbs(), TbsType::LdevidTbs)?;
+
         Ok(())
     }
 }
diff --git a/rom/dev/src/flow/cold_reset/mod.rs b/rom/dev/src/flow/cold_reset/mod.rs
index 6d39a4fe..4ec88239 100644
--- a/rom/dev/src/flow/cold_reset/mod.rs
+++ b/rom/dev/src/flow/cold_reset/mod.rs
@@ -32,6 +32,15 @@ use crypto::Ecc384KeyPair;
 pub const KEY_ID_CDI: KeyId = KeyId::KeyId6;
 pub const KEY_ID_PRIV_KEY: KeyId = KeyId::KeyId7;
 
+extern "C" {
+    static mut LDEVID_TBS_ORG: u8;
+    static mut FMCALIAS_TBS_ORG: u8;
+}
+
+pub enum TbsType {
+    LdevidTbs = 0,
+    FmcaliasTbs = 1,
+}
 /// Cold Reset Flow
 pub struct ColdResetFlow {}
 
@@ -72,3 +81,19 @@ impl ColdResetFlow {
         Ok(fht::make_fht(env))
     }
 }
+
+pub fn copy_tbs(tbs: &[u8], tbs_type: TbsType) -> CaliptraResult<()> {
+    let dst = match tbs_type {
+        TbsType::LdevidTbs => unsafe {
+            let ptr = &mut LDEVID_TBS_ORG as *mut u8;
+            core::slice::from_raw_parts_mut(ptr, tbs.len())
+        },
+        TbsType::FmcaliasTbs => unsafe {
+            let ptr = &mut FMCALIAS_TBS_ORG as *mut u8;
+            core::slice::from_raw_parts_mut(ptr, tbs.len())
+        },
+    };
+
+    dst[..tbs.len()].copy_from_slice(tbs);
+    Ok(())
+}
diff --git a/rom/dev/src/rom.ld b/rom/dev/src/rom.ld
index 5b05f58e..8e211015 100644
--- a/rom/dev/src/rom.ld
+++ b/rom/dev/src/rom.ld
@@ -16,42 +16,51 @@ OUTPUT_ARCH(riscv)
 OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv")
 ENTRY(_start)
 
-ROM_ORG     = 0x00000000;
-MBOX_ORG    = 0x30000000;
-ICCM_ORG    = 0x40000000;
-DCCM_ORG    = 0x50000000;
-MAN1_ORG    = 0x50000000;
-MAN2_ORG    = 0x50001000;
-FHT_ORG     = 0x50002000;
-DATA_ORG    = 0x50003000;
-STACK_ORG   = 0x5001C000;
-ESTACK_ORG  = 0x5001F800;
-NSTACK_ORG  = 0x5001FC00;
-
-ROM_SIZE    = 32K;
-MBOX_SIZE   = 128K;
-ICCM_SIZE   = 128K;
-DCCM_SIZE   = 128K;
-MAN1_SIZE   = 4K;
-MAN2_SIZE   = 4K;
-FHT_SIZE    = 4K;
-DATA_SIZE   = 100K;
-STACK_SIZE  = 14K;
-ESTACK_SIZE = 1K;
-NSTACK_SIZE = 1K;
+ROM_ORG          = 0x00000000;
+MBOX_ORG         = 0x30000000;
+ICCM_ORG         = 0x40000000;
+DCCM_ORG         = 0x50000000;
+MAN1_ORG         = 0x50000000;
+MAN2_ORG         = 0x50001000;
+FHT_ORG          = 0x50002000;
+LDEVID_TBS_ORG   = 0x50003000;
+FMCALIAS_TBS_ORG = 0x50003400;
+PCR_LOG_ORG      = 0x50003800;
+DATA_ORG         = 0x50004000;
+STACK_ORG        = 0x5001C000;
+ESTACK_ORG       = 0x5001F800;
+NSTACK_ORG       = 0x5001FC00;
+
+ROM_SIZE          = 32K;
+MBOX_SIZE         = 128K;
+ICCM_SIZE         = 128K;
+DCCM_SIZE         = 128K;
+MAN1_SIZE         = 4K;
+MAN2_SIZE         = 4K;
+FHT_SIZE          = 4K;
+LDEVID_TBS_SIZE   = 1K;
+FMCALIAS_TBS_SIZE = 1K;
+PCR_LOG_SIZE      = 2K;
+DATA_SIZE         = 96K;
+STACK_SIZE        = 14K;
+ESTACK_SIZE       = 1K;
+NSTACK_SIZE       = 1K;
 
 MEMORY
 {
-	ROM    (rx) : ORIGIN = ROM_ORG,    LENGTH = ROM_SIZE
-	MBOX   (rw) : ORIGIN = MBOX_ORG,   LENGTH = MBOX_SIZE
-	ICCM   (rx) : ORIGIN = ICCM_ORG,   LENGTH = ICCM_SIZE
-	MAN1   (rw) : ORIGIN = MAN1_ORG,   LENGTH = MAN1_SIZE
-	MAN2   (rw) : ORIGIN = MAN2_ORG,   LENGTH = MAN2_SIZE
-	FHT    (rw) : ORIGIN = FHT_ORG,    LENGTH = FHT_SIZE
-	DATA   (rw) : ORIGIN = DATA_ORG,   LENGTH = DATA_SIZE
-	STACK  (rw) : ORIGIN = STACK_ORG,  LENGTH = STACK_SIZE
-	ESTACK (rw) : ORIGIN = ESTACK_ORG, LENGTH = ESTACK_SIZE
-	NSTACK (rw) : ORIGIN = NSTACK_ORG, LENGTH = NSTACK_SIZE
+	ROM          (rx) : ORIGIN = ROM_ORG,          LENGTH = ROM_SIZE
+	MBOX         (rw) : ORIGIN = MBOX_ORG,         LENGTH = MBOX_SIZE
+	ICCM         (rx) : ORIGIN = ICCM_ORG,         LENGTH = ICCM_SIZE
+	MAN1         (rw) : ORIGIN = MAN1_ORG,         LENGTH = MAN1_SIZE
+	MAN2         (rw) : ORIGIN = MAN2_ORG,         LENGTH = MAN2_SIZE
+	FHT          (rw) : ORIGIN = FHT_ORG,          LENGTH = FHT_SIZE
+	LDEVID_TBS   (rw) : ORIGIN = LDEVID_TBS_ORG,   LENGTH = LDEVID_TBS_SIZE
+	FMCALIAS_TBS (rw) : ORIGIN = FMCALIAS_TBS_ORG, LENGTH = FMCALIAS_TBS_SIZE
+	PCR_LOG      (rw) : ORIGIN = PCR_LOG_ORG,      LENGTH = PCR_LOG_SIZE
+	DATA         (rw) : ORIGIN = DATA_ORG,         LENGTH = DATA_SIZE
+	STACK        (rw) : ORIGIN = STACK_ORG,        LENGTH = STACK_SIZE
+	ESTACK       (rw) : ORIGIN = ESTACK_ORG,       LENGTH = ESTACK_SIZE
+	NSTACK       (rw) : ORIGIN = NSTACK_ORG,       LENGTH = NSTACK_SIZE
 } 
 
 SECTIONS 


Test patch:
diff --git a/rom/dev/tests/test_image_validation.rs b/rom/dev/tests/test_image_validation.rs
index 78c0dea5..3d662af9 100644
--- a/rom/dev/tests/test_image_validation.rs
+++ b/rom/dev/tests/test_image_validation.rs
@@ -1,6 +1,8 @@
 // Licensed under the Apache-2.0 license
 
+use caliptra_builder::FwId;
 use caliptra_builder::{ImageOptions, APP_WITH_UART, FMC_WITH_UART, ROM_WITH_UART};
+use caliptra_drivers::state::MfgFlags;
 use caliptra_drivers::Array4x12;
 use caliptra_hw_model::{
     BootParams, DeviceLifecycle, Fuses, HwModel, InitParams, ModelError, SecurityState, U4,
@@ -12,6 +14,15 @@ use caliptra_image_fake_keys::{
 use caliptra_image_gen::{ImageGenerator, ImageGeneratorConfig, ImageGeneratorVendorConfig};
 use caliptra_image_openssl::OsslCrypto;
 use caliptra_image_types::{ImageBundle, ImageManifest, VENDOR_ECC_KEY_COUNT};
+use openssl::asn1::Asn1Integer;
+use openssl::asn1::Asn1Time;
+use openssl::bn::BigNum;
+use openssl::hash::MessageDigest;
+use openssl::pkey::{PKey, Private};
+use openssl::rsa::Rsa;
+use openssl::x509::X509Req;
+use openssl::x509::X509;
+use std::str;
 use zerocopy::AsBytes;
 
 mod helpers;
@@ -1178,6 +1189,65 @@ fn test_runtime_svn_less_than_fuse_svn() {
     );
 }
 
+#[test]
+fn cert_test() {
+    pub const TEST_FMC_WITH_UART: FwId = FwId {
+        crate_name: "caliptra-rom-test-fmc",
+        bin_name: "caliptra-rom-test-fmc",
+        features: &["emu"],
+    };
+
+    let fuses = Fuses::default();
+    let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART).unwrap();
+    let mut hw = caliptra_hw_model::new(BootParams {
+        init_params: InitParams {
+            rom: &rom,
+            security_state: SecurityState::from(fuses.life_cycle as u32),
+            ..Default::default()
+        },
+        fuses,
+        fw_image: None,
+    })
+    .unwrap();
+
+    let image_bundle = caliptra_builder::build_and_sign_image(
+        &TEST_FMC_WITH_UART,
+        &APP_WITH_UART,
+        ImageOptions::default(),
+    )
+    .unwrap();
+
+    let mut output = vec![];
+
+    // Set gen_idev_id_csr to generate CSR.
+    let flags = MfgFlags::GENERATE_IDEVID_CSR;
+    hw.soc_ifc()
+        .cptra_dbg_manuf_service_reg()
+        .write(|_| flags.bits());
+
+    #[cfg(feature = "verilator")]
+    {
+        // [TODO] Download the CSR from the mailbox and set the gen_idev_id_csr bit 0.
+    }
+
+    hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw());
+    hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+        .unwrap();
+
+    let result = hw.copy_output_until_exit_success(&mut output);
+    assert!(result.is_ok());
+    let output = String::from_utf8_lossy(&output);
+
+    // Get the idevid cert.
+    let idevid_cert = idevid_cert(&output);
+
+    // Get the ldevid cert.
+    let ldevid_cert = ldevid_cert(&idevid_cert, &output);
+
+    // Get the fmclias cert.
+    fmcalias_cert(&ldevid_cert, &output);
+}
+
 fn update_header(image_bundle: &mut ImageBundle) {
     let opts = ImageOptions::default();
     let config = ImageGeneratorConfig {
@@ -1277,3 +1347,181 @@ fn generate_image_bytes(image_bundle: &mut ImageBundle) -> Vec<u8> {
     image.extend_from_slice(&image_bundle.runtime);
     image
 }
+
+fn generate_self_signed_cert() -> (X509, PKey<Private>) {
+    let mut x509_name = openssl::x509::X509NameBuilder::new().unwrap();
+    x509_name
+        .append_entry_by_text("CN", "Caliptra Test")
+        .unwrap();
+    let x509_name = x509_name.build();
+
+    let mut x509_builder = openssl::x509::X509::builder().unwrap();
+    x509_builder.set_subject_name(&x509_name).unwrap();
+
+    // Set serial number.
+    let big_num = BigNum::from_u32(1).unwrap();
+    let serial_number = Asn1Integer::from_bn(big_num.as_ref()).unwrap();
+    x509_builder
+        .set_serial_number(serial_number.as_ref())
+        .unwrap();
+
+    // Set validity.
+    let not_valid_before = Asn1Time::days_from_now(0).unwrap();
+    let not_valid_after = Asn1Time::days_from_now(30).unwrap();
+    x509_builder
+        .set_not_before(not_valid_before.as_ref())
+        .unwrap();
+    x509_builder
+        .set_not_after(not_valid_after.as_ref())
+        .unwrap();
+
+    // Generate a pkey pair.
+    let rsa = Rsa::generate(2048).unwrap();
+    let pkey_pair = PKey::from_rsa(rsa).unwrap();
+
+    // Set public key.
+    x509_builder.set_pubkey(pkey_pair.as_ref()).unwrap();
+
+    // Since this is a self-signed certificate, we need to set the name of the
+    // issuer to the name of the subject.
+    x509_builder.set_issuer_name(x509_name.as_ref()).unwrap();
+
+    // Sign the certificate with the private key.
+    let hash = MessageDigest::md5();
+    x509_builder.sign(pkey_pair.as_ref(), hash).unwrap();
+
+    // Get the cert.
+    let cert = x509_builder.build();
+    println!("{}", str::from_utf8(&cert.to_text().unwrap()).unwrap());
+    (cert, pkey_pair)
+}
+
+fn idevid_cert(output: &str) -> X509 {
+    // Get CSR
+    let csr_str = get_data("[idev] CSR = ", output);
+    let csr = hex::decode(csr_str).unwrap();
+
+    // Verify the signature on the certificate is valid.
+    let req: X509Req = X509Req::from_der(&csr).unwrap();
+    println!(
+        "CSR:\n {}",
+        str::from_utf8(&req.to_text().unwrap()).unwrap()
+    );
+    assert!(req.verify(&req.public_key().unwrap()).unwrap());
+
+    // Generate a self-signed CA cert and get the corresponding key-pair.
+    let (ca_cert, ca_pkey_pair) = generate_self_signed_cert();
+
+    //
+    // Create the idevid certificate.
+    //
+    let mut x509_builder = openssl::x509::X509::builder().unwrap();
+
+    // Set the Subject Name from the CSR.
+    x509_builder.set_subject_name(req.subject_name()).unwrap();
+
+    // Set the Issue Name from the CA cert.
+    x509_builder.set_issuer_name(ca_cert.issuer_name()).unwrap();
+
+    // Set serial number.
+    let big_num = BigNum::from_u32(1).unwrap();
+    let serial_number = Asn1Integer::from_bn(big_num.as_ref()).unwrap();
+    x509_builder
+        .set_serial_number(serial_number.as_ref())
+        .unwrap();
+
+    // Set public key from the CSR.
+    x509_builder.set_pubkey(&req.public_key().unwrap()).unwrap();
+
+    // Set cert validity.
+    let not_valid_before = Asn1Time::days_from_now(0).unwrap();
+    let not_valid_after = Asn1Time::days_from_now(1).unwrap();
+    x509_builder
+        .set_not_before(not_valid_before.as_ref())
+        .unwrap();
+    x509_builder
+        .set_not_after(not_valid_after.as_ref())
+        .unwrap();
+
+    // Sign the cert with the CA's private key.
+    x509_builder
+        .sign(ca_pkey_pair.as_ref(), MessageDigest::md5())
+        .unwrap();
+
+    // Get the cert.
+    let idevid_cert = x509_builder.build();
+    println!(
+        "IDEVID Cert from CSR:\n{}",
+        str::from_utf8(&idevid_cert.to_text().unwrap()).unwrap()
+    );
+    idevid_cert
+}
+
+fn ldevid_cert(idevd_cert: &X509, output: &str) -> X509 {
+    // Get the ldevid cert
+    let ldevid_cert =
+        X509::from_der(&hex::decode(get_data("[fmc] LDEVID cert = ", output)).unwrap()).unwrap();
+    println!(
+        "LDEVID Cert:\n{}",
+        str::from_utf8(&ldevid_cert.to_text().unwrap()).unwrap()
+    );
+
+    // Get ldevid public key
+    let pub_key_from_dv = hex::decode(get_data("[fmc] LDEVID PUBLIC KEY DER = ", output)).unwrap();
+
+    // Verify the signature on the cert is valid.
+    let pub_key_from_cert = ldevid_cert
+        .public_key()
+        .as_ref()
+        .unwrap()
+        .public_key_to_der()
+        .unwrap();
+    assert_eq!(pub_key_from_dv, pub_key_from_cert[23..]);
+
+    // Verify the ldevid cert using idevid cert's public key.
+    assert!(ldevid_cert
+        .verify(idevd_cert.public_key().as_ref().unwrap())
+        .unwrap());
+
+    ldevid_cert
+}
+
+fn fmcalias_cert(ldevid_cert: &X509, output: &str) {
+    // Get the ldevid cert
+    let fmcalias_cert =
+        X509::from_der(&hex::decode(get_data("[fmc] FMCALIAS cert = ", output)).unwrap()).unwrap();
+    println!(
+        "FMCALIAS Cert:\n {}",
+        str::from_utf8(&fmcalias_cert.to_text().unwrap()).unwrap()
+    );
+
+    // Get fmclias public key
+    let pub_key_from_dv =
+        hex::decode(get_data("[fmc] FMCALIAS PUBLIC KEY DER = ", output)).unwrap();
+
+    // Verify the signature on the cert is valid.
+    let pub_key_from_cert = fmcalias_cert
+        .public_key()
+        .as_ref()
+        .unwrap()
+        .public_key_to_der()
+        .unwrap();
+    assert_eq!(pub_key_from_dv, pub_key_from_cert[23..]);
+
+    // Verify the ldevid cert using idevid cert's public key.
+    assert!(fmcalias_cert
+        .verify(ldevid_cert.public_key().as_ref().unwrap())
+        .unwrap());
+}
+
+fn get_data(to_match: &str, haystack: &str) -> String {
+    let mut index = haystack.find(to_match).unwrap();
+    index += to_match.len();
+    let mut str = String::new();
+    while haystack.chars().nth(index).unwrap() != '\n' {
+        str.push(haystack.chars().nth(index).unwrap());
+        index += 1;
+    }
+
+    str
+}
diff --git a/rom/dev/tools/test-fmc/Cargo.toml b/rom/dev/tools/test-fmc/Cargo.toml
index 1b6829f5..338f78a3 100644
--- a/rom/dev/tools/test-fmc/Cargo.toml
+++ b/rom/dev/tools/test-fmc/Cargo.toml
@@ -10,6 +10,8 @@ caliptra-drivers = { path = "../../../../drivers" }
 caliptra_common = { path = "../../../../common", default-features = false }
 ufmt = "0.2.0"
 zerocopy = "0.6.1"
+caliptra-x509 = { version = "0.1.0", path = "../../../../x509" , default-features = false }
+caliptra-cpu = { version = "0.1.0", path = "../../../../cpu" }
 
 [build-dependencies]
 cfg-if = "1.0.0"
@@ -17,4 +19,5 @@ cfg-if = "1.0.0"
 [features]
 default = ["std"]
 emu = ["caliptra-drivers/emu"]
+riscv = ["caliptra-cpu/riscv"]
 std = ["ufmt/std", "caliptra_common/std"]
diff --git a/rom/dev/tools/test-fmc/src/fmc.ld b/rom/dev/tools/test-fmc/src/fmc.ld
index 54e75ffd..e77b5acc 100644
--- a/rom/dev/tools/test-fmc/src/fmc.ld
+++ b/rom/dev/tools/test-fmc/src/fmc.ld
@@ -16,39 +16,48 @@ OUTPUT_ARCH(riscv)
 OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv")
 ENTRY(_start)
 
-MBOX_ORG    = 0x30000000;
-ICCM_ORG    = 0x40000000;
-DCCM_ORG    = 0x50000000;
-MAN1_ORG    = 0x50000000;
-MAN2_ORG    = 0x50001000;
-FHT_ORG     = 0x50002000;
-DATA_ORG    = 0x50003000;
-STACK_ORG   = 0x5001C000;
-ESTACK_ORG  = 0x5001F800;
-NSTACK_ORG  = 0x5001FC00;
-
-MBOX_SIZE   = 128K;
-ICCM_SIZE   = 8K;
-DCCM_SIZE   = 128K;
-MAN1_SIZE   = 4K;
-MAN2_SIZE   = 4K;
-FHT_SIZE    = 4K;
-DATA_SIZE   = 100K;
-STACK_SIZE  = 14K;
-ESTACK_SIZE = 1K;
-NSTACK_SIZE = 1K;
+MBOX_ORG         = 0x30000000;
+ICCM_ORG         = 0x40000000;
+DCCM_ORG         = 0x50000000;
+MAN1_ORG         = 0x50000000;
+MAN2_ORG         = 0x50001000;
+FHT_ORG          = 0x50002000;
+LDEVID_TBS_ORG   = 0x50003000;
+FMCALIAS_TBS_ORG = 0x50003400;
+PCR_LOG_ORG      = 0x50003800;
+DATA_ORG         = 0x50004000;
+STACK_ORG        = 0x5001C000;
+ESTACK_ORG       = 0x5001F800;
+NSTACK_ORG       = 0x5001FC00;
+
+MBOX_SIZE         = 128K;
+ICCM_SIZE         = 8K;
+DCCM_SIZE         = 128K;
+MAN1_SIZE         = 4K;
+MAN2_SIZE         = 4K;
+FHT_SIZE          = 4K;
+LDEVID_TBS_SIZE   = 1K;
+FMCALIAS_TBS_SIZE = 1K;
+PCR_LOG_SIZE      = 2K;
+DATA_SIZE         = 96K;
+STACK_SIZE        = 14K;
+ESTACK_SIZE       = 1K;
+NSTACK_SIZE       = 1K;
 
 MEMORY
 {
-	MBOX   (rw) : ORIGIN = MBOX_ORG,   LENGTH = MBOX_SIZE
-	ICCM   (rx) : ORIGIN = ICCM_ORG,   LENGTH = ICCM_SIZE
-	MAN1   (rw) : ORIGIN = MAN1_ORG,   LENGTH = MAN1_SIZE
-	MAN2   (rw) : ORIGIN = MAN2_ORG,   LENGTH = MAN2_SIZE
-	FHT    (rw) : ORIGIN = FHT_ORG,    LENGTH = FHT_SIZE
-	DATA   (rw) : ORIGIN = DATA_ORG,   LENGTH = DATA_SIZE
-	STACK  (rw) : ORIGIN = STACK_ORG,  LENGTH = STACK_SIZE
-	ESTACK (rw) : ORIGIN = ESTACK_ORG, LENGTH = ESTACK_SIZE
-	NSTACK (rw) : ORIGIN = NSTACK_ORG, LENGTH = NSTACK_SIZE
+	MBOX         (rw) : ORIGIN = MBOX_ORG,         LENGTH = MBOX_SIZE
+	ICCM         (rx) : ORIGIN = ICCM_ORG,         LENGTH = ICCM_SIZE
+	MAN1         (rw) : ORIGIN = MAN1_ORG,         LENGTH = MAN1_SIZE
+	MAN2         (rw) : ORIGIN = MAN2_ORG,         LENGTH = MAN2_SIZE
+	FHT          (rw) : ORIGIN = FHT_ORG,          LENGTH = FHT_SIZE
+	LDEVID_TBS   (rw) : ORIGIN = LDEVID_TBS_ORG,   LENGTH = LDEVID_TBS_SIZE
+	FMCALIAS_TBS (rw) : ORIGIN = FMCALIAS_TBS_ORG, LENGTH = FMCALIAS_TBS_SIZE
+	PCR_LOG      (rw) : ORIGIN = PCR_LOG_ORG,      LENGTH = PCR_LOG_SIZE
+	DATA         (rw) : ORIGIN = DATA_ORG,         LENGTH = DATA_SIZE
+	STACK        (rw) : ORIGIN = STACK_ORG,        LENGTH = STACK_SIZE
+	ESTACK       (rw) : ORIGIN = ESTACK_ORG,       LENGTH = ESTACK_SIZE
+	NSTACK       (rw) : ORIGIN = NSTACK_ORG,       LENGTH = NSTACK_SIZE
 } 
 
 SECTIONS 
diff --git a/rom/dev/tools/test-fmc/src/main.rs b/rom/dev/tools/test-fmc/src/main.rs
index 8a398d24..17767ffc 100644
--- a/rom/dev/tools/test-fmc/src/main.rs
+++ b/rom/dev/tools/test-fmc/src/main.rs
@@ -14,8 +14,15 @@ Abstract:
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(not(feature = "std"), no_main)]
 
+extern "C" {
+    static mut LDEVID_TBS_ORG: u8;
+    static mut FMCALIAS_TBS_ORG: u8;
+}
+
 use caliptra_common::FirmwareHandoffTable;
+use caliptra_drivers::DataVault;
 use caliptra_drivers::Mailbox;
+use caliptra_x509::{Ecdsa384CertBuilder, Ecdsa384Signature, FmcAliasCertTbs, LocalDevIdCertTbs};
 use zerocopy::FromBytes;
 
 #[cfg(not(feature = "std"))]
@@ -65,6 +72,8 @@ pub extern "C" fn fmc_entry() -> ! {
         fht.rt_fw_load_addr_idx
     );
 
+    create_certs();
+
     caliptra_drivers::ExitCtrl::exit(0)
 }
 
@@ -113,3 +122,76 @@ fn fmc_panic(_: &core::panic::PanicInfo) -> ! {
 
     loop {}
 }
+
+fn create_certs() {
+    //
+    // Create LDEVID cert.
+    //
+
+    // Retrieve the public key and signature from the data vault.
+    let data_vault = DataVault::default();
+    let ldevid_pub_key = data_vault.ldev_dice_pub_key();
+    let mut _pub_der: [u8; 97] = ldevid_pub_key.to_der();
+    cprint_slice!("[fmc] LDEVID PUBLIC KEY DER", _pub_der);
+
+    let sig = data_vault.ldev_dice_signature();
+
+    let ecdsa_sig = Ecdsa384Signature {
+        r: sig.r.into(),
+        s: sig.s.into(),
+    };
+    let mut tbs: [u8; core::mem::size_of::<LocalDevIdCertTbs>()] =
+        [0u8; core::mem::size_of::<LocalDevIdCertTbs>()];
+    copy_tbs(&mut tbs, true);
+
+    let mut cert: [u8; 651] = [0u8; 651];
+    let builder = Ecdsa384CertBuilder::new(
+        &tbs[..core::mem::size_of::<LocalDevIdCertTbs>()],
+        &ecdsa_sig,
+    )
+    .unwrap();
+    let _cert_len = builder.build(&mut cert).unwrap();
+    cprint_slice!("[fmc] LDEVID cert", cert);
+
+    //
+    // Create FMCALIAS cert.
+    //
+
+    // Retrieve the public key and signature from the data vault.
+    let fmcalias_pub_key = data_vault.fmc_pub_key();
+    let _pub_der: [u8; 97] = fmcalias_pub_key.to_der();
+    cprint_slice!("[fmc] FMCALIAS PUBLIC KEY DER", _pub_der);
+
+    let sig = data_vault.fmc_dice_signature();
+    let ecdsa_sig = Ecdsa384Signature {
+        r: sig.r.into(),
+        s: sig.s.into(),
+    };
+
+    let mut tbs: [u8; core::mem::size_of::<FmcAliasCertTbs>()] =
+        [0u8; core::mem::size_of::<FmcAliasCertTbs>()];
+    copy_tbs(&mut tbs, false);
+
+    let mut cert: [u8; 818] = [0u8; 818];
+    let builder =
+        Ecdsa384CertBuilder::new(&tbs[..core::mem::size_of::<FmcAliasCertTbs>()], &ecdsa_sig)
+            .unwrap();
+    let _cert_len = builder.build(&mut cert).unwrap();
+    cprint_slice!("[fmc] FMCALIAS cert", cert);
+}
+
+fn copy_tbs(tbs: &mut [u8], ldevid_tbs: bool) {
+    // Copy the tbs from DCCM
+    let src = if ldevid_tbs {
+        unsafe {
+            let ptr = &mut LDEVID_TBS_ORG as *mut u8;
+            core::slice::from_raw_parts_mut(ptr, tbs.len())
+        }
+    } else {
+        unsafe {
+            let ptr = &mut FMCALIAS_TBS_ORG as *mut u8;
+            core::slice::from_raw_parts_mut(ptr, tbs.len())
+        }
+    };
+    tbs.copy_from_slice(src);
+}
diff --git a/rom/dev/tools/test-rt/Cargo.toml b/rom/dev/tools/test-rt/Cargo.toml
index 3edb40d1..5012709d 100644
--- a/rom/dev/tools/test-rt/Cargo.toml
+++ b/rom/dev/tools/test-rt/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2021"
 [dependencies]
 caliptra-drivers = { path = "../../../../drivers" }
 ufmt = "0.2.0"
+caliptra-cpu = { version = "0.1.0", path = "../../../../cpu" }
 
 [build-dependencies]
 cfg-if = "1.0.0"
@@ -15,4 +16,5 @@ cfg-if = "1.0.0"
 [features]
 default = ["std"]
 emu = ["caliptra-drivers/emu"]
+riscv = ["caliptra-cpu/riscv"]
 std = ["ufmt/std"]