mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
Add support for "Mixed Up" MIME format
This is an PGP/MIME format produced by Microsoft Exchange and ProtonMail IMAP/SMTP Bridge, described in detail in https://tools.ietf.org/id/draft-dkg-openpgp-pgpmime-message-mangling-00.html This patch adds seamless support for "Mixed Up" Encryption, repairing mangled Autocrypt messages without notifying the user.
This commit is contained in:
100
src/e2ee.rs
100
src/e2ee.rs
@@ -209,29 +209,58 @@ pub async fn try_decrypt(
|
||||
Ok((out_mail, signatures))
|
||||
}
|
||||
|
||||
/// Returns a reference to the encrypted payload and validates the autocrypt structure.
|
||||
fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Result<&'a ParsedMail<'b>> {
|
||||
ensure!(
|
||||
mail.ctype.mimetype == "multipart/encrypted",
|
||||
"Not a multipart/encrypted message: {}",
|
||||
mail.ctype.mimetype
|
||||
);
|
||||
/// Returns a reference to the encrypted payload of a valid PGP/MIME message.
|
||||
///
|
||||
/// Returns `None` if the message is not a valid PGP/MIME message.
|
||||
fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail<'b>> {
|
||||
if mail.ctype.mimetype != "multipart/encrypted" {
|
||||
return None;
|
||||
}
|
||||
if let [first_part, second_part] = &mail.subparts[..] {
|
||||
ensure!(
|
||||
first_part.ctype.mimetype == "application/pgp-encrypted",
|
||||
"Invalid Autocrypt Level 1 version part: {:?}",
|
||||
first_part.ctype,
|
||||
);
|
||||
|
||||
ensure!(
|
||||
second_part.ctype.mimetype == "application/octet-stream",
|
||||
"Invalid Autocrypt Level 1 encrypted part: {:?}",
|
||||
second_part.ctype
|
||||
);
|
||||
|
||||
Ok(second_part)
|
||||
if first_part.ctype.mimetype == "application/pgp-encrypted"
|
||||
&& second_part.ctype.mimetype == "application/octet-stream"
|
||||
{
|
||||
Some(second_part)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
bail!("Invalid Autocrypt Level 1 Mime Parts")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the encrypted payload of a ["Mixed
|
||||
/// Up"][pgpmime-message-mangling] message.
|
||||
///
|
||||
/// According to [RFC 3156] encrypted messages should have
|
||||
/// `multipart/encrypted` MIME type and two parts, but Microsoft
|
||||
/// Exchange and ProtonMail IMAP/SMTP Bridge are known to mangle this
|
||||
/// structure by changing the type to `multipart/mixed` and prepending
|
||||
/// an empty part at the start.
|
||||
///
|
||||
/// ProtonMail IMAP/SMTP Bridge prepends a part literally saying
|
||||
/// "Empty Message", so we don't check its contents at all, checking
|
||||
/// only for `text/plain` type.
|
||||
///
|
||||
/// Returns `None` if the message is not a "Mixed Up" message.
|
||||
///
|
||||
/// [RFC 3156]: https://www.rfc-editor.org/info/rfc3156
|
||||
/// [pgpmime-message-mangling]: https://tools.ietf.org/id/draft-dkg-openpgp-pgpmime-message-mangling-00.html
|
||||
fn get_mixed_up_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail<'b>> {
|
||||
if mail.ctype.mimetype != "multipart/mixed" {
|
||||
return None;
|
||||
}
|
||||
if let [first_part, second_part, third_part] = &mail.subparts[..] {
|
||||
if first_part.ctype.mimetype == "text/plain"
|
||||
&& second_part.ctype.mimetype == "application/pgp-encrypted"
|
||||
&& third_part.ctype.mimetype == "application/octet-stream"
|
||||
{
|
||||
Some(third_part)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,12 +271,12 @@ async fn decrypt_if_autocrypt_message(
|
||||
public_keyring_for_validate: Keyring<SignedPublicKey>,
|
||||
ret_valid_signatures: &mut HashSet<Fingerprint>,
|
||||
) -> Result<Option<Vec<u8>>> {
|
||||
let encrypted_data_part = match get_autocrypt_mime(mail) {
|
||||
Err(_) => {
|
||||
let encrypted_data_part = match get_autocrypt_mime(mail).or_else(|| get_mixed_up_mime(mail)) {
|
||||
None => {
|
||||
// not an autocrypt mime message, abort and ignore
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(res) => res,
|
||||
Some(res) => res,
|
||||
};
|
||||
info!(context, "Detected Autocrypt-mime message");
|
||||
|
||||
@@ -547,4 +576,27 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
assert!(encrypt_helper.should_encrypt(&t, true, &ps).is_err());
|
||||
assert!(!encrypt_helper.should_encrypt(&t, false, &ps).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mixed_up_mime() -> Result<()> {
|
||||
// "Mixed Up" mail as received when sending an encrypted
|
||||
// message using Delta Chat Desktop via ProtonMail IMAP/SMTP
|
||||
// Bridge.
|
||||
let mixed_up_mime = include_bytes!("../test-data/message/protonmail-mixed-up.eml");
|
||||
let mail = mailparse::parse_mail(mixed_up_mime)?;
|
||||
assert!(get_autocrypt_mime(&mail).is_none());
|
||||
assert!(get_mixed_up_mime(&mail).is_some());
|
||||
|
||||
// Same "Mixed Up" mail repaired by Thunderbird 78.9.0.
|
||||
//
|
||||
// It added `X-Enigmail-Info: Fixed broken PGP/MIME message`
|
||||
// header although the repairing is done by the built-in
|
||||
// OpenPGP support, not Enigmail.
|
||||
let repaired_mime = include_bytes!("../test-data/message/protonmail-repaired.eml");
|
||||
let mail = mailparse::parse_mail(repaired_mime)?;
|
||||
assert!(get_autocrypt_mime(&mail).is_some());
|
||||
assert!(get_mixed_up_mime(&mail).is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ Filename encoding | Encoded Words ([RFC 2047](https://tools.ietf.
|
||||
Identify server folders | IMAP LIST Extension ([RFC 6154](https://tools.ietf.org/html/rfc6154))
|
||||
Push | IMAP IDLE ([RFC 2177](https://tools.ietf.org/html/rfc2177))
|
||||
Authorization | OAuth2 ([RFC 6749](https://tools.ietf.org/html/rfc6749))
|
||||
End-to-end encryption | [Autocrypt Level 1](https://autocrypt.org/level1.html), OpenPGP ([RFC 4880](https://tools.ietf.org/html/rfc4880)) and Security Multiparts for MIME ([RFC 1847](https://tools.ietf.org/html/rfc1847))
|
||||
End-to-end encryption | [Autocrypt Level 1](https://autocrypt.org/level1.html), OpenPGP ([RFC 4880](https://tools.ietf.org/html/rfc4880)), Security Multiparts for MIME ([RFC 1847](https://tools.ietf.org/html/rfc1847)) and [“Mixed Up” Encryption repairing](https://tools.ietf.org/id/draft-dkg-openpgp-pgpmime-message-mangling-00.html)
|
||||
Configuration assistance | [Autoconfigure](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration) and [Autodiscover](https://technet.microsoft.com/library/bb124251(v=exchg.150).aspx)
|
||||
Messenger functions | [Chat-over-Email](https://github.com/deltachat/deltachat-core-rust/blob/master/spec.md#chat-over-email-specification)
|
||||
Detect mailing list | List-Id ([RFC 2919](https://tools.ietf.org/html/rfc2919)) and Precedence ([RFC 3834](https://tools.ietf.org/html/rfc3834))
|
||||
|
||||
36
test-data/message/protonmail-mixed-up.eml
Normal file
36
test-data/message/protonmail-mixed-up.eml
Normal file
@@ -0,0 +1,36 @@
|
||||
Return-Path: <alice@example.com>
|
||||
Delivered-To: bob@example.org
|
||||
Date: Mon, 29 Mar 2021 11:30:57 +0000
|
||||
To: Bob <bob@example.org>
|
||||
From: Alice <alice@example.com>
|
||||
Reply-To: Alice <alice@example.com>
|
||||
Subject: ...
|
||||
Message-ID: <Mr.AkmaxDNOYj0.oNPtoFR8EHC@example.com>
|
||||
In-Reply-To: <Mr.Y1EWG9-FLhN.KUZ4cu74MYR@example.org>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
|
||||
--b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
Empty Message
|
||||
--b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0
|
||||
Content-Type: application/pgp-encrypted; name=attachment.pgp
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment; filename=attachment.pgp
|
||||
|
||||
VmVyc2lvbjogMQ0KDQo=
|
||||
|
||||
--b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0
|
||||
Content-Type: application/octet-stream; name=encrypted.asc
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment; filename=encrypted.asc
|
||||
|
||||
UEdQIFBBWUxPQUQgV0FTIEhFUkUK
|
||||
|
||||
--b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0--
|
||||
|
||||
31
test-data/message/protonmail-repaired.eml
Normal file
31
test-data/message/protonmail-repaired.eml
Normal file
@@ -0,0 +1,31 @@
|
||||
Return-Path: <alice@example.com>
|
||||
Delivered-To: bob@example.org
|
||||
Date: Mon, 29 Mar 2021 11:30:57 +0000
|
||||
To: Bob <bob@example.org>
|
||||
From: Alice <alice@example.com>
|
||||
Reply-To: Alice <alice@example.com>
|
||||
Subject: ...
|
||||
Message-ID: <Mr.AkmaxDNOYj0.oNPtoFR8EHC@example.com>
|
||||
In-Reply-To: <Mr.Y1EWG9-FLhN.KUZ4cu74MYR@example.org>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/encrypted;
|
||||
protocol="application/pgp-encrypted";
|
||||
boundary="b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0"
|
||||
X-Enigmail-Info: Fixed broken PGP/MIME message
|
||||
|
||||
--b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0
|
||||
Content-Type: application/pgp-encrypted; name=attachment.pgp
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment; filename=attachment.pgp
|
||||
|
||||
VmVyc2lvbjogMQ0KDQo=
|
||||
|
||||
--b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0
|
||||
Content-Type: application/octet-stream; name=encrypted.asc
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment; filename=encrypted.asc
|
||||
|
||||
UEdQIFBBWUxPQUQgV0FTIEhFUkUK
|
||||
|
||||
--b1_01FB8kHjERilpSep0FbmgBMNYR3TvWQ30jPthW5L0--
|
||||
|
||||
Reference in New Issue
Block a user