Split ML-KEM benchmarks into their own files, based on security level

Signed-off-by: Anjan Roy <hello@itzmeanjan.in>
This commit is contained in:
Anjan Roy
2024-06-18 18:06:03 +04:00
parent bf6a22a872
commit 91a86d4b3f
5 changed files with 333 additions and 236 deletions

View File

@@ -1,209 +0,0 @@
#include "bench_helper.hpp"
#include "ml_kem/internals/ml_kem.hpp"
#include "x86_64_cpu_ticks.hpp"
#include <benchmark/benchmark.h>
// Benchmarking IND-CCA2-secure Ml_kem KEM key generation algorithm
template<size_t k, size_t eta1, size_t bit_security_level>
void
bench_keygen(benchmark::State& state)
{
constexpr size_t slen = 32;
constexpr size_t pklen = ml_kem_utils::get_kem_public_key_len(k);
constexpr size_t sklen = ml_kem_utils::get_kem_secret_key_len(k);
std::vector<uint8_t> d(slen);
std::vector<uint8_t> z(slen);
std::vector<uint8_t> pkey(pklen);
std::vector<uint8_t> skey(sklen);
auto _d = std::span<uint8_t, slen>(d);
auto _z = std::span<uint8_t, slen>(z);
auto _pkey = std::span<uint8_t, pklen>(pkey);
auto _skey = std::span<uint8_t, sklen>(skey);
ml_kem_prng::prng_t<bit_security_level> prng{};
prng.read(_d);
prng.read(_z);
#ifdef __x86_64__
uint64_t total_ticks = 0ul;
#endif
for (auto _ : state) {
#ifdef __x86_64__
const uint64_t start = cpu_ticks();
#endif
ml_kem::keygen<k, eta1>(_d, _z, _pkey, _skey);
benchmark::DoNotOptimize(_d);
benchmark::DoNotOptimize(_z);
benchmark::DoNotOptimize(_pkey);
benchmark::DoNotOptimize(_skey);
benchmark::ClobberMemory();
#ifdef __x86_64__
const uint64_t end = cpu_ticks();
total_ticks += (end - start);
#endif
}
state.SetItemsProcessed(state.iterations());
#ifdef __x86_64__
total_ticks /= static_cast<uint64_t>(state.iterations());
state.counters["rdtsc"] = static_cast<double>(total_ticks);
#endif
}
// Benchmarking IND-CCA2-secure Ml_kem KEM encapsulation algorithm
template<size_t k, size_t eta1, size_t eta2, size_t du, size_t dv, size_t bit_security_level>
void
bench_encapsulate(benchmark::State& state)
{
constexpr size_t slen = 32;
constexpr size_t pklen = ml_kem_utils::get_kem_public_key_len(k);
constexpr size_t sklen = ml_kem_utils::get_kem_secret_key_len(k);
constexpr size_t ctlen = ml_kem_utils::get_kem_cipher_text_len(k, du, dv);
constexpr size_t klen = 32;
std::vector<uint8_t> d(slen);
std::vector<uint8_t> z(slen);
std::vector<uint8_t> m(slen);
std::vector<uint8_t> pkey(pklen);
std::vector<uint8_t> skey(sklen);
std::vector<uint8_t> cipher(ctlen);
std::vector<uint8_t> sender_key(klen);
auto _d = std::span<uint8_t, slen>(d);
auto _z = std::span<uint8_t, slen>(z);
auto _m = std::span<uint8_t, slen>(m);
auto _pkey = std::span<uint8_t, pklen>(pkey);
auto _skey = std::span<uint8_t, sklen>(skey);
auto _cipher = std::span<uint8_t, ctlen>(cipher);
auto _sender_key = std::span<uint8_t, klen>(sender_key);
ml_kem_prng::prng_t<bit_security_level> prng{};
prng.read(_d);
prng.read(_z);
ml_kem::keygen<k, eta1>(_d, _z, _pkey, _skey);
prng.read(_m);
#ifdef __x86_64__
uint64_t total_ticks = 0ul;
#endif
for (auto _ : state) {
#ifdef __x86_64__
const uint64_t start = cpu_ticks();
#endif
(void)ml_kem::encapsulate<k, eta1, eta2, du, dv>(_m, _pkey, _cipher, _sender_key);
benchmark::DoNotOptimize(_m);
benchmark::DoNotOptimize(_pkey);
benchmark::DoNotOptimize(_cipher);
benchmark::DoNotOptimize(_sender_key);
benchmark::ClobberMemory();
#ifdef __x86_64__
const uint64_t end = cpu_ticks();
total_ticks += (end - start);
#endif
}
state.SetItemsProcessed(state.iterations());
#ifdef __x86_64__
total_ticks /= static_cast<uint64_t>(state.iterations());
state.counters["rdtsc"] = static_cast<double>(total_ticks);
#endif
}
// Benchmarking IND-CCA2-secure Ml_kem KEM decapsulation algorithm
template<size_t k, size_t eta1, size_t eta2, size_t du, size_t dv, size_t bit_security_level>
void
bench_decapsulate(benchmark::State& state)
{
constexpr size_t slen = 32;
constexpr size_t pklen = ml_kem_utils::get_kem_public_key_len(k);
constexpr size_t sklen = ml_kem_utils::get_kem_secret_key_len(k);
constexpr size_t ctlen = ml_kem_utils::get_kem_cipher_text_len(k, du, dv);
constexpr size_t klen = 32;
std::vector<uint8_t> d(slen);
std::vector<uint8_t> z(slen);
std::vector<uint8_t> m(slen);
std::vector<uint8_t> pkey(pklen);
std::vector<uint8_t> skey(sklen);
std::vector<uint8_t> cipher(ctlen);
std::vector<uint8_t> sender_key(klen);
std::vector<uint8_t> receiver_key(klen);
auto _d = std::span<uint8_t, slen>(d);
auto _z = std::span<uint8_t, slen>(z);
auto _m = std::span<uint8_t, slen>(m);
auto _pkey = std::span<uint8_t, pklen>(pkey);
auto _skey = std::span<uint8_t, sklen>(skey);
auto _cipher = std::span<uint8_t, ctlen>(cipher);
auto _sender_key = std::span<uint8_t, klen>(sender_key);
auto _receiver_key = std::span<uint8_t, klen>(receiver_key);
ml_kem_prng::prng_t<bit_security_level> prng{};
prng.read(_d);
prng.read(_z);
ml_kem::keygen<k, eta1>(_d, _z, _pkey, _skey);
prng.read(_m);
(void)ml_kem::encapsulate<k, eta1, eta2, du, dv>(_m, _pkey, _cipher, _sender_key);
#ifdef __x86_64__
uint64_t total_ticks = 0ul;
#endif
for (auto _ : state) {
#ifdef __x86_64__
const uint64_t start = cpu_ticks();
#endif
ml_kem::decapsulate<k, eta1, eta2, du, dv>(_skey, _cipher, _receiver_key);
benchmark::DoNotOptimize(_skey);
benchmark::DoNotOptimize(_cipher);
benchmark::DoNotOptimize(_receiver_key);
benchmark::ClobberMemory();
#ifdef __x86_64__
const uint64_t end = cpu_ticks();
total_ticks += (end - start);
#endif
}
state.SetItemsProcessed(state.iterations());
assert(std::ranges::equal(_sender_key, _receiver_key));
#ifdef __x86_64__
total_ticks /= static_cast<uint64_t>(state.iterations());
state.counters["rdtsc"] = static_cast<double>(total_ticks);
#endif
}
// Ml_kem512
BENCHMARK(bench_keygen<2, 3, 128>)->Name("ml_kem512/keygen")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_encapsulate<2, 3, 2, 10, 4, 128>)->Name("ml_kem512/encap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_decapsulate<2, 3, 2, 10, 4, 128>)->Name("ml_kem512/decap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
// Ml_kem768
BENCHMARK(bench_keygen<3, 2, 192>)->Name("ml_kem768/keygen")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_encapsulate<3, 2, 2, 10, 4, 192>)->Name("ml_kem768/encap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_decapsulate<3, 2, 2, 10, 4, 192>)->Name("ml_kem768/decap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
// Ml_kem1024
BENCHMARK(bench_keygen<4, 2, 256>)->Name("ml_kem1024/keygen")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_encapsulate<4, 2, 2, 11, 5, 256>)->Name("ml_kem1024/encap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_decapsulate<4, 2, 2, 11, 5, 256>)->Name("ml_kem1024/decap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);

View File

@@ -0,0 +1,111 @@
#include "bench_helper.hpp"
#include "ml_kem/ml_kem_1024.hpp"
#include <benchmark/benchmark.h>
#include <cassert>
// Benchmarking ML-KEM-1024 key generation algorithm.
void
bench_ml_kem_1024_keygen(benchmark::State& state)
{
std::array<uint8_t, ml_kem_1024::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_1024::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_1024::PKEY_BYTE_LEN> pubkey{};
std::array<uint8_t, ml_kem_1024::SKEY_BYTE_LEN> seckey{};
ml_kem_prng::prng_t<256> prng{};
prng.read(seed_d);
prng.read(seed_z);
for (auto _ : state) {
ml_kem_1024::keygen(seed_d, seed_z, pubkey, seckey);
benchmark::DoNotOptimize(seed_d);
benchmark::DoNotOptimize(seed_z);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(seckey);
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations());
}
// Benchmarking ML-KEM-1024 encapsulation algorithm.
void
bench_ml_kem_1024_encapsulate(benchmark::State& state)
{
std::array<uint8_t, ml_kem_1024::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_1024::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_1024::SEED_M_BYTE_LEN> seed_m{};
std::array<uint8_t, ml_kem_1024::PKEY_BYTE_LEN> pubkey{};
std::array<uint8_t, ml_kem_1024::SKEY_BYTE_LEN> seckey{};
std::array<uint8_t, ml_kem_1024::CIPHER_TEXT_BYTE_LEN> cipher{};
std::array<uint8_t, ml_kem_1024::SHARED_SECRET_BYTE_LEN> shared_secret{};
ml_kem_prng::prng_t<256> prng{};
prng.read(seed_d);
prng.read(seed_z);
prng.read(seed_m);
ml_kem_1024::keygen(seed_d, seed_z, pubkey, seckey);
bool is_encapsulated = true;
for (auto _ : state) {
is_encapsulated &= ml_kem_1024::encapsulate(seed_m, pubkey, cipher, shared_secret);
benchmark::DoNotOptimize(is_encapsulated);
benchmark::DoNotOptimize(seed_m);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(cipher);
benchmark::DoNotOptimize(shared_secret);
benchmark::ClobberMemory();
}
assert(is_encapsulated);
state.SetItemsProcessed(state.iterations());
}
// Benchmarking ML-KEM-1024 decapsulation algorithm.
void
bench_ml_kem_1024_decapsulate(benchmark::State& state)
{
std::array<uint8_t, ml_kem_1024::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_1024::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_1024::SEED_M_BYTE_LEN> seed_m{};
std::array<uint8_t, ml_kem_1024::PKEY_BYTE_LEN> pubkey{};
std::array<uint8_t, ml_kem_1024::SKEY_BYTE_LEN> seckey{};
std::array<uint8_t, ml_kem_1024::CIPHER_TEXT_BYTE_LEN> cipher{};
std::array<uint8_t, ml_kem_1024::SHARED_SECRET_BYTE_LEN> shared_secret_sender{};
std::array<uint8_t, ml_kem_1024::SHARED_SECRET_BYTE_LEN> shared_secret_receiver{};
ml_kem_prng::prng_t<256> prng{};
prng.read(seed_d);
prng.read(seed_z);
prng.read(seed_m);
ml_kem_1024::keygen(seed_d, seed_z, pubkey, seckey);
(void)ml_kem_1024::encapsulate(seed_m, pubkey, cipher, shared_secret_sender);
for (auto _ : state) {
ml_kem_1024::decapsulate(seckey, cipher, shared_secret_receiver);
benchmark::DoNotOptimize(seckey);
benchmark::DoNotOptimize(cipher);
benchmark::DoNotOptimize(shared_secret_receiver);
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations());
assert(shared_secret_sender == shared_secret_receiver);
}
BENCHMARK(bench_ml_kem_1024_keygen)->Name("ml_kem_1024/keygen")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_ml_kem_1024_encapsulate)->Name("ml_kem_1024/encap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_ml_kem_1024_decapsulate)->Name("ml_kem_1024/decap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);

View File

@@ -0,0 +1,111 @@
#include "bench_helper.hpp"
#include "ml_kem/ml_kem_512.hpp"
#include <benchmark/benchmark.h>
#include <cassert>
// Benchmarking ML-KEM-512 key generation algorithm.
void
bench_ml_kem_512_keygen(benchmark::State& state)
{
std::array<uint8_t, ml_kem_512::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_512::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_512::PKEY_BYTE_LEN> pubkey{};
std::array<uint8_t, ml_kem_512::SKEY_BYTE_LEN> seckey{};
ml_kem_prng::prng_t<128> prng{};
prng.read(seed_d);
prng.read(seed_z);
for (auto _ : state) {
ml_kem_512::keygen(seed_d, seed_z, pubkey, seckey);
benchmark::DoNotOptimize(seed_d);
benchmark::DoNotOptimize(seed_z);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(seckey);
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations());
}
// Benchmarking ML-KEM-512 encapsulation algorithm.
void
bench_ml_kem_512_encapsulate(benchmark::State& state)
{
std::array<uint8_t, ml_kem_512::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_512::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_512::SEED_M_BYTE_LEN> seed_m{};
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_prng::prng_t<128> prng{};
prng.read(seed_d);
prng.read(seed_z);
prng.read(seed_m);
ml_kem_512::keygen(seed_d, seed_z, pubkey, seckey);
bool is_encapsulated = true;
for (auto _ : state) {
is_encapsulated &= ml_kem_512::encapsulate(seed_m, pubkey, cipher, shared_secret);
benchmark::DoNotOptimize(is_encapsulated);
benchmark::DoNotOptimize(seed_m);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(cipher);
benchmark::DoNotOptimize(shared_secret);
benchmark::ClobberMemory();
}
assert(is_encapsulated);
state.SetItemsProcessed(state.iterations());
}
// Benchmarking ML-KEM-512 decapsulation algorithm.
void
bench_ml_kem_512_decapsulate(benchmark::State& state)
{
std::array<uint8_t, ml_kem_512::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_512::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_512::SEED_M_BYTE_LEN> seed_m{};
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_sender{};
std::array<uint8_t, ml_kem_512::SHARED_SECRET_BYTE_LEN> shared_secret_receiver{};
ml_kem_prng::prng_t<128> prng{};
prng.read(seed_d);
prng.read(seed_z);
prng.read(seed_m);
ml_kem_512::keygen(seed_d, seed_z, pubkey, seckey);
(void)ml_kem_512::encapsulate(seed_m, pubkey, cipher, shared_secret_sender);
for (auto _ : state) {
ml_kem_512::decapsulate(seckey, cipher, shared_secret_receiver);
benchmark::DoNotOptimize(seckey);
benchmark::DoNotOptimize(cipher);
benchmark::DoNotOptimize(shared_secret_receiver);
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations());
assert(shared_secret_sender == shared_secret_receiver);
}
BENCHMARK(bench_ml_kem_512_keygen)->Name("ml_kem_512/keygen")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_ml_kem_512_encapsulate)->Name("ml_kem_512/encap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_ml_kem_512_decapsulate)->Name("ml_kem_512/decap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);

View File

@@ -0,0 +1,111 @@
#include "bench_helper.hpp"
#include "ml_kem/ml_kem_768.hpp"
#include <benchmark/benchmark.h>
#include <cassert>
// Benchmarking ML-KEM-768 key generation algorithm.
void
bench_ml_kem_768_keygen(benchmark::State& state)
{
std::array<uint8_t, ml_kem_768::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_768::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_768::PKEY_BYTE_LEN> pubkey{};
std::array<uint8_t, ml_kem_768::SKEY_BYTE_LEN> seckey{};
ml_kem_prng::prng_t<192> prng{};
prng.read(seed_d);
prng.read(seed_z);
for (auto _ : state) {
ml_kem_768::keygen(seed_d, seed_z, pubkey, seckey);
benchmark::DoNotOptimize(seed_d);
benchmark::DoNotOptimize(seed_z);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(seckey);
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations());
}
// Benchmarking ML-KEM-768 encapsulation algorithm.
void
bench_ml_kem_768_encapsulate(benchmark::State& state)
{
std::array<uint8_t, ml_kem_768::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_768::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_768::SEED_M_BYTE_LEN> seed_m{};
std::array<uint8_t, ml_kem_768::PKEY_BYTE_LEN> pubkey{};
std::array<uint8_t, ml_kem_768::SKEY_BYTE_LEN> seckey{};
std::array<uint8_t, ml_kem_768::CIPHER_TEXT_BYTE_LEN> cipher{};
std::array<uint8_t, ml_kem_768::SHARED_SECRET_BYTE_LEN> shared_secret{};
ml_kem_prng::prng_t<192> prng{};
prng.read(seed_d);
prng.read(seed_z);
prng.read(seed_m);
ml_kem_768::keygen(seed_d, seed_z, pubkey, seckey);
bool is_encapsulated = true;
for (auto _ : state) {
is_encapsulated &= ml_kem_768::encapsulate(seed_m, pubkey, cipher, shared_secret);
benchmark::DoNotOptimize(is_encapsulated);
benchmark::DoNotOptimize(seed_m);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(cipher);
benchmark::DoNotOptimize(shared_secret);
benchmark::ClobberMemory();
}
assert(is_encapsulated);
state.SetItemsProcessed(state.iterations());
}
// Benchmarking ML-KEM-768 decapsulation algorithm.
void
bench_ml_kem_768_decapsulate(benchmark::State& state)
{
std::array<uint8_t, ml_kem_768::SEED_D_BYTE_LEN> seed_d{};
std::array<uint8_t, ml_kem_768::SEED_Z_BYTE_LEN> seed_z{};
std::array<uint8_t, ml_kem_768::SEED_M_BYTE_LEN> seed_m{};
std::array<uint8_t, ml_kem_768::PKEY_BYTE_LEN> pubkey{};
std::array<uint8_t, ml_kem_768::SKEY_BYTE_LEN> seckey{};
std::array<uint8_t, ml_kem_768::CIPHER_TEXT_BYTE_LEN> cipher{};
std::array<uint8_t, ml_kem_768::SHARED_SECRET_BYTE_LEN> shared_secret_sender{};
std::array<uint8_t, ml_kem_768::SHARED_SECRET_BYTE_LEN> shared_secret_receiver{};
ml_kem_prng::prng_t<192> prng{};
prng.read(seed_d);
prng.read(seed_z);
prng.read(seed_m);
ml_kem_768::keygen(seed_d, seed_z, pubkey, seckey);
(void)ml_kem_768::encapsulate(seed_m, pubkey, cipher, shared_secret_sender);
for (auto _ : state) {
ml_kem_768::decapsulate(seckey, cipher, shared_secret_receiver);
benchmark::DoNotOptimize(seckey);
benchmark::DoNotOptimize(cipher);
benchmark::DoNotOptimize(shared_secret_receiver);
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations());
assert(shared_secret_sender == shared_secret_receiver);
}
BENCHMARK(bench_ml_kem_768_keygen)->Name("ml_kem_768/keygen")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_ml_kem_768_encapsulate)->Name("ml_kem_768/encap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(bench_ml_kem_768_decapsulate)->Name("ml_kem_768/decap")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);

View File

@@ -1,27 +0,0 @@
#pragma once
#ifdef __x86_64__
#include <cstdint>
#include <emmintrin.h>
#include <x86intrin.h>
// Issues x86_64 architecture specific intrinsic for obtaining CPU ticks passed by, while executing a set of instructions. For example
//
// start = cpu_ticks()
// {
// ... bunch
// ... of
// ... instructions
// }
// end = cpu_ticks()
//
// CPU ticks passed by executing above code block = end - start
static inline uint64_t
cpu_ticks()
{
_mm_mfence();
return __rdtsc();
}
#endif