Skip to content

Make ML-DSA Sign Benchmarking Deterministic #37

@itzmeanjan

Description

@itzmeanjan

When benchmarking ML-DSA sign function, we random sample all input arguments, making the algorithm non-deterministic - resulting in variable timing. This doesn't give correct performance profile during benchmarking.

// 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{};
randomshake::randomshake_t<128> csprng;
csprng.generate(seed);
csprng.generate(rnd);
csprng.generate(msg_span);
ml_dsa_44::keygen(seed, pubkey, seckey);
bool has_signed = true;
for (auto _ : state) {
benchmark::DoNotOptimize(has_signed);
benchmark::DoNotOptimize(rnd);
benchmark::DoNotOptimize(seckey);
benchmark::DoNotOptimize(msg_span);
benchmark::DoNotOptimize(sig);
has_signed &= ml_dsa_44::sign(rnd, seckey, msg_span, {}, sig);
benchmark::ClobberMemory();
}
assert(has_signed);
assert(ml_dsa_44::verify(pubkey, msg_span, {}, sig));
state.SetItemsProcessed(state.iterations());
}

To avoid this, we can seed the RandomShake CSRPNG (cryptographically secure pseudo-random number generator) with a fixed seed. Address it and update benchmark results. This will also make it easy to spot any performance degradation or optimization.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions