Subject: HTTP profile for TLS 1.3 0-RTT early data?

From: Erik Nygren <[email-masked]>
Date: Wed, 10 May 2017 17:57:08 -0400

Has anyone started working on an I-D for an HTTP (HTTP 1.1 and HTTP/2)
profile for TLS 1.3 0-RTT early data usage?  We've talked about it briefly
in meetings, but it seems like a work area that may need progress soon
before people start implementing things differently.  This same profile
same likely applies to early data for HTTP-over-QUIC as well.

Some topics to cover seem to be:

1) What HTTP requests can be safely sent by clients over 0-RTT early data
(with the inherent replay-ability and security considerations) ?  Mark
started some discussion here:  https://mnot.github.io/I-D/httpbis-retry/

2) An HTTP response status code for "Early data not supported for request:
retry request with 1-RTT".  This will be needed for cases where an HTTP
server isn't doing TLS termination and thus may have accepted early data
but still want to force the client to always send the request via 1-RTT
(eg, for a POST method).  This is especially critical if clients and
servers make different choices for how to handle #1.  This may want some
general considerations around how it is better to reject early data during
the handshake, but not all server implementations will be able to do this.

3) Being very explicit on how to handle early data that is rejected and
forced to be resent following connection establishment (ie, when the server
forces a client into 1-RTT mode by rejecting the early data).  A worst case
would be if a server actually handled the early data anyways but the client
thought the server had rejected it.  In HTTP/1.1 this could result in them
being off-by-one in requests/responses (and could lead to some HTTP Request
Smuggling style bugs and vulnerabilities.)

4) Perhaps a general HTTP request header for indicating that requests were
received via 0RTT early data (such as Cloudflare's "Cf-0rtt-Unique"
header).  This would be useful for both load balancers communicating to
application servers, as well as for CDNs communicating to origin servers.
This would presumably interact with #2 in cases where the appserver or
origin wanted to force a client to retry via 1-RTT.

5) ...  I'm sure there is more ...

Without some agreed on behaviors this could quickly become a mess.  For
example, if some clients decide to send POSTs via early data while some
servers assume clients will never do this and return 403 errors out of
paranoia.

Best, Erik

---

From: Mark Nottingham <[email-masked]>
Date: Thu, 11 May 2017 10:23:12 +1000

Hi Erik,

> On 11 May 2017, at 7:57 am, Erik Nygren <[email-masked]> wrote:
> 
> 
> Has anyone started working on an I-D for an HTTP (HTTP 1.1 and HTTP/2) profile for TLS 1.3 0-RTT early data usage?  We've talked about it briefly in meetings, but it seems like a work area that may need progress soon before people start implementing things differently.  This same profile same likely applies to early data for HTTP-over-QUIC as well.

[chair hat] Yes. This has been a perennial back-burner topic, and we probably need to move it forward, so we don't block TLS or QUIC.

[personal hat below]

> 1) What HTTP requests can be safely sent by clients over 0-RTT early data (with the inherent replay-ability and security considerations) ?  Mark started some discussion here:  https://mnot.github.io/I-D/httpbis-retry/

... along with concurrent list discussion.

My impression of the outcome there is that HTTP applications that depend on at-most-once delivery have to assure that at the application layer; HTTP provides no such guarantee (even though it implies it with the lack of idempotence for some methods). 

That said, if 0RT mechanisms allow unbounded replays, it might stress (and break) existing application retry protections; my understanding is that the current 0RT proposals do scope replays, however.

Does that seem like where we're at?

> 2) An HTTP response status code for "Early data not supported for request: retry request with 1-RTT".  This will be needed for cases where an HTTP server isn't doing TLS termination and thus may have accepted early data but still want to force the client to always send the request via 1-RTT (eg, for a POST method).  This is especially critical if clients and servers make different choices for how to handle #1.  This may want some general considerations around how it is better to reject early data during the handshake, but not all server implementations will be able to do this.

I wonder if this would be better expressed as a H2/QUIC error code. I also wonder whether it'll be really interoperable; even one client that doesn't implement it correctly (or at all) will be a big disincentive for its use.

If an origin doesn't have robust retry/replay protection in place for non-idempotent requests, it seems operationally simpler and safer for them to disable 0RT, rather than refusing it on a request-by-request basis. That's the discussion I think we should have here...

> 3) Being very explicit on how to handle early data that is rejected and forced to be resent following connection establishment (ie, when the server forces a client into 1-RTT mode by rejecting the early data).  A worst case would be if a server actually handled the early data anyways but the client thought the server had rejected it.  In HTTP/1.1 this could result in them being off-by-one in requests/responses (and could lead to some HTTP Request Smuggling style bugs and vulnerabilities.)

I'd hope that TLS/QUIC are explicit here, and that we wouldn't need to be; that's the beauty of layered protocols :)

> 4) Perhaps a general HTTP request header for indicating that requests were received via 0RTT early data (such as Cloudflare's "Cf-0rtt-Unique" header).  This would be useful for both load balancers communicating to application servers, as well as for CDNs communicating to origin servers.  This would presumably interact with #2 in cases where the appserver or origin wanted to force a client to retry via 1-RTT.

Seems reasonable. Then you could Vary on it. *evil grin*

Cheers,

--
Mark Nottingham   https://www.mnot.net/

---

From: Patrick McManus <[email-masked]>
Date: Wed, 10 May 2017 20:34:30 -0400

On Wed, May 10, 2017 at 8:23 PM, Mark Nottingham <[email-masked]> wrote:

> > 4) Perhaps a general HTTP request header for indicating that requests
> were received via 0RTT early data (such as Cloudflare's "Cf-0rtt-Unique"

this has some terrible hpack interactions if replay isn't really replay of
the same byte stream.

---

From: Patrick McManus <[email-masked]>
Date: Wed, 10 May 2017 20:36:53 -0400

belay that comment - I misunderstood the suggestion to apply to the request
generation, instead it is an intermediary injected header based on how it
was received. sorry for the misunderstanding.

---

From: Willy Tarreau <[email-masked]>
Date: Thu, 11 May 2017 07:33:15 +0200

Hi Mark,

On Thu, May 11, 2017 at 10:23:12AM +1000, Mark Nottingham wrote:
> If an origin doesn't have robust retry/replay protection in place for
> non-idempotent requests, it seems operationally simpler and safer for them to
> disable 0RT, rather than refusing it on a request-by-request basis. That's
> the discussion I think we should have here...

That's exactly the situation I'm facing for now with haproxy. A few
users have asked us to support 0RTT and by lack of way to 1) decide
which requests are really safe, and 2) tell the client it must replay
them using 1RTT, for now I refused to enable it. The load balancer
and the origin server will have a different view of the acceptability
of 0RTT, and all the chain must be able to accept or reject them, and
let the client retry.

I tend to think that a 4xx status code would make sense and would be
useful to pass the verdict back to the client. For example we could
return "418 not idempotent".

Willy

---

From: Stefan Eissing <[email-masked]>
Date: Thu, 11 May 2017 10:19:58 +0200

> Am 11.05.2017 um 07:33 schrieb Willy Tarreau <[email-masked]>:
> 
> Hi Mark,
> 

Even the "origin server" might not be aware what the application's
committal and guarantee here is. 

My thoughts for an implementation is:

- it has to work without the "upper" layer / next hop being aware of it
- it has to fail in a defined HTTP way. The HTTP request is tagged as
  possibly replayed, regardless of the actual transport. The answer
  needs to also work on that transport.
- The negative answer to a 0-RTT request might come early, might come
  late. For h2, other streams might have been opened, even answered,
  in the meantime.
- The sender selecting 0-RTT should only do so, if it understands the
  retry answer. (Once that is defined)
- The sender may well want to select 0-RTT only if it considers the
  data to be safe for replays *and* it expects the server to come to
  the same conclusion.
- So, ideally, sender and receiver have the same notion about what HTTP 
  data is acceptable for 0-RTT.

-Stefan

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 11 May 2017 20:31:04 +0900

>   data is acceptable for 0-RTT.

This is an interesting discussion!

I believe that there is no need for us to require a _client_ to resend
a HTTP request, even in case it sends a HTTP request in 0-RTT and then
turns out that the application running behind tells the "origin
server" that it cannot handle 0-RTT request.

IMO what the origin server should do is buffer the 0-RTT request
(note: in TLS 1.3, a server can cap the size of 0-RTT data), and if
the application refuses to handle the request due to the fact that it
has been sent in 0-RTT, wait until the client proves itself to be a
legitimate client (by sending an 1-RTT data), and then resend the
buffered request to the application.

In HTTP/2, the proof can be obtained by sending a PING frame from the
server after sending ServerFinished message (of TLS 1.3) and waiting
for the response to the PING frame.

So, while I agree that it is beneficial to have an agreement on how
the interaction scheme between the origin server and the application
running behind (possibly as an informational RFC), I do not see a
strong reason that we need to introduce some kind of profile due the
introduction of 0-RTT data in TLS 1.3.

-- 
Kazuho Oku

---

From: Stefan Eissing <[email-masked]>
Date: Thu, 11 May 2017 13:33:46 +0200

> Am 11.05.2017 um 13:31 schrieb Kazuho Oku <[email-masked]>:
> 

Hmm, how many RTTs will this proof take?

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 11 May 2017 20:35:33 +0900

> Hmm, how many RTTs will this proof take?

1RTT. The latency will be the same as when the client did not use 0-RTT.
OTOH, the obvious benefit of the proposed approach is less use of
bandwidth since there is no need for a client to resend the request.

-- 
Kazuho Oku

---

From: Stefan Eissing <[email-masked]>
Date: Thu, 11 May 2017 13:37:37 +0200

> Am 11.05.2017 um 13:35 schrieb Kazuho Oku <[email-masked]>:
> 

Ok, so when the server sees the first non-0-RTT byte from the client, the
handshake was accepted and the 0-RTT data can be regarded as genuine.

The PING merely triggers the data transfer in case the client does not
send anything on its own, I assume.

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 11 May 2017 20:38:47 +0900

> send anything on its own, I assume.

Yes. That is what I have been thinking. Thank you for clarifying that.

-- 
Kazuho Oku

---

From: Stefan Eissing <[email-masked]>
Date: Thu, 11 May 2017 13:46:10 +0200

> Am 11.05.2017 um 13:38 schrieb Kazuho Oku <[email-masked]>:
> 

Excellent, very nice thinking, Kazuho! This strategy really solves any
interop issues for origin servers. Nice.

In case of proxies/intermediates, the case is different, I think. 

If a proxy receives 0-RTT data, it can only make direct use of it inside
upstream 0-RTT data on a new connection. If it sends 0-RTT data on an
established connection, the server cannot detect the replay weakness.

A proxy, on forwarding 0-RTT data on an established connection, must first
verify the client handshake.

Does that make sense?

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 11 May 2017 20:55:33 +0900

> verify the client handshake.

Ah. Thank you for noticing that

I think that you are right in pointing out that the proposed scheme
does not work well if we have HTTP proxies relaying HTTPS requests.

-- 
Kazuho Oku

---

From: Willy Tarreau <[email-masked]>
Date: Thu, 11 May 2017 15:51:51 +0200

> verify the client handshake.

It's even worse. Proxies/load balancers have to take routing decisions inline
and immediately. It's not realistic to hold a request pending and want for
some possibly subsequent TLS layer events while you're blocking at L7. I
think we're adding quite a number of layering violations here unfortunately
:-(

In short when you receive this request that you know was transported over
0-RTT somewhere in the chain, you have to give a verdict : pass or reject.
Also very often the TLS decapsulation is handled by a TLS offloader which
is not HTTP-aware. The most HTTP aware ones are able to add a header
before passing the request to the next layer, which is pretty similar to
the load balancer situation. But clearly the origin server can have multiple
layers of intermediaries between it and the client.

Also in practice, 0-RTT will be effective at multiple places :

          0RTT      0RTT     clear/TLS+keep-alive
  client  ----> CDN ----> LB ----> server

In this chain the CDN will not know whether 0RTT is valid or not for the
client, and it will pass the request using 0RTT again to the origin's edge
made of the load balancer which has no clue either about the validity of
0RTT here. Only the origin server will possibly know. But 0RTT will have
been used twice in this diagram. We're in the exact situation where we
want any agent in the chain to be able to say "4xx retry this please"
so that the closest agent to the client does it first and saves a lot on
RTT to fix the problem.

Willy

---

From: Willy Tarreau <[email-masked]>
Date: Thu, 11 May 2017 16:02:48 +0200

On Thu, May 11, 2017 at 03:51:51PM +0200, Willy Tarreau wrote:
(...)
> so that the closest agent to the client does it first and saves a lot on

Another point is that given the problem here is to properly map HTTP
semantics on top of a potentially unsafe transport, I really think it
totally makes sense to use HTTP semantics (such as status codes) to
address it. It also means for example that clients will be able to
send POST requests (or those more than a few MSS) with Expect:100
letting the other end decide whether or not to return a 100 or a 4xx,
so for zero extra cost compared to the 1-RTT we can benefit from some
optimally safe opportunistic attempts to use 0-RTT most of the time
in a way where safety can be decided at the same place the action will
be taken.

Willy

---

From: Erik Nygren <[email-masked]>
Date: Thu, 11 May 2017 10:21:07 -0400

> in a way where safety can be decided at the same place the action will

I agree we certainly will need an HTTP status code here and require
clients that implement sending 0-RTT early data to understand that status
code
before sending the early data.   (This may be one reason there's urgency to
specify
something soon before clients start implementing without a specification.)

I really like Kazuho's idea for HTTP/2 connections.  There may also be a way
for H2 proxies to translate between that and status codes.  (ie, if they
get the HTTP 4xx
status code from upstream they can make sure that an 1-RTT connection is
established
and verified, possibly sending an H2 PING to do so, before resending the
HTTP request
as "early data verified by 1RTT handshake".)

Note that all of this only works for providing safety against replay
if the decision on when to require 1-RTT is deterministic for a given
request.
For example, doing an H2 PING along with the server_hello and waiting for
its
response before processing the request only helps if this will always happen
for a given early_data request.  The early data can always be replayed so
just
because it gets verified by one full 1-RTT handshake doesn't mean that same
early data can't just get replayed without a 1-RTT handshake by an attacker
immediately afterwards.

       Erik

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 11 May 2017 23:27:46 +0900

> layers of intermediaries between it and the client.

It is true that TLS 1.3 introduces two class of data: 0-RTT and 1-RTT.
Applications need to distinguish between the two if they are willing
to handle 0-RTT data.

I believe that 0-RTT data is a nice feature of TLS 1.3, and would not
like to see its use constrained by how some deployments are designed.

That said, I would argue that TLS offloaders are already sending
metadata (e.g., the cipher suite being used) to the backend server,
and that it could possibly be extended so that the applications
running behind the offloaders can distinguish between 0-RTT and 1-RTT
data.

One simple method (but likely to be effective way) of sending such
metadata would be to advertise a small 0-RTT window (e.g., one or two
MTUs) from the TLS offloader, and if client uses 0-RTT data, postpone
establishing the connection from the TLS offloader to the backend
until the offloader receives all the 0-RTT data (actually, I believe
that there is high chance that you would be receiving 0-RTT data while
doing Diffie-Hellman operations to resume the connection). Once all
the 0-RTT data are received, the offloader will connect to the
backend, send a chunk of metadata including the selected cipher suite
_and_ the amount of 0-RTT application data that would follow the
metadata.

> so that the closest agent to the client does it first and saves a lot on

My view is that this is an issue behind origin and that it should be
handled as such, instead creating a profile that requires a user-agent
to resend a HTTP request. I am not against defining how servers
running behind the origin should resend the requests (for better
interoperability).

-- 
Kazuho Oku

---

From: Willy Tarreau <[email-masked]>
Date: Thu, 11 May 2017 16:38:22 +0200

> something soon before clients start implementing without a specification.)

Yes definitely!

> as "early data verified by 1RTT handshake".)

For me the H2 ping is mainly a challenge on the connection, equivalent
the RTT achieved by Expect+100 do. On a protocol point of view that's
fine. But I hardly see how to make this thing pass over multiple layers.
You want to validate end-to-end (or at least more than hop-by-hop) and
the ping will only act to the next hop, while the problem caused by
the client and detected by the origin across the LB is still not solved.

> if the decision on when to require 1-RTT is deterministic for a given

That's why the status code makes it possible to go as far as we want.

Ideally I'd prefer a new Expect: + 100. But that's not doable without
a body. It would look like this :

  client                             server
  ------                             ------
  POST /some-url HTTP/1.1
  Host: some-host
  Expect: replay-safe
                                     HTTP/1.1 418 unsafe try again
                                 or
                                     HTTP/1.1 100 continue
  ... body ...

On 2.0 we possibly have other options like sending a window update
or priority frame (just thinking), but none of these would easily
address the end-to-end nature of what we want to validate :-/

> early data can't just get replayed without a 1-RTT handshake by an attacker

One more reason for asking the client to retransmit everything we decided
is not trusted.

Willy

---

From: Willy Tarreau <[email-masked]>
Date: Thu, 11 May 2017 16:51:03 +0200

> like to see its use constrained by how some deployments are designed.

Don't get me wrong, I think so as well and would like to be able to
enable it. But I don't want to give my users a totally unsafe thing
that they have no control over. What I know for sure is that most of
our users have no idea on the load balancer about the safety of the
requests they're forwarding to their hosted applications. Some of
them may apply some heuristics like block POSTs or whatever but as
mentionned by Erik its not enough and too much at the same time.

> running behind the offloaders can distinguish between 0-RTT and 1-RTT

Yes, which is why I want to pass this info so that the server sends
the signal back :-) In short I'm *willing* to make it possible for
hosting providers to offer 0-RTT to customers who request based on
their explicit demand for it after they understand the impacts, but
not by default to everyone.

> _and_ the amount of 0-RTT application data that would follow the

I may be missing something obvious to you but I don't see how that
fixes the problem.

What we do in haproxy is that we always wait for a full, perfectly
valid request before passing it. Once we have it, we apply some
policy rules based on multiple L7 criteria (host header, URI,
cookies, headers, you name it) and decide where to send it, or
to redirect or to reject it. So we already wait for these data.

But my understanding of 0-RTT is that we can receive a replayed
request which will match all of this entirely. So it's not a matter
of length. What I'd want instead is to ensure that at the moment I
pass it to the server I can tell the server "received over 0-RTT"
or "received over 1-RTT", and the server be able to take the
appropriate decision.

> to resend a HTTP request.

But in terms of applications, what does it mean to you to "handle it as
such" ? The application receives an "OK" validation for a sensitive operation
over 0-RTT, it cannot realistically ask the user "Please your OK was
received over an unsafe channel, would you please confirm again ?".
We need to provide a way for the server to validate the contents one way
or another

> I am not against defining how servers

That's all I'm asking for :-)

Willy

---

From: Kazuho Oku <[email-masked]>
Date: Fri, 12 May 2017 00:30:18 +0900

> I may be missing something obvious to you but I don't see how that

The kind of the deployment I was considering was one that deploys a
TLS terminator that just decrypts the data and sends to backend (i.e.
TLS to TCP, not an HTTP reverse proxy). My idea was that you could add
a new field to the Proxy Protocol that indicates the amount of 0-RTT
data that follows the header of the PROXY Protocol.

> We need to provide a way for the server to validate the contents one way

What I am arguing against is creating a specification that suggests an
user agent (not an intermediary) to resend HTTP request on a 0-RTT
enabled TLS connection. 0-RTT is an optimization for connections with
big latency. Such connections typically cost certain amount of money.
Asking the user agent to resend a HTTP request that has been sent over
a 0-RTT connection not only eliminates the merits provided by 0-RTT
but also doubles the consumed bandwidth. That does not sound right to
me. I think the right advise we should provide for such case is: turn
off 0-RTT.

Between the origin and the application server, I think that we can and
also need to resend HTTP requests. The cost of the network is
typically lower than the connection to the end user. Connections
between the two can be kept alive for much longer time to avoid the
overhead of connection establishment. And as you have discussed, the
act of coalescing incoming requests from multiple connections
inevitably means that we need to notify the application server if each
HTTP request has been received entirely by 0-RTT by using a HTTP
header (hence resend becomes unavoidable).

-- 
Kazuho Oku

---

From: Willy Tarreau <[email-masked]>
Date: Thu, 11 May 2017 18:25:21 +0200

> data that follows the header of the PROXY Protocol.

OK I got it now. However that would be very wrong from an HTTP point
of view as we'd be passing byte counts of header data, which everyone
transforms along the chain so that's not relevant anymore when it reaches
the application (which has no such notion by the way since it accesses
its headers from an array).

> me. I think the right advise we should provide for such case is: turn

In fact here I disagree. It doesn't eliminate this if it happens rarely
and allows the client to aggressively make use of 0-RTT because it knows
that if that's not accepted it will work. It's better than having the
client censor itself saying "this is a POST, it's not reasonable". If
the server consumes mostly POSTs and 90% of them are idempotent, the
client will benefit from 90% 0-RTT and 10% retries instead of 100%
1-RTT, which overall is still a win.

Also the server response could provide hints regarding what is allowed
or not. It may reply "0-RTT: {never,non-post,get,not-this-one}" etc so
that the client can better adapt to ensure there are very few retries.

I'd rather avoid to reproduce the HTTP/1 pipelining trouble we've all
met, by seeing a powerful performance improvement almost always disabled
because there's no way to recover from a failure.

> typically lower than the connection to the end user.

But it's also where it's the hardest because that's where you'd start
to have to buffer everything in order to retry, which very often is
not technically feasible, or requires limiting receipt to what can
be retransmitted, and sending congestion back to the client side
while waiting for the server's acceptance.

> Connections

Absolutely.

Willy

---

From: Ilari Liusvaara <[email-masked]>
Date: Thu, 11 May 2017 20:15:42 +0300

> return "418 not idempotent".

Reading the cryptographer's concerns regarding 0-RTT, the main ones
seem to be:

- Possible lack of strong anti-replay. Possibly leading to >10^6 (!!!)
  replays. 
  * This is enough to exploit pretty subtle side-channel attacks. And
    these attacks are at least extremely difficult to defend against.
  * This causes severe problems for high-performance rate-limiting
    systems.
- Even one replay messing up "try, wait, readback, retry" clients.
  * These clients try to write an entry, if it fails, wait, try to
    read the entry, and if not there, retry.
- Unordered replay: The server gets the 0-RTT request client deemed
  "failed" after the client's retry.
  * The retry is likely 1-RTT!
- Replaying requests to different cache nodes in order to leak
  information about what was requested
  * Prime cache using replayed requests.
  * Probe cache using known requests.

-Ilari

---

From: Martin Thomson <[email-masked]>
Date: Fri, 12 May 2017 09:53:19 +1000

> handshake was accepted and the 0-RTT data can be regarded as genuine.

This is basically what Cloudflare have deployed.  They hold some
requests until the handshake finishes.  Some requests are forwarded
(GET, OPTIONS, and HEAD are probably fine), but they are marked as
being potentially replayed.

What Cloudflare's solution misses (for obvious reasons) is a signal
from the origin that the 0-RTT request can be retried.  h2 has that
signal: RST_STREAM+REFUSED_STREAM
<http://httpwg.org/specs/rfc7540.html#REFUSED_STREAM>, but HTTP
doesn't in the general case.

It's almost like 1-RTT in terms of performance, but with the small
optimization you get from being able to use the first RTT to send data
to the gateway.

(As for Willy's concern, maybe consider the request "incomplete" until
the handshake completes, even if it is not.)

---

From: Willy Tarreau <[email-masked]>
Date: Fri, 12 May 2017 07:28:14 +0200

Hi Martin,

> the handshake completes, even if it is not.)

It's really not easy given that the L7 layer is fed by a decrypted data
stream of bytes, and notified about progress when new bytes appear.
Furthermore, today we have many people who split TLS decryption and
L7 processing into different processes in order to optimize latency
so the L7 process has no visibility on the TLS layer after it receives
the request.

And we're back to the original point : if we do this for all requests,
we're just doing 1-RTT 100% of the time in the end.

Right now it *seems* to me that 0-RTT is very interesting in theory,
possibly interesting in some specific client-to-server deployments, and
practically not usable in multi-tier architectures due to the absence of
end-to-end signaling. But I'm convinced we can improve the situation :-)

Willy

---

From: Martin Thomson <[email-masked]>
Date: Fri, 12 May 2017 15:38:10 +1000

> we're just doing 1-RTT 100% of the time in the end.

Two reasons why it isn't, or might not be:

1.The suggestion was that you would identify a subset of requests that
get passed on.  That might be GET, HEAD and OPTIONS if you want to be
conservative.

2. The data has arrived in less than 1-RTT.  That you don't act on it
later doesn't mean that you haven't at least saved on the time it
takes to move those bytes.  The downside being that your handshake
completion now sits behind a longer line of bytes that are all subject
to loss and therefore head of line blocking.

---

From: Stefan Eissing <[email-masked]>
Date: Fri, 12 May 2017 10:21:16 +0200

> Am 12.05.2017 um 07:38 schrieb Martin Thomson <[email-masked]>:
> 

I am more and more thinking that we need no safe-set definition. It seems
that clients and servers will have specific ideas about what to send and
what to accept in early data.

What a client wants to send early, might not be what the server is willing
to process right away. Servers can always chose to not enable early data at 
all, or, in case of doubt about the data, wait for the handshake to complete.

At least in h2 it seems to be clear how to do that, is there a way for 
http/1.1 to do the same? Is there maybe a generic TLS1.3 way to do that?

If we can define a way for TLS receivers to wait for the handshake, then
there is no need to expect failure from early data, except servers not
following this strategy or being reconfigured between sessions.

Or?

-Stefan

---

From: "Poul-Henning Kamp" <[email-masked]>
Date: Fri, 12 May 2017 08:21:36 +0000

--------
In message <[email-masked]>

>data that follows the header of the PROXY Protocol.

Can you even know that at the time you send the PROXY protocol header ?

-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
[email-masked]         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe    
Never attribute to malice what can adequately be explained by incompetence.

---

From: Kazuho Oku <[email-masked]>
Date: Fri, 12 May 2017 20:36:09 +0900

> Can you even know that at the time you send the PROXY protocol header ?

Actually, you do not even need to know or transmit the exact amount of
the 0-RTT data when you transmit the PROXY protocol header. What you
need to transmit is the amount of replayable data.

I believe that there could be several ways, but something like below
should work.

* postpone sending the PROXY Protocol header until receiving the first
flight of 0-RTT data
* include the size of the 0-RTT data being received as an attribute in
the PROXY Protocol header that is sent to the backend
* postpone forwarding the rest of 0-RTT data to the server until the
handshake succeeds

The assumption behind the approach is that the 0-RTT data from the
client (e.g. HTTP request) will likely fit in one packet, possibly in
the same packet that carries ClientHello (or in a few number of
packets that will arrive while the server does DH operation). The
approach also uses the fact that a successful handshake can be used to
rule out the possibility of the 0-RTT data being replayed by an
attacker.

-- 
Kazuho Oku

---

From: Martin Thomson <[email-masked]>
Date: Sun, 14 May 2017 11:02:42 +1000

> what to accept in early data.

Yes, this is going to be subjective.  The great thing about the
strategy that we have here is that nothing is vulnerable to replay
unless both client and server agree to that risk.

It's much trickier for intermediaries, who make this decision without
a great deal of information, but the calculus is still fundamentally
the same.

---

From: Benjamin Kaduk <[email-masked]>
Date: Mon, 15 May 2017 18:36:25 -0500

[late to the party; pulling snippets from multiple messages]

> unless both client and server agree to that risk.

I think in order to strictly get the property that the server has agreed
to that risk we need the new response code "try again 1-RTT", to
accommodate servers that cannot buffer the entire 0-RTT request or other
edge cases.  But I don't think that is controversial to have.

> a great deal of information, but the calculus is still fundamentally

I don't think we should let a lack of clarity on what the proxy
situation will look like keep us from writing up what to do for direct
client/server interactions -- yes, proxies would be restricted to 1-RTT,
but that's the state of affairs with TLS 1.2 anyway.

> http/1.1 to do the same? Is there maybe a generic TLS1.3 way to do that?

There cannot be a generic TLS 1.3 way to do so; it would be a layering
violation.  TLS just moves the bytes; the interpretation ("willing to
process right away") must be done by the application layer.  The
application is of course free to implement the "buffer everything until
handshake completion" strategy, but I am not sure we want to mandate
that level of buffering.

> following this strategy or being reconfigured between sessions.

I don't think there's a reliable way to do that, and we'll have to
provision a way for clients to handle early data failures.

> need to transmit is the amount of replayable data.

I don't think that information will always be available, as more 0-RTT
data may still be arriving.

> the PROXY Protocol header that is sent to the backend

There can be multiple flights of 0-RTT data; you don't know it's ended
until the EndOfEarlyData, which can be quite late.

> * postpone forwarding the rest of 0-RTT data to the server until the

Does this not require committing to buffer everything (else)?  It's far
from clear that all devices will be able to do so.

> rule out the possibility of the 0-RTT data being replayed by an

I cannot see how that would be a widely valid assumption, let alone
universally valid.

In the general case, I would expect it to be possible to transmit
multiple complete (h2) requests in 0-RTT data.  Is your proposal to only
let the first one through and buffer the rest?

>   * This causes severe problems for high-performance rate-limiting

I mostly expect "all" servers to rate limit 0-RTT acceptances to
O(10^4.5) per second per SNI name (or similar), i.e., a level that is
not expected to trigger during normal operation but would slow down a
side channel attack.  Perhaps we should include guidance on this.

> big latency. Such connections typically cost certain amount of money.

"Says who?"  There are many potential use cases for 0-RTT, not all
written down.

My understanding is that browsers are latching onto research showing
that even 20 milliseconds of extra delay can affect user response
statistics, and trying to go as fast as possible.  That applies even on
broadband, not just on satellite.

> me. I think the right advise we should provide for such case is: turn

I don't think we should recommend the user agent to resend HTTP requests
from rejected 0-RTT, but I think we will encounter some situations where
it is unavoidable.  We can give guidance on how to best avoid it, of course.

> introduction of 0-RTT data in TLS 1.3.

Just to clarify: it is mandatory that we have a profile document that
specifies the interaction between TLS 0-RTT data and HTTP; the TLS 1.3
specification says that "[p]rotocols MUST NOT use 0-RTT data without a
profile that defines its use".  That profile document may or may not end
up being very simple, but it must exist.

> I'd hope that TLS/QUIC are explicit here, and that we wouldn't need to be; that's the beauty of layered protocols :)

Well, TLS/QUIC are supposed to be explicit about whether early data is
rejected at the TLS layer, and we can probably rely on that.  But, (1)
that just means that the data is passed on to the application/HTTP
stack, which has its own crack to reject (subsets of) the early data. 
And, of course, (2) there can always be implementation bugs.  So we'll
want to be careful about the semantics of what rejection at the
different layers mean and how it is handled.

-Ben

---

From: Kazuho Oku <[email-masked]>
Date: Tue, 16 May 2017 11:58:48 +0900

2017-05-16 8:36 GMT+09:00 Benjamin Kaduk <[email-masked]>:
> I think in order to strictly get the property that the server has agreed to
> that risk we need the new response code "try again 1-RTT", to accommodate
> servers that cannot buffer the entire 0-RTT request or other edge cases.
> But I don't think that is controversial to have.
>
> I don't think we should let a lack of clarity on what the proxy situation
> will look like keep us from writing up what to do for direct client/server
> interactions -- yes, proxies would be restricted to 1-RTT, but that's the
> state of affairs with TLS 1.2 anyway.
>
>
>
> process right away") must be done by the application layer.  The application
> is of course free to implement the "buffer everything until handshake
> completion" strategy, but I am not sure we want to mandate that level of
> buffering.

I believe that there could be a generic way, if there is a provision
stating that a client is always required to send Finished. That is the
intention behind the PR:
https://github.com/tlswg/tls13-spec/pull/1017.

Of course, having such provision does require all servers to adopt the strategy.

> I don't think there's a reliable way to do that, and we'll have to provision
> a way for clients to handle early data failures.
>
>
>
>
> In message
> <[email-masked]>
> , Kazuho Oku writes:
>
> I don't think that information will always be available, as more 0-RTT data
> may still be arriving.
>
> There can be multiple flights of 0-RTT data; you don't know it's ended until
> the EndOfEarlyData, which can be quite late.
>
> Does this not require committing to buffer everything (else)?  It's far from
> clear that all devices will be able to do so.
>
> In the general case, I would expect it to be possible to transmit multiple
> complete (h2) requests in 0-RTT data.  Is your proposal to only let the
> first one through and buffer the rest?

Yes. That is what I am suggesting.

I agree that it is not optimal. And it's not something I would be
interested in implementing it.

OTOH, I still anticipate that such method would work well for HTTP,
since I believe that the most common case in which HTTP over 0-RTT
will be used is when a user returns back to a web site after some
time. In such case, only the request for the web page will be sent in
the first flight, and fetching of the resources that are required to
render the web page will start after the response to the first request
starts arriving at the client.

> I mostly expect "all" servers to rate limit 0-RTT acceptances to O(10^4.5)
> per second per SNI name (or similar), i.e., a level that is not expected to
> trigger during normal operation but would slow down a side channel attack.
> Perhaps we should include guidance on this.
>
>
>
>
> "Says who?"  There are many potential use cases for 0-RTT, not all written
> down.
>
> My understanding is that browsers are latching onto research showing that
> even 20 milliseconds of extra delay can affect user response statistics, and
> trying to go as fast as possible.  That applies even on broadband, not just
> on satellite.
>
> from rejected 0-RTT, but I think we will encounter some situations where it
> is unavoidable.  We can give guidance on how to best avoid it, of course.

Agreed.

> profile that defines its use".  That profile document may or may not end up
> being very simple, but it must exist.
>
>
>
> I'd hope that TLS/QUIC are explicit here, and that we wouldn't need to be;
> that's the beauty of layered protocols :)
>
>
> rejected at the TLS layer, and we can probably rely on that.  But, (1) that
> just means that the data is passed on to the application/HTTP stack, which
> has its own crack to reject (subsets of) the early data.  And, of course,
> (2) there can always be implementation bugs.  So we'll want to be careful
> about the semantics of what rejection at the different layers mean and how
> it is handled.
>
> -Ben

-- 
Kazuho Oku