golang/go · error

mlkem: inconsistent H(ek) in encoded bytes

Error message

mlkem: inconsistent H(ek) in encoded bytes

What it means

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).

Source

Thrown at src/crypto/internal/fips140/mlkem/mlkem1024.go:187

		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 := NewEncapsulationKey1024(b[:EncapsulationKeySize1024])
	if err != nil {
		return nil, err
	}
	dk.ρ = ek.ρ
	dk.h = ek.h
	dk.encryptionKey1024 = ek.encryptionKey1024
	b = b[EncapsulationKeySize1024:]

	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 TestingOnlyNewDecapsulationKey1024 function
	// comment advising against it.
	drbg.Read(dk.d[:])

	return dk, nil
}

// kemKeyGen1024 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 save

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Re-derive the blob from a single keypair so H(ek) is recomputed over the exact ek bytes embedded.
  2. Validate the 32-byte hash field equals sha3.Sum256(ek_bytes) before calling the constructor.
  3. Prefer NewDecapsulationKey1024 with the 64-byte seed so all derived fields stay consistent.
  4. Discard hand-assembled blobs; obtain fresh ACVP vectors from a vetted source.

Example fix

// before
dk, err := mlkem1024.TestingOnlyNewDecapsulationKey1024(assembled) // H(ek) stale
// after
h := sha3.New256()
h.Write(assembled[offset : offset+EncapsulationKeySize1024])
want := h.Sum(nil)
if !bytes.Equal(want, assembled[hashOff:hashOff+32]) {
    return errors.New("blob hash field stale; regenerate")
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the embedded H(ek) field against the ek half of the blob.
import "golang.org/x/crypto/sha3"
ekStart := k*encodingSize12 // after s-vector
ekEnd := ekStart + mlkem1024.EncapsulationKeySize1024
h := sha3.New256(); h.Write(b[ekStart:ekEnd])
want := h.Sum(nil)
if !bytes.Equal(want, b[ekEnd:ekEnd+32]) {
    return errors.New("H(ek) inconsistent; regenerate blob")
}

Type guard

func blobHashConsistent(b []byte) bool {
    ekStart := 2*encodingSize12 // k=2 for 1024
    h := sha3.New256(); h.Write(b[ekStart : ekStart+mlkem1024.EncapsulationKeySize1024])
    return bytes.Equal(h.Sum(nil), b[ekStart+mlkem1024.EncapsulationKeySize1024:ekStart+mlkem1024.EncapsulationKeySize1024+32])
}

Try / catch

dk, err := mlkem1024.TestingOnlyNewDecapsulationKey1024(b)
if err != nil && strings.Contains(err.Error(), "inconsistent H(ek)") {
    return errors.New("blob is internally inconsistent; regenerate from one keypair")
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/7dd592a9a1a65c21. Report an issue: GitHub.