fix: Don't send selfavatar in SecureJoin messages before contact verification (#5354)

Don't attach selfavatar in "v{c,g}-request" and "v{c,g}-auth-required" messages:
- These messages are deleted right after processing, so other devices won't see the avatar.
- It's also good for privacy because the contact isn't yet verified and these messages are auto-sent
  unlike usual unencrypted messages.
This commit is contained in:
iequidoo
2024-03-20 03:59:14 -03:00
committed by iequidoo
parent 0155d93622
commit 304e902fce
3 changed files with 44 additions and 24 deletions

View File

@@ -547,13 +547,14 @@ def test_see_new_verified_member_after_going_online(acfactory, tmp_path, lp):
assert msg_in.get_sender_contact().addr == ac2_addr assert msg_in.get_sender_contact().addr == ac2_addr
def test_use_new_verified_group_after_going_online(acfactory, tmp_path, lp): def test_use_new_verified_group_after_going_online(acfactory, data, tmp_path, lp):
"""Another test for the bug #3836: """Another test for the bug #3836:
- Bob has two devices, the second is offline. - Bob has two devices, the second is offline.
- Alice creates a verified group and sends a QR invitation to Bob. - Alice creates a verified group and sends a QR invitation to Bob.
- Bob joins the group. - Bob joins the group.
- Bob's second devices goes online, but sees a contact request instead of the verified group. - Bob's second devices goes online, but sees a contact request instead of the verified group.
- The "member added" message is not a system message but a plain text message. - The "member added" message is not a system message but a plain text message.
- Bob's second device doesn't display the Alice's avatar (bug #5354).
- Sending a message fails as the key is missing -- message info says "proper enc-key for <Alice> - Sending a message fails as the key is missing -- message info says "proper enc-key for <Alice>
missing, cannot encrypt". missing, cannot encrypt".
""" """
@@ -568,6 +569,10 @@ def test_use_new_verified_group_after_going_online(acfactory, tmp_path, lp):
ac2_offl.import_self_keys(str(dir)) ac2_offl.import_self_keys(str(dir))
ac2_offl.stop_io() ac2_offl.stop_io()
lp.sec("ac1: set avatar")
avatar_path = data.get_path("d.png")
ac1.set_avatar(avatar_path)
lp.sec("ac1: create verified-group QR, ac2 scans and joins") lp.sec("ac1: create verified-group QR, ac2 scans and joins")
chat = ac1.create_group_chat("hello", verified=True) chat = ac1.create_group_chat("hello", verified=True)
assert chat.is_protected() assert chat.is_protected()
@@ -580,11 +585,13 @@ def test_use_new_verified_group_after_going_online(acfactory, tmp_path, lp):
ac2_offl.start_io() ac2_offl.start_io()
# Receive "Member Me (<addr>) added by <addr>." message. # Receive "Member Me (<addr>) added by <addr>." message.
msg_in = ac2_offl._evtracker.wait_next_incoming_message() msg_in = ac2_offl._evtracker.wait_next_incoming_message()
contact = msg_in.get_sender_contact()
assert msg_in.is_system_message() assert msg_in.is_system_message()
assert msg_in.get_sender_contact().addr == ac1.get_config("addr") assert contact.addr == ac1.get_config("addr")
chat2 = msg_in.chat chat2 = msg_in.chat
assert chat2.is_protected() assert chat2.is_protected()
assert chat2.get_messages()[0].text == "Messages are guaranteed to be end-to-end encrypted from now on." assert chat2.get_messages()[0].text == "Messages are guaranteed to be end-to-end encrypted from now on."
assert open(contact.get_profile_image(), "rb").read() == open(avatar_path, "rb").read()
lp.sec("ac2_offl: sending message") lp.sec("ac2_offl: sending message")
msg_out = chat2.send_text("hello") msg_out = chat2.send_text("hello")

View File

@@ -2742,17 +2742,8 @@ async fn prepare_send_msg(
/// The caller has to interrupt SMTP loop or otherwise process new rows. /// The caller has to interrupt SMTP loop or otherwise process new rows.
pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -> Result<Vec<i64>> { pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -> Result<Vec<i64>> {
let needs_encryption = msg.param.get_bool(Param::GuaranteeE2ee).unwrap_or_default(); let needs_encryption = msg.param.get_bool(Param::GuaranteeE2ee).unwrap_or_default();
let mimefactory = MimeFactory::from_msg(context, msg).await?;
let attach_selfavatar = match shall_attach_selfavatar(context, msg.chat_id).await { let attach_selfavatar = mimefactory.attach_selfavatar;
Ok(attach_selfavatar) => attach_selfavatar,
Err(err) => {
warn!(context, "SMTP job cannot get selfavatar-state: {err:#}.");
false
}
};
let mimefactory = MimeFactory::from_msg(context, msg, attach_selfavatar).await?;
let mut recipients = mimefactory.recipients(); let mut recipients = mimefactory.recipients();
let from = context.get_primary_self_addr().await?; let from = context.get_primary_self_addr().await?;

View File

@@ -10,7 +10,7 @@ use lettre_email::{Address, Header, MimeMultipartType, PartBuilder};
use tokio::fs; use tokio::fs;
use crate::blob::BlobObject; use crate::blob::BlobObject;
use crate::chat::Chat; use crate::chat::{self, Chat};
use crate::config::Config; use crate::config::Config;
use crate::constants::{Chattype, DC_FROM_HANDSHAKE}; use crate::constants::{Chattype, DC_FROM_HANDSHAKE};
use crate::contact::Contact; use crate::contact::Contact;
@@ -83,7 +83,7 @@ pub struct MimeFactory<'a> {
sync_ids_to_delete: Option<String>, sync_ids_to_delete: Option<String>,
/// True if the avatar should be attached. /// True if the avatar should be attached.
attach_selfavatar: bool, pub attach_selfavatar: bool,
} }
/// Result of rendering a message, ready to be submitted to a send job. /// Result of rendering a message, ready to be submitted to a send job.
@@ -138,11 +138,7 @@ struct MessageHeaders {
} }
impl<'a> MimeFactory<'a> { impl<'a> MimeFactory<'a> {
pub async fn from_msg( pub async fn from_msg(context: &Context, msg: &'a Message) -> Result<MimeFactory<'a>> {
context: &Context,
msg: &'a Message,
attach_selfavatar: bool,
) -> Result<MimeFactory<'a>> {
let chat = Chat::load_from_db(context, msg.chat_id).await?; let chat = Chat::load_from_db(context, msg.chat_id).await?;
let from_addr = context.get_primary_self_addr().await?; let from_addr = context.get_primary_self_addr().await?;
@@ -217,6 +213,7 @@ impl<'a> MimeFactory<'a> {
}, },
) )
.await?; .await?;
let attach_selfavatar = Self::should_attach_selfavatar(context, msg).await;
let factory = MimeFactory { let factory = MimeFactory {
from_addr, from_addr,
@@ -392,6 +389,31 @@ impl<'a> MimeFactory<'a> {
} }
} }
async fn should_attach_selfavatar(context: &Context, msg: &Message) -> bool {
let cmd = msg.param.get_cmd();
(cmd != SystemMessage::SecurejoinMessage || {
let step = msg.param.get(Param::Arg).unwrap_or_default();
// Don't attach selfavatar at the earlier SecureJoin steps:
// - The corresponding messages, i.e. "v{c,g}-request" and "v{c,g}-auth-required" are
// deleted right after processing, so other devices won't see the avatar.
// - It's also good for privacy because the contact isn't yet verified and these
// messages are auto-sent unlike usual unencrypted messages.
step == "vg-request-with-auth"
|| step == "vc-request-with-auth"
|| step == "vg-member-added"
|| step == "vc-contact-confirm"
}) && match chat::shall_attach_selfavatar(context, msg.chat_id).await {
Ok(should) => should,
Err(err) => {
warn!(
context,
"should_attach_selfavatar: cannot get selfavatar state: {err:#}."
);
false
}
}
}
fn grpimage(&self) -> Option<String> { fn grpimage(&self) -> Option<String> {
match &self.loaded { match &self.loaded {
Loaded::Message { chat } => { Loaded::Message { chat } => {
@@ -1836,7 +1858,7 @@ mod tests {
Original-Message-ID: <2893@example.com>\n\ Original-Message-ID: <2893@example.com>\n\
Disposition: manual-action/MDN-sent-automatically; displayed\n\ Disposition: manual-action/MDN-sent-automatically; displayed\n\
\n", &t).await; \n", &t).await;
let mf = MimeFactory::from_msg(&t, &new_msg, false).await.unwrap(); let mf = MimeFactory::from_msg(&t, &new_msg).await.unwrap();
// The subject string should not be "Re: message opened" // The subject string should not be "Re: message opened"
assert_eq!("Re: Hello, Bob", mf.subject_str(&t).await.unwrap()); assert_eq!("Re: Hello, Bob", mf.subject_str(&t).await.unwrap());
} }
@@ -1971,7 +1993,7 @@ mod tests {
new_msg.chat_id = chat_id; new_msg.chat_id = chat_id;
chat::prepare_msg(&t, chat_id, &mut new_msg).await.unwrap(); chat::prepare_msg(&t, chat_id, &mut new_msg).await.unwrap();
let mf = MimeFactory::from_msg(&t, &new_msg, false).await.unwrap(); let mf = MimeFactory::from_msg(&t, &new_msg).await.unwrap();
mf.subject_str(&t).await.unwrap() mf.subject_str(&t).await.unwrap()
} }
@@ -2056,7 +2078,7 @@ mod tests {
new_msg.set_quote(&t, Some(&incoming_msg)).await.unwrap(); new_msg.set_quote(&t, Some(&incoming_msg)).await.unwrap();
} }
let mf = MimeFactory::from_msg(&t, &new_msg, false).await.unwrap(); let mf = MimeFactory::from_msg(&t, &new_msg).await.unwrap();
mf.subject_str(&t).await.unwrap() mf.subject_str(&t).await.unwrap()
} }
@@ -2104,7 +2126,7 @@ mod tests {
) )
.await; .await;
let mimefactory = MimeFactory::from_msg(&t, &msg, false).await.unwrap(); let mimefactory = MimeFactory::from_msg(&t, &msg).await.unwrap();
let recipients = mimefactory.recipients(); let recipients = mimefactory.recipients();
assert_eq!(recipients, vec!["charlie@example.com"]); assert_eq!(recipients, vec!["charlie@example.com"]);