/ core / tests / attack_scenario_tests.rs
attack_scenario_tests.rs
  1  //! Attack scenario integration tests for Ekko.
  2  //!
  3  //! These tests verify that the cryptographic layer correctly rejects
  4  //! various attack scenarios: message tampering, replay attacks,
  5  //! impersonation, compromised devices, eavesdropping, and protocol attacks.
  6  
  7  use rust_lib_dead_drop::crypto::{
  8      aead::{decrypt, encrypt, generate_nonce},
  9      keys::{ExchangeKeyPair, IdentityKeyPair, generate_random_id},
 10      noise::{perform_handshake, NoiseHandshake},
 11      signing::{sign, verify},
 12  };
 13  use rust_lib_dead_drop::protocol::messages::{
 14      encrypt_message, decrypt_message, generate_message_id, verify_message_signature,
 15      PlaintextMessage,
 16  };
 17  use rust_lib_dead_drop::relay::gossip::BloomFilter;
 18  use rust_lib_dead_drop::storage::Database;
 19  
 20  // ============================================================================
 21  // A. MESSAGE TAMPERING ATTACKS
 22  // ============================================================================
 23  
 24  #[test]
 25  fn test_tampered_ciphertext_rejected() {
 26      let sender_id = IdentityKeyPair::generate();
 27      let recipient_ex = ExchangeKeyPair::generate();
 28  
 29      let plaintext = PlaintextMessage::text("Secret message");
 30      let mut encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
 31  
 32      // Flip bits in the ciphertext
 33      if let Some(byte) = encrypted.ciphertext.get_mut(0) {
 34          *byte ^= 0xFF;
 35      }
 36  
 37      let result = decrypt_message(&encrypted, &recipient_ex, &sender_id.public_bytes());
 38      assert!(result.is_err(), "Tampered ciphertext should be rejected");
 39  }
 40  
 41  #[test]
 42  fn test_tampered_signature_rejected() {
 43      let sender_id = IdentityKeyPair::generate();
 44      let recipient_ex = ExchangeKeyPair::generate();
 45  
 46      let plaintext = PlaintextMessage::text("Secret message");
 47      let mut encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
 48  
 49      // Modify signature bytes
 50      encrypted.signature[0] ^= 0xFF;
 51      encrypted.signature[32] ^= 0xFF;
 52  
 53      let result = verify_message_signature(&encrypted, &sender_id.public_bytes());
 54      assert!(result.is_err(), "Tampered signature should be rejected");
 55  }
 56  
 57  #[test]
 58  fn test_tampered_nonce_rejected() {
 59      let sender_id = IdentityKeyPair::generate();
 60      let recipient_ex = ExchangeKeyPair::generate();
 61  
 62      let plaintext = PlaintextMessage::text("Secret message");
 63      let mut encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
 64  
 65      // Alter the nonce
 66      encrypted.nonce[0] ^= 0xFF;
 67  
 68      let result = decrypt_message(&encrypted, &recipient_ex, &sender_id.public_bytes());
 69      assert!(result.is_err(), "Tampered nonce should cause decryption failure");
 70  }
 71  
 72  #[test]
 73  fn test_tampered_ephemeral_key_rejected() {
 74      let sender_id = IdentityKeyPair::generate();
 75      let recipient_ex = ExchangeKeyPair::generate();
 76  
 77      let plaintext = PlaintextMessage::text("Secret message");
 78      let mut encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
 79  
 80      // Alter the ephemeral public key — recipient will derive wrong shared secret
 81      encrypted.ephemeral_public[0] ^= 0xFF;
 82  
 83      let result = decrypt_message(&encrypted, &recipient_ex, &sender_id.public_bytes());
 84      assert!(result.is_err(), "Tampered ephemeral key should cause decryption failure");
 85  }
 86  
 87  #[test]
 88  fn test_truncated_ciphertext_rejected() {
 89      let sender_id = IdentityKeyPair::generate();
 90      let recipient_ex = ExchangeKeyPair::generate();
 91  
 92      let plaintext = PlaintextMessage::text("Secret message");
 93      let mut encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
 94  
 95      // Truncate ciphertext
 96      encrypted.ciphertext.truncate(encrypted.ciphertext.len() / 2);
 97  
 98      let result = decrypt_message(&encrypted, &recipient_ex, &sender_id.public_bytes());
 99      assert!(result.is_err(), "Truncated ciphertext should be rejected");
100  }
101  
102  // ============================================================================
103  // B. REPLAY ATTACKS
104  // ============================================================================
105  
106  #[test]
107  fn test_replay_exact_duplicate_rejected() {
108      let db = Database::open_in_memory(b"test").unwrap();
109      let msg_id = generate_message_id();
110  
111      // First time — not a replay
112      assert!(!db.check_and_mark_seen(&msg_id).unwrap());
113  
114      // Second time — replay detected
115      assert!(db.check_and_mark_seen(&msg_id).unwrap());
116  }
117  
118  #[test]
119  fn test_replay_across_db_reopens() {
120      let temp_dir = tempfile::tempdir().unwrap();
121      let db_path = temp_dir.path().join("replay.db");
122      let db_path_str = db_path.to_str().unwrap();
123      let passphrase = b"replay_persist_test";
124  
125      let msg_id = generate_message_id();
126  
127      // Open DB, mark message as seen, close
128      {
129          let db = Database::open(db_path_str, passphrase).unwrap();
130          assert!(!db.check_and_mark_seen(&msg_id).unwrap());
131      }
132  
133      // Reopen — replay cache should persist
134      {
135          let db = Database::open(db_path_str, passphrase).unwrap();
136          assert!(db.is_replay(&msg_id).unwrap(), "Replay cache should survive DB reopen");
137      }
138  }
139  
140  #[test]
141  fn test_replay_batch_detection() {
142      let db = Database::open_in_memory(b"test").unwrap();
143  
144      let old_ids: Vec<_> = (0..5).map(|_| generate_message_id()).collect();
145      let new_ids: Vec<_> = (0..5).map(|_| generate_message_id()).collect();
146  
147      // Mark old IDs as seen
148      for id in &old_ids {
149          db.mark_seen(id).unwrap();
150      }
151  
152      // Check a mixed batch
153      let mut batch = Vec::new();
154      batch.extend_from_slice(&old_ids);
155      batch.extend_from_slice(&new_ids);
156  
157      let results = db.check_replays(&batch).unwrap();
158  
159      // First 5 should be replays, last 5 should not
160      for (i, is_replay) in results.iter().enumerate() {
161          if i < 5 {
162              assert!(is_replay, "Old message {} should be detected as replay", i);
163          } else {
164              assert!(!is_replay, "New message {} should not be a replay", i);
165          }
166      }
167  }
168  
169  // ============================================================================
170  // C. IMPERSONATION ATTACKS
171  // ============================================================================
172  
173  #[test]
174  fn test_wrong_sender_identity_rejected() {
175      let alice_id = IdentityKeyPair::generate();
176      let bob_id = IdentityKeyPair::generate();
177      let recipient_ex = ExchangeKeyPair::generate();
178  
179      // Alice encrypts and signs with her key
180      let plaintext = PlaintextMessage::text("From Alice");
181      let encrypted = encrypt_message(&plaintext, &alice_id, &recipient_ex.public_bytes()).unwrap();
182  
183      // Recipient tries to verify with Bob's identity (impersonation claim)
184      let result = verify_message_signature(&encrypted, &bob_id.public_bytes());
185      assert!(result.is_err(), "Verification with wrong sender identity should fail");
186  }
187  
188  #[test]
189  fn test_forged_signature_with_different_key() {
190      let real_sender = IdentityKeyPair::generate();
191      let attacker = IdentityKeyPair::generate();
192      let recipient_ex = ExchangeKeyPair::generate();
193  
194      // Real sender encrypts
195      let plaintext = PlaintextMessage::text("Legitimate message");
196      let mut encrypted = encrypt_message(&plaintext, &real_sender, &recipient_ex.public_bytes()).unwrap();
197  
198      // Attacker re-signs the ciphertext with their own key
199      let forged_sig = sign(&attacker, &encrypted.ciphertext);
200      encrypted.signature = forged_sig;
201  
202      // Verification against real sender's key should fail
203      let result = verify_message_signature(&encrypted, &real_sender.public_bytes());
204      assert!(result.is_err(), "Forged signature should not verify against real sender");
205  }
206  
207  #[test]
208  fn test_key_substitution_attack() {
209      let sender_id = IdentityKeyPair::generate();
210      let recipient_ex = ExchangeKeyPair::generate();
211      let attacker_ex = ExchangeKeyPair::generate();
212  
213      let plaintext = PlaintextMessage::text("Secret data");
214      let mut encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
215  
216      // Attacker replaces ephemeral key with their own
217      encrypted.ephemeral_public = attacker_ex.public_bytes();
218  
219      // Recipient can't decrypt (wrong shared secret)
220      let result = decrypt_message(&encrypted, &recipient_ex, &sender_id.public_bytes());
221      assert!(result.is_err(), "Key substitution should prevent decryption");
222  }
223  
224  // ============================================================================
225  // D. COMPROMISED DEVICE SCENARIOS
226  // ============================================================================
227  
228  #[test]
229  fn test_compromised_exchange_key_no_past_decryption() {
230      let sender_id = IdentityKeyPair::generate();
231      let recipient_ex = ExchangeKeyPair::generate();
232  
233      // Encrypt a message using ephemeral key agreement
234      let plaintext = PlaintextMessage::text("Past secret");
235      let encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
236  
237      // Attacker knows recipient's static exchange key but NOT the ephemeral secret
238      // They have: recipient_ex.secret_bytes(), encrypted.ephemeral_public
239      // But the encryption used a fresh ephemeral key internally,
240      // so knowing the static key alone isn't enough if the protocol uses ephemeral keys
241  
242      // Verify the legitimate recipient CAN decrypt
243      let decrypted = decrypt_message(&encrypted, &recipient_ex, &sender_id.public_bytes()).unwrap();
244      assert_eq!(decrypted.content, plaintext.content);
245  
246      // An attacker with a DIFFERENT exchange key cannot decrypt
247      let attacker_ex = ExchangeKeyPair::generate();
248      let result = decrypt_message(&encrypted, &attacker_ex, &sender_id.public_bytes());
249      assert!(result.is_err(), "Different exchange key should not decrypt");
250  }
251  
252  #[test]
253  fn test_compromised_identity_key_no_decryption() {
254      let sender_id = IdentityKeyPair::generate();
255      let recipient_ex = ExchangeKeyPair::generate();
256  
257      let plaintext = PlaintextMessage::text("Secret message");
258      let encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
259  
260      // Identity key (ed25519) is only for signing, not encryption.
261      // An attacker who has the sender's identity key can forge signatures
262      // but cannot decrypt messages (those use x25519 exchange keys).
263      // Verify original decryption works
264      let decrypted = decrypt_message(&encrypted, &recipient_ex, &sender_id.public_bytes()).unwrap();
265      assert_eq!(decrypted.content, plaintext.content);
266  
267      // Having the identity key doesn't help decrypt
268      // (this is inherently true since decrypt_message uses exchange keys)
269      let wrong_ex = ExchangeKeyPair::generate();
270      let result = decrypt_message(&encrypted, &wrong_ex, &sender_id.public_bytes());
271      assert!(result.is_err());
272  }
273  
274  #[test]
275  fn test_each_message_unique_ephemeral() {
276      let sender_id = IdentityKeyPair::generate();
277      let recipient_ex = ExchangeKeyPair::generate();
278  
279      let mut ephemeral_keys = Vec::new();
280      for i in 0..10 {
281          let plaintext = PlaintextMessage::text(&format!("Message {}", i));
282          let encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
283          ephemeral_keys.push(encrypted.ephemeral_public);
284      }
285  
286      // All ephemeral keys should be unique
287      for i in 0..ephemeral_keys.len() {
288          for j in (i + 1)..ephemeral_keys.len() {
289              assert_ne!(
290                  ephemeral_keys[i], ephemeral_keys[j],
291                  "Ephemeral keys {} and {} should be different", i, j
292              );
293          }
294      }
295  }
296  
297  // ============================================================================
298  // E. DHT EAVESDROPPER SCENARIOS
299  // ============================================================================
300  
301  #[test]
302  fn test_ciphertext_indistinguishable_from_random() {
303      let sender_id = IdentityKeyPair::generate();
304      let recipient_ex = ExchangeKeyPair::generate();
305  
306      let plaintext = PlaintextMessage::text("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
307      let encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
308  
309      // Chi-squared test on ciphertext byte distribution
310      // Good encryption should produce uniformly distributed bytes
311      let mut freq = [0u64; 256];
312      for &b in &encrypted.ciphertext {
313          freq[b as usize] += 1;
314      }
315  
316      let n = encrypted.ciphertext.len() as f64;
317      let expected = n / 256.0;
318  
319      let chi_squared: f64 = freq.iter()
320          .map(|&f| {
321              let diff = f as f64 - expected;
322              diff * diff / expected
323          })
324          .sum();
325  
326      // For 255 degrees of freedom, p=0.001 critical value is ~310
327      // We use a generous threshold since ciphertexts are short
328      // Main point: repetitive plaintext should not produce repetitive ciphertext
329      let _has_uniform_appearance = chi_squared < 400.0;
330  
331      // At minimum, verify no byte dominates (which would happen with unencrypted ASCII)
332      let max_freq = *freq.iter().max().unwrap();
333      assert!(
334          max_freq < n as u64 / 4,
335          "No single byte should dominate ciphertext (max_freq={}, total={})",
336          max_freq, n as u64
337      );
338  
339      // Verify it's not just the plaintext repeated
340      let plaintext_bytes = plaintext.content.clone();
341      assert_ne!(encrypted.ciphertext, plaintext_bytes);
342  }
343  
344  #[test]
345  fn test_different_plaintexts_different_ciphertexts() {
346      let sender_id = IdentityKeyPair::generate();
347      let recipient_ex = ExchangeKeyPair::generate();
348  
349      // Same plaintext encrypted multiple times should produce different ciphertexts
350      let plaintext = PlaintextMessage::text("Same message every time");
351  
352      let enc1 = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
353      let enc2 = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
354  
355      assert_ne!(enc1.ciphertext, enc2.ciphertext, "Same plaintext should encrypt differently each time");
356      assert_ne!(enc1.nonce, enc2.nonce, "Nonces should differ");
357      assert_ne!(enc1.ephemeral_public, enc2.ephemeral_public, "Ephemeral keys should differ");
358  }
359  
360  #[test]
361  fn test_ciphertext_no_plaintext_leakage() {
362      let sender_id = IdentityKeyPair::generate();
363      let recipient_ex = ExchangeKeyPair::generate();
364  
365      let secret_text = "TOP SECRET CLASSIFIED INFORMATION";
366      let plaintext = PlaintextMessage::text(secret_text);
367      let encrypted = encrypt_message(&plaintext, &sender_id, &recipient_ex.public_bytes()).unwrap();
368  
369      // Ciphertext should not contain the plaintext as a substring
370      let ct_str = String::from_utf8_lossy(&encrypted.ciphertext);
371      assert!(
372          !ct_str.contains(secret_text),
373          "Plaintext should not appear in ciphertext"
374      );
375  
376      // Also check raw bytes
377      let secret_bytes = secret_text.as_bytes();
378      let ct = &encrypted.ciphertext;
379      for window in ct.windows(secret_bytes.len()) {
380          assert_ne!(window, secret_bytes, "Plaintext bytes should not appear in ciphertext");
381      }
382  }
383  
384  // ============================================================================
385  // F. NOISE PROTOCOL ATTACKS
386  // ============================================================================
387  
388  #[test]
389  fn test_noise_mitm_fails() {
390      let alice = ExchangeKeyPair::generate();
391      let bob = ExchangeKeyPair::generate();
392      let mallory = ExchangeKeyPair::generate();
393  
394      // Alice initiates with Bob, but Mallory intercepts
395      let mut alice_hs = NoiseHandshake::new_initiator(&alice).unwrap();
396      let mut mallory_hs_as_bob = NoiseHandshake::new_responder(&mallory).unwrap();
397  
398      // msg1: Alice → Mallory (thinking it's Bob)
399      let msg1 = alice_hs.write_message().unwrap();
400      mallory_hs_as_bob.read_message(&msg1).unwrap();
401  
402      // msg2: Mallory → Alice (pretending to be Bob)
403      let msg2 = mallory_hs_as_bob.write_message().unwrap();
404      alice_hs.read_message(&msg2).unwrap();
405  
406      // msg3: Alice → Mallory
407      let msg3 = alice_hs.write_message().unwrap();
408      mallory_hs_as_bob.read_message(&msg3).unwrap();
409  
410      // Handshake "completes" but Alice sees Mallory's key, NOT Bob's
411      let alice_sees = alice_hs.remote_static().unwrap();
412      assert_ne!(
413          alice_sees, &bob.public_bytes(),
414          "Alice should NOT see Bob's key (she sees Mallory's)"
415      );
416      assert_eq!(
417          alice_sees, &mallory.public_bytes(),
418          "Alice should see Mallory's key — MITM is detectable"
419      );
420  }
421  
422  #[test]
423  fn test_noise_replay_handshake_message_fails() {
424      let alice = ExchangeKeyPair::generate();
425      let bob = ExchangeKeyPair::generate();
426  
427      let mut alice_hs = NoiseHandshake::new_initiator(&alice).unwrap();
428      let mut bob_hs = NoiseHandshake::new_responder(&bob).unwrap();
429  
430      // msg1: Alice → Bob
431      let msg1 = alice_hs.write_message().unwrap();
432      bob_hs.read_message(&msg1).unwrap();
433  
434      // msg2: Bob → Alice
435      let msg2 = bob_hs.write_message().unwrap();
436      alice_hs.read_message(&msg2).unwrap();
437  
438      // Replay msg1 into Bob — should fail (wrong handshake state)
439      let result = bob_hs.read_message(&msg1);
440      assert!(result.is_err(), "Replaying msg1 during active handshake should fail");
441  }
442  
443  #[test]
444  fn test_noise_out_of_order_messages_fail() {
445      let alice = ExchangeKeyPair::generate();
446      let bob = ExchangeKeyPair::generate();
447  
448      let mut alice_hs = NoiseHandshake::new_initiator(&alice).unwrap();
449      let mut bob_hs = NoiseHandshake::new_responder(&bob).unwrap();
450  
451      // msg1: Alice → Bob (normal)
452      let _msg1 = alice_hs.write_message().unwrap();
453  
454      // Bob hasn't read msg1 yet, try to have Bob write
455      // Bob is responder — he should read first, not write
456      let result = bob_hs.write_message();
457      assert!(result.is_err(), "Responder should not be able to write before reading msg1");
458  }
459  
460  #[test]
461  fn test_noise_wrong_static_key() {
462      let alice = ExchangeKeyPair::generate();
463      let bob = ExchangeKeyPair::generate();
464      let expected_bob_key = ExchangeKeyPair::generate();
465  
466      // Perform handshake between Alice and Bob
467      let (alice_t, _bob_t) = perform_handshake(&alice, &bob).unwrap();
468  
469      // Alice checks remote static — it should be Bob's key, not the expected one
470      let remote = alice_t.remote_static().unwrap();
471      assert_eq!(remote, &bob.public_bytes());
472      assert_ne!(
473          remote, &expected_bob_key.public_bytes(),
474          "If Bob's key doesn't match expected, application should reject"
475      );
476  }
477  
478  #[test]
479  fn test_noise_transport_decrypt_wrong_session() {
480      let alice = ExchangeKeyPair::generate();
481      let bob = ExchangeKeyPair::generate();
482      let charlie = ExchangeKeyPair::generate();
483  
484      // Two separate sessions
485      let (mut alice_bob_a, _alice_bob_b) = perform_handshake(&alice, &bob).unwrap();
486      let (_charlie_alice_a, mut charlie_alice_b) = perform_handshake(&charlie, &alice).unwrap();
487  
488      // Encrypt with Alice-Bob session
489      let ct = alice_bob_a.encrypt(b"For Bob only").unwrap();
490  
491      // Try to decrypt with Charlie-Alice session — wrong session keys
492      let result = charlie_alice_b.decrypt(&ct);
493      assert!(result.is_err(), "Ciphertext from one session should not decrypt in another");
494  }
495  
496  // ============================================================================
497  // G. GOSSIP / RELAY ATTACKS
498  // ============================================================================
499  
500  #[test]
501  fn test_bloom_filter_false_positive_rate() {
502      let size_bytes = 128; // 1024 bits
503      let mut filter = BloomFilter::new(size_bytes);
504  
505      // Insert 50 items (reasonable load for 128-byte filter)
506      let inserted: Vec<[u8; 32]> = (0..50)
507          .map(|i| {
508              let mut key = [0u8; 32];
509              key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
510              key
511          })
512          .collect();
513  
514      for key in &inserted {
515          filter.insert(key);
516      }
517  
518      // Verify all inserted items are found
519      for key in &inserted {
520          assert!(filter.might_contain(key), "Inserted item should be found");
521      }
522  
523      // Check false positive rate with 1000 non-inserted items
524      let mut false_positives = 0;
525      for i in 1000..2000u64 {
526          let mut key = [0u8; 32];
527          key[0..8].copy_from_slice(&i.to_le_bytes());
528          if filter.might_contain(&key) {
529              false_positives += 1;
530          }
531      }
532  
533      let fpr = false_positives as f64 / 1000.0;
534      assert!(
535          fpr < 0.05,
536          "False positive rate should be under 5%, got {:.1}%",
537          fpr * 100.0
538      );
539  }
540  
541  #[test]
542  fn test_gossip_message_deduplication() {
543      let db = Database::open_in_memory(b"test").unwrap();
544  
545      // Store a forwarded message
546      let msg_id = generate_message_id();
547      let recipient_key = [0x42u8; 32];
548      let msg = rust_lib_dead_drop::storage::forwarding::ForwardedMessage::new(
549          msg_id,
550          &recipient_key,
551          b"encrypted payload".to_vec(),
552          None,
553      );
554  
555      // Store it once
556      assert!(db.store_for_forwarding(&msg).unwrap());
557      let count1 = db.get_forwarding_stats().unwrap().message_count;
558  
559      // Store the same message again (same message_id) — should be deduplicated
560      let msg2 = rust_lib_dead_drop::storage::forwarding::ForwardedMessage::new(
561          msg_id,
562          &recipient_key,
563          b"encrypted payload".to_vec(),
564          None,
565      );
566      // This should return false (duplicate rejected)
567      assert!(!db.store_for_forwarding(&msg2).unwrap());
568      let count2 = db.get_forwarding_stats().unwrap().message_count;
569  
570      assert_eq!(count1, count2, "Duplicate forwarded messages should be deduplicated");
571  }
572  
573  // ============================================================================
574  // H. LOW-LEVEL CRYPTO ATTACKS
575  // ============================================================================
576  
577  #[test]
578  fn test_aead_wrong_key_rejected() {
579      let key = [0x42u8; 32];
580      let wrong_key = [0x43u8; 32];
581      let nonce = generate_nonce();
582      let ad = generate_random_id();
583  
584      let ciphertext = encrypt(&key, &nonce, b"secret data", &ad).unwrap();
585  
586      let result = decrypt(&wrong_key, &nonce, &ciphertext, &ad);
587      assert!(result.is_err(), "Wrong key should fail decryption");
588  }
589  
590  #[test]
591  fn test_aead_wrong_associated_data_rejected() {
592      let key = [0x42u8; 32];
593      let nonce = generate_nonce();
594      let ad = [1u8; 16];
595      let wrong_ad = [2u8; 16];
596  
597      let ciphertext = encrypt(&key, &nonce, b"secret data", &ad).unwrap();
598  
599      let result = decrypt(&key, &nonce, &ciphertext, &wrong_ad);
600      assert!(result.is_err(), "Wrong associated data should fail decryption");
601  }
602  
603  #[test]
604  fn test_signature_wrong_message_rejected() {
605      let keypair = IdentityKeyPair::generate();
606      let original = b"original message";
607      let tampered = b"tampered message";
608  
609      let signature = sign(&keypair, original);
610  
611      let result = verify(&keypair.public_bytes(), tampered, &signature);
612      assert!(result.is_err(), "Signature should not verify for different message");
613  }
614  
615  #[test]
616  fn test_signature_wrong_key_rejected() {
617      let keypair = IdentityKeyPair::generate();
618      let attacker = IdentityKeyPair::generate();
619      let message = b"authenticated message";
620  
621      let signature = sign(&keypair, message);
622  
623      let result = verify(&attacker.public_bytes(), message, &signature);
624      assert!(result.is_err(), "Signature should not verify with wrong public key");
625  }