Skip to content

Commit 1664be5

Browse files
committed
fix: receive_imf: Look up key contact by intended recipient fingerprint (#7661)
For now, do this only for `OneOneChat` and `MailingListOrBroadcast`, this is enough to correctly support messages from modern Delta Chat versions sending Intended Recipient Fingerprint subpackets and single-recipient messages from modern versions of other MUAs.
1 parent 8834b4c commit 1664be5

4 files changed

Lines changed: 61 additions & 11 deletions

File tree

src/pgp.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,13 @@ pub async fn pk_encrypt(
196196
hashed.push(Subpacket::critical(SubpacketData::SignatureCreationTime(
197197
chrono::Utc::now().trunc_subsecs(0),
198198
))?);
199+
// Test "elena" uses old Delta Chat.
200+
let skip = private_key_for_signing.dc_fingerprint().hex()
201+
== "B86586B6DEF437D674BFAFC02A6B2EBC633B9E82";
199202
for key in &public_keys_for_encryption {
203+
if skip {
204+
break;
205+
}
200206
let data = SubpacketData::IntendedRecipientFingerprint(key.fingerprint());
201207
let subpkt = match private_key_for_signing.version() < KeyVersion::V6 {
202208
true => Subpacket::regular(data)?,

src/receive_imf.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ use crate::events::EventType;
2727
use crate::headerdef::{HeaderDef, HeaderDefMap};
2828
use crate::imap::{GENERATED_PREFIX, markseen_on_imap_table};
2929
use crate::key::{DcKey, Fingerprint};
30-
use crate::key::{load_self_public_key_opt, self_fingerprint, self_fingerprint_opt};
30+
use crate::key::{
31+
load_self_public_key, load_self_public_key_opt, self_fingerprint, self_fingerprint_opt,
32+
};
3133
use crate::log::{LogExt as _, warn};
3234
use crate::message::{
3335
self, Message, MessageState, MessengerMessage, MsgId, Viewtype, rfc724_mid_exists,
@@ -386,8 +388,30 @@ async fn get_to_and_past_contact_ids(
386388
// This is an encrypted 1:1 chat.
387389
to_ids = pgp_to_ids
388390
} else {
389-
let ids = match mime_parser.was_encrypted() {
390-
true => {
391+
let ids = if mime_parser.was_encrypted() {
392+
let mut recipient_fps = mime_parser
393+
.signature
394+
.as_ref()
395+
.map(|(_, recipient_fps)| recipient_fps.iter().cloned().collect::<Vec<_>>())
396+
.unwrap_or_default();
397+
// If there are extra recipient fingerprints, it may be a non-chat "implicit
398+
// Bcc" message. Fall back to in-chat lookup if so.
399+
if !recipient_fps.is_empty() && recipient_fps.len() <= 2 {
400+
let self_fp = load_self_public_key(context).await?.dc_fingerprint();
401+
recipient_fps.retain(|fp| *fp != self_fp);
402+
if recipient_fps.is_empty() {
403+
vec![Some(ContactId::SELF)]
404+
} else {
405+
add_or_lookup_key_contacts(
406+
context,
407+
&mime_parser.recipients,
408+
&mime_parser.gossiped_keys,
409+
&recipient_fps,
410+
Origin::Hidden,
411+
)
412+
.await?
413+
}
414+
} else {
391415
lookup_key_contacts_fallback_to_chat(
392416
context,
393417
&mime_parser.recipients,
@@ -396,7 +420,8 @@ async fn get_to_and_past_contact_ids(
396420
)
397421
.await?
398422
}
399-
false => vec![],
423+
} else {
424+
vec![]
400425
};
401426
if mime_parser.was_encrypted() && !ids.contains(&None)
402427
// Prefer creating PGP chats if there are any key-contacts. At least this prevents

src/receive_imf/receive_imf_tests.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5111,9 +5111,9 @@ async fn test_dont_verify_by_verified_by_unknown() -> Result<()> {
51115111
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
51125112
async fn test_recv_outgoing_msg_before_securejoin() -> Result<()> {
51135113
let mut tcm = TestContextManager::new();
5114-
let a0 = &tcm.alice().await;
5115-
let a1 = &tcm.alice().await;
51165114
let bob = &tcm.bob().await;
5115+
let a0 = &tcm.elena().await;
5116+
let a1 = &tcm.elena().await;
51175117

51185118
tcm.execute_securejoin(bob, a0).await;
51195119
let chat_id_a0_bob = a0.create_chat_id(bob).await;
@@ -5154,14 +5154,31 @@ async fn test_recv_outgoing_msg_before_securejoin() -> Result<()> {
51545154
chat_a1.why_cant_send(a1).await?,
51555155
Some(CantSendReason::ContactRequest)
51565156
);
5157+
5158+
let a0 = &tcm.alice().await;
5159+
let a1 = &tcm.alice().await;
5160+
tcm.execute_securejoin(bob, a0).await;
5161+
let chat_id_a0_bob = a0.create_chat_id(bob).await;
5162+
let sent_msg = a0.send_text(chat_id_a0_bob, "Hi").await;
5163+
bob.recv_msg(&sent_msg).await;
5164+
let msg_a1 = a1.recv_msg(&sent_msg).await;
5165+
assert!(msg_a1.get_showpadlock());
5166+
let chat_a1 = Chat::load_from_db(a1, msg_a1.chat_id).await?;
5167+
assert_eq!(chat_a1.typ, Chattype::Single);
5168+
assert!(chat_a1.is_encrypted(a1).await?);
5169+
assert_eq!(
5170+
chat::get_chat_contacts(a1, chat_a1.id).await?,
5171+
[a1.add_or_lookup_contact_id(bob).await]
5172+
);
5173+
assert!(chat_a1.can_send(a1).await?);
51575174
Ok(())
51585175
}
51595176

51605177
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
51615178
async fn test_recv_outgoing_msg_before_having_key_and_after() -> Result<()> {
51625179
let mut tcm = TestContextManager::new();
5163-
let a0 = &tcm.alice().await;
5164-
let a1 = &tcm.alice().await;
5180+
let a0 = &tcm.elena().await;
5181+
let a1 = &tcm.elena().await;
51655182
let bob = &tcm.bob().await;
51665183

51675184
tcm.execute_securejoin(bob, a0).await;
@@ -5174,9 +5191,9 @@ async fn test_recv_outgoing_msg_before_having_key_and_after() -> Result<()> {
51745191
assert!(!chat_a1.is_encrypted(a1).await?);
51755192

51765193
// Device a1 somehow learns Bob's key and creates the corresponding chat. However, this doesn't
5177-
// help currently because we only look up key contacts by address in a particular chat and the
5178-
// new chat isn't referenced by the received message. This should be fixed by sending and
5179-
// receiving Intended Recipient Fingerprint subpackets.
5194+
// help because we only look up key contacts by address in a particular chat and the new chat
5195+
// isn't referenced by the received message. This is fixed by sending and receiving Intended
5196+
// Recipient Fingerprint subpackets which elena doesn't send.
51805197
a1.create_chat_id(bob).await;
51815198
let sent_msg = a0.send_text(chat_id_a0_bob, "Hi again").await;
51825199
let msg_a1 = a1.recv_msg(&sent_msg).await;

src/test_utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ impl TestContextManager {
117117
.await
118118
}
119119

120+
/// Returns new elena's "device".
121+
/// Elena doesn't send Intended Recipient Fingerprint subpackets to simulate old Delta Chat.
120122
pub async fn elena(&mut self) -> TestContext {
121123
TestContext::builder()
122124
.configure_elena()

0 commit comments

Comments
 (0)