-
-
Notifications
You must be signed in to change notification settings - Fork 98
PGP contacts #6796
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
PGP contacts #6796
Conversation
7acd920
to
280e5dd
Compare
7e5dab4
to
479879a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just 3 small things I was wondering while skimming over
/// This first creates a contact, but does not import the key, | ||
/// so may create a PGP-contact with a fingerprint | ||
/// but without the key. | ||
pub async fn get_pgp_chat(&self, other: &TestContext) -> Chat { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since PGP chats are the more common type of chat now, I think it would make sense to call this function get_chat()
and the other one get_email_chat()
or get_unencrypted_chat()
or similar. But it's not that important, e.g. if this would mean that a lot of test have to be changed.
ebe2a09
to
cac8d20
Compare
a way to know if a chat is an encrypted chat is missing, currently there is only "isPgpContact" that can be used to mark contacts/1:1 chat but for unencrypted groups/threads of classic email non-pgp contacts there is no API to recognize such chats and also put the "classic email" marker as for classic email contacts |
625f8f4
to
c87c241
Compare
@@ -187,6 +191,7 @@ impl BasicChat { | |||
id: chat_id, | |||
name: chat.name.clone(), | |||
is_protected: chat.is_protected(), | |||
is_encrypted: chat.is_encrypted(context).await?, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds up to 2 additional database calls to loading BasicChat
, though I can't think of a good way to avoid it, so maybe it's fine. Or maybe it should only be added to FullChat
, idk.
) -> Result<(ContactId, Modifier)> { | ||
Self::add_or_lookup_ex(context, name, addr, "", origin).await | ||
} | ||
|
||
/// Lookup a contact and create it if it does not exist yet. | ||
/// The contact is identified by the email-address, a name and an "origin" can be given. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment here should explain how fingerprint & addr parameters relate; the way I understand the code:
/// If `fingerprint` is passed, a PGP-contact with this fingerprint added / looked up.
/// Otherwise, an email-contact with `addr` is added / looked up.
/// A name and an "origin" can be given.
if !fingerprint.is_empty() { | ||
let fingerprint_self = load_self_public_key(context).await?.dc_fingerprint().hex(); | ||
if fingerprint == fingerprint_self { | ||
return Ok((ContactId::SELF, sth_modified)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loading the self fingerprint everytime a contact is looked up, just to check whether it's ContactId::SELF, seems unnecessarily slow to me.
I wonder if we can simplify & speed up things by not using the kepairs
table anymore, and instead storing the private and public key in configs. This would mean that it's not possible to have multiple self keys anymore, but I'm not sure there is anyone who is actually making use of the fact that DC theoretically supports using multiple self keys. It would also mean that we have to store the key as base64, not sure about the consequences there. An alternative would be to store the self fingerprint in a OnceCell, since we don't allow changing the primary key anyway.
But we can open an issue for this, and do it in a follow-up PR.
to_ids: &[ContactId], | ||
past_ids: &[ContactId], | ||
to_ids: &[Option<ContactId>], | ||
past_ids: &[Option<ContactId>], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to put a comment what a None
element means.
fb034c4
to
ea48d78
Compare
918277f
to
9cf065a
Compare
1cff5d4
to
907628e
Compare
…ent addr If a contact did AEAP in the past, then it can happen that there are multiple contacts with the same email address but different keys. In this case, we should make sure that we don't send messages to a different email address all of a sudden, and that there aren't two 1:1 chats with the same contact. Therefore, if the PGP contact has a different email address than the email contact, we keep the email contact in 1:1 chats.
dbade62
to
b7284c0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another day, another set of review comments
@@ -474,7 +397,7 @@ def test_verified_group_vs_delete_server_after(acfactory, tmp_path, lp): | |||
ac2_offl.start_io() | |||
msg_in = ac2_offl._evtracker.wait_next_incoming_message() | |||
assert not msg_in.is_system_message() | |||
assert msg_in.text.startswith("[The message was sent with non-verified encryption") | |||
assert msg_in.text.startswith("[The message was sent by non-verified contact") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a question: IIRC plan is to show the verified icon iff all group members are verified? In this case, we won't need this message anymore. But this can also come in a subsequent PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I would like to get rid of this message, but this is out of scope for this PR which is already large.
let to_ids: Vec<Option<ContactId>>; | ||
let past_ids: Vec<Option<ContactId>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a lot of code to calculate the to and past ids, would be nice to extract a function here:
async fn get_to_and_past_contacts(
context: &Context,
mime_parser: &MimeMessage,
incoming_origin: Origin,
) -> Result<(Vec<Option<ContactId>>, Vec<Option<ContactId>>)> {
let chat_id = if let Some(grpid) = mime_parser.get_chat_group_id() {
...
past_ids = add_or_lookup_contacts_by_address_list(
context,
&mime_parser.past_members,
Origin::Hidden,
)
.await?;
};
Ok((to_ids, past_ids))
}
let (to_ids, past_ids) =
get_to_and_past_contacts(context, &mime_parser, incoming_origin).await?;
/// * `is_partial_download`: the message is partially downloaded. | ||
/// We only know the email address and not the contact fingerprint, | ||
/// but try to assign the message to some PGP-contact. | ||
/// If we get it wrong, the message will be placed into the correct | ||
/// chat after downloading. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about unencrypted emails that are partially downloaded? They should be assigned to the email contact, not to some PGP contact with this email address. Maybe there is some code for this already and I just haven't seen it, otherwise we need a check for whether Content-Type is multipart/encrypted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't know if partially downloaded email is encrypted or not, we don't even prefetch content-type. Messages are assigned to some PGP-contact. After downloading, they will go to email-contact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this is not about prefetch but about partial downloads? And for partial downloads, we fetch the full headers, which includes Content-Type:
const BODY_PARTIAL: &str = "(FLAGS RFC822.SIZE BODY.PEEK[HEADER])";
Co-authored-by: Hocuri <hocuri@gmx.de>
There is a problem with 1:1 chat assignment. If there is no Autocrypt-Gossip on outgoing message, then to_id gets converted to email contact. If there is also a Chat-Verified header, receive_imf fails with an error like |
@@ -2397,27 +2486,32 @@ async fn apply_group_changes( | |||
} | |||
|
|||
if let Some(removed_addr) = mime_parser.get_header(HeaderDef::ChatGroupMemberRemoved) { | |||
removed_id = Contact::lookup_id_by_addr(context, removed_addr, Origin::Unknown).await?; | |||
removed_id = lookup_pgp_contact_by_address(context, removed_addr, chat_id).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider the following case:
- Alice's current key is member of a group. Her past key (on the same email address) is a past member of the group, since she reinstalled DC once without restoring a backup.
- Bob removes Alice from the group.
This code looks like that might not work reliably? Because lookup_pgp_contact_by_address()
also looks up past contacts, so that if Bob is unlucky, then this will try to remove Alice's old key again, which won't work of course, because Alice's old key already is a past member.
Or will old-Alice-key be removed past-members as soon as new-Alice-key is added, and everything is fine?
SyncAction::Block => { | ||
return contact::set_blocked(self, Nosync, contact_id, true).await | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a non-actionable remark: It's a bit weird that this calls contact::set_blocked(.., true)
, and then a few lines below, chat_id.block_ex()
is called, which again calls contact::set_blocked(.., true)
. But how (un)blocking a chat and (un)blocking a contact interacts is just super weird anyways. Not sure if anyone really uses the "block" feature, a spammer can just create a new account anyways, and if you want a chat to be out of your view you can archive&mute it. So, maybe it's fine that this feature is not too "polished".
Doing nothing here, and relying on chat_id.block.ex()
to also block the contact, would be even weirder, so, I'm not proposing to change anything here.
9bd2506
to
1684809
Compare
JSON-RPC:
reset_contact_encryption
API is removed.In deltachat-rpc-client API
Contact.reset_encryption
is removed."Group tracking plugin" in legacy Python API was removed because it relied on parsing email addresses from system messages with regexps.
Known multi-device (non-)issue: pinning a 1:1 chat from legacy client pins email chat via sync message.
Still missing, not all of this should be fixed before merging but at least CI should pass:
Chat-Group-Member-Fpr
header.test_prefer_encrypt_mutual_if_encrypted
has TODOs for assigning to chat by issuer fingerprint. This should also work if the key is attached and no Autocrypt header is sent.Signed-only messages should probably go to PGP-chat without a padlock. Fixing this is out of scope of this PR.Signed-only messages to to email-contact. If Thunderbird is configured to send Autocrypt header, chatting should work, this is already tested.Chat-Group-Member-Fpr
format should be fixed before release.