Skip to content

Commit a89b632

Browse files
committed
feat: assign messages to key-contacts based on Issuer Fingerprint
1 parent ac10103 commit a89b632

File tree

3 files changed

+116
-1
lines changed

3 files changed

+116
-1
lines changed

src/mimeparser.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ impl MimeMessage {
427427
None
428428
};
429429

430-
let public_keyring = if incoming {
430+
let mut public_keyring = if incoming {
431431
if let Some(autocrypt_header) = autocrypt_header {
432432
vec![autocrypt_header.public_key]
433433
} else {
@@ -437,6 +437,44 @@ impl MimeMessage {
437437
key::load_self_public_keyring(context).await?
438438
};
439439

440+
if let Some(signature) = match &decrypted_msg {
441+
Some(pgp::composed::Message::Literal { .. }) => None,
442+
Some(pgp::composed::Message::Compressed { .. }) => {
443+
// One layer of compression should already be handled by now.
444+
// We don't decompress messages compressed multiple times.
445+
None
446+
}
447+
Some(pgp::composed::Message::SignedOnePass { reader, .. }) => reader.signature(),
448+
Some(pgp::composed::Message::Signed { reader, .. }) => Some(reader.signature()),
449+
Some(pgp::composed::Message::Encrypted { .. }) => {
450+
// The message is already decrypted once.
451+
None
452+
}
453+
None => None,
454+
} {
455+
for issuer_fingerprint in signature.issuer_fingerprint() {
456+
let issuer_fingerprint =
457+
crate::key::Fingerprint::from(issuer_fingerprint.clone()).hex();
458+
if let Some(public_key_bytes) = context
459+
.sql
460+
.query_row_optional(
461+
"SELECT public_key
462+
FROM public_keys
463+
WHERE fingerprint=?",
464+
(&issuer_fingerprint,),
465+
|row| {
466+
let bytes: Vec<u8> = row.get(0)?;
467+
Ok(bytes)
468+
},
469+
)
470+
.await?
471+
{
472+
let public_key = SignedPublicKey::from_slice(&public_key_bytes)?;
473+
public_keyring.push(public_key)
474+
}
475+
}
476+
}
477+
440478
let mut signatures = if let Some(ref decrypted_msg) = decrypted_msg {
441479
crate::pgp::valid_signature_fingerprints(decrypted_msg, &public_keyring)?
442480
} else {

src/receive_imf/receive_imf_tests.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,6 +3316,31 @@ async fn test_thunderbird_autocrypt() -> Result<()> {
33163316
Ok(())
33173317
}
33183318

3319+
/// Tests that a message without an Autocrypt header is assigned to the key-contact
3320+
/// by using the signature Issuer Fingerprint.
3321+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3322+
async fn test_issuer_fingerprint() -> Result<()> {
3323+
let mut tcm = TestContextManager::new();
3324+
let alice = &tcm.alice().await;
3325+
let bob = &tcm.bob().await;
3326+
3327+
let alice_contact_id = bob.add_or_lookup_contact_id(alice).await;
3328+
3329+
let raw = include_bytes!("../../test-data/message/encrypted-signed.eml");
3330+
let received_msg = receive_imf(bob, raw, false).await?.unwrap();
3331+
3332+
assert_eq!(received_msg.msg_ids.len(), 1);
3333+
let msg_id = received_msg.msg_ids[0];
3334+
3335+
let message = Message::load_from_db(bob, msg_id).await?;
3336+
assert!(message.get_showpadlock());
3337+
3338+
let from_id = message.from_id;
3339+
assert_eq!(from_id, alice_contact_id);
3340+
3341+
Ok(())
3342+
}
3343+
33193344
/// Tests reception of a message from Thunderbird with attached key.
33203345
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
33213346
async fn test_prefer_encrypt_mutual_if_encrypted() -> Result<()> {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
2+
boundary="18566fe03178296e_f40510c3b59ace3_ecf843ae3ccd2b17"
3+
MIME-Version: 1.0
4+
5+
6+
Subject: [...]
7+
Date: Mon, 28 Jul 2025 14:15:14 +0000
8+
Message-ID: <48b9e9cc-2bae-4d41-89b4-a409e2c60c28@localhost>
9+
References: <48b9e9cc-2bae-4d41-89b4-a409e2c60c28@localhost>
10+
Chat-Version: 1.0
11+
12+
13+
--18566fe03178296e_f40510c3b59ace3_ecf843ae3ccd2b17
14+
Content-Type: application/pgp-encrypted; charset="utf-8"
15+
Content-Description: PGP/MIME version identification
16+
Content-Transfer-Encoding: 7bit
17+
18+
Version: 1
19+
20+
--18566fe03178296e_f40510c3b59ace3_ecf843ae3ccd2b17
21+
Content-Type: application/octet-stream; name="encrypted.asc";
22+
charset="utf-8"
23+
Content-Description: OpenPGP encrypted message
24+
Content-Disposition: inline; filename="encrypted.asc";
25+
Content-Transfer-Encoding: 7bit
26+
27+
-----BEGIN PGP MESSAGE-----
28+
29+
wV4D5tq63hTeebASAQdAaI0Cw7tTd10Oz0I1C1Ds5NpF/m6zXlx73Pxzib6Q2Qow
30+
36Fc9KiSZx+vXlw9mZ9zYpumIY/svkYcTRdZwohPWe4TL7iRC8gTZ43/VGZvb+1k
31+
wcBMA+PY3JvEjuMiAQf+MCZLm1vgocYO0xRz2J9Z9QGqScfxBMhYToZbFTx4DAha
32+
hejVmW9AGnNqm8yky2DJpqT3oy//D261HX+xzLfkfWFgHzKud7NtMN6II/d1jyqO
33+
A+K0LjSLPcR3aWl8g30+6bGhHCwc2spP3gIk1aE8S/1c/yQPxD7mqNSkYrY6BgHP
34+
+5Z7ocrud4RKVQayZsWpiiY962w1GJii1h4zE+xhkqFERr1OqepQB7CDf4FBsGr0
35+
qarJtcWCGqd9ksr/wn8Ew9MPHz+8ooCSlXzJ7Uac4VshUm9dXzO3NSS6MgTME0av
36+
WRlRiKuGCmrS4dyf/Tdj1yMB3jJ52SxdEq0yYYLKvdLBWAF9JU7sQp3x4oivpr5U
37+
wv5OP/tbfRVA2zqkOHlMhBAXaLOsiJrHYh3ieJLwfWSmEQARLE7sBgD5fqre1v+D
38+
Xpof8R42j9MQ3St/+nPDsLpbZ2a2RKBl16C8IYnmK1CYwh5lEK533HMHeSLka/ng
39+
soK2HqJxrhkxYpm5OPWN8liSdKlQ8mZXISGNPo8KEWFlPqONqj88UudpQiPCh2qw
40+
aeFC2Y1EkQQGpiTq1GuTQfG8zO4wa3FTW1wOELsvYozszS1Oc1exH767pS+ozNWa
41+
xk8J7ekEFvR5b3TUK2a/ucmRgXuuowCxxV6EiNeRqGa3SfQnQWnMkqsVIeIiARDt
42+
QMbRCrrAjJ+dQEekviq/hqq9+DRLuX0hOybe8aYjJttg7NLgaM04V4QuvWeVQa2g
43+
2IBR2Nw1pcvbyDRkTLsQ2AlR/ig5VPf+4oqN2nBuXgPzply9LU5JyhW9GNIhH+N1
44+
K6w8o5o4ebUx0ldaTjBnrFMUqih4nE9qrrb0UYMcQO3HVAueQii0CZqhJy/n/6U6
45+
cAhnh/fTI1wMoYuYR7cLdf+9P7dHcQPdt3FQA/NrRpv31tyzzKNeBr0VdvoT0uwX
46+
sv0nh+ACjnum7H8yCyoe1tIqUPW0byslROuHAfMwp7rsnBEGJ4AqFYNIdpfSFuia
47+
LsZmODaUG1n6XkamfKPin+Z5Mo8/bK0KwE317Zc/aQD/okvGv4SWsQmW
48+
=hj9/
49+
-----END PGP MESSAGE-----
50+
51+
52+
--18566fe03178296e_f40510c3b59ace3_ecf843ae3ccd2b17--

0 commit comments

Comments
 (0)