diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f2fb11a..254c171 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -103,7 +103,39 @@ jobs: - uses: hecrj/setup-rust-action@v2 - run: rustup target add ${{ matrix.target }} - run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} + + benches: + name: cargo bench compilation + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + backend_feature: + - --features ristretto255-ciphersuite + - + frontend_feature: + - + - --features danger + - --features serde + toolchain: + - stable + - 1.65.0 + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Install ${{ matrix.toolchain }} toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + override: true + + - name: Run cargo bench --no-run + uses: actions-rs/cargo@v1 + with: + command: bench + args: --no-default-features ${{ matrix.backend_feature }} --no-run clippy: name: cargo clippy diff --git a/Cargo.toml b/Cargo.toml index c96c30e..11eec3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,8 @@ subtle = { version = "2.3", default-features = false } zeroize = { version = "1.5", default-features = false } [dev-dependencies] +criterion = "0.3" +paste = "1.0" generic-array = { version = "0.14", features = ["more_lengths"] } hex = "0.4" p256 = { version = "0.13", default-features = false, features = [ @@ -63,6 +65,18 @@ regex = "1" serde_json = "1" sha2 = "0.10" +[[bench]] +name = "oprf" +harness = false + +[[bench]] +name = "voprf" +harness = false + +[[bench]] +name = "poprf" +harness = false + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] diff --git a/README.md b/README.md index a001518..580940d 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,15 @@ voprf = "0.5" Rust **1.65** or higher. +Microbenchmarks +--------------- + +The library also comes with a suite of microbenchmarks for each mode `oprf`, `voprf`, `poprf` that can be run via: +``` +cargo bench --bench +``` + + Contributors ------------ diff --git a/benches/oprf.rs b/benches/oprf.rs new file mode 100644 index 0000000..149f199 --- /dev/null +++ b/benches/oprf.rs @@ -0,0 +1,91 @@ +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use paste::paste; + +use p256::NistP256 as P256; +use p384::NistP384 as P384; +use p521::NistP521 as P521; +use rand::{rngs::StdRng, SeedableRng}; +use voprf::{OprfClient, OprfServer, Ristretto255}; + +macro_rules! make_oprf_benches { + ($cipher_suite:ident) => { + + paste! { + fn [](c: &mut Criterion) { + let rng = StdRng::seed_from_u64(0_u64); + c.bench_function(&format!("{}_{}", "oprf_client_blind", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter_batched_ref( + || rng.clone(), + |mut rng| { + OprfClient::<$cipher_suite>::blind(b"input", &mut rng) + .expect("Unable to construct client") + }, + BatchSize::SmallInput, + ) + }); + } + } + + paste! { + fn [](c: &mut Criterion) { + let mut rng = StdRng::seed_from_u64(0_u64); + let server = OprfServer::<$cipher_suite>::new(&mut rng).unwrap(); + let client_blind_result = + OprfClient::<$cipher_suite>::blind(b"input", &mut rng).expect("Unable to construct client"); + c.bench_function(&format!("{}_{}", "oprf_server_eval", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter( + || { + server + .blind_evaluate(&client_blind_result.message); + } + ) + }); + } + } + + paste! { + fn [](c: &mut Criterion) { + let mut rng = StdRng::seed_from_u64(0_u64); + let server = OprfServer::<$cipher_suite>::new(&mut rng).unwrap(); + let client_blind_result = + OprfClient::<$cipher_suite>::blind(b"input", &mut rng).expect("Unable to construct client"); + let server_evaluate_result = server + .blind_evaluate(&client_blind_result.message); + c.bench_function(&format!("{}_{}", "oprf_client_final", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter(|| { + client_blind_result + .state + .finalize( + b"input", + &server_evaluate_result, + ) + .expect("Unable to perform client finalization") + }) + }); + } + } + + }; +} + +make_oprf_benches!(Ristretto255); +make_oprf_benches!(P256); +make_oprf_benches!(P384); +make_oprf_benches!(P521); + +criterion_group!( + oprf, + bench_oprf_client_blind_ristretto255, + bench_oprf_server_evaluate_ristretto255, + bench_oprf_client_finalize_ristretto255, + bench_oprf_client_blind_p256, + bench_oprf_server_evaluate_p256, + bench_oprf_client_finalize_p256, + bench_oprf_client_blind_p384, + bench_oprf_server_evaluate_p384, + bench_oprf_client_finalize_p384, + bench_oprf_client_blind_p521, + bench_oprf_server_evaluate_p521, + bench_oprf_client_finalize_p521 +); +criterion_main!(oprf); diff --git a/benches/poprf.rs b/benches/poprf.rs new file mode 100644 index 0000000..7abb188 --- /dev/null +++ b/benches/poprf.rs @@ -0,0 +1,98 @@ +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use paste::paste; + +use p256::NistP256 as P256; +use p384::NistP384 as P384; +use p521::NistP521 as P521; +use rand::{rngs::StdRng, SeedableRng}; +use voprf::{PoprfClient, PoprfServer, Ristretto255}; + +macro_rules! make_poprf_benches { + ($cipher_suite:ident) => { + + paste! { + fn [](c: &mut Criterion) { + let rng = StdRng::seed_from_u64(0_u64); + c.bench_function(&format!("{}_{}", "poprf_client_blind", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter_batched_ref( + || rng.clone(), + |mut rng| { + PoprfClient::<$cipher_suite>::blind(b"input", &mut rng) + .expect("Unable to construct client") + }, + BatchSize::SmallInput, + ) + }); + } + } + + paste! { + fn [](c: &mut Criterion) { + let mut rng = StdRng::seed_from_u64(0_u64); + let server = PoprfServer::<$cipher_suite>::new(&mut rng).unwrap(); + let client_blind_result = + PoprfClient::<$cipher_suite>::blind(b"input", &mut rng).expect("Unable to construct client"); + c.bench_function(&format!("{}_{}", "poprf_server_eval", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter_batched_ref( + || rng.clone(), + |mut rng| { + server + .blind_evaluate(&mut rng, &client_blind_result.message, Some(b"tag")) + .expect("Unable to perform server evaluation") + }, + BatchSize::SmallInput, + ) + }); + } + } + + paste! { + fn [](c: &mut Criterion) { + let mut rng = StdRng::seed_from_u64(0_u64); + let server = PoprfServer::<$cipher_suite>::new(&mut rng).unwrap(); + let client_blind_result = + PoprfClient::<$cipher_suite>::blind(b"input", &mut rng).expect("Unable to construct client"); + let server_evaluate_result = server + .blind_evaluate(&mut rng, &client_blind_result.message, Some(b"tag")) + .expect("Unable to perform server evaluation"); + c.bench_function(&format!("{}_{}", "poprf_client_final", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter(|| { + client_blind_result + .state + .finalize( + b"input", + &server_evaluate_result.message, + &server_evaluate_result.proof, + server.get_public_key(), + Some(b"tag"), + ) + .expect("Unable to perform client finalization") + }) + }); + } + } + + }; +} + +make_poprf_benches!(Ristretto255); +make_poprf_benches!(P256); +make_poprf_benches!(P384); +make_poprf_benches!(P521); + +criterion_group!( + poprf, + bench_poprf_client_blind_ristretto255, + bench_poprf_server_evaluate_ristretto255, + bench_poprf_client_finalize_ristretto255, + bench_poprf_client_blind_p256, + bench_poprf_server_evaluate_p256, + bench_poprf_client_finalize_p256, + bench_poprf_client_blind_p384, + bench_poprf_server_evaluate_p384, + bench_poprf_client_finalize_p384, + bench_poprf_client_blind_p521, + bench_poprf_server_evaluate_p521, + bench_poprf_client_finalize_p521 +); +criterion_main!(poprf); diff --git a/benches/voprf.rs b/benches/voprf.rs new file mode 100644 index 0000000..f89bec7 --- /dev/null +++ b/benches/voprf.rs @@ -0,0 +1,95 @@ +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use paste::paste; + +use p256::NistP256 as P256; +use p384::NistP384 as P384; +use p521::NistP521 as P521; +use rand::{rngs::StdRng, SeedableRng}; +use voprf::{Ristretto255, VoprfClient, VoprfServer}; + +macro_rules! make_voprf_benches { + ($cipher_suite:ident) => { + + paste! { + fn [](c: &mut Criterion) { + let rng = StdRng::seed_from_u64(0_u64); + c.bench_function(&format!("{}_{}", "voprf_client_blind", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter_batched_ref( + || rng.clone(), + |mut rng| { + VoprfClient::<$cipher_suite>::blind(b"input", &mut rng) + .expect("Unable to construct client") + }, + BatchSize::SmallInput, + ) + }); + } + } + + paste! { + fn [](c: &mut Criterion) { + let mut rng = StdRng::seed_from_u64(0_u64); + let server = VoprfServer::<$cipher_suite>::new(&mut rng).unwrap(); + let client_blind_result = + VoprfClient::<$cipher_suite>::blind(b"input", &mut rng).expect("Unable to construct client"); + c.bench_function(&format!("{}_{}", "voprf_server_eval", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter_batched_ref( + || rng.clone(), + |mut rng| { + server + .blind_evaluate(&mut rng, &client_blind_result.message); + }, + BatchSize::SmallInput, + ) + }); + } + } + + paste! { + fn [](c: &mut Criterion) { + let mut rng = StdRng::seed_from_u64(0_u64); + let server = VoprfServer::<$cipher_suite>::new(&mut rng).unwrap(); + let client_blind_result = + VoprfClient::<$cipher_suite>::blind(b"input", &mut rng).expect("Unable to construct client"); + let server_evaluate_result = server + .blind_evaluate(&mut rng, &client_blind_result.message); + c.bench_function(&format!("{}_{}", "voprf_client_final", stringify!($cipher_suite).to_lowercase()), move |b| { + b.iter(|| { + client_blind_result + .state + .finalize( + b"input", + &server_evaluate_result.message, + &server_evaluate_result.proof, + server.get_public_key(), + ) + .expect("Unable to perform client finalization") + }) + }); + } + } + + }; +} + +make_voprf_benches!(Ristretto255); +make_voprf_benches!(P256); +make_voprf_benches!(P384); +make_voprf_benches!(P521); + +criterion_group!( + voprf, + bench_voprf_client_blind_ristretto255, + bench_voprf_server_evaluate_ristretto255, + bench_voprf_client_finalize_ristretto255, + bench_voprf_client_blind_p256, + bench_voprf_server_evaluate_p256, + bench_voprf_client_finalize_p256, + bench_voprf_client_blind_p384, + bench_voprf_server_evaluate_p384, + bench_voprf_client_finalize_p384, + bench_voprf_client_blind_p521, + bench_voprf_server_evaluate_p521, + bench_voprf_client_finalize_p521 +); +criterion_main!(voprf);