mirror of
https://github.com/chatmail/core.git
synced 2026-05-19 06:46:32 +03:00
Prefer encryption for the peer if the message is encrypted or signed with the known key (#3844)
Note that if the message is encrypted, we don't check whether it's signed with an attached key currently, otherwise a massive refactoring of the code is needed because for encrypted messages a signature is checked and discarded first now.
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# This directory contains email messages verbatim, and changing CRLF to
|
# This directory contains email messages verbatim, and changing CRLF to
|
||||||
# LF will corrupt them.
|
# LF will corrupt them.
|
||||||
test-data/* text=false
|
test-data/** text=false
|
||||||
|
|
||||||
# binary files should be detected by git, however, to be sure, you can add them here explicitly
|
# binary files should be detected by git, however, to be sure, you can add them here explicitly
|
||||||
*.png binary
|
*.png binary
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
- Fix cargo clippy and doc errors after Rust update to 1.66 #3850
|
- Fix cargo clippy and doc errors after Rust update to 1.66 #3850
|
||||||
- Don't send GroupNameChanged message if the group name doesn't change in terms of
|
- Don't send GroupNameChanged message if the group name doesn't change in terms of
|
||||||
improve_single_line_input() #3852
|
improve_single_line_input() #3852
|
||||||
|
- Prefer encryption for the peer if the message is encrypted or signed with the known key #3849
|
||||||
|
|
||||||
|
|
||||||
## 1.103.0
|
## 1.103.0
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ pub async fn try_decrypt(
|
|||||||
decryption_info: &DecryptionInfo,
|
decryption_info: &DecryptionInfo,
|
||||||
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
||||||
// Possibly perform decryption
|
// Possibly perform decryption
|
||||||
let public_keyring_for_validate = keyring_from_peerstate(&decryption_info.peerstate);
|
let public_keyring_for_validate = keyring_from_peerstate(decryption_info.peerstate.as_ref());
|
||||||
|
|
||||||
let encrypted_data_part = match get_autocrypt_mime(mail)
|
let encrypted_data_part = match get_autocrypt_mime(mail)
|
||||||
.or_else(|| get_mixed_up_mime(mail))
|
.or_else(|| get_mixed_up_mime(mail))
|
||||||
@@ -251,7 +251,7 @@ fn has_decrypted_pgp_armor(input: &[u8]) -> bool {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if the part is not a Multipart/Signed part, otherwise retruns the set of key
|
/// Returns `None` if the part is not a Multipart/Signed part, otherwise retruns the set of key
|
||||||
/// fingerprints for which there is a valid signature.
|
/// fingerprints for which there is a valid signature.
|
||||||
fn validate_detached_signature(
|
pub(crate) fn validate_detached_signature(
|
||||||
mail: &ParsedMail<'_>,
|
mail: &ParsedMail<'_>,
|
||||||
public_keyring_for_validate: &Keyring<SignedPublicKey>,
|
public_keyring_for_validate: &Keyring<SignedPublicKey>,
|
||||||
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
||||||
@@ -272,9 +272,9 @@ fn validate_detached_signature(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keyring_from_peerstate(peerstate: &Option<Peerstate>) -> Keyring<SignedPublicKey> {
|
pub(crate) fn keyring_from_peerstate(peerstate: Option<&Peerstate>) -> Keyring<SignedPublicKey> {
|
||||||
let mut public_keyring_for_validate: Keyring<SignedPublicKey> = Keyring::new();
|
let mut public_keyring_for_validate: Keyring<SignedPublicKey> = Keyring::new();
|
||||||
if let Some(ref peerstate) = *peerstate {
|
if let Some(peerstate) = peerstate {
|
||||||
if let Some(key) = &peerstate.public_key {
|
if let Some(key) = &peerstate.public_key {
|
||||||
public_keyring_for_validate.add(key.clone());
|
public_keyring_for_validate.add(key.clone());
|
||||||
} else if let Some(key) = &peerstate.gossip_key {
|
} else if let Some(key) = &peerstate.gossip_key {
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ use crate::blob::BlobObject;
|
|||||||
use crate::constants::{DC_DESIRED_TEXT_LINES, DC_DESIRED_TEXT_LINE_LEN};
|
use crate::constants::{DC_DESIRED_TEXT_LINES, DC_DESIRED_TEXT_LINE_LEN};
|
||||||
use crate::contact::{addr_cmp, addr_normalize, ContactId};
|
use crate::contact::{addr_cmp, addr_normalize, ContactId};
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::decrypt::{prepare_decryption, try_decrypt, DecryptionInfo};
|
use crate::decrypt::{
|
||||||
|
keyring_from_peerstate, prepare_decryption, try_decrypt, validate_detached_signature,
|
||||||
|
DecryptionInfo,
|
||||||
|
};
|
||||||
use crate::dehtml::dehtml;
|
use crate::dehtml::dehtml;
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
use crate::format_flowed::unformat_flowed;
|
use crate::format_flowed::unformat_flowed;
|
||||||
@@ -64,7 +67,8 @@ pub struct MimeMessage {
|
|||||||
/// If a message is not encrypted or the signature is not valid,
|
/// If a message is not encrypted or the signature is not valid,
|
||||||
/// this set is empty.
|
/// this set is empty.
|
||||||
pub signatures: HashSet<Fingerprint>,
|
pub signatures: HashSet<Fingerprint>,
|
||||||
|
/// Whether the message is encrypted in a domestic (not Autocrypt) sense
|
||||||
|
pub encrypted: bool,
|
||||||
/// The set of mail recipient addresses for which gossip headers were applied, regardless of
|
/// The set of mail recipient addresses for which gossip headers were applied, regardless of
|
||||||
/// whether they modified any peerstates.
|
/// whether they modified any peerstates.
|
||||||
pub gossiped_addr: HashSet<String>,
|
pub gossiped_addr: HashSet<String>,
|
||||||
@@ -232,91 +236,95 @@ impl MimeMessage {
|
|||||||
hop_info += &decryption_info.dkim_results.to_string();
|
hop_info += &decryption_info.dkim_results.to_string();
|
||||||
|
|
||||||
// `signatures` is non-empty exactly if the message was encrypted and correctly signed.
|
// `signatures` is non-empty exactly if the message was encrypted and correctly signed.
|
||||||
let (mail, signatures, warn_empty_signature) =
|
let (mail, signatures, encrypted) = match try_decrypt(context, &mail, &decryption_info)
|
||||||
match try_decrypt(context, &mail, &decryption_info).await {
|
.await
|
||||||
Ok(Some((raw, signatures))) => {
|
{
|
||||||
// Encrypted, but maybe unsigned message. Only if
|
Ok(Some((raw, signatures))) => {
|
||||||
// `signatures` set is non-empty, it is a valid
|
// Encrypted, but maybe unsigned message. Only if
|
||||||
// autocrypt message.
|
// `signatures` set is non-empty, it is a valid
|
||||||
|
// autocrypt message.
|
||||||
|
|
||||||
mail_raw = raw;
|
mail_raw = raw;
|
||||||
let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
|
let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
|
||||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||||
info!(context, "decrypted message mime-body:");
|
info!(context, "decrypted message mime-body:");
|
||||||
println!("{}", String::from_utf8_lossy(&mail_raw));
|
println!("{}", String::from_utf8_lossy(&mail_raw));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle any gossip headers if the mail was encrypted. See section
|
// Handle any gossip headers if the mail was encrypted. See section
|
||||||
// "3.6 Key Gossip" of <https://autocrypt.org/autocrypt-spec-1.1.0.pdf>
|
// "3.6 Key Gossip" of <https://autocrypt.org/autocrypt-spec-1.1.0.pdf>
|
||||||
// but only if the mail was correctly signed:
|
// but only if the mail was correctly signed:
|
||||||
if !signatures.is_empty() {
|
if !signatures.is_empty() {
|
||||||
let gossip_headers =
|
let gossip_headers = decrypted_mail.headers.get_all_values("Autocrypt-Gossip");
|
||||||
decrypted_mail.headers.get_all_values("Autocrypt-Gossip");
|
gossiped_addr = update_gossip_peerstates(
|
||||||
gossiped_addr =
|
|
||||||
update_gossip_peerstates(context, message_time, &mail, gossip_headers)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let known protected headers from the decrypted
|
|
||||||
// part override the unencrypted top-level
|
|
||||||
|
|
||||||
// Signature was checked for original From, so we
|
|
||||||
// do not allow overriding it.
|
|
||||||
let mut signed_from = None;
|
|
||||||
|
|
||||||
// We do not want to allow unencrypted subject in encrypted emails because the user might falsely think that the subject is safe.
|
|
||||||
// See <https://github.com/deltachat/deltachat-core-rust/issues/1790>.
|
|
||||||
headers.remove("subject");
|
|
||||||
|
|
||||||
MimeMessage::merge_headers(
|
|
||||||
context,
|
context,
|
||||||
&mut headers,
|
message_time,
|
||||||
&mut recipients,
|
&from.addr,
|
||||||
&mut signed_from,
|
&mail,
|
||||||
&mut list_post,
|
gossip_headers,
|
||||||
&mut chat_disposition_notification_to,
|
)
|
||||||
&decrypted_mail.headers,
|
.await?;
|
||||||
);
|
}
|
||||||
if let Some(signed_from) = signed_from {
|
|
||||||
if addr_cmp(&signed_from.addr, &from.addr) {
|
|
||||||
from_is_signed = true;
|
|
||||||
} else {
|
|
||||||
// There is a From: header in the encrypted &
|
|
||||||
// signed part, but it doesn't match the outer one.
|
|
||||||
// This _might_ be because the sender's mail server
|
|
||||||
// replaced the sending address, e.g. in a mailing list.
|
|
||||||
// Or it's because someone is doing some replay attack
|
|
||||||
// - OTOH, I can't come up with an attack scenario
|
|
||||||
// where this would be useful.
|
|
||||||
warn!(
|
|
||||||
context,
|
|
||||||
"From header in signed part does't match the outer one"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(decrypted_mail), signatures, true)
|
// let known protected headers from the decrypted
|
||||||
}
|
// part override the unencrypted top-level
|
||||||
Ok(None) => {
|
|
||||||
// Message was not encrypted.
|
// Signature was checked for original From, so we
|
||||||
// If it is not a read receipt, degrade encryption.
|
// do not allow overriding it.
|
||||||
if let Some(peerstate) = &mut decryption_info.peerstate {
|
let mut signed_from = None;
|
||||||
if message_time > peerstate.last_seen_autocrypt
|
|
||||||
&& mail.ctype.mimetype != "multipart/report"
|
// We do not want to allow unencrypted subject in encrypted emails because the user might falsely think that the subject is safe.
|
||||||
// Disallowing keychanges is disabled for now:
|
// See <https://github.com/deltachat/deltachat-core-rust/issues/1790>.
|
||||||
// && decryption_info.dkim_results.allow_keychange
|
headers.remove("subject");
|
||||||
{
|
|
||||||
peerstate.degrade_encryption(message_time);
|
MimeMessage::merge_headers(
|
||||||
peerstate.save_to_db(&context.sql).await?;
|
context,
|
||||||
}
|
&mut headers,
|
||||||
|
&mut recipients,
|
||||||
|
&mut signed_from,
|
||||||
|
&mut list_post,
|
||||||
|
&mut chat_disposition_notification_to,
|
||||||
|
&decrypted_mail.headers,
|
||||||
|
);
|
||||||
|
if let Some(signed_from) = signed_from {
|
||||||
|
if addr_cmp(&signed_from.addr, &from.addr) {
|
||||||
|
from_is_signed = true;
|
||||||
|
} else {
|
||||||
|
// There is a From: header in the encrypted &
|
||||||
|
// signed part, but it doesn't match the outer one.
|
||||||
|
// This _might_ be because the sender's mail server
|
||||||
|
// replaced the sending address, e.g. in a mailing list.
|
||||||
|
// Or it's because someone is doing some replay attack
|
||||||
|
// - OTOH, I can't come up with an attack scenario
|
||||||
|
// where this would be useful.
|
||||||
|
warn!(
|
||||||
|
context,
|
||||||
|
"From header in signed part does't match the outer one",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(Ok(mail), HashSet::new(), false)
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
warn!(context, "decryption failed: {}", err);
|
(Ok(decrypted_mail), signatures, true)
|
||||||
(Err(err), HashSet::new(), true)
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
// Message was not encrypted.
|
||||||
|
// If it is not a read receipt, degrade encryption.
|
||||||
|
if let Some(peerstate) = &mut decryption_info.peerstate {
|
||||||
|
if message_time > peerstate.last_seen_autocrypt
|
||||||
|
&& mail.ctype.mimetype != "multipart/report"
|
||||||
|
// Disallowing keychanges is disabled for now:
|
||||||
|
// && decryption_info.dkim_results.allow_keychange
|
||||||
|
{
|
||||||
|
peerstate.degrade_encryption(message_time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
(Ok(mail), HashSet::new(), false)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(context, "decryption failed: {}", err);
|
||||||
|
(Err(err), HashSet::new(), true)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut parser = MimeMessage {
|
let mut parser = MimeMessage {
|
||||||
parts: Vec::new(),
|
parts: Vec::new(),
|
||||||
@@ -331,6 +339,7 @@ impl MimeMessage {
|
|||||||
|
|
||||||
// only non-empty if it was a valid autocrypt message
|
// only non-empty if it was a valid autocrypt message
|
||||||
signatures,
|
signatures,
|
||||||
|
encrypted,
|
||||||
gossiped_addr,
|
gossiped_addr,
|
||||||
is_forwarded: false,
|
is_forwarded: false,
|
||||||
mdn_reports: Vec::new(),
|
mdn_reports: Vec::new(),
|
||||||
@@ -385,7 +394,7 @@ impl MimeMessage {
|
|||||||
// part.error = Some("Seems like DKIM failed, this either is an attack or (more likely) a bug in Authentication-Results checking. Please tell us about this at https://support.delta.chat.".to_string());
|
// part.error = Some("Seems like DKIM failed, this either is an attack or (more likely) a bug in Authentication-Results checking. Please tell us about this at https://support.delta.chat.".to_string());
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
if warn_empty_signature && parser.signatures.is_empty() {
|
if encrypted && parser.signatures.is_empty() {
|
||||||
for part in parser.parts.iter_mut() {
|
for part in parser.parts.iter_mut() {
|
||||||
part.error = Some("No valid signature".to_string());
|
part.error = Some("No valid signature".to_string());
|
||||||
}
|
}
|
||||||
@@ -400,6 +409,13 @@ impl MimeMessage {
|
|||||||
peerstate
|
peerstate
|
||||||
.handle_fingerprint_change(context, message_time)
|
.handle_fingerprint_change(context, message_time)
|
||||||
.await?;
|
.await?;
|
||||||
|
// When peerstate is set to Mutual, it's saved immediately to not lose that fact in case
|
||||||
|
// of an error. Otherwise we don't save peerstate until get here to reduce the number of
|
||||||
|
// calls to save_to_db() and not to degrade encryption if a mail wasn't parsed
|
||||||
|
// successfully.
|
||||||
|
if peerstate.prefer_encrypt != EncryptPreference::Mutual {
|
||||||
|
peerstate.save_to_db(&context.sql).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(parser)
|
Ok(parser)
|
||||||
@@ -852,6 +868,26 @@ impl MimeMessage {
|
|||||||
.parse_mime_recursive(context, first, is_related)
|
.parse_mime_recursive(context, first, is_related)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
if let Some(peerstate) = &mut self.decryption_info.peerstate {
|
||||||
|
let keyring = keyring_from_peerstate(Some(peerstate));
|
||||||
|
match validate_detached_signature(mail, &keyring) {
|
||||||
|
Ok(Some((_, fprints))) => {
|
||||||
|
if fprints.is_empty() {
|
||||||
|
warn!(context, "signed message is not signed with a known key");
|
||||||
|
} else if peerstate.prefer_encrypt != EncryptPreference::Mutual {
|
||||||
|
info!(
|
||||||
|
context,
|
||||||
|
"message is signed with the known key, setting \
|
||||||
|
prefer-encrypt=mutual for '{}'",
|
||||||
|
peerstate.addr,
|
||||||
|
);
|
||||||
|
Self::upgrade_to_mutual_encryption(context, peerstate).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => warn!(context, "not a 'multipart/signed' part??"),
|
||||||
|
Err(err) => warn!(context, "signed message validation failed: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(mime::MULTIPART, "report") => {
|
(mime::MULTIPART, "report") => {
|
||||||
/* RFC 6522: the first part is for humans, the second for machines */
|
/* RFC 6522: the first part is for humans, the second for machines */
|
||||||
@@ -929,6 +965,17 @@ impl MimeMessage {
|
|||||||
Ok(any_part_added)
|
Ok(any_part_added)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn upgrade_to_mutual_encryption(
|
||||||
|
context: &Context,
|
||||||
|
peerstate: &mut Peerstate,
|
||||||
|
) -> Result<()> {
|
||||||
|
if peerstate.public_key.is_none() {
|
||||||
|
peerstate.public_key = peerstate.gossip_key.take();
|
||||||
|
}
|
||||||
|
peerstate.prefer_encrypt = EncryptPreference::Mutual;
|
||||||
|
peerstate.save_to_db(&context.sql).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if any part was added, false otherwise.
|
/// Returns true if any part was added, false otherwise.
|
||||||
async fn add_single_part_if_known(
|
async fn add_single_part_if_known(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -1106,7 +1153,13 @@ impl MimeMessage {
|
|||||||
if peerstate.prefer_encrypt != EncryptPreference::Mutual
|
if peerstate.prefer_encrypt != EncryptPreference::Mutual
|
||||||
&& mime_type.type_() == mime::APPLICATION
|
&& mime_type.type_() == mime::APPLICATION
|
||||||
&& mime_type.subtype().as_str() == "pgp-keys"
|
&& mime_type.subtype().as_str() == "pgp-keys"
|
||||||
&& Self::try_set_peer_key_from_file_part(context, peerstate, decoded_data).await?
|
&& Self::try_set_peer_key_from_file_part(
|
||||||
|
context,
|
||||||
|
peerstate,
|
||||||
|
decoded_data,
|
||||||
|
self.encrypted,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
{
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -1196,6 +1249,7 @@ impl MimeMessage {
|
|||||||
context: &Context,
|
context: &Context,
|
||||||
peerstate: &mut Peerstate,
|
peerstate: &mut Peerstate,
|
||||||
decoded_data: &[u8],
|
decoded_data: &[u8],
|
||||||
|
mail_is_encrypted: bool,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let key = match str::from_utf8(decoded_data) {
|
let key = match str::from_utf8(decoded_data) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -1240,13 +1294,23 @@ impl MimeMessage {
|
|||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!(
|
|
||||||
context,
|
|
||||||
"will use attached PGP key for peer '{}' with mutual encryption", peerstate.addr,
|
|
||||||
);
|
|
||||||
peerstate.public_key = Some(key);
|
peerstate.public_key = Some(key);
|
||||||
peerstate.prefer_encrypt = EncryptPreference::Mutual;
|
if mail_is_encrypted {
|
||||||
peerstate.save_to_db(&context.sql).await?;
|
info!(
|
||||||
|
context,
|
||||||
|
"using attached PGP key for peer '{}' with prefer-encrypt=mutual as the mail is \
|
||||||
|
encrypted",
|
||||||
|
peerstate.addr,
|
||||||
|
);
|
||||||
|
peerstate.prefer_encrypt = EncryptPreference::Mutual;
|
||||||
|
peerstate.save_to_db(&context.sql).await?;
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
context,
|
||||||
|
"using attached PGP key for peer '{}'", peerstate.addr,
|
||||||
|
);
|
||||||
|
peerstate.prefer_encrypt = EncryptPreference::NoPreference;
|
||||||
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1624,6 +1688,7 @@ impl MimeMessage {
|
|||||||
async fn update_gossip_peerstates(
|
async fn update_gossip_peerstates(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
message_time: i64,
|
message_time: i64,
|
||||||
|
from: &str,
|
||||||
mail: &mailparse::ParsedMail<'_>,
|
mail: &mailparse::ParsedMail<'_>,
|
||||||
gossip_headers: Vec<String>,
|
gossip_headers: Vec<String>,
|
||||||
) -> Result<HashSet<String>> {
|
) -> Result<HashSet<String>> {
|
||||||
@@ -1641,7 +1706,7 @@ async fn update_gossip_peerstates(
|
|||||||
|
|
||||||
if !get_recipients(&mail.headers)
|
if !get_recipients(&mail.headers)
|
||||||
.iter()
|
.iter()
|
||||||
.any(|info| info.addr == header.addr.to_lowercase())
|
.any(|info| addr_cmp(&info.addr, &header.addr))
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
context,
|
context,
|
||||||
@@ -1649,6 +1714,14 @@ async fn update_gossip_peerstates(
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if addr_cmp(from, &header.addr) {
|
||||||
|
// Non-standard, but anyway we can't update the cached peerstate here.
|
||||||
|
warn!(
|
||||||
|
context,
|
||||||
|
"Ignoring gossiped \"{}\" as it equals the From address", &header.addr,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let peerstate;
|
let peerstate;
|
||||||
if let Some(mut p) = Peerstate::from_addr(context, &header.addr).await? {
|
if let Some(mut p) = Peerstate::from_addr(context, &header.addr).await? {
|
||||||
|
|||||||
@@ -806,7 +806,6 @@ mod tests {
|
|||||||
verified_key_fingerprint: None,
|
verified_key_fingerprint: None,
|
||||||
fingerprint_changed: false,
|
fingerprint_changed: false,
|
||||||
};
|
};
|
||||||
assert_eq!(peerstate.prefer_encrypt, EncryptPreference::NoPreference);
|
|
||||||
|
|
||||||
peerstate.apply_header(&header, 100);
|
peerstate.apply_header(&header, 100);
|
||||||
assert_eq!(peerstate.prefer_encrypt, EncryptPreference::Mutual);
|
assert_eq!(peerstate.prefer_encrypt, EncryptPreference::Mutual);
|
||||||
|
|||||||
@@ -5359,7 +5359,13 @@ Reply from different address
|
|||||||
|
|
||||||
let raw = include_bytes!("../test-data/message/thunderbird_with_autocrypt_unencrypted.eml");
|
let raw = include_bytes!("../test-data/message/thunderbird_with_autocrypt_unencrypted.eml");
|
||||||
receive_imf(&t, raw, false).await?;
|
receive_imf(&t, raw, false).await?;
|
||||||
|
let peerstate = Peerstate::from_addr(&t, "alice@example.org")
|
||||||
|
.await?
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(peerstate.prefer_encrypt, EncryptPreference::Mutual);
|
||||||
|
|
||||||
|
let raw = include_bytes!("../test-data/message/thunderbird_signed_unencrypted.eml");
|
||||||
|
receive_imf(&t, raw, false).await?;
|
||||||
let peerstate = Peerstate::from_addr(&t, "alice@example.org")
|
let peerstate = Peerstate::from_addr(&t, "alice@example.org")
|
||||||
.await?
|
.await?
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
56
test-data/message/thunderbird_signed_unencrypted.eml
Normal file
56
test-data/message/thunderbird_signed_unencrypted.eml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
From - Thu, 15 Dec 2022 14:45:17 GMT
|
||||||
|
X-Mozilla-Status: 0801
|
||||||
|
X-Mozilla-Status2: 00000000
|
||||||
|
Message-ID: <db254c95-c23e-2ad5-14db-11ad1f374dbf@example.org>
|
||||||
|
Date: Thu, 15 Dec 2022 11:45:16 -0300
|
||||||
|
MIME-Version: 1.0
|
||||||
|
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
||||||
|
Thunderbird/102.5.1
|
||||||
|
Content-Language: en-US
|
||||||
|
To: bob@example.net
|
||||||
|
From: Alice <alice@example.org>
|
||||||
|
Subject: test message 15:53
|
||||||
|
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0;
|
||||||
|
attachmentreminder=0; deliveryformat=0
|
||||||
|
X-Identity-Key: id3
|
||||||
|
Fcc: imap://alice%40example.org@in.example.org/Sent
|
||||||
|
Content-Type: multipart/signed; micalg=pgp-sha256;
|
||||||
|
protocol="application/pgp-signature";
|
||||||
|
boundary="------------iX39J1p7DOgblwacjo0e7jX7"
|
||||||
|
|
||||||
|
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
|
||||||
|
--------------iX39J1p7DOgblwacjo0e7jX7
|
||||||
|
Content-Type: multipart/mixed; boundary="------------WD4DG7TcI4p4lbzyM4toRaDw";
|
||||||
|
protected-headers="v1"
|
||||||
|
From: Alice <alice@example.org>
|
||||||
|
To: bob@example.net
|
||||||
|
Message-ID: <db254c95-c23e-2ad5-14db-11ad1f374dbf@example.org>
|
||||||
|
Subject: test message 15:53
|
||||||
|
|
||||||
|
--------------WD4DG7TcI4p4lbzyM4toRaDw
|
||||||
|
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||||
|
Content-Transfer-Encoding: base64
|
||||||
|
|
||||||
|
DQo=
|
||||||
|
|
||||||
|
--------------WD4DG7TcI4p4lbzyM4toRaDw--
|
||||||
|
|
||||||
|
--------------iX39J1p7DOgblwacjo0e7jX7
|
||||||
|
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
|
||||||
|
Content-Description: OpenPGP digital signature
|
||||||
|
Content-Disposition: attachment; filename="OpenPGP_signature"
|
||||||
|
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
wsD5BAABCAAjFiEEFKs/ZfwnS721+naMJfAHJFnkeuIFAmObMvwFAwAAAAAACgkQJfAHJFnkeuLM
|
||||||
|
TgwAnAADX93HE5vXmuBcAbRN2HKIwMzBtRtUF4FNPKchffUvvhSNpHkW2jW7A4hOHNgVSDQdqIUn
|
||||||
|
+62NgkaKrT1bZqozOZNHXMECHtKBwXWTkIAVqcBdvscCztVIgGby56OPnzZ5y09BsRaqqE5AhDgN
|
||||||
|
wGCLa6ipu5FYSF6+KzdO0GIPMY5aGRgVhtl4N01v4S3+r/Yu60MkN87nd15Eaqsrs60P9RmKJTt4
|
||||||
|
hDie35kKvHnPzLNs8+xLfqPuO/P7ZbPQgkgCwMAMsMDRUYOv+k5c/bL3PKiOENuDpQ7dkKJ2OzSn
|
||||||
|
nTcg8qhDf17vWe26C/QBhFiGEsrHNBQ1KW5by+cqjIUBJgXElFnPl35S5L3fn6JHZLcz6q+wQuJu
|
||||||
|
vGT1mJuP//jLFkMHSexukFIVXzn41rWPLd05rBqMgwRcOHMIyzE9zaO1aa8MF2TirPaZ5lH9rx/y
|
||||||
|
9DCU/d2sqbbYt8TGqj4hM3pqg5K22eq4KT1W7y8+28I5QfjZumLLrHBdYTnR
|
||||||
|
=6JTB
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
|
|
||||||
|
--------------iX39J1p7DOgblwacjo0e7jX7--
|
||||||
@@ -1,142 +1,142 @@
|
|||||||
From - Fri, 09 Dec 2022 13:16:11 GMT
|
From - Wed, 14 Dec 2022 18:53:03 GMT
|
||||||
X-Mozilla-Status: 0801
|
X-Mozilla-Status: 0801
|
||||||
X-Mozilla-Status2: 00000000
|
X-Mozilla-Status2: 00000000
|
||||||
Message-ID: <0c8e3ffc-99ae-eb68-15b5-15c4d85a5c12@example.org>
|
Message-ID: <87d75c7e-0f52-1335-e437-af605c09f954@example.org>
|
||||||
Date: Fri, 9 Dec 2022 10:16:11 -0300
|
Date: Wed, 14 Dec 2022 15:53:03 -0300
|
||||||
MIME-Version: 1.0
|
MIME-Version: 1.0
|
||||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
||||||
Thunderbird/102.5.1
|
Thunderbird/102.5.1
|
||||||
Content-Language: en-US
|
Content-Language: en-US
|
||||||
To: bob@example.net
|
To: bob@example.net
|
||||||
From: Alice <alice@example.org>
|
From: Alice <alice@example.org>
|
||||||
Subject: test message 10:15
|
Subject: test message 15:52
|
||||||
Autocrypt: addr=alice@example.org; keydata=
|
Autocrypt: addr=alice@example.org; keydata=
|
||||||
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
|
||||||
rhEBN10sGyg93A==
|
/qMLjKwBpKEd/w==
|
||||||
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0;
|
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0;
|
||||||
attachmentreminder=0; deliveryformat=0
|
attachmentreminder=0; deliveryformat=0
|
||||||
X-Identity-Key: id3
|
X-Identity-Key: id3
|
||||||
Fcc: imap://alice%40example.org@in.example.org/Sent
|
Fcc: imap://alice%40example.org@in.example.org/Sent
|
||||||
Content-Type: multipart/signed; micalg=pgp-sha256;
|
Content-Type: multipart/signed; micalg=pgp-sha256;
|
||||||
protocol="application/pgp-signature";
|
protocol="application/pgp-signature";
|
||||||
boundary="------------FFBOG29BVxcOkoFV1hnc0RaY"
|
boundary="------------x6XEHrf0vHmVgEo6f9bMGGUy"
|
||||||
|
|
||||||
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
|
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
|
||||||
--------------FFBOG29BVxcOkoFV1hnc0RaY
|
--------------x6XEHrf0vHmVgEo6f9bMGGUy
|
||||||
Content-Type: multipart/mixed; boundary="------------4cwiD0i5NnTXNSfPNpFwrv6V";
|
Content-Type: multipart/mixed; boundary="------------pePWGfS6inyAJsaJRFnx5r9s";
|
||||||
protected-headers="v1"
|
protected-headers="v1"
|
||||||
From: Alice <alice@example.org>
|
From: Alice <alice@example.org>
|
||||||
To: bob@example.net
|
To: bob@example.net
|
||||||
Message-ID: <0c8e3ffc-99ae-eb68-15b5-15c4d85a5c12@example.org>
|
Message-ID: <87d75c7e-0f52-1335-e437-af605c09f954@example.org>
|
||||||
Subject: test message 10:15
|
Subject: test message 15:52
|
||||||
|
|
||||||
--------------4cwiD0i5NnTXNSfPNpFwrv6V
|
--------------pePWGfS6inyAJsaJRFnx5r9s
|
||||||
Content-Type: multipart/mixed; boundary="------------fbNEFvfS22YOKnkTd1oAl0ak"
|
Content-Type: multipart/mixed; boundary="------------bG3L0s709hFHGhT5ybFZLKLf"
|
||||||
|
|
||||||
--------------fbNEFvfS22YOKnkTd1oAl0ak
|
--------------bG3L0s709hFHGhT5ybFZLKLf
|
||||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||||
Content-Transfer-Encoding: base64
|
Content-Transfer-Encoding: base64
|
||||||
|
|
||||||
MTIzDQoNCg==
|
DQo=
|
||||||
--------------fbNEFvfS22YOKnkTd1oAl0ak
|
--------------bG3L0s709hFHGhT5ybFZLKLf
|
||||||
Content-Type: application/pgp-keys; name="OpenPGP_0xE0D506661947F47C.asc"
|
Content-Type: application/pgp-keys; name="OpenPGP_0x25F0072459E47AE2.asc"
|
||||||
Content-Disposition: attachment; filename="OpenPGP_0xE0D506661947F47C.asc"
|
Content-Disposition: attachment; filename="OpenPGP_0x25F0072459E47AE2.asc"
|
||||||
Content-Description: OpenPGP public key
|
Content-Description: OpenPGP public key
|
||||||
Content-Transfer-Encoding: quoted-printable
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
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
|
||||||
rhEBN10sGyg93A=3D=3D
|
/qMLjKwBpKEd/w=3D=3D
|
||||||
=3DDPMe
|
=3DE1VA
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
--------------fbNEFvfS22YOKnkTd1oAl0ak--
|
--------------bG3L0s709hFHGhT5ybFZLKLf--
|
||||||
|
|
||||||
--------------4cwiD0i5NnTXNSfPNpFwrv6V--
|
--------------pePWGfS6inyAJsaJRFnx5r9s--
|
||||||
|
|
||||||
--------------FFBOG29BVxcOkoFV1hnc0RaY
|
--------------x6XEHrf0vHmVgEo6f9bMGGUy
|
||||||
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
|
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
|
||||||
Content-Description: OpenPGP digital signature
|
Content-Description: OpenPGP digital signature
|
||||||
Content-Disposition: attachment; filename="OpenPGP_signature"
|
Content-Disposition: attachment; filename="OpenPGP_signature"
|
||||||
|
|
||||||
-----BEGIN PGP SIGNATURE-----
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
wsD5BAABCAAjFiEEvtjufiR1TFlamixB4NUGZhlH9HwFAmOTNRsFAwAAAAAACgkQ4NUGZhlH9Hzw
|
wsD5BAABCAAjFiEEFKs/ZfwnS721+naMJfAHJFnkeuIFAmOaG48FAwAAAAAACgkQJfAHJFnkeuJx
|
||||||
Iwv/dNC7LDvRGmZ71IaivkUkSTbpGgg0gnCNOuf+B8OxUBQlWPkBmLxyrXbkxsTghFogDsVQeZQQ
|
1Av/SkGIP18ql7cImI4/t49MvZdIWNuqyKuHZr+7hCPDq0i3muKuy04e8AsGvhHRS8/aSSFkkCgf
|
||||||
DJ182KMgeC//rUN5DPJNrh95YZnav0nUpzW1mkFZjK+PdhbfdXKoXhJIqcw/7lpy/povRYZ20Igg
|
OM5JYwHjOVj7DLTGSfbGM9GGpbu4fP6wa+rCm/WHgRr2H/T4ggy6jNv4rBOMcSNXhpO+J/28Zjoi
|
||||||
tIHLa1NlqPPhSx/o2dsEqWeAtXF4e8T/jQSA5+ZQtVrdcTCNQG6zbqlHZuJ7bF1bwuHPgLgDhJ5k
|
47Dl1eH6B8HyiwqHSPixRqWAf0d8dIp5S7Wf4asb+cFA+rM/7UlZqidJP5ihtHA3A6C1SNRnMBk/
|
||||||
+T2ny80ZtkfLXJl5tQdblAomhBPfEOj+AeLCKsrJFO3WFZOvsuoKMPZpwW1wEh7+QYLABX/lRvqx
|
g3ABR45srubkgXu5QN4PsUalE0N4I00aCgR6WiPggJE2Zf1kslj5M7az3Az+dp0apRilxrlP4J4Y
|
||||||
IxjH1Tc26vttlOrVH13FKGSeWJELun+b2dP1LPiBQ7DOsrrFNs3fp56Nb7Y+exH5ld0jz0kJZTUD
|
KxEzim2X8tJbqvq8G7295NcNDH3YCx3sOT8utXM5NL9how+4c2iylD2m7Oczz3bv0TYDU/4ksWmO
|
||||||
yPqZpJXTsWkFPE7x1tbH/7goiH8f9DbQrvmqQ2fnCjzf3UJR3ZhG/13YAUEdLVkVzwMItEd6yisg
|
zwiMm+47+45UNLWjQ2sGTpui6nXFC7ZuGxzKjUrbpfvkFDIeFgO1XVw812YDoMHKMaEzr8fDhoKD
|
||||||
MP8mlbwm4aDeCiGXO/xhOoBVl6bn1HVSxo7mb0chHVyD1NOfd7qsxem0L/A2
|
LD14JUpwC47XsDlB4ZAMizcCYiK3MvTk1w/5I5ijzJmMiMIpPNxzYxIm82F3
|
||||||
=MOQB
|
=wlaI
|
||||||
-----END PGP SIGNATURE-----
|
-----END PGP SIGNATURE-----
|
||||||
|
|
||||||
--------------FFBOG29BVxcOkoFV1hnc0RaY--
|
--------------x6XEHrf0vHmVgEo6f9bMGGUy--
|
||||||
|
|||||||
Reference in New Issue
Block a user