From 2f651ba0f09f55b5be44b39bc059e54d6f1af393 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Wed, 10 Jun 2026 22:21:32 +0800 Subject: [PATCH] refactor(azure): remove legacy hardcoded intermediate certs Since https://github.com/flashbots/attested-tls/pull/49 was merged, the attestation generator fetches all intermediate certs and included them in TpmAttest. Therefore we no longer need hardcoded intermediate certs in the source code, as we expect the intermediate certs to be provided by the attestation generator. --- .../assets/global-virtual-tpm-ca-03.pem | 32 -------- .../attestation/src/azure/ak_certificate.rs | 73 ++----------------- crates/attestation/src/azure/mod.rs | 70 +----------------- 3 files changed, 8 insertions(+), 167 deletions(-) delete mode 100644 crates/attestation/assets/global-virtual-tpm-ca-03.pem diff --git a/crates/attestation/assets/global-virtual-tpm-ca-03.pem b/crates/attestation/assets/global-virtual-tpm-ca-03.pem deleted file mode 100644 index a8753d0..0000000 --- a/crates/attestation/assets/global-virtual-tpm-ca-03.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFnDCCA4SgAwIBAgITMwAAAAknQOWscnsOpgAAAAAACTANBgkqhkiG9w0BAQwF -ADBpMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u -MTowOAYDVQQDEzFBenVyZSBWaXJ0dWFsIFRQTSBSb290IENlcnRpZmljYXRlIEF1 -dGhvcml0eSAyMDIzMB4XDTI1MDQyNDE4MDExN1oXDTI3MDQyNDE4MDExN1owJTEj -MCEGA1UEAxMaR2xvYmFsIFZpcnR1YWwgVFBNIENBIC0gMDMwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQDYGYtis5ka0cxQkhU11jslgX6wzjR/UXQIFdUn -8juTUMJl91VokwUPX3WfXeog7mtbWyYWD8SI0BSnchRGlV8u3AhcW61/HetHqmIL -tD0c75UATi+gsTQnpwKPA/m38MGGyXFETr3xHXjilUPfIhmxO4ImuNJ0R95bZYhx -bLYmOZpVUcj8oz980An8HlIqSzrskQR6NiuEmikHkHc1/CpoNunrr8kQNPF6gxex -IrvXsKLUAuUqnNtcQWc/8Er5EN9+TdX6AOjUmKriVGbCInP1m/aC+DWH/+aJ/8aD -pKze6fe7OHh2BL9hxqIsmJAStIh4siRdLYTt8hKGmkdzOWnRAgMBAAGjggF/MIIB -ezASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwICBDAXBgNVHSUEEDAO -BgVngQUIAQYFZ4EFCAMwHQYDVR0OBBYEFGcJhvj5gV6TrfnJZOcUCtqZywotMB8G -A1UdIwQYMBaAFEv+JlqUwfYzw4NIJt3z5bBksqqVMHYGA1UdHwRvMG0wa6BpoGeG -ZWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL0F6dXJlJTIwVmly -dHVhbCUyMFRQTSUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIw -MjMuY3JsMIGDBggrBgEFBQcBAQR3MHUwcwYIKwYBBQUHMAKGZ2h0dHA6Ly93d3cu -bWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvQXp1cmUlMjBWaXJ0dWFsJTIwVFBN -JTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMy5jcnQwDQYJ -KoZIhvcNAQEMBQADggIBAJPP3Z2z1zhzUS3qSRVgyoUVnaxCGuMHzPQAZuoPBVpz -wKnv4HqyjMgT8pBtQqxkqAsg7KiqbPfO97bMCHcuqkkfHjw8yg6IYt01RjUjVPKq -lrsY2iw7hFWNWr8SGMa10JdNYNyf5dxob5+mKAwEOhLzKNwq9rM/uIvZky77pNly -RLt55XEPfBMYdI9I8uQ5Uqmrw7mVJfERMfTBhSQF9BrcajAsaLcs7qEUyj0yUdJf -cgZkfCoUEUSPr3OwLHaYeV1J6VidhIYsYo53sXXal91d60NspYgei2nJFei/+R3E -SWnGbPBW+EQ4FbvZXxu57zUMX9mM7lC+GoXLvA6/vtKShEi9ZXl2PSnBQ/R2A7b3 -AXyg4fmMLFausEk6OiuU8E/bvp+gPLOJ8YrX7SAJVuEn+koJaK5G7os5DMIh7/KM -l9cI9WxPwqoWjp4VBfrF4hDOCmKWrqtFUDQCML8qD8RTxlQKQtgeGAcNDfoAuL9K -VtSG5/iIhuyBEFYEHa3vRWbSaHCUzaHJsTmLcz4cp1VDdepzqZRVuErBzJKFnBXb -zRNW32EFmcAUKZImIsE5dgB7y7eiijf33VWNfWmK05fxzQziWFWRYlET4SVc3jMn -PBiY3N8BfK8EBOYbLvzo0qn2n3SAmPhYX3Ag6vbbIHd4Qc8DQKHRV0PB8D3jPGmD ------END CERTIFICATE----- diff --git a/crates/attestation/src/azure/ak_certificate.rs b/crates/attestation/src/azure/ak_certificate.rs index 9213334..e91d4b8 100644 --- a/crates/attestation/src/azure/ak_certificate.rs +++ b/crates/attestation/src/azure/ak_certificate.rs @@ -32,30 +32,6 @@ const MICROSOFT_RSA_DEVICES_ROOT_2021: &str = const AZURE_VIRTUAL_TPM_ROOT_2023: &str = include_str!("../../assets/azure-virtual-tpm-root-2023.pem"); -// globalVirtualTPMCA03 is the intermediate CA that issues TDX vTPM AK -// certificates Source: https://learn.microsoft.com/en-us/azure/virtual-machines/trusted-launch-faq -// Issuer: Azure Virtual TPM Root Certificate Authority 2023 -// Valid: 2025-04-24 to 2027-04-24 -const GLOBAL_VIRTUAL_TPMCA03_PEM: &str = include_str!("../../assets/global-virtual-tpm-ca-03.pem"); - -/// Legacy Azure intermediate certificates bundled with this crate. -/// -/// Deprecated verification-only fallback: this is kept only for backwards -/// compatibility with older evidence that did not serialize AIA-fetched -/// intermediates. New Azure vTPM evidence should carry the AK issuer chain -/// fetched from AIA instead of relying on this bundled, eventually-stale -/// list. -/// -/// Do not use this when generating new evidence or when deciding whether an -/// AIA-fetched issuer chain is complete. It should be removed once -/// supporting legacy evidence without `ak_intermediate_certificates_pem` is -/// no longer required. -static LEGACY_BUNDLED_AZURE_INTERMEDIATES: Lazy>> = Lazy::new(|| { - let (_type_label, cert_der) = - pem_rfc7468::decode_vec(GLOBAL_VIRTUAL_TPMCA03_PEM.as_bytes()).expect("Cannot decode PEM"); - vec![CertificateDer::from(cert_der)] -}); - /// The root anchors for azure static AZURE_ROOT_ANCHORS: Lazy>> = Lazy::new(|| { vec![ @@ -68,50 +44,17 @@ static AZURE_ROOT_ANCHORS: Lazy>> = Lazy::new(|| { /// Verify an AK certificate against pinned Azure root CAs. /// -/// This includes `LEGACY_BUNDLED_AZURE_INTERMEDIATES` as a -/// verification-only fallback so older evidence captured before AIA-fetched -/// intermediates were serialized continues to verify. +/// `intermediate_cert_ders` are untrusted evidence from the attestation (or +/// fetched from AIA during generation); verification pins the Azure roots. pub(crate) fn verify_ak_cert_with_azure_roots( ak_cert_der: &[u8], intermediate_cert_ders: &[Vec], now_secs: u64, -) -> Result<(), MaaError> { - verify_ak_cert_with_azure_roots_inner(ak_cert_der, intermediate_cert_ders, now_secs, true) -} - -/// Verify an AK certificate against pinned Azure root CAs using only the -/// intermediates supplied by the caller. -/// -/// This is used while following AIA URLs during evidence generation. After -/// each fetched issuer is appended, we call this to check whether the -/// fetched chain is already complete. It intentionally excludes the legacy -/// bundled intermediates, otherwise generation could stop early because a -/// hardcoded intermediate completed the path, and the serialized evidence -/// would still depend on that legacy fallback. -fn verify_ak_cert_with_provided_intermediates_only( - ak_cert_der: &[u8], - intermediate_cert_ders: &[Vec], - now_secs: u64, -) -> Result<(), MaaError> { - verify_ak_cert_with_azure_roots_inner(ak_cert_der, intermediate_cert_ders, now_secs, false) -} - -fn verify_ak_cert_with_azure_roots_inner( - ak_cert_der: &[u8], - intermediate_cert_ders: &[Vec], - now_secs: u64, - include_legacy_bundled_intermediates: bool, ) -> Result<(), MaaError> { let ak_cert_der: CertificateDer = ak_cert_der.into(); let end_entity_cert = EndEntityCert::try_from(&ak_cert_der)?; - - let mut intermediates = if include_legacy_bundled_intermediates { - LEGACY_BUNDLED_AZURE_INTERMEDIATES.clone() - } else { - Vec::new() - }; - intermediates.extend(intermediate_cert_ders.iter().cloned().map(CertificateDer::from)); - + let intermediates: Vec<_> = + intermediate_cert_ders.iter().cloned().map(CertificateDer::from).collect(); let now = UnixTime::since_unix_epoch(Duration::from_secs(now_secs)); end_entity_cert.verify_for_usage( @@ -145,9 +88,7 @@ pub(crate) fn fetch_ak_intermediates_from_aia( now_secs: u64, ) -> Result>, MaaError> { let mut intermediates = Vec::new(); - if verify_ak_cert_with_provided_intermediates_only(ak_cert_der, &intermediates, now_secs) - .is_ok() - { + if verify_ak_cert_with_azure_roots(ak_cert_der, &intermediates, now_secs).is_ok() { return Ok(intermediates); } @@ -159,9 +100,7 @@ pub(crate) fn fetch_ak_intermediates_from_aia( issuer_urls = fetched_issuer.ca_issuers_urls; intermediates.push(fetched_issuer.der); - if verify_ak_cert_with_provided_intermediates_only(ak_cert_der, &intermediates, now_secs) - .is_ok() - { + if verify_ak_cert_with_azure_roots(ak_cert_der, &intermediates, now_secs).is_ok() { return Ok(intermediates); } else if intermediates.len() == MAX_EVIDENCE_AK_INTERMEDIATE_CERTIFICATES { return Err(MaaError::AkIssuerChainTooDeep { diff --git a/crates/attestation/src/azure/mod.rs b/crates/attestation/src/azure/mod.rs index eb31173..e0953a3 100644 --- a/crates/attestation/src/azure/mod.rs +++ b/crates/attestation/src/azure/mod.rs @@ -668,7 +668,6 @@ mod test_utils { #[cfg(test)] mod tests { use super::*; - use crate::measurements::MeasurementPolicy; fn input_data_from_attestation(attestation_bytes: &[u8]) -> [u8; 64] { let attestation_document: AttestationDocument = @@ -695,76 +694,11 @@ mod tests { assert_eq!(hex::decode(var_data_values["user-data"].as_str().unwrap()).unwrap().len(), 64); } - /// Verify a stored attestation from a test-deployment on azure - #[tokio::test] - async fn test_verify() { - let attestation_bytes: &'static [u8] = - include_bytes!("../../test-assets/azure-tdx-1764662251380464271.yaml"); - - // To avoid this test stopping working when the certificate is no longer - // valid we pass in a timestamp - let now = 1771423480; - - let measurements_json = br#" - [{ - "measurement_id": "cvm-image-azure-tdx.rootfs-20241107200854.wic.vhd", - "attestation_type": "azure-tdx", - "measurements": { - "4": { - "expected": "c4a25a6d7704629f63db84d20ea8db0e9ce002b2801be9a340091fe7ac588699" - }, - "9": { - "expected": "9f4a5775122ca4703e135a9ae6041edead0064262e399df11ca85182b0f1541d" - }, - "11": { - "expected": "abd7c695ffdb6081e99636ee016d1322919c68d049b698b399d22ae215a121bf" - } - } - }] - "#; - - let measurement_policy = - MeasurementPolicy::from_json_bytes(measurements_json.to_vec()).unwrap(); - - let collateral_bytes: &'static [u8] = - include_bytes!("../../test-assets/azure-collateral02.yaml"); - - let async_collateral = serde_saphyr::from_slice(collateral_bytes).unwrap(); - let sync_collateral = serde_saphyr::from_slice(collateral_bytes).unwrap(); - let attestation_json = serde_json::to_vec( - &serde_saphyr::from_slice::(attestation_bytes).unwrap(), - ) - .unwrap(); - - let async_measurements = verify_azure_attestation_with_given_timestamp( - attestation_json.clone(), - [0; 64], // Input data - None, - async_collateral, - now, - false, - ) - .await - .unwrap(); - - let sync_measurements = verify_azure_attestation_with_given_timestamp_sync( - attestation_json, - [0; 64], // Input data - Pccs::new_without_prewarm(None), - sync_collateral, - now, - false, - ) - .unwrap(); - - assert_eq!(async_measurements, sync_measurements); - measurement_policy.check_measurement(&async_measurements).unwrap(); - } - /// Verify a complete observed Azure attestation payload that includes /// AK intermediates fetched from the leaf certificate's AIA URLs. #[tokio::test] - async fn test_verify_with_observed_ak_intermediates() { + async fn test_verify() { + // generated using [capture_azure_fixture] above. let attestation_bytes: &'static [u8] = include_bytes!("../../test-assets/azure-tdx-with-ak-intermediates-1780922561.yaml"); let collateral_bytes: &'static [u8] = include_bytes!(