diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c5094234..89ae18aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -213,3 +213,28 @@ jobs: flag: --release features: - crypto_nossl + + sw-lax-parser: + name: sw lax-parser ${{ matrix.runner }} ${{ matrix.toolchain }} ${{ matrix.profile.name }} ${{ matrix.features }} + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.toolchain }} + - run: cargo test ${{ matrix.profile.flag }} --features=lax-parser + + strategy: + fail-fast: false + matrix: + runner: + - ubuntu-latest + - macos-15-intel + - windows-latest + toolchain: + - 1.85.0 + - stable + profile: + - name: debug + - name: release + flag: --release diff --git a/Cargo.toml b/Cargo.toml index 0858b3c4..de48d53c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ sev = ["dep:rdrand"] snp = [] crypto_nossl = ["dep:p384", "dep:rsa", "dep:sha2", "dep:x509-cert"] serde = ["dep:serde", "dep:serde-big-array", "dep:serde_bytes"] +lax-parser = [] [target.'cfg(target_os = "linux")'.dependencies] iocuddle = "^0.1" diff --git a/src/certs/snp/ecdsa/mod.rs b/src/certs/snp/ecdsa/mod.rs index f7a2fb3a..5367baad 100644 --- a/src/certs/snp/ecdsa/mod.rs +++ b/src/certs/snp/ecdsa/mod.rs @@ -72,6 +72,7 @@ impl Decoder<()> for Signature { } } +#[cfg(not(feature = "lax-parser"))] impl Encoder<()> for Signature { fn encode(&self, writer: &mut impl Write, _: ()) -> Result<()> { writer.write_bytes(self.r, ())?; @@ -314,6 +315,7 @@ mod tests { } } + #[cfg(not(feature = "lax-parser"))] #[test] fn test_signature_serialization() { let sig: Signature = Default::default(); diff --git a/src/firmware/guest/types/snp.rs b/src/firmware/guest/types/snp.rs index e3d4cdb4..21ae606b 100644 --- a/src/firmware/guest/types/snp.rs +++ b/src/firmware/guest/types/snp.rs @@ -354,6 +354,7 @@ impl Default for AttestationReport { } } +#[cfg(not(feature = "lax-parser"))] impl Encoder<()> for AttestationReport { fn encode(&self, writer: &mut impl Write, _: ()) -> Result<(), std::io::Error> { // Determine the variant based on version and CPUID step @@ -1688,6 +1689,14 @@ Signature: assert_eq!(::default(), Version::new(0, 0, 0)); } + const VCEK: [u8; 64] = [ + 0xD4, 0x95, 0x54, 0xEC, 0x71, 0x7F, 0x4E, 0x5B, 0x0F, 0xE6, 0xB1, 0x43, 0xBC, 0xF0, 0x40, + 0x5B, 0xD7, 0xAE, 0x30, 0x47, 0x27, 0xED, 0xF4, 0x66, 0x03, 0xF2, 0xA7, 0x6A, 0xEF, 0x6A, + 0x3A, 0xBC, 0x15, 0xD7, 0xAF, 0x38, 0xDB, 0x75, 0x70, 0x39, 0x02, 0x9F, 0x0E, 0xFA, 0xCF, + 0xD0, 0x8E, 0x24, 0x43, 0x24, 0x88, 0x47, 0x38, 0xC7, 0x2B, 0x08, 0x2E, 0x2F, 0x87, 0xA4, + 0x4D, 0x54, 0x1E, 0xB6, + ]; + #[test] fn test_attestation_report_from_bytes() { // Create a valid attestation report bytes minus one byte. @@ -1696,15 +1705,32 @@ Signature: // Push the version byte at the beginning. bytes.insert(0, 2); - let vcek = [ - 0xD4, 0x95, 0x54, 0xEC, 0x71, 0x7F, 0x4E, 0x5B, 0x0F, 0xE6, 0xB1, 0x43, 0xBC, 0xF0, - 0x40, 0x5B, 0xD7, 0xAE, 0x30, 0x47, 0x27, 0xED, 0xF4, 0x66, 0x03, 0xF2, 0xA7, 0x6A, - 0xEF, 0x6A, 0x3A, 0xBC, 0x15, 0xD7, 0xAF, 0x38, 0xDB, 0x75, 0x70, 0x39, 0x02, 0x9F, - 0x0E, 0xFA, 0xCF, 0xD0, 0x8E, 0x24, 0x43, 0x24, 0x88, 0x47, 0x38, 0xC7, 0x2B, 0x08, - 0x2E, 0x2F, 0x87, 0xA4, 0x4D, 0x54, 0x1E, 0xB6, - ]; + bytes[0x1A8..0x1E0].copy_from_slice(&VCEK[..(0x1E0 - 0x1A8)]); + + // Test valid input + let result = AttestationReport::from_bytes(bytes.as_slice()); + assert!(result.is_ok()); + } + + #[cfg(feature = "lax-parser")] + #[test] + fn test_future_attestation_report_from_bytes() { + // Create a valid attestation report bytes minus one byte. + let mut bytes: Vec = vec![0; 1183]; + + // Push the version byte at the beginning. + bytes.insert(0, 9); + + // Set CPUID_FAM_ID + bytes[0x188] = 0x1A; + + // Set CPUID_MOD_ID + bytes[0x189] = 0x2; + + // Write into a reserved area; the standard parser would expect 0. + bytes[0x19F] = 1; - bytes[0x1A8..0x1E0].copy_from_slice(&vcek[..(0x1E0 - 0x1A8)]); + bytes[0x1A8..0x1E0].copy_from_slice(&VCEK[..(0x1E0 - 0x1A8)]); // Test valid input let result = AttestationReport::from_bytes(bytes.as_slice()); @@ -1724,6 +1750,7 @@ Signature: AttestationReport::from_bytes(bytes[..100].try_into().unwrap()).unwrap(); } + #[cfg(not(feature = "lax-parser"))] #[test] fn test_attestation_report_parse_and_write_bytes() { let report = AttestationReport { @@ -1801,6 +1828,7 @@ Signature: assert_eq!(original.to_bytes().unwrap(), cloned.to_bytes().unwrap()); } + #[cfg(not(feature = "lax-parser"))] #[test] fn test_attestation_report_complex_write() { let report = AttestationReport { @@ -1830,6 +1858,7 @@ Signature: assert_eq!(read_back.image_id, [0xBB; 16]); } + #[cfg(not(feature = "lax-parser"))] #[test] fn test_write_with_limited_writer() { let report = AttestationReport { diff --git a/src/firmware/host/mod.rs b/src/firmware/host/mod.rs index a0d995dc..e8acb2a2 100644 --- a/src/firmware/host/mod.rs +++ b/src/firmware/host/mod.rs @@ -286,6 +286,7 @@ impl Firmware { Ok(()) } + #[cfg(not(feature = "lax-parser"))] #[cfg(feature = "snp")] /// Insert a Version Loaded Endorsement Key Hashstick into the AMD Secure Processor. /// diff --git a/src/firmware/host/types/snp.rs b/src/firmware/host/types/snp.rs index ae44a020..22d50805 100644 --- a/src/firmware/host/types/snp.rs +++ b/src/firmware/host/types/snp.rs @@ -808,6 +808,7 @@ impl Default for WrappedVlekHashstick { } } +#[cfg(not(feature = "lax-parser"))] impl Encoder for WrappedVlekHashstick { fn encode( &self, @@ -1867,6 +1868,7 @@ mod tests { ); } + #[cfg(not(feature = "lax-parser"))] #[test] fn test_wrapped_vlek_hashstick_to_bytes() { // Create a test hashstick diff --git a/src/lib.rs b/src/lib.rs index 15f8fc96..b89e943e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,14 @@ compile_error!( "feature \"openssl\" and feature \"crypto_nossl\" cannot be enabled at the same time" ); +#[cfg(all( + any(feature = "openssl", feature = "crypto_nossl"), + feature = "lax-parser" +))] +compile_error!( + r#"feature "openssl" and feature "crypto_nossl" cannot be used together with "lax-parser""# +); + /// SEV and SEV-SNP certificates interface. pub mod certs; diff --git a/src/util/parser_helper/read_ext.rs b/src/util/parser_helper/read_ext.rs index dc7e4fcb..d86cbdc6 100644 --- a/src/util/parser_helper/read_ext.rs +++ b/src/util/parser_helper/read_ext.rs @@ -33,6 +33,7 @@ pub trait ReadExt: Read { while remaining > 0 { let n = remaining.min(CHUNK); self.read_exact(&mut buf[..n])?; + #[cfg(not(feature = "lax-parser"))] if buf[..n].iter().any(|&b| b != 0) { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, @@ -98,6 +99,7 @@ mod read_ext_tests { } // Test case 3: Skip, Invalid Data + #[cfg(not(feature = "lax-parser"))] #[test] fn test_skip_invalid_data() { let data = vec![0, 0, 1, 0, 0x12, 0x34, 0x56, 0x78]; diff --git a/src/util/parser_helper/write_ext.rs b/src/util/parser_helper/write_ext.rs index de6a1ffc..ed6bc72d 100644 --- a/src/util/parser_helper/write_ext.rs +++ b/src/util/parser_helper/write_ext.rs @@ -12,6 +12,7 @@ pub trait WriteExt: Write { value.encode(self, params) } + #[cfg(not(feature = "lax-parser"))] fn skip_bytes(&mut self) -> Result<&mut Self, std::io::Error> where Self: Sized, @@ -56,6 +57,7 @@ mod write_ext_tests { Ok(()) } + #[cfg(not(feature = "lax-parser"))] #[test] fn test_write_bytes_with_skip() -> Result<(), std::io::Error> { let mut writer = MockWriter::default();