Subject: [TLS] Alert after sending ServerHello

From: Roelof Du Toit <[email-masked]>
Date: Wed, 26 Apr 2017 03:01:40 +0000

During interop testing with an open-source stack I ran into the following:
CH ---->
<---- SH,{EE,Cert,CV,Fin}
alert ---->

The alert was due to a decode error on CV, and the stack in question sent the alert in a plaintext record.

The current draft specification has the following text in section 6:
"Like other messages, alert messages are encrypted as specified by the current connection state."
.. and the following in section 5.1:
"Once record protection has started, TLSPlaintext records are protected"

I expected the alert above to be sent in a ciphertext record, but I can also see the client sending plaintext alerts for ServerHello decode failures.
My conclusion is that the TLS 1.3 record layer on the server side should support receiving both ciphertext and plaintext records after ServerHello has been sent.

One way for the server to handle the scenario above is to defer using client_handshake_traffic_secret until the first app_data record is received.   That idea falls flat when the client is also sending early data and the server wants to ignore the early data using trial decrypt with client_handshake_traffic_secret:

CH ---->
(ed) -----X (discard)
       ...---- SH
(ed) ----X (trial decrypt + discard)
<---...
alert ---->

Another way this could work is if the server allows one (and only one) plaintext record (with ContentType = alert) from the client during the window where it has sent the SH,{EE,Cert,CV,Fin} sequence and is waiting for the client's first encrypted handshake message.

I would appreciate a recommendation in order to avoid future interop issues.

--Roelof

---

From: Benjamin Kaduk <[email-masked]>
Date: Tue, 25 Apr 2017 23:41:25 -0500

On 04/25/2017 10:01 PM, Roelof Du Toit wrote:
> The alert was due to a decode error on *CV*, and the stack in question
> sent the alert in a *plaintext* record.
>
>  
>

You could say which open-source stack you were using.

I know that OpenSSL, at least, still has:

        /*
         * TODO(TLS1.3): This actually causes a problem. We don't yet know
         * whether the next record we are going to receive is an unencrypted
         * alert, or an encrypted handshake message. We're going to need
         * something clever in the record layer for this.
         */

but I don't think I looked at what the sending side does, yet.

-Ben

---

From: Ilari Liusvaara <[email-masked]>
Date: Wed, 26 Apr 2017 09:19:49 +0300

On Wed, Apr 26, 2017 at 03:01:40AM +0000, Roelof Du Toit wrote:
> "Like other messages, alert messages are encrypted as specified by
> the current connection state." and the following in section 5.1:
> I expected the alert above to be sent in a ciphertext record, but I
> can also see the client sending plaintext alerts for ServerHello
> decode failures. My conclusion is that the TLS 1.3 record layer on
> the server side should support receiving both ciphertext and
> plaintext records after ServerHello has been sent.

Yes, that's the way the library I'm writing does it: The first
record received after ServerHello is sent can be cleartext alert for
the case where client chokes on ServerHello, or it can be encrypted
record.

It does not support 0-RTT (insufficient understanding of the actual
security properties, need to design API properly).

One can easily tell unencrypted alert and encrypted record from
envelope type (unencrypted alert has 21, encrypted alert or client
returning handshake is 23).
 
> One way for the server to handle the scenario above is to defer
> using client_handshake_traffic_secret until the first app_data
> record is received.   That idea falls flat when the client is
> also sending early data and the server wants to ignore the early
> data using trial decrypt with client_handshake_traffic_secret:

If you ever receive envelope-type-21 record, you know that the
connection has failed. All those are fatal errors.

The difference is really the error (illegal record type vs.
received alert).

-Ilari

---

From: Martin Thomson <[email-masked]>
Date: Wed, 26 Apr 2017 16:35:31 +1000

You are allowed to out NSS.  We know that it's not ideal.

I wrote that code and it's unencrypted for what I think is a good
reason (others very much disagree).  In part, it has to do with 0-RTT.

In 0-RTT, we want to ensure that the client is able to continue
sending 0-RTT until the entire server flight is received.  Thus, we
don't change out the keys until we receive and process the server
Finished.  The same code is used in 1-RTT.  I think that we have a bug
in that the alerts are 0-RTT encrypted; it might be better to send
them in the clear as well.

In effect, we assume that the entire flight is processed atomically
and generate errors based on that.  Only when the entire flight is
processed cleanly do we install keys and respond.

This is a pain for us, we don't have the code that Ilari talks about,
so some of our tests end up hitting decode errors on the server, but
it's been manageable thus far.

On 26 April 2017 at 13:01, Roelof Du Toit <[email-masked]> wrote:
> I expected the alert above to be sent in a ciphertext record, but I can also
> see the client sending plaintext alerts for ServerHello decode failures.
>
> My conclusion is that the TLS 1.3 record layer on the server side should
> support receiving both ciphertext and plaintext records after ServerHello
> has been sent.
>
>
>
> One way for the server to handle the scenario above is to defer using
> client_handshake_traffic_secret until the first app_data record is received.
> That idea falls flat when the client is also sending early data and the
> server wants to ignore the early data using trial decrypt with
> client_handshake_traffic_secret:
>
>
>
> CH ---->
>
> (ed) -----X (discard)
>
>        ...---- SH
>
> Another way this could work is if the server allows one (and only one)
> plaintext record (with ContentType = alert) from the client during the
> window where it has sent the SH,{EE,Cert,CV,Fin} sequence and is waiting for
> the client's first encrypted handshake message.
>
>
>
> _______________________________________________
> TLS mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/tls
>

---

From: Ilari Liusvaara <[email-masked]>
Date: Wed, 26 Apr 2017 10:19:52 +0300

> them in the clear as well.

AFAIK, the only situations where client can abort sending 0-RTT data
is noticing lack of server EarlyData extension (so server isn't
listening anyway), or if the entiere handshake is aborted.. Doing it
in other situations leads to subtle race conditions.

Of course, EarlyData is in EncryptedExtensions, so one must use the
Server Handshake keys to decrypt it. Which means processing ServerHello
without installing Client Handshake Keys (since one is still encrypting
0-RTT).

> processed cleanly do we install keys and respond.

My implementation processes message-by-message. So it installs the
client handshake keys after ServerHello.
 
> it's been manageable thus far.

The code I was talking about was handling the special case that the
server might receive either encrypted or unencrypted alert in response
to its flight. And the difference it makes is just what error is
declared as abort reason.

-Ilari

---

From: Martin Thomson <[email-masked]>
Date: Wed, 26 Apr 2017 22:00:19 +1000

> in other situations leads to subtle race conditions.

NSS stops sending 0-RTT as soon as it processes EncryptedExtensions.
It also stops if it receives a HelloRetryRequest.  In both cases you
know that the server is trial decrypting and so it will correctly
handle more 0-RTT data, but there is no point sending more if you know
that it is junk.

> client handshake keys after ServerHello.

NSS processes message-by-message, but the we just delay installing the
client handshake keys.  This allows us to get the most out of 0-RTT,
but has this small downside.

> declared as abort reason.

Yeah, it's totally manageable, but there are a few small warts in the
tests (which match the sent alert on one side with the received alert
on the other side in the normal case).

---

From: [email-masked] (Martin Rex)
Date: Wed, 26 Apr 2017 14:25:33 +0200 (CEST)

> declared as abort reason.

Up to TLSv1.2 there was no confusion about whether a TLS record
was encrypted or not: everything before "ChangeCipherSpec" is cleartext,
everything thereafter is encrypted.

Easy and straightforward solution would be to add the (meta-)information
whether the Contents of a TLSv1.3 record are encrypted or not into the
outer ContentType field, i.e. define an additional "encrypted Alert"
content type and ensure that Alerts ContentTypes are always visible
in the traditional TLS record header (rather than the bogus/futile
attempt to hide this information).

Having to do heuristics on something that can be easily deterministically
tagged as one or the other, seems a little odd.

-Martin

---

From: Martin Thomson <[email-masked]>
Date: Wed, 26 Apr 2017 22:42:24 +1000

> everything thereafter is encrypted.

That's not a problem here either.  It's trivial to tell between an
encrypted record and an unencrypted one.  The question - if there is
any - is when to start encrypting if there happens to be an error.

---

From: Roelof Du Toit <[email-masked]>
Date: Wed, 26 Apr 2017 13:43:45 +0000

Yes, I agree that the problem is error handling.  I get that it is trivial to distinguish encrypted from unencrypted, but I would appreciate it if the spec could clarify the appropriate actions in the scenario where an unencrypted (non-appdata) record is received after keys have been installed in the connection state.  It would be strange to send an alert in response to an alert.

PS: I was testing with OpenSSL when I hit the interop issue.

From: Martin Thomson <[email-masked]>
Date: Wednesday, April 26, 2017 at 8:42 AM
To: "[email-masked]" <[email-masked]>
Cc: Ilari Liusvaara <[email-masked]>, Roelof Du Toit <[email-masked]>, "[email-masked]" <[email-masked]>
Subject: [EXT] Re: [TLS] Alert after sending ServerHello

On 26 April 2017 at 22:25, Martin Rex <[email-masked]<mailto:[email-masked]>> wrote:

---

From: Ilari Liusvaara <[email-masked]>
Date: Wed, 26 Apr 2017 16:44:56 +0300

> handle more 0-RTT data, but there is no point sending more if you know

Oh yeah, there is also HelloRetryRequest that aborts 0-RTT data.

But stopping on receiving EncryptedExtensions with EarlyData extension
is racy.

-Ilari

---

From: Martin Thomson <[email-masked]>
Date: Thu, 27 Apr 2017 10:45:23 +1000

> But stopping on receiving EncryptedExtensions with EarlyData extension

How so?