Repository: chipsalliance/caliptra-sw (rust)

Issue:
The allow truncating request for invoke_dpe mailbox command
`INVOKE_DPE` as-defined is supposed to allow truncating requests/responses to avoid writing extraneous zero bytes to the mailbox. The command handler needs to be updated to allow that.

Pull request description:
Truncate zeroes from mailbox req/resp
1. Implement as_bytes_partial{_mut} for all mailbox resp structs containing variable length arrays so that we don't write extraneous zeroes to the mailbox.
2. For all mailbox req structs containing variable length arrays, parse them from bytes by creating a default struct and copying the bytes into the prefix, since read_from will fail if extraneous zeroes are omitted from the req struct.

Fixes #1092 

Patch:
diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs
index a2a70a46..11177c46 100644
--- a/api/src/mailbox.rs
+++ b/api/src/mailbox.rs
@@ -9,9 +9,9 @@ pub struct CommandId(pub u32);
 impl CommandId {
     pub const FIRMWARE_LOAD: Self = Self(0x46574C44); // "FWLD"
     pub const GET_IDEV_CSR: Self = Self(0x49444556); // "IDEV"
-    pub const GET_IDEV_CERT: Self = Self(0x49444543); // IDEC
-    pub const GET_IDEV_INFO: Self = Self(0x49444549); // IDEI
-    pub const POPULATE_IDEV_CERT: Self = Self(0x49444550); // IDEP
+    pub const GET_IDEV_CERT: Self = Self(0x49444543); // "IDEC"
+    pub const GET_IDEV_INFO: Self = Self(0x49444549); // "IDEI"
+    pub const POPULATE_IDEV_CERT: Self = Self(0x49444550); // "IDEP"
     pub const GET_LDEV_CERT: Self = Self(0x4C444556); // "LDEV"
     pub const ECDSA384_VERIFY: Self = Self(0x53494756); // "SIGV"
     pub const STASH_MEASUREMENT: Self = Self(0x4D454153); // "MEAS"
@@ -72,6 +72,45 @@ where
     }
 }
 
+#[repr(C)]
+#[derive(Debug, AsBytes, Default, FromBytes, PartialEq, Eq)]
+pub struct MailboxRespHeaderVarSize {
+    pub hdr: MailboxRespHeader,
+    pub data_len: u32,
+}
+pub trait ResponseVarSize: AsBytes + FromBytes {
+    fn data(&self) -> CaliptraResult<&[u8]> {
+        // Will panic if sizeof<Self>() is smaller than MailboxRespHeaderVarSize
+        // or Self doesn't have compatible alignment with
+        // MailboxRespHeaderVarSize (should be impossible if MailboxRespHeaderVarSize is the first field)
+        let (hdr, data) =
+            LayoutVerified::<_, MailboxRespHeaderVarSize>::new_from_prefix(self.as_bytes())
+                .ok_or(CaliptraError::RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE)?;
+        data.get(..hdr.data_len as usize)
+            .ok_or(CaliptraError::RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE)
+    }
+    fn partial_len(&self) -> CaliptraResult<usize> {
+        let (hdr, _) =
+            LayoutVerified::<_, MailboxRespHeaderVarSize>::new_from_prefix(self.as_bytes())
+                .ok_or(CaliptraError::RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE)?;
+        Ok(size_of::<MailboxRespHeaderVarSize>() + hdr.data_len as usize)
+    }
+    fn as_bytes_partial(&self) -> CaliptraResult<&[u8]> {
+        self.as_bytes()
+            .get(..self.partial_len()?)
+            .ok_or(CaliptraError::RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE)
+    }
+    fn as_bytes_partial_mut(&mut self) -> CaliptraResult<&mut [u8]> {
+        let partial_len = self.partial_len()?;
+        self.as_bytes_mut()
+            .get_mut(..partial_len)
+            .ok_or(CaliptraError::RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE)
+    }
+}
+impl<T: ResponseVarSize> Response for T {
+    const MIN_SIZE: usize = size_of::<MailboxRespHeaderVarSize>();
+}
+
 fn populate_checksum(msg: &mut [u8]) {
     let (checksum_bytes, payload_bytes) = msg.split_at_mut(size_of::<u32>());
     let checksum = crate::checksum::calc_checksum(0, payload_bytes);
@@ -96,47 +135,55 @@ pub enum MailboxResp {
 }
 
 impl MailboxResp {
-    pub fn as_bytes(&self) -> &[u8] {
+    pub fn as_bytes(&self) -> CaliptraResult<&[u8]> {
         match self {
-            MailboxResp::Header(resp) => resp.as_bytes(),
-            MailboxResp::GetIdevCert(resp) => resp.as_bytes(),
-            MailboxResp::GetIdevCsr(resp) => resp.as_bytes(),
-            MailboxResp::GetIdevInfo(resp) => resp.as_bytes(),
-            MailboxResp::GetLdevCert(resp) => resp.as_bytes(),
-            MailboxResp::StashMeasurement(resp) => resp.as_bytes(),
+            MailboxResp::Header(resp) => Ok(resp.as_bytes()),
+            MailboxResp::GetIdevCert(resp) => resp.as_bytes_partial(),
+            MailboxResp::GetIdevCsr(resp) => resp.as_bytes_partial(),
+            MailboxResp::GetIdevInfo(resp) => Ok(resp.as_bytes()),
+            MailboxResp::GetLdevCert(resp) => resp.as_bytes_partial(),
+            MailboxResp::StashMeasurement(resp) => Ok(resp.as_bytes()),
             MailboxResp::InvokeDpeCommand(resp) => resp.as_bytes_partial(),
-            MailboxResp::TestGetFmcAliasCert(resp) => resp.as_bytes(),
-            MailboxResp::FipsVersion(resp) => resp.as_bytes(),
-            MailboxResp::FwInfo(resp) => resp.as_bytes(),
-            MailboxResp::Capabilities(resp) => resp.as_bytes(),
+            MailboxResp::TestGetFmcAliasCert(resp) => resp.as_bytes_partial(),
+            MailboxResp::FipsVersion(resp) => Ok(resp.as_bytes()),
+            MailboxResp::FwInfo(resp) => Ok(resp.as_bytes()),
+            MailboxResp::Capabilities(resp) => Ok(resp.as_bytes()),
         }
     }
 
-    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
+    pub fn as_bytes_mut(&mut self) -> CaliptraResult<&mut [u8]> {
         match self {
-            MailboxResp::Header(resp) => resp.as_bytes_mut(),
-            MailboxResp::GetIdevCert(resp) => resp.as_bytes_mut(),
-            MailboxResp::GetIdevCsr(resp) => resp.as_bytes_mut(),
-            MailboxResp::GetIdevInfo(resp) => resp.as_bytes_mut(),
-            MailboxResp::GetLdevCert(resp) => resp.as_bytes_mut(),
-            MailboxResp::StashMeasurement(resp) => resp.as_bytes_mut(),
+            MailboxResp::Header(resp) => Ok(resp.as_bytes_mut()),
+            MailboxResp::GetIdevCert(resp) => resp.as_bytes_partial_mut(),
+            MailboxResp::GetIdevCsr(resp) => resp.as_bytes_partial_mut(),
+            MailboxResp::GetIdevInfo(resp) => Ok(resp.as_bytes_mut()),
+            MailboxResp::GetLdevCert(resp) => resp.as_bytes_partial_mut(),
+            MailboxResp::StashMeasurement(resp) => Ok(resp.as_bytes_mut()),
             MailboxResp::InvokeDpeCommand(resp) => resp.as_bytes_partial_mut(),
-            MailboxResp::TestGetFmcAliasCert(resp) => resp.as_bytes_mut(),
-            MailboxResp::FipsVersion(resp) => resp.as_bytes_mut(),
-            MailboxResp::FwInfo(resp) => resp.as_bytes_mut(),
-            MailboxResp::Capabilities(resp) => resp.as_bytes_mut(),
+            MailboxResp::TestGetFmcAliasCert(resp) => resp.as_bytes_partial_mut(),
+            MailboxResp::FipsVersion(resp) => Ok(resp.as_bytes_mut()),
+            MailboxResp::FwInfo(resp) => Ok(resp.as_bytes_mut()),
+            MailboxResp::Capabilities(resp) => Ok(resp.as_bytes_mut()),
         }
     }
 
     /// Calculate and set the checksum for a response payload
-    /// Takes into account the size override for variable-lenth payloads
+    /// Takes into account the size override for variable-length payloads
     pub fn populate_chksum(&mut self) -> CaliptraResult<()> {
         // Calc checksum, use the size override if provided
-        let checksum = crate::checksum::calc_checksum(0, &self.as_bytes()[size_of::<u32>()..]);
+        let resp_bytes = self.as_bytes()?;
+        if size_of::<u32>() >= resp_bytes.len() {
+            return Err(CaliptraError::RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE);
+        }
+        let checksum = crate::checksum::calc_checksum(0, &resp_bytes[size_of::<u32>()..]);
 
+        let mut_resp_bytes = self.as_bytes_mut()?;
+        if size_of::<MailboxRespHeader>() > mut_resp_bytes.len() {
+            return Err(CaliptraError::RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE);
+        }
         // cast as header struct
         let hdr: &mut MailboxRespHeader = LayoutVerified::<&mut [u8], MailboxRespHeader>::new(
-            &mut self.as_bytes_mut()[..size_of::<MailboxRespHeader>()],
+            &mut mut_resp_bytes[..size_of::<MailboxRespHeader>()],
         )
         .ok_or(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)?
         .into_mut();
@@ -164,6 +211,8 @@ pub enum MailboxReq {
     InvokeDpeCommand(InvokeDpeReq),
     FipsVersion(MailboxReqHeader),
     FwInfo(MailboxReqHeader),
+    PopulateIdevCert(PopulateIdevCertReq),
+    GetIdevCert(GetIdevCertReq),
 
     #[cfg(feature = "test_only_commands")]
     TestHmacVerify(HmacVerifyReq),
@@ -172,37 +221,41 @@ pub enum MailboxReq {
 }
 
 impl MailboxReq {
-    pub fn as_bytes(&self) -> &[u8] {
+    pub fn as_bytes(&self) -> CaliptraResult<&[u8]> {
         match self {
-            MailboxReq::EcdsaVerify(req) => req.as_bytes(),
-            MailboxReq::StashMeasurement(req) => req.as_bytes(),
-            MailboxReq::InvokeDpeCommand(req) => req.as_bytes(),
-            MailboxReq::FipsVersion(req) => req.as_bytes(),
-            MailboxReq::FwInfo(req) => req.as_bytes(),
-            MailboxReq::GetIdevCsr(req) => req.as_bytes(),
-            MailboxReq::GetLdevCert(req) => req.as_bytes(),
+            MailboxReq::EcdsaVerify(req) => Ok(req.as_bytes()),
+            MailboxReq::StashMeasurement(req) => Ok(req.as_bytes()),
+            MailboxReq::InvokeDpeCommand(req) => req.as_bytes_partial(),
+            MailboxReq::FipsVersion(req) => Ok(req.as_bytes()),
+            MailboxReq::FwInfo(req) => Ok(req.as_bytes()),
+            MailboxReq::GetIdevCsr(req) => Ok(req.as_bytes()),
+            MailboxReq::GetLdevCert(req) => Ok(req.as_bytes()),
+            MailboxReq::PopulateIdevCert(req) => req.as_bytes_partial(),
+            MailboxReq::GetIdevCert(req) => req.as_bytes_partial(),
 
             #[cfg(feature = "test_only_commands")]
-            MailboxReq::TestGetFmcAliasCert(req) => req.as_bytes(),
+            MailboxReq::TestGetFmcAliasCert(req) => Ok(req.as_bytes()),
             #[cfg(feature = "test_only_commands")]
-            MailboxReq::TestHmacVerify(req) => req.as_bytes(),
+            MailboxReq::TestHmacVerify(req) => Ok(req.as_bytes()),
         }
     }
 
-    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
+    pub fn as_bytes_mut(&mut self) -> CaliptraResult<&mut [u8]> {
         match self {
-            MailboxReq::EcdsaVerify(req) => req.as_bytes_mut(),
-            MailboxReq::GetIdevCsr(req) => req.as_bytes_mut(),
-            MailboxReq::GetLdevCert(req) => req.as_bytes_mut(),
-            MailboxReq::StashMeasurement(req) => req.as_bytes_mut(),
-            MailboxReq::InvokeDpeCommand(req) => req.as_bytes_mut(),
-            MailboxReq::FipsVersion(req) => req.as_bytes_mut(),
-            MailboxReq::FwInfo(req) => req.as_bytes_mut(),
+            MailboxReq::EcdsaVerify(req) => Ok(req.as_bytes_mut()),
+            MailboxReq::GetIdevCsr(req) => Ok(req.as_bytes_mut()),
+            MailboxReq::GetLdevCert(req) => Ok(req.as_bytes_mut()),
+            MailboxReq::StashMeasurement(req) => Ok(req.as_bytes_mut()),
+            MailboxReq::InvokeDpeCommand(req) => req.as_bytes_partial_mut(),
+            MailboxReq::FipsVersion(req) => Ok(req.as_bytes_mut()),
+            MailboxReq::FwInfo(req) => Ok(req.as_bytes_mut()),
+            MailboxReq::PopulateIdevCert(req) => req.as_bytes_partial_mut(),
+            MailboxReq::GetIdevCert(req) => req.as_bytes_partial_mut(),
 
             #[cfg(feature = "test_only_commands")]
-            MailboxReq::TestHmacVerify(req) => req.as_bytes_mut(),
+            MailboxReq::TestHmacVerify(req) => Ok(req.as_bytes_mut()),
             #[cfg(feature = "test_only_commands")]
-            MailboxReq::TestGetFmcAliasCert(req) => req.as_bytes_mut(),
+            MailboxReq::TestGetFmcAliasCert(req) => Ok(req.as_bytes_mut()),
         }
     }
 
@@ -215,6 +268,8 @@ impl MailboxReq {
             MailboxReq::InvokeDpeCommand(_) => CommandId::INVOKE_DPE,
             MailboxReq::FipsVersion(_) => CommandId::VERSION,
             MailboxReq::FwInfo(_) => CommandId::FW_INFO,
+            MailboxReq::PopulateIdevCert(_) => CommandId::POPULATE_IDEV_CERT,
+            MailboxReq::GetIdevCert(_) => CommandId::GET_IDEV_CERT,
 
             #[cfg(feature = "test_only_commands")]
             MailboxReq::TestHmacVerify(_) => CommandId::TEST_ONLY_HMAC384_VERIFY,
@@ -228,12 +283,12 @@ impl MailboxReq {
         // Calc checksum, use the size override if provided
         let checksum = crate::checksum::calc_checksum(
             self.cmd_code().into(),
-            &self.as_bytes()[size_of::<i32>()..],
+            &self.as_bytes()?[size_of::<i32>()..],
         );
 
         // cast as header struct
         let hdr: &mut MailboxReqHeader = LayoutVerified::<&mut [u8], MailboxReqHeader>::new(
-            &mut self.as_bytes_mut()[..size_of::<MailboxReqHeader>()],
+            &mut self.as_bytes_mut()?[..size_of::<MailboxReqHeader>()],
         )
         .ok_or(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)?
         .into_mut();
@@ -285,6 +340,17 @@ pub struct GetIdevCsrResp {
 impl GetIdevCsrResp {
     pub const DATA_MAX_SIZE: usize = 1024;
 }
+impl ResponseVarSize for GetIdevCsrResp {}
+
+impl Default for GetIdevCsrResp {
+    fn default() -> Self {
+        Self {
+            hdr: MailboxRespHeader::default(),
+            data_size: 0,
+            data: [0u8; GetIdevCsrResp::DATA_MAX_SIZE],
+        }
+    }
+}
 
 // GET_IDEV_CERT
 #[repr(C)]
@@ -298,6 +364,33 @@ pub struct GetIdevCertReq {
 }
 impl GetIdevCertReq {
     pub const DATA_MAX_SIZE: usize = 916; // Req max size = Resp max size - MAX_ECDSA384_SIG_LEN
+
+    pub fn as_bytes_partial(&self) -> CaliptraResult<&[u8]> {
+        if self.tbs_size as usize > Self::DATA_MAX_SIZE {
+            return Err(CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE);
+        }
+        let unused_byte_count = Self::DATA_MAX_SIZE - self.tbs_size as usize;
+        Ok(&self.as_bytes()[..size_of::<Self>() - unused_byte_count])
+    }
+
+    pub fn as_bytes_partial_mut(&mut self) -> CaliptraResult<&mut [u8]> {
+        if self.tbs_size as usize > Self::DATA_MAX_SIZE {
+            return Err(CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE);
+        }
+        let unused_byte_count = Self::DATA_MAX_SIZE - self.tbs_size as usize;
+        Ok(&mut self.as_bytes_mut()[..size_of::<Self>() - unused_byte_count])
+    }
+}
+impl Default for GetIdevCertReq {
+    fn default() -> Self {
+        Self {
+            hdr: MailboxReqHeader::default(),
+            tbs_size: 0,
+            signature_r: [0u8; 48],
+            signature_s: [0u8; 48],
+            tbs: [0u8; GetIdevCertReq::DATA_MAX_SIZE],
+        }
+    }
 }
 
 #[repr(C)]
@@ -310,6 +403,17 @@ pub struct GetIdevCertResp {
 impl GetIdevCertResp {
     pub const DATA_MAX_SIZE: usize = 1024;
 }
+impl ResponseVarSize for GetIdevCertResp {}
+
+impl Default for GetIdevCertResp {
+    fn default() -> Self {
+        Self {
+            hdr: MailboxRespHeader::default(),
+            cert_size: 0,
+            cert: [0u8; GetIdevCertResp::DATA_MAX_SIZE],
+        }
+    }
+}
 
 // GET_IDEV_INFO
 // No command-specific input args
@@ -342,14 +446,18 @@ pub struct GetLdevCertResp {
 }
 impl GetLdevCertResp {
     pub const DATA_MAX_SIZE: usize = 1024;
+}
+impl ResponseVarSize for GetLdevCertResp {}
 
-    pub fn data(&self) -> Option<&[u8]> {
-        self.data.get(..self.data_size as usize)
+impl Default for GetLdevCertResp {
+    fn default() -> Self {
+        Self {
+            hdr: MailboxRespHeader::default(),
+            data_size: 0,
+            data: [0u8; GetLdevCertResp::DATA_MAX_SIZE],
+        }
     }
 }
-impl Response for GetLdevCertResp {
-    const MIN_SIZE: usize = size_of::<MailboxRespHeader>() + size_of::<u32>();
-}
 
 // ECDSA384_SIGNATURE_VERIFY
 #[repr(C)]
@@ -410,7 +518,7 @@ impl Request for StashMeasurementReq {
 }
 
 #[repr(C)]
-#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
+#[derive(Debug, Default, AsBytes, FromBytes, PartialEq, Eq)]
 pub struct StashMeasurementResp {
     pub hdr: MailboxRespHeader,
     pub dpe_result: u32,
@@ -432,8 +540,23 @@ pub struct InvokeDpeReq {
 
 impl InvokeDpeReq {
     pub const DATA_MAX_SIZE: usize = 512;
-}
 
+    pub fn as_bytes_partial(&self) -> CaliptraResult<&[u8]> {
+        if self.data_size as usize > Self::DATA_MAX_SIZE {
+            return Err(CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE);
+        }
+        let unused_byte_count = Self::DATA_MAX_SIZE - self.data_size as usize;
+        Ok(&self.as_bytes()[..size_of::<Self>() - unused_byte_count])
+    }
+
+    pub fn as_bytes_partial_mut(&mut self) -> CaliptraResult<&mut [u8]> {
+        if self.data_size as usize > Self::DATA_MAX_SIZE {
+            return Err(CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE);
+        }
+        let unused_byte_count = Self::DATA_MAX_SIZE - self.data_size as usize;
+        Ok(&mut self.as_bytes_mut()[..size_of::<Self>() - unused_byte_count])
+    }
+}
 impl Default for InvokeDpeReq {
     fn default() -> Self {
         Self {
@@ -455,23 +578,10 @@ pub struct InvokeDpeResp {
     pub data_size: u32,
     pub data: [u8; InvokeDpeResp::DATA_MAX_SIZE], // variable length
 }
-
 impl InvokeDpeResp {
     pub const DATA_MAX_SIZE: usize = 2200;
-
-    fn as_bytes_partial(&self) -> &[u8] {
-        let unused_byte_count = Self::DATA_MAX_SIZE.saturating_sub(self.data_size as usize);
-        &self.as_bytes()[..size_of::<Self>() - unused_byte_count]
-    }
-
-    fn as_bytes_partial_mut(&mut self) -> &mut [u8] {
-        let unused_byte_count = Self::DATA_MAX_SIZE.saturating_sub(self.data_size as usize);
-        &mut self.as_bytes_mut()[..size_of::<Self>() - unused_byte_count]
-    }
-}
-impl Response for InvokeDpeResp {
-    const MIN_SIZE: usize = size_of::<MailboxRespHeader>() + size_of::<u32>();
 }
+impl ResponseVarSize for InvokeDpeResp {}
 
 impl Default for InvokeDpeResp {
     fn default() -> Self {
@@ -490,7 +600,7 @@ pub struct TestOnlyGetFmcAliasCertReq {
 }
 impl Request for TestOnlyGetFmcAliasCertReq {
     const ID: CommandId = CommandId::TEST_ONLY_GET_FMC_ALIAS_CERT;
-    type Resp = GetLdevCertResp;
+    type Resp = TestGetFmcAliasCertResp;
 }
 
 // TEST_ONLY_GET_FMC_ALIAS_CERT
@@ -504,9 +614,16 @@ pub struct TestGetFmcAliasCertResp {
 }
 impl TestGetFmcAliasCertResp {
     pub const DATA_MAX_SIZE: usize = 1024;
+}
+impl ResponseVarSize for TestGetFmcAliasCertResp {}
 
-    pub fn data(&self) -> Option<&[u8]> {
-        self.data.get(..self.data_size as usize)
+impl Default for TestGetFmcAliasCertResp {
+    fn default() -> Self {
+        Self {
+            hdr: MailboxRespHeader::default(),
+            data_size: 0,
+            data: [0u8; TestGetFmcAliasCertResp::DATA_MAX_SIZE],
+        }
     }
 }
 
@@ -562,6 +679,31 @@ pub struct PopulateIdevCertReq {
 }
 impl PopulateIdevCertReq {
     pub const MAX_CERT_SIZE: usize = 1024;
+
+    pub fn as_bytes_partial(&self) -> CaliptraResult<&[u8]> {
+        if self.cert_size as usize > Self::MAX_CERT_SIZE {
+            return Err(CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE);
+        }
+        let unused_byte_count = Self::MAX_CERT_SIZE - self.cert_size as usize;
+        Ok(&self.as_bytes()[..size_of::<Self>() - unused_byte_count])
+    }
+
+    pub fn as_bytes_partial_mut(&mut self) -> CaliptraResult<&mut [u8]> {
+        if self.cert_size as usize > Self::MAX_CERT_SIZE {
+            return Err(CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE);
+        }
+        let unused_byte_count = Self::MAX_CERT_SIZE - self.cert_size as usize;
+        Ok(&mut self.as_bytes_mut()[..size_of::<Self>() - unused_byte_count])
+    }
+}
+impl Default for PopulateIdevCertReq {
+    fn default() -> Self {
+        Self {
+            hdr: MailboxReqHeader::default(),
+            cert_size: 0,
+            cert: [0u8; PopulateIdevCertReq::MAX_CERT_SIZE],
+        }
+    }
 }
 
 #[cfg(test)]
diff --git a/error/src/lib.rs b/error/src/lib.rs
index db4c7be9..3e415896 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -360,6 +360,10 @@ impl CaliptraError {
         CaliptraError::new_const(0x000E0022);
     pub const RUNTIME_ADD_VALID_PAUSER_MEASUREMENT_TO_DPE_FAILED: CaliptraError =
         CaliptraError::new_const(0x000E0023);
+    pub const RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE: CaliptraError =
+        CaliptraError::new_const(0x000E0024);
+    pub const RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE: CaliptraError =
+        CaliptraError::new_const(0x000E0025);
 
     /// FMC Errors
     pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
diff --git a/runtime/src/dice.rs b/runtime/src/dice.rs
index 4e8bcb34..c4e5592c 100644
--- a/runtime/src/dice.rs
+++ b/runtime/src/dice.rs
@@ -18,11 +18,7 @@ pub struct GetLdevCertCmd;
 impl GetLdevCertCmd {
     #[cfg(feature = "test_only_commands")]
     pub(crate) fn execute(drivers: &mut Drivers) -> CaliptraResult<MailboxResp> {
-        let mut resp = GetLdevCertResp {
-            hdr: MailboxRespHeader::default(),
-            data_size: 0,
-            data: [0u8; GetLdevCertResp::DATA_MAX_SIZE],
-        };
+        let mut resp = GetLdevCertResp::default();
 
         resp.data_size = copy_ldevid_cert(
             &drivers.data_vault,
@@ -38,11 +34,7 @@ pub struct TestGetFmcAliasCertCmd;
 impl TestGetFmcAliasCertCmd {
     #[cfg(feature = "test_only_commands")]
     pub(crate) fn execute(drivers: &mut Drivers) -> CaliptraResult<MailboxResp> {
-        let mut resp = TestGetFmcAliasCertResp {
-            hdr: MailboxRespHeader::default(),
-            data_size: 0,
-            data: [0u8; TestGetFmcAliasCertResp::DATA_MAX_SIZE],
-        };
+        let mut resp = TestGetFmcAliasCertResp::default();
 
         resp.data_size = copy_fmc_alias_cert(
             &drivers.data_vault,
diff --git a/runtime/src/info.rs b/runtime/src/info.rs
index d5e02e67..3f702b47 100644
--- a/runtime/src/info.rs
+++ b/runtime/src/info.rs
@@ -8,7 +8,7 @@ use caliptra_common::mailbox_api::{
 };
 use caliptra_drivers::{CaliptraError, CaliptraResult};
 use caliptra_x509::{Ecdsa384CertBuilder, Ecdsa384Signature};
-use zerocopy::FromBytes;
+use zerocopy::{AsBytes, FromBytes};
 
 pub struct FwInfoCmd;
 impl FwInfoCmd {
@@ -52,7 +52,10 @@ impl IDevIdInfoCmd {
 pub struct IDevIdCertCmd;
 impl IDevIdCertCmd {
     pub(crate) fn execute(cmd_args: &[u8]) -> CaliptraResult<MailboxResp> {
-        if let Some(cmd) = GetIdevCertReq::read_from(cmd_args) {
+        if cmd_args.len() <= core::mem::size_of::<GetIdevCertReq>() {
+            let mut cmd = GetIdevCertReq::default();
+            cmd.as_bytes_mut()[..cmd_args.len()].copy_from_slice(cmd_args);
+
             // Validate tbs
             if cmd.tbs_size as usize > cmd.tbs.len() {
                 return Err(CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS);
@@ -86,7 +89,10 @@ impl IDevIdCertCmd {
 pub struct PopulateIDevIdCertCmd;
 impl PopulateIDevIdCertCmd {
     pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult<MailboxResp> {
-        if let Some(cmd) = PopulateIdevCertReq::read_from(cmd_args) {
+        if cmd_args.len() <= core::mem::size_of::<PopulateIdevCertReq>() {
+            let mut cmd = PopulateIdevCertReq::default();
+            cmd.as_bytes_mut()[..cmd_args.len()].copy_from_slice(cmd_args);
+
             let cert_size = cmd.cert_size as usize;
             if cert_size > cmd.cert.len() {
                 return Err(CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS);
diff --git a/runtime/src/invoke_dpe.rs b/runtime/src/invoke_dpe.rs
index d8dd2708..d65db3e1 100644
--- a/runtime/src/invoke_dpe.rs
+++ b/runtime/src/invoke_dpe.rs
@@ -12,7 +12,7 @@ use dpe::{
     response::{Response, ResponseHdr},
     DpeInstance,
 };
-use zerocopy::FromBytes;
+use zerocopy::{AsBytes, FromBytes};
 
 pub struct InvokeDpeCmd;
 impl InvokeDpeCmd {
@@ -20,7 +20,10 @@ impl InvokeDpeCmd {
     pub const PL1_DPE_ACTIVE_CONTEXT_THRESHOLD: usize = 16;
 
     pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult<MailboxResp> {
-        if let Some(cmd) = InvokeDpeReq::read_from(cmd_args) {
+        if cmd_args.len() <= core::mem::size_of::<InvokeDpeReq>() {
+            let mut cmd = InvokeDpeReq::default();
+            cmd.as_bytes_mut()[..cmd_args.len()].copy_from_slice(cmd_args);
+
             // Validate data length
             if cmd.data_size as usize > cmd.data.len() {
                 return Err(CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS);
diff --git a/runtime/src/packet.rs b/runtime/src/packet.rs
index 80becfaa..4939c1d9 100644
--- a/runtime/src/packet.rs
+++ b/runtime/src/packet.rs
@@ -88,7 +88,7 @@ impl Packet {
         //       it's up to the requestor to only use the valid portion of the data
         //       This can be fixed if needed by implementing a function in MailboxResp that returns the
         //       size taking into account the data size field. That can then be used to get a slice
-        mbox.write_response(resp.as_bytes())
+        mbox.write_response(resp.as_bytes()?)
     }
 
     pub fn as_bytes(&self) -> CaliptraResult<&[u8]> {


Test patch:
diff --git a/rom/dev/doc/test-coverage/test-coverage.md b/rom/dev/doc/test-coverage/test-coverage.md
index 4cf31287..37e491d9 100644
--- a/rom/dev/doc/test-coverage/test-coverage.md
+++ b/rom/dev/doc/test-coverage/test-coverage.md
@@ -129,7 +129,7 @@ Test Scenario| Test Name | ROM Error Code
 ---|---|---
 Check for any RUST panics added to the code | **test_panic_missing** | N/A
 Checks that extended error info is populated correctly upon watchdog timer timeout | **test_rom_wdt_timeout** | ROM_GLOBAL_WDT_EXPIRED
-
+Triggers a CPU fault and checks that extended error info is populated correctly | **test_cpu_fault** | ROM_GLOBAL_EXCEPTION
 
 # **Test Gaps**
 Test Scenario| Test Name | ROM Error Code
@@ -137,7 +137,6 @@ Test Scenario| Test Name | ROM Error Code
 Expand `smoke_test` to perform a hitless update and confirm everything is mixed into the identity correctly. | N/A | N/A
 Validate fix for #817: warm reset during hitless update | N/A | N/A
 Validate fix for #628: warm reset during cold reset | N/A | N/A
-Add test for CPU fault, and that extended error info is populated correctly | N/A | N/A
 Stress test: Perform many hitless updates in a row | N/A | N/A
 Ensure that boot ROM can load a 128k bundle into ICCM (assert ICCM contents in test) | N/A | N/A
 Ensure that hitless update flow can update an entire 128k bundle with completely different ICCM contents than original boot | N/A | N/A
diff --git a/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs b/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs
index 7a6e0db6..7719d618 100644
--- a/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs
+++ b/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs
@@ -9,7 +9,6 @@ use openssl::pkey::{PKey, Public};
 use openssl::x509::X509;
 use openssl::{rand::rand_bytes, x509::X509Req};
 use zerocopy::AsBytes;
-use zerocopy::FromBytes;
 
 use crate::helpers;
 
@@ -133,12 +132,14 @@ fn verify_key(
     };
 
     // Execute the command
-    let cert_resp = hw
+    let resp = hw
         .mailbox_execute(cmd_id, payload.as_bytes())
         .unwrap()
         .unwrap();
 
-    let cert_resp = GetLdevCertResp::read_from(cert_resp.as_bytes()).unwrap();
+    assert!(resp.len() <= std::mem::size_of::<GetLdevCertResp>());
+    let mut cert_resp = GetLdevCertResp::default();
+    cert_resp.as_bytes_mut()[..resp.len()].copy_from_slice(&resp);
 
     // Extract the certificate from the response
     let cert_der = &cert_resp.data[..(cert_resp.data_size as usize)];
diff --git a/runtime/tests/runtime_integration_tests/ecdsa.rs b/runtime/tests/runtime_integration_tests/ecdsa.rs
index 0d8b9093..a657c75b 100644
--- a/runtime/tests/runtime_integration_tests/ecdsa.rs
+++ b/runtime/tests/runtime_integration_tests/ecdsa.rs
@@ -102,7 +102,10 @@ fn ecdsa_cmd_run_wycheproof() {
                 // Do tests on mailbox
             });
             cmd.populate_chksum().unwrap();
-            let resp = model.mailbox_execute(u32::from(CommandId::ECDSA384_VERIFY), cmd.as_bytes());
+            let resp = model.mailbox_execute(
+                u32::from(CommandId::ECDSA384_VERIFY),
+                cmd.as_bytes().unwrap(),
+            );
             match test.result {
                 wycheproof::TestResult::Valid | wycheproof::TestResult::Acceptable => match resp {
                     Err(_) | Ok(None) => {
diff --git a/runtime/tests/runtime_integration_tests/hmac.rs b/runtime/tests/runtime_integration_tests/hmac.rs
index 4bfdefff..fae214ad 100644
--- a/runtime/tests/runtime_integration_tests/hmac.rs
+++ b/runtime/tests/runtime_integration_tests/hmac.rs
@@ -49,7 +49,7 @@ fn hmac_cmd_run_wycheproof() {
             cmd.populate_chksum().unwrap();
             let resp = model.mailbox_execute(
                 u32::from(CommandId::TEST_ONLY_HMAC384_VERIFY),
-                cmd.as_bytes(),
+                cmd.as_bytes().unwrap(),
             );
             match test.result {
                 wycheproof::TestResult::Valid | wycheproof::TestResult::Acceptable => match resp {
diff --git a/runtime/tests/runtime_integration_tests/integration_tests.rs b/runtime/tests/runtime_integration_tests/integration_tests.rs
index c4dce0d6..09f4f559 100644
--- a/runtime/tests/runtime_integration_tests/integration_tests.rs
+++ b/runtime/tests/runtime_integration_tests/integration_tests.rs
@@ -329,7 +329,10 @@ fn test_stash_measurement() {
     cmd.populate_chksum().unwrap();
 
     let resp = model
-        .mailbox_execute(u32::from(CommandId::STASH_MEASUREMENT), cmd.as_bytes())
+        .mailbox_execute(
+            u32::from(CommandId::STASH_MEASUREMENT),
+            cmd.as_bytes().unwrap(),
+        )
         .unwrap()
         .expect("We should have received a response");
 
@@ -395,21 +398,9 @@ fn test_invoke_dpe_get_profile_cmd() {
         data_size: InvokeDpeReq::DATA_MAX_SIZE as u32 + 1,
         ..cmd
     });
-    cmd.populate_chksum().unwrap();
-
-    // Make sure the command execution fails.
-    let resp = model
-        .mailbox_execute(u32::from(CommandId::INVOKE_DPE), cmd.as_bytes())
-        .unwrap_err();
-    if let ModelError::MailboxCmdFailed(code) = resp {
-        assert_eq!(
-            code,
-            u32::from(caliptra_drivers::CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS)
-        );
-    }
     assert_eq!(
-        model.soc_ifc().cptra_fw_error_non_fatal().read(),
-        u32::from(caliptra_drivers::CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS)
+        cmd.populate_chksum(),
+        Err(caliptra_drivers::CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE)
     );
 }
 
@@ -439,7 +430,7 @@ fn test_invoke_dpe_get_certificate_chain_cmd() {
     cmd.populate_chksum().unwrap();
 
     let resp = model
-        .mailbox_execute(u32::from(CommandId::INVOKE_DPE), cmd.as_bytes())
+        .mailbox_execute(u32::from(CommandId::INVOKE_DPE), cmd.as_bytes().unwrap())
         .unwrap()
         .expect("We should have received a response");
 
@@ -479,7 +470,7 @@ fn get_full_cert_chain(model: &mut DefaultHwModel, out: &mut [u8; 4096]) -> usiz
     cmd.populate_chksum().unwrap();
 
     let resp = model
-        .mailbox_execute(u32::from(CommandId::INVOKE_DPE), cmd.as_bytes())
+        .mailbox_execute(u32::from(CommandId::INVOKE_DPE), cmd.as_bytes().unwrap())
         .unwrap()
         .expect("We should have received a response");
 
@@ -510,7 +501,7 @@ fn get_full_cert_chain(model: &mut DefaultHwModel, out: &mut [u8; 4096]) -> usiz
     cmd.populate_chksum().unwrap();
 
     let resp = model
-        .mailbox_execute(u32::from(CommandId::INVOKE_DPE), cmd.as_bytes())
+        .mailbox_execute(u32::from(CommandId::INVOKE_DPE), cmd.as_bytes().unwrap())
         .unwrap()
         .expect("We should have received a response");
 
@@ -561,26 +552,18 @@ fn test_populate_idev_cert_cmd() {
     let mut cert_slice = [0u8; PopulateIdevCertReq::MAX_CERT_SIZE];
     cert_slice[..cert_bytes.len()].copy_from_slice(&cert_bytes);
 
-    let pop_idev_cmd = PopulateIdevCertReq {
+    let mut pop_idev_cmd = MailboxReq::PopulateIdevCert(PopulateIdevCertReq {
         hdr: MailboxReqHeader { chksum: 0 },
         cert_size: cert_bytes.len() as u32,
         cert: cert_slice,
-    };
-    let checksum = caliptra_common::checksum::calc_checksum(
-        u32::from(CommandId::POPULATE_IDEV_CERT),
-        &pop_idev_cmd.as_bytes()[4..],
-    );
-
-    let pop_idev_cmd = PopulateIdevCertReq {
-        hdr: MailboxReqHeader { chksum: checksum },
-        ..pop_idev_cmd
-    };
+    });
+    pop_idev_cmd.populate_chksum().unwrap();
 
     // call populate idev cert so that the idev cert is added to the certificate chain
     model
         .mailbox_execute(
             u32::from(CommandId::POPULATE_IDEV_CERT),
-            pop_idev_cmd.as_bytes(),
+            pop_idev_cmd.as_bytes().unwrap(),
         )
         .unwrap()
         .expect("We should have received a response");
@@ -1276,62 +1259,44 @@ fn test_idev_id_cert() {
     let tbs_size = 223;
     tbs[..tbs_size].copy_from_slice(&cert_der[tbs_offset..tbs_offset + tbs_size]);
 
-    let cmd = GetIdevCertReq {
+    let mut cmd = MailboxReq::GetIdevCert(GetIdevCertReq {
         hdr: MailboxReqHeader { chksum: 0 },
         tbs,
         signature_r,
         signature_s,
         tbs_size: tbs_size as u32,
-    };
-
-    let checksum = caliptra_common::checksum::calc_checksum(
-        u32::from(CommandId::GET_IDEV_CERT),
-        &cmd.as_bytes()[4..],
-    );
-
-    let cmd = GetIdevCertReq {
-        hdr: MailboxReqHeader { chksum: checksum },
-        ..cmd
-    };
+    });
+    cmd.populate_chksum().unwrap();
 
     let resp = model
-        .mailbox_execute(u32::from(CommandId::GET_IDEV_CERT), cmd.as_bytes())
+        .mailbox_execute(u32::from(CommandId::GET_IDEV_CERT), cmd.as_bytes().unwrap())
         .unwrap()
         .expect("We expected a response");
 
-    let cert = GetIdevCertResp::read_from(resp.as_slice()).unwrap();
-    assert!(cmd.tbs_size < cert.cert_size);
+    assert!(resp.len() <= std::mem::size_of::<GetIdevCertResp>());
+    let mut cert = GetIdevCertResp::default();
+    cert.as_bytes_mut()[..resp.len()].copy_from_slice(&resp);
+
+    assert!(caliptra_common::checksum::verify_checksum(
+        cert.hdr.chksum,
+        0x0,
+        &resp[core::mem::size_of_val(&cert.hdr.chksum)..],
+    ));
+
+    assert!(tbs_size < cert.cert_size as usize);
     let idev_cert = X509::from_der(&cert.cert[..cert.cert_size as usize]).unwrap();
     assert!(idev_cert.verify(&ec_key).unwrap());
 
     // Test with tbs_size too big.
-    let cmd = GetIdevCertReq {
+    let mut cmd = MailboxReq::GetIdevCert(GetIdevCertReq {
+        hdr: MailboxReqHeader { chksum: 0 },
+        tbs,
+        signature_r,
+        signature_s,
         tbs_size: GetIdevCertReq::DATA_MAX_SIZE as u32 + 1,
-        ..cmd
-    };
-
-    let checksum = caliptra_common::checksum::calc_checksum(
-        u32::from(CommandId::GET_IDEV_CERT),
-        &cmd.as_bytes()[4..],
-    );
-
-    let cmd = GetIdevCertReq {
-        hdr: MailboxReqHeader { chksum: checksum },
-        ..cmd
-    };
-
-    // Make sure the command execution fails.
-    let resp = model
-        .mailbox_execute(u32::from(CommandId::GET_IDEV_CERT), cmd.as_bytes())
-        .unwrap_err();
-    if let ModelError::MailboxCmdFailed(code) = resp {
-        assert_eq!(
-            code,
-            u32::from(caliptra_drivers::CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS)
-        );
-    }
+    });
     assert_eq!(
-        model.soc_ifc().cptra_fw_error_non_fatal().read(),
-        u32::from(caliptra_drivers::CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS)
+        cmd.populate_chksum(),
+        Err(caliptra_drivers::CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE)
     );
 }
diff --git a/test/tests/caliptra_integration_tests/fake_collateral_boot_test.rs b/test/tests/caliptra_integration_tests/fake_collateral_boot_test.rs
index 25419c66..5eafd59e 100755
--- a/test/tests/caliptra_integration_tests/fake_collateral_boot_test.rs
+++ b/test/tests/caliptra_integration_tests/fake_collateral_boot_test.rs
@@ -16,7 +16,7 @@ use caliptra_test::{
 };
 use openssl::sha::sha384;
 use std::io::Write;
-use zerocopy::{AsBytes, FromBytes};
+use zerocopy::AsBytes;
 
 // Need hardcoded HASH for the canned FMC alias cert
 const FMC_CANNED_DIGEST: [u32; 12] = [
@@ -110,7 +110,7 @@ fn fake_boot_test() {
     };
 
     // Execute the command
-    let ldev_cert_resp = hw
+    let resp = hw
         .mailbox_execute(
             u32::from(CommandId::TEST_ONLY_GET_LDEV_CERT),
             payload.as_bytes(),
@@ -118,7 +118,9 @@ fn fake_boot_test() {
         .unwrap()
         .unwrap();
 
-    let ldev_cert_resp = GetLdevCertResp::read_from(ldev_cert_resp.as_bytes()).unwrap();
+    assert!(resp.len() <= std::mem::size_of::<GetLdevCertResp>());
+    let mut ldev_cert_resp = GetLdevCertResp::default();
+    ldev_cert_resp.as_bytes_mut()[..resp.len()].copy_from_slice(&resp);
 
     // Verify checksum and FIPS approval
     assert!(caliptra_common::checksum::verify_checksum(
@@ -177,7 +179,7 @@ fn fake_boot_test() {
     };
 
     // Execute command
-    let fmc_alias_cert_resp = hw
+    let resp = hw
         .mailbox_execute(
             u32::from(CommandId::TEST_ONLY_GET_FMC_ALIAS_CERT),
             payload.as_bytes(),
@@ -185,8 +187,9 @@ fn fake_boot_test() {
         .unwrap()
         .unwrap();
 
-    let fmc_alias_cert_resp =
-        TestGetFmcAliasCertResp::read_from(fmc_alias_cert_resp.as_bytes()).unwrap();
+    assert!(resp.len() <= std::mem::size_of::<TestGetFmcAliasCertResp>());
+    let mut fmc_alias_cert_resp = TestGetFmcAliasCertResp::default();
+    fmc_alias_cert_resp.as_bytes_mut()[..resp.len()].copy_from_slice(&resp);
 
     // Verify checksum and FIPS approval
     assert!(caliptra_common::checksum::verify_checksum(
diff --git a/test/tests/caliptra_integration_tests/smoke_test.rs b/test/tests/caliptra_integration_tests/smoke_test.rs
index be824ba9..e6b8393e 100644
--- a/test/tests/caliptra_integration_tests/smoke_test.rs
+++ b/test/tests/caliptra_integration_tests/smoke_test.rs
@@ -1,7 +1,9 @@
 // Licensed under the Apache-2.0 license
 
 use caliptra_builder::{firmware, ImageOptions};
-use caliptra_common::mailbox_api::{TestOnlyGetFmcAliasCertReq, TestOnlyGetLdevCertReq};
+use caliptra_common::mailbox_api::{
+    ResponseVarSize, TestOnlyGetFmcAliasCertReq, TestOnlyGetLdevCertReq,
+};
 use caliptra_hw_model::{BootParams, HwModel, InitParams, SecurityState};
 use caliptra_hw_model_types::{DeviceLifecycle, Fuses};
 use caliptra_test::run_test;