mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 05:22:14 +03:00
Compare commits
3 Commits
v1.157.2
...
iequidoo/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e6e08593c | ||
|
|
156fb6ffd5 | ||
|
|
4e798ceae1 |
18
src/chat.rs
18
src/chat.rs
@@ -132,6 +132,9 @@ pub(crate) enum CantSendReason {
|
||||
/// Temporary state for 1:1 chats while SecureJoin is in progress, after a timeout sending
|
||||
/// messages (incl. unencrypted if we don't yet know the contact's pubkey) is allowed.
|
||||
SecurejoinWait,
|
||||
|
||||
/// The contact isn't forward verified which is needed by protected 1:1 chats.
|
||||
NoForwardVerification,
|
||||
}
|
||||
|
||||
impl fmt::Display for CantSendReason {
|
||||
@@ -152,6 +155,7 @@ impl fmt::Display for CantSendReason {
|
||||
}
|
||||
Self::NotAMember => write!(f, "not a member of the chat"),
|
||||
Self::SecurejoinWait => write!(f, "awaiting SecureJoin for 1:1 chat"),
|
||||
Self::NoForwardVerification => write!(f, "contact isn't forward verified"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1662,6 +1666,19 @@ impl Chat {
|
||||
> 0
|
||||
{
|
||||
Some(SecurejoinWait)
|
||||
} else if self.typ == Chattype::Single
|
||||
&& self.is_protected()
|
||||
&& match get_chat_contacts(context, self.id).await?.pop() {
|
||||
Some(contact_id) => {
|
||||
!Contact::get_by_id(context, contact_id)
|
||||
.await?
|
||||
.is_forward_verified(context)
|
||||
.await?
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
{
|
||||
Some(NoForwardVerification)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -2769,6 +2786,7 @@ async fn prepare_msg_common(
|
||||
CantSendReason::ProtectionBroken
|
||||
| CantSendReason::ContactRequest
|
||||
| CantSendReason::SecurejoinWait
|
||||
| CantSendReason::NoForwardVerification
|
||||
) && msg.param.get_cmd() == SystemMessage::SecurejoinMessage
|
||||
{
|
||||
// Send out the message, the securejoin message is supposed to repair the verification.
|
||||
|
||||
@@ -542,6 +542,8 @@ impl Peerstate {
|
||||
/// * `old_addr`: Old address of the peerstate in case of an AEAP transition.
|
||||
pub(crate) async fn save_to_db_ex(&self, sql: &Sql, old_addr: Option<&str>) -> Result<()> {
|
||||
let trans_fn = |t: &mut rusqlite::Transaction| {
|
||||
let verified_key_fingerprint =
|
||||
self.verified_key_fingerprint.as_ref().map(|fp| fp.hex());
|
||||
if let Some(old_addr) = old_addr {
|
||||
// We are doing an AEAP transition to the new address and the SQL INSERT below will
|
||||
// save the existing peerstate as belonging to this new address. We now need to
|
||||
@@ -551,11 +553,14 @@ impl Peerstate {
|
||||
// existing peerstate as this would break encryption to it. This is critical for
|
||||
// non-verified groups -- if we can't encrypt to the old address, we can't securely
|
||||
// remove it from the group (to add the new one instead).
|
||||
//
|
||||
// NB: We check that `verified_key_fingerprint` hasn't changed to protect from
|
||||
// possible races.
|
||||
t.execute(
|
||||
"UPDATE acpeerstates \
|
||||
SET verified_key=NULL, verified_key_fingerprint='', verifier='' \
|
||||
WHERE addr=?",
|
||||
(old_addr,),
|
||||
"UPDATE acpeerstates
|
||||
SET verified_key=NULL, verified_key_fingerprint='', verifier=''
|
||||
WHERE addr=? AND verified_key_fingerprint=?",
|
||||
(old_addr, &verified_key_fingerprint),
|
||||
)?;
|
||||
}
|
||||
t.execute(
|
||||
@@ -604,7 +609,7 @@ impl Peerstate {
|
||||
self.public_key_fingerprint.as_ref().map(|fp| fp.hex()),
|
||||
self.gossip_key_fingerprint.as_ref().map(|fp| fp.hex()),
|
||||
self.verified_key.as_ref().map(|k| k.to_bytes()),
|
||||
self.verified_key_fingerprint.as_ref().map(|fp| fp.hex()),
|
||||
&verified_key_fingerprint,
|
||||
self.verifier.as_deref().unwrap_or(""),
|
||||
self.secondary_verified_key.as_ref().map(|k| k.to_bytes()),
|
||||
self.secondary_verified_key_fingerprint
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::chat;
|
||||
use crate::chat::ChatId;
|
||||
use crate::chat::{self, CantSendReason, Chat, ChatId, ProtectionStatus};
|
||||
use crate::contact;
|
||||
use crate::contact::Contact;
|
||||
use crate::contact::ContactId;
|
||||
use crate::message::Message;
|
||||
use crate::peerstate::Peerstate;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::securejoin::get_securejoin_qr;
|
||||
use crate::stock_str;
|
||||
use crate::test_utils::mark_as_verified;
|
||||
use crate::test_utils::TestContext;
|
||||
@@ -394,3 +394,45 @@ async fn test_aeap_replay_attack() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_write_to_alice_after_aeap() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
let alice_grp_id = chat::create_group_chat(alice, ProtectionStatus::Protected, "Group").await?;
|
||||
let qr = get_securejoin_qr(alice, Some(alice_grp_id)).await?;
|
||||
tcm.exec_securejoin_qr(bob, alice, &qr).await;
|
||||
let bob_alice_contact = bob.add_or_lookup_contact(alice).await;
|
||||
assert!(bob_alice_contact.is_verified(bob).await?);
|
||||
let bob_alice_chat = bob.create_chat(alice).await;
|
||||
assert!(bob_alice_chat.is_protected());
|
||||
let bob_unprotected_grp_id = bob
|
||||
.create_group_with_members(ProtectionStatus::Unprotected, "Group", &[alice])
|
||||
.await;
|
||||
|
||||
tcm.change_addr(alice, "alice@someotherdomain.xyz").await;
|
||||
let sent = alice.send_text(alice_grp_id, "Hello!").await;
|
||||
bob.recv_msg(&sent).await;
|
||||
|
||||
assert!(!bob_alice_contact.is_verified(bob).await?);
|
||||
let bob_alice_chat = Chat::load_from_db(bob, bob_alice_chat.id).await?;
|
||||
assert!(bob_alice_chat.is_protected());
|
||||
let mut msg = Message::new_text("hi".to_string());
|
||||
assert!(chat::send_msg(bob, bob_alice_chat.id, &mut msg)
|
||||
.await
|
||||
.is_err());
|
||||
assert!(!bob_alice_chat.can_send(bob).await?);
|
||||
assert_eq!(
|
||||
bob_alice_chat.why_cant_send(bob).await?,
|
||||
Some(CantSendReason::NoForwardVerification)
|
||||
);
|
||||
|
||||
// But encrypted communication is still possible in unprotected groups with old Alice.
|
||||
let sent = bob
|
||||
.send_text(bob_unprotected_grp_id, "Alice, how is your address change?")
|
||||
.await;
|
||||
let msg = Message::load_from_db(bob, sent.sender_msg_id).await?;
|
||||
assert!(msg.get_showpadlock());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user