MCPcopy Create free account
hub / github.com/chatmail/core / handle_securejoin_handshake

Function handle_securejoin_handshake

src/securejoin.rs:418–739  ·  view source on GitHub ↗
(
    context: &Context,
    mime_message: &mut MimeMessage,
    contact_id: ContactId,
)

Source from the content-addressed store, hash-verified

416/// database; this is done by `receive_imf()` later on as needed.
417#[expect(clippy::arithmetic_side_effects)]
418pub(crate) async fn handle_securejoin_handshake(
419 context: &Context,
420 mime_message: &mut MimeMessage,
421 contact_id: ContactId,
422) -> Result<HandshakeMessage> {
423 if contact_id.is_special() {
424 return Err(Error::msg("Can not be called with special contact ID"));
425 }
426
427 let step = get_secure_join_step(mime_message).context("Not a Secure-Join message")?;
428
429 info!(context, "Received secure-join message {step:?}.");
430
431 // Opportunistically protect against a theoretical 'surreptitious forwarding' attack:
432 // If Eve obtains a QR code from Alice and starts a securejoin with her,
433 // and also lets Bob scan a manipulated QR code,
434 // she could reencrypt the v*-request-with-auth message to Bob while maintaining the signature,
435 // and Bob would regard the message as valid.
436 //
437 // This attack is not actually relevant in any threat model,
438 // because if Eve can see Alice's QR code and have Bob scan a manipulated QR code,
439 // she can just do a classical MitM attack.
440 //
441 // Protecting all messages sent by Delta Chat against 'surreptitious forwarding'
442 // by checking the 'intended recipient fingerprint'
443 // will improve security (completely unrelated to the securejoin protocol)
444 // and is something we want to do in the future:
445 // https://www.rfc-editor.org/rfc/rfc9580.html#name-surreptitious-forwarding
446 if !matches!(
447 step,
448 SecureJoinStep::Request { .. } | SecureJoinStep::RequestPubkey | SecureJoinStep::Pubkey
449 ) {
450 let mut self_found = false;
451 let self_fingerprint = load_self_public_key(context).await?.dc_fingerprint();
452 for key in mime_message.gossiped_keys.values() {
453 if key.public_key.dc_fingerprint() == self_fingerprint {
454 self_found = true;
455 break;
456 }
457 }
458 if !self_found {
459 // This message isn't intended for us. Possibly the peer doesn't own the key which the
460 // message is signed with but forwarded someone's message to us.
461 warn!(context, "Step {step}: No self addr+pubkey gossip found.");
462 return Ok(HandshakeMessage::Ignore);
463 }
464 }
465
466 match step {
467 SecureJoinStep::Request { ref invitenumber } => {
468 /*=======================================================
469 ==== Alice - the inviter side ====
470 ==== Step 3 in "Setup verified contact" protocol ====
471 =======================================================*/
472
473 // this message may be unencrypted (Bob, the joiner and the sender, might not have Alice's key yet)
474 // it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it,
475 // send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here.

Callers 2

receive_imf_innerFunction · 0.85
test_setup_contact_exFunction · 0.85

Calls 15

get_secure_join_stepFunction · 0.85
load_self_public_keyFunction · 0.85
existsFunction · 0.85
send_alice_handshake_msgFunction · 0.85
self_fingerprintFunction · 0.85
insert_tombstoneFunction · 0.85
insert_into_smtpFunction · 0.85
encrypted_and_signedFunction · 0.85
get_chat_id_by_grpidFunction · 0.85

Tested by 1

test_setup_contact_exFunction · 0.68