diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 94af041..748069a 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -77,6 +77,11 @@ impl VerificationOutput { pub fn is_failure(&self) -> Choice { !self.succeeded } + + /// The value used in the verification. + pub fn value(&self) -> &T { + &self.value + } } /// A helper struct for displaying the verification results. @@ -107,7 +112,7 @@ impl<'a, V, O> VerificationTreeDisplay<'a, V, O> { } } -impl<'a, V: VerificationMessage, O: Clone> Display for VerificationTreeDisplay<'a, V, O> { +impl<'a, V: VerificationMessage, O> Display for VerificationTreeDisplay<'a, V, O> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { self.verifier.fmt_padded(f, 0, &self.result) } diff --git a/verifier/src/tcb.rs b/verifier/src/tcb.rs index 09ed2db..a247ca5 100644 --- a/verifier/src/tcb.rs +++ b/verifier/src/tcb.rs @@ -32,11 +32,13 @@ extern crate alloc; use crate::advisories::{Advisories, AdvisoryStatus}; +use crate::{Accessor, VerificationMessage, VerificationOutput, Verifier}; use alloc::string::String; use alloc::vec::Vec; +use core::fmt::Formatter; use der::DateTime; use mc_sgx_dcap_types::{TcbInfo as PckTcb, COMPONENT_SVN_COUNT, FMSPC_SIZE}; -use p256::ecdsa::signature::Verifier; +use p256::ecdsa::signature::Verifier as SignatureVerifier; use p256::ecdsa::{Signature, VerifyingKey}; use serde::Deserialize; use serde_json::value::RawValue; @@ -278,10 +280,80 @@ impl<'a> TryFrom<&'a str> for TcbInfoRaw<'a> { } } +/// Verifier for ensuring a raw TCB was signed with the provided key +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TcbInfoRawVerifier { + key: VerifyingKey, + time: DateTime, +} + +impl TcbInfoRawVerifier { + /// Create a new instance. + /// + /// The `TcbInfoRawVerifier::verify()` will fail if the signature doesn't + /// match for the `key` or if the `time` is outside the `issueDate` and + /// `nextUpdate` times in the `tcbInfo`. + /// + /// # Arguments + /// - `key` - The public key to verify the `tcbInfo` signature with + /// This should be retrieved from the x509 certificate provided in the + /// TCB request from + /// + /// - `time` - The current system time + /// This is expected to be generated by: + /// ```ignore + /// let time = DateTime::from_system_time(SystemTime::now()).unwrap(); + /// ``` + /// or equivalent + pub fn new(key: VerifyingKey, time: DateTime) -> Self { + Self { key, time } + } +} + +impl<'a, E: Accessor>> Verifier for TcbInfoRawVerifier { + type Value = Option; + fn verify(&self, evidence: &E) -> VerificationOutput { + let tcb_raw = evidence.get(); + let result = tcb_raw.verify(&self.key, self.time); + let is_success = result.is_ok() as u8; + + let mut error = None; + if let Err(e) = result { + error = Some(e); + } + + VerificationOutput::new(error, is_success.into()) + } +} + +impl VerificationMessage> for TcbInfoRawVerifier { + fn fmt_padded( + &self, + f: &mut Formatter<'_>, + pad: usize, + result: &VerificationOutput>, + ) -> core::fmt::Result { + let is_success = result.is_success(); + let status = crate::choice_to_status_message(is_success); + write!(f, "{:pad$}{status} ", "")?; + + if is_success.into() { + write!(f, "The raw TCB info was verified for the provided key") + } else { + let error = result + .value() + .as_ref() + .expect("Should have an error if not successful"); + write!(f, "The raw TCB info could not be verified: {error}") + } + } +} + #[cfg(test)] mod tests { use super::*; - use alloc::vec; + use crate::VerificationTreeDisplay; + use alloc::{format, vec}; use p256::ecdsa::VerifyingKey; use serde_json::value::RawValue; use x509_cert::{der::DecodePem, Certificate}; @@ -803,4 +875,46 @@ mod tests { ); assert_eq!(tcb.is_corresponding_level(&pck_tcb), expected); } + + #[test] + fn tcb_raw_verifier_succeeds() { + let key = tcb_verifying_key(); + let tcb_json = include_str!("../data/tests/fmspc_00906ED50000_2023_05_10.json"); + let raw_tcb = TcbInfoRaw::try_from(tcb_json).expect("Failed to parse raw TCB"); + + let time = "2023-06-08T13:43:27Z" + .parse::() + .expect("Failed to parse time"); + + let verifier = TcbInfoRawVerifier::new(key, time); + let verification = verifier.verify(&raw_tcb); + + assert_eq!(verification.is_success().unwrap_u8(), 1); + + let displayable = VerificationTreeDisplay::new(&verifier, verification); + let expected = r#" + - [x] The raw TCB info was verified for the provided key"#; + assert_eq!(format!("\n{displayable}"), textwrap::dedent(expected)); + } + + #[test] + fn tcb_raw_verifier_fails_at_next_update() { + let key = tcb_verifying_key(); + let tcb_json = include_str!("../data/tests/fmspc_00906ED50000_2023_05_10.json"); + let raw_tcb = TcbInfoRaw::try_from(tcb_json).expect("Failed to parse raw TCB"); + + let time = "2023-06-09T13:43:27Z" + .parse::() + .expect("Failed to parse time"); + + let verifier = TcbInfoRawVerifier::new(key, time); + let verification = verifier.verify(&raw_tcb); + + assert_eq!(verification.is_success().unwrap_u8(), 0); + + let displayable = VerificationTreeDisplay::new(&verifier, verification); + let expected = r#" + - [ ] The raw TCB info could not be verified: TCB info expired"#; + assert_eq!(format!("\n{displayable}"), textwrap::dedent(expected)); + } }