Repository: chipsalliance/caliptra-sw
Pull request #402 (2023-06-20 19:20:39)

Packet abstraction and checksum utilities
This commit implements checksum utilities for mailbox commands as defined by the runtime specification.


Patch:
diff --git a/common/src/checksum.rs b/common/src/checksum.rs
new file mode 100644
index 00000000..255575d6
--- /dev/null
+++ b/common/src/checksum.rs
@@ -0,0 +1,33 @@
+// Licensed under the Apache-2.0 license
+
+/// Verify checksum
+pub fn verify_checksum(checksum: i32, cmd: u32, data: &[u8]) -> bool {
+    calc_checksum(cmd, data) - checksum == 0
+}
+
+/// Calculate the checksum
+/// 0 - (SUM(command code bytes) + SUM(request/response bytes))
+pub fn calc_checksum(cmd: u32, data: &[u8]) -> i32 {
+    let mut checksum = 0i32;
+    for c in cmd.to_le_bytes().iter() {
+        checksum += *c as i32;
+    }
+    for d in data {
+        checksum += *d as i32;
+    }
+
+    checksum
+}
+
+#[cfg(all(test, target_family = "unix"))]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_verify_checksum() {
+        let cmd = 0x00000001u32;
+        let data = [0x00000000u32; 1];
+        let checksum = calc_checksum(cmd, data[0].to_le_bytes().as_ref());
+        assert!(verify_checksum(checksum, cmd, &data[0].to_le_bytes()));
+    }
+}
diff --git a/common/src/lib.rs b/common/src/lib.rs
index 3de744ee..25dec6a3 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -8,6 +8,7 @@ pub mod hand_off;
 #[macro_use]
 pub mod printer;
 pub mod boot_status;
+pub mod checksum;
 pub mod fuse;
 pub mod pcr;
 
diff --git a/error/src/lib.rs b/error/src/lib.rs
index 56ab4cfc..8330ce5d 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -271,6 +271,7 @@ impl CaliptraError {
     pub const RUNTIME_UNIMPLEMENTED_COMMAND: CaliptraError = CaliptraError::new_const(0x000E0002);
     pub const RUNTIME_INSUFFICIENT_MEMORY: CaliptraError = CaliptraError::new_const(0x000E0003);
     pub const RUNTIME_ECDSA_VERIF_FAILED: CaliptraError = CaliptraError::new_const(0x000E0004);
+    pub const RUNTIME_INVALID_CHECKSUM: CaliptraError = CaliptraError::new_const(0x000E0005);
 
     /// FMC Errors
     pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 06323337..20f0dfcd 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -10,6 +10,9 @@ pub mod mailbox;
 
 use mailbox::Mailbox;
 
+pub mod packet;
+use packet::Packet;
+
 use caliptra_common::cprintln;
 use caliptra_drivers::{CaliptraError, CaliptraResult, DataVault, Ecc384};
 use caliptra_registers::{
@@ -66,7 +69,7 @@ impl Drivers {
 #[repr(C)]
 #[derive(AsBytes, FromBytes)]
 pub struct EcdsaVerifyCmd {
-    pub chksum: u32,
+    pub chksum: i32,
     pub pub_key_x: [u8; 48],
     pub pub_key_y: [u8; 48],
     pub signature_r: [u8; 48],
@@ -94,29 +97,18 @@ fn wait_for_cmd(_mbox: &mut Mailbox) {
 }
 
 fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
-    let mbox = &mut drivers.mbox;
-
-    let cmd_id = mbox.cmd();
-    let dlen = mbox.dlen() as usize;
-    let dlen_words = mbox.dlen_words() as usize;
-    let mut buf = [0u32; 1024];
-    mbox.copy_from_mbox(
-        buf.get_mut(..dlen_words)
-            .ok_or(CaliptraError::RUNTIME_INTERNAL)?,
-    );
+    let packet = Packet::copy_from_mbox(drivers)?;
 
-    if dlen > buf.len() * 4 {
-        // dlen larger than max message
-        Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)?;
-    }
+    cprintln!(
+        "[rt] Received command=0x{:x}, len={}",
+        packet.cmd,
+        packet.len
+    );
 
-    let cmd_bytes = buf
-        .as_bytes()
-        .get(..dlen)
-        .ok_or(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)?;
+    // Get the command bytes
+    let cmd_bytes = packet.as_bytes()?;
 
-    cprintln!("[rt] Received command=0x{:x}, len={}", cmd_id, mbox.dlen());
-    match CommandId::from(cmd_id) {
+    match CommandId::from(packet.cmd) {
         CommandId::FIRMWARE_LOAD => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
         CommandId::GET_IDEV_CSR => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
         CommandId::GET_LDEV_CERT => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
diff --git a/runtime/src/packet.rs b/runtime/src/packet.rs
new file mode 100644
index 00000000..3b960d38
--- /dev/null
+++ b/runtime/src/packet.rs
@@ -0,0 +1,60 @@
+// Licensed under the Apache-2.0 license
+
+// License by Apache-2.0
+use caliptra_drivers::CaliptraResult;
+
+use caliptra_drivers::CaliptraError;
+use zerocopy::AsBytes;
+
+#[derive(Debug, Clone)]
+pub struct Packet {
+    pub cmd: u32,
+    pub payload: [u32; MAX_PAYLOAD_SIZE],
+    pub len: usize,
+}
+
+const MAX_PAYLOAD_SIZE: usize = 1024;
+
+impl Default for Packet {
+    fn default() -> Self {
+        Self {
+            cmd: 0,
+            payload: [0u32; MAX_PAYLOAD_SIZE],
+            len: 0,
+        }
+    }
+}
+
+impl Packet {
+    pub fn copy_from_mbox(drivers: &mut crate::Drivers) -> CaliptraResult<Self> {
+        let mbox = &mut drivers.mbox;
+        let cmd = mbox.cmd();
+        let dlen_words = mbox.dlen_words() as usize;
+
+        if dlen_words > MAX_PAYLOAD_SIZE {
+            return Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY);
+        }
+
+        let mut packet = Packet {
+            cmd,
+            len: mbox.dlen() as usize,
+            ..Default::default()
+        };
+
+        mbox.copy_from_mbox(
+            packet
+                .payload
+                .get_mut(..dlen_words)
+                .ok_or(CaliptraError::RUNTIME_INTERNAL)?,
+        );
+
+        Ok(packet)
+    }
+
+    pub fn as_bytes(&self) -> CaliptraResult<&[u8]> {
+        self.payload
+            .as_bytes()
+            .get(..self.len)
+            .ok_or(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)
+    }
+}
diff --git a/runtime/src/verify.rs b/runtime/src/verify.rs
index daacf00f..3fb80457 100644
--- a/runtime/src/verify.rs
+++ b/runtime/src/verify.rs
@@ -1,14 +1,24 @@
 // Licensed under the Apache-2.0 license
 
-use crate::{Drivers, EcdsaVerifyCmd};
+use crate::{CommandId, Drivers, EcdsaVerifyCmd};
 use caliptra_drivers::{
     Array4x12, CaliptraError, CaliptraResult, Ecc384PubKey, Ecc384Scalar, Ecc384Signature,
 };
 use zerocopy::FromBytes;
 
+/// Start of payload (skips checksum field).
+const PAYLOAD_OFFSET: usize = 4;
+
 /// Handle the `ECDSA384_SIGNATURE_VERIFY` mailbox command
 pub(crate) fn handle_ecdsa_verify(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult<()> {
     if let Some(cmd) = EcdsaVerifyCmd::read_from(cmd_args) {
+        if !caliptra_common::checksum::verify_checksum(
+            cmd.chksum,
+            CommandId::ECDSA384_VERIFY.into(),
+            &cmd_args[PAYLOAD_OFFSET..],
+        ) {
+            return Err(CaliptraError::RUNTIME_INVALID_CHECKSUM);
+        }
         // Won't panic, full_digest is always larger than digest
         let full_digest = drivers.sha_acc.regs().digest().read();
         let mut digest = Array4x12::default();


Test patch:
diff --git a/runtime/tests/integration_tests.rs b/runtime/tests/integration_tests.rs
index 2baa8dc3..05137425 100644
--- a/runtime/tests/integration_tests.rs
+++ b/runtime/tests/integration_tests.rs
@@ -161,11 +161,38 @@ fn test_verify_cmd() {
         ],
     };
 
+    let checksum = caliptra_common::checksum::calc_checksum(
+        u32::from(CommandId::ECDSA384_VERIFY),
+        &cmd.as_bytes()[4..],
+    );
+
+    let cmd = EcdsaVerifyCmd {
+        chksum: checksum,
+        ..cmd
+    };
+
     let resp = model
         .mailbox_execute(u32::from(CommandId::ECDSA384_VERIFY), cmd.as_bytes())
         .unwrap();
     assert!(resp.is_none());
     assert_eq!(model.soc_ifc().cptra_fw_error_non_fatal().read(), 0);
+
+    let cmd = EcdsaVerifyCmd { chksum: 0, ..cmd };
+
+    // Make sure the command execution fails.
+    let resp = model
+        .mailbox_execute(u32::from(CommandId::ECDSA384_VERIFY), cmd.as_bytes())
+        .unwrap_err();
+    if let ModelError::MailboxCmdFailed(code) = resp {
+        assert_eq!(
+            code,
+            caliptra_drivers::CaliptraError::RUNTIME_INVALID_CHECKSUM.into()
+        );
+    }
+    assert_eq!(
+        model.soc_ifc().cptra_fw_error_non_fatal().read(),
+        caliptra_drivers::CaliptraError::RUNTIME_INVALID_CHECKSUM.into()
+    );
 }
 
 #[test]