From 23d95df66a7db60b043c4458914e78ffb72325e8 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 30 Nov 2025 04:45:03 +0000 Subject: [PATCH] feat: use v2 SEIPD when sending messages to self --- benches/decrypting.rs | 3 +- src/e2ee.rs | 4 ++- src/mimefactory.rs | 13 ++++++++ src/pgp.rs | 69 +++++++++++++++++++++++++++++++++++-------- 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/benches/decrypting.rs b/benches/decrypting.rs index 76acaa44b..435850782 100644 --- a/benches/decrypting.rs +++ b/benches/decrypting.rs @@ -38,7 +38,7 @@ use deltachat::{ internals_for_benches::key_from_asc, internals_for_benches::parse_and_get_text, internals_for_benches::store_self_keypair, - pgp::{KeyPair, decrypt, pk_encrypt, symm_encrypt_message}, + pgp::{KeyPair, SeipdVersion, decrypt, pk_encrypt, symm_encrypt_message}, stock_str::StockStrings, }; use rand::{Rng, rng}; @@ -111,6 +111,7 @@ fn criterion_benchmark(c: &mut Criterion) { key_pair.secret.clone(), true, true, + SeipdVersion::V2, ) .await .unwrap() diff --git a/src/e2ee.rs b/src/e2ee.rs index 59028db7e..5976308b6 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -8,7 +8,7 @@ use mail_builder::mime::MimePart; use crate::aheader::{Aheader, EncryptPreference}; use crate::context::Context; use crate::key::{SignedPublicKey, load_self_public_key, load_self_secret_key}; -use crate::pgp; +use crate::pgp::{self, SeipdVersion}; #[derive(Debug)] pub struct EncryptHelper { @@ -47,6 +47,7 @@ impl EncryptHelper { mail_to_encrypt: MimePart<'static>, compress: bool, anonymous_recipients: bool, + seipd_version: SeipdVersion, ) -> Result { let sign_key = load_self_secret_key(context).await?; @@ -60,6 +61,7 @@ impl EncryptHelper { sign_key, compress, anonymous_recipients, + seipd_version, ) .await?; diff --git a/src/mimefactory.rs b/src/mimefactory.rs index da8270dfd..732fff545 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -32,6 +32,7 @@ use crate::message::{Message, MsgId, Viewtype}; use crate::mimeparser::{SystemMessage, is_hidden}; use crate::param::Param; use crate::peer_channels::{create_iroh_header, get_iroh_topic_for_msg}; +use crate::pgp::SeipdVersion; use crate::simplify::escape_message_footer_marks; use crate::stock_str; use crate::tools::{ @@ -1258,6 +1259,17 @@ impl MimeFactory { } else { // Asymmetric encryption + let seipd_version = if encryption_pubkeys.is_empty() { + // If message is sent only to self, + // use v2 SEIPD. + SeipdVersion::V2 + } else { + // If message is sent to others, + // they may not support v2 SEIPD yet, + // so use v1 SEIPD. + SeipdVersion::V1 + }; + // Encrypt to self unconditionally, // even for a single-device setup. let mut encryption_keyring = vec![encrypt_helper.public_key.clone()]; @@ -1271,6 +1283,7 @@ impl MimeFactory { message, compress, anonymous_recipients, + seipd_version, ) .await? }; diff --git a/src/pgp.rs b/src/pgp.rs index 08f480dc3..beecabe7c 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -160,6 +160,20 @@ fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey .find(|subkey| subkey.is_encryption_key()) } +/// Version of SEIPD packet to use. +/// +/// See +/// +/// for the discussion on when v2 SEIPD should be used. +#[derive(Debug)] +pub enum SeipdVersion { + /// Use v1 SEIPD, for compatibility. + V1, + + /// Use v2 SEIPD when we know that v2 SEIPD is supported. + V2, +} + /// Encrypts `plain` text using `public_keys_for_encryption` /// and signs it using `private_key_for_signing`. pub async fn pk_encrypt( @@ -168,6 +182,7 @@ pub async fn pk_encrypt( private_key_for_signing: SignedSecretKey, compress: bool, anonymous_recipients: bool, + seipd_version: SeipdVersion, ) -> Result { Handle::current() .spawn_blocking(move || { @@ -178,21 +193,49 @@ pub async fn pk_encrypt( .filter_map(select_pk_for_encryption); let msg = MessageBuilder::from_bytes("", plain); - let mut msg = msg.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM); - for pkey in pkeys { - if anonymous_recipients { - msg.encrypt_to_key_anonymous(&mut rng, &pkey)?; - } else { - msg.encrypt_to_key(&mut rng, &pkey)?; + let encoded_msg = match seipd_version { + SeipdVersion::V1 => { + let mut msg = msg.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM); + + for pkey in pkeys { + if anonymous_recipients { + msg.encrypt_to_key_anonymous(&mut rng, &pkey)?; + } else { + msg.encrypt_to_key(&mut rng, &pkey)?; + } + } + + msg.sign(&*private_key_for_signing, Password::empty(), HASH_ALGORITHM); + if compress { + msg.compression(CompressionAlgorithm::ZLIB); + } + + msg.to_armored_string(&mut rng, Default::default())? } - } + SeipdVersion::V2 => { + let mut msg = msg.seipd_v2( + &mut rng, + SYMMETRIC_KEY_ALGORITHM, + AeadAlgorithm::Ocb, + ChunkSize::C8KiB, + ); - msg.sign(&*private_key_for_signing, Password::empty(), HASH_ALGORITHM); - if compress { - msg.compression(CompressionAlgorithm::ZLIB); - } + for pkey in pkeys { + if anonymous_recipients { + msg.encrypt_to_key_anonymous(&mut rng, &pkey)?; + } else { + msg.encrypt_to_key(&mut rng, &pkey)?; + } + } - let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?; + msg.sign(&*private_key_for_signing, Password::empty(), HASH_ALGORITHM); + if compress { + msg.compression(CompressionAlgorithm::ZLIB); + } + + msg.to_armored_string(&mut rng, Default::default())? + } + }; Ok(encoded_msg) }) @@ -547,6 +590,7 @@ mod tests { KEYS.alice_secret.clone(), compress, anonymous_recipients, + SeipdVersion::V2, ) .await .unwrap() @@ -716,6 +760,7 @@ mod tests { KEYS.alice_secret.clone(), true, true, + SeipdVersion::V2, ) .await?;