diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90c4dfa..ba79348 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: run: | ctest --test-dir build-${{ matrix.name }} \ --output-on-failure \ - -R 'kryzhovnik|adapters_smoke|merkletree_test' + -R 'test_kryzhovnik|test_kryzhovnik_wrapper|test_kryzhovnik_api|test_merkletree|test_adapters_smoke|test_adapters_batch' - name: Benchmark smoke (${{ matrix.name }}) run: | @@ -59,6 +59,7 @@ jobs: --params ${{ matrix.hypericum_paramset }} \ --batch-size 1 \ --iters 1 \ + --warmup 1 \ --verify 1 \ --out-csv /tmp/h_${{ matrix.name }}.csv \ --out-json /tmp/h_${{ matrix.name }}.json @@ -68,6 +69,7 @@ jobs: --params default \ --batch-size 1 \ --iters 1 \ + --warmup 1 \ --verify 1 \ --out-csv /tmp/s_${{ matrix.name }}.csv \ --out-json /tmp/s_${{ matrix.name }}.json @@ -77,6 +79,7 @@ jobs: --params ${{ matrix.kryzhovnik_paramset }} \ --batch-size 1 \ --iters 1 \ + --warmup 1 \ --verify 1 \ --out-csv /tmp/k_${{ matrix.name }}.csv \ --out-json /tmp/k_${{ matrix.name }}.json diff --git a/.gitmodules b/.gitmodules index 731777e..a2f523b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,17 @@ -[submodule "third_party/shipovnik"] - path = third_party/shipovnik - url = https://github.com/QAPP-tech/shipovnik_tc26.git [submodule "third_party/hypericum"] path = third_party/hypericum - url = https://github.com/QAPP-tech/hypericum_tc26.git + url = https://github.com/cherninkiy/hypericum-wrapper-tc26 + branch = main + +[submodule "third_party/shipovnik"] + path = third_party/shipovnik + url = https://github.com/cherninkiy/shipovnik-wrapper-tc26 + branch = main + [submodule "third_party/kryzhovnik"] path = third_party/kryzhovnik - url = https://github.com/cherninkiy/kryzhovnik-wrapper-tc26.git + url = https://github.com/cherninkiy/kryzhovnik-wrapper-tc26 + branch = main [submodule "third_party/iaik_merkle_tree"] path = third_party/iaik_merkle_tree url = https://github.com/IAIK/merkle-tree.git diff --git a/README.md b/README.md index 1f7ff79..d0d23c6 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ This project demonstrates **batch signing** (Merkle tree based) for three Russian post-quantum signature schemes: -- **Shipovnik** (based on Stern protocol) -- **Hypericum** (stateless, based on SPHINCS+, uses **Streebog** hash function from its own implementation) -- **Kryzhovnik** (lattice-based, similar to Dilithium) +- [Shipovnik](https://github.com/QAPP-tech/shipovnik_tc26) (based on Stern protocol) +- [Hypericum](https://github.com/QAPP-tech/hypericum_tc26) (stateless, based on SPHINCS+, uses **Streebog** hash function from its own implementation) +- [Kryzhovnik](https://github.com/ElenaKirshanova/pqc_LWR_signature) (lattice-based, similar to Dilithium) The goal is to measure the speedup of signing a batch of messages with a single root signature + proofs, compared to sequential signing. The results will help to assess the feasibility of batch signing in high-load PKI systems. @@ -22,31 +22,46 @@ The goal is to measure the speedup of signing a batch of messages with a single ## Features -- Generic Merkle tree implementation (wrapper around [IAIK/merkle-tree](https://github.com/IAIK/merkle-tree)) -- Hash abstraction layer (supports **Streebog** via Hypericum implementation, SHA-256 for fallback/testing) -- Signature abstraction layer for: - - Russian PQC: [Shipovnik](https://github.com/QAPP-tech/shipovnik_tc26), [Hypericum](https://github.com/QAPP-tech/hypericum_tc26), [Kryzhovnik](https://github.com/ElenaKirshanova/pqc_LWR_signature) - - (planned) ML-DSA (FIPS 204) via liboqs -- Batch signing and verification API -- Microbenchmarks: sequential vs batch signing, verification overhead -- Results exported as CSV + plots +- (planned) Generic Merkle tree implementation (wrapper around [IAIK/merkle-tree](https://github.com/IAIK/merkle-tree)) +- (planned) Hash abstraction layer (supports **Streebog** via Hypericum implementation, SHA-256 for fallback/testing) +- Signature abstraction layer for Russian PQC: + - [Shipovnik](https://github.com/QAPP-tech/shipovnik_tc26) + - [Hypericum](https://github.com/QAPP-tech/hypericum_tc26) + - [Kryzhovnik](https://github.com/ElenaKirshanova/pqc_LWR_signature) +- Sequential benchmark (`bench_seq`) with configurable paramsets, verification pass, and warmup iterations +- Test suite with unified `test_*` naming in CTest -The original Kryzhovnik implementation is documented above. In this repository, the `third_party/kryzhovnik` submodule is pinned to a compatibility fork used for integration with Hypericum and Shipovnik. +## Original Repos and Forks + +Algorithm links in this README always point to original repositories: + +- Shipovnik (original): https://github.com/QAPP-tech/shipovnik_tc26 +- Hypericum (original): https://github.com/QAPP-tech/hypericum_tc26 +- Kryzhovnik (original): https://github.com/ElenaKirshanova/pqc_LWR_signature + +This project currently integrates wrapper/compatibility forks as submodules: + +- https://github.com/cherninkiy/shipovnik-wrapper-tc26 +- https://github.com/cherninkiy/hypericum-wrapper-tc26 +- https://github.com/cherninkiy/kryzhovnik-wrapper-tc26 + +Why forks are used: + +- To add integration-oriented APIs required by this project (detached and status-return wrappers) +- To keep adapter contracts consistent across all three algorithms +- To preserve reproducible pinned revisions for CI and local builds ## Repository Structure ``` batch-pqc/ ├── third_party/ # git submodules -│ ├── shipovnik/ → https://github.com/QAPP-tech/shipovnik_tc26 -│ ├── hypericum/ → https://github.com/QAPP-tech/hypericum_tc26 +│ ├── shipovnik/ → https://github.com/cherninkiy/shipovnik-wrapper-tc26 +│ ├── hypericum/ → https://github.com/cherninkiy/hypericum-wrapper-tc26 │ ├── kryzhovnik/ → https://github.com/cherninkiy/kryzhovnik-wrapper-tc26 │ └── iaik_merkle_tree/ → https://github.com/IAIK/merkle-tree ├── src/ -│ ├── hash/ # hash provider (uses Streebog from hypericum, SHA-256 fallback) │ ├── signature/ # signature provider adapters -│ ├── merkle/ # Merkle wrapper over IAIK -│ ├── batch/ # batch signing logic │ └── utils/ # timers, message generators ├── bench/ # benchmarking executables ├── tests/ # unit tests (CTest) @@ -80,8 +95,8 @@ git submodule update --init --recursive # Run tests ./scripts/third_party.sh tests -# Run benchmarks (sequential vs batch) -./build/bench/bench_batch --algo hypericum --batch-size 16 --iterations 100 +# Run benchmark (sequential signing) +./build/bench/bench_seq --algo hypericum --batch-size 16 --iters 100 --verify 1 ``` ### Paramset Selection @@ -123,12 +138,13 @@ After running benchmarks, the `results/` directory will contain: ## Current Status (MVP) -- [ ] Repository structure with submodules -- [ ] Hash abstraction + Streebog integration (from hypericum) -- [ ] Merkle tree wrapper (uses IAIK tree) -- [ ] Batch signer/verifier with null signature (test mode) -- [ ] Adapters for real PQC algorithms (in progress: Hypericum first) -- [ ] Full benchmark suite with results +- [x] Repository structure with submodules +- [x] Adapters for Shipovnik / Hypericum / Kryzhovnik with unified `bb_status` +- [x] Detached/status-return integration APIs wired through wrapper submodules +- [x] Sequential benchmark `bench_seq` with warmup and corrected signature-size metric +- [x] Test coverage: `test_adapters_smoke`, `test_adapters_batch`, `test_kryzhovnik*`, `test_merkletree` +- [ ] Merkle-based batch signer/verifier implementation +- [ ] Sequential vs real batch signing benchmark comparison - [ ] Final report (PDF) **MVP is being developed in `dev` branch. After completion, a Pull Request to `main` will be opened for review.** diff --git a/README_ru.md b/README_ru.md index 40e75ac..b099526 100644 --- a/README_ru.md +++ b/README_ru.md @@ -14,9 +14,9 @@ Этот проект демонстрирует **пакетную подпись** (на основе дерева Меркла) для трёх российских пост-квантовых схем подписи: -- **Шиповник** (на основе протокола Штерна) -- **Гиперикум** (stateless, на основе SPHINCS+, использует хеш-функцию **Стрибог** из своей реализации) -- **Крыжовник** (на основе решёток, аналог Dilithium) +- [Шиповник](https://github.com/QAPP-tech/shipovnik_tc26) (на основе протокола Штерна) +- [Гиперикум](https://github.com/QAPP-tech/hypericum_tc26) (stateless, на основе SPHINCS+, использует хеш-функцию **Стрибог** из своей реализации) +- [Крыжовник](https://github.com/ElenaKirshanova/pqc_LWR_signature) (на основе решёток, аналог Dilithium) Цель — измерить ускорение при подписи пакета сообщений с помощью одной подписи корня дерева + доказательств по сравнению с последовательной подписью. Результаты помогут оценить применимость пакетной подписи в высоконагруженных PKI-системах. @@ -25,16 +25,34 @@ ## Возможности -- Реализация дерева Меркла (обёртка над [IAIK/merkle-tree](https://github.com/IAIK/merkle-tree)) -- Слой абстракции хеш-функций (поддерживает **Стрибог** через реализацию Hypericum, SHA-256 для тестов) -- Слой абстракции подписей: - - Российские PQC: [Шиповник](https://github.com/QAPP-tech/shipovnik_tc26), [Гиперикум](https://github.com/QAPP-tech/hypericum_tc26), [Крыжовник](https://github.com/ElenaKirshanova/pqc_LWR_signature) - - (планируется) ML-DSA (FIPS 204) через liboqs -- API для пакетной подписи и верификации -- Микробенчмарки: последовательная vs пакетная подпись, накладные расходы на верификацию -- Экспорт результатов в CSV и графики +- (планируется) Реализация дерева Меркла (обёртка над [IAIK/merkle-tree](https://github.com/IAIK/merkle-tree)) +- (планируется) Слой абстракции хеш-функций (поддерживает **Стрибог** через реализацию Hypericum, SHA-256 для тестов) +- Слой абстракции подписей для российских PQC: + - [Шиповник](https://github.com/QAPP-tech/shipovnik_tc26) + - [Гиперикум](https://github.com/QAPP-tech/hypericum_tc26) + - [Крыжовник](https://github.com/ElenaKirshanova/pqc_LWR_signature) +- Последовательный бенчмарк (`bench_seq`) с параметрами paramset, verify-проходом и прогревочными итерациями +- Набор тестов с унифицированными именами `test_*` в CTest -Выше указана ссылка на оригинальную реализацию Крыжовника. В этом репозитории сабмодуль `third_party/kryzhovnik` зафиксирован на форке совместимости, который используется для интеграции с Hypericum и Shipovnik. +## Оригинальные репозитории и форки + +Ссылки на алгоритмы в этом README всегда ведут на оригинальные репозитории: + +- Шиповник (оригинал): https://github.com/QAPP-tech/shipovnik_tc26 +- Гиперикум (оригинал): https://github.com/QAPP-tech/hypericum_tc26 +- Крыжовник (оригинал): https://github.com/ElenaKirshanova/pqc_LWR_signature + +В проекте как сабмодули используются интеграционные/совместимые форки: + +- https://github.com/cherninkiy/shipovnik-wrapper-tc26 +- https://github.com/cherninkiy/hypericum-wrapper-tc26 +- https://github.com/cherninkiy/kryzhovnik-wrapper-tc26 + +Зачем используются форки: + +- Для добавления интеграционных API, необходимых проекту (detached и status-return обёртки) +- Для унификации контрактов адаптеров между тремя алгоритмами +- Для воспроизводимых зафиксированных ревизий в CI и локальной сборке ## Структура репозитория @@ -42,15 +60,12 @@ batch-pqc/ ├── third_party/ # git submodules -│ ├── shipovnik/ → https://github.com/QAPP-tech/shipovnik_tc26 -│ ├── hypericum/ → https://github.com/QAPP-tech/hypericum_tc26 +│ ├── shipovnik/ → https://github.com/cherninkiy/shipovnik-wrapper-tc26 +│ ├── hypericum/ → https://github.com/cherninkiy/hypericum-wrapper-tc26 │ ├── kryzhovnik/ → https://github.com/cherninkiy/kryzhovnik-wrapper-tc26 │ └── iaik_merkle_tree/ → https://github.com/IAIK/merkle-tree ├── src/ -│ ├── hash/ # провайдер хешей (Стрибог из hypericum, SHA-256 как запасной) │ ├── signature/ # адаптеры для алгоритмов подписи -│ ├── merkle/ # обёртка Merkle над IAIK -│ ├── batch/ # логика пакетной подписи │ └── utils/ # таймеры, генераторы сообщений ├── bench/ # исполняемые файлы бенчмарков ├── tests/ # модульные тесты (CTest) @@ -85,8 +100,8 @@ git submodule update --init --recursive # Запуск тестов ./scripts/third_party.sh tests -# Запуск бенчмарков (последовательная vs пакетная) -./build/bench/bench_batch --algo hypericum --batch-size 16 --iterations 100 +# Запуск бенчмарка (последовательная подпись) +./build/bench/bench_seq --algo hypericum --batch-size 16 --iters 100 --verify 1 ``` ### Выбор paramset @@ -128,12 +143,13 @@ cmake --build build --parallel ## Текущий статус (MVP) -- [ ] Структура репозитория с субмодулями -- [ ] Абстракция хешей + интеграция Стрибога (из hypericum) -- [ ] Обёртка Merkle дерева (использует IAIK) -- [ ] Пакетная подпись/верификация с нулевой подписью (тестовый режим) -- [ ] Адаптеры для реальных PQC-алгоритмов (в процессе: сначала Гиперикум) -- [ ] Полный набор бенчмарков с результатами +- [x] Структура репозитория с субмодулями +- [x] Адаптеры для Шиповника / Гиперикума / Крыжовника с единым `bb_status` +- [x] Подключены detached/status-return API через wrapper-сабмодули +- [x] Последовательный бенчмарк `bench_seq` с warmup и исправленной метрикой размера подписей +- [x] Покрытие тестами: `test_adapters_smoke`, `test_adapters_batch`, `test_kryzhovnik*`, `test_merkletree` +- [ ] Реализация пакетной подписи/верификации на основе дерева Меркла +- [ ] Сравнение бенчмарков: последовательная vs реальная пакетная подпись - [ ] Финальный отчёт (PDF) **MVP разрабатывается в ветке `dev`. После завершения будет открыт Pull Request в `main` для ревью.** diff --git a/bench/bench_seq.c b/bench/bench_seq.c index 354b6b7..a9677e2 100644 --- a/bench/bench_seq.c +++ b/bench/bench_seq.c @@ -13,6 +13,7 @@ typedef struct bench_config { const char *params; size_t batch_size; size_t iters; + size_t warmup; size_t msg_size; uint64_t seed; int verify; @@ -38,6 +39,7 @@ static void print_usage(const char *prog) { 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(" --warmup Warmup iterations (default: 0)\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"); @@ -101,6 +103,7 @@ static int parse_args(int argc, char **argv, bench_config *cfg) { cfg->params = "default"; cfg->batch_size = 1; cfg->iters = 100; + cfg->warmup = 0; cfg->msg_size = BB_DEFAULT_MESSAGE_SIZE; cfg->seed = 1; cfg->verify = 1; @@ -133,6 +136,12 @@ static int parse_args(int argc, char **argv, bench_config *cfg) { } continue; } + if (strcmp(arg, "--warmup") == 0 && i + 1 < argc) { + if (parse_size(argv[++i], &cfg->warmup) != 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; @@ -183,6 +192,7 @@ static int run_one(const bb_algorithm *algo, const bench_config *cfg, bench_resu size_t sk_len = 0; size_t pk_len = 0; size_t total_sig_bytes = 0; + size_t total_iters; size_t it; bb_timer timer; @@ -207,7 +217,11 @@ static int run_one(const bb_algorithm *algo, const bench_config *cfg, bench_resu out->verify_ms = 0.0; out->serialize_time_ms = 0.0; - for (it = 0; it < cfg->iters; ++it) { + total_iters = cfg->warmup + cfg->iters; + + for (it = 0; it < total_iters; ++it) { + const int measured = it >= cfg->warmup; + size_t batch_sig_bytes = 0; size_t i; for (i = 0; i < cfg->batch_size; ++i) { @@ -216,46 +230,68 @@ static int run_one(const bb_algorithm *algo, const bench_config *cfg, bench_resu 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; + if (measured) { + 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); + } else { + 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; + batch_sig_bytes += sig_len; + + if (measured) { + 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; + if (measured) { + 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); + } else { + 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; - } + if (measured) { + serialized = (uint8_t *)realloc(serialized, batch_sig_bytes == 0 ? 1 : batch_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]; + { + 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); } - bb_timer_stop(&timer); - out->serialize_time_ms += bb_timer_elapsed_ms(&timer); } } @@ -263,7 +299,7 @@ static int run_one(const bb_algorithm *algo, const bench_config *cfg, bench_resu 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); + out->total_sig_size_mb = ((double)total_sig_bytes / (double)cfg->iters) / (1024.0 * 1024.0); free(sk); free(pk); @@ -323,6 +359,7 @@ static int write_json(FILE *f, const bench_result *results, size_t count, " \"params\": \"%s\",\n" " \"batch_size\": %zu,\n" " \"iters\": %zu,\n" + " \"warmup\": %zu,\n" " \"msg_size\": %zu,\n" " \"seed\": %llu,\n" " \"verify\": %d\n" @@ -332,6 +369,7 @@ static int write_json(FILE *f, const bench_result *results, size_t count, cfg->params, cfg->batch_size, cfg->iters, + cfg->warmup, cfg->msg_size, (unsigned long long)cfg->seed, cfg->verify) < 0) { diff --git a/scripts/third_party.sh b/scripts/third_party.sh index 1c28a98..e7915f1 100755 --- a/scripts/third_party.sh +++ b/scripts/third_party.sh @@ -33,6 +33,10 @@ do_install() { echo "[2/3] Initializing/updating git submodules..." git submodule sync --recursive git submodule update --init --recursive + if [ "${THIRD_PARTY_REMOTE_UPDATE:-0}" = "1" ]; then + echo "[2/3] Refreshing submodules from tracked remote branches..." + git submodule update --remote --merge --recursive + fi } do_build() { diff --git a/src/signature/hypericum_adapter.c b/src/signature/hypericum_adapter.c index ff114cf..c56cbf4 100644 --- a/src/signature/hypericum_adapter.c +++ b/src/signature/hypericum_adapter.c @@ -2,9 +2,6 @@ #include "api.h" -#include -#include - bb_status bb_hypericum_keygen( uint8_t *sk, size_t sk_capacity, @@ -39,8 +36,7 @@ bb_status bb_hypericum_sign( uint8_t *sig, size_t sig_capacity, size_t *sig_len) { - unsigned char *sm = NULL; - unsigned long long smlen = 0; + unsigned long long out_sig_len = 0; size_t required; (void)pk; @@ -58,25 +54,16 @@ bb_status bb_hypericum_sign( 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); + if (crypto_sign_detached(sig, &out_sig_len, msg, + (unsigned long long)msg_len, sk) != 0) { return BB_INTERNAL; } - if (smlen < (unsigned long long)CRYPTO_BYTES) { - free(sm); + if (out_sig_len != (unsigned long long)CRYPTO_BYTES) { return BB_INTERNAL; } - memcpy(sig, sm, required); - *sig_len = required; - - free(sm); + *sig_len = (size_t)out_sig_len; return BB_OK; } @@ -87,11 +74,6 @@ bb_status bb_hypericum_verify( 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; } @@ -102,32 +84,10 @@ bb_status bb_hypericum_verify( 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; + if (crypto_sign_verify_detached(sig, (unsigned long long)sig_len, msg, + (unsigned long long)msg_len, pk) != 0) { + return BB_INVALID_SIG; } - status = BB_OK; - -cleanup: - free(sm); - free(recovered); - return status; + return BB_OK; } diff --git a/src/signature/kryzhovnik_adapter.c b/src/signature/kryzhovnik_adapter.c index 8c9e7a4..bfa5da1 100644 --- a/src/signature/kryzhovnik_adapter.c +++ b/src/signature/kryzhovnik_adapter.c @@ -76,6 +76,6 @@ bb_status bb_kryzhovnik_verify( case KRYZHOVNIK_VERIFY_FAIL: return BB_INVALID_SIG; default: - return BB_BAD_ARG; + return BB_INTERNAL; } } diff --git a/src/signature/shipovnik_adapter.c b/src/signature/shipovnik_adapter.c index 2435984..16f5156 100644 --- a/src/signature/shipovnik_adapter.c +++ b/src/signature/shipovnik_adapter.c @@ -17,7 +17,9 @@ bb_status bb_shipovnik_keygen( return BB_BUFFER_TOO_SMALL; } - shipovnik_generate_keys(sk, pk); + if (shipovnik_generate_keys_ex(sk, pk) != 0) { + return BB_INTERNAL; + } *sk_len = (size_t)SHIPOVNIK_SECRETKEYBYTES; *pk_len = (size_t)SHIPOVNIK_PUBLICKEYBYTES; return BB_OK; @@ -48,7 +50,12 @@ bb_status bb_shipovnik_sign( return BB_BUFFER_TOO_SMALL; } - shipovnik_sign(sk, msg, msg_len, sig, &out_sig_len); + if (shipovnik_sign_ex(sk, msg, msg_len, sig, &out_sig_len) != 0) { + return BB_INTERNAL; + } + if (out_sig_len > (size_t)SHIPOVNIK_SIGBYTES) { + return BB_INTERNAL; + } *sig_len = out_sig_len; return BB_OK; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7a4bde4..eacdc28 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,15 @@ -add_executable(adapters_smoke adapters_smoke.c) +add_executable(test_adapters_smoke adapters_smoke.c) -target_link_libraries(adapters_smoke PRIVATE batch_bench_adapters) +target_link_libraries(test_adapters_smoke PRIVATE batch_bench_adapters) -target_compile_features(adapters_smoke PRIVATE c_std_99) +target_compile_features(test_adapters_smoke PRIVATE c_std_99) -add_test(NAME adapters_smoke COMMAND adapters_smoke) +add_test(NAME test_adapters_smoke COMMAND test_adapters_smoke) + +add_executable(test_adapters_batch test_adapters_batch.c) + +target_link_libraries(test_adapters_batch PRIVATE batch_bench_adapters) + +target_compile_features(test_adapters_batch PRIVATE c_std_99) + +add_test(NAME test_adapters_batch COMMAND test_adapters_batch) diff --git a/tests/test_adapters_batch.c b/tests/test_adapters_batch.c new file mode 100644 index 0000000..a22ad52 --- /dev/null +++ b/tests/test_adapters_batch.c @@ -0,0 +1,114 @@ +#include "batch_bench.h" +#include "utils/message_gen.h" + +#include +#include +#include +#include + +#define TEST_BATCH_SIZE 10 + +static int test_algorithm_batch(const bb_algorithm *algo) { + uint8_t *sk = NULL; + uint8_t *pk = NULL; + uint8_t *msgs = NULL; + uint8_t *sigs = NULL; + size_t *sig_lens = NULL; + size_t sk_len = 0; + size_t pk_len = 0; + size_t i; + + sk = (uint8_t *)malloc(algo->secret_key_bytes); + pk = (uint8_t *)malloc(algo->public_key_bytes); + msgs = (uint8_t *)malloc(TEST_BATCH_SIZE * BB_DEFAULT_MESSAGE_SIZE); + sigs = (uint8_t *)malloc(TEST_BATCH_SIZE * algo->signature_bytes); + sig_lens = (size_t *)calloc(TEST_BATCH_SIZE, sizeof(size_t)); + + if (sk == NULL || pk == NULL || msgs == NULL || sigs == NULL || sig_lens == NULL) { + free(sk); + free(pk); + free(msgs); + free(sigs); + free(sig_lens); + 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(msgs); + free(sigs); + free(sig_lens); + return 1; + } + + for (i = 0; i < TEST_BATCH_SIZE; ++i) { + uint8_t *msg = msgs + i * BB_DEFAULT_MESSAGE_SIZE; + uint8_t *sig = sigs + i * algo->signature_bytes; + + bb_fill_message(msg, BB_DEFAULT_MESSAGE_SIZE, 7, i); + + if (algo->sign(sk, sk_len, pk, pk_len, + msg, BB_DEFAULT_MESSAGE_SIZE, + sig, algo->signature_bytes, + &sig_lens[i]) != BB_OK) { + free(sk); + free(pk); + free(msgs); + free(sigs); + free(sig_lens); + return 1; + } + + if (algo->verify(pk, pk_len, + msg, BB_DEFAULT_MESSAGE_SIZE, + sig, sig_lens[i]) != BB_OK) { + free(sk); + free(pk); + free(msgs); + free(sigs); + free(sig_lens); + return 1; + } + } + + for (i = 0; i < TEST_BATCH_SIZE; ++i) { + uint8_t *msg = msgs + i * BB_DEFAULT_MESSAGE_SIZE; + uint8_t *sig = sigs + i * algo->signature_bytes; + + msg[0] ^= 0x01U; + if (algo->verify(pk, pk_len, + msg, BB_DEFAULT_MESSAGE_SIZE, + sig, sig_lens[i]) == BB_OK) { + free(sk); + free(pk); + free(msgs); + free(sigs); + free(sig_lens); + return 1; + } + msg[0] ^= 0x01U; + } + + free(sk); + free(pk); + free(msgs); + free(sigs); + free(sig_lens); + return 0; +} + +int main(void) { + size_t i; + + for (i = 0; i < bb_algorithm_count(); ++i) { + const bb_algorithm *algo = bb_algorithm_at(i); + if (test_algorithm_batch(algo) != 0) { + return 1; + } + } + + printf("test_adapters_batch: ok\n"); + return 0; +} diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 85714cf..c653714 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -189,21 +189,21 @@ if(EXISTS "${KRYZHOVNIK_DIR}/sign.c") target_compile_features(kryzhovnik PRIVATE c_std_99) if(BUILD_TESTING AND EXISTS "${KRYZHOVNIK_DIR}/pqs_test.c") - add_executable(kryzhovnik_test ${KRYZHOVNIK_DIR}/pqs_test.c) - target_link_libraries(kryzhovnik_test PRIVATE kryzhovnik) - add_test(NAME kryzhovnik_test COMMAND kryzhovnik_test) + add_executable(test_kryzhovnik ${KRYZHOVNIK_DIR}/pqs_test.c) + target_link_libraries(test_kryzhovnik PRIVATE kryzhovnik) + add_test(NAME test_kryzhovnik COMMAND test_kryzhovnik) endif() if(BUILD_TESTING AND EXISTS "${KRYZHOVNIK_DIR}/test_wrapper.c") - add_executable(kryzhovnik_wrapper_test ${KRYZHOVNIK_DIR}/test_wrapper.c) - target_link_libraries(kryzhovnik_wrapper_test PRIVATE kryzhovnik) - add_test(NAME kryzhovnik_wrapper_test COMMAND kryzhovnik_wrapper_test) + add_executable(test_kryzhovnik_wrapper ${KRYZHOVNIK_DIR}/test_wrapper.c) + target_link_libraries(test_kryzhovnik_wrapper PRIVATE kryzhovnik) + add_test(NAME test_kryzhovnik_wrapper COMMAND test_kryzhovnik_wrapper) endif() if(BUILD_TESTING AND EXISTS "${KRYZHOVNIK_DIR}/test_pqs.c") - add_executable(kryzhovnik_api_test ${KRYZHOVNIK_DIR}/test_pqs.c) - target_link_libraries(kryzhovnik_api_test PRIVATE kryzhovnik) - add_test(NAME kryzhovnik_api_test COMMAND kryzhovnik_api_test) + add_executable(test_kryzhovnik_api ${KRYZHOVNIK_DIR}/test_pqs.c) + target_link_libraries(test_kryzhovnik_api PRIVATE kryzhovnik) + add_test(NAME test_kryzhovnik_api COMMAND test_kryzhovnik_api) endif() else() message(WARNING "kryzhovnik submodule not initialized (run 'git submodule update --init')") @@ -233,10 +233,10 @@ if(EXISTS "${MERKLE_DIR}/src/merkletree.h") find_package(CppUnit REQUIRED) endif() if(CPPUNIT_FOUND) - add_executable(merkletree_test ${MERKLE_DIR}/tests/MerkleTreeTest.cpp) - target_include_directories(merkletree_test PRIVATE ${CPPUNIT_INCLUDE_DIRS}) - target_link_libraries(merkletree_test PRIVATE merkletree ${CPPUNIT_LIBRARIES}) - add_test(NAME merkletree_test COMMAND merkletree_test) + add_executable(test_merkletree ${MERKLE_DIR}/tests/MerkleTreeTest.cpp) + target_include_directories(test_merkletree PRIVATE ${CPPUNIT_INCLUDE_DIRS}) + target_link_libraries(test_merkletree PRIVATE merkletree ${CPPUNIT_LIBRARIES}) + add_test(NAME test_merkletree COMMAND test_merkletree) else() message(WARNING "CppUnit not found, skipping merkletree tests") endif() diff --git a/third_party/README.md b/third_party/README.md index 2a88ed6..45dc943 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -2,13 +2,24 @@ This directory contains git submodules for cryptographic primitives and Merkle tree logic used in batch-pqc. -- shipovnik/ → https://github.com/QAPP-tech/shipovnik_tc26 -- hypericum/ → https://github.com/QAPP-tech/hypericum_tc26 +Original algorithm repositories: + +- Shipovnik: https://github.com/QAPP-tech/shipovnik_tc26 +- Hypericum: https://github.com/QAPP-tech/hypericum_tc26 +- Kryzhovnik: https://github.com/ElenaKirshanova/pqc_LWR_signature + +Current submodule remotes used by this project: + +- shipovnik/ → https://github.com/cherninkiy/shipovnik-wrapper-tc26 +- hypericum/ → https://github.com/cherninkiy/hypericum-wrapper-tc26 - kryzhovnik/ → https://github.com/cherninkiy/kryzhovnik-wrapper-tc26 - iaik_merkle_tree/ → https://github.com/IAIK/merkle-tree -The original Kryzhovnik repository is https://github.com/ElenaKirshanova/pqc_LWR_signature. -This project uses a compatibility fork for the `third_party/kryzhovnik` submodule to keep compatibility with hypericum and shipovnik. +Fork rationale: + +- Add integration-oriented APIs (detached/status-return wrappers) +- Keep adapter contracts consistent across all algorithms +- Preserve reproducible pinned revisions for CI and local builds To initialize all submodules: diff --git a/third_party/hypericum b/third_party/hypericum index f3f2540..223ef2a 160000 --- a/third_party/hypericum +++ b/third_party/hypericum @@ -1 +1 @@ -Subproject commit f3f254038e5539132d112e8f97332a2a351131fe +Subproject commit 223ef2a32ffc155c0acfa1ec208210c8f2fa3b6d diff --git a/third_party/shipovnik b/third_party/shipovnik index a9139ef..3df4c16 160000 --- a/third_party/shipovnik +++ b/third_party/shipovnik @@ -1 +1 @@ -Subproject commit a9139ef6178a6dfebac3ae328817a361f0e85256 +Subproject commit 3df4c163253d75a4085be17ba05ffb40f68bc68f