Why it is important ? Read https://github.com/oreparaz/dudect/pull/30 Signed-off-by: Anjan Roy <hello@itzmeanjan.in>
Caution
This Kyber implementation is conformant with Kyber specification https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf and I also try to make it timing leakage free, using dudect (see https://github.com/oreparaz/dudect) but be informed that it is not yet audited. If you consider using it in production, be careful !
kyber
CRYSTALS-Kyber: Post-Quantum Public-key Encryption & Key-establishment Algorithm
Motivation
Kyber is being standardized by NIST as post-quantum secure key encapsulation mechanism (KEM), which can be used for key establishment.
Kyber offers an IND-CCA2-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.
Kyber Key Encapsulation Mechanism is built on top of IND-CPA-secure Kyber Public Key Encryption, 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 Kyber PKE - giving us the IND-CCA2-secure KEM construction. In KEM scheme, two parties interested in establishing a secure communication channel over public & insecure channel, can generate a shared secret key ( of arbitrary byte length ) from a key derivation function ( i.e. KDF which is SHAKE256 Xof in this context ) which is obtained by both of these parties as result of seeding SHAKE256 Xof with same secret. This secret is 32 -bytes and that's what is communicated by sender to receiver using underlying Kyber PKE scheme.
| Algorithm | Input | Output |
|---|---|---|
| KEM KeyGen | - | Public Key and Secret Key |
| Encapsulation | Public Key | Cipher Text and SHAKE256 KDF |
| Decapsulation | Secret Key and Cipher Text | SHAKE256 KDF |
Here I'm maintaining kyber - a header-only and easy-to-use ( see more in usage ) C++ library implementing Kyber KEM, supporting Kyber-{512, 768, 1024} parameter sets, as defined in table 1 of Kyber specification. sha3, subtle and dudect (for timing leakage tests) are dependencies of this library, which are pinned to specific git commits, using git submodule.
Note
Find Kyber specification https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf - this is the document that I followed when implementing Kyber. I suggest you go through the specification to get an in-depth understanding of Kyber PQC suite.
Prerequisites
- A C++ compiler with C++20 standard library such as
clang++/g++.
$ clang++ --version
Ubuntu clang version 17.0.2 (1~exp1ubuntu2.1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ g++ --version
g++ (Ubuntu 13.2.0-4ubuntu3) 13.2.0
- Build tools such as
make,cmake.
$ make --version
GNU Make 4.3
$ cmake --version
cmake version 3.25.1
- For testing Kyber KEM implementation, you need to globally install
google-testlibrary and headers. Follow this guide, if you don't have it installed. - For benchmarking Kyber KEM implementation, targeting CPU systems, you'll need to have
google-benchmarkheader 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 KEM routines, you should consider building
google-benchmarklibrary withlibPFMsupport, 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 mostly be imported automatically, but in case that doesn't work, you can manually initialize and update them by issuing
$ git submodule update --initfrom inside the root of this repository.
Testing
For testing functional correctness and conformance with Kyber specification, 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
Note: Randomizing tests' orders with a seed of 50193 .
[==========] Running 10 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 10 tests from KyberKEM
[ RUN ] KyberKEM.ArithmeticOverZq
[ OK ] KyberKEM.ArithmeticOverZq (126 ms)
[ RUN ] KyberKEM.Kyber768KeygenEncapsDecaps
[ OK ] KyberKEM.Kyber768KeygenEncapsDecaps (0 ms)
[ RUN ] KyberKEM.Kyber512KeygenEncapsDecaps
[ OK ] KyberKEM.Kyber512KeygenEncapsDecaps (0 ms)
[ RUN ] KyberKEM.Kyber768KnownAnswerTests
[ OK ] KyberKEM.Kyber768KnownAnswerTests (8 ms)
[ RUN ] KyberKEM.Kyber512KnownAnswerTests
[ OK ] KyberKEM.Kyber512KnownAnswerTests (5 ms)
[ RUN ] KyberKEM.CompressDecompressZq
[ OK ] KyberKEM.CompressDecompressZq (98 ms)
[ RUN ] KyberKEM.Kyber1024KnownAnswerTests
[ OK ] KyberKEM.Kyber1024KnownAnswerTests (13 ms)
[ RUN ] KyberKEM.NumberTheoreticTransform
[ OK ] KyberKEM.NumberTheoreticTransform (0 ms)
[ RUN ] KyberKEM.PolynomialSerialization
[ OK ] KyberKEM.PolynomialSerialization (0 ms)
[ RUN ] KyberKEM.Kyber1024KeygenEncapsDecaps
[ OK ] KyberKEM.Kyber1024KeygenEncapsDecaps (0 ms)
[----------] 10 tests from KyberKEM (253 ms total)
[----------] Global test environment tear-down
[==========] 10 tests from 1 test suite ran. (253 ms total)
[ PASSED ] 10 tests.
In case you're interested in running timing leakage tests using dudect, execute following
Note
dudectis integrated into this library implementation of Kyber 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 matrixA, because that fails the check anyway, due to use of uniform rejection sampling. As matrixAis public, it's not critical that it must be strictly constant-time.
make dudect_test -j # Only on x86_64 machine
# Each executable is run for at max 3 mins.
Tip
dudectdocumentation says iftstatistic is < 10, we're probably good, yes probably. You may want to readdudectdocumentation @ https://github.com/oreparaz/dudect. Also you might find the original paper @ https://ia.cr/2016/1123 interesting.
meas: 0.10 M, max t: +2.35, max tau: 7.27e-03, (5/tau)^2: 4.73e+05. For the moment, maybe constant time.
meas: 0.12 M, max t: +1.89, max tau: 5.57e-03, (5/tau)^2: 8.06e+05. For the moment, maybe constant time.
meas: 3.10 M, max t: +2.48, max tau: 1.41e-03, (5/tau)^2: 1.26e+07. For the moment, maybe constant time.
meas: 2.07 M, max t: +1.72, max tau: 1.20e-03, (5/tau)^2: 1.75e+07. For the moment, maybe constant time.
meas: 2.10 M, max t: +1.66, max tau: 1.14e-03, (5/tau)^2: 1.91e+07. For the moment, maybe constant time.
meas: 6.01 M, max t: +1.67, max tau: 6.82e-04, (5/tau)^2: 5.37e+07. For the moment, maybe constant time.
meas: 7.31 M, max t: +1.67, max tau: 6.18e-04, (5/tau)^2: 6.54e+07. For the moment, maybe constant time.
meas: 7.96 M, max t: +2.04, max tau: 7.22e-04, (5/tau)^2: 4.80e+07. For the moment, maybe constant time.
meas: 9.41 M, max t: +1.70, max tau: 5.54e-04, (5/tau)^2: 8.14e+07. For the moment, maybe constant time.
meas: 9.89 M, max t: +1.59, max tau: 5.05e-04, (5/tau)^2: 9.78e+07. For the moment, maybe constant time.
meas: 0.99 M, max t: +2.16, max tau: 2.18e-03, (5/tau)^2: 5.28e+06. For the moment, maybe constant time.
meas: 0.14 M, max t: +2.04, max tau: 5.44e-03, (5/tau)^2: 8.45e+05. For the moment, maybe constant time.
meas: 2.31 M, max t: +2.90, max tau: 1.90e-03, (5/tau)^2: 6.89e+06. For the moment, maybe constant time.
meas: 3.03 M, max t: +3.55, max tau: 2.04e-03, (5/tau)^2: 5.99e+06. For the moment, maybe constant time.
meas: 3.56 M, max t: +3.23, max tau: 1.71e-03, (5/tau)^2: 8.56e+06. For the moment, maybe constant time.
meas: 4.18 M, max t: +2.42, max tau: 1.18e-03, (5/tau)^2: 1.78e+07. For the moment, maybe constant time.
meas: 7.16 M, max t: +2.40, max tau: 8.96e-04, (5/tau)^2: 3.12e+07. For the moment, maybe constant time.
meas: 8.25 M, max t: +2.21, max tau: 7.68e-04, (5/tau)^2: 4.24e+07. For the moment, maybe constant time.
meas: 9.20 M, max t: +2.27, max tau: 7.48e-04, (5/tau)^2: 4.47e+07. For the moment, maybe constant time.
meas: 10.23 M, max t: +2.45, max tau: 7.66e-04, (5/tau)^2: 4.26e+07. For the moment, maybe constant time.
meas: 6.93 M, max t: +2.54, max tau: 9.65e-04, (5/tau)^2: 2.69e+07. For the moment, maybe constant time.
meas: 7.49 M, max t: +2.54, max tau: 9.30e-04, (5/tau)^2: 2.89e+07. For the moment, maybe constant time.
meas: 8.04 M, max t: +2.16, max tau: 7.61e-04, (5/tau)^2: 4.32e+07. For the moment, maybe constant time.
meas: 8.57 M, max t: +2.08, max tau: 7.10e-04, (5/tau)^2: 4.96e+07. For the moment, maybe constant time.
meas: 9.15 M, max t: +2.03, max tau: 6.72e-04, (5/tau)^2: 5.54e+07. For the moment, maybe constant time.
meas: 0.15 M, max t: +1.80, max tau: 4.60e-03, (5/tau)^2: 1.18e+06. For the moment, maybe constant time.
meas: 8.04 M, max t: +1.90, max tau: 6.70e-04, (5/tau)^2: 5.57e+07. For the moment, maybe constant time.
meas: 10.31 M, max t: +2.04, max tau: 6.35e-04, (5/tau)^2: 6.20e+07. For the moment, maybe constant time.
meas: 10.38 M, max t: +2.05, max tau: 6.35e-04, (5/tau)^2: 6.19e+07. For the moment, maybe constant time.
meas: 9.19 M, max t: +1.99, max tau: 6.56e-04, (5/tau)^2: 5.80e+07. For the moment, maybe constant time.
meas: 9.24 M, max t: +2.04, max tau: 6.69e-04, (5/tau)^2: 5.58e+07. For the moment, maybe constant time.
meas: 1.02 M, max t: +1.98, max tau: 1.97e-03, (5/tau)^2: 6.47e+06. For the moment, maybe constant time.
meas: 2.10 M, max t: +2.10, max tau: 1.45e-03, (5/tau)^2: 1.19e+07. For the moment, maybe constant time.
meas: 1.40 M, max t: +1.81, max tau: 1.52e-03, (5/tau)^2: 1.08e+07. For the moment, maybe constant time.
meas: 1.41 M, max t: +2.21, max tau: 1.86e-03, (5/tau)^2: 7.22e+06. For the moment, maybe constant time.
meas: 1.81 M, max t: +2.95, max tau: 2.19e-03, (5/tau)^2: 5.20e+06. For the moment, maybe constant time.
meas: 2.54 M, max t: +2.96, max tau: 1.86e-03, (5/tau)^2: 7.26e+06. For the moment, maybe constant time.
meas: 3.15 M, max t: +2.77, max tau: 1.56e-03, (5/tau)^2: 1.02e+07. For the moment, maybe constant time.
meas: 4.94 M, max t: +2.46, max tau: 1.11e-03, (5/tau)^2: 2.04e+07. For the moment, maybe constant time.
meas: 0.91 M, max t: +2.06, max tau: 2.17e-03, (5/tau)^2: 5.32e+06. For the moment, maybe constant time.
meas: 1.21 M, max t: +2.19, max tau: 1.99e-03, (5/tau)^2: 6.32e+06. For the moment, maybe constant time.
meas: 1.44 M, max t: +2.24, max tau: 1.87e-03, (5/tau)^2: 7.17e+06. For the moment, maybe constant time.
meas: 8.74 M, max t: +2.32, max tau: 7.87e-04, (5/tau)^2: 4.04e+07. For the moment, maybe constant time.
meas: 9.65 M, max t: +2.42, max tau: 7.80e-04, (5/tau)^2: 4.11e+07. For the moment, maybe constant time.
meas: 10.57 M, max t: +2.22, max tau: 6.82e-04, (5/tau)^2: 5.38e+07. For the moment, maybe constant time.
meas: 11.71 M, max t: +2.45, max tau: 7.16e-04, (5/tau)^2: 4.88e+07. For the moment, maybe constant time.
Benchmarking
For benchmarking Kyber KEM routines ( i.e. keygen, encaps and decaps ) for various suggested parameter sets, you have to issue.
make benchmark # If you haven't built google-benchmark library with libPFM support.
make perf # If you have built google-benchmark library with libPFM support.
Note
Benchmarking expects presence of
google-benchmarkheader and library in global namespace ( so that it can be found by the compiler ).
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 Kyber KEM routines in terms of CPU cycles. Follow this for more details.
On 12th Gen Intel(R) Core(TM) i7-1260P ( compiled with GCC-13.2.0 )
2023-12-28T22:37:22+04:00
Running ./build/perf.out
Run on (16 X 3037.9 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.57, 0.62, 0.43
---------------------------------------------------------------------------------------------------------
Benchmark Time CPU Iterations CYCLES items_per_second rdtsc
---------------------------------------------------------------------------------------------------------
kyber512/keygen_mean 14.1 us 14.1 us 10 64.2551k 71.0585k/s 35.0906k
kyber512/keygen_median 14.1 us 14.1 us 10 64.358k 71.1093k/s 35.062k
kyber512/keygen_stddev 0.139 us 0.140 us 10 336.699 706.085/s 347.453
kyber512/keygen_cv 0.99 % 0.99 % 10 0.52% 0.99% 0.99%
kyber512/keygen_min 13.8 us 13.8 us 10 63.5224k 69.9823k/s 34.448k
kyber512/keygen_max 14.3 us 14.3 us 10 64.6034k 72.3792k/s 35.627k
kyber1024/keygen_mean 37.6 us 37.6 us 10 173.352k 26.5753k/s 93.9093k
kyber1024/keygen_median 37.4 us 37.4 us 10 173.215k 26.7224k/s 93.369k
kyber1024/keygen_stddev 0.653 us 0.653 us 10 786.61 458.727/s 1.62786k
kyber1024/keygen_cv 1.73 % 1.74 % 10 0.45% 1.73% 1.73%
kyber1024/keygen_min 36.8 us 36.8 us 10 172.387k 25.8361k/s 91.825k
kyber1024/keygen_max 38.7 us 38.7 us 10 174.533k 27.1731k/s 96.567k
kyber1024/decap_mean 47.8 us 47.8 us 10 220.881k 20.936k/s 119.188k
kyber1024/decap_median 47.7 us 47.7 us 10 220.494k 20.9441k/s 119.135k
kyber1024/decap_stddev 0.327 us 0.326 us 10 876.006 142.939/s 815.352
kyber1024/decap_cv 0.68 % 0.68 % 10 0.40% 0.68% 0.68%
kyber1024/decap_min 47.3 us 47.4 us 10 219.937k 20.7556k/s 118.145k
kyber1024/decap_max 48.2 us 48.2 us 10 222.431k 21.1193k/s 120.224k
kyber768/keygen_mean 23.6 us 23.6 us 10 108.277k 42.3383k/s 58.9271k
kyber768/keygen_median 23.6 us 23.6 us 10 108.008k 42.3843k/s 58.8515k
kyber768/keygen_stddev 0.356 us 0.355 us 10 861.437 631.197/s 887.049
kyber768/keygen_cv 1.51 % 1.50 % 10 0.80% 1.49% 1.51%
kyber768/keygen_min 23.2 us 23.2 us 10 107.164k 41.1642k/s 57.96k
kyber768/keygen_max 24.3 us 24.3 us 10 109.738k 43.0367k/s 60.599k
kyber768/encap_mean 29.2 us 29.2 us 10 133.283k 34.1969k/s 72.9656k
kyber768/encap_median 29.2 us 29.2 us 10 133.036k 34.2633k/s 72.8075k
kyber768/encap_stddev 0.446 us 0.446 us 10 696.703 513.482/s 1.11308k
kyber768/encap_cv 1.53 % 1.53 % 10 0.52% 1.50% 1.53%
kyber768/encap_min 28.8 us 28.8 us 10 132.576k 33.0903k/s 71.751k
kyber768/encap_max 30.2 us 30.2 us 10 134.318k 34.768k/s 75.392k
kyber768/decap_mean 31.8 us 31.8 us 10 146.582k 31.4757k/s 79.2733k
kyber768/decap_median 31.8 us 31.8 us 10 146.415k 31.4736k/s 79.2685k
kyber768/decap_stddev 0.346 us 0.345 us 10 842.496 342.357/s 864.056
kyber768/decap_cv 1.09 % 1.09 % 10 0.57% 1.09% 1.09%
kyber768/decap_min 31.3 us 31.3 us 10 145.565k 31.0697k/s 78.03k
kyber768/decap_max 32.2 us 32.2 us 10 148.349k 31.9721k/s 80.298k
kyber512/decap_mean 19.8 us 19.8 us 10 90.6467k 50.6123k/s 49.2887k
kyber512/decap_median 19.9 us 19.9 us 10 90.5468k 50.349k/s 49.5365k
kyber512/decap_stddev 0.282 us 0.282 us 10 386.91 725.855/s 703.452
kyber512/decap_cv 1.43 % 1.43 % 10 0.43% 1.43% 1.43%
kyber512/decap_min 19.4 us 19.4 us 10 90.2405k 49.6676k/s 48.316k
kyber512/decap_max 20.1 us 20.1 us 10 91.6688k 51.6211k/s 50.221k
kyber1024/encap_mean 44.3 us 44.3 us 10 203.866k 22.5631k/s 110.616k
kyber1024/encap_median 44.1 us 44.1 us 10 203.541k 22.7015k/s 109.918k
kyber1024/encap_stddev 0.781 us 0.780 us 10 1.19386k 393.859/s 1.94932k
kyber1024/encap_cv 1.76 % 1.76 % 10 0.59% 1.75% 1.76%
kyber1024/encap_min 43.4 us 43.4 us 10 202.563k 21.9678k/s 108.235k
kyber1024/encap_max 45.5 us 45.5 us 10 206.041k 23.0525k/s 113.597k
kyber512/encap_mean 17.8 us 17.8 us 10 81.0681k 56.056k/s 44.4971k
kyber512/encap_median 17.9 us 17.9 us 10 81.0694k 55.9365k/s 44.5815k
kyber512/encap_stddev 0.260 us 0.259 us 10 200.398 822.202/s 648.275
kyber512/encap_cv 1.46 % 1.45 % 10 0.25% 1.47% 1.46%
kyber512/encap_min 17.4 us 17.4 us 10 80.8481k 55.0013k/s 43.277k
kyber512/encap_max 18.2 us 18.2 us 10 81.4717k 57.6231k/s 45.351k
On ARM Cortex-A72 i.e. Raspberry Pi 4B ( compiled with GCC-13.2.0 )
2023-12-28T23:28:27+04:00
Running ./build/perf.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: 0.96, 0.80, 0.34
----------------------------------------------------------------------------------------------
Benchmark Time CPU Iterations CYCLES items_per_second
----------------------------------------------------------------------------------------------
kyber1024/encap_mean 225 us 224 us 10 402.708k 4.46187k/s
kyber1024/encap_median 224 us 224 us 10 402k 4.46839k/s
kyber1024/encap_stddev 1.04 us 1.01 us 10 1.83888k 19.9568/s
kyber1024/encap_cv 0.46 % 0.45 % 10 0.46% 0.45%
kyber1024/encap_min 224 us 223 us 10 400.783k 4.42744k/s
kyber1024/encap_max 226 us 226 us 10 405.835k 4.48239k/s
kyber1024/decap_mean 252 us 251 us 10 451.164k 3.98273k/s
kyber1024/decap_median 252 us 251 us 10 451.455k 3.9805k/s
kyber1024/decap_stddev 0.813 us 0.807 us 10 1.42897k 12.8137/s
kyber1024/decap_cv 0.32 % 0.32 % 10 0.32% 0.32%
kyber1024/decap_min 250 us 250 us 10 448.489k 3.96263k/s
kyber1024/decap_max 253 us 252 us 10 453.484k 4.00678k/s
kyber512/keygen_mean 69.0 us 68.8 us 10 123.701k 14.5275k/s
kyber512/keygen_median 69.0 us 68.8 us 10 123.636k 14.5345k/s
kyber512/keygen_stddev 0.200 us 0.200 us 10 356.274 42.215/s
kyber512/keygen_cv 0.29 % 0.29 % 10 0.29% 0.29%
kyber512/keygen_min 68.6 us 68.5 us 10 123.032k 14.4587k/s
kyber512/keygen_max 69.3 us 69.2 us 10 124.294k 14.6072k/s
kyber768/keygen_mean 119 us 119 us 10 213.373k 8.42213k/s
kyber768/keygen_median 119 us 119 us 10 213.278k 8.42447k/s
kyber768/keygen_stddev 0.408 us 0.383 us 10 695.469 27.1211/s
kyber768/keygen_cv 0.34 % 0.32 % 10 0.33% 0.32%
kyber768/keygen_min 119 us 118 us 10 212.479k 8.36808k/s
kyber768/keygen_max 120 us 120 us 10 214.775k 8.45855k/s
kyber768/decap_mean 170 us 169 us 10 304.171k 5.90762k/s
kyber768/decap_median 170 us 169 us 10 304.077k 5.90893k/s
kyber768/decap_stddev 0.660 us 0.626 us 10 1.12079k 21.8452/s
kyber768/decap_cv 0.39 % 0.37 % 10 0.37% 0.37%
kyber768/decap_min 169 us 168 us 10 302.481k 5.86956k/s
kyber768/decap_max 171 us 170 us 10 306.146k 5.94026k/s
kyber512/decap_mean 107 us 107 us 10 191.962k 9.36013k/s
kyber512/decap_median 107 us 107 us 10 191.84k 9.36637k/s
kyber512/decap_stddev 0.529 us 0.539 us 10 966.932 46.9797/s
kyber512/decap_cv 0.49 % 0.50 % 10 0.50% 0.50%
kyber512/decap_min 106 us 106 us 10 190.437k 9.25135k/s
kyber512/decap_max 108 us 108 us 10 194.256k 9.43608k/s
kyber512/encap_mean 90.4 us 90.2 us 10 162.028k 11.0908k/s
kyber512/encap_median 90.4 us 90.2 us 10 162.064k 11.0875k/s
kyber512/encap_stddev 0.191 us 0.181 us 10 309.007 22.3167/s
kyber512/encap_cv 0.21 % 0.20 % 10 0.19% 0.20%
kyber512/encap_min 90.0 us 89.9 us 10 161.51k 11.0601k/s
kyber512/encap_max 90.6 us 90.4 us 10 162.418k 11.1282k/s
kyber768/encap_mean 148 us 148 us 10 265.56k 6.76723k/s
kyber768/encap_median 148 us 148 us 10 265.539k 6.76699k/s
kyber768/encap_stddev 0.537 us 0.454 us 10 823.561 20.7123/s
kyber768/encap_cv 0.36 % 0.31 % 10 0.31% 0.31%
kyber768/encap_min 147 us 147 us 10 264.361k 6.71907k/s
kyber768/encap_max 149 us 149 us 10 267.48k 6.79829k/s
kyber1024/keygen_mean 189 us 188 us 10 338.248k 5.31272k/s
kyber1024/keygen_median 188 us 188 us 10 337.954k 5.31735k/s
kyber1024/keygen_stddev 0.422 us 0.408 us 10 726.17 11.5098/s
kyber1024/keygen_cv 0.22 % 0.22 % 10 0.21% 0.22%
kyber1024/keygen_min 188 us 188 us 10 337.245k 5.29872k/s
kyber1024/keygen_max 189 us 189 us 10 339.141k 5.32926k/
Usage
kyber is written as a header-only C++ library, majorly targeting 64 -bit platforms and it's pretty easy to get started with. All you need to do is following.
- Clone
kyberrepository.
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 Kyber KEM you want to use, see include directory ), which includes declarations ( and definitions ) of all required KEM routines and constants ( such as byte length of public/ private keys and cipher text ).
// main.cpp
#include "kyber512_kem.hpp"
#include <algorithm>
#include <array>
#include <cassert>
int
main()
{
std::array<uint8_t, 32> d{}; // seed
std::array<uint8_t, 32> z{}; // seed
std::array<uint8_t, kyber512_kem::PKEY_LEN> pkey{};
std::array<uint8_t, kyber512_kem::SKEY_LEN> skey{};
std::array<uint8_t, 32> m{}; // seed
std::array<uint8_t, kyber512_kem::CIPHER_LEN> cipher{};
// Be careful !
//
// Read API documentation in include/prng.hpp
prng::prng_t prng;
prng.read(d);
prng.read(z);
prng.read(m);
kyber512_kem::keygen(d, z, pkey, skey);
auto skdf = kyber512_kem::encapsulate(m, pkey, cipher);
auto rkdf = kyber512_kem::decapsulate(skey, cipher);
std::array<uint8_t, 32> sender_key{};
skdf.squeeze(sender_key);
std::array<uint8_t, 32> receiver_key{};
rkdf.squeeze(receiver_key);
assert(std::ranges::equal(sender_key, receiver_key));
return 0;
}
- When compiling your program, let your compiler know where it can find
kyber,sha3andsubtleheaders, which includes their definitions ( kyber being a header-only library ) 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 -O3 -march=native -I $KYBER_HEADERS -I $SHA3_HEADERS -I $SUBTLE_HEADERS main.cpp
| Kyber KEM Variant | Namespace | Header |
|---|---|---|
| Kyber512 KEM Routines | kyber512_kem:: |
include/kyber512_kem.hpp |
| Kyber768 KEM Routines | kyber768_kem:: |
include/kyber768_kem.hpp |
| Kyber1024 KEM Routines | kyber1024_kem:: |
include/kyber1024_kem.hpp |
Note
Kyber parameter sets are selected from table 1 of Kyber specification https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf.
See example program, where I show how to use Kyber512 KEM API. You can almost similarly use Kyber768 or Kyber1024 KEM API, by just importing correct header file and using KEM functions/ constants from respective namespace.
g++ -std=c++20 -Wall -Wextra -pedantic -O3 -march=native -I ./include -I ./sha3/include -I ./subtle/include/ examples/kyber512_kem.cpp && ./a.out
Kyber512 KEM
pubkey : 175782d35b2666833aee098617626d88dbcc47091a011882d52105acc218c9287a95276a3259a6a94aa386d8148886abdcc1841f39260ce4754ebacc1fd36102905d4c623d0b27930b4c249ee7380758c0ac5982b0e932eda95184a40f55c451d835861ca2b314dbce97829f1b92752dda592d8960b2540f464988ea1c974c63467c439b1de540490b0af0491a6507951ebc971887bd2b4a11327381d99586f10668c83abe92fb649b113da7ec666729bc1cc38a1de137dd3cc4e3a6abb9881a2ee63e7df3ad6cb680664ba1559ca17448c968b7c867ac5f324911ffd43993b8a7b8f57094c786877c1208fa7f53e51d6f1a46ae71bc81f78ebe5808d48200b7e1bc81ec3d31070a6993aa5db237eb3a4c592aa559a73bd769583a0ad095ec1669b952be4a71fe8603f5d597f007a048cc9d7fea6735383b6b8bbf896b74dc48a21840a92c497a9bc7434b0241a9e42e6428515d477c4e0b3678fab1d619b794f01b828648e7577bb2e5297915b9fdf33cb291a37de51b51c7aca6f07994193bd981134da2340c23a93cda8b68e429ac801d3748b8d112b57e388511e3305e50a51184b623607447468be94351cd0b9111a119b4b3c6f270c1cfea749a2ac89455590280c369163946481dbaeb4693dbb376202db2d8464c61aea6411cd887080f5c59e1587da01510cd1b0e8b030a5c200639ba26376134e88279891b90373cc92e7a76c0aaa33d084ab3f61e175010996652e441300ad5aefda9cc88f17fef2102b643179e0a49a60c47ce06c5b1a0b150b09ca4593e5dd48a9b1979d103ba862c43ed354d2ec99575b70e741808288aa0e1cb792c0a458d4584ddfa1870d7b797e2aac7d4cc08916015401338d8841d226d9656661cda93f53343e0f906b82bce8f25428b02a639a47f7dda5b946a3785656fb6d083df5a5ec7493cc017a2469b1f43c96f2e3bbc9d6cb07bec82d721a4cfba6ca2c59b0e01bda98585692b9da753923f830b52c843b6d963f959ad60189f42d61df7808f4d131c4d233e246c4735193e516452061701e6114cf1587a54c79105f48fdce9c2134bb60550b242945ea011ec54c570054b93d96f072426b7c9b524db8d2f136b7db2d1f38897
seckey : a598a250c2008688af8f71a285abae5b528a19479acf915cd2f92a7365bc757c670accc4b2190aa77b7d0c76355962a0ea9b6a1f4400be77797a6851776815032307913aa475b733a1ba698b2134ea25a57bd9b979e2bcb7d99f24f06ee760227486ae1cdaba79065bc3180d79a0906c514e5b973435c00f34b87e882643ef6b42bcbca4a3b65207abb5dca76e49a9be7a6013d256bc09b1211b70bb28e2151200c6c1e00082e88634600a29e3cf5ff541051c703ac373a91228a6d30491221df6749e22b21429612ed4ba07a7d7789717809e498f2e3a1b8a6a40afe0a7d2460350074a2a5127cc20c0b03446977a612a096324337cd5bc455f77cdbe4600e147b02fe58bc9c383b1e84ea3bc5755d3a87ce515b07c96741a72d9eb702d445acc3374531c70ef221216db2c9198110d83084f7b508da18fd34b8ee9f45d1204a627609d09a89c73e8bfc1f987c6bc906fac0d01720b169061b3d8015a5121a0d3beab454a03cc24a9bc5725e6c4aaf44d8f8b7242443f289c0751226448ec794a02a1ba411caeabb99f7a90510b8812a91a0ad69f1476408940381724a1dbfb7f69642788267b068c585bf41bf3f857fd14bcd9506b95b6a257d7481a07628004944e136a4c97842c13451e960cf4e08a8b6666e17a6aac016d701c1a00c82072939a092397c7104d7fd6332b860034f2ace5191e2792cf10e21f5166304bf329696128d63640b7882809b750f1f89e5d513fa08a8439e1ad5fe0affd887b0f06ab91798c35d48f39261af3dab7ddbc899be21d1f751b8d317b8e280f400b637ac6a471b4065973a6253235c94117e22083562b715ce680fafb78da9113f0f52692c52625ea8e1c24a1d8837beb963a5ab078455c8a43cbab68dc4eaa4c7646c9bb45803442250e935738944c7228aa3f7137567eb1231c63bff7552a7858525b92bdca832a41cb20fb24647a62af1da27e50c41f3cd070ed8c1d1213c22b1540a5c1412d67ab4ff334c2e5217c06a5f8a93c0637bd0fb4736c19591f67c378aa80f7c9587b346bbfe81eff8574c7e0acc3164c3df048019639a80377b97457175782d35b2666833aee098617626d88dbcc47091a011882d52105acc218c9287a95276a3259a6a94aa386d8148886abdcc1841f39260ce4754ebacc1fd36102905d4c623d0b27930b4c249ee7380758c0ac5982b0e932eda95184a40f55c451d835861ca2b314dbce97829f1b92752dda592d8960b2540f464988ea1c974c63467c439b1de540490b0af0491a6507951ebc971887bd2b4a11327381d99586f10668c83abe92fb649b113da7ec666729bc1cc38a1de137dd3cc4e3a6abb9881a2ee63e7df3ad6cb680664ba1559ca17448c968b7c867ac5f324911ffd43993b8a7b8f57094c786877c1208fa7f53e51d6f1a46ae71bc81f78ebe5808d48200b7e1bc81ec3d31070a6993aa5db237eb3a4c592aa559a73bd769583a0ad095ec1669b952be4a71fe8603f5d597f007a048cc9d7fea6735383b6b8bbf896b74dc48a21840a92c497a9bc7434b0241a9e42e6428515d477c4e0b3678fab1d619b794f01b828648e7577bb2e5297915b9fdf33cb291a37de51b51c7aca6f07994193bd981134da2340c23a93cda8b68e429ac801d3748b8d112b57e388511e3305e50a51184b623607447468be94351cd0b9111a119b4b3c6f270c1cfea749a2ac89455590280c369163946481dbaeb4693dbb376202db2d8464c61aea6411cd887080f5c59e1587da01510cd1b0e8b030a5c200639ba26376134e88279891b90373cc92e7a76c0aaa33d084ab3f61e175010996652e441300ad5aefda9cc88f17fef2102b643179e0a49a60c47ce06c5b1a0b150b09ca4593e5dd48a9b1979d103ba862c43ed354d2ec99575b70e741808288aa0e1cb792c0a458d4584ddfa1870d7b797e2aac7d4cc08916015401338d8841d226d9656661cda93f53343e0f906b82bce8f25428b02a639a47f7dda5b946a3785656fb6d083df5a5ec7493cc017a2469b1f43c96f2e3bbc9d6cb07bec82d721a4cfba6ca2c59b0e01bda98585692b9da753923f830b52c843b6d963f959ad60189f42d61df7808f4d131c4d233e246c4735193e516452061701e6114cf1587a54c79105f48fdce9c2134bb60550b242945ea011ec54c570054b93d96f072426b7c9b524db8d2f136b7db2d1f3889778f791d583227a702cdfa4a9f95014df019495f14e02318b3704dc3794af523705be75f29753f47b2888ceef235d82caca9f983b40bf10b29672da272113a973
cipher : bcee459c896ea378dcc458a532c35c029eff6b8cf8adc83f484fb6f9bfe32612f7c936cbf4dbd7c5262288dc3966a0d769f94a0bd57913a60a71efae09321c22c53839d836cef5fb8bf5c630bd3b3d657492eabfc7e67a42a631c95391656f0fce607a181e418144dff3d97f1192a2825a94da5113bcffc2e5f3e043f7583e6159902ddd009f8bcb18046a05695917bdef48accc2e3708f8536aabb420a7fd7989c60bca6c1941af45eac2f03cf71c8506721f8cd69bd3c573f036e3e8ae72b85632d06e0cab6fa1fea078d84aa1a116ac58ee632a0542b2d0e6a7026ae814ceeb46478d1cefd082c9b19efa7bb6ddd7abda8e43eab7b5a5204449273ea056b36d3797371f855d0c7ff0436279b21b831ad0970c26cc39f8627deb932689b8df48e73b1b5893987fa4dbc65571a78287f1573beeb85db52a3edbad6f50725bcbfa40423e3ce1ab00c16ea3922bc42e6782ce224ccfb3c978d8704584b9768a8edb6a950c0208b1c1c9a6a4e0d6300a9cfe788389697460efc41308448e9752d2022dfdecd118440346e2fabb07559b76301943f3b186adaaba09828efb28db1cd4a5e82e01f360451cb3c487f371af05725ea0e7d61932a8dc38108e99182e9b50d2aa828a773a2e18f5271ac75e5a5c50b9221f893e5f7076732beb0ffb9e4b82e1c0648192c9547870372b78c6a3e3a1b00d904a4a1492d5944e0510acee62e40c78cecef97922b04807cdd47d4d403a7bb16316598e6eee760b257382d9648c9920c3395717d8ac829bd37465c0f3e7f0c7e6fc351aac802edb722200776906eb36f622c0b8702958e44317961f583265a83b8cfcd9eed80f15b9ef848ebb7355df9718a60c532e20074854797685b3e4a25f929fce9ad02a5af114f92210abd3b73fddf28f116c2d4c27ceda6428a3892eb0c18fc12b07596e4153f2a3df9aa440957704bc56bbbee06cd99def3218c046344b4c5a811840a088bcbbad76fca4a20b9bf608873b2830afd6097b05022e8b1d42af3e5e4f00303adc9f130a84cdde3fef9335ccd1120b3f2050f17ef0c10fd226268965cbfc13738ada0632
shared secret : 508ac79bf97e90d75267159ba5189b73c48ab41a91aec0f32edd6cd1e66465b5
Caution
Before you consider using Psuedo Random Number Generator which comes with this library implementation, I strongly advice you to go through include/prng.hpp.
Note
Looking at API documentation, in header files, can give you good idea of how to use Kyber 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 fromstd::{array, vector}. I opt for using statically definedstd::spanbased function interfaces because we always know, at compile-time, how many bytes the seeds/ keys/ cipher-texts/ shared-secrets are, for various different Kyber KEM parameters. This gives much better type safety and compile-time error reporting.