Change:
protection means that restrictions on their use are necessary to avoid replay attacks on the protocol. <del> A client MUST only use 0-RTT keys to protect data that is idempotent. </del> <ins> Of the frames defined in QUIC-TRANSPORT, the STREAM, RESET_STREAM, and CONNECTION_CLOSE frames are potentially unsafe for use with 0-RTT as they carry application data.  Application data that is received in 0-RTT could cause an application at the server to process the data multiple times rather than just once.  Additional actions taken by a server as a result of processing replayed application data could have unwanted consequences.  A client therefore MUST NOT use 0-RTT for application data unless specifically requested by the application that is in use. An application protocol that uses QUIC MUST include a profile that defines acceptable use of 0-RTT; otherwise, 0-RTT can only be used to carry QUIC frames that do not carry application data.  For example, a profile for HTTP is described in HTTP-REPLAY and used for HTTP/3; see Section 10.9 of QUIC-HTTP. Though replaying packets might result in additional connection attempts, the effect of processing replayed frames that do not carry application data is limited to changing the state of the affected connection.  A TLS handshake cannot be successfully completed using replayed packets. </ins> A client MAY wish to apply additional restrictions on what data it <del> sends prior to the completion of the TLS handshake.  A client otherwise treats 0-RTT keys as equivalent to 1-RTT keys, except that it MUST NOT send ACKs with 0-RTT keys. </del> <ins> sends prior to the completion of the TLS handshake. A client otherwise treats 0-RTT keys as equivalent to 1-RTT keys, except that it cannot send certain frames with 0-RTT keys; see Section 12.5 of QUIC-TRANSPORT. </ins> A client that receives an indication that its 0-RTT data has been accepted by a server can send 0-RTT data until it receives all of the

Discussion:
This captures the outcome of an older analysis I did. Basically, if a replay is attempted, you get a new connection attempt. Only the application data that is processed at that time is a risk. Also, all protocols need a profile that describes how to use 0-RTT, otherwise it can only be used for QUIC things. Like NEWCONNECTIONID (I'm struggling to think of anything else that might reasonably go here).
The text in is wrong. Subtly, but distinctly. This seems fairly direct about idempotency being the benchmark. However, that is not consistent with , which goes further than idempotency and strongly recommends that only safe methods be used. This will result in a substantive change. NAME should be aware that this will also result in applicability doc changes also.
I don't disagree on the change for QUIC, but is there an avenue to fix this in TLS? I'm slightly uncomfortable differing from TLS in this regard, even if we're "right."
I see NAME point, but it seems to me that this is a stronger constraint than TLS, not a weaker one. In that sense, I would argue that it is not at odds with TLS, even though it is different.
I think NAME makes a good point here, and I'm not sure if QUIC-TLS is more stringent than TLS 1.3. Taking a quick look of appendix E.5 of RFC 8446, I notice the following differences: TLS 1.3 states that stacks "MUST NOT enable 0-RTT unless specifically requested by the application", whereas PR states that stacks "MUST only use 0-RTT for application data that is permitted by the application." We might consider the requirements as being different. TLS 1.3 states that stacks "MUST NOT automatically resend 0-RTT data if it is rejected by the server unless instructed by the application." I'm not sure if we have something similar in QUIC-TLS. I haven't done a thorough check, but these differences might be concerning.
As far as I know we have no such restriction/requirements in QUIC-TLS. In MsQuic we allow the app to control this behavior.

When informed of 0-RTT rejection, they have the option of aborting any stream data they had queued (and allowed the use of 0-RTT). This would result in the corresponding STREAMRESET/STOPSENDING frames being sent to the peer in 1-RTT. I the app doesn't abort, then we'd immediately retransmit the stream data in a 1-RTT packet the first chance we got. Should we not be doing this? If not, how else would/could we handle this?
NAME I think you are correct in pointing out that we do not have such a restriction in QUIC-TLS. That in turn means that QUIC is deviating from TLS 1.3 that mandates the application to "instruct" if data sent in 0-RTT should be replayed. I think that having such a requirement in QUIC is difficult, because QUIC stacks might retransmit data sent in 0-RTT packets using 1-RTT packets. I think that leads to the conclusion that QUIC has to specify it's own "replay safety," because it cannot have have all the "out of abundance of caution" MUSTs that TLS 1.3 have. At the end of the day, I think is fine as it is.
-tls, Section 4.6.2 says: That means that the data which was previously sent is not re-sent by the stack; it's up to the application to re-establish state from scratch. Do you think this language needs to be more explicit?
What NAME said. I've suggested a change to the PR to address NAME other concern ( vs. ).
The here has to be the app layer, right? It cannot be QUIC because of the need for an application specific error code. Is this MUST absolutely necessary? It assumes the application protocol is sending data in 0-RTT that's dependent on the previous "connection characteristics", but that's not absolutely necessary. Edit: Another thought: If it is the client app layer that must reset and the MUST stays, how can you really enforce this for all application layer protocols/implementations?
NAME The text that you quote talks about the behavior of the transport.

It also implies that from the viewpoint of the application, the stream opened in 0-RTT will continue to flow using 1-RTT packets. My point is that such assumption is different from that of RFC 8446 which states that the stack "MUST NOT automatically resend 0-RTT data if it is rejected by the server unless instructed by the application." In QUIC-TLS we do not have such a requirement (asking applications to specify if streams opened during 0-RTT MUST be closed unless instructed by the application). As NAME says, we do not even know how that can be achieved. To summarize, the 0-RTT requirements of QUIC-TLS is different from RFC 8446, and QUIC-TLS is defining it's own rules.
NAME Yes, you should not do this. Take the case where 0-RTT is rejected because the stream limit is reduced. If your application sends on 10 streams and is only permitted to send on 5 with the updated transport parameters, those frames will be sent and cause various errors. The only response that is completely safe is to tell the application that everything it did was undone and that the connection has been reverted to a pristine state. NAME I think here that they are not as different as you imply. What is different here is that QUIC has a split between QUIC-stuff and app-stuff that we are being clearer about. TLS just sort of says "data" without qualification, which is OK because TLS with TCP only really has to deal with data (and EOED), assuming that you don't have other record types in 0-RTT (which is maybe possible, but likely a terrible idea). However, if you consider the Section 8 text to apply to application data, then we're entirely consistent. TLS doesn't say "idempotent is OK", it's quite a bit more nuanced than that. What we're doing is building on that, not redefining it.
NAME we don't resend the old data blindly. We will use the new transport parameters and generate new 0-RTT packets accordingly. Going back to a pristine state seems kind of catastrophic. You're saying we have to essentially force all application protocols to just start over completely?

I can think of several examples where it doesn't matter what the server's TP change to, the application data could just be resent as is (limited by new FC as needed): for example, the client's initial state or settings.
If you think that you can do better, then fine. But the number of ways in which this could be broken are numerous: different ALPN, different stream limits, different flow control, and all sorts of things. I'd say you are more likely to get catastrophic results by trying to retain state. Separately, it sounds like you are accepting data for which you don't have flow control credit, for which you are also tempting fate when it comes to deadlocks.
Can you explain how different ALPNs would work? My TLS understanding is probably lacking here. I don't understand that one. If you're doing/trying 0-RTT that means the client is using a single ALPN, right? If the connection continues, but 0-RTT is rejected, it'd still have to be that ALPN right? If by this, you mean accepting data on the sender side, in preparation to send it, before the receiver has indicated enough FC window to accept it, then yes, we do allow the app to queue as much data up for send as it has available. As far as "tempting fate" I'd have to strongly disagree. This is a very common pattern necessary to "keep the pipe full" for performance. If I had to go ask the app for more data every time I got a window update from the peer, perf would tank. Still, even with all these things changing, I don't see any current transport parameter or TLS setting that might change as a result of rejected 0-RTT that would prevent an application layer message saying something like "I support app feature X" being immediately retransmitting in 1-RTT by the transport (if the FC windows allow). For this reason, I don't think we MUST require applications to abandon everything and start over. We can strongly recommend and warn of possible issues. And as I mentioned before I don't know how you'd even enforce that must.
A client can offer multiple protocols in the ALPN extension. It should, because if it misrepresents its preferences, then that exposes it to downgrade attacks.

However, if it previously used one protocol and the server chooses a different protocol, the server has to reject 0-RTT. But the server does have that choice. At that point anything the client did in 0-RTT is completely invalid because it's a different protocol. I realize that, just noting the deadlock risk. I'm sure that it's OK if it's just message payloads. Probably. I never said that. I said that what you are doing is risky. Rejecting 0-RTT invalidates every assumption the client has made about what it can and can't do, starting from the application protocol. When you optimize in the way you have, you are making a bet about what stays the same. I just wanted to ensure that you understood the consequences of that bet.
So, even when doing 0-RTT the client is still expected/allowed to offer multiple ALPNS? As far as the MUST I thought someone pointed to text that essentially already stated that. I wasn't trying to put words in your mouth. I'm sorry if it came off that way. As to the risks involved, I totally agree there be dragons here...
Yes, if the client supports multiple protocols, it needs to include them always. If a better option than what was previously used becomes viable (e.g., the server might support h5), then using that is preferable to continued use of a less-preferred option. Even if that less-preferred option allows 0-RTT. The 0-RTT use is just once; the new protocol is potentially forever (especially if the reason for preferring the new protocol is that the old protocol has a security flaw).
Ok. Thanks NAME That makes sense. I'll need to add a test case for this, and make sure we support this appropriately.
There is a MUST, see NAME 's . NAME Is there clarification text that can help with your ?
NAME Thank you for elaborating. I think I misused the word "redefine." QUIC is using 0-RTT in a specific way that is permitted by TLS. Specifically, QUIC stacks are assumed to automatically resend 0-RTT data using 1-RTT keys, rather than delegating the decision to the application.

If necessary, we can point that out as the difference.
I think that there is a lot of commentary that might go along with these documents. Particular when it comes to understanding how things like 0-RTT and its interactions with transport state, loss recovery, and other things. I fear that if we get into too much detail in specifications it will be hard to get right and much harder to wade through to get the important stuff. I'm content leaving this to commentary.
Is there anything more we want to say in the applicability statement though? E.g. impact on ALPN seems to need clarification...
Keeping open as agreed.
I did not follow the development on this issue and PR closely, but I came back here as I had the following thought: If the application is able to protect itself against replay attacks, for example using entangled quantum effects, or something more mondane like server side state, then 0-RTT does not have to be idempotent. Requiring it to be idempotent would prevent an application from managing side-effects intelligently. It appears to me that the now merged PR indeed does remove the idempotency requirement, but at the same time this issue seems to suggest that there needs to be a requirement that is stronger that idempotency, which goes against the application that is able to handle this (along with a suitable application profile). quote
Right. The problem is that the client can't know what the server is doing. So it can't commit to anything more than what is recommended without assuming some risk. This text doesn't prevent an application protocol from defining signals that would allow a client to do something riskier. The only normative text to this effect is:
OK, so client in this context is the transport client. It basically cannot even do idempotent things without adult supervision, but with supervision, it can do more.
Closing this now that the IESG review of draft 33 has concluded
Thank you for the changes. LGTM modulo one editorial nitpick.