Skip to content

Conversation

link2xt
Copy link
Collaborator

@link2xt link2xt commented Aug 16, 2025

Since key-contacts cannot change their keys and having "verification" is less important for contact identification than having a chat history or shared chats with a contact, UIs have stopped displaying green checkmarks everywhere (deltachat/deltachat-android#3828).

This PR removes protected chats and replaces old mechanism of propagating verifications via protected chat messages marked with Chat-Verified: 1 header with a new _verified attribute of Autocrypt-Gossip headers.

Receiving _verified attribute in Autocrypt-Gossip is already supported in 2.13.0 release. This PR switches from using Chat-Verified header to Autocrypt-Gossip.

Closes #7080 (replace verification gossip mechanism with a new one that works using Autocrypt-Gossip header and is independent of "protected chats").

It also makes authentication tokens in QR codes expire and closes #7111
As a preparation for expiring QR codes it takes into account sync item timestamps (closes #7182) for QR code tokens, so synced QR code tokens have a timestamp of sync messages and when token removal is synchronized tokens newer than the sync item are not removed.

Also closes #7112 (removing deprecated and unneeded APIs)

Finally, this PR adds a migration that resets all indirect verifications. There was a bug #7107 (closed in #7113) that resulted in updating "verified by" information for already verified contacts, so the information about "verifiers" is lost and overwritten with incorrect indirect verifiers.
Expiring auth tokens also make new direct verifications more meaningful, but I have decided not to reset direct verifications as they are not affected by bugs that accidentally result in verifying someone directly. If the user did not publicly share the invite link, then verifications are valuable and resetting all of them will likely not be received well.

As we reset indirect verifications at the same time as we change the mechanism used to propagate verifications, old incorrect verifications will not propagate to new versions even though users do not upgrade at the same time.

We can also allow to remove verification from contacts (https://support.delta.chat/t/how-would-i-remove-the-verified-checkmarks-from-one-invite-code-in-retrospect/3403) as it will not break "protected" chats anymore, but this is out of scope for this PR.

What this PR does:

  • Remove is_protection_broken APIs. (moved to Various protected group changes #7146)
  • Remove is_profile_verified API. (moved to Various protected group changes #7146)
  • Stop indirectly verifying contacts via messages with a Chat-Verified: 1 header.
  • Add new _verified attribute for Autocrypt-Gossip and use it to indirectly verify key-contacts when such attribute is received from a verified contact (whether directly verified or not).
  • Make sure that direct verifications cannot happen via "gossip" from self via outgoing messages sent by other devices. This is another reason to reset existing verifications, apparently existing "direct" verifications may be not actually direct but gossiped by own devices.
  • Remove error paths from add_parts that result in "The message was sent by non-verified contact" messages. We likely don't need to pass verified_encryption around, it is only used to decide whether we want to accept verification gossip from the contact.
  • Remove Chat.is_protected() APIs.
  • Remove API to create new protected groups
  • Update dc_contact_is_verified documentation.
  • Expiring auth tokens
  • Fix Take sync message timestamp into account for QR tokens #7182 so just synced tokens are expired if sync message is old.
  • Reset existing verifications via migration. We should do this at the same time as we switch to the new gossiping mechanism, so it should be part of this PR.

@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch from c7e6f1c to e180d7f Compare August 16, 2025 02:32
@@ -34,88 +34,112 @@ Content-Transfer-Encoding: 7bit
-----BEGIN PGP MESSAGE-----

wU4D5tq63hTeebASAQdATHbs7R5uRADpjsyAvrozHqQ/9nSrspwbLN6XJKuR3xcg
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Payload here is replaced to have no Chat-Verified header inside and have _verified=1 in gossip headers. Payload got larger likely because rsop does not do compression and I had to reencrypt the payload.

@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch 4 times, most recently from 867c2ba to 780ca50 Compare August 17, 2025 02:31
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch 3 times, most recently from 53d6a2d to a6301b9 Compare August 17, 2025 06:22
@link2xt link2xt changed the title feat: verify contacts via Autocrypt-Gossip Verification v2 Aug 17, 2025
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch 11 times, most recently from 88700f4 to 5b31b5d Compare August 19, 2025 21:16
@link2xt link2xt changed the title Verification v2 Remove protected chats Aug 20, 2025
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch from 6b67844 to 6052d1a Compare August 20, 2025 03:33
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch from 6052d1a to 4e956bd Compare August 28, 2025 06:47
@link2xt link2xt marked this pull request as ready for review August 28, 2025 08:37
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch from f1389dd to 701dafe Compare August 28, 2025 08:48
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch from f035394 to 9225ad2 Compare September 9, 2025 01:45
@link2xt link2xt changed the title Remove protected chats Remove protected chats and expire auth tokens Sep 9, 2025
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch from 025d9f1 to 2a22afb Compare September 9, 2025 02:36
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch 10 times, most recently from 1e435ca to 66bffdb Compare September 9, 2025 08:20
@iequidoo iequidoo self-requested a review September 10, 2025 06:21
let sender_is_verified = sender_contact
.fingerprint()
.is_some_and(|fp| fp == fingerprint);
if !sender_is_verified {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not verified (i.e. marked so) yet, maybe rename this to sender_is_checked?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contacts are marked as verified in the profile, only protected/verified chats are removed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, sender_is_verified may be true, but then time() < timestamp + 600 below fails and the sender won't be actually verified. The variable name may be confusing

@@ -258,8 +258,8 @@ impl Context {
for item in &items.items {
match &item.data {
SyncDataOrUnknown::SyncData(data) => match data {
AddQrToken(token) => self.add_qr_token(token).await,
DeleteQrToken(token) => self.delete_qr_token(token).await,
AddQrToken(token) => self.add_qr_token(token, item.timestamp).await,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a remote timestamp, so better to limit it like MimeMessage::get_timestamp_sent() does, maybe even w/o any tolerance

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably makes sense to limit sync item timestamps at the time of parsing sync items rather than everywhere they are used.

let chat = Chat::load_from_db(context, chat_id).await?;
if chat.is_encrypted(context).await?
&& !chat.param.exists(Param::Devicetalk)
&& !chat.param.exists(Param::Selftalk)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, but probably https://delta.chat/en/help#save should say that saved messages are encrypted (if they are sent of course, e.g. in a multi-device case)

This mechanism replaces `Chat-Verified` header.
New parameter `_verified=1` in `Autocrypt-Gossip`
header marks that the sender has the gossiped key
verified.

Using `_verified=1` instead of `_verified`
because it is less likely to cause troubles
with existing Autocrypt header parsers.
This is also how https://www.rfc-editor.org/rfc/rfc2045
defines parameter syntax.
@link2xt link2xt force-pushed the link2xt/ykltkokxntvk branch from 66bffdb to 0f94529 Compare September 12, 2025 15:10
Create unprotected group in test_create_protected_grp_multidev
The test is renamed accordingly.

SystemMessage::ChatE2ee is added in encrypted groups
regardless of whether they are protected or not.
Previously new encrypted unprotected groups
had no message saying that messages are end-to-end encrypted
at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants