Skip to content
Open
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions proto/checkconfig.proto
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ message RootOfTrust {
// If true, then check is not permitted to download necessary files for
// verification.
bool get_collateral = 4;

// The set of acceptable TCB status values. If empty, only "UpToDate" is
// accepted. Possible values: "UpToDate", "SWHardeningNeeded",
// "ConfigurationNeeded", "ConfigurationAndSWHardeningNeeded", "OutOfDate",
// "OutOfDateConfigurationNeeded".
repeated string accepted_tcb_statuses = 5;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's worth making this field an enum than string? It would help to avoid configuring by mistake

}

// Config is the overall message input for the check tool. This provides all
Expand Down
55 changes: 35 additions & 20 deletions proto/checkconfig/checkconfig.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions tools/check/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ var (
maxRetryDelay = flag.Duration("max_retry_delay", defaultMaxRetryDelay, "Maximum Duration to wait between HTTP request retries.")
testLocalGetter = flag.Bool("test_local_getter", false, "Use this flag only to test this CLI tool when network access is not available")

acceptedTcbStatuses = flag.String("accepted_tcb_statuses", "", "Comma-separated list of acceptable TCB statuses (e.g. UpToDate,SWHardeningNeeded,ConfigurationNeeded). If empty, only UpToDate is accepted.")

// Assign the values of the flags to the corresponding proto fields
config = &ccpb.Config{
RootOfTrust: &ccpb.RootOfTrust{},
Expand Down Expand Up @@ -333,6 +335,13 @@ func populateRootOfTrust() error {
if len(paths) > 0 {
rot.CabundlePaths = paths
}
if *acceptedTcbStatuses != "" {
statuses := strings.Split(*acceptedTcbStatuses, ",")
for i := range statuses {
statuses[i] = strings.TrimSpace(statuses[i])
}
rot.AcceptedTcbStatuses = statuses
}
return nil
}

Expand Down
64 changes: 40 additions & 24 deletions verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ type Options struct {
// TrustedRoots specifies the root CertPool to trust when verifying PCK certificate chain.
// If nil, embedded certificate will be used
TrustedRoots *x509.CertPool
// AcceptedTCBStatuses is the set of TCB status values that the caller considers acceptable.
// If nil or empty, only "UpToDate" is accepted.
AcceptedTCBStatuses []pcs.TcbComponentStatus

chain *PCKCertificateChain
collateral *Collateral
Expand Down Expand Up @@ -224,12 +227,14 @@ func DefaultOptions() *Options {
}

type tdQuoteBodyOptions struct {
tcbInfo pcs.TcbInfo
pckCertExtensions *pcs.PckExtensions
tcbInfo pcs.TcbInfo
pckCertExtensions *pcs.PckExtensions
acceptedTCBStatuses []pcs.TcbComponentStatus
}

type qeReportOptions struct {
qeIdentity *pcs.EnclaveIdentity
qeIdentity *pcs.EnclaveIdentity
acceptedTCBStatuses []pcs.TcbComponentStatus
}

// PCKCertificateChain contains certificate chains
Expand Down Expand Up @@ -997,18 +1002,26 @@ func readQeTcbStatus(tcbLevels []pcs.TcbLevel, isvsvn uint32) (pcs.TcbLevel, err
return pcs.TcbLevel{}, fmt.Errorf("no matching QE TCB level found")
}

func checkQeTcbStatus(tcbLevels []pcs.TcbLevel, isvsvn uint32) error {
func isTcbStatusAccepted(status pcs.TcbComponentStatus, accepted []pcs.TcbComponentStatus) bool {
if len(accepted) == 0 {
return status == pcs.TcbComponentStatusUpToDate
}
for _, s := range accepted {
if s == status {
return true
}
}
return false
}

func checkQeTcbStatus(tcbLevels []pcs.TcbLevel, isvsvn uint32, accepted []pcs.TcbComponentStatus) error {
found, err := readQeTcbStatus(tcbLevels, isvsvn)
if err != nil {
return err
}

if found.TcbStatus == pcs.TcbComponentStatusOutOfDate {
return ErrEnclaveTcbStatus
}

if found.TcbStatus != pcs.TcbComponentStatusUpToDate {
return fmt.Errorf("QE TCB Status is not %q, found %q", pcs.TcbComponentStatusUpToDate, found.TcbStatus)
if !isTcbStatusAccepted(found.TcbStatus, accepted) {
return fmt.Errorf("QE TCB Status %q is not acceptable", found.TcbStatus)
}

return nil
Expand Down Expand Up @@ -1052,18 +1065,14 @@ func readTcbInfoTcbStatus(tcbInfo pcs.TcbInfo, tdQuoteBody any, pckCertExtension
return matchingTcbLevel, nil
}

func checkTcbInfoTcbStatus(tcbInfo pcs.TcbInfo, tdQuoteBody any, pckCertExtensions *pcs.PckExtensions) error {
func checkTcbInfoTcbStatus(tcbInfo pcs.TcbInfo, tdQuoteBody any, pckCertExtensions *pcs.PckExtensions, accepted []pcs.TcbComponentStatus) error {
found, err := readTcbInfoTcbStatus(tcbInfo, tdQuoteBody, pckCertExtensions)
if err != nil {
return err
}

if found.TcbStatus == pcs.TcbComponentStatusOutOfDate {
return ErrTdxTcbStatus
}

if found.TcbStatus != pcs.TcbComponentStatusUpToDate {
return fmt.Errorf("TDX TCB Status is not %q, found %q", pcs.TcbComponentStatusUpToDate, found.TcbStatus)
if !isTcbStatusAccepted(found.TcbStatus, accepted) {
return fmt.Errorf("TDX TCB Status %q is not acceptable", found.TcbStatus)
}

return nil
Expand Down Expand Up @@ -1111,7 +1120,7 @@ func verifyTdQuoteBody(tdQuoteBody any, tdQuoteBodyOptions *tdQuoteBodyOptions)
return fmt.Errorf("AttributesMask value(%q) is not equal to TdxModule.Attributes field in Intel PCS's reported TDX TCB info(%q)", hex.EncodeToString(attributesMask), hex.EncodeToString(tdQuoteBodyOptions.tcbInfo.TdxModule.Attributes.Bytes))
}

if err := checkTcbInfoTcbStatus(tdQuoteBodyOptions.tcbInfo, tdQuoteBody, tdQuoteBodyOptions.pckCertExtensions); err != nil {
if err := checkTcbInfoTcbStatus(tdQuoteBodyOptions.tcbInfo, tdQuoteBody, tdQuoteBodyOptions.pckCertExtensions, tdQuoteBodyOptions.acceptedTCBStatuses); err != nil {
return fmt.Errorf("TDX TCB info reported by Intel PCS failed TCB status check: %v", err)
}
return nil
Expand Down Expand Up @@ -1154,7 +1163,7 @@ func verifyQeReport(qeReport *pb.EnclaveReport, qeReportOptions *qeReportOptions
return fmt.Errorf("ISV PRODID value(%v) in QE Report is not equal to ISV PRODID value(%v) in Intel PCS's reported QE Identity", qeReport.GetIsvProdId(), qeReportOptions.qeIdentity.IsvProdID)
}

if err := checkQeTcbStatus(qeReportOptions.qeIdentity.TcbLevels, qeReport.GetIsvSvn()); err != nil {
if err := checkQeTcbStatus(qeReportOptions.qeIdentity.TcbLevels, qeReport.GetIsvSvn(), qeReportOptions.acceptedTCBStatuses); err != nil {
return fmt.Errorf("QE Identity reported by Intel PCS failed TCB status check: %v", err)
}
return nil
Expand Down Expand Up @@ -1242,7 +1251,7 @@ func verifyQuote(quote any, options *Options) error {
if collateral != nil {
logger.V(1).Info("Verifying TD Quote Body using TCB Info API response")
var err error
quoteBodyOptions := tdQuoteBodyOptions{tcbInfo: collateral.TdxTcbInfo.TcbInfo, pckCertExtensions: pckCertExtensions}
quoteBodyOptions := tdQuoteBodyOptions{tcbInfo: collateral.TdxTcbInfo.TcbInfo, pckCertExtensions: pckCertExtensions, acceptedTCBStatuses: options.AcceptedTCBStatuses}
switch q := quote.(type) {
case *pb.QuoteV4:
err = verifyTdQuoteBody(q.GetTdQuoteBody(), &quoteBodyOptions)
Expand All @@ -1258,7 +1267,8 @@ func verifyQuote(quote any, options *Options) error {
logger.V(1).Info("Verifying QE Report using QE Identity API response")
if err := verifyQeReport(qeReportCertificationData.GetQeReport(),
&qeReportOptions{
qeIdentity: &collateral.QeIdentity.EnclaveIdentity,
qeIdentity: &collateral.QeIdentity.EnclaveIdentity,
acceptedTCBStatuses: options.AcceptedTCBStatuses,
}); err != nil {
return err
}
Expand Down Expand Up @@ -1605,10 +1615,16 @@ func RootOfTrustToOptions(rot *ccpb.RootOfTrust) (*Options, error) {
return nil, err
}

var accepted []pcs.TcbComponentStatus
for _, s := range rot.AcceptedTcbStatuses {
accepted = append(accepted, pcs.TcbComponentStatus(s))
}

return &Options{
CheckRevocations: rot.CheckCrl,
GetCollateral: rot.GetCollateral,
TrustedRoots: trustedRoots,
CheckRevocations: rot.CheckCrl,
GetCollateral: rot.GetCollateral,
TrustedRoots: trustedRoots,
AcceptedTCBStatuses: accepted,
}, nil
}

Expand Down
19 changes: 10 additions & 9 deletions verify/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"context"
"crypto/x509"
"encoding/hex"
"errors"
"os"
"reflect"
"strings"
Expand Down Expand Up @@ -824,25 +823,26 @@ func TestNegativeTcbInfoTcbStatusV4(t *testing.T) {

setTcbSvnValues(10, 0, &tcbInfo.TcbLevels[0].Tcb.TdxTcbcomponents, &tcbInfo.TcbLevels[0].Tcb.SgxTcbcomponents)
wantErr := "no matching TCB level found"
if err := checkTcbInfoTcbStatus(tcbInfo, quote.GetTdQuoteBody(), ext); err == nil || err.Error() != wantErr {
if err := checkTcbInfoTcbStatus(tcbInfo, quote.GetTdQuoteBody(), ext, nil); err == nil || err.Error() != wantErr {
t.Errorf("SgxTcbComponents values greater: checkTcbInfoTcbStatus() = %v. Want error %v", err, wantErr)
}

setTcbSvnValues(0, 10, &tcbInfo.TcbLevels[0].Tcb.TdxTcbcomponents, &tcbInfo.TcbLevels[0].Tcb.SgxTcbcomponents)
if err := checkTcbInfoTcbStatus(tcbInfo, quote.GetTdQuoteBody(), ext); err == nil || err.Error() != wantErr {
if err := checkTcbInfoTcbStatus(tcbInfo, quote.GetTdQuoteBody(), ext, nil); err == nil || err.Error() != wantErr {
t.Errorf("TdxTcbComponents values greater: checkTcbInfoTcbStatus() = %v. Want error %v", err, wantErr)
}

tcbInfo.TcbLevels[0].Tcb.Pcesvn = 20
setTcbSvnValues(0, 0, &tcbInfo.TcbLevels[0].Tcb.TdxTcbcomponents, &tcbInfo.TcbLevels[0].Tcb.SgxTcbcomponents)
if err := checkTcbInfoTcbStatus(tcbInfo, quote.GetTdQuoteBody(), ext); err == nil || err.Error() != wantErr {
if err := checkTcbInfoTcbStatus(tcbInfo, quote.GetTdQuoteBody(), ext, nil); err == nil || err.Error() != wantErr {
t.Errorf("PCESvn value greater: checkTcbInfoTcbStatus() = %v. Want error %v", err, wantErr)
}

tcbInfo.TcbLevels[0].Tcb.Pcesvn = 0
tcbInfo.TcbLevels[0].TcbStatus = "OutOfDate"
if gotErr, wantErr := checkTcbInfoTcbStatus(tcbInfo, quote.GetTdQuoteBody(), ext), ErrTdxTcbStatus; gotErr == nil || !errors.Is(gotErr, wantErr) {
t.Errorf("TCB status expired: checkTcbInfoTcbStatus() = %v. Want error %v", err, wantErr)
wantErrOutOfDate := `TDX TCB Status "OutOfDate" is not acceptable`
if err := checkTcbInfoTcbStatus(tcbInfo, quote.GetTdQuoteBody(), ext, nil); err == nil || err.Error() != wantErrOutOfDate {
t.Errorf("TCB status expired: checkTcbInfoTcbStatus() = %v. Want error %v", err, wantErrOutOfDate)
}
}

Expand All @@ -867,14 +867,15 @@ func TestNegativeCheckQeStatusV4(t *testing.T) {

qeIdentity.TcbLevels[0].Tcb.Isvsvn = 10
wantErr := "no matching QE TCB level found"
if err := checkQeTcbStatus(qeIdentity.TcbLevels, qeReport.GetIsvSvn()); err == nil || err.Error() != wantErr {
if err := checkQeTcbStatus(qeIdentity.TcbLevels, qeReport.GetIsvSvn(), nil); err == nil || err.Error() != wantErr {
t.Errorf("No matching TCB level: verifyUsingQeIdentity() = %v. Want error %v", err, wantErr)
}

qeIdentity.TcbLevels[0].Tcb.Isvsvn = 0
qeIdentity.TcbLevels[0].TcbStatus = "OutOfDate"
if gotErr, wantErr := checkQeTcbStatus(qeIdentity.TcbLevels, qeReport.GetIsvSvn()), ErrEnclaveTcbStatus; gotErr == nil || !errors.Is(gotErr, wantErr) {
t.Errorf("TCB status expired: verifyUsingQeIdentity() = %v. Want error %v", err, wantErr)
wantErrOutOfDate := `QE TCB Status "OutOfDate" is not acceptable`
if err := checkQeTcbStatus(qeIdentity.TcbLevels, qeReport.GetIsvSvn(), nil); err == nil || err.Error() != wantErrOutOfDate {
t.Errorf("TCB status expired: verifyUsingQeIdentity() = %v. Want error %v", err, wantErrOutOfDate)
}
}

Expand Down