diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 1ca7323c9..309e72687 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -23,6 +23,7 @@ use crate::ephemeral::{stock_ephemeral_timer_changed, Timer as EphemeralTimer}; use crate::events::EventType; use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::imap::{markseen_on_imap_table, GENERATED_PREFIX}; +use crate::key::DcKey; use crate::location; use crate::log::LogExt; use crate::message::{ @@ -766,7 +767,18 @@ async fn add_parts( // That's why the config is checked here, and not above. && context.get_config_bool(Config::VerifiedOneOnOneChats).await? { - new_protection = ProtectionStatus::ProtectionBroken; + let decryption_info = &mime_parser.decryption_info; + new_protection = + match decryption_info.autocrypt_header.as_ref().filter(|ah| { + Some(&ah.public_key.fingerprint()) + == decryption_info + .peerstate + .as_ref() + .and_then(|p| p.verified_key_fingerprint.as_ref()) + }) { + Some(_) => chat.protected, + None => ProtectionStatus::ProtectionBroken, + }; } if chat.protected != new_protection { // The message itself will be sorted under the device message since the device diff --git a/src/test_utils.rs b/src/test_utils.rs index fe02cfa1f..98fbea15d 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -136,6 +136,22 @@ impl TestContextManager { received_msg } + /// - Let one TestContext send a message. + /// - Let the other TestContext receive it. + /// - Assert that the message text is the formatted `err`. + /// - Assert that the message info contains the original text. + pub async fn send_recv_with_err( + &self, + from: &TestContext, + to: &TestContext, + err: &str, + msg: &str, + ) -> Message { + let received_msg = self.try_send_recv(from, to, msg).await; + check_msg_with_err(to, &received_msg, err, msg).await; + received_msg + } + /// - Let one TestContext send a message /// - Let the other TestContext receive it pub async fn try_send_recv(&self, from: &TestContext, to: &TestContext, msg: &str) -> Message { @@ -1198,6 +1214,18 @@ async fn write_msg(context: &Context, prefix: &str, msg: &Message, buf: &mut Str .unwrap(); } +/// - Assert that the message text is the formatted error `err`. +/// - Assert that the message info contains the original text. +pub async fn check_msg_with_err(ctx: &TestContext, msg: &Message, err: &str, text: &str) { + assert_eq!(msg.text, format!("[{err}]")); + assert!(msg + .id + .get_info(ctx) + .await + .unwrap() + .contains(&format!("\n\n{text}\n\n"))); +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/tests/verified_chats.rs b/src/tests/verified_chats.rs index 413338929..19345a393 100644 --- a/src/tests/verified_chats.rs +++ b/src/tests/verified_chats.rs @@ -1,7 +1,7 @@ use anyhow::Result; use pretty_assertions::assert_eq; -use crate::chat::{Chat, ProtectionStatus}; +use crate::chat::{add_contact_to_chat, create_broadcast_list, Chat, ProtectionStatus}; use crate::chatlist::Chatlist; use crate::config::Config; use crate::constants::DC_GCL_FOR_FORWARDING; @@ -12,7 +12,9 @@ use crate::mimefactory::MimeFactory; use crate::mimeparser::SystemMessage; use crate::receive_imf::receive_imf; use crate::stock_str; -use crate::test_utils::{get_chat_msg, mark_as_verified, TestContext, TestContextManager}; +use crate::test_utils::{ + check_msg_with_err, get_chat_msg, mark_as_verified, TestContext, TestContextManager, +}; use crate::{e2ee, message}; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -742,6 +744,45 @@ async fn test_create_oneonone_chat_with_former_verified_contact() -> Result<()> Ok(()) } +/// Some messages are sent unencrypted, but they mustn't break a verified chat protection. +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_unencrypted() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = tcm.alice().await; + let bob = tcm.bob().await; + enable_verified_oneonone_chats(&[&alice]).await; + mark_as_verified(&alice, &bob).await; + + let err_str = "This message is not encrypted. See 'Info' for more details"; + let msg = tcm.send_recv_with_err(&bob, &alice, err_str, "hi").await; + assert!(!msg.get_showpadlock()); + let alice_chat = Chat::load_from_db(&alice, msg.chat_id).await?; + assert!(alice_chat.is_protected()); + assert!(!alice_chat.is_protection_broken()); + + let broadcast_id = create_broadcast_list(&bob).await?; + add_contact_to_chat( + &bob, + broadcast_id, + bob.add_or_lookup_contact(&alice).await.id, + ) + .await?; + let sent_msg = bob.send_text(broadcast_id, "hi all").await; + let msg = alice.recv_msg(&sent_msg).await; + check_msg_with_err(&alice, &msg, err_str, "hi all").await; + assert!(!msg.get_showpadlock()); + assert_eq!(msg.chat_id, alice_chat.id); + let alice_chat = Chat::load_from_db(&alice, msg.chat_id).await?; + assert!(alice_chat.is_protected()); + assert!(!alice_chat.is_protection_broken()); + + alice + .golden_test_chat(alice_chat.id, "verified_chats_test_unencrypted") + .await; + + Ok(()) +} + // ============== Helper Functions ============== async fn assert_verified(this: &TestContext, other: &TestContext, protected: ProtectionStatus) { diff --git a/test-data/golden/verified_chats_test_unencrypted b/test-data/golden/verified_chats_test_unencrypted new file mode 100644 index 000000000..6c4608ad3 --- /dev/null +++ b/test-data/golden/verified_chats_test_unencrypted @@ -0,0 +1,6 @@ +Single#Chat#10: bob@example.net [bob@example.net] 🛡️ +-------------------------------------------------------------------------------- +Msg#10: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️] +Msg#11: (Contact#Contact#10): [This message is not encrypted. See 'Info' for more details] [FRESH] +Msg#12: (Contact#Contact#10): [This message is not encrypted. See 'Info' for more details] [FRESH] +--------------------------------------------------------------------------------