diff --git a/src/e2ee.rs b/src/e2ee.rs index 8d19b44b0..3b312fa23 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -131,6 +131,54 @@ impl EncryptHelper { } } +/// Applies Autocrypt header to Autocrypt peer state. +/// +/// Returns updated peer state. +pub(crate) async fn get_autocrypt_peerstate( + context: &Context, + mail: &ParsedMail<'_>, + message_time: i64, +) -> Result> { + let from = mail + .headers + .get_header(HeaderDef::From_) + .and_then(|from_addr| mailparse::addrparse_header(from_addr).ok()) + .and_then(|from| from.extract_single_info()) + .map(|from| from.addr) + .unwrap_or_default(); + + let aheader = match Aheader::from_headers(&from, &mail.headers) { + Ok(header) => header, + Err(err) => { + warn!(context, "Failed to parse Autocrypt header: {}", err); + None + } + }; + + let peerstate = Peerstate::from_addr(context, &from).await?; + let peerstate = if let Some(aheader) = aheader { + if let Some(mut peerstate) = peerstate { + peerstate.apply_header(&aheader, message_time); + peerstate.save_to_db(&context.sql, false).await?; + Some(peerstate) + } else { + let p = Peerstate::from_header(&aheader, message_time); + p.save_to_db(&context.sql, true).await?; + Some(p) + } + } else { + peerstate + }; + + if let Some(peerstate) = peerstate.as_ref() { + peerstate + .handle_fingerprint_change(context, message_time) + .await?; + } + + Ok(peerstate) +} + /// Tries to decrypt a message, but only if it is structured as an /// Autocrypt message. /// @@ -142,65 +190,15 @@ impl EncryptHelper { pub async fn try_decrypt( context: &Context, mail: &ParsedMail<'_>, - message_time: i64, + peerstate: &Option, ) -> Result<(Option>, HashSet)> { - let from = mail - .headers - .get_header(HeaderDef::From_) - .and_then(|from_addr| mailparse::addrparse_header(from_addr).ok()) - .and_then(|from| from.extract_single_info()) - .map(|from| from.addr) - .unwrap_or_default(); - - let mut peerstate = Peerstate::from_addr(context, &from).await?; - - // Apply Autocrypt header - match Aheader::from_headers(&from, &mail.headers) { - Ok(Some(ref header)) => { - if let Some(ref mut peerstate) = peerstate { - peerstate.apply_header(header, message_time); - peerstate.save_to_db(&context.sql, false).await?; - } else { - let p = Peerstate::from_header(header, message_time); - p.save_to_db(&context.sql, true).await?; - peerstate = Some(p); - } - } - Ok(None) => {} - Err(err) => warn!(context, "Failed to parse Autocrypt header: {}", err), - } - // Possibly perform decryption - let mut public_keyring_for_validate: Keyring = Keyring::new(); - - if let Some(ref mut peerstate) = peerstate { - peerstate - .handle_fingerprint_change(context, message_time) - .await?; - if let Some(key) = &peerstate.public_key { - public_keyring_for_validate.add(key.clone()); - } else if let Some(key) = &peerstate.gossip_key { - public_keyring_for_validate.add(key.clone()); - } - } - + let public_keyring_for_validate = keyring_from_peerstate(&peerstate); let (out_mail, signatures) = match decrypt_if_autocrypt_message(context, mail, public_keyring_for_validate).await? { Some((out_mail, signatures)) => (Some(out_mail), signatures), None => (None, Default::default()), }; - - if let Some(mut peerstate) = peerstate { - // If message is not encrypted and it is not a read receipt, degrade encryption. - if out_mail.is_none() - && message_time > peerstate.last_seen_autocrypt - && !contains_report(mail) - { - peerstate.degrade_encryption(message_time); - peerstate.save_to_db(&context.sql, false).await?; - } - } - Ok((out_mail, signatures)) } @@ -283,6 +281,18 @@ fn get_attachment_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMai } } +fn keyring_from_peerstate(peerstate: &Option) -> Keyring { + let mut public_keyring_for_validate: Keyring = Keyring::new(); + if let Some(peerstate) = peerstate.as_ref() { + if let Some(key) = &peerstate.public_key { + public_keyring_for_validate.add(key.clone()); + } else if let Some(key) = &peerstate.gossip_key { + public_keyring_for_validate.add(key.clone()); + } + } + public_keyring_for_validate +} + async fn decrypt_if_autocrypt_message( context: &Context, mail: &ParsedMail<'_>, @@ -380,18 +390,6 @@ fn has_decrypted_pgp_armor(input: &[u8]) -> bool { false } -/// Checks if a MIME structure contains a multipart/report part. -/// -/// As reports are often unencrypted, we do not reset the Autocrypt header in -/// this case. -/// -/// However, Delta Chat itself has no problem with encrypted multipart/report -/// parts and MUAs should be encouraged to encrpyt multipart/reports as well so -/// that we could use the normal Autocrypt processing. -fn contains_report(mail: &ParsedMail<'_>) -> bool { - mail.ctype.mimetype == "multipart/report" -} - /// Ensures a private key exists for the configured user. /// /// Normally the private key is generated when the first message is diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 74bd1d04f..b0e45d1c3 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -214,8 +214,9 @@ impl MimeMessage { let mut mail_raw = Vec::new(); let mut gossiped_addr = Default::default(); - let (mail, signatures, warn_empty_signature) = - match e2ee::try_decrypt(context, &mail, message_time).await { + let peerstate = e2ee::get_autocrypt_peerstate(context, &mail, message_time).await?; + let (mail, signatures, warn_empty_signature) = if partial.is_none() { + match e2ee::try_decrypt(context, &mail, &peerstate).await { Ok((raw, signatures)) => { if let Some(raw) = raw { // Encrypted, but maybe unsigned message. Only if @@ -267,7 +268,17 @@ impl MimeMessage { (Ok(decrypted_mail), signatures, true) } else { - // Message was not encrypted + // Message was not encrypted. + // If it is not a read receipt, degrade encryption. + if let Some(mut peerstate) = peerstate { + if message_time > peerstate.last_seen_autocrypt + && mail.ctype.mimetype != "multipart/report" + { + peerstate.degrade_encryption(message_time); + peerstate.save_to_db(&context.sql, false).await?; + } + } + (Ok(mail), signatures, false) } } @@ -275,7 +286,11 @@ impl MimeMessage { warn!(context, "decryption failed: {}", err); (Err(err), Default::default(), true) } - }; + } + } else { + // Partial message, do not try to decrypt. + (Ok(mail), Default::default(), false) + }; let mut parser = MimeMessage { parts: Vec::new(),