{"record":{"id":"7dd592a9a1a65c21","repo":"golang/go","slug":"mlkem-inconsistent-h-ek-in-encoded-bytes","errorCode":null,"errorMessage":"mlkem: inconsistent H(ek) in encoded bytes","messagePattern":"mlkem: inconsistent H\\(ek\\) in encoded bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/mlkem/mlkem1024.go","lineNumber":187,"sourceCode":"\t\tvar err error\n\t\tdk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12])\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"mlkem: invalid secret key encoding\")\n\t\t}\n\t\tb = b[encodingSize12:]\n\t}\n\n\tek, err := NewEncapsulationKey1024(b[:EncapsulationKeySize1024])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdk.ρ = ek.ρ\n\tdk.h = ek.h\n\tdk.encryptionKey1024 = ek.encryptionKey1024\n\tb = b[EncapsulationKeySize1024:]\n\n\tif !bytes.Equal(dk.h[:], b[:32]) {\n\t\treturn nil, errors.New(\"mlkem: inconsistent H(ek) in encoded bytes\")\n\t}\n\tb = b[32:]\n\n\tcopy(dk.z[:], b)\n\n\t// Generate a random d value for use in Bytes(). This is a safety mechanism\n\t// that avoids returning a broken key vs a random key if this function is\n\t// called in contravention of the TestingOnlyNewDecapsulationKey1024 function\n\t// comment advising against it.\n\tdrbg.Read(dk.d[:])\n\n\treturn dk, nil\n}\n\n// kemKeyGen1024 generates a decapsulation key.\n//\n// It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and\n// K-PKE.KeyGen according to FIPS 203, Algorithm 13. The two are merged to save","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/mlkem/mlkem1024.go#L169-L205","documentation":"Thrown by TestingOnlyNewDecapsulationKey1024 when the 32-byte H(ek) digest embedded in the NIST blob does not match SHA3-256 of the encapsulation-key portion just parsed. FIPS 203 binds the decapsulation key to its public counterpart via this hash; a mismatch means the blob is internally inconsistent (the sk and ek halves are not from the same keypair).","triggerScenarios":"The byte slice has correct total length and decodable s-vector + ek, but the trailing 32-byte hash field was computed over a different ek, was zeroed, or came from concatenating two unrelated key blobs. Also fires if byte order/endianness was altered in transit.","commonSituations":"Manually concatenating dk_seed || ek || H(ek) || z from separate sources, base64/hex decode mismatch truncating the hash, mixing test vectors across ML-KEM parameter sets, or a copy-paste that swapped ek halves between keys.","solutions":["Re-derive the blob from a single keypair so H(ek) is recomputed over the exact ek bytes embedded.","Validate the 32-byte hash field equals sha3.Sum256(ek_bytes) before calling the constructor.","Prefer NewDecapsulationKey1024 with the 64-byte seed so all derived fields stay consistent.","Discard hand-assembled blobs; obtain fresh ACVP vectors from a vetted source."],"exampleFix":"// before\ndk, err := mlkem1024.TestingOnlyNewDecapsulationKey1024(assembled) // H(ek) stale\n// after\nh := sha3.New256()\nh.Write(assembled[offset : offset+EncapsulationKeySize1024])\nwant := h.Sum(nil)\nif !bytes.Equal(want, assembled[hashOff:hashOff+32]) {\n    return errors.New(\"blob hash field stale; regenerate\")\n}","handlingStrategy":"validation","validationCode":"// Pre-check the embedded H(ek) field against the ek half of the blob.\nimport \"golang.org/x/crypto/sha3\"\nekStart := k*encodingSize12 // after s-vector\nekEnd := ekStart + mlkem1024.EncapsulationKeySize1024\nh := sha3.New256(); h.Write(b[ekStart:ekEnd])\nwant := h.Sum(nil)\nif !bytes.Equal(want, b[ekEnd:ekEnd+32]) {\n    return errors.New(\"H(ek) inconsistent; regenerate blob\")\n}","typeGuard":"func blobHashConsistent(b []byte) bool {\n    ekStart := 2*encodingSize12 // k=2 for 1024\n    h := sha3.New256(); h.Write(b[ekStart : ekStart+mlkem1024.EncapsulationKeySize1024])\n    return bytes.Equal(h.Sum(nil), b[ekStart+mlkem1024.EncapsulationKeySize1024:ekStart+mlkem1024.EncapsulationKeySize1024+32])\n}","tryCatchPattern":"dk, err := mlkem1024.TestingOnlyNewDecapsulationKey1024(b)\nif err != nil && strings.Contains(err.Error(), \"inconsistent H(ek)\") {\n    return errors.New(\"blob is internally inconsistent; regenerate from one keypair\")\n}","preventionTips":["Never hand-assemble dk||ek||H(ek)||z from separate sources.","Persist the 64-byte seed instead; derive all fields from it.","Treat an H(ek) mismatch as a sign of corruption, not a recoverable parse error."],"tags":["mlkem","post-quantum","fips140","crypto","integrity","key-parsing","acvp"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}