Skip to content
Draft
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
89 changes: 47 additions & 42 deletions tdx/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ package tdx

import (
"context"
"encoding/json"
"encoding/hex"
"encoding/json"
"fmt"

"github.com/flashbots/cvm-reverse-proxy/internal/attestation"
Expand All @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -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
}
Expand All @@ -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)
}