NIST FIPS 203 (ML-KEM) standard compliant, C++20, fully constexpr
, header-only library implementation.
FIPS 203 compliance is assured by testing this implementation against ACVP Known Answer Tests and tons of property based tests.
Note
constexpr
? Yes, you can compile-time execute keygen, encaps or decaps. But why? I don't know, some usecase might arise.
Caution
This ML-KEM implementation is conformant with ML-KEM standard https://doi.org/10.6028/NIST.FIPS.203 and I also try to make it timing leakage free, but be informed that this implementation is not yet audited. If you consider using it in production, please be careful !
ML-KEM has been standardized by NIST as post-quantum secure key encapsulation mechanism (KEM), which can be used for key establishment, between two parties, communicating over insecure channel.
ML-KEM offers an IND-CCA-secure Key Encapsulation Mechanism - its security is based on the hardness of solving the learning-with-errors (LWE) problem in module (i.e. structured) lattices.
ML-KEM is built on top of IND-CPA-secure K-PKE, where two communicating parties, both generating their key pairs, while publishing only their public keys to each other, can encrypt fixed length ( = 32 -bytes ) message using peer's public key. Cipher text can be decrypted by corresponding secret key ( which is private to the keypair owner ) and 32 -bytes message can be recovered back. Then a slightly tweaked Fujisaki–Okamoto (FO) transform is applied on IND-CPA-secure K-PKE - giving us the IND-CCA-secure ML-KEM construction. In KEM scheme, two parties interested in establishing a secure communication channel, over public & insecure channel, can generate a 32 -bytes shared secret key. Now they can use this 32 -bytes shared secret key in any symmetric key primitive, either for encrypting their communication (in much faster way) or deriving new/ longer keys.
Algorithm | Input | Output |
---|---|---|
KeyGen | - | Public Key and Secret Key |
Encapsulation | Public Key | Cipher Text and 32B Shared Secret |
Decapsulation | Secret Key and Cipher Text | 32B Shared Secret |
Here I'm maintaining ml-kem
- a C++20 header-only fully constexpr
library, implementing ML-KEM, supporting ML-KEM-{512, 768, 1024} parameter sets, as defined in table 2 of ML-KEM standard. It's easy to use, see usage.
ML-KEM-768 shows following performance characteristics on desktop and server grade CPUs.
ML-KEM-768 Algorithm | Time taken on "12th Gen Intel(R) Core(TM) i7-1260P" (x86_64 ) |
Time taken on "AWS EC2 Instance c8g.large" (aarch64 ) |
---|---|---|
keygen | 22.3us | 31.5us |
encaps | 25.6us | 35.9us |
decaps | 30.1us | 43.7us |
Note
Find ML-KEM standard @ https://doi.org/10.6028/NIST.FIPS.203 - this is the document that I followed when implementing ML-KEM. I suggest you go through the specification to get an in-depth understanding of the scheme.
- A C++ compiler such as
clang++
/g++
, with support for compiling C++20 programs.
# I was using Clang-19 when developing this library.
$ clang++ --version
Ubuntu clang version 19.1.7 (3ubuntu1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-19/bin
- Build tools such as
make
,cmake
. - For testing ML-KEM implementation, you need to globally install
google-test
library and headers. Follow guide @ https://github.com/google/googletest/tree/main/googletest#standalone-cmake-project, if you don't have it installed. - For benchmarking ML-KEM implementation, you'll need to have
google-benchmark
header and library globally installed. I found guide @ https://github.com/google/benchmark#installation helpful.
Note
If you are on a machine running GNU/Linux kernel and you want to obtain CPU cycle count for ML-KEM routines, you should consider building google-benchmark
library with libPFM
support, following https://gist.github.com/itzmeanjan/05dc3e946f635d00c5e0b21aae6203a7, a step-by-step guide. Find more about libPFM @ https://perfmon2.sourceforge.net.
Tip
Git submodule based dependencies will normally be imported automatically, but in case that doesn't work, you can manually initialize and update them by issuing $ git submodule update --init --recursive
from inside the root of this repository.
For testing functional correctness of this implementation and conformance with ML-KEM standard, you have to run following command(s).
Note
All Known Answer Test (KAT) files live inside kats directory. KAT files from official reference implementation, are generated by following (reproducible) steps, described in https://gist.github.com/itzmeanjan/c8f5bc9640d0f0bdd2437dfe364d7710. ACVP KATs are generated by running $ make sync_acvp_kats
command.
make test -j # Run tests without any sort of sanitizers, with default C++ compiler.
CXX=clang++ make test -j # Switch to non-default compiler, by setting variable `CXX`.
make debug_asan_test -j # Run tests with AddressSanitizer enabled, with `-O1`.
make release_asan_test -j # Run tests with AddressSanitizer enabled, with `-O3 -march=native`.
make debug_ubsan_test -j # Run tests with UndefinedBehaviourSanitizer enabled, with `-O1`.
make release_ubsan_test -j # Run tests with UndefinedBehaviourSanitizer enabled, with `-O3 -march=native`.
PASSED TESTS (24/24):
1 ms: build/test/test.out ML_KEM.ML_KEM_512_DecapsFailureDueToBitFlippedCipherText
1 ms: build/test/test.out ML_KEM.ML_KEM_1024_KeygenEncapsDecaps
1 ms: build/test/test.out ML_KEM.PolynomialSerialization
1 ms: build/test/test.out ML_KEM.ML_KEM_512_KeygenEncapsDecaps
1 ms: build/test/test.out ML_KEM.ML_KEM_512_EncapsFailureDueToNonReducedPubKey
1 ms: build/test/test.out ML_KEM.ML_KEM_768_EncapsFailureDueToNonReducedPubKey
1 ms: build/test/test.out ML_KEM.ML_KEM_768_DecapsFailureDueToBitFlippedCipherText
2 ms: build/test/test.out ML_KEM.ML_KEM_1024_EncapsFailureDueToNonReducedPubKey
2 ms: build/test/test.out ML_KEM.ML_KEM_1024_DecapsFailureDueToBitFlippedCipherText
2 ms: build/test/test.out ML_KEM.ML_KEM_768_KeygenEncapsDecaps
3 ms: build/test/test.out ML_KEM.ML_KEM_512_SeckeyCheck_ACVP_KnownAnswerTests
4 ms: build/test/test.out ML_KEM.ML_KEM_512_Keygen_ACVP_KnownAnswerTests
4 ms: build/test/test.out ML_KEM.ML_KEM_512_Encaps_ACVP_KnownAnswerTests
4 ms: build/test/test.out ML_KEM.ML_KEM_768_Keygen_ACVP_KnownAnswerTests
4 ms: build/test/test.out ML_KEM.ML_KEM_768_Encaps_ACVP_KnownAnswerTests
5 ms: build/test/test.out ML_KEM.ML_KEM_768_SeckeyCheck_ACVP_KnownAnswerTests
6 ms: build/test/test.out ML_KEM.ML_KEM_1024_Encaps_ACVP_KnownAnswerTests
6 ms: build/test/test.out ML_KEM.ML_KEM_1024_Keygen_ACVP_KnownAnswerTests
6 ms: build/test/test.out ML_KEM.ML_KEM_1024_SeckeyCheck_ACVP_KnownAnswerTests
14 ms: build/test/test.out ML_KEM.ML_KEM_512_KnownAnswerTests
26 ms: build/test/test.out ML_KEM.ML_KEM_1024_KnownAnswerTests
28 ms: build/test/test.out ML_KEM.ML_KEM_768_KnownAnswerTests
125 ms: build/test/test.out ML_KEM.CompressDecompressZq
162 ms: build/test/test.out ML_KEM.ArithmeticOverZq
Note
There is a help menu, which introduces you to all available commands; just run make
from the root directory of this project.
For benchmarking ML-KEM public functions such as keygen, encaps and decaps, for various suggested parameter sets, you have to run following command(s).
make benchmark -j # If you haven't built google-benchmark library with libPFM support.
make perf -j # If you have built google-benchmark library with libPFM support.
Caution
When benchmarking, ensure that you've disabled CPU frequency scaling, by following guide @ https://github.com/google/benchmark/blob/main/docs/reducing_variance.md.
Benchmark results are in JSON format @ bench_result_on_Linux_6.11.0-19-generic_x86_64_with_g++_14.
Benchmark results are in JSON format @ bench_result_on_Linux_6.8.0-1021-aws_aarch64_with_g++_13.
ml-kem
is written as a header-only C++20 fully constexpr
library, mainly targeting 64 -bit mobile/ desktop/ server grade platforms and it's easy to get started with. All you need to do is following.
- Clone
ml-kem
repository.
cd
# Single step cloning and importing of submodules
git clone https://github.com/itzmeanjan/ml-kem.git --recurse-submodules
# Or clone and then run tests, which will automatically bring in dependencies
git clone https://github.com/itzmeanjan/ml-kem.git && pushd ml-kem && make test -j && popd
- Write your program; include proper header files ( based on which variant of ML-KEM you want to use, see include directory ), which includes declarations ( and definitions ) of all required ML-KEM routines and constants ( such as byte length of public/ private key, cipher text etc. ).
// main.cpp
#include "ml_kem/ml_kem_512.hpp"
#include "randomshake/randomshake.hpp"
#include <algorithm>
#include <array>
#include <cassert>
int
main()
{
std::array<uint8_t, ml_kem_512::SEED_D_BYTE_LEN> d{};
std::array<uint8_t, ml_kem_512::SEED_Z_BYTE_LEN> z{};
std::array<uint8_t, ml_kem_512::PKEY_BYTE_LEN> pkey{};
std::array<uint8_t, ml_kem_512::SKEY_BYTE_LEN> skey{};
std::array<uint8_t, ml_kem_512::SEED_M_BYTE_LEN> m{};
std::array<uint8_t, ml_kem_512::CIPHER_TEXT_BYTE_LEN> cipher{};
std::array<uint8_t, ml_kem_512::SHARED_SECRET_BYTE_LEN> sender_key{};
std::array<uint8_t, ml_kem_512::SHARED_SECRET_BYTE_LEN> receiver_key{};
randomshake::randomshake_t<128> csprng;
csprng.generate(d);
csprng.generate(z);
csprng.generate(m);
ml_kem_512::keygen(d, z, pkey, skey);
assert(ml_kem_512::encapsulate(m, pkey, cipher, sender_key)); // Key Encapsulation might fail, if input public key is malformed
ml_kem_512::decapsulate(skey, cipher, receiver_key);
assert(sender_key == receiver_key);
return 0;
}
- When compiling your program, let your compiler know where it can find
ml-kem
,sha3
,RandomShake
andsubtle
headers, which includes their definitions ( all of them are header-only libraries ) too.
# Assuming `ml-kem` was cloned just under $HOME
ML_KEM_HEADERS=~/ml-kem/include
SHA3_HEADERS=~/ml-kem/sha3/include
RANDOMSHAKE_HEADERS=~/ml-kem/RandomShake/include
SUBTLE_HEADERS=~/ml-kem/subtle/include
g++ -std=c++20 -Wall -Wextra -Wpedantic -O3 -march=native -I $ML_KEM_HEADERS -I $SHA3_HEADERS -I $RANDOMSHAKE_HEADERS -I $SUBTLE_HEADERS main.cpp
ML-KEM Variant | Namespace | Header |
---|---|---|
ML-KEM-512 Routines | ml_kem_512:: |
include/ml_kem/ml_kem_512.hpp |
ML-KEM-768 Routines | ml_kem_768:: |
include/ml_kem/ml_kem_768.hpp |
ML-KEM-1024 Routines | ml_kem_1024:: |
include/ml_kem/ml_kem_1024.hpp |
Note
ML-KEM parameter sets are taken from table 2 of ML-KEM standard @ https://doi.org/10.6028/NIST.FIPS.203.
All the functions, in this ML-KEM header-only library, are implemented as constexpr
functions. Hence you should be able to evaluate ML-KEM key generation, encapsulation or decapsulation at compile-time itself, given that all inputs are known at compile-time. I present you with the following demonstration program, which generates a ML-KEM-512 keypair and encapsulates a message, producing a ML-KEM-512 cipher text and a fixed size shared secret, given seed_{d, z, m}
as input - all at program compile-time. Notice, the static assertion.
/**
* Filename: compile-time-ml-kem-512.cpp
*
* Compile and run this program with
* $ g++ -std=c++20 -Wall -Wextra -Wpedantic -I include -I sha3/include -I subtle/include -I RandomShake/include compile-time-ml-kem-512.cpp && ./a.out
* or
* $ clang++ -std=c++20 -Wall -Wextra -Wpedantic -fconstexpr-steps=4000000 -I include -I sha3/include -I subtle/include -I RandomShake/include compile-time-ml-kem-512.cpp && ./a.out
*/
#include "ml_kem/ml_kem_512.hpp"
// Compile-time evaluation of ML-KEM-512 key generation and encapsulation, using NIST official KAT no. (1).
constexpr auto
eval_ml_kem_768_encaps() -> auto
{
using seed_t = std::array<uint8_t, ml_kem_512::SEED_D_BYTE_LEN>;
// 7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2d
constexpr seed_t seed_d = { 124, 153, 53, 160, 176, 118, 148, 170, 12, 109, 16, 228, 219, 107, 26, 221, 47, 216, 26, 37, 204, 177, 72, 3, 45, 205, 115, 153, 54, 115, 127, 45 };
// b505d7cfad1b497499323c8686325e4792f267aafa3f87ca60d01cb54f29202a
constexpr seed_t seed_z = {181, 5, 215, 207, 173, 27, 73, 116, 153, 50, 60, 134, 134, 50, 94, 71, 146, 242, 103, 170, 250, 63, 135, 202, 96, 208, 28, 181, 79, 41, 32, 42};
// eb4a7c66ef4eba2ddb38c88d8bc706b1d639002198172a7b1942eca8f6c001ba
constexpr seed_t seed_m = {235, 74, 124, 102, 239, 78, 186, 45, 219, 56, 200, 141, 139, 199, 6, 177, 214, 57, 0, 33, 152, 23, 42, 123, 25, 66, 236, 168, 246, 192, 1, 186};
std::array<uint8_t, ml_kem_512::PKEY_BYTE_LEN> pubkey{};
std::array<uint8_t, ml_kem_512::SKEY_BYTE_LEN> seckey{};
std::array<uint8_t, ml_kem_512::CIPHER_TEXT_BYTE_LEN> cipher{};
std::array<uint8_t, ml_kem_512::SHARED_SECRET_BYTE_LEN> shared_secret{};
ml_kem_512::keygen(seed_d, seed_z, pubkey, seckey);
(void)ml_kem_512::encapsulate(seed_m, pubkey, cipher, shared_secret);
return shared_secret;
}
int
main()
{
// This step is being evaluated at compile-time, thanks to the fact that my ML-KEM implementation is `constexpr`.
static constexpr auto computed_shared_secret = eval_ml_kem_768_encaps();
// b4c8e3c4115f9511f2fddb288c4b78c5cd7c89d2d4d321f46b4edc54ddf0eb36
constexpr std::array<uint8_t, ml_kem_512::SHARED_SECRET_BYTE_LEN> expected_shared_secret = { 180, 200, 227, 196, 17, 95, 149, 17, 242, 253, 219, 40, 140, 75, 120, 197, 205, 124, 137, 210, 212, 211, 33, 244, 107, 78, 220, 84, 221, 240, 235, 54 };
// Notice static_assert, yay !
static_assert(computed_shared_secret == expected_shared_secret, "Must be able to compute shared secret at compile-time !");
return 0;
}
See example ml_kem_768.cpp, where I show how to use ML-KEM-768 API. Execute following command to build and execute example.
make example -j
ML-KEM-768
Pubkey : 8ad4a0396b6d30127451d7bbeb0941560689c7952015635c0174c46c761a849c875f4a5973c6b510fa2fd31a690b01c63966a085b499e5352c0e0a45f2faa824012df176800f8348713c7f8aa9a0e1d3adab402b9fe3cbd2b11c70f3079eda7e7171cb4df1a77d178168d18637ba77342b9430d1b843842862ea44aa4c791de60aa84ccdf5515f42b0811276addf276676532fb15570a037201b4b41b09745aefb46608109ef46c071f2901fec1108e83d84a7493e661447c4210904276812ce56b0a312b57444e60c2923b7a6d75772859c53da264b061bd7205eae357cd35135a4310d8e3b1a684cc44c614f0b468ad75b95cb985d901653279ba4b96999c4779c68c1cbdfb50207b3cec7f48484a650d785c14bf0543ca4511be9ceb1da0124b5c0cc696e1300c5349636a5d290ca1b883b7643a90a00ef244f5734b5a9bb5783b7219a1618aae4b7a0f66d87e075c5410ade1a635877ac0a618ff846ac52ba5370684777f78b5ee8b6a3ac376320b68aeb626af2bb236b75ceeb91d166678e0a189014b437a17d8400413f677b363522ffe9a987ebaad22576a3b6858ca5796f304c082c9bd6385f9ff98e64ba2057e731a5d0b3550c22694bbac32173163a69512626a7624b414621730ca793255216e092905710e318a75865b4e1670b512700cd29afcb674b2d3b3338a2ad9d9b82890c98605bb8b531a8901307bcbbcc3e744603353109f095c96a5c50641335b3439c3522e12bc5ae9b47566826b58c2c7bf207f87b5f0bf25340671142e01cc908abfe68ba09935166c191834942e6ac47148c493bf1c2f1b755346a6bf926c5d5f44ec9c258634386c301400cd9a29b58568d12bb57b74bbc11a1eef550a395720d345a285658a0660cffbc49fec8b83b77a3aff25f014b01d4f72322f74d87801dafd3b1e2176d5f855915077ef376b031536f5a8c26012a39f3d201386a4c8c2246661a30888b45431398d400286ab06001553174468797e6ab01a65c1a6792ff06c4c36a2acad8a6c3c115c536aa8358787ce4c75aac85ec5602c2e67823a40d6ad497764800522055cf76b7631b20ae097458db53f2908eaa5a8d3528107caa992fb05a845c109b62c7045c8fc8a0ccc702b6b4874449f781cb4430799b577ce7b4120b950dd20179011a10144c54b7a9f6f1a01c484ee8968d1116c460da728958b574ccaa6e7b3b67a9235ca111d7fc575414c3f1751aa87960524b4ccebcca2272809d5a63bb456ac1380d06438c29524233f59c17d0888901d0b6c32be93b75b13152d40b358a705a04bc5b78a4227fb1be739558ec748c27e74660e65b0b2003f2216b6b103ba7313268a5783e9073c4b74387a30db99605d6f0bf15c93f2f80063f846239c21b22c1616d343280d46745e6609a83a657914bd00968253b5176a939ca2a2c4eba7f5d93cb0cb59e3bb6b230a65dfad5490ddb6fa0a3c99ec8764869330ee94f6df08080e996f7c9095cf12d46a7956986ae58597d0d8037ef7a73ece5349a995aab807e1603192d5a354ffc1020540d6fe8c854b1cc9a2c30a7a621d1173c3991084765b90fd2a58b06862b29aaa307c16004aefa268adc85985cfc361590410adab289eb79cb8f9abd87b9da457b5fdb2b8d48809f2c975e7140149b7fc57958a60a
Seckey : db99a8f00c2a1e3998056c0f4f27bae4c23c58b03fb19711e044a3737016410b97f3b432fd9b75ab45ac841005d5750a5de5ac9a5bb751f7b3931336ab1c5c7677186c76a4bcb3c79ac6c824d6b72d304e27fab86c31440a3a3a54cb025e8158c7ca92ffa342325c016b8b8263a7279bd681b4b796e198b25eac22f4a7026d0645f761113aa5bb50bb751be8b650701d6904035bbc441295806f6227a4e3cd4866bbcc83b713859e1883ad77838eba14629a2b3af069c00575a527e2ca611a5c6ee610699a097e7642cce908d720094a8301cf6223a8161513c76a93fa0ebd2a4efd713ce13abaea7b3fca9c221054503b7a792e374274ca25ae0756457cca59bb8d4522a3b03b52510913fed080a3833dcc020b0d620cd1dc65c0a23b732c197ad07828c126dad253ce63ac14a9a2ab42a78bd21fb91a069a237485e76b39e1948c13ac42a07ea1ebbccc35872fa25afd8908524829aea16e5e4b49d4095c9cc17bc70a8ee8e3b877da50c04703fe3a15e4ea447c085ab14875ef6776b850683a41197e995e5111a87d527a3fc883fe6720c61236f96433d27c03f5706ecbf7602034b4aebb8e132255b72623d5cc1a70aa9ce53b9932852b163a61a6a9701a44448f956fda155d11996e83183d9a003cb6b07182d427239149f317342c17742f1b612b90b4cd909eb5303d13b06a6af422ced67da1eabad2e950701218dfb54ca757ac2576a39d3b827f28c1356130e8a864725a61f723cbedb19fb2444e6fd73ca3e90dfe2a59a222831e26a4720a30f4824702b0442c52b832848a62e2b0778a72eed4882996a58890cf31970f9e93b5a85ab193b2abff64a3f1b10f9d5202f2946d84075e87191e81851ccffa2c591031d0bc17aff346e3cb391796c0c6c74444c161b348906ef8bb54c022c8870395f58dfa1cb33f25c32df2ce7a2813476514acd72aba073f2a7937de46a69c01b1a6e03df270af5bb65fa1db92e9ba612019926b8c4d2df472108ab863316652544596f3b51cf97b7179c886258d88d42af6546029b5332ffb506a1b0259d31972bb6900864c0d1a98af5b1f9d500810925493b31ab1e3b129a0cc08092613e43425bc03e3840bc5119563d43beb9849a3f4094ae8ae6afbba068563d8918dc2b711084c47e2db3ea5b95791fbca566ca751ab83f9e49f4e61764ce88f7c24116d246eecf3a229f0bbccccc4392370103667d5472fd193c4530a0764e8c705a168c651aa9b0825eed30d94a7111b895e8495a138f9a8b7ba5e563b4f529a25ad2867fe8b1ce13b6d41f16a2de991af874163a824111779236c1d4eeb7e415a5d686514bbf92c1ef06929e7881555608c7a3183a719121698b35216eddb27379577301449d5f3a5c1bc892562650709b7369b7707c6620cb54680226e3e102a66d53b26970b09c73c93b90acd7b335afb2d183979c6c7abb8ac0a06e8cf542b0c4fb91ff4183d51957ff2169b16961802c50f4ec64024faca5f9707727b866271244822a11d2015ea581a140a53b7078c71914bb2e04f3deab03bcb1117c3778070c5eeab155da285931b68b9f21749ec0985349c8cd03fe2585080e71c6665cb05ea091ff41e306c892e60686ad5a58ad4a0396b6d30127451d7bbeb0941560689c7952015635c0174c46c761a849c875f4a5973c6b510fa2fd31a690b01c63966a085b499e5352c0e0a45f2faa824012df176800f8348713c7f8aa9a0e1d3adab402b9fe3cbd2b11c70f3079eda7e7171cb4df1a77d178168d18637ba77342b9430d1b843842862ea44aa4c791de60aa84ccdf5515f42b0811276addf276676532fb15570a037201b4b41b09745aefb46608109ef46c071f2901fec1108e83d84a7493e661447c4210904276812ce56b0a312b57444e60c2923b7a6d75772859c53da264b061bd7205eae357cd35135a4310d8e3b1a684cc44c614f0b468ad75b95cb985d901653279ba4b96999c4779c68c1cbdfb50207b3cec7f48484a650d785c14bf0543ca4511be9ceb1da0124b5c0cc696e1300c5349636a5d290ca1b883b7643a90a00ef244f5734b5a9bb5783b7219a1618aae4b7a0f66d87e075c5410ade1a635877ac0a618ff846ac52ba5370684777f78b5ee8b6a3ac376320b68aeb626af2bb236b75ceeb91d166678e0a189014b437a17d8400413f677b363522ffe9a987ebaad22576a3b6858ca5796f304c082c9bd6385f9ff98e64ba2057e731a5d0b3550c22694bbac32173163a69512626a7624b414621730ca793255216e092905710e318a75865b4e1670b512700cd29afcb674b2d3b3338a2ad9d9b82890c98605bb8b531a8901307bcbbcc3e744603353109f095c96a5c50641335b3439c3522e12bc5ae9b47566826b58c2c7bf207f87b5f0bf25340671142e01cc908abfe68ba09935166c191834942e6ac47148c493bf1c2f1b755346a6bf926c5d5f44ec9c258634386c301400cd9a29b58568d12bb57b74bbc11a1eef550a395720d345a285658a0660cffbc49fec8b83b77a3aff25f014b01d4f72322f74d87801dafd3b1e2176d5f855915077ef376b031536f5a8c26012a39f3d201386a4c8c2246661a30888b45431398d400286ab06001553174468797e6ab01a65c1a6792ff06c4c36a2acad8a6c3c115c536aa8358787ce4c75aac85ec5602c2e67823a40d6ad497764800522055cf76b7631b20ae097458db53f2908eaa5a8d3528107caa992fb05a845c109b62c7045c8fc8a0ccc702b6b4874449f781cb4430799b577ce7b4120b950dd20179011a10144c54b7a9f6f1a01c484ee8968d1116c460da728958b574ccaa6e7b3b67a9235ca111d7fc575414c3f1751aa87960524b4ccebcca2272809d5a63bb456ac1380d06438c29524233f59c17d0888901d0b6c32be93b75b13152d40b358a705a04bc5b78a4227fb1be739558ec748c27e74660e65b0b2003f2216b6b103ba7313268a5783e9073c4b74387a30db99605d6f0bf15c93f2f80063f846239c21b22c1616d343280d46745e6609a83a657914bd00968253b5176a939ca2a2c4eba7f5d93cb0cb59e3bb6b230a65dfad5490ddb6fa0a3c99ec8764869330ee94f6df08080e996f7c9095cf12d46a7956986ae58597d0d8037ef7a73ece5349a995aab807e1603192d5a354ffc1020540d6fe8c854b1cc9a2c30a7a621d1173c3991084765b90fd2a58b06862b29aaa307c16004aefa268adc85985cfc361590410adab289eb79cb8f9abd87b9da457b5fdb2b8d48809f2c975e7140149b7fc57958a60a14b4dadf74818f1bbd16eab6f940840f81c2745ab77e22e873e285014b154b9e59d27e164c061cfaf0d595f45d7c821ab54bb7bf1a50108cf605247e5867d150
Encapsulated ? : true
Cipher : 618d4938da6a966795627c52fea714ae433de7faefdbbe3339cfd3fcce66c8c02b0fcdb3e73b2e579abc9d971d343e683d63c7c2c77941ec68774175f86ce9fbb35a80d0b417feabee12a359fec9b24af585560b8075f88e60050b30db3306948727dc104e66c5814355d96eb9204130b8463fdb9d8b41fe7d27a1a23ad06191443a3e8011dd4cb7368c10ddc0b0fb02547f5f0599a9cf3f4f3d805a77dba717a1c10b9350ff495bc0041f76e7369c58d9be90e79ea6ed7609988a1550557a691f80e8b06258ac703ba90c6f3d090d1195ec78dd536529fa0c7406845c885af50857eca3c0a2a4a90aa0c22dd121756c10f986f1614f3db3fcabead02df5567cdcc2851bb685bd3137cfc2dcc0ac5c1558ff144dd800602435790e7c0d478dae0563b50cefdee7790da47319ff245b13971d0523398cc685b3de2a4c3d1a2f60f5018234397d1c4c46c10b81118ea8e8b123c74cadae42c516ac3c5e7c39daabff369444c851ee299880bff64d6781487c4c3022fa559a5ad3919d1c5b644f36de02a8e1073ad29a6e516f71d7ced0d605bf2c5b16d1821fe4568cccb86896b04973daa3ba196e889382636678f2a39ea0ae09bf3ff44f3b9dc4e84d9666f40c206e0b180f3054e6a4ab34979030bfc82a045a457c37f6d103962f59080e1a86b68b568d8065e9258e7c9ae3afd059ab3c8686485796c020639387e404771749aac794f9d1cd9b1b6d9de137fe7b290199f13ff6a37538816924ca28f50310c8d490a25b86985e0677c2b8f5781c9897f499a764f1c5399840f8bd6c4b86c480b21492efa0e996569edad7873501415361621c402d97c77984d76dd5553278e8e9ebe7cac85803022803d48508b98715405977350c949657f46d042834f7b26dd734d25bab7f38e702491518141416841221b217b62f4dc1edbd2ed9974fc5b64ea8221ca7afc2bf58c277c5bbc0f5a17c61e6c33a9a163c35832641d8d825665b59931ab5d69fa672b5572ca134b6782df841045ce7f7fc47707e6083fa95967eecd243550b890d5c7c3560ecf5149f22884ce9dfdd4529b891def5fbeba8ba5b42e545e8f1a6b76ac8b50ea0a168035cfb5381bbe2defdf1b7182ecdd26fc19b4bdec5914fccc6cca5b925bf69e0d59702d85b67ed625ca27333174ce324ba454ba0d5116c88dd23fc4233dceb2aefb345652408b7e45905e0ab1fbbda1c6622e0210ffe6a0571f94535f84a427ad73d7f4b772b94f3d2e9307dd8ec5054f4956c54181c8cc3bf0cd6ce7f02375453450181c6c433884fe399a5943d4953f408497fba4d9901f5149577a955aa45b9eb5c97253314409990d069946fbf5ad8468823ae9befb27e5d31c6f489b98141488b98f894876f316e21856f07fc0156ac04ee1a6b2853ce6a90e97e948879eefa96fed1154a140487b00467106888c8c1df98737976814302a2d62030dfa4a5f70d83e5e4d819b39e5a155599930c4ddf357a6a57bfc92b77e39c5cc665ab354b4cde2b13dd03ff7d8b375887956470
Shared secret : e6a9fc79df8a91733c7f385bc66602a526b54bbf78ed2ac11029a42a2a56f515
Note
Looking at API documentation, in header files, can give you good idea of how to use ML-KEM API. Note, this library doesn't expose any raw pointer based interface, rather everything is wrapped under statically defined std::span
- which one can easily create from std::{array, vector}
. I opt for using statically defined std::span
based function interfaces because we always know, at compile-time, how many bytes the seeds/ keys/ cipher-texts/ shared-secrets are, for various different ML-KEM parameters. This gives much better type safety and compile-time error reporting.