feat!: stop tracking contact verification

Since V2 a contact is its key, there is no address-to-key binding left to verify.

The JSONRPC and FFI APIs are unchanged and report nothing as verified.

BREAKING CHANGE: the statistics JSON sent to the self-reporting-bot on Android changes: Contacts have `encrypted` instead of `verified` and lose `transitive_chain` properties and message stats have `encrypted` instead of `verified` and `unverified_encrypted`, and securejoin invites lose `already_verified`. The collecting bot stores incoming reports verbatim but analysis will have to make sense of older and newer reports.
This commit is contained in:
holger krekel
2026-09-16 16:09:09 +02:00
parent 373f1840a6
commit d8912a98ac
34 changed files with 203 additions and 1553 deletions
+25 -165
View File
@@ -1,5 +1,3 @@
use std::time::Duration;
use deltachat_contact_tools::EmailAddress;
use regex::Regex;
@@ -8,7 +6,6 @@ use crate::chat::{CantSendReason, ChatId, add_contact_to_chat, remove_contact_fr
use crate::chatlist::Chatlist;
use crate::constants::Chattype;
use crate::key::self_fingerprint;
use crate::mimeparser::{GossipedKey, SystemMessage};
use crate::qr::Qr;
use crate::receive_imf::receive_imf;
use crate::stock_str::{self, messages_e2ee_info_msg};
@@ -16,7 +13,6 @@ use crate::test_utils::{
AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, TestContext, TestContextManager,
TimeShiftFalsePositiveNote, get_chat_msg, sync,
};
use crate::tools::SystemTime;
#[derive(PartialEq)]
enum SetupContactCase {
@@ -193,10 +189,7 @@ async fn test_setup_contact_ext(case: SetupContactCase) -> (TestContext, TestCon
);
if case == SetupContactCase::WrongAliceGossip {
let wrong_pubkey = GossipedKey {
public_key: load_self_public_key(&bob).await.unwrap(),
verified: false,
};
let wrong_pubkey = load_self_public_key(&bob).await.unwrap();
let alice_pubkey = msg
.gossiped_keys
.insert(alice_addr.to_string(), wrong_pubkey)
@@ -206,7 +199,6 @@ async fn test_setup_contact_ext(case: SetupContactCase) -> (TestContext, TestCon
.await
.unwrap();
assert_eq!(handshake_msg, HandshakeMessage::Ignore);
assert_eq!(contact_bob.is_verified(&alice).await.unwrap(), false);
msg.gossiped_keys
.insert(alice_addr.to_string(), alice_pubkey)
@@ -215,20 +207,17 @@ async fn test_setup_contact_ext(case: SetupContactCase) -> (TestContext, TestCon
.await
.unwrap();
assert_eq!(handshake_msg, HandshakeMessage::Ignore);
assert!(contact_bob.is_verified(&alice).await.unwrap());
return (alice, bob);
}
// Alice should not yet have Bob verified
let contact_bob = alice.add_or_lookup_contact_no_key(&bob).await;
let contact_bob_id = contact_bob.id;
assert_eq!(contact_bob.is_key_contact(), true);
assert_eq!(contact_bob.is_verified(&alice).await.unwrap(), false);
assert_eq!(contact_bob.get_authname(), "");
tcm.section("Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm");
alice.recv_msg_trash(&sent).await;
assert_eq!(contact_bob.is_verified(&alice).await.unwrap(), true);
assert_eq!(contact_bob.e2ee_avail(&alice).await.unwrap(), true);
let contact_bob = Contact::get_by_id(&alice, contact_bob_id).await.unwrap();
assert_eq!(contact_bob.get_authname(), "Bob Examplenet");
assert!(contact_bob.get_name().is_empty());
@@ -248,7 +237,7 @@ async fn test_setup_contact_ext(case: SetupContactCase) -> (TestContext, TestCon
1
);
// Check Alice got the verified message in her single chat.
// Check Alice got the message in her single chat.
{
let chat = alice.get_chat(&bob).await;
let msg = get_chat_msg(&alice, chat.get_id(), 0, 1).await;
@@ -280,14 +269,14 @@ async fn test_setup_contact_ext(case: SetupContactCase) -> (TestContext, TestCon
"vc-contact-confirm"
);
// Bob has verified Alice already.
// Bob has Alice's key already.
//
// Alice may not have verified Bob yet.
assert_eq!(contact_alice.is_verified(&bob.ctx).await.unwrap(), true);
// Alice may not have Bob's key yet.
assert_eq!(contact_alice.e2ee_avail(&bob.ctx).await.unwrap(), true);
// Step 7: Bob receives vc-contact-confirm
bob.recv_msg_trash(&sent).await;
assert_eq!(contact_alice.is_verified(&bob.ctx).await.unwrap(), true);
assert_eq!(contact_alice.e2ee_avail(&bob.ctx).await.unwrap(), true);
let contact_alice = Contact::get_by_id(&bob.ctx, contact_alice_id)
.await
.unwrap();
@@ -359,13 +348,11 @@ async fn test_setup_contact_bob_knows_alice() -> Result<()> {
bob_fp.hex()
);
// Alice should not yet have Bob verified
let contact_bob = alice.add_or_lookup_contact_no_key(bob).await;
assert_eq!(contact_bob.is_verified(alice).await?, false);
tcm.section("Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm");
alice.recv_msg_trash(&sent).await;
assert_eq!(contact_bob.is_verified(alice).await?, true);
assert_eq!(contact_bob.e2ee_avail(alice).await?, true);
// Check Alice signalled success via the SecurejoinInviterProgress event.
let event = alice
@@ -399,16 +386,14 @@ async fn test_setup_contact_bob_knows_alice() -> Result<()> {
"vc-contact-confirm"
);
// Bob has verified Alice already.
// Bob has Alice's key already.
let contact_alice = bob.add_or_lookup_contact_no_key(alice).await;
assert_eq!(contact_alice.is_verified(bob).await?, true);
assert_eq!(contact_alice.e2ee_avail(bob).await?, true);
// Alice confirms that Bob is now verified.
//
// This does not change anything for Bob.
tcm.section("Step 7: Bob receives vc-contact-confirm");
bob.recv_msg_trash(&sent).await;
assert_eq!(contact_alice.is_verified(bob).await?, true);
assert_eq!(contact_alice.e2ee_avail(bob).await?, true);
Ok(())
}
@@ -567,13 +552,11 @@ async fn test_secure_join_group_ext(v3: bool, remove_invite: bool) -> Result<()>
bob_fp
);
// Alice should not yet have Bob verified
let contact_bob = alice.add_or_lookup_contact_no_key(&bob).await;
assert_eq!(contact_bob.is_verified(&alice).await?, false);
tcm.section("Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added");
alice.recv_msg_trash(&sent).await;
assert_eq!(contact_bob.is_verified(&alice).await?, true);
assert_eq!(contact_bob.e2ee_avail(&alice).await?, true);
// Check Alice signalled success via the SecurejoinInviterProgress event.
let event = alice
@@ -615,7 +598,7 @@ async fn test_secure_join_group_ext(v3: bool, remove_invite: bool) -> Result<()>
assert!(msg.get_header(HeaderDef::AutocryptGossip).is_some());
{
// Now Alice's chat with Bob should still be hidden, the verified message should
// Now Alice's chat with Bob should still be hidden, the member-added message should
// appear in the group chat.
if v3 {
assert!(
@@ -641,17 +624,17 @@ async fn test_secure_join_group_ext(v3: bool, remove_invite: bool) -> Result<()>
assert_eq!(msg.get_text(), expected_text);
}
// Bob has verified Alice already.
// Bob has Alice's key already.
//
// Alice may not have verified Bob yet.
// Alice may not have Bob's key yet.
let contact_alice = bob.add_or_lookup_contact_no_key(&alice).await;
assert_eq!(contact_alice.is_verified(&bob).await?, true);
assert_eq!(contact_alice.e2ee_avail(&bob).await?, true);
tcm.section("Step 7: Bob receives vg-member-added");
bob.recv_msg(&sent).await;
{
// Bob has Alice verified, message shows up in the group chat.
assert_eq!(contact_alice.is_verified(&bob).await?, true);
// The message shows up in the group chat.
assert_eq!(contact_alice.e2ee_avail(&bob).await?, true);
let chat = bob.get_chat(&alice).await;
assert_eq!(
chat.blocked,
@@ -832,11 +815,7 @@ async fn test_unknown_sender() -> Result<()> {
Ok(())
}
/// Tests that Bob gets Alice as verified
/// if `vc-contact-confirm` is lost.
/// Previously `vc-contact-confirm` was used
/// to confirm backward verification,
/// but backward verification is not tracked anymore.
/// Tests that Bob gets Alice's key if `vc-contact-confirm` is lost.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_lost_contact_confirm() {
let mut tcm = TestContextManager::new();
@@ -858,16 +837,16 @@ async fn test_lost_contact_confirm() {
let sent = bob.pop_sent_msg().await;
alice.recv_msg_trash(&sent).await;
// Alice has Bob verified now.
// Alice has Bob's key now.
let contact_bob = alice.add_or_lookup_contact_no_key(&bob).await;
assert_eq!(contact_bob.is_verified(&alice).await.unwrap(), true);
assert_eq!(contact_bob.e2ee_avail(&alice).await.unwrap(), true);
// Alice sends vc-contact-confirm, but it gets lost.
let _sent_vc_contact_confirm = alice.pop_sent_msg().await;
// Bob has alice as verified too, even though vc-contact-confirm is lost.
// Bob has Alice's key too, even though vc-contact-confirm is lost.
let contact_alice = bob.add_or_lookup_contact_no_key(&alice).await;
assert_eq!(contact_alice.is_verified(&bob).await.unwrap(), true);
assert_eq!(contact_alice.e2ee_avail(&bob).await.unwrap(), true);
}
/// Tests Bob joining two groups by scanning two QR codes
@@ -974,7 +953,7 @@ async fn test_parallel_setup_contact(bob_deletes_fiona_contact: bool) -> Result<
bob.recv_msg_trash(&sent_fiona_vc_contact_confirm).await;
let bob_fiona_contact = Contact::get_by_id(bob, bob_fiona_contact_id).await.unwrap();
assert_eq!(bob_fiona_contact.is_verified(bob).await.unwrap(), true);
assert_eq!(bob_fiona_contact.e2ee_avail(bob).await.unwrap(), true);
}
// Alice gets online and previously started SecureJoin process finishes.
@@ -990,7 +969,7 @@ async fn test_parallel_setup_contact(bob_deletes_fiona_contact: bool) -> Result<
bob.recv_msg_trash(&sent_alice_vc_contact_confirm).await;
let bob_alice_contact_id = bob.add_or_lookup_contact_id(alice).await;
let bob_alice_contact = Contact::get_by_id(bob, bob_alice_contact_id).await.unwrap();
assert_eq!(bob_alice_contact.is_verified(bob).await.unwrap(), true);
assert_eq!(bob_alice_contact.e2ee_avail(bob).await.unwrap(), true);
bob.assert_warn("Message does not match expected fingerprint")
.await;
@@ -1027,8 +1006,6 @@ async fn test_wrong_auth_token() -> Result<()> {
alice.recv_msg_trash(&sent).await;
let alice_bob_contact = alice.add_or_lookup_contact(bob).await;
assert!(!alice_bob_contact.is_verified(alice).await?);
alice.assert_warn("invalid auth code").await;
Ok(())
}
@@ -1100,123 +1077,6 @@ async fn test_send_avatar_in_securejoin() -> Result<()> {
Ok(())
}
/// Tests that scanning a QR code week later
/// allows Bob to establish a contact with Alice,
/// but does not mark Bob as verified for Alice.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_expired_contact_auth_token() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
// Alice creates a QR code.
let qr = get_securejoin_qr(alice, None).await?;
// One week passes, QR code expires.
SystemTime::shift(Duration::from_secs(7 * 24 * 3600));
// Bob scans the QR code.
join_securejoin(bob, &qr).await?;
// vc-request
alice.recv_msg_trash(&bob.pop_sent_msg().await).await;
// vc-auth-requried
bob.recv_msg_trash(&alice.pop_sent_msg().await).await;
// vc-request-with-auth
alice.recv_msg_trash(&bob.pop_sent_msg().await).await;
// Bob should not be verified for Alice.
let contact_bob = alice.add_or_lookup_contact_no_key(bob).await;
assert_eq!(contact_bob.is_verified(alice).await.unwrap(), false);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_expired_group_auth_token() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let alice_chat_id = chat::create_group(alice, "Group").await?;
// Alice creates a group QR code.
let qr = get_securejoin_qr(alice, Some(alice_chat_id)).await.unwrap();
// One week passes, QR code expires.
SystemTime::shift(Duration::from_secs(7 * 24 * 3600));
// Bob scans the QR code.
join_securejoin(bob, &qr).await?;
// vg-request
alice.recv_msg_trash(&bob.pop_sent_msg().await).await;
// vg-auth-requried
bob.recv_msg_trash(&alice.pop_sent_msg().await).await;
// vg-request-with-auth
alice.recv_msg_trash(&bob.pop_sent_msg().await).await;
// vg-member-added
let bob_member_added_msg = bob.recv_msg(&alice.pop_sent_msg().await).await;
assert!(bob_member_added_msg.is_info());
assert_eq!(
bob_member_added_msg.get_info_type(),
SystemMessage::MemberAddedToGroup
);
// Bob should not be verified for Alice.
let contact_bob = alice.add_or_lookup_contact_no_key(bob).await;
assert_eq!(contact_bob.is_verified(alice).await.unwrap(), false);
Ok(())
}
/// Tests that old token is considered expired
/// even if sync message just arrived.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_expired_synced_auth_token() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let alice2 = &tcm.alice().await;
let bob = &tcm.bob().await;
alice.set_config_bool(Config::SyncMsgs, true).await?;
alice2.set_config_bool(Config::SyncMsgs, true).await?;
// Alice creates a QR code on the second device.
let qr = get_securejoin_qr(alice2, None).await?;
alice2.send_sync_msg().await.unwrap();
let sync_msg = alice2.pop_sent_msg().await;
// One week passes, QR code expires.
SystemTime::shift(Duration::from_secs(7 * 24 * 3600));
alice.recv_msg_trash(&sync_msg).await;
// Bob scans the QR code.
join_securejoin(bob, &qr).await?;
// vc-request
alice.recv_msg_trash(&bob.pop_sent_msg().await).await;
// vc-auth-requried
bob.recv_msg_trash(&alice.pop_sent_msg().await).await;
// vc-request-with-auth
alice.recv_msg_trash(&bob.pop_sent_msg().await).await;
// Bob should not be verified for Alice.
let contact_bob = alice.add_or_lookup_contact_no_key(bob).await;
assert_eq!(contact_bob.is_verified(alice).await.unwrap(), false);
Ok(())
}
/// Tests that attempting to join already joined group does nothing.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_rejoin_group() -> Result<()> {