/ core / tests / crypto_integration_tests.rs
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  }