golang/go · error
mlkem: inconsistent H(ek) in encoded bytes
Error message
mlkem: inconsistent H(ek) in encoded bytes
What it means
Thrown by TestingOnlyNewDecapsulationKey768 when the 32-byte H(ek) digest embedded in the NIST expanded blob does not match SHA3-256 of the encapsulation-key portion. The decapsulation key is inconsistent with its embedded public key. 768 analogue of error 381.
Source
Thrown at src/crypto/internal/fips140/mlkem/mlkem768.go:246
var err error
dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12])
if err != nil {
return nil, errors.New("mlkem: invalid secret key encoding")
}
b = b[encodingSize12:]
}
ek, err := NewEncapsulationKey768(b[:EncapsulationKeySize768])
if err != nil {
return nil, err
}
dk.ρ = ek.ρ
dk.h = ek.h
dk.encryptionKey = ek.encryptionKey
b = b[EncapsulationKeySize768:]
if !bytes.Equal(dk.h[:], b[:32]) {
return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes")
}
b = b[32:]
copy(dk.z[:], b)
// Generate a random d value for use in Bytes(). This is a safety mechanism
// that avoids returning a broken key vs a random key if this function is
// called in contravention of the TestingOnlyNewDecapsulationKey768 function
// comment advising against it.
drbg.Read(dk.d[:])
return dk, nil
}
// kemKeyGen generates a decapsulation key.
//
// It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and
// K-PKE.KeyGen according to FIPS 203, Algorithm 13. The two are merged to saveView on GitHub (pinned to b6b368adc5)
Solutions
- Re-derive the blob from one keypair so H(ek) is recomputed over the embedded ek bytes.
- Precompute sha3.Sum256(ek) and compare to the stored hash field before calling.
- Use NewDecapsulationKey768 with the 64-byte seed for self-consistent derivation.
- Discard hand-assembled blobs; use vetted ACVP vectors.
Example fix
// before
dk, err := mlkem768.TestingOnlyNewDecapsulationKey768(assembled)
// after
h := sha3.New256(); h.Write(assembled[ekOff:ekOff+EncapsulationKeySize768])
if !bytes.Equal(h.Sum(nil), assembled[hashOff:hashOff+32]) {
return errors.New("blob H(ek) inconsistent; regenerate")
}
dk, err := mlkem768.TestingOnlyNewDecapsulationKey768(assembled) Defensive patterns
Strategy: validation
Validate before calling
// Pre-check the H(ek) field against the ek half of the 768 blob.
h := sha3.New256(); h.Write(b[ekStart:ekStart+mlkem768.EncapsulationKeySize768])
if !bytes.Equal(h.Sum(nil), b[hashStart:hashStart+32]) {
return errors.New("H(ek) inconsistent; regenerate blob")
} Type guard
func blob768HashConsistent(b []byte) bool {
ekStart := 2*encodingSize12 // k=2 for 768? verify k for ML-KEM-768
h := sha3.New256(); h.Write(b[ekStart : ekStart+mlkem768.EncapsulationKeySize768])
return bytes.Equal(h.Sum(nil), b[ekStart+mlkem768.EncapsulationKeySize768:ekStart+mlkem768.EncapsulationKeySize768+32])
} Try / catch
dk, err := mlkem768.TestingOnlyNewDecapsulationKey768(b)
if err != nil && strings.Contains(err.Error(), "inconsistent H(ek)") {
return errors.New("blob internally inconsistent; regenerate")
} Prevention
- Never hand-assemble the NIST expanded blob from parts of different keys.
- Persist and reload from the 64-byte seed.
- Treat an H(ek) mismatch as corruption, not a recoverable parse error.
When it happens
Trigger: Blob has correct length and decodable s-vector + ek, but the hash field was computed over a different ek, zeroed, or assembled from unrelated sources.
Common situations: Concatenating dk_seed || ek || H(ek) || z from separate keys, truncating the hash during transport, mixing parameter sets, or stale cached blobs after a key rotation.
Related errors
- mlkem: inconsistent H(ek) in encoded bytes
- mlkem: invalid secret key encoding
- mlkem: invalid NIST decapsulation key length
- mlkem: invalid secret key encoding
- mlkem: invalid encapsulation key length
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/c00f293113b52ff1.
Report an issue: GitHub.