From ca0953e5dcca47681fd7198defa6df6a0a4407e0 Mon Sep 17 00:00:00 2001 From: p083697 Date: Mon, 30 Oct 2023 11:24:50 +0000 Subject: [PATCH 01/23] implemented sgx verification and tdx parser --- attestationreport/PCKCertCRL | Bin 0 -> 303 bytes attestationreport/RootCaCRL | Bin 0 -> 293 bytes attestationreport/attestationreport.go | 58 ++ attestationreport/sgx.go | 791 +++++++++++++++++++++++++ attestationreport/sgx_structs.go | 511 ++++++++++++++++ attestationreport/sgx_test.go | 711 ++++++++++++++++++++++ attestationreport/tdx.go | 62 ++ attestationreport/tdx_struct.go | 202 +++++++ attestationreport/tdx_test.go | 383 ++++++++++++ attestationreport/validationreport.go | 22 + 10 files changed, 2740 insertions(+) create mode 100644 attestationreport/PCKCertCRL create mode 100644 attestationreport/RootCaCRL create mode 100644 attestationreport/sgx.go create mode 100644 attestationreport/sgx_structs.go create mode 100644 attestationreport/sgx_test.go create mode 100644 attestationreport/tdx.go create mode 100644 attestationreport/tdx_struct.go create mode 100644 attestationreport/tdx_test.go diff --git a/attestationreport/PCKCertCRL b/attestationreport/PCKCertCRL new file mode 100644 index 0000000000000000000000000000000000000000..ff10332b2878583e067bbf1dd35b86ec97cc57e8 GIT binary patch literal 303 zcmXqLV$?QhyvW4JXu!qBq1EPb&X$Fl$)M0s*+7wvIh2K&N6Is=BsE7N*gZlaz}Z_N zpeR2%wYWIHNWt0BP|838q=<`05TeLAzo;O;D6u3nKhIFaKnNtq&chv?m{*dh;GC0K zlxWCpzzGuN;9&x4hjQ42nL>lbd5w$>4UCKo%}q=U&7;6vLnA{dcY(fvF3^F@p|T=C z=K&on4`M5`NEnDUh+KHF>Q?FXpL(9+Gi<6wQZHQn6zPlX8)i=i12-l`hO2>}@9`}= z9qP5ByDMk&bMqs@yqjMJ_Cy-)nP}rI9P|XJkGd9SU3r|Z` OjcDamnG-s#%NqcSt5jD2 literal 0 HcmV?d00001 diff --git a/attestationreport/RootCaCRL b/attestationreport/RootCaCRL new file mode 100644 index 0000000000000000000000000000000000000000..4716247d51028dc7927eec58a667a986a360b8c5 GIT binary patch literal 293 zcmXqLVpKF}Ji)}sXu!qBq1EPb&X$Fl$sof}%0PmRIh2K&N6<5`BsE7N*gZlaC_leM z!PyZe#|4vf&Mzv+FG?)Q%+E6vF%SajW9Q)xPRuJwRB+BoEJ`%wHsAz_a_}$#wL>{< z!c3vT;=D%21||l^h6YAPriM{Kt_hH91m!N!H_!#Ti#b$Q1n4TD8|6W4Wflnou?7*P zRGw>5b4@xwH*c95v_C-FQk`uLvL~257z|vQ6avF*-EaP07``oDZtIuS>OFB`XVw=- zF@M~de4V8sYNG^`BE#efqHn~^p1=AP@026wpyqz8SXetF;MCrXbKiDd&(LB90I3d8 ALI3~& literal 0 HcmV?d00001 diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index 002013fa..b3051398 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -146,6 +146,34 @@ type SwMeasurement struct { Sha256 HexByte `json:"sha256" cbor:"2,keyasint"` } +// TdxMeasurement represents the attestation report +// element of type 'TDX Measurement' signed by the device +type TdxMeasurement struct { + Type string `json:"type" cbor:"0,keyasint"` + Report []byte `json:"blob" cbor:"1,keyasint"` + Certs [][]byte `json:"certs" cbor:"2,keyasint"` +} + +type TdxPolicy struct { + Type string `json:"type" cbor:"0,keyasint"` + Debug bool `json:"debug" cbor:"2,keyasint"` + // maybe also tcb min/max version, etc. +} + +// SgxMeasurement represents the attestation report +// element of type 'SGX Measurement' signed by the device +type SgxMeasurement struct { + Type string `json:"type" cbor:"0,keyasint"` + Report []byte `json:"blob" cbor:"1,keyasint"` + Certs [][]byte `json:"certs" cbor:"2,keyasint"` +} + +type SgxPolicy struct { + Type string `json:"type" cbor:"0,keyasint"` + Debug bool `json:"debug" cbor:"2,keyasint"` + // maybe also tcb min/max version, etc. +} + type SnpPolicy struct { Type string `json:"type" cbor:"0,keyasint"` SingleSocket bool `json:"singleSocket" cbor:"1,keyasint"` @@ -177,6 +205,30 @@ type SnpDetails struct { Tcb SnpTcb `json:"tcb" cbor:"4,keyasint"` } +type SGXCollateral struct { + // Format of CRLs: + // version 1.0: PEM, v3.0: DER base16, v3.1: DER raw binary + TeeType uint32 `json:"teeType" cbor:"2,keyasint" description:"Type of the Tee (0x00000000: SGX, 0x00000081: TDX)"` + TcbInfo json.RawMessage `json:"tcbInfo" cbor:"3,keyasint" description:"TCB Info structure in JSON format"` + TcbInfoSize uint32 `json:"tcbInfoSize" cbor:"4,keyasint" description:"Size of the TCB Info"` + QeIdentity json.RawMessage `json:"qeIdentity" cbor:"5,keyasint" description:"QE identity structure in JSON format"` + QeIdentitySize uint32 `json:"qeIdentitySize" cbor:"6,keyasint" description:"Size of the QE identity"` +} + +type SGXDetails struct { + Version uint16 `json:"version" cbor:"0,keyasint"` + Collateral SGXCollateral `json:"collateral" cbor:"1,keyasint"` + CAfingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint + Policy SgxPolicy `json:"policy" cbor:"3,keyasint"` + Attributes [16]byte `json:"attributes" cbor:"4,keyasint"` + IsvProdId uint16 `json:"isvProdId" cbor:"5,keyasint"` + MRSIGNER string `json:"mrsigner" cbor:"6,keyasint"` +} + +type TDXDetails struct { + // TODO: add attributes to this struct +} + // ReferenceValue represents the attestation report // element of types 'SNP Reference Value', 'TPM Reference Value' // and 'SW Reference Value' @@ -187,6 +239,8 @@ type ReferenceValue struct { Name string `json:"name,omitempty" cbor:"3,keyasint,omitempty"` Pcr *int `json:"pcr,omitempty" cbor:"4,keyasint,omitempty"` Snp *SnpDetails `json:"snp,omitempty" cbor:"5,keyasint,omitempty"` + Sgx *SGXDetails `json:"sgx,omitempty" cbor:"7,keyasint,omitempty"` + Tdx *TDXDetails `json:"tdx,omitempty" cbor:"8,keyasint,omitempty"` Description string `json:"description,omitempty" cbor:"6,keyasint,omitempty"` } @@ -308,6 +362,8 @@ type ArPlain struct { Type string `json:"type" cbor:"0,keyasint"` TpmM *TpmMeasurement `json:"tpmMeasurement,omitempty" cbor:"1,keyasint,omitempty"` SnpM *SnpMeasurement `json:"snpMeasurement,omitempty" cbor:"2,keyasint,omitempty"` + SgxM *SgxMeasurement `json:"sgxMeasurement,omitempty" cbor:"11,keyasint,omitempty"` + TdxM *TdxMeasurement `json:"tdxMeasurement,omitempty" cbor:"12,keyasint,omitempty"` IasM *IasMeasurement `cbor:"10,keyasint,omitempty"` SWM []SwMeasurement `json:"swMeasurements,omitempty" cbor:"3,keyasint,omitempty"` RtmManifest RtmManifest `json:"rtmManifest" cbor:"4,keyasint"` @@ -324,6 +380,8 @@ type ArPacked struct { Type string `json:"type" cbor:"0,keyasint"` TpmM *TpmMeasurement `json:"tpmMeasurement,omitempty" cbor:"1,keyasint,omitempty"` SnpM *SnpMeasurement `json:"snpMeasurement,omitempty" cbor:"2,keyasint,omitempty"` + SgxM *SgxMeasurement `json:"sgxMeasurement,omitempty" cbor:"10,keyasint,omitempty"` + TdxM *TdxMeasurement `json:"tdxMeasurement,omitempty" cbor:"11,keyasint,omitempty"` SWM []SwMeasurement `json:"swMeasurements,omitempty" cbor:"3,keyasint,omitempty"` RtmManifest []byte `json:"rtmManifests" cbor:"4,keyasint"` OsManifest []byte `json:"osManifest" cbor:"5,keyasint"` diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go new file mode 100644 index 00000000..ac026545 --- /dev/null +++ b/attestationreport/sgx.go @@ -0,0 +1,791 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attestationreport + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "crypto/x509" + "encoding/binary" + "encoding/hex" + "fmt" + "io/ioutil" + "math/big" + "net/http" + "os" + "reflect" + "regexp" + "time" + + "github.com/Fraunhofer-AISEC/cmc/internal" +) + +const ( + SGX_QUOTE_TYPE uint32 = 0x0 + TDX_QUOTE_TYPE uint32 = 0x81 + + ecdsa_p_256 = 2 + QUOTE_MIN_SIZE = 1020 // value from Intel SGX QVL + + QUOTE_HEADER_SIZE = 48 + SGX_QUOTE_BODY_SIZE = 384 + SGX_QUOTE_SIGNATURE_OFFSET = 436 + TDX_QUOTE_BODY_SIZE = 584 + TDX_QUOTE_SIGNATURE_OFFSET = 632 + + TDX_ID = "TDX" + SGX_ID = "SGX" + + // Params: ca = processor or platform, encoding = pem or der + PCS_PCK_CERT_CRL_URI = "https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=%v&encoding=der" + PCS_ROOT_CA_CRL_URI = "https://certificates.trustedservices.intel.com/IntelSGXRootCA.der" + ROOT_CA_CRL_NAME = "RootCaCRL" + PCK_CERT_CRL_NAME = "PCKCertCRL" + + SGX_EXTENSION_INDEX = 5 +) + +// main function to very a SGX Quote + Measurements +func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*SgxMeasurementResult, bool) { + var err error + result := &SgxMeasurementResult{} + ok := true + + // If the attestationreport does contain neither SGX measurements, nor SGX Reference Values + // there is nothing to to + if sgxM == nil && len(referenceValues) == 0 { + return nil, true + } + + if len(referenceValues) == 0 { + msg := "Could not find SGX Reference Value" + result.Summary.setFalse(&msg) + return result, false + } else if len(referenceValues) > 1 { + msg := fmt.Sprintf("Report contains %v reference values. Currently, only one SGX Reference Value is supported", + len(referenceValues)) + result.Summary.setFalse(&msg) + return result, false + } + sgxReferenceValue := referenceValues[0] + + // If the attestationreport contains SGX Reference Values, but no SGX measurement, the attestation must fail + if sgxM == nil { + for _, v := range referenceValues { + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: v.Name, + Digest: hex.EncodeToString(v.Sha256), + Success: false, + Type: "Reference Value", + }) + } + result.Summary.Success = false + return result, false + } + + // Validate Parameters: + if sgxM.Report == nil || len(sgxM.Report) < QUOTE_MIN_SIZE { + msg := "Invalid SGX Report." + result.Summary.setFalse(&msg) + return result, false + } + + if sgxReferenceValue.Type != "SGX Reference Value" { + msg := fmt.Sprintf("SGX Reference Value invalid type %v", sgxReferenceValue.Type) + result.Summary.setFalse(&msg) + return result, false + } + + if sgxReferenceValue.Sgx == nil { + msg := "SGX Reference Value is null" + result.Summary.setFalse(&msg) + return result, false + } + + var sgxQuote SgxReport + var quoteType uint32 = sgxReferenceValue.Sgx.Collateral.TeeType + if quoteType == SGX_QUOTE_TYPE { + // extract the attestation report into the SGXReport data structure + sgxQuote, err = DecodeSgxReport(sgxM.Report) + if err != nil { + msg := fmt.Sprintf("Failed to decode SGX report: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + } else { + log.Tracef("Unknown quote type (tee_type: %X)\n", quoteType) + return result, false + } + fmt.Println("QuoteSignatureDataLen: ", sgxQuote.QuoteSignatureDataLen) + + // Compare Nonce for Freshness (called Report Data in the SNP Attestation Report Structure) + // ReportData contains: nonce in ReportData field + nonce64 := make([]byte, 64) + copy(nonce64, nonce[:]) + + if cmp := bytes.Compare(sgxQuote.ISVEnclaveReport.ReportData[:], nonce64); cmp != 0 { + msg := fmt.Sprintf("Nonces mismatch: Plain Nonce: %v, Expected: %v, Got = %v", + nonce, hex.EncodeToString(nonce64), hex.EncodeToString(sgxQuote.ISVEnclaveReport.ReportData[:])) + result.Freshness.setFalse(&msg) + result.Summary.Success = false + return result, false + } else { + result.Freshness.Success = true + } + + // parse cert chain + certs, err := ParseCertificates(sgxM.Certs, true) + if err != nil { + msg := fmt.Sprintf("Failed to parse certificates: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + fmt.Println("certs:", certs) + + var current_time time.Time = time.Now() + log.Trace("current time: ", current_time) + + // (from DCAP Library): parse PCK Cert chain (from the quote) into CertificateChain object. return error in case of failure + // TODO: handle other QECertDataTypes (for now: throw an error) + var certChain SgxCertificates + if sgxQuote.QuoteSignatureData.QECertDataType == 5 { + certChain, err = ParseCertificates(sgxQuote.QuoteSignatureData.QECertData, true) + if err != nil { + msg := fmt.Sprintf("Failed to parse certificate chain from QECertData: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + } else { + msg := fmt.Sprintf("QECertDataType not supported: %v", sgxQuote.QuoteSignatureData.QECertDataType) + result.Summary.setFalse(&msg) + return result, false + } + + // (from DCAP Library): extract root CA from PCK cert chain in quote -> compare nullptr + if certChain.RootCACert == nil { + msg := "root cert is null" + result.Summary.setFalse(&msg) + return result, false + } + + // (from DCAP Library): check root public key + if !reflect.DeepEqual(certChain.RootCACert.PublicKey, certs.RootCACert.PublicKey) { + msg := "root cert public key didn't match" + result.Summary.setFalse(&msg) + return result, false + } + + // (from DCAP Library): parse and verify PCK certificate chain + x509CertChains, err := internal.VerifyCertChain( + []*x509.Certificate{certChain.PCKCert, certChain.IntermediateCert}, + []*x509.Certificate{certChain.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify pck certificate chain: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // download CRLs from PCS + root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) + if err != nil { + msg := fmt.Sprintf("downloading ROOT CA CRL from PCS failed: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + pck_crl_uri := fmt.Sprintf(PCS_PCK_CERT_CRL_URI, "processor") + pck_crl, err := fetchCRL(pck_crl_uri, PCK_CERT_CRL_NAME) + if err != nil { + msg := fmt.Sprintf("downloading PCK Cert CRL from PCS failed: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // perform CRL checks (signature + values) + res, err := CrlCheck(root_ca_crl, certChain.RootCACert, certChain.RootCACert) + if !res || err != nil { + msg := fmt.Sprintf("CRL check on rootCert failed: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + res, err = CrlCheck(pck_crl, certChain.PCKCert, certChain.IntermediateCert) + if !res || err != nil { + msg := fmt.Sprintf("CRL check on pckCert failed: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // Store details from validated certificate chain(s) in the report + for _, chain := range x509CertChains { + chainExtracted := []X509CertExtracted{} + for _, cert := range chain { + chainExtracted = append(chainExtracted, ExtractX509Infos(cert)) + } + result.Signature.ValidatedCerts = append(result.Signature.ValidatedCerts, chainExtracted) + } + + // (from DCAP Library): parse and verify TcbInfo object + tcbInfo, err := ParseTcbInfo(sgxReferenceValue.Sgx.Collateral.TcbInfo) + if err != nil { + log.Trace("tcb_info:", sgxReferenceValue.Sgx.Collateral.TcbInfo) + msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + err = verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), certs.TCBSigningCert) + if err != nil { + msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // (from DCAP Library): parse and verify QE Identity object + qeIdentity, err := ParseQEIdentity(sgxReferenceValue.Sgx.Collateral.QeIdentity) + if err != nil { + msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + err = VerifyQEIdentity(&sgxQuote.QuoteSignatureData.QEReport, &qeIdentity, + string(sgxReferenceValue.Sgx.Collateral.QeIdentity), certs.TCBSigningCert, SGX_QUOTE_TYPE) + if err != nil { + msg := fmt.Sprintf("Failed to verify QE Identity structure: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // Verify Quote Signature + sig, ret := VerifyIntelQuoteSignature(sgxM.Report, sgxQuote.QuoteSignatureData, + sgxQuote.QuoteSignatureDataLen, int(sgxQuote.QuoteHeader.AttestationKeyType), certs, + sgxReferenceValue.Sgx.CAfingerprint, SGX_QUOTE_TYPE) + if !ret { + msg := fmt.Sprintf("Failed to verify Quote Signature: %v", sig) + result.Summary.setFalse(&msg) + return result, false + } + + result.Signature = sig + + // check version + result.VersionMatch, ret = verifySgxVersion(sgxQuote.QuoteHeader, sgxReferenceValue.Sgx.Version) + if !ret { + return result, false + } + + // Verify Quote Body values + err = VerifyEnclaveReportValues(&sgxQuote.ISVEnclaveReport, &tcbInfo, &certChain, &sgxReferenceValue, result) + if err != nil { + msg := fmt.Sprintf("Failed to verify Enclave Report Values: %v", err) + result.Summary.setFalse(&msg) + result.Summary.Success = false + return result, false + } else { + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: sgxReferenceValue.Name, + Digest: hex.EncodeToString(sgxQuote.ISVEnclaveReport.MRENCLAVE[:]), + Success: true, + }) + ok = true + } + + result.Summary.Success = ok + + return result, ok +} + +func verifySgxVersion(quote QuoteHeader, version uint16) (Result, bool) { + r := Result{} + ok := quote.Version == version + if !ok { + msg := fmt.Sprintf("SGX report version mismatch: Report = %v, supplied = %v", quote.Version, version) + r.setFalse(&msg) + } else { + r.Success = true + } + return r, ok +} + +// verifies the quote signature +// Can be used by SGX/TDX: QuoteType = 0x00 (sgx) or 0x81 (TDX) +// TODO: Handle different QE Cert Data Types in here +func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature ECDSA256QuoteSignatureDataStructure, + quoteSignatureSize uint32, quoteSignatureType int, certs SgxCertificates, + fingerprint string, quoteType uint32) (SignatureResult, bool) { + result := SignatureResult{} + var digest [32]byte + + if quoteType != SGX_QUOTE_TYPE && quoteType != TDX_QUOTE_TYPE { + msg := fmt.Sprintf("Quote Type not supported %v", quoteType) + result.SignCheck.setFalse(&msg) + return result, false + } + + // check signature size + if uint32(len(reportRaw)-SGX_QUOTE_SIGNATURE_OFFSET) != quoteSignatureSize { + msg := fmt.Sprintf("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", + quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) + result.SignCheck.setFalse(&msg) + return result, false + } + + // for now: check attestation signature type = key type + if quoteSignatureType != ecdsa_p_256 { + msg := fmt.Sprintf("Signature Algorithm %v not supported", quoteSignatureType) + result.SignCheck.setFalse(&msg) + return result, false + } + + // Step 1: Verify ISV Enclave Report Signature + if quoteType == SGX_QUOTE_TYPE { + digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+SGX_QUOTE_BODY_SIZE]) + } else { + digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+TDX_QUOTE_BODY_SIZE]) + } + + // Convert r, s to Big Int + r := new(big.Int) + r.SetBytes(quoteSignature.ISVEnclaveReportSignature[:32]) + s := new(big.Int) + s.SetBytes(quoteSignature.ISVEnclaveReportSignature[32:]) + + // Extract the attestation public key (generated by the QE) from the certificate + ak_pub := quoteSignature.ECDSAAttestationKey + if len(ak_pub) == 0 { + msg := "Failed to extract ECDSA public key from certificate" + result.SignCheck.setFalse(&msg) + return result, false + } + + //get x and y from public key + pubX := new(big.Int) + pubX.SetBytes(ak_pub[:32]) + pubY := new(big.Int) + pubY.SetBytes(ak_pub[32:]) + + ecdsa_ak_pub := &ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: pubX, + Y: pubY, + } + + // Verify ECDSA Signature represented by r and s + ok := ecdsa.Verify(ecdsa_ak_pub, digest[:], r, s) + if !ok { + msg := "Failed to verify ISV Enclave report signature" + result.SignCheck.setFalse(&msg) + return result, false + } + log.Trace("Successfully verified ISV Enclave report signature") + result.SignCheck.Success = true + + // Step 2: Verify QE Report Signature + + // get QE Report from QE Report Signature Data + if quoteType == SGX_QUOTE_TYPE { + digest = sha256.Sum256(reportRaw[SGX_QUOTE_SIGNATURE_OFFSET+128 : SGX_QUOTE_SIGNATURE_OFFSET+128+SGX_QUOTE_BODY_SIZE]) + } else { + digest = sha256.Sum256(reportRaw[TDX_QUOTE_SIGNATURE_OFFSET+128 : TDX_QUOTE_SIGNATURE_OFFSET+128+TDX_QUOTE_BODY_SIZE]) + } + + // extract r and s from ECDSA QEReportSignature + r.SetBytes(quoteSignature.QEReportSignature[:32]) + s.SetBytes(quoteSignature.QEReportSignature[32:]) + + // Extract the PCK public key from the PCK certificate + pck_pub, ok := certs.PCKCert.PublicKey.(*ecdsa.PublicKey) + if pck_pub == nil || !ok { + msg := "Failed to extract PCK public key from certificate" + result.SignCheck.setFalse(&msg) + return result, false + } + + // Verify the ECDSA QEReportSignature + ok = ecdsa.Verify(pck_pub, digest[:], r, s) + if !ok { + msg := "Failed to verify QE report signature" + result.SignCheck.setFalse(&msg) + return result, false + } + + log.Trace("Successfully verified QE report signature") + result.SignCheck.Success = true + + // Step 3: Verify Report Data: SHA256(ECDSA Attestation Key || QE Authentication Data) || 32-0x00’s) + reportData := quoteSignature.QEReport.ReportData + hash_ref := sha256.Sum256(append(ak_pub[:], quoteSignature.QEAuthData...)) + reportDataRef := append(hash_ref[:], make([]byte, 32)...) + + if !bytes.Equal(reportData[:], reportDataRef[:]) { + msg := fmt.Sprintf("invalid SHA256(ECDSA Attestation Key || QE Authentication Data) || 32*0x00) in QEReport.ReportData. expected: %v, got: %v\n", reportDataRef, reportData) + result.CertChainCheck.setFalse(&msg) + return result, false + } + + // Step 4: Verify the SGX certificate chain + x509Chains, err := internal.VerifyCertChain([]*x509.Certificate{certs.PCKCert, certs.IntermediateCert}, + []*x509.Certificate{certs.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) + result.CertChainCheck.setFalse(&msg) + return result, false + } + + // Step 5: Verify that the reference value fingerprint matches the certificate fingerprint + refFingerprint, err := hex.DecodeString(fingerprint) + if err != nil { + msg := fmt.Sprintf("Failed to decode CA fingerprint %v: %v", fingerprint, err) + result.CertChainCheck.setFalse(&msg) + return result, false + } + caFingerprint := sha256.Sum256(certs.RootCACert.Raw) + if !bytes.Equal(refFingerprint, caFingerprint[:]) { + msg := fmt.Sprintf("CA fingerprint %v does not match measurement CA fingerprint %v", + fingerprint, hex.EncodeToString(caFingerprint[:])) + result.CertChainCheck.setFalse(&msg) + return result, false + } + result.CertChainCheck.Success = true + + //Step 6: Store details from (all) validated certificate chain(s) in the report + for _, chain := range x509Chains { + chainExtracted := []X509CertExtracted{} + for _, cert := range chain { + chainExtracted = append(chainExtracted, ExtractX509Infos(cert)) + } + result.ValidatedCerts = append(result.ValidatedCerts, chainExtracted) + } + + return result, true +} + +func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate) error { + if tcbInfo == nil || tcbKeyCert == nil { + return fmt.Errorf("invalid function parameter (null pointer exception)") + } + + regex := regexp.MustCompile(`\s+`) + regex.ReplaceAllString(tcbInfoBodyRaw, "") // remove whitespace + tcbInfoBodyRaw = tcbInfoBodyRaw[11 : len(tcbInfoBodyRaw)-128-16] // remove "{"tcbInfo":" from beginning and signature + rest from the end + + // get checksum of tcb info body + digest := sha256.Sum256([]byte(tcbInfoBodyRaw)) + + // get signature + r := new(big.Int) + s := new(big.Int) + r.SetBytes(tcbInfo.Signature[:32]) + s.SetBytes(tcbInfo.Signature[32:]) + + pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("failed to extract public key from certificate") + } + + // verify signature + ok = ecdsa.Verify(pub_key, digest[:], r, s) + if !ok { + return fmt.Errorf("failed to verify tcbInfo signature") + } + + now := time.Now() + + if now.After(tcbInfo.TcbInfo.NextUpdate) { + return fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) + } + + return nil +} + +// Only for SGX report body +func VerifyEnclaveReportValues(body *EnclaveReportBody, tcbInfo *TcbInfo, + certs *SgxCertificates, sgxReferenceValue *ReferenceValue, result *SgxMeasurementResult) error { + if body == nil || tcbInfo == nil || certs == nil || sgxReferenceValue == nil || result == nil { + return fmt.Errorf("invalid function parameter (null pointer exception)") + } + + // Parse and verify PCK certificate extensions + sgxExtensions, err := ParseSGXExtensions(certs.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant) + if err != nil { + return fmt.Errorf("failed to parse SGX Extensions from PCK Certificate: %v", err) + } + + if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { + return fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) + } + + if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { + return fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) + } + + // check MRENCLAVE reference value + if !reflect.DeepEqual(body.MRENCLAVE[:], []byte(sgxReferenceValue.Sha256)) { + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: sgxReferenceValue.Name, + Digest: hex.EncodeToString(sgxReferenceValue.Sha256[:]), + Success: false, + }) + return fmt.Errorf("MRENCLAVE mismatch. Expected: %v, Got. %v", sgxReferenceValue.Sha256, body.MRENCLAVE) + } else { + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: sgxReferenceValue.Name, + Digest: hex.EncodeToString(body.MRENCLAVE[:]), + Success: true, + }) + } + + // check attributes + attributes_quote := body.Attributes + + if !reflect.DeepEqual(sgxReferenceValue.Sgx.Attributes[:], attributes_quote[:]) { + return fmt.Errorf("attributes mismatch. Expected: %v, Got: %v", sgxReferenceValue.Sgx.Attributes, attributes_quote) + } + + // check mrsigner value + refSigner, err := hex.DecodeString(sgxReferenceValue.Sgx.MRSIGNER) + if err != nil { + return fmt.Errorf("decoding MRSIGNER reference value failed: %v", err) + } + + if !reflect.DeepEqual(refSigner, body.MRSIGNER[:]) { + return fmt.Errorf("MRSIGNER mismatch. Expected: %v, Got: %v", []byte(sgxReferenceValue.Sgx.MRSIGNER), body.MRSIGNER) + } + + // check isvProdId value + if body.ISVProdID != sgxReferenceValue.Sgx.IsvProdId { + return fmt.Errorf("IsvProdId mismatch. Expected: %v, Got: %v", sgxReferenceValue.Sgx.IsvProdId, body.ISVProdID) + } + + // TODO: check CPUSVN from report body + // TODO: check TCB level (call GetTcbStatusEnclave): + // - Check if TCB Level status is OutOfDate/REVOKED/ConfigurationNeeded/ConfigurationAndSWHardeningNeeded/UpToDate/SWHardeningNeeded/OutOfDateConfigurationNeeded/TCB Level error status is unrecognized + // - handle result of TCB Status based on policy + return nil +} + +// verify QE Identity and compare the values to the QE +// Can be used by SGX/TDX +func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, qeIdentityBodyRaw string, tcbKeyCert *x509.Certificate, teeType uint32) error { + if qeReportBody == nil || qeIdentity == nil || tcbKeyCert == nil { + return fmt.Errorf("invalid function parameter (null pointer exception)") + } + + regex := regexp.MustCompile(`\s+`) + regex.ReplaceAllString(qeIdentityBodyRaw, "") // remove whitespace + qeIdentityBodyRaw = qeIdentityBodyRaw[19 : len(qeIdentityBodyRaw)-128-16] // remove "{"enclaveIdentity":" from beginning and signature + rest from the end + + // get checksum of qe identity body + digest := sha256.Sum256([]byte(qeIdentityBodyRaw)) + + // get signature + r := new(big.Int) + s := new(big.Int) + r.SetBytes(qeIdentity.Signature[:32]) + s.SetBytes(qeIdentity.Signature[32:]) + + pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("failed to extract public key from certificate") + } + + // verify signature + ok = ecdsa.Verify(pub_key, digest[:], r, s) + if !ok { + return fmt.Errorf("failed to verify QE Identity signature") + } + + now := time.Now() + + if now.After(qeIdentity.EnclaveIdentity.NextUpdate) { + return fmt.Errorf("qeIdentity has expired since: %v", qeIdentity.EnclaveIdentity.NextUpdate) + } + + // from Intel SGX DCAP Library + if teeType == TDX_QUOTE_TYPE { + if qeIdentity.EnclaveIdentity.Version == 1 { + return fmt.Errorf("enclave Identity version 1 is invalid for TDX TEE") + } else if qeIdentity.EnclaveIdentity.Version == 2 { + if qeIdentity.EnclaveIdentity.Id != TD_QE { + return fmt.Errorf("enclave Identity is not generated for TDX and does not match Quote's TEE") + } + } + } else if teeType == SGX_QUOTE_TYPE { + if qeIdentity.EnclaveIdentity.Id != QE { + return fmt.Errorf("enclave Identity is not generated for SGX and does not match Quote's TEE") + } + } else { + return fmt.Errorf("unknown Quote's TEE. Enclave Identity cannot be valid") + } + + miscselect_qe_identity := binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.Miscselect) + miscselectMask_qe_identity := binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.MiscselectMask) + + if qeReportBody.MISCSELECT != (miscselect_qe_identity & miscselectMask_qe_identity) { + return fmt.Errorf("miscSelect value from Enclave Report: %v does not match miscSelect value from QE Identity: %v", + qeReportBody.MISCSELECT, (miscselect_qe_identity & miscselectMask_qe_identity)) + } + + // check attributes + attributes_quote := qeReportBody.Attributes + attributes_mask := qeIdentity.EnclaveIdentity.AttributesMask + + if len(attributes_mask) == len(attributes_quote) { + for i := range attributes_quote { + attributes_quote[i] &= attributes_mask[i] + } + } + + if !reflect.DeepEqual([]byte(qeIdentity.EnclaveIdentity.Attributes), attributes_quote[:]) { + return fmt.Errorf("attributes mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Attributes, attributes_quote) + } + + // check mrsigner value + mrsigner_quote := qeReportBody.MRSIGNER + mrsigner_qe_identity := qeIdentity.EnclaveIdentity.Mrsigner + if mrsigner_qe_identity != nil && + !reflect.DeepEqual([]byte(mrsigner_qe_identity), mrsigner_quote[:]) { + return fmt.Errorf("MRSIGNER mismatch. Expected: %v, Got: %v", mrsigner_qe_identity, mrsigner_quote) + } + + // check isvProdId value + if qeReportBody.ISVProdID != uint16(qeIdentity.EnclaveIdentity.IsvProdId) { + return fmt.Errorf("IsvProdId mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.IsvProdId, qeReportBody.ISVProdID) + } + + // TODO: return a corresponding result based on a policy. + switch GetTcbStatusQE(qeIdentity, qeReportBody) { + case Revoked: + log.Tracef("Value of tcbStatus for the selected Enclave's Identity tcbLevel (isvSvn: %v) is 'Revoked'", qeReportBody.ISVSVN) + case OutOfDate: + log.Tracef("Value of tcbStatus for the selected Enclave's Identity tcbLevel (isvSvn: %v) is 'OutOfDate'", qeReportBody.ISVSVN) + default: + } + + return nil +} + +func GetTcbStatusQE(qeIdentity *QEIdentity, body *EnclaveReportBody) TcbStatus { + for _, level := range qeIdentity.EnclaveIdentity.TcbLevels { + if uint16(level.Tcb.Isvsvn) <= body.ISVSVN { + return level.TcbStatus + } + } + return Revoked +} + +// TODO (important): implement this function (from DCAP QuoteVerifier.cpp: function checkTcbLevel()) +func GetTcbStatusEnclave(tcb_info *TcbInfo, quote *SgxReport, sgxExtensions SGXExtensionsValue) TcbStatus { + //tcbLevel := getMatchingTcbLevel(tcb_info, quote, sgxExtensions) + return Revoked +} + +// Check if CRL parameters are valid and check if the certificate has been revoked +func CrlCheck(crl *x509.RevocationList, cert *x509.Certificate, parentCert *x509.Certificate) (bool, error) { + + if cert == nil || crl == nil { + return false, fmt.Errorf("certificate or revocation null pointer exception") + } + + // Check CRL signature + err := crl.CheckSignatureFrom(parentCert) + if err != nil { + return false, fmt.Errorf("CRL signature is invalid: %v", err) + } + + // Check if CRL is up to date + now := time.Now() + if now.After(crl.NextUpdate) { + return false, fmt.Errorf("CRL has expired since: %v", crl.NextUpdate) + } + + if !reflect.DeepEqual(crl.RawIssuer, cert.RawIssuer) { + return false, fmt.Errorf("CRL RawIssuer is invalid. got: %v, expected: %v ", crl.RawIssuer, cert.RawIssuer) + } + + // Check if certificate has been revoked + for _, revokedCert := range crl.RevokedCertificates { + if cert.SerialNumber == revokedCert.SerialNumber { + return false, fmt.Errorf("certificate has been revoked since: %v", revokedCert.RevocationTime) + } + } + + return true, nil +} + +// fetch the CRL either from cache or download it from PCS +// TODO (important): implement dedicated update mechanism (currently only retrieved once from PCS and stored on the filesystem for testing) +func fetchCRL(uri string, name string) (*x509.RevocationList, error) { + _, err := os.Stat(name) + if err == nil { + fmt.Printf("File %s exists.\n", name) + // Read CRL + crl_raw, err := os.ReadFile(name) + if err != nil { + return nil, fmt.Errorf("failed to read quote.dat: %w", err) + } + // Parse CRL + crl, err := x509.ParseRevocationList(crl_raw) + if err != nil { + return nil, err + } + return crl, nil + } else if os.IsNotExist(err) { + return downloadCRL(uri, name) + } else { + return nil, err + } +} + +func downloadCRL(uri string, name string) (*x509.RevocationList, error) { + // Download CRL from PCS + resp, err := http.Get(uri) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("request failed with status code: %d", resp.StatusCode) + } + + // Read response body into a byte slice + crlData, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + // Store CRL in file + err = os.WriteFile(name, crlData, 0644) + if err != nil { + return nil, err + } + // Parse CRL + crl, err := x509.ParseRevocationList(crlData) + if err != nil { + return nil, err + } + + return crl, nil +} diff --git a/attestationreport/sgx_structs.go b/attestationreport/sgx_structs.go new file mode 100644 index 00000000..1cb62d0e --- /dev/null +++ b/attestationreport/sgx_structs.go @@ -0,0 +1,511 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attestationreport + +import ( + "bytes" + "crypto/x509" + "encoding/asn1" + "encoding/binary" + "encoding/json" + "fmt" + "time" + + "github.com/Fraunhofer-AISEC/cmc/internal" +) + +type EnclaveID string +type TcbStatus string + +const ( + QE EnclaveID = "QE" + QVE EnclaveID = "QVE" + TD_QE EnclaveID = "TD_QE" + + UpToDate TcbStatus = "UpToDate" + ConfigurationNeeded TcbStatus = "ConfigurationNeeded" + OutOfDate TcbStatus = "OutOfDate" + OutOfDateConfigurationNeeded TcbStatus = "OutOfDateConfigurationNeeded" + Revoked TcbStatus = "Revoked" +) + +// Overall structure: table 2 from https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf +// Endianess: Little Endian (all Integer fields) +type SgxReport struct { + QuoteHeader QuoteHeader + ISVEnclaveReport EnclaveReportBody + QuoteSignatureDataLen uint32 + QuoteSignatureData ECDSA256QuoteSignatureDataStructure // variable size +} + +// Quote Header: Table 3 from https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf +// 48 bytes +type QuoteHeader struct { + Version uint16 + AttestationKeyType uint16 // 2: ECDSA-256-with-P-256 curve + TeeType uint32 // value from dcap library (not in documentation) + QESVN uint16 + PCESVN uint16 + QEVendorID [16]byte + UserData [20]byte +} + +// Enclave Report Body: Table 5 from https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf +// 384 bytes +type EnclaveReportBody struct { + CPUSVN [16]byte + MISCSELECT uint32 + Reserved1 [28]byte + Attributes [16]byte + MRENCLAVE [32]byte + Reserved2 [32]byte + MRSIGNER [32]byte + Reserved3 [96]byte + ISVProdID uint16 + ISVSVN uint16 + Reserved4 [60]byte + ReportData [64]byte +} + +// table 4: https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf +type ECDSA256QuoteSignatureDataStructure struct { + ISVEnclaveReportSignature [64]byte + ECDSAAttestationKey [64]byte + QEReport EnclaveReportBody + QEReportSignature [64]byte + + QEAuthDataSize uint16 + QEAuthData []byte + + QECertDataType uint16 + QECertDataSize uint32 + QECertData []byte +} + +// from Intel SGX DCAP library: AttestationParsers.h +type TcbInfo struct { + TcbInfo TcbInfoBody `json:"tcbInfo"` + Signature HexByte `json:"signature"` +} + +type TcbInfoBody struct { + Id string `json:"id"` + Version uint32 `json:"version"` + IssueDate time.Time `json:"issueDate"` + NextUpdate time.Time `json:"nextUpdate"` + Fmspc HexByte `json:"fmspc"` + PceId HexByte `json:"pceId"` + TcbType uint32 `json:"tcbType"` + TcbEvaluationDataNumber uint32 `json:"tcbEvaluationDataNumber"` + TcbLevels []TcbLevel `json:"tcbLevels"` + + // Get properties of Intel’s TDX SEAM module + TdxModule TdxModule `json:"tdxModule"` // Only required for TDX +} + +type TcbLevel struct { + Tcb struct { + SgxTcbComponents []TcbComponent `json:"sgxTcbComponents"` + TdxTcbComponents []TcbComponent `json:"tdxTcbComponents"` + PceSvn uint32 `json:"pceSvn"` + } `json:"tcb"` + + TcbStatus string `json:"tcbStatus"` + TcbDate time.Time `json:"tcbDate"` + AdvisoryIDs []string `json:"advisoryIDs"` + + // TODO: not sure if these 3 values are entirely correct + Id string `json:"id"` + Version uint32 `json:"version"` + CpuSvnComponents HexByte `json:"cpuSvnComponents"` +} + +type TcbComponent struct { + Svn byte `json:"svn"` + Category string `json:"category"` + Type string `json:"type"` +} + +// TODO: not sure if this is correct +type TdxModule struct { + Mrsigner HexByte `json:"mrsigner"` + Attributes HexByte `json:"attributes"` + AttributesMask HexByte `json:"attributesMask"` +} + +type SgxCertificates struct { + RootCACert *x509.Certificate + IntermediateCert *x509.Certificate // Processor or Platform + PCKCert *x509.Certificate + TCBSigningCert *x509.Certificate +} + +type QEIdentity struct { + EnclaveIdentity QEIdentityBody `json:"enclaveIdentity"` + Signature HexByte `json:"signature"` +} + +type QEIdentityBody struct { + Id EnclaveID `json:"id"` + Version uint32 `json:"version"` + IssueDate time.Time `json:"issueDate"` + NextUpdate time.Time `json:"nextUpdate"` + TcbEvaluationDataNumber uint32 `json:"tcbEvaluationDataNumber"` + Miscselect HexByte `json:"miscselect"` + MiscselectMask HexByte `json:"miscselectMask"` + Attributes HexByte `json:"attributes"` + AttributesMask HexByte `json:"attributesMask"` + Mrsigner HexByte `json:"mrsigner"` + IsvProdId uint32 `json:"isvprodid"` + TcbLevels []TcbLevelEnclaveId `json:"tcbLevels"` +} + +type TcbLevelEnclaveId struct { + Tcb struct { + Isvsvn uint32 `json:"isvsvn"` + } `json:"tcb"` + TcbDate time.Time `json:"tcbDate"` + TcbStatus TcbStatus `json:"tcbStatus"` + AdvisoryIDs []string `json:"advisoryIDs"` +} + +// ------------------------- start SGX Extensions ------------------------- +// asn1 encoded data structure from pck certificate +type SGXExtensionsWrapper struct { + Value SGXExtensionsValue +} + +type SGXExtensionsValue struct { + // required: + Ppid PPID + Tcb TCB + PceId PCEID + Fmspc FMSPC + SgxType SGXTYPE + + //optional: + PlatformInstanceId PlatformInstanceId + Configuration Configuration +} + +type PPID struct { + Id asn1.ObjectIdentifier + Value []byte +} + +type TCB struct { + Id asn1.ObjectIdentifier + Value struct { + Comp_01 TCBComp + Comp_02 TCBComp + Comp_03 TCBComp + Comp_04 TCBComp + Comp_05 TCBComp + Comp_06 TCBComp + Comp_07 TCBComp + Comp_08 TCBComp + Comp_09 TCBComp + Comp_10 TCBComp + Comp_11 TCBComp + Comp_12 TCBComp + Comp_13 TCBComp + Comp_14 TCBComp + Comp_15 TCBComp + Comp_16 TCBComp + PceSvn TCBComp + CpuSvn struct { + Svn asn1.ObjectIdentifier + Value []byte + } + } +} + +type TCBComp struct { + Svn asn1.ObjectIdentifier + Value int +} + +type PCEID struct { + Id asn1.ObjectIdentifier + Value []byte +} + +type FMSPC struct { + Id asn1.ObjectIdentifier + Value []byte +} + +type SGXTYPE struct { + Id asn1.ObjectIdentifier + Value asn1.Enumerated +} + +type PlatformInstanceId struct { + Id asn1.ObjectIdentifier + Value []byte +} + +type Configuration struct { + Id asn1.ObjectIdentifier + Value []struct { + ConfigurationId asn1.ObjectIdentifier + ConfigurationValue ConfigurationValue + } +} + +type ConfigurationValue struct { + DynamicPlatform bool + CachedKeys bool + SMTEnabled bool +} + +// ------------------------- end SGX Extensions ------------------------- + +// expects enclave Identity structure in JSON format +func ParseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { + var sgx_extensions SGXExtensionsValue + + rest, err := asn1.Unmarshal(extensions, &sgx_extensions.Ppid) + if err != nil { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions PPID %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.Tcb) + if err != nil || len(rest) == 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions TCB %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.PceId) + if err != nil || len(rest) == 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions PCEID %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.Fmspc) + if err != nil || len(rest) == 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions FMSPC %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.SgxType) + if err != nil { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions SGXTYPE %v", err) + } else if len(rest) > 0 { + // parse optional parameters + rest, err = asn1.Unmarshal(rest, &sgx_extensions.PlatformInstanceId) + if err != nil || len(rest) == 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions TCB %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.Configuration) + if err != nil || len(rest) != 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions TCB %v", err) + } + } + + return sgx_extensions, nil +} + +func GetTCBCompByIndex(tcb TCB, index int) (TCBComp, error) { + switch index { + case 1: + return tcb.Value.Comp_01, nil + case 2: + return tcb.Value.Comp_02, nil + case 3: + return tcb.Value.Comp_03, nil + case 4: + return tcb.Value.Comp_04, nil + case 5: + return tcb.Value.Comp_05, nil + case 6: + return tcb.Value.Comp_06, nil + case 7: + return tcb.Value.Comp_07, nil + case 8: + return tcb.Value.Comp_08, nil + case 9: + return tcb.Value.Comp_09, nil + case 10: + return tcb.Value.Comp_10, nil + case 11: + return tcb.Value.Comp_11, nil + case 12: + return tcb.Value.Comp_12, nil + case 13: + return tcb.Value.Comp_13, nil + case 14: + return tcb.Value.Comp_14, nil + case 15: + return tcb.Value.Comp_15, nil + case 16: + return tcb.Value.Comp_16, nil + default: + return TCBComp{}, fmt.Errorf("invalid index") + } +} + +// expects enclave Identity structure in JSON format +func ParseQEIdentity(qeIdentityJson []byte) (QEIdentity, error) { + var qe_identity QEIdentity + err := json.Unmarshal(qeIdentityJson, &qe_identity) + if err != nil { + return QEIdentity{}, fmt.Errorf("failed to decode Enclave Identity %v", err) + } + return qe_identity, nil +} + +// expects tcb Info in JSON format +func ParseTcbInfo(tcbInfoJson []byte) (TcbInfo, error) { + var tcbInfo TcbInfo + err := json.Unmarshal(tcbInfoJson, &tcbInfo) + if err != nil { + return TcbInfo{}, fmt.Errorf("failed to decode TcbInfo %v", err) + } + return tcbInfo, nil +} + +// parse PEM/DER formatted certificates into a SgxCertificates struct +func ParseCertificates(certsRaw any, pem bool) (SgxCertificates, error) { + var certChain SgxCertificates + var certs []*x509.Certificate + var err error + + switch t := certsRaw.(type) { + case []byte: + if pem { + certs, err = internal.ParseCertsPem(t) + } else { + certs, err = internal.ParseCertsDer(t) + } + if err != nil { + return SgxCertificates{}, fmt.Errorf("failed to parse PEM certificates %v", err) + } + case [][]byte: + if pem { + certs, err = internal.ParseCertsPem(t) + } else { + certs, err = internal.ParseCertsDer(t) + } + if err != nil { + return SgxCertificates{}, fmt.Errorf("failed to parse DER certificates %v", err) + } + default: + return SgxCertificates{}, fmt.Errorf("ParseCertificates not implemented for type %T", certsRaw) + } + + // TODO: identify based on subject key identifier + for _, v := range certs { + switch v.Subject.CommonName { + case "Intel SGX PCK Certificate": + certChain.PCKCert = v + case "Intel SGX PCK Processor CA": + certChain.IntermediateCert = v + case "Intel SGX PCK Platform CA": + certChain.IntermediateCert = v + case "Intel SGX Root CA": + certChain.RootCACert = v + case "Intel SGX TCB Signing": + certChain.TCBSigningCert = v + } + } + + return certChain, nil +} + +// Parses the report into the SgxReport structure +func DecodeSgxReport(report []byte) (SgxReport, error) { + var reportStruct SgxReport + var header QuoteHeader + var body EnclaveReportBody + var sig ECDSA256QuoteSignatureDataStructure + var sigLen uint32 + + // parse header + buf := bytes.NewBuffer(report) + err := binary.Read(buf, binary.LittleEndian, &header) + if err != nil { + return SgxReport{}, fmt.Errorf("failed to decode SGX report header: %v", err) + } + + // parse body + err = binary.Read(buf, binary.LittleEndian, &body) + if err != nil { + return SgxReport{}, fmt.Errorf("failed to decode SGX report body: %v", err) + } + + // parse signature size + err = binary.Read(buf, binary.LittleEndian, &sigLen) + if err != nil { + return SgxReport{}, fmt.Errorf("failed to decode SGX report QuoteSignatureDataLen: %v", err) + } + + // parse signature + err = parseECDSASignature(buf, &sig) + if err != nil { + return SgxReport{}, fmt.Errorf("failed to decode SGX report ECDSA256QuotesignatureDataStructure: %v", err) + } + + // compose the final report struct + reportStruct.QuoteHeader = header + reportStruct.ISVEnclaveReport = body + reportStruct.QuoteSignatureDataLen = sigLen + reportStruct.QuoteSignatureData = sig + + return reportStruct, nil +} + +// parses quote signature data structure from buf to sig +func parseECDSASignature(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStructure) error { + + err := binary.Read(buf, binary.LittleEndian, &sig.ISVEnclaveReportSignature) + if err != nil { + return fmt.Errorf("failed to parse ISVEnclaveReportSignature") + } + err = binary.Read(buf, binary.LittleEndian, &sig.ECDSAAttestationKey) + if err != nil { + return fmt.Errorf("failed to parse ECDSAAttestationKey") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QEReport) + if err != nil { + return fmt.Errorf("failed to parse QEReport") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QEReportSignature) + if err != nil { + return fmt.Errorf("failed to parse QEReportSignature") + } + + err = binary.Read(buf, binary.LittleEndian, &sig.QEAuthDataSize) + if err != nil { + return fmt.Errorf("failed to parse QEAuthDataSize") + } + tmp := make([]byte, sig.QEAuthDataSize) + err = binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QEAuthData") + } + sig.QEAuthData = tmp + + err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataType) + if err != nil { + return fmt.Errorf("failed to parse QECertDataType") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataSize) + if err != nil { + return fmt.Errorf("failed to parse QECertDataSize") + } + tmp = make([]byte, sig.QECertDataSize) + binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QECertData") + } + sig.QECertData = tmp + + return nil +} diff --git a/attestationreport/sgx_test.go b/attestationreport/sgx_test.go new file mode 100644 index 00000000..ab16a776 --- /dev/null +++ b/attestationreport/sgx_test.go @@ -0,0 +1,711 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attestationreport + +import ( + "encoding/hex" + "fmt" + "reflect" + "testing" +) + +var ( + validSGXQuote = []byte{ + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, + 0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, + 0x95, 0x7f, 0x06, 0x07, 0x18, 0xb9, 0x7a, 0xf8, 0xc1, 0x57, 0xbf, 0x7f, + 0x6a, 0xad, 0xe5, 0xa7, 0xc5, 0x02, 0x02, 0xf1, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x75, 0x5c, 0xd5, 0x7f, 0x2c, 0x04, 0xb8, + 0x45, 0xc4, 0x91, 0x25, 0x77, 0xd6, 0x9a, 0xf3, 0xf9, 0x6c, 0xb7, 0xe3, + 0xea, 0xda, 0x57, 0xd1, 0xf9, 0xa7, 0xc6, 0x25, 0xf6, 0xa6, 0x23, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xe0, 0x54, 0x3f, + 0x55, 0x97, 0xb0, 0xf0, 0xe0, 0x28, 0xfa, 0x18, 0x95, 0x5e, 0x13, 0x07, + 0xcb, 0x7a, 0x8c, 0xf5, 0x4b, 0x37, 0xf5, 0x13, 0xff, 0x64, 0x96, 0x1e, + 0xad, 0xef, 0x94, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x10, 0x00, 0x00, 0xc7, 0x23, 0x4a, 0x7f, 0x53, 0x80, 0x58, 0xf5, + 0x9f, 0x51, 0xa1, 0x27, 0x12, 0x08, 0xc2, 0x90, 0xc6, 0xb9, 0xd0, 0xe9, + 0x34, 0x07, 0x19, 0xf3, 0x48, 0xa7, 0x6d, 0x4f, 0x62, 0xc0, 0x17, 0x24, + 0x7d, 0x35, 0xb6, 0x9d, 0x34, 0x04, 0x70, 0xdc, 0x3b, 0x80, 0xb0, 0x3e, + 0xa5, 0x6b, 0x4e, 0x25, 0x75, 0x39, 0x3e, 0xa8, 0x79, 0xe3, 0x75, 0x64, + 0x39, 0x7f, 0x54, 0x76, 0x4f, 0xa7, 0x74, 0xf7, 0x00, 0x36, 0x30, 0x58, + 0xf5, 0x43, 0x51, 0xeb, 0xb7, 0x5b, 0xdd, 0x00, 0x86, 0x73, 0x26, 0x2c, + 0x58, 0x56, 0xb0, 0x5d, 0xe5, 0xee, 0x23, 0xfe, 0xba, 0x84, 0xe3, 0x55, + 0xfc, 0xeb, 0x30, 0x02, 0x5f, 0xe1, 0xa7, 0x63, 0x58, 0xef, 0x53, 0x02, + 0xfb, 0x04, 0xf4, 0x1d, 0x6b, 0x01, 0xcb, 0x21, 0x73, 0xc3, 0x4e, 0x80, + 0x50, 0x86, 0xe2, 0xb7, 0xe1, 0xcd, 0xd8, 0x95, 0x9e, 0x98, 0x2c, 0xe4, + 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x2a, 0xa5, 0x0c, 0xe1, 0xc0, 0xce, 0xf0, + 0x3c, 0xcf, 0x89, 0xe7, 0xb5, 0xb1, 0x6b, 0x0d, 0x79, 0x78, 0xf5, 0xc2, + 0xb1, 0xed, 0xcf, 0x77, 0x4d, 0x87, 0x70, 0x2e, 0x81, 0x54, 0xd8, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x4f, 0x57, 0x75, + 0xd7, 0x96, 0x50, 0x3e, 0x96, 0x13, 0x7f, 0x77, 0xc6, 0x8a, 0x82, 0x9a, + 0x00, 0x56, 0xac, 0x8d, 0xed, 0x70, 0x14, 0x0b, 0x08, 0x1b, 0x09, 0x44, + 0x90, 0xc5, 0x7b, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x4a, 0x3c, 0xb5, + 0x08, 0x32, 0x9b, 0xab, 0xfe, 0xd9, 0x56, 0x2d, 0x68, 0x8f, 0xba, 0xfa, + 0x32, 0x30, 0x54, 0x52, 0xdb, 0x83, 0x28, 0xfa, 0x9f, 0xa5, 0x86, 0x4d, + 0x12, 0x70, 0xc8, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x04, 0xdd, 0x19, 0x00, 0x1e, 0x58, 0xcb, 0x1a, 0xf4, 0xa1, 0x1f, + 0xab, 0xf8, 0xb7, 0x62, 0xe0, 0x4b, 0x67, 0x63, 0x0f, 0xa7, 0xa7, 0x68, + 0xc3, 0x49, 0xfc, 0x86, 0xd5, 0x1f, 0x53, 0xd5, 0x0c, 0x3c, 0x7f, 0x11, + 0xcf, 0xca, 0xae, 0x69, 0xc9, 0xf6, 0x66, 0xb3, 0x1f, 0x8d, 0xaa, 0x7f, + 0xa6, 0xd4, 0xc3, 0x37, 0xb4, 0xc7, 0x9b, 0x5f, 0x38, 0x4d, 0xd5, 0x05, + 0x75, 0xc0, 0x0a, 0xd7, 0x20, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x05, 0x00, 0xdc, 0x0d, 0x00, 0x00, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, + 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, + 0x4d, 0x49, 0x49, 0x45, 0x6a, 0x44, 0x43, 0x43, 0x42, 0x44, 0x4b, 0x67, + 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x55, 0x63, 0x66, 0x6b, + 0x58, 0x56, 0x68, 0x49, 0x61, 0x64, 0x6f, 0x76, 0x50, 0x75, 0x55, 0x46, + 0x6a, 0x5a, 0x6c, 0x32, 0x2b, 0x63, 0x71, 0x44, 0x48, 0x78, 0x59, 0x77, + 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, + 0x41, 0x77, 0x49, 0x77, 0x0a, 0x63, 0x54, 0x45, 0x6a, 0x4d, 0x43, 0x45, + 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x61, 0x53, 0x57, 0x35, + 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x42, + 0x44, 0x53, 0x79, 0x42, 0x51, 0x63, 0x6d, 0x39, 0x6a, 0x5a, 0x58, 0x4e, + 0x7a, 0x62, 0x33, 0x49, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, + 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x0a, 0x45, 0x55, + 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x63, 0x6e, + 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, + 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, + 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, + 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, + 0x55, 0x45, 0x0a, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, + 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, + 0x6c, 0x56, 0x54, 0x4d, 0x42, 0x34, 0x58, 0x44, 0x54, 0x49, 0x7a, 0x4d, + 0x44, 0x51, 0x78, 0x4f, 0x54, 0x49, 0x78, 0x4d, 0x7a, 0x63, 0x30, 0x4e, + 0x6c, 0x6f, 0x58, 0x44, 0x54, 0x4d, 0x77, 0x4d, 0x44, 0x51, 0x78, 0x4f, + 0x54, 0x49, 0x78, 0x4d, 0x7a, 0x63, 0x30, 0x0a, 0x4e, 0x6c, 0x6f, 0x77, + 0x63, 0x44, 0x45, 0x69, 0x4d, 0x43, 0x41, 0x47, 0x41, 0x31, 0x55, 0x45, + 0x41, 0x77, 0x77, 0x5a, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, + 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x42, 0x44, 0x53, 0x79, 0x42, 0x44, + 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, 0x5a, 0x70, 0x59, 0x32, 0x46, 0x30, + 0x5a, 0x54, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, 0x45, + 0x0a, 0x43, 0x67, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, + 0x67, 0x51, 0x32, 0x39, 0x79, 0x63, 0x47, 0x39, 0x79, 0x59, 0x58, 0x52, + 0x70, 0x62, 0x32, 0x34, 0x78, 0x46, 0x44, 0x41, 0x53, 0x42, 0x67, 0x4e, + 0x56, 0x42, 0x41, 0x63, 0x4d, 0x43, 0x31, 0x4e, 0x68, 0x62, 0x6e, 0x52, + 0x68, 0x49, 0x45, 0x4e, 0x73, 0x59, 0x58, 0x4a, 0x68, 0x4d, 0x51, 0x73, + 0x77, 0x43, 0x51, 0x59, 0x44, 0x0a, 0x56, 0x51, 0x51, 0x49, 0x44, 0x41, + 0x4a, 0x44, 0x51, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, + 0x55, 0x45, 0x42, 0x68, 0x4d, 0x43, 0x56, 0x56, 0x4d, 0x77, 0x57, 0x54, + 0x41, 0x54, 0x42, 0x67, 0x63, 0x71, 0x68, 0x6b, 0x6a, 0x4f, 0x50, 0x51, + 0x49, 0x42, 0x42, 0x67, 0x67, 0x71, 0x68, 0x6b, 0x6a, 0x4f, 0x50, 0x51, + 0x4d, 0x42, 0x42, 0x77, 0x4e, 0x43, 0x41, 0x41, 0x54, 0x35, 0x0a, 0x61, + 0x4c, 0x33, 0x4c, 0x71, 0x58, 0x50, 0x6f, 0x56, 0x5a, 0x45, 0x32, 0x4b, + 0x64, 0x2f, 0x32, 0x70, 0x6c, 0x62, 0x6a, 0x42, 0x69, 0x48, 0x68, 0x33, + 0x2f, 0x42, 0x53, 0x4c, 0x7a, 0x39, 0x57, 0x49, 0x4e, 0x62, 0x39, 0x4e, + 0x70, 0x38, 0x53, 0x4c, 0x47, 0x37, 0x58, 0x51, 0x63, 0x6b, 0x51, 0x59, + 0x38, 0x41, 0x68, 0x36, 0x32, 0x35, 0x71, 0x42, 0x58, 0x69, 0x67, 0x78, + 0x4a, 0x56, 0x34, 0x0a, 0x65, 0x57, 0x44, 0x55, 0x61, 0x2b, 0x67, 0x67, + 0x65, 0x47, 0x77, 0x43, 0x74, 0x52, 0x4f, 0x72, 0x39, 0x69, 0x5a, 0x48, + 0x6f, 0x34, 0x49, 0x43, 0x70, 0x7a, 0x43, 0x43, 0x41, 0x71, 0x4d, 0x77, + 0x48, 0x77, 0x59, 0x44, 0x56, 0x52, 0x30, 0x6a, 0x42, 0x42, 0x67, 0x77, + 0x46, 0x6f, 0x41, 0x55, 0x30, 0x4f, 0x69, 0x71, 0x32, 0x6e, 0x58, 0x58, + 0x2b, 0x53, 0x35, 0x4a, 0x46, 0x35, 0x67, 0x38, 0x0a, 0x65, 0x78, 0x52, + 0x6c, 0x30, 0x4e, 0x58, 0x79, 0x57, 0x55, 0x30, 0x77, 0x62, 0x41, 0x59, + 0x44, 0x56, 0x52, 0x30, 0x66, 0x42, 0x47, 0x55, 0x77, 0x59, 0x7a, 0x42, + 0x68, 0x6f, 0x46, 0x2b, 0x67, 0x58, 0x59, 0x5a, 0x62, 0x61, 0x48, 0x52, + 0x30, 0x63, 0x48, 0x4d, 0x36, 0x4c, 0x79, 0x39, 0x68, 0x63, 0x47, 0x6b, + 0x75, 0x64, 0x48, 0x4a, 0x31, 0x63, 0x33, 0x52, 0x6c, 0x5a, 0x48, 0x4e, + 0x6c, 0x0a, 0x63, 0x6e, 0x5a, 0x70, 0x59, 0x32, 0x56, 0x7a, 0x4c, 0x6d, + 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x4c, 0x6d, 0x4e, 0x76, 0x62, 0x53, + 0x39, 0x7a, 0x5a, 0x33, 0x67, 0x76, 0x59, 0x32, 0x56, 0x79, 0x64, 0x47, + 0x6c, 0x6d, 0x61, 0x57, 0x4e, 0x68, 0x64, 0x47, 0x6c, 0x76, 0x62, 0x69, + 0x39, 0x32, 0x4e, 0x43, 0x39, 0x77, 0x59, 0x32, 0x74, 0x6a, 0x63, 0x6d, + 0x77, 0x2f, 0x59, 0x32, 0x45, 0x39, 0x0a, 0x63, 0x48, 0x4a, 0x76, 0x59, + 0x32, 0x56, 0x7a, 0x63, 0x32, 0x39, 0x79, 0x4a, 0x6d, 0x56, 0x75, 0x59, + 0x32, 0x39, 0x6b, 0x61, 0x57, 0x35, 0x6e, 0x50, 0x57, 0x52, 0x6c, 0x63, + 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, + 0x67, 0x51, 0x55, 0x53, 0x43, 0x41, 0x70, 0x71, 0x42, 0x54, 0x78, 0x49, + 0x6b, 0x4f, 0x74, 0x44, 0x70, 0x53, 0x74, 0x34, 0x54, 0x34, 0x59, 0x0a, + 0x49, 0x74, 0x54, 0x44, 0x4f, 0x6b, 0x51, 0x77, 0x44, 0x67, 0x59, 0x44, + 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, + 0x41, 0x67, 0x62, 0x41, 0x4d, 0x41, 0x77, 0x47, 0x41, 0x31, 0x55, 0x64, + 0x45, 0x77, 0x45, 0x42, 0x2f, 0x77, 0x51, 0x43, 0x4d, 0x41, 0x41, 0x77, + 0x67, 0x67, 0x48, 0x54, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, + 0x2b, 0x45, 0x30, 0x42, 0x0a, 0x44, 0x51, 0x45, 0x45, 0x67, 0x67, 0x48, + 0x45, 0x4d, 0x49, 0x49, 0x42, 0x77, 0x44, 0x41, 0x65, 0x42, 0x67, 0x6f, + 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, + 0x42, 0x42, 0x42, 0x42, 0x6f, 0x66, 0x79, 0x63, 0x57, 0x79, 0x4c, 0x55, + 0x7a, 0x72, 0x6b, 0x39, 0x4b, 0x52, 0x43, 0x77, 0x48, 0x6e, 0x62, 0x49, + 0x45, 0x4d, 0x49, 0x49, 0x42, 0x59, 0x77, 0x59, 0x4b, 0x0a, 0x4b, 0x6f, + 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x6a, + 0x43, 0x43, 0x41, 0x56, 0x4d, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, + 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, + 0x45, 0x43, 0x41, 0x51, 0x63, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, + 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, + 0x49, 0x43, 0x0a, 0x41, 0x51, 0x63, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, + 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, + 0x67, 0x4d, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, + 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, + 0x67, 0x51, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, + 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x0a, 0x41, 0x51, 0x30, 0x42, + 0x41, 0x67, 0x55, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, + 0x41, 0x67, 0x59, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, + 0x41, 0x67, 0x63, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, + 0x42, 0x41, 0x67, 0x67, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, + 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, + 0x42, 0x41, 0x67, 0x6b, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, + 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, + 0x42, 0x41, 0x67, 0x6f, 0x43, 0x0a, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, + 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x73, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, + 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x77, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, + 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x0a, 0x41, + 0x51, 0x30, 0x42, 0x41, 0x67, 0x30, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, + 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, + 0x51, 0x30, 0x42, 0x41, 0x67, 0x34, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, + 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, + 0x51, 0x30, 0x42, 0x41, 0x67, 0x38, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, + 0x41, 0x59, 0x4c, 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, + 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x41, 0x43, 0x41, 0x51, 0x41, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, + 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x45, 0x43, 0x41, 0x51, 0x30, 0x77, + 0x48, 0x77, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, + 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x49, 0x45, 0x0a, 0x45, 0x41, 0x63, + 0x48, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x77, 0x45, 0x41, 0x59, + 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, + 0x42, 0x41, 0x77, 0x51, 0x43, 0x41, 0x41, 0x41, 0x77, 0x46, 0x41, 0x59, + 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, + 0x42, 0x0a, 0x42, 0x41, 0x51, 0x47, 0x41, 0x48, 0x42, 0x71, 0x45, 0x41, + 0x41, 0x41, 0x4d, 0x41, 0x38, 0x47, 0x43, 0x69, 0x71, 0x47, 0x53, 0x49, + 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x55, 0x4b, 0x41, 0x51, + 0x41, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, + 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, 0x41, 0x41, 0x77, 0x52, 0x51, + 0x49, 0x68, 0x41, 0x4e, 0x47, 0x4f, 0x0a, 0x71, 0x31, 0x50, 0x58, 0x45, + 0x6b, 0x4b, 0x72, 0x76, 0x33, 0x62, 0x47, 0x4e, 0x59, 0x67, 0x43, 0x74, + 0x66, 0x44, 0x77, 0x4c, 0x78, 0x6d, 0x74, 0x2f, 0x75, 0x46, 0x71, 0x55, + 0x62, 0x65, 0x66, 0x46, 0x31, 0x66, 0x6a, 0x48, 0x42, 0x53, 0x31, 0x41, + 0x69, 0x41, 0x69, 0x36, 0x69, 0x52, 0x76, 0x6a, 0x42, 0x31, 0x7a, 0x49, + 0x38, 0x6c, 0x30, 0x31, 0x2b, 0x49, 0x68, 0x6a, 0x39, 0x70, 0x2f, 0x0a, + 0x64, 0x6e, 0x39, 0x51, 0x2f, 0x56, 0x70, 0x4b, 0x52, 0x6b, 0x78, 0x6c, + 0x65, 0x2b, 0x2b, 0x67, 0x74, 0x4d, 0x64, 0x6a, 0x75, 0x51, 0x3d, 0x3d, + 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, + 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6d, + 0x44, 0x43, 0x43, 0x41, 0x6a, 0x36, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, + 0x67, 0x49, 0x56, 0x41, 0x4e, 0x44, 0x6f, 0x71, 0x74, 0x70, 0x31, 0x31, + 0x2f, 0x6b, 0x75, 0x53, 0x52, 0x65, 0x59, 0x50, 0x48, 0x73, 0x55, 0x5a, + 0x64, 0x44, 0x56, 0x38, 0x6c, 0x6c, 0x4e, 0x4d, 0x41, 0x6f, 0x47, 0x43, + 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x0a, + 0x4d, 0x47, 0x67, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, + 0x42, 0x41, 0x4d, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, + 0x49, 0x46, 0x4e, 0x48, 0x57, 0x43, 0x42, 0x53, 0x62, 0x32, 0x39, 0x30, + 0x49, 0x45, 0x4e, 0x42, 0x4d, 0x52, 0x6f, 0x77, 0x47, 0x41, 0x59, 0x44, + 0x56, 0x51, 0x51, 0x4b, 0x44, 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, + 0x62, 0x43, 0x42, 0x44, 0x0a, 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, + 0x68, 0x64, 0x47, 0x6c, 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, + 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, + 0x75, 0x64, 0x47, 0x45, 0x67, 0x51, 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, + 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, + 0x4d, 0x41, 0x6b, 0x4e, 0x42, 0x4d, 0x51, 0x73, 0x77, 0x0a, 0x43, 0x51, + 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, + 0x41, 0x65, 0x46, 0x77, 0x30, 0x78, 0x4f, 0x44, 0x41, 0x31, 0x4d, 0x6a, + 0x45, 0x78, 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, 0x46, 0x77, + 0x30, 0x7a, 0x4d, 0x7a, 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, 0x4d, 0x44, + 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, 0x4d, 0x48, 0x45, 0x78, 0x49, 0x7a, + 0x41, 0x68, 0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x47, + 0x6b, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, 0x48, 0x57, + 0x43, 0x42, 0x51, 0x51, 0x30, 0x73, 0x67, 0x55, 0x48, 0x4a, 0x76, 0x59, + 0x32, 0x56, 0x7a, 0x63, 0x32, 0x39, 0x79, 0x49, 0x45, 0x4e, 0x42, 0x4d, + 0x52, 0x6f, 0x77, 0x47, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x4b, 0x44, + 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x0a, 0x62, 0x43, 0x42, 0x44, + 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, 0x68, 0x64, 0x47, 0x6c, 0x76, + 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x45, + 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, 0x67, + 0x51, 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, + 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, 0x42, + 0x0a, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, + 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, 0x42, 0x5a, 0x4d, 0x42, 0x4d, + 0x47, 0x42, 0x79, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x67, 0x45, + 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x77, 0x45, + 0x48, 0x41, 0x30, 0x49, 0x41, 0x42, 0x4c, 0x39, 0x71, 0x2b, 0x4e, 0x4d, + 0x70, 0x32, 0x49, 0x4f, 0x67, 0x0a, 0x74, 0x64, 0x6c, 0x31, 0x62, 0x6b, + 0x2f, 0x75, 0x57, 0x5a, 0x35, 0x2b, 0x54, 0x47, 0x51, 0x6d, 0x38, 0x61, + 0x43, 0x69, 0x38, 0x7a, 0x37, 0x38, 0x66, 0x73, 0x2b, 0x66, 0x4b, 0x43, + 0x51, 0x33, 0x64, 0x2b, 0x75, 0x44, 0x7a, 0x58, 0x6e, 0x56, 0x54, 0x41, + 0x54, 0x32, 0x5a, 0x68, 0x44, 0x43, 0x69, 0x66, 0x79, 0x49, 0x75, 0x4a, + 0x77, 0x76, 0x4e, 0x33, 0x77, 0x4e, 0x42, 0x70, 0x39, 0x69, 0x0a, 0x48, + 0x42, 0x53, 0x53, 0x4d, 0x4a, 0x4d, 0x4a, 0x72, 0x42, 0x4f, 0x6a, 0x67, + 0x62, 0x73, 0x77, 0x67, 0x62, 0x67, 0x77, 0x48, 0x77, 0x59, 0x44, 0x56, + 0x52, 0x30, 0x6a, 0x42, 0x42, 0x67, 0x77, 0x46, 0x6f, 0x41, 0x55, 0x49, + 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, + 0x37, 0x53, 0x56, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, + 0x71, 0x77, 0x77, 0x0a, 0x55, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x66, + 0x42, 0x45, 0x73, 0x77, 0x53, 0x54, 0x42, 0x48, 0x6f, 0x45, 0x57, 0x67, + 0x51, 0x34, 0x5a, 0x42, 0x61, 0x48, 0x52, 0x30, 0x63, 0x48, 0x4d, 0x36, + 0x4c, 0x79, 0x39, 0x6a, 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, 0x5a, 0x70, + 0x59, 0x32, 0x46, 0x30, 0x5a, 0x58, 0x4d, 0x75, 0x64, 0x48, 0x4a, 0x31, + 0x63, 0x33, 0x52, 0x6c, 0x5a, 0x48, 0x4e, 0x6c, 0x0a, 0x63, 0x6e, 0x5a, + 0x70, 0x59, 0x32, 0x56, 0x7a, 0x4c, 0x6d, 0x6c, 0x75, 0x64, 0x47, 0x56, + 0x73, 0x4c, 0x6d, 0x4e, 0x76, 0x62, 0x53, 0x39, 0x4a, 0x62, 0x6e, 0x52, + 0x6c, 0x62, 0x46, 0x4e, 0x48, 0x57, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x52, + 0x44, 0x51, 0x53, 0x35, 0x6b, 0x5a, 0x58, 0x49, 0x77, 0x48, 0x51, 0x59, + 0x44, 0x56, 0x52, 0x30, 0x4f, 0x42, 0x42, 0x59, 0x45, 0x46, 0x4e, 0x44, + 0x6f, 0x0a, 0x71, 0x74, 0x70, 0x31, 0x31, 0x2f, 0x6b, 0x75, 0x53, 0x52, + 0x65, 0x59, 0x50, 0x48, 0x73, 0x55, 0x5a, 0x64, 0x44, 0x56, 0x38, 0x6c, + 0x6c, 0x4e, 0x4d, 0x41, 0x34, 0x47, 0x41, 0x31, 0x55, 0x64, 0x44, 0x77, + 0x45, 0x42, 0x2f, 0x77, 0x51, 0x45, 0x41, 0x77, 0x49, 0x42, 0x42, 0x6a, + 0x41, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x52, 0x4d, 0x42, 0x41, 0x66, + 0x38, 0x45, 0x43, 0x44, 0x41, 0x47, 0x0a, 0x41, 0x51, 0x48, 0x2f, 0x41, + 0x67, 0x45, 0x41, 0x4d, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, + 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x41, 0x30, 0x67, 0x41, 0x4d, + 0x45, 0x55, 0x43, 0x49, 0x51, 0x43, 0x4a, 0x67, 0x54, 0x62, 0x74, 0x56, + 0x71, 0x4f, 0x79, 0x5a, 0x31, 0x6d, 0x33, 0x6a, 0x71, 0x69, 0x41, 0x58, + 0x4d, 0x36, 0x51, 0x59, 0x61, 0x36, 0x72, 0x35, 0x73, 0x57, 0x53, 0x0a, + 0x34, 0x79, 0x2f, 0x47, 0x37, 0x79, 0x38, 0x75, 0x49, 0x4a, 0x47, 0x78, + 0x64, 0x77, 0x49, 0x67, 0x52, 0x71, 0x50, 0x76, 0x42, 0x53, 0x4b, 0x7a, + 0x7a, 0x51, 0x61, 0x67, 0x42, 0x4c, 0x51, 0x71, 0x35, 0x73, 0x35, 0x41, + 0x37, 0x30, 0x70, 0x64, 0x6f, 0x69, 0x61, 0x52, 0x4a, 0x38, 0x7a, 0x2f, + 0x30, 0x75, 0x44, 0x7a, 0x34, 0x4e, 0x67, 0x56, 0x39, 0x31, 0x6b, 0x3d, + 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, + 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6a, + 0x7a, 0x43, 0x43, 0x41, 0x6a, 0x53, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, + 0x67, 0x49, 0x55, 0x49, 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, + 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, 0x56, 0x55, 0x72, 0x39, 0x51, 0x47, + 0x7a, 0x6b, 0x6e, 0x42, 0x71, 0x77, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, + 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x77, 0x0a, + 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, 0x45, + 0x41, 0x77, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, + 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, + 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, + 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, + 0x49, 0x45, 0x4e, 0x76, 0x0a, 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, + 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, + 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, + 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, + 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, + 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x0a, 0x42, 0x67, + 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x42, + 0x34, 0x58, 0x44, 0x54, 0x45, 0x34, 0x4d, 0x44, 0x55, 0x79, 0x4d, 0x54, + 0x45, 0x77, 0x4e, 0x44, 0x55, 0x78, 0x4d, 0x46, 0x6f, 0x58, 0x44, 0x54, + 0x51, 0x35, 0x4d, 0x54, 0x49, 0x7a, 0x4d, 0x54, 0x49, 0x7a, 0x4e, 0x54, + 0x6b, 0x31, 0x4f, 0x56, 0x6f, 0x77, 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, + 0x67, 0x47, 0x0a, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x52, 0x53, + 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, + 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, + 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, + 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x63, + 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x0a, 0x61, 0x57, 0x39, 0x75, + 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, + 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, + 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, + 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, + 0x0a, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x46, 0x6b, 0x77, 0x45, 0x77, 0x59, + 0x48, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x43, 0x41, 0x51, 0x59, + 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x44, 0x41, 0x51, 0x63, + 0x44, 0x51, 0x67, 0x41, 0x45, 0x43, 0x36, 0x6e, 0x45, 0x77, 0x4d, 0x44, + 0x49, 0x59, 0x5a, 0x4f, 0x6a, 0x2f, 0x69, 0x50, 0x57, 0x73, 0x43, 0x7a, + 0x61, 0x45, 0x4b, 0x69, 0x37, 0x0a, 0x31, 0x4f, 0x69, 0x4f, 0x53, 0x4c, + 0x52, 0x46, 0x68, 0x57, 0x47, 0x6a, 0x62, 0x6e, 0x42, 0x56, 0x4a, 0x66, + 0x56, 0x6e, 0x6b, 0x59, 0x34, 0x75, 0x33, 0x49, 0x6a, 0x6b, 0x44, 0x59, + 0x59, 0x4c, 0x30, 0x4d, 0x78, 0x4f, 0x34, 0x6d, 0x71, 0x73, 0x79, 0x59, + 0x6a, 0x6c, 0x42, 0x61, 0x6c, 0x54, 0x56, 0x59, 0x78, 0x46, 0x50, 0x32, + 0x73, 0x4a, 0x42, 0x4b, 0x35, 0x7a, 0x6c, 0x4b, 0x4f, 0x42, 0x0a, 0x75, + 0x7a, 0x43, 0x42, 0x75, 0x44, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, + 0x53, 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, 0x42, 0x51, 0x69, 0x5a, + 0x51, 0x7a, 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, 0x66, 0x4f, 0x44, 0x74, + 0x4a, 0x56, 0x53, 0x76, 0x31, 0x41, 0x62, 0x4f, 0x53, 0x63, 0x47, 0x72, + 0x44, 0x42, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, + 0x7a, 0x42, 0x4a, 0x0a, 0x4d, 0x45, 0x65, 0x67, 0x52, 0x61, 0x42, 0x44, + 0x68, 0x6b, 0x46, 0x6f, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, 0x76, + 0x4c, 0x32, 0x4e, 0x6c, 0x63, 0x6e, 0x52, 0x70, 0x5a, 0x6d, 0x6c, 0x6a, + 0x59, 0x58, 0x52, 0x6c, 0x63, 0x79, 0x35, 0x30, 0x63, 0x6e, 0x56, 0x7a, + 0x64, 0x47, 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, 0x64, 0x6d, 0x6c, 0x6a, + 0x5a, 0x58, 0x4d, 0x75, 0x61, 0x57, 0x35, 0x30, 0x0a, 0x5a, 0x57, 0x77, + 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, 0x30, 0x6c, 0x75, 0x64, 0x47, 0x56, + 0x73, 0x55, 0x30, 0x64, 0x59, 0x55, 0x6d, 0x39, 0x76, 0x64, 0x45, 0x4e, + 0x42, 0x4c, 0x6d, 0x52, 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, + 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, 0x67, 0x51, 0x55, 0x49, 0x6d, 0x55, + 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, + 0x56, 0x0a, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, 0x71, + 0x77, 0x77, 0x44, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, + 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, 0x41, 0x67, 0x45, 0x47, 0x4d, 0x42, + 0x49, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, 0x2f, 0x77, + 0x51, 0x49, 0x4d, 0x41, 0x59, 0x42, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, + 0x45, 0x77, 0x43, 0x67, 0x59, 0x49, 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, + 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, 0x51, 0x41, 0x77, 0x52, + 0x67, 0x49, 0x68, 0x41, 0x4f, 0x57, 0x2f, 0x35, 0x51, 0x6b, 0x52, 0x2b, + 0x53, 0x39, 0x43, 0x69, 0x53, 0x44, 0x63, 0x4e, 0x6f, 0x6f, 0x77, 0x4c, + 0x75, 0x50, 0x52, 0x4c, 0x73, 0x57, 0x47, 0x66, 0x2f, 0x59, 0x69, 0x37, + 0x47, 0x53, 0x58, 0x39, 0x34, 0x42, 0x67, 0x77, 0x54, 0x77, 0x67, 0x0a, + 0x41, 0x69, 0x45, 0x41, 0x34, 0x4a, 0x30, 0x6c, 0x72, 0x48, 0x6f, 0x4d, + 0x73, 0x2b, 0x58, 0x6f, 0x35, 0x6f, 0x2f, 0x73, 0x58, 0x36, 0x4f, 0x39, + 0x51, 0x57, 0x78, 0x48, 0x52, 0x41, 0x76, 0x5a, 0x55, 0x47, 0x4f, 0x64, + 0x52, 0x51, 0x37, 0x63, 0x76, 0x71, 0x52, 0x58, 0x61, 0x71, 0x49, 0x3d, + 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x00, + } + + SGXPCKCert = conv([]byte("-----BEGIN CERTIFICATE-----\nMIIEijCCBDKgAwIBAgIUUcfkXVhIadovPuUFjZl2+cqDHxYwCgYIKoZIzj0EAwIwcTEjMCEGA1UEAwwaSW50ZWwgU0dYIFBDSyBQcm9jZXNzb3IgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTIzMDUxMDE5MzAzNFoXDTMwMDUxMDE5MzAzNFowcDEiMCAGA1UEAwwZSW50ZWwgU0dYIFBDSyBDZXJ0aWZpY2F0ZTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT5aL3LqXPoVZE2Kd/2plbjBiHh3/BSLz9WINb9Np8SLG7XQckQY8Ah625qBXigxJV4eWDUa+ggeGwCtROr9iZHo4ICpzCCAqMwHwYDVR0jBBgwFoAU0Oiq2nXX+S5JF5g8exRl0NXyWU0wbAYDVR0fBGUwYzBhoF+gXYZbaHR0cHM6Ly9hcGkudHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9zZ3gvY2VydGlmaWNhdGlvbi92NC9wY2tjcmw/Y2E9cHJvY2Vzc29yJmVuY29kaW5nPWRlcjAdBgNVHQ4EFgQUSCApqBTxIkOtDpSt4T4YItTDOkQwDgYDVR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwggHTBgkqhkiG+E0BDQEEggHEMIIBwDAeBgoqhkiG+E0BDQEBBBBofycWyLUzrk9KRCwHnbIEMIIBYwYKKoZIhvhNAQ0BAjCCAVMwEAYLKoZIhvhNAQ0BAgECAQcwEAYLKoZIhvhNAQ0BAgICAQcwEAYLKoZIhvhNAQ0BAgMCAQAwEAYLKoZIhvhNAQ0BAgQCAQAwEAYLKoZIhvhNAQ0BAgUCAQAwEAYLKoZIhvhNAQ0BAgYCAQAwEAYLKoZIhvhNAQ0BAgcCAQAwEAYLKoZIhvhNAQ0BAggCAQAwEAYLKoZIhvhNAQ0BAgkCAQAwEAYLKoZIhvhNAQ0BAgoCAQAwEAYLKoZIhvhNAQ0BAgsCAQAwEAYLKoZIhvhNAQ0BAgwCAQAwEAYLKoZIhvhNAQ0BAg0CAQAwEAYLKoZIhvhNAQ0BAg4CAQAwEAYLKoZIhvhNAQ0BAg8CAQAwEAYLKoZIhvhNAQ0BAhACAQAwEAYLKoZIhvhNAQ0BAhECAQ0wHwYLKoZIhvhNAQ0BAhIEEAcHAAAAAAAAAAAAAAAAAAAwEAYKKoZIhvhNAQ0BAwQCAAAwFAYKKoZIhvhNAQ0BBAQGAHBqEAAAMA8GCiqGSIb4TQENAQUKAQAwCgYIKoZIzj0EAwIDRgAwQwIgW5VgrBqux967AZR/i3579JUD3Pc3V8+S1+DyzQ9Kl2kCHzICpI02b3AWrjowye4+pSvYyOYPjyEsC1fDp3gMCIU=\n-----END CERTIFICATE-----")) + + SGXPCKProcessorCaCert = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICmDCCAj6gAwIBAgIVANDoqtp11/kuSReYPHsUZdDV8llNMAoGCCqGSM49BAMCMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHExIzAhBgNVBAMMGkludGVsIFNHWCBQQ0sgUHJvY2Vzc29yIENBMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABL9q+NMp2IOgtdl1bk/uWZ5+TGQm8aCi8z78fs+fKCQ3d+uDzXnVTAT2ZhDCifyIuJwvN3wNBp9iHBSSMJMJrBOjgbswgbgwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0fBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFNDoqtp11/kuSReYPHsUZdDV8llNMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMAoGCCqGSM49BAMCA0gAMEUCIQCJgTbtVqOyZ1m3jqiAXM6QYa6r5sWS4y/G7y8uIJGxdwIgRqPvBSKzzQagBLQq5s5A70pdoiaRJ8z/0uDz4NgV91k=\n-----END CERTIFICATE-----")) + + SGXRootCaCert = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIwaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi71OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SVUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYIKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwgAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----")) + + TCBSigningCert = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIwaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwGA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGvP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2ljuypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0fBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlKQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAjftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----")) + + validSGXNonce = []byte("1234567") + invalidSGXNonce = []byte("7654321") + // hash of enclave measurements, part of the enclave (MRENCLAVE) + validSGXMeasurement = []byte{0x08, 0x75, 0x5c, 0xd5, 0x7f, 0x2c, 0x04, 0xb8, 0x45, 0xc4, 0x91, 0x25, 0x77, 0xd6, 0x9a, 0xf3, 0xf9, 0x6c, 0xb7, 0xe3, 0xea, 0xda, 0x57, 0xd1, 0xf9, 0xa7, 0xc6, 0x25, 0xf6, 0xa6, 0x23, 0x3c} + invalidSGXMeasurement = []byte{0x00, 0x75, 0x5c, 0xd5, 0x7f, 0x2c, 0x04, 0xb8, 0x45, 0xc4, 0x91, 0x25, 0x77, 0xd6, 0x9a, 0xf3, 0xf9, 0x6c, 0xb7, 0xe3, 0xea, 0xda, 0x57, 0xd1, 0xf9, 0xa7, 0xc6, 0x25, 0xf6, 0xa6, 0x23, 0x3c} + + validSGXCertChain = [][]byte{TCBSigningCert.Raw, SGXPCKCert.Raw, SGXPCKProcessorCaCert.Raw, SGXRootCaCert.Raw} + + rootCACertFingerprint = "44A0196B2B99F889B8E149E95B807A350E7424964399E885A7CBB8CCFAB674D3" + + // valid collateral + tee_type = uint32(0) + tcb_info = []byte(`{"tcbInfo":{"id":"SGX","version":3,"issueDate":"2023-07-18T23:45:46Z","nextUpdate":"2023-08-17T23:45:46Z","fmspc":"00706A100000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":15,"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":8},{"svn":8},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":7},{"svn":7},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2022-11-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":5},{"svn":5},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":4},{"svn":4},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-06-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":3},{"svn":3},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":10},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":9},"tcbDate":"2020-06-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":7},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":6},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":1},{"svn":1},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":4},"tcbDate":"2017-07-26T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00088","INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]}]},"signature":"8ae2a5689564301bf403a72f7e58dbe36402ae9d88b7ef058e60153bc46221190be5ac15a74c9519912c22185b4cac00670c558e01836314c0610cee0e0e963a"}`) + tcb_info_size = uint32(4391) + qe_identity = []byte(`{"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-07-19T00:13:19Z","nextUpdate":"2023-08-18T00:13:19Z","tcbEvaluationDataNumber":15,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"ced962b0a71c8003d686b7a3e459623a2f2536f96a2926a24b67390e681e22728571482da5f9b642989da7355f3148a4984c340df83b3d38aaffe206d77dc58f"}`) + qe_identity_size = uint32(1381) + + // old/invalid collateral + tcb_info_old = []byte(`{"tcbInfo":{"id":"SGX","version":3,"issueDate":"2023-05-20T23:45:45Z","nextUpdate":"2023-06-19T23:45:45Z","fmspc":"00706A100000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":15,"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":8},{"svn":8},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":7},{"svn":7},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2022-11-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":5},{"svn":5},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":4},{"svn":4},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-06-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":3},{"svn":3},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":10},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":9},"tcbDate":"2020-06-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":7},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":6},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":1},{"svn":1},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":4},"tcbDate":"2017-07-26T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00088","INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]}]},"signature":"0f0dcda69af0014b69e2af1a826d5cf5caee7fdcac2ec10740d7b6caedf04871005976e4cc0803bc50e824fb23c82b21078da45d867c30925a56e6d23fe53119"}`) + tcb_info_size_old = uint32(4391) + qe_identity_old = []byte(`{"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-05-20T23:50:17Z","nextUpdate":"2023-06-19T23:50:17Z","tcbEvaluationDataNumber":15,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"d2a951b5a145f82b089903cc4747c28edf9d9a63b55a043ff2e7a93ecb339bb0b935d4ad516e3e91e57a00d3f913769d60dddd1ed54e242ea9365c9ca5d280ed"}`) + qe_identity_size_old = uint32(1381) + + // test_crl_pem = []byte("-----BEGIN X509 CRL-----\nMIIBITCByAIBATAKBggqhkjOPQQDAjBoMRowGAYDVQQDDBFJbnRlbCBTR1ggUm9vdCBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTIzMDQwMzEwMjI1MVoXDTI0MDQwMjEwMjI1MVqgLzAtMAoGA1UdFAQDAgEBMB8GA1UdIwQYMBaAFCJlDNZanTSJ84O0lVK/UBs5JwasMAoGCCqGSM49BAMCA0gAMEUCIFFXfUfZ+6FXtl8etfRle7xeVsyvc1oD8blj1wSAWrEYAiEAk5AV7BY25+r6X0JsHkAmR8ZzEytoUMq9aM72utdoKgM=\n-----END X509 CRL-----") + // test_crl_der_16, _ = hex.DecodeString("308201213081c8020101300a06082a8648ce3d0403023068311a301806035504030c11496e74656c2053475820526f6f74204341311a3018060355040a0c11496e74656c20436f72706f726174696f6e3114301206035504070c0b53616e746120436c617261310b300906035504080c024341310b3009060355040613025553170d3233303430333130323235315a170d3234303430323130323235315aa02f302d300a0603551d140403020101301f0603551d2304183016801422650cd65a9d3489f383b49552bf501b392706ac300a06082a8648ce3d0403020348003045022051577d47d9fba157b65f1eb5f4657bbc5e56ccaf735a03f1b963d704805ab118022100939015ec1636e7eafa5f426c1e402647c673132b6850cabd68cef6bad7682a03") + + sgx_extensions_short = []byte{0x30, 0x1E, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x01, 0x0D, 0x01, 0x01, 0x04, 0x10, 0x68, 0x7F, 0x27, 0x16, 0xC8, 0xB5, 0x33, 0xAE, 0x4F, 0x4A, 0x44, 0x2C, 0x07, 0x9D, 0xB2, 0x04} + sgx_extensions, _ = hex.DecodeString("301E060A2A864886F84D010D01010410687F2716C8B533AE4F4A442C079DB20430820163060A2A864886F84D010D0102308201533010060B2A864886F84D010D0102010201073010060B2A864886F84D010D0102020201073010060B2A864886F84D010D0102030201003010060B2A864886F84D010D0102040201003010060B2A864886F84D010D0102050201003010060B2A864886F84D010D0102060201003010060B2A864886F84D010D0102070201003010060B2A864886F84D010D0102080201003010060B2A864886F84D010D0102090201003010060B2A864886F84D010D01020A0201003010060B2A864886F84D010D01020B0201003010060B2A864886F84D010D01020C0201003010060B2A864886F84D010D01020D0201003010060B2A864886F84D010D01020E0201003010060B2A864886F84D010D01020F0201003010060B2A864886F84D010D0102100201003010060B2A864886F84D010D01021102010D301F060B2A864886F84D010D0102120410070700000000000000000000000000003010060A2A864886F84D010D0103040200003014060A2A864886F84D010D0104040600706A100000300F060A2A864886F84D010D01050A0100") + + validSGXVersion uint16 = 0x03 + validSGXAttributes [16]byte = [16]byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + invalidSGXAttributes [16]byte = [16]byte{0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + validIsvProdId uint16 = 0x00 + validMRSIGNER = "37E0543F5597B0F0E028FA18955E1307CB7A8CF54B37F513FF64961EADEF94C4" + invalidMRSIGNER = "2101a93F5597B0F0E028FA18955E1307CB7A8CF54B37F513FF64961EADEF94C4" +) + +func Test_verifySgxMeasurements(t *testing.T) { + type args struct { + sgxM *SgxMeasurement + sgxV []ReferenceValue + nonce []byte + } + tests := []struct { + name string + args args + want bool + }{ + // TODO: Add more test cases + { + name: "Valid Attestation Report", + args: args{ + sgxM: &SgxMeasurement{ + Type: "SGX Measurement", + Report: validSGXQuote, + Certs: validSGXCertChain, + }, + sgxV: []ReferenceValue{ + { + Type: "SGX Reference Value", + Sha256: validSGXMeasurement, + Sgx: &SGXDetails{ + Version: validSGXVersion, + Collateral: SGXCollateral{ + TeeType: tee_type, + TcbInfo: tcb_info, + TcbInfoSize: tcb_info_size, + QeIdentity: qe_identity, + QeIdentitySize: qe_identity_size, + }, + CAfingerprint: rootCACertFingerprint, + Attributes: validSGXAttributes, + IsvProdId: validIsvProdId, + MRSIGNER: validMRSIGNER, + }, + }, + }, + nonce: validSGXNonce, + }, + want: true, + }, + // { + // name: "Invalid Tcb Info", + // args: args{ + // sgxM: &SgxMeasurement{ + // Type: "SGX Measurement", + // Report: validSGXQuote, + // Certs: validSGXCertChain, + // }, + // sgxV: []ReferenceValue{ + // { + // Type: "SGX Reference Value", + // Sha256: validSGXMeasurement, + // Sgx: &SGXDetails{ + // Version: validSGXVersion, + // Collateral: SGXCollateral{ + // TeeType: tee_type, + // TcbInfo: tcb_info_old, + // TcbInfoSize: tcb_info_size_old, + // QeIdentity: qe_identity, + // QeIdentitySize: qe_identity_size, + // }, + // CAfingerprint: rootCACertFingerprint, + // Attributes: validSGXAttributes, + // IsvProdId: validIsvProdId, + // MRSIGNER: validMRSIGNER, + // }, + // }, + // }, + // nonce: validSGXNonce, + // }, + // want: false, + // }, + // { + // name: "Invalid QE Identity", + // args: args{ + // sgxM: &SgxMeasurement{ + // Type: "SGX Measurement", + // Report: validSGXQuote, + // Certs: validSGXCertChain, + // }, + // sgxV: []ReferenceValue{ + // { + // Type: "SGX Reference Value", + // Sha256: validSGXMeasurement, + // Sgx: &SGXDetails{ + // Version: validSGXVersion, + // Collateral: SGXCollateral{ + // TeeType: tee_type, + // TcbInfo: tcb_info, + // TcbInfoSize: tcb_info_size, + // QeIdentity: qe_identity_old, + // QeIdentitySize: qe_identity_size_old, + // }, + // CAfingerprint: rootCACertFingerprint, + // Attributes: validSGXAttributes, + // IsvProdId: validIsvProdId, + // MRSIGNER: validMRSIGNER, + // }, + // }, + // }, + // nonce: validSGXNonce, + // }, + // want: false, + // }, + // { + // name: "Invalid Measurement", + // args: args{ + // sgxM: &SgxMeasurement{ + // Type: "SGX Measurement", + // Report: validSGXQuote, + // Certs: validSGXCertChain, + // }, + // sgxV: []ReferenceValue{ + // { + // Type: "SGX Reference Value", + // Sha256: invalidSGXMeasurement, + // Sgx: &SGXDetails{ + // Version: validSGXVersion, + // Collateral: SGXCollateral{ + // TeeType: tee_type, + // TcbInfo: tcb_info, + // TcbInfoSize: tcb_info_size, + // QeIdentity: qe_identity, + // QeIdentitySize: qe_identity_size, + // }, + // CAfingerprint: rootCACertFingerprint, + // Attributes: validSGXAttributes, + // IsvProdId: validIsvProdId, + // MRSIGNER: validMRSIGNER, + // }, + // }, + // }, + // nonce: validSGXNonce, + // }, + // want: false, + // }, + // { + // name: "Wrong Attributes", + // args: args{ + // sgxM: &SgxMeasurement{ + // Type: "SGX Measurement", + // Report: validSGXQuote, + // Certs: validSGXCertChain, + // }, + // sgxV: []ReferenceValue{ + // { + // Type: "SGX Reference Value", + // Sha256: validSGXMeasurement, + // Sgx: &SGXDetails{ + // Version: validSGXVersion, + // Collateral: SGXCollateral{ + // TeeType: tee_type, + // TcbInfo: tcb_info, + // TcbInfoSize: tcb_info_size, + // QeIdentity: qe_identity, + // QeIdentitySize: qe_identity_size, + // }, + // CAfingerprint: rootCACertFingerprint, + // Attributes: invalidSGXAttributes, + // IsvProdId: validIsvProdId, + // MRSIGNER: validMRSIGNER, + // }, + // }, + // }, + // nonce: validSGXNonce, + // }, + // want: false, + // }, + // { + // name: "Invalid Nonce", + // args: args{ + // sgxM: &SgxMeasurement{ + // Type: "SGX Measurement", + // Report: validSGXQuote, + // Certs: validSGXCertChain, + // }, + // sgxV: []ReferenceValue{ + // { + // Type: "SGX Reference Value", + // Sha256: validSGXMeasurement, + // Sgx: &SGXDetails{ + // Version: validSGXVersion, + // Collateral: SGXCollateral{ + // TeeType: tee_type, + // TcbInfo: tcb_info, + // TcbInfoSize: tcb_info_size, + // QeIdentity: qe_identity, + // QeIdentitySize: qe_identity_size, + // }, + // CAfingerprint: rootCACertFingerprint, + // Attributes: validSGXAttributes, + // IsvProdId: validIsvProdId, + // MRSIGNER: validMRSIGNER, + // }, + // }, + // }, + // nonce: invalidSGXNonce, + // }, + // want: false, + // }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res, got := verifySgxMeasurements(tt.args.sgxM, tt.args.nonce, tt.args.sgxV) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("verifySgxMeasurements() got = %v, want %v", got, tt.want) + } + if !got { + fmt.Println(res) + } + }) + } + +} + +func TestParseSGXExtensions(t *testing.T) { + type args struct { + extensions []byte + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test parse SGX Extensions", + args: args{ + extensions: sgx_extensions, + }, + wantErr: false, + }, + { + name: "Test parse SGX Extensions to short", + args: args{ + extensions: sgx_extensions_short, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseSGXExtensions(tt.args.extensions) + if (err != nil) != tt.wantErr { + t.Errorf("ParseSGXExtensions() error = %v, wantErr %v", err, tt.wantErr) + fmt.Println(got) + return + } + }) + } +} diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go new file mode 100644 index 00000000..e3a4509e --- /dev/null +++ b/attestationreport/tdx.go @@ -0,0 +1,62 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attestationreport + +import "fmt" + +func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*TdxMeasurementResult, bool) { + var err error + var qeReportCertData QEReportCertData + result := &TdxMeasurementResult{} + ok := true + + // If the attestationreport does contain neither TDX measurements, nor TDX Reference Values + // there is nothing to to + if tdxM == nil && len(referenceValues) == 0 { + return nil, true + } + + tdxQuote, err := DecodeTdxReportV4(tdxM.Report) + if err != nil { + msg := fmt.Sprintf("Failed to decode TDX report: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // fmt.Println(tdxQuote) + + fmt.Println("Certification Data Type: ", tdxQuote.QuoteSignatureData.QECertDataType) + + if tdxQuote.QuoteSignatureData.QECertDataType == 6 { + err = ParseQEReportCertificationData(tdxQuote.QuoteSignatureData.QECertData, &qeReportCertData) + if err != nil { + msg := fmt.Sprintf("Failed to parse QE Report Certification Data: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + fmt.Println("Successfully parsed QECertData: ", qeReportCertData) + + } else { + msg := fmt.Sprintf("QECertDataType not supported: %v", tdxQuote.QuoteSignatureData.QECertDataType) + result.Summary.setFalse(&msg) + return result, false + } + fmt.Println("QEReportCertData.QECertDataType", qeReportCertData.QECertDataType) + + fmt.Println("QEReportCertData.QECertData", qeReportCertData.QECertData) + + return result, ok +} diff --git a/attestationreport/tdx_struct.go b/attestationreport/tdx_struct.go new file mode 100644 index 00000000..e855d8ac --- /dev/null +++ b/attestationreport/tdx_struct.go @@ -0,0 +1,202 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attestationreport + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +// TDX Report V4 +type TdxReport struct { + QuoteHeader QuoteHeader + ISVEnclaveReport TdxReportBody + QuoteSignatureDataLen uint32 + QuoteSignatureData ECDSA256QuoteSignatureDataStructureV4 // variable size +} + +// 584 bytes (TDX 1.0) +type TdxReportBody struct { + TeeTcbSvn [16]byte + MrSeam [48]byte + MrSigner [48]byte + SeamAttributes [8]byte + TdAttributes [8]byte + XFAM [8]byte + MrTd [48]byte + MrConfigId [48]byte + MrOwner [48]byte + MrOwnerConfig [48]byte + RtMr0 [48]byte + RtMr1 [48]byte + RtMr2 [48]byte + RtMr3 [48]byte + ReportData [64]byte +} + +// Quote Signature for TDX, contains QE Certification Data version 4 +type ECDSA256QuoteSignatureDataStructureV4 struct { + QuoteSignature [64]byte + ECDSAAttestationKey [64]byte + QECertDataType uint16 + QECertDataSize uint32 + QECertData []byte // Version 4 +} + +// This is the datastructure of QECertDataType 6 +type QEReportCertData struct { + QEReport EnclaveReportBody + QEReportSignature [64]byte + QEAuthDataSize uint16 + QEAuthData []byte + QECertDataType uint16 // Type 5 (PCK Cert Chain) + QECertDataSize uint32 + // QECertData []byte // Version 4 (here: PCK Cert Chain) + QECertData SgxCertificates +} + +// Parses the report into the TDReport structure +func DecodeTdxReportV4(report []byte) (TdxReport, error) { + var reportStruct TdxReport + var header QuoteHeader + var body TdxReportBody + var sig ECDSA256QuoteSignatureDataStructureV4 + var sigLen uint32 + + // parse header + buf := bytes.NewBuffer(report) + err := binary.Read(buf, binary.LittleEndian, &header) + if err != nil { + return TdxReport{}, fmt.Errorf("failed to decode TD report header: %v", err) + } + + // parse body + err = binary.Read(buf, binary.LittleEndian, &body) + if err != nil { + return TdxReport{}, fmt.Errorf("failed to decode TD report body: %v", err) + } + + // parse signature size + err = binary.Read(buf, binary.LittleEndian, &sigLen) + if err != nil { + return TdxReport{}, fmt.Errorf("failed to decode TD report QuoteSignatureDataLen: %v", err) + } + + // parse signature + err = parseECDSASignatureV4(buf, &sig) + if err != nil { + return TdxReport{}, fmt.Errorf("failed to decode TD report QuoteSignatureData: %v", err) + } + + // compose the final report struct + reportStruct.QuoteHeader = header + reportStruct.ISVEnclaveReport = body + reportStruct.QuoteSignatureDataLen = sigLen + reportStruct.QuoteSignatureData = sig + + return reportStruct, nil +} + +// parses quote signature data structure from buf to sig +func parseECDSASignatureV4(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStructureV4) error { + + err := binary.Read(buf, binary.LittleEndian, &sig.QuoteSignature) + if err != nil { + return fmt.Errorf("failed to parse QuoteSignature") + } + err = binary.Read(buf, binary.LittleEndian, &sig.ECDSAAttestationKey) + if err != nil { + return fmt.Errorf("failed to parse ECDSAAttestationKey") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataType) + if err != nil { + return fmt.Errorf("failed to parse QECertDataType") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataSize) + if err != nil { + return fmt.Errorf("failed to parse QECertDataSize") + } + tmp := make([]byte, sig.QECertDataSize) + err = binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QECertData") + } + sig.QECertData = tmp + + return nil +} + +func ParseQEReportCertificationData(rawData []byte, qeReportCertData *QEReportCertData) error { + + // parse QE Report + buf := bytes.NewBuffer(rawData) + err := binary.Read(buf, binary.LittleEndian, &qeReportCertData.QEReport) + if err != nil { + return fmt.Errorf("failed to parse QE Report: %v", err) + } + + // parse QE Report Signature + err = binary.Read(buf, binary.LittleEndian, &qeReportCertData.QEReportSignature) + if err != nil { + return fmt.Errorf("failed to parse QE Report Signature: %v", err) + } + + // parse QE Authentication Data Size + err = binary.Read(buf, binary.LittleEndian, &qeReportCertData.QEAuthDataSize) + if err != nil { + return fmt.Errorf("failed to parse QE Authentication Data Size: %v", err) + } + + // parse QE Authentication Data + tmp := make([]byte, qeReportCertData.QEAuthDataSize) + err = binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QE Authentication Data: %v", err) + } + qeReportCertData.QEAuthData = tmp + + // parse QE Certification Data Type (has to be type 5) + err = binary.Read(buf, binary.LittleEndian, &qeReportCertData.QECertDataType) + if err != nil { + return fmt.Errorf("failed to parse QE Certification Data Type: %v", err) + } + if qeReportCertData.QECertDataType != 5 { + return fmt.Errorf("wrong QECertDataType. Expected: 5, Got: %v", qeReportCertData.QECertDataType) + } + + // parse QE Certification Data Size + err = binary.Read(buf, binary.LittleEndian, &qeReportCertData.QECertDataSize) + if err != nil { + return fmt.Errorf("failed to parse QE Certification Data Size: %v", err) + } + + // parse QE Certification Data (Raw) + tmp = make([]byte, qeReportCertData.QECertDataSize) + err = binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QE Certification Data: %v", err) + } + + // parse PCK Cert Chain (PCK Leaf Cert || Intermediate CA Cert || Root CA Cert) + certChain, err := ParseCertificates(tmp[:], false) + if err != nil { + return fmt.Errorf("failed to parse certificate chain from QECertData: %v", err) + } + qeReportCertData.QECertData = certChain + + return nil +} diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go new file mode 100644 index 00000000..2a5d76eb --- /dev/null +++ b/attestationreport/tdx_test.go @@ -0,0 +1,383 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attestationreport + +import ( + "fmt" + "testing" +) + +var ( + validTDXQuote = []byte{ + 0x04, 0x00, 0x02, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, + 0x95, 0x7f, 0x06, 0x07, 0x43, 0xcf, 0xf3, 0x10, 0x95, 0xd7, 0x8f, 0xaa, + 0xfd, 0xc1, 0x19, 0x5e, 0x9b, 0x56, 0xfa, 0x76, 0x1e, 0x6d, 0x6c, 0x1a, + 0x52, 0xc1, 0xa3, 0x8c, 0xf7, 0xed, 0xf3, 0x0a, 0x52, 0x4b, 0x4e, 0xbb, + 0x04, 0x9f, 0x59, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x0b, 0x00, 0x00, + 0x6a, 0xcd, 0x48, 0xb0, 0x10, 0xc3, 0xe8, 0x22, 0x45, 0x23, 0xd2, 0xdd, + 0x4e, 0x6a, 0x40, 0x08, 0xc2, 0xc6, 0xfa, 0xee, 0xb5, 0x0c, 0xdc, 0xa9, + 0x23, 0xef, 0x68, 0x49, 0x03, 0x02, 0xcc, 0x2c, 0xc6, 0xec, 0xcc, 0xc0, + 0x06, 0xd3, 0x0f, 0x7c, 0x23, 0x2f, 0x51, 0xd3, 0x23, 0xd0, 0x50, 0x6d, + 0xd6, 0x2a, 0x69, 0x5a, 0x76, 0x47, 0x39, 0x79, 0x80, 0xf3, 0x32, 0x76, + 0x16, 0x3b, 0xf3, 0x8a, 0xe5, 0x9d, 0x11, 0x57, 0x00, 0xfd, 0x53, 0x43, + 0x38, 0xb7, 0x48, 0xa9, 0x6f, 0x1f, 0x9c, 0x98, 0x21, 0x78, 0x7e, 0x54, + 0x42, 0xc2, 0xa1, 0xba, 0xb3, 0x8d, 0x30, 0x72, 0x6a, 0x11, 0x5a, 0x49, + 0xa4, 0x16, 0x11, 0x44, 0x5f, 0xb4, 0x1f, 0x23, 0x04, 0x9a, 0xca, 0x78, + 0xef, 0x57, 0x21, 0x3d, 0x0d, 0xd0, 0x09, 0x37, 0x61, 0x9b, 0x07, 0x10, + 0xee, 0x13, 0xf1, 0x70, 0xc5, 0x69, 0x22, 0xdf, 0x06, 0x00, 0x6e, 0x0b, + 0x00, 0x00, 0x52, 0xc1, 0xa3, 0x8c, 0xf7, 0xed, 0xf3, 0x0a, 0x52, 0x4b, + 0x4e, 0xbb, 0x04, 0x9f, 0x59, 0xc7, 0x13, 0x37, 0x3b, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0xce, 0xfa, 0x96, 0xce, 0x1e, 0xd0, 0xf1, 0xbf, 0xc1, + 0x1b, 0xbf, 0x59, 0x9b, 0xc0, 0xbe, 0xfd, 0x2b, 0x2e, 0x5a, 0xab, 0xb6, + 0x5f, 0xe9, 0xbe, 0xd7, 0xa5, 0x13, 0x31, 0x57, 0x6b, 0x11, 0xd2, 0x5a, + 0x5d, 0xa5, 0x27, 0x20, 0x08, 0xd5, 0x46, 0x83, 0x1c, 0x9d, 0x4d, 0xe0, + 0x9f, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xbd, + 0x9f, 0xc2, 0x12, 0x98, 0x35, 0xba, 0x1d, 0xcc, 0xa1, 0x93, 0x9d, 0x57, + 0xd0, 0x8e, 0x88, 0x56, 0x1b, 0x34, 0x74, 0x0d, 0x59, 0x40, 0x59, 0x72, + 0xd4, 0xba, 0x25, 0xa7, 0xe5, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x74, 0x85, 0x31, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xbf, + 0x58, 0x7f, 0xdf, 0xdf, 0xd6, 0x2b, 0xf3, 0x9a, 0x59, 0x09, 0x47, 0xa6, + 0x69, 0x8f, 0x97, 0xdb, 0xed, 0x71, 0xc1, 0x6e, 0x5f, 0x29, 0x10, 0x57, + 0x77, 0x31, 0x0c, 0x81, 0x58, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x39, 0xf4, 0x6b, 0xf2, 0x05, 0x9f, 0xa2, 0xf7, 0xaa, 0x47, + 0x41, 0x4c, 0x83, 0xdb, 0x4a, 0xfd, 0xbe, 0x42, 0x5f, 0xc3, 0x84, 0xaa, + 0x9a, 0x87, 0x68, 0x71, 0x80, 0xa7, 0x14, 0x18, 0xb1, 0x60, 0x57, 0x7d, + 0x45, 0xf8, 0xa1, 0x75, 0xc2, 0x3a, 0xd4, 0x5c, 0x62, 0x5c, 0x9d, 0x7e, + 0x79, 0x9f, 0x27, 0x84, 0x29, 0x2c, 0x87, 0x25, 0xce, 0x0f, 0xc9, 0xda, + 0x52, 0xf2, 0xe8, 0x55, 0x72, 0xbd, 0x00, 0x00, 0x05, 0x00, 0xa6, 0x09, + 0x00, 0x00, 0x30, 0x82, 0x02, 0x88, 0x30, 0x82, 0x02, 0x2e, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x14, 0x1a, 0x58, 0x65, 0x01, 0x9d, 0x18, 0xb0, + 0x4e, 0x04, 0x5a, 0xf8, 0x2d, 0x06, 0xc0, 0x85, 0x50, 0xe2, 0x0e, 0xb4, + 0x76, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, + 0x02, 0x30, 0x68, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x53, 0x47, 0x58, 0x20, + 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, + 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, + 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0b, 0x53, 0x61, + 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, 0x61, 0x72, 0x61, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x43, 0x41, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, + 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x38, 0x30, 0x36, 0x31, 0x33, 0x35, + 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x34, 0x39, 0x31, 0x32, 0x33, 0x31, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x68, 0x31, 0x1a, 0x30, + 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, + 0x6c, 0x20, 0x53, 0x47, 0x58, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, + 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x0c, 0x0b, 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, + 0x61, 0x72, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x0c, 0x02, 0x43, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x52, 0x8e, 0xfb, 0xf6, + 0x97, 0x45, 0x34, 0xef, 0xcd, 0x79, 0xf9, 0x95, 0x81, 0x53, 0x08, 0xe3, + 0x64, 0xd2, 0x03, 0x17, 0x71, 0xfe, 0xf2, 0x7f, 0x8e, 0x02, 0xeb, 0xa1, + 0x4d, 0x5e, 0x0b, 0x55, 0xda, 0x1c, 0xa1, 0xa3, 0xdf, 0xe0, 0x5a, 0x6d, + 0x6a, 0xee, 0xdf, 0xe9, 0x0f, 0x86, 0x67, 0x3a, 0x8f, 0x5a, 0x74, 0x41, + 0x70, 0x81, 0xa2, 0x9f, 0xe1, 0x0b, 0x59, 0xa0, 0x1e, 0xea, 0x7e, 0x4e, + 0xa3, 0x81, 0xb5, 0x30, 0x81, 0xb2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x1a, 0x58, 0x65, 0x01, 0x9d, + 0x18, 0xb0, 0x4e, 0x04, 0x5a, 0xf8, 0x2d, 0x06, 0xc0, 0x85, 0x50, 0xe2, + 0x0e, 0xb4, 0x76, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x45, + 0x30, 0x43, 0x30, 0x41, 0xa0, 0x3f, 0xa0, 0x3d, 0x86, 0x3b, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x6f, 0x6e, 0x2d, 0x65, 0x78, 0x69, + 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2d, + 0x6f, 0x6e, 0x6c, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x6c, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x53, 0x47, 0x58, 0x52, + 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x1a, 0x58, 0x65, 0x01, + 0x9d, 0x18, 0xb0, 0x4e, 0x04, 0x5a, 0xf8, 0x2d, 0x06, 0xc0, 0x85, 0x50, + 0xe2, 0x0e, 0xb4, 0x76, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x1e, + 0x77, 0xdb, 0xad, 0x27, 0x4d, 0xff, 0x2e, 0xc2, 0x18, 0xa1, 0x53, 0x88, + 0x5c, 0x9a, 0x39, 0x09, 0x5a, 0x87, 0xf0, 0xd7, 0xb5, 0xb6, 0xb7, 0x45, + 0x8f, 0xa7, 0xf1, 0xf5, 0xe9, 0x95, 0xe9, 0x02, 0x21, 0x00, 0x92, 0xb2, + 0x7c, 0xfc, 0x15, 0xff, 0x45, 0x4d, 0x37, 0xd4, 0x38, 0xdf, 0xfa, 0xa8, + 0xbf, 0x3b, 0x01, 0x0e, 0x97, 0xfc, 0x5a, 0x4e, 0xf5, 0x0e, 0x40, 0xf0, + 0x50, 0x0b, 0x71, 0x0b, 0x94, 0xbf, 0x30, 0x82, 0x02, 0x90, 0x30, 0x82, + 0x02, 0x37, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x03, 0x56, 0x21, + 0x20, 0xba, 0x5b, 0x85, 0xcf, 0xd4, 0xdb, 0x7a, 0xa5, 0xe4, 0x71, 0xb8, + 0x79, 0xf8, 0xb5, 0x98, 0xb1, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x68, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, + 0x53, 0x47, 0x58, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, 0x49, 0x6e, + 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x0b, 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, 0x61, 0x72, + 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, + 0x43, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x38, 0x30, + 0x36, 0x31, 0x33, 0x35, 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x33, 0x36, + 0x30, 0x38, 0x30, 0x36, 0x31, 0x33, 0x35, 0x35, 0x31, 0x34, 0x5a, 0x30, + 0x71, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, + 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x53, 0x47, 0x58, 0x20, 0x50, 0x43, + 0x4b, 0x20, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x20, + 0x43, 0x41, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x11, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x0c, 0x0b, 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, + 0x6c, 0x61, 0x72, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x0c, 0x02, 0x43, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x97, 0x86, 0x02, + 0x28, 0x8a, 0x4b, 0x61, 0x74, 0xd6, 0x38, 0xfc, 0x79, 0x29, 0x26, 0xc8, + 0x49, 0x99, 0xce, 0x3d, 0x82, 0x88, 0xda, 0x7c, 0x83, 0x45, 0x48, 0xc2, + 0x07, 0x6b, 0x67, 0x2b, 0xa0, 0x79, 0x18, 0xb8, 0xc0, 0xb4, 0x9b, 0x74, + 0x0c, 0x99, 0x78, 0x54, 0x87, 0x1e, 0x6e, 0x0c, 0x39, 0x3d, 0xd4, 0x23, + 0xff, 0x5f, 0xac, 0x59, 0x7e, 0x43, 0x56, 0x51, 0xd7, 0xbe, 0x74, 0x5e, + 0x6b, 0xa3, 0x81, 0xb5, 0x30, 0x81, 0xb2, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x1a, 0x58, 0x65, 0x01, + 0x9d, 0x18, 0xb0, 0x4e, 0x04, 0x5a, 0xf8, 0x2d, 0x06, 0xc0, 0x85, 0x50, + 0xe2, 0x0e, 0xb4, 0x76, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, + 0x45, 0x30, 0x43, 0x30, 0x41, 0xa0, 0x3f, 0xa0, 0x3d, 0x86, 0x3b, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x6f, 0x6e, 0x2d, 0x65, 0x78, + 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x65, 0x62, 0x75, 0x67, + 0x2d, 0x6f, 0x6e, 0x6c, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x53, 0x47, 0x58, + 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x03, 0x56, 0x21, + 0x20, 0xba, 0x5b, 0x85, 0xcf, 0xd4, 0xdb, 0x7a, 0xa5, 0xe4, 0x71, 0xb8, + 0x79, 0xf8, 0xb5, 0x98, 0xb1, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, + 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, + 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, + 0x46, 0x5c, 0xe5, 0x28, 0xf3, 0x62, 0xec, 0x9f, 0xbd, 0xa0, 0xe4, 0x2c, + 0x5c, 0xa7, 0x39, 0x60, 0xda, 0x2f, 0xdc, 0x76, 0xe2, 0xda, 0x97, 0x95, + 0xab, 0xbd, 0xba, 0x52, 0x4b, 0x13, 0xcd, 0x47, 0x02, 0x20, 0x34, 0x89, + 0x6f, 0xe9, 0x10, 0x35, 0x01, 0xa0, 0x22, 0x5c, 0x06, 0xce, 0x5e, 0xc9, + 0x89, 0x09, 0x8d, 0xe3, 0x8b, 0x42, 0x8c, 0xb1, 0xb4, 0xb3, 0x5e, 0x4b, + 0x2c, 0xeb, 0x68, 0x00, 0x67, 0xab, 0x30, 0x82, 0x04, 0x82, 0x30, 0x82, + 0x04, 0x29, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x15, 0x00, 0xf8, 0xfc, + 0xe9, 0xa7, 0xf7, 0xa5, 0x77, 0x57, 0x49, 0xa6, 0xa2, 0xb7, 0x76, 0x0a, + 0x26, 0x49, 0x4d, 0xd9, 0x05, 0x76, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x71, 0x31, 0x23, 0x30, 0x21, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x49, 0x6e, 0x74, 0x65, 0x6c, + 0x20, 0x53, 0x47, 0x58, 0x20, 0x50, 0x43, 0x4b, 0x20, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x20, 0x43, 0x41, 0x31, 0x1a, 0x30, + 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, + 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0b, + 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, 0x61, 0x72, 0x61, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x43, 0x41, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x38, 0x30, 0x36, 0x31, + 0x33, 0x35, 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x38, + 0x30, 0x36, 0x31, 0x33, 0x35, 0x35, 0x31, 0x34, 0x5a, 0x30, 0x70, 0x31, + 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x49, 0x6e, + 0x74, 0x65, 0x6c, 0x20, 0x53, 0x47, 0x58, 0x20, 0x50, 0x43, 0x4b, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, 0x49, 0x6e, + 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x0b, 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, 0x61, 0x72, + 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, + 0x43, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, + 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x21, 0xc4, 0xfa, 0x58, 0xbc, 0xed, + 0x0a, 0xe5, 0x7a, 0x89, 0x3f, 0x33, 0xce, 0x20, 0x18, 0x41, 0xbf, 0xe4, + 0xd9, 0xf0, 0x54, 0x54, 0xbd, 0xab, 0x61, 0x82, 0xfd, 0x89, 0xfa, 0x1d, + 0x01, 0xbb, 0x30, 0x7b, 0x51, 0x36, 0xbc, 0x77, 0xc8, 0x97, 0x79, 0xd7, + 0xe5, 0x32, 0xa6, 0x5e, 0xc3, 0xe9, 0xd4, 0xf4, 0x3a, 0xe0, 0x93, 0xfc, + 0x59, 0xae, 0x54, 0x2b, 0x94, 0xe2, 0x0b, 0x17, 0x66, 0x0b, 0xa3, 0x82, + 0x02, 0x9d, 0x30, 0x82, 0x02, 0x99, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x03, 0x56, 0x21, 0x20, 0xba, + 0x5b, 0x85, 0xcf, 0xd4, 0xdb, 0x7a, 0xa5, 0xe4, 0x71, 0xb8, 0x79, 0xf8, + 0xb5, 0x98, 0xb1, 0x30, 0x58, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x51, + 0x30, 0x4f, 0x30, 0x4d, 0xa0, 0x4b, 0xa0, 0x49, 0x86, 0x47, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x65, 0x64, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x69, + 0x6e, 0x74, 0x65, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x49, 0x6e, 0x74, + 0x65, 0x6c, 0x53, 0x47, 0x58, 0x50, 0x43, 0x4b, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x94, 0x30, 0x13, 0x7f, + 0x3b, 0x57, 0x60, 0x34, 0xfa, 0x35, 0xb8, 0x6b, 0xd9, 0x5d, 0xa9, 0x13, + 0x23, 0xdf, 0x85, 0x71, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x06, 0xc0, 0x30, 0x0c, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x82, + 0x01, 0xdd, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, + 0x01, 0x04, 0x82, 0x01, 0xce, 0x30, 0x82, 0x01, 0xca, 0x30, 0x1e, 0x06, + 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x01, 0x04, + 0x10, 0x21, 0x1d, 0x75, 0x42, 0xf3, 0x6c, 0x35, 0x11, 0xed, 0x7f, 0xb6, + 0xdd, 0x63, 0x49, 0xb3, 0xe2, 0x30, 0x82, 0x01, 0x6d, 0x06, 0x0a, 0x2a, + 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x30, 0x82, 0x01, + 0x5d, 0x30, 0x10, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x02, 0x01, 0x52, 0x30, 0x11, 0x06, 0x0b, 0x2a, + 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x00, 0xc1, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, + 0x01, 0x0d, 0x01, 0x02, 0x03, 0x02, 0x02, 0x00, 0xa3, 0x30, 0x11, 0x06, + 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x04, + 0x02, 0x02, 0x00, 0x8c, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, + 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x05, 0x02, 0x02, 0x00, 0xf7, 0x30, + 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, + 0x02, 0x06, 0x02, 0x02, 0x00, 0xed, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, + 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x07, 0x02, 0x02, 0x00, + 0xf3, 0x30, 0x10, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, + 0x0d, 0x01, 0x02, 0x08, 0x02, 0x01, 0x0a, 0x30, 0x10, 0x06, 0x0b, 0x2a, + 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x09, 0x02, 0x01, + 0x52, 0x30, 0x10, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, + 0x0d, 0x01, 0x02, 0x0a, 0x02, 0x01, 0x4b, 0x30, 0x10, 0x06, 0x0b, 0x2a, + 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x0b, 0x02, 0x01, + 0x4e, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, + 0x0d, 0x01, 0x02, 0x0c, 0x02, 0x02, 0x00, 0xbb, 0x30, 0x10, 0x06, 0x0b, + 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x0d, 0x02, + 0x01, 0x04, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, + 0x01, 0x0d, 0x01, 0x02, 0x0e, 0x02, 0x02, 0x00, 0x9f, 0x30, 0x10, 0x06, + 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x0f, + 0x02, 0x01, 0x59, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, + 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x10, 0x02, 0x02, 0x00, 0xc7, 0x30, 0x11, + 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, + 0x11, 0x02, 0x02, 0x29, 0x61, 0x30, 0x1f, 0x06, 0x0b, 0x2a, 0x86, 0x48, + 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x12, 0x04, 0x10, 0x52, 0xc1, + 0xa3, 0x8c, 0xf7, 0xed, 0xf3, 0x0a, 0x52, 0x4b, 0x4e, 0xbb, 0x04, 0x9f, + 0x59, 0xc7, 0x30, 0x10, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, + 0x01, 0x0d, 0x01, 0x03, 0x04, 0x02, 0x8a, 0x67, 0x30, 0x14, 0x06, 0x0a, + 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x04, 0x04, 0x06, + 0xed, 0x74, 0x2a, 0xf8, 0xad, 0xf5, 0x30, 0x0f, 0x06, 0x0a, 0x2a, 0x86, + 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x05, 0x0a, 0x01, 0x00, 0x30, + 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, + 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x5f, 0x70, 0x8e, 0x03, 0xb8, 0x92, + 0xdc, 0x6c, 0x0b, 0x3b, 0x5b, 0xf8, 0x16, 0xdd, 0x9f, 0x2f, 0x55, 0x94, + 0x95, 0x28, 0x64, 0xd7, 0xd3, 0x89, 0x6f, 0x55, 0xb9, 0x19, 0xc6, 0x52, + 0xac, 0xf3, 0x02, 0x20, 0x4c, 0x04, 0x98, 0x34, 0xad, 0x6b, 0x25, 0x0d, + 0x3b, 0xf7, 0x34, 0xd9, 0x56, 0xac, 0xf8, 0xe7, 0x3d, 0xac, 0x69, 0x02, + 0xe9, 0x55, 0x5a, 0x81, 0x79, 0xb1, 0x8c, 0x4d, 0x71, 0xc5, 0x59, 0x4a, + } + validTDXCertChain = [][]byte{} + validTDXMeasurement = []byte{} +) + +func Test_verifyTdxMeasurements(t *testing.T) { + type args struct { + tdxM *TdxMeasurement + tdxV []ReferenceValue + nonce []byte + } + tests := []struct { + name string + args args + want *TdxMeasurementResult + want1 bool + }{ + // TODO: Add more test cases. + { + name: "Valid Attestation Report", + args: args{ + tdxM: &TdxMeasurement{ + Type: "TDX Measurement", + Report: validTDXQuote, + Certs: validTDXCertChain, + }, + tdxV: []ReferenceValue{ + { + Type: "TDX Reference Value", + Sha256: validTDXMeasurement, + Tdx: &TDXDetails{}, + }, + }, + nonce: validSGXNonce, + }, + want1: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res, got := verifyTdxMeasurements(tt.args.tdxM, tt.args.nonce, tt.args.tdxV) + if got != tt.want1 { + t.Errorf("verifyTdxMeasurements() got = %v, want %v", got, tt.want1) + } + if !got { + fmt.Println(res) + } + }) + } + +} diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index ce7a1ef4..4334815f 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -73,6 +73,8 @@ type MeasurementResult struct { SnpMeasResult *SnpMeasurementResult `json:"snp,omitempty"` IasMeasResult *IasMeasurementResult `json:"ias,omitempty"` SwMeasResult []SwMeasurementResult `json:"sw,omitempty"` + SgxMeasResult *SgxMeasurementResult `json:"sgx,omitempty"` + TdxMeasResult *TdxMeasurementResult `json:"tdx,omitempty"` } // DevDescResult represents the results of the validation of the @@ -171,6 +173,26 @@ type SnpMeasurementResult struct { PolicyCheck PolicyCheck `json:"policyCheck"` } +// SgxMeasurementResult represents the results for the verification +// of Intel SGX measurements. +type SgxMeasurementResult struct { + Summary Result `json:"resultSummary"` + Freshness Result `json:"freshness"` + Signature SignatureResult `json:"signature"` + Artifacts []DigestResult `json:"artifacts"` + VersionMatch Result `json:"reportVersionMatch"` +} + +// TdxMeasurementResult represents the results for the verification +// of Intel TDX measurements. +type TdxMeasurementResult struct { + Summary Result `json:"resultSummary"` + Freshness Result `json:"freshness"` + Signature SignatureResult `json:"signature"` + Artifacts []DigestResult `json:"artifacts"` + VersionMatch Result `json:"reportVersionMatch"` +} + // IasMeasurementResult represents the results for the verification // of ARM PSA Initial Attestation Service Token measurements. type IasMeasurementResult struct { From 4ecb11da435b916b13d72c234b1358d3ff63528f Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Mon, 30 Oct 2023 15:19:10 +0000 Subject: [PATCH 02/23] implemented generic quote signature verification --- attestationreport/attestationreport.go | 5 +- attestationreport/sgx.go | 83 ++++++++++++++++---------- attestationreport/tdx.go | 63 ++++++++++++++----- attestationreport/tdx_struct.go | 50 +++++++--------- attestationreport/tdx_test.go | 7 ++- 5 files changed, 127 insertions(+), 81 deletions(-) diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index b3051398..75c5522c 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -226,7 +226,10 @@ type SGXDetails struct { } type TDXDetails struct { - // TODO: add attributes to this struct + // TODO: add more attributes to this struct + Version uint16 `json:"version" cbor:"0,keyasint"` + Cafingerprint string `json:"caFingerprint" cbor:"1,keyasint"` // Intel Root CA Certificate Fingerprint + } // ReferenceValue represents the attestation report diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index ac026545..37eb4a82 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -46,7 +46,7 @@ const ( SGX_QUOTE_BODY_SIZE = 384 SGX_QUOTE_SIGNATURE_OFFSET = 436 TDX_QUOTE_BODY_SIZE = 584 - TDX_QUOTE_SIGNATURE_OFFSET = 632 + TDX_QUOTE_SIGNATURE_OFFSET = 636 TDX_ID = "TDX" SGX_ID = "SGX" @@ -232,7 +232,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - // Store details from validated certificate chain(s) in the report + // Store details from validated certificate chain(s) in the validation report for _, chain := range x509CertChains { chainExtracted := []X509CertExtracted{} for _, cert := range chain { @@ -276,7 +276,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ // Verify Quote Signature sig, ret := VerifyIntelQuoteSignature(sgxM.Report, sgxQuote.QuoteSignatureData, sgxQuote.QuoteSignatureDataLen, int(sgxQuote.QuoteHeader.AttestationKeyType), certs, - sgxReferenceValue.Sgx.CAfingerprint, SGX_QUOTE_TYPE) + sgxReferenceValue.Sgx.CAfingerprint, quoteType) if !ret { msg := fmt.Sprintf("Failed to verify Quote Signature: %v", sig) result.Summary.setFalse(&msg) @@ -328,26 +328,36 @@ func verifySgxVersion(quote QuoteHeader, version uint16) (Result, bool) { // verifies the quote signature // Can be used by SGX/TDX: QuoteType = 0x00 (sgx) or 0x81 (TDX) // TODO: Handle different QE Cert Data Types in here -func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature ECDSA256QuoteSignatureDataStructure, +func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, quoteSignatureSize uint32, quoteSignatureType int, certs SgxCertificates, fingerprint string, quoteType uint32) (SignatureResult, bool) { result := SignatureResult{} var digest [32]byte + var ak_pub [64]byte // attestation public key (generated by the QE) + var reportData [64]byte + var hash_ref [32]byte - if quoteType != SGX_QUOTE_TYPE && quoteType != TDX_QUOTE_TYPE { + // check signature type and size + if quoteType == SGX_QUOTE_TYPE { + if uint32(len(reportRaw)-SGX_QUOTE_SIGNATURE_OFFSET) != quoteSignatureSize { + msg := fmt.Sprintf("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", + quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) + result.SignCheck.setFalse(&msg) + return result, false + } + } else if quoteType == TDX_QUOTE_TYPE { + if uint32(len(reportRaw)-TDX_QUOTE_SIGNATURE_OFFSET) != quoteSignatureSize { + msg := fmt.Sprintf("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", + quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) + result.SignCheck.setFalse(&msg) + return result, false + } + } else { msg := fmt.Sprintf("Quote Type not supported %v", quoteType) result.SignCheck.setFalse(&msg) return result, false } - // check signature size - if uint32(len(reportRaw)-SGX_QUOTE_SIGNATURE_OFFSET) != quoteSignatureSize { - msg := fmt.Sprintf("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", - quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) - result.SignCheck.setFalse(&msg) - return result, false - } - // for now: check attestation signature type = key type if quoteSignatureType != ecdsa_p_256 { msg := fmt.Sprintf("Signature Algorithm %v not supported", quoteSignatureType) @@ -356,20 +366,24 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature ECDSA256QuoteSig } // Step 1: Verify ISV Enclave Report Signature - if quoteType == SGX_QUOTE_TYPE { - digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+SGX_QUOTE_BODY_SIZE]) - } else { - digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+TDX_QUOTE_BODY_SIZE]) - } - // Convert r, s to Big Int r := new(big.Int) - r.SetBytes(quoteSignature.ISVEnclaveReportSignature[:32]) s := new(big.Int) - s.SetBytes(quoteSignature.ISVEnclaveReportSignature[32:]) - // Extract the attestation public key (generated by the QE) from the certificate - ak_pub := quoteSignature.ECDSAAttestationKey + switch quoteSig := quoteSignature.(type) { + case ECDSA256QuoteSignatureDataStructure: // for SGX + r.SetBytes(quoteSig.ISVEnclaveReportSignature[:32]) + s.SetBytes(quoteSig.ISVEnclaveReportSignature[32:]) + digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+SGX_QUOTE_BODY_SIZE]) + ak_pub = quoteSig.ECDSAAttestationKey + + case ECDSA256QuoteSignatureDataStructureV4: // for TDX + r.SetBytes(quoteSig.QuoteSignature[:32]) + s.SetBytes(quoteSig.QuoteSignature[32:]) + digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+TDX_QUOTE_BODY_SIZE]) + ak_pub = quoteSig.ECDSAAttestationKey + } + if len(ak_pub) == 0 { msg := "Failed to extract ECDSA public key from certificate" result.SignCheck.setFalse(&msg) @@ -399,18 +413,22 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature ECDSA256QuoteSig result.SignCheck.Success = true // Step 2: Verify QE Report Signature - // get QE Report from QE Report Signature Data - if quoteType == SGX_QUOTE_TYPE { + switch quoteSig := quoteSignature.(type) { + case ECDSA256QuoteSignatureDataStructure: // for SGX digest = sha256.Sum256(reportRaw[SGX_QUOTE_SIGNATURE_OFFSET+128 : SGX_QUOTE_SIGNATURE_OFFSET+128+SGX_QUOTE_BODY_SIZE]) - } else { - digest = sha256.Sum256(reportRaw[TDX_QUOTE_SIGNATURE_OFFSET+128 : TDX_QUOTE_SIGNATURE_OFFSET+128+TDX_QUOTE_BODY_SIZE]) + r.SetBytes(quoteSig.QEReportSignature[:32]) + s.SetBytes(quoteSig.QEReportSignature[32:]) + reportData = quoteSig.QEReport.ReportData + hash_ref = sha256.Sum256(append(ak_pub[:], quoteSig.QEAuthData...)) + case ECDSA256QuoteSignatureDataStructureV4: // for TDX + digest = sha256.Sum256(reportRaw[TDX_QUOTE_SIGNATURE_OFFSET+128+6 : TDX_QUOTE_SIGNATURE_OFFSET+128+6+SGX_QUOTE_BODY_SIZE]) + r.SetBytes(quoteSig.QECertData.QEReportSignature[:32]) + s.SetBytes(quoteSig.QECertData.QEReportSignature[32:]) + reportData = quoteSig.QECertData.QEReport.ReportData + hash_ref = sha256.Sum256(append(ak_pub[:], quoteSig.QECertData.QEAuthData...)) } - // extract r and s from ECDSA QEReportSignature - r.SetBytes(quoteSignature.QEReportSignature[:32]) - s.SetBytes(quoteSignature.QEReportSignature[32:]) - // Extract the PCK public key from the PCK certificate pck_pub, ok := certs.PCKCert.PublicKey.(*ecdsa.PublicKey) if pck_pub == nil || !ok { @@ -431,8 +449,7 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature ECDSA256QuoteSig result.SignCheck.Success = true // Step 3: Verify Report Data: SHA256(ECDSA Attestation Key || QE Authentication Data) || 32-0x00’s) - reportData := quoteSignature.QEReport.ReportData - hash_ref := sha256.Sum256(append(ak_pub[:], quoteSignature.QEAuthData...)) + reportDataRef := append(hash_ref[:], make([]byte, 32)...) if !bytes.Equal(reportData[:], reportDataRef[:]) { diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index e3a4509e..b7f5af1b 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -15,11 +15,16 @@ package attestationreport -import "fmt" +import ( + "crypto/x509" + "fmt" + "time" + + "github.com/Fraunhofer-AISEC/cmc/internal" +) func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*TdxMeasurementResult, bool) { var err error - var qeReportCertData QEReportCertData result := &TdxMeasurementResult{} ok := true @@ -29,6 +34,19 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return nil, true } + if len(referenceValues) == 0 { + msg := "Could not find TDX Reference Value" + result.Summary.setFalse(&msg) + return result, false + } else if len(referenceValues) > 1 { + msg := fmt.Sprintf("Report contains %v reference values. Currently, only one SGX Reference Value is supported", + len(referenceValues)) + result.Summary.setFalse(&msg) + return result, false + } + tdxReferenceValue := referenceValues[0] + + // TODO: support other report types tdxQuote, err := DecodeTdxReportV4(tdxM.Report) if err != nil { msg := fmt.Sprintf("Failed to decode TDX report: %v", err) @@ -36,27 +54,40 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - // fmt.Println(tdxQuote) + var current_time time.Time = time.Now() + log.Trace("current time: ", current_time) + + var certChain SgxCertificates = tdxQuote.QuoteSignatureData.QECertData.QECertData - fmt.Println("Certification Data Type: ", tdxQuote.QuoteSignatureData.QECertDataType) + if certChain.RootCACert == nil || certChain.IntermediateCert == nil || certChain.PCKCert == nil { + msg := "incomplete certificate chain" + result.Summary.setFalse(&msg) + return result, false + } + + // (from DCAP Library): parse and verify PCK certificate chain + _, err = internal.VerifyCertChain( + []*x509.Certificate{certChain.PCKCert, certChain.IntermediateCert}, + []*x509.Certificate{certChain.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify pck certificate chain: %v", err) + result.Summary.setFalse(&msg) + return result, false + } - if tdxQuote.QuoteSignatureData.QECertDataType == 6 { - err = ParseQEReportCertificationData(tdxQuote.QuoteSignatureData.QECertData, &qeReportCertData) - if err != nil { - msg := fmt.Sprintf("Failed to parse QE Report Certification Data: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - fmt.Println("Successfully parsed QECertData: ", qeReportCertData) + // TODO: verify the certificates (fetch CRLs and check expiration date) - } else { - msg := fmt.Sprintf("QECertDataType not supported: %v", tdxQuote.QuoteSignatureData.QECertDataType) + // Verify Quote Signature + sig, ret := VerifyIntelQuoteSignature(tdxM.Report, tdxQuote.QuoteSignatureData, + tdxQuote.QuoteSignatureDataLen, int(tdxQuote.QuoteHeader.AttestationKeyType), certChain, + tdxReferenceValue.Tdx.Cafingerprint, TDX_QUOTE_TYPE) + if !ret { + msg := fmt.Sprintf("Failed to verify Quote Signature: %v", sig) result.Summary.setFalse(&msg) return result, false } - fmt.Println("QEReportCertData.QECertDataType", qeReportCertData.QECertDataType) - fmt.Println("QEReportCertData.QECertData", qeReportCertData.QECertData) + //result.Signature = sig return result, ok } diff --git a/attestationreport/tdx_struct.go b/attestationreport/tdx_struct.go index e855d8ac..f3a69c09 100644 --- a/attestationreport/tdx_struct.go +++ b/attestationreport/tdx_struct.go @@ -54,19 +54,18 @@ type ECDSA256QuoteSignatureDataStructureV4 struct { ECDSAAttestationKey [64]byte QECertDataType uint16 QECertDataSize uint32 - QECertData []byte // Version 4 + QECertData QEReportCertDataV4 // Version 4 } // This is the datastructure of QECertDataType 6 -type QEReportCertData struct { +type QEReportCertDataV4 struct { QEReport EnclaveReportBody QEReportSignature [64]byte QEAuthDataSize uint16 QEAuthData []byte QECertDataType uint16 // Type 5 (PCK Cert Chain) QECertDataSize uint32 - // QECertData []byte // Version 4 (here: PCK Cert Chain) - QECertData SgxCertificates + QECertData SgxCertificates } // Parses the report into the TDReport structure @@ -111,7 +110,7 @@ func DecodeTdxReportV4(report []byte) (TdxReport, error) { return reportStruct, nil } -// parses quote signature data structure from buf to sig +// parse the full quote signature data structure (V4) from buf to sig func parseECDSASignatureV4(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStructureV4) error { err := binary.Read(buf, binary.LittleEndian, &sig.QuoteSignature) @@ -130,62 +129,53 @@ func parseECDSASignatureV4(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStr if err != nil { return fmt.Errorf("failed to parse QECertDataSize") } - tmp := make([]byte, sig.QECertDataSize) - err = binary.Read(buf, binary.LittleEndian, &tmp) + + rawData := make([]byte, sig.QECertDataSize) + err = binary.Read(buf, binary.LittleEndian, &rawData) if err != nil { return fmt.Errorf("failed to parse QECertData") } - sig.QECertData = tmp - - return nil -} - -func ParseQEReportCertificationData(rawData []byte, qeReportCertData *QEReportCertData) error { - // parse QE Report - buf := bytes.NewBuffer(rawData) - err := binary.Read(buf, binary.LittleEndian, &qeReportCertData.QEReport) + // parse QEReportCertDataV4 + buf = bytes.NewBuffer(rawData) + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEReport) if err != nil { return fmt.Errorf("failed to parse QE Report: %v", err) } - // parse QE Report Signature - err = binary.Read(buf, binary.LittleEndian, &qeReportCertData.QEReportSignature) + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEReportSignature) if err != nil { return fmt.Errorf("failed to parse QE Report Signature: %v", err) } - // parse QE Authentication Data Size - err = binary.Read(buf, binary.LittleEndian, &qeReportCertData.QEAuthDataSize) + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEAuthDataSize) if err != nil { return fmt.Errorf("failed to parse QE Authentication Data Size: %v", err) } - // parse QE Authentication Data - tmp := make([]byte, qeReportCertData.QEAuthDataSize) + tmp := make([]byte, sig.QECertData.QEAuthDataSize) err = binary.Read(buf, binary.LittleEndian, &tmp) if err != nil { return fmt.Errorf("failed to parse QE Authentication Data: %v", err) } - qeReportCertData.QEAuthData = tmp + sig.QECertData.QEAuthData = tmp // parse QE Certification Data Type (has to be type 5) - err = binary.Read(buf, binary.LittleEndian, &qeReportCertData.QECertDataType) + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QECertDataType) if err != nil { return fmt.Errorf("failed to parse QE Certification Data Type: %v", err) } - if qeReportCertData.QECertDataType != 5 { - return fmt.Errorf("wrong QECertDataType. Expected: 5, Got: %v", qeReportCertData.QECertDataType) + if sig.QECertData.QECertDataType != 5 { + return fmt.Errorf("wrong QECertDataType. Expected: 5, Got: %v", sig.QECertData.QECertDataType) } - // parse QE Certification Data Size - err = binary.Read(buf, binary.LittleEndian, &qeReportCertData.QECertDataSize) + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QECertDataSize) if err != nil { return fmt.Errorf("failed to parse QE Certification Data Size: %v", err) } // parse QE Certification Data (Raw) - tmp = make([]byte, qeReportCertData.QECertDataSize) + tmp = make([]byte, sig.QECertData.QECertDataSize) err = binary.Read(buf, binary.LittleEndian, &tmp) if err != nil { return fmt.Errorf("failed to parse QE Certification Data: %v", err) @@ -196,7 +186,7 @@ func ParseQEReportCertificationData(rawData []byte, qeReportCertData *QEReportCe if err != nil { return fmt.Errorf("failed to parse certificate chain from QECertData: %v", err) } - qeReportCertData.QECertData = certChain + sig.QECertData.QECertData = certChain return nil } diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 2a5d76eb..a3128eb5 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -333,6 +333,8 @@ var ( } validTDXCertChain = [][]byte{} validTDXMeasurement = []byte{} + + rootCAFingerprint = "BF85A53FC08F84CB1F73A4F75F48AF566E30AC040699BA0EC1B8D593C05B56FC" ) func Test_verifyTdxMeasurements(t *testing.T) { @@ -360,7 +362,10 @@ func Test_verifyTdxMeasurements(t *testing.T) { { Type: "TDX Reference Value", Sha256: validTDXMeasurement, - Tdx: &TDXDetails{}, + Tdx: &TDXDetails{ + Version: 0x04, + Cafingerprint: rootCAFingerprint, + }, }, }, nonce: validSGXNonce, From f43a345cb8db0fc5ccd543e09eaaaef467bce837 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Fri, 3 Nov 2023 12:41:59 +0000 Subject: [PATCH 03/23] new test quote (from TDX Simulator) --- attestationreport/tdx_test.go | 417 +++++++++++++++++++++++++++++++++- 1 file changed, 416 insertions(+), 1 deletion(-) diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index a3128eb5..2dc20e75 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -21,6 +21,420 @@ import ( ) var ( + aisecTDXQuote = []byte{ + 0x04, 0x00, 0x02, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, + 0x95, 0x7f, 0x06, 0x07, 0x72, 0xc9, 0xdf, 0x27, 0x32, 0x1a, 0x0f, 0xd3, + 0xa2, 0x91, 0xf0, 0x15, 0x06, 0x7e, 0x27, 0x5d, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2f, 0xd2, 0x79, 0xc1, 0x61, 0x64, 0xa9, 0x3d, + 0xd5, 0xbf, 0x37, 0x3d, 0x83, 0x43, 0x28, 0xd4, 0x60, 0x08, 0xc2, 0xb6, + 0x93, 0xaf, 0x9e, 0xbb, 0x86, 0x5b, 0x08, 0xb2, 0xce, 0xd3, 0x20, 0xc9, + 0xa8, 0x9b, 0x48, 0x69, 0xa9, 0xfa, 0xb6, 0x0f, 0xbe, 0x9d, 0x0c, 0x5a, + 0x53, 0x63, 0xc6, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x02, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x5f, 0xe2, 0x8d, 0xab, 0x35, 0x6d, 0x75, + 0x76, 0x7a, 0xb5, 0x6a, 0xe8, 0x3b, 0xc5, 0x9a, 0xc0, 0x45, 0xdc, 0x54, + 0x8b, 0xf4, 0x0a, 0x3f, 0xb1, 0x13, 0x90, 0x44, 0x6a, 0xf1, 0x9f, 0x6e, + 0xe2, 0x1b, 0x0b, 0x88, 0x74, 0xc9, 0xa7, 0x86, 0x4c, 0xed, 0xb6, 0x4c, + 0x65, 0x73, 0xd9, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe1, 0xaf, 0x75, 0xe6, 0x19, 0x27, 0x41, 0x0e, + 0x42, 0xb5, 0x4b, 0x39, 0xf6, 0x68, 0x1c, 0xf9, 0xb0, 0xbf, 0xba, 0xe5, + 0x12, 0xb1, 0x5e, 0x87, 0x0e, 0x4c, 0x8d, 0x9d, 0x5a, 0x5c, 0xb3, 0x85, + 0x57, 0x1b, 0x0e, 0x1d, 0xc2, 0xf7, 0x0b, 0xf9, 0xcc, 0xef, 0x08, 0x56, + 0x0f, 0x0a, 0x2b, 0x58, 0x01, 0x1c, 0xb9, 0xfd, 0xd5, 0x14, 0xb4, 0x47, + 0x96, 0x02, 0x39, 0xce, 0x77, 0xa0, 0xff, 0xc8, 0x07, 0x87, 0x77, 0x37, + 0x1f, 0x7e, 0xbf, 0xeb, 0x4c, 0xa0, 0x48, 0x0d, 0x03, 0x3c, 0xe5, 0xec, + 0x61, 0x43, 0x48, 0x06, 0x49, 0xf7, 0x5a, 0x90, 0x57, 0x9d, 0xf5, 0xf5, + 0x01, 0x6b, 0xe7, 0xca, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x4e, 0x81, 0xd1, 0xe3, 0xd7, 0x1e, 0x9f, + 0x77, 0xc9, 0xe1, 0xaa, 0xfc, 0xbd, 0xf1, 0x57, 0xaa, 0x53, 0x2d, 0x05, + 0x9c, 0x36, 0x37, 0xda, 0x19, 0xbd, 0x28, 0xf7, 0x0e, 0x65, 0x45, 0x40, + 0xb3, 0xc7, 0x11, 0x96, 0x9e, 0x30, 0x35, 0x15, 0xf9, 0x16, 0xbf, 0xf5, + 0xd4, 0xb0, 0x4c, 0x7e, 0x03, 0x7a, 0x84, 0xa0, 0xb0, 0xbb, 0x7a, 0xc9, + 0x78, 0xa3, 0xc0, 0x86, 0x08, 0x06, 0xc6, 0xbb, 0xd0, 0x10, 0x00, 0x00, + 0xe8, 0xab, 0x3f, 0x41, 0xa5, 0x6f, 0x17, 0x6e, 0x2b, 0x38, 0xac, 0x9d, + 0xf5, 0x04, 0x3e, 0xc3, 0xb8, 0x69, 0x05, 0xb5, 0x1c, 0x1f, 0x01, 0x24, + 0x3c, 0x6a, 0xa1, 0x75, 0xd4, 0x68, 0xdc, 0x93, 0xe7, 0x30, 0x84, 0x08, + 0x29, 0xf1, 0x29, 0xc8, 0xf1, 0xbd, 0x94, 0x71, 0x34, 0xf1, 0x9d, 0xb0, + 0x9a, 0x9a, 0x1a, 0x84, 0x63, 0xa3, 0x9e, 0xc8, 0x85, 0xe7, 0x46, 0xd5, + 0xf9, 0x36, 0x9f, 0x4d, 0xea, 0x98, 0xc4, 0xf0, 0x06, 0x51, 0xc2, 0x30, + 0x02, 0x08, 0xf6, 0x3f, 0x62, 0x30, 0x2a, 0x2e, 0x32, 0xa3, 0xb4, 0xd2, + 0xab, 0x8f, 0x8c, 0xaf, 0xe7, 0x31, 0x19, 0x6f, 0xbe, 0x63, 0x33, 0x89, + 0x67, 0x12, 0x39, 0x3b, 0xf7, 0x15, 0x4d, 0x0a, 0xa8, 0x55, 0x50, 0x26, + 0x19, 0x1c, 0x29, 0xcc, 0x44, 0x2a, 0xa7, 0x5e, 0xd9, 0xcf, 0xcc, 0x68, + 0x35, 0xbf, 0x64, 0x9a, 0x0f, 0x99, 0x66, 0xe5, 0x06, 0x00, 0x4a, 0x10, + 0x00, 0x00, 0x05, 0x05, 0x0f, 0x11, 0x03, 0xff, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x3e, 0x29, 0x8f, 0x3b, 0x7c, + 0xde, 0x28, 0xb0, 0x64, 0x93, 0xd0, 0x6f, 0xb2, 0xad, 0x6f, 0x95, 0x66, + 0xa9, 0x7f, 0xea, 0x6d, 0x3d, 0xe6, 0x62, 0x36, 0xb2, 0xaf, 0x1a, 0x35, + 0x15, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x9e, + 0x2a, 0x7c, 0x6f, 0x94, 0x8f, 0x17, 0x47, 0x4e, 0x34, 0xa7, 0xfc, 0x43, + 0xed, 0x03, 0x0f, 0x7c, 0x15, 0x63, 0xf1, 0xba, 0xbd, 0xdf, 0x63, 0x40, + 0xc8, 0x2e, 0x0e, 0x54, 0xa8, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x3d, + 0x2a, 0x57, 0x32, 0x92, 0x20, 0x31, 0xf3, 0x16, 0x65, 0x46, 0x0a, 0x6a, + 0x8f, 0xea, 0xeb, 0x2c, 0x88, 0x42, 0x53, 0x0d, 0xfc, 0x78, 0x03, 0x44, + 0x94, 0x25, 0xf4, 0x43, 0x51, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0xae, 0x7a, 0x12, 0xcc, 0x56, 0x57, 0xae, 0xd6, 0x03, + 0x73, 0xd6, 0x71, 0xe7, 0xb8, 0x61, 0xf6, 0xc0, 0x29, 0x3f, 0xbb, 0xcd, + 0xfe, 0x0c, 0x70, 0x35, 0x6f, 0xc6, 0x5d, 0x98, 0x3c, 0xe7, 0xe6, 0x78, + 0xf2, 0x57, 0x96, 0x54, 0x9c, 0xcc, 0x7d, 0xc3, 0x10, 0x76, 0x4e, 0xa1, + 0xf9, 0x09, 0x13, 0x4f, 0x05, 0xea, 0x72, 0xb8, 0x72, 0xdc, 0x6f, 0x79, + 0x59, 0x23, 0x59, 0xfb, 0xad, 0xe9, 0x20, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x05, 0x00, 0x62, 0x0e, 0x00, 0x00, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, 0x52, + 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x45, 0x38, 0x6a, 0x43, 0x43, 0x42, 0x4a, + 0x65, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x56, 0x41, 0x4a, + 0x76, 0x47, 0x49, 0x36, 0x66, 0x34, 0x50, 0x33, 0x7a, 0x4f, 0x65, 0x44, + 0x53, 0x4a, 0x59, 0x6c, 0x45, 0x73, 0x70, 0x4e, 0x4d, 0x49, 0x63, 0x55, + 0x71, 0x70, 0x4d, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, 0x4d, + 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x0a, 0x4d, 0x48, 0x41, 0x78, 0x49, + 0x6a, 0x41, 0x67, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x47, + 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, 0x48, 0x57, + 0x43, 0x42, 0x51, 0x51, 0x30, 0x73, 0x67, 0x55, 0x47, 0x78, 0x68, 0x64, + 0x47, 0x5a, 0x76, 0x63, 0x6d, 0x30, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, + 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x0a, + 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, + 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, + 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, + 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, + 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x0a, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, + 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, + 0x54, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x42, 0x34, 0x58, 0x44, 0x54, 0x49, + 0x7a, 0x4d, 0x44, 0x59, 0x79, 0x4e, 0x6a, 0x41, 0x34, 0x4e, 0x54, 0x55, + 0x77, 0x4f, 0x56, 0x6f, 0x58, 0x44, 0x54, 0x4d, 0x77, 0x4d, 0x44, 0x59, + 0x79, 0x4e, 0x6a, 0x41, 0x34, 0x4e, 0x54, 0x55, 0x77, 0x0a, 0x4f, 0x56, + 0x6f, 0x77, 0x63, 0x44, 0x45, 0x69, 0x4d, 0x43, 0x41, 0x47, 0x41, 0x31, + 0x55, 0x45, 0x41, 0x77, 0x77, 0x5a, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, + 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x42, 0x44, 0x53, 0x79, + 0x42, 0x44, 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, 0x5a, 0x70, 0x59, 0x32, + 0x46, 0x30, 0x5a, 0x54, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, + 0x55, 0x45, 0x0a, 0x43, 0x67, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, + 0x57, 0x77, 0x67, 0x51, 0x32, 0x39, 0x79, 0x63, 0x47, 0x39, 0x79, 0x59, + 0x58, 0x52, 0x70, 0x62, 0x32, 0x34, 0x78, 0x46, 0x44, 0x41, 0x53, 0x42, + 0x67, 0x4e, 0x56, 0x42, 0x41, 0x63, 0x4d, 0x43, 0x31, 0x4e, 0x68, 0x62, + 0x6e, 0x52, 0x68, 0x49, 0x45, 0x4e, 0x73, 0x59, 0x58, 0x4a, 0x68, 0x4d, + 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x0a, 0x56, 0x51, 0x51, 0x49, + 0x44, 0x41, 0x4a, 0x44, 0x51, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x42, 0x68, 0x4d, 0x43, 0x56, 0x56, 0x4d, 0x77, + 0x57, 0x54, 0x41, 0x54, 0x42, 0x67, 0x63, 0x71, 0x68, 0x6b, 0x6a, 0x4f, + 0x50, 0x51, 0x49, 0x42, 0x42, 0x67, 0x67, 0x71, 0x68, 0x6b, 0x6a, 0x4f, + 0x50, 0x51, 0x4d, 0x42, 0x42, 0x77, 0x4e, 0x43, 0x41, 0x41, 0x51, 0x52, + 0x0a, 0x59, 0x52, 0x39, 0x55, 0x73, 0x48, 0x4a, 0x56, 0x6f, 0x72, 0x67, + 0x35, 0x30, 0x48, 0x6d, 0x6e, 0x52, 0x33, 0x5a, 0x35, 0x4e, 0x6e, 0x35, + 0x47, 0x56, 0x71, 0x50, 0x48, 0x51, 0x4d, 0x2b, 0x76, 0x77, 0x73, 0x4c, + 0x45, 0x75, 0x73, 0x65, 0x4d, 0x64, 0x68, 0x42, 0x38, 0x6e, 0x30, 0x63, + 0x6c, 0x77, 0x34, 0x63, 0x64, 0x45, 0x6b, 0x6f, 0x56, 0x46, 0x36, 0x4f, + 0x38, 0x45, 0x68, 0x47, 0x52, 0x0a, 0x37, 0x59, 0x67, 0x6b, 0x72, 0x66, + 0x4e, 0x78, 0x65, 0x61, 0x57, 0x61, 0x4b, 0x44, 0x4a, 0x42, 0x32, 0x62, + 0x48, 0x64, 0x6f, 0x34, 0x49, 0x44, 0x44, 0x44, 0x43, 0x43, 0x41, 0x77, + 0x67, 0x77, 0x48, 0x77, 0x59, 0x44, 0x56, 0x52, 0x30, 0x6a, 0x42, 0x42, + 0x67, 0x77, 0x46, 0x6f, 0x41, 0x55, 0x6c, 0x57, 0x39, 0x64, 0x7a, 0x62, + 0x30, 0x62, 0x34, 0x65, 0x6c, 0x41, 0x53, 0x63, 0x6e, 0x55, 0x0a, 0x39, + 0x44, 0x50, 0x4f, 0x41, 0x56, 0x63, 0x4c, 0x33, 0x6c, 0x51, 0x77, 0x61, + 0x77, 0x59, 0x44, 0x56, 0x52, 0x30, 0x66, 0x42, 0x47, 0x51, 0x77, 0x59, + 0x6a, 0x42, 0x67, 0x6f, 0x46, 0x36, 0x67, 0x58, 0x49, 0x5a, 0x61, 0x61, + 0x48, 0x52, 0x30, 0x63, 0x48, 0x4d, 0x36, 0x4c, 0x79, 0x39, 0x68, 0x63, + 0x47, 0x6b, 0x75, 0x64, 0x48, 0x4a, 0x31, 0x63, 0x33, 0x52, 0x6c, 0x5a, + 0x48, 0x4e, 0x6c, 0x0a, 0x63, 0x6e, 0x5a, 0x70, 0x59, 0x32, 0x56, 0x7a, + 0x4c, 0x6d, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x4c, 0x6d, 0x4e, 0x76, + 0x62, 0x53, 0x39, 0x7a, 0x5a, 0x33, 0x67, 0x76, 0x59, 0x32, 0x56, 0x79, + 0x64, 0x47, 0x6c, 0x6d, 0x61, 0x57, 0x4e, 0x68, 0x64, 0x47, 0x6c, 0x76, + 0x62, 0x69, 0x39, 0x32, 0x4e, 0x43, 0x39, 0x77, 0x59, 0x32, 0x74, 0x6a, + 0x63, 0x6d, 0x77, 0x2f, 0x59, 0x32, 0x45, 0x39, 0x0a, 0x63, 0x47, 0x78, + 0x68, 0x64, 0x47, 0x5a, 0x76, 0x63, 0x6d, 0x30, 0x6d, 0x5a, 0x57, 0x35, + 0x6a, 0x62, 0x32, 0x52, 0x70, 0x62, 0x6d, 0x63, 0x39, 0x5a, 0x47, 0x56, + 0x79, 0x4d, 0x42, 0x30, 0x47, 0x41, 0x31, 0x55, 0x64, 0x44, 0x67, 0x51, + 0x57, 0x42, 0x42, 0x51, 0x73, 0x6b, 0x6a, 0x54, 0x5a, 0x65, 0x48, 0x6a, + 0x34, 0x77, 0x55, 0x49, 0x57, 0x79, 0x61, 0x61, 0x71, 0x4f, 0x5a, 0x49, + 0x55, 0x0a, 0x70, 0x43, 0x55, 0x63, 0x62, 0x54, 0x41, 0x4f, 0x42, 0x67, + 0x4e, 0x56, 0x48, 0x51, 0x38, 0x42, 0x41, 0x66, 0x38, 0x45, 0x42, 0x41, + 0x4d, 0x43, 0x42, 0x73, 0x41, 0x77, 0x44, 0x41, 0x59, 0x44, 0x56, 0x52, + 0x30, 0x54, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, 0x49, 0x77, 0x41, 0x44, + 0x43, 0x43, 0x41, 0x6a, 0x6b, 0x47, 0x43, 0x53, 0x71, 0x47, 0x53, 0x49, + 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x0a, 0x41, 0x51, 0x53, 0x43, 0x41, + 0x69, 0x6f, 0x77, 0x67, 0x67, 0x49, 0x6d, 0x4d, 0x42, 0x34, 0x47, 0x43, + 0x69, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, + 0x51, 0x45, 0x45, 0x45, 0x4e, 0x63, 0x59, 0x70, 0x49, 0x71, 0x50, 0x41, + 0x30, 0x4e, 0x70, 0x39, 0x5a, 0x4f, 0x64, 0x42, 0x4f, 0x4b, 0x77, 0x33, + 0x46, 0x34, 0x77, 0x67, 0x67, 0x46, 0x6a, 0x42, 0x67, 0x6f, 0x71, 0x0a, + 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, + 0x4d, 0x49, 0x49, 0x42, 0x55, 0x7a, 0x41, 0x51, 0x42, 0x67, 0x73, 0x71, + 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, + 0x41, 0x51, 0x49, 0x42, 0x42, 0x54, 0x41, 0x51, 0x42, 0x67, 0x73, 0x71, + 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, + 0x41, 0x67, 0x49, 0x42, 0x0a, 0x42, 0x54, 0x41, 0x51, 0x42, 0x67, 0x73, + 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, + 0x43, 0x41, 0x77, 0x49, 0x42, 0x41, 0x6a, 0x41, 0x51, 0x42, 0x67, 0x73, + 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, + 0x43, 0x42, 0x41, 0x49, 0x42, 0x41, 0x6a, 0x41, 0x51, 0x42, 0x67, 0x73, + 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x0a, 0x44, 0x51, + 0x45, 0x43, 0x42, 0x51, 0x49, 0x42, 0x41, 0x7a, 0x41, 0x51, 0x42, 0x67, + 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, + 0x45, 0x43, 0x42, 0x67, 0x49, 0x42, 0x41, 0x54, 0x41, 0x51, 0x42, 0x67, + 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, + 0x45, 0x43, 0x42, 0x77, 0x49, 0x42, 0x41, 0x44, 0x41, 0x51, 0x42, 0x67, + 0x73, 0x71, 0x0a, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x43, 0x43, 0x41, 0x49, 0x42, 0x41, 0x7a, 0x41, 0x51, 0x42, + 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x43, 0x43, 0x51, 0x49, 0x42, 0x41, 0x44, 0x41, 0x51, 0x42, + 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x43, 0x43, 0x67, 0x49, 0x42, 0x0a, 0x41, 0x44, 0x41, 0x51, + 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, + 0x44, 0x51, 0x45, 0x43, 0x43, 0x77, 0x49, 0x42, 0x41, 0x44, 0x41, 0x51, + 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, + 0x44, 0x51, 0x45, 0x43, 0x44, 0x41, 0x49, 0x42, 0x41, 0x44, 0x41, 0x51, + 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, + 0x0a, 0x44, 0x51, 0x45, 0x43, 0x44, 0x51, 0x49, 0x42, 0x41, 0x44, 0x41, + 0x51, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, + 0x42, 0x44, 0x51, 0x45, 0x43, 0x44, 0x67, 0x49, 0x42, 0x41, 0x44, 0x41, + 0x51, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, + 0x42, 0x44, 0x51, 0x45, 0x43, 0x44, 0x77, 0x49, 0x42, 0x41, 0x44, 0x41, + 0x51, 0x42, 0x67, 0x73, 0x71, 0x0a, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, + 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x45, 0x41, 0x49, 0x42, 0x41, 0x44, + 0x41, 0x51, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, + 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x45, 0x51, 0x49, 0x42, 0x43, 0x7a, + 0x41, 0x66, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, + 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x45, 0x67, 0x51, 0x51, 0x0a, 0x42, + 0x51, 0x55, 0x43, 0x41, 0x67, 0x4d, 0x42, 0x41, 0x41, 0x4d, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x44, 0x41, 0x51, 0x42, + 0x67, 0x6f, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x44, 0x42, 0x41, 0x49, 0x41, 0x41, 0x44, 0x41, 0x55, 0x42, + 0x67, 0x6f, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x45, 0x0a, 0x42, 0x41, 0x59, 0x41, 0x67, 0x47, 0x38, 0x46, + 0x41, 0x41, 0x41, 0x77, 0x44, 0x77, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, + 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x42, 0x51, 0x6f, 0x42, + 0x41, 0x54, 0x41, 0x65, 0x42, 0x67, 0x6f, 0x71, 0x68, 0x6b, 0x69, 0x47, + 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x47, 0x42, 0x42, 0x41, 0x68, + 0x64, 0x62, 0x6e, 0x34, 0x42, 0x63, 0x75, 0x2f, 0x0a, 0x41, 0x54, 0x33, + 0x71, 0x34, 0x57, 0x45, 0x73, 0x6d, 0x6b, 0x55, 0x64, 0x4d, 0x45, 0x51, + 0x47, 0x43, 0x69, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, + 0x4e, 0x41, 0x51, 0x63, 0x77, 0x4e, 0x6a, 0x41, 0x51, 0x42, 0x67, 0x73, + 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, + 0x48, 0x41, 0x51, 0x45, 0x42, 0x2f, 0x7a, 0x41, 0x51, 0x42, 0x67, 0x73, + 0x71, 0x0a, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, + 0x45, 0x48, 0x41, 0x67, 0x45, 0x42, 0x41, 0x44, 0x41, 0x51, 0x42, 0x67, + 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, + 0x45, 0x48, 0x41, 0x77, 0x45, 0x42, 0x2f, 0x7a, 0x41, 0x4b, 0x42, 0x67, + 0x67, 0x71, 0x68, 0x6b, 0x6a, 0x4f, 0x50, 0x51, 0x51, 0x44, 0x41, 0x67, + 0x4e, 0x4a, 0x41, 0x44, 0x42, 0x47, 0x0a, 0x41, 0x69, 0x45, 0x41, 0x75, + 0x33, 0x53, 0x56, 0x34, 0x53, 0x42, 0x4a, 0x59, 0x2b, 0x59, 0x34, 0x4d, + 0x43, 0x70, 0x39, 0x58, 0x6f, 0x75, 0x5a, 0x6a, 0x64, 0x54, 0x5a, 0x53, + 0x39, 0x4f, 0x35, 0x31, 0x49, 0x51, 0x34, 0x4d, 0x71, 0x32, 0x33, 0x77, + 0x67, 0x32, 0x63, 0x33, 0x34, 0x59, 0x43, 0x49, 0x51, 0x44, 0x6d, 0x46, + 0x79, 0x37, 0x63, 0x41, 0x67, 0x2b, 0x7a, 0x78, 0x75, 0x78, 0x53, 0x0a, + 0x63, 0x36, 0x41, 0x5a, 0x2b, 0x54, 0x4e, 0x2f, 0x4e, 0x67, 0x76, 0x48, + 0x56, 0x72, 0x74, 0x42, 0x42, 0x6d, 0x71, 0x56, 0x7a, 0x30, 0x73, 0x73, + 0x55, 0x4d, 0x4c, 0x36, 0x2b, 0x67, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, + 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6c, 0x6a, 0x43, 0x43, 0x41, + 0x6a, 0x32, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x56, 0x41, + 0x4a, 0x56, 0x76, 0x58, 0x63, 0x32, 0x39, 0x47, 0x2b, 0x48, 0x70, 0x51, + 0x45, 0x6e, 0x4a, 0x31, 0x50, 0x51, 0x7a, 0x7a, 0x67, 0x46, 0x58, 0x43, + 0x39, 0x35, 0x55, 0x4d, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, + 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x0a, 0x4d, 0x47, 0x67, 0x78, + 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, + 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, 0x48, + 0x57, 0x43, 0x42, 0x53, 0x62, 0x32, 0x39, 0x30, 0x49, 0x45, 0x4e, 0x42, + 0x4d, 0x52, 0x6f, 0x77, 0x47, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x4b, + 0x44, 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x62, 0x43, 0x42, 0x44, + 0x0a, 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, 0x68, 0x64, 0x47, 0x6c, + 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, + 0x45, 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, + 0x67, 0x51, 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, 0x41, + 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, + 0x42, 0x4d, 0x51, 0x73, 0x77, 0x0a, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, + 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, 0x41, 0x65, 0x46, 0x77, + 0x30, 0x78, 0x4f, 0x44, 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, 0x4d, 0x44, + 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, 0x46, 0x77, 0x30, 0x7a, 0x4d, 0x7a, + 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, + 0x42, 0x61, 0x4d, 0x48, 0x41, 0x78, 0x49, 0x6a, 0x41, 0x67, 0x0a, 0x42, + 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x47, 0x55, 0x6c, 0x75, 0x64, + 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, 0x48, 0x57, 0x43, 0x42, 0x51, 0x51, + 0x30, 0x73, 0x67, 0x55, 0x47, 0x78, 0x68, 0x64, 0x47, 0x5a, 0x76, 0x63, + 0x6d, 0x30, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, + 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, + 0x47, 0x56, 0x73, 0x0a, 0x49, 0x45, 0x4e, 0x76, 0x63, 0x6e, 0x42, 0x76, + 0x63, 0x6d, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, + 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, + 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, + 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, + 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x0a, 0x43, 0x7a, 0x41, + 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, 0x56, + 0x54, 0x4d, 0x46, 0x6b, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4b, 0x6f, 0x5a, + 0x49, 0x7a, 0x6a, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4b, 0x6f, 0x5a, + 0x49, 0x7a, 0x6a, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, + 0x45, 0x4e, 0x53, 0x42, 0x2f, 0x37, 0x74, 0x32, 0x31, 0x6c, 0x58, 0x53, + 0x4f, 0x0a, 0x32, 0x43, 0x75, 0x7a, 0x70, 0x78, 0x77, 0x37, 0x34, 0x65, + 0x4a, 0x42, 0x37, 0x32, 0x45, 0x79, 0x44, 0x47, 0x67, 0x57, 0x35, 0x72, + 0x58, 0x43, 0x74, 0x78, 0x32, 0x74, 0x56, 0x54, 0x4c, 0x71, 0x36, 0x68, + 0x4b, 0x6b, 0x36, 0x7a, 0x2b, 0x55, 0x69, 0x52, 0x5a, 0x43, 0x6e, 0x71, + 0x52, 0x37, 0x70, 0x73, 0x4f, 0x76, 0x67, 0x71, 0x46, 0x65, 0x53, 0x78, + 0x6c, 0x6d, 0x54, 0x6c, 0x4a, 0x6c, 0x0a, 0x65, 0x54, 0x6d, 0x69, 0x32, + 0x57, 0x59, 0x7a, 0x33, 0x71, 0x4f, 0x42, 0x75, 0x7a, 0x43, 0x42, 0x75, + 0x44, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x53, 0x4d, 0x45, 0x47, + 0x44, 0x41, 0x57, 0x67, 0x42, 0x51, 0x69, 0x5a, 0x51, 0x7a, 0x57, 0x57, + 0x70, 0x30, 0x30, 0x69, 0x66, 0x4f, 0x44, 0x74, 0x4a, 0x56, 0x53, 0x76, + 0x31, 0x41, 0x62, 0x4f, 0x53, 0x63, 0x47, 0x72, 0x44, 0x42, 0x53, 0x0a, + 0x42, 0x67, 0x4e, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, 0x7a, 0x42, 0x4a, + 0x4d, 0x45, 0x65, 0x67, 0x52, 0x61, 0x42, 0x44, 0x68, 0x6b, 0x46, 0x6f, + 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, 0x76, 0x4c, 0x32, 0x4e, 0x6c, + 0x63, 0x6e, 0x52, 0x70, 0x5a, 0x6d, 0x6c, 0x6a, 0x59, 0x58, 0x52, 0x6c, + 0x63, 0x79, 0x35, 0x30, 0x63, 0x6e, 0x56, 0x7a, 0x64, 0x47, 0x56, 0x6b, + 0x63, 0x32, 0x56, 0x79, 0x0a, 0x64, 0x6d, 0x6c, 0x6a, 0x5a, 0x58, 0x4d, + 0x75, 0x61, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x75, 0x59, 0x32, 0x39, + 0x74, 0x4c, 0x30, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, 0x30, 0x64, + 0x59, 0x55, 0x6d, 0x39, 0x76, 0x64, 0x45, 0x4e, 0x42, 0x4c, 0x6d, 0x52, + 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, + 0x45, 0x46, 0x67, 0x51, 0x55, 0x6c, 0x57, 0x39, 0x64, 0x0a, 0x7a, 0x62, + 0x30, 0x62, 0x34, 0x65, 0x6c, 0x41, 0x53, 0x63, 0x6e, 0x55, 0x39, 0x44, + 0x50, 0x4f, 0x41, 0x56, 0x63, 0x4c, 0x33, 0x6c, 0x51, 0x77, 0x44, 0x67, + 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, + 0x51, 0x44, 0x41, 0x67, 0x45, 0x47, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, + 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, 0x2f, 0x77, 0x51, 0x49, 0x4d, 0x41, + 0x59, 0x42, 0x0a, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, 0x41, 0x77, 0x43, + 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, + 0x77, 0x49, 0x44, 0x52, 0x77, 0x41, 0x77, 0x52, 0x41, 0x49, 0x67, 0x58, + 0x73, 0x56, 0x6b, 0x69, 0x30, 0x77, 0x2b, 0x69, 0x36, 0x56, 0x59, 0x47, + 0x57, 0x33, 0x55, 0x46, 0x2f, 0x32, 0x32, 0x75, 0x61, 0x58, 0x65, 0x30, + 0x59, 0x4a, 0x44, 0x6a, 0x31, 0x55, 0x65, 0x0a, 0x6e, 0x41, 0x2b, 0x54, + 0x6a, 0x44, 0x31, 0x61, 0x69, 0x35, 0x63, 0x43, 0x49, 0x43, 0x59, 0x62, + 0x31, 0x53, 0x41, 0x6d, 0x44, 0x35, 0x78, 0x6b, 0x66, 0x54, 0x56, 0x70, + 0x76, 0x6f, 0x34, 0x55, 0x6f, 0x79, 0x69, 0x53, 0x59, 0x78, 0x72, 0x44, + 0x57, 0x4c, 0x6d, 0x55, 0x52, 0x34, 0x43, 0x49, 0x39, 0x4e, 0x4b, 0x79, + 0x66, 0x50, 0x4e, 0x2b, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, + 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, + 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, + 0x49, 0x49, 0x43, 0x6a, 0x7a, 0x43, 0x43, 0x41, 0x6a, 0x53, 0x67, 0x41, + 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x49, 0x6d, 0x55, 0x4d, 0x31, + 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, 0x56, 0x55, + 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, 0x71, 0x77, 0x77, 0x43, + 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, + 0x77, 0x49, 0x77, 0x0a, 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, + 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x4a, 0x76, + 0x62, 0x33, 0x51, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, + 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, + 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x0a, 0x63, 0x6e, 0x42, + 0x76, 0x63, 0x6d, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, + 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, + 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, + 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, + 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, + 0x4a, 0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, + 0x56, 0x54, 0x4d, 0x42, 0x34, 0x58, 0x44, 0x54, 0x45, 0x34, 0x4d, 0x44, + 0x55, 0x79, 0x4d, 0x54, 0x45, 0x77, 0x4e, 0x44, 0x55, 0x78, 0x4d, 0x46, + 0x6f, 0x58, 0x44, 0x54, 0x51, 0x35, 0x4d, 0x54, 0x49, 0x7a, 0x4d, 0x54, + 0x49, 0x7a, 0x4e, 0x54, 0x6b, 0x31, 0x4f, 0x56, 0x6f, 0x77, 0x61, 0x44, + 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x0a, 0x41, 0x31, 0x55, 0x45, 0x41, + 0x77, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, + 0x30, 0x64, 0x59, 0x49, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, + 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, + 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, + 0x45, 0x4e, 0x76, 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x0a, + 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, + 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, + 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, + 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, + 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, + 0x42, 0x41, 0x59, 0x54, 0x0a, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x46, 0x6b, + 0x77, 0x45, 0x77, 0x59, 0x48, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, + 0x43, 0x41, 0x51, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, + 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x43, 0x36, 0x6e, + 0x45, 0x77, 0x4d, 0x44, 0x49, 0x59, 0x5a, 0x4f, 0x6a, 0x2f, 0x69, 0x50, + 0x57, 0x73, 0x43, 0x7a, 0x61, 0x45, 0x4b, 0x69, 0x37, 0x0a, 0x31, 0x4f, + 0x69, 0x4f, 0x53, 0x4c, 0x52, 0x46, 0x68, 0x57, 0x47, 0x6a, 0x62, 0x6e, + 0x42, 0x56, 0x4a, 0x66, 0x56, 0x6e, 0x6b, 0x59, 0x34, 0x75, 0x33, 0x49, + 0x6a, 0x6b, 0x44, 0x59, 0x59, 0x4c, 0x30, 0x4d, 0x78, 0x4f, 0x34, 0x6d, + 0x71, 0x73, 0x79, 0x59, 0x6a, 0x6c, 0x42, 0x61, 0x6c, 0x54, 0x56, 0x59, + 0x78, 0x46, 0x50, 0x32, 0x73, 0x4a, 0x42, 0x4b, 0x35, 0x7a, 0x6c, 0x4b, + 0x4f, 0x42, 0x0a, 0x75, 0x7a, 0x43, 0x42, 0x75, 0x44, 0x41, 0x66, 0x42, + 0x67, 0x4e, 0x56, 0x48, 0x53, 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, + 0x42, 0x51, 0x69, 0x5a, 0x51, 0x7a, 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, + 0x66, 0x4f, 0x44, 0x74, 0x4a, 0x56, 0x53, 0x76, 0x31, 0x41, 0x62, 0x4f, + 0x53, 0x63, 0x47, 0x72, 0x44, 0x42, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x48, + 0x52, 0x38, 0x45, 0x53, 0x7a, 0x42, 0x4a, 0x0a, 0x4d, 0x45, 0x65, 0x67, + 0x52, 0x61, 0x42, 0x44, 0x68, 0x6b, 0x46, 0x6f, 0x64, 0x48, 0x52, 0x77, + 0x63, 0x7a, 0x6f, 0x76, 0x4c, 0x32, 0x4e, 0x6c, 0x63, 0x6e, 0x52, 0x70, + 0x5a, 0x6d, 0x6c, 0x6a, 0x59, 0x58, 0x52, 0x6c, 0x63, 0x79, 0x35, 0x30, + 0x63, 0x6e, 0x56, 0x7a, 0x64, 0x47, 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, + 0x64, 0x6d, 0x6c, 0x6a, 0x5a, 0x58, 0x4d, 0x75, 0x61, 0x57, 0x35, 0x30, + 0x0a, 0x5a, 0x57, 0x77, 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, 0x30, 0x6c, + 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, 0x30, 0x64, 0x59, 0x55, 0x6d, 0x39, + 0x76, 0x64, 0x45, 0x4e, 0x42, 0x4c, 0x6d, 0x52, 0x6c, 0x63, 0x6a, 0x41, + 0x64, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, 0x67, 0x51, + 0x55, 0x49, 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, + 0x7a, 0x67, 0x37, 0x53, 0x56, 0x0a, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, + 0x6b, 0x6e, 0x42, 0x71, 0x77, 0x77, 0x44, 0x67, 0x59, 0x44, 0x56, 0x52, + 0x30, 0x50, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, 0x41, 0x67, + 0x45, 0x47, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, + 0x45, 0x42, 0x2f, 0x77, 0x51, 0x49, 0x4d, 0x41, 0x59, 0x42, 0x41, 0x66, + 0x38, 0x43, 0x41, 0x51, 0x45, 0x77, 0x43, 0x67, 0x59, 0x49, 0x0a, 0x4b, + 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, + 0x51, 0x41, 0x77, 0x52, 0x67, 0x49, 0x68, 0x41, 0x4f, 0x57, 0x2f, 0x35, + 0x51, 0x6b, 0x52, 0x2b, 0x53, 0x39, 0x43, 0x69, 0x53, 0x44, 0x63, 0x4e, + 0x6f, 0x6f, 0x77, 0x4c, 0x75, 0x50, 0x52, 0x4c, 0x73, 0x57, 0x47, 0x66, + 0x2f, 0x59, 0x69, 0x37, 0x47, 0x53, 0x58, 0x39, 0x34, 0x42, 0x67, 0x77, + 0x54, 0x77, 0x67, 0x0a, 0x41, 0x69, 0x45, 0x41, 0x34, 0x4a, 0x30, 0x6c, + 0x72, 0x48, 0x6f, 0x4d, 0x73, 0x2b, 0x58, 0x6f, 0x35, 0x6f, 0x2f, 0x73, + 0x58, 0x36, 0x4f, 0x39, 0x51, 0x57, 0x78, 0x48, 0x52, 0x41, 0x76, 0x5a, + 0x55, 0x47, 0x4f, 0x64, 0x52, 0x51, 0x37, 0x63, 0x76, 0x71, 0x52, 0x58, + 0x61, 0x71, 0x49, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, + 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x00, + } validTDXQuote = []byte{ 0x04, 0x00, 0x02, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, @@ -334,7 +748,8 @@ var ( validTDXCertChain = [][]byte{} validTDXMeasurement = []byte{} - rootCAFingerprint = "BF85A53FC08F84CB1F73A4F75F48AF566E30AC040699BA0EC1B8D593C05B56FC" + rootCAFingerprint = "BF85A53FC08F84CB1F73A4F75F48AF566E30AC040699BA0EC1B8D593C05B56FC" + aisecCertrootCAFingerprint = "44a0196b2b99f889b8e149e95b807a350e7424964399e885a7cbb8ccfab674d3" ) func Test_verifyTdxMeasurements(t *testing.T) { From 17dcf126105ac6b04727614278151a4c66e123b1 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Sun, 5 Nov 2023 15:37:40 +0000 Subject: [PATCH 04/23] Fixed SGXExtension parsing --- attestationreport/sgx_structs.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/attestationreport/sgx_structs.go b/attestationreport/sgx_structs.go index 1cb62d0e..3180aa32 100644 --- a/attestationreport/sgx_structs.go +++ b/attestationreport/sgx_structs.go @@ -258,23 +258,18 @@ type PlatformInstanceId struct { Value []byte } +// ConfigurationId determines the type of the ConfigurationValue: +// [0]: dynamicPlatform, [1]: cachedKeys, [2]: sMTenabled type Configuration struct { Id asn1.ObjectIdentifier Value []struct { ConfigurationId asn1.ObjectIdentifier - ConfigurationValue ConfigurationValue + ConfigurationValue bool } } -type ConfigurationValue struct { - DynamicPlatform bool - CachedKeys bool - SMTEnabled bool -} - // ------------------------- end SGX Extensions ------------------------- -// expects enclave Identity structure in JSON format func ParseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { var sgx_extensions SGXExtensionsValue @@ -301,11 +296,11 @@ func ParseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { // parse optional parameters rest, err = asn1.Unmarshal(rest, &sgx_extensions.PlatformInstanceId) if err != nil || len(rest) == 0 { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions TCB %v", err) + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions PlatfromInstanceId %v", err) } rest, err = asn1.Unmarshal(rest, &sgx_extensions.Configuration) if err != nil || len(rest) != 0 { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions TCB %v", err) + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions Configuration %v", err) } } From 9416b981e9bcea53ffa8e7d41ff22faa1743c2d9 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Sun, 5 Nov 2023 20:14:36 +0000 Subject: [PATCH 05/23] implemented basic tdx quote verification --- attestationreport/PCKCertCRL | Bin 303 -> 2663 bytes attestationreport/attestationreport.go | 11 +- attestationreport/sgx.go | 107 ++++++++-------- attestationreport/tdx.go | 164 +++++++++++++++++++++++-- attestationreport/tdx_struct.go | 8 +- attestationreport/tdx_test.go | 39 +++++- 6 files changed, 255 insertions(+), 74 deletions(-) diff --git a/attestationreport/PCKCertCRL b/attestationreport/PCKCertCRL index ff10332b2878583e067bbf1dd35b86ec97cc57e8..1a59b64109e9592936bf47cb97ac3a2f06d616f1 100644 GIT binary patch literal 2663 zcma*pX;2eq7zc1jkPyHF338+XDuU2L+!(^C7K}(T2u2XlSj7l(*qA^P#CQ^vV~A9Q zavwsAgvg%w4SwkXcQ1nS`OIiqeJ!3#+G!BU@zM?#DAMDgBoDVV6z0#T;6_L zsxQe3uz>a$8k>CHL*??~xjdF2j>{q8fThqqjImJ&iz8s!QrRpXi(~{~p{oYQD3JfX z&^JYehL|s0O(u~53ds#{CwVgeTCVL`xLXi_I*be@587hvjkp& z%@BO5#z}c?iyW!i1%(um^#E_C2_8Ysn7N?|YpSL7Cyf|feKW1d&|Qza0*!4IYIx?-LYEd+(5C4(atxzmvMdD=^CO0&NHTk7GW?p8a>2zxx%w ztNCyk9zj*jid`Ch$>|!(l(ufxPF`P#-WYufS#{GGb;0txan;om+d5mrul35_MoNIDqixLJtdN8mD8wTrh~<_2#XgV9$qM%_jIL5V5oh{%A>% z-uaaSx^n&ls-;YkjEFpQ+EVfP*~EE|ymB^i9(3hMjOgOwOBb@? z5foJxljtpa5ue>4iJbjylOMhHaS<6_fl;oCYljG>VIuz#i)P%r7EYfgb{HN(QK@~m zJ@82qb)ZRUIa4J#J2hXMj;wMsj#T+tAIkB*|LK9K#EGuem0gBc-~m?@KG}037n?sP z)_S?uwK@FM_AneCK?lr?o|MpOX)&>-%51NWP0?Q`5c$aJLc7}%c^|u_aOaXJQB87R zh0PEKS!vV(&g5RV^S9a6e1&zfe{I;coGOLe>4TJz-9R3}7a^W!3?c=^lzapprX#n9Krj&@ zBGZfjvO|$ad)|5T#a)R*=7}D|$iD;UH8(%y2p^j*>+kW@))aQ1t4th0OiB>aViU1; z0k4EFTAqwe;Wbgs|Kk%~c0l9o+3q&JSIpt(H$K|HTp4Kt9|iSKskZ+p&70zGzkD(# zw|t%Jn41+BS)GdbZgu$3f<}SU^D(@1uZj=s-iNFzk8I%)+ttdIr^n?J!3#>;Zj8}j zRmCd6Ez0MUt1^$3&)OI1`+clQ WQ*32PJ7(6VUCQ7jgdQlgSN{nY&*Z28 delta 189 zcmaDZvYyGxpovl2pz$ITBclNq8;4e#$2nUTW+sC|LuCU+Hs(+kW*#Zeypq%$g<$sx zg#c%7g@B^` compare nullptr - if certChain.RootCACert == nil { + if quoteCerts.RootCACert == nil { msg := "root cert is null" result.Summary.setFalse(&msg) return result, false } // (from DCAP Library): check root public key - if !reflect.DeepEqual(certChain.RootCACert.PublicKey, certs.RootCACert.PublicKey) { + if !reflect.DeepEqual(quoteCerts.RootCACert.PublicKey, referenceCerts.RootCACert.PublicKey) { msg := "root cert public key didn't match" result.Summary.setFalse(&msg) return result, false } - // (from DCAP Library): parse and verify PCK certificate chain - x509CertChains, err := internal.VerifyCertChain( - []*x509.Certificate{certChain.PCKCert, certChain.IntermediateCert}, - []*x509.Certificate{certChain.RootCACert}) - if err != nil { - msg := fmt.Sprintf("Failed to verify pck certificate chain: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - // download CRLs from PCS - root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) - if err != nil { - msg := fmt.Sprintf("downloading ROOT CA CRL from PCS failed: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - pck_crl_uri := fmt.Sprintf(PCS_PCK_CERT_CRL_URI, "processor") - pck_crl, err := fetchCRL(pck_crl_uri, PCK_CERT_CRL_NAME) + // (from DCAP Library): parse and verify the entire PCK certificate chain + x509CertChains, err := VerifyIntelCertChainFull(quoteCerts) if err != nil { - msg := fmt.Sprintf("downloading PCK Cert CRL from PCS failed: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - // perform CRL checks (signature + values) - res, err := CrlCheck(root_ca_crl, certChain.RootCACert, certChain.RootCACert) - if !res || err != nil { - msg := fmt.Sprintf("CRL check on rootCert failed: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - res, err = CrlCheck(pck_crl, certChain.PCKCert, certChain.IntermediateCert) - if !res || err != nil { - msg := fmt.Sprintf("CRL check on pckCert failed: %v", err) + msg := fmt.Sprintf("Failed to verify certificates from the quote: %v", err) result.Summary.setFalse(&msg) return result, false } @@ -244,13 +212,12 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ // (from DCAP Library): parse and verify TcbInfo object tcbInfo, err := ParseTcbInfo(sgxReferenceValue.Sgx.Collateral.TcbInfo) if err != nil { - log.Trace("tcb_info:", sgxReferenceValue.Sgx.Collateral.TcbInfo) msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) result.Summary.setFalse(&msg) return result, false } - err = verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), certs.TCBSigningCert) + err = verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) @@ -266,7 +233,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ } err = VerifyQEIdentity(&sgxQuote.QuoteSignatureData.QEReport, &qeIdentity, - string(sgxReferenceValue.Sgx.Collateral.QeIdentity), certs.TCBSigningCert, SGX_QUOTE_TYPE) + string(sgxReferenceValue.Sgx.Collateral.QeIdentity), referenceCerts.TCBSigningCert, SGX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify QE Identity structure: %v", err) result.Summary.setFalse(&msg) @@ -275,7 +242,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ // Verify Quote Signature sig, ret := VerifyIntelQuoteSignature(sgxM.Report, sgxQuote.QuoteSignatureData, - sgxQuote.QuoteSignatureDataLen, int(sgxQuote.QuoteHeader.AttestationKeyType), certs, + sgxQuote.QuoteSignatureDataLen, int(sgxQuote.QuoteHeader.AttestationKeyType), referenceCerts, sgxReferenceValue.Sgx.CAfingerprint, quoteType) if !ret { msg := fmt.Sprintf("Failed to verify Quote Signature: %v", sig) @@ -292,7 +259,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ } // Verify Quote Body values - err = VerifyEnclaveReportValues(&sgxQuote.ISVEnclaveReport, &tcbInfo, &certChain, &sgxReferenceValue, result) + err = VerifySgxQuoteBody(&sgxQuote.ISVEnclaveReport, &tcbInfo, "eCerts, &sgxReferenceValue, result) if err != nil { msg := fmt.Sprintf("Failed to verify Enclave Report Values: %v", err) result.Summary.setFalse(&msg) @@ -534,7 +501,7 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer } // Only for SGX report body -func VerifyEnclaveReportValues(body *EnclaveReportBody, tcbInfo *TcbInfo, +func VerifySgxQuoteBody(body *EnclaveReportBody, tcbInfo *TcbInfo, certs *SgxCertificates, sgxReferenceValue *ReferenceValue, result *SgxMeasurementResult) error { if body == nil || tcbInfo == nil || certs == nil || sgxReferenceValue == nil || result == nil { return fmt.Errorf("invalid function parameter (null pointer exception)") @@ -806,3 +773,45 @@ func downloadCRL(uri string, name string) (*x509.RevocationList, error) { return crl, nil } + +// verifies the given SGX certificate chain, fetches CRLs and chechs if the certs are outdated +// writes results back to verification report +func VerifyIntelCertChainFull(quoteCerts SgxCertificates) ([][]*x509.Certificate, error) { + // verify PCK certificate chain + x509CertChains, err := internal.VerifyCertChain( + []*x509.Certificate{quoteCerts.PCKCert, quoteCerts.IntermediateCert}, + []*x509.Certificate{quoteCerts.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify pck certificate chain: %v", err) + return nil, errors.New(msg) + } + + // download CRLs from PCS + root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) + if err != nil { + msg := fmt.Sprintf("downloading ROOT CA CRL from PCS failed: %v", err) + return nil, errors.New(msg) + } + + pck_crl_uri := fmt.Sprintf(PCS_PCK_CERT_CRL_URI, "processor") + pck_crl, err := fetchCRL(pck_crl_uri, PCK_CERT_CRL_NAME) + if err != nil { + msg := fmt.Sprintf("downloading PCK Cert CRL from PCS failed: %v", err) + return nil, errors.New(msg) + } + + // perform CRL checks (signature + values) + res, err := CrlCheck(root_ca_crl, quoteCerts.RootCACert, quoteCerts.RootCACert) + if !res || err != nil { + msg := fmt.Sprintf("CRL check on rootCert failed: %v", err) + return nil, errors.New(msg) + } + + res, err = CrlCheck(pck_crl, quoteCerts.PCKCert, quoteCerts.IntermediateCert) + if !res || err != nil { + msg := fmt.Sprintf("CRL check on pckCert failed: %v", err) + return nil, errors.New(msg) + } + + return x509CertChains, nil +} diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index b7f5af1b..9fb72434 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -16,11 +16,10 @@ package attestationreport import ( - "crypto/x509" + "encoding/hex" "fmt" + "reflect" "time" - - "github.com/Fraunhofer-AISEC/cmc/internal" ) func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*TdxMeasurementResult, bool) { @@ -54,32 +53,78 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } + // TODO: check nonce in the report + + // parse cert chain + referenceCerts, err := ParseCertificates(tdxM.Certs, true) + if err != nil { + msg := fmt.Sprintf("Failed to parse reference certificates: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + var current_time time.Time = time.Now() log.Trace("current time: ", current_time) - var certChain SgxCertificates = tdxQuote.QuoteSignatureData.QECertData.QECertData + var quoteCerts SgxCertificates = tdxQuote.QuoteSignatureData.QECertData.QECertData - if certChain.RootCACert == nil || certChain.IntermediateCert == nil || certChain.PCKCert == nil { + if quoteCerts.RootCACert == nil || quoteCerts.IntermediateCert == nil || quoteCerts.PCKCert == nil { msg := "incomplete certificate chain" result.Summary.setFalse(&msg) return result, false } - // (from DCAP Library): parse and verify PCK certificate chain - _, err = internal.VerifyCertChain( - []*x509.Certificate{certChain.PCKCert, certChain.IntermediateCert}, - []*x509.Certificate{certChain.RootCACert}) + // (from DCAP Library): parse and verify the entire PCK certificate chain + x509CertChains, err := VerifyIntelCertChainFull(quoteCerts) + if err != nil { + msg := err.Error() + result.Summary.setFalse(&msg) + return result, false + } + + // Store details from validated certificate chain(s) in the validation report + for _, chain := range x509CertChains { + chainExtracted := []X509CertExtracted{} + for _, cert := range chain { + chainExtracted = append(chainExtracted, ExtractX509Infos(cert)) + } + result.Signature.ValidatedCerts = append(result.Signature.ValidatedCerts, chainExtracted) + } + + // (from DCAP Library): parse and verify TcbInfo object + tcbInfo, err := ParseTcbInfo(tdxReferenceValue.Tdx.Collateral.TcbInfo) + if err != nil { + msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + err = verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert) if err != nil { - msg := fmt.Sprintf("Failed to verify pck certificate chain: %v", err) + msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) return result, false } - // TODO: verify the certificates (fetch CRLs and check expiration date) + // (from DCAP Library): parse and verify QE Identity object + qeIdentity, err := ParseQEIdentity(tdxReferenceValue.Tdx.Collateral.QeIdentity) + if err != nil { + msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + err = VerifyQEIdentity(&tdxQuote.QuoteSignatureData.QECertData.QEReport, &qeIdentity, + string(tdxReferenceValue.Tdx.Collateral.QeIdentity), referenceCerts.TCBSigningCert, TDX_QUOTE_TYPE) + if err != nil { + msg := fmt.Sprintf("Failed to verify QE Identity structure: %v", err) + result.Summary.setFalse(&msg) + return result, false + } // Verify Quote Signature sig, ret := VerifyIntelQuoteSignature(tdxM.Report, tdxQuote.QuoteSignatureData, - tdxQuote.QuoteSignatureDataLen, int(tdxQuote.QuoteHeader.AttestationKeyType), certChain, + tdxQuote.QuoteSignatureDataLen, int(tdxQuote.QuoteHeader.AttestationKeyType), quoteCerts, tdxReferenceValue.Tdx.Cafingerprint, TDX_QUOTE_TYPE) if !ret { msg := fmt.Sprintf("Failed to verify Quote Signature: %v", sig) @@ -87,7 +132,100 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - //result.Signature = sig + result.Signature = sig + + // check version + result.VersionMatch, ret = verifySgxVersion(tdxQuote.QuoteHeader, tdxReferenceValue.Tdx.Version) + if !ret { + return result, false + } + + // Verify Quote Body values + err = VerifyTdxQuoteBody(&tdxQuote.QuoteBody, &tcbInfo, "eCerts, &tdxReferenceValue, result) + if err != nil { + msg := fmt.Sprintf("Failed to verify TDX Report Body: %v", err) + result.Summary.setFalse(&msg) + result.Summary.Success = false + return result, false + } else { + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: tdxReferenceValue.Name, + Digest: hex.EncodeToString(tdxQuote.QuoteBody.MrSeam[:]), + Success: true, + }) + ok = true + } return result, ok } + +func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertificates, tdxReferenceValue *ReferenceValue, result *TdxMeasurementResult) error { + if body == nil || tcbInfo == nil || certs == nil || tdxReferenceValue == nil || result == nil { + return fmt.Errorf("invalid function parameter (null pointer exception)") + } + + // Parse and verify PCK certificate extensions + sgxExtensions, err := ParseSGXExtensions(certs.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant, only means it is a sequence) + if err != nil { + return fmt.Errorf("failed to parse SGX Extensions from PCK Certificate: %v", err) + } + + if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { + return fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) + } + + if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { + return fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) + } + + // check MrTd reference value + fmt.Println("MrSeam: ", hex.EncodeToString(body.MrSeam[:])) + fmt.Println("SeamAttributes: ", body.SeamAttributes[:]) + fmt.Println("MrSignerSeam: ", body.MrSignerSeam[:]) + fmt.Println("MrTd: ", hex.EncodeToString(body.MrTd[:])) + fmt.Println("TdAttributes: ", body.TdAttributes[:]) + + // (MrTd is the measurement of the initial contents of the TD) + if !reflect.DeepEqual(body.MrTd[:], []byte(tdxReferenceValue.Sha256)) { + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: tdxReferenceValue.Name, + Digest: hex.EncodeToString(tdxReferenceValue.Sha256[:]), + Success: false, + }) + return fmt.Errorf("MrSeam mismatch. Expected: %v, Got. %v", tdxReferenceValue.Sha256, body.MrTd) + } else { + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: tdxReferenceValue.Name, + Digest: hex.EncodeToString(body.MrSeam[:]), + Success: true, + }) + } + + // check SeamAttributes + attributes_quote := body.SeamAttributes + + if !reflect.DeepEqual(tdxReferenceValue.Tdx.SeamAttributes[:], attributes_quote[:]) { + return fmt.Errorf("SeamAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.SeamAttributes, attributes_quote) + } + + // check MrSignerSeam value + refSigner, err := hex.DecodeString(tdxReferenceValue.Tdx.MrSignerSeam) + if err != nil { + return fmt.Errorf("decoding MRSIGNERSEAM reference value failed: %v", err) + } + if !reflect.DeepEqual(refSigner[:], body.MrSignerSeam[:]) { + return fmt.Errorf("MRSIGNERSEAM mismatch. Expected: %v, Got: %v", refSigner, body.MrSignerSeam[:]) + } + + attributes_quote = body.TdAttributes + + if !reflect.DeepEqual(tdxReferenceValue.Tdx.TdAttributes[:], attributes_quote[:]) { + return fmt.Errorf("TdAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.TdAttributes, attributes_quote) + } + return nil +} diff --git a/attestationreport/tdx_struct.go b/attestationreport/tdx_struct.go index f3a69c09..a788e091 100644 --- a/attestationreport/tdx_struct.go +++ b/attestationreport/tdx_struct.go @@ -24,7 +24,7 @@ import ( // TDX Report V4 type TdxReport struct { QuoteHeader QuoteHeader - ISVEnclaveReport TdxReportBody + QuoteBody TdxReportBody QuoteSignatureDataLen uint32 QuoteSignatureData ECDSA256QuoteSignatureDataStructureV4 // variable size } @@ -33,7 +33,7 @@ type TdxReport struct { type TdxReportBody struct { TeeTcbSvn [16]byte MrSeam [48]byte - MrSigner [48]byte + MrSignerSeam [48]byte SeamAttributes [8]byte TdAttributes [8]byte XFAM [8]byte @@ -103,7 +103,7 @@ func DecodeTdxReportV4(report []byte) (TdxReport, error) { // compose the final report struct reportStruct.QuoteHeader = header - reportStruct.ISVEnclaveReport = body + reportStruct.QuoteBody = body reportStruct.QuoteSignatureDataLen = sigLen reportStruct.QuoteSignatureData = sig @@ -182,7 +182,7 @@ func parseECDSASignatureV4(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStr } // parse PCK Cert Chain (PCK Leaf Cert || Intermediate CA Cert || Root CA Cert) - certChain, err := ParseCertificates(tmp[:], false) + certChain, err := ParseCertificates(tmp[:], true) if err != nil { return fmt.Errorf("failed to parse certificate chain from QECertData: %v", err) } diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 2dc20e75..5e5ab1d6 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -16,6 +16,7 @@ package attestationreport import ( + "encoding/hex" "fmt" "testing" ) @@ -745,11 +746,28 @@ var ( 0x3b, 0xf7, 0x34, 0xd9, 0x56, 0xac, 0xf8, 0xe7, 0x3d, 0xac, 0x69, 0x02, 0xe9, 0x55, 0x5a, 0x81, 0x79, 0xb1, 0x8c, 0x4d, 0x71, 0xc5, 0x59, 0x4a, } - validTDXCertChain = [][]byte{} - validTDXMeasurement = []byte{} + + tcb_signing_cert_tdx = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj\nftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----")) + + // valid test collateral + tee_type_tdx = uint32(81) + // tcbInfo for aisec quote fetched from Intel server with FMSPC from pck cert + tcb_info_tdx = []byte(`{"tcbInfo":{"id":"TDX","version":3,"issueDate":"2023-11-05T15:18:58Z","nextUpdate":"2023-12-05T15:18:58Z","fmspc":"00806f050000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":16,"tdxModule":{"mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF"},"tdxModuleIdentities":[{"id":"TDX_01","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":2},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"}]}],"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":6,"category":"BIOS","type":"Early Microcode Update"},{"svn":6,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":6,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":5,"category":"BIOS","type":"Early Microcode Update"},{"svn":5,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":5,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00837"]},{"tcb":{"sgxtcbcomponents":[{"svn":5,"category":"BIOS","type":"Early Microcode Update"},{"svn":5,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":5,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00233","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00320","INTEL-SA-00329","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00477","INTEL-SA-00837"]}]},"signature":"551d46c841ebed44bf34b659009fa1f8ca6a6c5ee3aafb5a0ae59a2c989d761b534e6ec815a42db3af09d5362a3b79e49f6aabe8a1f4e8c6fe3cc7954d7c5b1e"}`) + tcb_info_tdx_size = uint32(3574) + qe_identity_tdx = []byte(`{"enclaveIdentity":{"id":"TD_QE","version":2,"issueDate":"2023-11-05T15:39:30Z","nextUpdate":"2023-12-05T15:39:30Z","tcbEvaluationDataNumber":16,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"DC9E2A7C6F948F17474E34A7FC43ED030F7C1563F1BABDDF6340C82E0E54A8C5","isvprodid":2,"tcbLevels":[{"tcb":{"isvsvn":4},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"}]},"signature":"93b578ccf06cbc9119e1ccd5d57575309495f48712a38227c4e1fee46ef9f87dd48576666ea7854da53f233765cb73fe10fafba310948935cdcca06b1e679a72"}`) + qe_identity_tdx_size = uint32(624) + + validTDXCertChain = [][]byte{tcb_signing_cert_tdx.Raw} + validTDXMeasurement, _ = hex.DecodeString(validMrTd) rootCAFingerprint = "BF85A53FC08F84CB1F73A4F75F48AF566E30AC040699BA0EC1B8D593C05B56FC" aisecCertrootCAFingerprint = "44a0196b2b99f889b8e149e95b807a350e7424964399e885a7cbb8ccfab674d3" + + validMrSeam = "2fd279c16164a93dd5bf373d834328d46008c2b693af9ebb865b08b2ced320c9a89b4869a9fab60fbe9d0c5a5363c656" + validMrSignerSeam = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + validSeamAttributes = [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + validTdAttributes = [8]byte{0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00} + validMrTd = "145fe28dab356d75767ab56ae83bc59ac045dc548bf40a3fb11390446af19f6ee21b0b8874c9a7864cedb64c6573d932" ) func Test_verifyTdxMeasurements(t *testing.T) { @@ -770,7 +788,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { args: args{ tdxM: &TdxMeasurement{ Type: "TDX Measurement", - Report: validTDXQuote, + Report: aisecTDXQuote, Certs: validTDXCertChain, }, tdxV: []ReferenceValue{ @@ -778,8 +796,19 @@ func Test_verifyTdxMeasurements(t *testing.T) { Type: "TDX Reference Value", Sha256: validTDXMeasurement, Tdx: &TDXDetails{ - Version: 0x04, - Cafingerprint: rootCAFingerprint, + Version: 0x04, + Collateral: SGXCollateral{ + TeeType: tee_type_tdx, + TcbInfo: tcb_info_tdx, + TcbInfoSize: tcb_info_tdx_size, + QeIdentity: qe_identity_tdx, + QeIdentitySize: qe_identity_tdx_size, + }, + Cafingerprint: aisecCertrootCAFingerprint, + SeamAttributes: validSeamAttributes, + MrSignerSeam: validMrSignerSeam, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, }, }, }, From bde0456ee3fa1dfbacc0def5acea586d81030b9e Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Sun, 5 Nov 2023 20:15:43 +0000 Subject: [PATCH 06/23] removed debug prints --- attestationreport/tdx.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 9fb72434..04c7c45a 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -182,12 +182,6 @@ func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi } // check MrTd reference value - fmt.Println("MrSeam: ", hex.EncodeToString(body.MrSeam[:])) - fmt.Println("SeamAttributes: ", body.SeamAttributes[:]) - fmt.Println("MrSignerSeam: ", body.MrSignerSeam[:]) - fmt.Println("MrTd: ", hex.EncodeToString(body.MrTd[:])) - fmt.Println("TdAttributes: ", body.TdAttributes[:]) - // (MrTd is the measurement of the initial contents of the TD) if !reflect.DeepEqual(body.MrTd[:], []byte(tdxReferenceValue.Sha256)) { result.Artifacts = append(result.Artifacts, @@ -208,7 +202,6 @@ func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi // check SeamAttributes attributes_quote := body.SeamAttributes - if !reflect.DeepEqual(tdxReferenceValue.Tdx.SeamAttributes[:], attributes_quote[:]) { return fmt.Errorf("SeamAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.SeamAttributes, attributes_quote) } @@ -223,7 +216,6 @@ func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi } attributes_quote = body.TdAttributes - if !reflect.DeepEqual(tdxReferenceValue.Tdx.TdAttributes[:], attributes_quote[:]) { return fmt.Errorf("TdAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.TdAttributes, attributes_quote) } From 542402558cd3fc5889559a79ce6fa03658b29fc6 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Mon, 6 Nov 2023 10:53:51 +0000 Subject: [PATCH 07/23] nonce check and tcb signing check --- attestationreport/tdx.go | 42 ++++++++++++++++++++++++++++++--- attestationreport/tdx_test.go | 44 +++++++++++++++++++++++++++++++---- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 04c7c45a..555cd676 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -16,10 +16,14 @@ package attestationreport import ( + "bytes" + "crypto/x509" "encoding/hex" "fmt" "reflect" "time" + + "github.com/Fraunhofer-AISEC/cmc/internal" ) func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*TdxMeasurementResult, bool) { @@ -38,13 +42,24 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ result.Summary.setFalse(&msg) return result, false } else if len(referenceValues) > 1 { - msg := fmt.Sprintf("Report contains %v reference values. Currently, only one SGX Reference Value is supported", + msg := fmt.Sprintf("Report contains %v reference values. Currently, only one TDX Reference Value is supported", len(referenceValues)) result.Summary.setFalse(&msg) return result, false } tdxReferenceValue := referenceValues[0] + if tdxReferenceValue.Type != "TDX Reference Value" { + msg := fmt.Sprintf("TDX Reference Value invalid type %v", tdxReferenceValue.Type) + result.Summary.setFalse(&msg) + return result, false + } + if tdxReferenceValue.Tdx == nil { + msg := "TDX Reference Value does not contain policy" + result.Summary.setFalse(&msg) + return result, false + } + // TODO: support other report types tdxQuote, err := DecodeTdxReportV4(tdxM.Report) if err != nil { @@ -53,12 +68,33 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - // TODO: check nonce in the report + // Compare Nonce for Freshness (called Report Data in the SNP Attestation Report Structure) + nonce64 := make([]byte, 64) + copy(nonce64, nonce) + if cmp := bytes.Compare(tdxQuote.QuoteBody.ReportData[:], nonce64); cmp != 0 { + msg := fmt.Sprintf("Nonces mismatch: Supplied Nonce = %v, Nonce in TDX Report = %v)", + hex.EncodeToString(nonce), hex.EncodeToString(tdxQuote.QuoteBody.ReportData[:])) + result.Freshness.setFalse(&msg) + ok = false + return result, ok + } else { + result.Freshness.Success = true + } // parse cert chain referenceCerts, err := ParseCertificates(tdxM.Certs, true) if err != nil { - msg := fmt.Sprintf("Failed to parse reference certificates: %v", err) + msg := fmt.Sprintf("Failed to parse reference certificates (TCBSigningCert + IntelRootCACert): %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // verify TCB Signing cert chain + _, err = internal.VerifyCertChain( + []*x509.Certificate{referenceCerts.TCBSigningCert}, + []*x509.Certificate{referenceCerts.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify TCB Signing certificate chain: %v", err) result.Summary.setFalse(&msg) return result, false } diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 5e5ab1d6..50948928 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -748,19 +748,19 @@ var ( } tcb_signing_cert_tdx = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj\nftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----")) + root_ca_cert_tdx = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIwaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi71OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SVUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYIKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwgAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----")) // valid test collateral - tee_type_tdx = uint32(81) - // tcbInfo for aisec quote fetched from Intel server with FMSPC from pck cert + tee_type_tdx = uint32(81) tcb_info_tdx = []byte(`{"tcbInfo":{"id":"TDX","version":3,"issueDate":"2023-11-05T15:18:58Z","nextUpdate":"2023-12-05T15:18:58Z","fmspc":"00806f050000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":16,"tdxModule":{"mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF"},"tdxModuleIdentities":[{"id":"TDX_01","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":2},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"}]}],"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":6,"category":"BIOS","type":"Early Microcode Update"},{"svn":6,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":6,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":5,"category":"BIOS","type":"Early Microcode Update"},{"svn":5,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":5,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00837"]},{"tcb":{"sgxtcbcomponents":[{"svn":5,"category":"BIOS","type":"Early Microcode Update"},{"svn":5,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":5,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00233","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00320","INTEL-SA-00329","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00477","INTEL-SA-00837"]}]},"signature":"551d46c841ebed44bf34b659009fa1f8ca6a6c5ee3aafb5a0ae59a2c989d761b534e6ec815a42db3af09d5362a3b79e49f6aabe8a1f4e8c6fe3cc7954d7c5b1e"}`) tcb_info_tdx_size = uint32(3574) qe_identity_tdx = []byte(`{"enclaveIdentity":{"id":"TD_QE","version":2,"issueDate":"2023-11-05T15:39:30Z","nextUpdate":"2023-12-05T15:39:30Z","tcbEvaluationDataNumber":16,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"DC9E2A7C6F948F17474E34A7FC43ED030F7C1563F1BABDDF6340C82E0E54A8C5","isvprodid":2,"tcbLevels":[{"tcb":{"isvsvn":4},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"}]},"signature":"93b578ccf06cbc9119e1ccd5d57575309495f48712a38227c4e1fee46ef9f87dd48576666ea7854da53f233765cb73fe10fafba310948935cdcca06b1e679a72"}`) qe_identity_tdx_size = uint32(624) - validTDXCertChain = [][]byte{tcb_signing_cert_tdx.Raw} + validTDXCertChain = [][]byte{tcb_signing_cert_tdx.Raw, root_ca_cert_tdx.Raw} validTDXMeasurement, _ = hex.DecodeString(validMrTd) - rootCAFingerprint = "BF85A53FC08F84CB1F73A4F75F48AF566E30AC040699BA0EC1B8D593C05B56FC" + // rootCAFingerprint = "BF85A53FC08F84CB1F73A4F75F48AF566E30AC040699BA0EC1B8D593C05B56FC" aisecCertrootCAFingerprint = "44a0196b2b99f889b8e149e95b807a350e7424964399e885a7cbb8ccfab674d3" validMrSeam = "2fd279c16164a93dd5bf373d834328d46008c2b693af9ebb865b08b2ced320c9a89b4869a9fab60fbe9d0c5a5363c656" @@ -768,6 +768,7 @@ var ( validSeamAttributes = [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} validTdAttributes = [8]byte{0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00} validMrTd = "145fe28dab356d75767ab56ae83bc59ac045dc548bf40a3fb11390446af19f6ee21b0b8874c9a7864cedb64c6573d932" + validTDXNonce, _ = hex.DecodeString("324e81d1e3d71e9f77c9e1aafcbdf157aa532d059c3637da19bd28f70e654540b3c711969e303515f916bff5d4b04c7e037a84a0b0bb7ac978a3c0860806c6bb") ) func Test_verifyTdxMeasurements(t *testing.T) { @@ -812,10 +813,43 @@ func Test_verifyTdxMeasurements(t *testing.T) { }, }, }, - nonce: validSGXNonce, + nonce: validTDXNonce, }, want1: true, }, + { + name: "Invalid Nonce", + args: args{ + tdxM: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: validTDXCertChain, + }, + tdxV: []ReferenceValue{ + { + Type: "TDX Reference Value", + Sha256: validTDXMeasurement, + Tdx: &TDXDetails{ + Version: 0x04, + Collateral: SGXCollateral{ + TeeType: tee_type_tdx, + TcbInfo: tcb_info_tdx, + TcbInfoSize: tcb_info_tdx_size, + QeIdentity: qe_identity_tdx, + QeIdentitySize: qe_identity_tdx_size, + }, + Cafingerprint: aisecCertrootCAFingerprint, + SeamAttributes: validSeamAttributes, + MrSignerSeam: validMrSignerSeam, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, + }, + }, + }, + nonce: []byte{0x00}, + }, + want1: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From baff8a72b5bc088b51e69bc8a12814a14f08b176 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Thu, 9 Nov 2023 17:02:56 +0000 Subject: [PATCH 08/23] tcb_info not yet done --- attestationreport/sgx.go | 64 ++++++++++++++++++++++++++++++-- attestationreport/sgx_structs.go | 36 +++++++++--------- attestationreport/tdx.go | 46 +++++++++++++---------- 3 files changed, 105 insertions(+), 41 deletions(-) diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index 361fcb75..400c4e81 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -209,6 +209,14 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ result.Signature.ValidatedCerts = append(result.Signature.ValidatedCerts, chainExtracted) } + // Parse and verify PCK certificate extensions + sgxExtensions, err := ParseSGXExtensions(quoteCerts.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant) + if err != nil { + msg := fmt.Sprintf("failed to parse SGX Extensions from PCK Certificate: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + // (from DCAP Library): parse and verify TcbInfo object tcbInfo, err := ParseTcbInfo(sgxReferenceValue.Sgx.Collateral.TcbInfo) if err != nil { @@ -217,7 +225,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - err = verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert) + err = verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) @@ -462,7 +470,7 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, return result, true } -func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate) error { +func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate, sgxExtensions SGXExtensionsValue) error { if tcbInfo == nil || tcbKeyCert == nil { return fmt.Errorf("invalid function parameter (null pointer exception)") } @@ -497,6 +505,54 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer return fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) } + if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { + return fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) + } + + if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { + return fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) + } + + // TODO: check TCB Levels (sgx/tdx tcb component svns, pcesvn) + // 1. Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to 16) + // with the corresponding values of SVNs in sgxtcbcomponents array of TCB Level. + var levelOk bool + for i, tcbLevel := range tcbInfo.TcbInfo.TcbLevels { + levelOk = true + tcbFromCert := GetTCBCompByIndex(sgxExtensions.Tcb, i) + // a) Compare all of the SGX TCB Comp SVNs from PCK Cert with with the corresponding values + // of SVNs in sgxtcbcomponents array of TCB Level + for _, component := range tcbLevel.Tcb.SgxTcbComponents { + fmt.Println("TCBCompSVN from PCK Cert: ", tcbFromCert.Value) + fmt.Println("SGXCompSVN from TCBLevel: ", component.Svn) + if tcbFromCert.Value < int(component.Svn) { + levelOk = false + break + } + } + if !levelOk { + continue + } + // b) Compare PCESVN + if sgxExtensions.Tcb.Value.PceSvn.Value < int(tcbLevel.Tcb.PceSvn) { + levelOk = false + break + } + // c) Compare all of the SVNs in TEE TCB SVN array retrieved from TD Report in Quote + // with the corresponding values of SVNs in tdxtcbcomponents array of TCB Level + for _, component := range tcbLevel.Tcb.TdxTcbComponents { + // TODO: get report body and TEE TCB SVN array and compare with the component svn values + fmt.Println("TDXCompSVN from TCBLevel: ", component.Svn) + + } + } + + if levelOk == false { + return fmt.Errorf("TCB Level mismatch!") + } + return nil } @@ -577,8 +633,8 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q return fmt.Errorf("invalid function parameter (null pointer exception)") } - regex := regexp.MustCompile(`\s+`) - regex.ReplaceAllString(qeIdentityBodyRaw, "") // remove whitespace + //regex := regexp.MustCompile(`\s+`) + //regex.ReplaceAllString(qeIdentityBodyRaw, "") // remove whitespace qeIdentityBodyRaw = qeIdentityBodyRaw[19 : len(qeIdentityBodyRaw)-128-16] // remove "{"enclaveIdentity":" from beginning and signature + rest from the end // get checksum of qe identity body diff --git a/attestationreport/sgx_structs.go b/attestationreport/sgx_structs.go index 3180aa32..0ebe3aae 100644 --- a/attestationreport/sgx_structs.go +++ b/attestationreport/sgx_structs.go @@ -307,42 +307,42 @@ func ParseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { return sgx_extensions, nil } -func GetTCBCompByIndex(tcb TCB, index int) (TCBComp, error) { +func GetTCBCompByIndex(tcb TCB, index int) TCBComp { switch index { case 1: - return tcb.Value.Comp_01, nil + return tcb.Value.Comp_01 case 2: - return tcb.Value.Comp_02, nil + return tcb.Value.Comp_02 case 3: - return tcb.Value.Comp_03, nil + return tcb.Value.Comp_03 case 4: - return tcb.Value.Comp_04, nil + return tcb.Value.Comp_04 case 5: - return tcb.Value.Comp_05, nil + return tcb.Value.Comp_05 case 6: - return tcb.Value.Comp_06, nil + return tcb.Value.Comp_06 case 7: - return tcb.Value.Comp_07, nil + return tcb.Value.Comp_07 case 8: - return tcb.Value.Comp_08, nil + return tcb.Value.Comp_08 case 9: - return tcb.Value.Comp_09, nil + return tcb.Value.Comp_09 case 10: - return tcb.Value.Comp_10, nil + return tcb.Value.Comp_10 case 11: - return tcb.Value.Comp_11, nil + return tcb.Value.Comp_11 case 12: - return tcb.Value.Comp_12, nil + return tcb.Value.Comp_12 case 13: - return tcb.Value.Comp_13, nil + return tcb.Value.Comp_13 case 14: - return tcb.Value.Comp_14, nil + return tcb.Value.Comp_14 case 15: - return tcb.Value.Comp_15, nil + return tcb.Value.Comp_15 case 16: - return tcb.Value.Comp_16, nil + return tcb.Value.Comp_16 default: - return TCBComp{}, fmt.Errorf("invalid index") + return TCBComp{} } } diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 555cd676..246cb8c4 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -81,24 +81,6 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ result.Freshness.Success = true } - // parse cert chain - referenceCerts, err := ParseCertificates(tdxM.Certs, true) - if err != nil { - msg := fmt.Sprintf("Failed to parse reference certificates (TCBSigningCert + IntelRootCACert): %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - // verify TCB Signing cert chain - _, err = internal.VerifyCertChain( - []*x509.Certificate{referenceCerts.TCBSigningCert}, - []*x509.Certificate{referenceCerts.RootCACert}) - if err != nil { - msg := fmt.Sprintf("Failed to verify TCB Signing certificate chain: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - var current_time time.Time = time.Now() log.Trace("current time: ", current_time) @@ -127,6 +109,32 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ result.Signature.ValidatedCerts = append(result.Signature.ValidatedCerts, chainExtracted) } + // parse reference cert chain (TCBSigningCert chain) + referenceCerts, err := ParseCertificates(tdxM.Certs, true) + if err != nil { + msg := fmt.Sprintf("Failed to parse reference certificates (TCBSigningCert + IntelRootCACert): %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // verify TCB Signing cert chain + _, err = internal.VerifyCertChain( + []*x509.Certificate{referenceCerts.TCBSigningCert}, + []*x509.Certificate{quoteCerts.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify TCB Signing certificate chain: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + + // Parse and verify PCK certificate extensions + sgxExtensions, err := ParseSGXExtensions(quoteCerts.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant) + if err != nil { + msg := fmt.Sprintf("failed to parse SGX Extensions from PCK Certificate: %v", err) + result.Summary.setFalse(&msg) + return result, false + } + // (from DCAP Library): parse and verify TcbInfo object tcbInfo, err := ParseTcbInfo(tdxReferenceValue.Tdx.Collateral.TcbInfo) if err != nil { @@ -135,7 +143,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - err = verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert) + err = verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) From e2a01bb03086e957a9eedd0d93d703a2f0282d67 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Sun, 12 Nov 2023 18:30:17 +0000 Subject: [PATCH 09/23] implemented tcb level verification --- attestationreport/sgx.go | 104 +++++++++++++++++++++------------------ attestationreport/tdx.go | 9 ++-- 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index 400c4e81..fe08b892 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -225,7 +225,8 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - err = verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions) + err = verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, + [16]byte{}, SGX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) @@ -261,7 +262,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ result.Signature = sig // check version - result.VersionMatch, ret = verifySgxVersion(sgxQuote.QuoteHeader, sgxReferenceValue.Sgx.Version) + result.VersionMatch, ret = verifyQuoteVersion(sgxQuote.QuoteHeader, sgxReferenceValue.Sgx.Version) if !ret { return result, false } @@ -288,11 +289,11 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, ok } -func verifySgxVersion(quote QuoteHeader, version uint16) (Result, bool) { +func verifyQuoteVersion(quote QuoteHeader, version uint16) (Result, bool) { r := Result{} ok := quote.Version == version if !ok { - msg := fmt.Sprintf("SGX report version mismatch: Report = %v, supplied = %v", quote.Version, version) + msg := fmt.Sprintf("Quote version mismatch: Report = %v, supplied = %v", quote.Version, version) r.setFalse(&msg) } else { r.Success = true @@ -470,7 +471,9 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, return result, true } -func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate, sgxExtensions SGXExtensionsValue) error { +// teeTcbSvn is only required for TDX (from TdxReportBody) +func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate, + sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) error { if tcbInfo == nil || tcbKeyCert == nil { return fmt.Errorf("invalid function parameter (null pointer exception)") } @@ -515,45 +518,57 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) } - // TODO: check TCB Levels (sgx/tdx tcb component svns, pcesvn) - // 1. Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to 16) - // with the corresponding values of SVNs in sgxtcbcomponents array of TCB Level. - var levelOk bool - for i, tcbLevel := range tcbInfo.TcbInfo.TcbLevels { - levelOk = true - tcbFromCert := GetTCBCompByIndex(sgxExtensions.Tcb, i) - // a) Compare all of the SGX TCB Comp SVNs from PCK Cert with with the corresponding values - // of SVNs in sgxtcbcomponents array of TCB Level - for _, component := range tcbLevel.Tcb.SgxTcbComponents { - fmt.Println("TCBCompSVN from PCK Cert: ", tcbFromCert.Value) - fmt.Println("SGXCompSVN from TCBLevel: ", component.Svn) - if tcbFromCert.Value < int(component.Svn) { - levelOk = false - break + // checking tcb level + var levelOk bool = false + for _, tcbLevel := range tcbInfo.TcbInfo.TcbLevels { + // Compare SGX TCB Comp SVNs from PCK Certificate with TCB Level + if compareSgxTcbCompSvns(sgxExtensions, tcbLevel) { + // Compare PCESVN value + if sgxExtensions.Tcb.Value.PceSvn.Value >= int(tcbLevel.Tcb.PceSvn) { + // Compare TEE TCB SVNs from TDX Report with TCB Level + if compareTeeTcbSvns(teeTcbSvn, tcbLevel) { + if tcbLevel.Tcb.TdxTcbComponents[1].Svn == teeTcbSvn[1] { + levelOk = true + fmt.Println("TCB Level Status: ", tcbLevel.TcbStatus) // handle result based on policy + break + } else { + return fmt.Errorf("TCB Level rejected: unsupported") + } + + } } } - if !levelOk { - continue - } - // b) Compare PCESVN - if sgxExtensions.Tcb.Value.PceSvn.Value < int(tcbLevel.Tcb.PceSvn) { - levelOk = false - break - } - // c) Compare all of the SVNs in TEE TCB SVN array retrieved from TD Report in Quote - // with the corresponding values of SVNs in tdxtcbcomponents array of TCB Level - for _, component := range tcbLevel.Tcb.TdxTcbComponents { - // TODO: get report body and TEE TCB SVN array and compare with the component svn values - fmt.Println("TDXCompSVN from TCBLevel: ", component.Svn) + } + if !levelOk { + return fmt.Errorf("TCB Level not supported") + } + return nil +} + +// helper function for verifyTcbInfo +func compareSgxTcbCompSvns(sgxExtensions SGXExtensionsValue, tcbLevel TcbLevel) bool { + // Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to 16) + // with the corresponding values of SVNs in sgxtcbcomponents array of TCB Level. + for i := 0; i < 16; i++ { + tcbFromCert := GetTCBCompByIndex(sgxExtensions.Tcb, i+1) + if byte(tcbFromCert.Value) < tcbLevel.Tcb.SgxTcbComponents[i].Svn { + return false } } + return true +} - if levelOk == false { - return fmt.Errorf("TCB Level mismatch!") +// helper function for verifyTcbInfo +func compareTeeTcbSvns(teeTcbSvn [16]byte, tcbLevel TcbLevel) bool { + // Compare all of the SVNs in TEE TCB SVN array retrieved from TD Report in Quote (from index 0 to 15) + // with the corresponding values of SVNs in tdxtcbcomponents array of TCB Level + for i := 0; i < 16; i++ { + if teeTcbSvn[i] < tcbLevel.Tcb.TdxTcbComponents[i].Svn { + return false + } } - - return nil + return true } // Only for SGX report body @@ -620,7 +635,6 @@ func VerifySgxQuoteBody(body *EnclaveReportBody, tcbInfo *TcbInfo, } // TODO: check CPUSVN from report body - // TODO: check TCB level (call GetTcbStatusEnclave): // - Check if TCB Level status is OutOfDate/REVOKED/ConfigurationNeeded/ConfigurationAndSWHardeningNeeded/UpToDate/SWHardeningNeeded/OutOfDateConfigurationNeeded/TCB Level error status is unrecognized // - handle result of TCB Status based on policy return nil @@ -633,8 +647,8 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q return fmt.Errorf("invalid function parameter (null pointer exception)") } - //regex := regexp.MustCompile(`\s+`) - //regex.ReplaceAllString(qeIdentityBodyRaw, "") // remove whitespace + regex := regexp.MustCompile(`\s+`) + regex.ReplaceAllString(qeIdentityBodyRaw, "") // remove whitespace qeIdentityBodyRaw = qeIdentityBodyRaw[19 : len(qeIdentityBodyRaw)-128-16] // remove "{"enclaveIdentity":" from beginning and signature + rest from the end // get checksum of qe identity body @@ -716,7 +730,7 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q } // TODO: return a corresponding result based on a policy. - switch GetTcbStatusQE(qeIdentity, qeReportBody) { + switch getTcbStatusQE(qeIdentity, qeReportBody) { case Revoked: log.Tracef("Value of tcbStatus for the selected Enclave's Identity tcbLevel (isvSvn: %v) is 'Revoked'", qeReportBody.ISVSVN) case OutOfDate: @@ -727,7 +741,7 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q return nil } -func GetTcbStatusQE(qeIdentity *QEIdentity, body *EnclaveReportBody) TcbStatus { +func getTcbStatusQE(qeIdentity *QEIdentity, body *EnclaveReportBody) TcbStatus { for _, level := range qeIdentity.EnclaveIdentity.TcbLevels { if uint16(level.Tcb.Isvsvn) <= body.ISVSVN { return level.TcbStatus @@ -736,12 +750,6 @@ func GetTcbStatusQE(qeIdentity *QEIdentity, body *EnclaveReportBody) TcbStatus { return Revoked } -// TODO (important): implement this function (from DCAP QuoteVerifier.cpp: function checkTcbLevel()) -func GetTcbStatusEnclave(tcb_info *TcbInfo, quote *SgxReport, sgxExtensions SGXExtensionsValue) TcbStatus { - //tcbLevel := getMatchingTcbLevel(tcb_info, quote, sgxExtensions) - return Revoked -} - // Check if CRL parameters are valid and check if the certificate has been revoked func CrlCheck(crl *x509.RevocationList, cert *x509.Certificate, parentCert *x509.Certificate) (bool, error) { diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 246cb8c4..5ee4f8dd 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -135,7 +135,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - // (from DCAP Library): parse and verify TcbInfo object + // Parse and verify TcbInfo object tcbInfo, err := ParseTcbInfo(tdxReferenceValue.Tdx.Collateral.TcbInfo) if err != nil { msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) @@ -143,14 +143,15 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - err = verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions) + err = verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, + sgxExtensions, tdxQuote.QuoteBody.TeeTcbSvn, TDX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) return result, false } - // (from DCAP Library): parse and verify QE Identity object + // Parse and verify QE Identity object qeIdentity, err := ParseQEIdentity(tdxReferenceValue.Tdx.Collateral.QeIdentity) if err != nil { msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) @@ -179,7 +180,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ result.Signature = sig // check version - result.VersionMatch, ret = verifySgxVersion(tdxQuote.QuoteHeader, tdxReferenceValue.Tdx.Version) + result.VersionMatch, ret = verifyQuoteVersion(tdxQuote.QuoteHeader, tdxReferenceValue.Tdx.Version) if !ret { return result, false } From 2d3c3c6706d6de2757af364eee16557932ed11eb Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Sun, 12 Nov 2023 21:11:35 +0000 Subject: [PATCH 10/23] changed sdx/tgx policy, added TcbStatus --- attestationreport/attestationreport.go | 10 +-- attestationreport/sgx.go | 95 ++++++++++++++++++-------- attestationreport/tdx.go | 37 +++++----- attestationreport/tdx_test.go | 5 ++ attestationreport/validationreport.go | 6 ++ 5 files changed, 103 insertions(+), 50 deletions(-) diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index 3c6f64ec..713c0e85 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -155,8 +155,9 @@ type TdxMeasurement struct { } type TdxPolicy struct { - Type string `json:"type" cbor:"0,keyasint"` - Debug bool `json:"debug" cbor:"2,keyasint"` + Type string `json:"type" cbor:"0,keyasint"` + Debug bool `json:"debug" cbor:"2,keyasint"` + ValidTcbStatus []string `json:"validTcbStatus" cbor:"3,keyasint"` // list of TCB status that are still accepted (e.g. Valid, OutOfDate) // maybe also tcb min/max version, etc. } @@ -169,8 +170,9 @@ type SgxMeasurement struct { } type SgxPolicy struct { - Type string `json:"type" cbor:"0,keyasint"` - Debug bool `json:"debug" cbor:"2,keyasint"` + Type string `json:"type" cbor:"0,keyasint"` + Debug bool `json:"debug" cbor:"2,keyasint"` + ValidTcbStatus []string `json:"validTcbStatus" cbor:"3,keyasint"` // list of TCB status that are still accepted (e.g. Valid, OutOfDate) // maybe also tcb min/max version, etc. } diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index fe08b892..2d8aa1d2 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -25,7 +25,7 @@ import ( "encoding/hex" "errors" "fmt" - "io/ioutil" + "io" "math/big" "net/http" "os" @@ -225,7 +225,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - err = verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, + tcbStatus, err := verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, [16]byte{}, SGX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) @@ -261,12 +261,6 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ result.Signature = sig - // check version - result.VersionMatch, ret = verifyQuoteVersion(sgxQuote.QuoteHeader, sgxReferenceValue.Sgx.Version) - if !ret { - return result, false - } - // Verify Quote Body values err = VerifySgxQuoteBody(&sgxQuote.ISVEnclaveReport, &tcbInfo, "eCerts, &sgxReferenceValue, result) if err != nil { @@ -284,11 +278,29 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ ok = true } + // Compare SGX Parameters + result.VersionMatch, ret = verifyQuoteVersion(sgxQuote.QuoteHeader, sgxReferenceValue.Sgx.Version) + if !ret { + return result, false + } + + result.PolicyCheck, ret = verifySgxPolicy(sgxQuote, sgxReferenceValue.Sgx.Policy, tcbStatus) + if !ret { + ok = false + } + result.Summary.Success = ok return result, ok } +// TODO: implement this function +func verifySgxPolicy(s SgxReport, v SgxPolicy, tcbStatus TcbStatus) (PolicyCheck, bool) { + r := PolicyCheck{} + + return r, true +} + func verifyQuoteVersion(quote QuoteHeader, version uint16) (Result, bool) { r := Result{} ok := quote.Version == version @@ -473,9 +485,9 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, // teeTcbSvn is only required for TDX (from TdxReportBody) func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate, - sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) error { + sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) (TcbStatus, error) { if tcbInfo == nil || tcbKeyCert == nil { - return fmt.Errorf("invalid function parameter (null pointer exception)") + return "", fmt.Errorf("invalid function parameter (null pointer exception)") } regex := regexp.MustCompile(`\s+`) @@ -493,57 +505,54 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) if !ok { - return fmt.Errorf("failed to extract public key from certificate") + return "", fmt.Errorf("failed to extract public key from certificate") } // verify signature ok = ecdsa.Verify(pub_key, digest[:], r, s) if !ok { - return fmt.Errorf("failed to verify tcbInfo signature") + return "", fmt.Errorf("failed to verify tcbInfo signature") } now := time.Now() if now.After(tcbInfo.TcbInfo.NextUpdate) { - return fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) + return "", fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) } if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { - return fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + return "", fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) } if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { - return fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + return "", fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) } // checking tcb level - var levelOk bool = false for _, tcbLevel := range tcbInfo.TcbInfo.TcbLevels { // Compare SGX TCB Comp SVNs from PCK Certificate with TCB Level if compareSgxTcbCompSvns(sgxExtensions, tcbLevel) { // Compare PCESVN value if sgxExtensions.Tcb.Value.PceSvn.Value >= int(tcbLevel.Tcb.PceSvn) { - // Compare TEE TCB SVNs from TDX Report with TCB Level - if compareTeeTcbSvns(teeTcbSvn, tcbLevel) { - if tcbLevel.Tcb.TdxTcbComponents[1].Svn == teeTcbSvn[1] { - levelOk = true - fmt.Println("TCB Level Status: ", tcbLevel.TcbStatus) // handle result based on policy - break - } else { - return fmt.Errorf("TCB Level rejected: unsupported") + if quoteType == SGX_QUOTE_TYPE { + return TcbStatus(tcbLevel.TcbStatus), nil + } else { + // Compare TEE TCB SVNs from TDX Report with TCB Level + if compareTeeTcbSvns(teeTcbSvn, tcbLevel) { + if tcbLevel.Tcb.TdxTcbComponents[1].Svn == teeTcbSvn[1] { + return TcbStatus(tcbLevel.TcbStatus), nil + } else { + return "", fmt.Errorf("TCB Level rejected: unsupported") + } } - } } } } - if !levelOk { - return fmt.Errorf("TCB Level not supported") - } - return nil + return "", fmt.Errorf("TCB Level not supported") } // helper function for verifyTcbInfo @@ -820,7 +829,7 @@ func downloadCRL(uri string, name string) (*x509.RevocationList, error) { } // Read response body into a byte slice - crlData, err := ioutil.ReadAll(resp.Body) + crlData, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -879,3 +888,29 @@ func VerifyIntelCertChainFull(quoteCerts SgxCertificates) ([][]*x509.Certificate return x509CertChains, nil } + +func VerifyTCBSigningCertChain(quoteCerts SgxCertificates) ([][]*x509.Certificate, error) { + // verify TCB Signing cert chain + tcbSigningCertChain, err := internal.VerifyCertChain( + []*x509.Certificate{quoteCerts.TCBSigningCert}, + []*x509.Certificate{quoteCerts.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify TCB Signing certificate chain: %v", err) + return nil, errors.New(msg) + } + + root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) + if err != nil { + msg := fmt.Sprintf("downloading Root CA CRL from PCS failed: %v", err) + return nil, errors.New(msg) + } + + // perform CRL checks (signature + values) + res, err := CrlCheck(root_ca_crl, quoteCerts.TCBSigningCert, quoteCerts.RootCACert) + if !res || err != nil { + msg := fmt.Sprintf("CRL check on TcbSigningCert failed: %v", err) + return nil, errors.New(msg) + } + + return tcbSigningCertChain, nil +} diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 5ee4f8dd..34198b39 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -17,13 +17,10 @@ package attestationreport import ( "bytes" - "crypto/x509" "encoding/hex" "fmt" "reflect" "time" - - "github.com/Fraunhofer-AISEC/cmc/internal" ) func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*TdxMeasurementResult, bool) { @@ -92,7 +89,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - // (from DCAP Library): parse and verify the entire PCK certificate chain + // Parse and verify the entire PCK certificate chain x509CertChains, err := VerifyIntelCertChainFull(quoteCerts) if err != nil { msg := err.Error() @@ -117,12 +114,9 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - // verify TCB Signing cert chain - _, err = internal.VerifyCertChain( - []*x509.Certificate{referenceCerts.TCBSigningCert}, - []*x509.Certificate{quoteCerts.RootCACert}) + _, err = VerifyTCBSigningCertChain(referenceCerts) if err != nil { - msg := fmt.Sprintf("Failed to verify TCB Signing certificate chain: %v", err) + msg := err.Error() result.Summary.setFalse(&msg) return result, false } @@ -143,7 +137,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - err = verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, + tcbStatus, err := verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, tdxQuote.QuoteBody.TeeTcbSvn, TDX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) @@ -179,12 +173,6 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ result.Signature = sig - // check version - result.VersionMatch, ret = verifyQuoteVersion(tdxQuote.QuoteHeader, tdxReferenceValue.Tdx.Version) - if !ret { - return result, false - } - // Verify Quote Body values err = VerifyTdxQuoteBody(&tdxQuote.QuoteBody, &tcbInfo, "eCerts, &tdxReferenceValue, result) if err != nil { @@ -202,9 +190,26 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ ok = true } + // check version + result.VersionMatch, ret = verifyQuoteVersion(tdxQuote.QuoteHeader, tdxReferenceValue.Tdx.Version) + if !ret { + return result, false + } + result.PolicyCheck, ret = verifyTdxPolicy(tdxQuote, tdxReferenceValue.Tdx.Policy, tcbStatus) + if !ret { + ok = false + } + return result, ok } +// TODO: implement this function +func verifyTdxPolicy(s TdxReport, v TdxPolicy, tcbStatus TcbStatus) (PolicyCheck, bool) { + r := PolicyCheck{} + + return r, true +} + func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertificates, tdxReferenceValue *ReferenceValue, result *TdxMeasurementResult) error { if body == nil || tcbInfo == nil || certs == nil || tdxReferenceValue == nil || result == nil { return fmt.Errorf("invalid function parameter (null pointer exception)") diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 50948928..865de023 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -838,6 +838,11 @@ func Test_verifyTdxMeasurements(t *testing.T) { QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, + Policy: TdxPolicy{ + Type: TDX_ID, + Debug: false, + ValidTcbStatus: []string{string(OutOfDate)}, + }, Cafingerprint: aisecCertrootCAFingerprint, SeamAttributes: validSeamAttributes, MrSignerSeam: validMrSignerSeam, diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index 4334815f..28381b2c 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -181,6 +181,9 @@ type SgxMeasurementResult struct { Signature SignatureResult `json:"signature"` Artifacts []DigestResult `json:"artifacts"` VersionMatch Result `json:"reportVersionMatch"` + FwCheck VersionCheck `json:"fwCheck"` + TcbCheck TcbCheck `json:"tcbCheck"` + PolicyCheck PolicyCheck `json:"policyCheck"` } // TdxMeasurementResult represents the results for the verification @@ -191,6 +194,9 @@ type TdxMeasurementResult struct { Signature SignatureResult `json:"signature"` Artifacts []DigestResult `json:"artifacts"` VersionMatch Result `json:"reportVersionMatch"` + FwCheck VersionCheck `json:"fwCheck"` + TcbCheck TcbCheck `json:"tcbCheck"` + PolicyCheck PolicyCheck `json:"policyCheck"` } // IasMeasurementResult represents the results for the verification From c08fa0eed5c2c96567dfa3aa783e40af9323739e Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Mon, 13 Nov 2023 11:36:37 +0000 Subject: [PATCH 11/23] cert chain check outsourced, changed sgx result --- attestationreport/sgx.go | 77 +++++++++++++++------------ attestationreport/tdx.go | 47 ++++++++-------- attestationreport/validationreport.go | 20 ++++--- 3 files changed, 77 insertions(+), 67 deletions(-) diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index 2d8aa1d2..f31baed6 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -192,23 +192,6 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - // (from DCAP Library): parse and verify the entire PCK certificate chain - x509CertChains, err := VerifyIntelCertChainFull(quoteCerts) - if err != nil { - msg := fmt.Sprintf("Failed to verify certificates from the quote: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - // Store details from validated certificate chain(s) in the validation report - for _, chain := range x509CertChains { - chainExtracted := []X509CertExtracted{} - for _, cert := range chain { - chainExtracted = append(chainExtracted, ExtractX509Infos(cert)) - } - result.Signature.ValidatedCerts = append(result.Signature.ValidatedCerts, chainExtracted) - } - // Parse and verify PCK certificate extensions sgxExtensions, err := ParseSGXExtensions(quoteCerts.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant) if err != nil { @@ -225,13 +208,14 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - tcbStatus, err := verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, + tcbInfoResult, err := verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, [16]byte{}, SGX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) return result, false } + result.TcbInfoCheck = tcbInfoResult // (from DCAP Library): parse and verify QE Identity object qeIdentity, err := ParseQEIdentity(sgxReferenceValue.Sgx.Collateral.QeIdentity) @@ -284,7 +268,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - result.PolicyCheck, ret = verifySgxPolicy(sgxQuote, sgxReferenceValue.Sgx.Policy, tcbStatus) + result.PolicyCheck, ret = verifySgxPolicy(sgxQuote, sgxReferenceValue.Sgx.Policy) if !ret { ok = false } @@ -295,7 +279,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ } // TODO: implement this function -func verifySgxPolicy(s SgxReport, v SgxPolicy, tcbStatus TcbStatus) (PolicyCheck, bool) { +func verifySgxPolicy(s SgxReport, v SgxPolicy) (PolicyCheck, bool) { r := PolicyCheck{} return r, true @@ -446,9 +430,8 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, return result, false } - // Step 4: Verify the SGX certificate chain - x509Chains, err := internal.VerifyCertChain([]*x509.Certificate{certs.PCKCert, certs.IntermediateCert}, - []*x509.Certificate{certs.RootCACert}) + // Step 4: Parse and verify the entire PCK certificate chain + x509Chains, err := VerifyIntelCertChainFull(certs) if err != nil { msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) result.CertChainCheck.setFalse(&msg) @@ -471,7 +454,7 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, } result.CertChainCheck.Success = true - //Step 6: Store details from (all) validated certificate chain(s) in the report + // Step 6: Store details from (all) validated certificate chain(s) in the report for _, chain := range x509Chains { chainExtracted := []X509CertExtracted{} for _, cert := range chain { @@ -485,9 +468,11 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, // teeTcbSvn is only required for TDX (from TdxReportBody) func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate, - sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) (TcbStatus, error) { + sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) (TcbInfoCheck, error) { + var result TcbInfoCheck + if tcbInfo == nil || tcbKeyCert == nil { - return "", fmt.Errorf("invalid function parameter (null pointer exception)") + return result, fmt.Errorf("invalid function parameter (null pointer exception)") } regex := regexp.MustCompile(`\s+`) @@ -505,28 +490,40 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) if !ok { - return "", fmt.Errorf("failed to extract public key from certificate") + result.Success = false + result.Details = "failed to extract public key from certificate" + return result, fmt.Errorf("failed to extract public key from certificate") } // verify signature ok = ecdsa.Verify(pub_key, digest[:], r, s) if !ok { - return "", fmt.Errorf("failed to verify tcbInfo signature") + result.Success = false + result.Details = "failed to verify tcbInfo signature" + return result, fmt.Errorf("failed to verify tcbInfo signature") } now := time.Now() if now.After(tcbInfo.TcbInfo.NextUpdate) { - return "", fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) + result.Success = false + result.Details = fmt.Sprintf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) + return result, fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) } if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { - return "", fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + result.Success = false + result.Details = fmt.Sprintf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) + return result, fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) } if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { - return "", fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + result.Success = false + result.Details = fmt.Sprintf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) + return result, fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) } @@ -537,14 +534,22 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer // Compare PCESVN value if sgxExtensions.Tcb.Value.PceSvn.Value >= int(tcbLevel.Tcb.PceSvn) { if quoteType == SGX_QUOTE_TYPE { - return TcbStatus(tcbLevel.TcbStatus), nil + result.Success = true + result.TcbLevelDate = tcbLevel.TcbDate + result.TcbLevelStatus = tcbLevel.TcbStatus + return result, nil } else { // Compare TEE TCB SVNs from TDX Report with TCB Level if compareTeeTcbSvns(teeTcbSvn, tcbLevel) { if tcbLevel.Tcb.TdxTcbComponents[1].Svn == teeTcbSvn[1] { - return TcbStatus(tcbLevel.TcbStatus), nil + result.Success = true + result.TcbLevelDate = tcbLevel.TcbDate + result.TcbLevelStatus = tcbLevel.TcbStatus + return result, nil } else { - return "", fmt.Errorf("TCB Level rejected: unsupported") + result.Success = false + result.Details = "TCB Level rejected: unsupported" + return result, fmt.Errorf("TCB Level rejected: unsupported") } } } @@ -552,7 +557,9 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer } } - return "", fmt.Errorf("TCB Level not supported") + result.Success = false + result.Details = "TCB Level not supported" + return result, fmt.Errorf("TCB Level not supported") } // helper function for verifyTcbInfo diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 34198b39..e9bf8145 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -89,23 +89,6 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - // Parse and verify the entire PCK certificate chain - x509CertChains, err := VerifyIntelCertChainFull(quoteCerts) - if err != nil { - msg := err.Error() - result.Summary.setFalse(&msg) - return result, false - } - - // Store details from validated certificate chain(s) in the validation report - for _, chain := range x509CertChains { - chainExtracted := []X509CertExtracted{} - for _, cert := range chain { - chainExtracted = append(chainExtracted, ExtractX509Infos(cert)) - } - result.Signature.ValidatedCerts = append(result.Signature.ValidatedCerts, chainExtracted) - } - // parse reference cert chain (TCBSigningCert chain) referenceCerts, err := ParseCertificates(tdxM.Certs, true) if err != nil { @@ -137,13 +120,14 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - tcbStatus, err := verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, + tcbInfoResult, err := verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, tdxQuote.QuoteBody.TeeTcbSvn, TDX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) return result, false } + result.TcbInfoCheck = tcbInfoResult // Parse and verify QE Identity object qeIdentity, err := ParseQEIdentity(tdxReferenceValue.Tdx.Collateral.QeIdentity) @@ -166,11 +150,8 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ tdxQuote.QuoteSignatureDataLen, int(tdxQuote.QuoteHeader.AttestationKeyType), quoteCerts, tdxReferenceValue.Tdx.Cafingerprint, TDX_QUOTE_TYPE) if !ret { - msg := fmt.Sprintf("Failed to verify Quote Signature: %v", sig) - result.Summary.setFalse(&msg) - return result, false + ok = false } - result.Signature = sig // Verify Quote Body values @@ -190,12 +171,17 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ ok = true } + fmt.Println(tdxQuote.QuoteBody.RtMr0) + fmt.Println(tdxQuote.QuoteBody.RtMr1) + fmt.Println(tdxQuote.QuoteBody.RtMr2) + fmt.Println(tdxQuote.QuoteBody.RtMr3) + // check version result.VersionMatch, ret = verifyQuoteVersion(tdxQuote.QuoteHeader, tdxReferenceValue.Tdx.Version) if !ret { return result, false } - result.PolicyCheck, ret = verifyTdxPolicy(tdxQuote, tdxReferenceValue.Tdx.Policy, tcbStatus) + result.PolicyCheck, ret = verifyTdxPolicy(tdxQuote, tdxReferenceValue.Tdx.Policy) if !ret { ok = false } @@ -204,7 +190,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ } // TODO: implement this function -func verifyTdxPolicy(s TdxReport, v TdxPolicy, tcbStatus TcbStatus) (PolicyCheck, bool) { +func verifyTdxPolicy(s TdxReport, v TdxPolicy) (PolicyCheck, bool) { r := PolicyCheck{} return r, true @@ -237,15 +223,24 @@ func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi result.Artifacts = append(result.Artifacts, DigestResult{ Name: tdxReferenceValue.Name, - Digest: hex.EncodeToString(tdxReferenceValue.Sha256[:]), + Digest: hex.EncodeToString(tdxReferenceValue.Sha256), + Success: false, + Type: "Reference Value", + }) + + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: tdxReferenceValue.Name, + Digest: hex.EncodeToString(body.MrTd[:]), Success: false, + Type: "Measurement", }) return fmt.Errorf("MrSeam mismatch. Expected: %v, Got. %v", tdxReferenceValue.Sha256, body.MrTd) } else { result.Artifacts = append(result.Artifacts, DigestResult{ Name: tdxReferenceValue.Name, - Digest: hex.EncodeToString(body.MrSeam[:]), + Digest: hex.EncodeToString(body.MrTd[:]), Success: true, }) } diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index 28381b2c..cc9f3d4d 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -18,6 +18,7 @@ package attestationreport import ( "crypto/x509" "math/big" + "time" "github.com/Fraunhofer-AISEC/cmc/internal" ) @@ -181,9 +182,9 @@ type SgxMeasurementResult struct { Signature SignatureResult `json:"signature"` Artifacts []DigestResult `json:"artifacts"` VersionMatch Result `json:"reportVersionMatch"` - FwCheck VersionCheck `json:"fwCheck"` - TcbCheck TcbCheck `json:"tcbCheck"` - PolicyCheck PolicyCheck `json:"policyCheck"` + TcbInfoCheck TcbInfoCheck `json:"tcbInfoCheck"` + PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test + FwCheck VersionCheck `json:"fwCheck"` // TODO: test } // TdxMeasurementResult represents the results for the verification @@ -194,9 +195,16 @@ type TdxMeasurementResult struct { Signature SignatureResult `json:"signature"` Artifacts []DigestResult `json:"artifacts"` VersionMatch Result `json:"reportVersionMatch"` - FwCheck VersionCheck `json:"fwCheck"` - TcbCheck TcbCheck `json:"tcbCheck"` - PolicyCheck PolicyCheck `json:"policyCheck"` + TcbInfoCheck TcbInfoCheck `json:"tcbInfoCheck"` + FwCheck VersionCheck `json:"fwCheck"` // TODO: test + PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test +} + +type TcbInfoCheck struct { + Success bool `json:"success"` + TcbLevelStatus string `json:"status"` + TcbLevelDate time.Time `json:"date"` + Details string `json:"details,omitempty"` } // IasMeasurementResult represents the results for the verification From 7772c465bba4eaec0754aa2d8f6f7beae54be69d Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Mon, 13 Nov 2023 14:29:29 +0000 Subject: [PATCH 12/23] implemented QEIdentityCheck for validationreport --- attestationreport/sgx.go | 117 +++++++++++++++----------- attestationreport/sgx_structs.go | 3 +- attestationreport/tdx.go | 6 +- attestationreport/validationreport.go | 41 +++++---- 4 files changed, 100 insertions(+), 67 deletions(-) diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index f31baed6..2aeff500 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -225,13 +225,14 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - err = VerifyQEIdentity(&sgxQuote.QuoteSignatureData.QEReport, &qeIdentity, + qeIdentityResult, err := VerifyQEIdentity(&sgxQuote.QuoteSignatureData.QEReport, &qeIdentity, string(sgxReferenceValue.Sgx.Collateral.QeIdentity), referenceCerts.TCBSigningCert, SGX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify QE Identity structure: %v", err) result.Summary.setFalse(&msg) return result, false } + result.QeIdentityCheck = qeIdentityResult // Verify Quote Signature sig, ret := VerifyIntelQuoteSignature(sgxM.Report, sgxQuote.QuoteSignatureData, @@ -542,6 +543,13 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer // Compare TEE TCB SVNs from TDX Report with TCB Level if compareTeeTcbSvns(teeTcbSvn, tcbLevel) { if tcbLevel.Tcb.TdxTcbComponents[1].Svn == teeTcbSvn[1] { + // fail if Status == REVOKED + if tcbLevel.TcbStatus == string(Revoked) { + result.Success = false + result.TcbLevelStatus = string(Revoked) + result.TcbLevelDate = tcbLevel.TcbDate + return result, fmt.Errorf("TCB Level status: REVOKED") + } result.Success = true result.TcbLevelDate = tcbLevel.TcbDate result.TcbLevelStatus = tcbLevel.TcbStatus @@ -556,6 +564,8 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer } } } + // - Check if TCB Level status is OutOfDate/REVOKED/ConfigurationNeeded/ConfigurationAndSWHardeningNeeded/UpToDate/SWHardeningNeeded/OutOfDateConfigurationNeeded/TCB Level error status is unrecognized + // - handle result of TCB Status based on policy result.Success = false result.Details = "TCB Level not supported" @@ -651,16 +661,15 @@ func VerifySgxQuoteBody(body *EnclaveReportBody, tcbInfo *TcbInfo, } // TODO: check CPUSVN from report body - // - Check if TCB Level status is OutOfDate/REVOKED/ConfigurationNeeded/ConfigurationAndSWHardeningNeeded/UpToDate/SWHardeningNeeded/OutOfDateConfigurationNeeded/TCB Level error status is unrecognized - // - handle result of TCB Status based on policy + return nil } -// verify QE Identity and compare the values to the QE -// Can be used by SGX/TDX -func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, qeIdentityBodyRaw string, tcbKeyCert *x509.Certificate, teeType uint32) error { +// verify QE Identity and compare the values to the QE (SGX/TDX) +func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, qeIdentityBodyRaw string, tcbKeyCert *x509.Certificate, teeType uint32) (QEIdentityCheck, error) { + result := QEIdentityCheck{} if qeReportBody == nil || qeIdentity == nil || tcbKeyCert == nil { - return fmt.Errorf("invalid function parameter (null pointer exception)") + return result, fmt.Errorf("invalid function parameter (null pointer exception)") } regex := regexp.MustCompile(`\s+`) @@ -678,92 +687,106 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) if !ok { - return fmt.Errorf("failed to extract public key from certificate") + return result, fmt.Errorf("failed to extract public key from certificate") } // verify signature ok = ecdsa.Verify(pub_key, digest[:], r, s) if !ok { - return fmt.Errorf("failed to verify QE Identity signature") + return result, fmt.Errorf("failed to verify QE Identity signature") } now := time.Now() - if now.After(qeIdentity.EnclaveIdentity.NextUpdate) { - return fmt.Errorf("qeIdentity has expired since: %v", qeIdentity.EnclaveIdentity.NextUpdate) + return result, fmt.Errorf("qeIdentity has expired since: %v", qeIdentity.EnclaveIdentity.NextUpdate) } // from Intel SGX DCAP Library if teeType == TDX_QUOTE_TYPE { if qeIdentity.EnclaveIdentity.Version == 1 { - return fmt.Errorf("enclave Identity version 1 is invalid for TDX TEE") + return result, fmt.Errorf("enclave Identity version 1 is invalid for TDX TEE") } else if qeIdentity.EnclaveIdentity.Version == 2 { if qeIdentity.EnclaveIdentity.Id != TD_QE { - return fmt.Errorf("enclave Identity is not generated for TDX and does not match Quote's TEE") + return result, fmt.Errorf("enclave Identity is not generated for TDX and does not match Quote's TEE") } } } else if teeType == SGX_QUOTE_TYPE { if qeIdentity.EnclaveIdentity.Id != QE { - return fmt.Errorf("enclave Identity is not generated for SGX and does not match Quote's TEE") + return result, fmt.Errorf("enclave Identity is not generated for SGX and does not match Quote's TEE") } } else { - return fmt.Errorf("unknown Quote's TEE. Enclave Identity cannot be valid") + return result, fmt.Errorf("unknown Quote's TEE. Enclave Identity cannot be valid") } - miscselect_qe_identity := binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.Miscselect) - miscselectMask_qe_identity := binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.MiscselectMask) + // check mrsigner + if !bytes.Equal([]byte(qeIdentity.EnclaveIdentity.Mrsigner), qeReportBody.MRSIGNER[:]) { + msg := fmt.Sprintf("MRSIGNER mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Mrsigner, qeReportBody.MRSIGNER) + result.Details = msg + result.Success = false + return result, nil + } + + // check isvProdId + if qeReportBody.ISVProdID != uint16(qeIdentity.EnclaveIdentity.IsvProdId) { + msg := fmt.Sprintf("IsvProdId mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.IsvProdId, qeReportBody.ISVProdID) + result.Details = msg + result.Success = false + return result, nil + } - if qeReportBody.MISCSELECT != (miscselect_qe_identity & miscselectMask_qe_identity) { - return fmt.Errorf("miscSelect value from Enclave Report: %v does not match miscSelect value from QE Identity: %v", - qeReportBody.MISCSELECT, (miscselect_qe_identity & miscselectMask_qe_identity)) + // check miscselect + miscselectMask := binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.MiscselectMask) + if binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.Miscselect) != (qeReportBody.MISCSELECT & miscselectMask) { + msg := fmt.Sprintf("miscSelect value from QEIdentity: %v does not match miscSelect value from QE Report: %v", + qeIdentity.EnclaveIdentity.Miscselect, (qeReportBody.MISCSELECT & miscselectMask)) + result.Details = msg + result.Success = false + return result, nil } // check attributes attributes_quote := qeReportBody.Attributes attributes_mask := qeIdentity.EnclaveIdentity.AttributesMask - if len(attributes_mask) == len(attributes_quote) { for i := range attributes_quote { attributes_quote[i] &= attributes_mask[i] } } - if !reflect.DeepEqual([]byte(qeIdentity.EnclaveIdentity.Attributes), attributes_quote[:]) { - return fmt.Errorf("attributes mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Attributes, attributes_quote) - } - - // check mrsigner value - mrsigner_quote := qeReportBody.MRSIGNER - mrsigner_qe_identity := qeIdentity.EnclaveIdentity.Mrsigner - if mrsigner_qe_identity != nil && - !reflect.DeepEqual([]byte(mrsigner_qe_identity), mrsigner_quote[:]) { - return fmt.Errorf("MRSIGNER mismatch. Expected: %v, Got: %v", mrsigner_qe_identity, mrsigner_quote) + msg := fmt.Sprintf("attributes mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Attributes, attributes_quote) + result.Details = msg + result.Success = false + return result, nil } - // check isvProdId value - if qeReportBody.ISVProdID != uint16(qeIdentity.EnclaveIdentity.IsvProdId) { - return fmt.Errorf("IsvProdId mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.IsvProdId, qeReportBody.ISVProdID) - } + tcbStatus, tcbDate := getTcbStatusAndDateQE(qeIdentity, qeReportBody) + log.Tracef("TcbStatus for Enclave's Identity tcbLevel (isvSvn: %v): '%v'", qeReportBody.ISVSVN, tcbStatus) - // TODO: return a corresponding result based on a policy. - switch getTcbStatusQE(qeIdentity, qeReportBody) { + switch tcbStatus { case Revoked: - log.Tracef("Value of tcbStatus for the selected Enclave's Identity tcbLevel (isvSvn: %v) is 'Revoked'", qeReportBody.ISVSVN) - case OutOfDate: - log.Tracef("Value of tcbStatus for the selected Enclave's Identity tcbLevel (isvSvn: %v) is 'OutOfDate'", qeReportBody.ISVSVN) + fallthrough + case NotSupported: + result.Details = "invalid tcbStatus" + result.TcbLevelStatus = string(tcbStatus) + result.TcbLevelDate = tcbDate + result.Success = false + return result, fmt.Errorf("tcbStatus: %v", tcbStatus) default: + result.TcbLevelStatus = string(tcbStatus) + result.TcbLevelDate = tcbDate + result.Success = true + return result, nil } - - return nil } -func getTcbStatusQE(qeIdentity *QEIdentity, body *EnclaveReportBody) TcbStatus { - for _, level := range qeIdentity.EnclaveIdentity.TcbLevels { - if uint16(level.Tcb.Isvsvn) <= body.ISVSVN { - return level.TcbStatus +func getTcbStatusAndDateQE(qeIdentity *QEIdentity, body *EnclaveReportBody) (TcbStatus, time.Time) { + for i := len(qeIdentity.EnclaveIdentity.TcbLevels) - 1; i >= 0; i-- { + tcbLevel := qeIdentity.EnclaveIdentity.TcbLevels[i] + if uint16(tcbLevel.Tcb.Isvsvn) <= body.ISVSVN { + return tcbLevel.TcbStatus, tcbLevel.TcbDate } } - return Revoked + return NotSupported, time.Now() } // Check if CRL parameters are valid and check if the certificate has been revoked diff --git a/attestationreport/sgx_structs.go b/attestationreport/sgx_structs.go index 0ebe3aae..2e26dd71 100644 --- a/attestationreport/sgx_structs.go +++ b/attestationreport/sgx_structs.go @@ -39,7 +39,8 @@ const ( ConfigurationNeeded TcbStatus = "ConfigurationNeeded" OutOfDate TcbStatus = "OutOfDate" OutOfDateConfigurationNeeded TcbStatus = "OutOfDateConfigurationNeeded" - Revoked TcbStatus = "Revoked" + Revoked TcbStatus = "REVOKED" + NotSupported TcbStatus = "NotSupported" ) // Overall structure: table 2 from https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index e9bf8145..7e029815 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -122,12 +122,12 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ tcbInfoResult, err := verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, tdxQuote.QuoteBody.TeeTcbSvn, TDX_QUOTE_TYPE) + result.TcbInfoCheck = tcbInfoResult if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) return result, false } - result.TcbInfoCheck = tcbInfoResult // Parse and verify QE Identity object qeIdentity, err := ParseQEIdentity(tdxReferenceValue.Tdx.Collateral.QeIdentity) @@ -137,8 +137,9 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - err = VerifyQEIdentity(&tdxQuote.QuoteSignatureData.QECertData.QEReport, &qeIdentity, + qeIdentityResult, err := VerifyQEIdentity(&tdxQuote.QuoteSignatureData.QECertData.QEReport, &qeIdentity, string(tdxReferenceValue.Tdx.Collateral.QeIdentity), referenceCerts.TCBSigningCert, TDX_QUOTE_TYPE) + result.QeIdentityCheck = qeIdentityResult if err != nil { msg := fmt.Sprintf("Failed to verify QE Identity structure: %v", err) result.Summary.setFalse(&msg) @@ -168,7 +169,6 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ Digest: hex.EncodeToString(tdxQuote.QuoteBody.MrSeam[:]), Success: true, }) - ok = true } fmt.Println(tdxQuote.QuoteBody.RtMr0) diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index cc9f3d4d..b698ac1c 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -177,27 +177,29 @@ type SnpMeasurementResult struct { // SgxMeasurementResult represents the results for the verification // of Intel SGX measurements. type SgxMeasurementResult struct { - Summary Result `json:"resultSummary"` - Freshness Result `json:"freshness"` - Signature SignatureResult `json:"signature"` - Artifacts []DigestResult `json:"artifacts"` - VersionMatch Result `json:"reportVersionMatch"` - TcbInfoCheck TcbInfoCheck `json:"tcbInfoCheck"` - PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test - FwCheck VersionCheck `json:"fwCheck"` // TODO: test + Summary Result `json:"resultSummary"` + Freshness Result `json:"freshness"` + Signature SignatureResult `json:"signature"` + Artifacts []DigestResult `json:"artifacts"` + VersionMatch Result `json:"reportVersionMatch"` + TcbInfoCheck TcbInfoCheck `json:"tcbInfoCheck"` + QeIdentityCheck QEIdentityCheck `json:"qeIdentityCheck"` + PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test + FwCheck VersionCheck `json:"fwCheck"` // TODO: test } // TdxMeasurementResult represents the results for the verification // of Intel TDX measurements. type TdxMeasurementResult struct { - Summary Result `json:"resultSummary"` - Freshness Result `json:"freshness"` - Signature SignatureResult `json:"signature"` - Artifacts []DigestResult `json:"artifacts"` - VersionMatch Result `json:"reportVersionMatch"` - TcbInfoCheck TcbInfoCheck `json:"tcbInfoCheck"` - FwCheck VersionCheck `json:"fwCheck"` // TODO: test - PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test + Summary Result `json:"resultSummary"` + Freshness Result `json:"freshness"` + Signature SignatureResult `json:"signature"` + Artifacts []DigestResult `json:"artifacts"` + VersionMatch Result `json:"reportVersionMatch"` + TcbInfoCheck TcbInfoCheck `json:"tcbInfoCheck"` + QeIdentityCheck QEIdentityCheck `json:"qeIdentityCheck"` + FwCheck VersionCheck `json:"fwCheck"` // TODO: test + PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test } type TcbInfoCheck struct { @@ -207,6 +209,13 @@ type TcbInfoCheck struct { Details string `json:"details,omitempty"` } +type QEIdentityCheck struct { + Success bool `json:"success"` + TcbLevelStatus string `json:"status"` + TcbLevelDate time.Time `json:"date"` + Details string `json:"details,omitempty"` +} + // IasMeasurementResult represents the results for the verification // of ARM PSA Initial Attestation Service Token measurements. type IasMeasurementResult struct { From 39047eda4f73ca0e919a77f98d0d5fc89f8a05f6 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Mon, 13 Nov 2023 17:46:45 +0000 Subject: [PATCH 13/23] report body checks continued --- attestationreport/attestationreport.go | 41 ++++++++++++++++++------ attestationreport/sgx.go | 2 +- attestationreport/sgx_test.go | 3 +- attestationreport/tdx.go | 43 +++++++++++++------------- attestationreport/tdx_test.go | 37 +++++++++++++++++----- 5 files changed, 84 insertions(+), 42 deletions(-) diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index 713c0e85..83a3150c 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -220,7 +220,7 @@ type SGXCollateral struct { type SGXDetails struct { Version uint16 `json:"version" cbor:"0,keyasint"` Collateral SGXCollateral `json:"collateral" cbor:"1,keyasint"` - CAfingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint + CaFingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint Policy SgxPolicy `json:"policy" cbor:"3,keyasint"` Attributes [16]byte `json:"attributes" cbor:"4,keyasint"` IsvProdId uint16 `json:"isvProdId" cbor:"5,keyasint"` @@ -228,15 +228,36 @@ type SGXDetails struct { } type TDXDetails struct { - // TODO: add more attributes to this struct - Version uint16 `json:"version" cbor:"0,keyasint"` - Collateral SGXCollateral `json:"collateral" cbor:"1,keyasint"` - Cafingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint - Policy TdxPolicy `json:"policy" cbor:"3,keyasint"` - SeamAttributes [8]byte `json:"seamattributes" cbor:"4,keyasint"` - MrSignerSeam string `json:"mrsignerseam" cbor:"6,keyasint"` - MrSeam string `json:"mrseam" cbor:"7,keyasint"` - TdAttributes [8]byte `json:"tdattributes" cbor:"8,keyasint"` + Version uint16 `json:"version" cbor:"0,keyasint"` + Collateral SGXCollateral `json:"collateral" cbor:"1,keyasint"` + CaFingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint + Policy TdxPolicy `json:"policy" cbor:"3,keyasint"` + TdId TDId `json:"tdId" cbor:"4,keyasint"` + TdAttributes TDAttributes `json:"tdAttributes" cbor:"5,keyasint"` + Xfam [8]byte `json:"xfam" cbor:"6,keyasint"` + + // TODO: check if these are necessary + SeamAttributes [8]byte `json:"seamattributes" cbor:"7,keyasint"` + MrSignerSeam string `json:"mrsignerseam" cbor:"8,keyasint"` + MrSeam string `json:"mrseam" cbor:"9,keyasint"` +} + +// Identity of TD, i.e. the contained measurement +// MrTd is already given in ReferenceValue.Sha256 +type TDId struct { + MrOwner [48]byte `json:"mrOwner" cbor:"0,keyasint"` + MrOwnerConfig [48]byte `json:"mrOwnerConfig" cbor:"1,keyasint"` + MrConfigId [48]byte `json:"mrConfigId" cbor:"2,keyasint"` + RtMr0 [48]byte `json:"rtMr0" cbor:"0,keyasint"` // updated by the TD virtual firmware/BIOS + RtMr1 [48]byte `json:"rtMr1" cbor:"0,keyasint"` // updated by the TD virtual firmware/BIOS + RtMr2 [48]byte `json:"rtMr2" cbor:"0,keyasint"` // runtime measurement + RtMr3 [48]byte `json:"rtMr3" cbor:"0,keyasint"` // runtime measurement +} + +type TDAttributes struct { + Tud byte `json:"tud" cbor:"0,keyasint"` + Sec [3]byte `json:"sec" cbor:"1,keyasint"` + Other [4]byte `json:"other" cbor:"2,keyasint"` } // ReferenceValue represents the attestation report diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index 2aeff500..8e530d38 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -237,7 +237,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ // Verify Quote Signature sig, ret := VerifyIntelQuoteSignature(sgxM.Report, sgxQuote.QuoteSignatureData, sgxQuote.QuoteSignatureDataLen, int(sgxQuote.QuoteHeader.AttestationKeyType), referenceCerts, - sgxReferenceValue.Sgx.CAfingerprint, quoteType) + sgxReferenceValue.Sgx.CaFingerprint, quoteType) if !ret { msg := fmt.Sprintf("Failed to verify Quote Signature: %v", sig) result.Summary.setFalse(&msg) diff --git a/attestationreport/sgx_test.go b/attestationreport/sgx_test.go index ab16a776..c105f699 100644 --- a/attestationreport/sgx_test.go +++ b/attestationreport/sgx_test.go @@ -488,7 +488,7 @@ func Test_verifySgxMeasurements(t *testing.T) { QeIdentity: qe_identity, QeIdentitySize: qe_identity_size, }, - CAfingerprint: rootCACertFingerprint, + CaFingerprint: rootCACertFingerprint, Attributes: validSGXAttributes, IsvProdId: validIsvProdId, MRSIGNER: validMRSIGNER, @@ -671,7 +671,6 @@ func Test_verifySgxMeasurements(t *testing.T) { } }) } - } func TestParseSGXExtensions(t *testing.T) { diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 7e029815..eb1e6bbb 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -149,7 +149,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ // Verify Quote Signature sig, ret := VerifyIntelQuoteSignature(tdxM.Report, tdxQuote.QuoteSignatureData, tdxQuote.QuoteSignatureDataLen, int(tdxQuote.QuoteHeader.AttestationKeyType), quoteCerts, - tdxReferenceValue.Tdx.Cafingerprint, TDX_QUOTE_TYPE) + tdxReferenceValue.Tdx.CaFingerprint, TDX_QUOTE_TYPE) if !ret { ok = false } @@ -171,11 +171,6 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ }) } - fmt.Println(tdxQuote.QuoteBody.RtMr0) - fmt.Println(tdxQuote.QuoteBody.RtMr1) - fmt.Println(tdxQuote.QuoteBody.RtMr2) - fmt.Println(tdxQuote.QuoteBody.RtMr3) - // check version result.VersionMatch, ret = verifyQuoteVersion(tdxQuote.QuoteHeader, tdxReferenceValue.Tdx.Version) if !ret { @@ -245,24 +240,28 @@ func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi }) } - // check SeamAttributes - attributes_quote := body.SeamAttributes - if !reflect.DeepEqual(tdxReferenceValue.Tdx.SeamAttributes[:], attributes_quote[:]) { - return fmt.Errorf("SeamAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.SeamAttributes, attributes_quote) + if !bytes.Equal(tdxReferenceValue.Tdx.TdId.MrOwner[:], body.MrOwner[:]) { + return fmt.Errorf("MrOwner mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.TdId.MrOwner, body.MrOwner) } + // check SeamAttributes + // attributes_quote := body.SeamAttributes + // if !reflect.DeepEqual(tdxReferenceValue.Tdx.SeamAttributes[:], attributes_quote[:]) { + // return fmt.Errorf("SeamAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.SeamAttributes, attributes_quote) + // } // check MrSignerSeam value - refSigner, err := hex.DecodeString(tdxReferenceValue.Tdx.MrSignerSeam) - if err != nil { - return fmt.Errorf("decoding MRSIGNERSEAM reference value failed: %v", err) - } - if !reflect.DeepEqual(refSigner[:], body.MrSignerSeam[:]) { - return fmt.Errorf("MRSIGNERSEAM mismatch. Expected: %v, Got: %v", refSigner, body.MrSignerSeam[:]) - } - - attributes_quote = body.TdAttributes - if !reflect.DeepEqual(tdxReferenceValue.Tdx.TdAttributes[:], attributes_quote[:]) { - return fmt.Errorf("TdAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.TdAttributes, attributes_quote) - } + // refSigner, err := hex.DecodeString(tdxReferenceValue.Tdx.MrSignerSeam) + // if err != nil { + // return fmt.Errorf("decoding MRSIGNERSEAM reference value failed: %v", err) + // } + // if !reflect.DeepEqual(refSigner[:], body.MrSignerSeam[:]) { + // return fmt.Errorf("MRSIGNERSEAM mismatch. Expected: %v, Got: %v", refSigner, body.MrSignerSeam[:]) + // } + + // fmt.Println(body.TdAttributes) + // attributes_quote = body.TdAttributes + // if !reflect.DeepEqual(tdxReferenceValue.Tdx.TdAttributes[:], attributes_quote[:]) { + // return fmt.Errorf("TdAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.TdAttributes, attributes_quote) + // } return nil } diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 865de023..3508d712 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -763,6 +763,15 @@ var ( // rootCAFingerprint = "BF85A53FC08F84CB1F73A4F75F48AF566E30AC040699BA0EC1B8D593C05B56FC" aisecCertrootCAFingerprint = "44a0196b2b99f889b8e149e95b807a350e7424964399e885a7cbb8ccfab674d3" + // valid report body values + mrOwner = [48]byte{} // 0x00s + mrOwnerConfig = [48]byte{} // 0x00s + mrConfigId = [48]byte{} // 0x00s + rtMr0 = [48]byte{225, 175, 117, 230, 25, 39, 65, 14, 66, 181, 75, 57, 246, 104, 28, 249, 176, 191, 186, 229, 18, 177, 94, 135, 14, 76, 141, 157, 90, 92, 179, 133, 87, 27, 14, 29, 194, 247, 11, 249, 204, 239, 8, 86, 15, 10, 43, 88} + rtMr1 = [48]byte{1, 28, 185, 253, 213, 20, 180, 71, 150, 2, 57, 206, 119, 160, 255, 200, 7, 135, 119, 55, 31, 126, 191, 235, 76, 160, 72, 13, 3, 60, 229, 236, 97, 67, 72, 6, 73, 247, 90, 144, 87, 157, 245, 245, 1, 107, 231, 202} + rtMr2 = [48]byte{} // 0x00s + rtMr3 = [48]byte{} // 0x00s + validMrSeam = "2fd279c16164a93dd5bf373d834328d46008c2b693af9ebb865b08b2ced320c9a89b4869a9fab60fbe9d0c5a5363c656" validMrSignerSeam = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" validSeamAttributes = [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} @@ -805,11 +814,21 @@ func Test_verifyTdxMeasurements(t *testing.T) { QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, - Cafingerprint: aisecCertrootCAFingerprint, - SeamAttributes: validSeamAttributes, - MrSignerSeam: validMrSignerSeam, - MrSeam: validMrSeam, - TdAttributes: validTdAttributes, + CaFingerprint: aisecCertrootCAFingerprint, + TdId: TDId{ + MrOwner: mrOwner, + MrOwnerConfig: mrOwnerConfig, + MrConfigId: mrConfigId, + RtMr0: rtMr0, + RtMr1: rtMr1, + RtMr2: rtMr2, + RtMr3: rtMr3, + }, + TdAttributes: TDAttributes{ + Tud: 0, + Sec: [3]byte{0x00, 0x00, 0x10}, + Other: [4]byte{0x00, 0x00, 0x00, 0x00}, + }, }, }, }, @@ -843,11 +862,15 @@ func Test_verifyTdxMeasurements(t *testing.T) { Debug: false, ValidTcbStatus: []string{string(OutOfDate)}, }, - Cafingerprint: aisecCertrootCAFingerprint, + CaFingerprint: aisecCertrootCAFingerprint, SeamAttributes: validSeamAttributes, MrSignerSeam: validMrSignerSeam, MrSeam: validMrSeam, - TdAttributes: validTdAttributes, + TdAttributes: TDAttributes{ + Tud: 0, + Sec: [3]byte{0x00, 0x00, 0x10}, + Other: [4]byte{0x00, 0x00, 0x00, 0x00}, + }, }, }, }, From 98e93c1ee4ae692b9ea392526e93f8acd9116818 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Thu, 16 Nov 2023 17:22:32 +0000 Subject: [PATCH 14/23] more implemented --- attestationreport/sgx.go | 80 +++++++++++---------------- attestationreport/sgx_structs.go | 7 +-- attestationreport/tdx.go | 38 ++++++------- attestationreport/validationreport.go | 26 ++++----- 4 files changed, 63 insertions(+), 88 deletions(-) diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index 8e530d38..5a550795 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -469,8 +469,8 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, // teeTcbSvn is only required for TDX (from TdxReportBody) func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate, - sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) (TcbInfoCheck, error) { - var result TcbInfoCheck + sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) (TcbLevelCheck, error) { + var result TcbLevelCheck if tcbInfo == nil || tcbKeyCert == nil { return result, fmt.Errorf("invalid function parameter (null pointer exception)") @@ -491,38 +491,38 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) if !ok { - result.Success = false - result.Details = "failed to extract public key from certificate" + result.Summary.Success = false + result.Summary.Details = "failed to extract public key from certificate" return result, fmt.Errorf("failed to extract public key from certificate") } // verify signature ok = ecdsa.Verify(pub_key, digest[:], r, s) if !ok { - result.Success = false - result.Details = "failed to verify tcbInfo signature" + result.Summary.Success = false + result.Summary.Details = "failed to verify tcbInfo signature" return result, fmt.Errorf("failed to verify tcbInfo signature") } now := time.Now() if now.After(tcbInfo.TcbInfo.NextUpdate) { - result.Success = false - result.Details = fmt.Sprintf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) + result.Summary.Success = false + result.Summary.Details = fmt.Sprintf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) return result, fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) } if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { - result.Success = false - result.Details = fmt.Sprintf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + result.Summary.Success = false + result.Summary.Details = fmt.Sprintf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) return result, fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) } if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { - result.Success = false - result.Details = fmt.Sprintf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + result.Summary.Success = false + result.Summary.Details = fmt.Sprintf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) return result, fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) @@ -535,7 +535,7 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer // Compare PCESVN value if sgxExtensions.Tcb.Value.PceSvn.Value >= int(tcbLevel.Tcb.PceSvn) { if quoteType == SGX_QUOTE_TYPE { - result.Success = true + result.Summary.Success = true result.TcbLevelDate = tcbLevel.TcbDate result.TcbLevelStatus = tcbLevel.TcbStatus return result, nil @@ -545,18 +545,18 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer if tcbLevel.Tcb.TdxTcbComponents[1].Svn == teeTcbSvn[1] { // fail if Status == REVOKED if tcbLevel.TcbStatus == string(Revoked) { - result.Success = false + result.Summary.Success = false result.TcbLevelStatus = string(Revoked) result.TcbLevelDate = tcbLevel.TcbDate return result, fmt.Errorf("TCB Level status: REVOKED") } - result.Success = true + result.Summary.Success = true result.TcbLevelDate = tcbLevel.TcbDate result.TcbLevelStatus = tcbLevel.TcbStatus return result, nil } else { - result.Success = false - result.Details = "TCB Level rejected: unsupported" + result.Summary.Success = false + result.Summary.Details = "TCB Level rejected: unsupported" return result, fmt.Errorf("TCB Level rejected: unsupported") } } @@ -567,8 +567,8 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer // - Check if TCB Level status is OutOfDate/REVOKED/ConfigurationNeeded/ConfigurationAndSWHardeningNeeded/UpToDate/SWHardeningNeeded/OutOfDateConfigurationNeeded/TCB Level error status is unrecognized // - handle result of TCB Status based on policy - result.Success = false - result.Details = "TCB Level not supported" + result.Summary.Success = false + result.Summary.Details = "TCB Level not supported" return result, fmt.Errorf("TCB Level not supported") } @@ -604,22 +604,6 @@ func VerifySgxQuoteBody(body *EnclaveReportBody, tcbInfo *TcbInfo, return fmt.Errorf("invalid function parameter (null pointer exception)") } - // Parse and verify PCK certificate extensions - sgxExtensions, err := ParseSGXExtensions(certs.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant) - if err != nil { - return fmt.Errorf("failed to parse SGX Extensions from PCK Certificate: %v", err) - } - - if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { - return fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", - tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) - } - - if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { - return fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", - tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) - } - // check MRENCLAVE reference value if !reflect.DeepEqual(body.MRENCLAVE[:], []byte(sgxReferenceValue.Sha256)) { result.Artifacts = append(result.Artifacts, @@ -666,8 +650,8 @@ func VerifySgxQuoteBody(body *EnclaveReportBody, tcbInfo *TcbInfo, } // verify QE Identity and compare the values to the QE (SGX/TDX) -func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, qeIdentityBodyRaw string, tcbKeyCert *x509.Certificate, teeType uint32) (QEIdentityCheck, error) { - result := QEIdentityCheck{} +func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, qeIdentityBodyRaw string, tcbKeyCert *x509.Certificate, teeType uint32) (TcbLevelCheck, error) { + result := TcbLevelCheck{} if qeReportBody == nil || qeIdentity == nil || tcbKeyCert == nil { return result, fmt.Errorf("invalid function parameter (null pointer exception)") } @@ -721,16 +705,16 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q // check mrsigner if !bytes.Equal([]byte(qeIdentity.EnclaveIdentity.Mrsigner), qeReportBody.MRSIGNER[:]) { msg := fmt.Sprintf("MRSIGNER mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Mrsigner, qeReportBody.MRSIGNER) - result.Details = msg - result.Success = false + result.Summary.Details = msg + result.Summary.Success = false return result, nil } // check isvProdId if qeReportBody.ISVProdID != uint16(qeIdentity.EnclaveIdentity.IsvProdId) { msg := fmt.Sprintf("IsvProdId mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.IsvProdId, qeReportBody.ISVProdID) - result.Details = msg - result.Success = false + result.Summary.Details = msg + result.Summary.Success = false return result, nil } @@ -739,8 +723,8 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q if binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.Miscselect) != (qeReportBody.MISCSELECT & miscselectMask) { msg := fmt.Sprintf("miscSelect value from QEIdentity: %v does not match miscSelect value from QE Report: %v", qeIdentity.EnclaveIdentity.Miscselect, (qeReportBody.MISCSELECT & miscselectMask)) - result.Details = msg - result.Success = false + result.Summary.Details = msg + result.Summary.Success = false return result, nil } @@ -754,8 +738,8 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q } if !reflect.DeepEqual([]byte(qeIdentity.EnclaveIdentity.Attributes), attributes_quote[:]) { msg := fmt.Sprintf("attributes mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Attributes, attributes_quote) - result.Details = msg - result.Success = false + result.Summary.Details = msg + result.Summary.Success = false return result, nil } @@ -766,15 +750,15 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q case Revoked: fallthrough case NotSupported: - result.Details = "invalid tcbStatus" + result.Summary.Details = "invalid tcbStatus" result.TcbLevelStatus = string(tcbStatus) result.TcbLevelDate = tcbDate - result.Success = false + result.Summary.Success = false return result, fmt.Errorf("tcbStatus: %v", tcbStatus) default: result.TcbLevelStatus = string(tcbStatus) result.TcbLevelDate = tcbDate - result.Success = true + result.Summary.Success = true return result, nil } } diff --git a/attestationreport/sgx_structs.go b/attestationreport/sgx_structs.go index 2e26dd71..83020b7b 100644 --- a/attestationreport/sgx_structs.go +++ b/attestationreport/sgx_structs.go @@ -113,7 +113,7 @@ type TcbInfoBody struct { TcbEvaluationDataNumber uint32 `json:"tcbEvaluationDataNumber"` TcbLevels []TcbLevel `json:"tcbLevels"` - // Get properties of Intel’s TDX SEAM module + // Holds properties of Intel’s TDX SEAM module TdxModule TdxModule `json:"tdxModule"` // Only required for TDX } @@ -127,11 +127,6 @@ type TcbLevel struct { TcbStatus string `json:"tcbStatus"` TcbDate time.Time `json:"tcbDate"` AdvisoryIDs []string `json:"advisoryIDs"` - - // TODO: not sure if these 3 values are entirely correct - Id string `json:"id"` - Version uint32 `json:"version"` - CpuSvnComponents HexByte `json:"cpuSvnComponents"` } type TcbComponent struct { diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index eb1e6bbb..4129e223 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -156,7 +156,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ result.Signature = sig // Verify Quote Body values - err = VerifyTdxQuoteBody(&tdxQuote.QuoteBody, &tcbInfo, "eCerts, &tdxReferenceValue, result) + err = verifyTdxQuoteBody(&tdxQuote.QuoteBody, &tcbInfo, "eCerts, &tdxReferenceValue, result) if err != nil { msg := fmt.Sprintf("Failed to verify TDX Report Body: %v", err) result.Summary.setFalse(&msg) @@ -191,29 +191,12 @@ func verifyTdxPolicy(s TdxReport, v TdxPolicy) (PolicyCheck, bool) { return r, true } -func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertificates, tdxReferenceValue *ReferenceValue, result *TdxMeasurementResult) error { +func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertificates, tdxReferenceValue *ReferenceValue, result *TdxMeasurementResult) error { if body == nil || tcbInfo == nil || certs == nil || tdxReferenceValue == nil || result == nil { return fmt.Errorf("invalid function parameter (null pointer exception)") } - // Parse and verify PCK certificate extensions - sgxExtensions, err := ParseSGXExtensions(certs.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant, only means it is a sequence) - if err != nil { - return fmt.Errorf("failed to parse SGX Extensions from PCK Certificate: %v", err) - } - - if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { - return fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", - tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) - } - - if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { - return fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", - tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) - } - - // check MrTd reference value - // (MrTd is the measurement of the initial contents of the TD) + // check MrTd reference value (measurement of the initial contents of the TD) if !reflect.DeepEqual(body.MrTd[:], []byte(tdxReferenceValue.Sha256)) { result.Artifacts = append(result.Artifacts, DigestResult{ @@ -243,6 +226,21 @@ func VerifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi if !bytes.Equal(tdxReferenceValue.Tdx.TdId.MrOwner[:], body.MrOwner[:]) { return fmt.Errorf("MrOwner mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.TdId.MrOwner, body.MrOwner) } + + if !bytes.Equal(tcbInfo.TcbInfo.TdxModule.Mrsigner, body.MrSignerSeam[:]) { + return fmt.Errorf("MrSigner mismatch. Expected: %v, Got: %v", tcbInfo.TcbInfo.TdxModule.Mrsigner, body.MrSignerSeam) + } + + // check attributes + // quoteSeamAttributes := body.SeamAttributes + // tcbInfoSeamAttributes := tcbInfo.TcbInfo.TdxModule.Attributes + + // if len(attributes_mask) == len() { + // for i := range attributes_quote { + // attributes_quote[i] &= attributes_mask[i] + // } + // } + // check SeamAttributes // attributes_quote := body.SeamAttributes // if !reflect.DeepEqual(tdxReferenceValue.Tdx.SeamAttributes[:], attributes_quote[:]) { diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index b698ac1c..f04d6e63 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -182,8 +182,8 @@ type SgxMeasurementResult struct { Signature SignatureResult `json:"signature"` Artifacts []DigestResult `json:"artifacts"` VersionMatch Result `json:"reportVersionMatch"` - TcbInfoCheck TcbInfoCheck `json:"tcbInfoCheck"` - QeIdentityCheck QEIdentityCheck `json:"qeIdentityCheck"` + TcbInfoCheck TcbLevelCheck `json:"tcbInfoCheck"` + QeIdentityCheck TcbLevelCheck `json:"qeIdentityCheck"` PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test FwCheck VersionCheck `json:"fwCheck"` // TODO: test } @@ -196,24 +196,22 @@ type TdxMeasurementResult struct { Signature SignatureResult `json:"signature"` Artifacts []DigestResult `json:"artifacts"` VersionMatch Result `json:"reportVersionMatch"` - TcbInfoCheck TcbInfoCheck `json:"tcbInfoCheck"` - QeIdentityCheck QEIdentityCheck `json:"qeIdentityCheck"` - FwCheck VersionCheck `json:"fwCheck"` // TODO: test - PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test + TcbInfoCheck TcbLevelCheck `json:"tcbInfoCheck"` + QeIdentityCheck TcbLevelCheck `json:"qeIdentityCheck"` + ExtendedTdCheck ExtendedTdCheck `json:"extendedTdCheck"` + + FwCheck VersionCheck `json:"fwCheck"` // TODO: test + PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test } -type TcbInfoCheck struct { - Success bool `json:"success"` +type TcbLevelCheck struct { + Summary Result `json:"success"` TcbLevelStatus string `json:"status"` TcbLevelDate time.Time `json:"date"` - Details string `json:"details,omitempty"` } -type QEIdentityCheck struct { - Success bool `json:"success"` - TcbLevelStatus string `json:"status"` - TcbLevelDate time.Time `json:"date"` - Details string `json:"details,omitempty"` +type ExtendedTdCheck struct { + Summary Result `json:"success"` } // IasMeasurementResult represents the results for the verification From a12eb5b46da7c95b7ba918b27cb7f1262f88d82f Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Sun, 19 Nov 2023 15:43:01 +0000 Subject: [PATCH 15/23] implemented tdx verification --- attestationreport/attestationreport.go | 14 +-- attestationreport/tdx.go | 132 ++++++++++++++++++------- attestationreport/tdx_test.go | 32 +++--- attestationreport/validationreport.go | 23 ++++- 4 files changed, 131 insertions(+), 70 deletions(-) diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index 83a3150c..bcb1460f 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -233,13 +233,9 @@ type TDXDetails struct { CaFingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint Policy TdxPolicy `json:"policy" cbor:"3,keyasint"` TdId TDId `json:"tdId" cbor:"4,keyasint"` - TdAttributes TDAttributes `json:"tdAttributes" cbor:"5,keyasint"` + TdAttributes [8]byte `json:"tdAttributes" cbor:"5,keyasint"` Xfam [8]byte `json:"xfam" cbor:"6,keyasint"` - - // TODO: check if these are necessary - SeamAttributes [8]byte `json:"seamattributes" cbor:"7,keyasint"` - MrSignerSeam string `json:"mrsignerseam" cbor:"8,keyasint"` - MrSeam string `json:"mrseam" cbor:"9,keyasint"` + MrSeam string `json:"mrseam" cbor:"9,keyasint"` } // Identity of TD, i.e. the contained measurement @@ -254,12 +250,6 @@ type TDId struct { RtMr3 [48]byte `json:"rtMr3" cbor:"0,keyasint"` // runtime measurement } -type TDAttributes struct { - Tud byte `json:"tud" cbor:"0,keyasint"` - Sec [3]byte `json:"sec" cbor:"1,keyasint"` - Other [4]byte `json:"other" cbor:"2,keyasint"` -} - // ReferenceValue represents the attestation report // element of types 'SNP Reference Value', 'TPM Reference Value' // and 'SW Reference Value' diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 4129e223..ff068e9d 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -213,7 +213,7 @@ func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi Success: false, Type: "Measurement", }) - return fmt.Errorf("MrSeam mismatch. Expected: %v, Got. %v", tdxReferenceValue.Sha256, body.MrTd) + return fmt.Errorf("MrTd mismatch. Expected: %v, Got. %v", tdxReferenceValue.Sha256, body.MrTd) } else { result.Artifacts = append(result.Artifacts, DigestResult{ @@ -223,43 +223,105 @@ func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi }) } - if !bytes.Equal(tdxReferenceValue.Tdx.TdId.MrOwner[:], body.MrOwner[:]) { - return fmt.Errorf("MrOwner mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.TdId.MrOwner, body.MrOwner) + // Perform Extended TD Check (see DCAP documentation) + result.ExtendedTdCheck.TdIdentity = append(result.ExtendedTdCheck.TdIdentity, + // TODO: maybe move the next two values to result.Artifacts + ComparisonResult{ + Name: "MrSignerSeam", + Success: bytes.Equal(tcbInfo.TcbInfo.TdxModule.Mrsigner[:], body.MrSignerSeam[:]), + Claimed: hex.EncodeToString(tcbInfo.TcbInfo.TdxModule.Mrsigner[:]), + Measured: hex.EncodeToString(body.MrSignerSeam[:]), + }, + ComparisonResult{ + Name: "MrSeam", + Success: tdxReferenceValue.Tdx.MrSeam == hex.EncodeToString(body.MrSeam[:]), + Claimed: tdxReferenceValue.Tdx.MrSeam, + Measured: hex.EncodeToString(body.MrSeam[:]), + }, + ComparisonResult{ + Name: "MrOwner", + Success: bytes.Equal(tdxReferenceValue.Tdx.TdId.MrOwner[:], body.MrOwner[:]), + Claimed: hex.EncodeToString(tdxReferenceValue.Tdx.TdId.MrOwner[:]), + Measured: hex.EncodeToString(body.MrOwner[:]), + }, + ComparisonResult{ + Name: "MrOwnerConfig", + Success: bytes.Equal(tdxReferenceValue.Tdx.TdId.MrOwnerConfig[:], body.MrOwnerConfig[:]), + Claimed: hex.EncodeToString(tdxReferenceValue.Tdx.TdId.MrOwnerConfig[:]), + Measured: hex.EncodeToString(body.MrOwnerConfig[:]), + }, + ComparisonResult{ + Name: "MrConfigId", + Success: bytes.Equal(tdxReferenceValue.Tdx.TdId.MrConfigId[:], body.MrConfigId[:]), + Claimed: hex.EncodeToString(tdxReferenceValue.Tdx.TdId.MrConfigId[:]), + Measured: hex.EncodeToString(body.MrConfigId[:]), + }, + ComparisonResult{ + Name: "RtMr0", + Success: bytes.Equal(tdxReferenceValue.Tdx.TdId.RtMr0[:], body.RtMr0[:]), + Claimed: hex.EncodeToString(tdxReferenceValue.Tdx.TdId.RtMr0[:]), + Measured: hex.EncodeToString(body.RtMr0[:]), + }, + ComparisonResult{ + Name: "RtMr1", + Success: bytes.Equal(tdxReferenceValue.Tdx.TdId.RtMr1[:], body.RtMr1[:]), + Claimed: hex.EncodeToString(tdxReferenceValue.Tdx.TdId.RtMr1[:]), + Measured: hex.EncodeToString(body.RtMr1[:]), + }, + ComparisonResult{ + Name: "RtMr2", + Success: bytes.Equal(tdxReferenceValue.Tdx.TdId.RtMr2[:], body.RtMr2[:]), + Claimed: hex.EncodeToString(tdxReferenceValue.Tdx.TdId.RtMr2[:]), + Measured: hex.EncodeToString(body.RtMr3[:]), + }, + ComparisonResult{ + Name: "RtMr3", + Success: bytes.Equal(tdxReferenceValue.Tdx.TdId.RtMr3[:], body.RtMr3[:]), + Claimed: hex.EncodeToString(tdxReferenceValue.Tdx.TdId.RtMr3[:]), + Measured: hex.EncodeToString(body.RtMr3[:]), + }, + ) + + seamAttributesQuote := body.SeamAttributes + if len(tcbInfo.TcbInfo.TdxModule.AttributesMask) == len(seamAttributesQuote) { + for i := range seamAttributesQuote { + seamAttributesQuote[i] &= tcbInfo.TcbInfo.TdxModule.AttributesMask[i] + } + } + seamAttributesResult := bytes.Equal(tcbInfo.TcbInfo.TdxModule.Attributes, seamAttributesQuote[:]) + + result.ExtendedTdCheck.TdAttributes = append(result.ExtendedTdCheck.TdAttributes, + AttributesCheck{ + Name: "TdAttributes", + Success: bytes.Equal(body.TdAttributes[:], tdxReferenceValue.Tdx.TdAttributes[:]), + Claimed: tdxReferenceValue.Tdx.TdAttributes[:], + Measured: body.TdAttributes[:], + }, + AttributesCheck{ + Name: "XFAM", + Success: bytes.Equal(body.XFAM[:], tdxReferenceValue.Tdx.Xfam[:]), + Claimed: tdxReferenceValue.Tdx.Xfam[:], + Measured: body.XFAM[:], + }, + AttributesCheck{ + Name: "SeamAttributes", + Success: seamAttributesResult, + Claimed: tcbInfo.TcbInfo.TdxModule.Attributes, + Measured: seamAttributesQuote[:], + }, + ) + + for _, v := range result.ExtendedTdCheck.TdIdentity { + if !v.Success { + return fmt.Errorf("TDX Quote Body Verification failed. %v: (Expected: %v, Got: %v)", v.Name, v.Claimed, v.Measured) + } } - if !bytes.Equal(tcbInfo.TcbInfo.TdxModule.Mrsigner, body.MrSignerSeam[:]) { - return fmt.Errorf("MrSigner mismatch. Expected: %v, Got: %v", tcbInfo.TcbInfo.TdxModule.Mrsigner, body.MrSignerSeam) + for _, v := range result.ExtendedTdCheck.TdAttributes { + if !v.Success { + return fmt.Errorf("TDX Quote Body Verification failed. %v: (Expected: %v, Got: %v)", v.Name, v.Claimed, v.Measured) + } } - // check attributes - // quoteSeamAttributes := body.SeamAttributes - // tcbInfoSeamAttributes := tcbInfo.TcbInfo.TdxModule.Attributes - - // if len(attributes_mask) == len() { - // for i := range attributes_quote { - // attributes_quote[i] &= attributes_mask[i] - // } - // } - - // check SeamAttributes - // attributes_quote := body.SeamAttributes - // if !reflect.DeepEqual(tdxReferenceValue.Tdx.SeamAttributes[:], attributes_quote[:]) { - // return fmt.Errorf("SeamAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.SeamAttributes, attributes_quote) - // } - - // check MrSignerSeam value - // refSigner, err := hex.DecodeString(tdxReferenceValue.Tdx.MrSignerSeam) - // if err != nil { - // return fmt.Errorf("decoding MRSIGNERSEAM reference value failed: %v", err) - // } - // if !reflect.DeepEqual(refSigner[:], body.MrSignerSeam[:]) { - // return fmt.Errorf("MRSIGNERSEAM mismatch. Expected: %v, Got: %v", refSigner, body.MrSignerSeam[:]) - // } - - // fmt.Println(body.TdAttributes) - // attributes_quote = body.TdAttributes - // if !reflect.DeepEqual(tdxReferenceValue.Tdx.TdAttributes[:], attributes_quote[:]) { - // return fmt.Errorf("TdAttributes mismatch. Expected: %v, Got: %v", tdxReferenceValue.Tdx.TdAttributes, attributes_quote) - // } return nil } diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 3508d712..06185b2d 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -772,12 +772,11 @@ var ( rtMr2 = [48]byte{} // 0x00s rtMr3 = [48]byte{} // 0x00s - validMrSeam = "2fd279c16164a93dd5bf373d834328d46008c2b693af9ebb865b08b2ced320c9a89b4869a9fab60fbe9d0c5a5363c656" - validMrSignerSeam = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - validSeamAttributes = [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - validTdAttributes = [8]byte{0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00} - validMrTd = "145fe28dab356d75767ab56ae83bc59ac045dc548bf40a3fb11390446af19f6ee21b0b8874c9a7864cedb64c6573d932" - validTDXNonce, _ = hex.DecodeString("324e81d1e3d71e9f77c9e1aafcbdf157aa532d059c3637da19bd28f70e654540b3c711969e303515f916bff5d4b04c7e037a84a0b0bb7ac978a3c0860806c6bb") + validMrSeam = "2fd279c16164a93dd5bf373d834328d46008c2b693af9ebb865b08b2ced320c9a89b4869a9fab60fbe9d0c5a5363c656" + validTdAttributes = [8]byte{0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00} + validMrTd = "145fe28dab356d75767ab56ae83bc59ac045dc548bf40a3fb11390446af19f6ee21b0b8874c9a7864cedb64c6573d932" + validTDXNonce, _ = hex.DecodeString("324e81d1e3d71e9f77c9e1aafcbdf157aa532d059c3637da19bd28f70e654540b3c711969e303515f916bff5d4b04c7e037a84a0b0bb7ac978a3c0860806c6bb") + validXFAM = [8]byte{0xE7, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00} ) func Test_verifyTdxMeasurements(t *testing.T) { @@ -824,11 +823,9 @@ func Test_verifyTdxMeasurements(t *testing.T) { RtMr2: rtMr2, RtMr3: rtMr3, }, - TdAttributes: TDAttributes{ - Tud: 0, - Sec: [3]byte{0x00, 0x00, 0x10}, - Other: [4]byte{0x00, 0x00, 0x00, 0x00}, - }, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, + Xfam: validXFAM, }, }, }, @@ -862,15 +859,10 @@ func Test_verifyTdxMeasurements(t *testing.T) { Debug: false, ValidTcbStatus: []string{string(OutOfDate)}, }, - CaFingerprint: aisecCertrootCAFingerprint, - SeamAttributes: validSeamAttributes, - MrSignerSeam: validMrSignerSeam, - MrSeam: validMrSeam, - TdAttributes: TDAttributes{ - Tud: 0, - Sec: [3]byte{0x00, 0x00, 0x10}, - Other: [4]byte{0x00, 0x00, 0x00, 0x00}, - }, + CaFingerprint: aisecCertrootCAFingerprint, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, + Xfam: validXFAM, }, }, }, diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index f04d6e63..170de678 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -184,8 +184,9 @@ type SgxMeasurementResult struct { VersionMatch Result `json:"reportVersionMatch"` TcbInfoCheck TcbLevelCheck `json:"tcbInfoCheck"` QeIdentityCheck TcbLevelCheck `json:"qeIdentityCheck"` - PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test - FwCheck VersionCheck `json:"fwCheck"` // TODO: test + + PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test + FwCheck VersionCheck `json:"fwCheck"` // TODO: test } // TdxMeasurementResult represents the results for the verification @@ -211,7 +212,23 @@ type TcbLevelCheck struct { } type ExtendedTdCheck struct { - Summary Result `json:"success"` + Summary Result `json:"success"` + TdIdentity []ComparisonResult `json:"tdId"` // TD identity values from quote body + TdAttributes []AttributesCheck `json:"tdAttributes"` // attributes and attribute masks from body +} + +type ComparisonResult struct { + Name string `json:"name"` + Success bool `json:"success"` + Claimed string `json:"claimed"` + Measured string `json:"measured"` +} + +type AttributesCheck struct { + Name string `json:"name"` + Success bool `json:"success"` + Claimed []byte `json:"claimed"` + Measured []byte `json:"measured"` } // IasMeasurementResult represents the results for the verification From 316753eb527fe0cd087badcdb1d85ae6431c957c Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Mon, 20 Nov 2023 16:06:15 +0000 Subject: [PATCH 16/23] started implementing tests for Verify() with TDX --- attestationreport/attestationreport.go | 11 +- attestationreport/attestationreport_test.go | 118 +++++ attestationreport/tdx.go | 19 - attestationreport/tdx_test.go | 475 +++++++------------- attestationreport/validationreport.go | 3 - 5 files changed, 291 insertions(+), 335 deletions(-) diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index bcb1460f..2d91da57 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -594,6 +594,13 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel result.Success = false } + // If present, verify AMD SEV SNP measurements against provided SNP reference values + result.MeasResult.TdxMeasResult, ok = verifyTdxMeasurements(ar.TdxM, nonce, + referenceValues["TDX Reference Value"]) + if !ok { + result.Success = false + } + // If present, verify ARM PSA EAT measurements against provided PSA reference values result.MeasResult.IasMeasResult, ok = verifyIasMeasurements(ar.IasM, nonce, referenceValues["IAS Reference Value"], cas) @@ -625,7 +632,7 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel // If no hardware trust anchor is present, the maximum certification level is 1 // If there are referenceValues with a higher trust level present, the remote attestation // must fail - if ar.TpmM == nil && ar.SnpM == nil && aggCertLevel > 1 { + if ar.TpmM == nil && ar.SnpM == nil && ar.TdxM == nil && aggCertLevel > 1 { msg := fmt.Sprintf("No hardware trust anchor measurements present but claimed certification level is %v, which requires a hardware trust anchor", aggCertLevel) result.ProcessingError = append(result.ProcessingError, msg) result.Success = false @@ -1001,7 +1008,7 @@ func collectReferenceValues(ar *ArPlain) (map[string][]ReferenceValue, error) { // Iterate through the reference values and sort them into the different types for _, v := range verList { - if v.Type != "SNP Reference Value" && v.Type != "SW Reference Value" && v.Type != "TPM Reference Value" { + if v.Type != "SNP Reference Value" && v.Type != "SW Reference Value" && v.Type != "TPM Reference Value" && v.Type != "TDX Reference Value" { return nil, fmt.Errorf("reference value of type %v is not supported", v.Type) } verMap[v.Type] = append(verMap[v.Type], v) diff --git a/attestationreport/attestationreport_test.go b/attestationreport/attestationreport_test.go index 6015e375..f3e509d9 100644 --- a/attestationreport/attestationreport_test.go +++ b/attestationreport/attestationreport_test.go @@ -22,6 +22,7 @@ import ( "crypto/rand" "crypto/x509" "crypto/x509/pkix" + "encoding/json" "fmt" "math/big" "reflect" @@ -226,6 +227,7 @@ func TestVerify(t *testing.T) { if got.Success != tt.want.Success { t.Errorf("Result.Success = %v, want %v", got.Success, tt.want.Success) } + fmt.Println(got) }) } } @@ -266,3 +268,119 @@ func Test_collectReferenceValues(t *testing.T) { }) } } + +var ( + validTdxReferenceValue = []ReferenceValue{ + { + Type: "TDX Reference Value", + Sha256: validTDXMeasurement, + Tdx: &TDXDetails{ + Version: 0x04, + Collateral: SGXCollateral{ + TeeType: tee_type_tdx, + TcbInfo: tcb_info_tdx, + TcbInfoSize: tcb_info_tdx_size, + QeIdentity: qe_identity_tdx, + QeIdentitySize: qe_identity_tdx_size, + }, + CaFingerprint: aisecCertrootCAFingerprint, + TdId: TDId{ + MrOwner: mrOwner, + MrOwnerConfig: mrOwnerConfig, + MrConfigId: mrConfigId, + RtMr0: rtMr0, + RtMr1: rtMr1, + RtMr2: rtMr2, + RtMr3: rtMr3, + }, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, + Xfam: validXFAM, + }, + }, + } + + tdxRtmManifest = RtmManifest{ + ReferenceValues: validTdxReferenceValue, + } +) + +func Test_verifyTdxReport(t *testing.T) { + type args struct { + tdxMeas *TdxMeasurement + } + tests := []struct { + name string + args args + want bool + }{ + // TODO: implement tests + { + name: "Valid TDX Report", + args: args{ + tdxMeas: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: validTDXCertChain, + }, + }, + want: true, + }, + } + logrus.SetLevel(logrus.TraceLevel) + + // Setup Test Keys and Certificates + log.Trace("Creating Keys and Certificates") + key, certchain, err := createCertsAndKeys() + if err != nil { + log.Errorf("Internal Error: Failed to create testing certs and keys: %v", err) + return + } + + swSigner := &SwSigner{ + priv: key, + certChain: certchain, + } + + s := JsonSerializer{} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + // Generating a TDX Report + ar := ArPacked{ + Type: "Attestation Report", + } + ar.Nonce = validTDXNonce + + ar.RtmManifest, err = json.Marshal(tdxRtmManifest) + if err != nil { + t.Errorf("failed to marshal the RtmManifest: %v", err) + } + + ar.TdxM = tt.args.tdxMeas + + report, err := s.Marshal(ar) + if err != nil { + t.Errorf("failed to marshal the Attestation Report: %v", err) + } + // end of Generate() + + // sign the report + arSigned, err := Sign(report, swSigner, s) + if err != nil { + t.Errorf("Internal Error: Failed to sign Attestion Report: %v", err) + } + + // call Verifier + res := Verify(arSigned, validTDXNonce, + internal.WriteCertPem(certchain[len(certchain)-1]), nil, 0) + + if res.Success != tt.want { + t.Errorf("verifyAr() wrong result. expected: %v, got: %v", tt.want, res.Success) + } + + fmt.Println(res) + }) + } +} diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index ff068e9d..4a05efab 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -162,13 +162,6 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ result.Summary.setFalse(&msg) result.Summary.Success = false return result, false - } else { - result.Artifacts = append(result.Artifacts, - DigestResult{ - Name: tdxReferenceValue.Name, - Digest: hex.EncodeToString(tdxQuote.QuoteBody.MrSeam[:]), - Success: true, - }) } // check version @@ -176,21 +169,10 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ if !ret { return result, false } - result.PolicyCheck, ret = verifyTdxPolicy(tdxQuote, tdxReferenceValue.Tdx.Policy) - if !ret { - ok = false - } return result, ok } -// TODO: implement this function -func verifyTdxPolicy(s TdxReport, v TdxPolicy) (PolicyCheck, bool) { - r := PolicyCheck{} - - return r, true -} - func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertificates, tdxReferenceValue *ReferenceValue, result *TdxMeasurementResult) error { if body == nil || tcbInfo == nil || certs == nil || tdxReferenceValue == nil || result == nil { return fmt.Errorf("invalid function parameter (null pointer exception)") @@ -225,7 +207,6 @@ func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi // Perform Extended TD Check (see DCAP documentation) result.ExtendedTdCheck.TdIdentity = append(result.ExtendedTdCheck.TdIdentity, - // TODO: maybe move the next two values to result.Artifacts ComparisonResult{ Name: "MrSignerSeam", Success: bytes.Equal(tcbInfo.TcbInfo.TdxModule.Mrsigner[:], body.MrSignerSeam[:]), diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 06185b2d..38e33450 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -436,316 +436,6 @@ var ( 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x00, } - validTDXQuote = []byte{ - 0x04, 0x00, 0x02, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, - 0x95, 0x7f, 0x06, 0x07, 0x43, 0xcf, 0xf3, 0x10, 0x95, 0xd7, 0x8f, 0xaa, - 0xfd, 0xc1, 0x19, 0x5e, 0x9b, 0x56, 0xfa, 0x76, 0x1e, 0x6d, 0x6c, 0x1a, - 0x52, 0xc1, 0xa3, 0x8c, 0xf7, 0xed, 0xf3, 0x0a, 0x52, 0x4b, 0x4e, 0xbb, - 0x04, 0x9f, 0x59, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x0b, 0x00, 0x00, - 0x6a, 0xcd, 0x48, 0xb0, 0x10, 0xc3, 0xe8, 0x22, 0x45, 0x23, 0xd2, 0xdd, - 0x4e, 0x6a, 0x40, 0x08, 0xc2, 0xc6, 0xfa, 0xee, 0xb5, 0x0c, 0xdc, 0xa9, - 0x23, 0xef, 0x68, 0x49, 0x03, 0x02, 0xcc, 0x2c, 0xc6, 0xec, 0xcc, 0xc0, - 0x06, 0xd3, 0x0f, 0x7c, 0x23, 0x2f, 0x51, 0xd3, 0x23, 0xd0, 0x50, 0x6d, - 0xd6, 0x2a, 0x69, 0x5a, 0x76, 0x47, 0x39, 0x79, 0x80, 0xf3, 0x32, 0x76, - 0x16, 0x3b, 0xf3, 0x8a, 0xe5, 0x9d, 0x11, 0x57, 0x00, 0xfd, 0x53, 0x43, - 0x38, 0xb7, 0x48, 0xa9, 0x6f, 0x1f, 0x9c, 0x98, 0x21, 0x78, 0x7e, 0x54, - 0x42, 0xc2, 0xa1, 0xba, 0xb3, 0x8d, 0x30, 0x72, 0x6a, 0x11, 0x5a, 0x49, - 0xa4, 0x16, 0x11, 0x44, 0x5f, 0xb4, 0x1f, 0x23, 0x04, 0x9a, 0xca, 0x78, - 0xef, 0x57, 0x21, 0x3d, 0x0d, 0xd0, 0x09, 0x37, 0x61, 0x9b, 0x07, 0x10, - 0xee, 0x13, 0xf1, 0x70, 0xc5, 0x69, 0x22, 0xdf, 0x06, 0x00, 0x6e, 0x0b, - 0x00, 0x00, 0x52, 0xc1, 0xa3, 0x8c, 0xf7, 0xed, 0xf3, 0x0a, 0x52, 0x4b, - 0x4e, 0xbb, 0x04, 0x9f, 0x59, 0xc7, 0x13, 0x37, 0x3b, 0x09, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0c, 0xce, 0xfa, 0x96, 0xce, 0x1e, 0xd0, 0xf1, 0xbf, 0xc1, - 0x1b, 0xbf, 0x59, 0x9b, 0xc0, 0xbe, 0xfd, 0x2b, 0x2e, 0x5a, 0xab, 0xb6, - 0x5f, 0xe9, 0xbe, 0xd7, 0xa5, 0x13, 0x31, 0x57, 0x6b, 0x11, 0xd2, 0x5a, - 0x5d, 0xa5, 0x27, 0x20, 0x08, 0xd5, 0x46, 0x83, 0x1c, 0x9d, 0x4d, 0xe0, - 0x9f, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xbd, - 0x9f, 0xc2, 0x12, 0x98, 0x35, 0xba, 0x1d, 0xcc, 0xa1, 0x93, 0x9d, 0x57, - 0xd0, 0x8e, 0x88, 0x56, 0x1b, 0x34, 0x74, 0x0d, 0x59, 0x40, 0x59, 0x72, - 0xd4, 0xba, 0x25, 0xa7, 0xe5, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x74, 0x85, 0x31, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xbf, - 0x58, 0x7f, 0xdf, 0xdf, 0xd6, 0x2b, 0xf3, 0x9a, 0x59, 0x09, 0x47, 0xa6, - 0x69, 0x8f, 0x97, 0xdb, 0xed, 0x71, 0xc1, 0x6e, 0x5f, 0x29, 0x10, 0x57, - 0x77, 0x31, 0x0c, 0x81, 0x58, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x39, 0xf4, 0x6b, 0xf2, 0x05, 0x9f, 0xa2, 0xf7, 0xaa, 0x47, - 0x41, 0x4c, 0x83, 0xdb, 0x4a, 0xfd, 0xbe, 0x42, 0x5f, 0xc3, 0x84, 0xaa, - 0x9a, 0x87, 0x68, 0x71, 0x80, 0xa7, 0x14, 0x18, 0xb1, 0x60, 0x57, 0x7d, - 0x45, 0xf8, 0xa1, 0x75, 0xc2, 0x3a, 0xd4, 0x5c, 0x62, 0x5c, 0x9d, 0x7e, - 0x79, 0x9f, 0x27, 0x84, 0x29, 0x2c, 0x87, 0x25, 0xce, 0x0f, 0xc9, 0xda, - 0x52, 0xf2, 0xe8, 0x55, 0x72, 0xbd, 0x00, 0x00, 0x05, 0x00, 0xa6, 0x09, - 0x00, 0x00, 0x30, 0x82, 0x02, 0x88, 0x30, 0x82, 0x02, 0x2e, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x14, 0x1a, 0x58, 0x65, 0x01, 0x9d, 0x18, 0xb0, - 0x4e, 0x04, 0x5a, 0xf8, 0x2d, 0x06, 0xc0, 0x85, 0x50, 0xe2, 0x0e, 0xb4, - 0x76, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, - 0x02, 0x30, 0x68, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, - 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x53, 0x47, 0x58, 0x20, - 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1a, 0x30, 0x18, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, - 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, - 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0b, 0x53, 0x61, - 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, 0x61, 0x72, 0x61, 0x31, 0x0b, 0x30, - 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x43, 0x41, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, - 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x38, 0x30, 0x36, 0x31, 0x33, 0x35, - 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x34, 0x39, 0x31, 0x32, 0x33, 0x31, - 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x68, 0x31, 0x1a, 0x30, - 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, - 0x6c, 0x20, 0x53, 0x47, 0x58, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, - 0x41, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, - 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, - 0x04, 0x07, 0x0c, 0x0b, 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, - 0x61, 0x72, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, - 0x0c, 0x02, 0x43, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, - 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, - 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x52, 0x8e, 0xfb, 0xf6, - 0x97, 0x45, 0x34, 0xef, 0xcd, 0x79, 0xf9, 0x95, 0x81, 0x53, 0x08, 0xe3, - 0x64, 0xd2, 0x03, 0x17, 0x71, 0xfe, 0xf2, 0x7f, 0x8e, 0x02, 0xeb, 0xa1, - 0x4d, 0x5e, 0x0b, 0x55, 0xda, 0x1c, 0xa1, 0xa3, 0xdf, 0xe0, 0x5a, 0x6d, - 0x6a, 0xee, 0xdf, 0xe9, 0x0f, 0x86, 0x67, 0x3a, 0x8f, 0x5a, 0x74, 0x41, - 0x70, 0x81, 0xa2, 0x9f, 0xe1, 0x0b, 0x59, 0xa0, 0x1e, 0xea, 0x7e, 0x4e, - 0xa3, 0x81, 0xb5, 0x30, 0x81, 0xb2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x1a, 0x58, 0x65, 0x01, 0x9d, - 0x18, 0xb0, 0x4e, 0x04, 0x5a, 0xf8, 0x2d, 0x06, 0xc0, 0x85, 0x50, 0xe2, - 0x0e, 0xb4, 0x76, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x45, - 0x30, 0x43, 0x30, 0x41, 0xa0, 0x3f, 0xa0, 0x3d, 0x86, 0x3b, 0x68, 0x74, - 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x6f, 0x6e, 0x2d, 0x65, 0x78, 0x69, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2d, - 0x6f, 0x6e, 0x6c, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x6c, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x53, 0x47, 0x58, 0x52, - 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, - 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x1a, 0x58, 0x65, 0x01, - 0x9d, 0x18, 0xb0, 0x4e, 0x04, 0x5a, 0xf8, 0x2d, 0x06, 0xc0, 0x85, 0x50, - 0xe2, 0x0e, 0xb4, 0x76, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, - 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, - 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, - 0xff, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, - 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x1e, - 0x77, 0xdb, 0xad, 0x27, 0x4d, 0xff, 0x2e, 0xc2, 0x18, 0xa1, 0x53, 0x88, - 0x5c, 0x9a, 0x39, 0x09, 0x5a, 0x87, 0xf0, 0xd7, 0xb5, 0xb6, 0xb7, 0x45, - 0x8f, 0xa7, 0xf1, 0xf5, 0xe9, 0x95, 0xe9, 0x02, 0x21, 0x00, 0x92, 0xb2, - 0x7c, 0xfc, 0x15, 0xff, 0x45, 0x4d, 0x37, 0xd4, 0x38, 0xdf, 0xfa, 0xa8, - 0xbf, 0x3b, 0x01, 0x0e, 0x97, 0xfc, 0x5a, 0x4e, 0xf5, 0x0e, 0x40, 0xf0, - 0x50, 0x0b, 0x71, 0x0b, 0x94, 0xbf, 0x30, 0x82, 0x02, 0x90, 0x30, 0x82, - 0x02, 0x37, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x03, 0x56, 0x21, - 0x20, 0xba, 0x5b, 0x85, 0xcf, 0xd4, 0xdb, 0x7a, 0xa5, 0xe4, 0x71, 0xb8, - 0x79, 0xf8, 0xb5, 0x98, 0xb1, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x68, 0x31, 0x1a, 0x30, 0x18, 0x06, - 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, - 0x53, 0x47, 0x58, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, - 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, 0x49, 0x6e, - 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, - 0x0c, 0x0b, 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, 0x61, 0x72, - 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, - 0x43, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x55, 0x53, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x38, 0x30, - 0x36, 0x31, 0x33, 0x35, 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x33, 0x36, - 0x30, 0x38, 0x30, 0x36, 0x31, 0x33, 0x35, 0x35, 0x31, 0x34, 0x5a, 0x30, - 0x71, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, - 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x53, 0x47, 0x58, 0x20, 0x50, 0x43, - 0x4b, 0x20, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x20, - 0x43, 0x41, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, - 0x11, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, - 0x55, 0x04, 0x07, 0x0c, 0x0b, 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, - 0x6c, 0x61, 0x72, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x08, 0x0c, 0x02, 0x43, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x97, 0x86, 0x02, - 0x28, 0x8a, 0x4b, 0x61, 0x74, 0xd6, 0x38, 0xfc, 0x79, 0x29, 0x26, 0xc8, - 0x49, 0x99, 0xce, 0x3d, 0x82, 0x88, 0xda, 0x7c, 0x83, 0x45, 0x48, 0xc2, - 0x07, 0x6b, 0x67, 0x2b, 0xa0, 0x79, 0x18, 0xb8, 0xc0, 0xb4, 0x9b, 0x74, - 0x0c, 0x99, 0x78, 0x54, 0x87, 0x1e, 0x6e, 0x0c, 0x39, 0x3d, 0xd4, 0x23, - 0xff, 0x5f, 0xac, 0x59, 0x7e, 0x43, 0x56, 0x51, 0xd7, 0xbe, 0x74, 0x5e, - 0x6b, 0xa3, 0x81, 0xb5, 0x30, 0x81, 0xb2, 0x30, 0x1f, 0x06, 0x03, 0x55, - 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x1a, 0x58, 0x65, 0x01, - 0x9d, 0x18, 0xb0, 0x4e, 0x04, 0x5a, 0xf8, 0x2d, 0x06, 0xc0, 0x85, 0x50, - 0xe2, 0x0e, 0xb4, 0x76, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, - 0x45, 0x30, 0x43, 0x30, 0x41, 0xa0, 0x3f, 0xa0, 0x3d, 0x86, 0x3b, 0x68, - 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x6f, 0x6e, 0x2d, 0x65, 0x78, - 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x65, 0x62, 0x75, 0x67, - 0x2d, 0x6f, 0x6e, 0x6c, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x6c, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x53, 0x47, 0x58, - 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, - 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x03, 0x56, 0x21, - 0x20, 0xba, 0x5b, 0x85, 0xcf, 0xd4, 0xdb, 0x7a, 0xa5, 0xe4, 0x71, 0xb8, - 0x79, 0xf8, 0xb5, 0x98, 0xb1, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, - 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, - 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, - 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, - 0x46, 0x5c, 0xe5, 0x28, 0xf3, 0x62, 0xec, 0x9f, 0xbd, 0xa0, 0xe4, 0x2c, - 0x5c, 0xa7, 0x39, 0x60, 0xda, 0x2f, 0xdc, 0x76, 0xe2, 0xda, 0x97, 0x95, - 0xab, 0xbd, 0xba, 0x52, 0x4b, 0x13, 0xcd, 0x47, 0x02, 0x20, 0x34, 0x89, - 0x6f, 0xe9, 0x10, 0x35, 0x01, 0xa0, 0x22, 0x5c, 0x06, 0xce, 0x5e, 0xc9, - 0x89, 0x09, 0x8d, 0xe3, 0x8b, 0x42, 0x8c, 0xb1, 0xb4, 0xb3, 0x5e, 0x4b, - 0x2c, 0xeb, 0x68, 0x00, 0x67, 0xab, 0x30, 0x82, 0x04, 0x82, 0x30, 0x82, - 0x04, 0x29, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x15, 0x00, 0xf8, 0xfc, - 0xe9, 0xa7, 0xf7, 0xa5, 0x77, 0x57, 0x49, 0xa6, 0xa2, 0xb7, 0x76, 0x0a, - 0x26, 0x49, 0x4d, 0xd9, 0x05, 0x76, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, - 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x71, 0x31, 0x23, 0x30, 0x21, - 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x49, 0x6e, 0x74, 0x65, 0x6c, - 0x20, 0x53, 0x47, 0x58, 0x20, 0x50, 0x43, 0x4b, 0x20, 0x50, 0x72, 0x6f, - 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x20, 0x43, 0x41, 0x31, 0x1a, 0x30, - 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, 0x49, 0x6e, 0x74, 0x65, - 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0b, - 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, 0x61, 0x72, 0x61, 0x31, - 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x43, 0x41, - 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, - 0x53, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x38, 0x30, 0x36, 0x31, - 0x33, 0x35, 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x38, - 0x30, 0x36, 0x31, 0x33, 0x35, 0x35, 0x31, 0x34, 0x5a, 0x30, 0x70, 0x31, - 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x49, 0x6e, - 0x74, 0x65, 0x6c, 0x20, 0x53, 0x47, 0x58, 0x20, 0x50, 0x43, 0x4b, 0x20, - 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x31, - 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x11, 0x49, 0x6e, - 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, - 0x0c, 0x0b, 0x53, 0x61, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x6c, 0x61, 0x72, - 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, - 0x43, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x55, 0x53, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, - 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x21, 0xc4, 0xfa, 0x58, 0xbc, 0xed, - 0x0a, 0xe5, 0x7a, 0x89, 0x3f, 0x33, 0xce, 0x20, 0x18, 0x41, 0xbf, 0xe4, - 0xd9, 0xf0, 0x54, 0x54, 0xbd, 0xab, 0x61, 0x82, 0xfd, 0x89, 0xfa, 0x1d, - 0x01, 0xbb, 0x30, 0x7b, 0x51, 0x36, 0xbc, 0x77, 0xc8, 0x97, 0x79, 0xd7, - 0xe5, 0x32, 0xa6, 0x5e, 0xc3, 0xe9, 0xd4, 0xf4, 0x3a, 0xe0, 0x93, 0xfc, - 0x59, 0xae, 0x54, 0x2b, 0x94, 0xe2, 0x0b, 0x17, 0x66, 0x0b, 0xa3, 0x82, - 0x02, 0x9d, 0x30, 0x82, 0x02, 0x99, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x03, 0x56, 0x21, 0x20, 0xba, - 0x5b, 0x85, 0xcf, 0xd4, 0xdb, 0x7a, 0xa5, 0xe4, 0x71, 0xb8, 0x79, 0xf8, - 0xb5, 0x98, 0xb1, 0x30, 0x58, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x51, - 0x30, 0x4f, 0x30, 0x4d, 0xa0, 0x4b, 0xa0, 0x49, 0x86, 0x47, 0x68, 0x74, - 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, - 0x65, 0x64, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x69, - 0x6e, 0x74, 0x65, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x49, 0x6e, 0x74, - 0x65, 0x6c, 0x53, 0x47, 0x58, 0x50, 0x43, 0x4b, 0x50, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, - 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x94, 0x30, 0x13, 0x7f, - 0x3b, 0x57, 0x60, 0x34, 0xfa, 0x35, 0xb8, 0x6b, 0xd9, 0x5d, 0xa9, 0x13, - 0x23, 0xdf, 0x85, 0x71, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, - 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x06, 0xc0, 0x30, 0x0c, 0x06, 0x03, - 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x82, - 0x01, 0xdd, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, - 0x01, 0x04, 0x82, 0x01, 0xce, 0x30, 0x82, 0x01, 0xca, 0x30, 0x1e, 0x06, - 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x01, 0x04, - 0x10, 0x21, 0x1d, 0x75, 0x42, 0xf3, 0x6c, 0x35, 0x11, 0xed, 0x7f, 0xb6, - 0xdd, 0x63, 0x49, 0xb3, 0xe2, 0x30, 0x82, 0x01, 0x6d, 0x06, 0x0a, 0x2a, - 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x30, 0x82, 0x01, - 0x5d, 0x30, 0x10, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, - 0x0d, 0x01, 0x02, 0x01, 0x02, 0x01, 0x52, 0x30, 0x11, 0x06, 0x0b, 0x2a, - 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x02, 0x02, 0x02, - 0x00, 0xc1, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, - 0x01, 0x0d, 0x01, 0x02, 0x03, 0x02, 0x02, 0x00, 0xa3, 0x30, 0x11, 0x06, - 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x04, - 0x02, 0x02, 0x00, 0x8c, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, - 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x05, 0x02, 0x02, 0x00, 0xf7, 0x30, - 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, - 0x02, 0x06, 0x02, 0x02, 0x00, 0xed, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, - 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x07, 0x02, 0x02, 0x00, - 0xf3, 0x30, 0x10, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, - 0x0d, 0x01, 0x02, 0x08, 0x02, 0x01, 0x0a, 0x30, 0x10, 0x06, 0x0b, 0x2a, - 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x09, 0x02, 0x01, - 0x52, 0x30, 0x10, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, - 0x0d, 0x01, 0x02, 0x0a, 0x02, 0x01, 0x4b, 0x30, 0x10, 0x06, 0x0b, 0x2a, - 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x0b, 0x02, 0x01, - 0x4e, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, - 0x0d, 0x01, 0x02, 0x0c, 0x02, 0x02, 0x00, 0xbb, 0x30, 0x10, 0x06, 0x0b, - 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x0d, 0x02, - 0x01, 0x04, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, - 0x01, 0x0d, 0x01, 0x02, 0x0e, 0x02, 0x02, 0x00, 0x9f, 0x30, 0x10, 0x06, - 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x0f, - 0x02, 0x01, 0x59, 0x30, 0x11, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, - 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x10, 0x02, 0x02, 0x00, 0xc7, 0x30, 0x11, - 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, - 0x11, 0x02, 0x02, 0x29, 0x61, 0x30, 0x1f, 0x06, 0x0b, 0x2a, 0x86, 0x48, - 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x02, 0x12, 0x04, 0x10, 0x52, 0xc1, - 0xa3, 0x8c, 0xf7, 0xed, 0xf3, 0x0a, 0x52, 0x4b, 0x4e, 0xbb, 0x04, 0x9f, - 0x59, 0xc7, 0x30, 0x10, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, - 0x01, 0x0d, 0x01, 0x03, 0x04, 0x02, 0x8a, 0x67, 0x30, 0x14, 0x06, 0x0a, - 0x2a, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x04, 0x04, 0x06, - 0xed, 0x74, 0x2a, 0xf8, 0xad, 0xf5, 0x30, 0x0f, 0x06, 0x0a, 0x2a, 0x86, - 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x0d, 0x01, 0x05, 0x0a, 0x01, 0x00, 0x30, - 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, - 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x5f, 0x70, 0x8e, 0x03, 0xb8, 0x92, - 0xdc, 0x6c, 0x0b, 0x3b, 0x5b, 0xf8, 0x16, 0xdd, 0x9f, 0x2f, 0x55, 0x94, - 0x95, 0x28, 0x64, 0xd7, 0xd3, 0x89, 0x6f, 0x55, 0xb9, 0x19, 0xc6, 0x52, - 0xac, 0xf3, 0x02, 0x20, 0x4c, 0x04, 0x98, 0x34, 0xad, 0x6b, 0x25, 0x0d, - 0x3b, 0xf7, 0x34, 0xd9, 0x56, 0xac, 0xf8, 0xe7, 0x3d, 0xac, 0x69, 0x02, - 0xe9, 0x55, 0x5a, 0x81, 0x79, 0xb1, 0x8c, 0x4d, 0x71, 0xc5, 0x59, 0x4a, - } tcb_signing_cert_tdx = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj\nftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----")) root_ca_cert_tdx = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIwaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi71OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SVUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYIKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwgAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----")) @@ -760,7 +450,6 @@ var ( validTDXCertChain = [][]byte{tcb_signing_cert_tdx.Raw, root_ca_cert_tdx.Raw} validTDXMeasurement, _ = hex.DecodeString(validMrTd) - // rootCAFingerprint = "BF85A53FC08F84CB1F73A4F75F48AF566E30AC040699BA0EC1B8D593C05B56FC" aisecCertrootCAFingerprint = "44a0196b2b99f889b8e149e95b807a350e7424964399e885a7cbb8ccfab674d3" // valid report body values @@ -870,6 +559,170 @@ func Test_verifyTdxMeasurements(t *testing.T) { }, want1: false, }, + { + name: "Invalid Certificate Chain", + args: args{ + tdxM: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: [][]byte{}, + }, + tdxV: []ReferenceValue{ + { + Type: "TDX Reference Value", + Sha256: validTDXMeasurement, + Tdx: &TDXDetails{ + Version: 0x04, + Collateral: SGXCollateral{ + TeeType: tee_type_tdx, + TcbInfo: tcb_info_tdx, + TcbInfoSize: tcb_info_tdx_size, + QeIdentity: qe_identity_tdx, + QeIdentitySize: qe_identity_tdx_size, + }, + CaFingerprint: aisecCertrootCAFingerprint, + TdId: TDId{ + MrOwner: mrOwner, + MrOwnerConfig: mrOwnerConfig, + MrConfigId: mrConfigId, + RtMr0: rtMr0, + RtMr1: rtMr1, + RtMr2: rtMr2, + RtMr3: rtMr3, + }, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, + Xfam: validXFAM, + }, + }, + }, + nonce: validTDXNonce, + }, + want1: false, + }, + { + name: "Invalid measurement", + args: args{ + tdxM: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: validTDXCertChain, + }, + tdxV: []ReferenceValue{ + { + Type: "TDX Reference Value", + Sha256: []byte("12345"), + Tdx: &TDXDetails{ + Version: 0x04, + Collateral: SGXCollateral{ + TeeType: tee_type_tdx, + TcbInfo: tcb_info_tdx, + TcbInfoSize: tcb_info_tdx_size, + QeIdentity: qe_identity_tdx, + QeIdentitySize: qe_identity_tdx_size, + }, + CaFingerprint: aisecCertrootCAFingerprint, + TdId: TDId{ + MrOwner: mrOwner, + MrOwnerConfig: mrOwnerConfig, + MrConfigId: mrConfigId, + RtMr0: rtMr0, + RtMr1: rtMr1, + RtMr2: rtMr2, + RtMr3: rtMr3, + }, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, + Xfam: validXFAM, + }, + }, + }, + nonce: validTDXNonce, + }, + want1: false, + }, + { + name: "Invalid Attributes", + args: args{ + tdxM: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: validTDXCertChain, + }, + tdxV: []ReferenceValue{ + { + Type: "TDX Reference Value", + Sha256: validTDXMeasurement, + Tdx: &TDXDetails{ + Version: 0x04, + Collateral: SGXCollateral{ + TeeType: tee_type_tdx, + TcbInfo: tcb_info_tdx, + TcbInfoSize: tcb_info_tdx_size, + QeIdentity: qe_identity_tdx, + QeIdentitySize: qe_identity_tdx_size, + }, + CaFingerprint: aisecCertrootCAFingerprint, + TdId: TDId{ + MrOwner: mrOwner, + MrOwnerConfig: mrOwnerConfig, + MrConfigId: mrConfigId, + RtMr0: rtMr0, + RtMr1: rtMr1, + RtMr2: rtMr2, + RtMr3: rtMr3, + }, + MrSeam: validMrSeam, + TdAttributes: [8]byte{}, + Xfam: validXFAM, + }, + }, + }, + nonce: validTDXNonce, + }, + want1: false, + }, + { + name: "Invalid TcbInfo Report", + args: args{ + tdxM: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: validTDXCertChain, + }, + tdxV: []ReferenceValue{ + { + Type: "TDX Reference Value", + Sha256: validTDXMeasurement, + Tdx: &TDXDetails{ + Version: 0x04, + Collateral: SGXCollateral{ + TeeType: tee_type_tdx, + TcbInfo: []byte{}, + TcbInfoSize: tcb_info_tdx_size, + QeIdentity: qe_identity_tdx, + QeIdentitySize: qe_identity_tdx_size, + }, + CaFingerprint: aisecCertrootCAFingerprint, + TdId: TDId{ + MrOwner: mrOwner, + MrOwnerConfig: mrOwnerConfig, + MrConfigId: mrConfigId, + RtMr0: rtMr0, + RtMr1: rtMr1, + RtMr2: rtMr2, + RtMr3: rtMr3, + }, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, + Xfam: validXFAM, + }, + }, + }, + nonce: validTDXNonce, + }, + want1: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index 170de678..8999d8e7 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -200,9 +200,6 @@ type TdxMeasurementResult struct { TcbInfoCheck TcbLevelCheck `json:"tcbInfoCheck"` QeIdentityCheck TcbLevelCheck `json:"qeIdentityCheck"` ExtendedTdCheck ExtendedTdCheck `json:"extendedTdCheck"` - - FwCheck VersionCheck `json:"fwCheck"` // TODO: test - PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test } type TcbLevelCheck struct { From 49b31925f4cf951f696c5799866fd37cebdb34f0 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Wed, 22 Nov 2023 20:58:10 +0000 Subject: [PATCH 17/23] fully implemented tdx verification --- attestationreport/PCKCertCRL | Bin 2663 -> 2662 bytes attestationreport/attestationreport.go | 57 +- attestationreport/attestationreport_test.go | 148 +++- attestationreport/intel_helpers.go | 665 +++++++++++++++++ attestationreport/sgx.go | 773 ++------------------ attestationreport/sgx_structs.go | 4 +- attestationreport/sgx_test.go | 13 +- attestationreport/tdx.go | 23 +- attestationreport/tdx_struct.go | 16 +- attestationreport/tdx_test.go | 40 +- attestationreport/validationreport.go | 45 +- 11 files changed, 961 insertions(+), 823 deletions(-) create mode 100644 attestationreport/intel_helpers.go diff --git a/attestationreport/PCKCertCRL b/attestationreport/PCKCertCRL index 1a59b64109e9592936bf47cb97ac3a2f06d616f1..255744d0174d7e9cd969000b55c7b76a5833135a 100644 GIT binary patch delta 822 zcmaDZ@=S!qpouGKB1^Zpk&&UXsiBEclsK=Ev7r%+J8_vZ2b_IT4~Z=@S(;G_g&&B< zZ%5)!KFBOQc@L7n#ET-6KOymf3PdJrFiE1A7|VpP0c^q~Bms~KCy@AH1%HtQKnjeR z5hj56nP~j^NPM6Hx^U~krg5?$bb|%#&;^Q-1VHAmMB;;W+(QxoDG*^rGtv`D0HmNE zi9dNED|Zg>=;{vu3lre~ delta 823 zcmaDR@?3<)pouGaB1^ZpfvKUHfw`e&lsK=Ev7r%+J8_vZ2b_IT4~Z=@S(;G_g&&B< zZ%5)!KFBOQc@L7n#ET-6KOymf3PdJrFiE1A7|VpP0c^q~Bms~KCy@AH1%HtQKnjeR z5hj56nP~j^NPM6Hx^U~krg5?$bb|%#&;^Q-1VHAmMB;;W+(QxoDG*^rGtv`D0HmNE zi9dNED z4C|IQS%tK|DsbjmAzC4Q>gmBHtIx%s-zE}QwDp2pefo5ulEaTa>UY)6`>i^Y&GpZU Xw*4WAE06xPZQIn5R~TA0L-hdw;_~F6 diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index 2d91da57..a446c58e 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -154,13 +154,6 @@ type TdxMeasurement struct { Certs [][]byte `json:"certs" cbor:"2,keyasint"` } -type TdxPolicy struct { - Type string `json:"type" cbor:"0,keyasint"` - Debug bool `json:"debug" cbor:"2,keyasint"` - ValidTcbStatus []string `json:"validTcbStatus" cbor:"3,keyasint"` // list of TCB status that are still accepted (e.g. Valid, OutOfDate) - // maybe also tcb min/max version, etc. -} - // SgxMeasurement represents the attestation report // element of type 'SGX Measurement' signed by the device type SgxMeasurement struct { @@ -169,13 +162,6 @@ type SgxMeasurement struct { Certs [][]byte `json:"certs" cbor:"2,keyasint"` } -type SgxPolicy struct { - Type string `json:"type" cbor:"0,keyasint"` - Debug bool `json:"debug" cbor:"2,keyasint"` - ValidTcbStatus []string `json:"validTcbStatus" cbor:"3,keyasint"` // list of TCB status that are still accepted (e.g. Valid, OutOfDate) - // maybe also tcb min/max version, etc. -} - type SnpPolicy struct { Type string `json:"type" cbor:"0,keyasint"` SingleSocket bool `json:"singleSocket" cbor:"1,keyasint"` @@ -210,48 +196,46 @@ type SnpDetails struct { type SGXCollateral struct { // Format of CRLs: // version 1.0: PEM, v3.0: DER base16, v3.1: DER raw binary - TeeType uint32 `json:"teeType" cbor:"2,keyasint" description:"Type of the Tee (0x00000000: SGX, 0x00000081: TDX)"` - TcbInfo json.RawMessage `json:"tcbInfo" cbor:"3,keyasint" description:"TCB Info structure in JSON format"` - TcbInfoSize uint32 `json:"tcbInfoSize" cbor:"4,keyasint" description:"Size of the TCB Info"` - QeIdentity json.RawMessage `json:"qeIdentity" cbor:"5,keyasint" description:"QE identity structure in JSON format"` - QeIdentitySize uint32 `json:"qeIdentitySize" cbor:"6,keyasint" description:"Size of the QE identity"` + TeeType uint32 `json:"teeType" cbor:"0,keyasint"` + TcbInfo json.RawMessage `json:"tcbInfo" cbor:"1,keyasint"` + TcbInfoSize uint32 `json:"tcbInfoSize" cbor:"2,keyasint"` + QeIdentity json.RawMessage `json:"qeIdentity" cbor:"3,keyasint"` + QeIdentitySize uint32 `json:"qeIdentitySize" cbor:"4,keyasint"` } type SGXDetails struct { Version uint16 `json:"version" cbor:"0,keyasint"` Collateral SGXCollateral `json:"collateral" cbor:"1,keyasint"` CaFingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint - Policy SgxPolicy `json:"policy" cbor:"3,keyasint"` - Attributes [16]byte `json:"attributes" cbor:"4,keyasint"` - IsvProdId uint16 `json:"isvProdId" cbor:"5,keyasint"` - MRSIGNER string `json:"mrsigner" cbor:"6,keyasint"` + Attributes [16]byte `json:"attributes" cbor:"3,keyasint"` + IsvProdId uint16 `json:"isvProdId" cbor:"4,keyasint"` + MrSigner string `json:"mrSigner" cbor:"5,keyasint"` + IsvSvn uint16 `json:"isvSvn" cbor:"6,keyasint"` } type TDXDetails struct { Version uint16 `json:"version" cbor:"0,keyasint"` Collateral SGXCollateral `json:"collateral" cbor:"1,keyasint"` CaFingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint - Policy TdxPolicy `json:"policy" cbor:"3,keyasint"` - TdId TDId `json:"tdId" cbor:"4,keyasint"` - TdAttributes [8]byte `json:"tdAttributes" cbor:"5,keyasint"` - Xfam [8]byte `json:"xfam" cbor:"6,keyasint"` - MrSeam string `json:"mrseam" cbor:"9,keyasint"` + TdId TDId `json:"tdId" cbor:"3,keyasint"` + TdAttributes [8]byte `json:"tdAttributes" cbor:"4,keyasint"` + Xfam [8]byte `json:"xfam" cbor:"5,keyasint"` + MrSeam string `json:"mrseam" cbor:"6,keyasint"` } -// Identity of TD, i.e. the contained measurement -// MrTd is already given in ReferenceValue.Sha256 +// Note: MrTd is already given in ReferenceValue.Sha256 type TDId struct { MrOwner [48]byte `json:"mrOwner" cbor:"0,keyasint"` MrOwnerConfig [48]byte `json:"mrOwnerConfig" cbor:"1,keyasint"` MrConfigId [48]byte `json:"mrConfigId" cbor:"2,keyasint"` - RtMr0 [48]byte `json:"rtMr0" cbor:"0,keyasint"` // updated by the TD virtual firmware/BIOS - RtMr1 [48]byte `json:"rtMr1" cbor:"0,keyasint"` // updated by the TD virtual firmware/BIOS - RtMr2 [48]byte `json:"rtMr2" cbor:"0,keyasint"` // runtime measurement - RtMr3 [48]byte `json:"rtMr3" cbor:"0,keyasint"` // runtime measurement + RtMr0 [48]byte `json:"rtMr0" cbor:"3,keyasint"` // updated by the TD virtual firmware/BIOS + RtMr1 [48]byte `json:"rtMr1" cbor:"4,keyasint"` // updated by the TD virtual firmware/BIOS + RtMr2 [48]byte `json:"rtMr2" cbor:"5,keyasint"` // runtime measurement + RtMr3 [48]byte `json:"rtMr3" cbor:"6,keyasint"` // runtime measurement } // ReferenceValue represents the attestation report -// element of types 'SNP Reference Value', 'TPM Reference Value' +// element of types 'SNP Reference Value', 'TPM Reference Value', 'SGX Reference Value', TDX Reference Value' // and 'SW Reference Value' type ReferenceValue struct { Type string `json:"type" cbor:"0,keyasint"` @@ -262,7 +246,7 @@ type ReferenceValue struct { Snp *SnpDetails `json:"snp,omitempty" cbor:"5,keyasint,omitempty"` Sgx *SGXDetails `json:"sgx,omitempty" cbor:"7,keyasint,omitempty"` Tdx *TDXDetails `json:"tdx,omitempty" cbor:"8,keyasint,omitempty"` - Description string `json:"description,omitempty" cbor:"6,keyasint,omitempty"` + Description string `json:"description,omitempty" cbor:"9,keyasint,omitempty"` } // AppDescription represents the attestation report @@ -770,6 +754,7 @@ func verifyAr(attestationReport []byte, result *VerificationResult, ar.TpmM = arPacked.TpmM ar.SnpM = arPacked.SnpM ar.SWM = arPacked.SWM + ar.TdxM = arPacked.TdxM ar.Nonce = arPacked.Nonce // Validate and unpack Rtm Manifest diff --git a/attestationreport/attestationreport_test.go b/attestationreport/attestationreport_test.go index f3e509d9..1b28a0b7 100644 --- a/attestationreport/attestationreport_test.go +++ b/attestationreport/attestationreport_test.go @@ -22,7 +22,6 @@ import ( "crypto/rand" "crypto/x509" "crypto/x509/pkix" - "encoding/json" "fmt" "math/big" "reflect" @@ -283,7 +282,7 @@ var ( QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, - CaFingerprint: aisecCertrootCAFingerprint, + CaFingerprint: tdxRootCAFingerprint, TdId: TDId{ MrOwner: mrOwner, MrOwnerConfig: mrOwnerConfig, @@ -300,33 +299,132 @@ var ( }, } - tdxRtmManifest = RtmManifest{ - ReferenceValues: validTdxReferenceValue, + validTdxRtmManifest = RtmManifest{ + MetaInfo: MetaInfo{ + Type: "RTM Manifest", + Name: "de.test.rtm", + Version: "2023-04-10T20:00:00Z", + }, + DevCommonName: "Test Developer", + Validity: Validity{ + NotBefore: "2023-04-10T20:00:00Z", + NotAfter: "2026-04-10T20:00:00Z", + }, + Description: "de.test.rtm", + CertificationLevel: 3, + ReferenceValues: validTdxReferenceValue, + } + + validTdxOsManifest = OsManifest{ + MetaInfo: MetaInfo{ + Type: "OS Manifest", + Name: "de.test.os", + Version: "2023-04-10T20:00:00Z", + }, + DevCommonName: "Test Developer", + Validity: Validity{ + NotBefore: "2023-04-10T20:00:00Z", + NotAfter: "2026-04-10T20:00:00Z", + }, + Description: "PoC Fraunhofer AISEC TDX Report", + CertificationLevel: 3, + Rtms: []string{ + "de.test.rtm", + }, + } + + validTdxDeviceDescription = DeviceDescription{ + MetaInfo: MetaInfo{ + Type: "Device Description", + Name: "test-device.test.de", + Version: "2023-04-10T20:00:00Z", + }, + Location: "Munich, Germany", + RtmManifest: "de.test.rtm", + OsManifest: "de.test.os", } ) func Test_verifyTdxReport(t *testing.T) { type args struct { - tdxMeas *TdxMeasurement + tdxMeas *TdxMeasurement + serializer Serializer + rtmManifest RtmManifest + osManifest OsManifest + deviceDescription DeviceDescription + nonce []byte } tests := []struct { name string args args want bool }{ - // TODO: implement tests { - name: "Valid TDX Report", + name: "Valid TDX Report JSON", + args: args{ + tdxMeas: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: validTDXCertChain, + }, + serializer: JsonSerializer{}, + rtmManifest: validTdxRtmManifest, + osManifest: validTdxOsManifest, + deviceDescription: validTdxDeviceDescription, + nonce: validTDXNonce, + }, + want: true, + }, + { + name: "Valid TDX Report CBOR", args: args{ tdxMeas: &TdxMeasurement{ Type: "TDX Measurement", Report: aisecTDXQuote, Certs: validTDXCertChain, }, + serializer: CborSerializer{}, + rtmManifest: validTdxRtmManifest, + osManifest: validTdxOsManifest, + deviceDescription: validTdxDeviceDescription, + nonce: validTDXNonce, }, want: true, }, + { + name: "Invalid TDX Certificate Chain", + args: args{ + tdxMeas: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: [][]byte{root_ca_cert_tdx.Raw}, + }, + serializer: JsonSerializer{}, + rtmManifest: validTdxRtmManifest, + osManifest: validTdxOsManifest, + deviceDescription: validTdxDeviceDescription, + nonce: validTDXNonce, + }, + want: false, + }, + { + name: "Invalid Nonce", + args: args{ + tdxMeas: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: validTDXCertChain, + }, + serializer: JsonSerializer{}, + rtmManifest: validTdxRtmManifest, + osManifest: validTdxOsManifest, + deviceDescription: validTdxDeviceDescription, + nonce: []byte{}, + }, + want: false, + }, } + logrus.SetLevel(logrus.TraceLevel) // Setup Test Keys and Certificates @@ -342,29 +440,49 @@ func Test_verifyTdxReport(t *testing.T) { certChain: certchain, } - s := JsonSerializer{} - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + s := tt.args.serializer // Generating a TDX Report + log.Trace("Generating a TDX Report") + ar := ArPacked{ - Type: "Attestation Report", + Type: "Attestation Report", + Nonce: tt.args.nonce, + TdxM: tt.args.tdxMeas, } - ar.Nonce = validTDXNonce - ar.RtmManifest, err = json.Marshal(tdxRtmManifest) + // create signed manifests and deviceDescription + rtmManifest, err := s.Marshal(tt.args.rtmManifest) if err != nil { t.Errorf("failed to marshal the RtmManifest: %v", err) } - - ar.TdxM = tt.args.tdxMeas + osManifest, err := s.Marshal(tt.args.osManifest) + if err != nil { + t.Errorf("failed to marshal the OsManifest: %v", err) + } + deviceDescription, err := s.Marshal(tt.args.deviceDescription) + if err != nil { + t.Errorf("failed to marshal the DeviceDescription: %v", err) + } + ar.RtmManifest, err = Sign(rtmManifest, swSigner, s) + if err != nil { + t.Errorf("failed to sign the RtmManifest: %v", err) + } + ar.OsManifest, err = Sign(osManifest, swSigner, s) + if err != nil { + t.Errorf("failed to sign the OsManifest: %v", err) + } + ar.DeviceDescription, err = Sign(deviceDescription, swSigner, s) + if err != nil { + t.Errorf("failed to sign the DeviceDescription: %v", err) + } report, err := s.Marshal(ar) if err != nil { t.Errorf("failed to marshal the Attestation Report: %v", err) } - // end of Generate() // sign the report arSigned, err := Sign(report, swSigner, s) diff --git a/attestationreport/intel_helpers.go b/attestationreport/intel_helpers.go new file mode 100644 index 00000000..906a4513 --- /dev/null +++ b/attestationreport/intel_helpers.go @@ -0,0 +1,665 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attestationreport + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "crypto/x509" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "io" + "math/big" + "net/http" + "os" + "regexp" + "time" + + "github.com/Fraunhofer-AISEC/cmc/internal" +) + +const ( + SGX_QUOTE_TYPE uint32 = 0x0 + TDX_QUOTE_TYPE uint32 = 0x81 + TDX_ID = "TDX" + SGX_ID = "SGX" + ECDSA_P_256 = 2 + SGX_EXTENSION_INDEX = 5 + + QUOTE_HEADER_SIZE = 48 + SGX_QUOTE_BODY_SIZE = 384 + SGX_QUOTE_SIGNATURE_OFFSET = 436 + SGX_QUOTE_MIN_SIZE = 1020 // value from Intel SGX QVL + TDX_QUOTE_BODY_SIZE = 584 + TDX_QUOTE_SIGNATURE_OFFSET = 636 + + // Url params: ca = processor/platform, encoding = pem/der + PCS_PCK_CERT_CRL_URI = "https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=%v&encoding=der" + PCS_ROOT_CA_CRL_URI = "https://certificates.trustedservices.intel.com/IntelSGXRootCA.der" + CA_PLATFORM = "platform" + CA_PROCESSOR = "processor" + ROOT_CA_CRL_NAME = "RootCaCRL" + PCK_CERT_CRL_NAME = "PCKCertCRL" +) + +// Verifies the quote signature +// Params: QuoteType = 0x00 (SGX) or 0x81 (TDX) +// TODO: Add support for different QE Cert Data Types +func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, + quoteSignatureSize uint32, quoteSignatureType int, certs SgxCertificates, + fingerprint string, quoteType uint32) (SignatureResult, bool) { + result := SignatureResult{} + var digest [32]byte + var ak_pub [64]byte // attestation public key (generated by the QE) + var reportData [64]byte + var hash_ref [32]byte + var err error + + // check signature type and size + switch quoteType { + case SGX_QUOTE_TYPE: + if uint32(len(reportRaw)-SGX_QUOTE_SIGNATURE_OFFSET) != quoteSignatureSize { + msg := fmt.Sprintf("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", + quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) + result.SignCheck.setFalse(&msg) + return result, false + } + case TDX_QUOTE_TYPE: + if uint32(len(reportRaw)-TDX_QUOTE_SIGNATURE_OFFSET) != quoteSignatureSize { + msg := fmt.Sprintf("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", + quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) + result.SignCheck.setFalse(&msg) + return result, false + } + default: + msg := fmt.Sprintf("Quote Type not supported %v", quoteType) + result.SignCheck.setFalse(&msg) + return result, false + } + + // for now only ECDSA_P_256 support + if quoteSignatureType != ECDSA_P_256 { + msg := fmt.Sprintf("Signature Algorithm %v not supported", quoteSignatureType) + result.SignCheck.setFalse(&msg) + return result, false + } + + // Step 1: Verify ISV Enclave Report Signature + r := new(big.Int) + s := new(big.Int) + + switch quoteSig := quoteSignature.(type) { + case ECDSA256QuoteSignatureDataStructure: // for SGX + r.SetBytes(quoteSig.ISVEnclaveReportSignature[:32]) + s.SetBytes(quoteSig.ISVEnclaveReportSignature[32:]) + digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+SGX_QUOTE_BODY_SIZE]) + ak_pub = quoteSig.ECDSAAttestationKey + + case ECDSA256QuoteSignatureDataStructureV4: // for TDX + r.SetBytes(quoteSig.QuoteSignature[:32]) + s.SetBytes(quoteSig.QuoteSignature[32:]) + digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+TDX_QUOTE_BODY_SIZE]) + ak_pub = quoteSig.ECDSAAttestationKey + } + + if len(ak_pub) == 0 { + msg := "Failed to extract ECDSA public key from certificate" + result.SignCheck.setFalse(&msg) + return result, false + } + + //get x and y from public key + pubX := new(big.Int) + pubX.SetBytes(ak_pub[:32]) + pubY := new(big.Int) + pubY.SetBytes(ak_pub[32:]) + + ecdsa_ak_pub := &ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: pubX, + Y: pubY, + } + + // Verify ECDSA Signature represented by r and s + ok := ecdsa.Verify(ecdsa_ak_pub, digest[:], r, s) + if !ok { + msg := "Failed to verify ISV Enclave report signature" + result.SignCheck.setFalse(&msg) + return result, false + } + log.Trace("Successfully verified ISV Enclave report signature") + result.SignCheck.Success = true + + // Step 2: Verify QE Report Signature (Get QE Report from QE Report Signature Data) + switch quoteSig := quoteSignature.(type) { + case ECDSA256QuoteSignatureDataStructure: // for SGX + digest = sha256.Sum256(reportRaw[SGX_QUOTE_SIGNATURE_OFFSET+128 : SGX_QUOTE_SIGNATURE_OFFSET+128+SGX_QUOTE_BODY_SIZE]) + r.SetBytes(quoteSig.QEReportSignature[:32]) + s.SetBytes(quoteSig.QEReportSignature[32:]) + reportData = quoteSig.QEReport.ReportData + hash_ref = sha256.Sum256(append(ak_pub[:], quoteSig.QEAuthData...)) + case ECDSA256QuoteSignatureDataStructureV4: // for TDX + digest = sha256.Sum256(reportRaw[TDX_QUOTE_SIGNATURE_OFFSET+128+6 : TDX_QUOTE_SIGNATURE_OFFSET+128+6+SGX_QUOTE_BODY_SIZE]) + r.SetBytes(quoteSig.QECertData.QEReportSignature[:32]) + s.SetBytes(quoteSig.QECertData.QEReportSignature[32:]) + reportData = quoteSig.QECertData.QEReport.ReportData + hash_ref = sha256.Sum256(append(ak_pub[:], quoteSig.QECertData.QEAuthData...)) + } + + // Extract the PCK public key from the PCK certificate + pck_pub, ok := certs.PCKCert.PublicKey.(*ecdsa.PublicKey) + if pck_pub == nil || !ok { + msg := "Failed to extract PCK public key from certificate" + result.SignCheck.setFalse(&msg) + return result, false + } + + // Verify the ECDSA QEReportSignature + ok = ecdsa.Verify(pck_pub, digest[:], r, s) + if !ok { + msg := "Failed to verify QE report signature" + result.SignCheck.setFalse(&msg) + return result, false + } + + log.Trace("Successfully verified QE report signature") + result.SignCheck.Success = true + + // Step 3: Verify Report Data: SHA256(ECDSA Attestation Key || QE Authentication Data) || 32-0x00’s) + reportDataRef := append(hash_ref[:], make([]byte, 32)...) + + if !bytes.Equal(reportData[:], reportDataRef[:]) { + msg := fmt.Sprintf("invalid SHA256(ECDSA Attestation Key || QE Authentication Data) || 32*0x00) in QEReport.ReportData. expected: %v, got: %v\n", reportDataRef, reportData) + result.CertChainCheck.setFalse(&msg) + return result, false + } + + // Step 4: Parse and verify the entire PCK certificate chain + var x509Chains [][]*x509.Certificate + + switch quoteType { + case SGX_QUOTE_TYPE: + x509Chains, err = VerifyIntelCertChainFull(certs, CA_PROCESSOR) + case TDX_QUOTE_TYPE: + x509Chains, err = VerifyIntelCertChainFull(certs, CA_PLATFORM) + } + if err != nil { + msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) + result.CertChainCheck.setFalse(&msg) + return result, false + } + + // Step 5: Verify that the reference value fingerprint matches the certificate fingerprint + refFingerprint, err := hex.DecodeString(fingerprint) + if err != nil { + msg := fmt.Sprintf("Failed to decode CA fingerprint %v: %v", fingerprint, err) + result.CertChainCheck.setFalse(&msg) + return result, false + } + caFingerprint := sha256.Sum256(certs.RootCACert.Raw) + if !bytes.Equal(refFingerprint, caFingerprint[:]) { + msg := fmt.Sprintf("CA fingerprint %v does not match measurement CA fingerprint %v", + fingerprint, hex.EncodeToString(caFingerprint[:])) + result.CertChainCheck.setFalse(&msg) + return result, false + } + result.CertChainCheck.Success = true + + // Step 6: Store details from (all) validated certificate chain(s) in the report + for _, chain := range x509Chains { + chainExtracted := []X509CertExtracted{} + for _, cert := range chain { + chainExtracted = append(chainExtracted, ExtractX509Infos(cert)) + } + result.ValidatedCerts = append(result.ValidatedCerts, chainExtracted) + } + + return result, true +} + +// teeTcbSvn is only required for TDX (from TdxReportBody) +func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate, + sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) (TcbLevelResult, error) { + var result TcbLevelResult + + if tcbInfo == nil || tcbKeyCert == nil { + return result, fmt.Errorf("invalid function parameter (null pointer exception)") + } + + regex := regexp.MustCompile(`\s+`) + regex.ReplaceAllString(tcbInfoBodyRaw, "") // remove whitespace + tcbInfoBodyRaw = tcbInfoBodyRaw[len(`{"tcbInfo":`) : len(tcbInfoBodyRaw)-128-16] // remove "{"tcbInfo":" from beginning and signature + rest from the end + + // get checksum of tcb info body + digest := sha256.Sum256([]byte(tcbInfoBodyRaw)) + + // get signature + r := new(big.Int) + s := new(big.Int) + r.SetBytes(tcbInfo.Signature[:32]) + s.SetBytes(tcbInfo.Signature[32:]) + + pub_key := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) + + // verify signature + ok := ecdsa.Verify(pub_key, digest[:], r, s) + if !ok { + result.Summary.Success = false + result.Summary.Details = "failed to verify tcbInfo signature" + return result, fmt.Errorf("failed to verify tcbInfo signature") + } + + now := time.Now() + + if now.After(tcbInfo.TcbInfo.NextUpdate) { + result.Summary.Success = false + result.Summary.Details = fmt.Sprintf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) + return result, fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) + } + + if !bytes.Equal([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { + result.Summary.Success = false + result.Summary.Details = fmt.Sprintf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) + return result, fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) + } + + if !bytes.Equal([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { + result.Summary.Success = false + result.Summary.Details = fmt.Sprintf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) + return result, fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", + tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) + } + + // Checking tcb level + for _, tcbLevel := range tcbInfo.TcbInfo.TcbLevels { + // Compare SGX TCB Comp SVNs from PCK Certificate with TCB Level + if !compareSgxTcbCompSvns(sgxExtensions, tcbLevel) { + continue + } + + // Compare PCESVN value + if sgxExtensions.Tcb.Value.PceSvn.Value < int(tcbLevel.Tcb.PceSvn) { + continue + } + + // Stop here for SGX + if quoteType == SGX_QUOTE_TYPE { + result.Summary.Success = true + result.TcbLevelDate = tcbLevel.TcbDate + result.TcbLevelStatus = tcbLevel.TcbStatus + return result, nil + } + + // Only TDX: Compare TEE TCB SVNs from TDX Report with TCB Level + if !compareTeeTcbSvns(teeTcbSvn, tcbLevel) || tcbLevel.Tcb.TdxTcbComponents[1].Svn != teeTcbSvn[1] { + result.Summary.Success = false + result.Summary.Details = "TCB Level rejected: unsupported" + return result, fmt.Errorf("TCB Level rejected: unsupported") + } + + // Only TDX: fail if Status == REVOKED + if tcbLevel.TcbStatus == string(Revoked) { + result.Summary.Success = false + result.TcbLevelStatus = string(Revoked) + result.TcbLevelDate = tcbLevel.TcbDate + return result, fmt.Errorf("TCB Level status: REVOKED") + } + + result.Summary.Success = true + result.TcbLevelDate = tcbLevel.TcbDate + result.TcbLevelStatus = tcbLevel.TcbStatus + return result, nil + } + + result.Summary.Success = false + result.Summary.Details = "TCB Level not supported" + return result, fmt.Errorf("TCB Level not supported") +} + +// helper function for verifyTcbInfo +func compareSgxTcbCompSvns(sgxExtensions SGXExtensionsValue, tcbLevel TcbLevel) bool { + // Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to 16) + // with the corresponding values of SVNs in sgxtcbcomponents array of TCB Level. + for i := 0; i < 16; i++ { + tcbFromCert := GetTCBCompByIndex(sgxExtensions.Tcb, i+1) + if byte(tcbFromCert.Value) < tcbLevel.Tcb.SgxTcbComponents[i].Svn { + return false + } + } + return true +} + +// helper function for verifyTcbInfo +func compareTeeTcbSvns(teeTcbSvn [16]byte, tcbLevel TcbLevel) bool { + // Compare all of the SVNs in TEE TCB SVN array retrieved from TD Report in Quote (from index 0 to 15) + // with the corresponding values of SVNs in tdxtcbcomponents array of TCB Level + for i := 0; i < 16; i++ { + if teeTcbSvn[i] < tcbLevel.Tcb.TdxTcbComponents[i].Svn { + return false + } + } + return true +} + +// verify QE Identity and compare the values to the QE (SGX/TDX) +func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, qeIdentityBodyRaw string, tcbKeyCert *x509.Certificate, teeType uint32) (TcbLevelResult, error) { + result := TcbLevelResult{} + if qeReportBody == nil || qeIdentity == nil || tcbKeyCert == nil { + return result, fmt.Errorf("invalid function parameter (null pointer exception)") + } + + regex := regexp.MustCompile(`\s+`) + regex.ReplaceAllString(qeIdentityBodyRaw, "") // remove whitespace + qeIdentityBodyRaw = qeIdentityBodyRaw[len(`{"enclaveIdentity":`) : len(qeIdentityBodyRaw)-128-16] // remove "{"enclaveIdentity":" from beginning and signature + rest from the end + + // get checksum of qe identity body + digest := sha256.Sum256([]byte(qeIdentityBodyRaw)) + + // get signature + r := new(big.Int) + s := new(big.Int) + r.SetBytes(qeIdentity.Signature[:32]) + s.SetBytes(qeIdentity.Signature[32:]) + + pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) + if !ok { + return result, fmt.Errorf("failed to extract public key from certificate") + } + + // verify signature + ok = ecdsa.Verify(pub_key, digest[:], r, s) + if !ok { + return result, fmt.Errorf("failed to verify QE Identity signature") + } + + now := time.Now() + if now.After(qeIdentity.EnclaveIdentity.NextUpdate) { + return result, fmt.Errorf("qeIdentity has expired since: %v", qeIdentity.EnclaveIdentity.NextUpdate) + } + + // from Intel SGX DCAP Library + if teeType == TDX_QUOTE_TYPE { + if qeIdentity.EnclaveIdentity.Version == 1 { + return result, fmt.Errorf("enclave Identity version 1 is invalid for TDX TEE") + } else if qeIdentity.EnclaveIdentity.Version == 2 { + if qeIdentity.EnclaveIdentity.Id != TD_QE { + return result, fmt.Errorf("enclave Identity is not generated for TDX and does not match Quote's TEE") + } + } + } else if teeType == SGX_QUOTE_TYPE { + if qeIdentity.EnclaveIdentity.Id != QE { + return result, fmt.Errorf("enclave Identity is not generated for SGX and does not match Quote's TEE") + } + } else { + return result, fmt.Errorf("unknown Quote's TEE. Enclave Identity cannot be valid") + } + + // check mrsigner + if !bytes.Equal([]byte(qeIdentity.EnclaveIdentity.Mrsigner), qeReportBody.MRSIGNER[:]) { + msg := fmt.Sprintf("MRSIGNER mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Mrsigner, qeReportBody.MRSIGNER) + result.Summary.Details = msg + result.Summary.Success = false + return result, nil + } + + // check isvProdId + if qeReportBody.ISVProdID != uint16(qeIdentity.EnclaveIdentity.IsvProdId) { + msg := fmt.Sprintf("IsvProdId mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.IsvProdId, qeReportBody.ISVProdID) + result.Summary.Details = msg + result.Summary.Success = false + return result, nil + } + + // check miscselect + miscselectMask := binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.MiscselectMask) + if binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.Miscselect) != (qeReportBody.MISCSELECT & miscselectMask) { + msg := fmt.Sprintf("miscSelect value from QEIdentity: %v does not match miscSelect value from QE Report: %v", + qeIdentity.EnclaveIdentity.Miscselect, (qeReportBody.MISCSELECT & miscselectMask)) + result.Summary.Details = msg + result.Summary.Success = false + return result, nil + } + + // check attributes + attributes_quote := qeReportBody.Attributes + attributes_mask := qeIdentity.EnclaveIdentity.AttributesMask + if len(attributes_mask) == len(attributes_quote) { + for i := range attributes_quote { + attributes_quote[i] &= attributes_mask[i] + } + } + if !bytes.Equal([]byte(qeIdentity.EnclaveIdentity.Attributes), attributes_quote[:]) { + msg := fmt.Sprintf("attributes mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Attributes, attributes_quote) + result.Summary.Details = msg + result.Summary.Success = false + return result, nil + } + + tcbStatus, tcbDate := getTcbStatusAndDateQE(qeIdentity, qeReportBody) + log.Tracef("TcbStatus for Enclave's Identity tcbLevel (isvSvn: %v): '%v'", qeReportBody.ISVSVN, tcbStatus) + + switch tcbStatus { + case Revoked: + fallthrough + case NotSupported: + result.Summary.Details = "invalid tcbStatus" + result.TcbLevelStatus = string(tcbStatus) + result.TcbLevelDate = tcbDate + result.Summary.Success = false + return result, fmt.Errorf("tcbStatus: %v", tcbStatus) + default: + result.TcbLevelStatus = string(tcbStatus) + result.TcbLevelDate = tcbDate + result.Summary.Success = true + return result, nil + } +} + +func getTcbStatusAndDateQE(qeIdentity *QEIdentity, body *EnclaveReportBody) (TcbStatus, time.Time) { + for i := len(qeIdentity.EnclaveIdentity.TcbLevels) - 1; i >= 0; i-- { + tcbLevel := qeIdentity.EnclaveIdentity.TcbLevels[i] + if uint16(tcbLevel.Tcb.Isvsvn) <= body.ISVSVN { + return tcbLevel.TcbStatus, tcbLevel.TcbDate + } + } + return NotSupported, time.Now() +} + +// Check if CRL parameters are valid and check if the certificate has been revoked +func CrlCheck(crl *x509.RevocationList, cert *x509.Certificate, parentCert *x509.Certificate) (bool, error) { + + if cert == nil || crl == nil { + return false, fmt.Errorf("certificate or revocation null pointer exception") + } + + // Check CRL signature + err := crl.CheckSignatureFrom(parentCert) + if err != nil { + return false, fmt.Errorf("CRL signature is invalid: %v", err) + } + + // Check if CRL is up to date + now := time.Now() + if now.After(crl.NextUpdate) { + return false, fmt.Errorf("CRL has expired since: %v", crl.NextUpdate) + } + + if !bytes.Equal(crl.RawIssuer, cert.RawIssuer) { + return false, fmt.Errorf("CRL RawIssuer is invalid. got: %v, expected: %v ", crl.RawIssuer, cert.RawIssuer) + } + + // Check if certificate has been revoked + for _, revokedCert := range crl.RevokedCertificates { + if cert.SerialNumber == revokedCert.SerialNumber { + return false, fmt.Errorf("certificate has been revoked since: %v", revokedCert.RevocationTime) + } + } + + return true, nil +} + +// fetch the CRL either from cache (filesystem) or download it from PCS +// TODO: implement a better caching mechanism +func fetchCRL(uri string, name string) (*x509.RevocationList, error) { + fileInfo, err := os.Stat(name) + if err == nil { + log.Tracef("File %s exists.\n", name) + + lastModifiedTime := fileInfo.ModTime() + currentTime := time.Now() + timeSinceLastModification := currentTime.Sub(lastModifiedTime) + + // Update the CRL if it is older than a day + if timeSinceLastModification > 24*time.Hour { + return downloadCRL(uri, name) + } + + // Read CRL + crl_raw, err := os.ReadFile(name) + if err != nil { + return nil, fmt.Errorf("failed to read quote.dat: %w", err) + } + + // Parse CRL + crl, err := x509.ParseRevocationList(crl_raw) + if err != nil { + return nil, err + } + return crl, nil + } else if os.IsNotExist(err) { + return downloadCRL(uri, name) + } else { + return nil, err + } +} + +// Download CRL from the Intel PCS +func downloadCRL(uri string, name string) (*x509.RevocationList, error) { + resp, err := http.Get(uri) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("request failed with status code: %d", resp.StatusCode) + } + + crlData, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // Store CRL in file + err = os.WriteFile(name, crlData, 0644) + if err != nil { + return nil, err + } + + // Parse CRL + crl, err := x509.ParseRevocationList(crlData) + if err != nil { + return nil, err + } + + return crl, nil +} + +// Verifies a given SGX certificate chain, fetches CRLs and checks if the certs are outdated +func VerifyIntelCertChainFull(quoteCerts SgxCertificates, ca string) ([][]*x509.Certificate, error) { + // verify PCK certificate chain + x509CertChains, err := internal.VerifyCertChain( + []*x509.Certificate{quoteCerts.PCKCert, quoteCerts.IntermediateCert}, + []*x509.Certificate{quoteCerts.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify pck certificate chain: %v", err) + return nil, errors.New(msg) + } + + // download CRLs from PCS + root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) + if err != nil { + msg := fmt.Sprintf("downloading ROOT CA CRL from PCS failed: %v", err) + return nil, errors.New(msg) + } + + pck_crl_uri := fmt.Sprintf(PCS_PCK_CERT_CRL_URI, ca) + pck_crl, err := fetchCRL(pck_crl_uri, PCK_CERT_CRL_NAME) + if err != nil { + msg := fmt.Sprintf("downloading PCK Cert CRL from PCS failed: %v", err) + return nil, errors.New(msg) + } + + // perform CRL checks (signature + values) + res, err := CrlCheck(root_ca_crl, quoteCerts.RootCACert, quoteCerts.RootCACert) + if !res || err != nil { + msg := fmt.Sprintf("CRL check on rootCert failed: %v", err) + return nil, errors.New(msg) + } + + res, err = CrlCheck(pck_crl, quoteCerts.PCKCert, quoteCerts.IntermediateCert) + if !res || err != nil { + msg := fmt.Sprintf("CRL check on pckCert failed: %v", err) + return nil, errors.New(msg) + } + + return x509CertChains, nil +} + +// Verifies the TCB signing cert chain +func VerifyTCBSigningCertChain(quoteCerts SgxCertificates) ([][]*x509.Certificate, error) { + tcbSigningCertChain, err := internal.VerifyCertChain( + []*x509.Certificate{quoteCerts.TCBSigningCert}, + []*x509.Certificate{quoteCerts.RootCACert}) + if err != nil { + msg := fmt.Sprintf("Failed to verify TCB Signing certificate chain: %v", err) + return nil, errors.New(msg) + } + + root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) + if err != nil { + msg := fmt.Sprintf("downloading Root CA CRL from PCS failed: %v", err) + return nil, errors.New(msg) + } + + // perform CRL checks (signature + values) + res, err := CrlCheck(root_ca_crl, quoteCerts.TCBSigningCert, quoteCerts.RootCACert) + if !res || err != nil { + msg := fmt.Sprintf("CRL check on TcbSigningCert failed: %v", err) + return nil, errors.New(msg) + } + + return tcbSigningCertChain, nil +} + +func verifyQuoteVersion(quote QuoteHeader, version uint16) (Result, bool) { + r := Result{} + ok := quote.Version == version + if !ok { + msg := fmt.Sprintf("Quote version mismatch: Report = %v, supplied = %v", quote.Version, version) + r.setFalse(&msg) + } else { + r.Success = true + } + return r, ok +} diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index 5a550795..715be9bc 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -17,51 +17,13 @@ package attestationreport import ( "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/sha256" "crypto/x509" - "encoding/binary" "encoding/hex" - "errors" "fmt" - "io" - "math/big" - "net/http" - "os" - "reflect" - "regexp" - "time" - - "github.com/Fraunhofer-AISEC/cmc/internal" + "strconv" + "strings" ) -const ( - SGX_QUOTE_TYPE uint32 = 0x0 - TDX_QUOTE_TYPE uint32 = 0x81 - - ecdsa_p_256 = 2 - QUOTE_MIN_SIZE = 1020 // value from Intel SGX QVL - - QUOTE_HEADER_SIZE = 48 - SGX_QUOTE_BODY_SIZE = 384 - SGX_QUOTE_SIGNATURE_OFFSET = 436 - TDX_QUOTE_BODY_SIZE = 584 - TDX_QUOTE_SIGNATURE_OFFSET = 636 - - TDX_ID = "TDX" - SGX_ID = "SGX" - - // Params: ca = processor or platform, encoding = pem or der - PCS_PCK_CERT_CRL_URI = "https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=%v&encoding=der" - PCS_ROOT_CA_CRL_URI = "https://certificates.trustedservices.intel.com/IntelSGXRootCA.der" - ROOT_CA_CRL_NAME = "RootCaCRL" - PCK_CERT_CRL_NAME = "PCKCertCRL" - - SGX_EXTENSION_INDEX = 5 -) - -// main function to very a SGX Quote + Measurements func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*SgxMeasurementResult, bool) { var err error result := &SgxMeasurementResult{} @@ -101,7 +63,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ } // Validate Parameters: - if sgxM.Report == nil || len(sgxM.Report) < QUOTE_MIN_SIZE { + if sgxM.Report == nil || len(sgxM.Report) < SGX_QUOTE_MIN_SIZE { msg := "Invalid SGX Report." result.Summary.setFalse(&msg) return result, false @@ -133,7 +95,6 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ log.Tracef("Unknown quote type (tee_type: %X)\n", quoteType) return result, false } - fmt.Println("QuoteSignatureDataLen: ", sgxQuote.QuoteSignatureDataLen) // Compare Nonce for Freshness (called Report Data in the SNP Attestation Report Structure) // ReportData contains: nonce in ReportData field @@ -157,10 +118,6 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ result.Summary.setFalse(&msg) return result, false } - fmt.Println("certs:", referenceCerts) - - var current_time time.Time = time.Now() - log.Trace("current time: ", current_time) // (from DCAP Library): parse PCK Cert chain (from the quote) into CertificateChain object. return error in case of failure // TODO: handle other QECertDataTypes (for now: throw an error) @@ -178,15 +135,25 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - // (from DCAP Library): extract root CA from PCK cert chain in quote -> compare nullptr + // Extract root CA from PCK cert chain in quote -> compare nullptr if quoteCerts.RootCACert == nil { msg := "root cert is null" result.Summary.setFalse(&msg) return result, false } - // (from DCAP Library): check root public key - if !reflect.DeepEqual(quoteCerts.RootCACert.PublicKey, referenceCerts.RootCACert.PublicKey) { + // Check root public key + quotePublicKeyBytes, err := x509.MarshalPKIXPublicKey(quoteCerts.RootCACert.PublicKey) + if err != nil { + return result, false + } + + referencePublicKeyBytes, err := x509.MarshalPKIXPublicKey(referenceCerts.RootCACert.PublicKey) + if err != nil { + return result, false + } + + if !bytes.Equal(quotePublicKeyBytes, referencePublicKeyBytes) { msg := "root cert public key didn't match" result.Summary.setFalse(&msg) return result, false @@ -200,7 +167,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - // (from DCAP Library): parse and verify TcbInfo object + // Parse and verify TcbInfo object tcbInfo, err := ParseTcbInfo(sgxReferenceValue.Sgx.Collateral.TcbInfo) if err != nil { msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) @@ -208,8 +175,8 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ return result, false } - tcbInfoResult, err := verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, - [16]byte{}, SGX_QUOTE_TYPE) + tcbInfoResult, err := verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, + sgxExtensions, [16]byte{}, SGX_QUOTE_TYPE) if err != nil { msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) result.Summary.setFalse(&msg) @@ -217,7 +184,7 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ } result.TcbInfoCheck = tcbInfoResult - // (from DCAP Library): parse and verify QE Identity object + // Parse and verify QE Identity object qeIdentity, err := ParseQEIdentity(sgxReferenceValue.Sgx.Collateral.QeIdentity) if err != nil { msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) @@ -239,378 +206,51 @@ func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues [ sgxQuote.QuoteSignatureDataLen, int(sgxQuote.QuoteHeader.AttestationKeyType), referenceCerts, sgxReferenceValue.Sgx.CaFingerprint, quoteType) if !ret { - msg := fmt.Sprintf("Failed to verify Quote Signature: %v", sig) - result.Summary.setFalse(&msg) - return result, false + ok = false } - result.Signature = sig // Verify Quote Body values - err = VerifySgxQuoteBody(&sgxQuote.ISVEnclaveReport, &tcbInfo, "eCerts, &sgxReferenceValue, result) + err = VerifySgxQuoteBody(&sgxQuote.ISVEnclaveReport, &tcbInfo, &sgxExtensions, &sgxReferenceValue, result) if err != nil { - msg := fmt.Sprintf("Failed to verify Enclave Report Values: %v", err) + msg := fmt.Sprintf("Failed to verify SGX Report Body: %v", err) result.Summary.setFalse(&msg) result.Summary.Success = false return result, false - } else { - result.Artifacts = append(result.Artifacts, - DigestResult{ - Name: sgxReferenceValue.Name, - Digest: hex.EncodeToString(sgxQuote.ISVEnclaveReport.MRENCLAVE[:]), - Success: true, - }) - ok = true } - // Compare SGX Parameters + // Check version result.VersionMatch, ret = verifyQuoteVersion(sgxQuote.QuoteHeader, sgxReferenceValue.Sgx.Version) if !ret { return result, false } - result.PolicyCheck, ret = verifySgxPolicy(sgxQuote, sgxReferenceValue.Sgx.Policy) - if !ret { - ok = false - } - result.Summary.Success = ok return result, ok } -// TODO: implement this function -func verifySgxPolicy(s SgxReport, v SgxPolicy) (PolicyCheck, bool) { - r := PolicyCheck{} - - return r, true -} - -func verifyQuoteVersion(quote QuoteHeader, version uint16) (Result, bool) { - r := Result{} - ok := quote.Version == version - if !ok { - msg := fmt.Sprintf("Quote version mismatch: Report = %v, supplied = %v", quote.Version, version) - r.setFalse(&msg) - } else { - r.Success = true - } - return r, ok -} - -// verifies the quote signature -// Can be used by SGX/TDX: QuoteType = 0x00 (sgx) or 0x81 (TDX) -// TODO: Handle different QE Cert Data Types in here -func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, - quoteSignatureSize uint32, quoteSignatureType int, certs SgxCertificates, - fingerprint string, quoteType uint32) (SignatureResult, bool) { - result := SignatureResult{} - var digest [32]byte - var ak_pub [64]byte // attestation public key (generated by the QE) - var reportData [64]byte - var hash_ref [32]byte - - // check signature type and size - if quoteType == SGX_QUOTE_TYPE { - if uint32(len(reportRaw)-SGX_QUOTE_SIGNATURE_OFFSET) != quoteSignatureSize { - msg := fmt.Sprintf("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", - quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) - result.SignCheck.setFalse(&msg) - return result, false - } - } else if quoteType == TDX_QUOTE_TYPE { - if uint32(len(reportRaw)-TDX_QUOTE_SIGNATURE_OFFSET) != quoteSignatureSize { - msg := fmt.Sprintf("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", - quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) - result.SignCheck.setFalse(&msg) - return result, false - } - } else { - msg := fmt.Sprintf("Quote Type not supported %v", quoteType) - result.SignCheck.setFalse(&msg) - return result, false - } - - // for now: check attestation signature type = key type - if quoteSignatureType != ecdsa_p_256 { - msg := fmt.Sprintf("Signature Algorithm %v not supported", quoteSignatureType) - result.SignCheck.setFalse(&msg) - return result, false - } - - // Step 1: Verify ISV Enclave Report Signature - // Convert r, s to Big Int - r := new(big.Int) - s := new(big.Int) - - switch quoteSig := quoteSignature.(type) { - case ECDSA256QuoteSignatureDataStructure: // for SGX - r.SetBytes(quoteSig.ISVEnclaveReportSignature[:32]) - s.SetBytes(quoteSig.ISVEnclaveReportSignature[32:]) - digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+SGX_QUOTE_BODY_SIZE]) - ak_pub = quoteSig.ECDSAAttestationKey - - case ECDSA256QuoteSignatureDataStructureV4: // for TDX - r.SetBytes(quoteSig.QuoteSignature[:32]) - s.SetBytes(quoteSig.QuoteSignature[32:]) - digest = sha256.Sum256(reportRaw[:QUOTE_HEADER_SIZE+TDX_QUOTE_BODY_SIZE]) - ak_pub = quoteSig.ECDSAAttestationKey - } - - if len(ak_pub) == 0 { - msg := "Failed to extract ECDSA public key from certificate" - result.SignCheck.setFalse(&msg) - return result, false - } - - //get x and y from public key - pubX := new(big.Int) - pubX.SetBytes(ak_pub[:32]) - pubY := new(big.Int) - pubY.SetBytes(ak_pub[32:]) - - ecdsa_ak_pub := &ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: pubX, - Y: pubY, - } - - // Verify ECDSA Signature represented by r and s - ok := ecdsa.Verify(ecdsa_ak_pub, digest[:], r, s) - if !ok { - msg := "Failed to verify ISV Enclave report signature" - result.SignCheck.setFalse(&msg) - return result, false - } - log.Trace("Successfully verified ISV Enclave report signature") - result.SignCheck.Success = true - - // Step 2: Verify QE Report Signature - // get QE Report from QE Report Signature Data - switch quoteSig := quoteSignature.(type) { - case ECDSA256QuoteSignatureDataStructure: // for SGX - digest = sha256.Sum256(reportRaw[SGX_QUOTE_SIGNATURE_OFFSET+128 : SGX_QUOTE_SIGNATURE_OFFSET+128+SGX_QUOTE_BODY_SIZE]) - r.SetBytes(quoteSig.QEReportSignature[:32]) - s.SetBytes(quoteSig.QEReportSignature[32:]) - reportData = quoteSig.QEReport.ReportData - hash_ref = sha256.Sum256(append(ak_pub[:], quoteSig.QEAuthData...)) - case ECDSA256QuoteSignatureDataStructureV4: // for TDX - digest = sha256.Sum256(reportRaw[TDX_QUOTE_SIGNATURE_OFFSET+128+6 : TDX_QUOTE_SIGNATURE_OFFSET+128+6+SGX_QUOTE_BODY_SIZE]) - r.SetBytes(quoteSig.QECertData.QEReportSignature[:32]) - s.SetBytes(quoteSig.QECertData.QEReportSignature[32:]) - reportData = quoteSig.QECertData.QEReport.ReportData - hash_ref = sha256.Sum256(append(ak_pub[:], quoteSig.QECertData.QEAuthData...)) - } - - // Extract the PCK public key from the PCK certificate - pck_pub, ok := certs.PCKCert.PublicKey.(*ecdsa.PublicKey) - if pck_pub == nil || !ok { - msg := "Failed to extract PCK public key from certificate" - result.SignCheck.setFalse(&msg) - return result, false - } - - // Verify the ECDSA QEReportSignature - ok = ecdsa.Verify(pck_pub, digest[:], r, s) - if !ok { - msg := "Failed to verify QE report signature" - result.SignCheck.setFalse(&msg) - return result, false - } - - log.Trace("Successfully verified QE report signature") - result.SignCheck.Success = true - - // Step 3: Verify Report Data: SHA256(ECDSA Attestation Key || QE Authentication Data) || 32-0x00’s) - - reportDataRef := append(hash_ref[:], make([]byte, 32)...) - - if !bytes.Equal(reportData[:], reportDataRef[:]) { - msg := fmt.Sprintf("invalid SHA256(ECDSA Attestation Key || QE Authentication Data) || 32*0x00) in QEReport.ReportData. expected: %v, got: %v\n", reportDataRef, reportData) - result.CertChainCheck.setFalse(&msg) - return result, false - } - - // Step 4: Parse and verify the entire PCK certificate chain - x509Chains, err := VerifyIntelCertChainFull(certs) - if err != nil { - msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) - result.CertChainCheck.setFalse(&msg) - return result, false - } - - // Step 5: Verify that the reference value fingerprint matches the certificate fingerprint - refFingerprint, err := hex.DecodeString(fingerprint) - if err != nil { - msg := fmt.Sprintf("Failed to decode CA fingerprint %v: %v", fingerprint, err) - result.CertChainCheck.setFalse(&msg) - return result, false - } - caFingerprint := sha256.Sum256(certs.RootCACert.Raw) - if !bytes.Equal(refFingerprint, caFingerprint[:]) { - msg := fmt.Sprintf("CA fingerprint %v does not match measurement CA fingerprint %v", - fingerprint, hex.EncodeToString(caFingerprint[:])) - result.CertChainCheck.setFalse(&msg) - return result, false - } - result.CertChainCheck.Success = true - - // Step 6: Store details from (all) validated certificate chain(s) in the report - for _, chain := range x509Chains { - chainExtracted := []X509CertExtracted{} - for _, cert := range chain { - chainExtracted = append(chainExtracted, ExtractX509Infos(cert)) - } - result.ValidatedCerts = append(result.ValidatedCerts, chainExtracted) - } - - return result, true -} - -// teeTcbSvn is only required for TDX (from TdxReportBody) -func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Certificate, - sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) (TcbLevelCheck, error) { - var result TcbLevelCheck - - if tcbInfo == nil || tcbKeyCert == nil { - return result, fmt.Errorf("invalid function parameter (null pointer exception)") - } - - regex := regexp.MustCompile(`\s+`) - regex.ReplaceAllString(tcbInfoBodyRaw, "") // remove whitespace - tcbInfoBodyRaw = tcbInfoBodyRaw[11 : len(tcbInfoBodyRaw)-128-16] // remove "{"tcbInfo":" from beginning and signature + rest from the end - - // get checksum of tcb info body - digest := sha256.Sum256([]byte(tcbInfoBodyRaw)) - - // get signature - r := new(big.Int) - s := new(big.Int) - r.SetBytes(tcbInfo.Signature[:32]) - s.SetBytes(tcbInfo.Signature[32:]) - - pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) - if !ok { - result.Summary.Success = false - result.Summary.Details = "failed to extract public key from certificate" - return result, fmt.Errorf("failed to extract public key from certificate") - } - - // verify signature - ok = ecdsa.Verify(pub_key, digest[:], r, s) - if !ok { - result.Summary.Success = false - result.Summary.Details = "failed to verify tcbInfo signature" - return result, fmt.Errorf("failed to verify tcbInfo signature") - } - - now := time.Now() - - if now.After(tcbInfo.TcbInfo.NextUpdate) { - result.Summary.Success = false - result.Summary.Details = fmt.Sprintf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) - return result, fmt.Errorf("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) - } - - if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.Fmspc), sgxExtensions.Fmspc.Value) { - result.Summary.Success = false - result.Summary.Details = fmt.Sprintf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", - tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) - return result, fmt.Errorf("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", - tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) - } - - if !reflect.DeepEqual([]byte(tcbInfo.TcbInfo.PceId), sgxExtensions.PceId.Value) { - result.Summary.Success = false - result.Summary.Details = fmt.Sprintf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", - tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) - return result, fmt.Errorf("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", - tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) - } - - // checking tcb level - for _, tcbLevel := range tcbInfo.TcbInfo.TcbLevels { - // Compare SGX TCB Comp SVNs from PCK Certificate with TCB Level - if compareSgxTcbCompSvns(sgxExtensions, tcbLevel) { - // Compare PCESVN value - if sgxExtensions.Tcb.Value.PceSvn.Value >= int(tcbLevel.Tcb.PceSvn) { - if quoteType == SGX_QUOTE_TYPE { - result.Summary.Success = true - result.TcbLevelDate = tcbLevel.TcbDate - result.TcbLevelStatus = tcbLevel.TcbStatus - return result, nil - } else { - // Compare TEE TCB SVNs from TDX Report with TCB Level - if compareTeeTcbSvns(teeTcbSvn, tcbLevel) { - if tcbLevel.Tcb.TdxTcbComponents[1].Svn == teeTcbSvn[1] { - // fail if Status == REVOKED - if tcbLevel.TcbStatus == string(Revoked) { - result.Summary.Success = false - result.TcbLevelStatus = string(Revoked) - result.TcbLevelDate = tcbLevel.TcbDate - return result, fmt.Errorf("TCB Level status: REVOKED") - } - result.Summary.Success = true - result.TcbLevelDate = tcbLevel.TcbDate - result.TcbLevelStatus = tcbLevel.TcbStatus - return result, nil - } else { - result.Summary.Success = false - result.Summary.Details = "TCB Level rejected: unsupported" - return result, fmt.Errorf("TCB Level rejected: unsupported") - } - } - } - } - } - } - // - Check if TCB Level status is OutOfDate/REVOKED/ConfigurationNeeded/ConfigurationAndSWHardeningNeeded/UpToDate/SWHardeningNeeded/OutOfDateConfigurationNeeded/TCB Level error status is unrecognized - // - handle result of TCB Status based on policy - - result.Summary.Success = false - result.Summary.Details = "TCB Level not supported" - return result, fmt.Errorf("TCB Level not supported") -} - -// helper function for verifyTcbInfo -func compareSgxTcbCompSvns(sgxExtensions SGXExtensionsValue, tcbLevel TcbLevel) bool { - // Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to 16) - // with the corresponding values of SVNs in sgxtcbcomponents array of TCB Level. - for i := 0; i < 16; i++ { - tcbFromCert := GetTCBCompByIndex(sgxExtensions.Tcb, i+1) - if byte(tcbFromCert.Value) < tcbLevel.Tcb.SgxTcbComponents[i].Svn { - return false - } - } - return true -} - -// helper function for verifyTcbInfo -func compareTeeTcbSvns(teeTcbSvn [16]byte, tcbLevel TcbLevel) bool { - // Compare all of the SVNs in TEE TCB SVN array retrieved from TD Report in Quote (from index 0 to 15) - // with the corresponding values of SVNs in tdxtcbcomponents array of TCB Level - for i := 0; i < 16; i++ { - if teeTcbSvn[i] < tcbLevel.Tcb.TdxTcbComponents[i].Svn { - return false - } - } - return true -} - -// Only for SGX report body func VerifySgxQuoteBody(body *EnclaveReportBody, tcbInfo *TcbInfo, - certs *SgxCertificates, sgxReferenceValue *ReferenceValue, result *SgxMeasurementResult) error { - if body == nil || tcbInfo == nil || certs == nil || sgxReferenceValue == nil || result == nil { + sgxExtensions *SGXExtensionsValue, sgxReferenceValue *ReferenceValue, result *SgxMeasurementResult) error { + if body == nil || tcbInfo == nil || sgxExtensions == nil || sgxReferenceValue == nil || result == nil { return fmt.Errorf("invalid function parameter (null pointer exception)") } // check MRENCLAVE reference value - if !reflect.DeepEqual(body.MRENCLAVE[:], []byte(sgxReferenceValue.Sha256)) { + if !bytes.Equal(body.MRENCLAVE[:], []byte(sgxReferenceValue.Sha256)) { result.Artifacts = append(result.Artifacts, DigestResult{ Name: sgxReferenceValue.Name, Digest: hex.EncodeToString(sgxReferenceValue.Sha256[:]), Success: false, + Type: "Reference Value", + }) + result.Artifacts = append(result.Artifacts, + DigestResult{ + Name: sgxReferenceValue.Name, + Digest: hex.EncodeToString(body.MRENCLAVE[:]), + Success: false, + Type: "Measurement", }) return fmt.Errorf("MRENCLAVE mismatch. Expected: %v, Got. %v", sgxReferenceValue.Sha256, body.MRENCLAVE) } else { @@ -622,309 +262,54 @@ func VerifySgxQuoteBody(body *EnclaveReportBody, tcbInfo *TcbInfo, }) } - // check attributes - attributes_quote := body.Attributes - - if !reflect.DeepEqual(sgxReferenceValue.Sgx.Attributes[:], attributes_quote[:]) { - return fmt.Errorf("attributes mismatch. Expected: %v, Got: %v", sgxReferenceValue.Sgx.Attributes, attributes_quote) - } - - // check mrsigner value - refSigner, err := hex.DecodeString(sgxReferenceValue.Sgx.MRSIGNER) - if err != nil { - return fmt.Errorf("decoding MRSIGNER reference value failed: %v", err) - } - - if !reflect.DeepEqual(refSigner, body.MRSIGNER[:]) { - return fmt.Errorf("MRSIGNER mismatch. Expected: %v, Got: %v", []byte(sgxReferenceValue.Sgx.MRSIGNER), body.MRSIGNER) - } - - // check isvProdId value - if body.ISVProdID != sgxReferenceValue.Sgx.IsvProdId { - return fmt.Errorf("IsvProdId mismatch. Expected: %v, Got: %v", sgxReferenceValue.Sgx.IsvProdId, body.ISVProdID) - } - - // TODO: check CPUSVN from report body - - return nil -} - -// verify QE Identity and compare the values to the QE (SGX/TDX) -func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, qeIdentityBodyRaw string, tcbKeyCert *x509.Certificate, teeType uint32) (TcbLevelCheck, error) { - result := TcbLevelCheck{} - if qeReportBody == nil || qeIdentity == nil || tcbKeyCert == nil { - return result, fmt.Errorf("invalid function parameter (null pointer exception)") - } - - regex := regexp.MustCompile(`\s+`) - regex.ReplaceAllString(qeIdentityBodyRaw, "") // remove whitespace - qeIdentityBodyRaw = qeIdentityBodyRaw[19 : len(qeIdentityBodyRaw)-128-16] // remove "{"enclaveIdentity":" from beginning and signature + rest from the end - - // get checksum of qe identity body - digest := sha256.Sum256([]byte(qeIdentityBodyRaw)) - - // get signature - r := new(big.Int) - s := new(big.Int) - r.SetBytes(qeIdentity.Signature[:32]) - s.SetBytes(qeIdentity.Signature[32:]) - - pub_key, ok := tcbKeyCert.PublicKey.(*ecdsa.PublicKey) - if !ok { - return result, fmt.Errorf("failed to extract public key from certificate") - } - - // verify signature - ok = ecdsa.Verify(pub_key, digest[:], r, s) - if !ok { - return result, fmt.Errorf("failed to verify QE Identity signature") - } - - now := time.Now() - if now.After(qeIdentity.EnclaveIdentity.NextUpdate) { - return result, fmt.Errorf("qeIdentity has expired since: %v", qeIdentity.EnclaveIdentity.NextUpdate) - } - - // from Intel SGX DCAP Library - if teeType == TDX_QUOTE_TYPE { - if qeIdentity.EnclaveIdentity.Version == 1 { - return result, fmt.Errorf("enclave Identity version 1 is invalid for TDX TEE") - } else if qeIdentity.EnclaveIdentity.Version == 2 { - if qeIdentity.EnclaveIdentity.Id != TD_QE { - return result, fmt.Errorf("enclave Identity is not generated for TDX and does not match Quote's TEE") - } - } - } else if teeType == SGX_QUOTE_TYPE { - if qeIdentity.EnclaveIdentity.Id != QE { - return result, fmt.Errorf("enclave Identity is not generated for SGX and does not match Quote's TEE") - } - } else { - return result, fmt.Errorf("unknown Quote's TEE. Enclave Identity cannot be valid") - } - - // check mrsigner - if !bytes.Equal([]byte(qeIdentity.EnclaveIdentity.Mrsigner), qeReportBody.MRSIGNER[:]) { - msg := fmt.Sprintf("MRSIGNER mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Mrsigner, qeReportBody.MRSIGNER) - result.Summary.Details = msg - result.Summary.Success = false - return result, nil - } - - // check isvProdId - if qeReportBody.ISVProdID != uint16(qeIdentity.EnclaveIdentity.IsvProdId) { - msg := fmt.Sprintf("IsvProdId mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.IsvProdId, qeReportBody.ISVProdID) - result.Summary.Details = msg - result.Summary.Success = false - return result, nil - } - - // check miscselect - miscselectMask := binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.MiscselectMask) - if binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.Miscselect) != (qeReportBody.MISCSELECT & miscselectMask) { - msg := fmt.Sprintf("miscSelect value from QEIdentity: %v does not match miscSelect value from QE Report: %v", - qeIdentity.EnclaveIdentity.Miscselect, (qeReportBody.MISCSELECT & miscselectMask)) - result.Summary.Details = msg - result.Summary.Success = false - return result, nil - } - - // check attributes - attributes_quote := qeReportBody.Attributes - attributes_mask := qeIdentity.EnclaveIdentity.AttributesMask - if len(attributes_mask) == len(attributes_quote) { - for i := range attributes_quote { - attributes_quote[i] &= attributes_mask[i] - } - } - if !reflect.DeepEqual([]byte(qeIdentity.EnclaveIdentity.Attributes), attributes_quote[:]) { - msg := fmt.Sprintf("attributes mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Attributes, attributes_quote) - result.Summary.Details = msg - result.Summary.Success = false - return result, nil - } - - tcbStatus, tcbDate := getTcbStatusAndDateQE(qeIdentity, qeReportBody) - log.Tracef("TcbStatus for Enclave's Identity tcbLevel (isvSvn: %v): '%v'", qeReportBody.ISVSVN, tcbStatus) - - switch tcbStatus { - case Revoked: - fallthrough - case NotSupported: - result.Summary.Details = "invalid tcbStatus" - result.TcbLevelStatus = string(tcbStatus) - result.TcbLevelDate = tcbDate - result.Summary.Success = false - return result, fmt.Errorf("tcbStatus: %v", tcbStatus) - default: - result.TcbLevelStatus = string(tcbStatus) - result.TcbLevelDate = tcbDate - result.Summary.Success = true - return result, nil - } -} - -func getTcbStatusAndDateQE(qeIdentity *QEIdentity, body *EnclaveReportBody) (TcbStatus, time.Time) { - for i := len(qeIdentity.EnclaveIdentity.TcbLevels) - 1; i >= 0; i-- { - tcbLevel := qeIdentity.EnclaveIdentity.TcbLevels[i] - if uint16(tcbLevel.Tcb.Isvsvn) <= body.ISVSVN { - return tcbLevel.TcbStatus, tcbLevel.TcbDate - } - } - return NotSupported, time.Now() -} - -// Check if CRL parameters are valid and check if the certificate has been revoked -func CrlCheck(crl *x509.RevocationList, cert *x509.Certificate, parentCert *x509.Certificate) (bool, error) { - - if cert == nil || crl == nil { - return false, fmt.Errorf("certificate or revocation null pointer exception") - } - - // Check CRL signature - err := crl.CheckSignatureFrom(parentCert) - if err != nil { - return false, fmt.Errorf("CRL signature is invalid: %v", err) - } - - // Check if CRL is up to date - now := time.Now() - if now.After(crl.NextUpdate) { - return false, fmt.Errorf("CRL has expired since: %v", crl.NextUpdate) - } - - if !reflect.DeepEqual(crl.RawIssuer, cert.RawIssuer) { - return false, fmt.Errorf("CRL RawIssuer is invalid. got: %v, expected: %v ", crl.RawIssuer, cert.RawIssuer) - } - - // Check if certificate has been revoked - for _, revokedCert := range crl.RevokedCertificates { - if cert.SerialNumber == revokedCert.SerialNumber { - return false, fmt.Errorf("certificate has been revoked since: %v", revokedCert.RevocationTime) + result.ExtendedQuoteCheck.TeeIdentity = append(result.ExtendedQuoteCheck.TeeIdentity, + ComparisonResult{ + Name: "MrSigner", + Success: strings.EqualFold(sgxReferenceValue.Sgx.MrSigner, hex.EncodeToString(body.MRSIGNER[:])), + Claimed: sgxReferenceValue.Sgx.MrSigner, + Measured: hex.EncodeToString(body.MRSIGNER[:]), + }, + // TODO: check how to compare CPUSVN value + ComparisonResult{ + Name: "CpuSvn", + Success: bytes.Equal(sgxExtensions.Tcb.Value.CpuSvn.Value, body.CPUSVN[:]), + Claimed: hex.EncodeToString(sgxExtensions.Tcb.Value.CpuSvn.Value), + Measured: hex.EncodeToString(body.CPUSVN[:]), + }, + ComparisonResult{ + Name: "IsvProdId", + Success: sgxReferenceValue.Sgx.IsvProdId == body.ISVProdID, + Claimed: strconv.Itoa(int(sgxReferenceValue.Sgx.IsvProdId)), + Measured: strconv.Itoa(int(body.ISVProdID)), + }, + ComparisonResult{ + Name: "IsvSvn", + Success: sgxReferenceValue.Sgx.IsvSvn == body.ISVSVN, + Claimed: strconv.Itoa(int(sgxReferenceValue.Sgx.IsvSvn)), + Measured: strconv.Itoa(int(body.ISVSVN)), + }, + ) + + result.ExtendedQuoteCheck.TeeAttributes = append(result.ExtendedQuoteCheck.TeeAttributes, + AttributesCheck{ + Name: "Attributes", + Success: bytes.Equal(sgxReferenceValue.Sgx.Attributes[:], body.Attributes[:]), + Claimed: sgxReferenceValue.Sgx.Attributes[:], + Measured: body.Attributes[:], + }, + ) + + for _, v := range result.ExtendedQuoteCheck.TeeIdentity { + if !v.Success { + return fmt.Errorf("TDX Quote Body Verification failed. %v: (Expected: %v, Got: %v)", v.Name, v.Claimed, v.Measured) } } - return true, nil -} - -// fetch the CRL either from cache or download it from PCS -// TODO (important): implement dedicated update mechanism (currently only retrieved once from PCS and stored on the filesystem for testing) -func fetchCRL(uri string, name string) (*x509.RevocationList, error) { - _, err := os.Stat(name) - if err == nil { - fmt.Printf("File %s exists.\n", name) - // Read CRL - crl_raw, err := os.ReadFile(name) - if err != nil { - return nil, fmt.Errorf("failed to read quote.dat: %w", err) - } - // Parse CRL - crl, err := x509.ParseRevocationList(crl_raw) - if err != nil { - return nil, err + for _, v := range result.ExtendedQuoteCheck.TeeAttributes { + if !v.Success { + return fmt.Errorf("TDX Quote Body Verification failed. %v: (Expected: %v, Got: %v)", v.Name, v.Claimed, v.Measured) } - return crl, nil - } else if os.IsNotExist(err) { - return downloadCRL(uri, name) - } else { - return nil, err } -} -func downloadCRL(uri string, name string) (*x509.RevocationList, error) { - // Download CRL from PCS - resp, err := http.Get(uri) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("request failed with status code: %d", resp.StatusCode) - } - - // Read response body into a byte slice - crlData, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - // Store CRL in file - err = os.WriteFile(name, crlData, 0644) - if err != nil { - return nil, err - } - // Parse CRL - crl, err := x509.ParseRevocationList(crlData) - if err != nil { - return nil, err - } - - return crl, nil -} - -// verifies the given SGX certificate chain, fetches CRLs and chechs if the certs are outdated -// writes results back to verification report -func VerifyIntelCertChainFull(quoteCerts SgxCertificates) ([][]*x509.Certificate, error) { - // verify PCK certificate chain - x509CertChains, err := internal.VerifyCertChain( - []*x509.Certificate{quoteCerts.PCKCert, quoteCerts.IntermediateCert}, - []*x509.Certificate{quoteCerts.RootCACert}) - if err != nil { - msg := fmt.Sprintf("Failed to verify pck certificate chain: %v", err) - return nil, errors.New(msg) - } - - // download CRLs from PCS - root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) - if err != nil { - msg := fmt.Sprintf("downloading ROOT CA CRL from PCS failed: %v", err) - return nil, errors.New(msg) - } - - pck_crl_uri := fmt.Sprintf(PCS_PCK_CERT_CRL_URI, "processor") - pck_crl, err := fetchCRL(pck_crl_uri, PCK_CERT_CRL_NAME) - if err != nil { - msg := fmt.Sprintf("downloading PCK Cert CRL from PCS failed: %v", err) - return nil, errors.New(msg) - } - - // perform CRL checks (signature + values) - res, err := CrlCheck(root_ca_crl, quoteCerts.RootCACert, quoteCerts.RootCACert) - if !res || err != nil { - msg := fmt.Sprintf("CRL check on rootCert failed: %v", err) - return nil, errors.New(msg) - } - - res, err = CrlCheck(pck_crl, quoteCerts.PCKCert, quoteCerts.IntermediateCert) - if !res || err != nil { - msg := fmt.Sprintf("CRL check on pckCert failed: %v", err) - return nil, errors.New(msg) - } - - return x509CertChains, nil -} - -func VerifyTCBSigningCertChain(quoteCerts SgxCertificates) ([][]*x509.Certificate, error) { - // verify TCB Signing cert chain - tcbSigningCertChain, err := internal.VerifyCertChain( - []*x509.Certificate{quoteCerts.TCBSigningCert}, - []*x509.Certificate{quoteCerts.RootCACert}) - if err != nil { - msg := fmt.Sprintf("Failed to verify TCB Signing certificate chain: %v", err) - return nil, errors.New(msg) - } - - root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) - if err != nil { - msg := fmt.Sprintf("downloading Root CA CRL from PCS failed: %v", err) - return nil, errors.New(msg) - } - - // perform CRL checks (signature + values) - res, err := CrlCheck(root_ca_crl, quoteCerts.TCBSigningCert, quoteCerts.RootCACert) - if !res || err != nil { - msg := fmt.Sprintf("CRL check on TcbSigningCert failed: %v", err) - return nil, errors.New(msg) - } - - return tcbSigningCertChain, nil + return nil } diff --git a/attestationreport/sgx_structs.go b/attestationreport/sgx_structs.go index 83020b7b..ed71a1ce 100644 --- a/attestationreport/sgx_structs.go +++ b/attestationreport/sgx_structs.go @@ -376,7 +376,7 @@ func ParseCertificates(certsRaw any, pem bool) (SgxCertificates, error) { certs, err = internal.ParseCertsDer(t) } if err != nil { - return SgxCertificates{}, fmt.Errorf("failed to parse PEM certificates %v", err) + return SgxCertificates{}, fmt.Errorf("failed to parse certificates %v", err) } case [][]byte: if pem { @@ -385,7 +385,7 @@ func ParseCertificates(certsRaw any, pem bool) (SgxCertificates, error) { certs, err = internal.ParseCertsDer(t) } if err != nil { - return SgxCertificates{}, fmt.Errorf("failed to parse DER certificates %v", err) + return SgxCertificates{}, fmt.Errorf("failed to parse certificates %v", err) } default: return SgxCertificates{}, fmt.Errorf("ParseCertificates not implemented for type %T", certsRaw) diff --git a/attestationreport/sgx_test.go b/attestationreport/sgx_test.go index c105f699..ba019d18 100644 --- a/attestationreport/sgx_test.go +++ b/attestationreport/sgx_test.go @@ -430,9 +430,9 @@ var ( // valid collateral tee_type = uint32(0) - tcb_info = []byte(`{"tcbInfo":{"id":"SGX","version":3,"issueDate":"2023-07-18T23:45:46Z","nextUpdate":"2023-08-17T23:45:46Z","fmspc":"00706A100000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":15,"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":8},{"svn":8},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":7},{"svn":7},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2022-11-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":5},{"svn":5},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":4},{"svn":4},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-06-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":3},{"svn":3},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":10},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":9},"tcbDate":"2020-06-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":7},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":6},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":1},{"svn":1},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":4},"tcbDate":"2017-07-26T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00088","INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]}]},"signature":"8ae2a5689564301bf403a72f7e58dbe36402ae9d88b7ef058e60153bc46221190be5ac15a74c9519912c22185b4cac00670c558e01836314c0610cee0e0e963a"}`) - tcb_info_size = uint32(4391) - qe_identity = []byte(`{"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-07-19T00:13:19Z","nextUpdate":"2023-08-18T00:13:19Z","tcbEvaluationDataNumber":15,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"ced962b0a71c8003d686b7a3e459623a2f2536f96a2926a24b67390e681e22728571482da5f9b642989da7355f3148a4984c340df83b3d38aaffe206d77dc58f"}`) + tcb_info = []byte(`{"tcbInfo":{"id":"SGX","version":3,"issueDate":"2023-11-22T19:46:39Z","nextUpdate":"2023-12-22T19:46:39Z","fmspc":"00706a100000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":16,"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":8},{"svn":8},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":7},{"svn":7},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2022-11-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":5},{"svn":5},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":4},{"svn":4},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-06-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":3},{"svn":3},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":10},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":9},"tcbDate":"2020-06-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":7},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":6},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":1},{"svn":1},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":4},"tcbDate":"2017-07-26T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00088","INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]}]},"signature":"3320aa700f3bf4a8cd4d8ed2928b400e7757240f518b57a595a091eafebe3b84ed618dfde6f5f502feccc5084c09a39e2b8fb2c6620705c1eeebc39eaa43eb15"}`) + tcb_info_size = uint32(4390) + qe_identity = []byte(`{"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-11-22T19:39:39Z","nextUpdate":"2023-12-22T19:39:39Z","tcbEvaluationDataNumber":16,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"3985e4e7996247d7ca663365bd2092a2cae84b0732d50e6b6ccff86ada831c8ef16b155883c321b88271815c84cb6bdcd6e9c5f34787b31f4993ac9a15125b8e"}`) qe_identity_size = uint32(1381) // old/invalid collateral @@ -441,9 +441,6 @@ var ( qe_identity_old = []byte(`{"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-05-20T23:50:17Z","nextUpdate":"2023-06-19T23:50:17Z","tcbEvaluationDataNumber":15,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"d2a951b5a145f82b089903cc4747c28edf9d9a63b55a043ff2e7a93ecb339bb0b935d4ad516e3e91e57a00d3f913769d60dddd1ed54e242ea9365c9ca5d280ed"}`) qe_identity_size_old = uint32(1381) - // test_crl_pem = []byte("-----BEGIN X509 CRL-----\nMIIBITCByAIBATAKBggqhkjOPQQDAjBoMRowGAYDVQQDDBFJbnRlbCBTR1ggUm9vdCBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTIzMDQwMzEwMjI1MVoXDTI0MDQwMjEwMjI1MVqgLzAtMAoGA1UdFAQDAgEBMB8GA1UdIwQYMBaAFCJlDNZanTSJ84O0lVK/UBs5JwasMAoGCCqGSM49BAMCA0gAMEUCIFFXfUfZ+6FXtl8etfRle7xeVsyvc1oD8blj1wSAWrEYAiEAk5AV7BY25+r6X0JsHkAmR8ZzEytoUMq9aM72utdoKgM=\n-----END X509 CRL-----") - // test_crl_der_16, _ = hex.DecodeString("308201213081c8020101300a06082a8648ce3d0403023068311a301806035504030c11496e74656c2053475820526f6f74204341311a3018060355040a0c11496e74656c20436f72706f726174696f6e3114301206035504070c0b53616e746120436c617261310b300906035504080c024341310b3009060355040613025553170d3233303430333130323235315a170d3234303430323130323235315aa02f302d300a0603551d140403020101301f0603551d2304183016801422650cd65a9d3489f383b49552bf501b392706ac300a06082a8648ce3d0403020348003045022051577d47d9fba157b65f1eb5f4657bbc5e56ccaf735a03f1b963d704805ab118022100939015ec1636e7eafa5f426c1e402647c673132b6850cabd68cef6bad7682a03") - sgx_extensions_short = []byte{0x30, 0x1E, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x01, 0x0D, 0x01, 0x01, 0x04, 0x10, 0x68, 0x7F, 0x27, 0x16, 0xC8, 0xB5, 0x33, 0xAE, 0x4F, 0x4A, 0x44, 0x2C, 0x07, 0x9D, 0xB2, 0x04} sgx_extensions, _ = hex.DecodeString("301E060A2A864886F84D010D01010410687F2716C8B533AE4F4A442C079DB20430820163060A2A864886F84D010D0102308201533010060B2A864886F84D010D0102010201073010060B2A864886F84D010D0102020201073010060B2A864886F84D010D0102030201003010060B2A864886F84D010D0102040201003010060B2A864886F84D010D0102050201003010060B2A864886F84D010D0102060201003010060B2A864886F84D010D0102070201003010060B2A864886F84D010D0102080201003010060B2A864886F84D010D0102090201003010060B2A864886F84D010D01020A0201003010060B2A864886F84D010D01020B0201003010060B2A864886F84D010D01020C0201003010060B2A864886F84D010D01020D0201003010060B2A864886F84D010D01020E0201003010060B2A864886F84D010D01020F0201003010060B2A864886F84D010D0102100201003010060B2A864886F84D010D01021102010D301F060B2A864886F84D010D0102120410070700000000000000000000000000003010060A2A864886F84D010D0103040200003014060A2A864886F84D010D0104040600706A100000300F060A2A864886F84D010D01050A0100") @@ -451,6 +448,7 @@ var ( validSGXAttributes [16]byte = [16]byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} invalidSGXAttributes [16]byte = [16]byte{0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} validIsvProdId uint16 = 0x00 + validIsvSvn uint16 = 0 validMRSIGNER = "37E0543F5597B0F0E028FA18955E1307CB7A8CF54B37F513FF64961EADEF94C4" invalidMRSIGNER = "2101a93F5597B0F0E028FA18955E1307CB7A8CF54B37F513FF64961EADEF94C4" ) @@ -491,7 +489,8 @@ func Test_verifySgxMeasurements(t *testing.T) { CaFingerprint: rootCACertFingerprint, Attributes: validSGXAttributes, IsvProdId: validIsvProdId, - MRSIGNER: validMRSIGNER, + IsvSvn: validIsvSvn, + MrSigner: validMRSIGNER, }, }, }, diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 4a05efab..2f66367e 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -19,8 +19,6 @@ import ( "bytes" "encoding/hex" "fmt" - "reflect" - "time" ) func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*TdxMeasurementResult, bool) { @@ -78,9 +76,6 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ result.Freshness.Success = true } - var current_time time.Time = time.Now() - log.Trace("current time: ", current_time) - var quoteCerts SgxCertificates = tdxQuote.QuoteSignatureData.QECertData.QECertData if quoteCerts.RootCACert == nil || quoteCerts.IntermediateCert == nil || quoteCerts.PCKCert == nil { @@ -91,7 +86,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ // parse reference cert chain (TCBSigningCert chain) referenceCerts, err := ParseCertificates(tdxM.Certs, true) - if err != nil { + if err != nil || referenceCerts.TCBSigningCert == nil || referenceCerts.RootCACert == nil { msg := fmt.Sprintf("Failed to parse reference certificates (TCBSigningCert + IntelRootCACert): %v", err) result.Summary.setFalse(&msg) return result, false @@ -164,7 +159,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, false } - // check version + // Check version result.VersionMatch, ret = verifyQuoteVersion(tdxQuote.QuoteHeader, tdxReferenceValue.Tdx.Version) if !ret { return result, false @@ -173,13 +168,14 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ return result, ok } -func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertificates, tdxReferenceValue *ReferenceValue, result *TdxMeasurementResult) error { +func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, + certs *SgxCertificates, tdxReferenceValue *ReferenceValue, result *TdxMeasurementResult) error { if body == nil || tcbInfo == nil || certs == nil || tdxReferenceValue == nil || result == nil { return fmt.Errorf("invalid function parameter (null pointer exception)") } // check MrTd reference value (measurement of the initial contents of the TD) - if !reflect.DeepEqual(body.MrTd[:], []byte(tdxReferenceValue.Sha256)) { + if !bytes.Equal(body.MrTd[:], []byte(tdxReferenceValue.Sha256)) { result.Artifacts = append(result.Artifacts, DigestResult{ Name: tdxReferenceValue.Name, @@ -187,7 +183,6 @@ func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi Success: false, Type: "Reference Value", }) - result.Artifacts = append(result.Artifacts, DigestResult{ Name: tdxReferenceValue.Name, @@ -206,7 +201,7 @@ func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi } // Perform Extended TD Check (see DCAP documentation) - result.ExtendedTdCheck.TdIdentity = append(result.ExtendedTdCheck.TdIdentity, + result.ExtendedQuoteCheck.TeeIdentity = append(result.ExtendedQuoteCheck.TeeIdentity, ComparisonResult{ Name: "MrSignerSeam", Success: bytes.Equal(tcbInfo.TcbInfo.TdxModule.Mrsigner[:], body.MrSignerSeam[:]), @@ -271,7 +266,7 @@ func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi } seamAttributesResult := bytes.Equal(tcbInfo.TcbInfo.TdxModule.Attributes, seamAttributesQuote[:]) - result.ExtendedTdCheck.TdAttributes = append(result.ExtendedTdCheck.TdAttributes, + result.ExtendedQuoteCheck.TeeAttributes = append(result.ExtendedQuoteCheck.TeeAttributes, AttributesCheck{ Name: "TdAttributes", Success: bytes.Equal(body.TdAttributes[:], tdxReferenceValue.Tdx.TdAttributes[:]), @@ -292,13 +287,13 @@ func verifyTdxQuoteBody(body *TdxReportBody, tcbInfo *TcbInfo, certs *SgxCertifi }, ) - for _, v := range result.ExtendedTdCheck.TdIdentity { + for _, v := range result.ExtendedQuoteCheck.TeeIdentity { if !v.Success { return fmt.Errorf("TDX Quote Body Verification failed. %v: (Expected: %v, Got: %v)", v.Name, v.Claimed, v.Measured) } } - for _, v := range result.ExtendedTdCheck.TdAttributes { + for _, v := range result.ExtendedQuoteCheck.TeeAttributes { if !v.Success { return fmt.Errorf("TDX Quote Body Verification failed. %v: (Expected: %v, Got: %v)", v.Name, v.Claimed, v.Measured) } diff --git a/attestationreport/tdx_struct.go b/attestationreport/tdx_struct.go index a788e091..18fa4fd1 100644 --- a/attestationreport/tdx_struct.go +++ b/attestationreport/tdx_struct.go @@ -22,14 +22,14 @@ import ( ) // TDX Report V4 -type TdxReport struct { +type TdxReportV4 struct { QuoteHeader QuoteHeader QuoteBody TdxReportBody QuoteSignatureDataLen uint32 QuoteSignatureData ECDSA256QuoteSignatureDataStructureV4 // variable size } -// 584 bytes (TDX 1.0) +// TDX 1.0: 584 bytes type TdxReportBody struct { TeeTcbSvn [16]byte MrSeam [48]byte @@ -69,8 +69,8 @@ type QEReportCertDataV4 struct { } // Parses the report into the TDReport structure -func DecodeTdxReportV4(report []byte) (TdxReport, error) { - var reportStruct TdxReport +func DecodeTdxReportV4(report []byte) (TdxReportV4, error) { + var reportStruct TdxReportV4 var header QuoteHeader var body TdxReportBody var sig ECDSA256QuoteSignatureDataStructureV4 @@ -80,25 +80,25 @@ func DecodeTdxReportV4(report []byte) (TdxReport, error) { buf := bytes.NewBuffer(report) err := binary.Read(buf, binary.LittleEndian, &header) if err != nil { - return TdxReport{}, fmt.Errorf("failed to decode TD report header: %v", err) + return TdxReportV4{}, fmt.Errorf("failed to decode TD report header: %v", err) } // parse body err = binary.Read(buf, binary.LittleEndian, &body) if err != nil { - return TdxReport{}, fmt.Errorf("failed to decode TD report body: %v", err) + return TdxReportV4{}, fmt.Errorf("failed to decode TD report body: %v", err) } // parse signature size err = binary.Read(buf, binary.LittleEndian, &sigLen) if err != nil { - return TdxReport{}, fmt.Errorf("failed to decode TD report QuoteSignatureDataLen: %v", err) + return TdxReportV4{}, fmt.Errorf("failed to decode TD report QuoteSignatureDataLen: %v", err) } // parse signature err = parseECDSASignatureV4(buf, &sig) if err != nil { - return TdxReport{}, fmt.Errorf("failed to decode TD report QuoteSignatureData: %v", err) + return TdxReportV4{}, fmt.Errorf("failed to decode TD report QuoteSignatureData: %v", err) } // compose the final report struct diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 38e33450..3eb18cd1 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -449,22 +449,20 @@ var ( validTDXCertChain = [][]byte{tcb_signing_cert_tdx.Raw, root_ca_cert_tdx.Raw} validTDXMeasurement, _ = hex.DecodeString(validMrTd) - - aisecCertrootCAFingerprint = "44a0196b2b99f889b8e149e95b807a350e7424964399e885a7cbb8ccfab674d3" + validTDXNonce, _ = hex.DecodeString("324e81d1e3d71e9f77c9e1aafcbdf157aa532d059c3637da19bd28f70e654540b3c711969e303515f916bff5d4b04c7e037a84a0b0bb7ac978a3c0860806c6bb") + tdxRootCAFingerprint = "44a0196b2b99f889b8e149e95b807a350e7424964399e885a7cbb8ccfab674d3" // valid report body values - mrOwner = [48]byte{} // 0x00s - mrOwnerConfig = [48]byte{} // 0x00s - mrConfigId = [48]byte{} // 0x00s - rtMr0 = [48]byte{225, 175, 117, 230, 25, 39, 65, 14, 66, 181, 75, 57, 246, 104, 28, 249, 176, 191, 186, 229, 18, 177, 94, 135, 14, 76, 141, 157, 90, 92, 179, 133, 87, 27, 14, 29, 194, 247, 11, 249, 204, 239, 8, 86, 15, 10, 43, 88} - rtMr1 = [48]byte{1, 28, 185, 253, 213, 20, 180, 71, 150, 2, 57, 206, 119, 160, 255, 200, 7, 135, 119, 55, 31, 126, 191, 235, 76, 160, 72, 13, 3, 60, 229, 236, 97, 67, 72, 6, 73, 247, 90, 144, 87, 157, 245, 245, 1, 107, 231, 202} - rtMr2 = [48]byte{} // 0x00s - rtMr3 = [48]byte{} // 0x00s - + mrOwner = [48]byte{} // 0x00s + mrOwnerConfig = [48]byte{} // 0x00s + mrConfigId = [48]byte{} // 0x00s + rtMr0 = [48]byte{225, 175, 117, 230, 25, 39, 65, 14, 66, 181, 75, 57, 246, 104, 28, 249, 176, 191, 186, 229, 18, 177, 94, 135, 14, 76, 141, 157, 90, 92, 179, 133, 87, 27, 14, 29, 194, 247, 11, 249, 204, 239, 8, 86, 15, 10, 43, 88} + rtMr1 = [48]byte{1, 28, 185, 253, 213, 20, 180, 71, 150, 2, 57, 206, 119, 160, 255, 200, 7, 135, 119, 55, 31, 126, 191, 235, 76, 160, 72, 13, 3, 60, 229, 236, 97, 67, 72, 6, 73, 247, 90, 144, 87, 157, 245, 245, 1, 107, 231, 202} + rtMr2 = [48]byte{} // 0x00s + rtMr3 = [48]byte{} // 0x00s validMrSeam = "2fd279c16164a93dd5bf373d834328d46008c2b693af9ebb865b08b2ced320c9a89b4869a9fab60fbe9d0c5a5363c656" - validTdAttributes = [8]byte{0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00} validMrTd = "145fe28dab356d75767ab56ae83bc59ac045dc548bf40a3fb11390446af19f6ee21b0b8874c9a7864cedb64c6573d932" - validTDXNonce, _ = hex.DecodeString("324e81d1e3d71e9f77c9e1aafcbdf157aa532d059c3637da19bd28f70e654540b3c711969e303515f916bff5d4b04c7e037a84a0b0bb7ac978a3c0860806c6bb") + validTdAttributes = [8]byte{0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00} validXFAM = [8]byte{0xE7, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00} ) @@ -480,7 +478,6 @@ func Test_verifyTdxMeasurements(t *testing.T) { want *TdxMeasurementResult want1 bool }{ - // TODO: Add more test cases. { name: "Valid Attestation Report", args: args{ @@ -502,7 +499,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, - CaFingerprint: aisecCertrootCAFingerprint, + CaFingerprint: tdxRootCAFingerprint, TdId: TDId{ MrOwner: mrOwner, MrOwnerConfig: mrOwnerConfig, @@ -543,12 +540,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, - Policy: TdxPolicy{ - Type: TDX_ID, - Debug: false, - ValidTcbStatus: []string{string(OutOfDate)}, - }, - CaFingerprint: aisecCertrootCAFingerprint, + CaFingerprint: tdxRootCAFingerprint, MrSeam: validMrSeam, TdAttributes: validTdAttributes, Xfam: validXFAM, @@ -580,7 +572,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, - CaFingerprint: aisecCertrootCAFingerprint, + CaFingerprint: tdxRootCAFingerprint, TdId: TDId{ MrOwner: mrOwner, MrOwnerConfig: mrOwnerConfig, @@ -621,7 +613,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, - CaFingerprint: aisecCertrootCAFingerprint, + CaFingerprint: tdxRootCAFingerprint, TdId: TDId{ MrOwner: mrOwner, MrOwnerConfig: mrOwnerConfig, @@ -662,7 +654,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, - CaFingerprint: aisecCertrootCAFingerprint, + CaFingerprint: tdxRootCAFingerprint, TdId: TDId{ MrOwner: mrOwner, MrOwnerConfig: mrOwnerConfig, @@ -703,7 +695,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { QeIdentity: qe_identity_tdx, QeIdentitySize: qe_identity_tdx_size, }, - CaFingerprint: aisecCertrootCAFingerprint, + CaFingerprint: tdxRootCAFingerprint, TdId: TDId{ MrOwner: mrOwner, MrOwnerConfig: mrOwnerConfig, diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index 8999d8e7..e7614602 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -177,41 +177,40 @@ type SnpMeasurementResult struct { // SgxMeasurementResult represents the results for the verification // of Intel SGX measurements. type SgxMeasurementResult struct { - Summary Result `json:"resultSummary"` - Freshness Result `json:"freshness"` - Signature SignatureResult `json:"signature"` - Artifacts []DigestResult `json:"artifacts"` - VersionMatch Result `json:"reportVersionMatch"` - TcbInfoCheck TcbLevelCheck `json:"tcbInfoCheck"` - QeIdentityCheck TcbLevelCheck `json:"qeIdentityCheck"` - - PolicyCheck PolicyCheck `json:"policyCheck"` // TODO: test - FwCheck VersionCheck `json:"fwCheck"` // TODO: test + Summary Result `json:"resultSummary"` + Freshness Result `json:"freshness"` + Signature SignatureResult `json:"signature"` + Artifacts []DigestResult `json:"artifacts"` + VersionMatch Result `json:"reportVersionMatch"` + TcbInfoCheck TcbLevelResult `json:"tcbInfoCheck"` + QeIdentityCheck TcbLevelResult `json:"qeIdentityCheck"` + ExtendedQuoteCheck ExtendedQuoteCheck `json:"extendedQuoteCheck"` } // TdxMeasurementResult represents the results for the verification // of Intel TDX measurements. type TdxMeasurementResult struct { - Summary Result `json:"resultSummary"` - Freshness Result `json:"freshness"` - Signature SignatureResult `json:"signature"` - Artifacts []DigestResult `json:"artifacts"` - VersionMatch Result `json:"reportVersionMatch"` - TcbInfoCheck TcbLevelCheck `json:"tcbInfoCheck"` - QeIdentityCheck TcbLevelCheck `json:"qeIdentityCheck"` - ExtendedTdCheck ExtendedTdCheck `json:"extendedTdCheck"` + Summary Result `json:"resultSummary"` + Freshness Result `json:"freshness"` + Signature SignatureResult `json:"signature"` + Artifacts []DigestResult `json:"artifacts"` + VersionMatch Result `json:"reportVersionMatch"` + TcbInfoCheck TcbLevelResult `json:"tcbInfoCheck"` + QeIdentityCheck TcbLevelResult `json:"qeIdentityCheck"` + ExtendedQuoteCheck ExtendedQuoteCheck `json:"extendedQuoteCheck"` } -type TcbLevelCheck struct { +type TcbLevelResult struct { Summary Result `json:"success"` TcbLevelStatus string `json:"status"` TcbLevelDate time.Time `json:"date"` } -type ExtendedTdCheck struct { - Summary Result `json:"success"` - TdIdentity []ComparisonResult `json:"tdId"` // TD identity values from quote body - TdAttributes []AttributesCheck `json:"tdAttributes"` // attributes and attribute masks from body +// Stores the results of the quote body verification +type ExtendedQuoteCheck struct { + Summary Result `json:"success"` + TeeIdentity []ComparisonResult `json:"teeIdentity"` // TD/SGX identity values from quote body + TeeAttributes []AttributesCheck `json:"teeAttributes"` // attributes and attribute masks from body } type ComparisonResult struct { From 40bf4a3329528448c39dc5685ca84698a71a905d Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Thu, 23 Nov 2023 23:58:23 +0000 Subject: [PATCH 18/23] removed sgx --- attestationreport/intel_helpers.go | 4 +- .../{sgx_structs.go => intel_structs.go} | 67 +- attestationreport/sgx.go | 315 -------- attestationreport/sgx_test.go | 709 ------------------ attestationreport/tdx.go | 10 +- attestationreport/tdx_struct.go | 4 +- 6 files changed, 14 insertions(+), 1095 deletions(-) rename attestationreport/{sgx_structs.go => intel_structs.go} (83%) delete mode 100644 attestationreport/sgx.go delete mode 100644 attestationreport/sgx_test.go diff --git a/attestationreport/intel_helpers.go b/attestationreport/intel_helpers.go index 906a4513..04d0a3e9 100644 --- a/attestationreport/intel_helpers.go +++ b/attestationreport/intel_helpers.go @@ -341,7 +341,7 @@ func compareSgxTcbCompSvns(sgxExtensions SGXExtensionsValue, tcbLevel TcbLevel) // Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to 16) // with the corresponding values of SVNs in sgxtcbcomponents array of TCB Level. for i := 0; i < 16; i++ { - tcbFromCert := GetTCBCompByIndex(sgxExtensions.Tcb, i+1) + tcbFromCert := getTCBCompByIndex(sgxExtensions.Tcb, i+1) if byte(tcbFromCert.Value) < tcbLevel.Tcb.SgxTcbComponents[i].Svn { return false } @@ -571,7 +571,7 @@ func downloadCRL(uri string, name string) (*x509.RevocationList, error) { } // Store CRL in file - err = os.WriteFile(name, crlData, 0644) + err = os.WriteFile("./cache/"+name, crlData, 0644) if err != nil { return nil, err } diff --git a/attestationreport/sgx_structs.go b/attestationreport/intel_structs.go similarity index 83% rename from attestationreport/sgx_structs.go rename to attestationreport/intel_structs.go index ed71a1ce..c7bb4870 100644 --- a/attestationreport/sgx_structs.go +++ b/attestationreport/intel_structs.go @@ -43,16 +43,6 @@ const ( NotSupported TcbStatus = "NotSupported" ) -// Overall structure: table 2 from https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf -// Endianess: Little Endian (all Integer fields) -type SgxReport struct { - QuoteHeader QuoteHeader - ISVEnclaveReport EnclaveReportBody - QuoteSignatureDataLen uint32 - QuoteSignatureData ECDSA256QuoteSignatureDataStructure // variable size -} - -// Quote Header: Table 3 from https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf // 48 bytes type QuoteHeader struct { Version uint16 @@ -64,7 +54,6 @@ type QuoteHeader struct { UserData [20]byte } -// Enclave Report Body: Table 5 from https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf // 384 bytes type EnclaveReportBody struct { CPUSVN [16]byte @@ -81,7 +70,6 @@ type EnclaveReportBody struct { ReportData [64]byte } -// table 4: https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf type ECDSA256QuoteSignatureDataStructure struct { ISVEnclaveReportSignature [64]byte ECDSAAttestationKey [64]byte @@ -96,7 +84,6 @@ type ECDSA256QuoteSignatureDataStructure struct { QECertData []byte } -// from Intel SGX DCAP library: AttestationParsers.h type TcbInfo struct { TcbInfo TcbInfoBody `json:"tcbInfo"` Signature HexByte `json:"signature"` @@ -135,7 +122,6 @@ type TcbComponent struct { Type string `json:"type"` } -// TODO: not sure if this is correct type TdxModule struct { Mrsigner HexByte `json:"mrsigner"` Attributes HexByte `json:"attributes"` @@ -266,7 +252,7 @@ type Configuration struct { // ------------------------- end SGX Extensions ------------------------- -func ParseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { +func parseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { var sgx_extensions SGXExtensionsValue rest, err := asn1.Unmarshal(extensions, &sgx_extensions.Ppid) @@ -303,7 +289,7 @@ func ParseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { return sgx_extensions, nil } -func GetTCBCompByIndex(tcb TCB, index int) TCBComp { +func getTCBCompByIndex(tcb TCB, index int) TCBComp { switch index { case 1: return tcb.Value.Comp_01 @@ -343,7 +329,7 @@ func GetTCBCompByIndex(tcb TCB, index int) TCBComp { } // expects enclave Identity structure in JSON format -func ParseQEIdentity(qeIdentityJson []byte) (QEIdentity, error) { +func parseQEIdentity(qeIdentityJson []byte) (QEIdentity, error) { var qe_identity QEIdentity err := json.Unmarshal(qeIdentityJson, &qe_identity) if err != nil { @@ -353,7 +339,7 @@ func ParseQEIdentity(qeIdentityJson []byte) (QEIdentity, error) { } // expects tcb Info in JSON format -func ParseTcbInfo(tcbInfoJson []byte) (TcbInfo, error) { +func parseTcbInfo(tcbInfoJson []byte) (TcbInfo, error) { var tcbInfo TcbInfo err := json.Unmarshal(tcbInfoJson, &tcbInfo) if err != nil { @@ -363,7 +349,7 @@ func ParseTcbInfo(tcbInfoJson []byte) (TcbInfo, error) { } // parse PEM/DER formatted certificates into a SgxCertificates struct -func ParseCertificates(certsRaw any, pem bool) (SgxCertificates, error) { +func parseCertificates(certsRaw any, pem bool) (SgxCertificates, error) { var certChain SgxCertificates var certs []*x509.Certificate var err error @@ -410,48 +396,6 @@ func ParseCertificates(certsRaw any, pem bool) (SgxCertificates, error) { return certChain, nil } -// Parses the report into the SgxReport structure -func DecodeSgxReport(report []byte) (SgxReport, error) { - var reportStruct SgxReport - var header QuoteHeader - var body EnclaveReportBody - var sig ECDSA256QuoteSignatureDataStructure - var sigLen uint32 - - // parse header - buf := bytes.NewBuffer(report) - err := binary.Read(buf, binary.LittleEndian, &header) - if err != nil { - return SgxReport{}, fmt.Errorf("failed to decode SGX report header: %v", err) - } - - // parse body - err = binary.Read(buf, binary.LittleEndian, &body) - if err != nil { - return SgxReport{}, fmt.Errorf("failed to decode SGX report body: %v", err) - } - - // parse signature size - err = binary.Read(buf, binary.LittleEndian, &sigLen) - if err != nil { - return SgxReport{}, fmt.Errorf("failed to decode SGX report QuoteSignatureDataLen: %v", err) - } - - // parse signature - err = parseECDSASignature(buf, &sig) - if err != nil { - return SgxReport{}, fmt.Errorf("failed to decode SGX report ECDSA256QuotesignatureDataStructure: %v", err) - } - - // compose the final report struct - reportStruct.QuoteHeader = header - reportStruct.ISVEnclaveReport = body - reportStruct.QuoteSignatureDataLen = sigLen - reportStruct.QuoteSignatureData = sig - - return reportStruct, nil -} - // parses quote signature data structure from buf to sig func parseECDSASignature(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStructure) error { @@ -471,7 +415,6 @@ func parseECDSASignature(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStruc if err != nil { return fmt.Errorf("failed to parse QEReportSignature") } - err = binary.Read(buf, binary.LittleEndian, &sig.QEAuthDataSize) if err != nil { return fmt.Errorf("failed to parse QEAuthDataSize") diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go deleted file mode 100644 index 715be9bc..00000000 --- a/attestationreport/sgx.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright (c) 2021 Fraunhofer AISEC -// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package attestationreport - -import ( - "bytes" - "crypto/x509" - "encoding/hex" - "fmt" - "strconv" - "strings" -) - -func verifySgxMeasurements(sgxM *SgxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*SgxMeasurementResult, bool) { - var err error - result := &SgxMeasurementResult{} - ok := true - - // If the attestationreport does contain neither SGX measurements, nor SGX Reference Values - // there is nothing to to - if sgxM == nil && len(referenceValues) == 0 { - return nil, true - } - - if len(referenceValues) == 0 { - msg := "Could not find SGX Reference Value" - result.Summary.setFalse(&msg) - return result, false - } else if len(referenceValues) > 1 { - msg := fmt.Sprintf("Report contains %v reference values. Currently, only one SGX Reference Value is supported", - len(referenceValues)) - result.Summary.setFalse(&msg) - return result, false - } - sgxReferenceValue := referenceValues[0] - - // If the attestationreport contains SGX Reference Values, but no SGX measurement, the attestation must fail - if sgxM == nil { - for _, v := range referenceValues { - result.Artifacts = append(result.Artifacts, - DigestResult{ - Name: v.Name, - Digest: hex.EncodeToString(v.Sha256), - Success: false, - Type: "Reference Value", - }) - } - result.Summary.Success = false - return result, false - } - - // Validate Parameters: - if sgxM.Report == nil || len(sgxM.Report) < SGX_QUOTE_MIN_SIZE { - msg := "Invalid SGX Report." - result.Summary.setFalse(&msg) - return result, false - } - - if sgxReferenceValue.Type != "SGX Reference Value" { - msg := fmt.Sprintf("SGX Reference Value invalid type %v", sgxReferenceValue.Type) - result.Summary.setFalse(&msg) - return result, false - } - - if sgxReferenceValue.Sgx == nil { - msg := "SGX Reference Value is null" - result.Summary.setFalse(&msg) - return result, false - } - - var sgxQuote SgxReport - var quoteType uint32 = sgxReferenceValue.Sgx.Collateral.TeeType - if quoteType == SGX_QUOTE_TYPE { - // extract the attestation report into the SGXReport data structure - sgxQuote, err = DecodeSgxReport(sgxM.Report) - if err != nil { - msg := fmt.Sprintf("Failed to decode SGX report: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - } else { - log.Tracef("Unknown quote type (tee_type: %X)\n", quoteType) - return result, false - } - - // Compare Nonce for Freshness (called Report Data in the SNP Attestation Report Structure) - // ReportData contains: nonce in ReportData field - nonce64 := make([]byte, 64) - copy(nonce64, nonce[:]) - - if cmp := bytes.Compare(sgxQuote.ISVEnclaveReport.ReportData[:], nonce64); cmp != 0 { - msg := fmt.Sprintf("Nonces mismatch: Plain Nonce: %v, Expected: %v, Got = %v", - nonce, hex.EncodeToString(nonce64), hex.EncodeToString(sgxQuote.ISVEnclaveReport.ReportData[:])) - result.Freshness.setFalse(&msg) - result.Summary.Success = false - return result, false - } else { - result.Freshness.Success = true - } - - // parse cert chain - referenceCerts, err := ParseCertificates(sgxM.Certs, true) - if err != nil { - msg := fmt.Sprintf("Failed to parse reference certificates: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - // (from DCAP Library): parse PCK Cert chain (from the quote) into CertificateChain object. return error in case of failure - // TODO: handle other QECertDataTypes (for now: throw an error) - var quoteCerts SgxCertificates - if sgxQuote.QuoteSignatureData.QECertDataType == 5 { - quoteCerts, err = ParseCertificates(sgxQuote.QuoteSignatureData.QECertData, true) - if err != nil { - msg := fmt.Sprintf("Failed to parse certificate chain from QECertData: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - } else { - msg := fmt.Sprintf("QECertDataType not supported: %v", sgxQuote.QuoteSignatureData.QECertDataType) - result.Summary.setFalse(&msg) - return result, false - } - - // Extract root CA from PCK cert chain in quote -> compare nullptr - if quoteCerts.RootCACert == nil { - msg := "root cert is null" - result.Summary.setFalse(&msg) - return result, false - } - - // Check root public key - quotePublicKeyBytes, err := x509.MarshalPKIXPublicKey(quoteCerts.RootCACert.PublicKey) - if err != nil { - return result, false - } - - referencePublicKeyBytes, err := x509.MarshalPKIXPublicKey(referenceCerts.RootCACert.PublicKey) - if err != nil { - return result, false - } - - if !bytes.Equal(quotePublicKeyBytes, referencePublicKeyBytes) { - msg := "root cert public key didn't match" - result.Summary.setFalse(&msg) - return result, false - } - - // Parse and verify PCK certificate extensions - sgxExtensions, err := ParseSGXExtensions(quoteCerts.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant) - if err != nil { - msg := fmt.Sprintf("failed to parse SGX Extensions from PCK Certificate: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - // Parse and verify TcbInfo object - tcbInfo, err := ParseTcbInfo(sgxReferenceValue.Sgx.Collateral.TcbInfo) - if err != nil { - msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - tcbInfoResult, err := verifyTcbInfo(&tcbInfo, string(sgxReferenceValue.Sgx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, - sgxExtensions, [16]byte{}, SGX_QUOTE_TYPE) - if err != nil { - msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - result.TcbInfoCheck = tcbInfoResult - - // Parse and verify QE Identity object - qeIdentity, err := ParseQEIdentity(sgxReferenceValue.Sgx.Collateral.QeIdentity) - if err != nil { - msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - - qeIdentityResult, err := VerifyQEIdentity(&sgxQuote.QuoteSignatureData.QEReport, &qeIdentity, - string(sgxReferenceValue.Sgx.Collateral.QeIdentity), referenceCerts.TCBSigningCert, SGX_QUOTE_TYPE) - if err != nil { - msg := fmt.Sprintf("Failed to verify QE Identity structure: %v", err) - result.Summary.setFalse(&msg) - return result, false - } - result.QeIdentityCheck = qeIdentityResult - - // Verify Quote Signature - sig, ret := VerifyIntelQuoteSignature(sgxM.Report, sgxQuote.QuoteSignatureData, - sgxQuote.QuoteSignatureDataLen, int(sgxQuote.QuoteHeader.AttestationKeyType), referenceCerts, - sgxReferenceValue.Sgx.CaFingerprint, quoteType) - if !ret { - ok = false - } - result.Signature = sig - - // Verify Quote Body values - err = VerifySgxQuoteBody(&sgxQuote.ISVEnclaveReport, &tcbInfo, &sgxExtensions, &sgxReferenceValue, result) - if err != nil { - msg := fmt.Sprintf("Failed to verify SGX Report Body: %v", err) - result.Summary.setFalse(&msg) - result.Summary.Success = false - return result, false - } - - // Check version - result.VersionMatch, ret = verifyQuoteVersion(sgxQuote.QuoteHeader, sgxReferenceValue.Sgx.Version) - if !ret { - return result, false - } - - result.Summary.Success = ok - - return result, ok -} - -func VerifySgxQuoteBody(body *EnclaveReportBody, tcbInfo *TcbInfo, - sgxExtensions *SGXExtensionsValue, sgxReferenceValue *ReferenceValue, result *SgxMeasurementResult) error { - if body == nil || tcbInfo == nil || sgxExtensions == nil || sgxReferenceValue == nil || result == nil { - return fmt.Errorf("invalid function parameter (null pointer exception)") - } - - // check MRENCLAVE reference value - if !bytes.Equal(body.MRENCLAVE[:], []byte(sgxReferenceValue.Sha256)) { - result.Artifacts = append(result.Artifacts, - DigestResult{ - Name: sgxReferenceValue.Name, - Digest: hex.EncodeToString(sgxReferenceValue.Sha256[:]), - Success: false, - Type: "Reference Value", - }) - result.Artifacts = append(result.Artifacts, - DigestResult{ - Name: sgxReferenceValue.Name, - Digest: hex.EncodeToString(body.MRENCLAVE[:]), - Success: false, - Type: "Measurement", - }) - return fmt.Errorf("MRENCLAVE mismatch. Expected: %v, Got. %v", sgxReferenceValue.Sha256, body.MRENCLAVE) - } else { - result.Artifacts = append(result.Artifacts, - DigestResult{ - Name: sgxReferenceValue.Name, - Digest: hex.EncodeToString(body.MRENCLAVE[:]), - Success: true, - }) - } - - result.ExtendedQuoteCheck.TeeIdentity = append(result.ExtendedQuoteCheck.TeeIdentity, - ComparisonResult{ - Name: "MrSigner", - Success: strings.EqualFold(sgxReferenceValue.Sgx.MrSigner, hex.EncodeToString(body.MRSIGNER[:])), - Claimed: sgxReferenceValue.Sgx.MrSigner, - Measured: hex.EncodeToString(body.MRSIGNER[:]), - }, - // TODO: check how to compare CPUSVN value - ComparisonResult{ - Name: "CpuSvn", - Success: bytes.Equal(sgxExtensions.Tcb.Value.CpuSvn.Value, body.CPUSVN[:]), - Claimed: hex.EncodeToString(sgxExtensions.Tcb.Value.CpuSvn.Value), - Measured: hex.EncodeToString(body.CPUSVN[:]), - }, - ComparisonResult{ - Name: "IsvProdId", - Success: sgxReferenceValue.Sgx.IsvProdId == body.ISVProdID, - Claimed: strconv.Itoa(int(sgxReferenceValue.Sgx.IsvProdId)), - Measured: strconv.Itoa(int(body.ISVProdID)), - }, - ComparisonResult{ - Name: "IsvSvn", - Success: sgxReferenceValue.Sgx.IsvSvn == body.ISVSVN, - Claimed: strconv.Itoa(int(sgxReferenceValue.Sgx.IsvSvn)), - Measured: strconv.Itoa(int(body.ISVSVN)), - }, - ) - - result.ExtendedQuoteCheck.TeeAttributes = append(result.ExtendedQuoteCheck.TeeAttributes, - AttributesCheck{ - Name: "Attributes", - Success: bytes.Equal(sgxReferenceValue.Sgx.Attributes[:], body.Attributes[:]), - Claimed: sgxReferenceValue.Sgx.Attributes[:], - Measured: body.Attributes[:], - }, - ) - - for _, v := range result.ExtendedQuoteCheck.TeeIdentity { - if !v.Success { - return fmt.Errorf("TDX Quote Body Verification failed. %v: (Expected: %v, Got: %v)", v.Name, v.Claimed, v.Measured) - } - } - - for _, v := range result.ExtendedQuoteCheck.TeeAttributes { - if !v.Success { - return fmt.Errorf("TDX Quote Body Verification failed. %v: (Expected: %v, Got: %v)", v.Name, v.Claimed, v.Measured) - } - } - - return nil -} diff --git a/attestationreport/sgx_test.go b/attestationreport/sgx_test.go deleted file mode 100644 index ba019d18..00000000 --- a/attestationreport/sgx_test.go +++ /dev/null @@ -1,709 +0,0 @@ -// Copyright (c) 2021 Fraunhofer AISEC -// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package attestationreport - -import ( - "encoding/hex" - "fmt" - "reflect" - "testing" -) - -var ( - validSGXQuote = []byte{ - 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, - 0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, - 0x95, 0x7f, 0x06, 0x07, 0x18, 0xb9, 0x7a, 0xf8, 0xc1, 0x57, 0xbf, 0x7f, - 0x6a, 0xad, 0xe5, 0xa7, 0xc5, 0x02, 0x02, 0xf1, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x75, 0x5c, 0xd5, 0x7f, 0x2c, 0x04, 0xb8, - 0x45, 0xc4, 0x91, 0x25, 0x77, 0xd6, 0x9a, 0xf3, 0xf9, 0x6c, 0xb7, 0xe3, - 0xea, 0xda, 0x57, 0xd1, 0xf9, 0xa7, 0xc6, 0x25, 0xf6, 0xa6, 0x23, 0x3c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xe0, 0x54, 0x3f, - 0x55, 0x97, 0xb0, 0xf0, 0xe0, 0x28, 0xfa, 0x18, 0x95, 0x5e, 0x13, 0x07, - 0xcb, 0x7a, 0x8c, 0xf5, 0x4b, 0x37, 0xf5, 0x13, 0xff, 0x64, 0x96, 0x1e, - 0xad, 0xef, 0x94, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34, - 0x35, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x44, 0x10, 0x00, 0x00, 0xc7, 0x23, 0x4a, 0x7f, 0x53, 0x80, 0x58, 0xf5, - 0x9f, 0x51, 0xa1, 0x27, 0x12, 0x08, 0xc2, 0x90, 0xc6, 0xb9, 0xd0, 0xe9, - 0x34, 0x07, 0x19, 0xf3, 0x48, 0xa7, 0x6d, 0x4f, 0x62, 0xc0, 0x17, 0x24, - 0x7d, 0x35, 0xb6, 0x9d, 0x34, 0x04, 0x70, 0xdc, 0x3b, 0x80, 0xb0, 0x3e, - 0xa5, 0x6b, 0x4e, 0x25, 0x75, 0x39, 0x3e, 0xa8, 0x79, 0xe3, 0x75, 0x64, - 0x39, 0x7f, 0x54, 0x76, 0x4f, 0xa7, 0x74, 0xf7, 0x00, 0x36, 0x30, 0x58, - 0xf5, 0x43, 0x51, 0xeb, 0xb7, 0x5b, 0xdd, 0x00, 0x86, 0x73, 0x26, 0x2c, - 0x58, 0x56, 0xb0, 0x5d, 0xe5, 0xee, 0x23, 0xfe, 0xba, 0x84, 0xe3, 0x55, - 0xfc, 0xeb, 0x30, 0x02, 0x5f, 0xe1, 0xa7, 0x63, 0x58, 0xef, 0x53, 0x02, - 0xfb, 0x04, 0xf4, 0x1d, 0x6b, 0x01, 0xcb, 0x21, 0x73, 0xc3, 0x4e, 0x80, - 0x50, 0x86, 0xe2, 0xb7, 0xe1, 0xcd, 0xd8, 0x95, 0x9e, 0x98, 0x2c, 0xe4, - 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x19, 0x2a, 0xa5, 0x0c, 0xe1, 0xc0, 0xce, 0xf0, - 0x3c, 0xcf, 0x89, 0xe7, 0xb5, 0xb1, 0x6b, 0x0d, 0x79, 0x78, 0xf5, 0xc2, - 0xb1, 0xed, 0xcf, 0x77, 0x4d, 0x87, 0x70, 0x2e, 0x81, 0x54, 0xd8, 0xbf, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x4f, 0x57, 0x75, - 0xd7, 0x96, 0x50, 0x3e, 0x96, 0x13, 0x7f, 0x77, 0xc6, 0x8a, 0x82, 0x9a, - 0x00, 0x56, 0xac, 0x8d, 0xed, 0x70, 0x14, 0x0b, 0x08, 0x1b, 0x09, 0x44, - 0x90, 0xc5, 0x7b, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x4a, 0x3c, 0xb5, - 0x08, 0x32, 0x9b, 0xab, 0xfe, 0xd9, 0x56, 0x2d, 0x68, 0x8f, 0xba, 0xfa, - 0x32, 0x30, 0x54, 0x52, 0xdb, 0x83, 0x28, 0xfa, 0x9f, 0xa5, 0x86, 0x4d, - 0x12, 0x70, 0xc8, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6c, 0x04, 0xdd, 0x19, 0x00, 0x1e, 0x58, 0xcb, 0x1a, 0xf4, 0xa1, 0x1f, - 0xab, 0xf8, 0xb7, 0x62, 0xe0, 0x4b, 0x67, 0x63, 0x0f, 0xa7, 0xa7, 0x68, - 0xc3, 0x49, 0xfc, 0x86, 0xd5, 0x1f, 0x53, 0xd5, 0x0c, 0x3c, 0x7f, 0x11, - 0xcf, 0xca, 0xae, 0x69, 0xc9, 0xf6, 0x66, 0xb3, 0x1f, 0x8d, 0xaa, 0x7f, - 0xa6, 0xd4, 0xc3, 0x37, 0xb4, 0xc7, 0x9b, 0x5f, 0x38, 0x4d, 0xd5, 0x05, - 0x75, 0xc0, 0x0a, 0xd7, 0x20, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, - 0x1e, 0x1f, 0x05, 0x00, 0xdc, 0x0d, 0x00, 0x00, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, - 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, - 0x4d, 0x49, 0x49, 0x45, 0x6a, 0x44, 0x43, 0x43, 0x42, 0x44, 0x4b, 0x67, - 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x55, 0x63, 0x66, 0x6b, - 0x58, 0x56, 0x68, 0x49, 0x61, 0x64, 0x6f, 0x76, 0x50, 0x75, 0x55, 0x46, - 0x6a, 0x5a, 0x6c, 0x32, 0x2b, 0x63, 0x71, 0x44, 0x48, 0x78, 0x59, 0x77, - 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, - 0x41, 0x77, 0x49, 0x77, 0x0a, 0x63, 0x54, 0x45, 0x6a, 0x4d, 0x43, 0x45, - 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x61, 0x53, 0x57, 0x35, - 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x42, - 0x44, 0x53, 0x79, 0x42, 0x51, 0x63, 0x6d, 0x39, 0x6a, 0x5a, 0x58, 0x4e, - 0x7a, 0x62, 0x33, 0x49, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, - 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x0a, 0x45, 0x55, - 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x63, 0x6e, - 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, - 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, - 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, - 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, - 0x55, 0x45, 0x0a, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, - 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, - 0x6c, 0x56, 0x54, 0x4d, 0x42, 0x34, 0x58, 0x44, 0x54, 0x49, 0x7a, 0x4d, - 0x44, 0x51, 0x78, 0x4f, 0x54, 0x49, 0x78, 0x4d, 0x7a, 0x63, 0x30, 0x4e, - 0x6c, 0x6f, 0x58, 0x44, 0x54, 0x4d, 0x77, 0x4d, 0x44, 0x51, 0x78, 0x4f, - 0x54, 0x49, 0x78, 0x4d, 0x7a, 0x63, 0x30, 0x0a, 0x4e, 0x6c, 0x6f, 0x77, - 0x63, 0x44, 0x45, 0x69, 0x4d, 0x43, 0x41, 0x47, 0x41, 0x31, 0x55, 0x45, - 0x41, 0x77, 0x77, 0x5a, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, - 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x42, 0x44, 0x53, 0x79, 0x42, 0x44, - 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, 0x5a, 0x70, 0x59, 0x32, 0x46, 0x30, - 0x5a, 0x54, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, 0x45, - 0x0a, 0x43, 0x67, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, - 0x67, 0x51, 0x32, 0x39, 0x79, 0x63, 0x47, 0x39, 0x79, 0x59, 0x58, 0x52, - 0x70, 0x62, 0x32, 0x34, 0x78, 0x46, 0x44, 0x41, 0x53, 0x42, 0x67, 0x4e, - 0x56, 0x42, 0x41, 0x63, 0x4d, 0x43, 0x31, 0x4e, 0x68, 0x62, 0x6e, 0x52, - 0x68, 0x49, 0x45, 0x4e, 0x73, 0x59, 0x58, 0x4a, 0x68, 0x4d, 0x51, 0x73, - 0x77, 0x43, 0x51, 0x59, 0x44, 0x0a, 0x56, 0x51, 0x51, 0x49, 0x44, 0x41, - 0x4a, 0x44, 0x51, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, - 0x55, 0x45, 0x42, 0x68, 0x4d, 0x43, 0x56, 0x56, 0x4d, 0x77, 0x57, 0x54, - 0x41, 0x54, 0x42, 0x67, 0x63, 0x71, 0x68, 0x6b, 0x6a, 0x4f, 0x50, 0x51, - 0x49, 0x42, 0x42, 0x67, 0x67, 0x71, 0x68, 0x6b, 0x6a, 0x4f, 0x50, 0x51, - 0x4d, 0x42, 0x42, 0x77, 0x4e, 0x43, 0x41, 0x41, 0x54, 0x35, 0x0a, 0x61, - 0x4c, 0x33, 0x4c, 0x71, 0x58, 0x50, 0x6f, 0x56, 0x5a, 0x45, 0x32, 0x4b, - 0x64, 0x2f, 0x32, 0x70, 0x6c, 0x62, 0x6a, 0x42, 0x69, 0x48, 0x68, 0x33, - 0x2f, 0x42, 0x53, 0x4c, 0x7a, 0x39, 0x57, 0x49, 0x4e, 0x62, 0x39, 0x4e, - 0x70, 0x38, 0x53, 0x4c, 0x47, 0x37, 0x58, 0x51, 0x63, 0x6b, 0x51, 0x59, - 0x38, 0x41, 0x68, 0x36, 0x32, 0x35, 0x71, 0x42, 0x58, 0x69, 0x67, 0x78, - 0x4a, 0x56, 0x34, 0x0a, 0x65, 0x57, 0x44, 0x55, 0x61, 0x2b, 0x67, 0x67, - 0x65, 0x47, 0x77, 0x43, 0x74, 0x52, 0x4f, 0x72, 0x39, 0x69, 0x5a, 0x48, - 0x6f, 0x34, 0x49, 0x43, 0x70, 0x7a, 0x43, 0x43, 0x41, 0x71, 0x4d, 0x77, - 0x48, 0x77, 0x59, 0x44, 0x56, 0x52, 0x30, 0x6a, 0x42, 0x42, 0x67, 0x77, - 0x46, 0x6f, 0x41, 0x55, 0x30, 0x4f, 0x69, 0x71, 0x32, 0x6e, 0x58, 0x58, - 0x2b, 0x53, 0x35, 0x4a, 0x46, 0x35, 0x67, 0x38, 0x0a, 0x65, 0x78, 0x52, - 0x6c, 0x30, 0x4e, 0x58, 0x79, 0x57, 0x55, 0x30, 0x77, 0x62, 0x41, 0x59, - 0x44, 0x56, 0x52, 0x30, 0x66, 0x42, 0x47, 0x55, 0x77, 0x59, 0x7a, 0x42, - 0x68, 0x6f, 0x46, 0x2b, 0x67, 0x58, 0x59, 0x5a, 0x62, 0x61, 0x48, 0x52, - 0x30, 0x63, 0x48, 0x4d, 0x36, 0x4c, 0x79, 0x39, 0x68, 0x63, 0x47, 0x6b, - 0x75, 0x64, 0x48, 0x4a, 0x31, 0x63, 0x33, 0x52, 0x6c, 0x5a, 0x48, 0x4e, - 0x6c, 0x0a, 0x63, 0x6e, 0x5a, 0x70, 0x59, 0x32, 0x56, 0x7a, 0x4c, 0x6d, - 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x4c, 0x6d, 0x4e, 0x76, 0x62, 0x53, - 0x39, 0x7a, 0x5a, 0x33, 0x67, 0x76, 0x59, 0x32, 0x56, 0x79, 0x64, 0x47, - 0x6c, 0x6d, 0x61, 0x57, 0x4e, 0x68, 0x64, 0x47, 0x6c, 0x76, 0x62, 0x69, - 0x39, 0x32, 0x4e, 0x43, 0x39, 0x77, 0x59, 0x32, 0x74, 0x6a, 0x63, 0x6d, - 0x77, 0x2f, 0x59, 0x32, 0x45, 0x39, 0x0a, 0x63, 0x48, 0x4a, 0x76, 0x59, - 0x32, 0x56, 0x7a, 0x63, 0x32, 0x39, 0x79, 0x4a, 0x6d, 0x56, 0x75, 0x59, - 0x32, 0x39, 0x6b, 0x61, 0x57, 0x35, 0x6e, 0x50, 0x57, 0x52, 0x6c, 0x63, - 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, - 0x67, 0x51, 0x55, 0x53, 0x43, 0x41, 0x70, 0x71, 0x42, 0x54, 0x78, 0x49, - 0x6b, 0x4f, 0x74, 0x44, 0x70, 0x53, 0x74, 0x34, 0x54, 0x34, 0x59, 0x0a, - 0x49, 0x74, 0x54, 0x44, 0x4f, 0x6b, 0x51, 0x77, 0x44, 0x67, 0x59, 0x44, - 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, - 0x41, 0x67, 0x62, 0x41, 0x4d, 0x41, 0x77, 0x47, 0x41, 0x31, 0x55, 0x64, - 0x45, 0x77, 0x45, 0x42, 0x2f, 0x77, 0x51, 0x43, 0x4d, 0x41, 0x41, 0x77, - 0x67, 0x67, 0x48, 0x54, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, - 0x2b, 0x45, 0x30, 0x42, 0x0a, 0x44, 0x51, 0x45, 0x45, 0x67, 0x67, 0x48, - 0x45, 0x4d, 0x49, 0x49, 0x42, 0x77, 0x44, 0x41, 0x65, 0x42, 0x67, 0x6f, - 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, - 0x42, 0x42, 0x42, 0x42, 0x6f, 0x66, 0x79, 0x63, 0x57, 0x79, 0x4c, 0x55, - 0x7a, 0x72, 0x6b, 0x39, 0x4b, 0x52, 0x43, 0x77, 0x48, 0x6e, 0x62, 0x49, - 0x45, 0x4d, 0x49, 0x49, 0x42, 0x59, 0x77, 0x59, 0x4b, 0x0a, 0x4b, 0x6f, - 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x6a, - 0x43, 0x43, 0x41, 0x56, 0x4d, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, - 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, - 0x45, 0x43, 0x41, 0x51, 0x63, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, - 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, - 0x49, 0x43, 0x0a, 0x41, 0x51, 0x63, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, - 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, - 0x67, 0x4d, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, - 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, - 0x67, 0x51, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, - 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x0a, 0x41, 0x51, 0x30, 0x42, - 0x41, 0x67, 0x55, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, - 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, - 0x41, 0x67, 0x59, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, - 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, - 0x41, 0x67, 0x63, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, - 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, - 0x42, 0x41, 0x67, 0x67, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, - 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, - 0x42, 0x41, 0x67, 0x6b, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, - 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, - 0x42, 0x41, 0x67, 0x6f, 0x43, 0x0a, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, - 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, - 0x30, 0x42, 0x41, 0x67, 0x73, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, - 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, - 0x30, 0x42, 0x41, 0x67, 0x77, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, - 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x0a, 0x41, - 0x51, 0x30, 0x42, 0x41, 0x67, 0x30, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, - 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, - 0x51, 0x30, 0x42, 0x41, 0x67, 0x34, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, - 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, - 0x51, 0x30, 0x42, 0x41, 0x67, 0x38, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, - 0x41, 0x59, 0x4c, 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, - 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x41, 0x43, 0x41, 0x51, 0x41, 0x77, - 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, - 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x45, 0x43, 0x41, 0x51, 0x30, 0x77, - 0x48, 0x77, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, - 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x49, 0x45, 0x0a, 0x45, 0x41, 0x63, - 0x48, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x77, 0x45, 0x41, 0x59, - 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, - 0x42, 0x41, 0x77, 0x51, 0x43, 0x41, 0x41, 0x41, 0x77, 0x46, 0x41, 0x59, - 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, - 0x42, 0x0a, 0x42, 0x41, 0x51, 0x47, 0x41, 0x48, 0x42, 0x71, 0x45, 0x41, - 0x41, 0x41, 0x4d, 0x41, 0x38, 0x47, 0x43, 0x69, 0x71, 0x47, 0x53, 0x49, - 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x55, 0x4b, 0x41, 0x51, - 0x41, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, - 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, 0x41, 0x41, 0x77, 0x52, 0x51, - 0x49, 0x68, 0x41, 0x4e, 0x47, 0x4f, 0x0a, 0x71, 0x31, 0x50, 0x58, 0x45, - 0x6b, 0x4b, 0x72, 0x76, 0x33, 0x62, 0x47, 0x4e, 0x59, 0x67, 0x43, 0x74, - 0x66, 0x44, 0x77, 0x4c, 0x78, 0x6d, 0x74, 0x2f, 0x75, 0x46, 0x71, 0x55, - 0x62, 0x65, 0x66, 0x46, 0x31, 0x66, 0x6a, 0x48, 0x42, 0x53, 0x31, 0x41, - 0x69, 0x41, 0x69, 0x36, 0x69, 0x52, 0x76, 0x6a, 0x42, 0x31, 0x7a, 0x49, - 0x38, 0x6c, 0x30, 0x31, 0x2b, 0x49, 0x68, 0x6a, 0x39, 0x70, 0x2f, 0x0a, - 0x64, 0x6e, 0x39, 0x51, 0x2f, 0x56, 0x70, 0x4b, 0x52, 0x6b, 0x78, 0x6c, - 0x65, 0x2b, 0x2b, 0x67, 0x74, 0x4d, 0x64, 0x6a, 0x75, 0x51, 0x3d, 0x3d, - 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, - 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, - 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, - 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6d, - 0x44, 0x43, 0x43, 0x41, 0x6a, 0x36, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, - 0x67, 0x49, 0x56, 0x41, 0x4e, 0x44, 0x6f, 0x71, 0x74, 0x70, 0x31, 0x31, - 0x2f, 0x6b, 0x75, 0x53, 0x52, 0x65, 0x59, 0x50, 0x48, 0x73, 0x55, 0x5a, - 0x64, 0x44, 0x56, 0x38, 0x6c, 0x6c, 0x4e, 0x4d, 0x41, 0x6f, 0x47, 0x43, - 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x0a, - 0x4d, 0x47, 0x67, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, - 0x42, 0x41, 0x4d, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, - 0x49, 0x46, 0x4e, 0x48, 0x57, 0x43, 0x42, 0x53, 0x62, 0x32, 0x39, 0x30, - 0x49, 0x45, 0x4e, 0x42, 0x4d, 0x52, 0x6f, 0x77, 0x47, 0x41, 0x59, 0x44, - 0x56, 0x51, 0x51, 0x4b, 0x44, 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, - 0x62, 0x43, 0x42, 0x44, 0x0a, 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, - 0x68, 0x64, 0x47, 0x6c, 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, - 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, - 0x75, 0x64, 0x47, 0x45, 0x67, 0x51, 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, - 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, - 0x4d, 0x41, 0x6b, 0x4e, 0x42, 0x4d, 0x51, 0x73, 0x77, 0x0a, 0x43, 0x51, - 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, - 0x41, 0x65, 0x46, 0x77, 0x30, 0x78, 0x4f, 0x44, 0x41, 0x31, 0x4d, 0x6a, - 0x45, 0x78, 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, 0x46, 0x77, - 0x30, 0x7a, 0x4d, 0x7a, 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, 0x4d, 0x44, - 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, 0x4d, 0x48, 0x45, 0x78, 0x49, 0x7a, - 0x41, 0x68, 0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x47, - 0x6b, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, 0x48, 0x57, - 0x43, 0x42, 0x51, 0x51, 0x30, 0x73, 0x67, 0x55, 0x48, 0x4a, 0x76, 0x59, - 0x32, 0x56, 0x7a, 0x63, 0x32, 0x39, 0x79, 0x49, 0x45, 0x4e, 0x42, 0x4d, - 0x52, 0x6f, 0x77, 0x47, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x4b, 0x44, - 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x0a, 0x62, 0x43, 0x42, 0x44, - 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, 0x68, 0x64, 0x47, 0x6c, 0x76, - 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x45, - 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, 0x67, - 0x51, 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, - 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, 0x42, - 0x0a, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, - 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, 0x42, 0x5a, 0x4d, 0x42, 0x4d, - 0x47, 0x42, 0x79, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x67, 0x45, - 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x77, 0x45, - 0x48, 0x41, 0x30, 0x49, 0x41, 0x42, 0x4c, 0x39, 0x71, 0x2b, 0x4e, 0x4d, - 0x70, 0x32, 0x49, 0x4f, 0x67, 0x0a, 0x74, 0x64, 0x6c, 0x31, 0x62, 0x6b, - 0x2f, 0x75, 0x57, 0x5a, 0x35, 0x2b, 0x54, 0x47, 0x51, 0x6d, 0x38, 0x61, - 0x43, 0x69, 0x38, 0x7a, 0x37, 0x38, 0x66, 0x73, 0x2b, 0x66, 0x4b, 0x43, - 0x51, 0x33, 0x64, 0x2b, 0x75, 0x44, 0x7a, 0x58, 0x6e, 0x56, 0x54, 0x41, - 0x54, 0x32, 0x5a, 0x68, 0x44, 0x43, 0x69, 0x66, 0x79, 0x49, 0x75, 0x4a, - 0x77, 0x76, 0x4e, 0x33, 0x77, 0x4e, 0x42, 0x70, 0x39, 0x69, 0x0a, 0x48, - 0x42, 0x53, 0x53, 0x4d, 0x4a, 0x4d, 0x4a, 0x72, 0x42, 0x4f, 0x6a, 0x67, - 0x62, 0x73, 0x77, 0x67, 0x62, 0x67, 0x77, 0x48, 0x77, 0x59, 0x44, 0x56, - 0x52, 0x30, 0x6a, 0x42, 0x42, 0x67, 0x77, 0x46, 0x6f, 0x41, 0x55, 0x49, - 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, - 0x37, 0x53, 0x56, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, - 0x71, 0x77, 0x77, 0x0a, 0x55, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x66, - 0x42, 0x45, 0x73, 0x77, 0x53, 0x54, 0x42, 0x48, 0x6f, 0x45, 0x57, 0x67, - 0x51, 0x34, 0x5a, 0x42, 0x61, 0x48, 0x52, 0x30, 0x63, 0x48, 0x4d, 0x36, - 0x4c, 0x79, 0x39, 0x6a, 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, 0x5a, 0x70, - 0x59, 0x32, 0x46, 0x30, 0x5a, 0x58, 0x4d, 0x75, 0x64, 0x48, 0x4a, 0x31, - 0x63, 0x33, 0x52, 0x6c, 0x5a, 0x48, 0x4e, 0x6c, 0x0a, 0x63, 0x6e, 0x5a, - 0x70, 0x59, 0x32, 0x56, 0x7a, 0x4c, 0x6d, 0x6c, 0x75, 0x64, 0x47, 0x56, - 0x73, 0x4c, 0x6d, 0x4e, 0x76, 0x62, 0x53, 0x39, 0x4a, 0x62, 0x6e, 0x52, - 0x6c, 0x62, 0x46, 0x4e, 0x48, 0x57, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x52, - 0x44, 0x51, 0x53, 0x35, 0x6b, 0x5a, 0x58, 0x49, 0x77, 0x48, 0x51, 0x59, - 0x44, 0x56, 0x52, 0x30, 0x4f, 0x42, 0x42, 0x59, 0x45, 0x46, 0x4e, 0x44, - 0x6f, 0x0a, 0x71, 0x74, 0x70, 0x31, 0x31, 0x2f, 0x6b, 0x75, 0x53, 0x52, - 0x65, 0x59, 0x50, 0x48, 0x73, 0x55, 0x5a, 0x64, 0x44, 0x56, 0x38, 0x6c, - 0x6c, 0x4e, 0x4d, 0x41, 0x34, 0x47, 0x41, 0x31, 0x55, 0x64, 0x44, 0x77, - 0x45, 0x42, 0x2f, 0x77, 0x51, 0x45, 0x41, 0x77, 0x49, 0x42, 0x42, 0x6a, - 0x41, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x52, 0x4d, 0x42, 0x41, 0x66, - 0x38, 0x45, 0x43, 0x44, 0x41, 0x47, 0x0a, 0x41, 0x51, 0x48, 0x2f, 0x41, - 0x67, 0x45, 0x41, 0x4d, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, - 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x41, 0x30, 0x67, 0x41, 0x4d, - 0x45, 0x55, 0x43, 0x49, 0x51, 0x43, 0x4a, 0x67, 0x54, 0x62, 0x74, 0x56, - 0x71, 0x4f, 0x79, 0x5a, 0x31, 0x6d, 0x33, 0x6a, 0x71, 0x69, 0x41, 0x58, - 0x4d, 0x36, 0x51, 0x59, 0x61, 0x36, 0x72, 0x35, 0x73, 0x57, 0x53, 0x0a, - 0x34, 0x79, 0x2f, 0x47, 0x37, 0x79, 0x38, 0x75, 0x49, 0x4a, 0x47, 0x78, - 0x64, 0x77, 0x49, 0x67, 0x52, 0x71, 0x50, 0x76, 0x42, 0x53, 0x4b, 0x7a, - 0x7a, 0x51, 0x61, 0x67, 0x42, 0x4c, 0x51, 0x71, 0x35, 0x73, 0x35, 0x41, - 0x37, 0x30, 0x70, 0x64, 0x6f, 0x69, 0x61, 0x52, 0x4a, 0x38, 0x7a, 0x2f, - 0x30, 0x75, 0x44, 0x7a, 0x34, 0x4e, 0x67, 0x56, 0x39, 0x31, 0x6b, 0x3d, - 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, - 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, - 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, - 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6a, - 0x7a, 0x43, 0x43, 0x41, 0x6a, 0x53, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, - 0x67, 0x49, 0x55, 0x49, 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, - 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, 0x56, 0x55, 0x72, 0x39, 0x51, 0x47, - 0x7a, 0x6b, 0x6e, 0x42, 0x71, 0x77, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, - 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x77, 0x0a, - 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, 0x45, - 0x41, 0x77, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, - 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, - 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, - 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, - 0x49, 0x45, 0x4e, 0x76, 0x0a, 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, - 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, - 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, - 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, - 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, - 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x0a, 0x42, 0x67, - 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x42, - 0x34, 0x58, 0x44, 0x54, 0x45, 0x34, 0x4d, 0x44, 0x55, 0x79, 0x4d, 0x54, - 0x45, 0x77, 0x4e, 0x44, 0x55, 0x78, 0x4d, 0x46, 0x6f, 0x58, 0x44, 0x54, - 0x51, 0x35, 0x4d, 0x54, 0x49, 0x7a, 0x4d, 0x54, 0x49, 0x7a, 0x4e, 0x54, - 0x6b, 0x31, 0x4f, 0x56, 0x6f, 0x77, 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, - 0x67, 0x47, 0x0a, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x52, 0x53, - 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, - 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, - 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, - 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x63, - 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x0a, 0x61, 0x57, 0x39, 0x75, - 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, - 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, - 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, - 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, - 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, - 0x0a, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x46, 0x6b, 0x77, 0x45, 0x77, 0x59, - 0x48, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x43, 0x41, 0x51, 0x59, - 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x44, 0x41, 0x51, 0x63, - 0x44, 0x51, 0x67, 0x41, 0x45, 0x43, 0x36, 0x6e, 0x45, 0x77, 0x4d, 0x44, - 0x49, 0x59, 0x5a, 0x4f, 0x6a, 0x2f, 0x69, 0x50, 0x57, 0x73, 0x43, 0x7a, - 0x61, 0x45, 0x4b, 0x69, 0x37, 0x0a, 0x31, 0x4f, 0x69, 0x4f, 0x53, 0x4c, - 0x52, 0x46, 0x68, 0x57, 0x47, 0x6a, 0x62, 0x6e, 0x42, 0x56, 0x4a, 0x66, - 0x56, 0x6e, 0x6b, 0x59, 0x34, 0x75, 0x33, 0x49, 0x6a, 0x6b, 0x44, 0x59, - 0x59, 0x4c, 0x30, 0x4d, 0x78, 0x4f, 0x34, 0x6d, 0x71, 0x73, 0x79, 0x59, - 0x6a, 0x6c, 0x42, 0x61, 0x6c, 0x54, 0x56, 0x59, 0x78, 0x46, 0x50, 0x32, - 0x73, 0x4a, 0x42, 0x4b, 0x35, 0x7a, 0x6c, 0x4b, 0x4f, 0x42, 0x0a, 0x75, - 0x7a, 0x43, 0x42, 0x75, 0x44, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, - 0x53, 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, 0x42, 0x51, 0x69, 0x5a, - 0x51, 0x7a, 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, 0x66, 0x4f, 0x44, 0x74, - 0x4a, 0x56, 0x53, 0x76, 0x31, 0x41, 0x62, 0x4f, 0x53, 0x63, 0x47, 0x72, - 0x44, 0x42, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, - 0x7a, 0x42, 0x4a, 0x0a, 0x4d, 0x45, 0x65, 0x67, 0x52, 0x61, 0x42, 0x44, - 0x68, 0x6b, 0x46, 0x6f, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, 0x76, - 0x4c, 0x32, 0x4e, 0x6c, 0x63, 0x6e, 0x52, 0x70, 0x5a, 0x6d, 0x6c, 0x6a, - 0x59, 0x58, 0x52, 0x6c, 0x63, 0x79, 0x35, 0x30, 0x63, 0x6e, 0x56, 0x7a, - 0x64, 0x47, 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, 0x64, 0x6d, 0x6c, 0x6a, - 0x5a, 0x58, 0x4d, 0x75, 0x61, 0x57, 0x35, 0x30, 0x0a, 0x5a, 0x57, 0x77, - 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, 0x30, 0x6c, 0x75, 0x64, 0x47, 0x56, - 0x73, 0x55, 0x30, 0x64, 0x59, 0x55, 0x6d, 0x39, 0x76, 0x64, 0x45, 0x4e, - 0x42, 0x4c, 0x6d, 0x52, 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, - 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, 0x67, 0x51, 0x55, 0x49, 0x6d, 0x55, - 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, - 0x56, 0x0a, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, 0x71, - 0x77, 0x77, 0x44, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, - 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, 0x41, 0x67, 0x45, 0x47, 0x4d, 0x42, - 0x49, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, 0x2f, 0x77, - 0x51, 0x49, 0x4d, 0x41, 0x59, 0x42, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, - 0x45, 0x77, 0x43, 0x67, 0x59, 0x49, 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, - 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, 0x51, 0x41, 0x77, 0x52, - 0x67, 0x49, 0x68, 0x41, 0x4f, 0x57, 0x2f, 0x35, 0x51, 0x6b, 0x52, 0x2b, - 0x53, 0x39, 0x43, 0x69, 0x53, 0x44, 0x63, 0x4e, 0x6f, 0x6f, 0x77, 0x4c, - 0x75, 0x50, 0x52, 0x4c, 0x73, 0x57, 0x47, 0x66, 0x2f, 0x59, 0x69, 0x37, - 0x47, 0x53, 0x58, 0x39, 0x34, 0x42, 0x67, 0x77, 0x54, 0x77, 0x67, 0x0a, - 0x41, 0x69, 0x45, 0x41, 0x34, 0x4a, 0x30, 0x6c, 0x72, 0x48, 0x6f, 0x4d, - 0x73, 0x2b, 0x58, 0x6f, 0x35, 0x6f, 0x2f, 0x73, 0x58, 0x36, 0x4f, 0x39, - 0x51, 0x57, 0x78, 0x48, 0x52, 0x41, 0x76, 0x5a, 0x55, 0x47, 0x4f, 0x64, - 0x52, 0x51, 0x37, 0x63, 0x76, 0x71, 0x52, 0x58, 0x61, 0x71, 0x49, 0x3d, - 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, - 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x0a, 0x00, - } - - SGXPCKCert = conv([]byte("-----BEGIN CERTIFICATE-----\nMIIEijCCBDKgAwIBAgIUUcfkXVhIadovPuUFjZl2+cqDHxYwCgYIKoZIzj0EAwIwcTEjMCEGA1UEAwwaSW50ZWwgU0dYIFBDSyBQcm9jZXNzb3IgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTIzMDUxMDE5MzAzNFoXDTMwMDUxMDE5MzAzNFowcDEiMCAGA1UEAwwZSW50ZWwgU0dYIFBDSyBDZXJ0aWZpY2F0ZTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT5aL3LqXPoVZE2Kd/2plbjBiHh3/BSLz9WINb9Np8SLG7XQckQY8Ah625qBXigxJV4eWDUa+ggeGwCtROr9iZHo4ICpzCCAqMwHwYDVR0jBBgwFoAU0Oiq2nXX+S5JF5g8exRl0NXyWU0wbAYDVR0fBGUwYzBhoF+gXYZbaHR0cHM6Ly9hcGkudHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9zZ3gvY2VydGlmaWNhdGlvbi92NC9wY2tjcmw/Y2E9cHJvY2Vzc29yJmVuY29kaW5nPWRlcjAdBgNVHQ4EFgQUSCApqBTxIkOtDpSt4T4YItTDOkQwDgYDVR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwggHTBgkqhkiG+E0BDQEEggHEMIIBwDAeBgoqhkiG+E0BDQEBBBBofycWyLUzrk9KRCwHnbIEMIIBYwYKKoZIhvhNAQ0BAjCCAVMwEAYLKoZIhvhNAQ0BAgECAQcwEAYLKoZIhvhNAQ0BAgICAQcwEAYLKoZIhvhNAQ0BAgMCAQAwEAYLKoZIhvhNAQ0BAgQCAQAwEAYLKoZIhvhNAQ0BAgUCAQAwEAYLKoZIhvhNAQ0BAgYCAQAwEAYLKoZIhvhNAQ0BAgcCAQAwEAYLKoZIhvhNAQ0BAggCAQAwEAYLKoZIhvhNAQ0BAgkCAQAwEAYLKoZIhvhNAQ0BAgoCAQAwEAYLKoZIhvhNAQ0BAgsCAQAwEAYLKoZIhvhNAQ0BAgwCAQAwEAYLKoZIhvhNAQ0BAg0CAQAwEAYLKoZIhvhNAQ0BAg4CAQAwEAYLKoZIhvhNAQ0BAg8CAQAwEAYLKoZIhvhNAQ0BAhACAQAwEAYLKoZIhvhNAQ0BAhECAQ0wHwYLKoZIhvhNAQ0BAhIEEAcHAAAAAAAAAAAAAAAAAAAwEAYKKoZIhvhNAQ0BAwQCAAAwFAYKKoZIhvhNAQ0BBAQGAHBqEAAAMA8GCiqGSIb4TQENAQUKAQAwCgYIKoZIzj0EAwIDRgAwQwIgW5VgrBqux967AZR/i3579JUD3Pc3V8+S1+DyzQ9Kl2kCHzICpI02b3AWrjowye4+pSvYyOYPjyEsC1fDp3gMCIU=\n-----END CERTIFICATE-----")) - - SGXPCKProcessorCaCert = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICmDCCAj6gAwIBAgIVANDoqtp11/kuSReYPHsUZdDV8llNMAoGCCqGSM49BAMCMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHExIzAhBgNVBAMMGkludGVsIFNHWCBQQ0sgUHJvY2Vzc29yIENBMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABL9q+NMp2IOgtdl1bk/uWZ5+TGQm8aCi8z78fs+fKCQ3d+uDzXnVTAT2ZhDCifyIuJwvN3wNBp9iHBSSMJMJrBOjgbswgbgwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0fBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFNDoqtp11/kuSReYPHsUZdDV8llNMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMAoGCCqGSM49BAMCA0gAMEUCIQCJgTbtVqOyZ1m3jqiAXM6QYa6r5sWS4y/G7y8uIJGxdwIgRqPvBSKzzQagBLQq5s5A70pdoiaRJ8z/0uDz4NgV91k=\n-----END CERTIFICATE-----")) - - SGXRootCaCert = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIwaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi71OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SVUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYIKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwgAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----")) - - TCBSigningCert = conv([]byte("-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIwaDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwGA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGvP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2ljuypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0fBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlKQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAjftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----")) - - validSGXNonce = []byte("1234567") - invalidSGXNonce = []byte("7654321") - // hash of enclave measurements, part of the enclave (MRENCLAVE) - validSGXMeasurement = []byte{0x08, 0x75, 0x5c, 0xd5, 0x7f, 0x2c, 0x04, 0xb8, 0x45, 0xc4, 0x91, 0x25, 0x77, 0xd6, 0x9a, 0xf3, 0xf9, 0x6c, 0xb7, 0xe3, 0xea, 0xda, 0x57, 0xd1, 0xf9, 0xa7, 0xc6, 0x25, 0xf6, 0xa6, 0x23, 0x3c} - invalidSGXMeasurement = []byte{0x00, 0x75, 0x5c, 0xd5, 0x7f, 0x2c, 0x04, 0xb8, 0x45, 0xc4, 0x91, 0x25, 0x77, 0xd6, 0x9a, 0xf3, 0xf9, 0x6c, 0xb7, 0xe3, 0xea, 0xda, 0x57, 0xd1, 0xf9, 0xa7, 0xc6, 0x25, 0xf6, 0xa6, 0x23, 0x3c} - - validSGXCertChain = [][]byte{TCBSigningCert.Raw, SGXPCKCert.Raw, SGXPCKProcessorCaCert.Raw, SGXRootCaCert.Raw} - - rootCACertFingerprint = "44A0196B2B99F889B8E149E95B807A350E7424964399E885A7CBB8CCFAB674D3" - - // valid collateral - tee_type = uint32(0) - tcb_info = []byte(`{"tcbInfo":{"id":"SGX","version":3,"issueDate":"2023-11-22T19:46:39Z","nextUpdate":"2023-12-22T19:46:39Z","fmspc":"00706a100000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":16,"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":8},{"svn":8},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":7},{"svn":7},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2022-11-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":5},{"svn":5},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":4},{"svn":4},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-06-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":3},{"svn":3},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":10},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":9},"tcbDate":"2020-06-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":7},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":6},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":1},{"svn":1},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":4},"tcbDate":"2017-07-26T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00088","INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]}]},"signature":"3320aa700f3bf4a8cd4d8ed2928b400e7757240f518b57a595a091eafebe3b84ed618dfde6f5f502feccc5084c09a39e2b8fb2c6620705c1eeebc39eaa43eb15"}`) - tcb_info_size = uint32(4390) - qe_identity = []byte(`{"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-11-22T19:39:39Z","nextUpdate":"2023-12-22T19:39:39Z","tcbEvaluationDataNumber":16,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"3985e4e7996247d7ca663365bd2092a2cae84b0732d50e6b6ccff86ada831c8ef16b155883c321b88271815c84cb6bdcd6e9c5f34787b31f4993ac9a15125b8e"}`) - qe_identity_size = uint32(1381) - - // old/invalid collateral - tcb_info_old = []byte(`{"tcbInfo":{"id":"SGX","version":3,"issueDate":"2023-05-20T23:45:45Z","nextUpdate":"2023-06-19T23:45:45Z","fmspc":"00706A100000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":15,"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":8},{"svn":8},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":7},{"svn":7},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13},"tcbDate":"2022-11-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":5},{"svn":5},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":4},{"svn":4},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11},"tcbDate":"2021-06-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":3},{"svn":3},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":10},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":9},"tcbDate":"2020-06-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":7},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":2},{"svn":2},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":6},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":1},{"svn":1},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]},{"tcb":{"sgxtcbcomponents":[{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":4},"tcbDate":"2017-07-26T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00088","INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00465","INTEL-SA-00477","INTEL-SA-00528","INTEL-SA-00617","INTEL-SA-00657","INTEL-SA-00767"]}]},"signature":"0f0dcda69af0014b69e2af1a826d5cf5caee7fdcac2ec10740d7b6caedf04871005976e4cc0803bc50e824fb23c82b21078da45d867c30925a56e6d23fe53119"}`) - tcb_info_size_old = uint32(4391) - qe_identity_old = []byte(`{"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-05-20T23:50:17Z","nextUpdate":"2023-06-19T23:50:17Z","tcbEvaluationDataNumber":15,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"d2a951b5a145f82b089903cc4747c28edf9d9a63b55a043ff2e7a93ecb339bb0b935d4ad516e3e91e57a00d3f913769d60dddd1ed54e242ea9365c9ca5d280ed"}`) - qe_identity_size_old = uint32(1381) - - sgx_extensions_short = []byte{0x30, 0x1E, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x01, 0x0D, 0x01, 0x01, 0x04, 0x10, 0x68, 0x7F, 0x27, 0x16, 0xC8, 0xB5, 0x33, 0xAE, 0x4F, 0x4A, 0x44, 0x2C, 0x07, 0x9D, 0xB2, 0x04} - sgx_extensions, _ = hex.DecodeString("301E060A2A864886F84D010D01010410687F2716C8B533AE4F4A442C079DB20430820163060A2A864886F84D010D0102308201533010060B2A864886F84D010D0102010201073010060B2A864886F84D010D0102020201073010060B2A864886F84D010D0102030201003010060B2A864886F84D010D0102040201003010060B2A864886F84D010D0102050201003010060B2A864886F84D010D0102060201003010060B2A864886F84D010D0102070201003010060B2A864886F84D010D0102080201003010060B2A864886F84D010D0102090201003010060B2A864886F84D010D01020A0201003010060B2A864886F84D010D01020B0201003010060B2A864886F84D010D01020C0201003010060B2A864886F84D010D01020D0201003010060B2A864886F84D010D01020E0201003010060B2A864886F84D010D01020F0201003010060B2A864886F84D010D0102100201003010060B2A864886F84D010D01021102010D301F060B2A864886F84D010D0102120410070700000000000000000000000000003010060A2A864886F84D010D0103040200003014060A2A864886F84D010D0104040600706A100000300F060A2A864886F84D010D01050A0100") - - validSGXVersion uint16 = 0x03 - validSGXAttributes [16]byte = [16]byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - invalidSGXAttributes [16]byte = [16]byte{0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - validIsvProdId uint16 = 0x00 - validIsvSvn uint16 = 0 - validMRSIGNER = "37E0543F5597B0F0E028FA18955E1307CB7A8CF54B37F513FF64961EADEF94C4" - invalidMRSIGNER = "2101a93F5597B0F0E028FA18955E1307CB7A8CF54B37F513FF64961EADEF94C4" -) - -func Test_verifySgxMeasurements(t *testing.T) { - type args struct { - sgxM *SgxMeasurement - sgxV []ReferenceValue - nonce []byte - } - tests := []struct { - name string - args args - want bool - }{ - // TODO: Add more test cases - { - name: "Valid Attestation Report", - args: args{ - sgxM: &SgxMeasurement{ - Type: "SGX Measurement", - Report: validSGXQuote, - Certs: validSGXCertChain, - }, - sgxV: []ReferenceValue{ - { - Type: "SGX Reference Value", - Sha256: validSGXMeasurement, - Sgx: &SGXDetails{ - Version: validSGXVersion, - Collateral: SGXCollateral{ - TeeType: tee_type, - TcbInfo: tcb_info, - TcbInfoSize: tcb_info_size, - QeIdentity: qe_identity, - QeIdentitySize: qe_identity_size, - }, - CaFingerprint: rootCACertFingerprint, - Attributes: validSGXAttributes, - IsvProdId: validIsvProdId, - IsvSvn: validIsvSvn, - MrSigner: validMRSIGNER, - }, - }, - }, - nonce: validSGXNonce, - }, - want: true, - }, - // { - // name: "Invalid Tcb Info", - // args: args{ - // sgxM: &SgxMeasurement{ - // Type: "SGX Measurement", - // Report: validSGXQuote, - // Certs: validSGXCertChain, - // }, - // sgxV: []ReferenceValue{ - // { - // Type: "SGX Reference Value", - // Sha256: validSGXMeasurement, - // Sgx: &SGXDetails{ - // Version: validSGXVersion, - // Collateral: SGXCollateral{ - // TeeType: tee_type, - // TcbInfo: tcb_info_old, - // TcbInfoSize: tcb_info_size_old, - // QeIdentity: qe_identity, - // QeIdentitySize: qe_identity_size, - // }, - // CAfingerprint: rootCACertFingerprint, - // Attributes: validSGXAttributes, - // IsvProdId: validIsvProdId, - // MRSIGNER: validMRSIGNER, - // }, - // }, - // }, - // nonce: validSGXNonce, - // }, - // want: false, - // }, - // { - // name: "Invalid QE Identity", - // args: args{ - // sgxM: &SgxMeasurement{ - // Type: "SGX Measurement", - // Report: validSGXQuote, - // Certs: validSGXCertChain, - // }, - // sgxV: []ReferenceValue{ - // { - // Type: "SGX Reference Value", - // Sha256: validSGXMeasurement, - // Sgx: &SGXDetails{ - // Version: validSGXVersion, - // Collateral: SGXCollateral{ - // TeeType: tee_type, - // TcbInfo: tcb_info, - // TcbInfoSize: tcb_info_size, - // QeIdentity: qe_identity_old, - // QeIdentitySize: qe_identity_size_old, - // }, - // CAfingerprint: rootCACertFingerprint, - // Attributes: validSGXAttributes, - // IsvProdId: validIsvProdId, - // MRSIGNER: validMRSIGNER, - // }, - // }, - // }, - // nonce: validSGXNonce, - // }, - // want: false, - // }, - // { - // name: "Invalid Measurement", - // args: args{ - // sgxM: &SgxMeasurement{ - // Type: "SGX Measurement", - // Report: validSGXQuote, - // Certs: validSGXCertChain, - // }, - // sgxV: []ReferenceValue{ - // { - // Type: "SGX Reference Value", - // Sha256: invalidSGXMeasurement, - // Sgx: &SGXDetails{ - // Version: validSGXVersion, - // Collateral: SGXCollateral{ - // TeeType: tee_type, - // TcbInfo: tcb_info, - // TcbInfoSize: tcb_info_size, - // QeIdentity: qe_identity, - // QeIdentitySize: qe_identity_size, - // }, - // CAfingerprint: rootCACertFingerprint, - // Attributes: validSGXAttributes, - // IsvProdId: validIsvProdId, - // MRSIGNER: validMRSIGNER, - // }, - // }, - // }, - // nonce: validSGXNonce, - // }, - // want: false, - // }, - // { - // name: "Wrong Attributes", - // args: args{ - // sgxM: &SgxMeasurement{ - // Type: "SGX Measurement", - // Report: validSGXQuote, - // Certs: validSGXCertChain, - // }, - // sgxV: []ReferenceValue{ - // { - // Type: "SGX Reference Value", - // Sha256: validSGXMeasurement, - // Sgx: &SGXDetails{ - // Version: validSGXVersion, - // Collateral: SGXCollateral{ - // TeeType: tee_type, - // TcbInfo: tcb_info, - // TcbInfoSize: tcb_info_size, - // QeIdentity: qe_identity, - // QeIdentitySize: qe_identity_size, - // }, - // CAfingerprint: rootCACertFingerprint, - // Attributes: invalidSGXAttributes, - // IsvProdId: validIsvProdId, - // MRSIGNER: validMRSIGNER, - // }, - // }, - // }, - // nonce: validSGXNonce, - // }, - // want: false, - // }, - // { - // name: "Invalid Nonce", - // args: args{ - // sgxM: &SgxMeasurement{ - // Type: "SGX Measurement", - // Report: validSGXQuote, - // Certs: validSGXCertChain, - // }, - // sgxV: []ReferenceValue{ - // { - // Type: "SGX Reference Value", - // Sha256: validSGXMeasurement, - // Sgx: &SGXDetails{ - // Version: validSGXVersion, - // Collateral: SGXCollateral{ - // TeeType: tee_type, - // TcbInfo: tcb_info, - // TcbInfoSize: tcb_info_size, - // QeIdentity: qe_identity, - // QeIdentitySize: qe_identity_size, - // }, - // CAfingerprint: rootCACertFingerprint, - // Attributes: validSGXAttributes, - // IsvProdId: validIsvProdId, - // MRSIGNER: validMRSIGNER, - // }, - // }, - // }, - // nonce: invalidSGXNonce, - // }, - // want: false, - // }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - res, got := verifySgxMeasurements(tt.args.sgxM, tt.args.nonce, tt.args.sgxV) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("verifySgxMeasurements() got = %v, want %v", got, tt.want) - } - if !got { - fmt.Println(res) - } - }) - } -} - -func TestParseSGXExtensions(t *testing.T) { - type args struct { - extensions []byte - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "Test parse SGX Extensions", - args: args{ - extensions: sgx_extensions, - }, - wantErr: false, - }, - { - name: "Test parse SGX Extensions to short", - args: args{ - extensions: sgx_extensions_short, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := ParseSGXExtensions(tt.args.extensions) - if (err != nil) != tt.wantErr { - t.Errorf("ParseSGXExtensions() error = %v, wantErr %v", err, tt.wantErr) - fmt.Println(got) - return - } - }) - } -} diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 2f66367e..733544a7 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -56,7 +56,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ } // TODO: support other report types - tdxQuote, err := DecodeTdxReportV4(tdxM.Report) + tdxQuote, err := decodeTdxReportV4(tdxM.Report) if err != nil { msg := fmt.Sprintf("Failed to decode TDX report: %v", err) result.Summary.setFalse(&msg) @@ -85,7 +85,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ } // parse reference cert chain (TCBSigningCert chain) - referenceCerts, err := ParseCertificates(tdxM.Certs, true) + referenceCerts, err := parseCertificates(tdxM.Certs, true) if err != nil || referenceCerts.TCBSigningCert == nil || referenceCerts.RootCACert == nil { msg := fmt.Sprintf("Failed to parse reference certificates (TCBSigningCert + IntelRootCACert): %v", err) result.Summary.setFalse(&msg) @@ -100,7 +100,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ } // Parse and verify PCK certificate extensions - sgxExtensions, err := ParseSGXExtensions(quoteCerts.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant) + sgxExtensions, err := parseSGXExtensions(quoteCerts.PCKCert.Extensions[SGX_EXTENSION_INDEX].Value[4:]) // skip the first value (not relevant) if err != nil { msg := fmt.Sprintf("failed to parse SGX Extensions from PCK Certificate: %v", err) result.Summary.setFalse(&msg) @@ -108,7 +108,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ } // Parse and verify TcbInfo object - tcbInfo, err := ParseTcbInfo(tdxReferenceValue.Tdx.Collateral.TcbInfo) + tcbInfo, err := parseTcbInfo(tdxReferenceValue.Tdx.Collateral.TcbInfo) if err != nil { msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) result.Summary.setFalse(&msg) @@ -125,7 +125,7 @@ func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues [ } // Parse and verify QE Identity object - qeIdentity, err := ParseQEIdentity(tdxReferenceValue.Tdx.Collateral.QeIdentity) + qeIdentity, err := parseQEIdentity(tdxReferenceValue.Tdx.Collateral.QeIdentity) if err != nil { msg := fmt.Sprintf("Failed to parse tcbInfo: %v", err) result.Summary.setFalse(&msg) diff --git a/attestationreport/tdx_struct.go b/attestationreport/tdx_struct.go index 18fa4fd1..24b446a9 100644 --- a/attestationreport/tdx_struct.go +++ b/attestationreport/tdx_struct.go @@ -69,7 +69,7 @@ type QEReportCertDataV4 struct { } // Parses the report into the TDReport structure -func DecodeTdxReportV4(report []byte) (TdxReportV4, error) { +func decodeTdxReportV4(report []byte) (TdxReportV4, error) { var reportStruct TdxReportV4 var header QuoteHeader var body TdxReportBody @@ -182,7 +182,7 @@ func parseECDSASignatureV4(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStr } // parse PCK Cert Chain (PCK Leaf Cert || Intermediate CA Cert || Root CA Cert) - certChain, err := ParseCertificates(tmp[:], true) + certChain, err := parseCertificates(tmp[:], true) if err != nil { return fmt.Errorf("failed to parse certificate chain from QECertData: %v", err) } From a66f55562d3c5161b7e998dd18ffec19cb9662e1 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Fri, 24 Nov 2023 00:07:40 +0000 Subject: [PATCH 19/23] removed sgx structs --- attestationreport/attestationreport.go | 39 +++++---------------- attestationreport/attestationreport_test.go | 2 +- attestationreport/tdx_test.go | 12 +++---- attestationreport/validationreport.go | 14 -------- 4 files changed, 16 insertions(+), 51 deletions(-) diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index a446c58e..a55af2a6 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -154,14 +154,6 @@ type TdxMeasurement struct { Certs [][]byte `json:"certs" cbor:"2,keyasint"` } -// SgxMeasurement represents the attestation report -// element of type 'SGX Measurement' signed by the device -type SgxMeasurement struct { - Type string `json:"type" cbor:"0,keyasint"` - Report []byte `json:"blob" cbor:"1,keyasint"` - Certs [][]byte `json:"certs" cbor:"2,keyasint"` -} - type SnpPolicy struct { Type string `json:"type" cbor:"0,keyasint"` SingleSocket bool `json:"singleSocket" cbor:"1,keyasint"` @@ -193,7 +185,7 @@ type SnpDetails struct { Tcb SnpTcb `json:"tcb" cbor:"4,keyasint"` } -type SGXCollateral struct { +type IntelCollateral struct { // Format of CRLs: // version 1.0: PEM, v3.0: DER base16, v3.1: DER raw binary TeeType uint32 `json:"teeType" cbor:"0,keyasint"` @@ -203,24 +195,14 @@ type SGXCollateral struct { QeIdentitySize uint32 `json:"qeIdentitySize" cbor:"4,keyasint"` } -type SGXDetails struct { - Version uint16 `json:"version" cbor:"0,keyasint"` - Collateral SGXCollateral `json:"collateral" cbor:"1,keyasint"` - CaFingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint - Attributes [16]byte `json:"attributes" cbor:"3,keyasint"` - IsvProdId uint16 `json:"isvProdId" cbor:"4,keyasint"` - MrSigner string `json:"mrSigner" cbor:"5,keyasint"` - IsvSvn uint16 `json:"isvSvn" cbor:"6,keyasint"` -} - type TDXDetails struct { - Version uint16 `json:"version" cbor:"0,keyasint"` - Collateral SGXCollateral `json:"collateral" cbor:"1,keyasint"` - CaFingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint - TdId TDId `json:"tdId" cbor:"3,keyasint"` - TdAttributes [8]byte `json:"tdAttributes" cbor:"4,keyasint"` - Xfam [8]byte `json:"xfam" cbor:"5,keyasint"` - MrSeam string `json:"mrseam" cbor:"6,keyasint"` + Version uint16 `json:"version" cbor:"0,keyasint"` + Collateral IntelCollateral `json:"collateral" cbor:"1,keyasint"` + CaFingerprint string `json:"caFingerprint" cbor:"2,keyasint"` // Intel Root CA Certificate Fingerprint + TdId TDId `json:"tdId" cbor:"3,keyasint"` + TdAttributes [8]byte `json:"tdAttributes" cbor:"4,keyasint"` + Xfam [8]byte `json:"xfam" cbor:"5,keyasint"` + MrSeam string `json:"mrseam" cbor:"6,keyasint"` } // Note: MrTd is already given in ReferenceValue.Sha256 @@ -235,7 +217,7 @@ type TDId struct { } // ReferenceValue represents the attestation report -// element of types 'SNP Reference Value', 'TPM Reference Value', 'SGX Reference Value', TDX Reference Value' +// element of types 'SNP Reference Value', 'TPM Reference Value', TDX Reference Value' // and 'SW Reference Value' type ReferenceValue struct { Type string `json:"type" cbor:"0,keyasint"` @@ -244,7 +226,6 @@ type ReferenceValue struct { Name string `json:"name,omitempty" cbor:"3,keyasint,omitempty"` Pcr *int `json:"pcr,omitempty" cbor:"4,keyasint,omitempty"` Snp *SnpDetails `json:"snp,omitempty" cbor:"5,keyasint,omitempty"` - Sgx *SGXDetails `json:"sgx,omitempty" cbor:"7,keyasint,omitempty"` Tdx *TDXDetails `json:"tdx,omitempty" cbor:"8,keyasint,omitempty"` Description string `json:"description,omitempty" cbor:"9,keyasint,omitempty"` } @@ -367,7 +348,6 @@ type ArPlain struct { Type string `json:"type" cbor:"0,keyasint"` TpmM *TpmMeasurement `json:"tpmMeasurement,omitempty" cbor:"1,keyasint,omitempty"` SnpM *SnpMeasurement `json:"snpMeasurement,omitempty" cbor:"2,keyasint,omitempty"` - SgxM *SgxMeasurement `json:"sgxMeasurement,omitempty" cbor:"11,keyasint,omitempty"` TdxM *TdxMeasurement `json:"tdxMeasurement,omitempty" cbor:"12,keyasint,omitempty"` IasM *IasMeasurement `cbor:"10,keyasint,omitempty"` SWM []SwMeasurement `json:"swMeasurements,omitempty" cbor:"3,keyasint,omitempty"` @@ -385,7 +365,6 @@ type ArPacked struct { Type string `json:"type" cbor:"0,keyasint"` TpmM *TpmMeasurement `json:"tpmMeasurement,omitempty" cbor:"1,keyasint,omitempty"` SnpM *SnpMeasurement `json:"snpMeasurement,omitempty" cbor:"2,keyasint,omitempty"` - SgxM *SgxMeasurement `json:"sgxMeasurement,omitempty" cbor:"10,keyasint,omitempty"` TdxM *TdxMeasurement `json:"tdxMeasurement,omitempty" cbor:"11,keyasint,omitempty"` SWM []SwMeasurement `json:"swMeasurements,omitempty" cbor:"3,keyasint,omitempty"` RtmManifest []byte `json:"rtmManifests" cbor:"4,keyasint"` diff --git a/attestationreport/attestationreport_test.go b/attestationreport/attestationreport_test.go index 1b28a0b7..e28de5d3 100644 --- a/attestationreport/attestationreport_test.go +++ b/attestationreport/attestationreport_test.go @@ -275,7 +275,7 @@ var ( Sha256: validTDXMeasurement, Tdx: &TDXDetails{ Version: 0x04, - Collateral: SGXCollateral{ + Collateral: IntelCollateral{ TeeType: tee_type_tdx, TcbInfo: tcb_info_tdx, TcbInfoSize: tcb_info_tdx_size, diff --git a/attestationreport/tdx_test.go b/attestationreport/tdx_test.go index 3eb18cd1..3b51de41 100644 --- a/attestationreport/tdx_test.go +++ b/attestationreport/tdx_test.go @@ -492,7 +492,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { Sha256: validTDXMeasurement, Tdx: &TDXDetails{ Version: 0x04, - Collateral: SGXCollateral{ + Collateral: IntelCollateral{ TeeType: tee_type_tdx, TcbInfo: tcb_info_tdx, TcbInfoSize: tcb_info_tdx_size, @@ -533,7 +533,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { Sha256: validTDXMeasurement, Tdx: &TDXDetails{ Version: 0x04, - Collateral: SGXCollateral{ + Collateral: IntelCollateral{ TeeType: tee_type_tdx, TcbInfo: tcb_info_tdx, TcbInfoSize: tcb_info_tdx_size, @@ -565,7 +565,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { Sha256: validTDXMeasurement, Tdx: &TDXDetails{ Version: 0x04, - Collateral: SGXCollateral{ + Collateral: IntelCollateral{ TeeType: tee_type_tdx, TcbInfo: tcb_info_tdx, TcbInfoSize: tcb_info_tdx_size, @@ -606,7 +606,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { Sha256: []byte("12345"), Tdx: &TDXDetails{ Version: 0x04, - Collateral: SGXCollateral{ + Collateral: IntelCollateral{ TeeType: tee_type_tdx, TcbInfo: tcb_info_tdx, TcbInfoSize: tcb_info_tdx_size, @@ -647,7 +647,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { Sha256: validTDXMeasurement, Tdx: &TDXDetails{ Version: 0x04, - Collateral: SGXCollateral{ + Collateral: IntelCollateral{ TeeType: tee_type_tdx, TcbInfo: tcb_info_tdx, TcbInfoSize: tcb_info_tdx_size, @@ -688,7 +688,7 @@ func Test_verifyTdxMeasurements(t *testing.T) { Sha256: validTDXMeasurement, Tdx: &TDXDetails{ Version: 0x04, - Collateral: SGXCollateral{ + Collateral: IntelCollateral{ TeeType: tee_type_tdx, TcbInfo: []byte{}, TcbInfoSize: tcb_info_tdx_size, diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index e7614602..6fa21a0b 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -74,7 +74,6 @@ type MeasurementResult struct { SnpMeasResult *SnpMeasurementResult `json:"snp,omitempty"` IasMeasResult *IasMeasurementResult `json:"ias,omitempty"` SwMeasResult []SwMeasurementResult `json:"sw,omitempty"` - SgxMeasResult *SgxMeasurementResult `json:"sgx,omitempty"` TdxMeasResult *TdxMeasurementResult `json:"tdx,omitempty"` } @@ -174,19 +173,6 @@ type SnpMeasurementResult struct { PolicyCheck PolicyCheck `json:"policyCheck"` } -// SgxMeasurementResult represents the results for the verification -// of Intel SGX measurements. -type SgxMeasurementResult struct { - Summary Result `json:"resultSummary"` - Freshness Result `json:"freshness"` - Signature SignatureResult `json:"signature"` - Artifacts []DigestResult `json:"artifacts"` - VersionMatch Result `json:"reportVersionMatch"` - TcbInfoCheck TcbLevelResult `json:"tcbInfoCheck"` - QeIdentityCheck TcbLevelResult `json:"qeIdentityCheck"` - ExtendedQuoteCheck ExtendedQuoteCheck `json:"extendedQuoteCheck"` -} - // TdxMeasurementResult represents the results for the verification // of Intel TDX measurements. type TdxMeasurementResult struct { From f7c0439c17c7052e9ef3addfac62f3558369f8d5 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Mon, 27 Nov 2023 11:01:45 +0000 Subject: [PATCH 20/23] implemented more tests --- attestationreport/attestationreport_test.go | 188 ++++++++++---------- attestationreport/intel_helpers.go | 2 +- 2 files changed, 95 insertions(+), 95 deletions(-) diff --git a/attestationreport/attestationreport_test.go b/attestationreport/attestationreport_test.go index e28de5d3..6233cc38 100644 --- a/attestationreport/attestationreport_test.go +++ b/attestationreport/attestationreport_test.go @@ -158,79 +158,6 @@ func createCertsAndKeys() (*ecdsa.PrivateKey, []*x509.Certificate, error) { return priv, []*x509.Certificate{leaf, ca}, nil } -func TestVerify(t *testing.T) { - type args struct { - serializer Serializer - } - tests := []struct { - name string - args args - want VerificationResult - }{ - { - name: "Empty Attestation Report JSON", - args: args{ - serializer: JsonSerializer{}, - }, - want: VerificationResult{Success: false}, - }, - { - name: "Empty Attestation Report CBOR", - args: args{ - serializer: CborSerializer{}, - }, - want: VerificationResult{Success: false}, - }, - } - - // Setup logger - logrus.SetLevel(logrus.TraceLevel) - - // Setup Test Keys and Certificates - log.Trace("Creating Keys and Certificates") - key, certchain, err := createCertsAndKeys() - if err != nil { - log.Errorf("Internal Error: Failed to create testing certs and keys: %v", err) - return - } - - swSigner := &SwSigner{ - priv: key, - certChain: certchain, - } - - // Perform unit tests - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - // Preparation: Create nonce - nonce := make([]byte, 8) - rand.Read(nonce) - - // Preparation: Generate and sign attestation report - var metadata [][]byte - a, err := Generate(nonce, metadata, []Driver{}, tt.args.serializer) - if err != nil { - t.Errorf("Preparation failed: %v", err) - } - ar, err := Sign(a, swSigner, tt.args.serializer) - if err != nil { - t.Errorf("Internal Error: Failed to sign Attestion Report: %v", err) - } - - // Run FUT - got := Verify( - ar, nonce, - internal.WriteCertPem(certchain[len(certchain)-1]), - nil, 0) - if got.Success != tt.want.Success { - t.Errorf("Result.Success = %v, want %v", got.Success, tt.want.Success) - } - fmt.Println(got) - }) - } -} - func Test_collectReferenceValues(t *testing.T) { type args struct { ar *ArPlain @@ -315,6 +242,22 @@ var ( ReferenceValues: validTdxReferenceValue, } + emptyTdxRtmManifest = RtmManifest{ + MetaInfo: MetaInfo{ + Type: "RTM Manifest", + Name: "de.test.rtm", + Version: "2023-04-10T20:00:00Z", + }, + DevCommonName: "Test Developer", + Validity: Validity{ + NotBefore: "2023-04-10T20:00:00Z", + NotAfter: "2026-04-10T20:00:00Z", + }, + Description: "de.test.rtm", + CertificationLevel: 3, + ReferenceValues: []ReferenceValue{}, + } + validTdxOsManifest = OsManifest{ MetaInfo: MetaInfo{ Type: "OS Manifest", @@ -333,6 +276,24 @@ var ( }, } + invalidTdxOsManifest = OsManifest{ + MetaInfo: MetaInfo{ + Type: "OS Manifest", + Name: "de.test.os", + Version: "2023-04-10T20:00:00Z", + }, + DevCommonName: "Test Developer", + Validity: Validity{ + NotBefore: "2023-04-10T20:00:00Z", + NotAfter: "2026-04-10T20:00:00Z", + }, + Description: "PoC Fraunhofer AISEC TDX Report", + CertificationLevel: 3, + Rtms: []string{ + "INVALID", + }, + } + validTdxDeviceDescription = DeviceDescription{ MetaInfo: MetaInfo{ Type: "Device Description", @@ -343,9 +304,20 @@ var ( RtmManifest: "de.test.rtm", OsManifest: "de.test.os", } + + invalidTdxDeviceDescription = DeviceDescription{ + MetaInfo: MetaInfo{ + Type: "Device Description", + Name: "test-device.test.de", + Version: "2023-04-10T20:00:00Z", + }, + Location: "Munich, Germany", + RtmManifest: "INVALID", + OsManifest: "INVALID", + } ) -func Test_verifyTdxReport(t *testing.T) { +func TestVerify(t *testing.T) { type args struct { tdxMeas *TdxMeasurement serializer Serializer @@ -357,7 +329,7 @@ func Test_verifyTdxReport(t *testing.T) { tests := []struct { name string args args - want bool + want VerificationResult }{ { name: "Valid TDX Report JSON", @@ -373,7 +345,7 @@ func Test_verifyTdxReport(t *testing.T) { deviceDescription: validTdxDeviceDescription, nonce: validTDXNonce, }, - want: true, + want: VerificationResult{Success: true}, }, { name: "Valid TDX Report CBOR", @@ -389,26 +361,38 @@ func Test_verifyTdxReport(t *testing.T) { deviceDescription: validTdxDeviceDescription, nonce: validTDXNonce, }, - want: true, + want: VerificationResult{Success: true}, }, { - name: "Invalid TDX Certificate Chain", + name: "Nonce mismatch", args: args{ tdxMeas: &TdxMeasurement{ Type: "TDX Measurement", Report: aisecTDXQuote, - Certs: [][]byte{root_ca_cert_tdx.Raw}, + Certs: validTDXCertChain, }, serializer: JsonSerializer{}, rtmManifest: validTdxRtmManifest, osManifest: validTdxOsManifest, deviceDescription: validTdxDeviceDescription, + nonce: []byte{}, + }, + want: VerificationResult{Success: false}, + }, + { + name: "Invalid Certification Level", + args: args{ + tdxMeas: nil, + serializer: JsonSerializer{}, + rtmManifest: emptyTdxRtmManifest, + osManifest: validTdxOsManifest, + deviceDescription: validTdxDeviceDescription, nonce: validTDXNonce, }, - want: false, + want: VerificationResult{Success: false}, }, { - name: "Invalid Nonce", + name: "Invalid Device Description", args: args{ tdxMeas: &TdxMeasurement{ Type: "TDX Measurement", @@ -418,10 +402,26 @@ func Test_verifyTdxReport(t *testing.T) { serializer: JsonSerializer{}, rtmManifest: validTdxRtmManifest, osManifest: validTdxOsManifest, + deviceDescription: invalidTdxDeviceDescription, + nonce: validTDXNonce, + }, + want: VerificationResult{Success: false}, + }, + { + name: "Incompatible RTM/OS Manifests", + args: args{ + tdxMeas: &TdxMeasurement{ + Type: "TDX Measurement", + Report: aisecTDXQuote, + Certs: validTDXCertChain, + }, + serializer: JsonSerializer{}, + rtmManifest: validTdxRtmManifest, + osManifest: invalidTdxOsManifest, deviceDescription: validTdxDeviceDescription, - nonce: []byte{}, + nonce: validTDXNonce, }, - want: false, + want: VerificationResult{Success: false}, }, } @@ -444,7 +444,7 @@ func Test_verifyTdxReport(t *testing.T) { t.Run(tt.name, func(t *testing.T) { s := tt.args.serializer - // Generating a TDX Report + // Preparation: Generate a TDX Report log.Trace("Generating a TDX Report") ar := ArPacked{ @@ -453,7 +453,7 @@ func Test_verifyTdxReport(t *testing.T) { TdxM: tt.args.tdxMeas, } - // create signed manifests and deviceDescription + // Preparation: create signed manifests and deviceDescription rtmManifest, err := s.Marshal(tt.args.rtmManifest) if err != nil { t.Errorf("failed to marshal the RtmManifest: %v", err) @@ -484,21 +484,21 @@ func Test_verifyTdxReport(t *testing.T) { t.Errorf("failed to marshal the Attestation Report: %v", err) } - // sign the report + // Preparation: Sign the report arSigned, err := Sign(report, swSigner, s) if err != nil { t.Errorf("Internal Error: Failed to sign Attestion Report: %v", err) } - // call Verifier - res := Verify(arSigned, validTDXNonce, - internal.WriteCertPem(certchain[len(certchain)-1]), nil, 0) - - if res.Success != tt.want { - t.Errorf("verifyAr() wrong result. expected: %v, got: %v", tt.want, res.Success) + // Run FUT + got := Verify( + arSigned, tt.args.nonce, + internal.WriteCertPem(certchain[len(certchain)-1]), + nil, 0) + if got.Success != tt.want.Success { + t.Errorf("Result.Success = %v, want %v", got.Success, tt.want.Success) } - - fmt.Println(res) + fmt.Println(got) }) } } diff --git a/attestationreport/intel_helpers.go b/attestationreport/intel_helpers.go index 04d0a3e9..68b412fc 100644 --- a/attestationreport/intel_helpers.go +++ b/attestationreport/intel_helpers.go @@ -571,7 +571,7 @@ func downloadCRL(uri string, name string) (*x509.RevocationList, error) { } // Store CRL in file - err = os.WriteFile("./cache/"+name, crlData, 0644) + err = os.WriteFile(name, crlData, 0644) if err != nil { return nil, err } From b6516fa40c7698b2e2cbd5a5b2771b43133000b6 Mon Sep 17 00:00:00 2001 From: CodingChrisIO Date: Mon, 27 Nov 2023 11:24:46 +0000 Subject: [PATCH 21/23] combined files --- attestationreport/attestationreport_test.go | 247 +++++------ attestationreport/intel_helpers.go | 416 ++++++++++++++++++ attestationreport/intel_structs.go | 445 -------------------- attestationreport/tdx.go | 171 ++++++++ attestationreport/tdx_struct.go | 192 --------- 5 files changed, 711 insertions(+), 760 deletions(-) delete mode 100644 attestationreport/intel_structs.go delete mode 100644 attestationreport/tdx_struct.go diff --git a/attestationreport/attestationreport_test.go b/attestationreport/attestationreport_test.go index 6233cc38..964ca88f 100644 --- a/attestationreport/attestationreport_test.go +++ b/attestationreport/attestationreport_test.go @@ -66,6 +66,129 @@ var ( } ) +// variables for TestVerify +var ( + validTdxReferenceValue = []ReferenceValue{ + { + Type: "TDX Reference Value", + Sha256: validTDXMeasurement, + Tdx: &TDXDetails{ + Version: 0x04, + Collateral: IntelCollateral{ + TeeType: tee_type_tdx, + TcbInfo: tcb_info_tdx, + TcbInfoSize: tcb_info_tdx_size, + QeIdentity: qe_identity_tdx, + QeIdentitySize: qe_identity_tdx_size, + }, + CaFingerprint: tdxRootCAFingerprint, + TdId: TDId{ + MrOwner: mrOwner, + MrOwnerConfig: mrOwnerConfig, + MrConfigId: mrConfigId, + RtMr0: rtMr0, + RtMr1: rtMr1, + RtMr2: rtMr2, + RtMr3: rtMr3, + }, + MrSeam: validMrSeam, + TdAttributes: validTdAttributes, + Xfam: validXFAM, + }, + }, + } + + validTdxRtmManifest = RtmManifest{ + MetaInfo: MetaInfo{ + Type: "RTM Manifest", + Name: "de.test.rtm", + Version: "2023-04-10T20:00:00Z", + }, + DevCommonName: "Test Developer", + Validity: Validity{ + NotBefore: "2023-04-10T20:00:00Z", + NotAfter: "2026-04-10T20:00:00Z", + }, + Description: "de.test.rtm", + CertificationLevel: 3, + ReferenceValues: validTdxReferenceValue, + } + + emptyTdxRtmManifest = RtmManifest{ + MetaInfo: MetaInfo{ + Type: "RTM Manifest", + Name: "de.test.rtm", + Version: "2023-04-10T20:00:00Z", + }, + DevCommonName: "Test Developer", + Validity: Validity{ + NotBefore: "2023-04-10T20:00:00Z", + NotAfter: "2026-04-10T20:00:00Z", + }, + Description: "de.test.rtm", + CertificationLevel: 3, + ReferenceValues: []ReferenceValue{}, + } + + validTdxOsManifest = OsManifest{ + MetaInfo: MetaInfo{ + Type: "OS Manifest", + Name: "de.test.os", + Version: "2023-04-10T20:00:00Z", + }, + DevCommonName: "Test Developer", + Validity: Validity{ + NotBefore: "2023-04-10T20:00:00Z", + NotAfter: "2026-04-10T20:00:00Z", + }, + Description: "PoC Fraunhofer AISEC TDX Report", + CertificationLevel: 3, + Rtms: []string{ + "de.test.rtm", + }, + } + + incompatibleTdxOsManifest = OsManifest{ + MetaInfo: MetaInfo{ + Type: "OS Manifest", + Name: "de.test.os", + Version: "2023-04-10T20:00:00Z", + }, + DevCommonName: "Test Developer", + Validity: Validity{ + NotBefore: "2023-04-10T20:00:00Z", + NotAfter: "2026-04-10T20:00:00Z", + }, + Description: "PoC Fraunhofer AISEC TDX Report", + CertificationLevel: 3, + Rtms: []string{ + "INVALID", + }, + } + + validTdxDeviceDescription = DeviceDescription{ + MetaInfo: MetaInfo{ + Type: "Device Description", + Name: "test-device.test.de", + Version: "2023-04-10T20:00:00Z", + }, + Location: "Munich, Germany", + RtmManifest: "de.test.rtm", + OsManifest: "de.test.os", + } + + invalidTdxDeviceDescription = DeviceDescription{ + MetaInfo: MetaInfo{ + Type: "Device Description", + Name: "test-device.test.de", + Version: "2023-04-10T20:00:00Z", + }, + Location: "Munich, Germany", + RtmManifest: "INVALID", + OsManifest: "INVALID", + } +) + type SwSigner struct { certChain []*x509.Certificate priv crypto.PrivateKey @@ -195,128 +318,6 @@ func Test_collectReferenceValues(t *testing.T) { } } -var ( - validTdxReferenceValue = []ReferenceValue{ - { - Type: "TDX Reference Value", - Sha256: validTDXMeasurement, - Tdx: &TDXDetails{ - Version: 0x04, - Collateral: IntelCollateral{ - TeeType: tee_type_tdx, - TcbInfo: tcb_info_tdx, - TcbInfoSize: tcb_info_tdx_size, - QeIdentity: qe_identity_tdx, - QeIdentitySize: qe_identity_tdx_size, - }, - CaFingerprint: tdxRootCAFingerprint, - TdId: TDId{ - MrOwner: mrOwner, - MrOwnerConfig: mrOwnerConfig, - MrConfigId: mrConfigId, - RtMr0: rtMr0, - RtMr1: rtMr1, - RtMr2: rtMr2, - RtMr3: rtMr3, - }, - MrSeam: validMrSeam, - TdAttributes: validTdAttributes, - Xfam: validXFAM, - }, - }, - } - - validTdxRtmManifest = RtmManifest{ - MetaInfo: MetaInfo{ - Type: "RTM Manifest", - Name: "de.test.rtm", - Version: "2023-04-10T20:00:00Z", - }, - DevCommonName: "Test Developer", - Validity: Validity{ - NotBefore: "2023-04-10T20:00:00Z", - NotAfter: "2026-04-10T20:00:00Z", - }, - Description: "de.test.rtm", - CertificationLevel: 3, - ReferenceValues: validTdxReferenceValue, - } - - emptyTdxRtmManifest = RtmManifest{ - MetaInfo: MetaInfo{ - Type: "RTM Manifest", - Name: "de.test.rtm", - Version: "2023-04-10T20:00:00Z", - }, - DevCommonName: "Test Developer", - Validity: Validity{ - NotBefore: "2023-04-10T20:00:00Z", - NotAfter: "2026-04-10T20:00:00Z", - }, - Description: "de.test.rtm", - CertificationLevel: 3, - ReferenceValues: []ReferenceValue{}, - } - - validTdxOsManifest = OsManifest{ - MetaInfo: MetaInfo{ - Type: "OS Manifest", - Name: "de.test.os", - Version: "2023-04-10T20:00:00Z", - }, - DevCommonName: "Test Developer", - Validity: Validity{ - NotBefore: "2023-04-10T20:00:00Z", - NotAfter: "2026-04-10T20:00:00Z", - }, - Description: "PoC Fraunhofer AISEC TDX Report", - CertificationLevel: 3, - Rtms: []string{ - "de.test.rtm", - }, - } - - invalidTdxOsManifest = OsManifest{ - MetaInfo: MetaInfo{ - Type: "OS Manifest", - Name: "de.test.os", - Version: "2023-04-10T20:00:00Z", - }, - DevCommonName: "Test Developer", - Validity: Validity{ - NotBefore: "2023-04-10T20:00:00Z", - NotAfter: "2026-04-10T20:00:00Z", - }, - Description: "PoC Fraunhofer AISEC TDX Report", - CertificationLevel: 3, - Rtms: []string{ - "INVALID", - }, - } - - validTdxDeviceDescription = DeviceDescription{ - MetaInfo: MetaInfo{ - Type: "Device Description", - Name: "test-device.test.de", - Version: "2023-04-10T20:00:00Z", - }, - Location: "Munich, Germany", - RtmManifest: "de.test.rtm", - OsManifest: "de.test.os", - } - - invalidTdxDeviceDescription = DeviceDescription{ - MetaInfo: MetaInfo{ - Type: "Device Description", - Name: "test-device.test.de", - Version: "2023-04-10T20:00:00Z", - }, - Location: "Munich, Germany", - RtmManifest: "INVALID", - OsManifest: "INVALID", - } -) - func TestVerify(t *testing.T) { type args struct { tdxMeas *TdxMeasurement @@ -417,7 +418,7 @@ func TestVerify(t *testing.T) { }, serializer: JsonSerializer{}, rtmManifest: validTdxRtmManifest, - osManifest: invalidTdxOsManifest, + osManifest: incompatibleTdxOsManifest, deviceDescription: validTdxDeviceDescription, nonce: validTDXNonce, }, diff --git a/attestationreport/intel_helpers.go b/attestationreport/intel_helpers.go index 68b412fc..c98b5c5a 100644 --- a/attestationreport/intel_helpers.go +++ b/attestationreport/intel_helpers.go @@ -21,8 +21,10 @@ import ( "crypto/elliptic" "crypto/sha256" "crypto/x509" + "encoding/asn1" "encoding/binary" "encoding/hex" + "encoding/json" "errors" "fmt" "io" @@ -35,6 +37,9 @@ import ( "github.com/Fraunhofer-AISEC/cmc/internal" ) +type EnclaveID string +type TcbStatus string + const ( SGX_QUOTE_TYPE uint32 = 0x0 TDX_QUOTE_TYPE uint32 = 0x81 @@ -57,8 +62,419 @@ const ( CA_PROCESSOR = "processor" ROOT_CA_CRL_NAME = "RootCaCRL" PCK_CERT_CRL_NAME = "PCKCertCRL" + + QE EnclaveID = "QE" + QVE EnclaveID = "QVE" + TD_QE EnclaveID = "TD_QE" + + UpToDate TcbStatus = "UpToDate" + ConfigurationNeeded TcbStatus = "ConfigurationNeeded" + OutOfDate TcbStatus = "OutOfDate" + OutOfDateConfigurationNeeded TcbStatus = "OutOfDateConfigurationNeeded" + Revoked TcbStatus = "REVOKED" + NotSupported TcbStatus = "NotSupported" ) +// 48 bytes +type QuoteHeader struct { + Version uint16 + AttestationKeyType uint16 // 2: ECDSA-256-with-P-256 curve + TeeType uint32 // value from dcap library (not in documentation) + QESVN uint16 + PCESVN uint16 + QEVendorID [16]byte + UserData [20]byte +} + +// 384 bytes +type EnclaveReportBody struct { + CPUSVN [16]byte + MISCSELECT uint32 + Reserved1 [28]byte + Attributes [16]byte + MRENCLAVE [32]byte + Reserved2 [32]byte + MRSIGNER [32]byte + Reserved3 [96]byte + ISVProdID uint16 + ISVSVN uint16 + Reserved4 [60]byte + ReportData [64]byte +} + +type ECDSA256QuoteSignatureDataStructure struct { + ISVEnclaveReportSignature [64]byte + ECDSAAttestationKey [64]byte + QEReport EnclaveReportBody + QEReportSignature [64]byte + + QEAuthDataSize uint16 + QEAuthData []byte + + QECertDataType uint16 + QECertDataSize uint32 + QECertData []byte +} + +type TcbInfo struct { + TcbInfo TcbInfoBody `json:"tcbInfo"` + Signature HexByte `json:"signature"` +} + +type TcbInfoBody struct { + Id string `json:"id"` + Version uint32 `json:"version"` + IssueDate time.Time `json:"issueDate"` + NextUpdate time.Time `json:"nextUpdate"` + Fmspc HexByte `json:"fmspc"` + PceId HexByte `json:"pceId"` + TcbType uint32 `json:"tcbType"` + TcbEvaluationDataNumber uint32 `json:"tcbEvaluationDataNumber"` + TcbLevels []TcbLevel `json:"tcbLevels"` + + // Holds properties of Intel’s TDX SEAM module + TdxModule TdxModule `json:"tdxModule"` // Only required for TDX +} + +type TcbLevel struct { + Tcb struct { + SgxTcbComponents []TcbComponent `json:"sgxTcbComponents"` + TdxTcbComponents []TcbComponent `json:"tdxTcbComponents"` + PceSvn uint32 `json:"pceSvn"` + } `json:"tcb"` + + TcbStatus string `json:"tcbStatus"` + TcbDate time.Time `json:"tcbDate"` + AdvisoryIDs []string `json:"advisoryIDs"` +} + +type TcbComponent struct { + Svn byte `json:"svn"` + Category string `json:"category"` + Type string `json:"type"` +} + +type TdxModule struct { + Mrsigner HexByte `json:"mrsigner"` + Attributes HexByte `json:"attributes"` + AttributesMask HexByte `json:"attributesMask"` +} + +type SgxCertificates struct { + RootCACert *x509.Certificate + IntermediateCert *x509.Certificate // Processor or Platform + PCKCert *x509.Certificate + TCBSigningCert *x509.Certificate +} + +type QEIdentity struct { + EnclaveIdentity QEIdentityBody `json:"enclaveIdentity"` + Signature HexByte `json:"signature"` +} + +type QEIdentityBody struct { + Id EnclaveID `json:"id"` + Version uint32 `json:"version"` + IssueDate time.Time `json:"issueDate"` + NextUpdate time.Time `json:"nextUpdate"` + TcbEvaluationDataNumber uint32 `json:"tcbEvaluationDataNumber"` + Miscselect HexByte `json:"miscselect"` + MiscselectMask HexByte `json:"miscselectMask"` + Attributes HexByte `json:"attributes"` + AttributesMask HexByte `json:"attributesMask"` + Mrsigner HexByte `json:"mrsigner"` + IsvProdId uint32 `json:"isvprodid"` + TcbLevels []TcbLevelEnclaveId `json:"tcbLevels"` +} + +type TcbLevelEnclaveId struct { + Tcb struct { + Isvsvn uint32 `json:"isvsvn"` + } `json:"tcb"` + TcbDate time.Time `json:"tcbDate"` + TcbStatus TcbStatus `json:"tcbStatus"` + AdvisoryIDs []string `json:"advisoryIDs"` +} + +// ------------------------- start SGX Extensions ------------------------- +// asn1 encoded data structure from pck certificate +type SGXExtensionsWrapper struct { + Value SGXExtensionsValue +} + +type SGXExtensionsValue struct { + // required: + Ppid PPID + Tcb TCB + PceId PCEID + Fmspc FMSPC + SgxType SGXTYPE + + //optional: + PlatformInstanceId PlatformInstanceId + Configuration Configuration +} + +type PPID struct { + Id asn1.ObjectIdentifier + Value []byte +} + +type TCB struct { + Id asn1.ObjectIdentifier + Value struct { + Comp_01 TCBComp + Comp_02 TCBComp + Comp_03 TCBComp + Comp_04 TCBComp + Comp_05 TCBComp + Comp_06 TCBComp + Comp_07 TCBComp + Comp_08 TCBComp + Comp_09 TCBComp + Comp_10 TCBComp + Comp_11 TCBComp + Comp_12 TCBComp + Comp_13 TCBComp + Comp_14 TCBComp + Comp_15 TCBComp + Comp_16 TCBComp + PceSvn TCBComp + CpuSvn struct { + Svn asn1.ObjectIdentifier + Value []byte + } + } +} + +type TCBComp struct { + Svn asn1.ObjectIdentifier + Value int +} + +type PCEID struct { + Id asn1.ObjectIdentifier + Value []byte +} + +type FMSPC struct { + Id asn1.ObjectIdentifier + Value []byte +} + +type SGXTYPE struct { + Id asn1.ObjectIdentifier + Value asn1.Enumerated +} + +type PlatformInstanceId struct { + Id asn1.ObjectIdentifier + Value []byte +} + +// ConfigurationId determines the type of the ConfigurationValue: +// [0]: dynamicPlatform, [1]: cachedKeys, [2]: sMTenabled +type Configuration struct { + Id asn1.ObjectIdentifier + Value []struct { + ConfigurationId asn1.ObjectIdentifier + ConfigurationValue bool + } +} + +// ------------------------- end SGX Extensions ------------------------- + +func parseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { + var sgx_extensions SGXExtensionsValue + + rest, err := asn1.Unmarshal(extensions, &sgx_extensions.Ppid) + if err != nil { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions PPID %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.Tcb) + if err != nil || len(rest) == 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions TCB %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.PceId) + if err != nil || len(rest) == 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions PCEID %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.Fmspc) + if err != nil || len(rest) == 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions FMSPC %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.SgxType) + if err != nil { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions SGXTYPE %v", err) + } else if len(rest) > 0 { + // parse optional parameters + rest, err = asn1.Unmarshal(rest, &sgx_extensions.PlatformInstanceId) + if err != nil || len(rest) == 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions PlatfromInstanceId %v", err) + } + rest, err = asn1.Unmarshal(rest, &sgx_extensions.Configuration) + if err != nil || len(rest) != 0 { + return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions Configuration %v", err) + } + } + + return sgx_extensions, nil +} + +func getTCBCompByIndex(tcb TCB, index int) TCBComp { + switch index { + case 1: + return tcb.Value.Comp_01 + case 2: + return tcb.Value.Comp_02 + case 3: + return tcb.Value.Comp_03 + case 4: + return tcb.Value.Comp_04 + case 5: + return tcb.Value.Comp_05 + case 6: + return tcb.Value.Comp_06 + case 7: + return tcb.Value.Comp_07 + case 8: + return tcb.Value.Comp_08 + case 9: + return tcb.Value.Comp_09 + case 10: + return tcb.Value.Comp_10 + case 11: + return tcb.Value.Comp_11 + case 12: + return tcb.Value.Comp_12 + case 13: + return tcb.Value.Comp_13 + case 14: + return tcb.Value.Comp_14 + case 15: + return tcb.Value.Comp_15 + case 16: + return tcb.Value.Comp_16 + default: + return TCBComp{} + } +} + +// expects enclave Identity structure in JSON format +func parseQEIdentity(qeIdentityJson []byte) (QEIdentity, error) { + var qe_identity QEIdentity + err := json.Unmarshal(qeIdentityJson, &qe_identity) + if err != nil { + return QEIdentity{}, fmt.Errorf("failed to decode Enclave Identity %v", err) + } + return qe_identity, nil +} + +// expects tcb Info in JSON format +func parseTcbInfo(tcbInfoJson []byte) (TcbInfo, error) { + var tcbInfo TcbInfo + err := json.Unmarshal(tcbInfoJson, &tcbInfo) + if err != nil { + return TcbInfo{}, fmt.Errorf("failed to decode TcbInfo %v", err) + } + return tcbInfo, nil +} + +// parse PEM/DER formatted certificates into a SgxCertificates struct +func parseCertificates(certsRaw any, pem bool) (SgxCertificates, error) { + var certChain SgxCertificates + var certs []*x509.Certificate + var err error + + switch t := certsRaw.(type) { + case []byte: + if pem { + certs, err = internal.ParseCertsPem(t) + } else { + certs, err = internal.ParseCertsDer(t) + } + if err != nil { + return SgxCertificates{}, fmt.Errorf("failed to parse certificates %v", err) + } + case [][]byte: + if pem { + certs, err = internal.ParseCertsPem(t) + } else { + certs, err = internal.ParseCertsDer(t) + } + if err != nil { + return SgxCertificates{}, fmt.Errorf("failed to parse certificates %v", err) + } + default: + return SgxCertificates{}, fmt.Errorf("ParseCertificates not implemented for type %T", certsRaw) + } + + for _, v := range certs { + switch v.Subject.CommonName { + case "Intel SGX PCK Certificate": + certChain.PCKCert = v + case "Intel SGX PCK Processor CA": + certChain.IntermediateCert = v + case "Intel SGX PCK Platform CA": + certChain.IntermediateCert = v + case "Intel SGX Root CA": + certChain.RootCACert = v + case "Intel SGX TCB Signing": + certChain.TCBSigningCert = v + } + } + + return certChain, nil +} + +// (for SGX): parses quote signature data structure from buf to sig +func parseECDSASignature(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStructure) error { + + err := binary.Read(buf, binary.LittleEndian, &sig.ISVEnclaveReportSignature) + if err != nil { + return fmt.Errorf("failed to parse ISVEnclaveReportSignature") + } + err = binary.Read(buf, binary.LittleEndian, &sig.ECDSAAttestationKey) + if err != nil { + return fmt.Errorf("failed to parse ECDSAAttestationKey") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QEReport) + if err != nil { + return fmt.Errorf("failed to parse QEReport") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QEReportSignature) + if err != nil { + return fmt.Errorf("failed to parse QEReportSignature") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QEAuthDataSize) + if err != nil { + return fmt.Errorf("failed to parse QEAuthDataSize") + } + tmp := make([]byte, sig.QEAuthDataSize) + err = binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QEAuthData") + } + sig.QEAuthData = tmp + + err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataType) + if err != nil { + return fmt.Errorf("failed to parse QECertDataType") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataSize) + if err != nil { + return fmt.Errorf("failed to parse QECertDataSize") + } + tmp = make([]byte, sig.QECertDataSize) + binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QECertData") + } + sig.QECertData = tmp + + return nil +} + // Verifies the quote signature // Params: QuoteType = 0x00 (SGX) or 0x81 (TDX) // TODO: Add support for different QE Cert Data Types diff --git a/attestationreport/intel_structs.go b/attestationreport/intel_structs.go deleted file mode 100644 index c7bb4870..00000000 --- a/attestationreport/intel_structs.go +++ /dev/null @@ -1,445 +0,0 @@ -// Copyright (c) 2021 Fraunhofer AISEC -// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package attestationreport - -import ( - "bytes" - "crypto/x509" - "encoding/asn1" - "encoding/binary" - "encoding/json" - "fmt" - "time" - - "github.com/Fraunhofer-AISEC/cmc/internal" -) - -type EnclaveID string -type TcbStatus string - -const ( - QE EnclaveID = "QE" - QVE EnclaveID = "QVE" - TD_QE EnclaveID = "TD_QE" - - UpToDate TcbStatus = "UpToDate" - ConfigurationNeeded TcbStatus = "ConfigurationNeeded" - OutOfDate TcbStatus = "OutOfDate" - OutOfDateConfigurationNeeded TcbStatus = "OutOfDateConfigurationNeeded" - Revoked TcbStatus = "REVOKED" - NotSupported TcbStatus = "NotSupported" -) - -// 48 bytes -type QuoteHeader struct { - Version uint16 - AttestationKeyType uint16 // 2: ECDSA-256-with-P-256 curve - TeeType uint32 // value from dcap library (not in documentation) - QESVN uint16 - PCESVN uint16 - QEVendorID [16]byte - UserData [20]byte -} - -// 384 bytes -type EnclaveReportBody struct { - CPUSVN [16]byte - MISCSELECT uint32 - Reserved1 [28]byte - Attributes [16]byte - MRENCLAVE [32]byte - Reserved2 [32]byte - MRSIGNER [32]byte - Reserved3 [96]byte - ISVProdID uint16 - ISVSVN uint16 - Reserved4 [60]byte - ReportData [64]byte -} - -type ECDSA256QuoteSignatureDataStructure struct { - ISVEnclaveReportSignature [64]byte - ECDSAAttestationKey [64]byte - QEReport EnclaveReportBody - QEReportSignature [64]byte - - QEAuthDataSize uint16 - QEAuthData []byte - - QECertDataType uint16 - QECertDataSize uint32 - QECertData []byte -} - -type TcbInfo struct { - TcbInfo TcbInfoBody `json:"tcbInfo"` - Signature HexByte `json:"signature"` -} - -type TcbInfoBody struct { - Id string `json:"id"` - Version uint32 `json:"version"` - IssueDate time.Time `json:"issueDate"` - NextUpdate time.Time `json:"nextUpdate"` - Fmspc HexByte `json:"fmspc"` - PceId HexByte `json:"pceId"` - TcbType uint32 `json:"tcbType"` - TcbEvaluationDataNumber uint32 `json:"tcbEvaluationDataNumber"` - TcbLevels []TcbLevel `json:"tcbLevels"` - - // Holds properties of Intel’s TDX SEAM module - TdxModule TdxModule `json:"tdxModule"` // Only required for TDX -} - -type TcbLevel struct { - Tcb struct { - SgxTcbComponents []TcbComponent `json:"sgxTcbComponents"` - TdxTcbComponents []TcbComponent `json:"tdxTcbComponents"` - PceSvn uint32 `json:"pceSvn"` - } `json:"tcb"` - - TcbStatus string `json:"tcbStatus"` - TcbDate time.Time `json:"tcbDate"` - AdvisoryIDs []string `json:"advisoryIDs"` -} - -type TcbComponent struct { - Svn byte `json:"svn"` - Category string `json:"category"` - Type string `json:"type"` -} - -type TdxModule struct { - Mrsigner HexByte `json:"mrsigner"` - Attributes HexByte `json:"attributes"` - AttributesMask HexByte `json:"attributesMask"` -} - -type SgxCertificates struct { - RootCACert *x509.Certificate - IntermediateCert *x509.Certificate // Processor or Platform - PCKCert *x509.Certificate - TCBSigningCert *x509.Certificate -} - -type QEIdentity struct { - EnclaveIdentity QEIdentityBody `json:"enclaveIdentity"` - Signature HexByte `json:"signature"` -} - -type QEIdentityBody struct { - Id EnclaveID `json:"id"` - Version uint32 `json:"version"` - IssueDate time.Time `json:"issueDate"` - NextUpdate time.Time `json:"nextUpdate"` - TcbEvaluationDataNumber uint32 `json:"tcbEvaluationDataNumber"` - Miscselect HexByte `json:"miscselect"` - MiscselectMask HexByte `json:"miscselectMask"` - Attributes HexByte `json:"attributes"` - AttributesMask HexByte `json:"attributesMask"` - Mrsigner HexByte `json:"mrsigner"` - IsvProdId uint32 `json:"isvprodid"` - TcbLevels []TcbLevelEnclaveId `json:"tcbLevels"` -} - -type TcbLevelEnclaveId struct { - Tcb struct { - Isvsvn uint32 `json:"isvsvn"` - } `json:"tcb"` - TcbDate time.Time `json:"tcbDate"` - TcbStatus TcbStatus `json:"tcbStatus"` - AdvisoryIDs []string `json:"advisoryIDs"` -} - -// ------------------------- start SGX Extensions ------------------------- -// asn1 encoded data structure from pck certificate -type SGXExtensionsWrapper struct { - Value SGXExtensionsValue -} - -type SGXExtensionsValue struct { - // required: - Ppid PPID - Tcb TCB - PceId PCEID - Fmspc FMSPC - SgxType SGXTYPE - - //optional: - PlatformInstanceId PlatformInstanceId - Configuration Configuration -} - -type PPID struct { - Id asn1.ObjectIdentifier - Value []byte -} - -type TCB struct { - Id asn1.ObjectIdentifier - Value struct { - Comp_01 TCBComp - Comp_02 TCBComp - Comp_03 TCBComp - Comp_04 TCBComp - Comp_05 TCBComp - Comp_06 TCBComp - Comp_07 TCBComp - Comp_08 TCBComp - Comp_09 TCBComp - Comp_10 TCBComp - Comp_11 TCBComp - Comp_12 TCBComp - Comp_13 TCBComp - Comp_14 TCBComp - Comp_15 TCBComp - Comp_16 TCBComp - PceSvn TCBComp - CpuSvn struct { - Svn asn1.ObjectIdentifier - Value []byte - } - } -} - -type TCBComp struct { - Svn asn1.ObjectIdentifier - Value int -} - -type PCEID struct { - Id asn1.ObjectIdentifier - Value []byte -} - -type FMSPC struct { - Id asn1.ObjectIdentifier - Value []byte -} - -type SGXTYPE struct { - Id asn1.ObjectIdentifier - Value asn1.Enumerated -} - -type PlatformInstanceId struct { - Id asn1.ObjectIdentifier - Value []byte -} - -// ConfigurationId determines the type of the ConfigurationValue: -// [0]: dynamicPlatform, [1]: cachedKeys, [2]: sMTenabled -type Configuration struct { - Id asn1.ObjectIdentifier - Value []struct { - ConfigurationId asn1.ObjectIdentifier - ConfigurationValue bool - } -} - -// ------------------------- end SGX Extensions ------------------------- - -func parseSGXExtensions(extensions []byte) (SGXExtensionsValue, error) { - var sgx_extensions SGXExtensionsValue - - rest, err := asn1.Unmarshal(extensions, &sgx_extensions.Ppid) - if err != nil { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions PPID %v", err) - } - rest, err = asn1.Unmarshal(rest, &sgx_extensions.Tcb) - if err != nil || len(rest) == 0 { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions TCB %v", err) - } - rest, err = asn1.Unmarshal(rest, &sgx_extensions.PceId) - if err != nil || len(rest) == 0 { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions PCEID %v", err) - } - rest, err = asn1.Unmarshal(rest, &sgx_extensions.Fmspc) - if err != nil || len(rest) == 0 { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions FMSPC %v", err) - } - rest, err = asn1.Unmarshal(rest, &sgx_extensions.SgxType) - if err != nil { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX Extensions SGXTYPE %v", err) - } else if len(rest) > 0 { - // parse optional parameters - rest, err = asn1.Unmarshal(rest, &sgx_extensions.PlatformInstanceId) - if err != nil || len(rest) == 0 { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions PlatfromInstanceId %v", err) - } - rest, err = asn1.Unmarshal(rest, &sgx_extensions.Configuration) - if err != nil || len(rest) != 0 { - return SGXExtensionsValue{}, fmt.Errorf("failed to decode SGX extensions Configuration %v", err) - } - } - - return sgx_extensions, nil -} - -func getTCBCompByIndex(tcb TCB, index int) TCBComp { - switch index { - case 1: - return tcb.Value.Comp_01 - case 2: - return tcb.Value.Comp_02 - case 3: - return tcb.Value.Comp_03 - case 4: - return tcb.Value.Comp_04 - case 5: - return tcb.Value.Comp_05 - case 6: - return tcb.Value.Comp_06 - case 7: - return tcb.Value.Comp_07 - case 8: - return tcb.Value.Comp_08 - case 9: - return tcb.Value.Comp_09 - case 10: - return tcb.Value.Comp_10 - case 11: - return tcb.Value.Comp_11 - case 12: - return tcb.Value.Comp_12 - case 13: - return tcb.Value.Comp_13 - case 14: - return tcb.Value.Comp_14 - case 15: - return tcb.Value.Comp_15 - case 16: - return tcb.Value.Comp_16 - default: - return TCBComp{} - } -} - -// expects enclave Identity structure in JSON format -func parseQEIdentity(qeIdentityJson []byte) (QEIdentity, error) { - var qe_identity QEIdentity - err := json.Unmarshal(qeIdentityJson, &qe_identity) - if err != nil { - return QEIdentity{}, fmt.Errorf("failed to decode Enclave Identity %v", err) - } - return qe_identity, nil -} - -// expects tcb Info in JSON format -func parseTcbInfo(tcbInfoJson []byte) (TcbInfo, error) { - var tcbInfo TcbInfo - err := json.Unmarshal(tcbInfoJson, &tcbInfo) - if err != nil { - return TcbInfo{}, fmt.Errorf("failed to decode TcbInfo %v", err) - } - return tcbInfo, nil -} - -// parse PEM/DER formatted certificates into a SgxCertificates struct -func parseCertificates(certsRaw any, pem bool) (SgxCertificates, error) { - var certChain SgxCertificates - var certs []*x509.Certificate - var err error - - switch t := certsRaw.(type) { - case []byte: - if pem { - certs, err = internal.ParseCertsPem(t) - } else { - certs, err = internal.ParseCertsDer(t) - } - if err != nil { - return SgxCertificates{}, fmt.Errorf("failed to parse certificates %v", err) - } - case [][]byte: - if pem { - certs, err = internal.ParseCertsPem(t) - } else { - certs, err = internal.ParseCertsDer(t) - } - if err != nil { - return SgxCertificates{}, fmt.Errorf("failed to parse certificates %v", err) - } - default: - return SgxCertificates{}, fmt.Errorf("ParseCertificates not implemented for type %T", certsRaw) - } - - // TODO: identify based on subject key identifier - for _, v := range certs { - switch v.Subject.CommonName { - case "Intel SGX PCK Certificate": - certChain.PCKCert = v - case "Intel SGX PCK Processor CA": - certChain.IntermediateCert = v - case "Intel SGX PCK Platform CA": - certChain.IntermediateCert = v - case "Intel SGX Root CA": - certChain.RootCACert = v - case "Intel SGX TCB Signing": - certChain.TCBSigningCert = v - } - } - - return certChain, nil -} - -// parses quote signature data structure from buf to sig -func parseECDSASignature(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStructure) error { - - err := binary.Read(buf, binary.LittleEndian, &sig.ISVEnclaveReportSignature) - if err != nil { - return fmt.Errorf("failed to parse ISVEnclaveReportSignature") - } - err = binary.Read(buf, binary.LittleEndian, &sig.ECDSAAttestationKey) - if err != nil { - return fmt.Errorf("failed to parse ECDSAAttestationKey") - } - err = binary.Read(buf, binary.LittleEndian, &sig.QEReport) - if err != nil { - return fmt.Errorf("failed to parse QEReport") - } - err = binary.Read(buf, binary.LittleEndian, &sig.QEReportSignature) - if err != nil { - return fmt.Errorf("failed to parse QEReportSignature") - } - err = binary.Read(buf, binary.LittleEndian, &sig.QEAuthDataSize) - if err != nil { - return fmt.Errorf("failed to parse QEAuthDataSize") - } - tmp := make([]byte, sig.QEAuthDataSize) - err = binary.Read(buf, binary.LittleEndian, &tmp) - if err != nil { - return fmt.Errorf("failed to parse QEAuthData") - } - sig.QEAuthData = tmp - - err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataType) - if err != nil { - return fmt.Errorf("failed to parse QECertDataType") - } - err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataSize) - if err != nil { - return fmt.Errorf("failed to parse QECertDataSize") - } - tmp = make([]byte, sig.QECertDataSize) - binary.Read(buf, binary.LittleEndian, &tmp) - if err != nil { - return fmt.Errorf("failed to parse QECertData") - } - sig.QECertData = tmp - - return nil -} diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 733544a7..94ff7e7d 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -17,10 +17,181 @@ package attestationreport import ( "bytes" + "encoding/binary" "encoding/hex" "fmt" ) +// TDX Report V4 +type TdxReportV4 struct { + QuoteHeader QuoteHeader + QuoteBody TdxReportBody + QuoteSignatureDataLen uint32 + QuoteSignatureData ECDSA256QuoteSignatureDataStructureV4 // variable size +} + +// TDX 1.0: 584 bytes +type TdxReportBody struct { + TeeTcbSvn [16]byte + MrSeam [48]byte + MrSignerSeam [48]byte + SeamAttributes [8]byte + TdAttributes [8]byte + XFAM [8]byte + MrTd [48]byte + MrConfigId [48]byte + MrOwner [48]byte + MrOwnerConfig [48]byte + RtMr0 [48]byte + RtMr1 [48]byte + RtMr2 [48]byte + RtMr3 [48]byte + ReportData [64]byte +} + +// Quote Signature for TDX, contains QE Certification Data version 4 +type ECDSA256QuoteSignatureDataStructureV4 struct { + QuoteSignature [64]byte + ECDSAAttestationKey [64]byte + QECertDataType uint16 + QECertDataSize uint32 + QECertData QEReportCertDataV4 // Version 4 +} + +// This is the datastructure of QECertDataType 6 +type QEReportCertDataV4 struct { + QEReport EnclaveReportBody + QEReportSignature [64]byte + QEAuthDataSize uint16 + QEAuthData []byte + QECertDataType uint16 // Type 5 (PCK Cert Chain) + QECertDataSize uint32 + QECertData SgxCertificates +} + +// Parses the report into the TDReport structure +func decodeTdxReportV4(report []byte) (TdxReportV4, error) { + var reportStruct TdxReportV4 + var header QuoteHeader + var body TdxReportBody + var sig ECDSA256QuoteSignatureDataStructureV4 + var sigLen uint32 + + // parse header + buf := bytes.NewBuffer(report) + err := binary.Read(buf, binary.LittleEndian, &header) + if err != nil { + return TdxReportV4{}, fmt.Errorf("failed to decode TD report header: %v", err) + } + + // parse body + err = binary.Read(buf, binary.LittleEndian, &body) + if err != nil { + return TdxReportV4{}, fmt.Errorf("failed to decode TD report body: %v", err) + } + + // parse signature size + err = binary.Read(buf, binary.LittleEndian, &sigLen) + if err != nil { + return TdxReportV4{}, fmt.Errorf("failed to decode TD report QuoteSignatureDataLen: %v", err) + } + + // parse signature + err = parseECDSASignatureV4(buf, &sig) + if err != nil { + return TdxReportV4{}, fmt.Errorf("failed to decode TD report QuoteSignatureData: %v", err) + } + + // compose the final report struct + reportStruct.QuoteHeader = header + reportStruct.QuoteBody = body + reportStruct.QuoteSignatureDataLen = sigLen + reportStruct.QuoteSignatureData = sig + + return reportStruct, nil +} + +// parse the full quote signature data structure (V4) from buf to sig +func parseECDSASignatureV4(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStructureV4) error { + + err := binary.Read(buf, binary.LittleEndian, &sig.QuoteSignature) + if err != nil { + return fmt.Errorf("failed to parse QuoteSignature") + } + err = binary.Read(buf, binary.LittleEndian, &sig.ECDSAAttestationKey) + if err != nil { + return fmt.Errorf("failed to parse ECDSAAttestationKey") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataType) + if err != nil { + return fmt.Errorf("failed to parse QECertDataType") + } + err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataSize) + if err != nil { + return fmt.Errorf("failed to parse QECertDataSize") + } + + rawData := make([]byte, sig.QECertDataSize) + err = binary.Read(buf, binary.LittleEndian, &rawData) + if err != nil { + return fmt.Errorf("failed to parse QECertData") + } + + // parse QEReportCertDataV4 + buf = bytes.NewBuffer(rawData) + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEReport) + if err != nil { + return fmt.Errorf("failed to parse QE Report: %v", err) + } + + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEReportSignature) + if err != nil { + return fmt.Errorf("failed to parse QE Report Signature: %v", err) + } + + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEAuthDataSize) + if err != nil { + return fmt.Errorf("failed to parse QE Authentication Data Size: %v", err) + } + + tmp := make([]byte, sig.QECertData.QEAuthDataSize) + err = binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QE Authentication Data: %v", err) + } + sig.QECertData.QEAuthData = tmp + + // parse QE Certification Data Type (has to be type 5) + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QECertDataType) + if err != nil { + return fmt.Errorf("failed to parse QE Certification Data Type: %v", err) + } + if sig.QECertData.QECertDataType != 5 { + return fmt.Errorf("wrong QECertDataType. Expected: 5, Got: %v", sig.QECertData.QECertDataType) + } + + err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QECertDataSize) + if err != nil { + return fmt.Errorf("failed to parse QE Certification Data Size: %v", err) + } + + // parse QE Certification Data (Raw) + tmp = make([]byte, sig.QECertData.QECertDataSize) + err = binary.Read(buf, binary.LittleEndian, &tmp) + if err != nil { + return fmt.Errorf("failed to parse QE Certification Data: %v", err) + } + + // parse PCK Cert Chain (PCK Leaf Cert || Intermediate CA Cert || Root CA Cert) + certChain, err := parseCertificates(tmp[:], true) + if err != nil { + return fmt.Errorf("failed to parse certificate chain from QECertData: %v", err) + } + sig.QECertData.QECertData = certChain + + return nil +} + func verifyTdxMeasurements(tdxM *TdxMeasurement, nonce []byte, referenceValues []ReferenceValue) (*TdxMeasurementResult, bool) { var err error result := &TdxMeasurementResult{} diff --git a/attestationreport/tdx_struct.go b/attestationreport/tdx_struct.go deleted file mode 100644 index 24b446a9..00000000 --- a/attestationreport/tdx_struct.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) 2021 Fraunhofer AISEC -// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package attestationreport - -import ( - "bytes" - "encoding/binary" - "fmt" -) - -// TDX Report V4 -type TdxReportV4 struct { - QuoteHeader QuoteHeader - QuoteBody TdxReportBody - QuoteSignatureDataLen uint32 - QuoteSignatureData ECDSA256QuoteSignatureDataStructureV4 // variable size -} - -// TDX 1.0: 584 bytes -type TdxReportBody struct { - TeeTcbSvn [16]byte - MrSeam [48]byte - MrSignerSeam [48]byte - SeamAttributes [8]byte - TdAttributes [8]byte - XFAM [8]byte - MrTd [48]byte - MrConfigId [48]byte - MrOwner [48]byte - MrOwnerConfig [48]byte - RtMr0 [48]byte - RtMr1 [48]byte - RtMr2 [48]byte - RtMr3 [48]byte - ReportData [64]byte -} - -// Quote Signature for TDX, contains QE Certification Data version 4 -type ECDSA256QuoteSignatureDataStructureV4 struct { - QuoteSignature [64]byte - ECDSAAttestationKey [64]byte - QECertDataType uint16 - QECertDataSize uint32 - QECertData QEReportCertDataV4 // Version 4 -} - -// This is the datastructure of QECertDataType 6 -type QEReportCertDataV4 struct { - QEReport EnclaveReportBody - QEReportSignature [64]byte - QEAuthDataSize uint16 - QEAuthData []byte - QECertDataType uint16 // Type 5 (PCK Cert Chain) - QECertDataSize uint32 - QECertData SgxCertificates -} - -// Parses the report into the TDReport structure -func decodeTdxReportV4(report []byte) (TdxReportV4, error) { - var reportStruct TdxReportV4 - var header QuoteHeader - var body TdxReportBody - var sig ECDSA256QuoteSignatureDataStructureV4 - var sigLen uint32 - - // parse header - buf := bytes.NewBuffer(report) - err := binary.Read(buf, binary.LittleEndian, &header) - if err != nil { - return TdxReportV4{}, fmt.Errorf("failed to decode TD report header: %v", err) - } - - // parse body - err = binary.Read(buf, binary.LittleEndian, &body) - if err != nil { - return TdxReportV4{}, fmt.Errorf("failed to decode TD report body: %v", err) - } - - // parse signature size - err = binary.Read(buf, binary.LittleEndian, &sigLen) - if err != nil { - return TdxReportV4{}, fmt.Errorf("failed to decode TD report QuoteSignatureDataLen: %v", err) - } - - // parse signature - err = parseECDSASignatureV4(buf, &sig) - if err != nil { - return TdxReportV4{}, fmt.Errorf("failed to decode TD report QuoteSignatureData: %v", err) - } - - // compose the final report struct - reportStruct.QuoteHeader = header - reportStruct.QuoteBody = body - reportStruct.QuoteSignatureDataLen = sigLen - reportStruct.QuoteSignatureData = sig - - return reportStruct, nil -} - -// parse the full quote signature data structure (V4) from buf to sig -func parseECDSASignatureV4(buf *bytes.Buffer, sig *ECDSA256QuoteSignatureDataStructureV4) error { - - err := binary.Read(buf, binary.LittleEndian, &sig.QuoteSignature) - if err != nil { - return fmt.Errorf("failed to parse QuoteSignature") - } - err = binary.Read(buf, binary.LittleEndian, &sig.ECDSAAttestationKey) - if err != nil { - return fmt.Errorf("failed to parse ECDSAAttestationKey") - } - err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataType) - if err != nil { - return fmt.Errorf("failed to parse QECertDataType") - } - err = binary.Read(buf, binary.LittleEndian, &sig.QECertDataSize) - if err != nil { - return fmt.Errorf("failed to parse QECertDataSize") - } - - rawData := make([]byte, sig.QECertDataSize) - err = binary.Read(buf, binary.LittleEndian, &rawData) - if err != nil { - return fmt.Errorf("failed to parse QECertData") - } - - // parse QEReportCertDataV4 - buf = bytes.NewBuffer(rawData) - err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEReport) - if err != nil { - return fmt.Errorf("failed to parse QE Report: %v", err) - } - - err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEReportSignature) - if err != nil { - return fmt.Errorf("failed to parse QE Report Signature: %v", err) - } - - err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QEAuthDataSize) - if err != nil { - return fmt.Errorf("failed to parse QE Authentication Data Size: %v", err) - } - - tmp := make([]byte, sig.QECertData.QEAuthDataSize) - err = binary.Read(buf, binary.LittleEndian, &tmp) - if err != nil { - return fmt.Errorf("failed to parse QE Authentication Data: %v", err) - } - sig.QECertData.QEAuthData = tmp - - // parse QE Certification Data Type (has to be type 5) - err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QECertDataType) - if err != nil { - return fmt.Errorf("failed to parse QE Certification Data Type: %v", err) - } - if sig.QECertData.QECertDataType != 5 { - return fmt.Errorf("wrong QECertDataType. Expected: 5, Got: %v", sig.QECertData.QECertDataType) - } - - err = binary.Read(buf, binary.LittleEndian, &sig.QECertData.QECertDataSize) - if err != nil { - return fmt.Errorf("failed to parse QE Certification Data Size: %v", err) - } - - // parse QE Certification Data (Raw) - tmp = make([]byte, sig.QECertData.QECertDataSize) - err = binary.Read(buf, binary.LittleEndian, &tmp) - if err != nil { - return fmt.Errorf("failed to parse QE Certification Data: %v", err) - } - - // parse PCK Cert Chain (PCK Leaf Cert || Intermediate CA Cert || Root CA Cert) - certChain, err := parseCertificates(tmp[:], true) - if err != nil { - return fmt.Errorf("failed to parse certificate chain from QECertData: %v", err) - } - sig.QECertData.QECertData = certChain - - return nil -} From c2e1f1302e517d4175d0ce0c6c249c440b817bd4 Mon Sep 17 00:00:00 2001 From: Christian Schmucker Date: Sun, 3 Dec 2023 23:33:09 +0000 Subject: [PATCH 22/23] get latest update in intel_helpers.go --- attestationreport/intel_helpers.go | 31 ++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/attestationreport/intel_helpers.go b/attestationreport/intel_helpers.go index c98b5c5a..78f848d5 100644 --- a/attestationreport/intel_helpers.go +++ b/attestationreport/intel_helpers.go @@ -62,6 +62,7 @@ const ( CA_PROCESSOR = "processor" ROOT_CA_CRL_NAME = "RootCaCRL" PCK_CERT_CRL_NAME = "PCKCertCRL" + CACHE_DIR = "cache" // stores the CRLs QE EnclaveID = "QE" QVE EnclaveID = "QVE" @@ -936,10 +937,11 @@ func CrlCheck(crl *x509.RevocationList, cert *x509.Certificate, parentCert *x509 // fetch the CRL either from cache (filesystem) or download it from PCS // TODO: implement a better caching mechanism -func fetchCRL(uri string, name string) (*x509.RevocationList, error) { - fileInfo, err := os.Stat(name) +func fetchCRL(uri string, name string, ca string) (*x509.RevocationList, error) { + filePath := fmt.Sprintf("%s/%s_%s", CACHE_DIR, name, ca) + fileInfo, err := os.Stat(filePath) if err == nil { - log.Tracef("File %s exists.\n", name) + log.Tracef("File %s exists.\n", filePath) lastModifiedTime := fileInfo.ModTime() currentTime := time.Now() @@ -947,13 +949,13 @@ func fetchCRL(uri string, name string) (*x509.RevocationList, error) { // Update the CRL if it is older than a day if timeSinceLastModification > 24*time.Hour { - return downloadCRL(uri, name) + return downloadCRL(uri, filePath) } // Read CRL - crl_raw, err := os.ReadFile(name) + crl_raw, err := os.ReadFile(filePath) if err != nil { - return nil, fmt.Errorf("failed to read quote.dat: %w", err) + return nil, fmt.Errorf("failed to read CRL: %w", err) } // Parse CRL @@ -963,14 +965,19 @@ func fetchCRL(uri string, name string) (*x509.RevocationList, error) { } return crl, nil } else if os.IsNotExist(err) { - return downloadCRL(uri, name) + err := os.MkdirAll(CACHE_DIR, 0755) + if err != nil { + fmt.Println("Error creating cache folder:", err) + return nil, err + } + return downloadCRL(uri, filePath) } else { return nil, err } } // Download CRL from the Intel PCS -func downloadCRL(uri string, name string) (*x509.RevocationList, error) { +func downloadCRL(uri string, filePath string) (*x509.RevocationList, error) { resp, err := http.Get(uri) if err != nil { return nil, err @@ -987,7 +994,7 @@ func downloadCRL(uri string, name string) (*x509.RevocationList, error) { } // Store CRL in file - err = os.WriteFile(name, crlData, 0644) + err = os.WriteFile(filePath, crlData, 0644) if err != nil { return nil, err } @@ -1013,14 +1020,14 @@ func VerifyIntelCertChainFull(quoteCerts SgxCertificates, ca string) ([][]*x509. } // download CRLs from PCS - root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) + root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME, "") if err != nil { msg := fmt.Sprintf("downloading ROOT CA CRL from PCS failed: %v", err) return nil, errors.New(msg) } pck_crl_uri := fmt.Sprintf(PCS_PCK_CERT_CRL_URI, ca) - pck_crl, err := fetchCRL(pck_crl_uri, PCK_CERT_CRL_NAME) + pck_crl, err := fetchCRL(pck_crl_uri, PCK_CERT_CRL_NAME, ca) if err != nil { msg := fmt.Sprintf("downloading PCK Cert CRL from PCS failed: %v", err) return nil, errors.New(msg) @@ -1052,7 +1059,7 @@ func VerifyTCBSigningCertChain(quoteCerts SgxCertificates) ([][]*x509.Certificat return nil, errors.New(msg) } - root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME) + root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME, "") if err != nil { msg := fmt.Sprintf("downloading Root CA CRL from PCS failed: %v", err) return nil, errors.New(msg) From 3ea52816410fb56ff724526f11f46d6a500a72c5 Mon Sep 17 00:00:00 2001 From: Christian Schmucker Date: Sun, 3 Dec 2023 23:34:07 +0000 Subject: [PATCH 23/23] updated .gigignore --- .gitignore | 1 + attestationreport/PCKCertCRL | Bin 2662 -> 0 bytes attestationreport/RootCaCRL | Bin 293 -> 0 bytes 3 files changed, 1 insertion(+) delete mode 100644 attestationreport/PCKCertCRL delete mode 100644 attestationreport/RootCaCRL diff --git a/.gitignore b/.gitignore index 187a3cac..ab71cc72 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ example-setup/**/*.cbor example-setup/metadata-signed/* tpmdriver/test_encrypted_ak.json est/server/server +attestationreport/cache/* diff --git a/attestationreport/PCKCertCRL b/attestationreport/PCKCertCRL deleted file mode 100644 index 255744d0174d7e9cd969000b55c7b76a5833135a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2662 zcma*pdpJ~i7zc1OBQq{tFmjn4W|P}4)0r9L+A3kqtY~6U+Abrx)MS_$*(sGF^pGVQ zw?S+clS@fM7t9K!3tdF$g2bw)cFfS0Bzw-b&p!L?ALs0!zjJ<{=lgr#bDncvAW16} zNYcWgQ7Ax51NT)bqxTzi3>t``tO7_Hm_T)mjxjS@u!lnm@ZLgVdH9f69JU~w%iB-# zaHp66M$jHD9o^4+Jh;3VE{`pU;zm>OfF5)YtD_yjjux;<9vn80P0h@&=>sMem)5dH;dS)? zNI(ZH1QSR!P{(M2McJbb)D-p6`34`$UDL*CQi14xJN-vdjox3X?q3I`G1Tjb6-_$AvU_1xDG+uO1{82Z_ALjGFN8nxp#^kt6U3ic0Cb z=|qUH8uisJE)5wFoSs>%Nkdk-#}g{tO%LX{-v9JKB(Y(tP_m2g3OwNIf+wNLxte(k z87ddY+BVBGZ4ZOt5p=-xaAg&fks1+M+>!0lwK4qbSTY}3oojbk(b~spF4(>zOjMoN zS7tVhMOKPYA6wczhqE_1m3+Brk$02t4_A>@@=$GNhD`JR^={3&Sn@F-u=LRkyaJmc zq%5#x*e?k3uI8p^gBQ1zWTe<2BD0^D*~GL?^dCKxIg;$m@G~Rg%n^}k+J%lryG-*J zy1g^*`IkecyX)Sp)4>Jg>B+;6EkCIgA>H&v6Dhm`qYjS8Go*8Rv)5g#YOr0oy+op} z2odQY_n}!9I<8ZN&TH*6T^G=ls)uw8roQ;zVl!OH$#XH|7U&oAY1V~9zyLPlhq zrn%2c;fv-clQRTGSkwRbM3)>;*gCg6Ozp{t=I1p$@(i)sZ3Z6&^{->p;-geN!`*uE zSVV5AoBgDN2^U$N*tw4yJiMeqVDo&EAl);-2X^g6R^kt4rv-@7ACsOKUIPaV6QDiq>=KymaQ_cdI0_L?6`at!R?_Ny!0EtBIuT)hB>z X;=a-33E{d(@lwf{< z!c3vT;=D%21||l^h6YAPriM{Kt_hH91m!N!H_!#Ti#b$Q1n4TD8|6W4Wflnou?7*P zRGw>5b4@xwH*c95v_C-FQk`uLvL~257z|vQ6avF*-EaP07``oDZtIuS>OFB`XVw=- zF@M~de4V8sYNG^`BE#efqHn~^p1=AP@026wpyqz8SXetF;MCrXbKiDd&(LB90I3d8 ALI3~&