mirror of
https://github.com/chatmail/core.git
synced 2026-05-20 07:16:31 +03:00
refactor: Remove mostly-unused SignUnencrypted option
This commit is contained in:
@@ -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")),
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
10
src/e2ee.rs
10
src/e2ee.rs
@@ -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.
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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<()> {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
49
src/pgp.rs
49
src/pgp.rs
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user