Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conform to NIST FIPS-204 Draft Standard #28

Merged
merged 38 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
68b461a
Get rid of testing NTT implementation
itzmeanjan Jun 30, 2024
07092c8
Conform to ML-DSA specification
itzmeanjan Jun 30, 2024
37e09fc
Update keygen->sign->verify tests so that they work with new signing API
itzmeanjan Jun 30, 2024
109ccc4
Update KAT files, testing conformance with ML-DSA official reference …
itzmeanjan Jun 30, 2024
deb804f
Updated KAT files with new `rnd` field
itzmeanjan Jun 30, 2024
2d95bbd
Change organization of header files
itzmeanjan Jul 14, 2024
0f91910
Refactor prime field arithmetic implementation
itzmeanjan Jul 14, 2024
a710199
Update namespace of prime field arithmetic to be more collision resis…
itzmeanjan Jul 14, 2024
6c35a6b
Refactor functions involving operations on high/ low order bits, maki…
itzmeanjan Jul 14, 2024
63ea8fc
Update command to find all ML-DSA source files
itzmeanjan Jul 14, 2024
3a0a2ee
Make compile-time *must* evaluable constants `static constexpr`
itzmeanjan Jul 16, 2024
38525ba
Refactor header unit holding compile-time evaluable parameter validat…
itzmeanjan Jul 16, 2024
9f26338
Refactor the example program, ensuring it works with recent API changes
itzmeanjan Jul 16, 2024
07ed211
Clean up header unit implementing utility functions
itzmeanjan Jul 16, 2024
7f877ff
Refactor shake256 based pseudo random number generator
itzmeanjan Jul 16, 2024
21e32ba
Refactor number theoretic transform implementation
itzmeanjan Jul 17, 2024
3c25760
Refactor polynomial arithmetic functions
itzmeanjan Jul 20, 2024
2fe2b0d
Refactor bit-packing operations
itzmeanjan Jul 20, 2024
5a84850
Refactor arithmetic and utility functions for vector of polynomials
itzmeanjan Jul 20, 2024
df5ec2a
Refactor polynomial and vector of polynomials sampling functions
itzmeanjan Jul 20, 2024
177892b
Refactor ML-DSA core keygen, sign and verify functions
itzmeanjan Jul 20, 2024
0f79ef3
Refactor ML-DSA-44 instantiation
itzmeanjan Jul 21, 2024
e3a3e36
Refactor ML-DSA-65 instantiation
itzmeanjan Jul 21, 2024
a8ff69e
Refactor ML-DSA-87 instantiation
itzmeanjan Jul 21, 2024
3f26b10
Add new header-unit for holding test helper functions
itzmeanjan Jul 22, 2024
9eb9a38
Add alternate function definition for `from_hex`
itzmeanjan Jul 22, 2024
27d3857
Reformat source tree while bumping `ColumnLimit` to *172*
itzmeanjan Jul 22, 2024
4cb4cad
Refactor ML-DSA Known Answer Tests - split them into different transl…
itzmeanjan Jul 22, 2024
5ebbe13
Refactor ML-DSA property-based tests - split them into different tran…
itzmeanjan Jul 22, 2024
e8f8739
Refactor tests ensuring correctness of internal functions
itzmeanjan Jul 22, 2024
5afa94f
Move (and rename test functions) dudect -based constant-time tests
itzmeanjan Jul 22, 2024
1bc2283
Ensure that test headers also gets formatted properly
itzmeanjan Jul 22, 2024
edad601
Use PRNG of correct security level in tests
itzmeanjan Jul 24, 2024
ddfcde0
Refactor benchmark harness to conform to new API
itzmeanjan Jul 24, 2024
8d95572
Update API usage example
itzmeanjan Jul 24, 2024
95dcd4c
Minor updates to Github Actions script
itzmeanjan Jul 24, 2024
c8021cd
Update project documentation
itzmeanjan Aug 1, 2024
5bec271
Merge branch 'master' into conform-to-nist-fips-204-ipd
itzmeanjan Aug 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakStringLiterals: true
ColumnLimit: 120
ColumnLimit: 172
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 2
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Test Dilithium Post-Quantum Digital Signature Scheme
name: Test NIST FIPS 204 ML-DSA

on:
push:
Expand All @@ -23,8 +23,8 @@ jobs:
mkdir build
pushd build
cmake .. -DBUILD_GMOCK=OFF
make
sudo make install
make -j
sudo make install -j
popd
popd
popd
Expand Down
11 changes: 6 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ DEP_IFLAGS = -I $(SHA3_INC_DIR)
DUDECT_DEP_IFLAGS = $(DEP_IFLAGS) -I $(DUDECT_INC_DIR)

SRC_DIR = include
DILITHIUM_SOURCES := $(wildcard $(SRC_DIR)/*.hpp)
ML_DSA_SOURCES := $(shell find $(SRC_DIR) -name '*.hpp')
BUILD_DIR = build
ASAN_BUILD_DIR = $(BUILD_DIR)/asan
UBSAN_BUILD_DIR = $(BUILD_DIR)/ubsan
Expand All @@ -22,6 +22,7 @@ DUDECT_BUILD_DIR = $(BUILD_DIR)/dudect
TEST_DIR = tests
DUDECT_TEST_DIR = $(TEST_DIR)/dudect
TEST_SOURCES := $(wildcard $(TEST_DIR)/*.cpp)
TEST_HEADERS := $(wildcard $(TEST_DIR)/*.hpp)
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))))
Expand Down Expand Up @@ -57,13 +58,13 @@ $(BUILD_DIR):
mkdir -p $@

$(SHA3_INC_DIR):
git submodule update --init
git submodule update --init sha3

$(GTEST_PARALLEL): $(SHA3_INC_DIR)
git submodule update --init
git submodule update --init gtest-parallel

$(DUDECT_INC_DIR): $(GTEST_PARALLEL)
git submodule update --init
git submodule update --init dudect

$(BUILD_DIR)/%.o: $(TEST_DIR)/%.cpp $(BUILD_DIR) $(SHA3_INC_DIR)
$(CXX) $(CXX_FLAGS) $(WARN_FLAGS) $(OPT_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@
Expand Down Expand Up @@ -119,5 +120,5 @@ perf: $(PERF_BINARY)
clean:
rm -rf $(BUILD_DIR)

format: $(DILITHIUM_SOURCES) $(TEST_SOURCES) $(DUDECT_TEST_SOURCES) $(BENCHMARK_SOURCES) $(BENCHMARK_HEADERS)
format: $(ML_DSA_SOURCES) $(TEST_SOURCES) $(TEST_HEADERS) $(DUDECT_TEST_SOURCES) $(BENCHMARK_SOURCES) $(BENCHMARK_HEADERS)
clang-format -i $^
541 changes: 166 additions & 375 deletions README.md

Large diffs are not rendered by default.

102 changes: 102 additions & 0 deletions benchmarks/bench_ml_dsa_44.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "bench_helper.hpp"
#include "ml_dsa/ml_dsa_44.hpp"
#include <benchmark/benchmark.h>

// Benchmark performance of ML-DSA-44 key generation algorithm.
void
ml_dsa_44_keygen(benchmark::State& state)
{
std::array<uint8_t, ml_dsa_44::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_44::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_44::SecKeyByteLen> seckey{};

ml_dsa_prng::prng_t<128> prng;
prng.read(seed);

for (auto _ : state) {
ml_dsa_44::keygen(seed, pubkey, seckey);

benchmark::DoNotOptimize(seed);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(seckey);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
}

// Benchmark performance of ML-DSA-44 signing algorithm.
void
ml_dsa_44_sign(benchmark::State& state)
{
const size_t mlen = state.range(0);

std::vector<uint8_t> msg(mlen, 0);
auto msg_span = std::span(msg);

std::array<uint8_t, ml_dsa_44::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_44::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_44::SecKeyByteLen> seckey{};
std::array<uint8_t, ml_dsa_44::SigningSeedByteLen> rnd{};
std::array<uint8_t, ml_dsa_44::SigByteLen> sig{};

ml_dsa_prng::prng_t<128> prng;
prng.read(seed);
prng.read(rnd);
prng.read(msg_span);

ml_dsa_44::keygen(seed, pubkey, seckey);

for (auto _ : state) {
ml_dsa_44::sign(rnd, seckey, msg_span, sig);

benchmark::DoNotOptimize(rnd);
benchmark::DoNotOptimize(seckey);
benchmark::DoNotOptimize(msg_span);
benchmark::DoNotOptimize(sig);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
assert(ml_dsa_44::verify(pubkey, msg_span, sig));
}

// Benchmark performance of ML-DSA-44 signature verification algorithm.
void
ml_dsa_44_verify(benchmark::State& state)
{
const size_t mlen = state.range(0);

std::vector<uint8_t> msg(mlen, 0);
auto msg_span = std::span(msg);

std::array<uint8_t, ml_dsa_44::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_44::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_44::SecKeyByteLen> seckey{};
std::array<uint8_t, ml_dsa_44::SigningSeedByteLen> rnd{};
std::array<uint8_t, ml_dsa_44::SigByteLen> sig{};

ml_dsa_prng::prng_t<128> prng;
prng.read(seed);
prng.read(rnd);
prng.read(msg_span);

ml_dsa_44::keygen(seed, pubkey, seckey);
ml_dsa_44::sign(rnd, seckey, msg_span, sig);

for (auto _ : state) {
bool is_valid = ml_dsa_44::verify(pubkey, msg_span, sig);

benchmark::DoNotOptimize(is_valid);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(msg_span);
benchmark::DoNotOptimize(sig);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
}

BENCHMARK(ml_dsa_44_keygen)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(ml_dsa_44_sign)->Arg(32)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(ml_dsa_44_verify)->Arg(32)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
102 changes: 102 additions & 0 deletions benchmarks/bench_ml_dsa_65.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "bench_helper.hpp"
#include "ml_dsa/ml_dsa_65.hpp"
#include <benchmark/benchmark.h>

// Benchmark performance of ML-DSA-65 key generation algorithm.
void
ml_dsa_65_keygen(benchmark::State& state)
{
std::array<uint8_t, ml_dsa_65::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_65::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_65::SecKeyByteLen> seckey{};

ml_dsa_prng::prng_t<192> prng;
prng.read(seed);

for (auto _ : state) {
ml_dsa_65::keygen(seed, pubkey, seckey);

benchmark::DoNotOptimize(seed);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(seckey);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
}

// Benchmark performance of ML-DSA-65 signing algorithm.
void
ml_dsa_65_sign(benchmark::State& state)
{
const size_t mlen = state.range(0);

std::vector<uint8_t> msg(mlen, 0);
auto msg_span = std::span(msg);

std::array<uint8_t, ml_dsa_65::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_65::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_65::SecKeyByteLen> seckey{};
std::array<uint8_t, ml_dsa_65::SigningSeedByteLen> rnd{};
std::array<uint8_t, ml_dsa_65::SigByteLen> sig{};

ml_dsa_prng::prng_t<192> prng;
prng.read(seed);
prng.read(rnd);
prng.read(msg_span);

ml_dsa_65::keygen(seed, pubkey, seckey);

for (auto _ : state) {
ml_dsa_65::sign(rnd, seckey, msg_span, sig);

benchmark::DoNotOptimize(rnd);
benchmark::DoNotOptimize(seckey);
benchmark::DoNotOptimize(msg_span);
benchmark::DoNotOptimize(sig);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
assert(ml_dsa_65::verify(pubkey, msg_span, sig));
}

// Benchmark performance of ML-DSA-65 signature verification algorithm.
void
ml_dsa_65_verify(benchmark::State& state)
{
const size_t mlen = state.range(0);

std::vector<uint8_t> msg(mlen, 0);
auto msg_span = std::span(msg);

std::array<uint8_t, ml_dsa_65::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_65::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_65::SecKeyByteLen> seckey{};
std::array<uint8_t, ml_dsa_65::SigningSeedByteLen> rnd{};
std::array<uint8_t, ml_dsa_65::SigByteLen> sig{};

ml_dsa_prng::prng_t<192> prng;
prng.read(seed);
prng.read(rnd);
prng.read(msg_span);

ml_dsa_65::keygen(seed, pubkey, seckey);
ml_dsa_65::sign(rnd, seckey, msg_span, sig);

for (auto _ : state) {
bool is_valid = ml_dsa_65::verify(pubkey, msg_span, sig);

benchmark::DoNotOptimize(is_valid);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(msg_span);
benchmark::DoNotOptimize(sig);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
}

BENCHMARK(ml_dsa_65_keygen)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(ml_dsa_65_sign)->Arg(32)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(ml_dsa_65_verify)->Arg(32)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
102 changes: 102 additions & 0 deletions benchmarks/bench_ml_dsa_87.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "bench_helper.hpp"
#include "ml_dsa/ml_dsa_87.hpp"
#include <benchmark/benchmark.h>

// Benchmark performance of ML-DSA-87 key generation algorithm.
void
ml_dsa_87_keygen(benchmark::State& state)
{
std::array<uint8_t, ml_dsa_87::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_87::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_87::SecKeyByteLen> seckey{};

ml_dsa_prng::prng_t<256> prng;
prng.read(seed);

for (auto _ : state) {
ml_dsa_87::keygen(seed, pubkey, seckey);

benchmark::DoNotOptimize(seed);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(seckey);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
}

// Benchmark performance of ML-DSA-87 signing algorithm.
void
ml_dsa_87_sign(benchmark::State& state)
{
const size_t mlen = state.range(0);

std::vector<uint8_t> msg(mlen, 0);
auto msg_span = std::span(msg);

std::array<uint8_t, ml_dsa_87::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_87::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_87::SecKeyByteLen> seckey{};
std::array<uint8_t, ml_dsa_87::SigningSeedByteLen> rnd{};
std::array<uint8_t, ml_dsa_87::SigByteLen> sig{};

ml_dsa_prng::prng_t<256> prng;
prng.read(seed);
prng.read(rnd);
prng.read(msg_span);

ml_dsa_87::keygen(seed, pubkey, seckey);

for (auto _ : state) {
ml_dsa_87::sign(rnd, seckey, msg_span, sig);

benchmark::DoNotOptimize(rnd);
benchmark::DoNotOptimize(seckey);
benchmark::DoNotOptimize(msg_span);
benchmark::DoNotOptimize(sig);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
assert(ml_dsa_87::verify(pubkey, msg_span, sig));
}

// Benchmark performance of ML-DSA-87 signature verification algorithm.
void
ml_dsa_87_verify(benchmark::State& state)
{
const size_t mlen = state.range(0);

std::vector<uint8_t> msg(mlen, 0);
auto msg_span = std::span(msg);

std::array<uint8_t, ml_dsa_87::KeygenSeedByteLen> seed{};
std::array<uint8_t, ml_dsa_87::PubKeyByteLen> pubkey{};
std::array<uint8_t, ml_dsa_87::SecKeyByteLen> seckey{};
std::array<uint8_t, ml_dsa_87::SigningSeedByteLen> rnd{};
std::array<uint8_t, ml_dsa_87::SigByteLen> sig{};

ml_dsa_prng::prng_t<256> prng;
prng.read(seed);
prng.read(rnd);
prng.read(msg_span);

ml_dsa_87::keygen(seed, pubkey, seckey);
ml_dsa_87::sign(rnd, seckey, msg_span, sig);

for (auto _ : state) {
bool is_valid = ml_dsa_87::verify(pubkey, msg_span, sig);

benchmark::DoNotOptimize(is_valid);
benchmark::DoNotOptimize(pubkey);
benchmark::DoNotOptimize(msg_span);
benchmark::DoNotOptimize(sig);
benchmark::ClobberMemory();
}

state.SetItemsProcessed(state.iterations());
}

BENCHMARK(ml_dsa_87_keygen)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(ml_dsa_87_sign)->Arg(32)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
BENCHMARK(ml_dsa_87_verify)->Arg(32)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max);
Loading