/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ package tdx import ( "context" "crypto" "crypto/x509" "encoding/json" "fmt" "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/azure" "github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/google/go-tdx-guest/abi" "github.com/google/go-tdx-guest/proto/tdx" "github.com/google/go-tdx-guest/validate" "github.com/google/go-tdx-guest/verify" "github.com/google/go-tdx-guest/verify/trust" "github.com/google/go-tpm-tools/proto/attest" "github.com/google/go-tpm/legacy/tpm2" ) // Validator for Azure confidential VM attestation using TDX. type Validator struct { variant.AzureTDX *vtpm.Validator cfg *config.AzureTDX getter trust.HTTPSGetter hclValidator hclAkValidator } // NewValidator returns a new Validator for Azure confidential VM attestation using TDX. func NewValidator(cfg *config.AzureTDX, log attestation.Logger) *Validator { v := &Validator{ cfg: cfg, getter: trust.DefaultHTTPSGetter(), hclValidator: &azure.HCLAkValidator{}, } v.Validator = vtpm.NewValidator( cfg.Measurements, v.getTrustedTPMKey, func(vtpm.AttestationDocument, *attest.MachineState) error { return nil }, log, ) return v } 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 { return nil, err } quotePb, err := abi.QuoteToProto(instanceInfo.AttestationReport) if err != nil { return nil, err } quote, ok := quotePb.(*tdx.QuoteV4) if !ok { return nil, fmt.Errorf("unexpected quote type: %T", quote) } if err := v.validateQuote(quote); err != nil { return nil, err } // Decode the public area of the attestation key and validate its trustworthiness. pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub) if err != nil { return nil, err } if err = v.hclValidator.Validate(instanceInfo.RuntimeData, quote.TdQuoteBody.ReportData, pubArea.RSAParameters); err != nil { return nil, fmt.Errorf("validating HCLAkPub: %w", err) } return pubArea.Key() } func (v *Validator) validateQuote(tdxQuote *tdx.QuoteV4) error { roots := x509.NewCertPool() roots.AddCert((*x509.Certificate)(&v.cfg.IntelRootKey)) if err := verify.TdxQuote(tdxQuote, &verify.Options{ // TODO: Re-enable CRL checking once issues on Azure's side are resolved. // CheckRevocations: true, // GetCollateral: true, TrustedRoots: roots, Getter: v.getter, }); err != nil { return err } if err := validate.TdxQuote(tdxQuote, &validate.Options{ HeaderOptions: validate.HeaderOptions{ MinimumQeSvn: v.cfg.QESVN, MinimumPceSvn: v.cfg.PCESVN, QeVendorID: v.cfg.QEVendorID, }, TdQuoteBodyOptions: validate.TdQuoteBodyOptions{ MinimumTeeTcbSvn: v.cfg.TEETCBSVN, MrSeam: v.cfg.MRSeam, Xfam: v.cfg.XFAM, }, }); err != nil { return err } return nil } type hclAkValidator interface { Validate(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error }