From faad576d1008fde85aaa5cedc711088464168364 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 2 Nov 2024 22:27:06 +0000 Subject: [PATCH] feat: experimental header protection for Autocrypt This change adds support for receiving Autocrypt header in the protected part of encrypted message. Autocrypt header is now also allowed in mailing lists. Previously Autocrypt header was rejected when List-Post header was present, but the check for the address being equal to the From: address is sufficient. New experimental `protect_autocrypt` config is disabled by default because Delta Chat with reception support should be released first on all platforms. --- deltachat-ffi/deltachat.h | 5 ++ src/authres.rs | 21 ++--- src/config.rs | 6 ++ src/context.rs | 6 ++ src/decrypt.rs | 99 ++---------------------- src/mimefactory.rs | 4 +- src/mimeparser.rs | 159 ++++++++++++++++++++++++++++---------- src/peerstate.rs | 20 +++-- src/pgp.rs | 67 +++++++++------- src/receive_imf.rs | 13 ++-- 10 files changed, 207 insertions(+), 193 deletions(-) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 34ff1914e..bccb269cc 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -506,6 +506,11 @@ char* dc_get_blobdir (const dc_context_t* context); * to not mess up with non-delivery-reports or read-receipts. * 0=no limit (default). * Changes affect future messages only. + * - `protect_autocrypt` = Enable Header Protection for Autocrypt header. + * This is an experimental option not compatible to other MUAs + * and older Delta Chat versions. + * 1 = enable. + * 0 = disable (default). * - `gossip_period` = How often to gossip Autocrypt keys in chats with multiple recipients, in * seconds. 2 days by default. * This is not supposed to be changed by UIs and only used for testing. diff --git a/src/authres.rs b/src/authres.rs index 46dd10eb1..4d6e86d83 100644 --- a/src/authres.rs +++ b/src/authres.rs @@ -520,8 +520,13 @@ Authentication-Results: dkim="; handle_authres(&t, &mail, "invalid@rom.com").await.unwrap(); } + // Test that Autocrypt works with mailing list. + // + // Previous versions of Delta Chat ignored Autocrypt based on the List-Post header. + // This is not needed: comparing of the From address to Autocrypt header address is enough. + // If the mailing list is not rewriting the From header, Autocrypt should be applied. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_autocrypt_in_mailinglist_ignored() -> Result<()> { + async fn test_autocrypt_in_mailinglist_not_ignored() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = tcm.alice().await; let bob = tcm.bob().await; @@ -533,28 +538,18 @@ Authentication-Results: dkim="; .insert_str(0, "List-Post: \n"); bob.recv_msg(&sent).await; let peerstate = Peerstate::from_addr(&bob, "alice@example.org").await?; - assert!(peerstate.is_none()); - - // Do the same without the mailing list header, this time the peerstate should be accepted - let sent = alice - .send_text(alice_bob_chat.id, "hellooo without mailing list") - .await; - bob.recv_msg(&sent).await; - let peerstate = Peerstate::from_addr(&bob, "alice@example.org").await?; assert!(peerstate.is_some()); - // This also means that Bob can now write encrypted to Alice: + // Bob can now write encrypted to Alice: let mut sent = bob .send_text(bob_alice_chat.id, "hellooo in the mailinglist again") .await; assert!(sent.load_from_db().await.get_showpadlock()); - // But if Bob writes to a mailing list, Alice doesn't show a padlock - // since she can't verify the signature without accepting Bob's key: sent.payload .insert_str(0, "List-Post: \n"); let rcvd = alice.recv_msg(&sent).await; - assert!(!rcvd.get_showpadlock()); + assert!(rcvd.get_showpadlock()); assert_eq!(&rcvd.text, "hellooo in the mailinglist again"); Ok(()) diff --git a/src/config.rs b/src/config.rs index ad279068b..46eae017a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -396,6 +396,12 @@ pub enum Config { /// Make all outgoing messages with Autocrypt header "multipart/signed". SignUnencrypted, + /// Enable header protection for `Autocrypt` header. + /// + /// This is an experimental setting not compatible to other MUAs + /// and older Delta Chat versions (core version <= v1.149.0). + ProtectAutocrypt, + /// Let the core save all events to the database. /// This value is used internally to remember the MsgId of the logging xdc #[strum(props(default = "0"))] diff --git a/src/context.rs b/src/context.rs index 04ec1c74d..ba1784fdf 100644 --- a/src/context.rs +++ b/src/context.rs @@ -990,6 +990,12 @@ impl Context { .await? .to_string(), ); + res.insert( + "protect_autocrypt", + self.get_config_int(Config::ProtectAutocrypt) + .await? + .to_string(), + ); res.insert( "debug_logging", self.get_config_int(Config::DebugLogging).await?.to_string(), diff --git a/src/decrypt.rs b/src/decrypt.rs index 7c33fe096..2dd7bc35f 100644 --- a/src/decrypt.rs +++ b/src/decrypt.rs @@ -1,125 +1,36 @@ //! End-to-end decryption support. use std::collections::HashSet; -use std::str::FromStr; use anyhow::Result; use deltachat_contact_tools::addr_cmp; use mailparse::ParsedMail; use crate::aheader::Aheader; -use crate::authres::handle_authres; -use crate::authres::{self, DkimResults}; use crate::context::Context; -use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::key::{DcKey, Fingerprint, SignedPublicKey, SignedSecretKey}; use crate::peerstate::Peerstate; use crate::pgp; /// Tries to decrypt a message, but only if it is structured as an Autocrypt message. /// -/// If successful and the message is encrypted, returns decrypted body and a set of valid -/// signature fingerprints. -/// -/// If the message is wrongly signed, HashSet will be empty. +/// If successful and the message is encrypted, returns decrypted body. pub fn try_decrypt( mail: &ParsedMail<'_>, private_keyring: &[SignedSecretKey], - public_keyring_for_validate: &[SignedPublicKey], -) -> Result, HashSet)>> { +) -> Result> { let Some(encrypted_data_part) = get_encrypted_mime(mail) else { return Ok(None); }; let data = encrypted_data_part.get_body_raw()?; + let msg = pgp::pk_decrypt(data, private_keyring)?; - let (plain, ret_valid_signatures) = - pgp::pk_decrypt(data, private_keyring, public_keyring_for_validate)?; - Ok(Some((plain, ret_valid_signatures))) -} - -pub(crate) async fn prepare_decryption( - context: &Context, - mail: &ParsedMail<'_>, - from: &str, - message_time: i64, -) -> Result { - if mail.headers.get_header(HeaderDef::ListPost).is_some() { - if mail.headers.get_header(HeaderDef::Autocrypt).is_some() { - info!( - context, - "Ignoring autocrypt header since this is a mailing list message. \ - NOTE: For privacy reasons, the mailing list software should remove Autocrypt headers." - ); - } - return Ok(DecryptionInfo { - from: from.to_string(), - autocrypt_header: None, - peerstate: None, - message_time, - dkim_results: DkimResults { dkim_passed: false }, - }); - } - - let autocrypt_header = if context.is_self_addr(from).await? { - None - } else if let Some(aheader_value) = mail.headers.get_header_value(HeaderDef::Autocrypt) { - match Aheader::from_str(&aheader_value) { - Ok(header) if addr_cmp(&header.addr, from) => Some(header), - Ok(header) => { - warn!( - context, - "Autocrypt header address {:?} is not {:?}.", header.addr, from - ); - None - } - Err(err) => { - warn!(context, "Failed to parse Autocrypt header: {:#}.", err); - None - } - } - } else { - None - }; - - let dkim_results = handle_authres(context, mail, from).await?; - let allow_aeap = get_encrypted_mime(mail).is_some(); - let peerstate = get_autocrypt_peerstate( - context, - from, - autocrypt_header.as_ref(), - message_time, - allow_aeap, - ) - .await?; - - Ok(DecryptionInfo { - from: from.to_string(), - autocrypt_header, - peerstate, - message_time, - dkim_results, - }) -} - -#[derive(Debug)] -pub struct DecryptionInfo { - /// The From address. This is the address from the unnencrypted, outer - /// From header. - pub from: String, - pub autocrypt_header: Option, - /// The peerstate that will be used to validate the signatures - pub peerstate: Option, - /// The timestamp when the message was sent. - /// If this is older than the peerstate's last_seen, this probably - /// means out-of-order message arrival, We don't modify the - /// peerstate in this case. - pub message_time: i64, - pub(crate) dkim_results: authres::DkimResults, + Ok(Some(msg)) } /// Returns a reference to the encrypted payload of a message. -fn get_encrypted_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail<'b>> { +pub(crate) fn get_encrypted_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail<'b>> { get_autocrypt_mime(mail) .or_else(|| get_mixed_up_mime(mail)) .or_else(|| get_attachment_mime(mail)) diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 4cafd4f38..83556c0d0 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -743,7 +743,9 @@ impl MimeFactory { hidden_headers.push(header); } else if header_name == "chat-user-avatar" { hidden_headers.push(header); - } else if header_name == "autocrypt" { + } else if header_name == "autocrypt" + && !context.get_config_bool(Config::ProtectAutocrypt).await? + { unprotected_headers.push(header.clone()); } else if header_name == "from" { // Unencrypted securejoin messages should _not_ include the display name: diff --git a/src/mimeparser.rs b/src/mimeparser.rs index f47c72326..e1f5ec1f8 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -4,6 +4,7 @@ use std::cmp::min; use std::collections::{HashMap, HashSet}; use std::path::Path; use std::str; +use std::str::FromStr; use anyhow::{bail, Context as _, Result}; use deltachat_contact_tools::{addr_cmp, addr_normalize, sanitize_bidi_characters}; @@ -14,6 +15,7 @@ use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, Si use rand::distributions::{Alphanumeric, DistString}; use crate::aheader::{Aheader, EncryptPreference}; +use crate::authres::handle_authres; use crate::blob::BlobObject; use crate::chat::{add_info_msg, ChatId}; use crate::config::Config; @@ -21,8 +23,8 @@ use crate::constants::{self, Chattype}; use crate::contact::{Contact, ContactId, Origin}; use crate::context::Context; use crate::decrypt::{ - keyring_from_peerstate, prepare_decryption, try_decrypt, validate_detached_signature, - DecryptionInfo, + get_autocrypt_peerstate, get_encrypted_mime, keyring_from_peerstate, try_decrypt, + validate_detached_signature, }; use crate::dehtml::dehtml; use crate::events::EventType; @@ -71,7 +73,8 @@ pub(crate) struct MimeMessage { /// messages to this address to post them to the list. pub list_post: Option, pub chat_disposition_notification_to: Option, - pub decryption_info: DecryptionInfo, + pub autocrypt_header: Option, + pub peerstate: Option, pub decrypting_failed: bool, /// Set of valid signature fingerprints if a message is an @@ -301,42 +304,101 @@ impl MimeMessage { let mut from = from.context("No from in message")?; let private_keyring = load_self_secret_keyring(context).await?; - let mut decryption_info = - prepare_decryption(context, &mail, &from.addr, timestamp_sent).await?; + let allow_aeap = get_encrypted_mime(&mail).is_some(); + + let dkim_results = handle_authres(context, &mail, &from.addr).await?; - // Memory location for a possible decrypted message. - let mut mail_raw = Vec::new(); let mut gossiped_keys = Default::default(); let mut from_is_signed = false; hop_info += "\n\n"; - hop_info += &decryption_info.dkim_results.to_string(); + hop_info += &dkim_results.to_string(); let incoming = !context.is_self_addr(&from.addr).await?; - let public_keyring = match decryption_info.peerstate.is_none() && !incoming { - true => key::load_self_public_keyring(context).await?, - false => keyring_from_peerstate(decryption_info.peerstate.as_ref()), - }; - let (mail, mut signatures, encrypted) = match tokio::task::block_in_place(|| { - try_decrypt(&mail, &private_keyring, &public_keyring) - }) { - Ok(Some((raw, signatures))) => { - mail_raw = raw; - let decrypted_mail = mailparse::parse_mail(&mail_raw)?; - if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { - info!( - context, - "decrypted message mime-body:\n{}", - String::from_utf8_lossy(&mail_raw), - ); + + let mut aheader_value: Option = mail.headers.get_header_value(HeaderDef::Autocrypt); + + let mail_raw; // Memory location for a possible decrypted message. + let decrypted_msg; // Decrypted signed OpenPGP message. + + let (mail, encrypted) = + match tokio::task::block_in_place(|| try_decrypt(&mail, &private_keyring)) { + Ok(Some(msg)) => { + mail_raw = msg.get_content()?.unwrap_or_default(); + + let decrypted_mail = mailparse::parse_mail(&mail_raw)?; + if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { + info!( + context, + "decrypted message mime-body:\n{}", + String::from_utf8_lossy(&mail_raw), + ); + } + + decrypted_msg = Some(msg); + if let Some(protected_aheader_value) = decrypted_mail + .headers + .get_header_value(HeaderDef::Autocrypt) + { + aheader_value = Some(protected_aheader_value); + } + + (Ok(decrypted_mail), true) + } + Ok(None) => { + mail_raw = Vec::new(); + decrypted_msg = None; + (Ok(mail), false) + } + Err(err) => { + mail_raw = Vec::new(); + decrypted_msg = None; + warn!(context, "decryption failed: {:#}", err); + (Err(err), false) + } + }; + + let autocrypt_header = if !incoming { + None + } else if let Some(aheader_value) = aheader_value { + match Aheader::from_str(&aheader_value) { + Ok(header) if addr_cmp(&header.addr, &from.addr) => Some(header), + Ok(header) => { + warn!( + context, + "Autocrypt header address {:?} is not {:?}.", header.addr, from.addr + ); + None + } + Err(err) => { + warn!(context, "Failed to parse Autocrypt header: {:#}.", err); + None } - (Ok(decrypted_mail), signatures, true) - } - Ok(None) => (Ok(mail), HashSet::new(), false), - Err(err) => { - warn!(context, "decryption failed: {:#}", err); - (Err(err), HashSet::new(), false) } + } else { + None }; + + // The peerstate that will be used to validate the signatures. + let mut peerstate = get_autocrypt_peerstate( + context, + &from.addr, + autocrypt_header.as_ref(), + timestamp_sent, + allow_aeap, + ) + .await?; + + let public_keyring = match peerstate.is_none() && !incoming { + true => key::load_self_public_keyring(context).await?, + false => keyring_from_peerstate(peerstate.as_ref()), + }; + + let mut signatures = if let Some(ref decrypted_msg) = decrypted_msg { + crate::pgp::valid_signature_fingerprints(decrypted_msg, &public_keyring)? + } else { + HashSet::new() + }; + let mail = mail.as_ref().map(|mail| { let (content, signatures_detached) = validate_detached_signature(mail, &public_keyring) .unwrap_or((mail, Default::default())); @@ -422,7 +484,7 @@ impl MimeMessage { Self::remove_secured_headers(&mut headers); // If it is not a read receipt, degrade encryption. - if let (Some(peerstate), Ok(mail)) = (&mut decryption_info.peerstate, mail) { + if let (Some(peerstate), Ok(mail)) = (&mut peerstate, mail) { if timestamp_sent > peerstate.last_seen_autocrypt && mail.ctype.mimetype != "multipart/report" { @@ -433,7 +495,7 @@ impl MimeMessage { if !encrypted { signatures.clear(); } - if let Some(peerstate) = &mut decryption_info.peerstate { + if let Some(peerstate) = &mut peerstate { if peerstate.prefer_encrypt != EncryptPreference::Mutual && !signatures.is_empty() { peerstate.prefer_encrypt = EncryptPreference::Mutual; peerstate.save_to_db(&context.sql).await?; @@ -449,7 +511,8 @@ impl MimeMessage { from_is_signed, incoming, chat_disposition_notification_to, - decryption_info, + autocrypt_header, + peerstate, decrypting_failed: mail.is_err(), // only non-empty if it was a valid autocrypt message @@ -1231,7 +1294,7 @@ impl MimeMessage { if decoded_data.is_empty() { return Ok(()); } - if let Some(peerstate) = &mut self.decryption_info.peerstate { + if let Some(peerstate) = &mut self.peerstate { if peerstate.prefer_encrypt != EncryptPreference::Mutual && mime_type.type_() == mime::APPLICATION && mime_type.subtype().as_str() == "pgp-keys" @@ -4012,12 +4075,8 @@ Content-Disposition: reaction\n\ // We do allow the time to be in the future a bit (because of unsynchronized clocks), // but only 60 seconds: - assert!(mime_message.decryption_info.message_time <= time() + 60); - assert!(mime_message.decryption_info.message_time >= beginning_time + 60); - assert_eq!( - mime_message.decryption_info.message_time, - mime_message.timestamp_sent - ); + assert!(mime_message.timestamp_sent <= time() + 60); + assert!(mime_message.timestamp_sent >= beginning_time + 60); assert!(mime_message.timestamp_rcvd <= time()); Ok(()) @@ -4088,4 +4147,24 @@ Content-Type: text/plain; charset=utf-8 "alice@example.org" ); } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_protect_autocrypt() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + + alice + .set_config_bool(Config::ProtectAutocrypt, true) + .await?; + bob.set_config_bool(Config::ProtectAutocrypt, true).await?; + + let msg = tcm.send_recv_accept(alice, bob, "Hello!").await; + assert_eq!(msg.get_showpadlock(), false); + + let msg = tcm.send_recv(bob, alice, "Hi!").await; + assert_eq!(msg.get_showpadlock(), true); + + Ok(()) + } } diff --git a/src/peerstate.rs b/src/peerstate.rs index fc034fcde..891235790 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -766,8 +766,7 @@ pub(crate) async fn maybe_do_aeap_transition( context: &Context, mime_parser: &mut crate::mimeparser::MimeMessage, ) -> Result<()> { - let info = &mime_parser.decryption_info; - let Some(peerstate) = &info.peerstate else { + let Some(peerstate) = &mime_parser.peerstate else { return Ok(()); }; @@ -815,13 +814,13 @@ pub(crate) async fn maybe_do_aeap_transition( // 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 { + if mime_parser.timestamp_sent <= peerstate.last_seen { info!( context, "Not doing AEAP from {} to {} because {} < {}.", &peerstate.addr, &mime_parser.from.addr, - info.message_time, + mime_parser.timestamp_sent, peerstate.last_seen ); return Ok(()); @@ -832,24 +831,23 @@ pub(crate) async fn maybe_do_aeap_transition( "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??")?; + let peerstate = mime_parser.peerstate.as_mut().context("no peerstate??")?; // Add info messages to chats with this (verified) contact // peerstate .handle_setup_change( context, - info.message_time, - PeerstateChange::Aeap(info.from.clone()), + mime_parser.timestamp_sent, + PeerstateChange::Aeap(mime_parser.from.addr.clone()), ) .await?; let old_addr = mem::take(&mut peerstate.addr); - peerstate.addr.clone_from(&info.from); - let header = info.autocrypt_header.as_ref().context( + peerstate.addr.clone_from(&mime_parser.from.addr); + let header = mime_parser.autocrypt_header.as_ref().context( "Internal error: Tried to do an AEAP transition without an autocrypt header??", )?; - peerstate.apply_header(context, header, info.message_time); + peerstate.apply_header(context, header, mime_parser.timestamp_sent); peerstate .save_to_db_ex(&context.sql, Some(&old_addr)) diff --git a/src/pgp.rs b/src/pgp.rs index c54ff7a41..447603232 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -297,34 +297,34 @@ pub fn pk_calc_signature( /// /// Receiver private keys are provided in /// `private_keys_for_decryption`. -/// -/// Returns decrypted message and fingerprints -/// of all keys from the `public_keys_for_validation` keyring that -/// have valid signatures there. -#[allow(clippy::implicit_hasher)] pub fn pk_decrypt( ctext: Vec, private_keys_for_decryption: &[SignedSecretKey], - public_keys_for_validation: &[SignedPublicKey], -) -> Result<(Vec, HashSet)> { - let mut ret_signature_fingerprints: HashSet = Default::default(); - +) -> Result { let cursor = Cursor::new(ctext); - let (msg, _) = Message::from_armor_single(cursor)?; + let (msg, _headers) = Message::from_armor_single(cursor)?; let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.iter().collect(); - let (msg, _) = msg.decrypt(|| "".into(), &skeys[..])?; + let (msg, _key_ids) = msg.decrypt(|| "".into(), &skeys[..])?; // get_content() will decompress the message if needed, // but this avoids decompressing it again to check signatures let msg = msg.decompress()?; - let content = match msg.get_content()? { - Some(content) => content, - None => bail!("The decrypted message is empty"), - }; + Ok(msg) +} +/// Returns fingerprints +/// of all keys from the `public_keys_for_validation` keyring that +/// have valid signatures there. +/// +/// If the message is wrongly signed, HashSet will be empty. +pub fn valid_signature_fingerprints( + msg: &pgp::composed::Message, + public_keys_for_validation: &[SignedPublicKey], +) -> Result> { + let mut ret_signature_fingerprints: HashSet = Default::default(); if let signed_msg @ pgp::composed::Message::Signed { .. } = msg { for pkey in public_keys_for_validation { if signed_msg.verify(&pkey.primary_key).is_ok() { @@ -333,7 +333,7 @@ pub fn pk_decrypt( } } } - Ok((content, ret_signature_fingerprints)) + Ok(ret_signature_fingerprints) } /// Validates detached signature. @@ -407,6 +407,18 @@ mod tests { use super::*; use crate::test_utils::{alice_keypair, bob_keypair}; + fn pk_decrypt_and_validate( + ctext: Vec, + private_keys_for_decryption: &[SignedSecretKey], + public_keys_for_validation: &[SignedPublicKey], + ) -> Result<(pgp::composed::Message, HashSet)> { + let msg = pk_decrypt(ctext, private_keys_for_decryption)?; + let ret_signature_fingerprints = + valid_signature_fingerprints(&msg, public_keys_for_validation)?; + + Ok((msg, ret_signature_fingerprints)) + } + #[test] fn test_split_armored_data_1() { let (typ, _headers, base64) = split_armored_data( @@ -534,34 +546,35 @@ mod tests { // Check decrypting as Alice let decrypt_keyring = vec![KEYS.alice_secret.clone()]; let sig_check_keyring = vec![KEYS.alice_public.clone()]; - let (plain, valid_signatures) = pk_decrypt( + let (msg, valid_signatures) = pk_decrypt_and_validate( ctext_signed().await.as_bytes().to_vec(), &decrypt_keyring, &sig_check_keyring, ) .unwrap(); - assert_eq!(plain, CLEARTEXT); + assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT); assert_eq!(valid_signatures.len(), 1); // Check decrypting as Bob let decrypt_keyring = vec![KEYS.bob_secret.clone()]; let sig_check_keyring = vec![KEYS.alice_public.clone()]; - let (plain, valid_signatures) = pk_decrypt( + let (msg, valid_signatures) = pk_decrypt_and_validate( ctext_signed().await.as_bytes().to_vec(), &decrypt_keyring, &sig_check_keyring, ) .unwrap(); - assert_eq!(plain, CLEARTEXT); + assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT); assert_eq!(valid_signatures.len(), 1); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_decrypt_no_sig_check() { let keyring = vec![KEYS.alice_secret.clone()]; - let (plain, valid_signatures) = - pk_decrypt(ctext_signed().await.as_bytes().to_vec(), &keyring, &[]).unwrap(); - assert_eq!(plain, CLEARTEXT); + let (msg, valid_signatures) = + pk_decrypt_and_validate(ctext_signed().await.as_bytes().to_vec(), &keyring, &[]) + .unwrap(); + assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT); assert_eq!(valid_signatures.len(), 0); } @@ -570,26 +583,26 @@ mod tests { // The validation does not have the public key of the signer. let decrypt_keyring = vec![KEYS.bob_secret.clone()]; let sig_check_keyring = vec![KEYS.bob_public.clone()]; - let (plain, valid_signatures) = pk_decrypt( + let (msg, valid_signatures) = pk_decrypt_and_validate( ctext_signed().await.as_bytes().to_vec(), &decrypt_keyring, &sig_check_keyring, ) .unwrap(); - assert_eq!(plain, CLEARTEXT); + assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT); assert_eq!(valid_signatures.len(), 0); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_decrypt_unsigned() { let decrypt_keyring = vec![KEYS.bob_secret.clone()]; - let (plain, valid_signatures) = pk_decrypt( + let (msg, valid_signatures) = pk_decrypt_and_validate( ctext_unsigned().await.as_bytes().to_vec(), &decrypt_keyring, &[], ) .unwrap(); - assert_eq!(plain, CLEARTEXT); + assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT); assert_eq!(valid_signatures.len(), 0); } } diff --git a/src/receive_imf.rs b/src/receive_imf.rs index f3d513a60..e7fdb81c3 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -201,7 +201,7 @@ pub(crate) async fn receive_imf_inner( }; crate::peerstate::maybe_do_aeap_transition(context, &mut mime_parser).await?; - if let Some(peerstate) = &mime_parser.decryption_info.peerstate { + if let Some(peerstate) = &mime_parser.peerstate { peerstate .handle_fingerprint_change(context, mime_parser.timestamp_sent) .await?; @@ -356,8 +356,7 @@ pub(crate) async fn receive_imf_inner( // Peerstate could be updated by handling the Securejoin handshake. let contact = Contact::get_by_id(context, from_id).await?; - mime_parser.decryption_info.peerstate = - Peerstate::from_addr(context, contact.get_addr()).await?; + mime_parser.peerstate = Peerstate::from_addr(context, contact.get_addr()).await?; } else { let to_id = to_ids.first().copied().unwrap_or_default(); // handshake may mark contacts as verified and must be processed before chats are created @@ -393,7 +392,7 @@ pub(crate) async fn receive_imf_inner( if verified_encryption == VerifiedEncryption::Verified && mime_parser.get_header(HeaderDef::ChatVerified).is_some() { - if let Some(peerstate) = &mut mime_parser.decryption_info.peerstate { + if let Some(peerstate) = &mut mime_parser.peerstate { // NOTE: it might be better to remember ID of the key // that we used to decrypt the message, but // it is unlikely that default key ever changes @@ -1006,7 +1005,7 @@ async fn add_parts( ) .await?; } - if let Some(peerstate) = &mime_parser.decryption_info.peerstate { + if let Some(peerstate) = &mime_parser.peerstate { restore_protection = new_protection != ProtectionStatus::Protected && peerstate.prefer_encrypt == EncryptPreference::Mutual // Check that the contact still has the Autocrypt key same as the @@ -2662,7 +2661,7 @@ async fn update_verified_keys( return Ok(None); } - let Some(peerstate) = &mut mimeparser.decryption_info.peerstate else { + let Some(peerstate) = &mut mimeparser.peerstate else { // No peerstate means no verified keys. return Ok(None); }; @@ -2735,7 +2734,7 @@ async fn has_verified_encryption( // this check is skipped for SELF as there is no proper SELF-peerstate // and results in group-splits otherwise. if from_id != ContactId::SELF { - let Some(peerstate) = &mimeparser.decryption_info.peerstate else { + let Some(peerstate) = &mimeparser.peerstate else { return Ok(NotVerified( "No peerstate, the contact isn't verified".to_string(), ));