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(), ));