From 291f8aa57158c1832a86d58a16d0eae9ee5c0704 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Mon, 17 Nov 2025 16:52:34 +0000 Subject: [PATCH 01/10] add boiler code for Azure vTPM certificate verification --- internal/attestation/azure/tdx/issuer.go | 106 ++++++++++++++++++++ internal/attestation/azure/tdx/tdx.go | 2 + internal/attestation/azure/tdx/validator.go | 90 +++++++++++++++++ 3 files changed, 198 insertions(+) diff --git a/internal/attestation/azure/tdx/issuer.go b/internal/attestation/azure/tdx/issuer.go index 7265e42..b2f6a6f 100644 --- a/internal/attestation/azure/tdx/issuer.go +++ b/internal/attestation/azure/tdx/issuer.go @@ -9,12 +9,14 @@ package tdx import ( "bytes" "context" + "crypto/x509" "encoding/base64" "encoding/binary" "encoding/json" "fmt" "io" "net/http" + "time" "github.com/flashbots/cvm-reverse-proxy/internal/attestation" "github.com/flashbots/cvm-reverse-proxy/internal/attestation/azure" @@ -27,6 +29,7 @@ import ( const ( imdsURL = "http://169.254.169.254/acc/tdquote" indexHCLReport = 0x1400001 + tpmAkCertIdx = 0x1C101D0 hclDataOffset = 1216 hclReportTypeOffset = 8 hclReportTypeOffsetStart = hclDataOffset + hclReportTypeOffset @@ -53,6 +56,7 @@ type Issuer struct { *vtpm.Issuer quoteGetter quoteGetter + log attestation.Logger } // NewIssuer initializes a new Azure Issuer. @@ -61,6 +65,7 @@ func NewIssuer(log attestation.Logger) *Issuer { quoteGetter: imdsQuoteGetter{ client: &http.Client{Transport: &http.Transport{Proxy: nil}}, }, + log: log, } i.Issuer = vtpm.NewIssuer( @@ -91,9 +96,73 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ return nil, fmt.Errorf("getting quote: %w", err) } + // Read the vTPM AK certificate from TPM NV index + // This certificate is signed by Azure and needs to be validated on the validator side + certDERRaw, err := tpm2.NVReadEx(tpm, tpmAkCertIdx, tpm2.HandleOwner, "", 0) + if err != nil { + return nil, fmt.Errorf("reading attestation key certificate from TPM: %w", err) + } + + i.log.Info(fmt.Sprintf("Read %d bytes from TPM AK cert index", len(certDERRaw))) + + // The TPM NV index contains trailing data. We need to extract just the certificate. + // X.509 DER certificates start with 0x30 (SEQUENCE) followed by length encoding + var cleanCertDER []byte + if len(certDERRaw) > 4 && certDERRaw[0] == 0x30 { + // Parse the DER length to extract exactly the certificate bytes + var certLen int + if certDERRaw[1] < 0x80 { + // Short form: length is in the second byte + certLen = int(certDERRaw[1]) + 2 + } else if certDERRaw[1] == 0x82 { + // Long form with 2 length bytes + certLen = (int(certDERRaw[2]) << 8) | int(certDERRaw[3]) + certLen += 4 // Add header bytes + } else if certDERRaw[1] == 0x81 { + // Long form with 1 length byte + certLen = int(certDERRaw[2]) + 3 + } else { + return nil, fmt.Errorf("unsupported DER length encoding: 0x%02x", certDERRaw[1]) + } + + if certLen > 0 && certLen <= len(certDERRaw) { + cleanCertDER = certDERRaw[:certLen] + i.log.Info(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", certLen, len(certDERRaw))) + } else { + return nil, fmt.Errorf("invalid certificate length: %d (total data: %d)", certLen, len(certDERRaw)) + } + } else { + return nil, fmt.Errorf("invalid certificate format: does not start with DER SEQUENCE tag") + } + + // Verify we can parse the extracted certificate + cert, err := x509.ParseCertificate(cleanCertDER) + if err != nil { + return nil, fmt.Errorf("parsing extracted attestation key certificate: %w", err) + } + + // Fetch the CA certificate if the AK cert has IssuingCertificateURL extension + var caCertDER []byte + if len(cert.IssuingCertificateURL) > 0 { + i.log.Info(fmt.Sprintf("Downloading CA certificate from: %s", cert.IssuingCertificateURL[0])) + caCert, err := downloadCACertificate(ctx, cert.IssuingCertificateURL) + if err != nil { + i.log.Warn(fmt.Sprintf("Failed to download CA certificate: %v", err)) + // Don't fail here - validator can still verify directly against root + } else { + // Use the parsed certificate's Raw field to ensure clean DER encoding + caCertDER = caCert.Raw + i.log.Info(fmt.Sprintf("Successfully downloaded CA certificate: %s", caCert.Subject.String())) + } + } else { + i.log.Info("No IssuingCertificateURL in AK certificate - will verify directly against root CA") + } + instanceInfo := InstanceInfo{ AttestationReport: quote, RuntimeData: runtimeData, + AkCert: cleanCertDER, // Use the clean certificate + CA: caCertDER, } instanceInfoJSON, err := json.Marshal(instanceInfo) if err != nil { @@ -102,6 +171,43 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ return instanceInfoJSON, nil } +// Helper function to download CA certificate from URLs +func downloadCACertificate(ctx context.Context, urls []string) (*x509.Certificate, error) { + client := &http.Client{Timeout: 10 * time.Second} + + for _, url := range urls { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + continue + } + + resp, err := client.Do(req) + if err != nil { + continue + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + continue + } + + certDER, err := io.ReadAll(resp.Body) + if err != nil { + continue + } + + // Parse and validate the certificate + cert, err := x509.ParseCertificate(certDER) + if err != nil { + continue + } + + return cert, nil + } + + return nil, fmt.Errorf("failed to download CA certificate from any URL") +} + func parseHCLReport(report []byte) (hwReport, runtimeData []byte, err error) { // First, ensure the extracted report is actually for TDX if len(report) < hclReportTypeOffsetStart+4 { diff --git a/internal/attestation/azure/tdx/tdx.go b/internal/attestation/azure/tdx/tdx.go index eaee616..829c5fe 100644 --- a/internal/attestation/azure/tdx/tdx.go +++ b/internal/attestation/azure/tdx/tdx.go @@ -23,4 +23,6 @@ package tdx type InstanceInfo struct { AttestationReport []byte RuntimeData []byte + AkCert []byte + CA []byte } diff --git a/internal/attestation/azure/tdx/validator.go b/internal/attestation/azure/tdx/validator.go index 48e138e..2dc5977 100644 --- a/internal/attestation/azure/tdx/validator.go +++ b/internal/attestation/azure/tdx/validator.go @@ -9,8 +9,10 @@ package tdx import ( "context" "crypto" + "crypto/rsa" "crypto/x509" "encoding/json" + "errors" "fmt" "github.com/flashbots/cvm-reverse-proxy/internal/attestation" @@ -18,6 +20,7 @@ import ( "github.com/flashbots/cvm-reverse-proxy/internal/attestation/variant" "github.com/flashbots/cvm-reverse-proxy/internal/attestation/vtpm" "github.com/flashbots/cvm-reverse-proxy/internal/config" + certutil "github.com/flashbots/cvm-reverse-proxy/internal/crypto" "github.com/google/go-tdx-guest/abi" "github.com/google/go-tdx-guest/pcs" @@ -31,6 +34,11 @@ import ( const AZURE_V6_BAD_FMSPC = "90c06f000000" +// ameRoot is the AME root CA certificate used to sign Azure's AME Infra CA certificates. +// The certificate can be found at http://crl.microsoft.com/pkiinfra/certs/AMERoot_ameroot.crt. +// This is the same root CA used by both TrustedLaunch and TDX VMs. +var ameRoot = mustParseX509("-----BEGIN CERTIFICATE-----\nMIIFVjCCAz6gAwIBAgIQJdrLVcnGd4FAnlaUgt5N/jANBgkqhkiG9w0BAQsFADA8\nMRMwEQYKCZImiZPyLGQBGRYDR0JMMRMwEQYKCZImiZPyLGQBGRYDQU1FMRAwDgYD\nVQQDEwdhbWVyb290MB4XDTE2MDUyNDIyNTI1NFoXDTI2MDUyNDIyNTcwM1owPDET\nMBEGCgmSJomT8ixkARkWA0dCTDETMBEGCgmSJomT8ixkARkWA0FNRTEQMA4GA1UE\nAxMHYW1lcm9vdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALv4uChY\noVuO+bxBOcn8v4FajoGkxo0YgVwEqEPDVPI6vzmnEqHVhQ1GMVeDyiRrgQT1vCk1\nHMMzo9LlWowPrzbXOwjOTFbXc36+UU41yNN2GeNa49RXbAkfbzKE/SYLfbqOD0dN\nZLwvOhgIb25oA1eAxW/DI/hvJLLKh2SscvkIyd3o2BUeFm7NtyYG/buCKJh8lOq8\n0iBwRoEoInb0vhorHaswSMmqY1g+AJndY/M7uGUqkhDGBhLu53bU9wbUPHsEI+wa\nq6WypCijZYT+C4BS5GJrEPZ2O92pztd+ULqhzNRoPj5RuElUww7+z5RnbCaupyBY\nOmmJMH30EiRSq8dK/irixXXwJraSywR5kyfmAkv6GYWlRlxFUiK3/co47JLA3TDK\nN0wfutbpqxdZQYyGfO2nZrr5JbKfSU0sMtOZDkK6hlafV++hfkVSvFfNHE5B5uN1\nMK6agl1dzi28HfJT9aO7cmjGxl1SJ5qoCvcwZNQ2SPHFdrslcwXEFOMDaEzVOA3V\n7j3+6lrT8sHXg0sErkcd8lrBImfzhLxM/Wh8CgOUNeUu3flUoxmFv3el+QWalSNy\n2SXs2NgWuYE5Iog7CHD/xCnoEnZwwjqLkrro4hYWE4Xj3VlA2Eq+VxqJOgdyFl3m\nckSZ08OcwLeprY4+2GEvCXNGNdXUmNNgk2PvAgMBAAGjVDBSMAsGA1UdDwQEAwIB\nhjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQpXlFeZK40ueusnA2njHUB\n0QkLKDAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQsFAAOCAgEAcznFDnJx\nsXaazFY1DuIPvUaiWS7ELxAVXMGZ7ROjLrDq1FNYVewL4emDqyEIEMFncec8rqyk\nVBvLQA5YqMCxQWJpL0SlgRSknzLh9ZVcQw1TshC49/XV2N/CLOuyInEQwS//46so\nT20Cf8UGUiOK472LZlvM4KchyDR3FTNtmMg0B/LKVjevpX9sk5MiyjjLUj3jtPIP\n7jpsfZDd/BNsg/89kpsIF5O64I7iYFj3MHu9o4UJcEX0hRt7OzUxqa9THTssvzE5\nVkWo8Rtou2T5TobKV6Rr5Ob9wchLXqVtCyZF16voEKheBnalhGUvErI/6VtBwLb7\n13C0JkKLBNMen+HClNliicVIaubnpY2g+AqxOgKBHiZnzq2HhE1qqEUf4VfqahNU\niaXtbtyo54f2dCf9UL9uG9dllN3nxBE/Y/aWF6E1M8Bslj1aYAtfUQ/xlhEXCly6\nzohw697i3XFUt76RwvfW8quvqdH9Mx0PBpYo4wJJRwAecSJQNy6wIJhAuDgOemXJ\nYViBi/bDnhPcFEVQxsypQSw91BUw7Mxh+W59H5MC25SAIw9fLMT9LRqSYpPyasNp\n4nACjR+bv/6cI+ICOrGmD2mrk2c4dNnYpDx96FfX/Y158RV0wotqIglACk6m1qyo\nyTra6P0Kvo6xz4KaVm8F7VDzUP+heAAhPAs=\n-----END CERTIFICATE-----\n") + // Validator for Azure confidential VM attestation using TDX. type Validator struct { variant.AzureTDX @@ -98,6 +106,12 @@ func (v *Validator) getTrustedTPMKey(_ context.Context, attDoc vtpm.AttestationD return nil, fmt.Errorf("validating HCLAkPub: %w", err) } + // Verify the vTPM AK certificate chain to prevent forging attestation attacks + // This ensures the attestation key is actually signed by Azure's CA + if err := v.verifyAKCertificate(instanceInfo, &pubArea); err != nil { + return nil, fmt.Errorf("verifying AK certificate: %w", err) + } + return pubArea.Key() } @@ -146,6 +160,82 @@ func (v *Validator) validateQuote(tdxQuote *tdx.QuoteV4) error { return nil } +// verifyAKCertificate verifies the vTPM attestation key certificate chain. +// This prevents attacks where an attacker could forge attestation by using their own key. +func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2.Public) error { + v.log.Info("Starting vTPM AK certificate verification") + + // Parse the AK certificate + akCert, err := x509.ParseCertificate(instanceInfo.AkCert) + if err != nil { + return fmt.Errorf("parsing attestation key certificate: %w", err) + } + v.log.Info(fmt.Sprintf("AK Certificate Subject: %s", akCert.Subject.String())) + v.log.Info(fmt.Sprintf("AK Certificate Issuer: %s", akCert.Issuer.String())) + + // Setup certificate pools for verification + roots := x509.NewCertPool() + roots.AddCert(ameRoot) + v.log.Info(fmt.Sprintf("AME Root Certificate Subject: %s", ameRoot.Subject.String())) + + intermediates := x509.NewCertPool() + + // Parse the CA certificate if provided + // For TDX, the AK certificate may be issued directly by the root CA + if len(instanceInfo.CA) > 0 { + akCertCA, err := x509.ParseCertificate(instanceInfo.CA) + if err != nil { + v.log.Warn(fmt.Sprintf("Failed to parse CA certificate: %v - will try direct root verification", err)) + } else { + v.log.Info(fmt.Sprintf("CA Certificate Subject: %s", akCertCA.Subject.String())) + v.log.Info(fmt.Sprintf("CA Certificate Issuer: %s", akCertCA.Issuer.String())) + intermediates.AddCert(akCertCA) + } + } else { + v.log.Info("No CA certificate provided - verifying directly against root CA") + } + + // Verify the certificate chain + v.log.Info("Verifying certificate chain against AME root CA") + if _, err := akCert.Verify(x509.VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + }); err != nil { + v.log.Warn(fmt.Sprintf("Certificate chain verification failed: %v", err)) + return fmt.Errorf("verifying attestation key certificate chain: %w", err) + } + v.log.Info("Certificate chain verification successful") + + // Verify that the public key in the certificate matches the TPM's AK public key + pubKey, err := pubArea.Key() + if err != nil { + return fmt.Errorf("getting public key from TPM: %w", err) + } + + pubKeyRSA, ok := pubKey.(*rsa.PublicKey) + if !ok { + return errors.New("attestation key is not an RSA key") + } + + if !pubKeyRSA.Equal(akCert.PublicKey) { + v.log.Warn("Certificate public key does not match TPM attestation key") + return errors.New("certificate public key does not match attestation key") + } + v.log.Info("Certificate public key matches TPM attestation key") + v.log.Info("vTPM AK certificate verification completed successfully") + + return nil +} + +func mustParseX509(pem string) *x509.Certificate { + cert, err := certutil.PemToX509Cert([]byte(pem)) + if err != nil { + panic(err) + } + return cert +} + type hclAkValidator interface { Validate(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error } From 68a408125638da6b6824308f758b8192f7550d61 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Mon, 17 Nov 2025 18:02:46 +0000 Subject: [PATCH 02/10] feat: add the required certificates for TDX vTPM --- internal/attestation/azure/tdx/validator.go | 157 +++++++++++++++++--- 1 file changed, 139 insertions(+), 18 deletions(-) diff --git a/internal/attestation/azure/tdx/validator.go b/internal/attestation/azure/tdx/validator.go index 2dc5977..8e3f8f4 100644 --- a/internal/attestation/azure/tdx/validator.go +++ b/internal/attestation/azure/tdx/validator.go @@ -34,10 +34,116 @@ import ( const AZURE_V6_BAD_FMSPC = "90c06f000000" -// ameRoot is the AME root CA certificate used to sign Azure's AME Infra CA certificates. -// The certificate can be found at http://crl.microsoft.com/pkiinfra/certs/AMERoot_ameroot.crt. -// This is the same root CA used by both TrustedLaunch and TDX VMs. -var ameRoot = mustParseX509("-----BEGIN CERTIFICATE-----\nMIIFVjCCAz6gAwIBAgIQJdrLVcnGd4FAnlaUgt5N/jANBgkqhkiG9w0BAQsFADA8\nMRMwEQYKCZImiZPyLGQBGRYDR0JMMRMwEQYKCZImiZPyLGQBGRYDQU1FMRAwDgYD\nVQQDEwdhbWVyb290MB4XDTE2MDUyNDIyNTI1NFoXDTI2MDUyNDIyNTcwM1owPDET\nMBEGCgmSJomT8ixkARkWA0dCTDETMBEGCgmSJomT8ixkARkWA0FNRTEQMA4GA1UE\nAxMHYW1lcm9vdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALv4uChY\noVuO+bxBOcn8v4FajoGkxo0YgVwEqEPDVPI6vzmnEqHVhQ1GMVeDyiRrgQT1vCk1\nHMMzo9LlWowPrzbXOwjOTFbXc36+UU41yNN2GeNa49RXbAkfbzKE/SYLfbqOD0dN\nZLwvOhgIb25oA1eAxW/DI/hvJLLKh2SscvkIyd3o2BUeFm7NtyYG/buCKJh8lOq8\n0iBwRoEoInb0vhorHaswSMmqY1g+AJndY/M7uGUqkhDGBhLu53bU9wbUPHsEI+wa\nq6WypCijZYT+C4BS5GJrEPZ2O92pztd+ULqhzNRoPj5RuElUww7+z5RnbCaupyBY\nOmmJMH30EiRSq8dK/irixXXwJraSywR5kyfmAkv6GYWlRlxFUiK3/co47JLA3TDK\nN0wfutbpqxdZQYyGfO2nZrr5JbKfSU0sMtOZDkK6hlafV++hfkVSvFfNHE5B5uN1\nMK6agl1dzi28HfJT9aO7cmjGxl1SJ5qoCvcwZNQ2SPHFdrslcwXEFOMDaEzVOA3V\n7j3+6lrT8sHXg0sErkcd8lrBImfzhLxM/Wh8CgOUNeUu3flUoxmFv3el+QWalSNy\n2SXs2NgWuYE5Iog7CHD/xCnoEnZwwjqLkrro4hYWE4Xj3VlA2Eq+VxqJOgdyFl3m\nckSZ08OcwLeprY4+2GEvCXNGNdXUmNNgk2PvAgMBAAGjVDBSMAsGA1UdDwQEAwIB\nhjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQpXlFeZK40ueusnA2njHUB\n0QkLKDAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQsFAAOCAgEAcznFDnJx\nsXaazFY1DuIPvUaiWS7ELxAVXMGZ7ROjLrDq1FNYVewL4emDqyEIEMFncec8rqyk\nVBvLQA5YqMCxQWJpL0SlgRSknzLh9ZVcQw1TshC49/XV2N/CLOuyInEQwS//46so\nT20Cf8UGUiOK472LZlvM4KchyDR3FTNtmMg0B/LKVjevpX9sk5MiyjjLUj3jtPIP\n7jpsfZDd/BNsg/89kpsIF5O64I7iYFj3MHu9o4UJcEX0hRt7OzUxqa9THTssvzE5\nVkWo8Rtou2T5TobKV6Rr5Ob9wchLXqVtCyZF16voEKheBnalhGUvErI/6VtBwLb7\n13C0JkKLBNMen+HClNliicVIaubnpY2g+AqxOgKBHiZnzq2HhE1qqEUf4VfqahNU\niaXtbtyo54f2dCf9UL9uG9dllN3nxBE/Y/aWF6E1M8Bslj1aYAtfUQ/xlhEXCly6\nzohw697i3XFUt76RwvfW8quvqdH9Mx0PBpYo4wJJRwAecSJQNy6wIJhAuDgOemXJ\nYViBi/bDnhPcFEVQxsypQSw91BUw7Mxh+W59H5MC25SAIw9fLMT9LRqSYpPyasNp\n4nACjR+bv/6cI+ICOrGmD2mrk2c4dNnYpDx96FfX/Y158RV0wotqIglACk6m1qyo\nyTra6P0Kvo6xz4KaVm8F7VDzUP+heAAhPAs=\n-----END CERTIFICATE-----\n") +// microsoftRSADevicesRoot2021 is the root CA certificate used to sign Azure TDX vTPM certificates. +// This is different from the AME root CA used by TrustedLaunch VMs. +// The certificate can be downloaded from: +// http://www.microsoft.com/pkiops/certs/Microsoft%20RSA%20Devices%20Root%20CA%202021.crt +var microsoftRSADevicesRoot2021 = mustParseX509(`-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIQGWCAkS2F96VGa+6hm2M3rjANBgkqhkiG9w0BAQwFADBa +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSsw +KQYDVQQDEyJNaWNyb3NvZnQgUlNBIERldmljZXMgUm9vdCBDQSAyMDIxMB4XDTIx +MDgyNjIzMzkxOFoXDTQ2MDgyNjIzNDcxNFowWjELMAkGA1UEBhMCVVMxHjAcBgNV +BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjErMCkGA1UEAxMiTWljcm9zb2Z0IFJT +QSBEZXZpY2VzIFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBALF4kgr3bAptorWmkrM6u47osmLfg67KxZPE4W74Zw5Bu64tjEuzegcB +6lFkoXi2V4eLdIRshk3l14jul6ghCML/6gh4hYiTExky3XMY05wg0d1o+AdhuyvC +anXvQZratosnL+KhR2qFeagthciIrCibIIKX91LvqRl/Eg8uo82fl30gieB40Sun +Pe/SfMJLb7AYbQ95yHK8G1lTFUHkIfPbAY6SfkOBUpNJ6UAtjlAmIaHYpdcdOayf +qXyhW3+Hf0Ou2wiKYJihCqh3TaI2hqmiv4p4CScug9sDcTyafA6OYLyTe3vx7Krn +BOUvkSkTj80GrXSKCWnrw+bE7z0deptPuLS6+n83ImLsBZ3XYhX4iUPmTRSU9vr7 +q0cZA8P8zAzLaeN+uK14l92u/7TMhkp5etmLE9DMd9MtnsLZSy18UpW4ZlBXxt9Z +w/RFKStlNbK5ILsI2HdSjgkF0DxZtNnCiEQehMu5DBfCdXo1P90iJhfF1MD+2Kh5 +xeuDQEC7Dh3gUSXIkOm/72u1fE52r0uY+aH1TCQGbCrijI9Jf78lFbI7L6Ll3YAa +89MrDs2tAQG0SaJdabh4k5orqaJOgaqrrq61RzcMjlZGI3dOdL+f6romKOccFkm0 +k+gwjvZ9xaJ5i9SB6Lq/GrA8YxzjmKHHVPmGGdm/v93R0oNGfyvxAgMBAAGjVDBS +MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSERIYG +AJg/LKqzxYnzrC7J5p0JAzAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwF +AAOCAgEAd3RAo42nyNbVvj+mxZ03VV+ceU6nCdgIS8RZfZBxf+lqupRzKUV9UW59 +IRCSeMH3gHfGSVhmwH1AJHkFIhd5meSShF4lPPmvYMmrbfOrwiUunqz2aix/QkRp +geMOe10wm6dEHHAw/eNi3PWhc+jdGJNV0SdnqcwJg/t5db8Y7RCVW+tG3DtEa63U +B4sGNlBbaUffdSdYL5TCRXm2mkcCWruu/gmDTgoabFmI4j9ss0shsIxwqVVEq2zk +EH1ypZrHSmVrTRh9hPHWpkOxnh9yqpGDXcSll09ZZUBUhx7YUX6p+BTVWnuuyR4T +bXS8P6fUS5Q2WF0WR07BrGYlBqomsEwMhth1SmBKn6tXfQyWkgr4pVl5XkkC7Bfv +pmw90csy8ycwog+x4L9kO1Nr6OPwnJ9V39oMifNDxnvYVBX7EhjoiARPp+97feNJ +YwMt4Os/WSeD++IhBB9xVsrI+jZufySQ02C/w1LBFR6zPy+a+v+6WlvMxDBEDWOj +JyDQ6kzkWxIG35klzLnwHybuIsFIIR1QGL1l47eW2dM4hB9oCay6z3FX5xYBIFvA +yp8up+KbjfH/NIWfPBXhYMW64DagB9P2cW5LBRz+AzDA+JF/OdYpb6vxv3lzjLQb +U9zMFwSrzEF5o2Aa/n+xZ90Naj78AYaTM18DalA17037fjucDN8= +-----END CERTIFICATE-----`) + +// azureVirtualTPMRoot2023 is the root CA for Azure vTPM (used by both Trusted Launch and TDX) +// Source: https://learn.microsoft.com/en-us/azure/virtual-machines/trusted-launch-faq +// Valid until: 2048-06-01 +var azureVirtualTPMRoot2023 = mustParseX509(`-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQUfQx2iySCIpOKeDZKd5KpzANBgkqhkiG9w0BAQwFADBp +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTow +OAYDVQQDEzFBenVyZSBWaXJ0dWFsIFRQTSBSb290IENlcnRpZmljYXRlIEF1dGhv +cml0eSAyMDIzMB4XDTIzMDYwMTE4MDg1M1oXDTQ4MDYwMTE4MTU0MVowaTELMAkG +A1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE6MDgGA1UE +AxMxQXp1cmUgVmlydHVhbCBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkg +MjAyMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALoMMwvdRJ7+bW00 +adKE1VemNqJS+268Ure8QcfZXVOsVO22+PL9WRoPnWo0r5dVoomYGbobh4HC72s9 +sGY6BGRe+Ui2LMwuWnirBtOjaJ34r1ZieNMcVNJT/dXW5HN/HLlm/gSKlWzqCEx6 +gFFAQTvyYl/5jYI4Oe05zJ7ojgjK/6ZHXpFysXnyUITJ9qgjn546IJh/G5OMC3mD +fFU7A/GAi+LYaOHSzXj69Lk1vCftNq9DcQHtB7otO0VxFkRLaULcfu/AYHM7FC/S +q6cJb9Au8K/IUhw/5lJSXZawLJwHpcEYzETm2blad0VHsACaLNucZL5wBi8GEusQ +9Wo8W1p1rUCMp89pufxa3Ar9sYZvWeJlvKggWcQVUlhvvIZEnT+fteEvwTdoajl5 +qSvZbDPGCPjb91rSznoiLq8XqgQBBFjnEiTL+ViaZmyZPYUsBvBY3lKXB1l2hgga +hfBIag4j0wcgqlL82SL7pAdGjq0Fou6SKgHnkkrV5CNxUBBVMNCwUoj5mvEjd5mF +7XPgfM98qNABb2Aqtfl+VuCkU/G1XvFoTqS9AkwbLTGFMS9+jCEU2rw6wnKuGv1T +x9iuSdNvsXt8stx4fkVeJvnFpJeAIwBZVgKRSTa3w3099k0mW8qGiMnwCI5SfdZ2 +SJyD4uEmszsnieE6wAWd1tLLg1jvAgMBAAGjVDBSMA4GA1UdDwEB/wQEAwIBhjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRL/iZalMH2M8ODSCbd8+WwZLKqlTAQ +BgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEALgNAyg8I0ANNO/8I +2BhpTOsbywN2YSmShAmig5h4sCtaJSM1dRXwA+keY6PCXQEt/PRAQAiHNcOF5zbu +OU1Bw/Z5Z7k9okt04eu8CsS2Bpc+POg9js6lBtmigM5LWJCH1goMD0kJYpzkaCzx +1TdD3yjo0xSxgGhabk5Iu1soD3OxhUyIFcxaluhwkiVINt3Jhy7G7VJTlEwkk21A +oOrQxUsJH0f2GXjYShS1r9qLPzLf7ykcOm62jHGmLZVZujBzLIdNk1bljP9VuGW+ +cISBwzkNeEMMFufcL2xh6s/oiUnXicFWvG7E6ioPnayYXrHy3Rh68XLnhfpzeCzv +bz/I4yMV38qGo/cAY2OJpXUuuD/ZbI5rT+lRBEkDW1kxHP8cpwkRwGopV8+gX2KS +UucIIN4l8/rrNDEX8T0b5U+BUqiO7Z5YnxCya/H0ZIwmQnTlLRTU2fW+OGG+xyIr +jMi/0l6/yWPUkIAkNtvS/yO7USRVLPbtGVk3Qre6HcqacCXzEjINcJhGEVg83Y8n +M+Y+a9J0lUnHytMSFZE85h88OseRS2QwqjozUo2j1DowmhSSUv9Na5Ae22ycciBk +EZSq8a4rSlwqthaELNpeoTLUk6iVoUkK/iLvaMvrkdj9yJY1O/gvlfN2aiNTST/2 +bd+PA4RBToG9rXn6vNkUWdbLibU= +-----END CERTIFICATE-----`) + +// 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 +var globalVirtualTPMCA03 = mustParseX509(`-----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-----`) // Validator for Azure confidential VM attestation using TDX. type Validator struct { @@ -173,38 +279,53 @@ func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2 v.log.Info(fmt.Sprintf("AK Certificate Subject: %s", akCert.Subject.String())) v.log.Info(fmt.Sprintf("AK Certificate Issuer: %s", akCert.Issuer.String())) - // Setup certificate pools for verification + // Setup certificate pools roots := x509.NewCertPool() - roots.AddCert(ameRoot) - v.log.Info(fmt.Sprintf("AME Root Certificate Subject: %s", ameRoot.Subject.String())) - intermediates := x509.NewCertPool() - // Parse the CA certificate if provided - // For TDX, the AK certificate may be issued directly by the root CA + // Add all known root CAs + // Microsoft RSA Devices Root CA 2021 (for older VMs) + roots.AddCert(microsoftRSADevicesRoot2021) + // Azure Virtual TPM Root Certificate Authority 2023 (for TDX and newer Trusted Launch) + roots.AddCert(azureVirtualTPMRoot2023) + + v.log.Info("Added root CAs: Microsoft RSA Devices Root CA 2021, Azure Virtual TPM Root CA 2023") + + // Add all known intermediate CAs + // Global Virtual TPM CA - 03 (for TDX VMs) + intermediates.AddCert(globalVirtualTPMCA03) + v.log.Info("Added intermediate CA: Global Virtual TPM CA - 03") + + // Also try to add any CA certificate provided by the issuer if len(instanceInfo.CA) > 0 { akCertCA, err := x509.ParseCertificate(instanceInfo.CA) if err != nil { - v.log.Warn(fmt.Sprintf("Failed to parse CA certificate: %v - will try direct root verification", err)) + v.log.Warn(fmt.Sprintf("Failed to parse provided CA certificate: %v", err)) } else { - v.log.Info(fmt.Sprintf("CA Certificate Subject: %s", akCertCA.Subject.String())) - v.log.Info(fmt.Sprintf("CA Certificate Issuer: %s", akCertCA.Issuer.String())) + v.log.Info(fmt.Sprintf("Added provided CA certificate: %s", akCertCA.Subject.String())) intermediates.AddCert(akCertCA) } - } else { - v.log.Info("No CA certificate provided - verifying directly against root CA") } // Verify the certificate chain - v.log.Info("Verifying certificate chain against AME root CA") - if _, err := akCert.Verify(x509.VerifyOptions{ + v.log.Info("Verifying certificate chain") + chains, err := akCert.Verify(x509.VerifyOptions{ Roots: roots, Intermediates: intermediates, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, - }); err != nil { + }) + if err != nil { v.log.Warn(fmt.Sprintf("Certificate chain verification failed: %v", err)) return fmt.Errorf("verifying attestation key certificate chain: %w", err) } + + // Log the verified chain + for i, chain := range chains { + v.log.Info(fmt.Sprintf("Verified chain %d:", i)) + for j, cert := range chain { + v.log.Info(fmt.Sprintf(" [%d] %s", j, cert.Subject.String())) + } + } v.log.Info("Certificate chain verification successful") // Verify that the public key in the certificate matches the TPM's AK public key From d71c7f7390cc66abf497772c46f2084b0b16e931 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Tue, 18 Nov 2025 14:54:12 +0000 Subject: [PATCH 03/10] remove unnecessary downloading intermediate CA certs --- internal/attestation/azure/tdx/issuer.go | 62 +-------------------- internal/attestation/azure/tdx/tdx.go | 1 - internal/attestation/azure/tdx/validator.go | 35 ++++-------- 3 files changed, 15 insertions(+), 83 deletions(-) diff --git a/internal/attestation/azure/tdx/issuer.go b/internal/attestation/azure/tdx/issuer.go index b2f6a6f..0609a8f 100644 --- a/internal/attestation/azure/tdx/issuer.go +++ b/internal/attestation/azure/tdx/issuer.go @@ -16,7 +16,6 @@ import ( "fmt" "io" "net/http" - "time" "github.com/flashbots/cvm-reverse-proxy/internal/attestation" "github.com/flashbots/cvm-reverse-proxy/internal/attestation/azure" @@ -103,7 +102,7 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ return nil, fmt.Errorf("reading attestation key certificate from TPM: %w", err) } - i.log.Info(fmt.Sprintf("Read %d bytes from TPM AK cert index", len(certDERRaw))) + i.log.Debug(fmt.Sprintf("Read %d bytes from TPM AK cert index", len(certDERRaw))) // The TPM NV index contains trailing data. We need to extract just the certificate. // X.509 DER certificates start with 0x30 (SEQUENCE) followed by length encoding @@ -127,7 +126,7 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ if certLen > 0 && certLen <= len(certDERRaw) { cleanCertDER = certDERRaw[:certLen] - i.log.Info(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", certLen, len(certDERRaw))) + i.log.Debug(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", certLen, len(certDERRaw))) } else { return nil, fmt.Errorf("invalid certificate length: %d (total data: %d)", certLen, len(certDERRaw)) } @@ -136,33 +135,15 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ } // Verify we can parse the extracted certificate - cert, err := x509.ParseCertificate(cleanCertDER) + _, err = x509.ParseCertificate(cleanCertDER) if err != nil { return nil, fmt.Errorf("parsing extracted attestation key certificate: %w", err) } - // Fetch the CA certificate if the AK cert has IssuingCertificateURL extension - var caCertDER []byte - if len(cert.IssuingCertificateURL) > 0 { - i.log.Info(fmt.Sprintf("Downloading CA certificate from: %s", cert.IssuingCertificateURL[0])) - caCert, err := downloadCACertificate(ctx, cert.IssuingCertificateURL) - if err != nil { - i.log.Warn(fmt.Sprintf("Failed to download CA certificate: %v", err)) - // Don't fail here - validator can still verify directly against root - } else { - // Use the parsed certificate's Raw field to ensure clean DER encoding - caCertDER = caCert.Raw - i.log.Info(fmt.Sprintf("Successfully downloaded CA certificate: %s", caCert.Subject.String())) - } - } else { - i.log.Info("No IssuingCertificateURL in AK certificate - will verify directly against root CA") - } - instanceInfo := InstanceInfo{ AttestationReport: quote, RuntimeData: runtimeData, AkCert: cleanCertDER, // Use the clean certificate - CA: caCertDER, } instanceInfoJSON, err := json.Marshal(instanceInfo) if err != nil { @@ -171,43 +152,6 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ return instanceInfoJSON, nil } -// Helper function to download CA certificate from URLs -func downloadCACertificate(ctx context.Context, urls []string) (*x509.Certificate, error) { - client := &http.Client{Timeout: 10 * time.Second} - - for _, url := range urls { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - continue - } - - resp, err := client.Do(req) - if err != nil { - continue - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - continue - } - - certDER, err := io.ReadAll(resp.Body) - if err != nil { - continue - } - - // Parse and validate the certificate - cert, err := x509.ParseCertificate(certDER) - if err != nil { - continue - } - - return cert, nil - } - - return nil, fmt.Errorf("failed to download CA certificate from any URL") -} - func parseHCLReport(report []byte) (hwReport, runtimeData []byte, err error) { // First, ensure the extracted report is actually for TDX if len(report) < hclReportTypeOffsetStart+4 { diff --git a/internal/attestation/azure/tdx/tdx.go b/internal/attestation/azure/tdx/tdx.go index 829c5fe..9449931 100644 --- a/internal/attestation/azure/tdx/tdx.go +++ b/internal/attestation/azure/tdx/tdx.go @@ -24,5 +24,4 @@ type InstanceInfo struct { AttestationReport []byte RuntimeData []byte AkCert []byte - CA []byte } diff --git a/internal/attestation/azure/tdx/validator.go b/internal/attestation/azure/tdx/validator.go index 8e3f8f4..57a9382 100644 --- a/internal/attestation/azure/tdx/validator.go +++ b/internal/attestation/azure/tdx/validator.go @@ -276,39 +276,28 @@ func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2 if err != nil { return fmt.Errorf("parsing attestation key certificate: %w", err) } - v.log.Info(fmt.Sprintf("AK Certificate Subject: %s", akCert.Subject.String())) - v.log.Info(fmt.Sprintf("AK Certificate Issuer: %s", akCert.Issuer.String())) + v.log.Debug(fmt.Sprintf("AK Certificate Subject: %s", akCert.Subject.String())) + v.log.Debug(fmt.Sprintf("AK Certificate Issuer: %s", akCert.Issuer.String())) // Setup certificate pools roots := x509.NewCertPool() intermediates := x509.NewCertPool() - // Add all known root CAs + // Add all known Azure's root CAs // Microsoft RSA Devices Root CA 2021 (for older VMs) roots.AddCert(microsoftRSADevicesRoot2021) // Azure Virtual TPM Root Certificate Authority 2023 (for TDX and newer Trusted Launch) roots.AddCert(azureVirtualTPMRoot2023) - v.log.Info("Added root CAs: Microsoft RSA Devices Root CA 2021, Azure Virtual TPM Root CA 2023") + v.log.Debug("Added root CAs: Microsoft RSA Devices Root CA 2021, Azure Virtual TPM Root CA 2023") - // Add all known intermediate CAs + // Add known Azure's intermediate CAs // Global Virtual TPM CA - 03 (for TDX VMs) intermediates.AddCert(globalVirtualTPMCA03) - v.log.Info("Added intermediate CA: Global Virtual TPM CA - 03") - - // Also try to add any CA certificate provided by the issuer - if len(instanceInfo.CA) > 0 { - akCertCA, err := x509.ParseCertificate(instanceInfo.CA) - if err != nil { - v.log.Warn(fmt.Sprintf("Failed to parse provided CA certificate: %v", err)) - } else { - v.log.Info(fmt.Sprintf("Added provided CA certificate: %s", akCertCA.Subject.String())) - intermediates.AddCert(akCertCA) - } - } + v.log.Debug("Added intermediate CA: Global Virtual TPM CA - 03") // Verify the certificate chain - v.log.Info("Verifying certificate chain") + v.log.Debug("Verifying certificate chain") chains, err := akCert.Verify(x509.VerifyOptions{ Roots: roots, Intermediates: intermediates, @@ -321,12 +310,12 @@ func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2 // Log the verified chain for i, chain := range chains { - v.log.Info(fmt.Sprintf("Verified chain %d:", i)) + v.log.Debug(fmt.Sprintf("Verified chain %d:", i)) for j, cert := range chain { - v.log.Info(fmt.Sprintf(" [%d] %s", j, cert.Subject.String())) + v.log.Debug(fmt.Sprintf(" [%d] %s", j, cert.Subject.String())) } } - v.log.Info("Certificate chain verification successful") + v.log.Debug("Certificate chain verification successful") // Verify that the public key in the certificate matches the TPM's AK public key pubKey, err := pubArea.Key() @@ -343,8 +332,8 @@ func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2 v.log.Warn("Certificate public key does not match TPM attestation key") return errors.New("certificate public key does not match attestation key") } - v.log.Info("Certificate public key matches TPM attestation key") - v.log.Info("vTPM AK certificate verification completed successfully") + v.log.Debug("Certificate public key matches TPM attestation key") + v.log.Debug("vTPM AK certificate verification completed successfully") return nil } From af84a824a9598c853d0df152c05093f88c8d17e9 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Tue, 18 Nov 2025 16:13:37 +0000 Subject: [PATCH 04/10] refactor and code refinement --- internal/attestation/azure/tdx/issuer.go | 87 +++++++++++++++++------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/internal/attestation/azure/tdx/issuer.go b/internal/attestation/azure/tdx/issuer.go index 0609a8f..ffa485c 100644 --- a/internal/attestation/azure/tdx/issuer.go +++ b/internal/attestation/azure/tdx/issuer.go @@ -106,33 +106,11 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ // The TPM NV index contains trailing data. We need to extract just the certificate. // X.509 DER certificates start with 0x30 (SEQUENCE) followed by length encoding - var cleanCertDER []byte - if len(certDERRaw) > 4 && certDERRaw[0] == 0x30 { - // Parse the DER length to extract exactly the certificate bytes - var certLen int - if certDERRaw[1] < 0x80 { - // Short form: length is in the second byte - certLen = int(certDERRaw[1]) + 2 - } else if certDERRaw[1] == 0x82 { - // Long form with 2 length bytes - certLen = (int(certDERRaw[2]) << 8) | int(certDERRaw[3]) - certLen += 4 // Add header bytes - } else if certDERRaw[1] == 0x81 { - // Long form with 1 length byte - certLen = int(certDERRaw[2]) + 3 - } else { - return nil, fmt.Errorf("unsupported DER length encoding: 0x%02x", certDERRaw[1]) - } - - if certLen > 0 && certLen <= len(certDERRaw) { - cleanCertDER = certDERRaw[:certLen] - i.log.Debug(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", certLen, len(certDERRaw))) - } else { - return nil, fmt.Errorf("invalid certificate length: %d (total data: %d)", certLen, len(certDERRaw)) - } - } else { - return nil, fmt.Errorf("invalid certificate format: does not start with DER SEQUENCE tag") + cleanCertDER, err := extractDERCertificate(certDERRaw) + if err != nil { + return nil, fmt.Errorf("extracting certificate from TPM data: %w", err) } + i.log.Debug(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", len(cleanCertDER), len(certDERRaw))) // Verify we can parse the extracted certificate _, err = x509.ParseCertificate(cleanCertDER) @@ -152,6 +130,63 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ return instanceInfoJSON, nil } +// extractDERCertificate extracts a clean X.509 DER certificate from raw TPM data. +// The TPM NV index may contain trailing data, so this function parses the DER +// structure to extract exactly the certificate bytes. +// +// X.509 DER certificates use ASN.1 encoding and start with: +// - Tag: 0x30 (SEQUENCE) +// - Length: encoded in one of three forms (short, long-1byte, long-2byte) +// - Content: the certificate data +func extractDERCertificate(certDERRaw []byte) ([]byte, error) { + if len(certDERRaw) < 4 { + return nil, fmt.Errorf("certificate data too short: %d bytes", len(certDERRaw)) + } + + // Verify it starts with DER SEQUENCE tag (0x30) + if certDERRaw[0] != 0x30 { + return nil, fmt.Errorf("invalid certificate format: does not start with DER SEQUENCE tag (0x30), got 0x%02x", certDERRaw[0]) + } + + // Parse the DER length encoding to determine certificate size + var certLen int + lengthByte := certDERRaw[1] + + if lengthByte < 0x80 { + // Short form: length fits in 7 bits (0-127 bytes) + // Format: 0x30 + certLen = int(lengthByte) + 2 // +2 for tag and length bytes + } else if lengthByte == 0x81 { + // Long form with 1 length byte (128-255 bytes) + // Format: 0x30 0x81 + if len(certDERRaw) < 3 { + return nil, fmt.Errorf("truncated DER encoding: expected length byte") + } + certLen = int(certDERRaw[2]) + 3 // +3 for tag, 0x81, and length byte + } else if lengthByte == 0x82 { + // Long form with 2 length bytes (256-65535 bytes) + // Format: 0x30 0x82 + if len(certDERRaw) < 4 { + return nil, fmt.Errorf("truncated DER encoding: expected 2 length bytes") + } + certLen = (int(certDERRaw[2]) << 8) | int(certDERRaw[3]) + certLen += 4 // +4 for tag, 0x82, and two length bytes + } else { + return nil, fmt.Errorf("unsupported DER length encoding: 0x%02x", lengthByte) + } + + // Validate the calculated length + if certLen <= 0 { + return nil, fmt.Errorf("invalid certificate length: %d", certLen) + } + if certLen > len(certDERRaw) { + return nil, fmt.Errorf("invalid certificate length: %d exceeds available data (%d bytes)", certLen, len(certDERRaw)) + } + + // Extract the exact certificate bytes + return certDERRaw[:certLen], nil +} + func parseHCLReport(report []byte) (hwReport, runtimeData []byte, err error) { // First, ensure the extracted report is actually for TDX if len(report) < hclReportTypeOffsetStart+4 { From fc395a760a3a3bea0632093824b8e354c5bcf45e Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Wed, 19 Nov 2025 14:12:41 +0000 Subject: [PATCH 05/10] remove debug logs --- internal/attestation/azure/tdx/validator.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/internal/attestation/azure/tdx/validator.go b/internal/attestation/azure/tdx/validator.go index 57a9382..2af5c00 100644 --- a/internal/attestation/azure/tdx/validator.go +++ b/internal/attestation/azure/tdx/validator.go @@ -269,15 +269,12 @@ func (v *Validator) validateQuote(tdxQuote *tdx.QuoteV4) error { // verifyAKCertificate verifies the vTPM attestation key certificate chain. // This prevents attacks where an attacker could forge attestation by using their own key. func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2.Public) error { - v.log.Info("Starting vTPM AK certificate verification") - // Parse the AK certificate akCert, err := x509.ParseCertificate(instanceInfo.AkCert) if err != nil { return fmt.Errorf("parsing attestation key certificate: %w", err) } - v.log.Debug(fmt.Sprintf("AK Certificate Subject: %s", akCert.Subject.String())) - v.log.Debug(fmt.Sprintf("AK Certificate Issuer: %s", akCert.Issuer.String())) + v.log.Debug(fmt.Sprintf("AK Certificate Subject: %s, Issuer: %s", akCert.Subject.String(), akCert.Issuer.String())) // Setup certificate pools roots := x509.NewCertPool() @@ -289,15 +286,11 @@ func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2 // Azure Virtual TPM Root Certificate Authority 2023 (for TDX and newer Trusted Launch) roots.AddCert(azureVirtualTPMRoot2023) - v.log.Debug("Added root CAs: Microsoft RSA Devices Root CA 2021, Azure Virtual TPM Root CA 2023") - // Add known Azure's intermediate CAs // Global Virtual TPM CA - 03 (for TDX VMs) intermediates.AddCert(globalVirtualTPMCA03) - v.log.Debug("Added intermediate CA: Global Virtual TPM CA - 03") // Verify the certificate chain - v.log.Debug("Verifying certificate chain") chains, err := akCert.Verify(x509.VerifyOptions{ Roots: roots, Intermediates: intermediates, @@ -315,7 +308,6 @@ func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2 v.log.Debug(fmt.Sprintf(" [%d] %s", j, cert.Subject.String())) } } - v.log.Debug("Certificate chain verification successful") // Verify that the public key in the certificate matches the TPM's AK public key pubKey, err := pubArea.Key() @@ -332,8 +324,6 @@ func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2 v.log.Warn("Certificate public key does not match TPM attestation key") return errors.New("certificate public key does not match attestation key") } - v.log.Debug("Certificate public key matches TPM attestation key") - v.log.Debug("vTPM AK certificate verification completed successfully") return nil } From 5fcccbd85811cef345d34e590a4819bd1d5766b7 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Wed, 19 Nov 2025 16:09:04 +0000 Subject: [PATCH 06/10] add feature flag for the vTPM certificate verification --- cmd/attested-get/main.go | 8 ++++++++ cmd/proxy-client/main.go | 9 ++++++++- cmd/proxy-server/main.go | 9 ++++++++- internal/attestation/azure/tdx/validator.go | 12 ++++++++++-- proxy/atls_config.go | 6 ++++-- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/cmd/attested-get/main.go b/cmd/attested-get/main.go index b1ff349..4144270 100644 --- a/cmd/attested-get/main.go +++ b/cmd/attested-get/main.go @@ -90,6 +90,12 @@ var flags []cli.Flag = []cli.Flag{ Value: false, Usage: "log debug messages", }, + &cli.BoolFlag{ + Name: "verify-ak-certificate", + Value: false, + EnvVars: []string{"VERIFY_AK_CERTIFICATE"}, + Usage: "verify Azure TDX vTPM attestation key certificate chain", + }, } func main() { @@ -113,6 +119,7 @@ func runClient(cCtx *cli.Context) (err error) { attestationTypeStr := cCtx.String("attestation-type") expectedMeasurementsPath := cCtx.String("expected-measurements") overrideAzurev6Tcbinfo := cCtx.Bool("override-azurev6-tcbinfo") + verifyAKCertificate := cCtx.Bool("verify-ak-certificate") // Setup logging log := common.SetupLogger(&common.LoggingOpts{ @@ -141,6 +148,7 @@ func runClient(cCtx *cli.Context) (err error) { attConfig := config.DefaultForAzureTDX() attConfig.SetMeasurements(measurements.M{}) validator := azure_tdx.NewValidator(attConfig, proxy.AttestationLogger{Log: log}) + validator.SetVerifyAKCertificate(verifyAKCertificate) if overrideAzurev6Tcbinfo { azure_tcbinfo_override.OverrideAzureValidatorsForV6SEAMLoader(log, []atls.Validator{validator}) } diff --git a/cmd/proxy-client/main.go b/cmd/proxy-client/main.go index 30421de..aad40cb 100644 --- a/cmd/proxy-client/main.go +++ b/cmd/proxy-client/main.go @@ -75,6 +75,12 @@ var flags []cli.Flag = []cli.Flag{ EnvVars: []string{"DEV_DUMMY_DCAP"}, Usage: "URL of the remote dummy DCAP service. Only with --client-attestation-type dummy.", }, + &cli.BoolFlag{ + Name: "verify-ak-certificate", + EnvVars: []string{"VERIFY_AK_CERTIFICATE"}, + Value: false, + Usage: "verify Azure TDX vTPM attestation key certificate chain", + }, } func main() { @@ -103,6 +109,7 @@ func runClient(cCtx *cli.Context) error { devDummyDcapURL := cCtx.String("dev-dummy-dcap") verifyTLS := cCtx.Bool("verify-tls") + verifyAKCertificate := cCtx.Bool("verify-ak-certificate") log := common.SetupLogger(&common.LoggingOpts{ Debug: logDebug, @@ -145,7 +152,7 @@ func runClient(cCtx *cli.Context) error { } } - validators, err := proxy.CreateAttestationValidatorsFromFile(log, serverMeasurements) + validators, err := proxy.CreateAttestationValidatorsFromFile(log, serverMeasurements, verifyAKCertificate) if err != nil { log.Error("could not create attestation validators from file", "err", err) return err diff --git a/cmd/proxy-server/main.go b/cmd/proxy-server/main.go index d570fa5..516b0d3 100644 --- a/cmd/proxy-server/main.go +++ b/cmd/proxy-server/main.go @@ -92,6 +92,12 @@ var flags []cli.Flag = []cli.Flag{ EnvVars: []string{"DEV_DUMMY_DCAP"}, Usage: "URL of the remote dummy DCAP service. Only with --server-attestation-type dummy.", }, + &cli.BoolFlag{ + Name: "verify-ak-certificate", + EnvVars: []string{"VERIFY_AK_CERTIFICATE"}, + Value: false, + Usage: "verify Azure TDX vTPM attestation key certificate chain", + }, } var log *slog.Logger @@ -120,6 +126,7 @@ func runServer(cCtx *cli.Context) error { overrideAzurev6Tcbinfo := cCtx.Bool("override-azurev6-tcbinfo") logJSON := cCtx.Bool("log-json") logDebug := cCtx.Bool("log-debug") + verifyAKCertificate := cCtx.Bool("verify-ak-certificate") tdx.SetLogDcapQuote(cCtx.Bool("log-dcap-quote")) serverAttestationTypeFlag := cCtx.String("server-attestation-type") @@ -148,7 +155,7 @@ func runServer(cCtx *cli.Context) error { return errors.New("not all of --tls-certificate-path and --tls-private-key-path specified") } - validators, err := proxy.CreateAttestationValidatorsFromFile(log, clientMeasurements) + validators, err := proxy.CreateAttestationValidatorsFromFile(log, clientMeasurements, verifyAKCertificate) if err != nil { log.Error("could not create attestation validators from file", "err", err) return err diff --git a/internal/attestation/azure/tdx/validator.go b/internal/attestation/azure/tdx/validator.go index 2af5c00..cd58bf3 100644 --- a/internal/attestation/azure/tdx/validator.go +++ b/internal/attestation/azure/tdx/validator.go @@ -156,6 +156,8 @@ type Validator struct { tcbOverride func(pcs.TcbInfo) pcs.TcbInfo log attestation.Logger + + verifyAKCertEnabled bool } // NewValidator returns a new Validator for Azure confidential VM attestation using TDX. @@ -184,6 +186,10 @@ func (v *Validator) SetTcbOverride(overrideFn func(pcs.TcbInfo) pcs.TcbInfo) *Va return v } +func (v *Validator) SetVerifyAKCertificate(enabled bool) { + v.verifyAKCertEnabled = enabled +} + func (v *Validator) getTrustedTPMKey(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) { var instanceInfo InstanceInfo if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil { @@ -214,8 +220,10 @@ func (v *Validator) getTrustedTPMKey(_ context.Context, attDoc vtpm.AttestationD // Verify the vTPM AK certificate chain to prevent forging attestation attacks // This ensures the attestation key is actually signed by Azure's CA - if err := v.verifyAKCertificate(instanceInfo, &pubArea); err != nil { - return nil, fmt.Errorf("verifying AK certificate: %w", err) + if v.verifyAKCertEnabled { + if err := v.verifyAKCertificate(instanceInfo, &pubArea); err != nil { + return nil, fmt.Errorf("verifying AK certificate: %w", err) + } } return pubArea.Key() diff --git a/proxy/atls_config.go b/proxy/atls_config.go index 7f46c40..881abcf 100644 --- a/proxy/atls_config.go +++ b/proxy/atls_config.go @@ -84,7 +84,7 @@ func CreateAttestationIssuer(log *slog.Logger, attestationType AttestationType) } } -func CreateAttestationValidatorsFromFile(log *slog.Logger, jsonMeasurementsPath string) ([]atls.Validator, error) { +func CreateAttestationValidatorsFromFile(log *slog.Logger, jsonMeasurementsPath string, verifyAKCert bool) ([]atls.Validator, error) { if jsonMeasurementsPath == "" { return nil, nil } @@ -113,9 +113,11 @@ func CreateAttestationValidatorsFromFile(log *slog.Logger, jsonMeasurementsPath case AttestationAzureTDX: attConfig := config.DefaultForAzureTDX() attConfig.SetMeasurements(measurement.Measurements) + validator := azure_tdx.NewValidator(attConfig, AttestationLogger{Log: log}) + validator.SetVerifyAKCertificate(verifyAKCert) validatorsByType[attestationType] = append( validatorsByType[attestationType], - azure_tdx.NewValidator(attConfig, AttestationLogger{Log: log}), + validator, ) case AttestationDCAPTDX: attConfig := &config.QEMUTDX{Measurements: measurements.DefaultsFor(cloudprovider.QEMU, variant.QEMUTDX{})} From cbc192f950b1d350625b146f1221f5ba007ef660 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Wed, 19 Nov 2025 16:41:05 +0000 Subject: [PATCH 07/10] avoid failing when attempting to read the vtpm certificate data --- internal/attestation/azure/tdx/issuer.go | 41 ++++++++++++++---------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/internal/attestation/azure/tdx/issuer.go b/internal/attestation/azure/tdx/issuer.go index ffa485c..4b0dd92 100644 --- a/internal/attestation/azure/tdx/issuer.go +++ b/internal/attestation/azure/tdx/issuer.go @@ -97,25 +97,32 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ // Read the vTPM AK certificate from TPM NV index // This certificate is signed by Azure and needs to be validated on the validator side + // If reading fails, we log a warning and continue - the validator will decide if this is critical + var cleanCertDER []byte certDERRaw, err := tpm2.NVReadEx(tpm, tpmAkCertIdx, tpm2.HandleOwner, "", 0) if err != nil { - return nil, fmt.Errorf("reading attestation key certificate from TPM: %w", err) - } - - i.log.Debug(fmt.Sprintf("Read %d bytes from TPM AK cert index", len(certDERRaw))) - - // The TPM NV index contains trailing data. We need to extract just the certificate. - // X.509 DER certificates start with 0x30 (SEQUENCE) followed by length encoding - cleanCertDER, err := extractDERCertificate(certDERRaw) - if err != nil { - return nil, fmt.Errorf("extracting certificate from TPM data: %w", err) - } - i.log.Debug(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", len(cleanCertDER), len(certDERRaw))) - - // Verify we can parse the extracted certificate - _, err = x509.ParseCertificate(cleanCertDER) - if err != nil { - return nil, fmt.Errorf("parsing extracted attestation key certificate: %w", err) + i.log.Warn(fmt.Sprintf("Failed to read attestation key certificate from TPM: %v", err)) + } else { + i.log.Debug(fmt.Sprintf("Read %d bytes from TPM AK cert index", len(certDERRaw))) + + // The TPM NV index contains trailing data. We need to extract just the certificate. + // X.509 DER certificates start with 0x30 (SEQUENCE) followed by length encoding + cleanCertDER, err = extractDERCertificate(certDERRaw) + if err != nil { + i.log.Warn(fmt.Sprintf("Failed to extract certificate from TPM data: %v", err)) + cleanCertDER = nil + } else { + i.log.Debug(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", len(cleanCertDER), len(certDERRaw))) + + // Verify we can parse the extracted certificate + _, err = x509.ParseCertificate(cleanCertDER) + if err != nil { + i.log.Warn(fmt.Sprintf("Failed to parse extracted attestation key certificate: %v", err)) + cleanCertDER = nil + } else { + i.log.Debug("Successfully extracted and validated AK certificate format") + } + } } instanceInfo := InstanceInfo{ From 0fc04e34c328398794aed6a966e976312bf48e3a Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Wed, 19 Nov 2025 16:54:31 +0000 Subject: [PATCH 08/10] chore: code refactoring --- internal/attestation/azure/tdx/issuer.go | 61 +++++++++++++----------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/internal/attestation/azure/tdx/issuer.go b/internal/attestation/azure/tdx/issuer.go index 4b0dd92..dc9e282 100644 --- a/internal/attestation/azure/tdx/issuer.go +++ b/internal/attestation/azure/tdx/issuer.go @@ -95,40 +95,17 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ return nil, fmt.Errorf("getting quote: %w", err) } - // Read the vTPM AK certificate from TPM NV index - // This certificate is signed by Azure and needs to be validated on the validator side - // If reading fails, we log a warning and continue - the validator will decide if this is critical - var cleanCertDER []byte - certDERRaw, err := tpm2.NVReadEx(tpm, tpmAkCertIdx, tpm2.HandleOwner, "", 0) + // Read and extract the vTPM AK certificate. If this fails, we log a warning and continue without it + akCert, err := i.readAKCertificateFromTPM(tpm) if err != nil { - i.log.Warn(fmt.Sprintf("Failed to read attestation key certificate from TPM: %v", err)) - } else { - i.log.Debug(fmt.Sprintf("Read %d bytes from TPM AK cert index", len(certDERRaw))) - - // The TPM NV index contains trailing data. We need to extract just the certificate. - // X.509 DER certificates start with 0x30 (SEQUENCE) followed by length encoding - cleanCertDER, err = extractDERCertificate(certDERRaw) - if err != nil { - i.log.Warn(fmt.Sprintf("Failed to extract certificate from TPM data: %v", err)) - cleanCertDER = nil - } else { - i.log.Debug(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", len(cleanCertDER), len(certDERRaw))) - - // Verify we can parse the extracted certificate - _, err = x509.ParseCertificate(cleanCertDER) - if err != nil { - i.log.Warn(fmt.Sprintf("Failed to parse extracted attestation key certificate: %v", err)) - cleanCertDER = nil - } else { - i.log.Debug("Successfully extracted and validated AK certificate format") - } - } + i.log.Warn(fmt.Sprintf("Failed to read AK certificate: %v", err)) + akCert = nil } instanceInfo := InstanceInfo{ AttestationReport: quote, RuntimeData: runtimeData, - AkCert: cleanCertDER, // Use the clean certificate + AkCert: akCert, // Use the clean certificate } instanceInfoJSON, err := json.Marshal(instanceInfo) if err != nil { @@ -137,6 +114,34 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ return instanceInfoJSON, nil } +// readAKCertificateFromTPM reads and extracts the attestation key certificate from TPM. +// Returns the clean DER-encoded certificate or an error if reading/extraction fails. +func (i *Issuer) readAKCertificateFromTPM(tpm io.ReadWriteCloser) ([]byte, error) { + certDERRaw, err := tpm2.NVReadEx(tpm, tpmAkCertIdx, tpm2.HandleOwner, "", 0) + if err != nil { + return nil, fmt.Errorf("reading attestation key certificate from TPM: %w", err) + } + + i.log.Debug(fmt.Sprintf("Read %d bytes from TPM AK cert index", len(certDERRaw))) + + // The TPM NV index contains trailing data. We need to extract just the certificate. + // X.509 DER certificates start with 0x30 (SEQUENCE) followed by length encoding + cleanCertDER, err := extractDERCertificate(certDERRaw) + if err != nil { + return nil, fmt.Errorf("extracting certificate from TPM data: %w", err) + } + + i.log.Debug(fmt.Sprintf("Extracted %d bytes certificate from %d bytes TPM data", len(cleanCertDER), len(certDERRaw))) + + // Verify we can parse the extracted certificate + _, err = x509.ParseCertificate(cleanCertDER) + if err != nil { + return nil, fmt.Errorf("parsing extracted attestation key certificate: %w", err) + } + + return cleanCertDER, nil +} + // extractDERCertificate extracts a clean X.509 DER certificate from raw TPM data. // The TPM NV index may contain trailing data, so this function parses the DER // structure to extract exactly the certificate bytes. From 4b7b558f023e26edc84ec370900633450ae7f646 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Wed, 19 Nov 2025 17:34:59 +0000 Subject: [PATCH 09/10] make new AkCert field optional for backward compatibility --- internal/attestation/azure/tdx/tdx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/attestation/azure/tdx/tdx.go b/internal/attestation/azure/tdx/tdx.go index 9449931..60c8fe8 100644 --- a/internal/attestation/azure/tdx/tdx.go +++ b/internal/attestation/azure/tdx/tdx.go @@ -23,5 +23,5 @@ package tdx type InstanceInfo struct { AttestationReport []byte RuntimeData []byte - AkCert []byte + AkCert []byte `json:"akCert,omitempty"` } From f370479fe713889edd8e99cb2c9e5f24f6544eb3 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Wed, 19 Nov 2025 17:47:44 +0000 Subject: [PATCH 10/10] add edge case handling --- internal/attestation/azure/tdx/validator.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/attestation/azure/tdx/validator.go b/internal/attestation/azure/tdx/validator.go index cd58bf3..e216720 100644 --- a/internal/attestation/azure/tdx/validator.go +++ b/internal/attestation/azure/tdx/validator.go @@ -277,6 +277,11 @@ func (v *Validator) validateQuote(tdxQuote *tdx.QuoteV4) error { // verifyAKCertificate verifies the vTPM attestation key certificate chain. // This prevents attacks where an attacker could forge attestation by using their own key. func (v *Validator) verifyAKCertificate(instanceInfo InstanceInfo, pubArea *tpm2.Public) error { + // Ensure that the AK certificate is provided + if len(instanceInfo.AkCert) == 0 { + return errors.New("no AK certificate provided in instance info") + } + // Parse the AK certificate akCert, err := x509.ParseCertificate(instanceInfo.AkCert) if err != nil {