diff --git a/crates/attestation/README.md b/crates/attestation/README.md index d487292..05c8f4b 100644 --- a/crates/attestation/README.md +++ b/crates/attestation/README.md @@ -65,7 +65,6 @@ These are the attestation type names used in the measurements file. - `none` - No attestation provided - `gcp-tdx` - DCAP TDX on Google Cloud Platform - `azure-tdx` - TDX on Azure, with vTPM attestation -- `qemu-tdx` - TDX on Qemu (no cloud platform) - `dcap-tdx` - DCAP TDX (platform not specified) Local attestation types can be automatically detected. This works by initially @@ -73,7 +72,7 @@ attempting an Azure attestation, and if it fails attempting a DCAP attestation, and if that fails assume no CVM attestation. On detecting DCAP, a call to the Google Cloud metadata API is used to detect whether we are on Google Cloud. -In the case of attestation types `dcap-tdx`, `gcp-tdx`, and `qemu-tdx`, a +In the case of attestation types `dcap-tdx` and `gcp-tdx`, a standard DCAP attestation is generated using the `configfs-tsm` linux filesystem interface. This means that the binary must be run with access to `/sys/kernel/config/tsm/report` which on many systems requires sudo. If @@ -294,6 +293,5 @@ Example: Portable policies currently only work with the `"gcp-tdx"` attestation type. For GCP, the verifier fetches the platform firmware blob from Google's metadata service (keyed by MRTD) and combines it with the image hashes to reconstruct -the expected registers. Support for other attestation types is planned; but `dcap_image_hashes` record with any other attestation type is rejected when parsing from JSON. diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index 4fe5dd8..8d04bf3 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -59,7 +59,7 @@ impl AttestationExchangeMessage { Err(AttestationError::AttestationTypeNotSupported) } } - AttestationType::DcapTdx | AttestationType::GcpTdx | AttestationType::QemuTdx => { + AttestationType::DcapTdx | AttestationType::GcpTdx => { let quote = dcap_qvl::verify::Quote::parse(&attestation_evidence.quote) .map_err(DcapVerificationError::from)?; Ok(Some(MultiMeasurements::from_dcap_qvl_quote("e)?)) @@ -86,7 +86,7 @@ impl From for AttestationType { match attestation_type { attest_types::AttestationType::GcpTdx => AttestationType::GcpTdx, attest_types::AttestationType::AzureTdx => AttestationType::AzureTdx, - attest_types::AttestationType::SelfHostedTdx => AttestationType::QemuTdx, + attest_types::AttestationType::SelfHostedTdx => AttestationType::DcapTdx, } } } @@ -99,9 +99,7 @@ impl TryFrom for attest_types::AttestationType { AttestationType::None => Err(AttestationError::AttestationTypeNotAccepted), AttestationType::AzureTdx => Ok(attest_types::AttestationType::AzureTdx), AttestationType::GcpTdx => Ok(attest_types::AttestationType::GcpTdx), - AttestationType::DcapTdx | AttestationType::QemuTdx => { - Ok(attest_types::AttestationType::SelfHostedTdx) - } + AttestationType::DcapTdx => Ok(attest_types::AttestationType::SelfHostedTdx), } } } @@ -117,9 +115,8 @@ pub enum AttestationType { GcpTdx, /// TDX on Azure, with MAA AzureTdx, - /// TDX on Qemu (no cloud platform) - QemuTdx, - /// DCAP TDX + /// DCAP TDX (no cloud platform specified) + #[serde(alias = "qemu-tdx")] // To support legacy measurements file format DcapTdx, } @@ -129,7 +126,6 @@ impl AttestationType { match self { AttestationType::None => "none", AttestationType::AzureTdx => "azure-tdx", - AttestationType::QemuTdx => "qemu-tdx", AttestationType::GcpTdx => "gcp-tdx", AttestationType::DcapTdx => "dcap-tdx", } @@ -269,7 +265,7 @@ impl AttestationGenerator { Err(AttestationError::AttestationTypeNotSupported) } } - AttestationType::DcapTdx | AttestationType::GcpTdx | AttestationType::QemuTdx => { + AttestationType::DcapTdx | AttestationType::GcpTdx => { #[cfg(any(test, feature = "mock"))] let platform = mock_platform_metadata(self.attestation_type)?; #[cfg(not(any(test, feature = "mock")))] @@ -471,7 +467,7 @@ impl AttestationVerifier { return Err(AttestationError::AttestationTypeNotSupported); } } - AttestationType::DcapTdx | AttestationType::GcpTdx | AttestationType::QemuTdx => { + AttestationType::DcapTdx | AttestationType::GcpTdx => { let attestation_evidence = attestation_exchange_message .attestation_evidence .as_ref() @@ -543,7 +539,7 @@ impl AttestationVerifier { return Err(AttestationError::AttestationTypeNotSupported); } } - AttestationType::DcapTdx | AttestationType::QemuTdx | AttestationType::GcpTdx => { + AttestationType::DcapTdx | AttestationType::GcpTdx => { let attestation_evidence = attestation_exchange_message .attestation_evidence .as_ref() diff --git a/crates/attestation/src/measurements.rs b/crates/attestation/src/measurements.rs index fbdba06..956669e 100644 --- a/crates/attestation/src/measurements.rs +++ b/crates/attestation/src/measurements.rs @@ -189,7 +189,7 @@ impl MultiMeasurements { }) .collect::>()?, ), - AttestationType::DcapTdx | AttestationType::GcpTdx | AttestationType::QemuTdx => { + AttestationType::DcapTdx | AttestationType::GcpTdx => { let measurements_map = measurements_map .into_iter() .map(|(k, v)| { @@ -267,7 +267,7 @@ impl MeasurementRecord { measurements: match attestation_type { AttestationType::None => ExpectedMeasurements::NoAttestation, AttestationType::AzureTdx => ExpectedMeasurements::Azure(HashMap::new()), - AttestationType::DcapTdx | AttestationType::GcpTdx | AttestationType::QemuTdx => { + AttestationType::DcapTdx | AttestationType::GcpTdx => { ExpectedMeasurements::Dcap(HashMap::new()) } }, @@ -308,7 +308,6 @@ impl MeasurementPolicy { accepted_measurements: vec![ MeasurementRecord::allow_no_attestation(), MeasurementRecord::allow_any_measurement(AttestationType::DcapTdx), - MeasurementRecord::allow_any_measurement(AttestationType::QemuTdx), MeasurementRecord::allow_any_measurement(AttestationType::GcpTdx), MeasurementRecord::allow_any_measurement(AttestationType::AzureTdx), ], @@ -320,7 +319,6 @@ impl MeasurementPolicy { Self { accepted_measurements: vec![ MeasurementRecord::allow_any_measurement(AttestationType::DcapTdx), - MeasurementRecord::allow_any_measurement(AttestationType::QemuTdx), MeasurementRecord::allow_any_measurement(AttestationType::GcpTdx), MeasurementRecord::allow_any_measurement(AttestationType::AzureTdx), ], @@ -528,29 +526,28 @@ impl MeasurementPolicy { )?; ExpectedMeasurements::Azure(azure_measurements) } - AttestationType::DcapTdx | - AttestationType::GcpTdx | - AttestationType::QemuTdx => ExpectedMeasurements::Dcap( - measurements - .iter() - .map(|(index_str, entry)| { - Ok(( - DcapMeasurementRegister::from_policy_key(index_str)?, - parse_measurement_entry::<48>(entry, index_str)?, - )) - }) - .collect::>, - MeasurementFormatError, - >>()?, - ), + AttestationType::DcapTdx | AttestationType::GcpTdx => { + ExpectedMeasurements::Dcap( + measurements + .iter() + .map(|(index_str, entry)| { + Ok(( + DcapMeasurementRegister::from_policy_key(index_str)?, + parse_measurement_entry::<48>(entry, index_str)?, + )) + }) + .collect::>, + MeasurementFormatError, + >>()?, + ) + } }, (None, Some(image_hashes)) => match attestation_type { // Currently only GCP is supported for portable measurement policy - but support // for other types is planned AttestationType::GcpTdx => ExpectedMeasurements::Image(image_hashes), AttestationType::DcapTdx | - AttestationType::QemuTdx | AttestationType::None | AttestationType::AzureTdx => { return Err( @@ -941,6 +938,30 @@ mod tests { } } + #[tokio::test] + async fn test_parse_legacy_qemu_attestation_type_as_dcap() { + let json = r#"[ + { + "measurement_id": "legacy-qemu", + "attestation_type": "qemu-tdx", + "measurements": { + "mrtd": { + "expected_any": [ + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ] + } + } + } + ]"#; + + let policy = MeasurementPolicy::from_json_bytes(json.as_bytes().to_vec()).unwrap(); + assert_eq!(policy.accepted_measurements.len(), 1); + assert!(matches!( + policy.accepted_measurements[0].measurements, + ExpectedMeasurements::Dcap(_) + )); + } + /// A JSON policy that pins image-component hashes rather than raw /// register values must parse into [`ExpectedMeasurements::Image`] /// so the verifier can reconstruct the expected RTMRs from platform