Skip to main content
Version: 3.9.0

ML-KEM – Post-Quantum Key Encapsulation (Kyber)

Overview

ML-KEM (Module-Lattice Key Encapsulation Mechanism) – is a lattice-based key-encapsulation protocol selected by NIST for post-quantum cryptography (FIPS 203).
ML-KEM provides three security categories that correspond to the Kyber512 (Level 1), Kyber768 (Level 3) and Kyber1024 (Level 5) parameter sets.


Byte sizes

Parameter setPublic keySecret keyCipher-textShared secretEntropy bytesMessage bytesSecurity category
Kyber512800 B1632 B768 B32 B64 B32 BLevel 1
Kyber7681184 B2400 B1088 B32 B64 B32 BLevel 3
Kyber10241568 B3168 B1568 B32 B64 B32 BLevel 5

C++ API

All three operations are templated by the Kyber parameter set (Kyber512Params, Kyber768Params, Kyber1024Params). The templates are explicitly instantiated by Icicle, so you only have to include the header and link against icicle_pqc.

MlKemConfig struct

struct MlKemConfig {
icicleStreamHandle stream = nullptr; // Optional async stream
bool is_async = false; // If true – return immediately and synchronize later

// Location hints – set to `true` if the corresponding buffer already resides on the device
bool messages_on_device = false;
bool entropy_on_device = false;
bool public_keys_on_device = false;
bool secret_keys_on_device = false;
bool ciphertexts_on_device = false;
bool shared_secrets_on_device = false;

int batch_size = 1; // Number of independent KEMs processed in parallel

ConfigExtension* ext = nullptr; // Backend-specific tuning knobs (optional)
};

Key pair generation

template <typename Params /* Kyber512Params | Kyber768Params | Kyber1024Params */>
eIcicleError keygen(
const std::byte* entropy, // [batch_size × ENTROPY_BYTES]
MlKemConfig config,
std::byte* public_keys, // [batch_size × Params::PUBLIC_KEY_BYTES]
std::byte* secret_keys // [batch_size × Params::SECRET_KEY_BYTES]
);

Encapsulation

template <typename Params>
eIcicleError encapsulate(
const std::byte* message, // [batch_size × MESSAGE_BYTES] arbitrary plaintext
const std::byte* public_keys, // [batch_size × Params::PUBLIC_KEY_BYTES]
MlKemConfig config,
std::byte* ciphertexts, // [batch_size × Params::CIPHERTEXT_BYTES]
std::byte* shared_secrets // [batch_size × Params::SHARED_SECRET_BYTES]
);

Decapsulation

template <typename Params>
eIcicleError decapsulate(
const std::byte* secret_keys, // [batch_size × Params::SECRET_KEY_BYTES]
const std::byte* ciphertexts, // [batch_size × Params::CIPHERTEXT_BYTES]
MlKemConfig config,
std::byte* shared_secrets // [batch_size × Params::SHARED_SECRET_BYTES]
);

Example: generate & encapsulate (Kyber768)

#include "icicle/pqc/ml_kem.h"
using namespace icicle::pqc::ml_kem;

int main() {
const int batch_size = 1 << 12;
// Config
MlKemConfig config;
config.batch_size = batch_size;

// Allocate buffers
auto entropy = this->random_entropy(batch_size * ENTROPY_BYTES);
std::vector<std::byte> public_key(batch_size * TypeParam::PUBLIC_KEY_BYTES);
std::vector<std::byte> secret_key(batch_size * TypeParam::SECRET_KEY_BYTES);
std::vector<std::byte> ciphertext(batch_size * TypeParam::CIPHERTEXT_BYTES);
std::vector<std::byte> shared_secret_enc(batch_size * TypeParam::SHARED_SECRET_BYTES);
std::vector<std::byte> shared_secret_dec(batch_size * TypeParam::SHARED_SECRET_BYTES);

auto message = this->random_entropy(batch_size * MESSAGE_BYTES);

// Key generation
auto err = keygen<TypeParam>(entropy.data(), config, public_key.data(), secret_key.data());

// Encapsulation
err = encapsulate<TypeParam>(message.data(), public_key.data(), config, ciphertext.data(), shared_secret_enc.data());

// Decapsulation
err = decapsulate<TypeParam>(secret_key.data(), ciphertext.data(), config, shared_secret_dec.data());
}

References

NIST FIPS 203 – Module-Lattice-Based Key-Encapsulation Mechanism Standard.