Subject: [TLS] draft-vvv-tls-alps-01

From: David Benjamin <[email-masked]>
Date: Mon, 12 Oct 2020 17:38:20 -0400

Hi all,

Victor and I recently published draft-vvv-tls-alps-01. It has a couple of
changes that I wanted to get the WG’s thoughts on. The changes are
switching the bespoke ClientApplicationSettings message to a client
EncryptedExtensions message and clarifying the 0-RTT handling.

https://tools.ietf.org/html/draft-vvv-tls-alps-01

I think it was EKR who suggested a client EncryptedExtensions message, so
future extensions can more easily add to the client Finished leg. A new
message means picking when it is sent, and a new extensions block means
picking when extensions are allowed. We’ve initially gone with:

   -

   We add a CEE tag to the TLS 1.3 column in the IANA registry.
   -

   The client sends EncryptedExtensions, possibly empty, if and only if the
   server sent at least one CEE-tagged extension in server
   EncryptedExtensions. (No unsolicited server extensions, so the client will
   know the status of each extension.)
   -

   The client may send an extension in EncryptedExtensions if the server
   sent the corresponding extension in server EncryptedExtensions. (Whether it
   is mandatory or optional once negotiated is the extension’s business.)

Other formulations may also work. We picked this to avoid an optional
message (see half-RTT tickets below), and to try to match the existing
scheme.

For 0-RTT, ALPS aims to align with ALPN where we carry over the ticket
state (like ALPN, ALPS is resolved before any application data) and decline
0-RTT on mismatch. That leaves the question of resending the information
(receiver checks it matches) vs omitting it (receiver implicitly carries it
over). ALPN does the former, but we’ve currently gone with the latter for
ALPS. Both seem comparable implementation-wise, but we picked this for
fewer bytes on the wire, and to avoid breaking half-RTT tickets. We’d
particularly like to hear the WG’s thoughts on this last point.

Section 4.6.1 of RFC8446 has the following text:

https://tools.ietf.org/html/rfc8446#section-4.6.1

   Note: Although the resumption master secret depends on the client's

   second flight, a server which does not request client authentication

   MAY compute the remainder of the transcript independently and then

   send a NewSessionTicket immediately upon sending its Finished rather

   than waiting for the client Finished.  This might be appropriate in

   cases where the client is expected to open multiple TLS connections

   in parallel and would benefit from the reduced overhead of a

   resumption handshake, for example.

This relies on the server being able to predict the client Finished flight
in 0-RTT handshakes. Two risks here:

   -

   Whether EncryptedExtensions is sent must be deterministic (as in our
   formulation).
   -

   Extension order is not fixed. If we ever have two client encrypted
   extensions sent over 0-RTT, the server cannot predict which will go first.

We thus punted this in -01 by not sending the redundant extension in 0-RTT
at all. Another option would be locking the client EE extension order. But
I think we should decide whether half-RTT tickets are a constraint we want
to carry going forward.

Half-RTT tickets were originally added for a stateless reject flow in QUIC
that no longer applies. We’ve since found it useful for 0-RTT, to avoid
some problematic I/O patterns. (See
https://mailarchive.ietf.org/arch/msg/tls/hymweZ66b2C8nnYyXF8cwj7qopc, and this
comment
<https://github.com/openssl/openssl/pull/6944#issuecomment-413858136> from
I think an NGINX developer.) We’ve since realized we can avoid the
surprising I/O patterns by deferring NewSessionTicket to the next
application write and have done so in BoringSSL for 1-RTT. For 0-RTT, we
currently still use half-RTT tickets because the cost is connections don’t
deliver tickets until the second application round-trip rather than the
first:

ClientHello
GET /request1 HTTP/1.1
                        ServerHello..Finished
                              HTTP/1.1 200 OK
EndOfEarlyData..Finished
GET /request2 HTTP/1.1
                             NewSessionTicket
                              HTTP/1.1 200 OK

Delaying this seems surprising and not ideal. We already see that
post-handshake NST is itself surprising to application developers, since
the client only picks up tickets on the first read. This would *further*
delay it to some late read. It’s also something the RFC explicitly says you
can do, and dropping this would contradict that decision. At the same time,
the prediction bits are goofy and constraining.

Thoughts?

David