Skip to content

Commit 3418726

Browse files
committed
feat: Send Intended Recipient Fingerprint subpackets
Implement "5.2.3.36. Intended Recipient Fingerprint" from RFC 9580.
1 parent 96b3ef2 commit 3418726

File tree

2 files changed

+82
-10
lines changed

2 files changed

+82
-10
lines changed

src/mimeparser/mimeparser_tests.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,34 @@ async fn test_extra_imf_headers() -> Result<()> {
14201420
Ok(())
14211421
}
14221422

1423+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1424+
async fn test_intended_recipient_fingerprint() -> Result<()> {
1425+
let mut tcm = TestContextManager::new();
1426+
let t = &tcm.alice().await;
1427+
let members = [tcm.bob().await, tcm.fiona().await];
1428+
let chat_id = chat::create_group(t, "").await?;
1429+
1430+
chat::send_text_msg(t, chat_id, "hi!".to_string()).await?;
1431+
let t_fp = t.add_or_lookup_contact(t).await.fingerprint().unwrap();
1432+
let sent_msg = t.pop_sent_msg().await;
1433+
let (fp, recipient_fps) = t.parse_msg(&sent_msg).await.signature.unwrap();
1434+
assert_eq!(fp, t_fp);
1435+
assert!(recipient_fps.is_empty());
1436+
1437+
for i in 0..members.len() {
1438+
let member = &members[i];
1439+
let contact = t.add_or_lookup_contact(member).await;
1440+
chat::add_contact_to_chat(t, chat_id, contact.id).await?;
1441+
let sent_msg = t.pop_sent_msg().await;
1442+
let (fp, recipient_fps) = t.parse_msg(&sent_msg).await.signature.unwrap();
1443+
assert_eq!(fp, t_fp);
1444+
assert_eq!(recipient_fps.len(), i + 1);
1445+
assert!(!recipient_fps.contains(&t_fp));
1446+
assert!(recipient_fps.contains(&contact.fingerprint().unwrap()));
1447+
}
1448+
Ok(())
1449+
}
1450+
14231451
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
14241452
async fn test_long_in_reply_to() -> Result<()> {
14251453
let t = TestContext::new_alice().await;

src/pgp.rs

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ use pgp::armor::BlockType;
1010
use pgp::composed::{
1111
ArmorOptions, DecryptionOptions, Deserializable, DetachedSignature, KeyType as PgpKeyType,
1212
Message, MessageBuilder, SecretKeyParamsBuilder, SignedPublicKey, SignedPublicSubKey,
13-
SignedSecretKey, SubkeyParamsBuilder, TheRing,
13+
SignedSecretKey, SubkeyParamsBuilder, SubpacketConfig, TheRing,
1414
};
1515
use pgp::crypto::aead::{AeadAlgorithm, ChunkSize};
1616
use pgp::crypto::ecc_curve::ECCCurve;
1717
use pgp::crypto::hash::HashAlgorithm;
1818
use pgp::crypto::sym::SymmetricKeyAlgorithm;
1919
use pgp::packet::{SignatureConfig, SignatureType, Subpacket, SubpacketData};
2020
use pgp::types::{
21-
CompressionAlgorithm, KeyDetails, Password, PublicKeyTrait, SecretKeyTrait as _, StringToKey,
21+
CompressionAlgorithm, KeyDetails, KeyVersion, Password, PublicKeyTrait, SecretKeyTrait as _,
22+
StringToKey,
2223
};
2324
use rand_old::{Rng as _, thread_rng};
2425
use tokio::runtime::Handle;
@@ -190,6 +191,35 @@ pub async fn pk_encrypt(
190191
let pkeys = public_keys_for_encryption
191192
.iter()
192193
.filter_map(select_pk_for_encryption);
194+
let subpkts = {
195+
let private_key_fp = private_key_for_signing.fingerprint();
196+
let mut hashed = Vec::with_capacity(1 + public_keys_for_encryption.len());
197+
hashed.push(Subpacket::critical(SubpacketData::SignatureCreationTime(
198+
chrono::Utc::now().trunc_subsecs(0),
199+
))?);
200+
for key in &public_keys_for_encryption {
201+
let fp = key.fingerprint();
202+
if fp == private_key_fp {
203+
continue;
204+
}
205+
let data = SubpacketData::IntendedRecipientFingerprint(fp);
206+
let subpkt = match private_key_for_signing.version() < KeyVersion::V6 {
207+
true => Subpacket::regular(data)?,
208+
false => Subpacket::critical(data)?,
209+
};
210+
hashed.push(subpkt);
211+
}
212+
hashed.push(Subpacket::regular(SubpacketData::IssuerFingerprint(
213+
private_key_fp,
214+
))?);
215+
let mut unhashed = vec![];
216+
if private_key_for_signing.version() <= KeyVersion::V4 {
217+
unhashed.push(Subpacket::regular(SubpacketData::Issuer(
218+
private_key_for_signing.key_id(),
219+
))?);
220+
}
221+
SubpacketConfig::UserDefined { hashed, unhashed }
222+
};
193223

194224
let msg = MessageBuilder::from_bytes("", plain);
195225
let encoded_msg = match seipd_version {
@@ -205,7 +235,12 @@ pub async fn pk_encrypt(
205235
}
206236

207237
let hash_algorithm = private_key_for_signing.hash_alg();
208-
msg.sign(&*private_key_for_signing, Password::empty(), hash_algorithm);
238+
msg.sign_with_subpackets(
239+
&*private_key_for_signing,
240+
Password::empty(),
241+
hash_algorithm,
242+
subpkts,
243+
);
209244
if compress {
210245
msg.compression(CompressionAlgorithm::ZLIB);
211246
}
@@ -229,7 +264,12 @@ pub async fn pk_encrypt(
229264
}
230265

231266
let hash_algorithm = private_key_for_signing.hash_alg();
232-
msg.sign(&*private_key_for_signing, Password::empty(), hash_algorithm);
267+
msg.sign_with_subpackets(
268+
&*private_key_for_signing,
269+
Password::empty(),
270+
hash_algorithm,
271+
subpkts,
272+
);
233273
if compress {
234274
msg.compression(CompressionAlgorithm::ZLIB);
235275
}
@@ -264,9 +304,14 @@ pub fn pk_calc_signature(
264304
chrono::Utc::now().trunc_subsecs(0),
265305
))?,
266306
];
267-
config.unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(
268-
private_key_for_signing.key_id(),
269-
))?];
307+
config.unhashed_subpackets = vec![];
308+
if private_key_for_signing.version() <= KeyVersion::V4 {
309+
config
310+
.unhashed_subpackets
311+
.push(Subpacket::regular(SubpacketData::Issuer(
312+
private_key_for_signing.key_id(),
313+
))?);
314+
}
270315

271316
let signature = config.sign(
272317
&private_key_for_signing.primary_key,
@@ -634,8 +679,7 @@ mod tests {
634679
assert_eq!(content, CLEARTEXT);
635680
assert_eq!(valid_signatures.len(), 1);
636681
for recipient_fps in valid_signatures.values() {
637-
// Intended Recipient Fingerprint subpackets aren't added currently.
638-
assert_eq!(recipient_fps.len(), 0);
682+
assert_eq!(recipient_fps.len(), 1);
639683
}
640684

641685
// Check decrypting as Bob
@@ -650,7 +694,7 @@ mod tests {
650694
assert_eq!(content, CLEARTEXT);
651695
assert_eq!(valid_signatures.len(), 1);
652696
for recipient_fps in valid_signatures.values() {
653-
assert_eq!(recipient_fps.len(), 0);
697+
assert_eq!(recipient_fps.len(), 1);
654698
}
655699
}
656700

0 commit comments

Comments
 (0)