From 8f1412366026efca8438edc96c01deec7fff8fbf Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 10:34:27 +0000 Subject: [PATCH 01/13] feat(signature): add unified adapter API and registry - Add public batch_bench API with algorithm metadata - Add adapter function contracts for three PQC schemes - Add algorithm registry and lookup by stable names --- src/batch_bench.h | 61 ++++++++++++++++++++++++++++ src/signature/adapters.h | 87 ++++++++++++++++++++++++++++++++++++++++ src/signature/registry.c | 65 ++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 src/batch_bench.h create mode 100644 src/signature/adapters.h create mode 100644 src/signature/registry.c diff --git a/src/batch_bench.h b/src/batch_bench.h new file mode 100644 index 0000000..7ef4e71 --- /dev/null +++ b/src/batch_bench.h @@ -0,0 +1,61 @@ +#ifndef BATCH_BENCH_H +#define BATCH_BENCH_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum bb_status { + BB_OK = 0, + BB_INVALID_SIG = 1, + BB_BAD_ARG = 2, + BB_BUFFER_TOO_SMALL = 3, + BB_INTERNAL = 4, +} bb_status; + +typedef struct bb_algorithm { + const char *name; + size_t public_key_bytes; + size_t secret_key_bytes; + size_t signature_bytes; + + bb_status (*keygen)( + uint8_t *sk, + size_t sk_capacity, + size_t *sk_len, + uint8_t *pk, + size_t pk_capacity, + size_t *pk_len); + + bb_status (*sign)( + const uint8_t *sk, + size_t sk_len, + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + uint8_t *sig, + size_t sig_capacity, + size_t *sig_len); + + bb_status (*verify)( + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + const uint8_t *sig, + size_t sig_len); +} bb_algorithm; + +size_t bb_algorithm_count(void); +const bb_algorithm *bb_algorithm_at(size_t index); +const bb_algorithm *bb_find_algorithm(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/signature/adapters.h b/src/signature/adapters.h new file mode 100644 index 0000000..a119fb4 --- /dev/null +++ b/src/signature/adapters.h @@ -0,0 +1,87 @@ +#ifndef BATCH_BENCH_ADAPTERS_H +#define BATCH_BENCH_ADAPTERS_H + +#include "../batch_bench.h" + +bb_status bb_hypericum_keygen( + uint8_t *sk, + size_t sk_capacity, + size_t *sk_len, + uint8_t *pk, + size_t pk_capacity, + size_t *pk_len); + +bb_status bb_hypericum_sign( + const uint8_t *sk, + size_t sk_len, + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + uint8_t *sig, + size_t sig_capacity, + size_t *sig_len); + +bb_status bb_hypericum_verify( + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + const uint8_t *sig, + size_t sig_len); + +bb_status bb_shipovnik_keygen( + uint8_t *sk, + size_t sk_capacity, + size_t *sk_len, + uint8_t *pk, + size_t pk_capacity, + size_t *pk_len); + +bb_status bb_shipovnik_sign( + const uint8_t *sk, + size_t sk_len, + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + uint8_t *sig, + size_t sig_capacity, + size_t *sig_len); + +bb_status bb_shipovnik_verify( + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + const uint8_t *sig, + size_t sig_len); + +bb_status bb_kryzhovnik_keygen( + uint8_t *sk, + size_t sk_capacity, + size_t *sk_len, + uint8_t *pk, + size_t pk_capacity, + size_t *pk_len); + +bb_status bb_kryzhovnik_sign( + const uint8_t *sk, + size_t sk_len, + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + uint8_t *sig, + size_t sig_capacity, + size_t *sig_len); + +bb_status bb_kryzhovnik_verify( + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + const uint8_t *sig, + size_t sig_len); + +#endif diff --git a/src/signature/registry.c b/src/signature/registry.c new file mode 100644 index 0000000..0d35500 --- /dev/null +++ b/src/signature/registry.c @@ -0,0 +1,65 @@ +#include "../batch_bench.h" +#include "adapters.h" + +#include "api.h" +#include "shipovnik.h" +#include "kryzhovnik_wrapper.h" + +#include + +static const bb_algorithm k_algorithms[] = { + { + .name = "hypericum_m_128_20", + .public_key_bytes = CRYPTO_PUBLICKEYBYTES, + .secret_key_bytes = CRYPTO_SECRETKEYBYTES, + .signature_bytes = CRYPTO_BYTES, + .keygen = bb_hypericum_keygen, + .sign = bb_hypericum_sign, + .verify = bb_hypericum_verify, + }, + { + .name = "shipovnik", + .public_key_bytes = SHIPOVNIK_PUBLICKEYBYTES, + .secret_key_bytes = SHIPOVNIK_SECRETKEYBYTES, + .signature_bytes = SHIPOVNIK_SIGBYTES, + .keygen = bb_shipovnik_keygen, + .sign = bb_shipovnik_sign, + .verify = bb_shipovnik_verify, + }, + { + .name = "kryzhovnik", + .public_key_bytes = KRYZHOVNIK_PUBLIC_KEY_BYTES, + .secret_key_bytes = KRYZHOVNIK_SECRET_KEY_BYTES, + .signature_bytes = KRYZHOVNIK_SIGNATURE_BYTES, + .keygen = bb_kryzhovnik_keygen, + .sign = bb_kryzhovnik_sign, + .verify = bb_kryzhovnik_verify, + }, +}; + +size_t bb_algorithm_count(void) { + return sizeof(k_algorithms) / sizeof(k_algorithms[0]); +} + +const bb_algorithm *bb_algorithm_at(size_t index) { + if (index >= bb_algorithm_count()) { + return NULL; + } + return &k_algorithms[index]; +} + +const bb_algorithm *bb_find_algorithm(const char *name) { + size_t i; + + if (name == NULL) { + return NULL; + } + + for (i = 0; i < bb_algorithm_count(); ++i) { + if (strcmp(k_algorithms[i].name, name) == 0) { + return &k_algorithms[i]; + } + } + + return NULL; +} From 9030d8762e169aa9c177ba208aa250a4b5998464 Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 10:37:02 +0000 Subject: [PATCH 02/13] feat(signature): implement algorithm adapter backends - Add Hypericum adapter with sig||msg pack and unpack logic - Add Shipovnik adapter for keygen, sign, and verify - Add Kryzhovnik adapter over wrapper byte API --- src/signature/hypericum_adapter.c | 133 +++++++++++++++++++++++++++++ src/signature/kryzhovnik_adapter.c | 69 +++++++++++++++ src/signature/shipovnik_adapter.c | 74 ++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 src/signature/hypericum_adapter.c create mode 100644 src/signature/kryzhovnik_adapter.c create mode 100644 src/signature/shipovnik_adapter.c diff --git a/src/signature/hypericum_adapter.c b/src/signature/hypericum_adapter.c new file mode 100644 index 0000000..ff114cf --- /dev/null +++ b/src/signature/hypericum_adapter.c @@ -0,0 +1,133 @@ +#include "adapters.h" + +#include "api.h" + +#include +#include + +bb_status bb_hypericum_keygen( + uint8_t *sk, + size_t sk_capacity, + size_t *sk_len, + uint8_t *pk, + size_t pk_capacity, + size_t *pk_len) { + if (sk == NULL || pk == NULL || sk_len == NULL || pk_len == NULL) { + return BB_BAD_ARG; + } + if (sk_capacity < (size_t)CRYPTO_SECRETKEYBYTES || + pk_capacity < (size_t)CRYPTO_PUBLICKEYBYTES) { + return BB_BUFFER_TOO_SMALL; + } + + if (crypto_sign_keypair(pk, sk) != 0) { + return BB_INTERNAL; + } + + *sk_len = (size_t)CRYPTO_SECRETKEYBYTES; + *pk_len = (size_t)CRYPTO_PUBLICKEYBYTES; + return BB_OK; +} + +bb_status bb_hypericum_sign( + const uint8_t *sk, + size_t sk_len, + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + uint8_t *sig, + size_t sig_capacity, + size_t *sig_len) { + unsigned char *sm = NULL; + unsigned long long smlen = 0; + size_t required; + + (void)pk; + (void)pk_len; + + if (sk == NULL || msg == NULL || sig == NULL || sig_len == NULL) { + return BB_BAD_ARG; + } + if (sk_len != (size_t)CRYPTO_SECRETKEYBYTES) { + return BB_BAD_ARG; + } + + required = (size_t)CRYPTO_BYTES; + if (sig_capacity < required) { + return BB_BUFFER_TOO_SMALL; + } + + sm = (unsigned char *)malloc(required + msg_len); + if (sm == NULL) { + return BB_INTERNAL; + } + + if (crypto_sign(sm, &smlen, msg, (unsigned long long)msg_len, sk) != 0) { + free(sm); + return BB_INTERNAL; + } + + if (smlen < (unsigned long long)CRYPTO_BYTES) { + free(sm); + return BB_INTERNAL; + } + + memcpy(sig, sm, required); + *sig_len = required; + + free(sm); + return BB_OK; +} + +bb_status bb_hypericum_verify( + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + const uint8_t *sig, + size_t sig_len) { + unsigned char *sm = NULL; + unsigned char *recovered = NULL; + unsigned long long recovered_len = 0; + bb_status status = BB_INVALID_SIG; + + if (pk == NULL || msg == NULL || sig == NULL) { + return BB_BAD_ARG; + } + if (pk_len != (size_t)CRYPTO_PUBLICKEYBYTES) { + return BB_BAD_ARG; + } + if (sig_len != (size_t)CRYPTO_BYTES) { + return BB_INVALID_SIG; + } + + sm = (unsigned char *)malloc(sig_len + msg_len); + recovered = (unsigned char *)malloc(msg_len); + if (sm == NULL || recovered == NULL) { + status = BB_INTERNAL; + goto cleanup; + } + + memcpy(sm, sig, sig_len); + memcpy(sm + sig_len, msg, msg_len); + + if (crypto_sign_open(recovered, &recovered_len, sm, + (unsigned long long)(sig_len + msg_len), pk) != 0) { + status = BB_INVALID_SIG; + goto cleanup; + } + + if (recovered_len != (unsigned long long)msg_len || + memcmp(recovered, msg, msg_len) != 0) { + status = BB_INVALID_SIG; + goto cleanup; + } + + status = BB_OK; + +cleanup: + free(sm); + free(recovered); + return status; +} diff --git a/src/signature/kryzhovnik_adapter.c b/src/signature/kryzhovnik_adapter.c new file mode 100644 index 0000000..10ce205 --- /dev/null +++ b/src/signature/kryzhovnik_adapter.c @@ -0,0 +1,69 @@ +#include "adapters.h" + +#include "kryzhovnik_wrapper.h" + +bb_status bb_kryzhovnik_keygen( + uint8_t *sk, + size_t sk_capacity, + size_t *sk_len, + uint8_t *pk, + size_t pk_capacity, + size_t *pk_len) { + if (sk == NULL || pk == NULL || sk_len == NULL || pk_len == NULL) { + return BB_BAD_ARG; + } + if (sk_capacity < (size_t)KRYZHOVNIK_SECRET_KEY_BYTES || + pk_capacity < (size_t)KRYZHOVNIK_PUBLIC_KEY_BYTES) { + return BB_BUFFER_TOO_SMALL; + } + + kryzhovnik_generate_keys(sk, pk); + *sk_len = (size_t)KRYZHOVNIK_SECRET_KEY_BYTES; + *pk_len = (size_t)KRYZHOVNIK_PUBLIC_KEY_BYTES; + return BB_OK; +} + +bb_status bb_kryzhovnik_sign( + const uint8_t *sk, + size_t sk_len, + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + uint8_t *sig, + size_t sig_capacity, + size_t *sig_len) { + if (sk == NULL || pk == NULL || msg == NULL || sig == NULL || sig_len == NULL) { + return BB_BAD_ARG; + } + if (sk_len != (size_t)KRYZHOVNIK_SECRET_KEY_BYTES || + pk_len != (size_t)KRYZHOVNIK_PUBLIC_KEY_BYTES) { + return BB_BAD_ARG; + } + if (sig_capacity < (size_t)KRYZHOVNIK_SIGNATURE_BYTES) { + return BB_BUFFER_TOO_SMALL; + } + + kryzhovnik_sign(sk, pk, msg, msg_len, sig, sig_len); + return BB_OK; +} + +bb_status bb_kryzhovnik_verify( + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + const uint8_t *sig, + size_t sig_len) { + if (pk == NULL || msg == NULL || sig == NULL) { + return BB_BAD_ARG; + } + if (pk_len != (size_t)KRYZHOVNIK_PUBLIC_KEY_BYTES) { + return BB_BAD_ARG; + } + if (sig_len != (size_t)KRYZHOVNIK_SIGNATURE_BYTES) { + return BB_INVALID_SIG; + } + + return kryzhovnik_verify(pk, sig, msg, msg_len) == 0 ? BB_OK : BB_INVALID_SIG; +} diff --git a/src/signature/shipovnik_adapter.c b/src/signature/shipovnik_adapter.c new file mode 100644 index 0000000..2435984 --- /dev/null +++ b/src/signature/shipovnik_adapter.c @@ -0,0 +1,74 @@ +#include "adapters.h" + +#include "shipovnik.h" + +bb_status bb_shipovnik_keygen( + uint8_t *sk, + size_t sk_capacity, + size_t *sk_len, + uint8_t *pk, + size_t pk_capacity, + size_t *pk_len) { + if (sk == NULL || pk == NULL || sk_len == NULL || pk_len == NULL) { + return BB_BAD_ARG; + } + if (sk_capacity < (size_t)SHIPOVNIK_SECRETKEYBYTES || + pk_capacity < (size_t)SHIPOVNIK_PUBLICKEYBYTES) { + return BB_BUFFER_TOO_SMALL; + } + + shipovnik_generate_keys(sk, pk); + *sk_len = (size_t)SHIPOVNIK_SECRETKEYBYTES; + *pk_len = (size_t)SHIPOVNIK_PUBLICKEYBYTES; + return BB_OK; +} + +bb_status bb_shipovnik_sign( + const uint8_t *sk, + size_t sk_len, + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + uint8_t *sig, + size_t sig_capacity, + size_t *sig_len) { + size_t out_sig_len = 0; + + (void)pk; + (void)pk_len; + + if (sk == NULL || msg == NULL || sig == NULL || sig_len == NULL) { + return BB_BAD_ARG; + } + if (sk_len != (size_t)SHIPOVNIK_SECRETKEYBYTES) { + return BB_BAD_ARG; + } + if (sig_capacity < (size_t)SHIPOVNIK_SIGBYTES) { + return BB_BUFFER_TOO_SMALL; + } + + shipovnik_sign(sk, msg, msg_len, sig, &out_sig_len); + *sig_len = out_sig_len; + return BB_OK; +} + +bb_status bb_shipovnik_verify( + const uint8_t *pk, + size_t pk_len, + const uint8_t *msg, + size_t msg_len, + const uint8_t *sig, + size_t sig_len) { + if (pk == NULL || msg == NULL || sig == NULL) { + return BB_BAD_ARG; + } + if (pk_len != (size_t)SHIPOVNIK_PUBLICKEYBYTES) { + return BB_BAD_ARG; + } + if (sig_len > (size_t)SHIPOVNIK_SIGBYTES) { + return BB_INVALID_SIG; + } + + return shipovnik_verify(pk, sig, msg, msg_len) == 0 ? BB_OK : BB_INVALID_SIG; +} From 015006ea3f330bb19b305f9517eced3b9f355230 Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 10:53:57 +0000 Subject: [PATCH 03/13] refactor(kryzhovnik): simplify wrapper API names --- src/signature/kryzhovnik_adapter.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/signature/kryzhovnik_adapter.c b/src/signature/kryzhovnik_adapter.c index 10ce205..8c9e7a4 100644 --- a/src/signature/kryzhovnik_adapter.c +++ b/src/signature/kryzhovnik_adapter.c @@ -17,7 +17,9 @@ bb_status bb_kryzhovnik_keygen( return BB_BUFFER_TOO_SMALL; } - kryzhovnik_generate_keys(sk, pk); + if (kryzhovnik_keygen(sk, sk_capacity, pk, pk_capacity) != KRYZHOVNIK_OK) { + return BB_INTERNAL; + } *sk_len = (size_t)KRYZHOVNIK_SECRET_KEY_BYTES; *pk_len = (size_t)KRYZHOVNIK_PUBLIC_KEY_BYTES; return BB_OK; @@ -44,7 +46,10 @@ bb_status bb_kryzhovnik_sign( return BB_BUFFER_TOO_SMALL; } - kryzhovnik_sign(sk, pk, msg, msg_len, sig, sig_len); + if (kryzhovnik_sign(sk, sk_len, pk, pk_len, msg, msg_len, + sig, sig_capacity, sig_len) != KRYZHOVNIK_OK) { + return BB_INTERNAL; + } return BB_OK; } @@ -65,5 +70,12 @@ bb_status bb_kryzhovnik_verify( return BB_INVALID_SIG; } - return kryzhovnik_verify(pk, sig, msg, msg_len) == 0 ? BB_OK : BB_INVALID_SIG; + switch (kryzhovnik_verify(pk, pk_len, sig, sig_len, msg, msg_len)) { + case KRYZHOVNIK_OK: + return BB_OK; + case KRYZHOVNIK_VERIFY_FAIL: + return BB_INVALID_SIG; + default: + return BB_BAD_ARG; + } } From 7436fc82698b48e138772babfd37aef2650bfc4b Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 11:10:05 +0000 Subject: [PATCH 04/13] feat(utils): add timer and message generator - Add monotonic high-resolution timer utilities - Add deterministic 1KB-friendly message generator - Add src CMake target entries for adapter library --- src/CMakeLists.txt | 24 ++++++++++++++++++++++++ src/utils/message_gen.c | 32 ++++++++++++++++++++++++++++++++ src/utils/message_gen.h | 11 +++++++++++ src/utils/timer.c | 35 +++++++++++++++++++++++++++++++++++ src/utils/timer.h | 17 +++++++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 src/CMakeLists.txt create mode 100644 src/utils/message_gen.c create mode 100644 src/utils/message_gen.h create mode 100644 src/utils/timer.c create mode 100644 src/utils/timer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..e42da2f --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,24 @@ +add_library(batch_bench_adapters STATIC + signature/registry.c + signature/hypericum_adapter.c + signature/shipovnik_adapter.c + signature/kryzhovnik_adapter.c + utils/timer.c + utils/message_gen.c +) + +target_include_directories(batch_bench_adapters + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE + ${PROJECT_SOURCE_DIR}/third_party/kryzhovnik +) + +target_link_libraries(batch_bench_adapters + PUBLIC + hypericum + shipovnik + kryzhovnik +) + +target_compile_features(batch_bench_adapters PRIVATE c_std_99) diff --git a/src/utils/message_gen.c b/src/utils/message_gen.c new file mode 100644 index 0000000..0079558 --- /dev/null +++ b/src/utils/message_gen.c @@ -0,0 +1,32 @@ +#include "message_gen.h" + +// Deterministic pseudo-random message generator for reproducible benchmarks. +// bb_mix64: SplitMix64 finalizer – mixes bits for good avalanche effect. +// bb_fill_message: fills 'out' with bytes derived from seed and message index. +// Same (seed, index, out_len) always yields identical output, no external storage. + +static uint64_t bb_mix64(uint64_t x) { + // SplitMix64 finalizer constants for deterministic diffusion. + x ^= x >> 30; + x *= 0xbf58476d1ce4e5b9ULL; + x ^= x >> 27; + x *= 0x94d049bb133111ebULL; + x ^= x >> 31; + return x; +} + +void bb_fill_message(uint8_t *out, size_t out_len, uint64_t seed, uint64_t index) { + uint64_t state; + size_t i; + + if (out == NULL) { + return; + } + + // 0x9e... is the golden-ratio increment used to decorrelate indices. + state = bb_mix64(seed ^ (index + 0x9e3779b97f4a7c15ULL)); + for (i = 0; i < out_len; ++i) { + state = bb_mix64(state + i + 1U); + out[i] = (uint8_t)(state & 0xFFU); + } +} diff --git a/src/utils/message_gen.h b/src/utils/message_gen.h new file mode 100644 index 0000000..1262885 --- /dev/null +++ b/src/utils/message_gen.h @@ -0,0 +1,11 @@ +#ifndef BATCH_BENCH_MESSAGE_GEN_H +#define BATCH_BENCH_MESSAGE_GEN_H + +#include +#include + +#define BB_DEFAULT_MESSAGE_SIZE 1024 + +void bb_fill_message(uint8_t *out, size_t out_len, uint64_t seed, uint64_t index); + +#endif diff --git a/src/utils/timer.c b/src/utils/timer.c new file mode 100644 index 0000000..d79aa95 --- /dev/null +++ b/src/utils/timer.c @@ -0,0 +1,35 @@ +#include "timer.h" + +#include + +uint64_t bb_now_monotonic_ns(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ((uint64_t)ts.tv_sec * 1000000000ULL) + (uint64_t)ts.tv_nsec; +} + +void bb_timer_start(bb_timer *timer) { + if (timer == NULL) { + return; + } + timer->start_ns = bb_now_monotonic_ns(); + timer->end_ns = timer->start_ns; +} + +void bb_timer_stop(bb_timer *timer) { + if (timer == NULL) { + return; + } + timer->end_ns = bb_now_monotonic_ns(); +} + +uint64_t bb_timer_elapsed_ns(const bb_timer *timer) { + if (timer == NULL || timer->end_ns < timer->start_ns) { + return 0; + } + return timer->end_ns - timer->start_ns; +} + +double bb_timer_elapsed_ms(const bb_timer *timer) { + return (double)bb_timer_elapsed_ns(timer) / 1000000.0; +} diff --git a/src/utils/timer.h b/src/utils/timer.h new file mode 100644 index 0000000..37c8477 --- /dev/null +++ b/src/utils/timer.h @@ -0,0 +1,17 @@ +#ifndef BATCH_BENCH_TIMER_H +#define BATCH_BENCH_TIMER_H + +#include + +typedef struct bb_timer { + uint64_t start_ns; + uint64_t end_ns; +} bb_timer; + +uint64_t bb_now_monotonic_ns(void); +void bb_timer_start(bb_timer *timer); +void bb_timer_stop(bb_timer *timer); +uint64_t bb_timer_elapsed_ns(const bb_timer *timer); +double bb_timer_elapsed_ms(const bb_timer *timer); + +#endif From c5b8a56869a02d1d0b1ce0015bf6d8fac4634722 Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 11:32:12 +0000 Subject: [PATCH 05/13] chore(submodule): update kryzhovnik wrapper revision - Bump third_party/kryzhovnik to strict wrapper API commit - Align superproject pointer with latest fork changes --- third_party/kryzhovnik | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/kryzhovnik b/third_party/kryzhovnik index 710500a..7ac29e4 160000 --- a/third_party/kryzhovnik +++ b/third_party/kryzhovnik @@ -1 +1 @@ -Subproject commit 710500a98a4cbd01c8572a0d8bc57498f390f776 +Subproject commit 7ac29e4cb2cadce3d4ae8f329baaedf3e94fd10f From 75e120a6f68bda4ec221e3471f1aaac570c27660 Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 11:35:44 +0000 Subject: [PATCH 06/13] build(cmake): wire src bench and tests targets - Add src and bench subdirectories to top-level build - Register tests subdirectory under BUILD_TESTING guard - Add initial CMake targets for benchmark and smoke tests --- CMakeLists.txt | 8 +++++++- bench/CMakeLists.txt | 5 +++++ tests/CMakeLists.txt | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 bench/CMakeLists.txt create mode 100644 tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index de9201e..00d9bf8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,4 +7,10 @@ if(BUILD_TESTING) include(CTest) endif() -add_subdirectory(third_party) \ No newline at end of file +add_subdirectory(third_party) +add_subdirectory(src) +add_subdirectory(bench) + +if(BUILD_TESTING) + add_subdirectory(tests) +endif() \ No newline at end of file diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt new file mode 100644 index 0000000..51d7000 --- /dev/null +++ b/bench/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(bench_seq bench_seq.c) + +target_link_libraries(bench_seq PRIVATE batch_bench_adapters) + +target_compile_features(bench_seq PRIVATE c_std_99) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..7a4bde4 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(adapters_smoke adapters_smoke.c) + +target_link_libraries(adapters_smoke PRIVATE batch_bench_adapters) + +target_compile_features(adapters_smoke PRIVATE c_std_99) + +add_test(NAME adapters_smoke COMMAND adapters_smoke) From 0261bde2f4748e27b32e8d19e3d06968e23c3a9a Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 11:37:28 +0000 Subject: [PATCH 07/13] feat(bench): add baseline sequential benchmark runner - Add CLI benchmark tool for unified adapter API - Export CSV and JSON with stage-ready metric columns - Measure keygen, sequential sign, verify, and serialization --- bench/bench_seq.c | 438 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 bench/bench_seq.c diff --git a/bench/bench_seq.c b/bench/bench_seq.c new file mode 100644 index 0000000..9f8cfbd --- /dev/null +++ b/bench/bench_seq.c @@ -0,0 +1,438 @@ +#include "batch_bench.h" +#include "utils/message_gen.h" +#include "utils/timer.h" + +#include +#include +#include +#include +#include + +typedef struct bench_config { + const char *algo; + size_t batch_size; + size_t iters; + size_t msg_size; + uint64_t seed; + int verify; + const char *out_csv; + const char *out_json; +} bench_config; + +typedef struct bench_result { + const char *algorithm; + size_t batch_size; + double keygen_ms; + double time_seq_ms; + double verify_ms; + double serialize_time_ms; + double peak_memory_mb; + double total_sig_size_mb; +} bench_result; + +static void print_usage(const char *prog) { + printf("Usage: %s [options]\n", prog); + printf(" --algo Algorithm name or all (default: all)\n"); + printf(" --batch-size Messages per iteration (default: 1)\n"); + printf(" --iters Iterations (default: 100)\n"); + printf(" --msg-size Message size in bytes (default: 1024)\n"); + printf(" --seed Deterministic message seed (default: 1)\n"); + printf(" --verify <0|1> Run verify pass (default: 1)\n"); + printf(" --out-csv Output CSV path\n"); + printf(" --out-json Output JSON path\n"); + printf(" --help Show this message\n"); +} + +static int parse_size(const char *s, size_t *out) { + char *end = NULL; + unsigned long long v; + + if (s == NULL || out == NULL) { + return -1; + } + errno = 0; + v = strtoull(s, &end, 10); + if (errno != 0 || end == s || *end != '\0') { + return -1; + } + *out = (size_t)v; + return 0; +} + +static int parse_u64(const char *s, uint64_t *out) { + char *end = NULL; + unsigned long long v; + + if (s == NULL || out == NULL) { + return -1; + } + errno = 0; + v = strtoull(s, &end, 10); + if (errno != 0 || end == s || *end != '\0') { + return -1; + } + *out = (uint64_t)v; + return 0; +} + +static int parse_int(const char *s, int *out) { + char *end = NULL; + long v; + + if (s == NULL || out == NULL) { + return -1; + } + errno = 0; + v = strtol(s, &end, 10); + if (errno != 0 || end == s || *end != '\0') { + return -1; + } + *out = (int)v; + return 0; +} + +static int parse_args(int argc, char **argv, bench_config *cfg) { + int i; + + cfg->algo = "all"; + cfg->batch_size = 1; + cfg->iters = 100; + cfg->msg_size = BB_DEFAULT_MESSAGE_SIZE; + cfg->seed = 1; + cfg->verify = 1; + cfg->out_csv = NULL; + cfg->out_json = NULL; + + for (i = 1; i < argc; ++i) { + const char *arg = argv[i]; + if (strcmp(arg, "--help") == 0) { + print_usage(argv[0]); + return 1; + } + if (strcmp(arg, "--algo") == 0 && i + 1 < argc) { + cfg->algo = argv[++i]; + continue; + } + if (strcmp(arg, "--batch-size") == 0 && i + 1 < argc) { + if (parse_size(argv[++i], &cfg->batch_size) != 0 || cfg->batch_size == 0) { + return -1; + } + continue; + } + if (strcmp(arg, "--iters") == 0 && i + 1 < argc) { + if (parse_size(argv[++i], &cfg->iters) != 0 || cfg->iters == 0) { + return -1; + } + continue; + } + if (strcmp(arg, "--msg-size") == 0 && i + 1 < argc) { + if (parse_size(argv[++i], &cfg->msg_size) != 0 || cfg->msg_size == 0) { + return -1; + } + continue; + } + if (strcmp(arg, "--seed") == 0 && i + 1 < argc) { + if (parse_u64(argv[++i], &cfg->seed) != 0) { + return -1; + } + continue; + } + if (strcmp(arg, "--verify") == 0 && i + 1 < argc) { + if (parse_int(argv[++i], &cfg->verify) != 0 || (cfg->verify != 0 && cfg->verify != 1)) { + return -1; + } + continue; + } + if (strcmp(arg, "--out-csv") == 0 && i + 1 < argc) { + cfg->out_csv = argv[++i]; + continue; + } + if (strcmp(arg, "--out-json") == 0 && i + 1 < argc) { + cfg->out_json = argv[++i]; + continue; + } + return -1; + } + + return 0; +} + +static double get_peak_memory_mb(void) { + struct rusage ru; + if (getrusage(RUSAGE_SELF, &ru) != 0) { + return 0.0; + } + return (double)ru.ru_maxrss / 1024.0; +} + +static int run_one(const bb_algorithm *algo, const bench_config *cfg, bench_result *out) { + uint8_t *sk = NULL; + uint8_t *pk = NULL; + uint8_t *msg = NULL; + uint8_t *sigs = NULL; + size_t *sig_lens = NULL; + uint8_t *serialized = NULL; + size_t sk_len = 0; + size_t pk_len = 0; + size_t total_sig_bytes = 0; + size_t it; + bb_timer timer; + + sk = (uint8_t *)malloc(algo->secret_key_bytes); + pk = (uint8_t *)malloc(algo->public_key_bytes); + msg = (uint8_t *)malloc(cfg->msg_size); + sigs = (uint8_t *)malloc(cfg->batch_size * algo->signature_bytes); + sig_lens = (size_t *)calloc(cfg->batch_size, sizeof(size_t)); + if (sk == NULL || pk == NULL || msg == NULL || sigs == NULL || sig_lens == NULL) { + goto fail; + } + + bb_timer_start(&timer); + if (algo->keygen(sk, algo->secret_key_bytes, &sk_len, + pk, algo->public_key_bytes, &pk_len) != BB_OK) { + goto fail; + } + bb_timer_stop(&timer); + out->keygen_ms = bb_timer_elapsed_ms(&timer); + + out->time_seq_ms = 0.0; + out->verify_ms = 0.0; + out->serialize_time_ms = 0.0; + + for (it = 0; it < cfg->iters; ++it) { + size_t i; + + for (i = 0; i < cfg->batch_size; ++i) { + size_t sig_len = 0; + uint8_t *sig_slot = sigs + (i * algo->signature_bytes); + + bb_fill_message(msg, cfg->msg_size, cfg->seed, it * cfg->batch_size + i); + + bb_timer_start(&timer); + if (algo->sign(sk, sk_len, pk, pk_len, + msg, cfg->msg_size, + sig_slot, algo->signature_bytes, + &sig_len) != BB_OK) { + goto fail; + } + bb_timer_stop(&timer); + out->time_seq_ms += bb_timer_elapsed_ms(&timer); + + sig_lens[i] = sig_len; + total_sig_bytes += sig_len; + + if (cfg->verify) { + bb_timer_start(&timer); + if (algo->verify(pk, pk_len, msg, cfg->msg_size, + sig_slot, sig_len) != BB_OK) { + goto fail; + } + bb_timer_stop(&timer); + out->verify_ms += bb_timer_elapsed_ms(&timer); + } + } + + serialized = (uint8_t *)realloc(serialized, total_sig_bytes == 0 ? 1 : total_sig_bytes); + if (serialized == NULL) { + goto fail; + } + + { + size_t cursor = 0; + bb_timer_start(&timer); + for (i = 0; i < cfg->batch_size; ++i) { + memcpy(serialized + cursor, + sigs + (i * algo->signature_bytes), + sig_lens[i]); + cursor += sig_lens[i]; + } + bb_timer_stop(&timer); + out->serialize_time_ms += bb_timer_elapsed_ms(&timer); + } + } + + out->algorithm = algo->name; + out->batch_size = cfg->batch_size; + out->peak_memory_mb = get_peak_memory_mb(); + out->total_sig_size_mb = (double)total_sig_bytes / (1024.0 * 1024.0); + + free(sk); + free(pk); + free(msg); + free(sigs); + free(sig_lens); + free(serialized); + return 0; + +fail: + free(sk); + free(pk); + free(msg); + free(sigs); + free(sig_lens); + free(serialized); + return -1; +} + +static void print_result(const bench_result *r) { + printf("algo=%s batch=%zu keygen_ms=%.3f seq_ms=%.3f verify_ms=%.3f serialize_ms=%.3f peak_mem_mb=%.2f total_sig_mb=%.3f\n", + r->algorithm, + r->batch_size, + r->keygen_ms, + r->time_seq_ms, + r->verify_ms, + r->serialize_time_ms, + r->peak_memory_mb, + r->total_sig_size_mb); +} + +static int write_csv(FILE *f, const bench_result *r) { + return fprintf( + f, + "%s,%zu,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f\n", + r->algorithm, + r->batch_size, + r->time_seq_ms, + 0.0, + 0.0, + r->peak_memory_mb, + r->total_sig_size_mb, + r->serialize_time_ms) < 0 + ? -1 + : 0; +} + +static int write_json(FILE *f, const bench_result *results, size_t count, + const bench_config *cfg) { + size_t i; + if (fprintf(f, + "{\n" + " \"config\": {\n" + " \"algo\": \"%s\",\n" + " \"batch_size\": %zu,\n" + " \"iters\": %zu,\n" + " \"msg_size\": %zu,\n" + " \"seed\": %llu,\n" + " \"verify\": %d\n" + " },\n" + " \"results\": [\n", + cfg->algo, + cfg->batch_size, + cfg->iters, + cfg->msg_size, + (unsigned long long)cfg->seed, + cfg->verify) < 0) { + return -1; + } + + for (i = 0; i < count; ++i) { + const bench_result *r = &results[i]; + if (fprintf(f, + " {\"algorithm\":\"%s\",\"batch_size\":%zu," + "\"time_seq_ms\":%.6f,\"time_batch_ms\":%.6f," + "\"speedup\":%.6f,\"peak_memory_MB\":%.6f," + "\"total_sig_size_MB\":%.6f,\"serialize_time_ms\":%.6f}%s\n", + r->algorithm, + r->batch_size, + r->time_seq_ms, + 0.0, + 0.0, + r->peak_memory_mb, + r->total_sig_size_mb, + r->serialize_time_ms, + i + 1 == count ? "" : ",") < 0) { + return -1; + } + } + + return fprintf(f, " ]\n}\n") < 0 ? -1 : 0; +} + +int main(int argc, char **argv) { + bench_config cfg; + bench_result *results = NULL; + size_t results_count = 0; + FILE *csv = NULL; + FILE *json = NULL; + size_t i; + int parse_rc; + + parse_rc = parse_args(argc, argv, &cfg); + if (parse_rc == 1) { + return 0; + } + if (parse_rc != 0) { + print_usage(argv[0]); + return 1; + } + + results = (bench_result *)calloc(bb_algorithm_count(), sizeof(bench_result)); + if (results == NULL) { + return 1; + } + + if (cfg.out_csv != NULL) { + csv = fopen(cfg.out_csv, "w"); + if (csv == NULL) { + free(results); + return 1; + } + fprintf(csv, + "algorithm,batch_size,time_seq_ms,time_batch_ms,speedup," + "peak_memory_MB,total_sig_size_MB,serialize_time_ms\n"); + } + + for (i = 0; i < bb_algorithm_count(); ++i) { + const bb_algorithm *algo = bb_algorithm_at(i); + + if (strcmp(cfg.algo, "all") != 0 && strcmp(cfg.algo, algo->name) != 0) { + continue; + } + + if (run_one(algo, &cfg, &results[results_count]) != 0) { + if (csv != NULL) { + fclose(csv); + } + free(results); + return 1; + } + + print_result(&results[results_count]); + if (csv != NULL && write_csv(csv, &results[results_count]) != 0) { + fclose(csv); + free(results); + return 1; + } + + ++results_count; + } + + if (results_count == 0) { + if (csv != NULL) { + fclose(csv); + } + free(results); + return 1; + } + + if (csv != NULL) { + fclose(csv); + } + + if (cfg.out_json != NULL) { + json = fopen(cfg.out_json, "w"); + if (json == NULL) { + free(results); + return 1; + } + if (write_json(json, results, results_count, &cfg) != 0) { + fclose(json); + free(results); + return 1; + } + fclose(json); + } + + free(results); + return 0; +} From 8fb9befbe495a3d34379b9e5982dabf755ac862b Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 11:46:41 +0000 Subject: [PATCH 08/13] feat(bench): include params in benchmark outputs - Add --params flow from benchmark script to bench_seq - Export params field in CSV and JSON result artifacts - Add smoke test target source and linker workaround for third-party collisions --- bench/CMakeLists.txt | 4 ++ bench/bench_seq.c | 22 ++++-- scripts/benchmark.sh | 153 +++++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 4 ++ tests/adapters_smoke.c | 69 +++++++++++++++++++ 5 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 scripts/benchmark.sh create mode 100644 tests/adapters_smoke.c diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 51d7000..f82e80b 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -2,4 +2,8 @@ add_executable(bench_seq bench_seq.c) target_link_libraries(bench_seq PRIVATE batch_bench_adapters) +if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_link_options(bench_seq PRIVATE -Wl,--allow-multiple-definition) +endif() + target_compile_features(bench_seq PRIVATE c_std_99) diff --git a/bench/bench_seq.c b/bench/bench_seq.c index 9f8cfbd..354b6b7 100644 --- a/bench/bench_seq.c +++ b/bench/bench_seq.c @@ -10,6 +10,7 @@ typedef struct bench_config { const char *algo; + const char *params; size_t batch_size; size_t iters; size_t msg_size; @@ -21,6 +22,7 @@ typedef struct bench_config { typedef struct bench_result { const char *algorithm; + const char *params; size_t batch_size; double keygen_ms; double time_seq_ms; @@ -33,6 +35,7 @@ typedef struct bench_result { static void print_usage(const char *prog) { printf("Usage: %s [options]\n", prog); printf(" --algo Algorithm name or all (default: all)\n"); + printf(" --params Paramset tag for result output (default: default)\n"); printf(" --batch-size Messages per iteration (default: 1)\n"); printf(" --iters Iterations (default: 100)\n"); printf(" --msg-size Message size in bytes (default: 1024)\n"); @@ -95,6 +98,7 @@ static int parse_args(int argc, char **argv, bench_config *cfg) { int i; cfg->algo = "all"; + cfg->params = "default"; cfg->batch_size = 1; cfg->iters = 100; cfg->msg_size = BB_DEFAULT_MESSAGE_SIZE; @@ -113,6 +117,10 @@ static int parse_args(int argc, char **argv, bench_config *cfg) { cfg->algo = argv[++i]; continue; } + if (strcmp(arg, "--params") == 0 && i + 1 < argc) { + cfg->params = argv[++i]; + continue; + } if (strcmp(arg, "--batch-size") == 0 && i + 1 < argc) { if (parse_size(argv[++i], &cfg->batch_size) != 0 || cfg->batch_size == 0) { return -1; @@ -252,6 +260,7 @@ static int run_one(const bb_algorithm *algo, const bench_config *cfg, bench_resu } out->algorithm = algo->name; + out->params = cfg->params; out->batch_size = cfg->batch_size; out->peak_memory_mb = get_peak_memory_mb(); out->total_sig_size_mb = (double)total_sig_bytes / (1024.0 * 1024.0); @@ -275,8 +284,9 @@ static int run_one(const bb_algorithm *algo, const bench_config *cfg, bench_resu } static void print_result(const bench_result *r) { - printf("algo=%s batch=%zu keygen_ms=%.3f seq_ms=%.3f verify_ms=%.3f serialize_ms=%.3f peak_mem_mb=%.2f total_sig_mb=%.3f\n", + printf("algo=%s params=%s batch=%zu keygen_ms=%.3f seq_ms=%.3f verify_ms=%.3f serialize_ms=%.3f peak_mem_mb=%.2f total_sig_mb=%.3f\n", r->algorithm, + r->params, r->batch_size, r->keygen_ms, r->time_seq_ms, @@ -289,8 +299,9 @@ static void print_result(const bench_result *r) { static int write_csv(FILE *f, const bench_result *r) { return fprintf( f, - "%s,%zu,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f\n", + "%s,%s,%zu,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f\n", r->algorithm, + r->params, r->batch_size, r->time_seq_ms, 0.0, @@ -309,6 +320,7 @@ static int write_json(FILE *f, const bench_result *results, size_t count, "{\n" " \"config\": {\n" " \"algo\": \"%s\",\n" + " \"params\": \"%s\",\n" " \"batch_size\": %zu,\n" " \"iters\": %zu,\n" " \"msg_size\": %zu,\n" @@ -317,6 +329,7 @@ static int write_json(FILE *f, const bench_result *results, size_t count, " },\n" " \"results\": [\n", cfg->algo, + cfg->params, cfg->batch_size, cfg->iters, cfg->msg_size, @@ -328,11 +341,12 @@ static int write_json(FILE *f, const bench_result *results, size_t count, for (i = 0; i < count; ++i) { const bench_result *r = &results[i]; if (fprintf(f, - " {\"algorithm\":\"%s\",\"batch_size\":%zu," + " {\"algorithm\":\"%s\",\"params\":\"%s\",\"batch_size\":%zu," "\"time_seq_ms\":%.6f,\"time_batch_ms\":%.6f," "\"speedup\":%.6f,\"peak_memory_MB\":%.6f," "\"total_sig_size_MB\":%.6f,\"serialize_time_ms\":%.6f}%s\n", r->algorithm, + r->params, r->batch_size, r->time_seq_ms, 0.0, @@ -378,7 +392,7 @@ int main(int argc, char **argv) { return 1; } fprintf(csv, - "algorithm,batch_size,time_seq_ms,time_batch_ms,speedup," + "algorithm,params,batch_size,time_seq_ms,time_batch_ms,speedup," "peak_memory_MB,total_sig_size_MB,serialize_time_ms\n"); } diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh new file mode 100644 index 0000000..afcc33b --- /dev/null +++ b/scripts/benchmark.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BUILD_DIR="${ROOT_DIR}/build" +OUTPUT_DIR="${ROOT_DIR}/results/bench-$(date +%Y%m%d-%H%M%S)" +ALGOS="all" +PARAMS="m_128_20" +BATCH_SIZES="1,2,4,8,16,32,64" +ITERS=100 +MSG_SIZE=1024 +SEED=1 +VERIFY=1 +DRY_RUN=0 + +usage() { + cat < + --params + hypericum: (default: m_128_20) + shipovnik: + kryzhovnik: + + --batch-sizes (default: 1,2,4,8,16,32,64) + --iters (default: 100) + --msg-size (default: 1024) + --seed (default: 1) + --verify <0|1> (default: 1) + --build-dir (default: ./build) + --output-dir (default: ./results/bench-) + --dry-run +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --algo) + ALGOS="$2" + shift 2 + ;; + --batch-sizes) + BATCH_SIZES="$2" + shift 2 + ;; + --params) + PARAMS="$2" + shift 2 + ;; + --iters) + ITERS="$2" + shift 2 + ;; + --msg-size) + MSG_SIZE="$2" + shift 2 + ;; + --seed) + SEED="$2" + shift 2 + ;; + --verify) + VERIFY="$2" + shift 2 + ;; + --build-dir) + BUILD_DIR="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --dry-run) + DRY_RUN=1 + shift + ;; + --help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +mkdir -p "$OUTPUT_DIR" + +echo "[benchmark] root: $ROOT_DIR" +echo "[benchmark] build dir: $BUILD_DIR" +echo "[benchmark] output: $OUTPUT_DIR" +echo "[benchmark] params: $PARAMS" + +auto_configure_build() { + cmake -S "$ROOT_DIR" -B "$BUILD_DIR" -DHYPERICUM_PARAMSET=$PARAMS + cmake --build "$BUILD_DIR" --parallel +} + +run_one() { + local algo="$1" + local batch_size="$2" + local csv_out="$OUTPUT_DIR/${algo}_b${batch_size}.csv" + local json_out="$OUTPUT_DIR/${algo}_b${batch_size}.json" + + local cmd=( + "$BUILD_DIR/bench/bench_seq" + --algo "$algo" + --batch-size "$batch_size" + --iters "$ITERS" + --msg-size "$MSG_SIZE" + --seed "$SEED" + --params "$PARAMS" + --verify "$VERIFY" + --out-csv "$csv_out" + --out-json "$json_out" + ) + + if [[ "$DRY_RUN" -eq 1 ]]; then + echo "[dry-run] ${cmd[*]}" + else + echo "[run] algo=$algo batch_size=$batch_size" + "${cmd[@]}" + fi +} + +if [[ "$DRY_RUN" -eq 1 ]]; then + echo "[dry-run] cmake -S $ROOT_DIR -B $BUILD_DIR -DHYPERICUM_PARAMSET=$PARAMS" + echo "[dry-run] cmake --build $BUILD_DIR --parallel" +else + auto_configure_build +fi + +IFS=',' read -r -a batches <<< "$BATCH_SIZES" + +if [[ "$ALGOS" == "all" ]]; then + for algo in shipovnik "hypericum_${PARAMS}" kryzhovnik; do + for b in "${batches[@]}"; do + run_one "$algo" "$b" + done + done +else + if [[ "$ALGOS" == "hypericum" ]]; then + ALGOS="hypericum_${PARAMS}" + fi + for b in "${batches[@]}"; do + run_one "$ALGOS" "$b" + done +fi + +echo "[benchmark] done" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7a4bde4..53493a9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,10 @@ add_executable(adapters_smoke adapters_smoke.c) target_link_libraries(adapters_smoke PRIVATE batch_bench_adapters) +if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_link_options(adapters_smoke PRIVATE -Wl,--allow-multiple-definition) +endif() + target_compile_features(adapters_smoke PRIVATE c_std_99) add_test(NAME adapters_smoke COMMAND adapters_smoke) diff --git a/tests/adapters_smoke.c b/tests/adapters_smoke.c new file mode 100644 index 0000000..f1e8a6c --- /dev/null +++ b/tests/adapters_smoke.c @@ -0,0 +1,69 @@ +#include "batch_bench.h" +#include "utils/message_gen.h" + +#include +#include +#include + +int main(void) { + size_t i; + uint8_t msg[BB_DEFAULT_MESSAGE_SIZE]; + + bb_fill_message(msg, sizeof(msg), 1, 0); + + for (i = 0; i < bb_algorithm_count(); ++i) { + const bb_algorithm *algo = bb_algorithm_at(i); + uint8_t *sk = (uint8_t *)malloc(algo->secret_key_bytes); + uint8_t *pk = (uint8_t *)malloc(algo->public_key_bytes); + uint8_t *sig = (uint8_t *)malloc(algo->signature_bytes); + size_t sk_len = 0; + size_t pk_len = 0; + size_t sig_len = 0; + + if (sk == NULL || pk == NULL || sig == NULL) { + free(sk); + free(pk); + free(sig); + return 1; + } + + if (algo->keygen(sk, algo->secret_key_bytes, &sk_len, + pk, algo->public_key_bytes, &pk_len) != BB_OK) { + free(sk); + free(pk); + free(sig); + return 1; + } + + if (algo->sign(sk, sk_len, pk, pk_len, msg, sizeof(msg), + sig, algo->signature_bytes, &sig_len) != BB_OK) { + free(sk); + free(pk); + free(sig); + return 1; + } + + if (algo->verify(pk, pk_len, msg, sizeof(msg), sig, sig_len) != BB_OK) { + free(sk); + free(pk); + free(sig); + return 1; + } + + msg[0] ^= 0x01U; + if (algo->verify(pk, pk_len, msg, sizeof(msg), sig, sig_len) == BB_OK) { + free(sk); + free(pk); + free(sig); + return 1; + } + msg[0] ^= 0x01U; + + free(sk); + free(pk); + free(sig); + } + + printf("adapters_smoke: ok\n"); + return 0; +} From e207b80fce2e8bcc962eec04a9a3fd11209034f7 Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 11:59:15 +0000 Subject: [PATCH 09/13] fix(build): prevent shipovnik randombytes symbol clash - Namespace Shipovnik randombytes and secure_erase symbols - Remove temporary allow-multiple-definition linker flags - Keep benchmark script executable mode for direct runs --- bench/CMakeLists.txt | 4 ---- scripts/benchmark.sh | 0 tests/CMakeLists.txt | 4 ---- third_party/CMakeLists.txt | 6 +++++- 4 files changed, 5 insertions(+), 9 deletions(-) mode change 100644 => 100755 scripts/benchmark.sh diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index f82e80b..51d7000 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -2,8 +2,4 @@ add_executable(bench_seq bench_seq.c) target_link_libraries(bench_seq PRIVATE batch_bench_adapters) -if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_link_options(bench_seq PRIVATE -Wl,--allow-multiple-definition) -endif() - target_compile_features(bench_seq PRIVATE c_std_99) diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh old mode 100644 new mode 100755 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 53493a9..7a4bde4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,10 +2,6 @@ add_executable(adapters_smoke adapters_smoke.c) target_link_libraries(adapters_smoke PRIVATE batch_bench_adapters) -if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_link_options(adapters_smoke PRIVATE -Wl,--allow-multiple-definition) -endif() - target_compile_features(adapters_smoke PRIVATE c_std_99) add_test(NAME adapters_smoke COMMAND adapters_smoke) diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 730c40f..78e0fc9 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -110,7 +110,11 @@ if(TARGET streebog AND EXISTS "${SHIPOVNIK_DIR}/src/shipovnik.c") ${SHIPOVNIK_DIR}/src ${STREEBOG_DIR} ) - target_compile_definitions(shipovnik PRIVATE ENTROPY_SOURCE="${ENTROPY_SOURCE}") + target_compile_definitions(shipovnik PRIVATE + ENTROPY_SOURCE="${ENTROPY_SOURCE}" + randombytes=shipovnik_randombytes + secure_erase=shipovnik_secure_erase + ) target_link_libraries(shipovnik PRIVATE streebog) add_executable(shipovnik_example ${SHIPOVNIK_DIR}/shipovnik_example.c) From 898c4407152e4a65db2e30e73edb0af56fae6c24 Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 12:45:27 +0000 Subject: [PATCH 10/13] fix(bench): make hypericum result routing stable - Use stable registry name hypericum for adapter lookup - Remove hypericum_ script remapping logic - Keep params as explicit output field in CSV and JSON --- scripts/benchmark.sh | 5 +---- src/signature/registry.c | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index afcc33b..97ce6cb 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -136,15 +136,12 @@ fi IFS=',' read -r -a batches <<< "$BATCH_SIZES" if [[ "$ALGOS" == "all" ]]; then - for algo in shipovnik "hypericum_${PARAMS}" kryzhovnik; do + for algo in shipovnik hypericum kryzhovnik; do for b in "${batches[@]}"; do run_one "$algo" "$b" done done else - if [[ "$ALGOS" == "hypericum" ]]; then - ALGOS="hypericum_${PARAMS}" - fi for b in "${batches[@]}"; do run_one "$ALGOS" "$b" done diff --git a/src/signature/registry.c b/src/signature/registry.c index 0d35500..89bdd7e 100644 --- a/src/signature/registry.c +++ b/src/signature/registry.c @@ -9,7 +9,7 @@ static const bb_algorithm k_algorithms[] = { { - .name = "hypericum_m_128_20", + .name = "hypericum", .public_key_bytes = CRYPTO_PUBLICKEYBYTES, .secret_key_bytes = CRYPTO_SECRETKEYBYTES, .signature_bytes = CRYPTO_BYTES, From e6ba811e9a100a24653f158e8ad249837c9e875f Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 12:57:05 +0000 Subject: [PATCH 11/13] fix(scripts): add resume-safe benchmark artifacts - Skip already completed csv and json outputs by default - Write through temporary files and move on success - Clean zero-byte partial files after interrupted runs --- scripts/benchmark.sh | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 97ce6cb..5fba206 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -12,6 +12,7 @@ MSG_SIZE=1024 SEED=1 VERIFY=1 DRY_RUN=0 +SKIP_EXISTING=1 usage() { cat < (default: ./build) --output-dir (default: ./results/bench-) --dry-run + --skip-existing <0|1> (default: 1) EOF } @@ -75,6 +77,10 @@ while [[ $# -gt 0 ]]; do DRY_RUN=1 shift ;; + --skip-existing) + SKIP_EXISTING="$2" + shift 2 + ;; --help) usage exit 0 @@ -93,6 +99,7 @@ echo "[benchmark] root: $ROOT_DIR" echo "[benchmark] build dir: $BUILD_DIR" echo "[benchmark] output: $OUTPUT_DIR" echo "[benchmark] params: $PARAMS" +echo "[benchmark] skip: $SKIP_EXISTING" auto_configure_build() { cmake -S "$ROOT_DIR" -B "$BUILD_DIR" -DHYPERICUM_PARAMSET=$PARAMS @@ -104,6 +111,23 @@ run_one() { local batch_size="$2" local csv_out="$OUTPUT_DIR/${algo}_b${batch_size}.csv" local json_out="$OUTPUT_DIR/${algo}_b${batch_size}.json" + local csv_tmp="$csv_out.tmp" + local json_tmp="$json_out.tmp" + + if [[ "$SKIP_EXISTING" -eq 1 ]]; then + if [[ -s "$csv_out" && -s "$json_out" ]]; then + echo "[skip] algo=$algo batch_size=$batch_size (artifacts exist)" + return + fi + fi + + rm -f "$csv_tmp" "$json_tmp" + if [[ -f "$csv_out" && ! -s "$csv_out" ]]; then + rm -f "$csv_out" + fi + if [[ -f "$json_out" && ! -s "$json_out" ]]; then + rm -f "$json_out" + fi local cmd=( "$BUILD_DIR/bench/bench_seq" @@ -114,8 +138,8 @@ run_one() { --seed "$SEED" --params "$PARAMS" --verify "$VERIFY" - --out-csv "$csv_out" - --out-json "$json_out" + --out-csv "$csv_tmp" + --out-json "$json_tmp" ) if [[ "$DRY_RUN" -eq 1 ]]; then @@ -123,6 +147,8 @@ run_one() { else echo "[run] algo=$algo batch_size=$batch_size" "${cmd[@]}" + mv "$csv_tmp" "$csv_out" + mv "$json_tmp" "$json_out" fi } From 08edb3307e8176c625944979b5337984d41a5084 Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 20:11:05 +0000 Subject: [PATCH 12/13] build(third_party): keep kryzhovnik C-only integration - Remove CXX-only compile settings for kryzhovnik targets - Keep kryzhovnik and tests on C compiler and C99 standard - Update kryzhovnik submodule pointer to C-only migration commit --- third_party/CMakeLists.txt | 13 +++---------- third_party/kryzhovnik | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 78e0fc9..c21423e 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -152,38 +152,31 @@ if(EXISTS "${KRYZHOVNIK_DIR}/sign.c") list(APPEND KRYZHOVNIK_SOURCES ${KRYZHOVNIK_DIR}/kryzhovnik_wrapper.c) endif() - set_source_files_properties(${KRYZHOVNIK_SOURCES} PROPERTIES LANGUAGE CXX) add_library(kryzhovnik STATIC ${KRYZHOVNIK_SOURCES}) - set_target_properties(kryzhovnik PROPERTIES LINKER_LANGUAGE CXX) target_include_directories(kryzhovnik PUBLIC $ ) target_link_libraries(kryzhovnik PRIVATE ${KRYZHOVNIK_OPENSSL_TARGET}) target_compile_options(kryzhovnik PRIVATE - $<$,$>:-Wall -Wextra -Wshadow -Wconversion -O3 -march=native> - $<$:/W4 /O2> + $<$,$>:-Wall -Wextra -Wshadow -Wconversion -O3 -march=native> + $<$:/W4 /O2> ) + target_compile_features(kryzhovnik PRIVATE c_std_99) if(BUILD_TESTING AND EXISTS "${KRYZHOVNIK_DIR}/pqs_test.c") - set_source_files_properties(${KRYZHOVNIK_DIR}/pqs_test.c PROPERTIES LANGUAGE CXX) add_executable(kryzhovnik_test ${KRYZHOVNIK_DIR}/pqs_test.c) - set_target_properties(kryzhovnik_test PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(kryzhovnik_test PRIVATE kryzhovnik) add_test(NAME kryzhovnik_test COMMAND kryzhovnik_test) endif() if(BUILD_TESTING AND EXISTS "${KRYZHOVNIK_DIR}/test_wrapper.c") - set_source_files_properties(${KRYZHOVNIK_DIR}/test_wrapper.c PROPERTIES LANGUAGE CXX) add_executable(kryzhovnik_wrapper_test ${KRYZHOVNIK_DIR}/test_wrapper.c) - set_target_properties(kryzhovnik_wrapper_test PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(kryzhovnik_wrapper_test PRIVATE kryzhovnik) add_test(NAME kryzhovnik_wrapper_test COMMAND kryzhovnik_wrapper_test) endif() if(BUILD_TESTING AND EXISTS "${KRYZHOVNIK_DIR}/test_pqs.c") - set_source_files_properties(${KRYZHOVNIK_DIR}/test_pqs.c PROPERTIES LANGUAGE CXX) add_executable(kryzhovnik_api_test ${KRYZHOVNIK_DIR}/test_pqs.c) - set_target_properties(kryzhovnik_api_test PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(kryzhovnik_api_test PRIVATE kryzhovnik) add_test(NAME kryzhovnik_api_test COMMAND kryzhovnik_api_test) endif() diff --git a/third_party/kryzhovnik b/third_party/kryzhovnik index 7ac29e4..43c316b 160000 --- a/third_party/kryzhovnik +++ b/third_party/kryzhovnik @@ -1 +1 @@ -Subproject commit 7ac29e4cb2cadce3d4ae8f329baaedf3e94fd10f +Subproject commit 43c316b3b56509f0c9bfa66d3c388c4ec23fc8b9 From b67e453bf407413ab69de27bc5ddd74e34d789bf Mon Sep 17 00:00:00 2001 From: cherninkiy Date: Mon, 6 Apr 2026 22:16:05 +0000 Subject: [PATCH 13/13] feat(bench): unify kryzhovnik paramsets and debug flow - Add KRYZHOVNIK_PARAMSET support in third_party CMake - Update benchmark script for per-algorithm paramset routing - Document kryzhovnik debug paramset usage in README files - Add security.sage helper script and update submodule pointer --- .gitignore | 5 +- README.md | 26 ++++ README_ru.md | 26 ++++ scripts/benchmark.sh | 47 ++++-- scripts/security.sage | 286 +++++++++++++++++++++++++++++++++++++ third_party/CMakeLists.txt | 19 +++ third_party/kryzhovnik | 2 +- 7 files changed, 399 insertions(+), 12 deletions(-) create mode 100644 scripts/security.sage diff --git a/.gitignore b/.gitignore index 9bfd15b..f6ea97f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Build artifacts -/build/ +/build*/ CMakeFiles/ CMakeCache.txt cmake_install.cmake @@ -55,3 +55,6 @@ __pycache__/ *~ .DS_Store Thumbs.db + +#Environment files +.env diff --git a/README.md b/README.md index 8c7e7a6..99630c9 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,32 @@ git submodule update --init --recursive ./build/bench/bench_batch --algo hypericum --batch-size 16 --iterations 100 ``` +### Paramset Selection + +Both Hypericum and Kryzhovnik support build-time paramset selection via CMake. + +```bash +cmake -S . -B build \ + -DHYPERICUM_PARAMSET=m_128_20 \ + -DKRYZHOVNIK_PARAMSET=medium +cmake --build build --parallel +``` + +Supported Kryzhovnik values: `small`, `medium`, `large`, `debug`. + +`debug` is intended for fast diagnostics and troubleshooting, not security evaluation. + +The benchmark helper script also supports separate flags: + +```bash +./scripts/benchmark.sh \ + --algo kryzhovnik \ + --kryzhovnik-params small \ + --hypericum-params m_128_20 +``` + +Note: Kryzhovnik constants are aligned with `security.sage` for `small/medium/large`. Keep validating against the official specification before production use. + ### Expected Output (after MVP) After running benchmarks, the `results/` directory will contain: diff --git a/README_ru.md b/README_ru.md index 0bbb3ea..ecc2cb8 100644 --- a/README_ru.md +++ b/README_ru.md @@ -89,6 +89,32 @@ git submodule update --init --recursive ./build/bench/bench_batch --algo hypericum --batch-size 16 --iterations 100 ``` +### Выбор paramset + +Для Hypericum и Kryzhovnik поддерживается выбор набора параметров на этапе сборки через CMake. + +```bash +cmake -S . -B build \ + -DHYPERICUM_PARAMSET=m_128_20 \ + -DKRYZHOVNIK_PARAMSET=medium +cmake --build build --parallel +``` + +Поддерживаемые значения для Kryzhovnik: `small`, `medium`, `large`, `debug`. + +`debug` предназначен для быстрой диагностики и отладки, а не для оценки стойкости. + +В benchmark-скрипте можно задавать наборы отдельно: + +```bash +./scripts/benchmark.sh \ + --algo kryzhovnik \ + --kryzhovnik-params small \ + --hypericum-params m_128_20 +``` + +Примечание: константы Kryzhovnik для `small/medium/large` синхронизированы со скриптом `security.sage`; перед production-использованием их нужно дополнительно верифицировать по официальной спецификации. + ### Ожидаемые результаты (после MVP) После запуска бенчмарков в директории `results/` появятся: diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 5fba206..33e2355 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -5,7 +5,8 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BUILD_DIR="${ROOT_DIR}/build" OUTPUT_DIR="${ROOT_DIR}/results/bench-$(date +%Y%m%d-%H%M%S)" ALGOS="all" -PARAMS="m_128_20" +HYPERICUM_PARAMS="m_128_20" +KRYZHOVNIK_PARAMS="medium" BATCH_SIZES="1,2,4,8,16,32,64" ITERS=100 MSG_SIZE=1024 @@ -18,10 +19,10 @@ usage() { cat < - --params - hypericum: (default: m_128_20) - shipovnik: - kryzhovnik: + --hypericum-params (default: m_128_20) + --kryzhovnik-params (default: medium) + --params (legacy: sets hypericum; + also sets kryzhovnik when value is small|medium|large) --batch-sizes (default: 1,2,4,8,16,32,64) --iters (default: 100) @@ -46,7 +47,20 @@ while [[ $# -gt 0 ]]; do shift 2 ;; --params) - PARAMS="$2" + HYPERICUM_PARAMS="$2" + case "$2" in + small|medium|large|debug) + KRYZHOVNIK_PARAMS="$2" + ;; + esac + shift 2 + ;; + --hypericum-params) + HYPERICUM_PARAMS="$2" + shift 2 + ;; + --kryzhovnik-params) + KRYZHOVNIK_PARAMS="$2" shift 2 ;; --iters) @@ -98,11 +112,14 @@ mkdir -p "$OUTPUT_DIR" echo "[benchmark] root: $ROOT_DIR" echo "[benchmark] build dir: $BUILD_DIR" echo "[benchmark] output: $OUTPUT_DIR" -echo "[benchmark] params: $PARAMS" +echo "[benchmark] hypericum: $HYPERICUM_PARAMS" +echo "[benchmark] kryzhovnik:$KRYZHOVNIK_PARAMS" echo "[benchmark] skip: $SKIP_EXISTING" auto_configure_build() { - cmake -S "$ROOT_DIR" -B "$BUILD_DIR" -DHYPERICUM_PARAMSET=$PARAMS + cmake -S "$ROOT_DIR" -B "$BUILD_DIR" \ + -DHYPERICUM_PARAMSET="$HYPERICUM_PARAMS" \ + -DKRYZHOVNIK_PARAMSET="$KRYZHOVNIK_PARAMS" cmake --build "$BUILD_DIR" --parallel } @@ -113,6 +130,16 @@ run_one() { local json_out="$OUTPUT_DIR/${algo}_b${batch_size}.json" local csv_tmp="$csv_out.tmp" local json_tmp="$json_out.tmp" + local params_label="default" + + case "$algo" in + hypericum) + params_label="$HYPERICUM_PARAMS" + ;; + kryzhovnik) + params_label="$KRYZHOVNIK_PARAMS" + ;; + esac if [[ "$SKIP_EXISTING" -eq 1 ]]; then if [[ -s "$csv_out" && -s "$json_out" ]]; then @@ -136,7 +163,7 @@ run_one() { --iters "$ITERS" --msg-size "$MSG_SIZE" --seed "$SEED" - --params "$PARAMS" + --params "$params_label" --verify "$VERIFY" --out-csv "$csv_tmp" --out-json "$json_tmp" @@ -153,7 +180,7 @@ run_one() { } if [[ "$DRY_RUN" -eq 1 ]]; then - echo "[dry-run] cmake -S $ROOT_DIR -B $BUILD_DIR -DHYPERICUM_PARAMSET=$PARAMS" + echo "[dry-run] cmake -S $ROOT_DIR -B $BUILD_DIR -DHYPERICUM_PARAMSET=$HYPERICUM_PARAMS -DKRYZHOVNIK_PARAMSET=$KRYZHOVNIK_PARAMS" echo "[dry-run] cmake --build $BUILD_DIR --parallel" else auto_configure_build diff --git a/scripts/security.sage b/scripts/security.sage new file mode 100644 index 0000000..a4977c5 --- /dev/null +++ b/scripts/security.sage @@ -0,0 +1,286 @@ +from estimator import * + +def SVP_Classical(dim): + return 8*(dim)*(3/2)**(dim/2) + +def SVP_Quantum(dim): + return 8*(dim)*ZZ(2)**RR(0.265*dim) + + +# LWR +def LWR_estiamtes(n, l, k, q, p, s, model=SVP_Classical): + nLWE = n*l + m = k*n # m = k*n + s_bound = s + secret_distribution = (-s_bound, s_bound) + alpha = sqrt(2*pi)*1/RR(2*p) + success_probability = 0.99 + reduction_cost_model = lambda beta, d, B: model(beta) + res_primal = primal_usvp(nLWE, alpha, q, secret_distribution=secret_distribution, m=m, success_probability=success_probability, reduction_cost_model=reduction_cost_model) + res_dual = dual_scale(nLWE, alpha, q, secret_distribution=secret_distribution, m=m, success_probability=success_probability, reduction_cost_model=reduction_cost_model) + return res_primal, res_dual + +# Testing SIS +# This analysis is given in Dilithium, C3 +# https://eprint.iacr.org/eprint-bin/getfile.pl?entry=2017/633&version=20180910:112250&file=633.pdf + + +# go from beta to delta for BKZ +def get_delta(beta): + return RR(beta/(2*pi*e) * (pi*beta)**(1/beta))**(1/(2*(beta-1))) + +def getGSA(d, q, beta, nrows): + delta = get_delta(beta) + log_delta =log(delta,2) + + log_q = log(q) + zone1 = nrows*[log_q] + slope = -(1/(beta-1))*log(beta/(2*pi*e)*(pi*beta)^(1/beta)) + zone2_length = int(floor(log(q)/-slope)) + zone2 = [log_q + i * slope for i in range(1, zone2_length+1)] + zone3 =(d-nrows)*[0] + GSA = zone1+zone2+zone3 + + lattice_vol = nrows*log_q + current_vol = sum(GSA[i] for i in range(d)) + + #correct the volume since now current_vol > lattice_vol + ind = 0 + while current_vol>lattice_vol: + current_vol -= GSA[ind] + current_vol += GSA[ind+d] + ind += 1 + GSA = GSA[ind:ind+d] + + i_index = max(0, nrows-ind) + j_index = min(i_index + zone2_length, d) + #error we make by overshooting the while loop + err = lattice_vol - current_vol + for i in range(i_index, j_index): + GSA[i] += err / (j_index-i_index+1) + + return GSA, i_index, j_index + + +def getGSA_noq(d, q, beta, nrows): + delta = get_delta(beta) + log_delta =log(delta,2) + + log_q = log(q) + slope = -(1/(beta-1))*log(beta/(2*pi*e)*(pi*beta)^(1/beta)) + + lattice_vol = nrows*log_q + current_vol = 0 + GSA_tmp = 0 + GSA = [] + for i in range(d): + GSA_tmp -= slope + current_vol += GSA_tmp + if lattice_vol=0, 'i_index>=0' + assert j_index<=len(GSA), 'j_index<=N' + assert i_index!=j_index, 'i_index!=j_index' + + Pr1 = (2.*B)/q + log_Pr1 = ((i_index)*(log(Pr1, 2))) + + norm_BKZ = exp(GSA[i_index]) + sigma = norm_BKZ / sqrt(j_index - i_index + 1) + Pr2 = erf(B / (sigma * sqrt(2.))) + log_Pr2 = (j_index-i_index+1)* log(Pr2, 2) + + logPr_total_ = (beta/2.0)*log(4./3., 2) + log_Pr1+log_Pr2 + logPr_total = min(0,logPr_total_) + log_runTimeSVP = log(model(beta),2).n() + log_runTime = log_runTimeSVP - logPr_total + + return [i_index, j_index, round(log_Pr1+log_Pr2), log_runTimeSVP, round(log_runTime)] + + +def SIS_esimates(n, l, k, q, B, model=SVP_Classical): + d = n*(k + l + 1) + m = n*k + best_rt = 10000 + beta_range = range(100, d, 10) + for beta in beta_range: + if (log(model(beta),2) > best_rt): + break + for w in [d]: + lat_dim = w + log_runtime = RunTime(beta, lat_dim, q, m, B, model=model) + if(log_runtime[4] + $ ) target_link_libraries(kryzhovnik PRIVATE ${KRYZHOVNIK_OPENSSL_TARGET}) target_compile_options(kryzhovnik PRIVATE diff --git a/third_party/kryzhovnik b/third_party/kryzhovnik index 43c316b..9b900c0 160000 --- a/third_party/kryzhovnik +++ b/third_party/kryzhovnik @@ -1 +1 @@ -Subproject commit 43c316b3b56509f0c9bfa66d3c388c4ec23fc8b9 +Subproject commit 9b900c073dbb0a1dbc1a6f2a563e0a943d205b2e