diff --git a/src/mimefactory.rs b/src/mimefactory.rs index eac134f80..0880af4f6 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -10,6 +10,8 @@ use deltachat_contact_tools::sanitize_bidi_characters; use iroh_gossip::proto::TopicId; use mail_builder::headers::HeaderType; use mail_builder::headers::address::Address; +use mail_builder::headers::raw::Raw; +use mail_builder::headers::text::Text; use mail_builder::mime::MimePart; use tokio::fs; @@ -32,7 +34,7 @@ use crate::message::{Message, MsgId, Viewtype}; use crate::mimeparser::SystemMessage; use crate::param::Param; use crate::peer_channels::{create_iroh_header, get_iroh_topic_for_msg}; -use crate::pgp::{SeipdVersion, addresses_from_public_key, pubkey_supports_seipdv2}; +use crate::pgp::{SeipdVersion, addresses_from_public_key, pubkey_supports_seipdv2, relay_addrs}; use crate::simplify::escape_message_footer_marks; use crate::stock_str; use crate::tools::{IsNoneOrEmpty, create_outgoing_rfc724_mid, remove_subject_prefix, time}; @@ -578,9 +580,7 @@ impl MimeFactory { let public_key = SignedPublicKey::from_slice(&public_key_bytes)?; - let relays = - addresses_from_public_key(&public_key).unwrap_or_else(|| vec![addr.clone()]); - recipients.extend(relays); + recipients.extend(relay_addrs(&public_key, &addr)); to.push((authname, addr.clone())); Encryption::Asymmetric { @@ -897,7 +897,7 @@ impl MimeFactory { } } else if contact.is_key_contact() { let encryption_pubkeys = if let Some(key) = contact.public_key(context).await? { - recipients = addresses_from_public_key(&key).unwrap_or_else(|| vec![addr.clone()]); + recipients = relay_addrs(&key, &addr); vec![(addr.clone(), key)] } else { Vec::new() @@ -2439,6 +2439,42 @@ fn b_encode(value: &str) -> String { ) } +/// Returns the headers to place into the encrypted part +/// of messages that are not part of a chat. +async fn non_chat_protected_headers( + context: &Context, + subject: &str, +) -> Result)>> { + let date = chrono::DateTime::::from_timestamp(time(), 0) + .unwrap() + .to_rfc2822(); + let mut headers = vec![ + ("To", Address::new_list(vec![hidden_recipients()]).into()), + ("Date", Raw::new(date).into()), + ("Subject", Text::new(subject.to_string()).into()), + ]; + // Automatic Response headers + if context.get_config_bool(Config::Bot).await? { + headers.push(("Auto-Submitted", Raw::new("auto-generated").into())); + } + Ok(headers) +} + +/// Renders `queued_mail` for SMTP with the own key pair and primary address. +async fn render_with_self_key(context: &Context, queued_mail: QueuedMail) -> Result { + let public_key = key::load_self_public_key(context).await?; + let secret_key = key::load_self_secret_key(context).await?; + let from_addr = context.get_primary_self_addr().await?; + let rendered_mail = render_queued_mail( + queued_mail, + &public_key, + &secret_key, + from_addr, + RenderSideEffects::default(), + )?; + Ok(rendered_mail.message) +} + pub(crate) async fn render_symm_encrypted_securejoin_message( context: &Context, step: &str, @@ -2451,81 +2487,29 @@ pub(crate) async fn render_symm_encrypted_securejoin_message( let message: MimePart<'static> = MimePart::new("text/plain", "Secure-Join"); - let mut headers = Vec::<(&'static str, HeaderType<'static>)>::new(); - - let to: Vec> = vec![hidden_recipients()]; - headers.push(( - "To", - mail_builder::headers::address::Address::new_list(to.clone()).into(), - )); - - let timestamp = time(); - let date = chrono::DateTime::::from_timestamp(timestamp, 0) - .unwrap() - .to_rfc2822(); - headers.push(("Date", mail_builder::headers::raw::Raw::new(date).into())); - - headers.push(( - "Subject", - mail_builder::headers::text::Text::new("Secure-Join".to_string()).into(), - )); - - // Automatic Response headers - if context.get_config_bool(Config::Bot).await? { - headers.push(( - "Auto-Submitted", - mail_builder::headers::raw::Raw::new("auto-generated".to_string()).into(), - )); - } - - headers.push(( - "Secure-Join", - mail_builder::headers::raw::Raw::new(step.to_string()).into(), - )); - - headers.push(( - "Secure-Join-Auth", - mail_builder::headers::text::Text::new(auth.to_string()).into(), - )); + let mut headers = non_chat_protected_headers(context, "Secure-Join").await?; + headers.push(("Secure-Join", Raw::new(step.to_string()).into())); + headers.push(("Secure-Join-Auth", Text::new(auth.to_string()).into())); let message = add_headers_to_encrypted_part(message, headers); - // Disable compression for SecureJoin to ensure - // there are no compression side channels - // leaking information about the tokens. - let should_compress = false; - - // Only sign the message if we attach the pubkey. - let should_sign = should_attach_pubkey; - - let raw_message = part_to_bytes(message); - let queued_mail = QueuedMail { - raw_message, + raw_message: part_to_bytes(message), display_name: String::new(), rfc724_mid: rfc724_mid.to_string(), encryption: Encryption::Symmetric { shared_secret: shared_secret.to_string(), }, should_attach_pubkey, - should_sign, - should_compress, + // Only sign the message if we attach the pubkey. + should_sign: should_attach_pubkey, + // Disable compression for SecureJoin to ensure + // there are no compression side channels + // leaking information about the tokens. + should_compress: false, }; - let public_key = key::load_self_public_key(context).await?; - let secret_key = key::load_self_secret_key(context).await?; - let side_effects = RenderSideEffects::default(); - - let from_addr = context.get_primary_self_addr().await?; - let rendered_mail = render_queued_mail( - queued_mail, - &public_key, - &secret_key, - from_addr, - side_effects, - )?; - - Ok(rendered_mail.message) + render_with_self_key(context, queued_mail).await } /// Renders MIME part into a vector of bytes. diff --git a/src/pgp.rs b/src/pgp.rs index 1200f7f4f..466d81a6d 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -452,6 +452,12 @@ pub(crate) fn addresses_from_public_key(public_key: &SignedPublicKey) -> Option< None } +/// Returns the addresses to reach the owner of `public_key`, +/// falling back to `addr` if the key carries no relay list. +pub(crate) fn relay_addrs(public_key: &SignedPublicKey, addr: &str) -> Vec { + addresses_from_public_key(public_key).unwrap_or_else(|| vec![addr.to_string()]) +} + /// Returns true if public key advertises SEIPDv2 feature. pub(crate) fn pubkey_supports_seipdv2(public_key: &SignedPublicKey) -> bool { // If any Direct Key Signature or any User ID signature has SEIPDv2 feature, diff --git a/src/securejoin.rs b/src/securejoin.rs index 7102e7bf2..2c9394528 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -16,11 +16,12 @@ use crate::key; use crate::key::{DcKey, Fingerprint, load_self_public_key, self_fingerprint}; use crate::log::LogExt as _; use crate::log::warn; -use crate::message::{self, Message, MsgId, Viewtype}; +use crate::message::{self, Message, Viewtype}; use crate::mimeparser::{MimeMessage, SystemMessage}; use crate::param::Param; use crate::qr::check_qr; use crate::securejoin::bob::JoinerProgress; +use crate::smtp::insert_into_smtp; use crate::sync::Sync::*; use crate::tools::{create_id, create_outgoing_rfc724_mid, time}; use crate::{SecurejoinSource, mimefactory, stats}; @@ -743,24 +744,6 @@ pub(crate) async fn handle_securejoin_handshake( } } -async fn insert_into_smtp( - context: &Context, - rfc724_mid: &str, - recipients: &str, - rendered_message: String, - msg_id: MsgId, -) -> Result<(), Error> { - context - .sql - .execute( - "INSERT INTO smtp (rfc724_mid, recipients, mime, msg_id) - VALUES (?1, ?2, ?3, ?4)", - (&rfc724_mid, &recipients, &rendered_message, msg_id), - ) - .await?; - Ok(()) -} - /// Observe self-sent Securejoin message. /// /// In a multi-device-setup, there may be other devices that "see" the handshake messages. diff --git a/src/securejoin/bob.rs b/src/securejoin/bob.rs index 4ce5aed41..2db554204 100644 --- a/src/securejoin/bob.rs +++ b/src/securejoin/bob.rs @@ -16,9 +16,8 @@ use crate::message::{self, Message, MsgId, Viewtype}; use crate::mimeparser::{MimeMessage, SystemMessage}; use crate::param::{Param, Params}; use crate::pgp::addresses_from_public_key; -use crate::securejoin::{ - ContactId, encrypted_and_signed, insert_into_smtp, verify_sender_by_fingerprint, -}; +use crate::securejoin::{ContactId, encrypted_and_signed, verify_sender_by_fingerprint}; +use crate::smtp::insert_into_smtp; use crate::stock_str; use crate::sync::Sync::*; use crate::tools::{create_outgoing_rfc724_mid, time}; diff --git a/src/smtp.rs b/src/smtp.rs index 28ecb0fc6..f404d2daa 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -327,6 +327,25 @@ pub(crate) async fn smtp_send( status } +/// Inserts a rendered message into the `smtp` table for sending. +pub(crate) async fn insert_into_smtp( + context: &Context, + rfc724_mid: &str, + recipients: &str, + rendered_message: String, + msg_id: MsgId, +) -> Result<(), Error> { + context + .sql + .execute( + "INSERT INTO smtp (rfc724_mid, recipients, mime, msg_id) + VALUES (?1, ?2, ?3, ?4)", + (&rfc724_mid, &recipients, &rendered_message, msg_id), + ) + .await?; + Ok(()) +} + /// Sends message identified by `smtp` table rowid over SMTP connection. /// /// Removes row if the message should not be retried, otherwise increments retry count.