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 save

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Re-derive the blob from one keypair so H(ek) is recomputed over the embedded ek bytes.
  2. Precompute sha3.Sum256(ek) and compare to the stored hash field before calling.
  3. Use NewDecapsulationKey768 with the 64-byte seed for self-consistent derivation.
  4. 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

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


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