diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index e72b9a2..2d04411 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -6,13 +6,14 @@ mod report_body; pub use report_body::{ - AttributesVerifier, ConfigIdVerifier, ConfigSvnVerifier, IsvSvnVerifier, + AttributesVerifier, ConfigIdVerifier, ConfigSvnVerifier, CpuSvnVerifier, IsvSvnVerifier, MiscellaneousSelectVerifier, MrEnclaveVerifier, MrSignerVerifier, ReportDataVerifier, }; use core::fmt::{Debug, Display, Formatter}; use mc_sgx_core_types::{ - Attributes, ConfigId, ConfigSvn, IsvSvn, MiscellaneousSelect, MrEnclave, MrSigner, ReportData, + Attributes, ConfigId, ConfigSvn, CpuSvn, IsvSvn, MiscellaneousSelect, MrEnclave, MrSigner, + ReportData, }; use subtle::{Choice, CtOption}; @@ -58,7 +59,14 @@ pub enum VerificationError { /// The actual SVN that was present actual: ConfigSvn, }, - /// The ISV svn value of {actual:?} is less than the expected value of {expected:?} + /// The CPU SVN value of {actual:?} is less than the expected value of {expected:?} + CpuSvnTooSmall { + /// The minimum SVN + expected: CpuSvn, + /// The actual SVN that was present + actual: CpuSvn, + }, + /// The ISV SVN value of {actual:?} is less than the expected value of {expected:?} IsvSvnTooSmall { /// The minimum SVN expected: IsvSvn, @@ -568,7 +576,7 @@ mod tests { let displayable = failure.display(); assert_eq!( format!("{displayable}"), - "The ISV svn value of IsvSvn(2) is less than the expected value of IsvSvn(3)" + "The ISV SVN value of IsvSvn(2) is less than the expected value of IsvSvn(3)" ); } @@ -651,7 +659,7 @@ mod tests { - [x] Passed - [ ] - The ISV svn value of IsvSvn(1) is less than the expected value of IsvSvn(3) + The ISV SVN value of IsvSvn(1) is less than the expected value of IsvSvn(3) - [ ] The MiscellaneousSelect did not match expected:MiscellaneousSelect(2) actual:MiscellaneousSelect(3)"#; assert_eq!(format!("\n{displayable}"), textwrap::dedent(expected)); diff --git a/verifier/src/report_body.rs b/verifier/src/report_body.rs index 75d23b8..b0f68b8 100644 --- a/verifier/src/report_body.rs +++ b/verifier/src/report_body.rs @@ -5,10 +5,10 @@ use crate::{VerificationError, Verifier}; use core::fmt::Debug; use mc_sgx_core_types::{ - Attributes, ConfigId, ConfigSvn, IsvSvn, MiscellaneousSelect, MrEnclave, MrSigner, ReportBody, - ReportData, + Attributes, ConfigId, ConfigSvn, CpuSvn, IsvSvn, MiscellaneousSelect, MrEnclave, MrSigner, + ReportBody, ReportData, }; -use subtle::{ConstantTimeLess, CtOption}; +use subtle::{ConstantTimeEq, ConstantTimeLess, CtOption}; /// Trait for getting access to the type `T` that needs to be verified. /// @@ -54,6 +54,7 @@ report_body_field_accessor! { Attributes, attributes; ConfigId, config_id; ConfigSvn, config_svn; + CpuSvn, cpu_svn; IsvSvn, isv_svn; MiscellaneousSelect, miscellaneous_select; MrEnclave, mr_enclave; @@ -138,7 +139,7 @@ impl> Verifier for GreaterThanEqualVerifier let actual = evidence.get(); // This verifier ensures the actual is greater than or equal to the - // expected. `CtOpton` is used to indicate an error, so we invert the + // expected. `CtOption` is used to indicate an error, so we invert the // comparison. let is_some = actual.as_ref().ct_lt(expected.as_ref()); CtOption::new( @@ -148,6 +149,42 @@ impl> Verifier for GreaterThanEqualVerifier } } +/// Verifier for ensuring [`CpuSvn`] is greater than or equal to an +/// expected [`CpuSvn`] +pub type CpuSvnVerifier = GreaterThanEqualVerifier; +impl IntoVerificationError for CpuSvn { + fn into_verification_error(expected: Self, actual: Self) -> VerificationError { + VerificationError::CpuSvnTooSmall { expected, actual } + } +} + +fn cpu_svn_to_u64s(cpu_svn: &CpuSvn) -> (u64, u64) { + let cpu_svn_bytes: &[u8] = cpu_svn.as_ref(); + let mut bytes = [0u8; 8]; + bytes.copy_from_slice(&cpu_svn_bytes[0..8]); + let high = ::from_be_bytes(bytes); + bytes.copy_from_slice(&cpu_svn_bytes[8..]); + let low = ::from_be_bytes(bytes); + (high, low) +} + +impl> Verifier for GreaterThanEqualVerifier { + type Error = VerificationError; + fn verify(&self, evidence: &E) -> CtOption { + let expected = self.expected.clone(); + let actual = evidence.get(); + + // Per the Intel docs, CPU SVN is a 16 byte BE value. Since we may not + // support u128 on all platforms we compare the 64 bit values. + let (actual_high, actual_low) = cpu_svn_to_u64s(&actual); + let (expected_high, expected_low) = cpu_svn_to_u64s(&expected); + let is_some = actual_high.ct_lt(&expected_high) + | (actual_high.ct_eq(&expected_high) & actual_low.ct_lt(&expected_low)); + + CtOption::new(CpuSvn::into_verification_error(expected, actual), is_some) + } +} + /// Verifier for ensuring [`IsvSvn`] is greater than or equal to an expected /// [`IsvSvn`] pub type IsvSvnVerifier = GreaterThanEqualVerifier; @@ -164,7 +201,7 @@ impl> Verifier for GreaterThanEqualVerifier { let actual = evidence.get(); // This verifier ensures the actual is greater than or equal to the - // expected. `CtOpton` is used to indicate an error, so we invert the + // expected. `CtOption` is used to indicate an error, so we invert the // comparison. let is_some = actual.as_ref().ct_lt(expected.as_ref()); CtOption::new(IsvSvn::into_verification_error(expected, actual), is_some) @@ -574,4 +611,62 @@ mod test { assert_eq!(verifier.verify(&report_data).is_some().unwrap_u8(), 1); } + + #[test] + fn cpu_svn_succeeds() { + let cpu_svn = CpuSvn::from(REPORT_BODY_SRC.cpu_svn); + let verifier = CpuSvnVerifier::new(cpu_svn.clone()); + + assert_eq!(verifier.verify(&cpu_svn).is_none().unwrap_u8(), 1); + } + + #[test] + fn cpu_svn_fails_on_high_u64() { + let mut cpu_svn = CpuSvn::from(REPORT_BODY_SRC.cpu_svn); + let verifier = CpuSvnVerifier::new(cpu_svn.clone()); + + let bytes: &mut [u8] = cpu_svn.as_mut(); + bytes[7] -= 1; + + assert_eq!(verifier.verify(&cpu_svn).is_some().unwrap_u8(), 1); + } + + #[test] + fn cpu_svn_fails_on_low_u64() { + let mut cpu_svn = CpuSvn::from(REPORT_BODY_SRC.cpu_svn); + let verifier = CpuSvnVerifier::new(cpu_svn.clone()); + + let bytes: &mut [u8] = cpu_svn.as_mut(); + bytes[15] -= 1; + + assert_eq!(verifier.verify(&cpu_svn).is_some().unwrap_u8(), 1); + } + + #[test] + fn cpu_svn_succeeds_when_high_u64_greater() { + let mut cpu_svn = CpuSvn::from(REPORT_BODY_SRC.cpu_svn); + let verifier = CpuSvnVerifier::new(cpu_svn.clone()); + + let bytes: &mut [u8] = cpu_svn.as_mut(); + bytes[7] += 1; + + // Making this less, to show how the high 64 takes precedence + bytes[15] -= 1; + + assert_eq!(verifier.verify(&cpu_svn).is_none().unwrap_u8(), 1); + } + + #[test] + fn cpu_svn_fails_when_high_u64_less_but_low_greater() { + let mut cpu_svn = CpuSvn::from(REPORT_BODY_SRC.cpu_svn); + let verifier = CpuSvnVerifier::new(cpu_svn.clone()); + + let bytes: &mut [u8] = cpu_svn.as_mut(); + bytes[7] -= 1; + + // Making this greater, to show how the high 64 takes precedence + bytes[15] += 1; + + assert_eq!(verifier.verify(&cpu_svn).is_some().unwrap_u8(), 1); + } }