Code change:
As of , the client MUST use the same handshake message.
NAME That's quite complicated, and I'm not sure if we've chosen the right design here. I'd like to keep this PR editorial though. I opened to discuss this.
In URL, NAME points out that after a Retry, all 0-RTT packets sent in the first flight are still valid, although they will probably have been dropped by the server (unless there's significant reordering). That means that a client SHOULD retransmit all 0-RTT packets in the second flight. We now have two different scenarios: In the case of a HelloRetryRequest, the client is prohibited from sending 0-RTT, and has to cancel retransmissions for all outstanding 0-RTT packets. In the case of Retry, the 0-RTT packets from the first flight are declared lost (without any effect on the cwnd though) and retransmitted. Considering that Retry adds a roundtrip anyway (and thereby limits the usefulness of 0-RTT), and that we only expect it to be used by a server that's under attack (which makes it likely to refuse 0-RTT later in the handshake), does it really make sense to send 0-RTT in that case?
NAME I am not sure if I agree with the observation. The transport draft states that a server MAY send Retry packets in response to Initial and 0-RTT packets. A server can either discard or buffer 0-RTT packets that it receives (). Notice that the server is allowed to "buffer" 0-RTT packets. I am not sure if I like this ambiguity (because I think most if not all of us will be dropping 0-RTT packets that carry the original DCID when sending a Retry), but IMO the draft is clear.
NAME thanks for pointing that out. My point is that we should change the draft (i.e. this is a design issue, not an editorial issue). The reason a server (or a LB) performs a Retry is that it either has reasons to suspect that the client isn't reachable at the 2-tuple it sent the packet from, or that it is under load, and either uses the Retry to redirect traffic to a different backend server, or just to buy itself some time Under both circumstances, a server wouldn't want to allocate additional state for buffering 0-RTT packets. I'm therefore suggesting to change the draft such that: A server that performs a Retry MUST discard 0-RTT packets. A client MUST NOT use 0-RTT after a Retry was performed. Does that make sense to you?
Yeah, I can envision a server that considers buffering some number of packets to be cheaper than doing a TLS handshake. For such servers, delaying the TLS handshake until client proves it's existence but buffering the 0-RTT packets in the meantime would make sense. That said, I'd be fine with this, assuming that the discarded 0-RTT packets are the ones that carry the original DCID. I would prefer not having this requirement. Once the client is known to exist, a server needs to communicate with the client. Use of 0-RTT helps reduce the server load, because when used, average lifetime of connections becomes 1-RTT less. That leads us to having less concurrent connections.
When our implementation does Retry, it's because we've hit a memory pressure limit and we want to protect ourselves from attack. If a client supplies us with a valid token, we will accept 0-RTT still, but we won't buffer it from the previous Initial (without a token). So, if the client gets a Retry and still wants to do 0-RTT, they need to retransmit it, with the new CID, in a new packet (with a new packet number). I don't see why that design should be prevented.
Ok, I see that there's value in doing 0-RTT after a Retry, and furthermore, there seem to be good reasons (prevention of tampering by middle boxes) to disallow 0-RTT after a HelloRetryRequest in TLS 1.3, as NAME pointed out OOB. Since we can assume that most (all?) server implementation will drop 0-RTT packets when sending a Retry, the only reasonable thing to do for a client is to treat all 0-RTT packets as lost and retransmit them. Considering that, it would make sense to require servers to drop all Retry packets from the first flight, as NAME suggests.
NAME Thank you for suggesting a path forward. I think my weak preference goes to something slightly different. I think we are in agreement that most if not all the servers would not buffer the 0-RTT packets carrying the original DCIDs when they send Retrys. It makes sense to acknowledge the fact in the specification and suggest clients to retransmit 0-RTT data it has already sent. What I am not sure is if there is a reason to forbid servers from buffering such 0-RTT packets. From client's point of view, sending 0-RTT data once per connection is an optimization. Retransmitting 0-RTT data when the server sends a Retry is further optimization. If we think that way, servers that buffer 0-RTT packets when sending a Retry has increased chance of utilizing 0-RTT data. Compared to that, there is no reason to mandate servers to drop 0-RTT packets. To summarize, I think all we need to clarify is that servers responding with Retries are likely to discard 0-RTT packets that carry the original DCIDs and therefore that retransmitting 0-RTT data makes sense. But nothing more.
What if client takes action on Retry, and the old 0-RTT state lingers - wouldn't that make servers more vulnerable to DDoS attacks from packet sniffers?
This also bears on . If the DCID doesn't change, then the server won't be able to differentiate the first flight of 0-RTT packets, which you're proposing to drop, from the second flight of 0-RTT packets which it accepts. The current text says that the server MAY buffer and process them later; you're saying that servers probably won't. I think that's fine, but some might -- hence, I favor NAME suggestion to retain the permission to buffer but the caution to the client that the packets were quite possibly lost/dropped. In terms of actual spec change, I think that means this text in : ...becomes a SHOULD instead of a MAY.
It seems like you have sorted this out. I have no problem with using "SHOULD" over "MAY" here. Note that the reason we have this distinction is logical separation of the mechanism of Retry and 0-RTT acceptance. Retry strictly precedes the connection attempt. No point in blocking 0-RTT if you have to Retry. HelloRetryRequest is different in that certain characteristics it might cause to reset affect whether the 0-RTT works. Retry is required to use the same ClientHello, so there is no need to have the two interact; you can treat a Retry almost as a loss event for the first Initial and everything works fine.
So, the effect of not changing the handshake message is that all previously-sent 0-RTT data is still valid, but has potentially/probably been lost rather than queued. Clients obviously MUST NOT reuse the packet number for different data, but in their not-resetting, they also can't change the data that they've already sent in those packets. They'll need to retransmit it, because otherwise there's a potential DoS attack by delaying/replaying a 0-RTT packet by one RTT against a server that does Retries.This PR looks good to me as an editorial improvement. Regarding NAME point, the transport draft explicitly states that a server sending a Retry can buffer 0-RTT packets that carried the original DCID, and process them after receiving a response to the Retry. To put it differently, I think we are clear on the semantics and that there are ways to implement this without having security concerns. That said, I understand that NAME has opened a design issue () to discuss if that is the design we want to have.LGTM

Related discussion:
text: from a packet number of 0.  Thus, 0-RTT packets could need to use a longer packet number encoding. <del> A client SHOULD instead generate a fresh cryptographic handshake message and start packet numbers from 0.  This ensures that new 0-RTT packets will not use the same keys, avoiding any risk of key and nonce reuse; this also prevents 0-RTT packets from previous handshake attempts from being accepted as part of the connection. </del> A client MUST NOT send 0-RTT packets once it starts processing 1-RTT packets from the server.  This means that 0-RTT packets cannot contain any response to frames from 1-RTT packets.  For instance, a