Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions crates/attestation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,14 @@ 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
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
Expand Down Expand Up @@ -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.
20 changes: 8 additions & 12 deletions crates/attestation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(&quote)?))
Expand All @@ -86,7 +86,7 @@ impl From<attest_types::AttestationType> 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,
}
}
}
Expand All @@ -99,9 +99,7 @@ impl TryFrom<AttestationType> 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),
}
}
}
Expand All @@ -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,
}

Expand All @@ -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",
}
Expand Down Expand Up @@ -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")))]
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
63 changes: 42 additions & 21 deletions crates/attestation/src/measurements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ impl MultiMeasurements {
})
.collect::<Result<_, MeasurementFormatError>>()?,
),
AttestationType::DcapTdx | AttestationType::GcpTdx | AttestationType::QemuTdx => {
AttestationType::DcapTdx | AttestationType::GcpTdx => {
let measurements_map = measurements_map
.into_iter()
.map(|(k, v)| {
Expand Down Expand Up @@ -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())
}
},
Expand Down Expand Up @@ -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),
],
Expand All @@ -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),
],
Expand Down Expand Up @@ -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::<Result<
HashMap<DcapMeasurementRegister, Vec<[u8; 48]>>,
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::<Result<
HashMap<DcapMeasurementRegister, Vec<[u8; 48]>>,
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(
Expand Down Expand Up @@ -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
Expand Down
Loading