From ce2343ef6045cb7432b606170723308f220c989b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Tue, 24 Sep 2024 12:22:04 +0200 Subject: [PATCH] Add benchmark --- crates/bitwarden-core/src/lib.rs | 14 +- crates/bitwarden-crypto/Cargo.toml | 6 +- .../benches/new_encryptable.rs | 180 ++++++++++++++++++ 3 files changed, 187 insertions(+), 13 deletions(-) create mode 100644 crates/bitwarden-crypto/benches/new_encryptable.rs diff --git a/crates/bitwarden-core/src/lib.rs b/crates/bitwarden-core/src/lib.rs index 53b2571b5..17966e61f 100644 --- a/crates/bitwarden-core/src/lib.rs +++ b/crates/bitwarden-core/src/lib.rs @@ -151,20 +151,10 @@ mod testcrypto { .unwrap(); // And decrypt values in parallel - let mut data = Vec::with_capacity(500_000); + let mut data = Vec::with_capacity(250); for _ in 0..data.capacity() { data.push("hello world, this is an encryption test!".using_key(MySymmKeyRef::User)); } - let now = std::time::Instant::now(); - let _ = service.encrypt_list(&data).unwrap(); - println!("Batch encrypting took {:?}", now.elapsed()); - - let now = std::time::Instant::now(); - for d in data { - let _ = service.encrypt(d).unwrap(); - } - println!("Individual encrypting took {:?}", now.elapsed()); - - // panic!("DONE") + service.encrypt_list(&data).unwrap(); } } diff --git a/crates/bitwarden-crypto/Cargo.toml b/crates/bitwarden-crypto/Cargo.toml index 4109b659e..6b0c28079 100644 --- a/crates/bitwarden-crypto/Cargo.toml +++ b/crates/bitwarden-crypto/Cargo.toml @@ -50,7 +50,7 @@ zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } memsec = { version = "0.7.0", features = ["alloc_ext"] } [dev-dependencies] -criterion = "0.5.1" +criterion = { version = "0.5.1", features = ["html_reports"] } rand_chacha = "0.3.1" serde_json = ">=1.0.96, <2.0" @@ -64,5 +64,9 @@ name = "zeroizing_allocator" harness = false required-features = ["no-memory-hardening"] +[[bench]] +name = "new_encryptable" +harness = false + [lints] workspace = true diff --git a/crates/bitwarden-crypto/benches/new_encryptable.rs b/crates/bitwarden-crypto/benches/new_encryptable.rs new file mode 100644 index 000000000..892bb5ea9 --- /dev/null +++ b/crates/bitwarden-crypto/benches/new_encryptable.rs @@ -0,0 +1,180 @@ +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; + +pub fn criterion_benchmark(c: &mut Criterion) { + let user_key = SymmetricCryptoKey::generate(rand::thread_rng()); + + let org_id = uuid::Uuid::parse_str("91b000b6-81ce-47f4-9802-3390e0b895ed").expect(""); + let org_key = SymmetricCryptoKey::generate(rand::thread_rng()); + + let cipher_key = SymmetricCryptoKey::generate(rand::thread_rng()); + let cipher_key_user_enc = cipher_key.to_vec().encrypt_with_key(&user_key).expect(""); + let cipher_view = CipherView { + key: Some(cipher_key_user_enc.clone()), + name: "test".to_string(), + }; + + let service: CryptoService = CryptoService::new(); + #[allow(deprecated)] + { + service.insert_symmetric_key(MySymmKeyRef::User, user_key.clone()); + service.insert_symmetric_key(MySymmKeyRef::Organization(org_id), org_key.clone()); + } + + let cipher_views = vec![cipher_view.clone(); 10_000]; + + { + let mut group = c.benchmark_group("New encryptable"); + + for size in [1, 10, 100, 1_000, 10_000].iter() { + group.throughput(Throughput::Elements(*size as u64)); + + group.bench_with_input( + BenchmarkId::new("encrypt X ciphers individually", size), + size, + |b, &size| { + b.iter(|| { + for c in cipher_views.iter().take(size).cloned() { + service.encrypt(black_box(c)).expect(""); + } + }); + }, + ); + + group.bench_with_input( + BenchmarkId::new("encrypt X ciphers batch", size), + size, + |b, &size| { + b.iter(|| service.encrypt_list(black_box(&cipher_views[..size]))); + }, + ); + } + + group.finish(); + } + + { + let mut group = c.benchmark_group("Old encryptable"); + + for size in [1, 10, 100, 1_000, 10_000].iter() { + group.throughput(Throughput::Elements(*size as u64)); + + group.bench_with_input( + BenchmarkId::new("encrypt X ciphers individually", size), + size, + |b, &size| { + b.iter(|| { + for c in cipher_views.iter().take(size).cloned() { + black_box(c).encrypt_with_key(&user_key).expect(""); + } + }); + }, + ); + + group.bench_with_input( + BenchmarkId::new("encrypt X ciphers batch", size), + size, + |b, &size| { + b.iter(|| { + black_box(cipher_views[0..size].to_vec()) + .encrypt_with_key(&user_key) + .expect(""); + }); + }, + ); + } + + group.finish(); + } +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); + +use bitwarden_crypto::{ + key_refs, service::*, CryptoError, EncString, KeyDecryptable, KeyEncryptable, + SymmetricCryptoKey, +}; + +key_refs! { + #[symmetric] + pub enum MySymmKeyRef { + User, + Organization(uuid::Uuid), + #[local] + Local(&'static str), + } + + #[asymmetric] + pub enum MyAsymmKeyRef { + UserPrivateKey, + #[local] + Local(&'static str), + } +} + +#[allow(unused)] +struct Cipher { + key: Option, + name: EncString, +} + +#[derive(Clone)] +struct CipherView { + key: Option, + name: String, +} + +impl UsesKey for Cipher { + fn uses_key(&self) -> MySymmKeyRef { + MySymmKeyRef::User + } +} +impl UsesKey for CipherView { + fn uses_key(&self) -> MySymmKeyRef { + MySymmKeyRef::User + } +} + +const CIPHER_KEY: MySymmKeyRef = MySymmKeyRef::Local("cipher_key"); + +// New encryptable implementations + +impl Encryptable for CipherView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: MySymmKeyRef, + ) -> Result { + let cipher_key = match &self.key { + Some(cipher_key) => ctx.decrypt_and_store_symmetric_key(key, CIPHER_KEY, cipher_key)?, + None => key, + }; + + Ok(Cipher { + key: self.key.clone(), + name: self.name.as_str().encrypt(ctx, cipher_key)?, + }) + } +} + +// Old encryptable implementations + +impl KeyEncryptable for CipherView { + fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { + let cipher_key = self + .key + .as_ref() + .map(|k| { + let mut kk: Vec = k.decrypt_with_key(key)?; + SymmetricCryptoKey::try_from(kk.as_mut_slice()) + }) + .transpose()?; + + let key = cipher_key.as_ref().unwrap_or(key); + + Ok(Cipher { + key: self.key.clone(), + name: self.name.encrypt_with_key(key)?, + }) + } +}