Repository: chipsalliance/caliptra-sw
Pull request #293 (2023-05-19 00:57:29)

Add rt functions to retrieve and build certs produced by ROM
Get the LDevID and FmcAlias certs produced by ROM and stich them together to ensure runtime can properly produce these certificates.

jhand2: Note: I can wait to merge this PR until https://github.com/chipsalliance/caliptra-sw/pull/277 is merged

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 103e8122..2a4dc671 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -508,8 +508,10 @@ dependencies = [
  "caliptra-image-serde",
  "caliptra-image-types",
  "caliptra-registers",
+ "caliptra-x509",
  "caliptra_common",
  "cfg-if",
+ "openssl",
  "ufmt",
  "zerocopy",
 ]
diff --git a/fmc/memory.x b/fmc/memory.x
index f1e2135d..ff98d9b0 100644
--- a/fmc/memory.x
+++ b/fmc/memory.x
@@ -5,7 +5,7 @@ META_ORG  = ROM_SIZE;
 ICCM_ORG  = 0x40000000;
 DCCM_ORG  = 0x50000000;
 FHT_ORG   = 0x50002000;
-DATA_ORG  = 0x50003000;
+DATA_ORG  = 0x50004000;
 STACK_ORG = 0x5001C000;
 ESTACK_ORG  = 0x5001F800;
 NSTACK_ORG  = 0x5001FC00;
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index c81acdd7..a4f6f5de 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -10,6 +10,7 @@ caliptra-cpu = { version = "0.1.0", path = "../cpu" }
 caliptra-drivers = { path = "../drivers" }
 caliptra-registers = { path = "../registers" }
 caliptra_common = { path = "../common", default-features = false }
+caliptra-x509 = { path = "../x509", default-features = false }
 ufmt = "0.2.0"
 zerocopy = "0.6.1"
 
@@ -25,6 +26,7 @@ caliptra-image-gen = { path = "../image/gen" }
 caliptra-image-openssl = { path = "../image/openssl" }
 caliptra-image-serde = { path = "../image/serde" }
 caliptra-image-types = { path = "../image/types" }
+openssl = "0.10"
 
 [features]
 riscv = ["caliptra-cpu/riscv"]
diff --git a/runtime/memory.x b/runtime/memory.x
index 62fd739b..3fbcff24 100644
--- a/runtime/memory.x
+++ b/runtime/memory.x
@@ -5,7 +5,9 @@ META_ORG  = ROM_SIZE;
 ICCM_ORG  = 0x40002000;
 DCCM_ORG  = 0x50000000;
 FHT_ORG   = 0x50002000;
-DATA_ORG  = 0x50003000;
+LDEVID_TBS_ORG   = 0x50003000;
+FMCALIAS_TBS_ORG = 0x50003400;
+DATA_ORG  = 0x50004000;
 STACK_ORG = 0x5001C000;
 ESTACK_ORG  = 0x5001F800;
 NSTACK_ORG  = 0x5001FC00;
@@ -18,6 +20,8 @@ DCCM_SIZE   = 128K;
 MAN1_SIZE   = 4K;
 MAN2_SIZE   = 4K;
 FHT_SIZE    = 4K;
+LDEVID_TBS_SIZE   = 1K;
+FMCALIAS_TBS_SIZE = 1K;
 DATA_SIZE   = 100K;
 STACK_SIZE  = 14K;
 ESTACK_SIZE = 1K;
@@ -30,6 +34,8 @@ MEMORY
 	META (r)  : ORIGIN = META_ORG, LENGTH = META_SIZE
 	ICCM (rx) : ORIGIN = ICCM_ORG, LENGTH = ICCM_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
 	DATA (rw) : ORIGIN = DATA_ORG, LENGTH = DATA_SIZE
 	STACK(rw) : ORIGIN = STACK_ORG,  LENGTH = STACK_SIZE
 	ESTACK (rw) : ORIGIN = ESTACK_ORG, LENGTH = ESTACK_SIZE
diff --git a/runtime/src/dice.rs b/runtime/src/dice.rs
new file mode 100644
index 00000000..0e54ee50
--- /dev/null
+++ b/runtime/src/dice.rs
@@ -0,0 +1,53 @@
+// Licensed under the Apache-2.0 license
+
+use crate::RuntimeErr;
+use caliptra_drivers::{CaliptraResult, DataVault};
+use caliptra_x509::{Ecdsa384CertBuilder, Ecdsa384Signature, FmcAliasCertTbs, LocalDevIdCertTbs};
+
+extern "C" {
+    static mut LDEVID_TBS_ORG: [u8; LocalDevIdCertTbs::TBS_TEMPLATE_LEN];
+    static mut FMCALIAS_TBS_ORG: [u8; FmcAliasCertTbs::TBS_TEMPLATE_LEN];
+}
+
+enum CertType {
+    LDevId,
+    FmcAlias,
+}
+
+/// Copy LDevID certificate produced by ROM to `cert` buffer
+///
+/// Returns the number of bytes written to `cert`
+pub fn copy_ldevid_cert(dv: &DataVault, cert: &mut [u8]) -> CaliptraResult<usize> {
+    cert_from_dccm(dv, cert, CertType::LDevId)
+}
+
+/// Copy FMC Alias certificate produced by ROM to `cert` buffer
+///
+/// Returns the number of bytes written to `cert`
+pub fn copy_fmc_alias_cert(dv: &DataVault, cert: &mut [u8]) -> CaliptraResult<usize> {
+    cert_from_dccm(dv, cert, CertType::FmcAlias)
+}
+
+/// Copy a certificate from `dccm_offset`, append signature, and write the
+/// output to `cert`.
+fn cert_from_dccm(dv: &DataVault, cert: &mut [u8], cert_type: CertType) -> CaliptraResult<usize> {
+    let (tbs, sig) = match cert_type {
+        CertType::LDevId => (unsafe { &LDEVID_TBS_ORG[..] }, dv.ldev_dice_signature()),
+        CertType::FmcAlias => (unsafe { &FMCALIAS_TBS_ORG[..] }, dv.fmc_dice_signature()),
+    };
+
+    // DataVault returns a different type than CertBuilder accepts
+    let bldr_sig = Ecdsa384Signature {
+        r: sig.r.into(),
+        s: sig.s.into(),
+    };
+    let Some(builder) = Ecdsa384CertBuilder::new(tbs, &bldr_sig) else {
+        return Err(RuntimeErr::InsufficientMemory.into());
+    };
+
+    let Some(size) = builder.build(cert) else {
+        return Err(RuntimeErr::InsufficientMemory.into());
+    };
+
+    Ok(size)
+}
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index c9f6d31c..66c70750 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -2,14 +2,18 @@
 
 #![no_std]
 
-mod mailbox;
+pub mod dice;
 mod verify;
 
+// Used by runtime tests
+pub mod mailbox;
+
 use mailbox::Mailbox;
 
 use caliptra_common::cprintln;
-use caliptra_drivers::{caliptra_err_def, CaliptraResult, Ecc384};
+use caliptra_drivers::{caliptra_err_def, CaliptraResult, DataVault, Ecc384};
 use caliptra_registers::{
+    dv::DvReg,
     ecc::EccReg,
     mbox::{enums::MboxStatusE, MboxCsr},
     sha512_acc::Sha512AccCsr,
@@ -54,6 +58,7 @@ pub struct Drivers {
     pub mbox: Mailbox,
     pub sha_acc: Sha512AccCsr,
     pub ecdsa: Ecc384,
+    pub data_vault: DataVault,
 }
 impl Drivers {
     /// # Safety
@@ -66,6 +71,7 @@ impl Drivers {
             mbox: Mailbox::new(MboxCsr::new()),
             sha_acc: Sha512AccCsr::new(),
             ecdsa: Ecc384::new(EccReg::new()),
+            data_vault: DataVault::new(DvReg::new()),
         }
     }
 }
diff --git a/runtime/src/mailbox.rs b/runtime/src/mailbox.rs
index e5713b0d..64b6b016 100644
--- a/runtime/src/mailbox.rs
+++ b/runtime/src/mailbox.rs
@@ -26,7 +26,7 @@ impl Mailbox {
     }
 
     // Set the length of the current mailbox data in bytes
-    pub fn _set_dlen(&mut self, len: u32) {
+    pub fn set_dlen(&mut self, len: u32) {
         let mbox = self.mbox.regs_mut();
         mbox.dlen().write(|_| len);
     }
@@ -48,7 +48,7 @@ impl Mailbox {
         }
     }
 
-    pub fn _copy_to_mbox(&mut self, buf: &[Unalign<u32>]) {
+    pub fn copy_to_mbox(&mut self, buf: &[Unalign<u32>]) {
         let mbox = self.mbox.regs_mut();
         for word in buf {
             mbox.datain().write(|_| word.get());
@@ -56,14 +56,14 @@ impl Mailbox {
     }
 
     /// Write a word-aligned `buf` to the mailbox
-    pub fn _write_response(&mut self, buf: &[u8]) -> CaliptraResult<()> {
+    pub fn write_response(&mut self, buf: &[u8]) -> CaliptraResult<()> {
         let Some(buf_words) = LayoutVerified::new_slice_unaligned(buf) else {
             // buf size is not a multiple of word size
             return Err(RuntimeErr::InternalErr.into());
         };
 
-        self._set_dlen(buf.len() as u32);
-        self._copy_to_mbox(&buf_words);
+        self.set_dlen(buf.len() as u32);
+        self.copy_to_mbox(&buf_words);
 
         Ok(())
     }
diff --git a/x509/build/code_gen.rs b/x509/build/code_gen.rs
index 24c1abc8..ec8bb7c4 100644
--- a/x509/build/code_gen.rs
+++ b/x509/build/code_gen.rs
@@ -84,7 +84,7 @@ impl CodeGen {
 
         let tbs_len = template.tbs().len();
         let tbs_len_const = quote!(
-            const TBS_TEMPLATE_LEN: usize = #tbs_len;
+            pub const TBS_TEMPLATE_LEN: usize = #tbs_len;
         );
 
         let tbs = template.tbs();


Test patch:
diff --git a/runtime/test-fw/Cargo.toml b/runtime/test-fw/Cargo.toml
index 5c007dfd..81b6b646 100644
--- a/runtime/test-fw/Cargo.toml
+++ b/runtime/test-fw/Cargo.toml
@@ -28,6 +28,11 @@ name = "mbox"
 path = "src/mbox_tests.rs"
 required-features = ["riscv"]
 
+[[bin]]
+name = "cert"
+path = "src/cert_tests.rs"
+required-features = ["riscv"]
+
 [build-dependencies]
 cfg-if = "1.0.0"
 
diff --git a/runtime/test-fw/src/cert_tests.rs b/runtime/test-fw/src/cert_tests.rs
new file mode 100644
index 00000000..1fc21b20
--- /dev/null
+++ b/runtime/test-fw/src/cert_tests.rs
@@ -0,0 +1,46 @@
+// Licensed under the Apache-2.0 license
+
+//! Send DICE certificates over the mailbox
+
+#![no_main]
+#![no_std]
+
+use caliptra_registers::mbox::enums::MboxStatusE;
+use caliptra_runtime::{dice, Drivers};
+use caliptra_test_harness::{runtime_handlers, test_suite};
+
+fn mbox_responder() {
+    let drivers = unsafe { Drivers::new_from_registers() };
+    let mut mbox = drivers.mbox;
+
+    loop {
+        while !mbox.is_cmd_ready() {
+            // Wait for a request from the SoC.
+        }
+        let cmd = mbox.cmd();
+
+        match cmd {
+            // Send LDevID Cert
+            0x1000_0000 => {
+                let mut ldev = [0u8; 1024];
+                dice::copy_ldevid_cert(&drivers.data_vault, &mut ldev).unwrap();
+                mbox.write_response(&ldev).unwrap();
+                mbox.set_status(MboxStatusE::DataReady);
+            }
+            // Send FMC Alias Cert
+            0x2000_0000 => {
+                let mut fmc = [0u8; 1024];
+                dice::copy_fmc_alias_cert(&drivers.data_vault, &mut fmc).unwrap();
+                mbox.write_response(&fmc).unwrap();
+                mbox.set_status(MboxStatusE::DataReady);
+            }
+            _ => {
+                mbox.set_status(MboxStatusE::CmdFailure);
+            }
+        }
+    }
+}
+
+test_suite! {
+    mbox_responder,
+}
diff --git a/runtime/tests/integration_tests.rs b/runtime/tests/integration_tests.rs
index 01ebbc36..e23a99ee 100644
--- a/runtime/tests/integration_tests.rs
+++ b/runtime/tests/integration_tests.rs
@@ -3,6 +3,7 @@
 use caliptra_builder::{FwId, ImageOptions, APP_WITH_UART, FMC_WITH_UART, ROM_WITH_UART};
 use caliptra_hw_model::{BootParams, DefaultHwModel, HwModel, InitParams, ModelError, ShaAccMode};
 use caliptra_runtime::{CommandId, EcdsaVerifyCmd};
+use openssl::x509::X509;
 use zerocopy::AsBytes;
 
 // Run test_bin as a ROM image. The is used for faster tests that can run
@@ -83,6 +84,28 @@ fn test_boot() {
     model.step_until_exit_success().unwrap();
 }
 
+#[test]
+fn test_rom_certs() {
+    let mut model = run_rt_test(Some("cert"));
+
+    // Get certs over the mailbox
+    let ldev_resp = model.mailbox_execute(0x1000_0000, &[]).unwrap().unwrap();
+    let ldevid: &[u8] = ldev_resp.as_bytes();
+
+    let fmc_resp = model.mailbox_execute(0x2000_0000, &[]).unwrap().unwrap();
+    let fmc: &[u8] = fmc_resp.as_bytes();
+
+    // Ensure certs are valid X.509
+    let ldev_cert: X509 = X509::from_der(ldevid).unwrap();
+    let fmc_cert: X509 = X509::from_der(fmc).unwrap();
+
+    // Check the FMC is signed by LDevID
+    assert!(fmc_cert.verify(&ldev_cert.public_key().unwrap()).unwrap());
+
+    // TODO: Check that LDevID is signed by IDevID
+    // Runtime does not currently have access to the IDevID public key.
+}
+
 #[test]
 fn test_verify_cmd() {
     let mut model = run_rom_test("mbox");