diff --git a/tdx/validator.go b/tdx/validator.go index 993ab2c..194cbb4 100644 --- a/tdx/validator.go +++ b/tdx/validator.go @@ -8,8 +8,8 @@ package tdx import ( "context" - "encoding/json" "encoding/hex" + "encoding/json" "fmt" "github.com/flashbots/cvm-reverse-proxy/internal/attestation" @@ -18,10 +18,10 @@ import ( "github.com/flashbots/cvm-reverse-proxy/internal/config" "github.com/google/go-tdx-guest/abi" - "github.com/google/go-tdx-guest/validate" - "github.com/google/go-tdx-guest/verify" ccpb "github.com/google/go-tdx-guest/proto/checkconfig" pb "github.com/google/go-tdx-guest/proto/tdx" + "github.com/google/go-tdx-guest/validate" + "github.com/google/go-tdx-guest/verify" ) // Validator is the TDX attestation validator. @@ -46,11 +46,11 @@ func NewValidator(cfg *config.QEMUTDX, log attestation.Logger) *Validator { } func hexToBytes(hexString string) []byte { - bytes, err := hex.DecodeString(hexString) - if err != nil { - panic(err) - } - return bytes + bytes, err := hex.DecodeString(hexString) + if err != nil { + panic(err) + } + return bytes } // Validate validates the given attestation document using TDX attestation. @@ -83,47 +83,43 @@ func (v *Validator) Validate(ctx context.Context, attDocRaw []byte, nonce []byte // Assign the values of the flags to the corresponding proto fields config := &ccpb.Config{ RootOfTrust: &ccpb.RootOfTrust{ - CheckCrl: true, + CheckCrl: true, GetCollateral: true, }, Policy: &ccpb.Policy{ HeaderPolicy: &ccpb.HeaderPolicy{ - MinimumQeSvn: 0, + MinimumQeSvn: 0, MinimumPceSvn: 0, - QeVendorId: hexToBytes("939a7233f79c4ca9940a0db3957f0607"), + QeVendorId: hexToBytes("939a7233f79c4ca9940a0db3957f0607"), }, TdQuoteBodyPolicy: &ccpb.TDQuoteBodyPolicy{ - TdAttributes: hexToBytes("0000001000000000"), - MrConfigId: hexToBytes("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - MrOwner: hexToBytes("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - MrOwnerConfig: hexToBytes("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - ReportData: extraData, + TdAttributes: hexToBytes("0000001000000000"), // no debug, LASS enabled + ReportData: extraData, }, }, } - // Only add measurement validation if we have valid measurements - if v.expected != nil { - config.Policy.TdQuoteBodyPolicy.MrTd = v.expected[0].Expected - config.Policy.TdQuoteBodyPolicy.Rtmrs = [][]byte{ - v.expected[1].Expected, - v.expected[2].Expected, - v.expected[3].Expected, - v.expected[4].Expected, - } - } + // Only add measurement validation if we have valid measurements + if v.expected != nil { + config.Policy.TdQuoteBodyPolicy.MrTd = v.expected[0].Expected + config.Policy.TdQuoteBodyPolicy.Rtmrs = [][]byte{ + v.expected[1].Expected, + v.expected[2].Expected, + v.expected[3].Expected, + v.expected[4].Expected, + } + } // config.Policy.TdQuoteBodyPolicy.MinimumTeeTcbSvn="" // skipping MinimumTeeTcbSvn as this is part of tcbinfo + // considering skipping MRSEAM, the tdx module can only be provided by intel and there's already trust here. Also the TDX module svn is part of the tcbinfo check - // yet still we might want to add a check for it at some point - // config.Policy.TdQuoteBodyPolicy.MrSeam="bf70f5c1c2c1610bf2ddad348a88ebf6a550256e949e52122c743dc97cde50ccafad2fc5927d150f307fba3b8ca21872" + config.Policy.TdQuoteBodyPolicy.MrSeam = hexToBytes("bf70f5c1c2c1610bf2ddad348a88ebf6a550256e949e52122c743dc97cde50ccafad2fc5927d150f307fba3b8ca21872") quote, err := convertRawQuote(attDoc.RawQuote) - if err != nil { - return nil, fmt.Errorf("could not get quote from raw doc: %v", err) - } + if err != nil { + return nil, fmt.Errorf("could not get quote from raw doc: %v", err) + } - // TCP Level options, err := verify.RootOfTrustToOptions(config.RootOfTrust) if err != nil { return nil, fmt.Errorf("converting root of trust to options: %w", err) @@ -143,18 +139,27 @@ func (v *Validator) Validate(ctx context.Context, attDocRaw []byte, nonce []byte return nil, fmt.Errorf("error validating the TDX Quote: %v", err) } + // Lastly, verify xFAM does not have any disallowed bits set. Return error if it does. + allowedXFAM := hexToBytes("e700060000000000") // fp, sse, avx, avx512, and the 6 is unspecified (reserved) but present on GCP + for i := range quote.TdQuoteBody.Xfam { + nonAllowedBits := allowedXFAM[i] & (0xff ^ quote.TdQuoteBody.Xfam[i]) + if nonAllowedBits != byte(0) { + return nil, fmt.Errorf("error validating the TDX Quote: disallowed xFAM attribute in byte %d: %x", i, nonAllowedBits) + } + } + return attDoc.UserData, nil } func convertRawQuote(rawQuote []byte) (*pb.QuoteV4, error) { anyQuote, err := abi.QuoteToProto(rawQuote) - if err != nil { - return nil, fmt.Errorf("could not convert raw bytes to QuoteV4: %v", err) - } - quote, ok := anyQuote.(*pb.QuoteV4) - if !ok { - return nil, fmt.Errorf("Quote is not a QuoteV4") - } + if err != nil { + return nil, fmt.Errorf("could not convert raw bytes to QuoteV4: %v", err) + } + quote, ok := anyQuote.(*pb.QuoteV4) + if !ok { + return nil, fmt.Errorf("Quote is not a QuoteV4") + } return quote, nil } @@ -177,9 +182,9 @@ func ParseDcapTDXAttestationMeasurementsRaw(attDocRaw []byte) (map[uint32][]byte } quote, err := convertRawQuote(attDoc.RawQuote) - if err != nil { - return nil, fmt.Errorf("could not get quote from raw doc: %v", err) - } + if err != nil { + return nil, fmt.Errorf("could not get quote from raw doc: %v", err) + } return parseDcapTDXAttestationMeasurements(quote) }