From cae87a27909c6ca6ed10cf130be94b2db6ea6302 Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Fri, 13 Dec 2024 04:58:33 +0100 Subject: [PATCH] chore: add signatures benchmarks (#354) --- CHANGELOG.md | 1 + Cargo.lock | 10 +++--- Cargo.toml | 12 ++++--- benches/README.md | 58 +++++++++++++++++++++++++++---- benches/dsa.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 benches/dsa.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 762a583..3481d28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [BREAKING] Refactor error messages and use `thiserror` to derive errors (#344). - [BREAKING] Updated Winterfell dependency to v0.11 (#346). - Added RPO-STARK based DSA (#349). +- Added benchmarks for DSA implementations (#354). ## 0.12.0 (2024-10-30) diff --git a/Cargo.lock b/Cargo.lock index ea60d06..56f7d47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -526,7 +526,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miden-crypto" -version = "0.13.0" +version = "0.14.0" dependencies = [ "assert_matches", "blake3", @@ -865,18 +865,18 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 2b7b579..0c0d603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "miden-crypto" -version = "0.13.0" +version = "0.14.0" description = "Miden Cryptographic primitives" authors = ["miden contributors"] readme = "README.md" license = "MIT" repository = "https://github.com/0xPolygonMiden/crypto" -documentation = "https://docs.rs/miden-crypto/0.13.0" +documentation = "https://docs.rs/miden-crypto/0.14.0" categories = ["cryptography", "no-std"] keywords = ["miden", "crypto", "hash", "merkle"] edition = "2021" @@ -19,6 +19,10 @@ bench = false doctest = false required-features = ["executable"] +[[bench]] +name = "dsa" +harness = false + [[bench]] name = "hash" harness = false @@ -63,11 +67,13 @@ std = [ [dependencies] blake3 = { version = "1.5", default-features = false } clap = { version = "4.5", optional = true, features = ["derive"] } +getrandom = { version = "0.2", features = ["js"] } num = { version = "0.4", default-features = false, features = ["alloc", "libm"] } num-complex = { version = "0.4", default-features = false } rand = { version = "0.8", default-features = false } rand_chacha = { version = "0.3", default-features = false } rand_core = { version = "0.6", default-features = false } +rand-utils = {git = 'https://github.com/Al-Kindi-0/winterfell', package = "winter-rand-utils" , branch = 'al-zk', optional = true } rayon = { version = "1.10", optional = true } serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } sha3 = { version = "0.10", default-features = false } @@ -78,8 +84,6 @@ winter-prover = {git = 'https://github.com/Al-Kindi-0/winterfell', branch = 'al- winter-verifier = {git = 'https://github.com/Al-Kindi-0/winterfell', branch = 'al-zk' } winter-math = {git = 'https://github.com/Al-Kindi-0/winterfell', branch = 'al-zk' } winter-utils = {git = 'https://github.com/Al-Kindi-0/winterfell', branch = 'al-zk' } -rand-utils = {git = 'https://github.com/Al-Kindi-0/winterfell', package = "winter-rand-utils" , branch = 'al-zk', optional = true } -getrandom = { version = "0.2", features = ["js"] } [dev-dependencies] assert_matches = { version = "1.5", default-features = false } diff --git a/benches/README.md b/benches/README.md index 3113d62..d8ae865 100644 --- a/benches/README.md +++ b/benches/README.md @@ -1,4 +1,6 @@ -# Miden VM Hash Functions +# Benchmarks + +## Miden VM Hash Functions In the Miden VM, we make use of different hash functions. Some of these are "traditional" hash functions, like `BLAKE3`, which are optimized for out-of-STARK performance, while others are algebraic hash functions, like `Rescue Prime`, and are more optimized for a better performance inside the STARK. In what follows, we benchmark several such hash functions and compare against other constructions that are used by other proving systems. More precisely, we benchmark: * **BLAKE3** as specified [here](https://github.com/BLAKE3-team/BLAKE3-specs/blob/master/blake3.pdf) and implemented [here](https://github.com/BLAKE3-team/BLAKE3) (with a wrapper exposed via this crate). @@ -8,13 +10,13 @@ In the Miden VM, we make use of different hash functions. Some of these are "tra * **Rescue Prime Optimized (RPO)** as specified [here](https://eprint.iacr.org/2022/1577) and implemented in this crate. * **Rescue Prime Extended (RPX)** a variant of the [xHash](https://eprint.iacr.org/2023/1045) hash function as implemented in this crate. -## Comparison and Instructions +### Comparison and Instructions -### Comparison +#### Comparison We benchmark the above hash functions using two scenarios. The first is a 2-to-1 $(a,b)\mapsto h(a,b)$ hashing where both $a$, $b$ and $h(a,b)$ are the digests corresponding to each of the hash functions. The second scenario is that of sequential hashing where we take a sequence of length $100$ field elements and hash these to produce a single digest. The digests are $4$ field elements in a prime field with modulus $2^{64} - 2^{32} + 1$ (i.e., 32 bytes) for Poseidon, Rescue Prime and RPO, and an array `[u8; 32]` for SHA3 and BLAKE3. -#### Scenario 1: 2-to-1 hashing `h(a,b)` +##### Scenario 1: 2-to-1 hashing `h(a,b)` | Function | BLAKE3 | SHA3 | Poseidon | Rp64_256 | RPO_256 | RPX_256 | | ------------------- | ------ | ------- | --------- | --------- | ------- | ------- | @@ -26,7 +28,7 @@ The second scenario is that of sequential hashing where we take a sequence of le | Intel Core i5-8279U | 68 ns | 536 ns | 2.0 µs | 13.6 µs | 8.5 µs | 4.4 µs | | Intel Xeon 8375C | 67 ns | | | | 8.2 µs | | -#### Scenario 2: Sequential hashing of 100 elements `h([a_0,...,a_99])` +##### Scenario 2: Sequential hashing of 100 elements `h([a_0,...,a_99])` | Function | BLAKE3 | SHA3 | Poseidon | Rp64_256 | RPO_256 | RPX_256 | | ------------------- | -------| ------- | --------- | --------- | ------- | ------- | @@ -42,7 +44,7 @@ Notes: - On Graviton 3, RPO256 and RPX256 are run with SVE acceleration enabled. - On AMD EPYC 9R14, RPO256 and RPX256 are run with AVX2 acceleration enabled. -### Instructions +#### Instructions Before you can run the benchmarks, you'll need to make sure you have Rust [installed](https://www.rust-lang.org/tools/install). After that, to run the benchmarks for RPO and BLAKE3, clone the current repository, and from the root directory of the repo run the following: ``` @@ -54,3 +56,47 @@ To run the benchmarks for Rescue Prime, Poseidon and SHA3, clone the following [ ``` cargo bench hash ``` + +## Miden VM DSA + +We make use of the following digital signature algorithms (DSA) in the Miden VM: + +* **RPO-Falcon512** as specified [here](https://falcon-sign.info/falcon.pdf) with the one difference being the use of the RPO hash function for the hash-to-point algorithm (Algorithm 3 in the previous reference) instead of SHAKE256. +* **RPO-STARK** as specified [here](https://eprint.iacr.org/2024/1553), where the parameters are the ones for the unique-decoding regime (UDR) with the two differences: + * We rely on Conjecture 1 in the [ethSTARK](https://eprint.iacr.org/2021/582) paper. + * The number of FRI queries is $30$ and the grinding factor is $12$ bits. Thus using the previous point we can argue that the modified version achieves at least $102$ bits of average-case existential unforgeability security against $2^{113}$-query bound adversaries that can obtain up to $2^{64}$ signatures under the same public key. + + + +### Comparison and Instructions + +#### Comparison + + +##### Key Generation + +| DSA | RPO-Falcon512 | RPO-STARK | +| ------------------- | :-----------: | :-------: | +| Apple M1 Pro | 590 ms | 6 µs | +| Intel Core i5-8279U | 585 ms | 10 µs | + +##### Signature Generation + +| DSA | RPO-Falcon512 | RPO-STARK | +| ------------------- | :-----------: | :-------: | +| Apple M1 Pro | 1.5 ms | 78 ms | +| Intel Core i5-8279U | 1.8 ms | 130 ms | + +##### Signature Verification + +| DSA | RPO-Falcon512 | RPO-STARK | +| ------------------- | :-----------: | :-------: | +| Apple M1 Pro | 0.7 ms | 4.5 ms | +| Intel Core i5-8279U | 1.2 ms | 7.9 ms | + +#### Instructions +Before you can run the benchmarks, you'll need to make sure you have Rust [installed](https://www.rust-lang.org/tools/install). After that, to run the benchmarks, clone the current repository, and from the root directory of the repo run the following: + + ``` + cargo bench --bench dsa + ``` \ No newline at end of file diff --git a/benches/dsa.rs b/benches/dsa.rs new file mode 100644 index 0000000..5c5da0a --- /dev/null +++ b/benches/dsa.rs @@ -0,0 +1,88 @@ +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use miden_crypto::dsa::{ + rpo_falcon512::SecretKey as FalconSecretKey, rpo_stark::SecretKey as RpoStarkSecretKey, +}; +use rand_utils::rand_array; + +fn key_gen_falcon(c: &mut Criterion) { + c.bench_function("Falcon public key generation", |bench| { + bench.iter_batched(|| FalconSecretKey::new(), |sk| sk.public_key(), BatchSize::SmallInput) + }); + + c.bench_function("Falcon secret key generation", |bench| { + bench.iter_batched(|| {}, |_| FalconSecretKey::new(), BatchSize::SmallInput) + }); +} + +fn key_gen_rpo_stark(c: &mut Criterion) { + c.bench_function("RPO-STARK public key generation", |bench| { + bench.iter_batched( + || RpoStarkSecretKey::random(), + |sk| sk.public_key(), + BatchSize::SmallInput, + ) + }); + + c.bench_function("RPO-STARK secret key generation", |bench| { + bench.iter_batched(|| {}, |_| RpoStarkSecretKey::random(), BatchSize::SmallInput) + }); +} + +fn signature_gen_falcon(c: &mut Criterion) { + c.bench_function("Falcon signature generation", |bench| { + bench.iter_batched( + || (FalconSecretKey::new(), rand_array().into()), + |(sk, msg)| sk.sign(msg), + BatchSize::SmallInput, + ) + }); +} + +fn signature_gen_rpo_stark(c: &mut Criterion) { + c.bench_function("RPO-STARK signature generation", |bench| { + bench.iter_batched( + || (RpoStarkSecretKey::random(), rand_array().into()), + |(sk, msg)| sk.sign(msg), + BatchSize::SmallInput, + ) + }); +} + +fn signature_ver_falcon(c: &mut Criterion) { + c.bench_function("Falcon signature verification", |bench| { + bench.iter_batched( + || { + let sk = FalconSecretKey::new(); + let msg = rand_array().into(); + (sk.public_key(), msg, sk.sign(msg)) + }, + |(pk, msg, sig)| pk.verify(msg, &sig), + BatchSize::SmallInput, + ) + }); +} + +fn signature_ver_rpo_stark(c: &mut Criterion) { + c.bench_function("RPO-STARK signature verification", |bench| { + bench.iter_batched( + || { + let sk = RpoStarkSecretKey::random(); + let msg = rand_array().into(); + (sk.public_key(), msg, sk.sign(msg)) + }, + |(pk, msg, sig)| pk.verify(msg, &sig), + BatchSize::SmallInput, + ) + }); +} + +criterion_group!( + dsa_group, + key_gen_falcon, + key_gen_rpo_stark, + signature_gen_falcon, + signature_gen_rpo_stark, + signature_ver_falcon, + signature_ver_rpo_stark +); +criterion_main!(dsa_group);