Skip to content

Conversation

Hocuri
Copy link
Collaborator

@Hocuri Hocuri commented Oct 2, 2025

Follow-up for #7042, part of #6884.

This will make it possible to create invite-QR codes for broadcast channels, and make them symmetrically end-to-end encrypted.

The symmetric encryption

Symmetric encryption uses a shared secret. Currently, we use AES128 for encryption everywhere in Delta Chat, so, this is what I'm using for broadcast channels (though it wouldn't be hard to switch to AES256).

The secret shared between all members of a broadcast channel has 258 bits of entropy (see fn create_broadcast_shared_secret in the code).

Since the shared secrets have more entropy than the AES session keys, it's not necessary to have a hard-to-compute string2key algorithm, so, I'm using the string2key algorithm salted. This is fast enough that Delta Chat can just try out all known shared secrets. 1 In order to prevent DOS attacks, Delta Chat will not attempt to decrypt with a string2key algorithm other than salted 2.

Footnotes

  1. In a symmetrically encrypted message, it's not visible which secret was used to encrypt without trying out all secrets. If this does turn out to be too slow in the future, then we can assign a short, non-unique (~2 characters) id to every shared secret, and send it in cleartext. The receiving Delta Chat will then only try out shared secrets with this id. Of course, this would leak a little bit of metadata in cleartext, so, I would like to avoid it.

  2. A DOS attacker could send a message with a lot of encrypted session keys, all of which use a very hard-to-compute string2key algorithm. Delta Chat would then try to decrypt all of the encrypted session keys with all of the known shared secrets. In order to prevent this, as I said, Delta Chat will not attempt to decrypt with a string2key algorithm other than salted

Comment on lines -4230 to -4234
} else if chat.typ == Chattype::InBroadcast && contact_id == ContactId::SELF {
// For incoming broadcast channels, it's not possible to remove members,
// but it's possible to leave:
let self_addr = context.get_primary_self_addr().await?;
send_member_removal_msg(context, &chat, contact_id, &self_addr).await?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Leaving a broadcast channel now uses the same code as leaving a group, so, we don't need this block anymore.

See this code a few lines above:

    if matches!(
        chat.typ,
        Chattype::Group | Chattype::OutBroadcast | Chattype::InBroadcast
    ) {

Comment on lines -4011 to -4013
if is_contact_in_chat(context, chat_id, contact_id).await? {
return Ok(false);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I removed this block because we're in the else clause of if is_contact_in_chat(context, chat_id, contact_id).await?, so, this was dead code

"invalid contact_id {} for adding to group",
contact_id
);
ensure!(!chat.is_mailing_list(), "Mailing lists can't be changed");
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I removed this line because above we're ckecking ensure!(chat.typ == Chattype::Group || chat.typ == Chattype::OutBroadcast [...]), so, this was dead code

hash_alg: HashAlgorithm::default(),
salt,
};
let mut msg = msg.seipd_v2(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This uses SEIPD v2, while in other places, we use SEIPD v1. I assume that we used SEIPD v1 because v2 didn't exist yet at the time, and SEIPD v2 is superior, so, we should use it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant