QNSP

Blog · 2026-05-14 · 8 min read

What ML-KEM Actually Does: A Plain-English Walkthrough of FIPS 203

ML-KEM (formerly CRYSTALS-Kyber) is NIST's primary post-quantum key encapsulation standard. This is a plain-English walkthrough of what it does, why it's a KEM (not a cipher), and how the three parameter sets — ML-KEM-512 / 768 / 1024 — actually differ.

ML-KEMFIPS 203PQC-explainedcryptography-101
By Christopher Frost, Founder, CUI LABS PTE. LTD.

Module-Lattice-based Key Encapsulation Mechanism — ML-KEM, formerly known as CRYSTALS-Kyber — is the first NIST-finalised post-quantum cryptographic standard, published as FIPS 203 in August 2024. It is QNSP's default key encapsulation mechanism in every tier and powers PQC TLS key agreement, KMS-wrapped data keys, and vault secret encryption across the platform.

This post explains what ML-KEM actually does, in plain English, with the math kept to a minimum. The goal is that someone evaluating a PQC platform can read this and have a working mental model of what they're buying.

First: what's a KEM, and why isn't it a cipher?

A Key Encapsulation Mechanism (KEM) is not a way to encrypt arbitrary data. It is a way for two parties to agree on a shared secret over an untrusted channel — and the shared secret is then used as the key for a symmetric cipher (like AES-256-GCM) that does the actual data encryption.

This is a deliberate separation of concerns. Public-key cryptography (KEMs included) is slow and produces small outputs. Symmetric cryptography is fast and handles arbitrary data sizes. The standard pattern is: use a KEM to agree on a session key, use AES to encrypt the actual payload with that session key.

ML-KEM follows this pattern. The shared secret it produces is always exactly 32 bytes (256 bits) — exactly the size of an AES-256 key. The 'encapsulation' it does is over those 32 bytes; the bulk-data encryption happens separately, in AES.

The three operations

ML-KEM exposes three operations:

  1. KeyGen() → (publicKey, secretKey). Generate a new keypair. The public key can be shared; the secret key must stay with its owner.
  2. Encapsulate(publicKey) → (ciphertext, sharedSecret). Anyone with the public key can generate a fresh shared secret and a ciphertext that encodes it. They use the shared secret immediately for AES; they send the ciphertext to the public key's owner.
  3. Decapsulate(secretKey, ciphertext) → sharedSecret. The owner of the secret key uses it to recover the same 32-byte shared secret from the ciphertext. Now both parties have the same shared secret without ever having transmitted it.

The security guarantee: an adversary who sees the public key and the ciphertext, but does not have the secret key, cannot recover the shared secret. With a sufficiently large quantum computer, they still cannot — which is the whole point of post-quantum cryptography.

Why 'Module-Lattice-based'?

ML-KEM's security rests on a problem called Module Learning With Errors (Module-LWE), which is an algebraic problem over polynomial rings. The shortest plausible explanation: given a matrix multiplied by a secret vector with small noise added, recover the secret vector. This problem is believed to be hard for both classical and quantum computers.

You don't need to understand the math to use ML-KEM. The point of the standard is that NIST and the broader cryptographic community have done years of analysis on Module-LWE, and the consensus is that it's hard enough — even with future quantum advances — to be safe for production use.

The three parameter sets: 512, 768, 1024

ML-KEM ships in three parameter sets. The numbers refer to the underlying lattice dimensions and roughly correspond to NIST security categories:

  • ML-KEM-512 (NIST security level 1) — equivalent strength to AES-128. Public key: 800 bytes. Secret key: 1,632 bytes. Ciphertext: 768 bytes. Used for development / testing where size matters more than maximum security.
  • ML-KEM-768 (NIST security level 3) — equivalent strength to AES-192. Public key: 1,184 bytes. Secret key: 2,400 bytes. Ciphertext: 1,088 bytes. The production default — recommended for general use including hybrid TLS.
  • ML-KEM-1024 (NIST security level 5) — equivalent strength to AES-256. Public key: 1,568 bytes. Secret key: 3,168 bytes. Ciphertext: 1,568 bytes. Used for highest-assurance workloads, required on QNSP's government tier.

The trade-off across parameter sets is straightforward: stronger security means larger keys and ciphertexts, which means more bandwidth per TLS handshake and more storage per stored key.

For context: classical X25519 has a 32-byte public key and a 32-byte shared secret. ML-KEM-768 has a 1,184-byte public key (37× larger) and a 1,088-byte ciphertext. This size difference is the main operational cost of moving to PQC — most production TLS deployments will use hybrid (X25519 + ML-KEM-768) for the foreseeable future to maintain classical-client compatibility.

How QNSP uses ML-KEM

Across QNSP's 18 backend services, ML-KEM is used for:

  • PQC TLS key agreement — hybrid X25519+ML-KEM-768 at the edge gateway and inter-service mTLS
  • KMS data-key wrapping — every encrypted object in QNSP storage has its data-encryption key wrapped under an ML-KEM operation
  • Vault secret-key derivation — secrets stored in the vault use ML-KEM to bind the encryption key to the vault's PQC root
  • Encrypted vector search — search-service uses ML-KEM-derived keys to encrypt the index entries (SSE-X)

ACVP conformance: 240/240 across both providers

NIST publishes Algorithm Validation Test System (ACVP) test vectors for ML-KEM that any implementation can be validated against. QNSP runs the full ML-KEM ACVP suite against two independent providers: @noble/post-quantum (pure-JavaScript reference) and @cuilabs/liboqs-native 0.15.1 (native-C, Open Quantum Safe).

Both providers pass all 240 ML-KEM ACVP tests: 75 keyGen, 75 encapsulation AFT, 30 decapsulation VAL, plus 60 §7.2 / §7.3 key-validation tests. The live evidence is at /verify/conformance with a SHA-3-256 tamper digest binding the result file.

For the other algorithms in the FIPS-finalised set: ML-DSA (FIPS 204) is the digital signature counterpart to ML-KEM and uses the same Module-LWE foundation. SLH-DSA (FIPS 205) is hash-based, with the most conservative security assumption available. FN-DSA / Falcon (FIPS 206 pending) is the size-optimised lattice signature alternative.
Per-algorithm detail (variants, key sizes, ACVP status, use cases, trade-offs) at /algorithms/ml-kem. Live ACVP evidence at /verify/conformance.
Related
← Back to blog