mirror of
https://github.com/chatmail/core.git
synced 2026-04-21 07:26:29 +03:00
feat: Make one-to-one chats read-only the first seconds of a SecureJoin (#5512)
This protects Bob (the joiner) of sending unexpected-unencrypted messages during an otherwise nicely running SecureJoin. If things get stuck, however, we do not want to block communication -- the chat is just opportunistic as usual, but that needs to be communicated: 1. If Bob's chat with Alice is `Unprotected` and a SecureJoin is started, then add info-message "Establishing guaranteed end-to-end encryption, please wait..." and let `Chat::can_send()` return `false`. 2. Once the info-message "Messages are guaranteed to be e2ee from now on" is added, let `Chat::can_send()` return `true`. 3. If after SECUREJOIN_WAIT_TIMEOUT seconds `2.` did not happen, add another info-message "Could not yet establish guaranteed end-to-end encryption but you may already send a message" and also let `Chat::can_send()` return `true`. Both `2.` and `3.` require the event `ChatModified` being sent out so that UI pick up the change wrt `Chat::can_send()` (this is the same way how groups become updated wrt `can_send()` changes). SECUREJOIN_WAIT_TIMEOUT should be 10-20 seconds so that we are reasonably sure that the app remains active and receiving also on mobile devices. If the app is killed during this time then we may need to do step 3 for any pending Bob-join chats (right now, Bob can only join one chat at a time).
This commit is contained in:
@@ -9,11 +9,11 @@ use super::bobstate::{BobHandshakeStage, BobState};
|
||||
use super::qrinvite::QrInvite;
|
||||
use super::HandshakeMessage;
|
||||
use crate::chat::{is_contact_in_chat, ChatId, ProtectionStatus};
|
||||
use crate::constants::{Blocked, Chattype};
|
||||
use crate::constants::{self, Blocked, Chattype};
|
||||
use crate::contact::Contact;
|
||||
use crate::context::Context;
|
||||
use crate::events::EventType;
|
||||
use crate::mimeparser::MimeMessage;
|
||||
use crate::mimeparser::{MimeMessage, SystemMessage};
|
||||
use crate::sync::Sync::*;
|
||||
use crate::tools::{create_smeared_timestamp, time};
|
||||
use crate::{chat, stock_str};
|
||||
@@ -69,7 +69,29 @@ pub(super) async fn start_protocol(context: &Context, invite: QrInvite) -> Resul
|
||||
QrInvite::Contact { .. } => {
|
||||
// For setup-contact the BobState already ensured the 1:1 chat exists because it
|
||||
// uses it to send the handshake messages.
|
||||
Ok(state.alice_chat())
|
||||
let chat_id = state.alice_chat();
|
||||
// Calculate the sort timestamp before checking the chat protection status so that if we
|
||||
// race with its change, we don't add our message below the protection message.
|
||||
let sort_to_bottom = true;
|
||||
let ts_sort = chat_id
|
||||
.calc_sort_timestamp(context, 0, sort_to_bottom, false)
|
||||
.await?;
|
||||
if chat_id.is_protected(context).await? == ProtectionStatus::Unprotected {
|
||||
let ts_start = time();
|
||||
chat::add_info_msg_with_cmd(
|
||||
context,
|
||||
chat_id,
|
||||
&stock_str::securejoin_wait(context).await,
|
||||
SystemMessage::SecurejoinWait,
|
||||
ts_sort,
|
||||
Some(ts_start),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
chat_id.spawn_securejoin_wait(context, constants::SECUREJOIN_WAIT_TIMEOUT);
|
||||
}
|
||||
Ok(chat_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,6 +341,15 @@ impl BobState {
|
||||
async fn send_handshake_message(&self, context: &Context, step: BobHandshakeMsg) -> Result<()> {
|
||||
send_handshake_message(context, &self.invite, self.chat_id, step).await
|
||||
}
|
||||
|
||||
/// Returns whether we are waiting for a SecureJoin message from Alice, i.e. the protocol hasn't
|
||||
/// yet completed.
|
||||
pub(crate) fn in_progress(&self) -> bool {
|
||||
!matches!(
|
||||
self.next,
|
||||
SecureJoinStep::Terminated | SecureJoinStep::Completed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends the requested handshake message to Alice.
|
||||
|
||||
Reference in New Issue
Block a user