feat: ignore encryption preferences

Encryption preference is sent in Autocrypt header,
but otherwise ignored.

Delta Chat always prefers encryption if it is available.
This commit is contained in:
link2xt
2025-03-03 22:52:10 +00:00
committed by l
parent 339f695bd6
commit 51bbdadfad
8 changed files with 32 additions and 285 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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<String> {
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())

View File

@@ -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;

View File

@@ -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);

View File

@@ -442,7 +442,6 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<Configure
ctx.set_config(Config::MvboxMove, Some("0")).await?;
ctx.set_config(Config::OnlyFetchMvbox, None).await?;
ctx.set_config(Config::ShowEmails, None).await?;
ctx.set_config(Config::E2eeEnabled, Some("1")).await?;
}
let create_mvbox = !is_chatmail;

View File

@@ -54,22 +54,13 @@ impl EncryptHelper {
peerstates: &[(Option<Peerstate>, String)],
) -> Result<bool> {
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();

View File

@@ -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);
}
}