/ core / benches / crypto_benchmarks.rs
crypto_benchmarks.rs
  1  //! Benchmarks for cryptographic operations.
  2  //!
  3  //! Run with: cargo bench
  4  
  5  use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
  6  
  7  use dead_drop_core::crypto::{
  8      aead::{encrypt, decrypt, generate_nonce, seal, open},
  9      ecdh::perform_key_agreement,
 10      hkdf::{derive_key_32, derive_message_key},
 11      keys::{ExchangeKeyPair, IdentityKeyPair, EphemeralKeyPair, generate_random_id},
 12      noise::perform_handshake,
 13      rotating_id::{derive_rotating_id as derive_rot_id, find_matching_contact_for_slot, ContactKeys, current_time_slot},
 14      signing::{sign, verify, sign_hashed, verify_hashed},
 15  };
 16  
 17  fn bench_key_generation(c: &mut Criterion) {
 18      let mut group = c.benchmark_group("key_generation");
 19  
 20      group.bench_function("identity_keypair", |b| {
 21          b.iter(|| IdentityKeyPair::generate())
 22      });
 23  
 24      group.bench_function("exchange_keypair", |b| {
 25          b.iter(|| ExchangeKeyPair::generate())
 26      });
 27  
 28      group.bench_function("ephemeral_keypair", |b| {
 29          b.iter(|| EphemeralKeyPair::generate())
 30      });
 31  
 32      group.finish();
 33  }
 34  
 35  fn bench_ecdh(c: &mut Criterion) {
 36      let alice = ExchangeKeyPair::generate();
 37      let bob = ExchangeKeyPair::generate();
 38  
 39      c.bench_function("ecdh_key_agreement", |b| {
 40          b.iter(|| alice.diffie_hellman(black_box(&bob.public_bytes())))
 41      });
 42  }
 43  
 44  fn bench_signing(c: &mut Criterion) {
 45      let keypair = IdentityKeyPair::generate();
 46      let small_msg = vec![0u8; 64];
 47      let medium_msg = vec![0u8; 1024];
 48      let large_msg = vec![0u8; 64 * 1024];
 49  
 50      let mut group = c.benchmark_group("signing");
 51  
 52      group.bench_function("sign_64B", |b| {
 53          b.iter(|| sign(&keypair, black_box(&small_msg)))
 54      });
 55  
 56      group.bench_function("sign_1KB", |b| {
 57          b.iter(|| sign(&keypair, black_box(&medium_msg)))
 58      });
 59  
 60      group.bench_function("sign_64KB", |b| {
 61          b.iter(|| sign(&keypair, black_box(&large_msg)))
 62      });
 63  
 64      let signature = sign(&keypair, &small_msg);
 65      group.bench_function("verify_64B", |b| {
 66          b.iter(|| verify(&keypair.public_bytes(), black_box(&small_msg), black_box(&signature)))
 67      });
 68  
 69      group.finish();
 70  }
 71  
 72  fn bench_hashed_signing(c: &mut Criterion) {
 73      let keypair = IdentityKeyPair::generate();
 74      let large_msg = vec![0u8; 1024 * 1024]; // 1 MB
 75  
 76      let mut group = c.benchmark_group("hashed_signing");
 77  
 78      group.bench_function("sign_hashed_1MB", |b| {
 79          b.iter(|| sign_hashed(&keypair, black_box(&large_msg)))
 80      });
 81  
 82      let signature = sign_hashed(&keypair, &large_msg);
 83      group.bench_function("verify_hashed_1MB", |b| {
 84          b.iter(|| verify_hashed(&keypair.public_bytes(), black_box(&large_msg), black_box(&signature)))
 85      });
 86  
 87      group.finish();
 88  }
 89  
 90  fn bench_aead(c: &mut Criterion) {
 91      let key = [0x42u8; 32];
 92      let nonce = generate_nonce();
 93  
 94      let sizes = [64, 1024, 4096, 16384, 65536];
 95  
 96      let mut group = c.benchmark_group("aead_encrypt");
 97      for size in sizes {
 98          let plaintext = vec![0xABu8; size];
 99          let aad = vec![0x01u8; 16];
100  
101          group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| {
102              b.iter(|| encrypt(&key, &nonce, black_box(&plaintext), black_box(&aad)))
103          });
104      }
105      group.finish();
106  
107      let mut group = c.benchmark_group("aead_decrypt");
108      for size in sizes {
109          let plaintext = vec![0xABu8; size];
110          let aad = vec![0x01u8; 16];
111          let ciphertext = encrypt(&key, &nonce, &plaintext, &aad).unwrap();
112  
113          group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| {
114              b.iter(|| decrypt(&key, &nonce, black_box(&ciphertext), black_box(&aad)))
115          });
116      }
117      group.finish();
118  }
119  
120  fn bench_seal_open(c: &mut Criterion) {
121      let key = [0x42u8; 32];
122      let plaintext = vec![0xABu8; 1024];
123      let aad = vec![0x01u8; 16];
124  
125      let mut group = c.benchmark_group("seal_open");
126  
127      group.bench_function("seal_1KB", |b| {
128          b.iter(|| seal(&key, black_box(&plaintext), black_box(&aad)))
129      });
130  
131      let sealed = seal(&key, &plaintext, &aad).unwrap();
132      group.bench_function("open_1KB", |b| {
133          b.iter(|| open(&key, black_box(&sealed), black_box(&aad)))
134      });
135  
136      group.finish();
137  }
138  
139  fn bench_hkdf(c: &mut Criterion) {
140      let secret = [0x42u8; 32];
141      let salt = [0x01u8; 16];
142      let info = b"dead-drop-message-v1";
143  
144      let mut group = c.benchmark_group("hkdf");
145  
146      group.bench_function("derive_key_32", |b| {
147          b.iter(|| derive_key_32(black_box(&secret), black_box(&salt), black_box(info)))
148      });
149  
150      let message_id = generate_random_id();
151      group.bench_function("derive_message_key", |b| {
152          b.iter(|| derive_message_key(black_box(&secret), black_box(&message_id)))
153      });
154  
155      group.finish();
156  }
157  
158  fn bench_key_agreement(c: &mut Criterion) {
159      let recipient = ExchangeKeyPair::generate();
160      let message_id = generate_random_id();
161  
162      c.bench_function("perform_key_agreement", |b| {
163          b.iter(|| perform_key_agreement(black_box(&recipient.public_bytes()), black_box(&message_id)))
164      });
165  }
166  
167  fn bench_noise_handshake(c: &mut Criterion) {
168      let alice = ExchangeKeyPair::generate();
169      let bob = ExchangeKeyPair::generate();
170  
171      c.bench_function("noise_handshake_full", |b| {
172          b.iter(|| perform_handshake(black_box(&alice), black_box(&bob)))
173      });
174  }
175  
176  fn bench_noise_transport(c: &mut Criterion) {
177      let alice = ExchangeKeyPair::generate();
178      let bob = ExchangeKeyPair::generate();
179      let (mut alice_t, mut bob_t) = perform_handshake(&alice, &bob).unwrap();
180  
181      let plaintext = vec![0xABu8; 1024];
182  
183      let mut group = c.benchmark_group("noise_transport");
184  
185      group.bench_function("encrypt_1KB", |b| {
186          b.iter(|| alice_t.encrypt(black_box(&plaintext)))
187      });
188  
189      let ciphertext = alice_t.encrypt(&plaintext).unwrap();
190      group.bench_function("decrypt_1KB", |b| {
191          b.iter(|| bob_t.decrypt(black_box(&ciphertext)))
192      });
193  
194      group.finish();
195  }
196  
197  fn bench_rotating_id(c: &mut Criterion) {
198      let shared_secret = [0x42u8; 32];
199      let time_slot = current_time_slot();
200  
201      let mut group = c.benchmark_group("rotating_id");
202  
203      group.bench_function("derive", |b| {
204          b.iter(|| derive_rot_id(black_box(&shared_secret), black_box(time_slot)))
205      });
206  
207      // Benchmark contact matching with different contact list sizes
208      let alice = ExchangeKeyPair::generate();
209  
210      for num_contacts in [10, 50, 100, 500] {
211          let contacts: Vec<ContactKeys> = (0..num_contacts)
212              .map(|_| {
213                  let ex = ExchangeKeyPair::generate();
214                  ContactKeys {
215                      contact_id: generate_random_id(),
216                      exchange_public: ex.public_bytes(),
217                  }
218              })
219              .collect();
220  
221          // Add a known contact at the end
222          let bob = ExchangeKeyPair::generate();
223          let mut contacts_with_bob = contacts.clone();
224          contacts_with_bob.push(ContactKeys {
225              contact_id: generate_random_id(),
226              exchange_public: bob.public_bytes(),
227          });
228  
229          let bob_id = dead_drop_core::crypto::rotating_id::expected_rotating_id_for_slot(
230              &bob,
231              &alice.public_bytes(),
232              time_slot,
233          );
234  
235          group.bench_with_input(
236              BenchmarkId::new("find_contact", num_contacts),
237              &num_contacts,
238              |b, _| {
239                  b.iter(|| {
240                      find_matching_contact_for_slot(
241                          black_box(&bob_id),
242                          black_box(&contacts_with_bob),
243                          black_box(&alice),
244                          black_box(time_slot),
245                      )
246                  })
247              },
248          );
249      }
250  
251      group.finish();
252  }
253  
254  fn bench_full_message_flow(c: &mut Criterion) {
255      let sender_identity = IdentityKeyPair::generate();
256      let recipient_exchange = ExchangeKeyPair::generate();
257  
258      let plaintext = vec![0xABu8; 1024];
259  
260      c.bench_function("full_message_encrypt_sign", |b| {
261          b.iter(|| {
262              let message_id = generate_random_id();
263              let agreement = perform_key_agreement(&recipient_exchange.public_bytes(), &message_id);
264              let sealed = seal(&agreement.key, &plaintext, &message_id).unwrap();
265              let _signature = sign(&sender_identity, &sealed);
266              (agreement, sealed)
267          })
268      });
269  }
270  
271  criterion_group!(
272      benches,
273      bench_key_generation,
274      bench_ecdh,
275      bench_signing,
276      bench_hashed_signing,
277      bench_aead,
278      bench_seal_open,
279      bench_hkdf,
280      bench_key_agreement,
281      bench_noise_handshake,
282      bench_noise_transport,
283      bench_rotating_id,
284      bench_full_message_flow,
285  );
286  
287  criterion_main!(benches);