diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index fcadacfb..e1670fc9 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -25,6 +25,7 @@ import ( "encoding/json" "errors" "fmt" + "strconv" "github.com/Fraunhofer-AISEC/cmc/internal" "github.com/fxamacker/cbor/v2" @@ -413,7 +414,7 @@ func Generate(nonce []byte, metadata [][]byte, measurers []Driver, s Serializer) // Extract plain payload (i.e. the manifest/description itself) data, err := s.GetPayload(metadata[i]) if err != nil { - log.Warnf("Failed to parse metadata object %v: %v", i, err) + log.Tracef("Failed to parse metadata object %v: %v", i, err) continue } @@ -422,7 +423,7 @@ func Generate(nonce []byte, metadata [][]byte, measurers []Driver, s Serializer) elem := new(MetaInfo) err = s.Unmarshal(data, elem) if err != nil { - log.Warnf("Failed to unmarshal data from metadata object %v: %v", i, err) + log.Tracef("Failed to unmarshal data from metadata object %v: %v", i, err) continue } @@ -497,9 +498,9 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel cas, err := internal.ParseCertsPem(casPem) if err != nil { + log.Tracef("Failed to parse specified CA certificate(s): %v", err) result.Success = false - result.ProcessingError = append(result.ProcessingError, - fmt.Sprintf("Failed to parse specified CA certificate(s): %v", err)) + result.ErrorCode = ParseCA return result } @@ -512,36 +513,34 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel log.Trace("Detected CBOR serialization") s = CborSerializer{} } else { + log.Trace("Unable to detect AR serialization format") result.Success = false - result.ProcessingError = append(result.ProcessingError, - "Unable to detect AR serialization format") + result.ErrorCode = UnknownSerialization return result } // Verify and unpack attestation report - ok, ar := verifyAr(arRaw, &result, cas, s) - if ar == nil { - result.InternalError = true - } - if !ok { + ar, tr, code := verifyAr(arRaw, cas, s) + result.ReportSignature = tr.SignatureCheck + if code != NotSet { + result.ErrorCode = code result.Success = false - return result } // Verify and unpack metadata from attestation report - ok, metadata := verifyMetadata(ar, &result, cas, s) - if metadata == nil { - result.InternalError = true - } + metadata, mr, ok := verifyMetadata(ar, cas, s) if !ok { result.Success = false - return result } + result.MetadataResult = *mr // Verify nonce if res := bytes.Compare(ar.Nonce, nonce); res != 0 { - msg := fmt.Sprintf("Nonces mismatch: SuppliedNonce = %v, AttestationReport Nonce = %v", hex.EncodeToString(nonce), hex.EncodeToString(ar.Nonce)) - result.FreshnessCheck.setFalse(&msg) + log.Tracef("Nonces mismatch: supplied nonce: %v, report nonce = %v", + hex.EncodeToString(nonce), hex.EncodeToString(ar.Nonce)) + result.FreshnessCheck.Success = false + result.FreshnessCheck.Expected = hex.EncodeToString(ar.Nonce) + result.FreshnessCheck.Got = hex.EncodeToString(nonce) result.Success = false } else { result.FreshnessCheck.Success = true @@ -549,8 +548,9 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel refVals, err := collectReferenceValues(metadata) if err != nil { - result.ProcessingError = append(result.ProcessingError, err.Error()) + log.Tracef("Failed to collect reference values: %v", err) result.Success = false + result.ErrorCode = RefValTypeNotSupported } hwAttest := false @@ -600,9 +600,9 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel hwAttest = true default: + log.Tracef("Unsupported measurement type '%v'", mtype) result.Success = false - msg := fmt.Sprintf("Unsupported measurement type '%v'", mtype) - result.ProcessingError = append(result.ProcessingError, msg) + result.ErrorCode = MeasurementTypeNotSupported } } @@ -624,86 +624,105 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel // If there are reference values with a higher trust level present, the remote attestation // must fail if !hwAttest && 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) + log.Tracef("No hardware trust anchor measurements present but claimed certification level is %v, which requires a hardware trust anchor", aggCertLevel) + result.ErrorCode = InvalidCertificationLevel result.Success = false } result.SwCertLevel = aggCertLevel // Verify the compatibility of the attestation report through verifying the // compatibility of all components - appDescriptions := make([]string, 0) - for _, a := range metadata.DeviceDescription.AppDescriptions { - appDescriptions = append(appDescriptions, a.AppManifest) - } // Check that the OS and RTM Manifest are specified in the Device Description if metadata.DeviceDescription.RtmManifest == metadata.RtmManifest.Name { result.DevDescResult.CorrectRtm.Success = true } else { - msg := fmt.Sprintf("Device Description listed wrong RTM Manifest: %v vs. %v", metadata.DeviceDescription.RtmManifest, metadata.RtmManifest.Name) - result.DevDescResult.CorrectRtm.setFalse(&msg) + log.Tracef("Device Description listed wrong RTM Manifest: %v vs. %v", + metadata.DeviceDescription.RtmManifest, metadata.RtmManifest.Name) + result.DevDescResult.CorrectRtm.Success = false + result.DevDescResult.CorrectRtm.Expected = metadata.DeviceDescription.RtmManifest + result.DevDescResult.CorrectRtm.Got = metadata.RtmManifest.Name result.DevDescResult.Summary.Success = false result.Success = false } if metadata.DeviceDescription.OsManifest == metadata.OsManifest.Name { result.DevDescResult.CorrectOs.Success = true } else { - msg := fmt.Sprintf("Device Description listed wrong OS Manifest: %v vs. %v", metadata.DeviceDescription.OsManifest, metadata.OsManifest.Name) - result.DevDescResult.CorrectOs.setFalse(&msg) + log.Tracef("Device Description listed wrong OS Manifest: %v vs. %v", + metadata.DeviceDescription.OsManifest, metadata.OsManifest.Name) + result.DevDescResult.CorrectOs.Success = false + result.DevDescResult.CorrectOs.Expected = metadata.DeviceDescription.OsManifest + result.DevDescResult.CorrectOs.Got = metadata.OsManifest.Name result.DevDescResult.Summary.Success = false result.Success = false } // Check that every AppManifest has a corresponding AppDescription - result.DevDescResult.CorrectApps.Success = true + appDescriptions := make([]string, 0) + for _, a := range metadata.DeviceDescription.AppDescriptions { + appDescriptions = append(appDescriptions, a.AppManifest) + } for _, a := range metadata.AppManifests { + r := Result{ + Success: true, + ExpectedOneOf: appDescriptions, + Got: a.Name, + } if !contains(a.Name, appDescriptions) { - msg := fmt.Sprintf("Device Description does not list the following App Manifest: %v", a.Name) - result.DevDescResult.CorrectApps.setFalseMulti(&msg) + log.Tracef("Device Description does not list the following App Manifest: %v", a.Name) + r.Success = false result.DevDescResult.Summary.Success = false result.Success = false } + result.DevDescResult.CorrectApps = append(result.DevDescResult.CorrectApps, r) } // Check that the Rtm Manifest is compatible with the OS Manifest if contains(metadata.RtmManifest.Name, metadata.OsManifest.Rtms) { result.DevDescResult.RtmOsCompatibility.Success = true } else { - msg := fmt.Sprintf("RTM Manifest %v is not compatible with OS Manifest %v", metadata.RtmManifest.Name, metadata.OsManifest.Name) - result.DevDescResult.RtmOsCompatibility.setFalse(&msg) + log.Tracef("RTM Manifest %v is not compatible with OS Manifest %v", + metadata.RtmManifest.Name, metadata.OsManifest.Name) + result.DevDescResult.RtmOsCompatibility.Success = false + result.DevDescResult.RtmOsCompatibility.ExpectedOneOf = metadata.OsManifest.Rtms + result.DevDescResult.RtmOsCompatibility.Got = metadata.RtmManifest.Name result.DevDescResult.Summary.Success = false result.Success = false } // Check that the OS Manifest is compatible with all App Manifests - result.DevDescResult.OsAppsCompatibility.Success = true for _, a := range metadata.AppManifests { + r := Result{ + Success: true, + ExpectedOneOf: a.Oss, + Got: metadata.OsManifest.Name, + } if !contains(metadata.OsManifest.Name, a.Oss) { - msg := fmt.Sprintf("OS Manifest %v is not compatible with App Manifest %v", metadata.OsManifest.Name, a.Name) - result.DevDescResult.OsAppsCompatibility.setFalseMulti(&msg) + log.Tracef("OS Manifest %v is not compatible with App Manifest %v", metadata.OsManifest.Name, a.Name) + r.Success = false result.DevDescResult.Summary.Success = false result.Success = false } + result.DevDescResult.OsAppsCompatibility = + append(result.DevDescResult.OsAppsCompatibility, r) } // Validate policies if specified + result.PolicySuccess = true if policies != nil { - result.PolicySuccess = true - p, ok := policyEngines[polEng] if !ok { - msg := fmt.Sprintf("Internal error: policy engine %v not implemented", polEng) - result.ProcessingError = append(result.ProcessingError, msg) - log.Trace(msg) + log.Tracef("Internal error: policy engine %v not implemented", polEng) result.Success = false + result.ErrorCode = PolicyEngineNotImplemented + result.PolicySuccess = false } else { ok = p.Validate(policies, result) if !ok { + log.Trace("Custom policy validation failed") result.Success = false - msg := "Custom policy validation failed" - result.ProcessingError = append(result.ProcessingError, msg) - log.Warnf(msg) + result.ErrorCode = VerifyPolicies + result.PolicySuccess = false } } } else { @@ -735,43 +754,33 @@ func extendSha384(hash []byte, data []byte) []byte { return ret } -func verifyAr(attestationReport []byte, result *VerificationResult, - cas []*x509.Certificate, s Serializer, -) (bool, *AttestationReport) { - - if result == nil { - log.Warn("Provided Validation Result was nil") - return false, nil - } +func verifyAr(attestationReport []byte, cas []*x509.Certificate, s Serializer, +) (*AttestationReport, TokenResult, ErrorCode) { ar := AttestationReport{} //Validate Attestation Report signature - tokenRes, payload, ok := s.VerifyToken(attestationReport, cas) - result.ReportSignature = tokenRes.SignatureCheck + result, payload, ok := s.VerifyToken(attestationReport, cas) if !ok { log.Trace("Validation of Attestation Report failed") - result.Success = false - return false, &ar + return nil, result, VerifyAR } err := s.Unmarshal(payload, &ar) if err != nil { - msg := fmt.Sprintf("Parsing of Attestation Report failed: %v", err) - result.ProcessingError = append(result.ProcessingError, msg) - log.Trace(msg) - result.Success = false - return false, &ar + log.Tracef("Parsing of Attestation Report failed: %v", err) + return nil, result, ParseAR } - return true, &ar + return &ar, result, NotSet } -func verifyMetadata(ar *AttestationReport, result *VerificationResult, - cas []*x509.Certificate, s Serializer, -) (bool, *Metadata) { +func verifyMetadata(ar *AttestationReport, cas []*x509.Certificate, s Serializer, +) (*Metadata, *MetadataResult, bool) { - metadata := Metadata{} + metadata := &Metadata{} + result := &MetadataResult{} + success := true // Validate and unpack Rtm Manifest tokenRes, payload, ok := s.VerifyToken(ar.RtmManifest, cas) @@ -779,20 +788,21 @@ func verifyMetadata(ar *AttestationReport, result *VerificationResult, result.RtmResult.SignatureCheck = tokenRes.SignatureCheck if !ok { log.Trace("Validation of RTM Manifest failed") - result.Success = false + success = false } else { err := s.Unmarshal(payload, &metadata.RtmManifest) if err != nil { - msg := fmt.Sprintf("Unpacking of RTM Manifest failed: %v", err) - result.RtmResult.Summary.setFalseMulti(&msg) - result.Success = false + log.Tracef("Unpacking of RTM Manifest failed: %v", err) + result.RtmResult.Summary.Success = false + result.RtmResult.Summary.ErrorCode = ParseRTMManifest + success = false } else { result.RtmResult.MetaInfo = metadata.RtmManifest.MetaInfo result.RtmResult.ValidityCheck = checkValidity(metadata.RtmManifest.Validity) result.RtmResult.Details = metadata.RtmManifest.Details if !result.RtmResult.ValidityCheck.Success { result.RtmResult.Summary.Success = false - result.Success = false + success = false } } } @@ -803,20 +813,21 @@ func verifyMetadata(ar *AttestationReport, result *VerificationResult, result.OsResult.SignatureCheck = tokenRes.SignatureCheck if !ok { log.Trace("Validation of OS Manifest failed") - result.Success = false + success = false } else { err := s.Unmarshal(payload, &metadata.OsManifest) if err != nil { - msg := fmt.Sprintf("Unpacking of OS Manifest failed: %v", err) - result.OsResult.Summary.setFalseMulti(&msg) - result.Success = false + log.Tracef("Unpacking of OS Manifest failed: %v", err) + result.OsResult.Summary.Success = false + result.OsResult.Summary.ErrorCode = ParseOSManifest + success = false } else { result.OsResult.MetaInfo = metadata.OsManifest.MetaInfo result.OsResult.ValidityCheck = checkValidity(metadata.OsManifest.Validity) result.OsResult.Details = metadata.OsManifest.Details if !result.OsResult.ValidityCheck.Success { result.OsResult.Summary.Success = false - result.Success = false + success = false } } } @@ -830,22 +841,23 @@ func verifyMetadata(ar *AttestationReport, result *VerificationResult, result.AppResults[i].SignatureCheck = tokenRes.SignatureCheck if !ok { log.Trace("Validation of App Manifest failed") - result.Success = false + success = false } else { var am AppManifest err := s.Unmarshal(payload, &am) if err != nil { - msg := fmt.Sprintf("Unpacking of App Manifest failed: %v", err) - result.AppResults[i].Summary.setFalseMulti(&msg) - result.Success = false + log.Tracef("Unpacking of App Manifest failed: %v", err) + result.AppResults[i].Summary.Success = false + result.AppResults[i].Summary.ErrorCode = Parse + success = false } else { metadata.AppManifests = append(metadata.AppManifests, am) result.AppResults[i].MetaInfo = am.MetaInfo result.AppResults[i].ValidityCheck = checkValidity(am.Validity) if !result.AppResults[i].ValidityCheck.Success { - log.Trace("App Manifest invalid - " + am.Name) + log.Tracef("App Manifest %v validity check failed", am.Name) result.AppResults[i].Summary.Success = false - result.Success = false + success = false } } @@ -860,13 +872,14 @@ func verifyMetadata(ar *AttestationReport, result *VerificationResult, result.CompDescResult.SignatureCheck = tokenRes.SignatureCheck if !ok { log.Trace("Validation of Company Description Signatures failed") - result.Success = false + success = false } else { err := s.Unmarshal(payload, &metadata.CompanyDescription) if err != nil { - msg := fmt.Sprintf("Unpacking of Company Description failed: %v", err) - result.CompDescResult.Summary.setFalseMulti(&msg) - result.Success = false + log.Tracef("Unpacking of Company Description failed: %v", err) + result.CompDescResult.Summary.Success = false + result.CompDescResult.Summary.ErrorCode = Parse + success = false } else { result.CompDescResult.MetaInfo = metadata.CompanyDescription.MetaInfo result.CompDescResult.CompCertLevel = metadata.CompanyDescription.CertificationLevel @@ -875,7 +888,7 @@ func verifyMetadata(ar *AttestationReport, result *VerificationResult, if !result.CompDescResult.ValidityCheck.Success { log.Trace("Company Description invalid") result.CompDescResult.Summary.Success = false - result.Success = false + success = false } } } @@ -887,12 +900,14 @@ func verifyMetadata(ar *AttestationReport, result *VerificationResult, result.DevDescResult.SignatureCheck = tokenRes.SignatureCheck if !ok { log.Trace("Validation of Device Description failed") - result.Success = false + success = false } else { err := s.Unmarshal(payload, &metadata.DeviceDescription) if err != nil { - msg := fmt.Sprintf("Unpacking of Device Description failed: %v", err) - result.DevDescResult.Summary.setFalseMulti(&msg) + log.Tracef("Unpacking of Device Description failed: %v", err) + result.DevDescResult.Summary.Success = false + result.DevDescResult.Summary.ErrorCode = Parse + success = false } else { result.DevDescResult.MetaInfo = metadata.DeviceDescription.MetaInfo result.DevDescResult.Description = metadata.DeviceDescription.Description @@ -900,7 +915,7 @@ func verifyMetadata(ar *AttestationReport, result *VerificationResult, } } - return true, &metadata + return metadata, result, success } func checkValidity(val Validity) Result { @@ -909,26 +924,30 @@ func checkValidity(val Validity) Result { notBefore, err := time.Parse(time.RFC3339, val.NotBefore) if err != nil { - msg := fmt.Sprintf("Failed to parse NotBefore time. Time.Parse returned %v", err) - result.setFalse(&msg) + log.Tracef("Failed to parse NotBefore time. Time.Parse returned %v", err) + result.Success = false + result.ErrorCode = ParseTime return result } notAfter, err := time.Parse(time.RFC3339, val.NotAfter) if err != nil { - msg := fmt.Sprintf("Failed to parse NotAfter time. Time.Parse returned %v", err) - result.setFalse(&msg) + log.Tracef("Failed to parse NotAfter time. Time.Parse returned %v", err) + result.Success = false + result.ErrorCode = ParseTime return result } currentTime := time.Now() if notBefore.After(currentTime) { - msg := "Validity check failed: Artifact is not valid yet" - result.setFalse(&msg) + log.Trace("Validity check failed: Artifact is not valid yet") + result.Success = false + result.ErrorCode = NotYetValid } if currentTime.After(notAfter) { - msg := "Validity check failed: Artifact validity has expired" - result.setFalse(&msg) + log.Trace("Validity check failed: Artifact validity has expired") + result.Success = false + result.ErrorCode = Expired } return result @@ -963,51 +982,77 @@ func collectReferenceValues(metadata *Metadata) (map[string][]ReferenceValue, er return verMap, nil } -func checkExtensionUint8(cert *x509.Certificate, oid string, value uint8) error { +func checkExtensionUint8(cert *x509.Certificate, oid string, value uint8) (Result, bool) { for _, ext := range cert.Extensions { if ext.Id.String() == oid { if len(ext.Value) != 3 && len(ext.Value) != 4 { - return fmt.Errorf("extension %v value unexpected length %v (expected 3 or 4)", oid, len(ext.Value)) + log.Tracef("extension %v value unexpected length %v (expected 3 or 4)", + oid, len(ext.Value)) + return Result{Success: false, ErrorCode: OidLength}, false } if ext.Value[0] != 0x2 { - return fmt.Errorf("extension %v value[0] = %v does not match expected value 2 (tag Integer)", oid, ext.Value[0]) + log.Tracef("extension %v value[0]: %v does not match expected value 2 (tag Integer)", + oid, ext.Value[0]) + return Result{Success: false, ErrorCode: OidTag}, false } if ext.Value[1] == 0x1 { if ext.Value[2] != value { - return fmt.Errorf("extension %v value[2] = %v does not match expected value %v", oid, ext.Value[2], value) + log.Tracef("extension %v value[2]: %v does not match expected value %v", + oid, ext.Value[2], value) + return Result{ + Success: false, + Expected: strconv.FormatUint(uint64(value), 10), + Got: strconv.FormatUint(uint64(ext.Value[2]), 10), + }, false } } else if ext.Value[1] == 0x2 { // Due to openssl, the sign bit must remain zero for positive integers // even though this field is defined as unsigned int in the AMD spec // Thus, if the most significant bit is required, one byte of additional 0x00 padding is added if ext.Value[2] != 0x00 || ext.Value[3] != value { - return fmt.Errorf("extension %v value = %v%v does not match expected value %v", oid, ext.Value[2], ext.Value[3], value) + log.Tracef("extension %v value = %v%v does not match expected value %v", + oid, ext.Value[2], ext.Value[3], value) + return Result{ + Success: false, + Expected: strconv.FormatUint(uint64(value), 10), + Got: strconv.FormatUint(uint64(ext.Value[3]), 10), + }, false } } else { - return fmt.Errorf("extension %v value[1] = %v does not match expected value 1 or 2 (length of integer)", oid, ext.Value[1]) + log.Tracef("extension %v value[1]: %v does not match expected value 1 or 2 (length of integer)", + oid, ext.Value[1]) + return Result{Success: false, ErrorCode: OidLength}, false } - return nil + return Result{Success: true}, true } } - return fmt.Errorf("extension %v not present in certificate", oid) + log.Tracef("extension %v not present in certificate", oid) + return Result{Success: false, ErrorCode: OidNotPresent}, false } -func checkExtensionBuf(cert *x509.Certificate, oid string, buf []byte) error { +func checkExtensionBuf(cert *x509.Certificate, oid string, buf []byte) (Result, bool) { for _, ext := range cert.Extensions { if ext.Id.String() == oid { if cmp := bytes.Compare(ext.Value, buf); cmp != 0 { - return fmt.Errorf("extension %v value %v does not match expected value %v", oid, hex.EncodeToString(ext.Value), hex.EncodeToString(buf)) + log.Tracef("extension %v value %v does not match expected value %v", + oid, hex.EncodeToString(ext.Value), hex.EncodeToString(buf)) + return Result{ + Success: false, + Expected: hex.EncodeToString(buf), + Got: hex.EncodeToString(ext.Value), + }, false } - return nil + return Result{Success: false}, false } } - return fmt.Errorf("extension %v not present in certificate", oid) + log.Tracef("extension %v not present in certificate", oid) + return Result{Success: false, ErrorCode: OidNotPresent}, false } func contains(elem string, list []string) bool { @@ -1018,19 +1063,3 @@ func contains(elem string, list []string) bool { } return false } - -func (r *Result) setFalse(msg *string) { - r.Success = false - if msg != nil { - r.Details = *msg - log.Trace(*msg) - } -} - -func (r *ResultMulti) setFalseMulti(msg *string) { - r.Success = false - if msg != nil { - r.Details = append(r.Details, *msg) - log.Trace(*msg) - } -} diff --git a/attestationreport/cbor.go b/attestationreport/cbor.go index 5496f4a8..d4108bde 100644 --- a/attestationreport/cbor.go +++ b/attestationreport/cbor.go @@ -146,8 +146,9 @@ func (s CborSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok x5Chain, okConv := sig.Headers.Unprotected[cose.HeaderLabelX5Chain].([]interface{}) if !okConv { - msg := fmt.Sprintf("failed to parse x5c header: %v", err) - result.SignatureCheck[i].CertChainCheck.setFalse(&msg) + log.Warnf("failed to parse x5c header: %v", err) + result.SignatureCheck[i].CertChainCheck.Success = false + result.SignatureCheck[i].CertChainCheck.ErrorCode = ParseX5C ok = false continue } @@ -155,15 +156,17 @@ func (s CborSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok for _, rawCert := range x5Chain { cert, okCert := rawCert.([]byte) if !okCert { - msg := "failed to decode certificate chain" - result.SignatureCheck[i].CertChainCheck.setFalse(&msg) + log.Warnf("failed to decode certificate chain") + result.SignatureCheck[i].CertChainCheck.Success = false + result.SignatureCheck[i].CertChainCheck.ErrorCode = DecodeCertChain ok = false continue } x509Cert, err := x509.ParseCertificate(cert) if err != nil { - msg := fmt.Sprintf("failed to parse leaf certificate: %v", err) - result.SignatureCheck[i].CertChainCheck.setFalse(&msg) + log.Warnf("failed to parse leaf certificate: %v", err) + result.SignatureCheck[i].CertChainCheck.Success = false + result.SignatureCheck[i].CertChainCheck.ErrorCode = ParseCert ok = false continue } @@ -173,8 +176,9 @@ func (s CborSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok x509Chains, err := internal.VerifyCertChain(certChain, roots) if err != nil { - msg := fmt.Sprintf("failed to verify certificate chain: %v", err) - result.SignatureCheck[i].CertChainCheck.setFalse(&msg) + log.Warnf("failed to verify certificate chain: %v", err) + result.SignatureCheck[i].CertChainCheck.Success = false + result.SignatureCheck[i].CertChainCheck.ErrorCode = VerifyCertChain ok = false continue } @@ -192,8 +196,9 @@ func (s CborSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok publicKey, okKey := certChain[0].PublicKey.(*ecdsa.PublicKey) if !okKey { - msg := fmt.Sprintf("Failed to extract public key from certificate: %v", err) - result.SignatureCheck[i].SignCheck.setFalse(&msg) + log.Warnf("Failed to extract public key from certificate: %v", err) + result.SignatureCheck[i].SignCheck.Success = false + result.SignatureCheck[i].SignCheck.ErrorCode = ExtractPubKey ok = false continue } @@ -201,8 +206,9 @@ func (s CborSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok // create a verifier from a trusted private key verifier, err := cose.NewVerifier(cose.AlgorithmES256, publicKey) if err != nil { - msg := fmt.Sprintf("Failed to create verifier: %v", err) - result.SignatureCheck[i].SignCheck.setFalse(&msg) + log.Warnf("Failed to create verifier: %v", err) + result.SignatureCheck[i].SignCheck.Success = false + result.SignatureCheck[i].SignCheck.ErrorCode = Internal ok = false continue } @@ -212,10 +218,11 @@ func (s CborSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok err = msgToVerify.Verify(nil, verifiers...) if err != nil { - msg := fmt.Sprintf("Error verifying cbor signature: %v", err) + log.Warnf("Error verifying cbor signature: %v", err) // Can only be set for all signatures here for i := range result.SignatureCheck { - result.SignatureCheck[i].SignCheck.setFalse(&msg) + result.SignatureCheck[i].SignCheck.Success = false + result.SignatureCheck[i].SignCheck.ErrorCode = VerifySignature } return result, nil, false } else { diff --git a/attestationreport/iat.go b/attestationreport/iat.go index 615b1a40..48fd5a02 100644 --- a/attestationreport/iat.go +++ b/attestationreport/iat.go @@ -20,7 +20,6 @@ import ( "crypto/ecdsa" "crypto/x509" "encoding/hex" - "fmt" "github.com/Fraunhofer-AISEC/cmc/internal" "github.com/veraison/go-cose" @@ -60,14 +59,14 @@ func verifyIasMeasurements(iasM Measurement, nonce []byte, referenceValues []Ref log.Tracef("Parsing %v certificates", len(iasM.Certs)) certs, err := internal.ParseCertsDer(iasM.Certs) if err != nil { - msg := fmt.Sprintf("failed to parse IAS certificates: %v", err) - result.Summary.setFalse(&msg) + log.Tracef("failed to parse IAS certificates: %v", err) + result.Summary.SetErr(ParseEvidence) return result, false } if len(referenceValues) == 0 { - msg := "Could not find IAS Reference Value" - result.Summary.setFalse(&msg) + log.Tracef("Could not find IAS Reference Value") + result.Summary.SetErr(RefValNotPresent) return result, false } s := CborSerializer{} @@ -76,19 +75,19 @@ func verifyIasMeasurements(iasM Measurement, nonce []byte, referenceValues []Ref iatresult, payload, ok := verifyIat(iasM.Evidence, certs[0]) if !ok { - msg := "IAS signature verification failed" - result.Summary.setFalse(&msg) + log.Tracef("IAS signature verification failed") + result.Summary.SetErr(VerifySignature) return result, false } - result.Signature = iatresult + result.Signature.SignCheck = iatresult log.Trace("Unmarshalling CBOR IAT") iat := &Iat{} err = s.Unmarshal(payload, iat) if err != nil { - msg := fmt.Sprintf("Failed to unmarshal IAT: %v", err) - result.Summary.setFalse(&msg) + log.Tracef("Failed to unmarshal IAT: %v", err) + result.Summary.SetErr(ParseEvidence) return result, false } @@ -98,8 +97,10 @@ func verifyIasMeasurements(iasM Measurement, nonce []byte, referenceValues []Ref if bytes.Equal(nonce, iat.AuthChallenge) { result.Freshness.Success = true } else { - msg := fmt.Sprintf("Nonces mismatch: Supplied Nonce = %v, IAT Nonce = %v)", hex.EncodeToString(nonce), hex.EncodeToString(iat.AuthChallenge)) - result.Freshness.setFalse(&msg) + log.Tracef("Nonces mismatch: Supplied Nonce = %v, IAT Nonce = %v)", hex.EncodeToString(nonce), hex.EncodeToString(iat.AuthChallenge)) + result.Freshness.Success = false + result.Freshness.Expected = hex.EncodeToString(nonce) + result.Freshness.Got = hex.EncodeToString(iat.AuthChallenge) ok = false } @@ -108,8 +109,8 @@ func verifyIasMeasurements(iasM Measurement, nonce []byte, referenceValues []Ref // Verify certificate chain x509Chains, err := internal.VerifyCertChain(certs, cas) if err != nil { - msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) - result.Signature.CertChainCheck.setFalse(&msg) + log.Tracef("Failed to verify certificate chain: %v", err) + result.Signature.CertChainCheck.SetErr(VerifyCertChain) ok = false } else { result.Signature.CertChainCheck.Success = true @@ -132,8 +133,8 @@ func verifyIasMeasurements(iasM Measurement, nonce []byte, referenceValues []Ref for _, ver := range referenceValues { log.Tracef("Found reference value %v: %v", ver.Name, hex.EncodeToString(ver.Sha256)) if ver.Type != "IAS Reference Value" { - msg := fmt.Sprintf("IAS Reference Value invalid type %v", ver.Type) - result.Summary.setFalse(&msg) + log.Tracef("IAS Reference Value invalid type %v", ver.Type) + result.Summary.SetErr(RefValType) return result, false } found := false @@ -189,43 +190,34 @@ func verifyIasMeasurements(iasM Measurement, nonce []byte, referenceValues []Ref return result, ok } -func verifyIat(data []byte, cert *x509.Certificate) (SignatureResult, []byte, bool) { - - result := SignatureResult{ - CertChainCheck: Result{Success: true}, - } +func verifyIat(data []byte, cert *x509.Certificate) (Result, []byte, bool) { // create a Sign1Message from a raw COSE_Sign payload var msgToVerify cose.Sign1Message err := msgToVerify.UnmarshalCBOR(data) if err != nil { - log.Warnf("error unmarshalling cose: %v", err) - return result, nil, false + log.Tracef("error unmarshalling cose: %v", err) + return Result{Success: false, ErrorCode: ParseEvidence}, nil, false } publicKey, okKey := cert.PublicKey.(*ecdsa.PublicKey) if !okKey { - msg := fmt.Sprintf("Failed to extract public key from certificate: %v", err) - result.SignCheck.setFalse(&msg) - return result, nil, false + log.Tracef("Failed to extract public key from certificate: %v", err) + return Result{Success: false, ErrorCode: ExtractPubKey}, nil, false } // create a verifier from a trusted private key verifier, err := cose.NewVerifier(cose.AlgorithmES256, publicKey) if err != nil { - msg := fmt.Sprintf("Failed to create verifier: %v", err) - result.SignCheck.setFalse(&msg) - return result, nil, false + log.Tracef("Failed to create verifier: %v", err) + return Result{Success: false, ErrorCode: Internal}, nil, false } err = msgToVerify.Verify(nil, verifier) if err != nil { - msg := fmt.Sprintf("Failed to verify COSE token: %v", err) - result.SignCheck.setFalse(&msg) - return result, nil, false + log.Tracef("Failed to verify COSE token: %v", err) + return Result{Success: false, ErrorCode: VerifySignature}, nil, false } - result.SignCheck.Success = true - - return result, msgToVerify.Payload, true + return Result{Success: true}, msgToVerify.Payload, true } diff --git a/attestationreport/intel_helpers.go b/attestationreport/intel_helpers.go index f3457d56..32e83c46 100644 --- a/attestationreport/intel_helpers.go +++ b/attestationreport/intel_helpers.go @@ -25,13 +25,13 @@ import ( "encoding/binary" "encoding/hex" "encoding/json" - "errors" "fmt" "io" "math/big" "net/http" "os" "regexp" + "strconv" "time" "github.com/Fraunhofer-AISEC/cmc/internal" @@ -489,28 +489,28 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, 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", + log.Tracef("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) - result.SignCheck.setFalse(&msg) + result.SignCheck.SetErr(SignatureLength) 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", + log.Tracef("parsed QuoteSignatureData size doesn't match QuoteSignatureDataLen. expected: %v, got: %v\n", quoteSignatureSize, uint32(len(reportRaw)-signature_offset)) - result.SignCheck.setFalse(&msg) + result.SignCheck.SetErr(SignatureLength) return result, false } default: - msg := fmt.Sprintf("Quote Type not supported %v", quoteType) - result.SignCheck.setFalse(&msg) + log.Tracef("Quote Type not supported %v", quoteType) + result.SignCheck.SetErr(EvidenceType) 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) + log.Tracef("Signature Algorithm %v not supported", quoteSignatureType) + result.SignCheck.SetErr(UnsupportedAlgorithm) return result, false } @@ -533,8 +533,8 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, } if len(ak_pub) == 0 { - msg := "Failed to extract ECDSA public key from certificate" - result.SignCheck.setFalse(&msg) + log.Tracef("Failed to extract ECDSA public key from certificate") + result.SignCheck.SetErr(ExtractPubKey) return result, false } @@ -553,8 +553,8 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, // 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) + log.Tracef("Failed to verify ISV Enclave report signature") + result.SignCheck.SetErr(VerifySignature) return result, false } log.Trace("Successfully verified ISV Enclave report signature") @@ -579,16 +579,16 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, // 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) + log.Tracef("Failed to extract PCK public key from certificate") + result.SignCheck.SetErr(ExtractPubKey) 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) + log.Tracef("Failed to verify QE report signature") + result.SignCheck.SetErr(VerifySignature) return result, false } @@ -599,38 +599,38 @@ func VerifyIntelQuoteSignature(reportRaw []byte, quoteSignature any, 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) + log.Tracef("invalid SHA256(ECDSA Attestation Key || QE Authentication Data) || 32*0x00) in QEReport.ReportData. expected: %v, got: %v\n", reportDataRef, reportData) + result.CertChainCheck.SetErr(VerifyCertChain) return result, false } // Step 4: Parse and verify the entire PCK certificate chain var x509Chains [][]*x509.Certificate - + var code ErrorCode switch quoteType { case SGX_QUOTE_TYPE: - x509Chains, err = VerifyIntelCertChainFull(certs, CA_PROCESSOR, intelCache) + x509Chains, code = VerifyIntelCertChainFull(certs, CA_PROCESSOR, intelCache) case TDX_QUOTE_TYPE: - x509Chains, err = VerifyIntelCertChainFull(certs, CA_PLATFORM, intelCache) + x509Chains, code = VerifyIntelCertChainFull(certs, CA_PLATFORM, intelCache) } if err != nil { - msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) - result.CertChainCheck.setFalse(&msg) + log.Tracef("Failed to verify certificate chain: %v", err) + result.CertChainCheck.SetErr(code) 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) + log.Tracef("Failed to decode CA fingerprint %v: %v", fingerprint, err) + result.CertChainCheck.SetErr(ParseCAFingerprint) 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", + log.Tracef("CA fingerprint %v does not match measurement CA fingerprint %v", fingerprint, hex.EncodeToString(caFingerprint[:])) - result.CertChainCheck.setFalse(&msg) + result.CertChainCheck.SetErr(CaFingerprint) return result, false } result.CertChainCheck.Success = true @@ -649,16 +649,20 @@ 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) (TcbLevelResult, error) { + sgxExtensions SGXExtensionsValue, teeTcbSvn [16]byte, quoteType uint32) TcbLevelResult { var result TcbLevelResult if tcbInfo == nil || tcbKeyCert == nil { - return result, fmt.Errorf("invalid function parameter (null pointer exception)") + log.Tracef("invalid function parameter (null pointer exception)") + result.Summary.SetErr(Internal) + return result } 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 + // remove whitespaces + regex.ReplaceAllString(tcbInfoBodyRaw, "") + // remove "{"tcbInfo":" from beginning and signature + rest from the end + tcbInfoBodyRaw = tcbInfoBodyRaw[len(`{"tcbInfo":`) : len(tcbInfoBodyRaw)-128-16] // get checksum of tcb info body digest := sha256.Sum256([]byte(tcbInfoBodyRaw)) @@ -674,33 +678,32 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer // 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") + log.Tracef("failed to verify tcbInfo signature") + result.Summary.SetErr(VerifyTcbInfo) + return result } 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) + log.Tracef("tcbInfo has expired since: %v", tcbInfo.TcbInfo.NextUpdate) + result.Summary.SetErr(TcbInfoExpired) + return result } 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", + log.Tracef("FMSPC value from TcbInfo (%v) and FMSPC value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.Fmspc, sgxExtensions.Fmspc.Value) + result.Summary.SetErr(SgxFmpcMismatch) + return result + } 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", + log.Tracef("PCEID value from TcbInfo (%v) and PCEID value from SGX Extensions in PCK Cert (%v) do not match", tcbInfo.TcbInfo.PceId, sgxExtensions.PceId.Value) + result.Summary.SetErr(SgxPceidMismatch) + return result } // Checking tcb level @@ -720,33 +723,31 @@ func verifyTcbInfo(tcbInfo *TcbInfo, tcbInfoBodyRaw string, tcbKeyCert *x509.Cer result.Summary.Success = true result.TcbLevelDate = tcbLevel.TcbDate result.TcbLevelStatus = tcbLevel.TcbStatus - return result, nil + return result } // 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") + result.Summary.SetErr(TcbLevelUnsupported) + return result } // 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.SetErr(TcbLevelRevoked) + return result } result.Summary.Success = true result.TcbLevelDate = tcbLevel.TcbDate result.TcbLevelStatus = tcbLevel.TcbStatus - return result, nil + return result } - result.Summary.Success = false - result.Summary.Details = "TCB Level not supported" - return result, fmt.Errorf("TCB Level not supported") + result.Summary.SetErr(TcbLevelUnsupported) + return result } // helper function for verifyTcbInfo @@ -828,29 +829,47 @@ 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.Summary.Details = msg + log.Tracef("MRSIGNER mismatch. Expected: %v, Got: %v", + qeIdentity.EnclaveIdentity.Mrsigner, qeReportBody.MRSIGNER) result.Summary.Success = false + result.MrSigner = Result{ + Success: false, + Expected: hex.EncodeToString(qeIdentity.EnclaveIdentity.Mrsigner), + Got: hex.EncodeToString(qeReportBody.MRSIGNER[:]), + } return result, nil } + result.MrSigner = Result{Success: true} // 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 + log.Tracef("IsvProdId mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.IsvProdId, qeReportBody.ISVProdID) result.Summary.Success = false + result.IsvProdId = Result{ + Success: false, + Expected: strconv.FormatUint(uint64(qeIdentity.EnclaveIdentity.IsvProdId), 10), + Got: strconv.FormatUint(uint64(qeReportBody.ISVProdID), 10), + } return result, nil } + result.IsvProdId = Result{Success: true} // 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", + refMiscSelect := binary.LittleEndian.Uint32(qeIdentity.EnclaveIdentity.Miscselect) + reportMiscSelect := qeReportBody.MISCSELECT & miscselectMask + if refMiscSelect != reportMiscSelect { + log.Tracef("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 + result.MrSigner = Result{ + Success: false, + Expected: strconv.FormatUint(uint64(refMiscSelect), 10), + Got: strconv.FormatUint(uint64(reportMiscSelect), 10), + } return result, nil } + result.MrSigner = Result{Success: true} // check attributes attributes_quote := qeReportBody.Attributes @@ -861,11 +880,16 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q } } 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 + log.Tracef("attributes mismatch. Expected: %v, Got: %v", qeIdentity.EnclaveIdentity.Attributes, attributes_quote) result.Summary.Success = false + result.Attributes = Result{ + Success: false, + Expected: hex.EncodeToString(qeIdentity.EnclaveIdentity.Attributes), + Got: hex.EncodeToString(attributes_quote[:]), + } return result, nil } + result.Attributes = Result{Success: true} tcbStatus, tcbDate := getTcbStatusAndDateQE(qeIdentity, qeReportBody) log.Tracef("TcbStatus for Enclave's Identity tcbLevel (isvSvn: %v): '%v'", qeReportBody.ISVSVN, tcbStatus) @@ -874,7 +898,6 @@ func VerifyQEIdentity(qeReportBody *EnclaveReportBody, qeIdentity *QEIdentity, q case Revoked: fallthrough case NotSupported: - result.Summary.Details = "invalid tcbStatus" result.TcbLevelStatus = string(tcbStatus) result.TcbLevelDate = tcbDate result.Summary.Success = false @@ -1021,78 +1044,80 @@ func downloadCRL(uri string) (*x509.RevocationList, error) { } // Verifies a given SGX certificate chain, fetches CRLs and checks if the certs are outdated -func VerifyIntelCertChainFull(quoteCerts SgxCertificates, ca string, intelCache string) ([][]*x509.Certificate, error) { +func VerifyIntelCertChainFull(quoteCerts SgxCertificates, ca string, intelCache string) ([][]*x509.Certificate, ErrorCode) { // 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) + log.Tracef("Failed to verify pck certificate chain: %v", err) + return nil, VerifyPCKChain } // download CRLs from PCS root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME, "", intelCache) if err != nil { - msg := fmt.Sprintf("downloading ROOT CA CRL from PCS failed: %v", err) - return nil, errors.New(msg) + log.Tracef("downloading ROOT CA CRL from PCS failed: %v", err) + return nil, DownloadRootCRL } pck_crl_uri := fmt.Sprintf(PCS_PCK_CERT_CRL_URI, ca) pck_crl, err := fetchCRL(pck_crl_uri, PCK_CERT_CRL_NAME, ca, intelCache) if err != nil { - msg := fmt.Sprintf("downloading PCK Cert CRL from PCS failed: %v", err) - return nil, errors.New(msg) + log.Tracef("downloading PCK Cert CRL from PCS failed: %v", err) + return nil, DownloadPCKCRL } // 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) + log.Tracef("CRL check on rootCert failed: %v", err) + return nil, CRLCheckRoot } 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) + log.Tracef("CRL check on pckCert failed: %v", err) + return nil, CRLCheckPCK } - return x509CertChains, nil + return x509CertChains, NotSet } // Verifies the TCB signing cert chain -func VerifyTCBSigningCertChain(quoteCerts SgxCertificates, intelCache string) ([][]*x509.Certificate, error) { +func VerifyTCBSigningCertChain(quoteCerts SgxCertificates, intelCache string) ([][]*x509.Certificate, ErrorCode) { 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) + log.Tracef("Failed to verify TCB Signing certificate chain: %v", err) + return nil, VerifyTCBChain } root_ca_crl, err := fetchCRL(PCS_ROOT_CA_CRL_URI, ROOT_CA_CRL_NAME, "", intelCache) if err != nil { - msg := fmt.Sprintf("downloading Root CA CRL from PCS failed: %v", err) - return nil, errors.New(msg) + log.Tracef("downloading Root CA CRL from PCS failed: %v", err) + return nil, DownloadRootCRL } // 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) + log.Tracef("CRL check on TcbSigningCert failed: %v", err) + return nil, CRLCheckSigningCert } - return tcbSigningCertChain, nil + return tcbSigningCertChain, NotSet } 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) + log.Tracef("Quote version mismatch: Report = %v, supplied = %v", quote.Version, version) + r.Success = false + r.Expected = strconv.FormatUint(uint64(version), 10) + r.Got = strconv.FormatUint(uint64(quote.Version), 10) } else { r.Success = true } diff --git a/attestationreport/json.go b/attestationreport/json.go index 8d2266d2..674c09d7 100644 --- a/attestationreport/json.go +++ b/attestationreport/json.go @@ -164,8 +164,9 @@ func (s JsonSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok log.Debug("Using system certificate pool in absence of provided root certifcates") rootpool, err = x509.SystemCertPool() if err != nil { - msg := "Failed to setup trusted cert pool with system certificate pool" - result.Summary.setFalseMulti(&msg) + log.Warnf("Failed to setup trusted cert pool with system certificate pool") + result.Summary.Success = false + result.Summary.ErrorCode = SetupSystemCA return result, nil, false } } else { @@ -182,14 +183,16 @@ func (s JsonSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok jwsData, err := jose.ParseSigned(string(data)) if err != nil { - msg := fmt.Sprintf("Data could not be parsed: %v", err) - result.Summary.setFalseMulti(&msg) + log.Warnf("Data could not be parsed: %v", err) + result.Summary.Success = false + result.Summary.ErrorCode = ParseJSON return result, nil, false } if len(jwsData.Signatures) == 0 { - msg := "JWS does not contain signatures" - result.Summary.setFalseMulti(&msg) + log.Warnf("JWS does not contain signatures") + result.Summary.Success = false + result.Summary.ErrorCode = JWSNoSignatures return result, nil, false } @@ -200,8 +203,9 @@ func (s JsonSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok certs, err := sig.Protected.Certificates(opts) if err != nil { - msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) - result.SignatureCheck[i].CertChainCheck.setFalse(&msg) + log.Warnf("Failed to verify certificate chain: %v", err) + result.SignatureCheck[i].CertChainCheck.Success = false + result.SignatureCheck[i].CertChainCheck.ErrorCode = VerifyCertChain ok = false continue } @@ -221,20 +225,23 @@ func (s JsonSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok if err == nil { result.SignatureCheck[i].SignCheck.Success = true } else { - msg := fmt.Sprintf("Signature verification failed: %v", err) - result.SignatureCheck[i].SignCheck.setFalse(&msg) + log.Warnf("Signature verification failed: %v", err) + result.SignatureCheck[i].SignCheck.Success = false + result.SignatureCheck[i].SignCheck.ErrorCode = VerifySignature ok = false } if index[i] != i { - msg := "order of signatures incorrect" - result.Summary.setFalseMulti(&msg) + log.Warn("order of signatures incorrect") + result.Summary.Success = false + result.Summary.ErrorCode = JWSSignatureOrder } if i > 0 { if !bytes.Equal(payloads[i], payloads[i-1]) { - msg := "payloads differ for jws with multiple signatures" - result.Summary.setFalseMulti(&msg) + log.Warn("payloads differ for jws with multiple signatures") + result.Summary.Success = false + result.Summary.ErrorCode = JWSPayload } } } diff --git a/attestationreport/sgx.go b/attestationreport/sgx.go index b952e686..3cc89ed8 100644 --- a/attestationreport/sgx.go +++ b/attestationreport/sgx.go @@ -88,33 +88,33 @@ func verifySgxMeasurements(sgxM Measurement, nonce []byte, intelCache string, re log.Trace("Verifying SGX measurements") if len(referenceValues) == 0 { - msg := "Could not find SGX Reference Value" - result.Summary.setFalse(&msg) + log.Tracef("Could not find SGX Reference Value") + result.Summary.SetErr(RefValNotPresent) return result, false } else if len(referenceValues) > 1 { - msg := fmt.Sprintf("Report contains %v reference values. Currently, only one SGX Reference Value is supported", + log.Tracef("Report contains %v reference values. Currently, only one SGX Reference Value is supported", len(referenceValues)) - result.Summary.setFalse(&msg) + result.Summary.SetErr(RefValMultiple) return result, false } sgxReferenceValue := referenceValues[0] // Validate Parameters: if sgxM.Evidence == nil || len(sgxM.Evidence) < SGX_QUOTE_MIN_SIZE { - msg := "Invalid SGX Report." - result.Summary.setFalse(&msg) + log.Tracef("Invalid SGX Report") + result.Summary.SetErr(ParseEvidence) return result, false } if sgxReferenceValue.Type != "SGX Reference Value" { - msg := fmt.Sprintf("SGX Reference Value invalid type %v", sgxReferenceValue.Type) - result.Summary.setFalse(&msg) + log.Tracef("SGX Reference Value invalid type %v", sgxReferenceValue.Type) + result.Summary.SetErr(RefValType) return result, false } if sgxReferenceValue.Sgx == nil { - msg := "SGX Reference Value is null" - result.Summary.setFalse(&msg) + log.Tracef("SGX Reference Value details not present") + result.Summary.SetErr(DetailsNotPresent) return result, false } @@ -124,8 +124,8 @@ func verifySgxMeasurements(sgxM Measurement, nonce []byte, intelCache string, re // extract the attestation report into the SGXReport data structure sgxQuote, err = DecodeSgxReport(sgxM.Evidence) if err != nil { - msg := fmt.Sprintf("Failed to decode SGX report: %v", err) - result.Summary.setFalse(&msg) + log.Tracef("Failed to decode SGX report: %v", err) + result.Summary.SetErr(ParseEvidence) return result, false } } else { @@ -139,9 +139,12 @@ func verifySgxMeasurements(sgxM Measurement, nonce []byte, intelCache string, re 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) + log.Tracef("Nonces mismatch: Plain Nonce: %v, Expected: %v, Got = %v", + nonce, hex.EncodeToString(nonce64), + hex.EncodeToString(sgxQuote.ISVEnclaveReport.ReportData[:])) + result.Freshness.Success = false + result.Freshness.Expected = hex.EncodeToString(nonce64) + result.Freshness.Got = hex.EncodeToString(sgxQuote.ISVEnclaveReport.ReportData[:]) result.Summary.Success = false return result, false } else { @@ -151,8 +154,8 @@ func verifySgxMeasurements(sgxM Measurement, nonce []byte, intelCache string, re // Parse certificate chain referenceCerts, err := parseCertificates(sgxM.Certs, true) if err != nil { - msg := fmt.Sprintf("Failed to parse reference certificates: %v", err) - result.Summary.setFalse(&msg) + log.Tracef("Failed to parse reference certificates: %v", err) + result.Summary.SetErr(ParseCert) return result, false } @@ -161,20 +164,20 @@ func verifySgxMeasurements(sgxM Measurement, nonce []byte, intelCache string, re 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) + log.Tracef("Failed to parse certificate chain from QECertData: %v", err) + result.Summary.SetErr(ParseCert) return result, false } } else { - msg := fmt.Sprintf("QECertDataType not supported: %v", sgxQuote.QuoteSignatureData.QECertDataType) - result.Summary.setFalse(&msg) + log.Tracef("QECertDataType not supported: %v", sgxQuote.QuoteSignatureData.QECertDataType) + result.Summary.SetErr(ParseCert) 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) + log.Tracef("root cert is null") + result.Summary.SetErr(ParseCA) return result, false } @@ -190,49 +193,49 @@ func verifySgxMeasurements(sgxM Measurement, nonce []byte, intelCache string, re } if !bytes.Equal(quotePublicKeyBytes, referencePublicKeyBytes) { - msg := "root cert public key didn't match" - result.Summary.setFalse(&msg) + log.Tracef("root cert public key didn't match") + result.Summary.SetErr(CaFingerprint) 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) + log.Tracef("failed to parse SGX Extensions from PCK Certificate: %v", err) + result.Summary.SetErr(ParseExtensions) 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) + log.Tracef("Failed to parse tcbInfo: %v", err) + result.Summary.SetErr(ParseTcbInfo) 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) + result.SgxResult.TcbInfoCheck = verifyTcbInfo(&tcbInfo, + string(sgxReferenceValue.Sgx.Collateral.TcbInfo), + referenceCerts.TCBSigningCert, sgxExtensions, [16]byte{}, SGX_QUOTE_TYPE) + if !result.SgxResult.TcbInfoCheck.Summary.Success { + log.Tracef("Failed to verify TCB info structure: %v", err) + result.Summary.SetErr(VerifyTcbInfo) return result, false } - result.SgxResult.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) + log.Tracef("Failed to parse QE identity: %v", err) + result.Summary.SetErr(ParseQEIdentity) 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) + log.Tracef("Failed to verify QE Identity structure: %v", err) + result.Summary.SetErr(VerifyQEIdentityErr) return result, false } result.SgxResult.QeIdentityCheck = qeIdentityResult @@ -249,8 +252,8 @@ func verifySgxMeasurements(sgxM Measurement, nonce []byte, intelCache string, re // 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) + log.Tracef("Failed to verify SGX Report Body: %v", err) + result.Summary.SetErr(VerifySignature) result.Summary.Success = false return result, false } diff --git a/attestationreport/snp.go b/attestationreport/snp.go index aff2a5c6..3cb56ea2 100644 --- a/attestationreport/snp.go +++ b/attestationreport/snp.go @@ -25,6 +25,7 @@ import ( "encoding/hex" "fmt" "math/big" + "strconv" "github.com/Fraunhofer-AISEC/cmc/internal" ) @@ -93,33 +94,33 @@ func verifySnpMeasurements(snpM Measurement, nonce []byte, referenceValues []Ref ok := true if len(referenceValues) == 0 { - msg := "Could not find SNP Reference Value" - result.Summary.setFalse(&msg) + log.Trace("Could not find SNP Reference Value") + result.Summary.SetErr(RefValNotPresent) return result, false } else if len(referenceValues) > 1 { - msg := fmt.Sprintf("Report contains %v reference values. Currently, only 1 SNP Reference Value is supported", + log.Tracef("Report contains %v reference values. Currently, only 1 SNP Reference Value is supported", len(referenceValues)) - result.Summary.setFalse(&msg) + result.Summary.SetErr(RefValMultiple) return result, false } snpReferenceValue := referenceValues[0] if snpReferenceValue.Type != "SNP Reference Value" { - msg := fmt.Sprintf("SNP Reference Value invalid type %v", snpReferenceValue.Type) - result.Summary.setFalse(&msg) + log.Tracef("SNP Reference Value invalid type %v", snpReferenceValue.Type) + result.Summary.SetErr(RefValType) return result, false } if snpReferenceValue.Snp == nil { - msg := "SNP Reference Value does not contain policy" - result.Summary.setFalse(&msg) + log.Trace("SNP Reference Value does not contain policy") + result.Summary.SetErr(DetailsNotPresent) return result, false } // Extract the SNP attestation report data structure s, err := DecodeSnpReport(snpM.Evidence) if err != nil { - msg := fmt.Sprintf("Failed to decode SNP report: %v", err) - result.Summary.setFalse(&msg) + log.Tracef("Failed to decode SNP report: %v", err) + result.Summary.SetErr(ParseEvidence) return result, false } @@ -127,9 +128,11 @@ func verifySnpMeasurements(snpM Measurement, nonce []byte, referenceValues []Ref nonce64 := make([]byte, 64) copy(nonce64, nonce) if cmp := bytes.Compare(s.ReportData[:], nonce64); cmp != 0 { - msg := fmt.Sprintf("Nonces mismatch: Supplied Nonce = %v, Nonce in SNP Report = %v)", + log.Tracef("Nonces mismatch: Supplied Nonce = %v, Nonce in SNP Report = %v)", hex.EncodeToString(nonce), hex.EncodeToString(s.ReportData[:])) - result.Freshness.setFalse(&msg) + result.Freshness.Success = false + result.Freshness.Expected = hex.EncodeToString(nonce) + result.Freshness.Got = hex.EncodeToString(s.ReportData[:]) ok = false } else { result.Freshness.Success = true @@ -137,8 +140,8 @@ func verifySnpMeasurements(snpM Measurement, nonce []byte, referenceValues []Ref certs, err := internal.ParseCertsDer(snpM.Certs) if err != nil { - msg := fmt.Sprintf("Failed to parse certificates: %v", err) - result.Summary.setFalse(&msg) + log.Tracef("Failed to parse certificates: %v", err) + result.Summary.SetErr(ParseCert) return result, false } @@ -215,8 +218,10 @@ func verifySnpVersion(s snpreport, version uint32) (Result, bool) { r := Result{} ok := s.Version == version if !ok { - msg := fmt.Sprintf("SNP report version mismatch: Report = %v, supplied = %v", s.Version, version) - r.setFalse(&msg) + log.Tracef("SNP report version mismatch: Report = %v, supplied = %v", s.Version, version) + r.Success = false + r.Expected = strconv.FormatUint(uint64(version), 10) + r.Got = strconv.FormatUint(uint64(s.Version), 10) } else { r.Success = true } @@ -360,8 +365,8 @@ func verifySnpSignature( result := SignatureResult{} if len(reportRaw) < (header_offset + signature_offset) { - msg := "Internal Error: Report buffer too small" - result.SignCheck.setFalse(&msg) + log.Warn("Internal Error: Report buffer too small") + result.SignCheck.SetErr(Internal) return result, false } @@ -386,32 +391,32 @@ func verifySnpSignature( s.SetBytes(sRaw) // Examine SNP x509 extensions - extensionResult, ok := verifySnpExtensions(certs[0], &report) - result.ExtensionsCheck = &extensionResult + extensionResults, ok := verifySnpExtensions(certs[0], &report) + result.ExtensionsCheck = extensionResults if !ok { return result, false } // Check that the algorithm is supported if report.SignatureAlgo != ecdsa384_with_sha384 { - msg := fmt.Sprintf("Signature Algorithm %v not supported", report.SignatureAlgo) - result.SignCheck.setFalse(&msg) + log.Tracef("Signature Algorithm %v not supported", report.SignatureAlgo) + result.SignCheck.SetErr(UnsupportedAlgorithm) return result, false } // Extract the public key from the certificate pub, ok := certs[0].PublicKey.(*ecdsa.PublicKey) if !ok { - msg := "Failed to extract ECDSA public key from certificate" - result.SignCheck.setFalse(&msg) + log.Trace("Failed to extract ECDSA public key from certificate") + result.SignCheck.SetErr(ExtractPubKey) return result, false } // Verify ECDSA Signature represented by r and s ok = ecdsa.Verify(pub, digest[:], r, s) if !ok { - msg := "Failed to verify SNP report signature" - result.SignCheck.setFalse(&msg) + log.Trace("Failed to verify SNP report signature") + result.SignCheck.SetErr(VerifySignature) return result, false } log.Trace("Successfully verified SNP report signature") @@ -421,27 +426,29 @@ func verifySnpSignature( ca := certs[len(certs)-1] x509Chains, err := internal.VerifyCertChain(certs[:len(certs)-1], []*x509.Certificate{ca}) if err != nil { - msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) - result.CertChainCheck.setFalse(&msg) + log.Tracef("Failed to verify certificate chain: %v", err) + result.CertChainCheck.SetErr(VerifyCertChain) return result, false } // Verify that the reference value fingerprint matches the certificate fingerprint if fingerprint == "" { - msg := "Reference value SNP CA fingerprint not present" - result.CertChainCheck.setFalse(&msg) + log.Trace("Reference value SNP CA fingerprint not present") + result.CertChainCheck.SetErr(NotPresent) return result, false } refFingerprint, err := hex.DecodeString(fingerprint) if err != nil { - msg := fmt.Sprintf("Failed to decode CA fingerprint %v: %v", fingerprint, err) - result.CertChainCheck.setFalse(&msg) + log.Tracef("Failed to decode CA fingerprint %v: %v", fingerprint, err) + result.CertChainCheck.SetErr(ParseCAFingerprint) return result, false } caFingerprint := sha256.Sum256(ca.Raw) if !bytes.Equal(refFingerprint, caFingerprint[:]) { - msg := fmt.Sprintf("Reference Values CA fingerprint '%v' does not match trusted CA fingerprint '%v'", + log.Tracef("Reference Values CA fingerprint '%v' does not match trusted CA fingerprint '%v'", fingerprint, hex.EncodeToString(caFingerprint[:])) - result.CertChainCheck.setFalse(&msg) + result.CertChainCheck.Success = false + result.CertChainCheck.Expected = fingerprint + result.CertChainCheck.Got = hex.EncodeToString(caFingerprint[:]) return result, false } result.CertChainCheck.Success = true @@ -458,44 +465,45 @@ func verifySnpSignature( return result, true } -func verifySnpExtensions(cert *x509.Certificate, report *snpreport) (ResultMulti, bool) { - result := ResultMulti{} - ok := true +func verifySnpExtensions(cert *x509.Certificate, report *snpreport) ([]Result, bool) { + success := true + var ok bool + var r Result tcb := report.CurrentTcb - if err := checkExtensionUint8(cert, "1.3.6.1.4.1.3704.1.3.1", uint8(tcb)); err != nil { - msg := fmt.Sprintf("SEV BL Extension Check failed: %v", err) - result.setFalseMulti(&msg) - ok = false + results := make([]Result, 0) + + if r, ok = checkExtensionUint8(cert, "1.3.6.1.4.1.3704.1.3.1", uint8(tcb)); !ok { + log.Tracef("SEV BL Extension Check failed:") + success = false } + results = append(results, r) - if err := checkExtensionUint8(cert, "1.3.6.1.4.1.3704.1.3.2", uint8(tcb>>8)); err != nil { - msg := fmt.Sprintf("SEV TEE Extension Check failed: %v", err) - result.setFalseMulti(&msg) + if r, ok = checkExtensionUint8(cert, "1.3.6.1.4.1.3704.1.3.2", uint8(tcb>>8)); !ok { + log.Tracef("SEV TEE Extension Check failed") ok = false } + results = append(results, r) - if err := checkExtensionUint8(cert, "1.3.6.1.4.1.3704.1.3.3", uint8(tcb>>48)); err != nil { - msg := fmt.Sprintf("SEV SNP Extension Check failed: %v", err) - result.setFalseMulti(&msg) + if r, ok = checkExtensionUint8(cert, "1.3.6.1.4.1.3704.1.3.3", uint8(tcb>>48)); !ok { + log.Tracef("SEV SNP Extension Check failed") ok = false } + results = append(results, r) - if err := checkExtensionUint8(cert, "1.3.6.1.4.1.3704.1.3.8", uint8(tcb>>56)); err != nil { - msg := fmt.Sprintf("SEV UCODE Extension Check failed: %v", err) - result.setFalseMulti(&msg) + if r, ok = checkExtensionUint8(cert, "1.3.6.1.4.1.3704.1.3.8", uint8(tcb>>56)); !ok { + log.Tracef("SEV UCODE Extension Check failed") ok = false } + results = append(results, r) - if err := checkExtensionBuf(cert, "1.3.6.1.4.1.3704.1.4", report.ChipId[:]); err != nil { - msg := fmt.Sprintf("Chip ID Extension Check failed: %v", err) - result.setFalseMulti(&msg) + if r, ok = checkExtensionBuf(cert, "1.3.6.1.4.1.3704.1.4", report.ChipId[:]); !ok { + log.Tracef("Chip ID Extension Check failed") ok = false } + results = append(results, r) - result.Success = ok - - return result, ok + return results, success } func min(v []uint8) uint8 { diff --git a/attestationreport/sw.go b/attestationreport/sw.go index d044ae10..52f75829 100644 --- a/attestationreport/sw.go +++ b/attestationreport/sw.go @@ -17,7 +17,6 @@ package attestationreport import ( "bytes" - "fmt" ) func VerifySwMeasurements(swMeasurements []Measurement, refVals []ReferenceValue) ([]MeasurementResult, bool) { @@ -40,8 +39,8 @@ func VerifySwMeasurements(swMeasurements []Measurement, refVals []ReferenceValue } } if !found { - msg := fmt.Sprintf("no SW Measurement found for SW Reference Value %v (hash: %v)", v.Name, v.Sha256) - result.Summary.setFalse(&msg) + log.Tracef("no SW Measurement found for SW Reference Value %v (hash: %v)", v.Name, v.Sha256) + result.Summary.SetErr(RefValNoMatch) ok = false } swMeasurementResults = append(swMeasurementResults, result) @@ -59,8 +58,8 @@ func VerifySwMeasurements(swMeasurements []Measurement, refVals []ReferenceValue if !found { result := MeasurementResult{} result.SwResult.MeasName = swM.Description - msg := fmt.Sprintf("no SW Reference Value found for SW Measurement: %v", swM.Sha256) - result.Summary.setFalse(&msg) + log.Tracef("no SW Reference Value found for SW Measurement: %v", swM.Sha256) + result.Summary.SetErr(MeasurementNoMatch) swMeasurementResults = append(swMeasurementResults, result) ok = false } diff --git a/attestationreport/tdx.go b/attestationreport/tdx.go index 993bf9d0..2988567a 100644 --- a/attestationreport/tdx.go +++ b/attestationreport/tdx.go @@ -204,33 +204,33 @@ func verifyTdxMeasurements(tdxM Measurement, nonce []byte, intelCache string, re log.Trace("Verifying TDX measurements") if len(referenceValues) == 0 { - msg := "Could not find TDX Reference Value" - result.Summary.setFalse(&msg) + log.Tracef("Could not find TDX Reference Value") + result.Summary.SetErr(RefValNotPresent) return result, false } else if len(referenceValues) > 1 { - msg := fmt.Sprintf("Report contains %v reference values. Currently, only one TDX Reference Value is supported", + log.Tracef("Report contains %v reference values. Currently, only one TDX Reference Value is supported", len(referenceValues)) - result.Summary.setFalse(&msg) + result.Summary.SetErr(RefValMultiple) 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) + log.Tracef("TDX Reference Value invalid type %v", tdxReferenceValue.Type) + result.Summary.SetErr(RefValType) return result, false } if tdxReferenceValue.Tdx == nil { - msg := "TDX Reference Value does not contain policy" - result.Summary.setFalse(&msg) + log.Tracef("TDX Reference Value does not contain details") + result.Summary.SetErr(DetailsNotPresent) return result, false } // Currently only support for report version 4 tdxQuote, err := decodeTdxReportV4(tdxM.Evidence) if err != nil { - msg := fmt.Sprintf("Failed to decode TDX report: %v", err) - result.Summary.setFalse(&msg) + log.Tracef("Failed to decode TDX report: %v", err) + result.Summary.SetErr(ParseEvidence) return result, false } @@ -238,9 +238,11 @@ func verifyTdxMeasurements(tdxM Measurement, nonce []byte, intelCache string, re 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)", + log.Tracef("Nonces mismatch: Supplied Nonce = %v, Nonce in TDX Report = %v)", hex.EncodeToString(nonce), hex.EncodeToString(tdxQuote.QuoteBody.ReportData[:])) - result.Freshness.setFalse(&msg) + result.Freshness.Success = false + result.Freshness.Expected = hex.EncodeToString(nonce) + result.Freshness.Got = hex.EncodeToString(tdxQuote.QuoteBody.ReportData[:]) ok = false return result, ok } else { @@ -250,56 +252,56 @@ func verifyTdxMeasurements(tdxM Measurement, nonce []byte, intelCache string, re var quoteCerts SgxCertificates = tdxQuote.QuoteSignatureData.QECertData.QECertData if quoteCerts.RootCACert == nil || quoteCerts.IntermediateCert == nil || quoteCerts.PCKCert == nil { - msg := "incomplete certificate chain" - result.Summary.setFalse(&msg) + log.Tracef("incomplete certificate chain") + result.Summary.SetErr(VerifyCertChain) return result, false } // parse reference cert chain (TCBSigningCert chain) 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) + log.Tracef("Failed to parse reference certificates (TCBSigningCert + IntelRootCACert): %v", err) + result.Summary.SetErr(ParseCert) return result, false } - _, err = VerifyTCBSigningCertChain(referenceCerts, intelCache) - if err != nil { - msg := err.Error() - result.Summary.setFalse(&msg) + _, code := VerifyTCBSigningCertChain(referenceCerts, intelCache) + if code != NotSet { + log.Tracef("%v", err.Error()) + result.Summary.SetErr(code) 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) + log.Tracef("failed to parse SGX Extensions from PCK Certificate: %v", err) + result.Summary.SetErr(ParseCert) return result, false } // 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) + log.Tracef("Failed to parse tcbInfo: %v", err) + result.Summary.SetErr(ParseTcbInfo) return result, false } - tcbInfoResult, err := verifyTcbInfo(&tcbInfo, string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, + result.TdxResult.TcbInfoCheck = verifyTcbInfo(&tcbInfo, + string(tdxReferenceValue.Tdx.Collateral.TcbInfo), referenceCerts.TCBSigningCert, sgxExtensions, tdxQuote.QuoteBody.TeeTcbSvn, TDX_QUOTE_TYPE) - result.TdxResult.TcbInfoCheck = tcbInfoResult - if err != nil { - msg := fmt.Sprintf("Failed to verify TCB info structure: %v", err) - result.Summary.setFalse(&msg) + if !result.TdxResult.TcbInfoCheck.Summary.Success { + log.Tracef("Failed to verify TCB info structure: %v", err) + result.Summary.SetErr(VerifyTcbInfo) return result, false } // 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) + log.Tracef("Failed to parse QE Identity: %v", err) + result.Summary.SetErr(ParseQEIdentity) return result, false } @@ -307,8 +309,8 @@ func verifyTdxMeasurements(tdxM Measurement, nonce []byte, intelCache string, re string(tdxReferenceValue.Tdx.Collateral.QeIdentity), referenceCerts.TCBSigningCert, TDX_QUOTE_TYPE) result.TdxResult.QeIdentityCheck = qeIdentityResult if err != nil { - msg := fmt.Sprintf("Failed to verify QE Identity structure: %v", err) - result.Summary.setFalse(&msg) + log.Tracef("Failed to verify QE Identity structure: %v", err) + result.Summary.SetErr(VerifyQEIdentityErr) return result, false } @@ -324,8 +326,8 @@ func verifyTdxMeasurements(tdxM Measurement, nonce []byte, intelCache string, re // 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) + log.Tracef("Failed to verify TDX Report Body: %v", err) + result.Summary.SetErr(VerifySignature) result.Summary.Success = false return result, false } diff --git a/attestationreport/tpm.go b/attestationreport/tpm.go index 9690aff1..462973d0 100644 --- a/attestationreport/tpm.go +++ b/attestationreport/tpm.go @@ -21,7 +21,6 @@ import ( "crypto/sha256" "crypto/x509" "encoding/hex" - "fmt" "sort" "github.com/Fraunhofer-AISEC/cmc/internal" @@ -50,8 +49,8 @@ func verifyTpmMeasurements(tpmM Measurement, nonce []byte, referenceValues []Ref // Extract TPM Quote (TPMS ATTEST) and signature tpmsAttest, err := tpm2.DecodeAttestationData(tpmM.Evidence) if err != nil { - msg := fmt.Sprintf("Failed to decode TPM attestation data: %v", err) - result.Summary.setFalse(&msg) + log.Warnf("Failed to decode TPM attestation data: %v", err) + result.Summary.SetErr(ParseEvidence) return result, false } @@ -59,9 +58,11 @@ func verifyTpmMeasurements(tpmM Measurement, nonce []byte, referenceValues []Ref if bytes.Equal(nonce, tpmsAttest.ExtraData) { result.Freshness.Success = true } else { - msg := fmt.Sprintf("Nonces mismatch: Supplied Nonce = %v, TPM Quote Nonce = %v)", + log.Warnf("Nonces mismatch: Supplied Nonce = %v, TPM Quote Nonce = %v)", hex.EncodeToString(nonce), hex.EncodeToString(tpmsAttest.ExtraData)) - result.Freshness.setFalse(&msg) + result.Freshness.Success = false + result.Freshness.Expected = hex.EncodeToString(nonce) + result.Freshness.Got = hex.EncodeToString(tpmsAttest.ExtraData) ok = false } @@ -80,17 +81,19 @@ func verifyTpmMeasurements(tpmM Measurement, nonce []byte, referenceValues []Ref log.Trace("Aggregated PCR matches quote PCR") result.TpmResult.AggPcrQuoteMatch.Success = true } else { - msg := fmt.Sprintf("Aggregated PCR does not match Quote PCR: %v vs. %v", + log.Warnf("Aggregated PCR does not match Quote PCR: %v vs. %v", hex.EncodeToString(verPcr[:]), hex.EncodeToString(tpmsAttest.AttestedQuoteInfo.PCRDigest)) - result.TpmResult.AggPcrQuoteMatch.setFalse(&msg) + result.TpmResult.AggPcrQuoteMatch.Success = false + result.TpmResult.AggPcrQuoteMatch.Expected = hex.EncodeToString(verPcr[:]) + result.TpmResult.AggPcrQuoteMatch.Got = hex.EncodeToString(tpmsAttest.AttestedQuoteInfo.PCRDigest) ok = false } mCerts, err := internal.ParseCertsDer(tpmM.Certs) if err != nil { - msg := fmt.Sprintf("Failed to load measurement certs: %v", err) - result.Signature.CertChainCheck.setFalse(&msg) + log.Warnf("Failed to parse measurement certs: %v", err) + result.Signature.CertChainCheck.SetErr(ParseCert) result.Summary.Success = false return result, false } @@ -102,8 +105,8 @@ func verifyTpmMeasurements(tpmM Measurement, nonce []byte, referenceValues []Ref x509Chains, err := internal.VerifyCertChain(mCerts, cas) if err != nil { - msg := fmt.Sprintf("Failed to verify certificate chain: %v", err) - result.Signature.CertChainCheck.setFalse(&msg) + log.Warnf("Failed to verify certificate chain: %v", err) + result.Signature.CertChainCheck.SetErr(VerifyCertChain) ok = false } else { result.Signature.CertChainCheck.Success = true @@ -333,45 +336,38 @@ func recalculatePcrs(tpmM Measurement, referenceValues []ReferenceValue) (map[in } func verifyTpmQuoteSignature(quote, sig []byte, cert *x509.Certificate) Result { - result := Result{} - result.Success = true buf := new(bytes.Buffer) buf.Write((sig)) tpmtSig, err := tpm2.DecodeSignature(buf) if err != nil { - msg := fmt.Sprintf("Failed to decode TPM signature: %v", err) - result.setFalse(&msg) - return result + log.Warnf("Failed to decode TPM signature: %v", err) + return Result{Success: false, ErrorCode: Parse} } if tpmtSig.Alg != tpm2.AlgRSASSA { - msg := fmt.Sprintf("Hash algorithm %v not supported", tpmtSig.Alg) - result.setFalse(&msg) - return result + log.Warnf("Hash algorithm %v not supported", tpmtSig.Alg) + return Result{Success: false, ErrorCode: UnsupportedAlgorithm} } // Extract public key from x509 certificate pubKey, ok := cert.PublicKey.(*rsa.PublicKey) if !ok { - msg := "Failed to extract public key from certificate" - result.setFalse(&msg) - return result + log.Warnf("Failed to extract public key from certificate") + return Result{Success: false, ErrorCode: ExtractPubKey} } hashAlg, err := tpmtSig.RSA.HashAlg.Hash() if err != nil { - msg := "Hash algorithm not supported" - result.setFalse(&msg) - return result + log.Warnf("Hash algorithm not supported") + return Result{Success: false, ErrorCode: UnsupportedAlgorithm} } // Hash the quote and Verify the TPM Quote signature hashed := sha256.Sum256(quote) err = rsa.VerifyPKCS1v15(pubKey, hashAlg, hashed[:], tpmtSig.RSA.Signature) if err != nil { - msg := fmt.Sprintf("Failed to verify TPM quote signature: %v", err) - result.setFalse(&msg) - return result + log.Warnf("Failed to verify TPM quote signature: %v", err) + return Result{Success: false, ErrorCode: VerifySignature} } - return result + return Result{Success: true} } diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index c8b44e65..d936ae2a 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -17,7 +17,9 @@ package attestationreport import ( "crypto/x509" + "fmt" "math/big" + "strings" "time" "github.com/Fraunhofer-AISEC/cmc/internal" @@ -28,20 +30,23 @@ import ( type VerificationResult struct { Type string `json:"type"` Success bool `json:"raSuccessful"` - Prover string `json:"prover,omitempty"` // Name of the proving device the report was created for - Created string `json:"created,omitempty"` // Timestamp the attestation verification was completed - SwCertLevel int `json:"swCertLevel"` // Overall certification level for the software stack - FreshnessCheck Result `json:"freshnessCheck"` // Result for comparison of the expected nonce to the one provided in the attestation report + ErrorCode ErrorCode `json:"errorCode,omitempty"` // Set in case of global errors + Prover string `json:"prover,omitempty"` // Name of the proving device the report was created for + Created string `json:"created,omitempty"` // Timestamp the attestation verification was completed + SwCertLevel int `json:"swCertLevel"` // Overall certification level for the software stack + FreshnessCheck Result `json:"freshnessCheck"` // Result for comparison of the expected nonce to the one provided in the attestation report Measurements []MeasurementResult `json:"measurements"` ReportSignature []SignatureResult `json:"reportSignatureCheck"` // Result for validation of the overall report signature - CompDescResult *CompDescResult `json:"companyValidation,omitempty"` - RtmResult ManifestResult `json:"rtmValidation"` - OsResult ManifestResult `json:"osValidation"` - AppResults []ManifestResult `json:"appValidation,omitempty"` - DevDescResult DevDescResult `json:"deviceDescValidation"` - PolicySuccess bool `json:"policySuccess,omitempty"` // Result of custom policy validation (if utilized) - ProcessingError []string `json:"processingError,omitempty"` // Documentation of processing errors (dependent from provided Attestation Report) which hindered a complete validation - InternalError bool `json:"internalError,omitempty"` + MetadataResult + PolicySuccess bool `json:"policySuccess,omitempty"` // Result of custom policy validation (if utilized) +} + +type MetadataResult struct { + CompDescResult *CompDescResult `json:"companyValidation,omitempty"` + RtmResult ManifestResult `json:"rtmValidation"` + OsResult ManifestResult `json:"osValidation"` + AppResults []ManifestResult `json:"appValidation,omitempty"` + DevDescResult DevDescResult `json:"deviceDescValidation"` } // CompDescResult represents the results of the validation of the @@ -49,7 +54,7 @@ type VerificationResult struct { type CompDescResult struct { MetaInfo CompCertLevel int `json:"compCertLevel"` // Certification level for the company operating the device - Summary ResultMulti `json:"resultSummary"` // Summarizing value illustrating whether any issues were detected during validation of the Company Description + Summary Result `json:"result"` // Summarizing value illustrating whether any issues were detected during validation of the Company Description SignatureCheck []SignatureResult `json:"signatureValidation"` // Results for validation of the Description Signatures and the used certificates ValidityCheck Result `json:"validityCheck"` // Result from checking the validity of the description } @@ -58,7 +63,7 @@ type CompDescResult struct { // manifest provided in the Attestation Report. type ManifestResult struct { MetaInfo - Summary ResultMulti `json:"resultSummary"` + Summary Result `json:"result"` SignatureCheck []SignatureResult `json:"signatureValidation"` ValidityCheck Result `json:"validityCheck"` Details any `json:"details,omitempty"` @@ -70,12 +75,12 @@ type DevDescResult struct { MetaInfo Description string `json:"description"` Location string `json:"location"` - Summary ResultMulti `json:"resultSummary"` + Summary Result `json:"result"` CorrectRtm Result `json:"correctRtm"` CorrectOs Result `json:"correctOs"` - CorrectApps ResultMulti `json:"correctApps"` + CorrectApps []Result `json:"correctApps"` RtmOsCompatibility Result `json:"rtmOsCompatibility"` - OsAppsCompatibility ResultMulti `json:"osAppCompatibility"` + OsAppsCompatibility []Result `json:"osAppCompatibility"` SignatureCheck []SignatureResult `json:"signatureValidation"` } @@ -158,7 +163,7 @@ type BooleanMatch struct { } type TcbCheck struct { - Summary Result `json:"resultSummary"` + Summary Result `json:"result"` Bl VersionCheck `json:"bl"` Tee VersionCheck `json:"tee"` Snp VersionCheck `json:"snp"` @@ -166,7 +171,7 @@ type TcbCheck struct { } type PolicyCheck struct { - Summary Result `json:"resultSummary"` + Summary Result `json:"result"` Abi VersionCheck `json:"abi"` Smt BooleanMatch `json:"smt"` Migration BooleanMatch `json:"migration"` @@ -182,6 +187,10 @@ type AttributesCheck struct { type TcbLevelResult struct { Summary Result `json:"success"` + MrSigner Result `json:"mrsigner"` + IsvProdId Result `json:"isvProdId"` + MiscSelect Result `json:"miscSelect"` + Attributes Result `json:"attributes"` TcbLevelStatus string `json:"status"` TcbLevelDate time.Time `json:"date"` } @@ -210,7 +219,7 @@ type SignatureResult struct { ValidatedCerts [][]X509CertExtracted `json:"validatedCerts"` //Stripped information from validated x509 cert chain(s) for additional checks from the policies module SignCheck Result `json:"signatureVerification"` // Result from checking the signature has been calculated with this certificate CertChainCheck Result `json:"certChainValidation"` // Result from validatint the certification chain back to a shared root of trust - ExtensionsCheck *ResultMulti `json:"extensionsCheck,omitempty"` + ExtensionsCheck []Result `json:"extensionsCheck,omitempty"` } // X509CertExtracted represents a x509 certificate with attributes @@ -337,27 +346,92 @@ func ExtKeyUsageToString(usage []x509.ExtKeyUsage) []string { return res } -// Result is a generic type for storing a boolean result value -// and details on the validation (used in case of errors). -type Result struct { - Success bool `json:"success"` - Details string `json:"details,omitempty"` -} - -// ResultMulti is a generic type for storing a boolean result value -// and possibly multiple details on the validation (used in case of errors). -type ResultMulti struct { - Success bool `json:"success"` - Details []string `json:"details,omitempty"` -} - // TokenResult is a helper struct for the validation of JWS or COSE tokens focussing // on the validation of the provided signatures. type TokenResult struct { - Summary ResultMulti `json:"resultSummary"` + Summary Result `json:"result"` SignatureCheck []SignatureResult `json:"signatureValidation"` } +type ErrorCode int + +const ( + NotSet ErrorCode = iota + CaFingerprint + CRLCheckRoot + CRLCheckPCK + CRLCheckSigningCert + DecodeCertChain + UnknownSerialization + DownloadRootCRL + DownloadPCKCRL + EvidenceLength + EvidenceType + Expired + ExtractPubKey + Internal + InvalidCertificationLevel + JWSNoSignatures + JWSSignatureOrder + JWSPayload + MeasurementNoMatch + MeasurementTypeNotSupported + NotPresent + NotYetValid + OidLength + OidNotPresent + OidTag + Parse + ParseAR + ParseX5C + ParseCA + ParseCAFingerprint + ParseCert + ParseTcbInfo + ParseJSON + ParseOSManifest + ParseEvidence + ParseExtensions + ParseQEIdentity + ParseRTMManifest + ParseTime + PolicyEngineNotImplemented + RefValTypeNotSupported + SetupSystemCA + SgxFmpcMismatch + SgxPceidMismatch + SignatureLength + DetailsNotPresent + RefValMultiple + RefValNotPresent + RefValType + RefValNoMatch + TcbInfoExpired + TcbLevelUnsupported + TcbLevelRevoked + UnsupportedAlgorithm + VerifyAR + VerifyCertChain + VerifyPCKChain + VerifyOSManifest + VerifyPolicies + VerifyQEIdentityErr + VerifyRTMManifest + VerifySignature + VerifyTCBChain + VerifyTcbInfo + ExtensionsCheck +) + +type Result struct { + Success bool `json:"success"` + Got string `json:"got"` + Expected string `json:"expected,omitempty"` + ExpectedOneOf []string `json:"expectedOneOf,omitempty"` // Required for compatibility + ExpectedBetween []string `json:"expectedBetween,omitempty"` // Required for validity + ErrorCode ErrorCode `json:"errorCode"` +} + // ExtractX509Infos extracts relevant attributes from cert and transform some attribute // into a more human-readable form by translating enums to a string representations. func ExtractX509Infos(cert *x509.Certificate) X509CertExtracted { @@ -439,3 +513,279 @@ func ExtractX509Infos(cert *x509.Certificate) X509CertExtracted { return certExtracted } + +func (e ErrorCode) String() string { + switch e { + case NotSet: + return fmt.Sprintf("%v (Error code not set)", int(e)) + case CaFingerprint: + return fmt.Sprintf("%v (CA fingerprint error)", int(e)) + case CRLCheckRoot: + return fmt.Sprintf("%v (CRL check root error)", int(e)) + case CRLCheckPCK: + return fmt.Sprintf("%v (CRL check PCK error)", int(e)) + case CRLCheckSigningCert: + return fmt.Sprintf("%v (CRL check signing certificate error)", int(e)) + case DecodeCertChain: + return fmt.Sprintf("%v (Decode certificate chain error)", int(e)) + case UnknownSerialization: + return fmt.Sprintf("%v (Unknown serialization error)", int(e)) + case DownloadRootCRL: + return fmt.Sprintf("%v (Download root CRL error)", int(e)) + case DownloadPCKCRL: + return fmt.Sprintf("%v (Download PCK CRL error)", int(e)) + case EvidenceLength: + return fmt.Sprintf("%v (Evidence length error)", int(e)) + case EvidenceType: + return fmt.Sprintf("%v (Evidence type error)", int(e)) + case Expired: + return fmt.Sprintf("%v (Validity Expired error)", int(e)) + case ExtractPubKey: + return fmt.Sprintf("%v (Extract public key error)", int(e)) + case Internal: + return fmt.Sprintf("%v (Internal error)", int(e)) + case InvalidCertificationLevel: + return fmt.Sprintf("%v (Invalid certification level error)", int(e)) + case JWSNoSignatures: + return fmt.Sprintf("%v (JWS no signatures error)", int(e)) + case JWSSignatureOrder: + return fmt.Sprintf("%v (JWS signature order error)", int(e)) + case JWSPayload: + return fmt.Sprintf("%v (JWS payload error)", int(e)) + case MeasurementNoMatch: + return fmt.Sprintf("%v (Measurement no match error)", int(e)) + case MeasurementTypeNotSupported: + return fmt.Sprintf("%v (Measurement type not supported error)", int(e)) + case NotPresent: + return fmt.Sprintf("%v (Not present error)", int(e)) + case NotYetValid: + return fmt.Sprintf("%v (Not yet valid error)", int(e)) + case OidLength: + return fmt.Sprintf("%v (OID length error)", int(e)) + case OidNotPresent: + return fmt.Sprintf("%v (OID not present error)", int(e)) + case OidTag: + return fmt.Sprintf("%v (OID tag error)", int(e)) + case Parse: + return fmt.Sprintf("%v (Parse error)", int(e)) + case ParseAR: + return fmt.Sprintf("%v (Parse AR error)", int(e)) + case ParseX5C: + return fmt.Sprintf("%v (Parse X5C error)", int(e)) + case ParseCA: + return fmt.Sprintf("%v (Parse CA error)", int(e)) + case ParseCAFingerprint: + return fmt.Sprintf("%v (Parse CA fingerprint error)", int(e)) + case ParseCert: + return fmt.Sprintf("%v (Parse certificate error)", int(e)) + case ParseTcbInfo: + return fmt.Sprintf("%v (Parse TCB info error)", int(e)) + case ParseJSON: + return fmt.Sprintf("%v (Parse JSON error)", int(e)) + case ParseOSManifest: + return fmt.Sprintf("%v (Parse OS manifest error)", int(e)) + case ParseEvidence: + return fmt.Sprintf("%v (Parse evidence error)", int(e)) + case ParseExtensions: + return fmt.Sprintf("%v (Parse extensions error)", int(e)) + case ParseQEIdentity: + return fmt.Sprintf("%v (Parse QE identity error)", int(e)) + case ParseRTMManifest: + return fmt.Sprintf("%v (Parse RTM manifest error)", int(e)) + case ParseTime: + return fmt.Sprintf("%v (Parse time error)", int(e)) + case PolicyEngineNotImplemented: + return fmt.Sprintf("%v (Policy engine not implemented error)", int(e)) + case RefValTypeNotSupported: + return fmt.Sprintf("%v (Reference value type not supported error)", int(e)) + case SetupSystemCA: + return fmt.Sprintf("%v (Setup system CA error)", int(e)) + case SgxFmpcMismatch: + return fmt.Sprintf("%v (SGX FMPC mismatch error)", int(e)) + case SgxPceidMismatch: + return fmt.Sprintf("%v (SGX PCEID mismatch error)", int(e)) + case SignatureLength: + return fmt.Sprintf("%v (Signature length error)", int(e)) + case DetailsNotPresent: + return fmt.Sprintf("%v (Details not present error)", int(e)) + case RefValMultiple: + return fmt.Sprintf("%v (Reference value multiple error)", int(e)) + case RefValNotPresent: + return fmt.Sprintf("%v (Reference value not present error)", int(e)) + case RefValType: + return fmt.Sprintf("%v (Reference value type error)", int(e)) + case RefValNoMatch: + return fmt.Sprintf("%v (Reference value no match error)", int(e)) + case TcbInfoExpired: + return fmt.Sprintf("%v (TCB info expired error)", int(e)) + case TcbLevelUnsupported: + return fmt.Sprintf("%v (TCB level unsupported error)", int(e)) + case TcbLevelRevoked: + return fmt.Sprintf("%v (TCB level revoked error)", int(e)) + case UnsupportedAlgorithm: + return fmt.Sprintf("%v (Unsupported algorithm error)", int(e)) + case VerifyAR: + return fmt.Sprintf("%v (Verify AR error)", int(e)) + case VerifyCertChain: + return fmt.Sprintf("%v (Verify certificate chain error)", int(e)) + case VerifyPCKChain: + return fmt.Sprintf("%v (Verify PCK chain error)", int(e)) + case VerifyOSManifest: + return fmt.Sprintf("%v (Verify OS manifest error)", int(e)) + case VerifyPolicies: + return fmt.Sprintf("%v (Verify policies error)", int(e)) + case VerifyQEIdentityErr: + return fmt.Sprintf("%v (Verify QE identity error)", int(e)) + case VerifyRTMManifest: + return fmt.Sprintf("%v (Verify RTM manifest error)", int(e)) + case VerifySignature: + return fmt.Sprintf("%v (Verify signature error)", int(e)) + case VerifyTCBChain: + return fmt.Sprintf("%v (Verify TCB chain error)", int(e)) + case VerifyTcbInfo: + return fmt.Sprintf("%v (Verify TCB info error)", int(e)) + case ExtensionsCheck: + return fmt.Sprintf("%v (Extensions check error)", int(e)) + default: + return fmt.Sprintf("Unknown error code: %v", int(e)) + } +} + +func (r *Result) SetErr(e ErrorCode) { + r.Success = false + r.ErrorCode = e +} + +func (r *Result) PrintErr(format string, args ...interface{}) { + if r.Success { + return + } + if r.ErrorCode == NotSet { + log.Warnf("%v failed", fmt.Sprintf(format, args...)) + } else { + log.Warnf("%v failed with error code %v", fmt.Sprintf(format, args...), r.ErrorCode) + } + if r.Expected != "" { + log.Warnf("\tExpected: %v", r.Expected) + log.Warnf("\tGot : %v", r.Got) + } + if len(r.ExpectedBetween) == 2 { + log.Warnf("\tExpected: %v - %v", r.ExpectedBetween[0], r.ExpectedBetween[1]) + log.Warnf("\tGot : %v", r.Got) + } + if len(r.ExpectedOneOf) > 0 { + log.Warnf("\tExpected: %v", strings.Join(r.ExpectedOneOf, ",")) + log.Warnf("\tGot : %v", r.Got) + } +} + +func (r *SignatureResult) PrintErr(format string, args ...interface{}) { + r.SignCheck.PrintErr("%v signature check", fmt.Sprintf(format, args...)) + r.CertChainCheck.PrintErr("%v cert chain check", fmt.Sprintf(format, args...)) + for _, e := range r.ExtensionsCheck { + e.PrintErr("%v extension check", fmt.Sprintf(format, args...)) + } +} + +func (r *VerificationResult) PrintErr() { + + if !r.Success { + + if r.ErrorCode == NotSet { + log.Warn("Verification failed") + } else { + log.Warnf("Verification failed with error code: %v", r.ErrorCode) + } + + r.FreshnessCheck.PrintErr("Report freshness check") + + for _, m := range r.Measurements { + m.Summary.PrintErr("%v", m.Type) + m.Freshness.PrintErr("Measurement freshness check") + m.Signature.PrintErr("Measurement") + for _, a := range m.Artifacts { + if !a.Success { + details := "" + if a.Pcr != nil { + details = "PCR%v" + } + log.Warnf("%v Measurement %v: %v verification failed", details, a.Name, a.Digest) + } + } + if m.TpmResult != nil { + m.TpmResult.AggPcrQuoteMatch.PrintErr("Aggregated PCR verification") + for _, p := range m.TpmResult.PcrMatch { + if !p.Success { + log.Warnf("PCR%v calculated: %v, measured: %v", p.Pcr, p.Calculated, + p.Measured) + } + } + } + if m.SnpResult != nil { + m.SnpResult.VersionMatch.PrintErr("Version match") + // TODO + log.Warnf("Detailed SNP evaluation not yet implemented") + } + if m.SgxResult != nil { + m.SgxResult.VersionMatch.PrintErr("Version match") + // TODO + log.Warnf("Detailed SNP evaluation not yet implemented") + } + if m.TdxResult != nil { + m.TdxResult.VersionMatch.PrintErr("Version match") + // TODO + log.Warnf("Detailed SNP evaluation not yet implemented") + } + } + + for _, s := range r.ReportSignature { + s.PrintErr("Report") + } + + if r.CompDescResult != nil { + r.CompDescResult.Summary.PrintErr("Company description check") + for _, s := range r.CompDescResult.SignatureCheck { + s.PrintErr("Company Description") + } + r.CompDescResult.ValidityCheck.PrintErr("Company description validity check") + } + + r.OsResult.Summary.PrintErr("OS Manifest check") + for _, s := range r.OsResult.SignatureCheck { + s.PrintErr("OS Manifest") + } + r.OsResult.ValidityCheck.PrintErr("OS Manifest validity check") + + r.RtmResult.Summary.PrintErr("RTM Manifest check") + for _, s := range r.RtmResult.SignatureCheck { + s.PrintErr("RTM Manifest") + } + r.RtmResult.ValidityCheck.PrintErr("RTM Manifest validity check") + + for _, a := range r.AppResults { + a.Summary.PrintErr("App Manifest %v check", a.Name) + for _, s := range a.SignatureCheck { + s.PrintErr("App Manifest %v", a.Name) + } + a.ValidityCheck.PrintErr("App Manifest %v validity check", a.Name) + } + + r.DevDescResult.Summary.PrintErr("Device Description check") + for _, s := range r.DevDescResult.SignatureCheck { + s.PrintErr("Device Description") + } + r.DevDescResult.CorrectRtm.PrintErr("Correct RTM check") + r.DevDescResult.CorrectOs.PrintErr("Correct OS check") + for _, a := range r.DevDescResult.CorrectApps { + a.PrintErr("Correct App check") + } + r.DevDescResult.RtmOsCompatibility.PrintErr("RTM OS compatibility") + for _, a := range r.DevDescResult.OsAppsCompatibility { + a.PrintErr("OS App compatibiltiy check") + } + + if !r.PolicySuccess { + log.Warnf("Custom policy validation failed") + } + } +} diff --git a/testtool/tls.go b/testtool/tls.go index b3c32922..0dfdca9e 100644 --- a/testtool/tls.go +++ b/testtool/tls.go @@ -53,6 +53,8 @@ func dialInternalAddr(c *config, api atls.CmcApiSelect, addr string, tlsConf *tl defer wg.Wait() go publishResultAsync(c.Publish, result, wg) } + // Log errors if any + result.PrintErr() }), atls.WithCmc(cmc)) if err != nil { @@ -203,6 +205,8 @@ func listenInternal(c *config, api atls.CmcApiSelect, cmc *cmc.Cmc) { // and result is not empty go publishResult(c.Publish, result) } + // Log errors if any + result.PrintErr() }), atls.WithCmc(cmc)) if err != nil {