ML-KEM

ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) is the post-quantum key encapsulation mechanism standardized by NIST in FIPS 203. It is derived from CRYSTALS-Kyber.

Its security is based on the hardness of the Module Learning With Errors problem (Module-LWE), which is believed to be hard even for quantum computers. Compared to ECC, keys and ciphertexts are much larger.

FIPS 203 defines three parameter sets:

Parameter set

Constant

NIST security category

Public key

Expanded private key

Ciphertext

ML-KEM-512

MLKEM.ML_KEM_512

1

800 bytes

1632 bytes

768 bytes

ML-KEM-768

MLKEM.ML_KEM_768

3

1184 bytes

2400 bytes

1088 bytes

ML-KEM-1024

MLKEM.ML_KEM_1024

5

1568 bytes

3168 bytes

1568 bytes

Pass one of these constants as the param_set argument. They are the members of the enum Crypto.PublicKey.MLKEM.ParamSet, and the value of each member is the name of its parameter set (for instance, 'ML-KEM-768'). The shared secret is always 32 bytes long.

A private key can be represented in two ways:

  • the seed: the 64 random bytes the key is generated from (\(d \| z\) in FIPS 203). It is the most compact form.

  • the expanded private key: the decapsulation key defined in FIPS 203 (Algorithm 16). It cannot be converted back to the seed.

The names used in this module follow the specifications, but avoid dk on its own, because the specifications disagree on its meaning:

This module

FIPS 203

draft-ietf-hpke-pq

seed

\(d \| z\)

dk

expanded_dk

dk

expanded_dk

ek

ek

ek

With this module you can generate a new ML-KEM key:

>>> from Crypto.PublicKey import MLKEM
>>>
>>> key = MLKEM.generate(param_set=MLKEM.ML_KEM_768)
>>> seed = key.seed       # 64 bytes, keep it secret
>>> ek = key.ek           # 1184 bytes, the public key

and rebuild it later from its seed (or from the expanded private key), or get the public key:

>>> key = MLKEM.construct(param_set=MLKEM.ML_KEM_768, seed=seed)
>>> pub_key = MLKEM.construct(param_set=MLKEM.ML_KEM_768, ek=ek)
>>> pub_key == key.public_key()
True

construct() performs the input checks of FIPS 203: the modulus check on a public key (Section 7.2) and the hash check on an expanded private key (Section 7.3).

Currently, ML-KEM keys can only be used with HPKE (Crypto.Protocol.HPKE), alone or combined with ECDH in a hybrid KEM.

The implementation is based on mlkem-native.

ML-KEM keys (FIPS 203)

class Crypto.PublicKey.MLKEM.MLKEMKey(*, param_set, seed=None, expanded_dk=None, ek=None)

Class defining an ML-KEM key (private or public). Do not instantiate directly. Use generate() or construct() instead.

Variables:

param_set (ParamSet) – The parameter set.

property ek: bytes

The encapsulation key, that is, the public key (ek in FIPS 203, Algorithm 16).

property expanded_dk: bytes

The expanded decapsulation key (dk in FIPS 203, Algorithm 16).

has_private()

True if this key can be used for decapsulation.

Return type:

bool

public_key()

A matching ML-KEM public key.

Return type:

MLKEMKey

Returns:

a new MLKEMKey object

property seed: bytes

The 64-byte seed of the private key (\(d \| z\) in FIPS 203).

Not available if the private key was created from the expanded form only.

Crypto.PublicKey.MLKEM.ML_KEM_1024 = ParamSet.ML_KEM_1024

Same as ParamSet.ML_KEM_1024

Crypto.PublicKey.MLKEM.ML_KEM_512 = ParamSet.ML_KEM_512

Same as ParamSet.ML_KEM_512

Crypto.PublicKey.MLKEM.ML_KEM_768 = ParamSet.ML_KEM_768

Same as ParamSet.ML_KEM_768

class Crypto.PublicKey.MLKEM.ParamSet(value)

The ML-KEM parameter sets (FIPS 203, Section 8). The value of each member is its name in FIPS 203.

ML_KEM_1024 = 'ML-KEM-1024'

NIST security category 5

ML_KEM_512 = 'ML-KEM-512'

NIST security category 1

ML_KEM_768 = 'ML-KEM-768'

NIST security category 3

Crypto.PublicKey.MLKEM.construct(*, param_set, seed=None, expanded_dk=None, ek=None)

Build an ML-KEM key (private or public) from its components.

Parameters:
  • param_set (ParamSet) – Mandatory. The parameter set, for instance MLKEM.ML_KEM_768.

  • seed (bytes) – The 64-byte seed of a private key: \(d \| z\) in FIPS 203 (the input of ML-KEM.KeyGen_internal, Algorithm 16).

  • expanded_dk (bytes) – The expanded decapsulation key (dk in FIPS 203). It must pass the hash check of FIPS 203 (Section 7.3).

  • ek (bytes) – The encapsulation key, that is, the public key (ek in FIPS 203). It must pass the modulus check of FIPS 203 (Section 7.2).

At least one of seed, expanded_dk and ek must be present. If more than one is present, they must match, otherwise ValueError is raised.

Returns:

a new ML-KEM key object

Return type:

MLKEMKey

Crypto.PublicKey.MLKEM.generate(*, param_set, randfunc=None)

Generate a new ML-KEM private key (FIPS 203, Algorithm 19).

Parameters:
  • param_set (ParamSet) – Mandatory. The parameter set, for instance MLKEM.ML_KEM_768.

  • randfunc (callable) – Optional. The RNG to read the 64-byte seed from. If None, Crypto.Random.get_random_bytes() is used.

Returns:

a new private key

Return type:

MLKEMKey