From 71abc7e095e1a3cb667c6ba70f06c4c590cacff1 Mon Sep 17 00:00:00 2001 From: Thomas Tendyck Date: Wed, 18 Sep 2024 13:19:54 +0000 Subject: [PATCH] add advisory list to attestation report --- attestation/attestation.go | 25 +++----- enclave/ert.go | 10 +-- internal/attestation/attestation.go | 16 ++--- internal/attestation/claim.go | 29 +++++++++ internal/attestation/claim.h | 2 + internal/attestation/claim_test.go | 94 +++++++++++++++++++++++++++++ 6 files changed, 145 insertions(+), 31 deletions(-) create mode 100644 internal/attestation/claim_test.go diff --git a/attestation/attestation.go b/attestation/attestation.go index 11dbca05..bb216662 100644 --- a/attestation/attestation.go +++ b/attestation/attestation.go @@ -15,13 +15,15 @@ import ( // Report is a parsed enclave report. type Report struct { - Data []byte // The report data that has been included in the report. - SecurityVersion uint // Security version of the enclave. For SGX enclaves, this is the ISVSVN value. - Debug bool // If true, the report is for a debug enclave. - UniqueID []byte // The unique ID for the enclave. For SGX enclaves, this is the MRENCLAVE value. - SignerID []byte // The signer ID for the enclave. For SGX enclaves, this is the MRSIGNER value. - ProductID []byte // The Product ID for the enclave. For SGX enclaves, this is the ISVPRODID value. - TCBStatus tcbstatus.Status // The status of the enclave's TCB level. + Data []byte // The report data that has been included in the report. + SecurityVersion uint // Security version of the enclave. For SGX enclaves, this is the ISVSVN value. + Debug bool // If true, the report is for a debug enclave. + UniqueID []byte // The unique ID for the enclave. For SGX enclaves, this is the MRENCLAVE value. + SignerID []byte // The signer ID for the enclave. For SGX enclaves, this is the MRSIGNER value. + ProductID []byte // The Product ID for the enclave. For SGX enclaves, this is the ISVPRODID value. + TCBStatus tcbstatus.Status // The status of the enclave's TCB level. + TCBAdvisories []string // IDs of Intel security advisories that provide insight into the reasons when the TCB status is not UpToDate. + TCBAdvisoriesErr error // Error that occurred while getting the advisory array (if any). } var ( @@ -51,12 +53,5 @@ func VerifyAzureAttestationToken(token string, providerURL string) (Report, erro if err != nil { return Report{}, err } - return Report{ - Data: report.Data, - SecurityVersion: report.SecurityVersion, - Debug: report.Debug, - UniqueID: report.UniqueID, - SignerID: report.SignerID, - ProductID: report.ProductID, - }, nil + return Report(report), nil } diff --git a/enclave/ert.go b/enclave/ert.go index ce310654..c7ce497e 100644 --- a/enclave/ert.go +++ b/enclave/ert.go @@ -106,15 +106,7 @@ func VerifyRemoteReport(reportBytes []byte) (attestation.Report, error) { if err != nil { return attestation.Report{}, err } - return attestation.Report{ - Data: report.Data, - SecurityVersion: report.SecurityVersion, - Debug: report.Debug, - UniqueID: report.UniqueID, - SignerID: report.SignerID, - ProductID: report.ProductID, - TCBStatus: report.TCBStatus, - }, verifyErr + return attestation.Report(report), verifyErr } // GetLocalReport gets a report signed by the enclave platform for use in local attestation. diff --git a/internal/attestation/attestation.go b/internal/attestation/attestation.go index 2def6563..9ff10482 100644 --- a/internal/attestation/attestation.go +++ b/internal/attestation/attestation.go @@ -25,13 +25,15 @@ import ( // Report is a parsed enclave report. type Report struct { - Data []byte // The report data that has been included in the report. - SecurityVersion uint // Security version of the enclave. For SGX enclaves, this is the ISVSVN value. - Debug bool // If true, the report is for a debug enclave. - UniqueID []byte // The unique ID for the enclave. For SGX enclaves, this is the MRENCLAVE value. - SignerID []byte // The signer ID for the enclave. For SGX enclaves, this is the MRSIGNER value. - ProductID []byte // The Product ID for the enclave. For SGX enclaves, this is the ISVPRODID value. - TCBStatus tcbstatus.Status // The status of the enclave's TCB level. + Data []byte // The report data that has been included in the report. + SecurityVersion uint // Security version of the enclave. For SGX enclaves, this is the ISVSVN value. + Debug bool // If true, the report is for a debug enclave. + UniqueID []byte // The unique ID for the enclave. For SGX enclaves, this is the MRENCLAVE value. + SignerID []byte // The signer ID for the enclave. For SGX enclaves, this is the MRSIGNER value. + ProductID []byte // The Product ID for the enclave. For SGX enclaves, this is the ISVPRODID value. + TCBStatus tcbstatus.Status // The status of the enclave's TCB level. + TCBAdvisories []string // IDs of Intel security advisories that provide insight into the reasons when the TCB status is not UpToDate. + TCBAdvisoriesErr error // Error that occurred while getting the advisory array (if any). } // https://github.com/openenclave/openenclave/blob/master/include/openenclave/internal/report.h diff --git a/internal/attestation/claim.go b/internal/attestation/claim.go index a1ca0d5d..7fecfc1a 100644 --- a/internal/attestation/claim.go +++ b/internal/attestation/claim.go @@ -10,7 +10,10 @@ package attestation import "C" import ( + "bytes" + "encoding/json" "errors" + "math" "unsafe" "github.com/edgelesssys/ego/attestation/tcbstatus" @@ -24,6 +27,8 @@ func ParseClaims(claims uintptr, claimsLength uintptr) (Report, error) { func parseClaims(claims []C.oe_claim_t) (Report, error) { report := Report{TCBStatus: tcbstatus.Unknown} hasAttributes := false + var tcbInfo []byte + var tcbInfoIndex uint = math.MaxUint for _, claim := range claims { switch C.GoString(claim.name) { @@ -46,12 +51,17 @@ func parseClaims(claims []C.oe_claim_t) (Report, error) { report.TCBStatus = tcbstatus.Status(claimUint(claim)) case C.OE_CLAIM_SGX_REPORT_DATA: report.Data = claimBytes(claim) + case C.OE_CLAIM_SGX_TCB_INFO: + tcbInfo = claimBytes(claim) + case C.OE_CLAIM_SGX_TCB_INFO_INDEX: + tcbInfoIndex = claimUint(claim) } } if !hasAttributes { return Report{}, errors.New("missing attributes in report claims") } + report.TCBAdvisories, report.TCBAdvisoriesErr = getAdvisoriesFromTCBInfo(tcbInfo, tcbInfoIndex) return report, nil } @@ -65,3 +75,22 @@ func claimUint(claim C.oe_claim_t) uint { func claimBytes(claim C.oe_claim_t) []byte { return C.GoBytes(unsafe.Pointer(claim.value), C.int(claim.value_size)) } + +func getAdvisoriesFromTCBInfo(tcbInfo []byte, tcbInfoIndex uint) ([]string, error) { + tcbInfo = bytes.Trim(tcbInfo, "\x00") // claim from OE includes null terminator + + var info struct { + TCBInfo struct { + TCBLevels []struct{ AdvisoryIDs []string } + } + } + if err := json.Unmarshal(tcbInfo, &info); err != nil { + return nil, err + } + + levels := info.TCBInfo.TCBLevels + if uint(len(levels)) <= tcbInfoIndex { + return nil, errors.New("invalid TCB info index") + } + return levels[tcbInfoIndex].AdvisoryIDs, nil +} diff --git a/internal/attestation/claim.h b/internal/attestation/claim.h index 6419b536..683a72f1 100644 --- a/internal/attestation/claim.h +++ b/internal/attestation/claim.h @@ -22,3 +22,5 @@ typedef struct _oe_claim #define OE_CLAIM_PRODUCT_ID "product_id" #define OE_CLAIM_TCB_STATUS "tcb_status" #define OE_CLAIM_SGX_REPORT_DATA "sgx_report_data" +#define OE_CLAIM_SGX_TCB_INFO "sgx_tcb_info" +#define OE_CLAIM_SGX_TCB_INFO_INDEX "sgx_tcb_info_index" diff --git a/internal/attestation/claim_test.go b/internal/attestation/claim_test.go new file mode 100644 index 00000000..bd2a584f --- /dev/null +++ b/internal/attestation/claim_test.go @@ -0,0 +1,94 @@ +package attestation + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetAdvisoriesFromTCBInfo(t *testing.T) { + const tcbInfo = ` +{ + "tcbInfo": { + "tcbLevels": [ + { + "tcbStatus": "UpToDate" + }, + { + "tcbStatus": "OutOfDate", + "advisoryIDs": [ + "id 1" + ] + }, + { + "tcbStatus": "OutOfDate", + "advisoryIDs": [ + "id 2", + "id 3", + "id 4" + ] + } + ] + } +} +` + + testCases := map[string]struct { + info string + index uint + wantErr bool + wantAdvisories []string + }{ + "0 advisories at index 0": { + info: tcbInfo, + index: 0, + }, + "1 advisory at index 1": { + info: tcbInfo, + index: 1, + wantAdvisories: []string{"id 1"}, + }, + "3 advisories at index 2": { + info: tcbInfo, + index: 2, + wantAdvisories: []string{"id 2", "id 3", "id 4"}, + }, + "error at index 3": { + info: tcbInfo, + index: 3, + wantErr: true, + }, + "no TCB info": { + info: "", + index: 0, + wantErr: true, + }, + "null-terminated TCB info": { + info: tcbInfo + "\x00", + index: 1, + wantAdvisories: []string{"id 1"}, + }, + "multiple null terminators": { + info: tcbInfo + "\x00\x00\x00", + index: 1, + wantAdvisories: []string{"id 1"}, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + advisories, err := getAdvisoriesFromTCBInfo([]byte(tc.info), tc.index) + if tc.wantErr { + assert.Error(err) + return + } + require.NoError(err) + + assert.Equal(tc.wantAdvisories, advisories) + }) + } +}