11//! OpenPGP helper module using [rPGP facilities](https://github.com/rpgp/rpgp).
22
3- use std:: collections:: { BTreeMap , HashSet } ;
3+ use std:: collections:: { BTreeMap , HashMap , HashSet } ;
44use std:: io:: { BufRead , Cursor } ;
55
66use anyhow:: { Context as _, Result , bail} ;
@@ -370,19 +370,28 @@ fn check_symmetric_encryption(msg: &Message<'_>) -> std::result::Result<(), &'st
370370
371371/// Returns fingerprints
372372/// of all keys from the `public_keys_for_validation` keyring that
373- /// have valid signatures there.
373+ /// have valid signatures in `msg` and corresponding intended recipient fingerprints
374+ /// (<https://www.rfc-editor.org/rfc/rfc9580.html#name-intended-recipient-fingerpr>) if any.
374375///
375- /// If the message is wrongly signed, HashSet will be empty.
376+ /// If the message is wrongly signed, returns an empty map .
376377pub fn valid_signature_fingerprints (
377378 msg : & pgp:: composed:: Message ,
378379 public_keys_for_validation : & [ SignedPublicKey ] ,
379- ) -> HashSet < Fingerprint > {
380- let mut ret_signature_fingerprints: HashSet < Fingerprint > = Default :: default ( ) ;
380+ ) -> HashMap < Fingerprint , Vec < Fingerprint > > {
381+ let mut ret_signature_fingerprints = HashMap :: new ( ) ;
381382 if msg. is_signed ( ) {
382383 for pkey in public_keys_for_validation {
383- if msg. verify ( & pkey. primary_key ) . is_ok ( ) {
384+ if let Ok ( signature ) = msg. verify ( & pkey. primary_key ) {
384385 let fp = pkey. dc_fingerprint ( ) ;
385- ret_signature_fingerprints. insert ( fp) ;
386+ let mut recipient_fps = Vec :: new ( ) ;
387+ if let Some ( cfg) = signature. config ( ) {
388+ for subpkt in & cfg. hashed_subpackets {
389+ if let SubpacketData :: IntendedRecipientFingerprint ( fp) = & subpkt. data {
390+ recipient_fps. push ( fp. clone ( ) . into ( ) ) ;
391+ }
392+ }
393+ }
394+ ret_signature_fingerprints. insert ( fp, recipient_fps) ;
386395 }
387396 }
388397 }
@@ -497,13 +506,14 @@ mod tests {
497506 use pgp:: composed:: Esk ;
498507 use pgp:: packet:: PublicKeyEncryptedSessionKey ;
499508
509+ #[ expect( clippy:: type_complexity) ]
500510 fn pk_decrypt_and_validate < ' a > (
501511 ctext : & ' a [ u8 ] ,
502512 private_keys_for_decryption : & ' a [ SignedSecretKey ] ,
503513 public_keys_for_validation : & [ SignedPublicKey ] ,
504514 ) -> Result < (
505515 pgp:: composed:: Message < ' static > ,
506- HashSet < Fingerprint > ,
516+ HashMap < Fingerprint , Vec < Fingerprint > > ,
507517 Vec < u8 > ,
508518 ) > {
509519 let mut msg = decrypt ( ctext. to_vec ( ) , private_keys_for_decryption, & [ ] ) ?;
@@ -611,7 +621,7 @@ mod tests {
611621 }
612622
613623 #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
614- async fn test_decrypt_singed ( ) {
624+ async fn test_decrypt_signed ( ) {
615625 // Check decrypting as Alice
616626 let decrypt_keyring = vec ! [ KEYS . alice_secret. clone( ) ] ;
617627 let sig_check_keyring = vec ! [ KEYS . alice_public. clone( ) ] ;
@@ -623,6 +633,10 @@ mod tests {
623633 . unwrap ( ) ;
624634 assert_eq ! ( content, CLEARTEXT ) ;
625635 assert_eq ! ( valid_signatures. len( ) , 1 ) ;
636+ for recipient_fps in valid_signatures. values ( ) {
637+ // Intended Recipient Fingerprint subpackets aren't added currently.
638+ assert_eq ! ( recipient_fps. len( ) , 0 ) ;
639+ }
626640
627641 // Check decrypting as Bob
628642 let decrypt_keyring = vec ! [ KEYS . bob_secret. clone( ) ] ;
@@ -635,6 +649,9 @@ mod tests {
635649 . unwrap ( ) ;
636650 assert_eq ! ( content, CLEARTEXT ) ;
637651 assert_eq ! ( valid_signatures. len( ) , 1 ) ;
652+ for recipient_fps in valid_signatures. values ( ) {
653+ assert_eq ! ( recipient_fps. len( ) , 0 ) ;
654+ }
638655 }
639656
640657 #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
0 commit comments