@@ -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