refactor: Remove mostly-unused SignUnencrypted option

This commit is contained in:
Hocuri
2026-04-26 13:34:02 +02:00
parent aa1f129a48
commit 36cb7b49fc
8 changed files with 11 additions and 200 deletions

View File

@@ -407,9 +407,6 @@ pub enum Config {
#[strum(props(default = "1"))] #[strum(props(default = "1"))]
SyncMsgs, SyncMsgs,
/// Make all outgoing messages with Autocrypt header "multipart/signed".
SignUnencrypted,
/// Let the core save all events to the database. /// Let the core save all events to the database.
/// This value is used internally to remember the MsgId of the logging xdc /// This value is used internally to remember the MsgId of the logging xdc
#[strum(props(default = "0"))] #[strum(props(default = "0"))]
@@ -710,7 +707,6 @@ impl Context {
| Config::Bot | Config::Bot
| Config::NotifyAboutWrongPw | Config::NotifyAboutWrongPw
| Config::SyncMsgs | Config::SyncMsgs
| Config::SignUnencrypted
| Config::DisableIdle => { | Config::DisableIdle => {
ensure!( ensure!(
matches!(value, None | Some("0") | Some("1")), matches!(value, None | Some("0") | Some("1")),

View File

@@ -991,12 +991,6 @@ impl Context {
.await? .await?
.to_string(), .to_string(),
); );
res.insert(
"sign_unencrypted",
self.get_config_int(Config::SignUnencrypted)
.await?
.to_string(),
);
res.insert( res.insert(
"debug_logging", "debug_logging",
self.get_config_int(Config::DebugLogging).await?.to_string(), self.get_config_int(Config::DebugLogging).await?.to_string(),

View File

@@ -79,16 +79,6 @@ impl EncryptHelper {
Ok(ctext) Ok(ctext)
} }
/// Signs the passed-in `mail` using the private key from `context`.
/// Returns the payload and the signature.
pub async fn sign(self, context: &Context, mail: &MimePart<'static>) -> Result<String> {
let sign_key = load_self_secret_key(context).await?;
let mut buffer = Vec::new();
mail.clone().write_part(&mut buffer)?;
let signature = pgp::pk_calc_signature(buffer, &sign_key)?;
Ok(signature)
}
} }
/// Ensures a private key exists for the configured user. /// Ensures a private key exists for the configured user.

View File

@@ -1227,53 +1227,18 @@ impl MimeFactory {
message.header(header, value) message.header(header, value)
}); });
let message = MimePart::new("multipart/mixed", vec![message]); let message = MimePart::new("multipart/mixed", vec![message]);
let mut message = protected_headers let message = protected_headers
.iter() .iter()
.fold(message, |message, (header, value)| { .fold(message, |message, (header, value)| {
message.header(*header, value.clone()) message.header(*header, value.clone())
}); });
if skip_autocrypt || !context.get_config_bool(Config::SignUnencrypted).await? { // Deduplicate unprotected headers that also are in the protected headers:
// Deduplicate unprotected headers that also are in the protected headers: let protected: HashSet<&str> =
let protected: HashSet<&str> = HashSet::from_iter(protected_headers.iter().map(|(header, _value)| *header));
HashSet::from_iter(protected_headers.iter().map(|(header, _value)| *header)); unprotected_headers.retain(|(header, _value)| !protected.contains(header));
unprotected_headers.retain(|(header, _value)| !protected.contains(header));
message message
} else {
for (h, v) in &mut message.headers {
if h == "Content-Type"
&& let mail_builder::headers::HeaderType::ContentType(ct) = v
{
let mut ct_new = ct.clone();
ct_new = ct_new.attribute("protected-headers", "v1");
if use_std_header_protection {
ct_new = ct_new.attribute("hp", "clear");
}
*ct = ct_new;
break;
}
}
let signature = encrypt_helper.sign(context, &message).await?;
MimePart::new(
"multipart/signed; protocol=\"application/pgp-signature\"; protected",
vec![
message,
MimePart::new(
"application/pgp-signature; name=\"signature.asc\"",
signature,
)
.header(
"Content-Description",
mail_builder::headers::raw::Raw::<'static>::new(
"OpenPGP digital signature",
),
)
.attachment("signature"),
],
)
}
}; };
let MimeFactory { let MimeFactory {

View File

@@ -601,70 +601,6 @@ async fn test_selfavatar_unencrypted() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_unencrypted_signed() {
// create chat with bob, set selfavatar
let t = TestContext::new_alice().await;
t.set_config(Config::SignUnencrypted, Some("1"))
.await
.unwrap();
let chat = t.create_chat_with_contact("bob", "bob@example.org").await;
let file = t.dir.path().join("avatar.png");
let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
tokio::fs::write(&file, bytes).await.unwrap();
t.set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
.await
.unwrap();
// send message to bob: that should get multipart/signed.
// `Subject:` is protected by copying it.
// make sure, `Subject:` stays in the outer header (imf header)
let mut msg = Message::new_text("this is the text!".to_string());
let sent_msg = t.send_msg(chat.id, &mut msg).await;
let mut payload = sent_msg.payload().splitn(4, "\r\n\r\n");
let part = payload.next().unwrap();
assert_eq!(part.match_indices("multipart/signed").count(), 1);
assert_eq!(part.match_indices("From:").count(), 1);
assert_eq!(part.match_indices("Message-ID:").count(), 1);
assert_eq!(part.match_indices("Subject:").count(), 1);
assert_eq!(part.match_indices("Autocrypt:").count(), 1);
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
let part = payload.next().unwrap();
assert_eq!(
part.match_indices("multipart/mixed; protected-headers=\"v1\"")
.count(),
1
);
assert_eq!(part.match_indices("From:").count(), 1);
assert_eq!(part.match_indices("Message-ID:").count(), 0);
assert_eq!(part.match_indices("Subject:").count(), 1);
assert_eq!(part.match_indices("Autocrypt:").count(), 1);
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
let part = payload.next().unwrap();
assert_eq!(part.match_indices("text/plain").count(), 1);
assert_eq!(part.match_indices("From:").count(), 0);
assert_eq!(part.match_indices("Message-ID:").count(), 1);
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
assert_eq!(part.match_indices("Subject:").count(), 0);
let body = payload.next().unwrap();
assert_eq!(body.match_indices("this is the text!").count(), 1);
let bob = TestContext::new_bob().await;
bob.recv_msg(&sent_msg).await;
let alice_id = Contact::lookup_id_by_addr(&bob.ctx, "alice@example.org", Origin::Unknown)
.await
.unwrap()
.unwrap();
let alice_contact = Contact::get_by_id(&bob.ctx, alice_id).await.unwrap();
assert_eq!(alice_contact.is_key_contact(), false);
}
/// Test that removed member address does not go into the `To:` field. /// Test that removed member address does not go into the `To:` field.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_remove_member_bcc() -> Result<()> { async fn test_remove_member_bcc() -> Result<()> {

View File

@@ -305,6 +305,7 @@ impl MimeMessage {
// Parse hidden headers. // Parse hidden headers.
let mimetype = mail.ctype.mimetype.parse::<Mime>()?; let mimetype = mail.ctype.mimetype.parse::<Mime>()?;
let (part, mimetype) = let (part, mimetype) =
// We do not sign unencrypted messages ourselves, but are able to receive them.
if mimetype.type_() == mime::MULTIPART && mimetype.subtype().as_str() == "signed" { if mimetype.type_() == mime::MULTIPART && mimetype.subtype().as_str() == "signed" {
if let Some(part) = mail.subparts.first() { if let Some(part) = mail.subparts.first() {
// We don't remove "subject" from `headers` because currently just signed // We don't remove "subject" from `headers` because currently just signed
@@ -329,7 +330,6 @@ impl MimeMessage {
(&mail, mimetype) (&mail, mimetype)
} }
} else { } else {
// Currently we do not sign unencrypted messages by default.
(&mail, mimetype) (&mail, mimetype)
}; };
if mimetype.type_() == mime::MULTIPART if mimetype.type_() == mime::MULTIPART

View File

@@ -2041,35 +2041,6 @@ async fn test_multiple_autocrypt_hdrs() -> Result<()> {
Ok(()) Ok(())
} }
/// Tests that timestamp of signed but not encrypted message is protected.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_protected_date() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
alice.set_config(Config::SignUnencrypted, Some("1")).await?;
let alice_chat = alice.create_email_chat(bob).await;
let alice_msg_id = chat::send_text_msg(alice, alice_chat.id, "Hello!".to_string()).await?;
let alice_msg = Message::load_from_db(alice, alice_msg_id).await?;
assert_eq!(alice_msg.get_showpadlock(), false);
let mut sent_msg = alice.pop_sent_msg().await;
sent_msg.payload = sent_msg.payload.replacen(
"Date:",
"Date: Wed, 17 Mar 2021 14:30:53 +0100 (CET)\r\nX-Not-Date:",
1,
);
let bob_msg = bob.recv_msg(&sent_msg).await;
assert_eq!(alice_msg.get_text(), bob_msg.get_text());
// Timestamp that the sender has put into the message
// should always be displayed as is on the receiver.
assert_eq!(alice_msg.get_timestamp(), bob_msg.get_timestamp());
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_huge_image_becomes_file() -> Result<()> { async fn test_huge_image_becomes_file() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;

View File

@@ -6,15 +6,15 @@ use std::io::Cursor;
use anyhow::{Context as _, Result, ensure}; use anyhow::{Context as _, Result, ensure};
use deltachat_contact_tools::{EmailAddress, may_be_valid_addr}; use deltachat_contact_tools::{EmailAddress, may_be_valid_addr};
use pgp::composed::{ use pgp::composed::{
ArmorOptions, Deserializable, DetachedSignature, EncryptionCaps, KeyType as PgpKeyType, Deserializable, DetachedSignature, EncryptionCaps, KeyType as PgpKeyType, MessageBuilder,
MessageBuilder, SecretKeyParamsBuilder, SignedKeyDetails, SignedPublicKey, SignedPublicSubKey, SecretKeyParamsBuilder, SignedKeyDetails, SignedPublicKey, SignedPublicSubKey, SignedSecretKey,
SignedSecretKey, SubkeyParamsBuilder, SubpacketConfig, SubkeyParamsBuilder, SubpacketConfig,
}; };
use pgp::crypto::aead::{AeadAlgorithm, ChunkSize}; use pgp::crypto::aead::{AeadAlgorithm, ChunkSize};
use pgp::crypto::ecc_curve::ECCCurve; use pgp::crypto::ecc_curve::ECCCurve;
use pgp::crypto::hash::HashAlgorithm; use pgp::crypto::hash::HashAlgorithm;
use pgp::crypto::sym::SymmetricKeyAlgorithm; use pgp::crypto::sym::SymmetricKeyAlgorithm;
use pgp::packet::{Signature, SignatureConfig, SignatureType, Subpacket, SubpacketData}; use pgp::packet::{Signature, Subpacket, SubpacketData};
use pgp::types::{ use pgp::types::{
CompressionAlgorithm, Imprint, KeyDetails, KeyVersion, Password, SignedUser, SigningKey as _, CompressionAlgorithm, Imprint, KeyDetails, KeyVersion, Password, SignedUser, SigningKey as _,
StringToKey, StringToKey,
@@ -202,47 +202,6 @@ pub async fn pk_encrypt(
.await? .await?
} }
/// Produces a detached signature for `plain` text using `private_key_for_signing`.
pub fn pk_calc_signature(
plain: Vec<u8>,
private_key_for_signing: &SignedSecretKey,
) -> Result<String> {
let rng = thread_rng();
let mut config = SignatureConfig::from_key(
rng,
&private_key_for_signing.primary_key,
SignatureType::Binary,
)?;
config.hashed_subpackets = vec![
Subpacket::regular(SubpacketData::IssuerFingerprint(
private_key_for_signing.fingerprint(),
))?,
Subpacket::critical(SubpacketData::SignatureCreationTime(
pgp::types::Timestamp::now(),
))?,
];
config.unhashed_subpackets = vec![];
if private_key_for_signing.version() <= KeyVersion::V4 {
config
.unhashed_subpackets
.push(Subpacket::regular(SubpacketData::IssuerKeyId(
private_key_for_signing.legacy_key_id(),
))?);
}
let signature = config.sign(
&private_key_for_signing.primary_key,
&Password::empty(),
plain.as_slice(),
)?;
let sig = DetachedSignature::new(signature);
Ok(sig.to_armored_string(ArmorOptions::default())?)
}
/// Returns fingerprints /// Returns fingerprints
/// of all keys from the `public_keys_for_validation` keyring that /// of all keys from the `public_keys_for_validation` keyring that
/// have valid signatures in `msg` and corresponding intended recipient fingerprints /// have valid signatures in `msg` and corresponding intended recipient fingerprints