add benchmarks (#205)

* add benchmarks

* Apply suggestions from code review

Co-authored-by: Marek <mail@marek.onl>

* cargo fmt

---------

Co-authored-by: Marek <mail@marek.onl>
This commit is contained in:
Conrado Gouvea 2023-01-30 15:48:13 -03:00 committed by GitHub
parent 084ed95f46
commit 956d8d3c7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 348 additions and 77 deletions

View File

@ -30,6 +30,7 @@ zeroize = { version = "1.5.4", default-features = false, features = ["derive"] }
proptest = { version = "1.0", optional = true }
proptest-derive = { version = "0.3", optional = true }
serde_json = { version = "1.0", optional = true }
criterion = { version = "0.4", optional = true }
[dev-dependencies]
curve25519-dalek = { version = "=4.0.0-pre.1", features = ["serde"] }
@ -43,6 +44,9 @@ sha2 = "0.10.2"
[features]
nightly = []
default = []
# Exposes ciphersuite-generic tests for other crates to use
test-impl = ["proptest", "proptest-derive", "serde_json"]
internals = []
# Exposes ciphersuite-generic tests for other crates to use
test-impl = ["proptest", "proptest-derive", "serde_json", "criterion"]
[lib]
bench = false

191
frost-core/src/benches.rs Normal file
View File

@ -0,0 +1,191 @@
//! Ciphersuite-generic benchmark functions.
use std::collections::HashMap;
use criterion::{BenchmarkId, Criterion, Throughput};
use rand_core::{CryptoRng, RngCore};
use crate::{batch, frost, Ciphersuite, Signature, SigningKey, VerifyingKey};
struct Item<C: Ciphersuite> {
vk: VerifyingKey<C>,
sig: Signature<C>,
}
fn sigs_with_distinct_keys<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
rng: &mut R,
) -> impl Iterator<Item = Item<C>> {
let mut rng = rng.clone();
std::iter::repeat_with(move || {
let msg = b"Bench";
let sk = SigningKey::new(&mut rng);
let vk = VerifyingKey::from(&sk);
let sig = sk.sign(&mut rng, &msg[..]);
Item { vk, sig }
})
}
/// Benchmark batched signature verification with the specified ciphersuite.
pub fn bench_batch_verify<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
c: &mut Criterion,
name: &str,
rng: &mut R,
) {
let mut group = c.benchmark_group(format!("Batch Verification {}", name));
for &n in [8usize, 16, 24, 32, 40, 48, 56, 64].iter() {
group.throughput(Throughput::Elements(n as u64));
let sigs = sigs_with_distinct_keys::<C, R>(rng)
.take(n)
.collect::<Vec<_>>();
group.bench_with_input(
BenchmarkId::new("Unbatched verification", n),
&sigs,
|b, sigs| {
b.iter(|| {
for item in sigs.iter() {
let msg = b"Bench";
let Item { vk, sig } = item;
let _ = vk.verify(msg, sig);
}
})
},
);
group.bench_with_input(
BenchmarkId::new("Batched verification", n),
&sigs,
|b, sigs| {
let mut rng = rng.clone();
b.iter(|| {
let mut batch = batch::Verifier::new();
for item in sigs.iter() {
let msg = b"Bench";
let Item { vk, sig } = item;
batch.queue((*vk, *sig, msg));
}
batch.verify(&mut rng)
})
},
);
}
group.finish();
}
/// Benchmark FROST signing with the specified ciphersuite.
pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
c: &mut Criterion,
name: &str,
rng: &mut R,
) {
let mut group = c.benchmark_group(format!("FROST Signing {}", name));
for &n in [3u16, 10, 100, 1000].iter() {
let max_signers = n;
let min_signers = (n * 2 + 2) / 3;
group.bench_with_input(
BenchmarkId::new("Key Generation with Dealer", max_signers),
&(max_signers, min_signers),
|b, (max_signers, min_signers)| {
let mut rng = rng.clone();
b.iter(|| {
frost::keys::keygen_with_dealer::<C, R>(*max_signers, *min_signers, &mut rng)
.unwrap();
})
},
);
let (shares, pubkeys) =
frost::keys::keygen_with_dealer::<C, R>(max_signers, min_signers, rng).unwrap();
// Verifies the secret shares from the dealer
let key_packages: HashMap<_, _> = shares
.into_iter()
.map(|share| {
(
share.identifier,
frost::keys::KeyPackage::try_from(share).unwrap(),
)
})
.collect();
group.bench_with_input(
BenchmarkId::new("Round 1", min_signers),
&key_packages,
|b, key_packages| {
b.iter(|| {
let participant_identifier = 1u16.try_into().expect("should be nonzero");
frost::round1::commit(
participant_identifier,
key_packages
.get(&participant_identifier)
.unwrap()
.secret_share(),
rng,
);
})
},
);
let mut nonces: HashMap<_, _> = HashMap::new();
let mut commitments: HashMap<_, _> = HashMap::new();
for participant_index in 1..=min_signers {
let participant_identifier = participant_index.try_into().expect("should be nonzero");
let (nonce, commitment) = frost::round1::commit(
participant_identifier,
key_packages
.get(&participant_identifier)
.unwrap()
.secret_share(),
rng,
);
nonces.insert(participant_identifier, nonce);
commitments.insert(participant_identifier, commitment);
}
let message = "message to sign".as_bytes();
let comms = commitments.clone().into_values().collect();
let signing_package = frost::SigningPackage::new(comms, message.to_vec());
group.bench_with_input(
BenchmarkId::new("Round 2", min_signers),
&(
key_packages.clone(),
nonces.clone(),
signing_package.clone(),
),
|b, (key_packages, nonces, signing_package)| {
b.iter(|| {
let participant_identifier = 1u16.try_into().expect("should be nonzero");
let key_package = key_packages.get(&participant_identifier).unwrap();
let nonces_to_use = &nonces.get(&participant_identifier).unwrap();
frost::round2::sign(signing_package, nonces_to_use, key_package).unwrap();
})
},
);
let mut signature_shares = Vec::new();
for participant_identifier in nonces.keys() {
let key_package = key_packages.get(participant_identifier).unwrap();
let nonces_to_use = &nonces.get(participant_identifier).unwrap();
let signature_share =
frost::round2::sign(&signing_package, nonces_to_use, key_package).unwrap();
signature_shares.push(signature_share);
}
group.bench_with_input(
BenchmarkId::new("Aggregate", min_signers),
&(signing_package.clone(), signature_shares.clone(), pubkeys),
|b, (signing_package, signature_shares, pubkeys)| {
b.iter(|| {
frost::aggregate(signing_package, &signature_shares[..], pubkeys).unwrap();
})
},
);
}
group.finish();
}

View File

@ -181,6 +181,7 @@ fn derive_lagrange_coeff<C: Ciphersuite>(
/// Generated by the coordinator of the signing operation and distributed to
/// each signing party
#[derive(Clone)]
pub struct SigningPackage<C: Ciphersuite> {
/// The set of commitments participants published in the first round of the
/// protocol.

View File

@ -60,11 +60,11 @@ where
/// Generates a new uniformly random secret value using the provided RNG.
// TODO: should this only be behind test?
pub fn random<R>(mut rng: R) -> Self
pub fn random<R>(rng: &mut R) -> Self
where
R: CryptoRng + RngCore,
{
Self(random_nonzero::<C, R>(&mut rng))
Self(random_nonzero::<C, R>(rng))
}
}
@ -322,15 +322,15 @@ where
pub fn keygen_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
mut rng: R,
rng: &mut R,
) -> Result<(Vec<SecretShare<C>>, PublicKeyPackage<C>), Error<C>> {
let mut bytes = [0; 64];
rng.fill_bytes(&mut bytes);
let secret = SharedSecret::random(&mut rng);
let secret = SharedSecret::random(rng);
let group_public = VerifyingKey::from(&secret);
let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, &mut rng);
let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, rng);
let secret_shares = generate_secret_shares(&secret, max_signers, min_signers, coefficients)?;
let mut signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>> =

View File

@ -14,6 +14,8 @@ use std::{
use rand_core::{CryptoRng, RngCore};
pub mod batch;
#[cfg(any(test, feature = "test-impl"))]
pub mod benches;
mod error;
pub mod frost;
mod scalar_mul;

View File

@ -44,3 +44,12 @@ serde_json = "1.0"
[features]
nightly = []
default = []
[lib]
# Disables non-criterion benchmark which is not used; prevents errors
# when using criterion-specific flags
bench = false
[[bench]]
name = "bench"
harness = false

View File

@ -0,0 +1,19 @@
use criterion::{criterion_group, criterion_main, Criterion};
use rand::thread_rng;
use frost_ed25519::*;
fn bench_ed25519_batch_verify(c: &mut Criterion) {
let mut rng = thread_rng();
frost_core::benches::bench_batch_verify::<Ed25519Sha512, _>(c, "ed25519", &mut rng);
}
fn bench_ed25519_sign(c: &mut Criterion) {
let mut rng = thread_rng();
frost_core::benches::bench_sign::<Ed25519Sha512, _>(c, "ed25519", &mut rng);
}
criterion_group!(benches, bench_ed25519_batch_verify, bench_ed25519_sign);
criterion_main!(benches);

View File

@ -42,3 +42,12 @@ serde_json = "1.0"
[features]
nightly = []
default = []
[lib]
# Disables non-criterion benchmark which is not used; prevents errors
# when using criterion-specific flags
bench = false
[[bench]]
name = "bench"
harness = false

View File

@ -0,0 +1,21 @@
use criterion::{criterion_group, criterion_main, Criterion};
use rand::thread_rng;
use frost_ed448::*;
// bench_ed448_batch_verify not included until batch verification is fixed for Ed448
#[allow(unused)]
fn bench_ed448_batch_verify(c: &mut Criterion) {
let mut rng = thread_rng();
frost_core::benches::bench_batch_verify::<Ed448Shake256, _>(c, "ed448", &mut rng);
}
fn bench_ed448_sign(c: &mut Criterion) {
let mut rng = thread_rng();
frost_core::benches::bench_sign::<Ed448Shake256, _>(c, "ed448", &mut rng);
}
criterion_group!(benches, bench_ed448_sign);
criterion_main!(benches);

View File

@ -44,6 +44,11 @@ serde_json = "1.0"
nightly = []
default = []
# [[bench]]
# name = "bench"
# harness = false
[lib]
# Disables non-criterion benchmark which is not used; prevents errors
# when using criterion-specific flags
bench = false
[[bench]]
name = "bench"
harness = false

View File

@ -0,0 +1,19 @@
use criterion::{criterion_group, criterion_main, Criterion};
use rand::thread_rng;
use frost_p256::*;
fn bench_p256_batch_verify(c: &mut Criterion) {
let mut rng = thread_rng();
frost_core::benches::bench_batch_verify::<P256Sha256, _>(c, "p256", &mut rng);
}
fn bench_p256_sign(c: &mut Criterion) {
let mut rng = thread_rng();
frost_core::benches::bench_sign::<P256Sha256, _>(c, "p256", &mut rng);
}
criterion_group!(benches, bench_p256_batch_verify, bench_p256_sign);
criterion_main!(benches);

View File

@ -25,7 +25,7 @@ sha2 = "0.10.2"
[dev-dependencies]
bincode = "1"
criterion = "0.4"
criterion = { version = "0.4", features = ["html_reports"] }
ed25519-dalek = "1.0.1"
ed25519-zebra = "3.0.0"
lazy_static = "1.4"
@ -39,6 +39,11 @@ serde_json = "1.0"
nightly = []
default = []
[lib]
# Disables non-criterion benchmark which is not used; prevents errors
# when using criterion-specific flags
bench = false
[[bench]]
name = "bench"
harness = false

View File

@ -1,72 +1,23 @@
// use std::convert::TryFrom;
use criterion::{criterion_group, criterion_main, Criterion};
use rand::thread_rng;
// use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
// use rand::thread_rng;
use frost_ristretto255::*;
// use frost_ristretto255::*;
fn bench_ristretto255_batch_verify(c: &mut Criterion) {
let mut rng = thread_rng();
// struct Item {
// vk: VerifyingKey,
// sig: Signature,
// }
frost_core::benches::bench_batch_verify::<Ristretto255Sha512, _>(c, "ristretto255", &mut rng);
}
// fn sigs_with_distinct_keys() -> impl Iterator<Item = Item> {
// std::iter::repeat_with(|| {
// let msg = b"Bench";
// let sk = SigningKey::new(thread_rng());
// let vk = VerifyingKey::from(&sk).into();
// let sig = sk.sign(thread_rng(), &msg[..]);
// Item { vk, sig }
// })
// }
fn bench_ristretto255_sign(c: &mut Criterion) {
let mut rng = thread_rng();
// fn bench_batch_verify(c: &mut Criterion) {
// let mut group = c.benchmark_group("Batch Verification");
// for &n in [8usize, 16, 24, 32, 40, 48, 56, 64].iter() {
// group.throughput(Throughput::Elements(n as u64));
frost_core::benches::bench_sign::<Ristretto255Sha512, _>(c, "ristretto255", &mut rng);
}
// let sigs = sigs_with_distinct_keys().take(n).collect::<Vec<_>>();
// group.bench_with_input(
// BenchmarkId::new("Unbatched verification", n),
// &sigs,
// |b, sigs| {
// b.iter(|| {
// for item in sigs.iter() {
// let msg = b"Bench";
// let Item { vk, sig } = item;
// {
// vk.verify(msg, sig);
// }
// }
// })
// },
// );
// group.bench_with_input(
// BenchmarkId::new("Batched verification", n),
// &sigs,
// |b, sigs| {
// b.iter(|| {
// let mut batch = batch::Verifier::new();
// for item in sigs.iter() {
// let msg = b"Bench";
// let Item { vk, sig } = item;
// {
// batch.queue((*vk, *sig, msg));
// }
// }
// batch.verify(thread_rng())
// })
// },
// );
// }
// group.finish();
// }
// criterion_group!(benches, bench_batch_verify);
// criterion_main!(benches);
fn main() {}
criterion_group!(
benches,
bench_ristretto255_batch_verify,
bench_ristretto255_sign
);
criterion_main!(benches);

View File

@ -44,3 +44,12 @@ serde_json = "1.0"
[features]
nightly = []
default = []
[lib]
# Disables non-criterion benchmark which is not used; prevents errors
# when using criterion-specific flags
bench = false
[[bench]]
name = "bench"
harness = false

View File

@ -0,0 +1,19 @@
use criterion::{criterion_group, criterion_main, Criterion};
use rand::thread_rng;
use frost_secp256k1::*;
fn bench_secp256k1_batch_verify(c: &mut Criterion) {
let mut rng = thread_rng();
frost_core::benches::bench_batch_verify::<Secp256K1Sha256, _>(c, "secp256k1", &mut rng);
}
fn bench_secp256k1_sign(c: &mut Criterion) {
let mut rng = thread_rng();
frost_core::benches::bench_sign::<Secp256K1Sha256, _>(c, "secp256k1", &mut rng);
}
criterion_group!(benches, bench_secp256k1_batch_verify, bench_secp256k1_sign);
criterion_main!(benches);

View File

@ -7,3 +7,10 @@ edition = "2021"
[dependencies]
regex = "1.6.0"
[[bin]]
name = "gendoc"
path = "src/main.rs"
# Disables non-criterion benchmark which is not used; prevents errors
# when using criterion-specific flags
bench = false