ML-KEM Overview
Table of contents
- Algorithm Type: Key Encapsulation Mechanism (KEM)
- Cryptographic Assumption: ML-KEM is a based on CRYSTALS-KYBER, a lattice-based key-encapsulation mechanism.
- Copyright: Public Domain or Apache 2.0 License
- **GitHub Source
FIPS-203 ML-KEM
FIPS 203 is the Module-Lattice-Based Key-Encapsulation Mechanism Standard, developed by the National Institute of Standards and Technology (NIST). It specifies the ML-KEM algorithms, derived from the CRYSTALS-KYBER submission, which are designed for key generation, encapsulation, and decapsulation. These algorithms are part of a broader effort to create cryptographic systems that can resist attacks by quantum computers.
The primary focus of FIPS 203 is to provide secure, quantum-resistant methods for establishing shared secret keys between two parties communicating over a public channel. This is achieved through the use of ML-KEM, which is based on the computational difficulty of the Module Learning with Errors problem. The standard specifies three parameter sets for ML-KEM: ML-KEM-512, ML-KEM-768, and ML-KEM-1024, each offering different levels of security and performance
On August 13, 2024, NIST officially standardized the ML-KEM encryption algorithm.
ML-KEM CAVP Validation
The TQ42 Cryptography v0.2.2 implementations of ML-KEM are fully compliant with the latest NIST standard, FIPS 203. The algorithm has undergone validation through the NIST Cryptographic Algorithm Validation Program (CAVP). For additional information, please visit the For further details, please visit the NIST CAVP webpage.
The validation process spans multiple operating systems and hardware configurations, including:
- Red Hat 9.4
- Ubuntu 24.04
- Windows 11
- Windows 2022
- IOS 17.5.1
- Android 14
ML-KEM advantages over classical KEM
- ML-KEMs are designed to be quantum-secure, meaning they offer protection against potential attacks by quantum computers, which pose significant threats to traditional cryptographic systems In contrast, classical KEMs such as those based on RSA or ECC are vulnerable to quantum attacks, as quantum algorithms like Shor’s algorithm can efficiently break them.
- ML-KEM, particularly the CRYSTALS-Kyber variant, balances performance with security. It supports various parameter sets that provide different levels of security, ranging from ML-KEM-512 with security equivalence to AES-128, to ML-KEM-1024, equivalent to AES-256. This flexibility allows it to cater to different security requirements and resource constraints. Classical KEMs often do not provide such a wide range of parameter sets, limiting their adaptability to different security needs.
FIPS 203 applications
FIPS 203, which standardizes Module-Lattice-based Key-Encapsulation Mechanisms (ML-KEM), has a variety of applications across multiple domains. Given its quantum-resistant properties, its primary utility is in securing communications and data against potential quantum computing threats. Here are some specific applications of FIPS 203:
- Integrate ML-KEM into Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols to ensure encrypted communication channels are secure against quantum attacks.
- Use ML-KEM to secure connections in Virtual Private Networks (VPNs), ensuring that remote communications are protected.
- Secure communication between IoT devices and central hubs using ML-KEM to prevent eavesdropping and unauthorized access.
- Enhance the security of online transactions and account management by using ML-KEM to protect sensitive financial data.
- Secure private keys and transactions in cryptocurrency systems to prevent theft and tampering.
- Use ML-KEM to secure classified communications, ensuring that sensitive governmental and military information remains confidential.
NIST’s Known Answer Tests (KAT)
The TQ42 Cryptography ML-KEM algorithm implementation has successfully passed the Known Answer Tests (KAT) provided by NIST. This confirms that the algorithm performs reliably as anticipated. For those interested in a deeper dive into the specifics of these tests, they are available for review.
Leveraging ML-KEM and True Entropy
The customization of the ML-KEM algorithm within TQ42 Cryptography is designed to work in synergy with true entropy, sourced from the Single Photon Quantum Random Number Generator (QRNG). This technology ensures that the randomness required for cryptographic keys is of the highest quality, providing unparalleled security for company data. Since the effectiveness of any cryptographic algorithm heavily relies on the randomness of its keys, incorporating QRNG-derived true entropy with TQ42’s customized ML-KEM algorithm ensures that your company’s sensitive information is safeguarded in the most robust manner possible.
ML-KEM - Parameter set summary
Encapsulation key size | Decapsulation key size | Ciphertext size | Shared secret size | Security category | |
---|---|---|---|---|---|
ML-KEM-512 | 800 bytes | 1632 bytes | 768 bytes | 32 bytes | 1 |
ML-KEM-768 | 1184 bytes | 2400 bytes | 1088 bytes | 32 bytes | 3 |
ML-KEM-1024 | 1568 bytes | 3168 bytes | 1568 bytes | 32 bytes | 5 |
API overview
To include the necessary library, please refer to the Getting Started Guide. After following the guide, include the pqc/ml-kem.h
header in your code. All Key Exchange Mechanism algorithms have a unified API. For ML-KEM, you can set the algorithm to work using one of the constants PQC_CIPHER_ML_KEM_512, PQC_CIPHER_ML_KEM_768, PQC_CIPHER_ML_KEM_1024. To learn about all the available methods for Key Exchange Mechanism APIs, visit the KEM API Overview page.
Example
Code
#include <cstring>
#include <iostream>
#include <vector>
#include <pqc/common.h>
// Include header for appropriate key exchange algorithm:
// #include <pqc/kyber.h> for Kyber
// #include <pqc/mceliece.h> for McEliece
// #include <pqc/ml-kem.h> for ML-KEM
#include <pqc/ml-kem.h>
/*
Example of Key exchange mechanism using asymmetric encryption.
Alice and Bob create their key pairs. Public keys and encoded message are shared
Message is encoded and shared from Alice to Bob with PQC_kem_encapsulate_secret
Message is decoded by Bob with PQC_kem_decapsulate_secret
*/
int main()
{
// Select appropriate cipher
const uint32_t cipher = PQC_CIPHER_ML_KEM_1024;
const size_t pk_len = PQC_cipher_get_length(cipher, PQC_LENGTH_PUBLIC);
const size_t ss_len = PQC_cipher_get_length(cipher, PQC_LENGTH_SHARED);
const size_t msg_len = PQC_cipher_get_length(cipher, PQC_LENGTH_MESSAGE);
std::vector<uint8_t> pk(pk_len); // public (encapsulation) key
std::vector<uint8_t> ss_alice(ss_len); // shared secret (Alice)
std::vector<uint8_t> ss_bob(ss_len); // shared secret (Bob)
std::vector<uint8_t> msg(msg_len); // message from Alice to Bob
// Context init for Bob
CIPHER_HANDLE bob = PQC_context_init_asymmetric(cipher, nullptr, 0, nullptr, 0);
if (bob == PQC_BAD_CIPHER)
{
std::cout << "Context init for Bob failed!" << std::endl;
return -1;
}
// Bob generates public (encapsulation) key (to share with Alice) and secure (decapsulation) key
size_t gen_keypair_result = PQC_context_keypair_generate(bob);
if (gen_keypair_result != PQC_OK)
{
std::cout << "Key generation failed!" << std::endl;
return -1;
}
// Get public (encapsulation) key to share it with Alice
size_t pk_get_result = PQC_context_get_public_key(bob, pk.data(), pk.size());
if (pk_get_result != PQC_OK)
{
std::cout << "Public key getting error!" << std::endl;
return -1;
}
// Context init for Alice
CIPHER_HANDLE alice = PQC_context_init_asymmetric(cipher, pk.data(), pk.size(), nullptr, 0);
if (alice == PQC_BAD_CIPHER)
{
std::cout << "Context init for Alice failed!" << std::endl;
return -1;
}
// Alice derives shared key to be used for data encryption and message for other party call
size_t enc_result = PQC_kem_encapsulate_secret(alice, msg.data(), msg.size(), ss_alice.data(), ss_alice.size());
if (enc_result != PQC_OK)
{
std::cout << "Secret hasn't been successfully encapsulated!" << std::endl;
return -1;
}
// Bob derives shared key from message and private key
size_t dec_result = PQC_kem_decapsulate_secret(bob, msg.data(), msg.size(), ss_bob.data(), ss_bob.size());
if (dec_result != PQC_OK)
{
std::cout << "Secret hasn't been successfully decapsulated!" << std::endl;
return -1;
}
if (ss_bob == ss_alice)
std::cout << "Shared secrets are equal!" << std::endl;
else
std::cout << "Error! Shared secrets are not equal!" << std::endl;
// Encapsulation / decapsulation with party info
std::fill(ss_alice.begin(), ss_alice.end(), (uint8_t)0); // clear
std::fill(ss_bob.begin(), ss_bob.end(), (uint8_t)1); // clear
const size_t info_size = 10;
// party_a_info (in): additional data to be used for key derivation
uint8_t party_a_info[info_size] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
enc_result =
PQC_kem_encapsulate(alice, msg.data(), msg.size(), party_a_info, info_size, ss_alice.data(), ss_alice.size());
if (enc_result != PQC_OK)
{
std::cout << "Secret hasn't been successfully encapsulated using party info!" << std::endl;
return -1;
}
dec_result =
PQC_kem_decapsulate(bob, msg.data(), msg.size(), party_a_info, info_size, ss_bob.data(), ss_bob.size());
if (dec_result != PQC_OK)
{
std::cout << "Secret hasn't been successfully decapsulated using party info!" << std::endl;
return -1;
}
if (ss_bob == ss_alice)
std::cout << "Shared secrets are equal!" << std::endl;
else
std::cout << "Error! Shared secrets are not equal!" << std::endl;
PQC_context_close(alice);
PQC_context_close(bob);
return 0;
}