diff --git a/src/chat.rs b/src/chat.rs index 955467125..a0b0f082a 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -5118,10 +5118,8 @@ pub(crate) enum SyncAction { chat_name: String, shared_secret: String, }, - CreateInBroadcast { - chat_name: String, - shared_secret: String, - }, + /// Mark the contact with the given fingerprint as verified by self. + MarkVerified, Rename(String), /// Set chat contacts by their addresses. SetContacts(Vec), @@ -5177,6 +5175,14 @@ impl Context { SyncAction::Unblock => { return contact::set_blocked(self, Nosync, contact_id, false).await; } + SyncAction::MarkVerified => { + return contact::mark_contact_id_as_verified( + self, + contact_id, + ContactId::SELF, + ) + .await; + } _ => (), } ChatIdBlocked::get_for_contact(self, contact_id, Blocked::Request) @@ -5208,8 +5214,9 @@ impl Context { SyncAction::Accept => chat_id.accept_ex(self, Nosync).await, SyncAction::SetVisibility(v) => chat_id.set_visibility_ex(self, Nosync, *v).await, SyncAction::SetMuted(duration) => set_muted_ex(self, Nosync, chat_id, *duration).await, - SyncAction::CreateOutBroadcast { .. } | SyncAction::CreateInBroadcast { .. } => { - // Create action should have been handled by handle_sync_create_chat() already + SyncAction::CreateOutBroadcast { .. } | SyncAction::MarkVerified => { + // Create action should have been handled by handle_sync_create_chat() already. + // MarkVerified action should have been handled by mark_contact_id_as_verified() already. Err(anyhow!("sync_alter_chat({id:?}, {action:?}): Bad request.")) } SyncAction::Rename(to) => rename_ex(self, Nosync, chat_id, to).await, @@ -5222,7 +5229,7 @@ impl Context { } async fn handle_sync_create_chat(&self, action: &SyncAction, grpid: &str) -> Result { - Ok(match action { + match action { SyncAction::CreateOutBroadcast { chat_name, shared_secret, @@ -5235,29 +5242,10 @@ impl Context { shared_secret.to_string(), ) .await?; - return Ok(true); + Ok(true) } - SyncAction::CreateInBroadcast { - chat_name, - shared_secret, - } => { - let chat_id = ChatId::create_multiuser_record( - self, - Chattype::InBroadcast, - grpid, - chat_name, - Blocked::Not, - ProtectionStatus::Unprotected, - None, - smeared_time(self), - ) - .await?; - save_broadcast_shared_secret(self, chat_id, shared_secret).await?; - - return Ok(true); - } - _ => false, - }) + _ => Ok(false), + } } /// Emits the appropriate `MsgsChanged` event. Should be called if the number of unnoticed diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index ff5399add..f82d55d4f 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -3133,8 +3133,19 @@ async fn test_leave_broadcast_multidevice() -> Result<()> { alice.recv_msg_trash(&request).await; let answer = alice.pop_sent_msg().await; bob0.recv_msg(&answer).await; + + // Sync Bob's verification of Alice: + sync(bob0, bob1).await; + // TODO uncommenting the next line creates a message "Can't decrypt outgoing messages, probably you're using DC on multiple devices without transferring your key" + // bob1.recv_msg(&request).await; bob1.recv_msg(&answer).await; + // The 1:1 chat should not be visible to the user on any of the devices. + // The contact should be marked as verified. + check_direct_chat_is_hidden_and_contact_is_verified(alice, bob0).await; + check_direct_chat_is_hidden_and_contact_is_verified(bob0, alice).await; + check_direct_chat_is_hidden_and_contact_is_verified(bob1, alice).await; + tcm.section("Alice sends first message to broadcast."); let sent_msg = alice.send_text(alice_chat_id, "Hello!").await; let bob0_hello = bob0.recv_msg(&sent_msg).await; @@ -3165,6 +3176,20 @@ async fn test_leave_broadcast_multidevice() -> Result<()> { Ok(()) } +async fn check_direct_chat_is_hidden_and_contact_is_verified( + t: &TestContext, + contact: &TestContext, +) { + let contact = t.add_or_lookup_contact_no_key(contact).await; + if let Some(direct_chat) = ChatIdBlocked::lookup_by_contact(t, contact.id) + .await + .unwrap() + { + assert_eq!(direct_chat.blocked, Blocked::Yes); + } + assert!(contact.is_verified(t).await.unwrap()); +} + /// Test that only the owner of the broadcast channel /// can send messages into the chat. /// diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 4c34b9ffd..1927de27b 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -3521,6 +3521,7 @@ async fn apply_out_broadcast_changes( } else if let Some(added_addr) = mime_parser.get_header(HeaderDef::ChatGroupMemberAdded) { // TODO this block can be removed, // now that all of Alice's devices get to know about Bob joining via Bob's QR message. + // TODO test if this creates some problems with duplicate member-added messages on Alice's device let contact = lookup_key_contact_by_address(context, added_addr, None).await?; if let Some(contact) = contact { better_msg.get_or_insert( diff --git a/src/securejoin.rs b/src/securejoin.rs index b96a40fae..fabfb0c6d 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -595,8 +595,6 @@ pub(crate) async fn observe_securejoin_on_other_device( inviter_progress(context, contact_id, 1000); } - // TODO not sure if I should add vb-request-with-auth here - // Actually, I'm not even sure why vg-request-with-auth is here - why do we create a 1:1 chat?? if step == "vg-request-with-auth" || step == "vc-request-with-auth" { // This actually reflects what happens on the first device (which does the secure // join) and causes a subsequent "vg-member-added" message to create an unblocked diff --git a/src/securejoin/bob.rs b/src/securejoin/bob.rs index 293dc4a6f..0106ba078 100644 --- a/src/securejoin/bob.rs +++ b/src/securejoin/bob.rs @@ -10,7 +10,7 @@ use crate::contact::Origin; use crate::context::Context; use crate::events::EventType; use crate::key::self_fingerprint; -use crate::log::info; +use crate::log::{LogExt as _, info}; use crate::message::{Message, Viewtype}; use crate::mimeparser::{MimeMessage, SystemMessage}; use crate::param::Param; @@ -81,6 +81,13 @@ pub(super) async fn start_protocol(context: &Context, invite: QrInvite) -> Resul contact_id: invite.contact_id(), progress: JoinerProgress::RequestWithAuthSent.to_usize(), }); + + // Our second device won't be able to decrypt the outgoing message + // because it will be symmetrically encrypted with the AUTH token. + // So, we need to send a sync message: + let id = chat::SyncId::ContactFingerprint(invite.fingerprint().hex()); + let action = chat::SyncAction::MarkVerified; + chat::sync(context, id, action).await.log_err(context).ok(); } else { // Start the version 1 protocol and initialise the state. let has_key = context