From ce988002d5303d95c9b5e457a3c6b1c5d802d300 Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Sun, 12 Nov 2023 21:25:07 -0800 Subject: [PATCH] [WIP] Benchmark OpenSSL Sign/Verify --- example_crypto/Cargo.toml | 8 + example_crypto/benches/iters.rs | 23 ++ example_crypto/benches/keygen.rs | 33 +-- example_crypto/benches/secp384r1.rs | 44 ++++ example_crypto/benches/sign.rs | 336 ++++++++++++++++++++------ example_crypto/src/openssl.rs | 1 + example_crypto/src/openssl/benches.rs | 85 +++++++ 7 files changed, 441 insertions(+), 89 deletions(-) create mode 100644 example_crypto/benches/iters.rs create mode 100644 example_crypto/benches/secp384r1.rs create mode 100644 example_crypto/src/openssl/benches.rs diff --git a/example_crypto/Cargo.toml b/example_crypto/Cargo.toml index c2d5655..cfe7e7c 100644 --- a/example_crypto/Cargo.toml +++ b/example_crypto/Cargo.toml @@ -10,10 +10,18 @@ publish = false name = "keygen" harness = false +[[bench]] +name = "iters" +harness = false + [[bench]] name = "rand" harness = false +[[bench]] +name = "secp384r1" +harness = false + [[bench]] name = "sign" harness = false diff --git a/example_crypto/benches/iters.rs b/example_crypto/benches/iters.rs new file mode 100644 index 0000000..bf2a7ed --- /dev/null +++ b/example_crypto/benches/iters.rs @@ -0,0 +1,23 @@ +use criterion::{criterion_group, criterion_main, Criterion}; + +pub struct EndlessIter { + pub values: Vec, +} + +impl Iterator for EndlessIter { + type Item = T; + + fn next(&mut self) -> Option { + todo!() + } +} + +fn bench(c: &mut Criterion) {} + +criterion_group! { + name = iters; + config = Criterion::default(); + targets = bench +} + +criterion_main!(iters); diff --git a/example_crypto/benches/keygen.rs b/example_crypto/benches/keygen.rs index 28f54de..1eeaaeb 100644 --- a/example_crypto/benches/keygen.rs +++ b/example_crypto/benches/keygen.rs @@ -1,32 +1,23 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use example_crypto::openssl::keygen::{keygen_ec, keygen_ed25519, keygen_ed448, keygen_rsa, keygen_x25519, keygen_x448}; -use openssl::ec::EcGroup; -use openssl::nid::Nid; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use example_crypto::openssl::benches::KeyGenerator; -pub fn bench_keygen(c: &mut Criterion) { - let secp256k1 = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); +fn bench_keygen(c: &mut Criterion) { + let mut group = c.benchmark_group("openssl::keygen"); - // // eddsa - c.bench_function("openssl::keygen::ed25519", |b| b.iter(|| keygen_ed25519())); - c.bench_function("openssl::keygen::ed448", |b| b.iter(|| keygen_ed448())); - c.bench_function("openssl::keygen::x25519", |b| b.iter(|| keygen_x25519())); - c.bench_function("openssl::keygen::x448", |b| b.iter(|| keygen_x448())); - - // ecdsa - c.bench_function("openssl::keygen::secp256", |b| { - b.iter(|| keygen_ec(&secp256k1)) + group.bench_function("prime256v1", |b| { + b.iter(black_box(KeyGenerator::prime256v1)) }); - // rsa - c.bench_function("openssl::keygen::rsa2048", |b| b.iter(|| keygen_rsa(2048))); - c.bench_function("openssl::keygen::rsa3072", |b| b.iter(|| keygen_rsa(3072))); - c.bench_function("openssl::keygen::rsa4096", |b| b.iter(|| keygen_rsa(4096))); + group.bench_function("secp256k1", |b| b.iter(black_box(KeyGenerator::secp256k1))); + group.bench_function("secp384r", |b| b.iter(black_box(KeyGenerator::secp384r1))); + group.bench_function("ed25519", |b| b.iter(black_box(KeyGenerator::ed25519))); + group.bench_function("ed448", |b| b.iter(black_box(KeyGenerator::ed448))); } -criterion_group!{ +criterion_group! { name = keygen; config = Criterion::default(); targets = bench_keygen } -criterion_main!(keygen); \ No newline at end of file +criterion_main!(keygen); diff --git a/example_crypto/benches/secp384r1.rs b/example_crypto/benches/secp384r1.rs new file mode 100644 index 0000000..977c7fa --- /dev/null +++ b/example_crypto/benches/secp384r1.rs @@ -0,0 +1,44 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use openssl::ec::{EcGroup, EcKey}; +use openssl::hash::MessageDigest; +use openssl::nid::Nid; +use openssl::pkey::{PKey, Private}; +use openssl::sign::Signer; + +struct Key { + pkey: PKey, + digest: MessageDigest, +} + +impl Key { + fn secp384r1(digest: MessageDigest) -> Self { + Self { + pkey: PKey::from_ec_key( + EcKey::generate(EcGroup::from_curve_name(Nid::SECP384R1).unwrap().as_ref()) + .unwrap(), + ) + .unwrap(), + digest, + } + } + + pub fn secp384r1_sha256() -> Self { + Self::secp384r1(MessageDigest::sha256()) + } + + pub fn secp384r1_sha384() -> Self { + Self::secp384r1(MessageDigest::sha384()) + } +} + +fn bench_secp384r1(c: &mut Criterion) { + c.bench_function("secp384r1::generate", |b| b.ite) +} + +criterion_group! { + name = secp384r1; + config = Criterion::default(); + targets = bench_secp384r1 +} + +criterion_main!(secp384r1); diff --git a/example_crypto/benches/sign.rs b/example_crypto/benches/sign.rs index 00d4c54..6df3232 100644 --- a/example_crypto/benches/sign.rs +++ b/example_crypto/benches/sign.rs @@ -1,80 +1,280 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use example_crypto::openssl::sign::Ed25519Signer; +use criterion::measurement::Measurement; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion}; +use openssl::ec::{EcGroup, EcKey}; +use openssl::hash::MessageDigest; +use openssl::nid::Nid; +use openssl::pkey::{PKey, Private}; +use openssl::sign::{Signer, Verifier}; +use rand::prelude::SliceRandom; use rand::{thread_rng, RngCore}; -use std::sync::atomic::{AtomicUsize, Ordering}; - -fn bench_sign_ed25519(c: &mut Criterion) { - // eddsa - c.bench_function("openssl::sign::ed25519", |b| { - const KEY_COUNT: usize = 256; - const DATA_COUNT: usize = 512; - - // build 256 signers - let keys: Vec = (0..KEY_COUNT).map(|_| Ed25519Signer::random()).collect(); - - let data: Vec<[u8; 32]> = (0..DATA_COUNT) - .map(|_| { - let mut d = [0; 32]; - thread_rng().fill_bytes(&mut d); - d - }) - .collect(); - - let (signer_index, data_index) = (AtomicUsize::new(0), AtomicUsize::new(0)); - - b.iter(|| { - let (current_signer, current_data) = ( - signer_index.fetch_add(1, Ordering::AcqRel) % KEY_COUNT, - data_index.fetch_add(1, Ordering::AcqRel) % DATA_COUNT, - ); - - // get em fast - let _sig = unsafe { - keys.get_unchecked(current_signer) - .sign(data.get_unchecked(current_data)) - }; - }) - }); +use std::fmt::{Display, Formatter}; + +const SIGN_COUNT: usize = 4096; + +const SIGN_INDEX_REPEAT: usize = 4; + +pub struct EndlessShuffledIter<'a, T> { + data: &'a [T], + indices: Vec, + current: usize, +} + +impl<'a, T> EndlessShuffledIter<'a, T> { + pub fn new(data: &'a [T]) -> Self { + Self::with_factor(data, SIGN_INDEX_REPEAT) + } + + pub fn with_factor(data: &'a [T], factor: usize) -> Self { + let mut indices = Vec::new(); + + for _ in 0..factor { + let mut i: Vec = (0..data.len()).collect(); + i.shuffle(&mut thread_rng()); + indices.extend(i); + } + + Self { + data, + indices, + current: 0, + } + } +} + +impl<'a, T> Iterator for EndlessShuffledIter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + // get the current index by getting the index in the array + let index = self.current; + + // update the index + self.current = (self.current + 1) % self.data.len(); + + // fetch the data + self.data.get(index) + } +} + +pub struct SignBencherData { + pub items: Vec>, +} + +impl SignBencherData { + pub fn new(size: usize, factory: F, digest: Option) -> Self + where + F: Fn() -> PKey, + { + Self { + items: (0..size) + .map(|_| SignBencher::generate_valid(factory(), digest)) + .collect(), + } + } +} + +pub struct SignBencher { + pub data: [u8; S], + pub signature: Vec, + pub key: PKey, + pub digest: Option, } -fn bench_sign_ed448(c: &mut Criterion) { - // eddsa - c.bench_function("openssl::sign::ed448", |b| { - const KEY_COUNT: usize = 256; - const DATA_COUNT: usize = 512; - - // build 256 signers - let keys: Vec = (0..KEY_COUNT).map(|_| Ed25519Signer::random()).collect(); - - let data: Vec<[u8; 32]> = (0..DATA_COUNT) - .map(|_| { - let mut d = [0; 32]; - thread_rng().fill_bytes(&mut d); - d - }) - .collect(); - - let (signer_index, data_index) = (AtomicUsize::new(0), AtomicUsize::new(0)); - - b.iter(|| { - let (current_signer, current_data) = ( - signer_index.fetch_add(1, Ordering::AcqRel) % KEY_COUNT, - data_index.fetch_add(1, Ordering::AcqRel) % DATA_COUNT, - ); - - // get em fast - let _sig = unsafe { - keys.get_unchecked(current_signer) - .sign(data.get_unchecked(current_data)) - }; - }) +impl SignBencher { + pub fn generate_valid(key: PKey, digest: Option) -> Self { + let data = { + let mut b = [0; S]; + thread_rng().fill_bytes(&mut b); + b + }; + + let signature = match digest.as_ref() { + Some(md) => Signer::new(*md, &key) + .unwrap() + .sign_oneshot_to_vec(&data) + .unwrap(), + None => Signer::new_without_digest(&key) + .unwrap() + .sign_oneshot_to_vec(&data) + .unwrap(), + }; + + Self { + data, + signature, + key, + digest, + } + } + + pub fn verify(&self) -> bool { + match &self.digest { + Some(md) => Verifier::new(*md, &self.key) + .unwrap() + .verify(self.signature.as_slice()) + .unwrap(), + None => Verifier::new_without_digest(&self.key) + .unwrap() + .verify(self.signature.as_slice()) + .unwrap(), + } + } +} + +struct BenchConfig { + algo: BenchAlgo, + digest: BenchDigest, +} + +impl BenchConfig { + pub fn ecdsa(algo: BenchAlgo, digest: BenchDigest) -> Self { + Self { algo, digest } + } + + pub fn eddsa(algo: BenchAlgo) -> Self { + Self { + algo, + digest: BenchDigest::Null, + } + } +} + +impl Display for BenchConfig { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.digest.eq(&BenchDigest::Null) { + write!(f, "{}", self.algo) + } else { + write!(f, "{}::{}", self.algo, self.digest) + } + } +} + +#[derive(Debug, Eq, PartialEq)] +enum BenchAlgo { + Prime256V1, + Secp256K1, + Secp384R1, + Ed448, + Ed25519, +} + +impl BenchAlgo { + pub fn gen_key(&self) -> PKey { + match &self { + Self::Ed25519 => PKey::generate_ed25519().unwrap(), + Self::Ed448 => PKey::generate_ed448().unwrap(), + Self::Prime256V1 => PKey::from_ec_key( + EcKey::generate(&EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap()).unwrap(), + ) + .unwrap(), + Self::Secp256K1 => PKey::from_ec_key( + EcKey::generate(&EcGroup::from_curve_name(Nid::SECP256K1).unwrap()).unwrap(), + ) + .unwrap(), + Self::Secp384R1 => PKey::from_ec_key( + EcKey::generate(&EcGroup::from_curve_name(Nid::SECP384R1).unwrap()).unwrap(), + ) + .unwrap(), + } + } +} + +impl Display for BenchAlgo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match &self { + Self::Prime256V1 => "prime256v1", + Self::Secp256K1 => "secp256k1", + Self::Secp384R1 => "secp384r1", + Self::Ed25519 => "ed25519", + Self::Ed448 => "ed448", + } + ) + } +} + +#[derive(Debug, Eq, PartialEq)] +enum BenchDigest { + Null, + Sha256, + #[allow(unused)] + Sha384, + #[allow(unused)] + Sha512, +} + +impl BenchDigest { + pub fn to_md(&self) -> Option { + match &self { + Self::Null => None, + Self::Sha256 => Some(MessageDigest::sha256()), + Self::Sha384 => Some(MessageDigest::sha384()), + Self::Sha512 => Some(MessageDigest::sha512()), + } + } +} + +impl Display for BenchDigest { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match &self { + Self::Null => "none", + Self::Sha256 => "sha256", + Self::Sha384 => "sha384", + Self::Sha512 => "sha512", + } + ) + } +} + +fn bench(c: &mut Criterion) { + bench_sized::<32>(c); + bench_sized::<64>(c); +} + +fn bench_sized(c: &mut Criterion) { + let mut group = c.benchmark_group(format!("openssl::verify::{S}")); + + bench_sized_individual::(&mut group, BenchConfig::eddsa(BenchAlgo::Ed25519)); + bench_sized_individual::(&mut group, BenchConfig::eddsa(BenchAlgo::Ed448)); + bench_sized_individual::( + &mut group, + BenchConfig::ecdsa(BenchAlgo::Prime256V1, BenchDigest::Sha256), + ); + bench_sized_individual::( + &mut group, + BenchConfig::ecdsa(BenchAlgo::Secp256K1, BenchDigest::Sha256), + ); + bench_sized_individual::( + &mut group, + BenchConfig::ecdsa(BenchAlgo::Secp384R1, BenchDigest::Sha256), + ); +} + +fn bench_sized_individual(g: &mut BenchmarkGroup, config: BenchConfig) +where + M: Measurement, +{ + // prewarm + let data = + SignBencherData::::new(SIGN_COUNT, || config.algo.gen_key(), config.digest.to_md()); + + // create endless iterator + let mut iter = EndlessShuffledIter::new(data.items.as_slice()); + + g.bench_function(format!("{}", config), |b| { + b.iter(|| black_box(iter.next().unwrap().verify())); }); } criterion_group! { name = sign; config = Criterion::default(); - targets = bench_sign_ed25519, + targets = bench, } criterion_main!(sign); diff --git a/example_crypto/src/openssl.rs b/example_crypto/src/openssl.rs index 52e68b0..db2dd2d 100644 --- a/example_crypto/src/openssl.rs +++ b/example_crypto/src/openssl.rs @@ -1,4 +1,5 @@ pub mod asymm; +pub mod benches; pub mod client_ca; pub mod keygen; pub mod sign; diff --git a/example_crypto/src/openssl/benches.rs b/example_crypto/src/openssl/benches.rs new file mode 100644 index 0000000..560c7f3 --- /dev/null +++ b/example_crypto/src/openssl/benches.rs @@ -0,0 +1,85 @@ +use openssl::ec::{EcGroup, EcKey}; +use openssl::hash::MessageDigest; +use openssl::nid::Nid; +use openssl::pkey::{PKey, Private}; + +pub struct KeyGenerator {} + +impl KeyGenerator { + fn ec(nid: Nid) -> PKey { + PKey::from_ec_key(EcKey::generate(&EcGroup::from_curve_name(nid).unwrap()).unwrap()) + .unwrap() + } + + pub fn prime256v1() -> PKey { + Self::ec(Nid::X9_62_PRIME256V1) + } + + pub fn secp256k1() -> PKey { + Self::ec(Nid::SECP256K1) + } + + pub fn secp384r1() -> PKey { + Self::ec(Nid::SECP384R1) + } + + pub fn ed25519() -> PKey { + PKey::generate_ed25519().unwrap() + } + + pub fn ed448() -> PKey { + PKey::generate_ed448().unwrap() + } +} + +pub struct SignVerifyKey { + pub algo: SignatureAlgo, + pub pkey: PKey, + pub digest: MessageDigest, +} + +pub enum SignatureAlgo { + ECDSA, + EdDSA, +} + +impl SignVerifyKey { + fn ec(nid: Nid, digest: MessageDigest) -> Self { + Self { + algo: SignatureAlgo::ECDSA, + pkey: PKey::from_ec_key( + EcKey::generate(&EcGroup::from_curve_name(nid).unwrap()).unwrap(), + ) + .unwrap(), + digest, + } + } + + pub fn prime256v1(digest: MessageDigest) -> Self { + Self::ec(Nid::X9_62_PRIME256V1, digest) + } + + pub fn secp256k1(digest: MessageDigest) -> Self { + Self::ec(Nid::SECP256K1, digest) + } + + pub fn secp384r1(digest: MessageDigest) -> Self { + Self::ec(Nid::SECP384R1, digest) + } + + pub fn ed25519() -> Self { + Self { + algo: SignatureAlgo::EdDSA, + pkey: KeyGenerator::ed25519(), + digest: MessageDigest::null(), + } + } + + pub fn ed448() -> Self { + Self { + algo: SignatureAlgo::EdDSA, + pkey: KeyGenerator::ed448(), + digest: MessageDigest::null(), + } + } +}