From d64498884589dd7eda966150500cce24f58286b2 Mon Sep 17 00:00:00 2001 From: iequidoo Date: Mon, 2 Jan 2023 13:09:56 -0300 Subject: [PATCH] Securejoin: Fix adding and handling Autocrypt-Gossip headers (#3836) - If bcc_self is set, gossip headers must be added despite of the number of group members. - If another device observes Secure-Join, instead of looking for Secure-Join-Fingerprint in "vg-member-added"/"vc-contact-confirm" messages it must use keys from Autocrypt-Gossip headers as described in the Countermitm doc (https://countermitm.readthedocs.io/en/latest/new.html#joining-a-verified-group-secure-join). --- CHANGELOG.md | 4 ++ src/mimefactory.rs | 4 +- src/securejoin.rs | 102 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 89 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bddc660f..91263fafd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,13 @@ ## Unreleased +### Fixes +- Securejoin: Fix adding and handling Autocrypt-Gossip headers #3914 + ### API-Changes - jsonrpc: add verified-by information to `Contact`-Object + ## 1.106.0 ### Changes diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 04c3543ab..b0918d484 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -689,7 +689,9 @@ impl<'a> MimeFactory<'a> { .fold(message, |message, header| message.header(header)); // Add gossip headers in chats with multiple recipients - if peerstates.len() > 1 && self.should_do_gossip(context).await? { + if (peerstates.len() > 1 || context.get_config_bool(Config::BccSelf).await?) + && self.should_do_gossip(context).await? + { for peerstate in peerstates.iter().filter_map(|(state, _)| state.as_ref()) { if peerstate.peek_key(min_verified).is_some() { if let Some(header) = peerstate.render_gossip_header(min_verified) { diff --git a/src/securejoin.rs b/src/securejoin.rs index 420966ceb..92ddb0733 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -579,36 +579,98 @@ pub(crate) async fn observe_securejoin_on_other_device( .await?; return Ok(HandshakeMessage::Ignore); } - let fingerprint: Fingerprint = - match mime_message.get_header(HeaderDef::SecureJoinFingerprint) { - Some(fp) => fp.parse()?, + let addr = Contact::load_from_db(context, contact_id) + .await? + .get_addr() + .to_string(); + if mime_message.gossiped_addr.contains(&addr) { + let mut peerstate = match Peerstate::from_addr(context, &addr).await? { + Some(p) => p, None => { could_not_establish_secure_connection( - context, - contact_id, - info_chat_id(context, contact_id).await?, - "Fingerprint not provided, please update Delta Chat on all your devices.", - ) - .await?; + context, + contact_id, + info_chat_id(context, contact_id).await?, + &format!("No peerstate in db for '{}' at step {}", &addr, step), + ) + .await?; return Ok(HandshakeMessage::Ignore); } }; - if mark_peer_as_verified( - context, - &fingerprint, - Contact::load_from_db(context, contact_id) - .await? - .get_addr() - .to_owned(), - ) - .await - .is_err() + let fingerprint = match peerstate.gossip_key_fingerprint.clone() { + Some(fp) => fp, + None => { + could_not_establish_secure_connection( + context, + contact_id, + info_chat_id(context, contact_id).await?, + &format!( + "No gossip key fingerprint in db for '{}' at step {}", + &addr, step, + ), + ) + .await?; + return Ok(HandshakeMessage::Ignore); + } + }; + if peerstate.set_verified( + PeerstateKeyType::GossipKey, + &fingerprint, + PeerstateVerifiedStatus::BidirectVerified, + addr, + ) { + peerstate.prefer_encrypt = EncryptPreference::Mutual; + peerstate.save_to_db(&context.sql).await.unwrap_or_default(); + } else { + could_not_establish_secure_connection( + context, + contact_id, + info_chat_id(context, contact_id).await?, + &format!( + "Could not mark peer as verified for fingerprint {} at step {}", + fingerprint.hex(), + step, + ), + ) + .await?; + return Ok(HandshakeMessage::Ignore); + } + } else if let Some(fingerprint) = + mime_message.get_header(HeaderDef::SecureJoinFingerprint) { + // FIXME: Old versions of DC send this header instead of gossips. Remove this + // eventually. + let fingerprint = fingerprint.parse()?; + if mark_peer_as_verified( + context, + &fingerprint, + Contact::load_from_db(context, contact_id) + .await? + .get_addr() + .to_owned(), + ) + .await + .is_err() + { + could_not_establish_secure_connection( + context, + contact_id, + info_chat_id(context, contact_id).await?, + format!("Fingerprint mismatch on observing {}.", step).as_ref(), + ) + .await?; + return Ok(HandshakeMessage::Ignore); + } + } else { could_not_establish_secure_connection( context, contact_id, info_chat_id(context, contact_id).await?, - format!("Fingerprint mismatch on observing {}.", step).as_ref(), + &format!( + "No gossip header for '{}' at step {}, please update Delta Chat on all \ + your devices.", + &addr, step, + ), ) .await?; return Ok(HandshakeMessage::Ignore);