Subject: [lamps] Protected Headers updates: proposed guidance for receiving MUAs to handle two distinct schemes

From: Daniel Kahn Gillmor <[email-masked]>
Date: Mon, 02 Nov 2020 18:55:58 -0500

Hi folks--

Bernie and i did some work on trying to get the Protected Headers draft
into better shape.  I've split out some baseline (not
protected-header-specific) guidance for cryptographically-capable MUAs
at draft-dkg-lamps-e2e-mail-guidance, which this work relies on.

In particular for the protected headers draft, I've written some
detailed guidance on *interpreting* protected headers which i think
should be useful for any implementer.  I don't think any of this
guidance should be controversial for an implementer who wants to deal
with messages as they're found in the wild.

For the receiving side, an implementer probably wants to be able to
handle two distinct forms of protected header: the scheme specified in
S/MIME 3.1, and the injected headers scheme used by tools like Enigmail,
(which I think is marginally friendlier to legacy e-mail clients).

My proposed edits describe the two different schemes this way:

> This document describes two different structures for how message headers can be cryptographically protected, and provides guidance for implementers of MUAs that generate and interpret such messages.
> It takes particular care to ensure that messages interact reasonably well with legacy MUAs.
> 
> ## Two Schemes of Protected Headers 
> 
> Unfortunately, there are two different schemes for cryptographically-protected email headers that may be in use on the Internet today.
> This document addresses them both and provides guidance to implementers.
> 
> One scheme is the form specified in S/MIME 3.1 and later, which involves wrapping a `message/rfc822` MIME object with a Cryptographic Envelope.
> This document calls this scheme "Wrapped Message", and it is documented in more detail in [RFC8551].
> Experience has shown that this form does not interact well with some legacy MUAs (see {{wrapped-message-problems}}).
> 
> Consequently, another form of header protection is produced and consumed by some MUAs, where the protected headers are placed directly on the Cryptographic Payload, without using an intervening `message/*` MIME object.
> This document calls this scheme "Injected Headers", and it is documented in more detail in [I-D.autocrypt-lamps-protected-headers].

These two schemes offer equivalent semantics of cryptographic protection
for e-mail headers, but they differ in how they interact with legacy
clients.

The proposed changes then go into some detail about how a receiving
MUA should handle protected headers -- regardless of which scheme the
incoming message uses.

I've attached a draft of that "Receiving Side" here, and welcome
feedback on it.

The outline of the proposed section rewrite, so you get a feel for it:

 *. Receiving Side
 *.1 Identifying that a Message has Protected Headers
 *.2 Updating the Cryptographic Summary
 *.3 Rendering a Wrapped Message
 *.3.1 Example Signed-Only Wrapped Message
 *.3.2 Example Encrypted-and-Signed Wrapped Message
 *.4 Rendering a Message with Injected Headers
 *.4.1 Example Signed-only Message with Injected Headers
 *.4.2 Example Signed-and-Encrypted Message with Injected Headers
 *.4.3 Do Not Render Legacy Display Part
 *.4.3.1 Legacy Display Detection Algorithm
 *.4.3.2 Legacy Display Example
 *.5 Affordances for Debugging and Troubleshooting
 *.6 Composing a Reply to an Encrypted Message with Protected Headers
 *.6.1 Avoid Leaking Encrypted Headers in Reply
 *.6.2 Avoid Misdirected Replies to Encrypted Messages with Protected Headers
 *.7 Implicitly-rendered Header Fields
 *.8 Unprotected Headers Added in Transit
 *.8.1 Mailing list headers: List-* and Archived-At

As you can see, sections *.3 and *.4 outline the two
marginally-different schemes, but all the other guidance is relevant
regardless of the scheme received.

I'm now working on guidance for *generating* protected headers, but i'm
still happy to get criticism and proposed fixes for this interpretation
guidance.

All the best,

         --dkg

### Receiving Side

An MUA that receives a cryptographically-protected e-mail will render it for the user.

The receiving MUA will render the message body, a selected subset of header fields, and (as described in [I-D.dkg-lamps-e2e-mail-guidance]) provide a summary of the cryptographic properties of the message.

Most MUAs only render a subset of header fields by default.
For example, few MUAs typically render `Message-Id` or `Received` header fields for the user, but most do render `From`, `To`, `Cc`, `Date`, and `Subject`.

A MUA that knows how to handle a message with protected headers makes the following two changes to its behavior when rendering a message:

- If it detects that an incoming message had protected headers, it renders header fields for the message from the protected headers, ignoring the external (unprotected) headers.
- It includes information in the message's cryptographic summary to indicate the types of protection that applied to each rendered header field (if any).

A MUA that handles protected headers does *not* need to render any new header fields that it did not render before.

#### Identifying that a Message has Protected Headers

An incoming message can be identified as having protected headers based on one of two signals:

- The Cryptographic Payload has `Content-Type: message/rfc822` or `Content-Type: message/global` and the parameter `forwarded` has a value of `no`.
  See {{rendering-wrapped-message}} for rendering guidance.
  
- The Cryptographic Payload has some other `Content-Type` and it has parameter `protected-headers` set to `v1`.
  See {{rendering-injected-headers}} for rendering guidance.
  
Messages of both types exist in the wild, and a sensible MUA should be able to handle them both.
They provide the same semantics and the same meaning.

#### Updating the Cryptographic Summary

Regardless of whether a cryptographically-protected message has protected headers, the cryptographic summary of the message should be modified to indicate what protections the headers have.

Each header individually has exactly one the following protections:

- `unprotected` (this is the case for all headers in messages that have no protected headers)
- `signed-only` (bound into the same validated signature as the enclosing message, but also visible in transit)
- `encrypted-only` (only appears within the cryptographic payload; the corresponding external header was either omitted or obfuscated)
- `encrypted-and-signed` (same as encrypted, but additionally is under a validatd signature)

Note that while the message itself may be `encrypted-and-signed`, some headers may be replicated on the outside of the message (e.g. `Date`)
Those headers would be `signed-only`, despite the message itself being `encrypted-and-signed`.

Rendering this information is likely to be complex and messy --- users may not understand it.
It is beyond the scope of this document to suggest any specific graphical affordances or user experience.
Future work should include examples of successful rendering of this information.

#### Rendering a Wrapped Message {#rendering-wrapped-message}

When the Cryptographic Payload has `Content-Type` of `message/rfc822` or `message/global`, and the parameter `forwarded` is set to `no`, the values of the protected headers are drawn from the headers of the Cryptographic Payload, and the body that is rendered is the body of the Cryptographic Payload.

##### Example Signed-Only Wrapped Message

Consider a message with this structure, where the MUA is able to validate the cryptographic signature:

~~~
A └─╴application/pkcs7-mime; smime-type="signed-data"
   ⇩ (unwraps to)
B  └┬╴message/rfc822 [Cryptographic Payload]
C   └┬╴multipart/alternative [Rendered Body]
D    ├─╴text/plain
E    └─╴text/html
~~~

The message body should be rendered the same way as this message:

~~~
C └┬╴multipart/alternative
D  ├─╴text/plain
E  └─╴text/html
~~~

It should render header fieldss taken from part `B`.

Its cryptographic summary should indicates that the message was signed and all rendered header fields were included in the signature.

The MUA SHOULD ignore header fields from part `A` for the purposes of rendering.

##### Example Encrypted-and-Signed Wrapped Message {#example-signed-and-encrypted-wrapped}

Consider a message with this structure, where the MUA is able to validate the cryptographic signature:

~~~
F └─╴application/pkcs7-mime; smime-type="enveloped-data"
   ↧ (decrypts to)
G  └─╴application/pkcs7-mime; smime-type="signed-data"
    ⇩ (unwraps to)
H   └┬╴message/rfc822 [Cryptographic Payload]
I    └┬╴multipart/alternative [Rendered Body]
J     ├─╴text/plain
K     └─╴text/html
~~~

The message body should be rendered the same way as this message:

~~~
I └┬╴multipart/alternative
J  ├─╴text/plain
K  └─╴text/html
~~~

It should render headers taken from part `H`.

Its cryptographic summary should indicates that the message was signed and encrypted.
Each rendered header field found in `H` should be compared against the header field of the same name from `F`.
If the value found in `F` matches the value found in `H`, the header field should be marked as `signed-only`.
If no matching header field was found in `F`, or the value found did not match the value from `H`, the header field should be marked as `signed-and-encrypted`.

#### Rendering a Message with Injected Headers  {#rendering-injected-headers}

When the Cryptographic Payload does not have a `Content-Type` of `message/rfc822` or `message/global`, and the parameter `protected-headers` is set to `v1`, the values of the protected headers are drawn from the headers of the Cryptographic Payload, and the body that is rendered is the Cryptographic Payload itself.

##### Example Signed-only Message with Injected Headers

~~~
L └─╴application/pkcs7-mime; smime-type="signed-data"
   ⇩ (unwraps to)
M  └┬╴multipart/alternative [Cryptographic Payload + Rendered Body]
N   ├─╴text/plain
O   └─╴text/html
~~~

The message body should be rendered the same way as this message:

~~~
M └┬╴multipart/alternative
N  ├─╴text/plain
O  └─╴text/html
~~~

It should render header fieldss taken from part `M`.

Its cryptographic summary should indicates that the message was signed and all rendered header fields were included in the signature.

The MUA SHOULD ignore header fields from part `L` for the purposes of rendering.

##### Example Signed-and-Encrypted Message with Injected Headers {#example-signed-and-encrypted-injected}

Consider a message with this structure, where the MUA is able to validate the cryptographic signature:

~~~
P └─╴application/pkcs7-mime; smime-type="enveloped-data"
   ↧ (decrypts to)
Q  └─╴application/pkcs7-mime; smime-type="signed-data"
    ⇩ (unwraps to)
R   └┬╴multipart/alternative [Cryptographic Payload + Rendered Body]
S    ├─╴text/plain
T    └─╴text/html
~~~

The message body should be rendered the same way as this message:

~~~
R └┬╴multipart/alternative
S  ├─╴text/plain
T  └─╴text/html
~~~

It should render headers taken from part `R`.

Its cryptographic summary should indicates that the message was signed and encrypted.
As in {{example-signed-and-encrypted-wrapped}}, each rendered header field found in `R` should be compared against the header field of the same name from `P`.
If the value found in `P` matches the value found in `R`, the header field should be marked as `signed-only`.
If no matching header field was found in `P`, or the value found did not match the value from `R`, the header field should be marked as `signed-and-encrypted`.

##### Do Not Render Legacy Display Part

As described [I-D.autocrypt-lamps-protected-headers], a message with cryptographic confidentiality protection  MAY include a "Legacy Display" part for backward-compatibility with legacy MUAs

The receiving MUA SHOULD avoid rendering the Legacy Display part to the user at all, since it is aware of and can render the actual Protected Headers.

If a Legacy Display part is detected, it and its enclosing `multipart/mixed` wrapper should be discarded before rendering.

###### Legacy Display Detection Algorithm

A receiving MUA acting on a message SHOULD detect the presence of a Legacy Display part and the corresponding "original body" with the following simple algorithm:

 - Check that all of the following are true for the message:
  - The Cryptographic Envelope must contain an encrypting Cryptographic Layer
  - The Cryptographic Payload must have a `Content-Type` of `multipart/mixed`
  - The Cryptographic Payload must have exactly two subparts
  - The first subpart of the Cryptographic Payload must have a `Content-Type` of `text/plain` or `text/rfc822-headers`
  - The first subpart of the Cryptographic Payload's `Content-Type` must contain a property of `protected-headers`, and its value must be `v1`.
 - If all of the above are true, then the first subpart is the Legacy Display part, and the second subpart is the "original body".  Otherwise, the message does not have a Legacy Display part.

###### Legacy Display Example

Consider a message with this structure, where the MUA is able to validate the cryptographic signature:

~~~
U └─╴application/pkcs7-mime; smime-type="enveloped-data"
   ↧ (decrypts to)
V  └─╴application/pkcs7-mime; smime-type="signed-data"
    ⇩ (unwraps to)
W   └┬╴multipart/mixed [Cryptographic Payload]
X    ├─╴text/plain [Legacy Display]
Y    └┬╴multipart/alternative [Rendered Body]
Z     ├─╴text/plain
A'    └─╴text/html
~~~

The message body should be rendered the same way as this message, effectively hiding the Legacy Display part (`X`) and its wrapper:

~~~
Y └┬╴multipart/alternative
Z  ├─╴text/plain
A' └─╴text/html
~~~

It should render headers taken from part `W`, following the same guidance as in {{example-signed-and-encrypted-injected}} and {{example-signed-and-encrypted-wrapped}} about the cryptographic status of each rendered header field.

#### Affordances for Debugging and Troubleshooting {#debugging-and-troubleshooting}

Note that advanced users of an MUA may need access to the original message, for example to troubleshoot problems with the MUA itself, or problems with the SMTP transport path taken by the message.

A MUA that applies these rendering guidelines SHOULD ensure that the full original source of the message as it was received remains available to such a user for debugging and troubleshooting.

#### Composing a Reply to an Encrypted Message with Protected Headers

When composing a reply to an encrypted message with protected headers, the MUA is acting both as a receiving MUA and as a sending MUA.
Special guidance applies here, as things can go wrong in at least two ways: leaking previously-confidential information, and replying to the wrong party.

##### Avoid Leaking Encrypted Headers in Reply

As noted in [I-D.dkg-lamps-e2e-mail-guidance], an MUA in this position MUST NOT leak previously-encrypted content in the clear in a followup message.
The same is true for protected headers.

Values from any header field that was identified as either `encrypted` or `signed-and-encrypted` based on the steps outlined above MUST NOT be placed in cleartext output when generating a message.

In particular, if `Subject` was encrypted, and it is copied into the draft encrypted reply, the replying MUA MUST obfuscate the `Subject` field in the cleartext header as described above.

##### Avoid Misdirected Replies to Encrypted Messages with Protected Headers {#avoid-misdirected-replies}

When replying to a message, the Composing MUA typically decides who to send the reply to based on:

- the `Reply-To`, `Mail-Followup-To`, or `From` headers
- optionally, the other `To` or `Cc` headers (if the user chose to "reply all")

When a message has protected headers, the replying MUA MUST populate the destination fields of the draft message using the protected headers, and ignore any unprotected headers.

This mitigates against an attack where Mallory gets a copy of an encrypted message from Alice to Bob, and then replays the message to Bob with an additional `Cc` to Mallory's own e-mail address in the message's outer header.

If Bob knows Mallory's certificate already, and he replies to such a message without following the guidance in this section, it's likely that his MUA will encrypt the cleartext of the message directly to Mallory.

#### Implicitly-rendered Header Fields

While `From` and `To` and `Cc` and `Subject` and `Date` are often explicitly rendered to the user, some header fields do affect message display, without being explicitly rendered.

For example, `Message-Id`, `References`, and `In-Reply-To` header fields may collectively be used to place a message in a "thread" or series of messages.

In another example, {{avoid-misdirected-replies}} observes that the value of the `Reply-To` field can influence the draft reply message.
So while the user may never see the `Reply-To` header directly, it is implicitly "rendered" when the user interacts with the message by replying to it.

An MUA that depends on any implicitly-rendered header field in a message with protected headers SHOULD use the value from the protected header, and SHOULD NOT use any value found outside the cryptographic protection.

#### Unprotected Headers Added in Transit

Some headers are legitimately added in transit, and could not have been known to the sender at message composition time.

The most common of these headers are `Received` and `DKIM-Signature`, neither of which are typically rendered, either explicitly or implicitly.

If a receiving MUA has specific knowledge about a given header field, including that:

- the header field would not have been known to the original sender, and
- the header field might be rendered explicitly or implicitly,

then the MUA MAY decide to operate on the value of that header field from the unprotected header section, even though the message has protected headers.

The MUA MAY prefer to verify that the headers in question have additional transit-derived cryptographic protections (e.g., to test whether they are covered by a valid `DKIM-Signature`) before rendering or acting on them.

Specific examples appear below.

##### Mailing list headers: List-* and Archived-At

If the message arrives through a mailing list, the list manager itself may inject headers (most of which start with `List-`) in the message:

- `List-Archive`
- `List-Subscribe`
- `List-Unsubscribe`
- `List-Id`
- `List-Help`
- `List-Post`
- `Archived-At`

For some MUAs, these headers are implicitly rendered, by providing buttons for actions like "Subscribe", "View Archived Version", "Reply List", "List Info", etc.

An MUA that receives a message with protected headers that contains these header fields in the unprotected section, and that has reason to believe the message is coming through a mailing list MAY decide to render them to the user (explicitly or implicitly) even though they are not protected.

FIXME: other examples of unprotected transit headers?

---

From: Daniel Kahn Gillmor <[email-masked]>
Date: Tue, 03 Nov 2020 20:31:47 -0500

Hey all--

Re-reading the work i sent earlier, i noticed a small but important
mistake.  In particular, i'd misidentified the MIME part where the
protected headers live in "Wrapped Message"-style header protections:

On Mon 2020-11-02 18:55:58 -0500, Daniel Kahn Gillmor wrote:
> It should render header fields taken from part `B`.

In fact, the rendered header fields should be taken from part `C`.

> It should render headers taken from part `H`.

Similarly, here, the rendered headers shold b taken from part `I`, not `H`.

> If no matching header field was found in `F`, or the value found did not match the value from `H`, the header field should be marked as `signed-and-encrypted`.

I've fixed this in the version in revision control.  I'd appreciate any
other review of the text!

     --dkg

---

From: Alexey Melnikov <[email-masked]>
Date: Wed, 4 Nov 2020 13:39:31 +0000

Hi DKG,

> other review of the text!

After your fixes the text looks good to me!

Best Regards,

Alexey