From 28cce5e31d3df541195e8805bb6e5edc83cfa72d Mon Sep 17 00:00:00 2001 From: holger krekel Date: Fri, 27 Mar 2026 11:03:42 +0100 Subject: [PATCH] fix: determine whether a message is an own message by looking at signature. multiple devices can temporarly have different sets of self addresses, and still need to properly recognize incoming versus outgoing messages. Disclaimer: some LLM tooling was initially involved but i went over everything by hand, and also addressed review comments. --- src/mimeparser.rs | 15 +++++++++++--- src/receive_imf/receive_imf_tests.rs | 31 +++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 7165e3c67..e57c31e10 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -373,7 +373,7 @@ impl MimeMessage { hop_info += "\n\n"; hop_info += &dkim_results.to_string(); - let incoming = !context.is_self_addr(&from.addr).await?; + let from_is_not_self_addr = !context.is_self_addr(&from.addr).await?; let mut aheader_values = mail.headers.get_all_values(HeaderDef::Autocrypt.into()); @@ -438,7 +438,7 @@ impl MimeMessage { }; let mut autocrypt_header = None; - if incoming { + if from_is_not_self_addr { // See `get_all_addresses_from_header()` for why we take the last valid header. for val in aheader_values.iter().rev() { autocrypt_header = match Aheader::from_str(val) { @@ -469,7 +469,7 @@ impl MimeMessage { None }; - let mut public_keyring = if incoming { + let mut public_keyring = if from_is_not_self_addr { if let Some(autocrypt_header) = autocrypt_header { vec![autocrypt_header.public_key] } else { @@ -654,6 +654,15 @@ impl MimeMessage { .into_iter() .last() .map(|(fp, recipient_fps)| (fp, recipient_fps.into_iter().collect::>())); + + let incoming = if let Some((ref sig_fp, _)) = signature { + sig_fp.hex() != key::self_fingerprint(context).await? + } else { + // rare case of getting a cleartext message + // so we determine 'incoming' flag by From-address + from_is_not_self_addr + }; + let mut parser = MimeMessage { parts: Vec::new(), headers, diff --git a/src/receive_imf/receive_imf_tests.rs b/src/receive_imf/receive_imf_tests.rs index 2bc281142..ecc3c10c0 100644 --- a/src/receive_imf/receive_imf_tests.rs +++ b/src/receive_imf/receive_imf_tests.rs @@ -13,9 +13,10 @@ use crate::constants::DC_GCL_FOR_FORWARDING; use crate::contact; use crate::imap::prefetch_should_download; use crate::imex::{ImexMode, imex}; +use crate::key; use crate::securejoin::get_securejoin_qr; use crate::test_utils::{ - E2EE_INFO_MSGS, TestContext, TestContextManager, get_chat_msg, mark_as_verified, + E2EE_INFO_MSGS, TestContext, TestContextManager, alice_keypair, get_chat_msg, mark_as_verified, }; use crate::tools::{SystemTime, time}; @@ -5561,3 +5562,31 @@ async fn test_calendar_alternative() -> Result<()> { Ok(()) } + +/// Tests that outgoing encrypted messages are detected +/// by verifying own signature, completely ignoring From address. +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_outgoing_determined_by_signature() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + + // alice_dev2: same key, different address. + let different_from = "very@different.from"; + assert!(!alice.is_self_addr(different_from).await?); + let alice_dev2 = &tcm.unconfigured().await; + alice_dev2.configure_addr(different_from).await; + key::store_self_keypair(alice_dev2, &alice_keypair()).await?; + assert_ne!( + alice.get_config(Config::Addr).await?.unwrap(), + different_from + ); + + // Send message from alice_dev2 and check alice sees it as outgoing + let chat_id = alice_dev2.create_chat_id(bob).await; + let sent_msg = alice_dev2.send_text(chat_id, "hello from new device").await; + let msg = alice.recv_msg(&sent_msg).await; + assert_eq!(msg.state, MessageState::OutDelivered); + + Ok(()) +}