diff --git a/src/peerstate.rs b/src/peerstate.rs index 00c640e04..c0d4c7e91 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -766,23 +766,65 @@ pub(crate) async fn maybe_do_aeap_transition( // If the from addr is different from the peerstate address we know, // we may want to do an AEAP transition. - if !addr_cmp(&peerstate.addr, &mime_parser.from.addr) - // Check if it's a chat message; we do this to avoid - // some accidental transitions if someone writes from multiple - // addresses with an MUA. - && mime_parser.has_chat_version() - // Check if the message is encrypted and signed correctly. If it's not encrypted, it's - // probably from a new contact sharing the same key. - && !mime_parser.signatures.is_empty() - // Check if the From: address was also in the signed part of the email. - // Without this check, an attacker could replay a message from Alice - // to Bob. Then Bob's device would do an AEAP transition from Alice's - // to the attacker's address, allowing for easier phishing. - && mime_parser.from_is_signed - // DC avoids sending messages with the same timestamp, that's why `>` is here unlike in - // `Peerstate::apply_header()`. - && info.message_time > peerstate.last_seen - { + if !addr_cmp(&peerstate.addr, &mime_parser.from.addr) { + // Check if it's a chat message; we do this to avoid + // some accidental transitions if someone writes from multiple + // addresses with an MUA. + if !mime_parser.has_chat_version() { + info!( + context, + "Not doing AEAP from {} to {} because the message is not a chat message.", + &peerstate.addr, + &mime_parser.from.addr + ); + return Ok(()); + } + + // Check if the message is encrypted and signed correctly. If it's not encrypted, it's + // probably from a new contact sharing the same key. + if mime_parser.signatures.is_empty() { + info!( + context, + "Not doing AEAP from {} to {} because the message is not encrypted and signed.", + &peerstate.addr, + &mime_parser.from.addr + ); + return Ok(()); + } + + // Check if the From: address was also in the signed part of the email. + // Without this check, an attacker could replay a message from Alice + // to Bob. Then Bob's device would do an AEAP transition from Alice's + // to the attacker's address, allowing for easier phishing. + if !mime_parser.from_is_signed { + info!( + context, + "Not doing AEAP from {} to {} because From: is not signed.", + &peerstate.addr, + &mime_parser.from.addr + ); + return Ok(()); + } + + // DC avoids sending messages with the same timestamp, that's why messages + // with equal timestamps are ignored here unlike in `Peerstate::apply_header()`. + if info.message_time <= peerstate.last_seen { + info!( + context, + "Not doing AEAP from {} to {} because {} < {}.", + &peerstate.addr, + &mime_parser.from.addr, + info.message_time, + peerstate.last_seen + ); + return Ok(()); + } + + info!( + context, + "Doing AEAP transition from {} to {}.", &peerstate.addr, &mime_parser.from.addr + ); + let info = &mut mime_parser.decryption_info; let peerstate = info.peerstate.as_mut().context("no peerstate??")?; // Add info messages to chats with this (verified) contact