Post-Quantum Encryption: ML-KEM and ML-DSA in Practice

· 7 min read

A large enough quantum computer would break RSA and elliptic-curve cryptography, which protect most key exchanges and signatures today. NIST published the replacements in August 2024. This guide explains what changes, what doesn't, and how to use the new algorithms through an API.

What quantum computers break, and what they don't

  • RSA, Diffie-Hellman and elliptic curves (ECDH, ECDSA, Ed25519) are broken by Shor's algorithm. These need replacing.
  • AES-256 and SHA-256 are only weakened, by Grover's algorithm, which roughly halves their effective strength. AES-256 keeps about 128 bits of security, which is still considered safe. These stay.

Key exchange is the urgent part. Someone can record encrypted traffic today and decrypt it once a quantum computer exists, a strategy known as "harvest now, decrypt later". Data that has to stay secret for ten years or more is already exposed to it.

The new standards

  • ML-KEM (FIPS 203), formerly CRYSTALS-Kyber, is for key encapsulation and replaces RSA and ECDH key exchange. ML-KEM-768 is the usual default.
  • ML-DSA (FIPS 204), formerly CRYSTALS-Dilithium, is for digital signatures and replaces RSA and ECDSA signatures. ML-DSA-65 is the usual default.

NIST's draft transition plan (NIST IR 8547) would deprecate today's quantum-vulnerable algorithms from 2030 and disallow them after 2035.

Key exchange with ML-KEM-768

Key encapsulation isn't encryption of a message. The recipient publishes a public key. The sender runs encapsulate against it and gets two things: a 32-byte shared secret to use as an encryption key, and a ciphertext to send to the recipient. The recipient runs decapsulate on that ciphertext and gets the same shared secret.

ML-KEM-768 with the AMARACUS API
# 1. Recipient: generate a key pair -> data.publicKey, data.privateKey
curl -X POST https://api.amaracus.tech/v1/pqc/keypair \
  -H "Authorization: Bearer $AMARACUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "algorithm": "ML-KEM-768" }'

# 2. Sender: encapsulate -> data.sharedSecret, data.ciphertext
curl -X POST https://api.amaracus.tech/v1/pqc/encapsulate \
  -H "Authorization: Bearer $AMARACUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "algorithm": "ML-KEM-768", "publicKey": "<publicKey>" }'

# 3. Recipient: decapsulate -> the same data.sharedSecret
curl -X POST https://api.amaracus.tech/v1/pqc/decapsulate \
  -H "Authorization: Bearer $AMARACUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "algorithm": "ML-KEM-768", "privateKey": "<privateKey>", "ciphertext": "<ciphertext>" }'

The keypair and decapsulate calls carry the private key over TLS. Generate each key pair on the side that will keep it, and store the private key like any other secret.

Turning the shared secret into an encryption key

Run the shared secret through HKDF with a label for your application, then use the result as an AES-256-GCM key, as in encrypting data in Node.js:

derive-key.mjs
import { hkdfSync } from "node:crypto";

// sharedSecret: data.sharedSecret from encapsulate or decapsulate (base64)
const aesKey = Buffer.from(
  hkdfSync("sha256", Buffer.from(sharedSecret, "base64"), Buffer.alloc(0), "my-app v1 message key", 32)
);

Hybrid mode

Many teams combine ML-KEM with classic X25519, so a connection stays secure as long as either algorithm holds. Chrome and Cloudflare already use this X25519 + ML-KEM-768 hybrid for TLS. In the AMARACUS API, pass "algorithm": "X25519+ML-KEM-768" to the same keypair, encapsulate and decapsulate endpoints.

Signatures with ML-DSA

ML-DSA-65 sign and verify
# Key pair -> data.publicKey, data.privateKey
curl -X POST https://api.amaracus.tech/v1/pqc/keypair \
  -H "Authorization: Bearer $AMARACUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "algorithm": "ML-DSA-65" }'

# Sign -> data.signature
curl -X POST https://api.amaracus.tech/v1/pqc/sign \
  -H "Authorization: Bearer $AMARACUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "algorithm": "ML-DSA-65", "privateKey": "<privateKey>", "message": "invoice #1042: $310.00" }'

# Verify -> data.valid
curl -X POST https://api.amaracus.tech/v1/pqc/verify \
  -H "Authorization: Bearer $AMARACUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "algorithm": "ML-DSA-65", "publicKey": "<publicKey>", "message": "invoice #1042: $310.00", "signature": "<signature>" }'

ML-DSA signatures are much larger than classic ones: an ML-DSA-65 signature is 3,309 bytes, against 64 bytes for Ed25519. Check that your database columns, tokens and message formats can hold them.

Frequently asked questions

Is AES-256 quantum resistant?

As far as is known, yes. Grover's algorithm reduces AES-256 to roughly 128-bit strength, which is still out of reach. The algorithms to replace are RSA and elliptic-curve key exchange and signatures.

Do I need post-quantum encryption now?

If your data has to stay confidential for many years, start with key exchange now, because recorded traffic can be decrypted later. Signatures are less urgent: forging one needs a quantum computer at the time of the attack.

What is the difference between ML-KEM and Kyber?

ML-KEM is the standardized form of CRYSTALS-Kyber, published as FIPS 203 in August 2024. The final standard differs in small details, so older Kyber implementations don't interoperate with ML-KEM.

Is AMARACUS FIPS 140-3 validated?

No. AMARACUS implements the FIPS 203 and FIPS 204 algorithms, but its cryptographic module has not been through FIPS 140-3 validation. If your compliance rules require a validated module, take that into account.