From 9a991cb1e9ea12d3222a3ede5a7785340835c965 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 28 Jan 2024 14:52:07 +0400 Subject: [PATCH 1/5] add dudect as git submodule based dependency Signed-off-by: Anjan Roy --- .gitmodules | 3 +++ dudect | 1 + 2 files changed, 4 insertions(+) create mode 160000 dudect diff --git a/.gitmodules b/.gitmodules index 8e85d6c..d3ed2b6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "gtest-parallel"] path = gtest-parallel url = https://github.com/google/gtest-parallel.git +[submodule "dudect"] + path = dudect + url = https://github.com/oreparaz/dudect.git diff --git a/dudect b/dudect new file mode 160000 index 0000000..a18fdee --- /dev/null +++ b/dudect @@ -0,0 +1 @@ +Subproject commit a18fdee2386b63466502e9cb273cb14226679b4b From e0641e122f17ea795b83df17d64b57cbbe50bed1 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 28 Jan 2024 21:02:29 +0400 Subject: [PATCH 2/5] add timing leakage detection tests for sphincs+-128f-simple key generation algorithm Signed-off-by: Anjan Roy --- .../test_sphincs+_128f_simple_keygen.cpp | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/dudect/test_sphincs+_128f_simple_keygen.cpp diff --git a/tests/dudect/test_sphincs+_128f_simple_keygen.cpp b/tests/dudect/test_sphincs+_128f_simple_keygen.cpp new file mode 100644 index 0000000..2cb5def --- /dev/null +++ b/tests/dudect/test_sphincs+_128f_simple_keygen.cpp @@ -0,0 +1,78 @@ +#include "sphincs+_128f_simple.hpp" +#include + +#define DUDECT_IMPLEMENTATION +#define DUDECT_VISIBLITY_STATIC +#include "dudect.h" + +uint8_t +do_one_computation(uint8_t* const data) +{ + constexpr size_t doff0 = 0; + constexpr size_t doff1 = doff0 + sphincs_plus_128f_simple::n; + constexpr size_t doff2 = doff1 + sphincs_plus_128f_simple::n; + constexpr size_t doff3 = doff2 + sphincs_plus_128f_simple::n; + + auto sk_seed = std::span(data + doff0, doff1 - doff0); + auto sk_prf = std::span(data + doff1, doff2 - doff1); + auto pk_seed = std::span(data + doff2, doff3 - doff2); + + std::array skey{}; + std::array pkey{}; + + uint8_t ret_val = 0; + + sphincs_plus_128f_simple::keygen(sk_seed, sk_prf, pk_seed, skey, pkey); + ret_val ^= (skey[0] ^ skey[skey.size() - 1]) ^ (pkey[0] ^ pkey[pkey.size() - 1]); + + return ret_val; +} + +void +prepare_inputs(dudect_config_t* const c, uint8_t* const input_data, uint8_t* const classes) +{ + randombytes(input_data, c->number_measurements * c->chunk_size); + + for (size_t i = 0; i < c->number_measurements; i++) { + classes[i] = randombit(); + if (classes[i] == 0) { + std::memset(input_data + i * c->chunk_size, 0x00, c->chunk_size); + } + } +} + +dudect_state_t +test_sphincs_plus_128f_simple_keygen() +{ + constexpr size_t chunk_size = sphincs_plus_128f_simple::n + // Secret Key Seed + sphincs_plus_128f_simple::n + // Secret Key PRF + sphincs_plus_128f_simple::n; // Public Key Seed + constexpr size_t number_measurements = 1e5; + + dudect_config_t config = { + chunk_size, + number_measurements, + }; + dudect_ctx_t ctx; + dudect_init(&ctx, &config); + + dudect_state_t state = DUDECT_NO_LEAKAGE_EVIDENCE_YET; + while (state == DUDECT_NO_LEAKAGE_EVIDENCE_YET) { + state = dudect_main(&ctx); + } + + dudect_free(&ctx); + + printf("Detected timing leakage in \"%s\", defined in file \"%s\"\n", __func__, __FILE_NAME__); + return state; +} + +int +main() +{ + if (test_sphincs_plus_128f_simple_keygen() != DUDECT_NO_LEAKAGE_EVIDENCE_YET) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} From 9b2e24394ee913d323aa09ec4fba50c9db3c99e6 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 28 Jan 2024 23:00:17 +0400 Subject: [PATCH 3/5] add timing leakage detection tests for sphincs+-128f-simple signing algorithm Signed-off-by: Anjan Roy --- .../dudect/test_sphincs+_128f_simple_sign.cpp | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/dudect/test_sphincs+_128f_simple_sign.cpp diff --git a/tests/dudect/test_sphincs+_128f_simple_sign.cpp b/tests/dudect/test_sphincs+_128f_simple_sign.cpp new file mode 100644 index 0000000..0fd25a0 --- /dev/null +++ b/tests/dudect/test_sphincs+_128f_simple_sign.cpp @@ -0,0 +1,95 @@ +#include "prng.hpp" +#include "sphincs+_128f_simple.hpp" +#include + +#define DUDECT_IMPLEMENTATION +#define DUDECT_VISIBLITY_STATIC +#include "dudect.h" + +constexpr size_t MSG_BYTE_LEN = 64; + +uint8_t +do_one_computation(uint8_t* const data) +{ + constexpr size_t doff0 = 0; + constexpr size_t doff1 = doff0 + MSG_BYTE_LEN; + constexpr size_t doff2 = doff1 + sphincs_plus_128f_simple::SecKeyLen; + constexpr size_t doff3 = doff2 + sphincs_plus_128f_simple::n; + + auto msg = std::span(data + doff0, doff1 - doff0); + auto skey = std::span(data + doff1, doff2 - doff1); + auto rand_bytes = std::span(data + doff2, doff3 - doff2); + + std::array sig{}; + + uint8_t ret_val = 0; + + sphincs_plus_128f_simple::sign(msg, skey, rand_bytes, sig); + ret_val ^= sig[0] ^ sig[sig.size() - 1]; + + return ret_val; +} + +void +prepare_inputs(dudect_config_t* const c, uint8_t* const input_data, uint8_t* const classes) +{ + randombytes(input_data, c->number_measurements * c->chunk_size); + + for (size_t i = 0; i < c->number_measurements; i++) { + classes[i] = randombit(); + if (classes[i] == 0) { + const size_t chunk_off = i * c->chunk_size; + const size_t skey_begin = chunk_off + MSG_BYTE_LEN; + + std::array sk_seed{}; + std::array sk_prf{}; + std::array pk_seed{}; + auto skey = std::span(input_data + skey_begin, sphincs_plus_128f_simple::SecKeyLen); + std::array pkey{}; + + prng::prng_t prng; + + prng.read(sk_seed); + prng.read(sk_prf); + prng.read(pk_seed); + + sphincs_plus_128f_simple::keygen(sk_seed, sk_prf, pk_seed, skey, pkey); + } + } +} + +dudect_state_t +test_sphincs_plus_128f_simple_sign() +{ + constexpr size_t chunk_size = MSG_BYTE_LEN + // Message to be signed + sphincs_plus_128f_simple::SecKeyLen + // Secret key used for signing the message + sphincs_plus_128f_simple::n; // Random bytes used for randomized message signing + constexpr size_t number_measurements = 1e4; + + dudect_config_t config = { + chunk_size, + number_measurements, + }; + dudect_ctx_t ctx; + dudect_init(&ctx, &config); + + dudect_state_t state = DUDECT_NO_LEAKAGE_EVIDENCE_YET; + while (state == DUDECT_NO_LEAKAGE_EVIDENCE_YET) { + state = dudect_main(&ctx); + } + + dudect_free(&ctx); + + printf("Detected timing leakage in \"%s\", defined in file \"%s\"\n", __func__, __FILE_NAME__); + return state; +} + +int +main() +{ + if (test_sphincs_plus_128f_simple_sign() != DUDECT_NO_LEAKAGE_EVIDENCE_YET) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} From b6ada0103832e1a0f2dda25c1626948b4407757c Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sun, 28 Jan 2024 23:00:34 +0400 Subject: [PATCH 4/5] add Make recipes for ease of building timing leakage detection tests Signed-off-by: Anjan Roy --- Makefile | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fffd24c..8a640fb 100644 --- a/Makefile +++ b/Makefile @@ -7,20 +7,26 @@ ASAN_FLAGS = -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsaniti UBSAN_FLAGS = -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=undefined # From https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html SHA3_INC_DIR = ./sha3/include +DUDECT_INC_DIR = ./dudect/src I_FLAGS = -I ./include DEP_IFLAGS = -I $(SHA3_INC_DIR) +DUDECT_DEP_IFLAGS = $(DEP_IFLAGS) -I $(DUDECT_INC_DIR) SRC_DIR = include SPHINCS+_SOURCES := $(wildcard $(SRC_DIR)/*.hpp) BUILD_DIR = build ASAN_BUILD_DIR = $(BUILD_DIR)/asan UBSAN_BUILD_DIR = $(BUILD_DIR)/ubsan +DUDECT_BUILD_DIR = $(BUILD_DIR)/dudect TEST_DIR = tests +DUDECT_TEST_DIR = $(TEST_DIR)/dudect TEST_SOURCES := $(wildcard $(TEST_DIR)/*.cpp) TEST_OBJECTS := $(addprefix $(BUILD_DIR)/, $(notdir $(patsubst %.cpp,%.o,$(TEST_SOURCES)))) ASAN_TEST_OBJECTS := $(addprefix $(ASAN_BUILD_DIR)/, $(notdir $(patsubst %.cpp,%.o,$(TEST_SOURCES)))) UBSAN_TEST_OBJECTS := $(addprefix $(UBSAN_BUILD_DIR)/, $(notdir $(patsubst %.cpp,%.o,$(TEST_SOURCES)))) +DUDECT_TEST_SOURCES := $(wildcard $(DUDECT_TEST_DIR)/*.cpp) +DUDECT_TEST_BINARIES := $(addprefix $(DUDECT_BUILD_DIR)/, $(notdir $(patsubst %.cpp,%.out,$(DUDECT_TEST_SOURCES)))) TEST_LINK_FLAGS = -lgtest -lgtest_main TEST_BINARY = $(BUILD_DIR)/test.out ASAN_TEST_BINARY = $(ASAN_BUILD_DIR)/test.out @@ -48,12 +54,18 @@ $(ASAN_BUILD_DIR): $(BUILD_DIR) $(UBSAN_BUILD_DIR): $(BUILD_DIR) mkdir -p $@ +$(DUDECT_BUILD_DIR): $(BUILD_DIR) + mkdir -p $@ + $(SHA3_INC_DIR): git submodule update --init $(GTEST_PARALLEL): $(SHA3_INC_DIR) git submodule update --init +$(DUDECT_INC_DIR): $(GTEST_PARALLEL) + git submodule update --init + $(BUILD_DIR)/%.o: $(TEST_DIR)/%.cpp $(BUILD_DIR) $(SHA3_INC_DIR) $(CXX) $(CXX_FLAGS) $(WARN_FLAGS) $(OPT_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@ @@ -72,6 +84,9 @@ $(ASAN_TEST_BINARY): $(ASAN_TEST_OBJECTS) $(UBSAN_TEST_BINARY): $(UBSAN_TEST_OBJECTS) $(CXX) $(UBSAN_FLAGS) $^ $(TEST_LINK_FLAGS) -o $@ +$(DUDECT_BUILD_DIR)/%.out: $(DUDECT_TEST_DIR)/%.cpp $(DUDECT_BUILD_DIR) $(SHA3_INC_DIR) $(DUDECT_INC_DIR) + $(CXX) $(CXX_FLAGS) $(WARN_FLAGS) $(OPT_FLAGS) $(I_FLAGS) $(DUDECT_DEP_IFLAGS) -lm $(LINK_FLAGS) $< -o $@ + test: $(TEST_BINARY) $(GTEST_PARALLEL) $(GTEST_PARALLEL) $< --print_test_times @@ -81,6 +96,8 @@ asan_test: $(ASAN_TEST_BINARY) $(GTEST_PARALLEL) ubsan_test: $(UBSAN_TEST_BINARY) $(GTEST_PARALLEL) $(GTEST_PARALLEL) $< --print_test_times +dudect_test_build: $(DUDECT_TEST_BINARIES) + $(BUILD_DIR)/%.o: $(BENCHMARK_DIR)/%.cpp $(BUILD_DIR) $(SHA3_INC_DIR) $(CXX) $(CXX_FLAGS) $(WARN_FLAGS) $(OPT_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@ @@ -103,5 +120,5 @@ perf: $(PERF_BINARY) clean: rm -rf $(BUILD_DIR) -format: $(SPHINCS+_SOURCES) $(TEST_SOURCES) $(BENCHMARK_SOURCES) $(BENCHMARK_HEADERS) +format: $(SPHINCS+_SOURCES) $(TEST_SOURCES) $(DUDECT_TEST_SOURCES) $(BENCHMARK_SOURCES) $(BENCHMARK_HEADERS) clang-format -i $^ From 4bf4b2ccc8d92816ee548afacf4faab6bf588f1d Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Wed, 31 Jan 2024 17:56:16 +0400 Subject: [PATCH 5/5] mention about `dudect` -based timing leakage detection tests in the README Signed-off-by: Anjan Roy --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 615a5a0..6953ece 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ > [!CAUTION] -> This Sphincs+ implementation is conformant with the Sphincs+ [specification](https://sphincs.org/data/sphincs+-r3.1-specification.pdf) and I also try to make it constant-time but be informed that it is not yet audited. **If you consider using it in production, be careful !** +> This Sphincs+ implementation is conformant with Sphincs+ specification @ https://sphincs.org/data/sphincs+-r3.1-specification.pdf. I also try to make it timing leakage free, using `dudect` (see https://github.com/oreparaz/dudect) -based tests, but be informed that this implementation is not yet audited. *If you consider using it in production, be careful !* # sphincs-plus SPHINCS+: Stateless Hash-based Digital Signature Algorithm @@ -73,7 +73,9 @@ For ensuring that SPHINCS+ implementation is functionally correct and compliant > This implementation of SPHINCS+ specification is **tested** to be compatible and conformant with r3.1 of the specification. That's ensured by generating known answer tests ( KATs ) following https://gist.github.com/itzmeanjan/d483872509b8a1a7c4d6614ec9d43e6c and testing this implementation using those test vectors. ```bash -make -j +make -j # Run tests without any sort of sanitizers +make asan_test -j # Run tests with AddressSanitizer enabled +make ubsan_test -j # Run tests with UndefinedBehaviourSanitizer enabled ``` ```bash @@ -108,6 +110,45 @@ PASSED TESTS (27/27): 592956 ms: build/test.out SphincsPlus.SphincsPlus192sRobustKnownAnswerTests ``` +You can run timing leakage tests, using `dudect`; execute following + +> [!NOTE] +> `dudect` is integrated into this library implementation of Sphincs+ DSA to find any sort of timing leakages. It checks for constant-timeness of most of both `keygen` and `sign` function implementations, for only one variant i.e. **128f-simple**. + +```bash +# Can only be built and run on x86_64 machine. +make dudect_test_build -j + +# Before running the constant-time tests, it's a good idea to put all CPU cores on "performance" mode. +# You may find the guide @ https://github.com/google/benchmark/blob/main/docs/reducing_variance.md helpful. + +# Given Sphincs+ is slow, compared to Dilithium/ Falcon, following tests are required to be run +# for longer, so that we can collect enough execution timing samples. +timeout 2h taskset -c 0 ./build/dudect/test_sphincs+_128f_simple_keygen.out +timeout 2h taskset -c 0 ./build/dudect/test_sphincs+_128f_simple_sign.out +``` + +> [!TIP] +> `dudect` documentation says if `t` statistic is `< 10`, we're *probably* good, yes **probably**. You may want to read `dudect` documentation @ https://github.com/oreparaz/dudect. Also you might find the original paper @ https://ia.cr/2016/1123 interesting. + +```bash +... +meas: 0.69 M, max t: +2.58, max tau: 3.11e-03, (5/tau)^2: 2.58e+06. For the moment, maybe constant time. +meas: 0.70 M, max t: +2.74, max tau: 3.27e-03, (5/tau)^2: 2.34e+06. For the moment, maybe constant time. +meas: 0.71 M, max t: +2.73, max tau: 3.24e-03, (5/tau)^2: 2.38e+06. For the moment, maybe constant time. +meas: 0.72 M, max t: +2.62, max tau: 3.09e-03, (5/tau)^2: 2.61e+06. For the moment, maybe constant time. +meas: 0.73 M, max t: +2.66, max tau: 3.11e-03, (5/tau)^2: 2.58e+06. For the moment, maybe constant time. +meas: 0.74 M, max t: +2.70, max tau: 3.14e-03, (5/tau)^2: 2.53e+06. For the moment, maybe constant time. +meas: 0.75 M, max t: +2.62, max tau: 3.03e-03, (5/tau)^2: 2.72e+06. For the moment, maybe constant time. +meas: 0.76 M, max t: +2.60, max tau: 2.99e-03, (5/tau)^2: 2.80e+06. For the moment, maybe constant time. +meas: 0.77 M, max t: +2.62, max tau: 2.99e-03, (5/tau)^2: 2.80e+06. For the moment, maybe constant time. +meas: 0.78 M, max t: +2.52, max tau: 2.85e-03, (5/tau)^2: 3.07e+06. For the moment, maybe constant time. +meas: 0.79 M, max t: +2.57, max tau: 2.89e-03, (5/tau)^2: 3.00e+06. For the moment, maybe constant time. +meas: 0.80 M, max t: +2.51, max tau: 2.81e-03, (5/tau)^2: 3.18e+06. For the moment, maybe constant time. +meas: 0.81 M, max t: +2.49, max tau: 2.77e-03, (5/tau)^2: 3.25e+06. For the moment, maybe constant time. +meas: 0.82 M, max t: +2.52, max tau: 2.78e-03, (5/tau)^2: 3.23e+06. For the moment, maybe constant time. +``` + ## Benchmarking Benchmarking key generation, signing and verification algorithms for various instantiations of SPHINCS+ digital signature scheme can be done, by issuing