From 09943dea1a8ac94a5d1e635b242cc31367257569 Mon Sep 17 00:00:00 2001 From: Thore Sommer Date: Tue, 14 Nov 2023 12:20:02 +0200 Subject: [PATCH] extract optional and required SGX claims provided by Open Enclave --- attestation/attestation.go | 121 ++++++++++++++++++++++++++++ eclient/eclient.go | 4 +- enclave/enclave.go | 4 +- enclave/ert.go | 11 +-- internal/attestation/attestation.go | 36 +++++++++ internal/attestation/claim.go | 90 +++++++++++++++++++++ internal/attestation/claim.h | 3 + internal/attestation/sgx_evidence.h | 41 ++++++++++ 8 files changed, 297 insertions(+), 13 deletions(-) create mode 100644 internal/attestation/sgx_evidence.h diff --git a/attestation/attestation.go b/attestation/attestation.go index 540221ee..6ecd0893 100644 --- a/attestation/attestation.go +++ b/attestation/attestation.go @@ -22,6 +22,127 @@ type Report struct { 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. + UEID []byte // The universal entity ID. For SGX enclaves, this is QE identity value with an additional first bit that indicates the OE UEID type. + SGXClaims *SGXClaims +} + +// SGX specific claims provided by Open Enclave +type SGXClaims struct { + SGXRequired SGXRequired + SGXOptional *SGXOptional +} + +// Claims that are in every Open Enclave generated report for SGX +type SGXRequired struct { + PfGpExinfoEnabled bool + ISVExtendedProductID []byte + IsMode64Bit bool + HasProvisionKey bool + HasEINITTokenKey bool + UsesKSS bool + ConfigID []byte + ConfigSVN []byte + ISVFamilyID []byte + CPUSVN []byte +} + +// SQX quote verification collaterals and PCESVN claims from OE. +// Those are optional and might be empty +type SGXOptional struct { + TCBInfo []byte + TCBIssuerChain []byte + PCKCRL []byte + RootCACRL []byte + CRLIssuerChain []byte + QEIDInfo []byte + QEIDIssuerChain []byte + PCESVN []byte +} + +func FromInternal(internal attestation.Report) Report { + var reportSGXClaims *SGXClaims + if internal.SGXClaims != nil { + reportSGXClaims = &SGXClaims{} + reportSGXClaims.SGXRequired = SGXRequired{ + PfGpExinfoEnabled: internal.SGXClaims.SGXRequired.PfGpExinfoEnabled, + ISVExtendedProductID: internal.SGXClaims.SGXRequired.ISVExtendedProductID, + IsMode64Bit: internal.SGXClaims.SGXRequired.IsMode64Bit, + HasProvisionKey: internal.SGXClaims.SGXRequired.HasProvisionKey, + HasEINITTokenKey: internal.SGXClaims.SGXRequired.HasEINITTokenKey, + UsesKSS: internal.SGXClaims.SGXRequired.UsesKSS, + ConfigID: internal.SGXClaims.SGXRequired.ConfigID, + ConfigSVN: internal.SGXClaims.SGXRequired.ConfigSVN, + ISVFamilyID: internal.SGXClaims.SGXRequired.ISVFamilyID, + CPUSVN: internal.SGXClaims.SGXRequired.CPUSVN, + } + if internal.SGXClaims.SGXOptional != nil { + reportSGXClaims.SGXOptional = &SGXOptional{ + TCBInfo: internal.SGXClaims.SGXOptional.TCBInfo, + TCBIssuerChain: internal.SGXClaims.SGXOptional.TCBIssuerChain, + PCKCRL: internal.SGXClaims.SGXOptional.PCKCRL, + RootCACRL: internal.SGXClaims.SGXOptional.RootCACRL, + CRLIssuerChain: internal.SGXClaims.SGXOptional.CRLIssuerChain, + QEIDInfo: internal.SGXClaims.SGXOptional.QEIDInfo, + QEIDIssuerChain: internal.SGXClaims.SGXOptional.QEIDIssuerChain, + PCESVN: internal.SGXClaims.SGXOptional.PCESVN, + } + } + } + + return Report{ + Data: internal.Data, + SecurityVersion: internal.SecurityVersion, + Debug: internal.Debug, + UniqueID: internal.UniqueID, + SignerID: internal.SignerID, + ProductID: internal.ProductID, + TCBStatus: internal.TCBStatus, + UEID: internal.UEID, + SGXClaims: reportSGXClaims, + } +} + +func (report Report) ToInternal() attestation.Report { + var reportSGXClaims *attestation.SGXClaims + if report.SGXClaims != nil { + reportSGXClaims = &attestation.SGXClaims{} + reportSGXClaims.SGXRequired = attestation.SGXRequired{ + PfGpExinfoEnabled: report.SGXClaims.SGXRequired.PfGpExinfoEnabled, + ISVExtendedProductID: report.SGXClaims.SGXRequired.ISVExtendedProductID, + IsMode64Bit: report.SGXClaims.SGXRequired.IsMode64Bit, + HasProvisionKey: report.SGXClaims.SGXRequired.HasProvisionKey, + HasEINITTokenKey: report.SGXClaims.SGXRequired.HasEINITTokenKey, + UsesKSS: report.SGXClaims.SGXRequired.UsesKSS, + ConfigID: report.SGXClaims.SGXRequired.ConfigID, + ConfigSVN: report.SGXClaims.SGXRequired.ConfigSVN, + ISVFamilyID: report.SGXClaims.SGXRequired.ISVFamilyID, + CPUSVN: report.SGXClaims.SGXRequired.CPUSVN, + } + if report.SGXClaims.SGXOptional != nil { + reportSGXClaims.SGXOptional = &attestation.SGXOptional{ + TCBInfo: report.SGXClaims.SGXOptional.TCBInfo, + TCBIssuerChain: report.SGXClaims.SGXOptional.TCBIssuerChain, + PCKCRL: report.SGXClaims.SGXOptional.PCKCRL, + RootCACRL: report.SGXClaims.SGXOptional.RootCACRL, + CRLIssuerChain: report.SGXClaims.SGXOptional.CRLIssuerChain, + QEIDInfo: report.SGXClaims.SGXOptional.QEIDInfo, + QEIDIssuerChain: report.SGXClaims.SGXOptional.QEIDIssuerChain, + PCESVN: report.SGXClaims.SGXOptional.PCESVN, + } + } + } + + return attestation.Report{ + Data: report.Data, + SecurityVersion: report.SecurityVersion, + Debug: report.Debug, + UniqueID: report.UniqueID, + SignerID: report.SignerID, + ProductID: report.ProductID, + TCBStatus: report.TCBStatus, + UEID: report.UEID, + SGXClaims: reportSGXClaims, + } } var ( diff --git a/eclient/eclient.go b/eclient/eclient.go index 5634cc24..1ba9e28e 100644 --- a/eclient/eclient.go +++ b/eclient/eclient.go @@ -22,7 +22,7 @@ import ( // The caller must verify the returned report's content. func VerifyRemoteReport(reportBytes []byte) (attestation.Report, error) { report, err := verifyRemoteReport(reportBytes) - return attestation.Report(report), err + return attestation.FromInternal(report), err } // CreateAttestationClientTLSConfig creates a tls.Config object that verifies a certificate with embedded report. @@ -37,7 +37,7 @@ func CreateAttestationClientTLSConfig(verifyReport func(attestation.Report) erro return internal.CreateAttestationClientTLSConfig( verifyRemoteReport, appliedOpts, - func(rep internal.Report) error { return verifyReport(attestation.Report(rep)) }, + func(rep internal.Report) error { return verifyReport(attestation.FromInternal(rep)) }, ) } diff --git a/enclave/enclave.go b/enclave/enclave.go index 563808d4..bd44b407 100644 --- a/enclave/enclave.go +++ b/enclave/enclave.go @@ -66,10 +66,10 @@ func CreateAttestationClientTLSConfig(verifyReport func(attestation.Report) erro return internal.CreateAttestationClientTLSConfig( func(reportBytes []byte) (internal.Report, error) { report, err := VerifyRemoteReport(reportBytes) - return internal.Report(report), err + return report.ToInternal(), err }, appliedOpts, - func(rep internal.Report) error { return verifyReport(attestation.Report(rep)) }, + func(rep internal.Report) error { return verifyReport(attestation.FromInternal(rep)) }, ) } diff --git a/enclave/ert.go b/enclave/ert.go index cf05a387..37b43ff5 100644 --- a/enclave/ert.go +++ b/enclave/ert.go @@ -106,15 +106,8 @@ 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.FromInternal(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..f189b603 100644 --- a/internal/attestation/attestation.go +++ b/internal/attestation/attestation.go @@ -32,6 +32,42 @@ type Report struct { 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. + UEID []byte // The universal entity ID. For SGX enclaves, this is QE identity value with an additional first bit that indicates the OE UEID type. + HardwareModel []byte + SGXClaims *SGXClaims +} + +// SGX specific claims provided by Open Enclave +type SGXClaims struct { + SGXRequired SGXRequired + SGXOptional *SGXOptional +} + +// Claims that are in every Open Enclave generated report for SGX +type SGXRequired struct { + PfGpExinfoEnabled bool + ISVExtendedProductID []byte + IsMode64Bit bool + HasProvisionKey bool + HasEINITTokenKey bool + UsesKSS bool + ConfigID []byte + ConfigSVN []byte + ISVFamilyID []byte + CPUSVN []byte +} + +// SQX quote verification collaterals and PCESVN claims from OE. +// Those are optional and might be empty +type SGXOptional struct { + TCBInfo []byte + TCBIssuerChain []byte + PCKCRL []byte + RootCACRL []byte + CRLIssuerChain []byte + QEIDInfo []byte + QEIDIssuerChain []byte + PCESVN []byte } // 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 c78b80c7..64e962b2 100644 --- a/internal/attestation/claim.go +++ b/internal/attestation/claim.go @@ -7,9 +7,11 @@ package attestation // #include "claim.h" +// #include "sgx_evidence.h" import "C" import ( "errors" + "fmt" "unsafe" "github.com/edgelesssys/ego/attestation/tcbstatus" @@ -23,6 +25,10 @@ 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 reportSGX SGXClaims + var reportSGXOptional SGXOptional + var claimCountSGXrequired = 0 + var claimCountSGXoptional = 0 for _, claim := range claims { switch C.GoString(claim.name) { @@ -45,12 +51,92 @@ 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_UEID: + // The UEID is prefixed with a type which is currently always OE_UEID_TYPE_RAND for SGX + claimUEID := claimBytes(claim) + if len(claimUEID) > 0 && claimUEID[0] != C.OE_UEID_TYPE_RAND { + return Report{}, errors.New("Expected UEID of type OE_UEID_TYPE_RAND") + } + report.UEID = claimUEID + // SGX Required claims + case C.OE_CLAIM_SGX_PF_GP_EXINFO_ENABLED: + reportSGX.SGXRequired.PfGpExinfoEnabled = claimBool(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_ISV_EXTENDED_PRODUCT_ID: + reportSGX.SGXRequired.ISVExtendedProductID = claimBytes(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_IS_MODE64BIT: + reportSGX.SGXRequired.IsMode64Bit = claimBool(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_HAS_PROVISION_KEY: + reportSGX.SGXRequired.HasProvisionKey = claimBool(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_HAS_EINITTOKEN_KEY: + reportSGX.SGXRequired.HasEINITTokenKey = claimBool(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_USES_KSS: + reportSGX.SGXRequired.UsesKSS = claimBool(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_CONFIG_ID: + reportSGX.SGXRequired.ConfigID = claimBytes(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_CONFIG_SVN: + reportSGX.SGXRequired.ConfigSVN = claimBytes(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_ISV_FAMILY_ID: + reportSGX.SGXRequired.ISVFamilyID = claimBytes(claim) + claimCountSGXrequired++ + case C.OE_CLAIM_SGX_CPU_SVN: + reportSGX.SGXRequired.CPUSVN = claimBytes(claim) + claimCountSGXrequired++ + //SGX optional claims + case C.OE_CLAIM_SGX_TCB_INFO: + reportSGXOptional.TCBInfo = claimBytes(claim) + claimCountSGXoptional++ + case C.OE_CLAIM_SGX_TCB_ISSUER_CHAIN: + reportSGXOptional.TCBIssuerChain = claimBytes(claim) + claimCountSGXoptional++ + case C.OE_CLAIM_SGX_PCK_CRL: + reportSGXOptional.PCKCRL = claimBytes(claim) + claimCountSGXoptional++ + case C.OE_CLAIM_SGX_ROOT_CA_CRL: + reportSGXOptional.RootCACRL = claimBytes(claim) + claimCountSGXoptional++ + case C.OE_CLAIM_SGX_CRL_ISSUER_CHAIN: + reportSGXOptional.CRLIssuerChain = claimBytes(claim) + claimCountSGXoptional++ + case C.OE_CLAIM_SGX_QE_ID_INFO: + reportSGXOptional.QEIDInfo = claimBytes(claim) + claimCountSGXoptional++ + case C.OE_CLAIM_SGX_QE_ID_ISSUER_CHAIN: + reportSGXOptional.QEIDIssuerChain = claimBytes(claim) + claimCountSGXoptional++ + case C.OE_CLAIM_SGX_PCE_SVN: + reportSGXOptional.PCESVN = claimBytes(claim) + claimCountSGXoptional++ } + + } + if claimCountSGXrequired > 0 && claimCountSGXrequired != C.OE_SGX_REQUIRED_CLAIMS_COUNT { + return Report{}, fmt.Errorf("some required SGX claims are missing. Only got: %d, expected: %d", claimCountSGXrequired, C.OE_SGX_REQUIRED_CLAIMS_COUNT) + } + + if claimCountSGXoptional > C.OE_SGX_OPTIONAL_CLAIMS_COUNT { + return Report{}, fmt.Errorf("optional SGX claims are too many. Got: %d, expected maximum: %d", claimCountSGXoptional, C.OE_SGX_OPTIONAL_CLAIMS_COUNT) } if !hasAttributes { return Report{}, errors.New("missing attributes in report claims") } + + if claimCountSGXoptional > 0 { + reportSGX.SGXOptional = &reportSGXOptional + } + + if claimCountSGXrequired > 0 { + report.SGXClaims = &reportSGX + } + return report, nil } @@ -61,6 +147,10 @@ func claimUint(claim C.oe_claim_t) uint { return uint(*(*C.uint32_t)(unsafe.Pointer(claim.value))) } +func claimBool(claim C.oe_claim_t) bool { + return bool(*(*C._Bool)(unsafe.Pointer(claim.value))) +} + func claimBytes(claim C.oe_claim_t) []byte { return C.GoBytes(unsafe.Pointer(claim.value), C.int(claim.value_size)) } diff --git a/internal/attestation/claim.h b/internal/attestation/claim.h index 6419b536..a78cdb2e 100644 --- a/internal/attestation/claim.h +++ b/internal/attestation/claim.h @@ -22,3 +22,6 @@ 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_UEID "ueid" + +#define OE_UEID_TYPE_RAND 0x01 diff --git a/internal/attestation/sgx_evidence.h b/internal/attestation/sgx_evidence.h new file mode 100644 index 00000000..674d2a30 --- /dev/null +++ b/internal/attestation/sgx_evidence.h @@ -0,0 +1,41 @@ +// Copyright (c) Open Enclave SDK contributors. +// Licensed under the MIT License. + +// Based on attestation/sgx/evidence.h + +// SGX specific claims +// Required: SGX report body fields that every SQX Quote verification should +// output. +// 1 boolean flag indicated by "sgx_misc_select_t" +#define OE_CLAIM_SGX_PF_GP_EXINFO_ENABLED "sgx_pf_gp_exit_info_enabled" +#define OE_CLAIM_SGX_ISV_EXTENDED_PRODUCT_ID "sgx_isv_extended_product_id" +// 4 boolean flags indicated by "sgx_attributes_t" +#define OE_CLAIM_SGX_IS_MODE64BIT "sgx_is_mode64bit" +#define OE_CLAIM_SGX_HAS_PROVISION_KEY "sgx_has_provision_key" +#define OE_CLAIM_SGX_HAS_EINITTOKEN_KEY "sgx_has_einittoken_key" +#define OE_CLAIM_SGX_USES_KSS "sgx_uses_kss" +#define OE_CLAIM_SGX_CONFIG_ID "sgx_config_id" +#define OE_CLAIM_SGX_CONFIG_SVN "sgx_config_svn" +#define OE_CLAIM_SGX_ISV_FAMILY_ID "sgx_isv_family_id" +#define OE_CLAIM_SGX_CPU_SVN "sgx_cpu_svn" +#define OE_SGX_REQUIRED_CLAIMS_COUNT 10 + +/* + * Optional: SQX Quote data + */ +// SQX quote verification collaterals. +#define OE_CLAIM_SGX_TCB_INFO "sgx_tcb_info" +#define OE_CLAIM_SGX_TCB_ISSUER_CHAIN "sgx_tcb_issuer_chain" +#define OE_CLAIM_SGX_PCK_CRL "sgx_pck_crl" +#define OE_CLAIM_SGX_ROOT_CA_CRL "sgx_root_ca_crl" +#define OE_CLAIM_SGX_CRL_ISSUER_CHAIN "sgx_crl_issuer_chain" +#define OE_CLAIM_SGX_QE_ID_INFO "sgx_qe_id_info" +#define OE_CLAIM_SGX_QE_ID_ISSUER_CHAIN "sgx_qe_id_issuer_chain" +#define OE_SGX_OPTIONAL_CLAIMS_SGX_COLLATERALS_COUNT 7 +// SGX PCESVN. +#define OE_CLAIM_SGX_PCE_SVN "sgx_pce_svn" +#define OE_SGX_OPTIONAL_CLAIMS_COUNT 8 + +// Additional SGX specific claim: for the report data embedded in the SGX quote. + +#define OE_CLAIM_SGX_REPORT_DATA "sgx_report_data"