From 51bbdadfad7224cd253d35bbd1268f04f801040e Mon Sep 17 00:00:00 2001 From: link2xt Date: Mon, 3 Mar 2025 22:52:10 +0000 Subject: [PATCH] feat: ignore encryption preferences Encryption preference is sent in Autocrypt header, but otherwise ignored. Delta Chat always prefers encryption if it is available. --- python/src/deltachat/testplugin.py | 2 - python/tests/test_1_online.py | 166 ----------------------------- src/chat.rs | 21 ++-- src/chat/chat_tests.rs | 10 +- src/config/config_tests.rs | 2 +- src/configure.rs | 1 - src/e2ee.rs | 72 +------------ src/webxdc/webxdc_tests.rs | 43 ++++---- 8 files changed, 32 insertions(+), 285 deletions(-) diff --git a/python/src/deltachat/testplugin.py b/python/src/deltachat/testplugin.py index 92da00ea5..460db0795 100644 --- a/python/src/deltachat/testplugin.py +++ b/python/src/deltachat/testplugin.py @@ -423,8 +423,6 @@ class ACFactory: where we can make valid SMTP and IMAP connections with. """ configdict = next(self._liveconfig_producer).copy() - if "e2ee_enabled" not in configdict: - configdict["e2ee_enabled"] = "1" if self.pytestconfig.getoption("--strict-tls"): # Enable strict certificate checks for online accounts diff --git a/python/tests/test_1_online.py b/python/tests/test_1_online.py index a2b09b032..9238a8ad9 100644 --- a/python/tests/test_1_online.py +++ b/python/tests/test_1_online.py @@ -919,64 +919,6 @@ def test_gossip_optimization(acfactory, lp): assert gossiped_timestamp == int(msg.time_sent.timestamp()) -def test_gossip_encryption_preference(acfactory, lp): - """Test that encryption preference of group members is gossiped to new members. - This is a Delta Chat extension to Autocrypt 1.1.0, which Autocrypt-Gossip headers - SHOULD NOT contain encryption preference. - """ - ac1, ac2, ac3 = acfactory.get_online_accounts(3) - - lp.sec("ac1 learns that ac2 prefers encryption") - ac1.create_chat(ac2) - msg = ac2.create_chat(ac1).send_text("first message") - msg = ac1._evtracker.wait_next_incoming_message() - assert msg.text == "first message" - assert not msg.is_encrypted() - res = "End-to-end encryption preferred:\n{}".format(ac2.get_config("addr")) - assert msg.chat.get_encryption_info() == res - lp.sec("ac2 learns that ac3 prefers encryption") - ac2.create_chat(ac3) - msg = ac3.create_chat(ac2).send_text("I prefer encryption") - msg = ac2._evtracker.wait_next_incoming_message() - assert msg.text == "I prefer encryption" - assert not msg.is_encrypted() - - lp.sec("ac3 does not know that ac1 prefers encryption") - ac1.create_chat(ac3) - chat = ac3.create_chat(ac1) - res = "No encryption:\n{}".format(ac1.get_config("addr")) - assert chat.get_encryption_info() == res - msg = chat.send_text("not encrypted") - msg = ac1._evtracker.wait_next_incoming_message() - assert msg.text == "not encrypted" - assert not msg.is_encrypted() - - lp.sec("ac1 creates a group chat with ac2") - group_chat = ac1.create_group_chat("hello") - group_chat.add_contact(ac2) - encryption_info = group_chat.get_encryption_info() - res = "End-to-end encryption preferred:\n{}".format(ac2.get_config("addr")) - assert encryption_info == res - msg = group_chat.send_text("hi") - - msg = ac2._evtracker.wait_next_incoming_message() - assert msg.is_encrypted() - assert msg.text == "hi" - - lp.sec("ac2 adds ac3 to the group") - msg.chat.add_contact(ac3) - assert msg.is_encrypted() - - lp.sec("ac3 learns that ac1 prefers encryption") - msg = ac3._evtracker.wait_next_incoming_message() - encryption_info = msg.chat.get_encryption_info().splitlines() - assert encryption_info[0] == "End-to-end encryption preferred:" - assert ac1.get_config("addr") in encryption_info[1:] - assert ac2.get_config("addr") in encryption_info[1:] - msg = chat.send_text("encrypted") - assert msg.is_encrypted() - - def test_send_first_message_as_long_unicode_with_cr(acfactory, lp): ac1, ac2 = acfactory.get_online_accounts(2) @@ -1167,61 +1109,6 @@ def test_dont_show_emails(acfactory, lp): assert len(msg.chat.get_messages()) == 3 -def test_prefer_encrypt(acfactory, lp): - """Test quorum rule for encryption preference in 1:1 and group chat.""" - ac1 = acfactory.new_online_configuring_account(fix_is_chatmail=True) - ac2 = acfactory.new_online_configuring_account(fix_is_chatmail=True) - ac3 = acfactory.new_online_configuring_account(fix_is_chatmail=True) - acfactory.bring_accounts_online() - ac1.set_config("e2ee_enabled", "0") - ac2.set_config("e2ee_enabled", "1") - ac3.set_config("e2ee_enabled", "0") - - # Make sure we do not send a copy to ourselves. This is to - # test that we count own preference even when we are not in - # the recipient list. - ac1.set_config("bcc_self", "0") - ac2.set_config("bcc_self", "0") - ac3.set_config("bcc_self", "0") - - acfactory.introduce_each_other([ac1, ac2, ac3]) - - lp.sec("ac1: sending message to ac2") - chat1 = ac1.create_chat(ac2) - msg1 = chat1.send_text("message1") - assert not msg1.is_encrypted() - ac2._evtracker.wait_next_incoming_message() - - lp.sec("ac2: sending message to ac1") - chat2 = ac2.create_chat(ac1) - msg2 = chat2.send_text("message2") - # Own preference is `Mutual` and we have the peer's key. - assert msg2.is_encrypted() - ac1._evtracker.wait_next_incoming_message() - - lp.sec("ac1: sending message to group chat with ac2 and ac3") - group = ac1.create_group_chat("hello") - group.add_contact(ac2) - group.add_contact(ac3) - msg3 = group.send_text("message3") - assert not msg3.is_encrypted() - ac2._evtracker.wait_next_incoming_message() - ac3._evtracker.wait_next_incoming_message() - - lp.sec("ac3: start preferring encryption and inform ac1") - ac3.set_config("e2ee_enabled", "1") - chat3 = ac3.create_chat(ac1) - msg4 = chat3.send_text("message4") - # Own preference is `Mutual` and we have the peer's key. - assert msg4.is_encrypted() - ac1._evtracker.wait_next_incoming_message() - - lp.sec("ac1: sending another message to group chat with ac2 and ac3") - msg5 = group.send_text("message5") - # Majority prefers encryption now - assert msg5.is_encrypted() - - def test_bot(acfactory, lp): """Test that bot messages can be identified as such""" ac1, ac2 = acfactory.get_online_accounts(2) @@ -1250,59 +1137,6 @@ def test_bot(acfactory, lp): assert msg_in.is_bot() -def test_quote_encrypted(acfactory, lp): - """Test that replies to encrypted messages with quotes are encrypted.""" - ac1, ac2 = acfactory.get_online_accounts(2) - - lp.sec("ac1: create chat with ac2") - chat = ac1.create_chat(ac2) - - lp.sec("sending text message from ac1 to ac2") - msg1 = chat.send_text("message1") - assert not msg1.is_encrypted() - - lp.sec("wait for ac2 to receive message") - msg2 = ac2._evtracker.wait_next_incoming_message() - assert msg2.text == "message1" - assert not msg2.is_encrypted() - - lp.sec("create new chat with contact and send back (encrypted) message") - msg2.create_chat().send_text("message-back") - - lp.sec("wait for ac1 to receive message") - msg3 = ac1._evtracker.wait_next_incoming_message() - assert msg3.text == "message-back" - assert msg3.is_encrypted() - - lp.sec("ac1: e2ee_enabled=0 and see if reply is encrypted") - print("ac1: e2ee_enabled={}".format(ac1.get_config("e2ee_enabled"))) - print("ac2: e2ee_enabled={}".format(ac2.get_config("e2ee_enabled"))) - ac1.set_config("e2ee_enabled", "0") - - for quoted_msg in msg1, msg3: - # Save the draft with a quote. - msg_draft = Message.new_empty(ac1, "text") - msg_draft.set_text("message reply") - msg_draft.quote = quoted_msg - chat.set_draft(msg_draft) - - # Get the draft and send it. - msg_draft = chat.get_draft() - chat.send_msg(msg_draft) - - chat.set_draft(None) - assert chat.get_draft() is None - - # Quote should be replaced with "..." if quoted message is encrypted. - msg_in = ac2._evtracker.wait_next_incoming_message() - assert msg_in.text == "message reply" - assert not msg_in.is_encrypted() - if quoted_msg.is_encrypted(): - assert msg_in.quoted_text == "..." - else: - assert msg_in.quoted_text == quoted_msg.text - - def test_quote_attachment(tmp_path, acfactory, lp): """Test that replies with an attachment and a quote are received correctly.""" ac1, ac2 = acfactory.get_online_accounts(2) diff --git a/src/chat.rs b/src/chat.rs index a74fe9c77..6db3b465a 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1313,8 +1313,7 @@ impl ChatId { /// /// To get more verbose summary for a contact, including its key fingerprint, use [`Contact::get_encrinfo`]. pub async fn get_encryption_info(self, context: &Context) -> Result { - let mut ret_mutual = String::new(); - let mut ret_nopreference = String::new(); + let mut ret_available = String::new(); let mut ret_reset = String::new(); for contact_id in get_chat_contacts(context, self) @@ -1330,8 +1329,9 @@ impl ChatId { .filter(|peerstate| peerstate.peek_key(false).is_some()) .map(|peerstate| peerstate.prefer_encrypt) { - Some(EncryptPreference::Mutual) => ret_mutual += &format!("{addr}\n"), - Some(EncryptPreference::NoPreference) => ret_nopreference += &format!("{addr}\n"), + Some(EncryptPreference::Mutual) | Some(EncryptPreference::NoPreference) => { + ret_available += &format!("{addr}\n") + } Some(EncryptPreference::Reset) | None => ret_reset += &format!("{addr}\n"), }; } @@ -1343,23 +1343,14 @@ impl ChatId { ret.push('\n'); ret += &ret_reset; } - if !ret_nopreference.is_empty() { + if !ret_available.is_empty() { if !ret.is_empty() { ret.push('\n'); } ret += &stock_str::e2e_available(context).await; ret.push(':'); ret.push('\n'); - ret += &ret_nopreference; - } - if !ret_mutual.is_empty() { - if !ret.is_empty() { - ret.push('\n'); - } - ret += &stock_str::e2e_preferred(context).await; - ret.push(':'); - ret.push('\n'); - ret += &ret_mutual; + ret += &ret_available; } Ok(ret.trim().to_string()) diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index 11f58bd9e..1ce96380b 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -299,10 +299,6 @@ async fn test_member_add_remove() -> Result<()> { let alice = tcm.alice().await; let bob = tcm.bob().await; - // Disable encryption so we can inspect raw message contents. - alice.set_config(Config::E2eeEnabled, Some("0")).await?; - bob.set_config(Config::E2eeEnabled, Some("0")).await?; - // Create contact for Bob on the Alice side with name "robert". let alice_bob_contact_id = Contact::create(&alice, "robert", "bob@example.net").await?; @@ -373,9 +369,6 @@ async fn test_parallel_member_remove() -> Result<()> { let alice = tcm.alice().await; let bob = tcm.bob().await; - alice.set_config(Config::E2eeEnabled, Some("0")).await?; - bob.set_config(Config::E2eeEnabled, Some("0")).await?; - let alice_bob_contact_id = Contact::create(&alice, "Bob", "bob@example.net").await?; let alice_fiona_contact_id = Contact::create(&alice, "Fiona", "fiona@example.net").await?; let alice_claire_contact_id = Contact::create(&alice, "Claire", "claire@example.net").await?; @@ -2677,11 +2670,10 @@ async fn test_chat_get_encryption_info() -> Result<()> { "No encryption:\n\ fiona@example.net\n\ \n\ - End-to-end encryption preferred:\n\ + End-to-end encryption available:\n\ bob@example.net" ); - bob.set_config(Config::E2eeEnabled, Some("0")).await?; send_text_msg(&bob, direct_chat.id, "Hello!".to_string()).await?; alice.recv_msg(&bob.pop_sent_msg().await).await; diff --git a/src/config/config_tests.rs b/src/config/config_tests.rs index 3cb218a18..c35e9a023 100644 --- a/src/config/config_tests.rs +++ b/src/config/config_tests.rs @@ -86,7 +86,7 @@ async fn test_set_config_bool() -> Result<()> { let t = TestContext::new().await; // We need some config that defaults to true - let c = Config::E2eeEnabled; + let c = Config::MdnsEnabled; assert_eq!(t.get_config_bool(c).await?, true); t.set_config_bool(c, false).await?; assert_eq!(t.get_config_bool(c).await?, false); diff --git a/src/configure.rs b/src/configure.rs index 854ac9b7f..1b17cf22f 100644 --- a/src/configure.rs +++ b/src/configure.rs @@ -442,7 +442,6 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result, String)], ) -> Result { let is_chatmail = context.is_chatmail().await?; - let mut prefer_encrypt_count = if self.prefer_encrypt == EncryptPreference::Mutual { - 1 - } else { - 0 - }; + let mut prefer_encrypt_count = 1; for (peerstate, addr) in peerstates { match peerstate { Some(peerstate) => { - let prefer_encrypt = peerstate.prefer_encrypt; - info!(context, "Peerstate for {addr:?} is {prefer_encrypt}."); if match peerstate.prefer_encrypt { - EncryptPreference::NoPreference | EncryptPreference::Reset => { - (peerstate.prefer_encrypt != EncryptPreference::Reset || is_chatmail) - && self.prefer_encrypt == EncryptPreference::Mutual - } - EncryptPreference::Mutual => true, + EncryptPreference::Reset => is_chatmail, + EncryptPreference::NoPreference | EncryptPreference::Mutual => true, } { prefer_encrypt_count += 1; } @@ -176,6 +167,7 @@ pub async fn ensure_secret_key_exists(context: &Context) -> Result<()> { mod tests { use super::*; use crate::chat::send_text_msg; + use crate::config::Config; use crate::key::DcKey; use crate::message::{Message, Viewtype}; use crate::param::Param; @@ -329,7 +321,6 @@ Sent with my Delta Chat Messenger: https://delta.chat"; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_should_encrypt() -> Result<()> { let t = TestContext::new_alice().await; - assert!(t.get_config_bool(Config::E2eeEnabled).await?); let encrypt_helper = EncryptHelper::new(&t).await.unwrap(); let ps = new_peerstates(EncryptPreference::NoPreference); @@ -352,61 +343,6 @@ Sent with my Delta Chat Messenger: https://delta.chat"; Ok(()) } - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_should_encrypt_e2ee_disabled() -> Result<()> { - let t = &TestContext::new_alice().await; - t.set_config_bool(Config::E2eeEnabled, false).await?; - let encrypt_helper = EncryptHelper::new(t).await.unwrap(); - - let ps = new_peerstates(EncryptPreference::NoPreference); - assert!(!encrypt_helper.should_encrypt(t, false, &ps).await?); - - let ps = new_peerstates(EncryptPreference::Reset); - assert!(encrypt_helper.should_encrypt(t, true, &ps).await?); - - let mut ps = new_peerstates(EncryptPreference::Mutual); - // Own preference is `NoPreference` and there's no majority with `Mutual`. - assert!(!encrypt_helper.should_encrypt(t, false, &ps).await?); - // Now the majority wants to encrypt. Let's encrypt, anyway there are other cases when we - // can't send unencrypted, e.g. protected groups. - ps.push(ps[0].clone()); - assert!(encrypt_helper.should_encrypt(t, false, &ps).await?); - - // Test with missing peerstate. - let ps = vec![(None, "bob@foo.bar".to_string())]; - assert!(encrypt_helper.should_encrypt(t, true, &ps).await.is_err()); - Ok(()) - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_chatmail_prefers_to_encrypt() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; - let bob = &tcm.bob().await; - bob.set_config_bool(Config::IsChatmail, true).await?; - - let bob_chat_id = tcm - .send_recv_accept(alice, bob, "Hello from DC") - .await - .chat_id; - receive_imf( - bob, - b"From: alice@example.org\n\ - To: bob@example.net\n\ - Message-ID: <2222@example.org>\n\ - Date: Sun, 22 Mar 3000 22:37:58 +0000\n\ - \n\ - Hello from another MUA\n", - false, - ) - .await?; - send_text_msg(bob, bob_chat_id, "hi".to_string()).await?; - let sent_msg = bob.pop_sent_msg().await; - let msg = Message::load_from_db(bob, sent_msg.sender_msg_id).await?; - assert!(msg.get_showpadlock()); - Ok(()) - } - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_chatmail_can_send_unencrypted() -> Result<()> { let mut tcm = TestContextManager::new(); diff --git a/src/webxdc/webxdc_tests.rs b/src/webxdc/webxdc_tests.rs index 300e620b8..1d0e92492 100644 --- a/src/webxdc/webxdc_tests.rs +++ b/src/webxdc/webxdc_tests.rs @@ -1648,29 +1648,26 @@ async fn test_webxdc_no_internet_access() -> Result<()> { let group_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?; let broadcast_id = create_broadcast_list(&t).await?; - for e2ee in ["1", "0"] { - t.set_config(Config::E2eeEnabled, Some(e2ee)).await?; - for chat_id in [self_id, single_id, group_id, broadcast_id] { - for internet_xdc in [true, false] { - let mut instance = create_webxdc_instance( - &t, - "foo.xdc", - if internet_xdc { - include_bytes!("../../test-data/webxdc/request-internet-access.xdc") - } else { - include_bytes!("../../test-data/webxdc/minimal.xdc") - }, - )?; - let instance_id = send_msg(&t, chat_id, &mut instance).await?; - t.send_webxdc_status_update( - instance_id, - r#"{"summary":"real summary", "payload": 42}"#, - ) - .await?; - let instance = Message::load_from_db(&t, instance_id).await?; - let info = instance.get_webxdc_info(&t).await?; - assert_eq!(info.internet_access, false); - } + for chat_id in [self_id, single_id, group_id, broadcast_id] { + for internet_xdc in [true, false] { + let mut instance = create_webxdc_instance( + &t, + "foo.xdc", + if internet_xdc { + include_bytes!("../../test-data/webxdc/request-internet-access.xdc") + } else { + include_bytes!("../../test-data/webxdc/minimal.xdc") + }, + )?; + let instance_id = send_msg(&t, chat_id, &mut instance).await?; + t.send_webxdc_status_update( + instance_id, + r#"{"summary":"real summary", "payload": 42}"#, + ) + .await?; + let instance = Message::load_from_db(&t, instance_id).await?; + let info = instance.get_webxdc_info(&t).await?; + assert_eq!(info.internet_access, false); } }