{"record":{"id":"2159fac78e308ba7","repo":"OpenNHP/opennhp","slug":"key-usage-mismatch-got-d-want-d","errorCode":null,"errorMessage":"key usage mismatch: got %d, want %d","messagePattern":"key usage mismatch: got (.+?), want (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/core/verifier/csv/csv.go","lineNumber":419,"sourceCode":"\t// verify pek cert signature (self-signed)\n\tcekIdLen := int(binary.LittleEndian.Uint16(cekData[0xa4:0xa6]))\n\tif err := a.verifySm2SignatureWithId(\n\t\tcekData[0x14:0x34], cekData[0x5c:0x7c],\n\t\tpekData[0x41c:0x43c], pekData[0x464:0x484],\n\t\tcekData[0xa6:0xa6+cekIdLen], pekData[:0x414],\n\t); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (a *Attestation) verifyHygonCertInfo(hrk []byte, curveId, keyUsage int, keyId []byte) error {\n\thygonKeyUsage := hrk[0x24:0x28]\n\n\thygonKeyUsageInt := int(binary.LittleEndian.Uint32(hygonKeyUsage))\n\tif hygonKeyUsageInt != keyUsage {\n\t\treturn fmt.Errorf(\"key usage mismatch: got %d, want %d\", keyUsage, keyUsage)\n\t}\n\n\thygonCurveId := hrk[0x40:0x44]\n\thygonCurveIdInt := int(binary.LittleEndian.Uint32(hygonCurveId))\n\tif hygonCurveIdInt != curveId {\n\t\treturn fmt.Errorf(\"curve id mismatch: got %d, want %d\", curveId, curveId)\n\t}\n\n\thygonCertifyingId := hrk[0x14:0x24]\n\tif !bytes.Equal(hygonCertifyingId, keyId) {\n\t\treturn fmt.Errorf(\"certifying id mismatch: got %x, want %x\", hygonCertifyingId, keyId)\n\t}\n\n\treturn nil\n}\n\nfunc (a *Attestation) verifyCSVCertInfo(csvCert []byte, sigUsage int, sigAlgo int, keyUsage int, keyId []byte) error {\n\tcsvKeyUsage := csvCert[0x08:0x0C]","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/verifier/csv/csv.go#L401-L437","documentation":"verifyHygonCertInfo parses the key-usage field at offset 0x24..0x28 of a Hygon root (HRK) or HSK certificate blob as a little-endian uint32 and compares it with the expected keyUsage passed by the caller. If they differ it returns this mismatch error — a tampered, corrupted, or wrong-generation certificate blob. NOTE: the format string is buggy — it prints keyUsage twice instead of the parsed value (\"got %d, want %d\", keyUsage, keyUsage), so both numbers shown are the EXPECTED value.","triggerScenarios":"verifyCertChain calls verifyHygonCertInfo with expected usages 0 (HRK) and 0x13 (HSK); the error fires when the certificate blob's key-usage dword at 0x24 is not exactly that value — e.g. the downloaded hsk_cek for the chipId is not a valid HSK certificate, the HRK blob was corrupted, or memory offsets shifted due to a Hygon firmware/generation change.","commonSituations":"Attesting evidence from a different Hygon CPU generation than the blob downloaded for its chipId; truncated or corrupt certificate data (e.g. a slice shorter than 0x340 bytes being treated as hskData); manually supplied HRK that fails the hardcoded SM3 digest check earlier would fail first, so this usually indicates HSK/CEK data problems.","solutions":["Fix the format string to print the parsed value (hygonKeyUsageInt) so you can actually see what was found.","Dump the certificate blob around offset 0x24 and confirm which Hygon generation it belongs to; ensure the hsk_cek download for that chipId returned the correct HSK certificate.","Verify the blob length is at least 0x340 bytes before slicing (guards against short/HTML error pages being cached as hskCek).","If a Hygon firmware update changed the certificate layout, update the hardcoded offsets/usages in verifyHygonCertInfo."],"exampleFix":"// before\nif hygonKeyUsageInt != keyUsage {\n    return fmt.Errorf(\"key usage mismatch: got %d, want %d\", keyUsage, keyUsage)\n}\n// after\nif hygonKeyUsageInt != keyUsage {\n    return fmt.Errorf(\"key usage mismatch: got %d, want %d\", hygonKeyUsageInt, keyUsage)\n}","handlingStrategy":"validation","validationCode":"func validHygonCertBlob(blob []byte) bool {\n    if len(blob) < 0x340 {\n        return false\n    }\n    usage := binary.LittleEndian.Uint32(blob[0x24:0x28])\n    return usage == 0x00 || usage == 0x13\n}","typeGuard":"func asHygonCert(b []byte) ([]byte, bool) {\n    if len(b) >= 0x340 && (binary.LittleEndian.Uint32(b[0x24:0x28]) == 0x00 || binary.LittleEndian.Uint32(b[0x24:0x28]) == 0x13) {\n        return b, true\n    }\n    return nil, false\n}","tryCatchPattern":"if err := attestation.Verify(ctx, evidence); err != nil {\n    if strings.Contains(err.Error(), \"key usage mismatch\") {\n        // wrong/corrupt Hygon cert blob: re-fetch CEK or reject the evidence as invalid\n    }\n}","preventionTips":["Validate downloaded/cached certificate blob length and key fields before caching.","Never cache an HTTP response body without confirming it is a real certificate (size + field sanity).","Fix the logging bug so mismatches print the actual observed key usage.","Pin per-generation Hygon certificate expectations and update them alongside firmware upgrades."],"tags":["attestation","certificate-validation","parsing","logging-bug"],"backgroundTag":"schema-validation-failed","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}