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);