mirror of
https://github.com/chatmail/core.git
synced 2026-05-09 01:46:30 +03:00
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.
This commit is contained in:
@@ -373,7 +373,7 @@ impl MimeMessage {
|
|||||||
hop_info += "\n\n";
|
hop_info += "\n\n";
|
||||||
hop_info += &dkim_results.to_string();
|
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());
|
let mut aheader_values = mail.headers.get_all_values(HeaderDef::Autocrypt.into());
|
||||||
|
|
||||||
@@ -438,7 +438,7 @@ impl MimeMessage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut autocrypt_header = None;
|
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.
|
// See `get_all_addresses_from_header()` for why we take the last valid header.
|
||||||
for val in aheader_values.iter().rev() {
|
for val in aheader_values.iter().rev() {
|
||||||
autocrypt_header = match Aheader::from_str(val) {
|
autocrypt_header = match Aheader::from_str(val) {
|
||||||
@@ -469,7 +469,7 @@ impl MimeMessage {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut public_keyring = if incoming {
|
let mut public_keyring = if from_is_not_self_addr {
|
||||||
if let Some(autocrypt_header) = autocrypt_header {
|
if let Some(autocrypt_header) = autocrypt_header {
|
||||||
vec![autocrypt_header.public_key]
|
vec![autocrypt_header.public_key]
|
||||||
} else {
|
} else {
|
||||||
@@ -654,6 +654,15 @@ impl MimeMessage {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.last()
|
.last()
|
||||||
.map(|(fp, recipient_fps)| (fp, recipient_fps.into_iter().collect::<HashSet<_>>()));
|
.map(|(fp, recipient_fps)| (fp, recipient_fps.into_iter().collect::<HashSet<_>>()));
|
||||||
|
|
||||||
|
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 {
|
let mut parser = MimeMessage {
|
||||||
parts: Vec::new(),
|
parts: Vec::new(),
|
||||||
headers,
|
headers,
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ use crate::constants::DC_GCL_FOR_FORWARDING;
|
|||||||
use crate::contact;
|
use crate::contact;
|
||||||
use crate::imap::prefetch_should_download;
|
use crate::imap::prefetch_should_download;
|
||||||
use crate::imex::{ImexMode, imex};
|
use crate::imex::{ImexMode, imex};
|
||||||
|
use crate::key;
|
||||||
use crate::securejoin::get_securejoin_qr;
|
use crate::securejoin::get_securejoin_qr;
|
||||||
use crate::test_utils::{
|
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};
|
use crate::tools::{SystemTime, time};
|
||||||
|
|
||||||
@@ -5561,3 +5562,31 @@ async fn test_calendar_alternative() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user