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 }