From b8d890f4104ab418d727325ce5dc5ca6e9e72eea Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 21 Feb 2026 10:31:22 +0400 Subject: [PATCH 1/8] Get rid of git submodule based dependencies Signed-off-by: Anjan Roy --- .gitmodules | 6 ------ gtest-parallel | 1 - sha3 | 1 - 3 files changed, 8 deletions(-) delete mode 100644 .gitmodules delete mode 160000 gtest-parallel delete mode 160000 sha3 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 4c1ec8f..0000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "sha3"] - path = sha3 - url = https://github.com/itzmeanjan/sha3.git -[submodule "gtest-parallel"] - path = gtest-parallel - url = https://github.com/google/gtest-parallel diff --git a/gtest-parallel b/gtest-parallel deleted file mode 160000 index cd488bd..0000000 --- a/gtest-parallel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cd488bdedc1d2cffb98201a17afc1b298b0b90f1 diff --git a/sha3 b/sha3 deleted file mode 160000 index bd911ad..0000000 --- a/sha3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bd911ad3e658b7798b308a3b668c635c703c7ec4 From 90dd808070de991cc2ba62a0377d5590310a1fec Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 21 Feb 2026 11:18:41 +0400 Subject: [PATCH 2/8] Remove all makefile Signed-off-by: Anjan Roy --- Makefile | 44 ---------------------- benches/bench.mk | 31 --------------- examples/example.mk | 15 -------- tests/test.mk | 91 --------------------------------------------- 4 files changed, 181 deletions(-) delete mode 100644 Makefile delete mode 100644 benches/bench.mk delete mode 100644 examples/example.mk delete mode 100644 tests/test.mk diff --git a/Makefile b/Makefile deleted file mode 100644 index 96d0797..0000000 --- a/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -.DEFAULT_GOAL := help - -# Collects inspiration from https://github.com/0xPolygonMiden/crypto/blob/3909b0199368b13fdfa934a324f984572d521e39/Makefile#L1-L5 -# and https://github.com/gtramontina/sourcing/blob/853252ee184c16bc69dd53e8457107d718aca04f/Makefile#L68-L72 -.PHONY: help -help: - @for file in $(MAKEFILE_LIST); do \ - grep -E '^[a-zA-Z_-]+:.*?## .*$$' $${file} | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}';\ - done - - -CXX ?= clang++ -CXX_DEFS += -CXX_FLAGS := -std=c++20 -WARN_FLAGS := -Wall -Wextra -Wpedantic -DEBUG_FLAGS := -O1 -g -RELEASE_FLAGS := -O3 -march=native -LINK_OPT_FLAGS := -flto - -I_FLAGS := -I ./include -SHA3_INC_DIR := ./sha3/include -DEP_IFLAGS := -I $(SHA3_INC_DIR) - -SRC_DIR := include -RANDOMSHAKE_SOURCES := $(shell find $(SRC_DIR) -name '*.hpp') -BUILD_DIR := build - -include tests/test.mk -include benches/bench.mk -include examples/example.mk - -$(SHA3_INC_DIR): - git submodule update --init sha3 - -$(GTEST_PARALLEL): $(SHA3_INC_DIR) - git submodule update --init gtest-parallel - -.PHONY: clean -clean: ## Remove build directory - rm -rf $(BUILD_DIR) - -.PHONY: format -format: $(RANDOMSHAKE_SOURCES) $(TEST_SOURCES) $(TEST_HEADERS) $(BENCHMARK_SOURCES) $(BENCHMARK_HEADERS) $(EXAMPLE_SOURCES) $(EXAMPLE_HEADERS) ## Format source code - clang-format -i $^ diff --git a/benches/bench.mk b/benches/bench.mk deleted file mode 100644 index 8a16777..0000000 --- a/benches/bench.mk +++ /dev/null @@ -1,31 +0,0 @@ -BENCHMARK_BUILD_DIR := $(BUILD_DIR)/benchmark - -BENCHMARK_DIR := benches -BENCHMARK_SOURCES := $(wildcard $(BENCHMARK_DIR)/*.cpp) -BENCHMARK_HEADERS := $(wildcard $(BENCHMARK_DIR)/*.hpp) -BENCHMARK_OBJECTS := $(addprefix $(BENCHMARK_BUILD_DIR)/, $(notdir $(BENCHMARK_SOURCES:.cpp=.o))) -BENCHMARK_LINK_FLAGS := -lbenchmark -lbenchmark_main -lpthread -BENCHMARK_BINARY := $(BENCHMARK_BUILD_DIR)/bench.out -PERF_LINK_FLAGS := -lbenchmark -lbenchmark_main -lpfm -lpthread -PERF_BINARY := $(BENCHMARK_BUILD_DIR)/perf.out -BENCHMARK_OUT_FILE := bench_result_on_$(shell uname -s)_$(shell uname -r)_$(shell uname -m)_with_$(CXX)_$(shell $(CXX) -dumpversion).json - -$(BENCHMARK_BUILD_DIR): - mkdir -p $@ - -$(BENCHMARK_BUILD_DIR)/%.o: $(BENCHMARK_DIR)/%.cpp $(BENCHMARK_BUILD_DIR) $(SHA3_INC_DIR) - $(CXX) $(CXX_DEFS) $(CXX_FLAGS) $(WARN_FLAGS) $(RELEASE_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@ - -$(BENCHMARK_BINARY): $(BENCHMARK_OBJECTS) - $(CXX) $(RELEASE_FLAGS) $(LINK_OPT_FLAGS) $^ $(BENCHMARK_LINK_FLAGS) -o $@ - -benchmark: $(BENCHMARK_BINARY) ## Build and run all benchmarks, without libPFM -based CPU CYCLE counter statistics - # Must *not* build google-benchmark with libPFM - ./$< --benchmark_min_warmup_time=.1 --benchmark_enable_random_interleaving=false --benchmark_repetitions=10 --benchmark_min_time=0.1s --benchmark_display_aggregates_only=true --benchmark_report_aggregates_only=true --benchmark_counters_tabular=true --benchmark_out_format=json --benchmark_out=$(BENCHMARK_OUT_FILE) - -$(PERF_BINARY): $(BENCHMARK_OBJECTS) - $(CXX) $(RELEASE_FLAGS) $(LINK_OPT_FLAGS) $^ $(PERF_LINK_FLAGS) -o $@ - -perf: $(PERF_BINARY) ## Build and run all benchmarks, while also collecting libPFM -based CPU CYCLE counter statistics - # Must build google-benchmark with libPFM, follow https://gist.github.com/itzmeanjan/05dc3e946f635d00c5e0b21aae6203a7 - ./$< --benchmark_min_warmup_time=.1 --benchmark_enable_random_interleaving=false --benchmark_repetitions=10 --benchmark_min_time=0.1s --benchmark_display_aggregates_only=true --benchmark_report_aggregates_only=true --benchmark_counters_tabular=true --benchmark_perf_counters=CYCLES --benchmark_out_format=json --benchmark_out=$(BENCHMARK_OUT_FILE) diff --git a/examples/example.mk b/examples/example.mk deleted file mode 100644 index fbdb439..0000000 --- a/examples/example.mk +++ /dev/null @@ -1,15 +0,0 @@ -EXAMPLE_BUILD_DIR := $(BUILD_DIR)/example - -EXAMPLE_DIR := examples -EXAMPLE_SOURCES := $(wildcard $(EXAMPLE_DIR)/*.cpp) -EXAMPLE_HEADERS := $(wildcard $(EXAMPLE_DIR)/*.hpp) -EXAMPLE_EXECS := $(addprefix $(EXAMPLE_BUILD_DIR)/, $(notdir $(EXAMPLE_SOURCES:.cpp=.exe))) - -$(EXAMPLE_BUILD_DIR): - mkdir -p $@ - -$(EXAMPLE_BUILD_DIR)/%.exe: $(EXAMPLE_DIR)/%.cpp $(EXAMPLE_BUILD_DIR) $(SHA3_INC_DIR) - $(CXX) $(CXX_DEFS) $(CXX_FLAGS) $(WARN_FLAGS) $(RELEASE_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) $< -o $@ - -example: $(EXAMPLE_EXECS) ## Build and run all example programs, demonstrating usage of RandomShake CSPRNG API - $(foreach exec,$^,./$(exec); echo "--- --- ---";) diff --git a/tests/test.mk b/tests/test.mk deleted file mode 100644 index c136b6d..0000000 --- a/tests/test.mk +++ /dev/null @@ -1,91 +0,0 @@ -ASAN_FLAGS := -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address # From https://clang.llvm.org/docs/AddressSanitizer.html -DEBUG_ASAN_FLAGS := $(DEBUG_FLAGS) $(ASAN_FLAGS) -RELEASE_ASAN_FLAGS := -g $(RELEASE_FLAGS) $(ASAN_FLAGS) -UBSAN_FLAGS := -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=undefined # From https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html -DEBUG_UBSAN_FLAGS := $(DEBUG_FLAGS) $(UBSAN_FLAGS) -RELEASE_UBSAN_FLAGS := -g $(RELEASE_FLAGS) $(UBSAN_FLAGS) - -TEST_BUILD_DIR := $(BUILD_DIR)/test -ASAN_BUILD_DIR := $(BUILD_DIR)/asan -DEBUG_ASAN_BUILD_DIR := $(ASAN_BUILD_DIR)/debug -RELEASE_ASAN_BUILD_DIR := $(ASAN_BUILD_DIR)/release -UBSAN_BUILD_DIR := $(BUILD_DIR)/ubsan -DEBUG_UBSAN_BUILD_DIR := $(UBSAN_BUILD_DIR)/debug -RELEASE_UBSAN_BUILD_DIR := $(UBSAN_BUILD_DIR)/release - -TEST_DIR := tests -TEST_SOURCES := $(wildcard $(TEST_DIR)/*.cpp) -TEST_HEADERS := $(wildcard $(TEST_DIR)/*.hpp) -TEST_OBJECTS := $(addprefix $(TEST_BUILD_DIR)/, $(notdir $(TEST_SOURCES:.cpp=.o))) -TEST_BINARY := $(TEST_BUILD_DIR)/test.out -TEST_LINK_FLAGS := -lgtest -lgtest_main -GTEST_PARALLEL := ./gtest-parallel/gtest-parallel - -DEBUG_ASAN_TEST_OBJECTS := $(addprefix $(DEBUG_ASAN_BUILD_DIR)/, $(notdir $(TEST_SOURCES:.cpp=.o))) -RELEASE_ASAN_TEST_OBJECTS := $(addprefix $(RELEASE_ASAN_BUILD_DIR)/, $(notdir $(TEST_SOURCES:.cpp=.o))) -DEBUG_ASAN_TEST_BINARY := $(DEBUG_ASAN_BUILD_DIR)/test.out -RELEASE_ASAN_TEST_BINARY := $(RELEASE_ASAN_BUILD_DIR)/test.out -DEBUG_UBSAN_TEST_OBJECTS := $(addprefix $(DEBUG_UBSAN_BUILD_DIR)/, $(notdir $(TEST_SOURCES:.cpp=.o))) -RELEASE_UBSAN_TEST_OBJECTS := $(addprefix $(RELEASE_UBSAN_BUILD_DIR)/, $(notdir $(TEST_SOURCES:.cpp=.o))) -DEBUG_UBSAN_TEST_BINARY := $(DEBUG_UBSAN_BUILD_DIR)/test.out -RELEASE_UBSAN_TEST_BINARY := $(RELEASE_UBSAN_BUILD_DIR)/test.out - -$(DEBUG_ASAN_BUILD_DIR): - mkdir -p $@ - -$(RELEASE_ASAN_BUILD_DIR): - mkdir -p $@ - -$(DEBUG_UBSAN_BUILD_DIR): - mkdir -p $@ - -$(RELEASE_UBSAN_BUILD_DIR): - mkdir -p $@ - -$(TEST_BUILD_DIR): - mkdir -p $@ - -$(TEST_BUILD_DIR)/%.o: $(TEST_DIR)/%.cpp $(TEST_BUILD_DIR) $(SHA3_INC_DIR) - $(CXX) $(CXX_DEFS) $(CXX_FLAGS) $(WARN_FLAGS) $(RELEASE_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@ - -$(DEBUG_ASAN_BUILD_DIR)/%.o: $(TEST_DIR)/%.cpp $(DEBUG_ASAN_BUILD_DIR) $(SHA3_INC_DIR) - $(CXX) $(CXX_DEFS) $(CXX_FLAGS) $(WARN_FLAGS) $(DEBUG_ASAN_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@ - -$(RELEASE_ASAN_BUILD_DIR)/%.o: $(TEST_DIR)/%.cpp $(RELEASE_ASAN_BUILD_DIR) $(SHA3_INC_DIR) - $(CXX) $(CXX_DEFS) $(CXX_FLAGS) $(WARN_FLAGS) $(RELEASE_ASAN_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@ - -$(DEBUG_UBSAN_BUILD_DIR)/%.o: $(TEST_DIR)/%.cpp $(DEBUG_UBSAN_BUILD_DIR) $(SHA3_INC_DIR) - $(CXX) $(CXX_DEFS) $(CXX_FLAGS) $(WARN_FLAGS) $(DEBUG_UBSAN_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@ - -$(RELEASE_UBSAN_BUILD_DIR)/%.o: $(TEST_DIR)/%.cpp $(RELEASE_UBSAN_BUILD_DIR) $(SHA3_INC_DIR) - $(CXX) $(CXX_DEFS) $(CXX_FLAGS) $(WARN_FLAGS) $(RELEASE_UBSAN_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@ - -$(TEST_BINARY): $(TEST_OBJECTS) - $(CXX) $(RELEASE_FLAGS) $(LINK_OPT_FLAGS) $^ $(TEST_LINK_FLAGS) -o $@ - -$(DEBUG_ASAN_TEST_BINARY): $(DEBUG_ASAN_TEST_OBJECTS) - $(CXX) $(DEBUG_ASAN_FLAGS) $^ $(TEST_LINK_FLAGS) -o $@ - -$(RELEASE_ASAN_TEST_BINARY): $(RELEASE_ASAN_TEST_OBJECTS) - $(CXX) $(RELEASE_ASAN_FLAGS) $^ $(TEST_LINK_FLAGS) -o $@ - -$(DEBUG_UBSAN_TEST_BINARY): $(DEBUG_UBSAN_TEST_OBJECTS) - $(CXX) $(DEBUG_UBSAN_FLAGS) $^ $(TEST_LINK_FLAGS) -o $@ - -$(RELEASE_UBSAN_TEST_BINARY): $(RELEASE_UBSAN_TEST_OBJECTS) - $(CXX) $(RELEASE_UBSAN_FLAGS) $^ $(TEST_LINK_FLAGS) -o $@ - -test: $(TEST_BINARY) $(GTEST_PARALLEL) ## Build and run all tests in RELEASE mode - $(GTEST_PARALLEL) $< --print_test_times - -debug_asan_test: $(DEBUG_ASAN_TEST_BINARY) $(GTEST_PARALLEL) ## Build and run all tests in DEBUG mode, with Address Sanitizer - $(GTEST_PARALLEL) $< --print_test_times - -release_asan_test: $(RELEASE_ASAN_TEST_BINARY) $(GTEST_PARALLEL) ## Build and run all tests in RELEASE mode, with Address Sanitizer - $(GTEST_PARALLEL) $< --print_test_times - -debug_ubsan_test: $(DEBUG_UBSAN_TEST_BINARY) $(GTEST_PARALLEL) ## Build and run all tests in DEBUG mode, with Undefined Behavior Sanitizer - $(GTEST_PARALLEL) $< --print_test_times - -release_ubsan_test: $(RELEASE_UBSAN_TEST_BINARY) $(GTEST_PARALLEL) ## Build and run all tests in RELEASE mode, with Undefined Behavior Sanitizer - $(GTEST_PARALLEL) $< --print_test_times From ebedefc16591ec1e73834f706ef6bedd931c9864 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 21 Feb 2026 11:19:00 +0400 Subject: [PATCH 3/8] Introduce cmake as build system Signed-off-by: Anjan Roy --- CMakeLists.txt | 186 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a13ca69 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,186 @@ +cmake_minimum_required(VERSION 3.28) + +project(randomshake VERSION 0.1.0 LANGUAGES CXX) + +# --- Options --- +option(RANDOMSHAKE_BUILD_TESTS "Build tests" OFF) +option(RANDOMSHAKE_BUILD_EXAMPLES "Build examples" OFF) +option(RANDOMSHAKE_BUILD_BENCHMARKS "Build benchmarks" OFF) +option(RANDOMSHAKE_FETCH_DEPS "Fetch missing dependencies (GTest, Benchmark)" OFF) + +# --- Top-level-only settings (skipped when consumed via FetchContent/add_subdirectory) --- +if(PROJECT_IS_TOP_LEVEL) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + + option(RANDOMSHAKE_ENABLE_LTO "Enable Interprocedural Optimization (LTO)" ON) + option(RANDOMSHAKE_NATIVE_OPT "Enable -march=native (not suitable for cross-compilation)" OFF) + option(RANDOMSHAKE_ASAN "Enable AddressSanitizer" OFF) + option(RANDOMSHAKE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF) + + # --- Tools --- + find_program(CLANG_TIDY_EXE clang-tidy) + find_program(CLANG_FORMAT_EXE clang-format) + + if(CLANG_TIDY_EXE) + file(GLOB_RECURSE TIDY_SOURCES "examples/*.cpp") + + add_custom_target(tidy + COMMAND ${CLANG_TIDY_EXE} -p ${CMAKE_BINARY_DIR} --config-file=${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy --header-filter=${CMAKE_CURRENT_SOURCE_DIR}/include/randomshake/.* ${TIDY_SOURCES} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Running clang-tidy" + ) + endif() + + if(CLANG_FORMAT_EXE) + file(GLOB_RECURSE FORMAT_SOURCES + "include/**/*.hpp" "examples/*.cpp" + "benches/*.hpp" "benches/*.cpp" + "tests/*.hpp" "tests/*.cpp") + + add_custom_target(format + COMMAND ${CLANG_FORMAT_EXE} -i -style=file ${FORMAT_SOURCES} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Running clang-format" + ) + endif() + + # --- Standard settings --- + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + + # --- Compiler Warnings --- + if(MSVC) + set(RANDOMSHAKE_WARNING_FLAGS /W4) + else() + set(RANDOMSHAKE_WARNING_FLAGS -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wformat=2 -Wcast-qual -Wold-style-cast -Wundef -Werror) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + list(APPEND RANDOMSHAKE_WARNING_FLAGS -Wno-pass-failed) + endif() + endif() + + # --- Sanitizers --- + if(RANDOMSHAKE_ASAN) + add_compile_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) + add_link_options(-fsanitize=address) + endif() + + if(RANDOMSHAKE_UBSAN) + add_compile_options(-fsanitize=undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-sanitize-recover=undefined) + add_link_options(-fsanitize=undefined) + endif() + + # --- Native Optimization --- + if(RANDOMSHAKE_NATIVE_OPT) + add_compile_options(-march=native) + message(STATUS "Enabled -march=native (machine-specific optimization)") + endif() + + # --- LTO --- + if(RANDOMSHAKE_ENABLE_LTO) + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(WARNING "LTO is not supported: ${output}") + endif() + endif() +endif() + +# --- Dependency: sha3 (via FetchContent) --- +include(FetchContent) +FetchContent_Declare( + sha3 + GIT_REPOSITORY https://github.com/itzmeanjan/sha3.git + GIT_TAG master + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(sha3) + +# --- Library (header-only → INTERFACE) --- +add_library(randomshake INTERFACE) +target_include_directories(randomshake INTERFACE + $ + $ +) +target_link_libraries(randomshake INTERFACE sha3) +target_compile_features(randomshake INTERFACE cxx_std_20) + +# --- Tests --- +if(RANDOMSHAKE_BUILD_TESTS) + enable_testing() + if(RANDOMSHAKE_FETCH_DEPS) + message(STATUS "Fetching GTest...") + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.17.0.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + else() + find_package(GTest REQUIRED) + endif() + + file(GLOB TEST_SOURCES CONFIGURE_DEPENDS "tests/*.cpp") + + add_executable(randomshake_tests ${TEST_SOURCES}) + target_link_libraries(randomshake_tests PRIVATE randomshake GTest::gtest_main) + target_include_directories(randomshake_tests PRIVATE tests) + target_compile_options(randomshake_tests PRIVATE ${RANDOMSHAKE_WARNING_FLAGS}) + + include(GoogleTest) + gtest_discover_tests(randomshake_tests) +endif() + +# --- Benchmarks --- +if(RANDOMSHAKE_BUILD_BENCHMARKS) + if(RANDOMSHAKE_FETCH_DEPS) + message(STATUS "Fetching Google Benchmark...") + include(FetchContent) + FetchContent_Declare( + benchmark + URL https://github.com/google/benchmark/archive/refs/tags/v1.9.5.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) + set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(benchmark) + else() + find_package(benchmark REQUIRED) + endif() + + file(GLOB BENCHMARK_SOURCES CONFIGURE_DEPENDS "benches/*.cpp") + find_library(LIBPFM pfm) + + add_executable(randomshake_benchmarks ${BENCHMARK_SOURCES}) + target_link_libraries(randomshake_benchmarks PRIVATE randomshake benchmark::benchmark_main) + + if(LIBPFM) + target_link_libraries(randomshake_benchmarks PRIVATE ${LIBPFM}) + message(STATUS "Found libpfm: ${LIBPFM} - linking it to benchmarks") + endif() + + target_include_directories(randomshake_benchmarks PRIVATE benches) + target_compile_options(randomshake_benchmarks PRIVATE ${RANDOMSHAKE_WARNING_FLAGS}) +endif() + +# --- Examples --- +if(RANDOMSHAKE_BUILD_EXAMPLES) + file(GLOB EXAMPLE_SOURCES CONFIGURE_DEPENDS "examples/*.cpp") + foreach(ex_src ${EXAMPLE_SOURCES}) + get_filename_component(ex_name ${ex_src} NAME_WE) + set(ex_target "${ex_name}_example") + add_executable(${ex_target} ${ex_src}) + target_link_libraries(${ex_target} PRIVATE randomshake) + target_compile_options(${ex_target} PRIVATE ${RANDOMSHAKE_WARNING_FLAGS}) + endforeach() +endif() + +# --- Install --- +include(GNUInstallDirs) +install(TARGETS randomshake EXPORT randomshake-config INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(EXPORT randomshake-config NAMESPACE randomshake:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/randomshake) From c2adc279a2f98120c56d9881452ae79ae5a6c55e Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 21 Feb 2026 11:19:29 +0400 Subject: [PATCH 4/8] Update gitignore file pattern Signed-off-by: Anjan Roy --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d1ae5a9..df4ac50 100644 --- a/.gitignore +++ b/.gitignore @@ -32,5 +32,5 @@ *.app .cache -build +build*/ compile_commands.json From 08cf4937db4478a04963f1f36a802f9d6f13c0e9 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 21 Feb 2026 11:19:46 +0400 Subject: [PATCH 5/8] Add .clang-tidy file Signed-off-by: Anjan Roy --- .clang-tidy | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .clang-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..d0e02bc --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,35 @@ +--- +Checks: ' + bugprone-*, + cert-*, + clang-analyzer-*, + concurrency-*, + cppcoreguidelines-*, + hicpp-*, + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -bugprone-easily-swappable-parameters, + -clang-analyzer-fuchsia.*, + -clang-analyzer-osx.*, + -clang-analyzer-optin.mpi.*, + -clang-analyzer-optin.osx.*, + -clang-analyzer-optin.performance.GCDAntipattern, + -clang-analyzer-webkit.*, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-avoid-magic-numbers, + -hicpp-use-auto, + -hicpp-signed-bitwise, + -misc-include-cleaner, + -modernize-use-trailing-return-type, + -modernize-use-auto, + -modernize-use-ranges, + -readability-magic-numbers +' +CheckOptions: + readability-identifier-length.MinimumLoopCounterLength: 1 +WarningsAsErrors: '*' +HeaderFilterRegex: 'include/randomshake/.*' +... From 9ef5db69255803c06e805d3723bc8a1a48cba803 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 21 Feb 2026 11:21:50 +0400 Subject: [PATCH 6/8] Address all compiler warnings and clang-tidy reports Signed-off-by: Anjan Roy --- benches/bench_csprng_creation.cpp | 12 ++++++---- benches/bench_csprng_generation.cpp | 18 ++++++++------ benches/bench_utils.hpp | 4 ++-- examples/CMakeLists.txt | 19 +++++++++++++++ examples/csprng_generate_byte_seq.cpp | 2 +- ...generate_byte_seq_with_non_default_xof.cpp | 2 +- examples/csprng_with_bernoulli_dist.cpp | 3 +-- examples/csprng_with_binomial_dist.cpp | 3 +-- examples/csprng_with_uniform_int_dist.cpp | 3 +-- examples/csprng_with_uniform_real_dist.cpp | 24 +++++++++++-------- include/randomshake/randomshake.hpp | 23 +++++++----------- tests/test_consts.hpp | 2 +- tests/test_deterministic_csprng.cpp | 11 +++------ tests/test_ratcheting.cpp | 12 ++++++---- tests/test_utils.hpp | 12 +++++----- 15 files changed, 84 insertions(+), 66 deletions(-) create mode 100644 examples/CMakeLists.txt diff --git a/benches/bench_csprng_creation.cpp b/benches/bench_csprng_creation.cpp index 465fee2..5d75f9d 100644 --- a/benches/bench_csprng_creation.cpp +++ b/benches/bench_csprng_creation.cpp @@ -3,13 +3,15 @@ #include #include -static void +namespace { + +void bench_deterministic_csprng_creation(benchmark::State& state) { std::array::seed_byte_len> seed{}; seed.fill(0xde); - for (auto _ : state) { + for (auto _itr : state) { benchmark::DoNotOptimize(seed); randomshake::randomshake_t csprng(seed); @@ -18,10 +20,10 @@ bench_deterministic_csprng_creation(benchmark::State& state) } } -static void +void bench_nondeterministic_csprng_creation(benchmark::State& state) { - for (auto _ : state) { + for (auto _itr : state) { randomshake::randomshake_t csprng; benchmark::DoNotOptimize(&csprng); @@ -29,6 +31,8 @@ bench_nondeterministic_csprng_creation(benchmark::State& state) } } +} + BENCHMARK(bench_deterministic_csprng_creation) ->Name("deterministic_csprng/create") ->ComputeStatistics("min", compute_min) diff --git a/benches/bench_csprng_generation.cpp b/benches/bench_csprng_generation.cpp index 0fe05d7..6b0daff 100644 --- a/benches/bench_csprng_generation.cpp +++ b/benches/bench_csprng_generation.cpp @@ -5,8 +5,10 @@ #include #include +namespace { + template -static void +void bench_csprng_output_generation(benchmark::State& state) { std::array::seed_byte_len> seed{}; @@ -15,7 +17,7 @@ bench_csprng_output_generation(benchmark::State& state) randomshake::randomshake_t csprng(seed); result_type result{}; - for (auto _ : state) { + for (auto _itr : state) { benchmark::DoNotOptimize(&csprng); benchmark::DoNotOptimize(result); @@ -26,11 +28,11 @@ bench_csprng_output_generation(benchmark::State& state) benchmark::ClobberMemory(); } - state.SetBytesProcessed(state.iterations() * sizeof(result_type)); + state.SetBytesProcessed(state.iterations() * static_cast(sizeof(result_type))); } template -static void +void bench_csprng_byte_sequence_squeezing(benchmark::State& state) { std::array::seed_byte_len> seed{}; @@ -38,10 +40,10 @@ bench_csprng_byte_sequence_squeezing(benchmark::State& state) randomshake::randomshake_t csprng(seed); - constexpr size_t RANDOM_OUTPUT_BYTE_LEN = 1'024 * 1'024; // 1 MB + constexpr size_t RANDOM_OUTPUT_BYTE_LEN = 1'024UL * 1'024UL; // 1 MB std::vector rand_byte_seq(RANDOM_OUTPUT_BYTE_LEN, 0); - for (auto _ : state) { + for (auto _itr : state) { benchmark::DoNotOptimize(&csprng); benchmark::DoNotOptimize(rand_byte_seq); @@ -52,7 +54,9 @@ bench_csprng_byte_sequence_squeezing(benchmark::State& state) benchmark::ClobberMemory(); } - state.SetBytesProcessed(state.iterations() * rand_byte_seq.size()); + state.SetBytesProcessed(state.iterations() * static_cast(rand_byte_seq.size())); +} + } BENCHMARK(bench_csprng_output_generation)->Name("csprng/generate_u8")->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max); diff --git a/benches/bench_utils.hpp b/benches/bench_utils.hpp index 58e62ec..e6fe90e 100644 --- a/benches/bench_utils.hpp +++ b/benches/bench_utils.hpp @@ -2,5 +2,5 @@ #include #include -const auto compute_min = [](const std::vector& v) -> double { return *std::min_element(v.begin(), v.end()); }; -const auto compute_max = [](const std::vector& v) -> double { return *std::max_element(v.begin(), v.end()); }; +const auto compute_min = [](const std::vector& vals) -> double { return *std::min_element(vals.begin(), vals.end()); }; +const auto compute_max = [](const std::vector& vals) -> double { return *std::max_element(vals.begin(), vals.end()); }; diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..5b5630d --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.28) +project(randomshake-example LANGUAGES CXX) + +find_package(randomshake QUIET) + +if(NOT randomshake_FOUND) + message(STATUS + "randomshake not found via find_package, using FetchContent...") + include(FetchContent) + FetchContent_Declare( + randomshake + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + FetchContent_MakeAvailable(randomshake) +endif() + +add_executable(csprng_generate_byte_seq csprng_generate_byte_seq.cpp) +target_link_libraries(csprng_generate_byte_seq PRIVATE randomshake) +target_compile_features(csprng_generate_byte_seq PRIVATE cxx_std_20) diff --git a/examples/csprng_generate_byte_seq.cpp b/examples/csprng_generate_byte_seq.cpp index 1efdfba..d3a1b9a 100644 --- a/examples/csprng_generate_byte_seq.cpp +++ b/examples/csprng_generate_byte_seq.cpp @@ -7,7 +7,7 @@ main() { randomshake::randomshake_t csprng; - constexpr size_t RANDOM_OUTPUT_BYTE_LEN = 1'024 * 1'024; + constexpr size_t RANDOM_OUTPUT_BYTE_LEN = 1'024UL * 1'024UL; std::vector rand_byte_seq(RANDOM_OUTPUT_BYTE_LEN, 0); csprng.generate(rand_byte_seq); diff --git a/examples/csprng_generate_byte_seq_with_non_default_xof.cpp b/examples/csprng_generate_byte_seq_with_non_default_xof.cpp index f6be986..6ba2fd0 100644 --- a/examples/csprng_generate_byte_seq_with_non_default_xof.cpp +++ b/examples/csprng_generate_byte_seq_with_non_default_xof.cpp @@ -12,7 +12,7 @@ main() */ randomshake::randomshake_t csprng; - constexpr size_t RANDOM_OUTPUT_BYTE_LEN = 1'024 * 1'024; + constexpr size_t RANDOM_OUTPUT_BYTE_LEN = 1'024UL * 1'024UL; std::vector rand_byte_seq(RANDOM_OUTPUT_BYTE_LEN, 0); csprng.generate(rand_byte_seq); diff --git a/examples/csprng_with_bernoulli_dist.cpp b/examples/csprng_with_bernoulli_dist.cpp index 15258f7..5dd7f88 100644 --- a/examples/csprng_with_bernoulli_dist.cpp +++ b/examples/csprng_with_bernoulli_dist.cpp @@ -3,7 +3,6 @@ #include #include #include -#include // This example collects inspiration from https://en.cppreference.com/w/cpp/numeric/random/bernoulli_distribution. // See the example section. @@ -14,7 +13,7 @@ main() std::bernoulli_distribution dist{ .25 }; std::map hist; - for (auto _ : std::ranges::iota_view{ 0, 10'000 }) { + for (int idx = 0; idx < 10'000; ++idx) { ++hist[dist(csprng)]; } diff --git a/examples/csprng_with_binomial_dist.cpp b/examples/csprng_with_binomial_dist.cpp index 24b3e4f..d4bda3e 100644 --- a/examples/csprng_with_binomial_dist.cpp +++ b/examples/csprng_with_binomial_dist.cpp @@ -1,7 +1,6 @@ #include "randomshake/randomshake.hpp" #include #include -#include // This example collects inspiration from https://seth.rocks/articles/cpprandom. See the last code-snippet. int @@ -10,7 +9,7 @@ main() randomshake::randomshake_t csprng; std::binomial_distribution dist{ 1'000, .5 }; - for (auto _ : std::ranges::iota_view{ 1, 10 }) { + for (int idx = 1; idx < 10; ++idx) { std::cout << "[BINOMIAL dISTRIBUTION] Number of heads in 1,000 flips: " << dist(csprng) << '\n'; } diff --git a/examples/csprng_with_uniform_int_dist.cpp b/examples/csprng_with_uniform_int_dist.cpp index d790cfa..d49f00a 100644 --- a/examples/csprng_with_uniform_int_dist.cpp +++ b/examples/csprng_with_uniform_int_dist.cpp @@ -3,7 +3,6 @@ #include #include #include -#include int main() @@ -15,7 +14,7 @@ main() randomshake::randomshake_t csprng(seed); std::uniform_int_distribution dist{ 97, 102 }; - for (auto _ : std::ranges::iota_view{ 1, 10 }) { + for (int idx = 1; idx < 10; ++idx) { std::cout << "[UNIFORM iNT dISTRIBUTION] Random integer sampled in [97, 102]: " << dist(csprng) << '\n'; } diff --git a/examples/csprng_with_uniform_real_dist.cpp b/examples/csprng_with_uniform_real_dist.cpp index 6810ada..2dcdef3 100644 --- a/examples/csprng_with_uniform_real_dist.cpp +++ b/examples/csprng_with_uniform_real_dist.cpp @@ -6,12 +6,14 @@ #include #include +namespace { + float compute_mean(std::span vals) { - float res = 0.f; - for (auto v : vals) { - res += v; + float res = 0.F; + for (auto val : vals) { + res += val; } return res / static_cast(vals.size()); @@ -22,9 +24,9 @@ compute_standard_deviation(std::span vals) { const auto mean = compute_mean(vals); - float squared_diff = 0.f; - for (auto v : vals) { - squared_diff += std::pow(v - mean, 2.f); + float squared_diff = 0.F; + for (auto val : vals) { + squared_diff += std::pow(val - mean, 2.F); } const auto squared_diff_mean = squared_diff / static_cast(vals.size()); @@ -36,14 +38,16 @@ compute_standard_deviation(std::span vals) float expected_standard_deviation_for_continuous_uniform_distributed_real_numbers(const float start_interval, const float end_interval) { - return (end_interval - start_interval) / std::sqrt(12.f); + return (end_interval - start_interval) / std::sqrt(12.F); +} + } int main() { - const float start_interval = 0.f; - const float end_interval = 1.f; + const float start_interval = 0.F; + const float end_interval = 1.F; assert(start_interval < end_interval); @@ -51,7 +55,7 @@ main() std::uniform_real_distribution dist{ start_interval, end_interval }; constexpr size_t NUMBER_OF_RANDOM_FLOATS = 1'000'000; - std::vector rand_floats(NUMBER_OF_RANDOM_FLOATS, 0.f); + std::vector rand_floats(NUMBER_OF_RANDOM_FLOATS, 0.F); std::ranges::generate(rand_floats, [&]() { return dist(csprng); }); const auto computed_sd = compute_standard_deviation(rand_floats); diff --git a/include/randomshake/randomshake.hpp b/include/randomshake/randomshake.hpp index a7a7a54..ca77bfd 100644 --- a/include/randomshake/randomshake.hpp +++ b/include/randomshake/randomshake.hpp @@ -28,7 +28,7 @@ template forceinline void DoNotOptimize(Tp& value) { - asm volatile("" : "+r,m"(value) : :); + asm volatile("" : "+r,m"(value) : :); // NOLINT(hicpp-no-assembler) } // Enum listing supported eXtendable Output Functions (XOFs), which can be used for producing pseudo-random byte stream. @@ -108,7 +108,7 @@ struct randomshake_t private: xof_selector_t::type state{}; std::array::ratchet_period_byte_len> buffer{}; - size_t buffer_offset = 0u; + size_t buffer_offset = 0U; public: using result_type = UIntType; @@ -126,8 +126,8 @@ struct randomshake_t std::array seed{}; auto seed_span = std::span(seed); - std::random_device rd{}; - const auto entropy = rd.entropy(); + std::random_device rdev{}; + const auto entropy = rdev.entropy(); if (entropy == 0.) { std::cout << "[RANDOMSHAKE WARNING] Non-deterministic seed generator has zero entropy ! " "Read https://en.cppreference.com/w/cpp/numeric/random/random_device/entropy for more insight.\n"; @@ -136,10 +136,10 @@ struct randomshake_t constexpr size_t step_by = sizeof(std::random_device::result_type); size_t seed_offset = 0; while (seed_offset < seed_span.size()) { - const auto v = rd(); + const auto val = rdev(); static_assert(seed_byte_len % step_by == 0, "Seed byte length must be a multiple of `step_by`, for following memcpy to work correctly !"); - std::memcpy(seed_span.subspan(seed_offset, step_by).data(), reinterpret_cast(&v), step_by); + std::memcpy(seed_span.subspan(seed_offset, step_by).data(), &val, step_by); seed_offset += step_by; } @@ -197,11 +197,7 @@ struct randomshake_t } result_type result{}; - - auto src_ptr = reinterpret_cast(buffer.data()) + buffer_offset; - auto dst_ptr = reinterpret_cast(&result); - - std::memcpy(dst_ptr, src_ptr, required_num_bytes); + std::memcpy(&result, &buffer[buffer_offset], required_num_bytes); buffer_offset += required_num_bytes; return result; @@ -217,10 +213,7 @@ struct randomshake_t const size_t required_num_bytes = output.size() - out_offset; const size_t copyable_num_bytes = std::min(readable_num_bytes, required_num_bytes); - auto src_ptr = reinterpret_cast(buffer.data()) + buffer_offset; - auto dst_ptr = reinterpret_cast(output.data()) + out_offset; - - std::memcpy(dst_ptr, src_ptr, copyable_num_bytes); + std::memcpy(&output[out_offset], &buffer[buffer_offset], copyable_num_bytes); buffer_offset += copyable_num_bytes; out_offset += copyable_num_bytes; diff --git a/tests/test_consts.hpp b/tests/test_consts.hpp index 9ad8cb7..73e5d7b 100644 --- a/tests/test_consts.hpp +++ b/tests/test_consts.hpp @@ -1,4 +1,4 @@ #pragma once #include -static constexpr size_t GENERATED_RANDOM_BYTE_LEN = 1'024 * 1'024; // = 1MB +static constexpr size_t GENERATED_RANDOM_BYTE_LEN = 1'024UL * 1'024UL; // = 1MB diff --git a/tests/test_deterministic_csprng.cpp b/tests/test_deterministic_csprng.cpp index b242121..b01613e 100644 --- a/tests/test_deterministic_csprng.cpp +++ b/tests/test_deterministic_csprng.cpp @@ -90,14 +90,9 @@ TEST(RandomSHAKE, Deterministic_CSPRNG_Using_Same_Seed_With_Diff_Result_Type_Pro std::ranges::generate(generated_rand_u32, [&]() { return csprng_u32(); }); std::ranges::generate(generated_rand_u64, [&]() { return csprng_u64(); }); - std::span generated_rand_u8_span(generated_rand_u8); - std::span generated_rand_u16_span(reinterpret_cast(generated_rand_u16.data()), GENERATED_RANDOM_BYTE_LEN); - std::span generated_rand_u32_span(reinterpret_cast(generated_rand_u32.data()), GENERATED_RANDOM_BYTE_LEN); - std::span generated_rand_u64_span(reinterpret_cast(generated_rand_u64.data()), GENERATED_RANDOM_BYTE_LEN); - - EXPECT_TRUE(std::ranges::equal(generated_rand_u8_span, generated_rand_u16_span)); - EXPECT_TRUE(std::ranges::equal(generated_rand_u16_span, generated_rand_u32_span)); - EXPECT_TRUE(std::ranges::equal(generated_rand_u32_span, generated_rand_u64_span)); + EXPECT_EQ(0, std::memcmp(generated_rand_u8.data(), generated_rand_u16.data(), GENERATED_RANDOM_BYTE_LEN)); + EXPECT_EQ(0, std::memcmp(generated_rand_u16.data(), generated_rand_u32.data(), GENERATED_RANDOM_BYTE_LEN)); + EXPECT_EQ(0, std::memcmp(generated_rand_u32.data(), generated_rand_u64.data(), GENERATED_RANDOM_BYTE_LEN)); } TEST(RandomSHAKE, Deterministic_CSPRNG_Using_Same_Seed_With_Diff_Public_API) diff --git a/tests/test_ratcheting.cpp b/tests/test_ratcheting.cpp index 2421463..d9c552f 100644 --- a/tests/test_ratcheting.cpp +++ b/tests/test_ratcheting.cpp @@ -14,7 +14,7 @@ struct dummy_noratchet_csprng randomshake::xof_selector_t::type state; public: - dummy_noratchet_csprng(std::span::seed_byte_len> seed) + explicit dummy_noratchet_csprng(std::span::seed_byte_len> seed) { state.reset(); state.absorb(seed); @@ -24,17 +24,17 @@ struct dummy_noratchet_csprng uint8_t operator()() { uint8_t result = 0; - - auto res_ptr = reinterpret_cast(&result); - auto res_span = std::span(res_ptr, sizeof(result)); + auto res_span = std::span(&result, 1); state.squeeze(res_span); return result; } }; +namespace { + template -static void +void test_ratchet_getting_activated_post_ratchet_period_bytes_output() { // --- Paint output buffers. --- @@ -94,6 +94,8 @@ test_ratchet_getting_activated_post_ratchet_period_bytes_output() EXPECT_FALSE(std::ranges::equal(last_of_original, last_of_dummy)); } +} + TEST(RandomSHAKE, Deterministic_CSPRNG_Detect_Ratchet_Working_For_SHAKE256_XOF) { test_ratchet_getting_activated_post_ratchet_period_bytes_output(); diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp index d42b887..71bc538 100644 --- a/tests/test_utils.hpp +++ b/tests/test_utils.hpp @@ -10,7 +10,7 @@ namespace randomshake_test_utils { Given a byte, this routine flips bit at specified index i, mutating input. If i is not a valid index, it is a no-op. */ -static inline constexpr void +static constexpr void do_bitflip(uint8_t& val, const size_t bit_idx) { constexpr auto T_bw = std::numeric_limits>::digits; @@ -18,13 +18,13 @@ do_bitflip(uint8_t& val, const size_t bit_idx) return; } - const auto hi_bit_mask = 0xffu << (bit_idx + 1); - const auto lo_bit_mask = 0xffu >> (T_bw - bit_idx); + const auto hi_bit_mask = 0xffU << (bit_idx + 1); + const auto lo_bit_mask = 0xffU >> (T_bw - bit_idx); - const auto selected_bit = (val >> bit_idx) & 0b1u; - const auto selected_bit_flipped = ~selected_bit & 0b1u; + const auto selected_bit = (val >> bit_idx) & 0b1U; + const auto selected_bit_flipped = ~selected_bit & 0b1U; - val = (val & hi_bit_mask) ^ (selected_bit_flipped << bit_idx) ^ (val & lo_bit_mask); + val = static_cast((val & hi_bit_mask) ^ (selected_bit_flipped << bit_idx) ^ (val & lo_bit_mask)); } } From 9c52377333a112d420206feded8fa38b26697e44 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 21 Feb 2026 11:24:03 +0400 Subject: [PATCH 7/8] Update github actions ci script to use cmake for building the project Signed-off-by: Anjan Roy --- .github/workflows/test_ci.yml | 99 ++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 5138416..38068c7 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -1,4 +1,3 @@ -# Collects inspiration from https://github.com/itzmeanjan/sha3/blob/fb21648e136d7a64ce5c065fa829d4e3254414f4/.github/workflows/test_ci.yml name: Test RandomShake CSPRNG on: @@ -8,39 +7,75 @@ on: branches: [ "main" ] jobs: - build: - runs-on: ${{matrix.os}} + lint: + runs-on: ubuntu-latest + env: + LLVM_VERSION: 20 + steps: + - uses: actions/checkout@v6 + + - name: Install LLVM ${{ env.LLVM_VERSION }} + run: | + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-${{ env.LLVM_VERSION }} main" | sudo tee /etc/apt/sources.list.d/llvm.list + sudo apt-get update + sudo apt-get install -y clang-${{ env.LLVM_VERSION }} clang-tidy-${{ env.LLVM_VERSION }} + + - name: Configure + run: > + cmake -B build + -DCMAKE_CXX_COMPILER=clang++-${{ env.LLVM_VERSION }} + -DCMAKE_BUILD_TYPE=Release + -DRANDOMSHAKE_BUILD_TESTS=ON + -DRANDOMSHAKE_BUILD_EXAMPLES=ON + -DRANDOMSHAKE_FETCH_DEPS=ON + -DCLANG_TIDY_EXE=clang-tidy-${{ env.LLVM_VERSION }} + + - name: clang-tidy + run: cmake --build build --target tidy + + test: + runs-on: ${{ matrix.os }} strategy: + fail-fast: false + max-parallel: 2 matrix: - os: [ - ubuntu-latest, # x86_64 - macos-latest, # aarch64 - ] + os: [ ubuntu-latest ] compiler: [g++, clang++] - build_type: [debug, release] - test_type: [standard, asan, ubsan] + sanitizer: [none, asan, ubsan] + build_type: [Debug, Release] + include: + - os: macos-latest + compiler: clang++ + sanitizer: none + build_type: Release + exclude: + - sanitizer: none + build_type: Debug steps: - - uses: actions/checkout@v6 - - - name: Setup Google Test - uses: Bacondish2023/setup-googletest@v1 - with: - tag: v1.17.0 - - - name: Execute Tests on ${{matrix.os}}, compiled with ${{matrix.compiler}} - if: ${{matrix.test_type == 'standard'}} - run: | - CXX=${{matrix.compiler}} make test -j - make clean - - - name: Execute Tests with ${{matrix.test_type}}, in ${{matrix.build_type}} mode, on ${{matrix.os}}, compiled with ${{matrix.compiler}} - if: ${{matrix.test_type != 'standard'}} - run: | - CXX=${{matrix.compiler}} make ${{matrix.build_type}}_${{matrix.test_type}}_test -j - make clean - - - name: Build and run examples - run: | - make example -j - make clean + - uses: actions/checkout@v6 + + - name: Configure + run: > + cmake -B build + -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DRANDOMSHAKE_BUILD_TESTS=ON + -DRANDOMSHAKE_BUILD_EXAMPLES=ON + -DRANDOMSHAKE_FETCH_DEPS=ON + -DRANDOMSHAKE_ASAN=${{ matrix.sanitizer == 'asan' && 'ON' || 'OFF' }} + -DRANDOMSHAKE_UBSAN=${{ matrix.sanitizer == 'ubsan' && 'ON' || 'OFF' }} + + - name: Build + run: cmake --build build -j + + - name: Test + run: ctest --test-dir build --output-on-failure -j + + - name: Run Examples + if: ${{ matrix.sanitizer == 'none' && matrix.build_type == 'Release' }} + run: | + for exe in build/csprng_*_example; do + ./$exe + done From be4d2eb88277e8bbdabf9e8d50c103ed60ba40c8 Mon Sep 17 00:00:00 2001 From: Anjan Roy Date: Sat, 21 Feb 2026 12:06:31 +0400 Subject: [PATCH 8/8] Update readme with latest state of project Signed-off-by: Anjan Roy --- README.md | 189 ++++++++++++++++++++++----------------------- examples/README.md | 53 +++++++++++++ 2 files changed, 145 insertions(+), 97 deletions(-) create mode 100644 examples/README.md diff --git a/README.md b/README.md index c3c9287..3d0bf41 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # RandomSHAKE -(Turbo)SHAKE256 XOF -based Portable C++20 Cryptographically Secure Pseudo Random Number Generator (CSPRNG) - plug and play with C++ standard library's `` header's statistical distributions. +(Turbo)SHAKE256 XOF -based Portable C++20 Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) - plug and play with C++ standard library's `` header's statistical distributions, for generating pseudo-random numbers. -## Why ? +## Introduction -C++11 introduced `` header to its standard library, which offers multiple pseudo-random number generator engines and statistical distributions. The design of the `` header is very much modular, it's possible to plug any psuedo-random number generator engine with any distribution and start getting results as per the rules of the statistical distribution. All that is needed, is providing the distribution with a source *U*niform *R*andom *N*umber *G*enerator (URNG). One thing that the standard library's `` header lacks is a cryptographically secure psuedo-random number generator engine. Using any of the provided engines such as Mersenne Twister or Linear Congruential engine with provided distributions, in cryptographic settings, might be quite catastrophic! +C++11 introduced `` header to its standard library, which offers multiple pseudo-random bit generator engines and statistical distributions. The design of the `` header is very much modular, it's possible to plug any psuedo-random bit generator engine with any distribution and start squeezing results as per the rules of the statistical distribution. All that is needed, is providing the distribution with a source *U*niform *R*andom *B*it *G*enerator (URBG). One thing that the standard library's `` header lacks is a cryptographically secure psuedo-random number generator engine. Using any of the provided engines such as Mersenne Twister or Linear Congruential engine with provided distributions, in cryptographic settings, might be quite catastrophic! This is where "RandomSHAKE" comes, collecting inspiration from . -"RandomSHAKE" is a *C*ryptographically *S*ecure *P*seudo-*R*andom *N*umber *G*enerator (CSPRNG) engine, which is backed by (Turbo)SHAKE256 eXtendable Output Function (XOF) with occasional ratcheting, generating unsigned integer (of all standard bit-widths) stream. It's optional to specify which XOF you want to use. By default you get TurboSHAKE256, which doubles the throughput compared to SHAKE256. In case you really want SHAKE256, you need to be explicit. For initializing the CSPRNG, either you can rely on the convenient default constructor, which samples initial seed from the **non-deterministic** `std::random_device` API or you can explicitly supply a seed for **deterministic** and reproducible behaviour. It's very easy to plug this CSPRNG engine into any of the C++ standard library's statistical distributions defined in `` header. Just plug and play. And now you can use those distributions with "RandomSHAKE" CSPRNG, in cryptographic settings - producing integers or floats, whatever you need. It also offers API for generating arbitrary long byte stream at a time. +"RandomSHAKE" is a *C*ryptographically *S*ecure *P*seudo-*R*andom *N*umber *G*enerator (CSPRNG) engine, which is backed by (Turbo)SHAKE256 eXtendable Output Function (XOF) with occasional ratcheting. It's optional to specify which XOF you want to use. By default you get TurboSHAKE256, which doubles the throughput compared to SHAKE256. In case you really want SHAKE256, you need to be explicit. For initializing the CSPRNG, either you can rely on the convenient default constructor, which samples initial seed from the **non-deterministic** `std::random_device` API or you can explicitly supply a seed for **deterministic** and reproducible behaviour. It's very easy to plug this CSPRNG engine into any of the C++ standard library's statistical distributions defined in `` header. Just plug and play. And now you can use those distributions with "RandomSHAKE" CSPRNG, in cryptographic settings - producing integers or floats, whatever you need. It also offers API for generating arbitrary long byte stream at a time. > [!CAUTION] > Using the non-deterministic CSPRNG initialization API is very convenient, but there is a caveat - this CSPRNG samples its seed from `std::random_device` engine, which is supposed to be non-deterministic, but is not guaranteed to be - it's implementation-defined behavior. I strongly advise you to read . @@ -76,91 +76,61 @@ Sampling of `u64` | 833.3 MB/s | 545.8 MB/s Sampling arbitrary long byte sequence (using TurboSHAKE256 XOF, default) | 869.2 MB/s | 637.5 MB/s Sampling arbitrary long byte sequence (using SHAKE256 XOF, explicit) | 485.2 MB/s | 354.7 MB/s -## How to setup ? +## Prerequisites -- Ensure that you have access to a C++ compiler, which supports compiling C++20 program. - -```bash -# I'm using -$ c++ --version -c++ (Ubuntu 15.2.0-4ubuntu4) 15.2.0 -``` - -- You will also need both `make` and `cmake` for building this project and test framework/ benchmark harness. - -```bash -$ make --version -GNU Make 4.4.1 - -$ cmake --version -cmake version 3.31.6 -``` - -- For running tests, you must have `google-test` globally installed. Follow steps described @ . -- For running benchmarks, you must have `google-benchmark` globally installed. You may find steps described @ helpful. +- A C++ compiler such as `g++`/ `clang++`, with support for C++20 standard library. +- CMake (>= 3.28). +- `google-test` and `google-benchmark` can either be installed system-wide, or fetched automatically by CMake (pass `-DRANDOMSHAKE_FETCH_DEPS=ON`). > [!NOTE] -> In case you are planning to run benchmarks on a machine which runs GNU/Linux kernel, I suggest you build `google-benchmark` with libPFM, so that you get to know how many CPU cycles does it take for each benchmarked functions to execute. I describe the steps @ . +> If you are on a machine running GNU/Linux kernel and you want to obtain CPU cycles or Cycles/ byte or instruction/ cycle etc., when benchmarking RandomSHAKE CSPRNG, you should consider building `google-benchmark` library yourself with `libPFM` support, following the step-by-step guide @ . Find more about libPFM @ . > [!NOTE] > I use the BASH script @ to quickly setup a GNU/Linux machine, so that I can run tests and benchmarks. Running this script does the whole setup phase for you on Ubuntu and adjacent family of Linux distributions. -## How to test ? +## Testing -For running all functional correctness tests, just issue - -```bash -make test -j -``` +For running all functional correctness tests, issue following commands. ```bash -PASSED TESTS (9/9): - 1 ms: build/test/test.out RandomSHAKETestUtility.Bit_Flipping_Must_Work_Correctly - 4 ms: build/test/test.out RandomSHAKE.Deterministic_CSPRNG_Oneshot_vs_Multishot_Squeezing - 11 ms: build/test/test.out RandomSHAKE.Deterministic_CSPRNG_Using_Same_Seed_With_Diff_XOF_Kind_Produces_Ne_Output - 11 ms: build/test/test.out RandomSHAKE.Deterministic_CSPRNG_Using_Same_Seed_With_Diff_Public_API - 11 ms: build/test/test.out RandomSHAKE.Deterministic_CSPRNG_Detect_Ratchet_Working_For_TurboSHAKE256_XOF - 16 ms: build/test/test.out RandomSHAKE.Deterministic_CSPRNG_Using_Diff_Seed_Produces_Ne_Output - 17 ms: build/test/test.out RandomSHAKE.Deterministic_CSPRNG_Using_Same_Seed_Produces_Eq_Output - 18 ms: build/test/test.out RandomSHAKE.Deterministic_CSPRNG_Detect_Ratchet_Working_For_SHAKE256_XOF - 20 ms: build/test/test.out RandomSHAKE.Deterministic_CSPRNG_Using_Same_Seed_With_Diff_Result_Type_Produces_Eq_Output +cmake -B build -DCMAKE_BUILD_TYPE=Release -DRANDOMSHAKE_BUILD_TESTS=ON -DRANDOMSHAKE_FETCH_DEPS=ON +cmake --build build -j +ctest --test-dir build --output-on-failure -j ``` -> [!NOTE] -> There is a help menu, which introduces you to all available commands; just run `make` from the root directory of this project. - -You may want to run tests with AddressSanitizer or UndefinedBehaviorSanitizer enabled, in either debug mode or release mode. You can simply issue +To enable sanitizers or specify a compiler: ```bash -make debug_asan_test -j -make debug_ubsan_test -j +# With AddressSanitizer +cmake -B build -DRANDOMSHAKE_BUILD_TESTS=ON -DRANDOMSHAKE_FETCH_DEPS=ON -DRANDOMSHAKE_ASAN=ON -DCMAKE_BUILD_TYPE=Release # Should be run in Debug too -make release_asan_test -j -make release_ubsan_test -j -``` - -By default Make will use default c++ compiler of the system to build tests, but you specify your choice, by setting the `CXX` variable, before invoking any of the Make commands. +# With UndefinedBehaviorSanitizer +cmake -B build -DRANDOMSHAKE_BUILD_TESTS=ON -DRANDOMSHAKE_FETCH_DEPS=ON -DRANDOMSHAKE_UBSAN=ON -DCMAKE_BUILD_TYPE=Release -```bash -CXX=clang++ make test -j +# Specify compiler +cmake -B build -DCMAKE_BUILD_TYPE=Release -DRANDOMSHAKE_BUILD_TESTS=ON -DRANDOMSHAKE_FETCH_DEPS=ON -DCMAKE_CXX_COMPILER=clang++ ``` -## How to benchmark ? +## Benchmarking -For benchmarking both the creation of a CSPRNG instance and sampling of random unsigned integers from it, just issue. +For benchmarking both the creation of a CSPRNG instance and sampling of random unsigned integers from it, issue following commands. + +> [!CAUTION] +> You must disable CPU frequency scaling during benchmarking, following [this](https://github.com/google/benchmark/blob/4931aefb51d1e5872b096a97f43e13fa0fc33c8c/docs/reducing_variance.md) guide. ```bash -make benchmark -j -``` +cmake -B build -DRANDOMSHAKE_BUILD_BENCHMARKS=ON -DRANDOMSHAKE_FETCH_DEPS=ON -DCMAKE_BUILD_TYPE=Release -DRANDOMSHAKE_NATIVE_OPT=ON +cmake --build build -j -In case, you decided to build google-benchmark with libPFM, so that you can get access to h/w CPU cycles counter, you have to issue +# Run benchmarks, with cpu-cycle count. Must be built with libPFM. +./build/randomshake_benchmarks --benchmark_min_warmup_time=.1 --benchmark_enable_random_interleaving=false --benchmark_repetitions=10 --benchmark_min_time=0.1s --benchmark_display_aggregates_only=true --benchmark_counters_tabular=true --benchmark_perf_counters=CYCLES -```bash -make perf -j +# Run benchmarks, with just time. +./build/randomshake_benchmarks --benchmark_min_warmup_time=.1 --benchmark_enable_random_interleaving=false --benchmark_repetitions=10 --benchmark_min_time=0.1s --benchmark_display_aggregates_only=true --benchmark_counters_tabular=true ``` -> [!CAUTION] -> You must disable CPU frequency scaling during benchmarking. I found guide @ helpful. +> [!NOTE] +> If `libpfm` is installed on the system, it will be automatically linked to the benchmark binary, enabling hardware performance counter support. I've run benchmarks on some platforms and here are the results. @@ -173,53 +143,77 @@ I've run benchmarks on some platforms and here are the results. - x86_64. AWS EC2 Instance `c8i.large` i.e. Intel Xeon 6975P-C. JSON dump @ [bench_result_on_Linux_6.14.0-1015-aws_x86_64_with_g++_13](./bench_result_on_Linux_6.14.0-1015-aws_x86_64_with_g++_13.json). - aarch64. AWS EC2 Instance `c8g.large` i.e. AWS Graviton4. JSON dump @ [bench_result_on_Linux_6.14.0-1015-aws_aarch64_with_g++_13](./bench_result_on_Linux_6.14.0-1015-aws_aarch64_with_g++_13.json). -## How to use ? +## Development -Using "RandomSHAKE" CSPRNG is very easy. +### Static Analysis -- Clone this repository, whiile importing all git submodule -based dependencies. +Run `clang-tidy` across all headers via the example translation units: ```bash -git clone https://github.com/itzmeanjan/RandomShake.git --recurse-submodules +cmake -B build -DCMAKE_BUILD_TYPE=Release -DRANDOMSHAKE_BUILD_EXAMPLES=ON +cmake --build build --target tidy ``` -- Write a C++ function, using this CSPRNG, while including "randomshake/randomshake.hpp" header file. +### Code Formatting -```cpp -// csprng_with_binomial_dist.cpp - -#include "randomshake/randomshake.hpp" -#include -#include -#include - -// This example collects inspiration from https://seth.rocks/articles/cpprandom. See the last code-snippet. -int -main() -{ - randomshake::randomshake_t csprng; - std::binomial_distribution dist{ 1'000, .5 }; - - for (auto _ : std::ranges::iota_view{ 1, 10 }) { - std::cout << "[BINOMIAL dISTRIBUTION] Number of heads in 1,000 flips: " << dist(csprng) << '\n'; - } - - return EXIT_SUCCESS; -} +Run `clang-format` on all source files (requires a `.clang-format` file in the project root): + +```bash +cmake -B build -DCMAKE_BUILD_TYPE=Release -DRANDOMSHAKE_BUILD_EXAMPLES=ON -DRANDOMSHAKE_BUILD_TESTS=ON +cmake --build build --target format ``` -> [!NOTE] -> In above demonstration, I'm showing how to use "RandomSHAKE" CSPRNG with C++ standard library's Binomial Distribution, but it should be fairly easy, plugging this CSPRNG with any other available distribution in `` header. +## Usage -- Compile the C++ translation unit, while including path to both "RandomSHAKE" and "sha3". +`randomshake` is a header-only C++20 library, designed to be easy to integrate into your CMake project. + +### Integration + +You can install `randomshake` system-wide and use `find_package`: ```bash -c++ -std=c++20 -Wall -Wextra -Wpedantic -O3 -march=native -I ./include -I ./sha3/include examples/csprng_with_binomial_dist.cpp +# Install system-wide (default prefix: /usr/local) +cmake -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build +sudo cmake --install build + +# Install to custom directory (e.g., ./dist) +cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./dist +cmake --build build +cmake --install build +``` + +Or using `FetchContent` in your `CMakeLists.txt`: + +```cmake +include(FetchContent) +FetchContent_Declare( + randomshake + GIT_REPOSITORY https://github.com/itzmeanjan/RandomShake.git + GIT_TAG master + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(randomshake) + +add_executable(my_app main.cpp) +target_link_libraries(my_app PRIVATE randomshake) ``` -- And finally run the executable. +Alternatively, just point your compiler's include path to the `./include` directory. + +See [examples/CMakeLists.txt](./examples/CMakeLists.txt) for a standalone consumption example that uses `find_package` with `FetchContent` fallback. + +### Examples + +We maintain a few examples of using "RandomSHAKE" API with various C++ STL distributions @ [examples](./examples) directory. Build and run them by issuing: ```bash +cmake -B build -DCMAKE_BUILD_TYPE=Release -DRANDOMSHAKE_BUILD_EXAMPLES=ON +cmake --build build -j +``` + +```bash +$ ./build/csprng_with_binomial_dist_example [BINOMIAL dISTRIBUTION] Number of heads in 1,000 flips: 515 [BINOMIAL dISTRIBUTION] Number of heads in 1,000 flips: 522 [BINOMIAL dISTRIBUTION] Number of heads in 1,000 flips: 499 @@ -231,6 +225,7 @@ c++ -std=c++20 -Wall -Wextra -Wpedantic -O3 -march=native -I ./include -I ./sha [BINOMIAL dISTRIBUTION] Number of heads in 1,000 flips: 491 ``` -In case you just want to generate arbitrary many random bytes, there is an API `generate` - which can generate arbitrary many random bytes and it should be fine calling this as many times needed. Ratcheting is taken care of under the hood. +> [!NOTE] +> In above demonstration, I'm showing how to use "RandomSHAKE" CSPRNG with C++ standard library's Binomial Distribution, but it should be fairly easy, plugging this CSPRNG with any other available distribution in `` header. -I maintain a few examples of using "RandomSHAKE" API with various C++ STL distributions @ [examples](./examples) directory. You can run them all by `$ make example -j`. +In case you just want to generate arbitrary many random bytes, there is an API `generate` - which can generate arbitrary many random bytes and it should be fine calling this as many times needed. Ratcheting is taken care of under the hood. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..e97be41 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,53 @@ +# RandomSHAKE Example + +A standalone example project demonstrating how to use the [RandomSHAKE](https://github.com/itzmeanjan/RandomShake) library. + +## Quick Start + +```bash +cd examples + +# Configure and build +cmake -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build + +# Run +./build/csprng_generate_byte_seq +``` + +## Using `randomshake` in Your Own Project + +The simplest way to integrate `randomshake` into your CMake project is via `FetchContent`: + +```cmake +cmake_minimum_required(VERSION 3.28) +project(my_app LANGUAGES CXX) + +include(FetchContent) +FetchContent_Declare( + randomshake + GIT_REPOSITORY https://github.com/itzmeanjan/RandomShake.git + GIT_TAG master + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(randomshake) + +add_executable(my_app main.cpp) +target_link_libraries(my_app PRIVATE randomshake) +target_compile_features(my_app PRIVATE cxx_std_20) +``` + +Alternatively, install `randomshake` system-wide and use `find_package`: + +```bash +# From the RandomShake root directory +cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local +cmake --build build +sudo cmake --install build +``` + +```cmake +find_package(randomshake REQUIRED) +add_executable(my_app main.cpp) +target_link_libraries(my_app PRIVATE randomshake) +```