Skip to content

Module-Lattice-based Key Encapsulation Mechanism (draft) Standard by NIST

License

Notifications You must be signed in to change notification settings

itzmeanjan/kyber

Repository files navigation

Caution

This ML-KEM implementation is conformant with ML-KEM draft standard https://doi.org/10.6028/NIST.FIPS.203.ipd and I also try to make it timing leakage free, using dudect (see https://github.com/oreparaz/dudect) -based tests, but be informed that this implementation is not yet audited. If you consider using it in production, please be careful !

ML-KEM (formerly known as Kyber)

Module-Lattice -based Key Encapsulation Mechanism Standard by NIST.

Motivation

ML-KEM is being 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 be 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 kyber - a C++20 header-only constexpr library, implementing ML-KEM, supporting ML-KEM-{512, 768, 1024} parameter sets, as defined in table 2 of ML-KEM draft standard. It's pretty easy to use, see usage.

Note

Find ML-KEM draft standard @ https://doi.org/10.6028/NIST.FIPS.203.ipd - 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.

Prerequisites

  • A C++ compiler with C++20 standard library such as clang++/ g++.
$ clang++ --version
Ubuntu clang version 17.0.6 (9ubuntu1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
  • Build tools such as make, cmake.
$ make --version
GNU Make 4.3

$ cmake --version
cmake version 3.25.1

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 from inside the root of this repository.

Testing

For testing functional correctness of this implementation and conformance with ML-KEM draft standard, you have to issue

Note

Known Answer Test (KAT) files living in this directory are generated by following (reproducible) steps, described in https://gist.github.com/itzmeanjan/c8f5bc9640d0f0bdd2437dfe364d7710.

make -j            # Run tests without any sort of sanitizers
make asan_test -j  # Run tests with AddressSanitizer enabled
make ubsan_test -j # Run tests with UndefinedBehaviourSanitizer enabled
PASSED TESTS (9/9):
       3 ms: build/test.out ML_KEM.ML_KEM_512_KeygenEncapsDecaps
       3 ms: build/test.out ML_KEM.PolynomialSerialization
       4 ms: build/test.out ML_KEM.ML_KEM_768_KeygenEncapsDecaps
       4 ms: build/test.out ML_KEM.ML_KEM_1024_KeygenEncapsDecaps
      41 ms: build/test.out ML_KEM.ML_KEM_512_KnownAnswerTests
      63 ms: build/test.out ML_KEM.ML_KEM_1024_KnownAnswerTests
      64 ms: build/test.out ML_KEM.ML_KEM_768_KnownAnswerTests
     226 ms: build/test.out ML_KEM.CompressDecompressZq
     284 ms: build/test.out ML_KEM.ArithmeticOverZq

In case you're interested in running timing leakage tests using dudect, execute following

Note

dudect is integrated into this library implementation of ML-KEM to find any sort of timing leakages. It checks for constant-timeness of all vital functions including Fujisaki-Okamoto transform, used in decapsulation step. It doesn't check constant-timeness of function which samples public matrix A, because that fails the check anyway, due to use of uniform rejection sampling. As matrix A is public, it's not critical that it must be strictly constant-time.

# Can only be built and run x86_64 machine.

make dudect_test_build -j

# Before running the constant-time tests, it's a good idea to put all CPU cores on "performance" mode.
# You may find guide @ https://github.com/google/benchmark/blob/main/docs/reducing_variance.md helpful.

timeout 10m taskset -c 0 ./build/dudect/test_ml_kem_512.out
timeout 10m taskset -c 0 ./build/dudect/test_ml_kem_768.out
timeout 10m taskset -c 0 ./build/dudect/test_ml_kem_1024.out

Tip

dudect documentation says if t statistic is < 10, we're probably good, yes probably. You may want to read dudect documentation @ https://github.com/oreparaz/dudect. Also you might find the original paper @ https://ia.cr/2016/1123 interesting.

...
meas:   58.90 M, max t:   +2.61, max tau: 3.40e-04, (5/tau)^2: 2.16e+08. For the moment, maybe constant time.
meas:   58.99 M, max t:   +2.65, max tau: 3.45e-04, (5/tau)^2: 2.10e+08. For the moment, maybe constant time.
meas:   59.07 M, max t:   +2.65, max tau: 3.44e-04, (5/tau)^2: 2.11e+08. For the moment, maybe constant time.
meas:   59.16 M, max t:   +2.63, max tau: 3.42e-04, (5/tau)^2: 2.13e+08. For the moment, maybe constant time.
meas:   59.25 M, max t:   +2.68, max tau: 3.49e-04, (5/tau)^2: 2.06e+08. For the moment, maybe constant time.
meas:   59.33 M, max t:   +2.65, max tau: 3.44e-04, (5/tau)^2: 2.12e+08. For the moment, maybe constant time.
meas:   59.42 M, max t:   +2.75, max tau: 3.57e-04, (5/tau)^2: 1.96e+08. For the moment, maybe constant time.
meas:   59.50 M, max t:   +2.72, max tau: 3.53e-04, (5/tau)^2: 2.01e+08. For the moment, maybe constant time.
meas:   59.59 M, max t:   +2.68, max tau: 3.47e-04, (5/tau)^2: 2.08e+08. For the moment, maybe constant time.
meas:   59.66 M, max t:   +2.70, max tau: 3.50e-04, (5/tau)^2: 2.04e+08. For the moment, maybe constant time.
meas:   59.74 M, max t:   +2.70, max tau: 3.50e-04, (5/tau)^2: 2.05e+08. For the moment, maybe constant time.
meas:   59.82 M, max t:   +2.72, max tau: 3.51e-04, (5/tau)^2: 2.03e+08. For the moment, maybe constant time.
meas:   59.89 M, max t:   +2.72, max tau: 3.51e-04, (5/tau)^2: 2.03e+08. For the moment, maybe constant time.
meas:   59.97 M, max t:   +2.64, max tau: 3.41e-04, (5/tau)^2: 2.14e+08. For the moment, maybe constant time.

Benchmarking

For benchmarking ML-KEM public functions such as keygen, encaps and decaps, for various suggested parameter sets, you have to issue.

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.

Note

make perf - was issued when collecting following benchmarks. Notice, cycles column, denoting cost of executing ML-KEM functions, in terms of CPU cycles. Follow https://github.com/google/benchmark/blob/main/docs/perf_counters.md for more details.

On 12th Gen Intel(R) Core(TM) i7-1260P

Compiled with gcc (Ubuntu 14-20240412-0ubuntu1) 14.0.1 20240412.

$ uname -srm
Linux 6.8.0-35-generic x86_64
2024-06-18T21:12:04+04:00
Running ./build/perf.out
Run on (16 X 842.086 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x8)
  L1 Instruction 32 KiB (x8)
  L2 Unified 1280 KiB (x8)
  L3 Unified 18432 KiB (x1)
Load Average: 0.59, 0.65, 0.66
------------------------------------------------------------------------------------------------
Benchmark                          Time             CPU   Iterations     CYCLES items_per_second
------------------------------------------------------------------------------------------------
ml_kem_1024/keygen_mean         37.7 us         37.7 us           10   168.625k       26.5586k/s
ml_kem_1024/keygen_median       37.8 us         37.8 us           10   168.466k       26.4937k/s
ml_kem_1024/keygen_stddev      0.867 us        0.856 us           10    883.281        605.108/s
ml_kem_1024/keygen_cv           2.30 %          2.27 %            10      0.52%            2.28%
ml_kem_1024/keygen_min          36.5 us         36.5 us           10   167.909k       25.8962k/s
ml_kem_1024/keygen_max          38.7 us         38.6 us           10   171.052k       27.3982k/s
ml_kem_512/decap_mean           20.4 us         20.4 us           10   92.5549k       49.0213k/s
ml_kem_512/decap_median         20.3 us         20.3 us           10   92.4039k       49.1818k/s
ml_kem_512/decap_stddev        0.258 us        0.252 us           10    577.305        600.776/s
ml_kem_512/decap_cv             1.26 %          1.23 %            10      0.62%            1.23%
ml_kem_512/decap_min            20.0 us         20.0 us           10   92.1723k       47.8732k/s
ml_kem_512/decap_max            20.9 us         20.9 us           10   94.1701k        49.888k/s
ml_kem_512/encap_mean           16.4 us         16.4 us           10   72.6916k       60.9038k/s
ml_kem_512/encap_median         16.4 us         16.4 us           10   72.6753k       60.8974k/s
ml_kem_512/encap_stddev        0.253 us        0.250 us           10    97.0585        935.823/s
ml_kem_512/encap_cv             1.54 %          1.53 %            10      0.13%            1.54%
ml_kem_512/encap_min            15.9 us         15.9 us           10   72.5484k       59.7296k/s
ml_kem_512/encap_max            16.8 us         16.7 us           10   72.8346k       62.8025k/s
ml_kem_768/decap_mean           33.0 us         33.0 us           10   148.191k       30.3166k/s
ml_kem_768/decap_median         33.1 us         33.1 us           10   148.138k       30.1903k/s
ml_kem_768/decap_stddev        0.518 us        0.509 us           10    212.758        473.277/s
ml_kem_768/decap_cv             1.57 %          1.54 %            10      0.14%            1.56%
ml_kem_768/decap_min            32.1 us         32.1 us           10   147.836k       29.7687k/s
ml_kem_768/decap_max            33.6 us         33.6 us           10    148.61k       31.1568k/s
ml_kem_512/keygen_mean          14.6 us         14.6 us           10   63.4765k       68.3813k/s
ml_kem_512/keygen_median        14.8 us         14.8 us           10   63.4589k       67.7965k/s
ml_kem_512/keygen_stddev       0.241 us        0.240 us           10     60.264       1.14394k/s
ml_kem_512/keygen_cv            1.65 %          1.64 %            10      0.09%            1.67%
ml_kem_512/keygen_min           14.1 us         14.1 us           10   63.3859k       67.5222k/s
ml_kem_512/keygen_max           14.8 us         14.8 us           10   63.5564k       71.0285k/s
ml_kem_1024/decap_mean          49.3 us         49.3 us           10   216.516k       20.2885k/s
ml_kem_1024/decap_median        49.5 us         49.4 us           10   216.383k       20.2235k/s
ml_kem_1024/decap_stddev       0.649 us        0.634 us           10    346.756        261.841/s
ml_kem_1024/decap_cv            1.32 %          1.29 %            10      0.16%            1.29%
ml_kem_1024/decap_min           48.3 us         48.3 us           10   216.031k        19.967k/s
ml_kem_1024/decap_max           50.1 us         50.1 us           10   217.187k       20.6884k/s
ml_kem_1024/encap_mean          41.8 us         41.8 us           10   183.083k       23.9532k/s
ml_kem_1024/encap_median        41.8 us         41.8 us           10   183.077k       23.9381k/s
ml_kem_1024/encap_stddev       0.563 us        0.551 us           10     218.08        315.804/s
ml_kem_1024/encap_cv            1.35 %          1.32 %            10      0.12%            1.32%
ml_kem_1024/encap_min           41.0 us         41.0 us           10   182.737k       23.5351k/s
ml_kem_1024/encap_max           42.6 us         42.5 us           10   183.483k       24.4145k/s
ml_kem_768/encap_mean           27.4 us         27.4 us           10   121.805k       36.5012k/s
ml_kem_768/encap_median         27.4 us         27.4 us           10   121.632k        36.553k/s
ml_kem_768/encap_stddev        0.692 us        0.687 us           10    644.207        909.698/s
ml_kem_768/encap_cv             2.52 %          2.50 %            10      0.53%            2.49%
ml_kem_768/encap_min            26.5 us         26.5 us           10   121.249k       35.0289k/s
ml_kem_768/encap_max            28.6 us         28.5 us           10   123.228k       37.7644k/s
ml_kem_768/keygen_mean          25.0 us         25.0 us           10   110.546k       40.0317k/s
ml_kem_768/keygen_median        25.0 us         25.0 us           10   110.151k       40.0223k/s
ml_kem_768/keygen_stddev       0.855 us        0.854 us           10    861.179       1.36001k/s
ml_kem_768/keygen_cv            3.42 %          3.41 %            10      0.78%            3.40%
ml_kem_768/keygen_min           24.1 us         24.1 us           10   109.801k       38.1413k/s
ml_kem_768/keygen_max           26.2 us         26.2 us           10   112.141k       41.5697k/s

On ARM Cortex-A72 i.e. Raspberry Pi 4B

Compiled with gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0.

$ uname -srm
Linux 6.8.0-1005-raspi aarch64
2024-06-18T21:49:48+04:00
Running ./build/bench.out
Run on (4 X 1800 MHz CPU s)
CPU Caches:
  L1 Data 32 KiB (x4)
  L1 Instruction 48 KiB (x4)
  L2 Unified 1024 KiB (x1)
Load Average: 3.51, 3.90, 2.28
-------------------------------------------------------------------------------------
Benchmark                          Time             CPU   Iterations items_per_second
-------------------------------------------------------------------------------------
ml_kem_1024/decap_mean           258 us          258 us           10       3.87579k/s
ml_kem_1024/decap_median         258 us          258 us           10       3.88038k/s
ml_kem_1024/decap_stddev       0.963 us        0.959 us           10         14.346/s
ml_kem_1024/decap_cv            0.37 %          0.37 %            10            0.37%
ml_kem_1024/decap_min            257 us          257 us           10       3.84585k/s
ml_kem_1024/decap_max            260 us          260 us           10       3.89065k/s
ml_kem_768/decap_mean            174 us          174 us           10        5.7436k/s
ml_kem_768/decap_median          174 us          174 us           10       5.74181k/s
ml_kem_768/decap_stddev        0.323 us        0.324 us           10        10.6771/s
ml_kem_768/decap_cv             0.19 %          0.19 %            10            0.19%
ml_kem_768/decap_min             174 us          174 us           10       5.72691k/s
ml_kem_768/decap_max             175 us          175 us           10       5.75986k/s
ml_kem_768/keygen_mean           119 us          119 us           10       8.40489k/s
ml_kem_768/keygen_median         119 us          119 us           10        8.4065k/s
ml_kem_768/keygen_stddev       0.217 us        0.237 us           10        16.7154/s
ml_kem_768/keygen_cv            0.18 %          0.20 %            10            0.20%
ml_kem_768/keygen_min            119 us          119 us           10       8.37403k/s
ml_kem_768/keygen_max            119 us          119 us           10       8.43292k/s
ml_kem_1024/encap_mean           216 us          216 us           10        4.6302k/s
ml_kem_1024/encap_median         216 us          216 us           10       4.63436k/s
ml_kem_1024/encap_stddev        1.03 us         1.02 us           10        21.7423/s
ml_kem_1024/encap_cv            0.48 %          0.47 %            10            0.47%
ml_kem_1024/encap_min            215 us          215 us           10       4.59301k/s
ml_kem_1024/encap_max            218 us          218 us           10       4.65477k/s
ml_kem_512/decap_mean            109 us          109 us           10       9.21521k/s
ml_kem_512/decap_median          108 us          108 us           10       9.22127k/s
ml_kem_512/decap_stddev        0.248 us        0.243 us           10        20.5809/s
ml_kem_512/decap_cv             0.23 %          0.22 %            10            0.22%
ml_kem_512/decap_min             108 us          108 us           10       9.17837k/s
ml_kem_512/decap_max             109 us          109 us           10       9.24305k/s
ml_kem_768/encap_mean            140 us          140 us           10       7.12907k/s
ml_kem_768/encap_median          140 us          140 us           10       7.13583k/s
ml_kem_768/encap_stddev        0.597 us        0.596 us           10        30.1105/s
ml_kem_768/encap_cv             0.43 %          0.42 %            10            0.42%
ml_kem_768/encap_min             140 us          140 us           10       7.05566k/s
ml_kem_768/encap_max             142 us          142 us           10       7.16165k/s
ml_kem_1024/keygen_mean          188 us          188 us           10       5.32413k/s
ml_kem_1024/keygen_median        188 us          188 us           10       5.32187k/s
ml_kem_1024/keygen_stddev      0.537 us        0.534 us           10        15.1453/s
ml_kem_1024/keygen_cv           0.29 %          0.28 %            10            0.28%
ml_kem_1024/keygen_min           187 us          187 us           10       5.29511k/s
ml_kem_1024/keygen_max           189 us          189 us           10       5.34655k/s
ml_kem_512/encap_mean           83.7 us         83.7 us           10       11.9524k/s
ml_kem_512/encap_median         83.5 us         83.5 us           10       11.9776k/s
ml_kem_512/encap_stddev        0.421 us        0.420 us           10        59.8055/s
ml_kem_512/encap_cv             0.50 %          0.50 %            10            0.50%
ml_kem_512/encap_min            83.2 us         83.2 us           10       11.8419k/s
ml_kem_512/encap_max            84.4 us         84.4 us           10       12.0191k/s
ml_kem_512/keygen_mean          69.2 us         69.2 us           10       14.4436k/s
ml_kem_512/keygen_median        69.2 us         69.2 us           10       14.4496k/s
ml_kem_512/keygen_stddev       0.267 us        0.269 us           10        55.9869/s
ml_kem_512/keygen_cv            0.39 %          0.39 %            10            0.39%
ml_kem_512/keygen_min           68.9 us         68.9 us           10       14.3569k/s
ml_kem_512/keygen_max           69.7 us         69.7 us           10       14.5198k/s

On Apple M1 Max

Compiled with Apple clang version 15.0.0 (clang-1500.3.9.4).

$ uname -srm
Darwin 23.5.0 arm64
2024-06-18T21:24:57+04:00
Running ./build/bench.out
Run on (10 X 24 MHz CPU s)
CPU Caches:
  L1 Data 64 KiB
  L1 Instruction 128 KiB
  L2 Unified 4096 KiB (x10)
Load Average: 2.12, 4.39, 7.54
-------------------------------------------------------------------------------------
Benchmark                          Time             CPU   Iterations items_per_second
-------------------------------------------------------------------------------------
ml_kem_768/keygen_mean          20.7 us         20.7 us           10       48.4041k/s
ml_kem_768/keygen_median        20.7 us         20.7 us           10       48.4089k/s
ml_kem_768/keygen_stddev       0.031 us        0.029 us           10        68.1992/s
ml_kem_768/keygen_cv            0.15 %          0.14 %            10            0.14%
ml_kem_768/keygen_min           20.6 us         20.6 us           10       48.2768k/s
ml_kem_768/keygen_max           20.7 us         20.7 us           10       48.5023k/s
ml_kem_1024/keygen_mean         32.5 us         32.5 us           10       30.8076k/s
ml_kem_1024/keygen_median       32.4 us         32.4 us           10       30.8861k/s
ml_kem_1024/keygen_stddev      0.159 us        0.161 us           10        152.372/s
ml_kem_1024/keygen_cv           0.49 %          0.50 %            10            0.49%
ml_kem_1024/keygen_min          32.4 us         32.3 us           10       30.5386k/s
ml_kem_1024/keygen_max          32.8 us         32.7 us           10       30.9448k/s
ml_kem_768/encap_mean           22.7 us         22.7 us           10        44.144k/s
ml_kem_768/encap_median         22.7 us         22.7 us           10       44.1494k/s
ml_kem_768/encap_stddev        0.037 us        0.037 us           10         72.779/s
ml_kem_768/encap_cv             0.16 %          0.16 %            10            0.16%
ml_kem_768/encap_min            22.6 us         22.6 us           10       43.9993k/s
ml_kem_768/encap_max            22.8 us         22.7 us           10         44.26k/s
ml_kem_768/decap_mean           26.7 us         26.6 us           10       37.5449k/s
ml_kem_768/decap_median         26.6 us         26.6 us           10       37.5935k/s
ml_kem_768/decap_stddev        0.108 us        0.098 us           10        137.284/s
ml_kem_768/decap_cv             0.40 %          0.37 %            10            0.37%
ml_kem_768/decap_min            26.6 us         26.5 us           10       37.2779k/s
ml_kem_768/decap_max            26.9 us         26.8 us           10       37.6739k/s
ml_kem_512/keygen_mean          12.1 us         12.1 us           10       82.8747k/s
ml_kem_512/keygen_median        12.1 us         12.1 us           10       82.9135k/s
ml_kem_512/keygen_stddev       0.016 us        0.018 us           10        120.443/s
ml_kem_512/keygen_cv            0.13 %          0.15 %            10            0.15%
ml_kem_512/keygen_min           12.1 us         12.0 us           10       82.7218k/s
ml_kem_512/keygen_max           12.1 us         12.1 us           10       83.0684k/s
ml_kem_512/encap_mean           13.4 us         13.4 us           10       74.4965k/s
ml_kem_512/encap_median         13.4 us         13.4 us           10        74.512k/s
ml_kem_512/encap_stddev        0.016 us        0.016 us           10        88.0048/s
ml_kem_512/encap_cv             0.12 %          0.12 %            10            0.12%
ml_kem_512/encap_min            13.4 us         13.4 us           10       74.3506k/s
ml_kem_512/encap_max            13.5 us         13.4 us           10       74.6472k/s
ml_kem_1024/encap_mean          35.5 us         35.4 us           10       28.2336k/s
ml_kem_1024/encap_median        35.5 us         35.4 us           10        28.209k/s
ml_kem_1024/encap_stddev       0.133 us        0.134 us           10        106.629/s
ml_kem_1024/encap_cv            0.38 %          0.38 %            10            0.38%
ml_kem_1024/encap_min           35.3 us         35.2 us           10       28.0729k/s
ml_kem_1024/encap_max           35.6 us         35.6 us           10       28.3909k/s
ml_kem_1024/decap_mean          40.4 us         40.3 us           10       24.8064k/s
ml_kem_1024/decap_median        40.4 us         40.3 us           10       24.8086k/s
ml_kem_1024/decap_stddev       0.066 us        0.070 us           10        42.8027/s
ml_kem_1024/decap_cv            0.16 %          0.17 %            10            0.17%
ml_kem_1024/decap_min           40.3 us         40.2 us           10        24.734k/s
ml_kem_1024/decap_max           40.5 us         40.4 us           10       24.8586k/s
ml_kem_512/decap_mean           16.4 us         16.3 us           10       61.1867k/s
ml_kem_512/decap_median         16.4 us         16.3 us           10       61.1979k/s
ml_kem_512/decap_stddev        0.024 us        0.022 us           10        81.9971/s
ml_kem_512/decap_cv             0.15 %          0.13 %            10            0.13%
ml_kem_512/decap_min            16.3 us         16.3 us           10       61.0308k/s
ml_kem_512/decap_max            16.4 us         16.4 us           10        61.308k/s

Usage

kyber is written as a header-only C++20 constexpr library, majorly targeting 64 -bit desktop/ server grade platforms and it's pretty easy to get started with. All you need to do is following.

  • Clone kyber repository.
cd

# Multi-step cloning and importing of submodules
git clone https://github.com/itzmeanjan/kyber.git && pushd kyber && git submodule update --init && popd
# Or do single step cloning and importing of submodules
git clone https://github.com/itzmeanjan/kyber.git --recurse-submodules
  • Write your program while including 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 <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{};

  // Be careful !
  //
  // Read API documentation in include/ml_kem/internals/rng/prng.hpp
  ml_kem_prng::prng_t<128> prng;

  prng.read(d);
  prng.read(z);
  prng.read(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 kyber, sha3 and subtle headers, which includes their definitions ( all of them are header-only libraries ) too.
# Assuming `kyber` was cloned just under $HOME

KYBER_HEADERS=~/kyber/include
SHA3_HEADERS=~/kyber/sha3/include
SUBTLE_HEADERS=~/kyber/subtle/include

g++ -std=c++20 -Wall -Wextra -pedantic -O3 -march=native -I $KYBER_HEADERS -I $SHA3_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 draft standard @ https://doi.org/10.6028/NIST.FIPS.203.ipd.

See example program, where I show how to use ML-KEM-512 API.

g++ -std=c++20 -Wall -Wextra -pedantic -O3 -march=native -I ./include -I ./sha3/include -I ./subtle/include/ examples/ml_kem_768.cpp && ./a.out
ML-KEM-768
Pubkey         : 6653a1f5242faad7b37863433dc56538957f3c412102a17d28bc328c4781c566331f8c0b77093baef24a58d6312ddc719ac67ac2874f3adc8a3e6530adbc14cc069159a99e56277895c17c04da1644db23a6e9c16f31c21959400a8abd483a3fcfc0c5fd759917322a66a2aa77a6956f3b8387443640746b0ac8a282521dd784332d56aa745898c3fcc60a56a0716931bbe69b26c4514d529c79979355c8b40eb97fe7c485ceaa45d610145b4bce7da6343db46b6bf42182931a3ed98bafb66614e024cf8c9e51a90b1fc3702a2b4fe3b0c537fa9a1680b4d2f2044c557b1819300a6225be6c234d07d06a702eeb7110ef05c8973b0cab182efbb9ba07811b87b24e2a652cb428240c53423efcaf201973bf3342e86a8d477191d3544217f143586ba351fb7729ac8a51a51c8ab719fd3568c615a7a438b8967301754cac96a8552af82d8ce8840da56cb7481ad54581904c0d390732eceb23df4483cd7593d949bb0c985042f71018862b0d126702a7b55c8c7d9d44cfea157d4013c57ceb18bcfc2c95d8bb8d6178e0ac738ffcc1b3fca525a7ae83652e0b75836fe6c77d182626a8ca85262a17bc60645105a503b2f0f707e765552d49979273b0cb5870124933c6557ef795b36bf093f6c35ef722c9b2854999d20b5fd23dc6d2381ef38bcf547e37faa8ccda3dea2409deda7992a1951849ce3e7b11f3f98cb0d2283e458af854af9d74c57516a924e74222e9bcac529e88a02913d9ae29ec3cc42269d08ca1bf13a941f95d0bb05da9ac4a1ea2bb86b4c631853ec5f2129834a70ba923c8f1bc3fd5cad8692ef4401417b362f0e729497633794abc21e95a59319403002085b113a7b0544165210c1726346a088a933347a265ba055429e637fc40b111d38446461d77546166f923e5249427e5c62092b6ee2a2b585273c3d545b99673419194e54978d71ca606e238a053988db999904207b8326c5b27a38966c4d99460386c453d12821602b444320da205c980da3ab9d3461d405601a7226c143cf4492b8bf4c63a949a8ad81224c71005abcf6afb4ba7ba94ee437079494f9d78c69ac950711765a7e50ab42ba6bf64a5a7d30e64d14fb845ab37b4cc5b099c44e3cf4f9bc61f3640b5b98560474f9f1054dce9be10db77dea35a2375c66d0a26d7f73e7385b03ca8194dbaaf601184f826bd0a86b1d023ae9548b6d4602cd25c3f46e1c66dca6fd183007d043f6f3a6f5a56089180744d579bfe3cc65f003d91084adac4c0ac5811ea8a3e5aa6500eac125a423000297a7975585a083bcfc807b7722b5a0438c1b11b62abcb9a4623f8090c451690455acf97814074c0b6d6d19180f08afbd5ba0e259ee910a61f684d14e7996e47ba5a19994a13a642a1bb563411979bcaf7b302d70ba750a89867653b93596d03b260c7c0a024949bb7b1b110dc8267d5c5305390da26c7e6296add6ba7533b92540c28b337c5b392a6024c57cc09b899ec72e1466de604aa0c909baeb0b0078324e810481f760ae694b5e88cb034bca48bf70881047c7b6ab7ce04b96ad2bd0142b387bc1824a22742c7ce18ebac7744a616a5631e40ab817426c6130ece8641661ab863c44c2adb64029990aea24c94bec0ad7bfba46cd1894775ac6549b1a63446cade59357e125c589a73
Seckey         : 7fc5b38c54324b214d9db93e4069c03a097931156a9e18291ac9b0ac787dfcd189a5971b3dd79fab1b5bacbb35a8d33d08faa3493c8d26886c0f487bee757b16837799912bbd99200502227ff510a4c39d924434af8233220084888b134fb608b9d424c66b8ec5c34069327c5ac591ee0b774ef42f922aceeae21d44738b74e644c5dc0fff4c8ed2cb25f495126840442370159a7c5bb6631c4b7aa4d0445a1fa63c104b492e986e1109bcab369fa0889ca41202d22c8511b8a35719163336a87ea672798168038439ab5645acf813f773a0a575169250c2a2062618aa3b77d015750abaf6a707019945e590beaf6cc499b8305ef66583e42c24f9b2d720ac286a8b513261d36a4c34eb7a6a012488aa3e58553e5245ac7e7089d327568ec66e9bccc96fa5ce0bcc8b1fda3343857adc564d4a631e695b7ecea241947561519b48a75b4f89ab992f201d3f5c958254c9c8653ee2d08756388152db8284d0a48db1a7b96282e4b168cf16a51362534c4c90ca21c28f5b76a12c6042f61a7de52c2bf8901f3738cec81c1ac75399b63320217ce8833e06c0b022b5883415009e08428cc07570660cd14a818cc2ad1a00aeb011b9622a48dde6632277b784d28651c17438d8457e67a6dbc016e387cdabf15e3c456c17c1b3825376bdb3c3f418ac251c96605b208f35c0027559405240bc419303ea9d1c9a36c10ccc0b443782e1c1e860ad0af914ab7a53cfaa0d7ef1100be74da5b43fcfe4bc60e7397cf009ddda713eb7610a15716e9b9e7ac55269c9494bc910a3753c0a0942ec654f1500826f900bb4776dde03166ef376dd67cc385406d2f4047ffc819b2657e596c964189727f3b8a9705f4dc011fda20c73846f62146085547d872519636697e16c7e89d741c917974e490812482ab704099e003acab375f1376536b85c5c12196b2a7514461a2fc2032bb65164f6cbe16c8fb6754a3cd75bb26635a1f5b96d113dfb1a3b301438608c39be68adb5b359d00b0b39a77f7167a18c75532b18798aea17d6ea64bf798c3ec0438a1c94d3445f906985f3791a3f4a98df13cd0af24d8feba0dbeb08b42635327c31cff5b6da791ad1c33055b18fa6d8b5c7f454c39a70d1e86f96660cea737e7ca52014a6c662e346a09bc25e4027602b4472dc93ee06c91a698be675bb39d2319e91a083b92e4b7b0bcc38771977c300e889ae60bb38b97ca68836d0bcabde4b6a0d70042ee67e051a1503927c422593df1668bb789d7060cc6b09d0a891384d5bbe90b480ba93afa99a079ea85e28a0236437a0ba397705356b0f90757e754f34fb5a591b3888eb5d719b79f462abab333ed078bb90c4b11dec276713c4cb06bca9d98068f48b90802451f69d2acb08abe66c61f6081f008d04fa7bad176da4201c9c2a3b3b29b3e7734b1961b6e2ea8d963350d8301a2dc48ce2e056156275cae8c23665b47a5a49c1abc5a01c98cee22a9dd7584c15b304e5c61cd449453208417075a6c3b999f626795379f1d556da168832a47ac638ce89a59c5a9c0a9746ad219142cf782280311b9cea87598c46ba673ac30a281a052dcd710e1bac328d822c19db8ee7e38925c7378431937b812e03382963b9c26653a1f5242faad7b37863433dc56538957f3c412102a17d28bc328c4781c566331f8c0b77093baef24a58d6312ddc719ac67ac2874f3adc8a3e6530adbc14cc069159a99e56277895c17c04da1644db23a6e9c16f31c21959400a8abd483a3fcfc0c5fd759917322a66a2aa77a6956f3b8387443640746b0ac8a282521dd784332d56aa745898c3fcc60a56a0716931bbe69b26c4514d529c79979355c8b40eb97fe7c485ceaa45d610145b4bce7da6343db46b6bf42182931a3ed98bafb66614e024cf8c9e51a90b1fc3702a2b4fe3b0c537fa9a1680b4d2f2044c557b1819300a6225be6c234d07d06a702eeb7110ef05c8973b0cab182efbb9ba07811b87b24e2a652cb428240c53423efcaf201973bf3342e86a8d477191d3544217f143586ba351fb7729ac8a51a51c8ab719fd3568c615a7a438b8967301754cac96a8552af82d8ce8840da56cb7481ad54581904c0d390732eceb23df4483cd7593d949bb0c985042f71018862b0d126702a7b55c8c7d9d44cfea157d4013c57ceb18bcfc2c95d8bb8d6178e0ac738ffcc1b3fca525a7ae83652e0b75836fe6c77d182626a8ca85262a17bc60645105a503b2f0f707e765552d49979273b0cb5870124933c6557ef795b36bf093f6c35ef722c9b2854999d20b5fd23dc6d2381ef38bcf547e37faa8ccda3dea2409deda7992a1951849ce3e7b11f3f98cb0d2283e458af854af9d74c57516a924e74222e9bcac529e88a02913d9ae29ec3cc42269d08ca1bf13a941f95d0bb05da9ac4a1ea2bb86b4c631853ec5f2129834a70ba923c8f1bc3fd5cad8692ef4401417b362f0e729497633794abc21e95a59319403002085b113a7b0544165210c1726346a088a933347a265ba055429e637fc40b111d38446461d77546166f923e5249427e5c62092b6ee2a2b585273c3d545b99673419194e54978d71ca606e238a053988db999904207b8326c5b27a38966c4d99460386c453d12821602b444320da205c980da3ab9d3461d405601a7226c143cf4492b8bf4c63a949a8ad81224c71005abcf6afb4ba7ba94ee437079494f9d78c69ac950711765a7e50ab42ba6bf64a5a7d30e64d14fb845ab37b4cc5b099c44e3cf4f9bc61f3640b5b98560474f9f1054dce9be10db77dea35a2375c66d0a26d7f73e7385b03ca8194dbaaf601184f826bd0a86b1d023ae9548b6d4602cd25c3f46e1c66dca6fd183007d043f6f3a6f5a56089180744d579bfe3cc65f003d91084adac4c0ac5811ea8a3e5aa6500eac125a423000297a7975585a083bcfc807b7722b5a0438c1b11b62abcb9a4623f8090c451690455acf97814074c0b6d6d19180f08afbd5ba0e259ee910a61f684d14e7996e47ba5a19994a13a642a1bb563411979bcaf7b302d70ba750a89867653b93596d03b260c7c0a024949bb7b1b110dc8267d5c5305390da26c7e6296add6ba7533b92540c28b337c5b392a6024c57cc09b899ec72e1466de604aa0c909baeb0b0078324e810481f760ae694b5e88cb034bca48bf70881047c7b6ab7ce04b96ad2bd0142b387bc1824a22742c7ce18ebac7744a616a5631e40ab817426c6130ece8641661ab863c44c2adb64029990aea24c94bec0ad7bfba46cd1894775ac6549b1a63446cade59357e125c589a73dd18d5e8aad6acb35a89e0958c3ae122197bb6fed165733ca120172d11335a4d60d73fb91d0ffac552692219ef3082477a0f6399aa5dce8a72fd0afaa3b627c9
Encapsulated ? : true
Cipher         : 1d04afad6cf4058acb290f72298587c8afb9e022fc0a4b3e1aa5fdc79cfbe44e7781317adbc1f92fd01a6ad3840386710a369276c50671d2b58272505793736bb9d0e8883c200270ddae19fbc86af41aba366b4ddfd67f8771905b3fccca6da805a1e13a9e697500779cfe52484811e906042fa6e6e93ef641e5e7a46c39969c4683ee7cb440fc4cc452dab5215d6ec32a36fa0e8d7501b5d7dcc9dbfb51cbb1c036b052a7354544a6707099ded7b5e5c5024e2a6f356b2d300585128a30d7b964842d5c06659990c85468b42f5f2b46c39b4fa740a3f7006da01ffa09fb2fd6b5b0e9174bd7a801972b647df2825842b8ad146220a1ddcc9eee6967954e8d960bbf5ea8a74ae0306061c44e2995eb451171bd3eb4679579922e48e713ad40cddcd14343dc57a181e3067f1b01895122a447cf002b600c96a30c5f809efcc459cebc8723ca5b5147d2f9d09186f31bba013f19e63294cc5a57c0184b838cb9d51c62e0303c9a029cf6a5c489ccb43bd0bdd4da61f147d6ef9c2b95a758d0c2b9a9265e7cf4255989c07799940c517ecd527cca2acf62d104e2d45a176e35852d81f42397c93d3b2b1c7fde3cc6f4cd5d6c166f7312e34f690a07ecbaac69a045358564142422b45c58784cd5d2d69d9084b7e9f33176893bee2f1589725ed1a443f4b9095e97294f740e8471f468a51db85cc66176af022db77314579776b69eaa8594dbed5d0e0b549675e12c742913da76e3de732c24f7811d8ee32ade2ac1bcb8763c0e898a67695aaab9478c80dc29cc3ae9f1c4b63c116bda64e1e8727881ebe4c1db30219a87d7ff8805675b56a4907d9408bb96438a5182c66a47739f8b12cd5241b5f4e995f4f1fc85041eeaeb158d7ea9c1601a9b3849c6977137a0e82afb72b16748efa456fbae5b28ed82107d79dec3da87d0c0261267a3dbe9dcedb374d96fc00b7478b30f917b2312e7e79133923c2d9aba394bfcdbd00539f7d2d4fdecf9821fdb4c15f253e5ad80d10e360fb84b45e01415a4d5759cd5000ea5c4e80f60a887f9e8ad35ef7cabab83eeb59bf81b3bb10b440707e877c558ca9c80df8d3d8741b838ddf9a5e0e7826a1f6ee0c4f2241687ab0573b18814d21a668861962400148b45a24fdfeb3638a1f16b7c344b088cfffc851317753c1e0602bb0cbfb5357132baf29d6123862eb8b29229a5fd9b173ad4c1b098d11ff23f6ee1c7d357235e647dd99451162cfbed33b7d05df5578859538a9edbeae2cf8ac0903c36e7db352c147c11725a3c5c611b149a4c87e24589d9e31d30a9a8b2cdd863b8dd3ab8c90cde061426a2afedb4aff424cde10e70f1e38207d0fc8be467b4f063739d920bb1906144a704c7ba5be6645899270e5da6380dabfb16e7f906a1f484501005cb383692e054533697a63c8a2f8e1b891b37d5b23afef1de8f9a257f7c9577466fbd87223c5773795ac23ab4cfc0043a965e8695e764174bdc1c778d3d1d6e2a65d9cb7a4b1eb31ca818b0c8abe779fd61a34ee78cfc49fd7682
Shared secret  : ee30e0696c36480afb066fa2971535f195a30ce08aacc3dfc182ed0947a44f3a

Caution

Before you consider using Psuedo Random Number Generator which comes with this library implementation, I strongly advice you to go through include/ml_kem/internals/rng/prng.hpp.

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.