Skip to content

Commit ac76928

Browse files
committed
added security tests and security expalantion
1 parent 0bf9a1f commit ac76928

10 files changed

Lines changed: 3038 additions & 0 deletions

File tree

crates/fula-crypto/src/hpke.rs

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,229 @@ mod tests {
405405
deserialized.encapsulated_key.as_bytes()
406406
);
407407
}
408+
409+
// ==================== Security Tests ====================
410+
411+
/// Test that tampering with ciphertext is detected
412+
#[test]
413+
fn test_ciphertext_tampering_detected() {
414+
let keypair = KekKeyPair::generate();
415+
let plaintext = b"Authenticated message";
416+
417+
let encryptor = Encryptor::new(keypair.public_key());
418+
let mut encrypted = encryptor.encrypt(plaintext).unwrap();
419+
420+
// Tamper with ciphertext
421+
if !encrypted.ciphertext.is_empty() {
422+
encrypted.ciphertext[0] ^= 0xFF;
423+
}
424+
425+
let decryptor = Decryptor::new(&keypair);
426+
assert!(decryptor.decrypt(&encrypted).is_err(), "Tampered ciphertext should fail");
427+
}
428+
429+
/// Test that tampering with nonce is detected
430+
#[test]
431+
fn test_nonce_tampering_detected() {
432+
let keypair = KekKeyPair::generate();
433+
let plaintext = b"Message with nonce integrity";
434+
435+
let encryptor = Encryptor::new(keypair.public_key());
436+
let mut encrypted = encryptor.encrypt(plaintext).unwrap();
437+
438+
// Tamper with nonce
439+
let mut nonce_bytes = encrypted.nonce.as_bytes().to_vec();
440+
nonce_bytes[0] ^= 0x01;
441+
encrypted.nonce = Nonce::from_bytes(&nonce_bytes).unwrap();
442+
443+
let decryptor = Decryptor::new(&keypair);
444+
assert!(decryptor.decrypt(&encrypted).is_err(), "Tampered nonce should fail");
445+
}
446+
447+
/// Test that tampering with encapsulated key is detected
448+
#[test]
449+
fn test_encapsulated_key_tampering_detected() {
450+
let keypair = KekKeyPair::generate();
451+
let plaintext = b"Message with key integrity";
452+
453+
let encryptor = Encryptor::new(keypair.public_key());
454+
let mut encrypted = encryptor.encrypt(plaintext).unwrap();
455+
456+
// Tamper with encapsulated key
457+
encrypted.encapsulated_key.ephemeral_public[0] ^= 0x01;
458+
459+
let decryptor = Decryptor::new(&keypair);
460+
assert!(decryptor.decrypt(&encrypted).is_err(), "Tampered encapsulated key should fail");
461+
}
462+
463+
/// Test that same plaintext produces different ciphertexts (semantic security)
464+
#[test]
465+
fn test_semantic_security() {
466+
let keypair = KekKeyPair::generate();
467+
let plaintext = b"Same message encrypted multiple times";
468+
469+
let encryptor = Encryptor::new(keypair.public_key());
470+
471+
let encrypted1 = encryptor.encrypt(plaintext).unwrap();
472+
let encrypted2 = encryptor.encrypt(plaintext).unwrap();
473+
let encrypted3 = encryptor.encrypt(plaintext).unwrap();
474+
475+
// All ciphertexts should be different
476+
assert_ne!(encrypted1.ciphertext, encrypted2.ciphertext);
477+
assert_ne!(encrypted2.ciphertext, encrypted3.ciphertext);
478+
assert_ne!(encrypted1.ciphertext, encrypted3.ciphertext);
479+
480+
// All nonces should be different
481+
assert_ne!(encrypted1.nonce.as_bytes(), encrypted2.nonce.as_bytes());
482+
assert_ne!(encrypted2.nonce.as_bytes(), encrypted3.nonce.as_bytes());
483+
484+
// All encapsulated keys should be different (ephemeral keys)
485+
assert_ne!(
486+
encrypted1.encapsulated_key.as_bytes(),
487+
encrypted2.encapsulated_key.as_bytes()
488+
);
489+
}
490+
491+
/// Test that ciphertext doesn't contain plaintext patterns
492+
#[test]
493+
fn test_no_plaintext_leakage() {
494+
let keypair = KekKeyPair::generate();
495+
let plaintext = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; // Repeated pattern
496+
497+
let encryptor = Encryptor::new(keypair.public_key());
498+
let encrypted = encryptor.encrypt(plaintext).unwrap();
499+
500+
// Ciphertext should not contain long runs of the same byte
501+
let max_run = encrypted.ciphertext.windows(4)
502+
.filter(|w| w.iter().all(|&b| b == w[0]))
503+
.count();
504+
505+
assert!(max_run < 3, "Ciphertext may leak plaintext patterns");
506+
}
507+
508+
/// Test key isolation - one keypair cannot decrypt another's data
509+
#[test]
510+
fn test_key_isolation() {
511+
let keypair1 = KekKeyPair::generate();
512+
let keypair2 = KekKeyPair::generate();
513+
let keypair3 = KekKeyPair::generate();
514+
515+
let encryptor1 = Encryptor::new(keypair1.public_key());
516+
let encrypted = encryptor1.encrypt(b"secret for keypair1").unwrap();
517+
518+
// Only keypair1 can decrypt
519+
let decryptor1 = Decryptor::new(&keypair1);
520+
assert!(decryptor1.decrypt(&encrypted).is_ok());
521+
522+
// keypair2 and keypair3 cannot decrypt
523+
let decryptor2 = Decryptor::new(&keypair2);
524+
let decryptor3 = Decryptor::new(&keypair3);
525+
assert!(decryptor2.decrypt(&encrypted).is_err());
526+
assert!(decryptor3.decrypt(&encrypted).is_err());
527+
}
528+
529+
/// Test empty plaintext handling
530+
#[test]
531+
fn test_empty_plaintext() {
532+
let keypair = KekKeyPair::generate();
533+
let plaintext = b"";
534+
535+
let encryptor = Encryptor::new(keypair.public_key());
536+
let encrypted = encryptor.encrypt(plaintext).unwrap();
537+
538+
let decryptor = Decryptor::new(&keypair);
539+
let decrypted = decryptor.decrypt(&encrypted).unwrap();
540+
541+
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
542+
}
543+
544+
/// Test large message handling
545+
#[test]
546+
fn test_large_message() {
547+
let keypair = KekKeyPair::generate();
548+
let plaintext = vec![0x42u8; 1024 * 1024]; // 1 MB
549+
550+
let encryptor = Encryptor::new(keypair.public_key());
551+
let encrypted = encryptor.encrypt(&plaintext).unwrap();
552+
553+
let decryptor = Decryptor::new(&keypair);
554+
let decrypted = decryptor.decrypt(&encrypted).unwrap();
555+
556+
assert_eq!(plaintext, decrypted);
557+
}
558+
559+
/// Test binary data with all byte values
560+
#[test]
561+
fn test_binary_data() {
562+
let keypair = KekKeyPair::generate();
563+
let plaintext: Vec<u8> = (0..=255).collect();
564+
565+
let encryptor = Encryptor::new(keypair.public_key());
566+
let encrypted = encryptor.encrypt(&plaintext).unwrap();
567+
568+
let decryptor = Decryptor::new(&keypair);
569+
let decrypted = decryptor.decrypt(&encrypted).unwrap();
570+
571+
assert_eq!(plaintext, decrypted);
572+
}
573+
574+
/// Test that version field is correctly set
575+
#[test]
576+
fn test_version_field() {
577+
let keypair = KekKeyPair::generate();
578+
let encryptor = Encryptor::new(keypair.public_key());
579+
let encrypted = encryptor.encrypt(b"test").unwrap();
580+
581+
assert_eq!(encrypted.version, crate::CRYPTO_VERSION);
582+
}
583+
584+
/// Test decryption from secret key directly
585+
#[test]
586+
fn test_decrypt_from_secret_key() {
587+
let keypair = KekKeyPair::generate();
588+
let plaintext = b"Decrypt using secret key directly";
589+
590+
let encryptor = Encryptor::new(keypair.public_key());
591+
let encrypted = encryptor.encrypt(plaintext).unwrap();
592+
593+
// Decrypt using secret key directly
594+
let decryptor = Decryptor::from_secret_key(keypair.secret_key());
595+
let decrypted = decryptor.decrypt(&encrypted).unwrap();
596+
597+
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
598+
}
599+
600+
/// Test that truncated ciphertext fails
601+
#[test]
602+
fn test_truncated_ciphertext_fails() {
603+
let keypair = KekKeyPair::generate();
604+
let plaintext = b"Message that will be truncated";
605+
606+
let encryptor = Encryptor::new(keypair.public_key());
607+
let mut encrypted = encryptor.encrypt(plaintext).unwrap();
608+
609+
// Truncate ciphertext
610+
if encrypted.ciphertext.len() > 10 {
611+
encrypted.ciphertext.truncate(encrypted.ciphertext.len() - 10);
612+
}
613+
614+
let decryptor = Decryptor::new(&keypair);
615+
assert!(decryptor.decrypt(&encrypted).is_err(), "Truncated ciphertext should fail");
616+
}
617+
618+
/// Test that appended data fails
619+
#[test]
620+
fn test_appended_data_fails() {
621+
let keypair = KekKeyPair::generate();
622+
let plaintext = b"Original message";
623+
624+
let encryptor = Encryptor::new(keypair.public_key());
625+
let mut encrypted = encryptor.encrypt(plaintext).unwrap();
626+
627+
// Append extra data
628+
encrypted.ciphertext.extend_from_slice(b"extra garbage");
629+
630+
let decryptor = Decryptor::new(&keypair);
631+
assert!(decryptor.decrypt(&encrypted).is_err(), "Appended data should fail");
632+
}
408633
}

0 commit comments

Comments
 (0)