Developer Guide

ML-KEM (Kyber) Explained: Complete Implementation Guide

15 min read Updated December 2024 FIPS 203

ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) is the NIST post-quantum cryptography standard for secure key exchange. Published as FIPS 203 in August 2024, ML-KEM is based on the CRYSTALS-Kyber algorithm and provides quantum-resistant key encapsulation to replace vulnerable algorithms like RSA and ECDH.

This guide covers everything developers need to know about ML-KEM: how it works, security levels, implementation strategies, and practical code examples using the Open Quantum Safe library.

What is ML-KEM?

ML-KEM is a key encapsulation mechanism (KEM) that enables two parties to establish a shared secret key over an insecure channel. Unlike key exchange protocols like Diffie-Hellman, KEMs work by having one party encapsulate a secret into a ciphertext that only the holder of the corresponding private key can decapsulate.

KEM vs Key Exchange In a KEM, the sender generates a random shared secret and encapsulates it using the recipient's public key. The recipient decapsulates using their private key to recover the same shared secret. This shared secret is then used to derive symmetric keys for encryption.

How ML-KEM Works

ML-KEM consists of three algorithms:

  1. KeyGen(): Generates a public/private key pair
  2. Encaps(pk): Takes a public key, outputs a ciphertext and shared secret
  3. Decaps(sk, ct): Takes a secret key and ciphertext, outputs the shared secret

The security of ML-KEM is based on the Module Learning With Errors (MLWE) problem, a mathematical problem believed to be hard for both classical and quantum computers to solve.

Security Levels and Parameters

ML-KEM comes in three security levels, each offering different trade-offs between security strength and performance:

Parameter Set Security Level Public Key Ciphertext Shared Secret
ML-KEM-512 NIST Level 1 (~AES-128) 800 bytes 768 bytes 32 bytes
ML-KEM-768 NIST Level 3 (~AES-192) 1,184 bytes 1,088 bytes 32 bytes
ML-KEM-1024 NIST Level 5 (~AES-256) 1,568 bytes 1,568 bytes 32 bytes
Which level should you use? ML-KEM-768 is recommended for most applications. It provides strong post-quantum security (equivalent to AES-192) with reasonable key and ciphertext sizes. Use ML-KEM-512 only for constrained environments, and ML-KEM-1024 for highest security requirements.

Implementation with liboqs

The Open Quantum Safe (OQS) project provides the most widely-used open-source implementation of ML-KEM through the liboqs library. Here's how to get started:

Installation

Bash
# Ubuntu/Debian prerequisites
sudo apt install cmake gcc ninja-build libssl-dev

# Clone and build liboqs
git clone https://github.com/open-quantum-safe/liboqs.git
cd liboqs && mkdir build && cd build
cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr/local ..
ninja && sudo ninja install

C Implementation Example

C
#include <oqs/oqs.h>
#include <stdio.h>
#include <string.h>

int main() {
    // Initialize ML-KEM-768
    OQS_KEM *kem = OQS_KEM_new(OQS_KEM_alg_ml_kem_768);
    if (kem == NULL) {
        printf("ML-KEM-768 not available\n");
        return 1;
    }

    // Allocate memory
    uint8_t *public_key = malloc(kem->length_public_key);
    uint8_t *secret_key = malloc(kem->length_secret_key);
    uint8_t *ciphertext = malloc(kem->length_ciphertext);
    uint8_t *shared_secret_enc = malloc(kem->length_shared_secret);
    uint8_t *shared_secret_dec = malloc(kem->length_shared_secret);

    // Generate key pair (recipient)
    OQS_KEM_keypair(kem, public_key, secret_key);

    // Encapsulate (sender creates shared secret)
    OQS_KEM_encaps(kem, ciphertext, shared_secret_enc, public_key);

    // Decapsulate (recipient recovers shared secret)
    OQS_KEM_decaps(kem, shared_secret_dec, ciphertext, secret_key);

    // Verify shared secrets match
    if (memcmp(shared_secret_enc, shared_secret_dec,
               kem->length_shared_secret) == 0) {
        printf("Key exchange successful!\n");
    }

    // Clean up
    OQS_KEM_free(kem);
    free(public_key);
    free(secret_key);
    free(ciphertext);
    free(shared_secret_enc);
    free(shared_secret_dec);

    return 0;
}

Python Implementation

Python
import oqs

# Initialize ML-KEM-768
kem = oqs.KeyEncapsulation("ML-KEM-768")

# Recipient generates key pair
public_key = kem.generate_keypair()
print(f"Public key size: {len(public_key)} bytes")

# Sender encapsulates a shared secret
ciphertext, shared_secret_sender = kem.encap_secret(public_key)
print(f"Ciphertext size: {len(ciphertext)} bytes")

# Recipient decapsulates to get the same shared secret
shared_secret_recipient = kem.decap_secret(ciphertext)

# Verify they match
assert shared_secret_sender == shared_secret_recipient
print("Key exchange successful!")
print(f"Shared secret: {shared_secret_sender.hex()[:32]}...")

TLS Integration

For TLS connections, use the oqs-provider with OpenSSL 3.x to enable post-quantum key exchange:

Bash
# Install oqs-provider
git clone https://github.com/open-quantum-safe/oqs-provider.git
cd oqs-provider && mkdir build && cd build
cmake -GNinja ..
ninja && sudo ninja install

# Configure OpenSSL to use the provider
# Add to openssl.cnf:
# [openssl_init]
# providers = provider_sect
# [provider_sect]
# oqsprovider = oqsprovider_sect
# [oqsprovider_sect]
# activate = 1

# Test with hybrid key exchange (X25519 + ML-KEM-768)
openssl s_client -groups x25519_mlkem768 -connect example.com:443
Use Hybrid Mode During Transition During the transition period, use hybrid key exchange combining classical (X25519/P-256) with ML-KEM. This provides protection even if one algorithm is broken. Example: x25519_mlkem768

Implementation Best Practices

Security Considerations

Performance Optimization

Operation ML-KEM-768 X25519 (Classical)
Key Generation ~30 microseconds ~25 microseconds
Encapsulation ~35 microseconds ~80 microseconds
Decapsulation ~40 microseconds ~80 microseconds

ML-KEM is comparable in speed to classical algorithms, making it suitable for high-throughput applications. The main overhead is increased key and ciphertext sizes.

Migration Strategy

Follow these steps to migrate your applications to ML-KEM:

  1. Inventory: Identify all systems using RSA, DH, or ECDH for key exchange
  2. Test Environment: Deploy ML-KEM in a test environment using liboqs
  3. Hybrid Mode: Implement hybrid key exchange (classical + ML-KEM) first
  4. Performance Testing: Benchmark key sizes and operation times
  5. Gradual Rollout: Enable ML-KEM for internal systems before external-facing services
  6. Monitor: Track success rates and performance metrics

Frequently Asked Questions

What is the difference between Kyber and ML-KEM?
Kyber was the original algorithm name during NIST's competition. ML-KEM is the standardized name in FIPS 203. They are functionally the same algorithm with minor parameter naming updates.
Is ML-KEM ready for production use?
Yes, ML-KEM (FIPS 203) is a finalized NIST standard. Major companies including Google, Cloudflare, and AWS are already deploying it. For security-critical applications, use vendor-certified implementations.
How does ML-KEM compare to RSA key exchange?
ML-KEM offers smaller key sizes than RSA (1,184 bytes vs 2,048+ bytes for equivalent security) and faster operations. Most importantly, ML-KEM is quantum-resistant while RSA will be broken by quantum computers.
Should I use ML-KEM-512, 768, or 1024?
ML-KEM-768 is recommended for most applications. Use ML-KEM-512 only in constrained environments, and ML-KEM-1024 for highest security requirements or data requiring long-term protection.
Can ML-KEM be used with existing TLS infrastructure?
Yes, using the OQS-Provider for OpenSSL 3.x. Most TLS libraries are adding native support. Chrome, Firefox, and other browsers already support hybrid key exchange with ML-KEM.