crypto_integration_tests.rs
1 //! Integration tests for the crypto module. 2 //! 3 //! These tests verify that all cryptographic components work together 4 //! correctly in realistic usage scenarios. 5 6 use rust_lib_dead_drop::crypto::{ 7 aead::{decrypt, encrypt, generate_nonce, open, seal}, 8 ecdh::{derive_decryption_key, perform_key_agreement}, 9 hkdf::{derive_key_32, derive_message_key, derive_rotating_id, derive_storage_key}, 10 keys::{ 11 generate_random_bytes, generate_random_id, ContactPublicKeys, EphemeralKeyPair, 12 ExchangeKeyPair, IdentityKeyPair, 13 }, 14 noise::{perform_handshake, NoiseHandshake, Role}, 15 rotating_id::{ 16 current_time_slot, expected_rotating_id_for_slot, find_matching_contact_for_slot, 17 ContactKeys, 18 }, 19 signing::{sign, sign_hashed, verify, verify_hashed, SignedMessage, blake2b_256, compute_fingerprint}, 20 }; 21 22 /// Test the complete flow of establishing a contact relationship. 23 #[test] 24 fn test_contact_establishment_flow() { 25 // Step 1: Alice generates her key pairs 26 let alice_identity = IdentityKeyPair::generate(); 27 let alice_exchange = ExchangeKeyPair::generate(); 28 29 // Step 2: Bob generates his key pairs 30 let bob_identity = IdentityKeyPair::generate(); 31 let bob_exchange = ExchangeKeyPair::generate(); 32 33 // Step 3: They meet in person and exchange public keys (simulating QR codes) 34 let alice_qr_data = ContactPublicKeys::new( 35 alice_identity.public_bytes(), 36 alice_exchange.public_bytes(), 37 ); 38 39 let bob_qr_data = ContactPublicKeys::new( 40 bob_identity.public_bytes(), 41 bob_exchange.public_bytes(), 42 ); 43 44 // Step 4: Both validate the received keys 45 assert!(alice_qr_data.validate().is_ok()); 46 assert!(bob_qr_data.validate().is_ok()); 47 48 // Step 5: They verify fingerprints out of band 49 let alice_fp = compute_fingerprint(&alice_identity.public_bytes()); 50 let bob_fp = compute_fingerprint(&bob_identity.public_bytes()); 51 52 // Fingerprints should be human-readable and different 53 assert!(alice_fp.contains(':')); 54 assert!(bob_fp.contains(':')); 55 assert_ne!(alice_fp, bob_fp); 56 57 // Step 6: Both can now compute shared secrets 58 let alice_shared = alice_exchange.diffie_hellman(&bob_qr_data.exchange); 59 let bob_shared = bob_exchange.diffie_hellman(&alice_qr_data.exchange); 60 61 assert_eq!(alice_shared, bob_shared); 62 } 63 64 /// Test complete message encryption and decryption with signing. 65 #[test] 66 fn test_signed_encrypted_message_flow() { 67 // Setup: Alice and Bob have exchanged keys 68 let alice_identity = IdentityKeyPair::generate(); 69 let alice_exchange = ExchangeKeyPair::generate(); 70 71 let bob_identity = IdentityKeyPair::generate(); 72 let bob_exchange = ExchangeKeyPair::generate(); 73 74 // Alice wants to send a message to Bob 75 let message_id = generate_random_id(); 76 let plaintext = b"This is a top secret message that only Bob should read."; 77 78 // Step 1: Alice performs key agreement with Bob's exchange key 79 let agreement = perform_key_agreement(&bob_exchange.public_bytes(), &message_id); 80 81 // Step 2: Alice encrypts the message 82 let nonce = generate_nonce(); 83 let ciphertext = encrypt(&agreement.key, &nonce, plaintext, &message_id).unwrap(); 84 85 // Step 3: Alice signs the ciphertext (not the plaintext!) 86 let signature = sign(&alice_identity, &ciphertext); 87 88 // Step 4: Package everything that Bob needs 89 // In real protocol: message_id, agreement.ephemeral_public, nonce, ciphertext, signature 90 91 // Bob receives the package 92 // Step 5: Bob verifies the signature first 93 let sig_result = verify(&alice_identity.public_bytes(), &ciphertext, &signature); 94 assert!(sig_result.is_ok(), "Signature verification failed"); 95 96 // Step 6: Bob derives the decryption key 97 let decryption_key = derive_decryption_key( 98 &bob_exchange, 99 &agreement.ephemeral_public, 100 &message_id, 101 ); 102 103 // Step 7: Bob decrypts the message 104 let decrypted = decrypt(&decryption_key, &nonce, &ciphertext, &message_id).unwrap(); 105 106 assert_eq!(plaintext.as_slice(), decrypted.as_slice()); 107 } 108 109 /// Test the seal/open convenience functions. 110 #[test] 111 fn test_seal_open_convenience() { 112 let sender_exchange = ExchangeKeyPair::generate(); 113 let recipient_exchange = ExchangeKeyPair::generate(); 114 115 let message_id = generate_random_id(); 116 let plaintext = b"Using seal/open for simplified encryption"; 117 118 // Sender seals the message 119 let agreement = perform_key_agreement(&recipient_exchange.public_bytes(), &message_id); 120 let sealed = seal(&agreement.key, plaintext, &message_id).unwrap(); 121 122 // Recipient opens the message 123 let decryption_key = derive_decryption_key( 124 &recipient_exchange, 125 &agreement.ephemeral_public, 126 &message_id, 127 ); 128 let opened = open(&decryption_key, &sealed, &message_id).unwrap(); 129 130 assert_eq!(plaintext.as_slice(), opened.as_slice()); 131 } 132 133 /// Test Noise handshake in both directions. 134 #[test] 135 fn test_noise_bidirectional_communication() { 136 let alice = ExchangeKeyPair::generate(); 137 let bob = ExchangeKeyPair::generate(); 138 139 // Perform handshake 140 let (mut alice_transport, mut bob_transport) = perform_handshake(&alice, &bob).unwrap(); 141 142 // Verify they know each other's static keys 143 assert_eq!(alice_transport.remote_static(), Some(&bob.public_bytes())); 144 assert_eq!(bob_transport.remote_static(), Some(&alice.public_bytes())); 145 146 // Test bidirectional communication 147 let messages = [ 148 ("Alice", "Hello Bob, this is Alice!"), 149 ("Bob", "Hi Alice, nice to hear from you!"), 150 ("Alice", "Did you get my last message?"), 151 ("Bob", "Yes, I did!"), 152 ]; 153 154 for (sender, msg) in messages.iter() { 155 if *sender == "Alice" { 156 let ct = alice_transport.encrypt(msg.as_bytes()).unwrap(); 157 let pt = bob_transport.decrypt(&ct).unwrap(); 158 assert_eq!(msg.as_bytes(), pt.as_slice()); 159 } else { 160 let ct = bob_transport.encrypt(msg.as_bytes()).unwrap(); 161 let pt = alice_transport.decrypt(&ct).unwrap(); 162 assert_eq!(msg.as_bytes(), pt.as_slice()); 163 } 164 } 165 } 166 167 /// Test step-by-step Noise handshake. 168 #[test] 169 fn test_noise_handshake_step_by_step() { 170 let alice = ExchangeKeyPair::generate(); 171 let bob = ExchangeKeyPair::generate(); 172 173 let mut alice_hs = NoiseHandshake::new_initiator(&alice).unwrap(); 174 let mut bob_hs = NoiseHandshake::new_responder(&bob).unwrap(); 175 176 // Verify initial states 177 assert_eq!(alice_hs.role(), Role::Initiator); 178 assert_eq!(bob_hs.role(), Role::Responder); 179 180 // Message 1: Alice → Bob (→ e) 181 let msg1 = alice_hs.write_message().unwrap(); 182 bob_hs.read_message(&msg1).unwrap(); 183 184 // At this point, Bob doesn't know Alice's static key yet 185 assert!(bob_hs.remote_static().is_none()); 186 187 // Message 2: Bob → Alice (← e, ee, s, es) 188 let msg2 = bob_hs.write_message().unwrap(); 189 alice_hs.read_message(&msg2).unwrap(); 190 191 // Now Alice knows Bob's static key 192 assert_eq!(alice_hs.remote_static(), Some(&bob.public_bytes())); 193 194 // Message 3: Alice → Bob (→ s, se) 195 let msg3 = alice_hs.write_message().unwrap(); 196 bob_hs.read_message(&msg3).unwrap(); 197 198 // Now Bob knows Alice's static key 199 assert_eq!(bob_hs.remote_static(), Some(&alice.public_bytes())); 200 201 // Both should be complete 202 assert!(alice_hs.is_complete()); 203 assert!(bob_hs.is_complete()); 204 205 // Convert to transports and verify they work 206 let mut alice_t = alice_hs.into_transport().unwrap(); 207 let mut bob_t = bob_hs.into_transport().unwrap(); 208 209 let test_msg = b"Handshake completed!"; 210 let ct = alice_t.encrypt(test_msg).unwrap(); 211 let pt = bob_t.decrypt(&ct).unwrap(); 212 assert_eq!(test_msg.as_slice(), pt.as_slice()); 213 } 214 215 /// Test rotating ID generation and matching. 216 #[test] 217 fn test_rotating_id_matching() { 218 let alice = ExchangeKeyPair::generate(); 219 let bob = ExchangeKeyPair::generate(); 220 let charlie = ExchangeKeyPair::generate(); 221 222 let time_slot = current_time_slot(); 223 224 // Alice's contact list includes Bob and Charlie 225 let contacts = vec![ 226 ContactKeys { 227 contact_id: generate_random_id(), 228 exchange_public: bob.public_bytes(), 229 }, 230 ContactKeys { 231 contact_id: generate_random_id(), 232 exchange_public: charlie.public_bytes(), 233 }, 234 ]; 235 236 // Bob advertises his rotating ID 237 let bob_rotating_id = expected_rotating_id_for_slot(&bob, &alice.public_bytes(), time_slot); 238 239 // Alice scans and finds Bob 240 let found = find_matching_contact_for_slot(&bob_rotating_id, &contacts, &alice, time_slot); 241 assert!(found.is_some()); 242 assert_eq!(found.unwrap(), contacts[0].contact_id); 243 244 // Charlie advertises his rotating ID 245 let charlie_rotating_id = expected_rotating_id_for_slot(&charlie, &alice.public_bytes(), time_slot); 246 247 // Alice scans and finds Charlie 248 let found = find_matching_contact_for_slot(&charlie_rotating_id, &contacts, &alice, time_slot); 249 assert!(found.is_some()); 250 assert_eq!(found.unwrap(), contacts[1].contact_id); 251 252 // Unknown person advertises 253 let dave = ExchangeKeyPair::generate(); 254 let dave_rotating_id = expected_rotating_id_for_slot(&dave, &alice.public_bytes(), time_slot); 255 256 // Alice doesn't recognize Dave 257 let found = find_matching_contact_for_slot(&dave_rotating_id, &contacts, &alice, time_slot); 258 assert!(found.is_none()); 259 } 260 261 /// Test key derivation for different purposes. 262 #[test] 263 fn test_key_derivation_separation() { 264 let master_secret = [0x42u8; 32]; 265 266 // Derive different types of keys 267 let storage_key = derive_storage_key(&master_secret, b"device-salt"); 268 let message_key = derive_message_key(&master_secret, &[0x01u8; 16]); 269 let rotating_id = derive_rotating_id(&master_secret, 12345); 270 let generic_key = derive_key_32(&master_secret, b"salt", b"info"); 271 272 // All should be different (domain separation working) 273 assert_ne!(storage_key.as_slice(), message_key.as_slice()); 274 assert_ne!(message_key[..16].as_ref(), rotating_id.as_slice()); 275 assert_ne!(storage_key.as_slice(), generic_key.as_slice()); 276 } 277 278 /// Test SignedMessage struct for convenient signing. 279 #[test] 280 fn test_signed_message_struct() { 281 let keypair = IdentityKeyPair::generate(); 282 let message = b"Important announcement".to_vec(); 283 284 // Create signed message 285 let signed = SignedMessage::new(&keypair, message.clone()); 286 287 // Verify it 288 assert!(signed.verify(&keypair.public_bytes()).is_ok()); 289 290 // Serialize and deserialize 291 let bytes = signed.to_bytes(); 292 let restored = SignedMessage::from_bytes(&bytes).unwrap(); 293 294 assert_eq!(signed, restored); 295 assert!(restored.verify(&keypair.public_bytes()).is_ok()); 296 } 297 298 /// Test hashed signing for large messages. 299 #[test] 300 fn test_hashed_signing_large_message() { 301 let keypair = IdentityKeyPair::generate(); 302 let large_message = vec![0xABu8; 1024 * 1024]; // 1 MB 303 304 // Sign with hashing 305 let signature = sign_hashed(&keypair, &large_message); 306 307 // Verify with hashing 308 assert!(verify_hashed(&keypair.public_bytes(), &large_message, &signature).is_ok()); 309 310 // Wrong message should fail 311 let wrong_message = vec![0xCDu8; 1024 * 1024]; 312 assert!(verify_hashed(&keypair.public_bytes(), &wrong_message, &signature).is_err()); 313 } 314 315 /// Test that ephemeral keys provide forward secrecy. 316 #[test] 317 fn test_forward_secrecy_property() { 318 let recipient = ExchangeKeyPair::generate(); 319 let message_id = generate_random_id(); 320 321 // Send multiple messages with different ephemeral keys 322 let messages = vec![ 323 b"First secret message".to_vec(), 324 b"Second secret message".to_vec(), 325 b"Third secret message".to_vec(), 326 ]; 327 328 let mut encryptions = Vec::new(); 329 330 for plaintext in &messages { 331 // Each message gets a new ephemeral key 332 let agreement = perform_key_agreement(&recipient.public_bytes(), &message_id); 333 let sealed = seal(&agreement.key, plaintext, &message_id).unwrap(); 334 335 encryptions.push((agreement.ephemeral_public, sealed)); 336 } 337 338 // All ephemeral public keys should be different 339 for i in 0..encryptions.len() { 340 for j in (i + 1)..encryptions.len() { 341 assert_ne!(encryptions[i].0, encryptions[j].0); 342 } 343 } 344 345 // All ciphertexts should be different (even if plaintext were the same) 346 for i in 0..encryptions.len() { 347 for j in (i + 1)..encryptions.len() { 348 assert_ne!(encryptions[i].1, encryptions[j].1); 349 } 350 } 351 } 352 353 /// Test key persistence and restoration. 354 #[test] 355 fn test_key_persistence() { 356 // Generate keys 357 let identity = IdentityKeyPair::generate(); 358 let exchange = ExchangeKeyPair::generate(); 359 360 // Extract bytes (for storage) 361 let identity_secret = identity.secret_bytes(); 362 let identity_public = identity.public_bytes(); 363 let exchange_secret = exchange.secret_bytes(); 364 let exchange_public = exchange.public_bytes(); 365 366 // Restore from bytes 367 let restored_identity = IdentityKeyPair::from_secret_bytes(&identity_secret).unwrap(); 368 let restored_exchange = ExchangeKeyPair::from_secret_bytes(exchange_secret); 369 370 // Verify restoration 371 assert_eq!(identity_public, restored_identity.public_bytes()); 372 assert_eq!(exchange_public, restored_exchange.public_bytes()); 373 374 // Verify functionality 375 let message = b"Test message"; 376 let original_sig = sign(&identity, message); 377 let restored_sig = sign(&restored_identity, message); 378 assert_eq!(original_sig, restored_sig); 379 } 380 381 /// Test ContactPublicKeys serialization. 382 #[test] 383 fn test_contact_public_keys_serialization() { 384 let identity = IdentityKeyPair::generate(); 385 let exchange = ExchangeKeyPair::generate(); 386 387 let contact = ContactPublicKeys::new(identity.public_bytes(), exchange.public_bytes()); 388 389 // JSON serialization 390 let json = serde_json::to_string(&contact).unwrap(); 391 let from_json: ContactPublicKeys = serde_json::from_str(&json).unwrap(); 392 assert_eq!(contact, from_json); 393 394 // Binary serialization (more compact) 395 let binary = bincode::serialize(&contact).unwrap(); 396 let from_binary: ContactPublicKeys = bincode::deserialize(&binary).unwrap(); 397 assert_eq!(contact, from_binary); 398 399 // Binary should be smaller than JSON 400 assert!(binary.len() < json.len()); 401 } 402 403 /// Test error cases are handled properly. 404 #[test] 405 fn test_error_handling() { 406 use rust_lib_dead_drop::error::DeadDropError; 407 408 // Invalid signature 409 let keypair = IdentityKeyPair::generate(); 410 let message = b"test"; 411 let wrong_sig = [0u8; 64]; 412 let result = verify(&keypair.public_bytes(), message, &wrong_sig); 413 assert!(matches!(result, Err(DeadDropError::InvalidSignature))); 414 415 // Invalid public key - note that many 32-byte values are technically valid Ed25519 keys 416 // so we just check that verification fails (either InvalidKey or InvalidSignature) 417 let invalid_pk = [0u8; 32]; 418 let result = verify(&invalid_pk, message, &wrong_sig); 419 assert!(result.is_err()); 420 421 // Tampered ciphertext 422 let key = [0x42u8; 32]; 423 let nonce = generate_nonce(); 424 let mut ciphertext = encrypt(&key, &nonce, b"test", b"").unwrap(); 425 ciphertext[0] ^= 0xFF; 426 let result = decrypt(&key, &nonce, &ciphertext, b""); 427 assert!(matches!(result, Err(DeadDropError::Decryption(_)))); 428 } 429 430 /// Stress test: Many messages through Noise transport. 431 #[test] 432 fn test_noise_many_messages() { 433 let alice = ExchangeKeyPair::generate(); 434 let bob = ExchangeKeyPair::generate(); 435 436 let (mut alice_t, mut bob_t) = perform_handshake(&alice, &bob).unwrap(); 437 438 for i in 0..100 { 439 let msg = format!("Message number {}", i); 440 let ct = alice_t.encrypt(msg.as_bytes()).unwrap(); 441 let pt = bob_t.decrypt(&ct).unwrap(); 442 assert_eq!(msg.as_bytes(), pt.as_slice()); 443 } 444 } 445 446 /// Test binary data handling (null bytes, all values). 447 #[test] 448 fn test_binary_data() { 449 let alice = ExchangeKeyPair::generate(); 450 let bob = ExchangeKeyPair::generate(); 451 452 let (mut alice_t, mut bob_t) = perform_handshake(&alice, &bob).unwrap(); 453 454 // All byte values 455 let all_bytes: Vec<u8> = (0..=255).collect(); 456 let ct = alice_t.encrypt(&all_bytes).unwrap(); 457 let pt = bob_t.decrypt(&ct).unwrap(); 458 assert_eq!(all_bytes, pt); 459 460 // Null bytes in middle 461 let with_nulls = b"hello\x00world\x00\x00end"; 462 let ct = alice_t.encrypt(with_nulls).unwrap(); 463 let pt = bob_t.decrypt(&ct).unwrap(); 464 assert_eq!(with_nulls.as_slice(), pt.as_slice()); 465 }