Skip to content

Commit da17848

Browse files
committed
feat: Add Config::StdHeaderProtectionComposing (enables composing as defined in RFC 9788) (#7130)
But disable it by default for now. We will enable it when most users update to versions capable to receive protected headers.
1 parent 32708ef commit da17848

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

src/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,11 @@ pub enum Config {
440440

441441
/// Return an error from `receive_imf_inner()` for a fully downloaded message. For tests.
442442
FailOnReceivingFullMsg,
443+
444+
/// Enable composing emails with Header Protection as defined in
445+
/// https://www.rfc-editor.org/rfc/rfc9788.html "Header Protection for Cryptographically
446+
/// Protected Email".
447+
StdHeaderProtectionComposing,
443448
}
444449

445450
impl Config {

src/context.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,13 @@ impl Context {
10671067
.await?
10681068
.unwrap_or_default(),
10691069
);
1070+
res.insert(
1071+
"std_header_protection_composing",
1072+
self.sql
1073+
.get_raw_config("std_header_protection_composing")
1074+
.await?
1075+
.unwrap_or_default(),
1076+
);
10701077

10711078
let elapsed = time_elapsed(&self.creation_time);
10721079
res.insert("uptime", duration_to_str(elapsed));

src/mimefactory.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,9 @@ impl MimeFactory {
10541054
}
10551055
}
10561056

1057+
let use_std_header_protection = context
1058+
.get_config_bool(Config::StdHeaderProtectionComposing)
1059+
.await?;
10571060
let outer_message = if let Some(encryption_keys) = self.encryption_keys {
10581061
// Store protected headers in the inner message.
10591062
let message = protected_headers
@@ -1069,6 +1072,22 @@ impl MimeFactory {
10691072
message.header(header, value)
10701073
});
10711074

1075+
if use_std_header_protection {
1076+
message = unprotected_headers
1077+
.iter()
1078+
// Structural headers shouldn't be added as "HP-Outer". They are defined in
1079+
// <https://www.rfc-editor.org/rfc/rfc9787.html#structural-header-fields>.
1080+
.filter(|(name, _)| {
1081+
!(name.eq_ignore_ascii_case("mime-version")
1082+
|| name.eq_ignore_ascii_case("content-type")
1083+
|| name.eq_ignore_ascii_case("content-transfer-encoding")
1084+
|| name.eq_ignore_ascii_case("content-disposition"))
1085+
})
1086+
.fold(message, |message, (name, value)| {
1087+
message.header(format!("HP-Outer: {name}"), value.clone())
1088+
});
1089+
}
1090+
10721091
// Add gossip headers in chats with multiple recipients
10731092
let multiple_recipients =
10741093
encryption_keys.len() > 1 || context.get_config_bool(Config::BccSelf).await?;
@@ -1158,7 +1177,13 @@ impl MimeFactory {
11581177
for (h, v) in &mut message.headers {
11591178
if h == "Content-Type" {
11601179
if let mail_builder::headers::HeaderType::ContentType(ct) = v {
1161-
*ct = ct.clone().attribute("protected-headers", "v1");
1180+
let mut ct_new = ct.clone();
1181+
ct_new = ct_new.attribute("protected-headers", "v1");
1182+
if use_std_header_protection {
1183+
ct_new = ct_new.attribute("hp", "cipher");
1184+
}
1185+
*ct = ct_new;
1186+
break;
11621187
}
11631188
}
11641189
}
@@ -1244,7 +1269,13 @@ impl MimeFactory {
12441269
for (h, v) in &mut message.headers {
12451270
if h == "Content-Type" {
12461271
if let mail_builder::headers::HeaderType::ContentType(ct) = v {
1247-
*ct = ct.clone().attribute("protected-headers", "v1");
1272+
let mut ct_new = ct.clone();
1273+
ct_new = ct_new.attribute("protected-headers", "v1");
1274+
if use_std_header_protection {
1275+
ct_new = ct_new.attribute("hp", "clear");
1276+
}
1277+
*ct = ct_new;
1278+
break;
12481279
}
12491280
}
12501281
}

0 commit comments

Comments
 (0)