Subject: [TLS] Separate APIs for 0-RTT

From: Eric Rescorla <[email-masked]>
Date: Tue, 13 Jun 2017 09:28:49 +0100

The current text says:

   0-RTT data has very different security properties from data
   transmitted after a completed handshake: it can be replayed.
   Implementations SHOULD provide different functions for reading and
   writing 0-RTT data and data transmitted after the handshake, and
   SHOULD NOT automatically resend 0-RTT data if it is rejected by the
   server.

I think the second piece of guidance (about automatic re-send) is still
good but it seems like implementations are mostly converging on a single
API. Of the implementations I know, NSS, BoringSSL (I think), and Mint have
a single API and OpenSSL's use of two APIs is an outlier. I don't think
it's helpful to have a SHOULD that a lot of people violate, especially when
this also indicates we don't have consensus on this SHOULD.

I propose we remove this recommendation in favor of one which simply says
that implementations should need applications to opt-in to 0-RTT. That
would allow implementations to have either API.

-Ekr

---

From: Ilari Liusvaara <[email-masked]>
Date: Tue, 13 Jun 2017 13:54:09 +0300

> would allow implementations to have either API.

I think it is VERY bad idea for TLS client library to do 0-RTT without
application explicitly opting in to that (e.g., via setting a special
setting, or using API call sequences that didn't work at all for
n-RTT).

Consider for example that:

- The application starts by sending a POST request.
- The application starts by sending a GET to confidential URL.

Both cases lead to things possibly going very badly wrong if TLS
library silently does 0-RTT. And both are kind of requests that might
be written before TLS connection is fuly up.

And if ALPN is included, there is always a possibility that the
initial guess on protocol was wrong, and the data can't just be
autoretransmitted, but TLS stack has to ask the application to
roll back the state.

The server side does not have as obvious failure modes if 0-RTT is
enabled without application knowledge, but that does not mean such
failure modes are not out there.

-Ilari

---

From: Eric Rescorla <[email-masked]>
Date: Tue, 13 Jun 2017 12:01:22 +0100

> setting, or using API call sequences that didn't work at all for

I agree with this. I am suggesting that a setting rather than a separate
API is a
reasonable approach.

-Ekr

---

From: "Salz, Rich" <[email-masked]>
Date: Tue, 13 Jun 2017 12:22:50 +0000

Microsoft also has a separate API for 0RTT data.  I would characterize things as the two most popular browsers have stated their intention to have a single API, and the two most popular system libraries have two.  Outlier is clearly wrong.

I agree we don’t have consensus, but do make sure that any wording change accommodates the fact that the split isn’t all-versus-one.

---

From: Eric Rescorla <[email-masked]>
Date: Tue, 13 Jun 2017 13:27:29 +0100

On Tue, Jun 13, 2017 at 1:22 PM, Salz, Rich <[email-masked]> wrote:

> Microsoft also has a separate API for 0RTT data.  I would characterize
> things as the two most popular browsers have stated their intention to have
> a single API, and the two most popular system libraries have two.  Outlier
> is clearly wrong.
>

I did not know that about Microsoft. Thanks for the update. I take back
"outlier"

> I agree we don’t have consensus, but do make sure that any wording change
> accommodates the fact that the split isn’t all-versus-one.
>

I was intending to use wording that was neutral between the two options
without any claims about popularity.

Thanks,
-Ekr

---

From: Steven Valdez <[email-masked]>
Date: Tue, 13 Jun 2017 14:22:42 +0000

Confirming that BoringSSL is using a single API for early/regular data,
since we ran into issues/complications with our implementation of dual APIs
with our use cases.

On Tue, Jun 13, 2017 at 8:28 AM Eric Rescorla <[email-masked]> wrote:

> _______________________________________________
> TLS mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/tls
>

---

From: Andrei Popov <[email-masked]>
Date: Tue, 13 Jun 2017 16:12:13 +0000

Correct, I’m planning a separate API surface for 0-RTT, as OpenSSL did.

WRT RFC language, perhaps a reasonable compromise would be to say that a TLS implementation SHOULD only enable 0-RTT application data upon explicit opt-in by the application?

This is more flexible and may involve separate APIs, new off-by-default flags in the existing APIS, whatever else makes sense for a particular TLS implementation…

Cheers,

Andrei

From: TLS [mailto:[email-masked]] On Behalf Of Eric Rescorla
Sent: Tuesday, June 13, 2017 5:27 AM
To: Salz, Rich <[email-masked]>
Cc: [email-masked]
Subject: Re: [TLS] Separate APIs for 0-RTT

On Tue, Jun 13, 2017 at 1:22 PM, Salz, Rich <[email-masked]<mailto:[email-masked]>> wrote:
Microsoft also has a separate API for 0RTT data.  I would characterize things as the two most popular browsers have stated their intention to have a single API, and the two most popular system libraries have two.  Outlier is clearly wrong.

I did not know that about Microsoft. Thanks for the update. I take back "outlier"

I was intending to use wording that was neutral between the two options without any claims about popularity.

Thanks,
-Ekr

---

From: Eric Rescorla <[email-masked]>
Date: Tue, 13 Jun 2017 17:29:05 +0100

This would be fine with me.

-Ekr

On Tue, Jun 13, 2017 at 5:12 PM, Andrei Popov <[email-masked]>
wrote:

> WRT RFC language, perhaps a reasonable compromise would be to say that a
> TLS implementation SHOULD only enable 0-RTT application data upon explicit
> opt-in by the application?
>
>
>
> This is more flexible and may involve separate APIs, new off-by-default
> flags in the existing APIS, whatever else makes sense for a particular TLS
> implementation…
>
>
>
> Cheers,
>
>
>
> Andrei
>
>
>
> *From:* TLS [mailto:[email-masked]] *On Behalf Of *Eric Rescorla
> *Sent:* Tuesday, June 13, 2017 5:27 AM
> *To:* Salz, Rich <[email-masked]>
> *Cc:* [email-masked]
> *Subject:* Re: [TLS] Separate APIs for 0-RTT
>
>
>

---

From: Loganaden Velvindron <[email-masked]>
Date: Tue, 13 Jun 2017 20:36:28 +0400

> setting, or using API call sequences that didn't work at all for

I also agree here.

---

From: Benjamin Beurdouche <[email-masked]>
Date: Tue, 13 Jun 2017 18:49:30 +0200

Please forgive me for this naive question, but is there a specific incentive for using “SHOULD” instead of
“MUST" only enable 0-RTT application data upon explicit opt-in by the application...
My fear is that, even with RFC 2119 terminology, 0RTT will likely be the cause of many problems in the future
and that being extra careful here is important… :)

Best,
Benjamin

> On Jun 13, 2017, at 6:12 PM, Andrei Popov <[email-masked]> wrote:
>

---

From: Andrei Popov <[email-masked]>
Date: Tue, 13 Jun 2017 17:10:39 +0000

*   My fear is that, even with RFC 2119 terminology, 0RTT will likely be the cause of many problems in the future
  *   and that being extra careful here is important… :)

I agree with this assessment. A MUST would certainly work for me. There are two reasons I suggested SHOULD:

  1.  A MUST would be non-enforceable (a TLS client or server can’t enforce the use of a particular API by the peer).
  2.  There’s lack of consensus on the topic of 0RTT and I’m trying to suggest a compromise😊.

Cheers,

Andrei

From: Benjamin Beurdouche [mailto:[email-masked]]
Sent: Tuesday, June 13, 2017 9:50 AM
To: Eric Rescorla <[email-masked]>
Cc: Rich Salz <[email-masked]>; ML IETF TLS <[email-masked]>; Andrei Popov <[email-masked]>
Subject: Re: [TLS] Separate APIs for 0-RTT

Best,
Benjamin

On Jun 13, 2017, at 6:12 PM, Andrei Popov <[email-masked]<mailto:[email-masked]>> wrote:

Cheers,

Andrei

---

From: Benjamin Kaduk <[email-masked]>
Date: Tue, 13 Jun 2017 12:36:55 -0500

That's fine with me as well, though I am now considering the question of
having an API for the server application to know whether a given request
was received over 0- or 1-RTT.

-Ben

On 06/13/2017 11:29 AM, Eric Rescorla wrote:
> On Tue, Jun 13, 2017 at 5:12 PM, Andrei Popov
> <[email-masked] <mailto:[email-masked]>> wrote:
>
>     WRT RFC language, perhaps a reasonable compromise would be to say
>     that a TLS implementation SHOULD only enable 0-RTT application
>     data upon explicit opt-in by the application?
>
>      
>
>     This is more flexible and may involve separate APIs, new
>     off-by-default flags in the existing APIS, whatever else makes
>     sense for a particular TLS implementation…
>
>      
>
>     Cheers,
>
>      
>
>     Andrei
>
>      
>
>     *From:* TLS [mailto:[email-masked]
>     <mailto:[email-masked]>] *On Behalf Of *Eric Rescorla
>     *To:* Salz, Rich <[email-masked] <mailto:[email-masked]>>
>     *Cc:* [email-masked] <mailto:[email-masked]>
>         Microsoft also has a separate API for 0RTT data.  I would
>         characterize things as the two most popular browsers have
>         stated their intention to have a single API, and the two most
>         popular system libraries have two.  Outlier is clearly wrong.
>
>      
>
>         I agree we don’t have consensus, but do make sure that any
>         wording change accommodates the fact that the split isn’t
>         all-versus-one.
>
>      
>
>     I was intending to use wording that was neutral between the two
>     options without any claims about popularity.
>
>      
>
>     Thanks,
>
>     -Ekr
>
>      
>
>
>
>

---

From: Ilari Liusvaara <[email-masked]>
Date: Tue, 13 Jun 2017 20:37:44 +0300

>   1.  A MUST would be non-enforceable (a TLS client or server can’t enforce the use of a particular API by the peer).

I would say what the endpoint _itself_ does is more relevant than what
the _peer_ does. I gave some examples of things going badly wrong if
the application does not opt-in, especially at client side.

And on server side, the requirement to only accept "safe" things for
0-RTT can't be done without opt-in.

>   2.  There’s lack of consensus on the topic of 0RTT and I’m trying to suggest a compromise😊.

Not in all aspects of it.

-Ilari

---

From: Colm MacCárthaigh <[email-masked]>
Date: Tue, 13 Jun 2017 10:46:11 -0700

> was received over 0- or 1-RTT.

For s2n, I'm leaning towards recommending the opposite; signaling on the
client side, if opt-in 0-RTT fails, but no signaling on the server side
(though still opt-in). My reasoning is based on experience with that "X-"
server-side header trick; it misleads people into what's going on in a way
that leads to brokenness. The application people think they only have to
de-dupe the 0-RTT sections, but that's not true.

-- 
Colm

---

From: Benjamin Kaduk <[email-masked]>
Date: Tue, 13 Jun 2017 13:04:19 -0500

On 06/13/2017 12:46 PM, Colm MacCárthaigh wrote:
>
>
> On Tue, Jun 13, 2017 at 10:36 AM, Benjamin Kaduk <[email-masked]
> <mailto:[email-masked]>> wrote:
>
>     That's fine with me as well, though I am now considering the
>     question of having an API for the server application to know
>     whether a given request was received over 0- or 1-RTT.
>
>
>
> For s2n, I'm leaning towards recommending the opposite; signaling on
> the client side, if opt-in 0-RTT fails, but no signaling on the server
> side (though still opt-in). My reasoning is based on experience with
> that "X-" server-side header trick; it misleads people into what's
> going on in a way that leads to brokenness. The application people
> think they only have to de-dupe the 0-RTT sections, but that's not true. 
>

I have been operating under the impression that at least some
application profiles for early data will require that certain
application protocol requests (e.g., something like HTTP POST) must be
rejected at the application layer as "not appropriate for 0-RTT data",
which requires the application to know if the request was received over
0-RTT data.

-Ben

---

From: Colm MacCárthaigh <[email-masked]>
Date: Tue, 13 Jun 2017 11:07:35 -0700

On Tue, Jun 13, 2017 at 11:04 AM, Benjamin Kaduk <[email-masked]> wrote:

> I have been operating under the impression that at least some application
> profiles for early data will require that certain application protocol
> requests (e.g., something like HTTP POST) must be rejected at the
> application layer as "not appropriate for 0-RTT data", which requires the
> application to know if the request was received over 0-RTT data.
>

That's a really good point; you've changed my mind. It's obviously a good
idea to return a 5XX to a POST over 0-RTT and that would need this.

-- 
Colm

---

From: Ilari Liusvaara <[email-masked]>
Date: Tue, 13 Jun 2017 21:35:36 +0300

> idea to return a 5XX to a POST over 0-RTT and that would need this.

I think the proper code to send is 400. The request is client error,
nor server error, so it is 4XX. And there does not seem to be suitable
4XX code, so it goes to catch-all client error code 400.

For HTTP/2, refusing the stream (sending stream error 7 without sending
server headers)  is also a good choice, as this should trigger a
retransmission of the offending request (POST requests failed by
refusing the stream are retryable).

-Ilari

---

From: Benjamin Kaduk <[email-masked]>
Date: Tue, 13 Jun 2017 13:47:41 -0500

> refusing the stream are retryable).

At least the http 0-RTT profile that I started writing was going to
allocate a new 4XX error code for this purpose.  I am under no pretense
that my version of such a document will resemble anything that finally
gets published, though.

-Ben

---

From: Andrei Popov <[email-masked]>
Date: Tue, 13 Jun 2017 18:57:05 +0000

Regarding RFC language, I think we could be more specific:

1. A TLS implementation SHOULD/MUST only send 0-RTT application data if the application has explicitly opted in;

2. A TLS implementation SHOULD/MUST only accept 0-RTT application data if the application has explicitly opted in;

3. When delivering 0-RTT application data to the application, a TLS implementation SHOULD/MUST provide a way for the application to distinguish it from the rest of the application data.

Or some better phrasing that our capable editor can craft😊.

Cheers,

Andrei

From: TLS [mailto:[email-masked]] On Behalf Of Benjamin Kaduk
Sent: Tuesday, June 13, 2017 11:48 AM
To: Ilari Liusvaara <[email-masked]>; Colm MacCárthaigh <[email-masked]>
Cc: [email-masked]
Subject: Re: [TLS] Separate APIs for 0-RTT

On Tue, Jun 13, 2017 at 11:04 AM, Benjamin Kaduk <[email-masked]><mailto:[email-masked]> wrote:

At least the http 0-RTT profile that I started writing was going to allocate a new 4XX error code for this purpose.  I am under no pretense that my version of such a document will resemble anything that finally gets published, though.

-Ben

---

From: Benjamin Kaduk <[email-masked]>
Date: Tue, 13 Jun 2017 14:03:22 -0500

+1 (with my vote for MUST).

-Ben

On 06/13/2017 01:57 PM, Andrei Popov wrote:
> 1. A TLS implementation SHOULD/MUST only send 0-RTT application data
> if the application has explicitly opted in;
>
> 2. A TLS implementation SHOULD/MUST only accept 0-RTT application data
> if the application has explicitly opted in;
>
> 3. When delivering 0-RTT application data to the application, a TLS
> implementation SHOULD/MUST provide a way for the application to
> distinguish it from the rest of the application data.
>
>  
>
> *From:*TLS [mailto:[email-masked]] *On Behalf Of *Benjamin Kaduk
> *Sent:* Tuesday, June 13, 2017 11:48 AM
> *To:* Ilari Liusvaara <[email-masked]>; Colm MacCárthaigh
> <[email-masked]>
> *Cc:* [email-masked]
>         On Tue, Jun 13, 2017 at 11:04 AM, Benjamin Kaduk <[email-masked]> <mailto:[email-masked]> wrote:
>
>          
>
> allocate a new 4XX error code for this purpose.  I am under no
> pretense that my version of such a document will resemble anything
> that finally gets published, though.
>
> -Ben
>

---

From: Ilari Liusvaara <[email-masked]>
Date: Tue, 13 Jun 2017 23:55:30 +0300

> 3. When delivering 0-RTT application data to the application, a TLS implementation SHOULD/MUST provide a way for the application to distinguish it from the rest of the application data.

First of these has to be MUST, or you get problems like I outlined
earlier.

And to implement checking for client only sending "safe" data, you need
the second and third.

-Ilari

---

From: Petr Špaček <[email-masked]>
Date: Wed, 14 Jun 2017 08:17:19 +0200

> And to implement checking for client only sending "safe" data, you need

I support MUST for the three points above.

-- 
Petr Špaček  @  CZ.NIC

---

From: David Benjamin <[email-masked]>
Date: Wed, 14 Jun 2017 19:55:24 +0000

On Wed, Jun 14, 2017 at 2:17 AM Petr Špaček <[email-masked]> wrote:

> implementation SHOULD/MUST provide a way for the application to distinguish
> it from the rest of the application data.
> >

The third one is not practical as one moves up layers. Instead, I believe
we can get the same benefit with a simpler signal.

TLS fundamentally is a transformation from a vaguely TCP-like transport to
another vaguely TCP-like transport. Consider TLS records: TLS could have
decided record boundaries were meaningful and applications can use it in
their framing layers, but instead TLS exposes a byte stream, because it
intentionally looks like TCP.

Of course, 0-RTT unavoidably must stretch the “vaguely”. Suppose there is a
semantically meaningful difference between 0-RTT and 1-RTT data. I can see
why this is attractive. It moves the problem out of TLS. But this signal is
pointless if applications don’t use it. If everyone did the following, we
haven’t solved anything:

if (InEarlyData()) {
  return EarlyDataRead(...);
} else {
  return NormalRead(...);
}

So we must consider this signal’s uses. Consider HTTP/2. The goal may be to
tag requests as “0-RTT”, because we wish to reject 0-RTT POSTs or so.

What if the server receives data with the 0-RTT boundary spanning an HTTP/2
frame? Is that a 0-RTT request? 1-RTT? Invalid? If I’m parsing that, I have
to concatenate, and we’re back to that if/else strawman above. HTTP2 is
arguably an easy case. Maybe my protocol is a compressed stream. Carrying a
low-level byte boundary through layers of application data parsing and
processing is not practical.

We could say that the application profile should modify the protocol to
reject such cases. Now, we’re taking on complexity in every protocol
specification and parser.

It also brings complexity on the sender side. Perhaps I am halfway through
writing an HTTP/2 frame and, in parallel, I receive the ServerHello. We
moved 0-RTT data out of a ClientHello extension way back in draft -07 so
0-RTT data can be streamed while waiting for ServerHello. This is
especially useful for HTTP/2 where reads and writes flow in parallel. This
means the sender must synchronize with the TLS socket to delay the 1-RTT
transition.

Now suppose the TLS stack receives that atomic data and it doesn’t fit in
the 0-RTT send limit. That won’t work, so the sender must query the early
data size and send over 1-RTT if it doesn’t fit.

Now suppose this HTTP request takes multiple frames to send. One can send
multiple HEADERS frames in HTTP/2. That won’t work, so we actually need the
synchronization to cover the entire request. Maybe my request has a
response body. We need to cover that too, and we need to know the size for
send limit purposes.

Now suppose assembling the HTTP request takes an asynchronous step after
connection binding. Maybe I’m signing something for tokbind. Maybe I have
some weird browser extension API. In this worldview, all the while, the
HTTP/2 multiplexer must lock out all other requests and the client
Finished, even if the ServerHello has already come in. That last one is
particularly nasty if the server is delaying an already-sent request until
the 1-RTT point (cf.
https://www.ietf.org/mail-archive/web/tls/current/msg21486.html).

Perhaps I keep the request assembling logic, HTTP/2 multiplexers, and TLS
sockets in different threads or processes. Now this synchronization must
span all of these. As one adds layers, the complexity grows.

The root problem here is we’ve changed TLS’s core abstraction. One might
argue all this complexity is warranted for 0-RTT. We are trying to solve a
problem here. But I think we can solve it simpler:

Our problem is a server wishes not to process some HTTP requests (or other
protocol units) at 0-RTT and needs to detect this case. So check a boolean
signal for whether the connection has currently passed the 1-RTT point
before any unsafe processing. A “1-RTT request” will always return 1-RTT. A
“0-RTT request” will usually return 0-RTT, but if it spans the boundary or
the processing pipeline was just slow, perhaps we don’t query until after
the client Finished. That’s actually fine. We get the replay protection we
need out of the client Finished.

This solves our problem without the complexity. Two APIs or an explicit
boundary is one way to expose this boolean, but this boolean, unlike the
hard boundary, is much easier to carry across higher-level protocol layers,
and does not imply impractical sender constraints.

David

---

From: Andrei Popov <[email-masked]>
Date: Wed, 14 Jun 2017 21:01:08 +0000

*   What if the server receives data with the 0-RTT boundary spanning an HTTP/2 frame? Is that a 0-RTT request? 1-RTT? Invalid?
It appears safe to treat such data as 0-RTT; only the application can make this call, and it needs info from the TLS stack to make this call.

  *   We could say that the application profile should modify the protocol to reject such cases. Now, we’re taking on complexity in every protocol specification and parser.
It would of course be preferable (some would argue, necessary) to secure 0-RTT application_data at the TLS layer, but so far we’ve failed to come up with a way to do so.

  *   Our problem is a server wishes not to process some HTTP requests (or other protocol units) at 0-RTT and needs to detect this case. So check a boolean signal for whether the connection has currently passed the 1-RTT point before any unsafe processing.
I think this would be a valid implementation of #3. There may be other implementation options that make more sense for a different TLS API or a different application protocol, so I’d rather not put a specific implementation option in the RFC.

Cheers,

Andrei

---

From: Brian Sniffen <[email-masked]>
Date: Wed, 14 Jun 2017 17:38:42 -0400

> since we ran into issues/complications with our implementation of dual APIs

I predict that those are exactly the places you're going to have later
security incidents.  In particular, the use case of fusing the early and
regular data into a single stream is going to lead to problems like
Triple Handshake.

The history of APIs where some bits have different secrecy, freshness,
or authentication than the rest says they all end up with bugs related
to user assumptions that blur away the difference.

-Brian

-- 
Brian Sniffen <[email-masked]>
/(* Akamai - Faster Forward

---

From: David Benjamin <[email-masked]>
Date: Wed, 14 Jun 2017 22:23:54 +0000

On Wed, Jun 14, 2017 at 5:01 PM Andrei Popov <[email-masked]>
wrote:

>
>    - What if the server receives data with the 0-RTT boundary spanning an
>    HTTP/2 frame? Is that a 0-RTT request? 1-RTT? Invalid?
>
> It appears safe to treat such data as 0-RTT; only the application can make
> this call, and it needs info from the TLS stack to make this call.
>

I actually would suggest it being 1-RTT. Or rather that a processing-time
check would count it as 1-RTT. Though I think I don't see anything
particularly wrong with it being 0-RTT either? *shrug*

>
>
>
>    - We could say that the application profile should modify the protocol
>    to reject such cases. Now, we’re taking on complexity in every protocol
> It would of course be preferable (some would argue, necessary) to secure
> 0-RTT application_data at the TLS layer, but so far we’ve failed to come up
> with a way to do so.
>
>
>
>    - Our problem is a server wishes not to process some HTTP requests (or
>    other protocol units) at 0-RTT and needs to detect this case. So check a
>    boolean signal for whether the connection has currently passed the 1-RTT
>    point before any unsafe processing.
>
> I think this would be a valid implementation of #3. There may be other
> implementation options that make more sense for a different TLS API or a
> different application protocol, so I’d rather not put a specific
> implementation option in the RFC.
>

Sure, if that is the interpretation of #3, that works fine for me. My
objection is to trying to pass along a hard separation between the two
chunks of data, to the point that the sender actually cares about some data
getting sent *before* the boundary. Caring about some data being sent after
the boundary is very reasonable and natural. POSTs, perhaps. Before is
complex because you must synchronize and defer the boundary. The trouble is
a before constraint gets implied by lots of stuff.

Specifically, I would propose a looser phrasing for #3:

When accepting 0-RTT as a server, a TLS implementation SHOULD/MUST provide
a way for the application to determine if the client Finished has been
processed.

That is, it is not the identity of the bytes that matters much. It's
whether the connection has been confirmed when you perform an unsafe
action. I believe this still satisfies the properties we want, but without
breaking standard interfaces. Very near the TLS stack, at the point where
the record boundary abstraction starts leaking (it's common to only give
you back a single record on read), either API is equally easy to provide.
The looser phrasing is needed for composition once you start going up a
layer or to.

David

---

From: Colm MacCárthaigh <[email-masked]>
Date: Wed, 14 Jun 2017 15:47:41 -0700

> The looser phrasing is needed for composition once you start going up a

Suppose a request, or a frame, spans two different client certificate
authentication contexts (or unauthenticated, and authenticated); how is
that handled today? or is it just forbidden?

-- 
Colm

---

From: David Benjamin <[email-masked]>
Date: Wed, 14 Jun 2017 23:31:19 +0000

> that handled today? or is it just forbidden?

In TLS 1.2, that would require renegotiation, which is not allowed in
HTTP/2. Our client and server implementations enforce this, for this and
other reasons.
http://httpwg.org/specs/rfc7540.html#rfc.section.9.2.1

Sadly, in Chrome as an HTTP/1.1 client, we are stuck with supporting this
mess, so we allow it at HTTP/1.1, but we will only accept client
certificate requests between request and response and further forbid all
handshake/application interleave. HTTP/1.1 without pipelining is just
barely simple enough that one can get away with all this. (In all other
cases, BoringSSL does not support renegotiation at all.)

The mess around trying to make byte boundaries semantically meaningful is
why I'd previously advocated leaving TLS 1.3 post-handshake auth to the
application profile, and why I advocated that it never be allowed in
HTTP/2. HTTP/2 should use something at its layer, possibly leaning on
exported authenticators. We have no plans to implement TLS 1.3
post-handshake auth in BoringSSL, but if we were ever to implement it, it
would likely be under the same constraints as renegotiation (off by
default, client-only, HTTP/1.1-only, and only at that one point in the
protocol with no interleave).

I assume what you're getting at here is that my 0-RTT and post-handshake
auth preferences are the opposite? :-) That's true. In the post-handshake
auth case, making the boundary insignificant to the higher-level protocol
isn't really feasible. Conversely, forbidding 0-RTT with HTTP/2 would be
rather unfortunate. But it seems the tight synchronization is not needed
here, so we don't need to go that route.

---

From: David Benjamin <[email-masked]>
Date: Wed, 14 Jun 2017 23:38:15 +0000

> here, so we don't need to go that route.

I should probably add the two scenarios aren't quite analogous. The 0-RTT
boundary is a problem even in HTTP/1.1-like protocols because there is a
send limit. If you're trying to fit the HTTP/1.1 request before the
boundary, you can't stream it (request body?). The size needs to be known
ahead of time.

David

---

From: Benjamin Kaduk <[email-masked]>
Date: Wed, 14 Jun 2017 20:28:26 -0500

On 06/14/2017 02:55 PM, David Benjamin wrote:
> On Wed, Jun 14, 2017 at 2:17 AM Petr Špaček <[email-masked]
> <mailto:[email-masked]>> wrote:
>
>
>
>     >> 1. A TLS implementation SHOULD/MUST only send 0-RTT application
>     data if the application has explicitly opted in;
>     >>
>     >> 2. A TLS implementation SHOULD/MUST only accept 0-RTT
>     application data if the application has explicitly opted in;
>     >>
>     >> 3. When delivering 0-RTT application data to the application, a
>     TLS implementation SHOULD/MUST provide a way for the application
>     to distinguish it from the rest of the application data.
>     >
> The third one is not practical as one moves up layers. Instead, I
> believe we can get the same benefit with a simpler signal.
>  
> TLS fundamentally is a transformation from a vaguely TCP-like
> transport to another vaguely TCP-like transport. Consider TLS records:
> TLS could have decided record boundaries were meaningful and
> applications can use it in their framing layers, but instead TLS
> exposes a byte stream, because it intentionally looks like TCP.
>  

It is good to keep this in mind, yes.

> Of course, 0-RTT unavoidably must stretch the “vaguely”. Suppose there
> is a semantically meaningful difference between 0-RTT and 1-RTT data.
> I can see why this is attractive. It moves the problem out of TLS. But
> this signal is pointless if applications don’t use it. If everyone did
> the following, we haven’t solved anything:
>  
> if (InEarlyData()) {

Sure, that's not very useful.
But, you can also come at it from a different point of view, namely that
right now, all applications just do NormalRead(), and EarlyDataRead()
for all protocols is simply not defined right now.  It "doesn't exist",
as it were.  We can of course learn from the ongoing experiments that
are using EarlyDataRead() for http, as part of designing what the actual
formal semantics should be.

> So we must consider this signal’s uses. Consider HTTP/2. The goal may
> be to tag requests as “0-RTT”, because we wish to reject 0-RTT POSTs
> or so.
>  
> What if the server receives data with the 0-RTT boundary spanning an
> HTTP/2 frame? Is that a 0-RTT request? 1-RTT? Invalid? If I’m parsing
> that, I have to concatenate, and we’re back to that if/else strawman
> above. HTTP2 is arguably an easy case. Maybe my protocol is a
> compressed stream. Carrying a low-level byte boundary through layers
> of application data parsing and processing is not practical.
>  

I think it would be valid to call it a 0-RTT request and reject it, and
also valid to call it a 1-RTT request and handle it -- it can be at the
implementation's discretion, and I don't know that TLS itself needs to
specify which.

> We could say that the application profile should modify the protocol
> to reject such cases. Now, we’re taking on complexity in every
> protocol specification and parser.
>  

But we're *already* taking on complexity in every protocol spec, by
requiring an application profile.  If you start from the assumption that
there is just one data stream and a transition point, then yes, this is
a lot of complexity on each application.  But I see the starting point
as that there only is NormalRead(), and adding EarlyDataRead() requires
a protocol extension and implementations of that extension.

So, we should be talking about how much complexity is required, not just
complaining that we can't drop ReadEarlyOrNormal() in wherever we
currently have NormalRead().

> It also brings complexity on the sender side. Perhaps I am halfway
> through writing an HTTP/2 frame and, in parallel, I receive the
> ServerHello. We moved 0-RTT data out of a ClientHello extension way
> back in draft -07 so 0-RTT data can be streamed while waiting for
> ServerHello. This is especially useful for HTTP/2 where reads and
> writes flow in parallel. This means the sender must synchronize with
> the TLS socket to delay the 1-RTT transition.
>  
> Now suppose the TLS stack receives that atomic data and it doesn’t fit
> in the 0-RTT send limit. That won’t work, so the sender must query the
> early data size and send over 1-RTT if it doesn’t fit.
>  
> Now suppose this HTTP request takes multiple frames to send. One can
> send multiple HEADERS frames in HTTP/2. That won’t work, so we
> actually need the synchronization to cover the entire request. Maybe
> my request has a response body. We need to cover that too, and we need
> to know the size for send limit purposes.
>  
> Now suppose assembling the HTTP request takes an asynchronous step
> after connection binding. Maybe I’m signing something for tokbind.
> Maybe I have some weird browser extension API. In this worldview, all
> the while, the HTTP/2 multiplexer must lock out all other requests and
> the client Finished, even if the ServerHello has already come in. That
> last one is particularly nasty if the server is delaying an
> already-sent request until the 1-RTT point (cf.
> https://www.ietf.org/mail-archive/web/tls/current/msg21486.html
> <https://urldefense.proofpoint.com/v2/url?u=https-3A__www.ietf.org_mail-2Darchive_web_tls_current_msg21486.html&d=DwMFaQ&c=96ZbZZcaMF4w0F4jpN6LZg&r=sssDLkeEEBWNIXmTsdpw8TZ3tAJx-Job4p1unc7rOhM&m=3V6ikOaK-5gWCk_BRcwmJlyqMlEcsRJK52cDehFYT3c&s=LNWu_RYl22MMryxWXGi7Srs23cM0aZzmfiOGWNn8D0k&e=>).
>  
> Perhaps I keep the request assembling logic, HTTP/2 multiplexers, and
> TLS sockets in different threads or processes. Now this
> synchronization must span all of these. As one adds layers, the
> complexity grows.
>  

Yes, this hypothetical situation is probably an unreasonable amount of
complexity.  But it presumes some sense of what HTTP/2 will want to
permit in early data, a concept which currently is formally undefined --
if HTTP/2 *really* wants to keep requests from splitting the 0/1-RTT
boundary, they could define some extremely restricted subset of
functionality that only allows a single self-contained frame in early
data.  I don't expect that to happen, but it would be a valid
application profile as far as TLS is concerned.

One might also consider an RPC protocol that wants to switch to using
TLS 1.3 for security benefits.  It would be natural for the application
profile for that to say that only certain RPCs are permitted in 0-RTT,
and a given marshalled request must not cross the 0/1-RTT boundary. 
This is clearly a silly example, as it's not really using a "TCP-like
stream" of data from TLS, but it is arguably less complex to have the
strict separation of semantics between 0- and 1-RTT data.
On the other hand, I'm not actually convinced that there are any
security benefits gained from the strict separation, so it remains at
the core an argument on aesthetics, and not much different from the
arguments that have been presented so far.

> The root problem here is we’ve changed TLS’s core abstraction. One
> might argue all this complexity is warranted for 0-RTT. We are trying
> to solve a problem here. But I think we can solve it simpler:
>  
> Our problem is a server wishes not to process some HTTP requests (or
> other protocol units) at 0-RTT and needs to detect this case. So check
> a boolean signal for whether the connection has currently passed the
> 1-RTT point before any unsafe processing. A “1-RTT request” will
> always return 1-RTT. A “0-RTT request” will usually return 0-RTT, but
> if it spans the boundary or the processing pipeline was just slow,
> perhaps we don’t query until after the client Finished. That’s
> actually fine. We get the replay protection we need out of the client
> Finished.
>  

I agree with Andrei that this scheme satisfies point #3 (or that if it
does not, we should rewrite condition #3 to make it a satisfactory
option).  Your proposal later in the thread seems about as good as the
original #3 to me (though I might wordsmith either a bit more).

> This solves our problem without the complexity. Two APIs or an
> explicit boundary is one way to expose this boolean, but this boolean,
> unlike the hard boundary, is much easier to carry across higher-level
> protocol layers, and does not imply impractical sender constraints.
>

It is easier to carry across layers, yes.  I don't think the TLS spec
should specify one of the two options under discussion (or a different
one); we should describe the properties that need to be available for
the application to make safe decisions.

I suspect that what Brian was trying to say is that this single boolean
being easier to carry across layers may also mean it is easier to write
code that ends up being unsafe.  I don't know; I don't think anyone
*can* know, and all we have is supposition and gut instincts.  I'm very
worried that no matter what APIs libraries offer, some high-profile
application/implementation is going to do something insecure and we have
another named vulnerability.  I'm also not sure that there's any useful
we discussion we can have right now on that front, since there's not
really hard data.  The closest we have seems to be Colm's security
analysis of 0-RTT data (and it would be great to have more such
analysis), which leaves me thinking that we should strictly disallow
"unlimited" (millions) of replays and set some specific limit.  (We'd of
course have to argue about what that limit is, which would not be fun,
but probably would be better than millions of replays.)

If I could try to synthesize something concise from these discussions,
it seems that a balance of flexibility and simplicity is to provide
semantics that let an application write data with a boolean to ndicate
that either it must be 1-RTT or the stack should feel free to use either
0- or 1-RTT as it sees fit, and to read data with a boolean to indicate
"whether it was 0-RTT or 1-RTT", most likely based on whether the client
Finished has been verified but potentially based on record-layer
information.

Circling back to my earlier question about how much complexity burden we
place on application profiles and implementations, this seems not too
bad.  An application currently uses a write API that is presumed to be
"must be 1-RTT" until changed, and locations writing 0-RTT-safe data can
switch to using a new API in a targetted fashion, leaving the TLS stack
to hold on to 0-RTT-unsafe data (and all subsequent data) until the
handshake is finished.  The reading story is a little less simple, as
the current API doesn't really have the requisite semantics, so the
application is living in a gray zone.  It seems that a prerequisite for
making the API call that says "enable 0-RTT on this server socket" is to
convert all read calls to the new form and reject 0-RTT versions of
structures that are not permitted by the profile, the converse of the
write side.  But, it seems that doing anything less is unsafe, and
perhaps this is the minimum viable level of complexity.  I think it at
least addresses the concerns you raise in the context of your HTTP/2
example.  Libraries and applications will of course be free to use
separate functions for read/write-early/regular if they want, but I
think it is fine for TLS to structure the requirements on
implementations in such a way that the simpler API is permitted.

It is probably also useful to provide guidance to authors of application
profiles for how to incorporate 0-RTT data into their protocol.  That
could be something like "philosophically, TLS provides a TCP-like stream
of data to the application, but until the client Finished is verified
there are weaker security properties than afterwards, and certain
classes of traffic should be deferred until after that point".  It could
also be something like "TLS provides two qualitatively different streams
of data with different security properties; one has a finite length
limit dictated by the server and should only receive a limited class of
protocol messages of bounded size, and the other resembles historical
TLS and is safe for all traffic."  I don't actually know what consensus
we could come to on that point, or if the chairs are interested in
running a contentious consensus call on that question -- after all, a
TLS 1.3 spec could be considered complete without such guidance.  But,
leaving it out might just lead to the argument being rehashed over and
over again for each protocol.  The deciding point is probably whether we
want to ensure that we can support complicated protocols that will not
want to limit themselves to the stringent requirements of the latter
formulation, such as your HTTP/2 example above.  If we can do that
safely (in terms of not leaving applications wide open to future
security holes), we would end up in the first (single-stream with state
change) option.  I'm just not sure how I can be comfortably convinced
that we can do so safely.  Right now, I'm something like uncomfortably
mostly convinced, which is not a very pleasant feeling.  In a world of
millions of replays, maybe it's safer to tell applications that 0-RTT is
a distinct separate thing that needs to go in its own bucket.

-Ben

---

From: Martin Thomson <[email-masked]>
Date: Fri, 16 Jun 2017 10:16:12 +1000

On 15 June 2017 at 08:23, David Benjamin <[email-masked]> wrote:
> When accepting 0-RTT as a server, a TLS implementation SHOULD/MUST provide a
> way for the application to determine if the client Finished has been
> processed.

I'm going to throw my support behind this distinction.  Though I would
phrase this more simply as "the handshake is complete".

---

From: Timothy Jackson <[email-masked]>
Date: Fri, 23 Jun 2017 02:10:32 +0000

+1 and a preference for MUST, just so people understand the importance.

Since we're agreed that 0-RTT data and 1-RTT data have (almost) the same security properties once the handshake completes, it seems to me, unless I've missed something, that a lot of protocols will accept 0-RTT but withhold the response until after the handshake completes. I expect this massively simplifies the analysis the for the app developers.

Clientdata = readData()
Reply = CreateReply(client data); //time intensive operation (e.g. Database, CDN cache lookup)

While(!clientFinished())
Wait(); //do nothing until 1-RTT finished

Send(reply)

This has the benefit of allowing slow lookups/processing to happen against 0-RTT, while delaying the "risky actions" until after 1-RTT. If I'm not mistaken, it would also make timing attacks harder since any cache misses would be at least partly masked by the time required for the 1-RTT handshake.

Dual streams seems to just add complexity here. What I really care about as a developer is whether I can fully trust the 0-RTT data, which is determined by whether the handshake is finished.

Cheers,

Tim
--
Tim Jackson

Senior Product Security Architect, MobileIron Inc.

________________________________

From: "Martin Thomson" <[email-masked]<mailto:[email-masked]>>
Date: Thursday, June 15, 2017 at 5:16:29 PM
To: "David Benjamin" <[email-masked]<mailto:[email-masked]>>
Cc: "[email-masked]" <[email-masked]<mailto:[email-masked]>>
Subject: Re: [TLS] Separate APIs for 0-RTT

---

From: Benjamin Kaduk <[email-masked]>
Date: Thu, 22 Jun 2017 21:30:57 -0500

On 06/22/2017 09:10 PM, Timothy Jackson wrote:
> Since we're agreed that 0-RTT data and 1-RTT data have (almost) the
> same security properties once the handshake completes, it seems to me,
> unless I've missed something, that a lot of protocols will accept
> 0-RTT but withhold the response until after the handshake completes. I
> expect this massively simplifies the analysis the for the app developers.
>
> Clientdata = readData()
> This has the benefit of allowing slow lookups/processing to happen
> against 0-RTT, while delaying the "risky actions" until after 1-RTT.
> If I'm not mistaken, it would also make timing 

This is still the server taking action on 0-RTT data before the
handshake completes, using resources on potentially spoofed input. 
Caution is needed to not utilize excessive server resources in such cases.

-Ben

> attacks harder since any cache misses would be at least partly masked
> by the time required for the 1-RTT handshake.
>
> Dual streams seems to just add complexity here. What I really care
> about as a developer is whether I can fully trust the 0-RTT data,
> which is determined by whether the handshake is finished.

---

From: Yoav Nir <[email-masked]>
Date: Fri, 23 Jun 2017 06:43:37 +0300

> On 23 Jun 2017, at 5:10, Timothy Jackson <[email-masked]> wrote:
> 

This assumes that CreateReply() has no side effects. It’s valid as long as all actions done by the server in response to the client data is getting stuff from a database.

> From: "Martin Thomson" <[email-masked] <mailto:[email-masked]>>
> To: "David Benjamin" <[email-masked] <mailto:[email-masked]>>
> Cc: "[email-masked]" <[email-masked] <mailto:[email-masked]>>
> https://www.ietf.org/mailman/listinfo/tls <https://www.ietf.org/mailman/listinfo/tls>
> [email-masked] <mailto:[email-masked]>
> https://www.ietf.org/mailman/listinfo/tls <https://www.ietf.org/mailman/listinfo/tls>