4.3. <del> QUIC starts a loss epoch when a packet is lost and ends one when any packet sent after the epoch starts is acknowledged.  TCP waits for the gap in the sequence number space to be filled, and so if a segment is lost multiple times in a row, the loss epoch may not end for several round trips.  Because both should reduce their congestion windows only once per epoch, QUIC will do it once for every round trip that experiences loss, while TCP may only do it once across multiple round trips. </del>  4.4.

This isn't critical, but it's clearer and easier to parse. All to the good.
Derrell Piper writes:
The one nit here regards this sentence: This seems right to me. You might rewrite this as: But I don't think that is appreciably better. I think we can close this with no action unless we get some enthusiasm for rewording.
I think the issue is that "epoch starts is" looks like either an extra word or subject/verb agreement error. It's not actually; if you diagram the sentence out, "after the epoch starts" is in a dependent clause and "any packet ... is acknowledged" is correct agreement. That said, the rewording is easier to read because it doesn't look wrong and you don't have to try different possible parsings in your head before you find the one which works. We've tried to avoid trial decryption; let's avoid it in grammar, too.
Thanks, this seems like an improvement to me.

4.3.  <ins> QUIC starts a loss epoch when a packet is lost.  The loss epoch ends when any packet sent after the start of the epoch is acknowledged. TCP waits for the gap in the sequence number space to be filled, and so if a segment is lost multiple times in a row, the loss epoch may not end for several round trips.  Because both should reduce their congestion windows only once per epoch, QUIC will do it once for every round trip that experiences loss, while TCP may only do it once across multiple round trips. </ins> 4.4.


(IPv4), the DF bit MUST be set if possible, to prevent fragmentation on the path. <del> Datagrams are required to be of a minimum size under some conditions. However, the size of the datagram is not authenticated.  Therefore, an endpoint MUST NOT close a connection when it receives a datagram that does not meet size constraints, though the endpoint MAY discard such datagrams. </del>  14.1.

NAME I think you missed the change proposed to the reference. It should point to somewhere that imposes minimum datagram size (e.g., {{validate-handshake}}, not {{version-negotiation}}).
Urgh. Thanks for catching that, NAME sent.
([IPv4]), the DF bit MUST be set if possible, to prevent fragmentation on the path. However, the size of the datagram is not authenticated. Therefore, an endpoint MUST NOT close a connection when it receives a datagram that does not meet size constraints, though the endpoint MAY discard such datagrams. I really think this section needs re-writing for clarity.
I find this text perfectly clear, so I will need help in identifying in what ways the text lacks clarity.
Other than kazuho's nit which I regard as minor, this is good.

(IPv4), the DF bit MUST be set if possible, to prevent fragmentation on the path.  <ins> QUIC sometimes requires datagrams to be no smaller than a certain size; see version-negotiation as an example.  However, the size of a datagram is not authenticated.  That is, if an endpoint receives a datagram of a certain size, it cannot know that the sender sent the datagram at the same size.  Therefore, an endpoint MUST NOT close a connection when it receives a datagram that does not meet size constraints; the endpoint MAY however discard such datagrams. </ins> 14.1.


22.2.  IANA [SHALL add/has added] a registry for "QUIC Transport Parameters" under a "QUIC" heading. is, 27, 58, 89, ...) are reserved; these values MUST NOT be assigned by IANA and MUST NOT appear in the listing of assigned values. <del> 22.3. </del>  IANA [SHALL add/has added] a registry for "QUIC Frame Types" under a "QUIC" heading. Note that the registry does not include the "Pkts" and "Spec" columns from frame-types. <del> 22.4. </del>  IANA [SHALL add/has added] a registry for "QUIC Transport Error Codes" under a "QUIC" heading.

(Not sure to what extent this is editorial.)

Version Negotiation I am surprised that there is no IANA version registry
I think the wiki at URL should be the basis for a registry here, given that it reserves some ranges.
Do you believe those registrations should be enshrined in the RFC, or simply submitted to IANA to seed the registry?
I support using the transport document for to create an IANA based version registry and its ranges. However, I think the proprietary registrations from the WIKI can just as easy be submitted at time of registry creation as the values are FCFS. The IETF draft range maybe should be mentioned in the IANA consideration. However, the range is way larger than any single draft document can use. So it can fit multiple draft names.
I'll take this and write up a FCFS policy. As for draft versions, I think that we can limit the range we take to the last 10 we used. 0xff00001d needs protection, and maybe some of those preceding it, but no deployments of 0xff000000 exist.
It would be nice for the new registry to include the currently deployed Google QUIC versions if possible. In particular Q043, Q046, Q050 and T051 represent significant global traffic today.
NAME do you mind if I ask for an updated list when the time comes? NAME that probably goes for you too.
That's a good idea
I would keep the 0xff000000 range as is - we can reuse if we do a v2, and we don't know how many revs we will need
A question, shouldn't this registry actually be in Invariants? Version field is an invariant and not specific to V1.
That's what I thought, too. (I actually looked there as well when NAME flagged this in his review of -transport.)
It is considerably easier to reference the general registry guidance and definitions in this document rather than find a way to reference or port them.
Fair point. Maybe a paragraph in -invariants explaining that a version registry exists and that it is defined in -transport would be good though?
NAME from my perspective, to define it in -transport and just reference it from Invariants clearly can be made to work. The only issue that might arises in the future is for example when QUIC v1 is declared historic. Then the registry rules become historic despite they being current for any other version. So from a RFC life cycle perspective the version registry aligns with invariants.
Do registries automatically become historic when the RFCs that define them do? I didn't know that.
As part of deprecating or declaring an RFC historic instructions regarding the registry can be issued. Note that just because an RFC is historic it does not necessarily mean that no one is still using it and it is safe to recover its code points.
No, the registries do not become historic. The issue, is that if you would declare QUICv1 obsolete in QUICv2, then you can't reference the RFC for draft-ietf-quic-transport as that is obsolete for the registry rules when you register the QUICv2 identifier. Thus, you need to copy them to v2. And assuming that we might have several in force QUIC versions this can jump around. By placing the registration rules in invariants this issues would be avoided. As that RFC will only be updated due to need to change invariants or these specific rules.
I don't think QUIC v2 would necessarily obsolete v1, FWIW.
No, but Magnus’ approach sounds the best one. However I did raise the issue of whether, like IP, the QUIC versions should be an IANA registry.
NAME probably not, but maybe QUICv4 will obsolete v1. I do expect that the obsoleting version specifying documents are more likely than invariant becoming obsoleted. Clearly both approaches can be made to work, there are slightly different trade-offs what may need to happen down the line.
For Facebook, we have never used a draft version for our traffic. Our strategy is that we use different versions that are essentially aliases to draft versions. So far that has consisted of: 0xfaceb000 (variously draft-9 or draft-17) 0xfaceb001 (draft-24) 0xfaceb002 (draft-27) 0xfaceb00e-f (experimental versions) Since old clients take a while to upgrade, we support the -1 version for a few months before just disabling it and letting clients fallback to TCP. As for why we do this, it's because we want to retain control over mvfst mvfst interactions without potentially messing up public clients. In the future I think we'd plan on keeping with this strategy and moving forward e.g. to 0xfaceb003 next time we want to iterate the version.
I think this one is reasonably resolved with . Marking this .
NAME we think that this can be resolved by merging the associated PR , does that seem ok to you?
Yes, I am fine with the PR.
NAME please land the associated PR when you are ready.
Closing this now that the IESG have approved the document(s).
I feel like there's a little bit of value in having a separate IETF Consensus or Standard Required space, simply to highlight when a spec has that level of broad adoption. However, like you, I feel like that's fairly mild, so I'm also okay if we don't do that.

22.2. <ins> IANA [SHALL add/has added] a registry for "QUIC Versions" under a "QUIC" heading. The "QUIC Versions" registry governs a 32-bit space; see versions. This registry follows the registration policy from iana-policy. Permanent registrations in this registry are assigned using the Specification Required policy (RFC8126). The codepoint of 0x00000001 to the protocol is assigned with permanent status to the protocol defined in this document.  The codepoint of 0x00000000 is permanently reserved; the note for this codepoint [shall] indicate[s] that this version is reserved for Version Negotiation. All codepoints that follow the pattern 0x?a?a?a?a are reserved and MUST NOT be assigned by IANA and MUST NOT appear in the listing of assigned values. [[RFC editor: please remove the following note before publication.]] Several pre-standardization versions will likely be in use at the time of publication.  There is no need to document these in an RFC, but recording information about these version will ensure that the information in the registry is accurate.  The document editors or working group chairs can facilitate getting the necessary information. 22.3. </ins> IANA [SHALL add/has added] a registry for "QUIC Transport Parameters" under a "QUIC" heading. is, 27, 58, 89, ...) are reserved; these values MUST NOT be assigned by IANA and MUST NOT appear in the listing of assigned values.  <ins> 22.4. </ins> IANA [SHALL add/has added] a registry for "QUIC Frame Types" under a "QUIC" heading. Note that the registry does not include the "Pkts" and "Spec" columns from frame-types.  <ins> 22.5. </ins> IANA [SHALL add/has added] a registry for "QUIC Transport Error Codes" under a "QUIC" heading.


data for a terminated connection is to use the stateless reset process (stateless-reset).  11.2. If an application-level error affects a single stream, but otherwise

But we managed to hide that in the security considerations.
This PR is fine, but I agree with NAME above. I think we should add the exception and a reference to this text in 17.2.
How many other rules do we need to create exemptions for? What if the Initial contains a bad frame? What if the CRYPTO frame is too long?
If you don't want to have the discussion about exceptions, then write SHOULD instead of MUST.
I don't want to go to SHOULD. Because for other packet types, MUST is what we want. And finding and creating exceptions is impossible. So I have added a "this permission overrides rules that mandate a connection error".
That's a good suggestion. I moved it. Then it is more likely to be seen.
This issue derives from the efforts of NAME to write an HTTP3 test suite, and outline potential implementation issues when sticking to the spec. Section 17.2 of the transport spec describes the format of long header packets, and says: Reserved Bits: Two bits (those with a mask of 0x0c) of byte 0 are reserved across multiple packet types. These bits are protected using header protection; see Section 5.4 of [QUIC-TLS]. The value included prior to protection MUST be set to 0. An endpoint MUST treat receipt of a packet that has a non-zero value for these bits after removing both packet and header protection as a connection error of type PROTOCOL_VIOLATION. Discarding such a packet after only removing header protection can expose the endpoint to attacks; see Section 9.5 of [QUIC-TLS]. I can see doing that for Handshake or 0-RTT packets, but for Initial packets that feels risky. Any man-on-the-side can send forged Initial packets, including forged Initial packets with reserved bits set. Do we really intend to say that such packets cause the connection to break? What if the handshake has already progressed? My gut feeling is that it is simpler and safer to ignore malformed Initial packets and discard them silently.
IIUC, allows endpoints to implement such strategy, quote: Endpoints are permitted to use other methods to detect and attempt to recover from interference with the handshake. Invalid packets may be identified and discarded using other methods, but no specific method is mandated in this document.
NAME has correctly identified the escape hatch we left. Initial packets are the only place where this really applies, but it goes much further than just those two bits.
Are you saying the MUST is not a real MUST because in the "Security Considerations" there is a suggestion that implementations could deviate??? IMHO that would be weird, given emphasis on the meaning of MUST.
My opinion is: An endpoint MUST send PROTOCOLVIOLATION for 1-RTT. An endpoint MAY send PROTOCOLVIOLATION or MAY discard the packet for other types.
NAME the proposal here is to allow discarding only for Initial. Other types are effectively authenticated.  Handshake might not be confirmed in the sense that you have key confirmation and peer authentication, but I can't imagine anyone attempting to do anything other than abort if an attacker gets that far).
I'm OK with discarding only Initial.
I think what NAME says makes sense. This is a "MUST unless" however we cannot have that stated at every point. Having a catch-all statement makes sense. I tend to wonder if the catch-all statement should be in the Protected Packets section or in the Error Handling section, but the PR looks good regardless.Thanks for the new text, NAME -- this is good.

data for a terminated connection is to use the stateless reset process (stateless-reset). <ins> As the AEAD on Initial packets does not provide strong authentication, an endpoint MAY discard an invalid Initial packet. Discarding an Initial packet is permitted even where this specification otherwise mandates a connection error.  An endpoint can only discard a packet if it does not process the frames in the packet or reverts the effects of any processing.  Discarding invalid Initial packets might be used to reduce exposure to denial of service; see handshake-dos. </ins> 11.2. If an application-level error affects a single stream, but otherwise


bytes. An opaque blob that the client can use with a future Initial <del> packet.  The token MUST NOT be empty.  An endpoint MUST treat receipt of a NEW_TOKEN frame with an empty Token field as a connection error of type FRAME_ENCODING_ERROR. </del>  <del> An endpoint might receive multiple NEW_TOKEN frames that contain the </del>  same token value if packets containing the frame are incorrectly <del> determined to be lost.  Endpoints are responsible for discarding </del>  duplicate values, which might be used to link connection attempts; see validate-future. <del> Clients MUST NOT send NEW_TOKEN frames.  Servers MUST treat receipt </del>  of a NEW_TOKEN frame as a connection error of type PROTOCOL_VIOLATION.

Since only servers can send NEW_TOKEN, using "client" is clearer. "Servers MUST treat" is used only here, so it is changed into "A server MUST treat".
Please see the for full context. This issue is tracking the following IANA question: >IANA Question --> Section 22.1.2 says, "Applications to register codepoints in QUIC registries MAY include a codepoint as part of the registration. IANA MUST allocate the selected codepoint unless that codepoint is already assigned or the codepoint is the first unallocated codepoint in the registry."
I think it is a good question that has two aspects. If a registrant is requesting additional consecutive values beyond an earlier registration is that okay? But other registrants they need to draw a new random value to not block the next value? Or any future allocation should have to select a new random value? Also, is it IANA or the designated expert that is making the determination if this request is allowed or not?
I think what this is intended to convey is: Permanent registrations for the first unallocated codepoint are intended for use in Standards-track documents The "negotiation" involved in a provisional registration is essentially to confirm that the requestor is doing so for use in a document which is likely to become a Standards-track RFC. It's the "likely to" that becomes squishy. Do we actually need this?
So in I have a comment that indicates a potential solution or direction for how to work with this using either of two process that will be clearer. Either provisional for test and development value and then assign a final value when approving the specification, or early allocation per RFC 7120. I also like to point out that there is very large hole in this algorithm. If a registry has assigned 0-N then asking for N+1 is rejected, but asking for N+2 is the approved without question per these rules unless you are in a registry with the special rules for 0-63 values.
Could we make some progress here?
I think fixes this too. NAME ?
This was fixed by
Thanks, NAME !

bytes. An opaque blob that the client can use with a future Initial  <ins> packet.  The token MUST NOT be empty.  A client MUST treat receipt of a NEW_TOKEN frame with </ins>  <ins> A client might receive multiple NEW_TOKEN frames that contain the </ins> same token value if packets containing the frame are incorrectly  <ins> determined to be lost.  Clients are responsible for discarding </ins> duplicate values, which might be used to link connection attempts; see validate-future.  <ins> Clients MUST NOT send NEW_TOKEN frames.  A server MUST treat receipt </ins> of a NEW_TOKEN frame as a connection error of type PROTOCOL_VIOLATION.


The creation of a registry MAY identify a range of codepoints where registrations are governed by a different registration policy.  For <del> instance, the registries for 62-bit codepoints in this document have stricter policies for codepoints in the range from 0 to 63. </del>  Any stricter requirements for permanent registrations do not prevent provisional registrations for affected codepoints.  For instance, a <del> provisional registration for a frame type (iana-frames) of 61 could be requested. </del>  All registrations made by Standards Track publications MUST be permanent.

NAME You have a comment here pending. I'm planning to merge this PR in ~15 hours from now, so please say something now if you still would prefer a change.
Section 22.1.4: "For instance, the registries for 62-bit codepoints in this document have stricter policies for codepoints in the range from 0 to 63." Looking then at 22.2 it states: The "QUIC Transport Parameters" registry governs a 62-bit space. This registry follows the registration policy from Section 22.1. Permanent registrations in this registry are assigned using the Specification Required policy ([RFC8126]). No mention of the special registration rule for values in the 0-63 range. Section 22.3 and 22.4 do have these special rules. So should 22.1.4 be changed to state "may"/"sometime" have this rule. Or should a special rule be added to 22.2 and the version registry?

The creation of a registry MAY identify a range of codepoints where registrations are governed by a different registration policy.  For  <ins> instance, the frame type registry in iana-frames has a stricter policy for codepoints in the range from 0 to 63. </ins> Any stricter requirements for permanent registrations do not prevent provisional registrations for affected codepoints.  For instance, a  <ins> provisional registration for a frame type of 61 could be requested. </ins> All registrations made by Standards Track publications MUST be permanent.


first unallocated codepoint in the selected space.  Requests for multiple codepoints MAY use a contiguous range.  This minimizes the risk that differing semantics are attributed to the same codepoint by <del> different implementations.  Use of the first codepoint in a range is intended for use by specifications that are developed through the standards process STD and its allocation MUST be negotiated with IANA before use. </del>  For codepoints that are encoded in variable-length integers (integer- encoding), such as frame types, codepoints that encode to four or Applications to register codepoints in QUIC registries MAY include a codepoint as part of the registration.  IANA MUST allocate the <del> selected codepoint unless that codepoint is already assigned or the codepoint is the first unallocated codepoint in the registry. </del>  22.1.3.

And cite RFC 7120 for how that can be assigned.
Oops, I realized that this was marked "design". I'll reopen the issue so that we don't lose it.
Please see the for full context. This issue is tracking the following IANA question: >IANA Question --> Section 22.1.2 says, "Applications to register codepoints in QUIC registries MAY include a codepoint as part of the registration. IANA MUST allocate the selected codepoint unless that codepoint is already assigned or the codepoint is the first unallocated codepoint in the registry."
I think it is a good question that has two aspects. If a registrant is requesting additional consecutive values beyond an earlier registration is that okay? But other registrants they need to draw a new random value to not block the next value? Or any future allocation should have to select a new random value? Also, is it IANA or the designated expert that is making the determination if this request is allowed or not?
I think what this is intended to convey is: Permanent registrations for the first unallocated codepoint are intended for use in Standards-track documents The "negotiation" involved in a provisional registration is essentially to confirm that the requestor is doing so for use in a document which is likely to become a Standards-track RFC. It's the "likely to" that becomes squishy. Do we actually need this?
So in I have a comment that indicates a potential solution or direction for how to work with this using either of two process that will be clearer. Either provisional for test and development value and then assign a final value when approving the specification, or early allocation per RFC 7120. I also like to point out that there is very large hole in this algorithm. If a registry has assigned 0-N then asking for N+1 is rejected, but asking for N+2 is the approved without question per these rules unless you are in a registry with the special rules for 0-63 values.
Could we make some progress here?
I think fixes this too. NAME ?
This was fixed by
Please see the for full context. This issue is tracking the following IANA question: intended for use by specifications that are developed through the standards process [STD] and its allocation MUST be negotiated with IANA before use."
When talking to IANA today, they did ask about how early allocation (RFC7120) interacts with this question. So I understand why IANA react to the "negotiate" language as that is requiring them to apply value judgment rather than follow basic rules to allocate or not. So I think there are a couple of different way here. Require standards action for allocation with the first unallocated value. During the development phase of that option the document recommends either of these two options. A. Use a randomly selected value Provisional assignment. When going for approval specify that a new code point is allocated that has that specific property desired (short encoding?). B. Request early allocation (RFC7120) for that standards action code point. I think what one uses has a lot to do with the perceived maturity of what the code point represent. I think A has significant advantages as if one need to do format changes one can simply request a new provisional code point, that is not possible with B. I think there may exist a point of making it clear also for IETF / Standards Process work that there is a point of doing provisional registrations as soon as one need a code point for testing.
I think that this is a good plan. I've created for that. This says that the first available codepoint is reserved for Standards Action. I've also pointed to 7120, which was always the intended process (I was just lazy about finding and citing it).
NAME why did you reopen this?
The change in was marked as design; I figured that you (as chairs) would want to discuss and resolve.
Ah, I missed that - thanks.
NAME Do we just want to point IANA to the new text here?
I have done so via email.
NAME did IANA respond to your email? do they still have questions or is this resolved?
We got what we needed and I just merged the change.
Closing this now that the IESG have approved the document(s).

first unallocated codepoint in the selected space.  Requests for multiple codepoints MAY use a contiguous range.  This minimizes the risk that differing semantics are attributed to the same codepoint by  <ins> different implementations. Use of the first available codepoint in a range is reserved for allocation using the Standards Action policy; see Section 4.9 of RFC8126.  The early codepoint assignment process EARLY-ASSIGN can be used for these values. </ins> For codepoints that are encoded in variable-length integers (integer- encoding), such as frame types, codepoints that encode to four or Applications to register codepoints in QUIC registries MAY include a codepoint as part of the registration.  IANA MUST allocate the  <ins> selected codepoint if the codepoint is unassigned and the requirements of the registration policy are met. </ins> 22.1.3.


The payload of a packet that contains frames MUST contain at least one frame, and MAY contain multiple frames and multiple frame types. <del> Frames always fit within a single QUIC packet and cannot span multiple packets. </del>  Each frame begins with a Frame Type, indicating its type, followed by additional type-dependent fields:

Sec 12.4 says: There is no specification of error handling in the case of zero frame. I would like to know what kind of error MUST be sent. Also, the packet formats contains the following field: Should we change this to the following?

The payload of a packet that contains frames MUST contain at least one frame, and MAY contain multiple frames and multiple frame types.  <ins> An endpoint MUST treat receipt of a packet containing no frames as a connection error of type PROTOCOL_VIOLATION.  Frames always fit within a single QUIC packet and cannot span multiple packets. </ins> Each frame begins with a Frame Type, indicating its type, followed by additional type-dependent fields:


New registries created in this document operate under the QUIC registration policy documented in Section 22.1 of QUIC-TRANSPORT. These registries all include the common set of fields listed in <del> Section 22.1.1 of QUIC-TRANSPORT. </del>  The initial allocations in these registries created in this document are all assigned permanent status and list a change controller of the

Please see the for full context. This issue is tracking the following IANA question:
Responded to e-mail as well, but for GitHub tracking purposes, I believe HTTP/3 should be its own registry page, separate from QUIC and analogous to HTTP/2.
NAME I think that is a good proposal.
Can we put that in the draft then?

New registries created in this document operate under the QUIC registration policy documented in Section 22.1 of QUIC-TRANSPORT. These registries all include the common set of fields listed in  <ins> Section 22.1.1 of QUIC-TRANSPORT.  These registries [SHALL be/are] collected under a "Hypertext Transfer Protocol version 3 (HTTP/3) Parameters" heading. </ins> The initial allocations in these registries created in this document are all assigned permanent status and list a change controller of the


previously learned about the server to send Application Data immediately.  This Application Data can be replayed by an attacker so 0-RTT is not suitable for carrying instructions that might <del> initiate any non-idempotent action. </del>  A simplified TLS handshake with 0-RTT application data is shown in tls-full. 4.6.2. A server accepts 0-RTT by sending an early_data extension in the <del> EncryptedExtensions (see Section 4.2.10 of TLS13).  The server then </del>  processes and acknowledges the 0-RTT packets that it receives. A server rejects 0-RTT by sending the EncryptedExtensions without an 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>  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>  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

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.

previously learned about the server to send Application Data immediately.  This Application Data can be replayed by an attacker so 0-RTT is not suitable for carrying instructions that might  <ins> initiate any action that could cause unwanted effects if replayed. </ins> A simplified TLS handshake with 0-RTT application data is shown in tls-full. 4.6.2. A server accepts 0-RTT by sending an early_data extension in the  <ins> EncryptedExtensions; see Section 4.2.10 of TLS13.  The server then </ins> processes and acknowledges the 0-RTT packets that it receives. A server rejects 0-RTT by sending the EncryptedExtensions without an protection means that restrictions on their use are necessary to avoid replay attacks on the protocol.  <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  <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


stateless reset size. Endpoints MUST discard packets that are too small to be valid QUIC <del> packets.  With the set of AEAD functions defined in QUIC-TLS, packets that are smaller than 21 bytes are never valid. </del>  Endpoints MUST send stateless reset packets formatted as a packet with a short header.  However, endpoints MUST treat any packet ending

Minimum packet size varies between the packet types (and other things like the length of CID being used). Therefore, the sentence is merely providing an example, and should be stated as such. .
Currently section 10.3 of the transport doc says: However, Version Negotiation packets are not guaranteed to be larger than 21 bytes. For example, a VN packet with an 0-byte destination connection ID, 8 byte source connection ID and single version would be 19 bytes. If a client receives such a packet, must it be dropped? This text suggests it must. But it's not clear to mean (or the slack channel) that this interpretation is expected. Of course, a server could work around this issue by appending a sufficient number of GREASE'd versions to make the packet large enough. But I'm not sure if that is mandatory?
The second sentence merely points out that QUIC packets that use AEAD can never be smaller than 21 bytes. VN packets do not use AEAD, hence that 21 byte limit does not apply. Of course, endpoints can do their own maths to figure out the minimal size of VN packets that they might receive. Just my two cents. We might want to clarify the italic part ("that use AEAD") in the specification.
I agree with NAME that the spec isn't technically wrong, but I find it unclear. It wouldn't surprise me if someone were to read this and add to their packet ingestion code. I think all we need here is an editorial clarification such as
Note: 21 is the correct number, as requires endpoints to expand the payload size to at least 4 + len(AEAD tag), so that we can always have full-sized entropy for header protection.
Thanks Kazuho, this is great. What I like about this is that it also implies the possibility of a higher minimum. For instance, the limit on long headers is higher (28 for Initial, 27 for 0-RTT and handshake if I'm right, but you can also add the minimum connection ID length for receiving to that).

stateless reset size. Endpoints MUST discard packets that are too small to be valid QUIC  <ins> packets.  To give an example, with the set of AEAD functions defined in QUIC-TLS, short header packets that are smaller than 21 bytes are never valid. </ins> Endpoints MUST send stateless reset packets formatted as a packet with a short header.  However, endpoints MUST treat any packet ending


selected cipher suite; see hp-aes and hp-chacha. fig-sample shows an example long header packet (Initial) and a short <del> header packet. fig-sample shows the fields in each header that are covered by header protection and the portion of the protected packet payload that is sampled. </del>  Before a TLS cipher suite can be used with QUIC, a header protection algorithm MUST be specified for the AEAD used with that cipher suite. any number more (subject to limits inherent to the protocol, such as congestion control and anti-amplification).  For instance, the server's first flight contains Initial packets, Handshake packets, <del> and "0.5-RTT data" in 1-RTT packets with a short header. </del>  tls-0rtt-handshake shows an example of a connection with a 0-RTT handshake and a single packet of 0-RTT data.  Note that as described Handshake space: All Handshake packets (packet-handshake) are in this space. <del> Application data space: All 0-RTT (packet-0rtt) and 1-RTT (short- header) encrypted packets are in this space. </del>  As described in QUIC-TLS, each packet type uses different protection keys. 0-RTT (packet-0rtt) <del> 1-RTT (short-header) </del>  Only a CONNECTION_CLOSE frame of type 0x1c can appear in Initial or Handshake packets. This version of QUIC defines a single packet type that uses the short packet header. <del> The short header can be used after the version and 1-RTT keys are negotiated.  Packets that use the short header contain the following fields: </del>  The most significant bit (0x80) of byte 0 is set to 0 for the short header. number field is encoded in Packet Number Length field.  See packet-encoding for details. <del> Packets with a short header always include a 1-RTT protected payload. </del>  The header form bit and the connection ID field of a short header packet are version-independent.  The remaining fields are specific to the selected QUIC version.  See QUIC-INVARIANTS for details on how packets from different versions of QUIC are interpreted. <del> 17.3.1. </del>  <del> The latency spin bit enables passive latency monitoring from observation points on the network path throughout the duration of a connection.  The server reflects the spin value received, while the client 'spins' it after one RTT.  On-path observers can measure the time between two spin bit toggle events to estimate the end-to-end RTT of a connection. </del>  <del> The spin bit is only present in the short packet header, since it is possible to measure the initial RTT of a connection by observing the </del>  handshake.  Therefore, the spin bit is available after version negotiation and connection establishment are completed.  On-path measurement and use of the latency spin bit is further discussed in QUIC-MANAGEABILITY. <del> The spin bit is an OPTIONAL feature of QUIC.  A QUIC stack that chooses to support the spin bit MUST implement it as specified in this section. </del>  Each endpoint unilaterally decides if the spin bit is enabled or disabled for a connection.  Implementations MUST allow administrators connection ID. If the spin bit is enabled for the connection, the endpoint maintains <del> a spin value for each network path and sets the spin bit in the short header to the currently stored value when a packet with a short header is sent on that path.  The spin value is initialized to 0 in the endpoint for each network path.  Each endpoint also remembers the </del>  highest packet number seen from its peer on each path. <del> When a server receives a short header packet that increases the highest packet number seen by the server from the client on a given network path, it sets the spin value for that path to be equal to the spin bit in the received packet. </del>  <del> When a client receives a short header packet that increases the highest packet number seen by the client from the server on a given network path, it sets the spin value for that path to the inverse of the spin bit in the received packet. </del>  An endpoint resets the spin value for a network path to zero when changing the connection ID being used on that network path.

This PR restructures 17.3 to be aligned with 17.2. Note that while the diff seems to show whole paragraphs changed in the section, all I've done is replaced with . Note that I've not replaced all uses of . I've retained it where it is used alongside , for symmetry. This still touches more text than I had hoped, but it makes the structure of Section 17 clearer.
NAME Thanks for the note. I've updated the tls doc, but I've left most uses of as they are. I've retained them for symmetry, since in all the ones I've retained, it appears alongside .
While the transport draft talks a lot about "1-RTT packets", the term is not defined. We also have a sentence that seem to imply that a long-header variant of 1-RTT packets might exist (URL). We refer to the packet less frequently as "short header packets" or "packet with a short header." As a resolution, I think we might want to change the title of section 17.3 from "Short Header Packets" to "1-RTT Packets". Rest of the text can remain the same, as the first sentence of that section says that it is the only type of short header packet that we define.
Update: I think we might want to make corresponding changes to the TLS draft. Re TLS draft, I think we can replace all the occurrences of "short header packet(s)", "packet(s) with (a) short header" in the TLS draft with "1-RTT packet(s)", as well making necessary changes to table 1.This looks good. The actual changes are much smaller than the diff appears, and I think the symmetry with 17.2 helps readability despite the extra layer.

selected cipher suite; see hp-aes and hp-chacha. fig-sample shows an example long header packet (Initial) and a short  <ins> header packet (1-RTT). fig-sample shows the fields in each header that are covered by header protection and the portion of the protected packet payload that is sampled. </ins> Before a TLS cipher suite can be used with QUIC, a header protection algorithm MUST be specified for the AEAD used with that cipher suite. any number more (subject to limits inherent to the protocol, such as congestion control and anti-amplification).  For instance, the server's first flight contains Initial packets, Handshake packets,  <ins> and "0.5-RTT data" in 1-RTT packets. </ins> tls-0rtt-handshake shows an example of a connection with a 0-RTT handshake and a single packet of 0-RTT data.  Note that as described Handshake space: All Handshake packets (packet-handshake) are in this space.  <ins> Application data space: All 0-RTT (packet-0rtt) and 1-RTT (packet- 1rtt) encrypted packets are in this space. </ins> As described in QUIC-TLS, each packet type uses different protection keys. 0-RTT (packet-0rtt)  <ins> 1-RTT (packet-1rtt) </ins> Only a CONNECTION_CLOSE frame of type 0x1c can appear in Initial or Handshake packets. This version of QUIC defines a single packet type that uses the short packet header.  <ins> 17.3.1. A 1-RTT packet uses a short packet header.  It is used after the version and 1-RTT keys are negotiated. 1-RTT packets contain the following fields: </ins> The most significant bit (0x80) of byte 0 is set to 0 for the short header. number field is encoded in Packet Number Length field.  See packet-encoding for details.  <ins> 1-RTT packets always include a 1-RTT protected payload. </ins> The header form bit and the connection ID field of a short header packet are version-independent.  The remaining fields are specific to the selected QUIC version.  See QUIC-INVARIANTS for details on how packets from different versions of QUIC are interpreted.  <ins> 17.4. </ins>  <ins> The latency spin bit, which is defined for 1-RTT packets (packet- 1rtt), enables passive latency monitoring from observation points on the network path throughout the duration of a connection.  The server reflects the spin value received, while the client 'spins' it after one RTT.  On-path observers can measure the time between two spin bit toggle events to estimate the end-to-end RTT of a connection. </ins>  <ins> The spin bit is only present in 1-RTT packets, since it is possible to measure the initial RTT of a connection by observing the </ins> handshake.  Therefore, the spin bit is available after version negotiation and connection establishment are completed.  On-path measurement and use of the latency spin bit is further discussed in QUIC-MANAGEABILITY.  <ins> The spin bit is an OPTIONAL feature of this version of QUIC.  A QUIC stack that chooses to support the spin bit MUST implement it as specified in this section. </ins> Each endpoint unilaterally decides if the spin bit is enabled or disabled for a connection.  Implementations MUST allow administrators connection ID. If the spin bit is enabled for the connection, the endpoint maintains  <ins> a spin value for each network path and sets the spin bit in the packet header to the currently stored value when a 1-RTT packet is sent on that path.  The spin value is initialized to 0 in the endpoint for each network path.  Each endpoint also remembers the </ins> highest packet number seen from its peer on each path.  <ins> When a server receives a 1-RTT packet that increases the highest packet number seen by the server from the client on a given network path, it sets the spin value for that path to be equal to the spin bit in the received packet. </ins>  <ins> When a client receives a 1-RTT packet that increases the highest packet number seen by the client from the server on a given network path, it sets the spin value for that path to the inverse of the spin bit in the received packet. </ins> An endpoint resets the spin value for a network path to zero when changing the connection ID being used on that network path.


An opaque blob that the client can use with a future Initial packet.  The token MUST NOT be empty.  A client MUST treat receipt <del> of a NEW_TOKEN frame with </del>  A client might receive multiple NEW_TOKEN frames that contain the same token value if packets containing the frame are incorrectly

I looked into the commits but I cannot identify the commit which removed this line.
Gah! Thanks NAME for catching this!!

An opaque blob that the client can use with a future Initial packet.  The token MUST NOT be empty.  A client MUST treat receipt  <ins> of a NEW_TOKEN frame with an empty Token field as a connection error of type FRAME_ENCODING_ERROR. </ins> A client might receive multiple NEW_TOKEN frames that contain the same token value if packets containing the frame are incorrectly


the change.  An endpoint that notices a changed Key Phase bit updates keys and decrypts the packet that contains the changed value. <del> This mechanism replaces the TLS KeyUpdate message.  Endpoints MUST NOT send a TLS KeyUpdate message.  Endpoints MUST treat the receipt of a TLS KeyUpdate message as a connection error of type 0x10a, equivalent to a fatal TLS alert of unexpected_message (see tls- errors). </del>  ex-key-update shows a key update process, where the initial set of keys used (identified with @M) are replaced by updated keys

QUIC-TLS defines some "adjustments"(URL) to TLS 1.3. As they are adjustments, we are updating TLS only when necessary. However, discussion in seems to have revealed one corner case that is ambiguous. This PR addresses that problem. To be specific, TLS 1.3 defines the necessary action when an endpoint receives KeyUpdate message in epochs other than 1-RTT. However, current statement in QUIC-TLS can be read as if it is updating that. This PR makes it clear that the "adjustment" applies to only when the use of KeyUpdate is permitted by TLS 1.3 but not in QUIC-TLS.
NAME now supports an error case of TLS KeyUpdate in 1-RTT. Based on experiments with , it appeared that TLS stack might be freed already when any CRYPTOs including KeyUpdate in 1-RTT are received. So, we should also allow in this case.
I don't see any reason to specifically allow PROTOCOLVIOLATION here (it's allowed as a fallback for any error code anyway), as it's possible for a QUIC stack to generate an error code in the CRYPTOERROR space, even if the TLS stack has already been shut down.
NAME It's possible but it's not reasonable because QUIC stack needs to parse TLS messages. BTW, where can I find the fallback rule?
NAME A QUIC stack that discards the TLS state post-handshake is committing to handle TLS messages by itself. Therefore, it has to handle them correctly. In practice, such QUIC stack running as clients have to handle NewSessionTicket messages correctly; therefore they need to be capable of parsing TLS messages. Such stacks running as servers can respond with unexpected_message alert for any post-handshake message that they receive, because in RFC 8446 there isn't a case where a client unilaterally sends a post-handshake message with the only exception being KeyUpdate. It is defined at URL
Thanks. I'm fine with this rule. So, I withdraw my suggestion.
Sec 4.5 says: This does not specify how a server MUST do if in Initial/Handshake/1-RTT is received. I think that a server MUST send the TLS alert of .
Sounds reasonable to me.
I'm not sure if we need to change, or if the proposed change is reasonable. Doesn't RFC 8446 already state that the receiver MUST error-close the connection when it receives EndOfEarlyData in other epochs? Assuming that that is the case, I think that the current text is correct, because the section talks about "QUIC-Specific Adjustments to the TLS Handshake."
The draft also says: What is the difference between KeyUpdate and EndOfEarlyData?
NAME I'd argue that the only way to interpret that text is that the KeyUpdate message cannot be used even when permitted by TLS 1.3 (i.e. in 1-RTT packets). The italic part is implied, rather than explicitly stated like the case for EndOfEarlyData. If there's ambiguity, we can try to be explicit. I do not have a strong opinion on if we need to. PS. To be clear, what I am saying is that the behavior for the following cases are defined by TLS 1.3, and that we should not re-specify them: use of EndOfEarlyData in epoch other than 0-RTT use of KeyUpdate in epoch other than 1-RTT
To let readers interpret the text as "cannot be used", we should simply use "cannot be used". If "MUST NOT" is used, the handling should be specified.
I do not think anything should change personally.
FWIW I've filed that does not do anything regarding the original issue, but makes changes to the specification regarding KeyUpdate to clarify that it is not intended to respecify how TLS rejects that message during handshake.
Marking as editoiral. If I'm wrong please speak up.
This LGTM. I would consider this an editorial change.

the change.  An endpoint that notices a changed Key Phase bit updates keys and decrypts the packet that contains the changed value.  <ins> This mechanism replaces the key update mechanism of TLS, which relies on KeyUpdate messages sent using 1-RTT encryption keys.  Endpoints MUST NOT send a TLS KeyUpdate message.  Endpoints MUST treat the receipt of a TLS KeyUpdate message in a 1-RTT packet as a connection error of type 0x10a, equivalent to a fatal TLS alert of unexpected_message; see tls-errors. </ins> ex-key-update shows a key update process, where the initial set of keys used (identified with @M) are replaced by updated keys


7.6. <del> When a sender establishes loss of all in-flight packets sent over a long enough duration, the network is considered to be experiencing </del>  persistent congestion. 7.6.1.

I started this to but there's not much to fix. This does not change normative text, it just makes the intro match the detailed text which follows.
I'm not sure - if padding is included to discover PMTU or whatever, and the pipe clogs up, that is a problem even if the contribution is not ack eliciting. It is of course still important to not block acks.
I understood the text as saying that you can probably assume that all packets (including non-ack-eliciting ones) got lost when you don't hear anything back for a while... Also if you receive a packet that only ACKs non-ack-eliciting packets this would be weird but it probably means there is not persistent congestion because you know something went though.
This is good. The problem with non-ack-eliciting packets is that a sender cannot infer anything about network conditions when it isn't expecting an ack. Meaning that the receiver could have received everything and simply not responded if none of those packets are ack-eliciting.
NAME yes, you don't know anything when you don't received an ACK. However, you may still received an ACK for non-ACK-eliciting packet (as that is not forbidden) and then you do know something that you should not ignore. The text is unclear about that. Maybe adding another sentence is the right thing to do.
NAME I'm not sure I follow. If a sender receives an ack for a non-ack-eliciting packet, that only establishes its receipt. I don't understand how what you are saying is a problem with this text. That said, I do think this text incorrectly depicts the actual mechanism below, so I've written .
NAME I think you're saying the same thing. The text as currently written suggests that: Send an ack-eliciting packet Send 100 non-ack-eliciting packets Send another ack-eliciting packet Receive an ack for the 100 non-ack-eliciting packets If persistent congestion is the loss of all packets in that time frame, this isn't persistent congestion. If persistent congestion is the loss of all ack-eliciting packets in that time frame, this is. The change in the follow-up PRs brings it back to what I think was originally intended.
NAME : But we do want loss of non-ack-eliciting packets to count towards persistent congestion; they aren't special in terms of loss, they're just special in terms of eliciting an ack. Which is why we only care about the "edge packets" being ack-eliciting.
On the mailing list Timo Völker asked some questions about persistent congestion and why it was only applicable to in-flight packets. During the explanation, I realized that it should probably be ack-eliciting packets, not in-flight. UPDATE: It already is ack-eliciting in all but one spot. I wrote PR to fix that one spot.
To have the context, I quote your explanation. I think ack-eliciting makes more sense here than in-flight. At least, I understand that for the persistent congestion detection the first and the last packet need to be ack-eliciting. But why does this have to be true for packets sent between these two ack-eliciting packets? Here is an example for the edge case. The loss of three out of 50 packets let the sender consider this a persistent congestion. In general, I don't see a real issue here. But, I would like to understand why it is helpful to ignore the fact that non-ack-eliciting packets, sent between two lost ack-eliciting packets, have been acknowledged.
This absolutely should not be in flight packets. This may not cause a major issue as it stands but it is not technically correct.
The text is largely correct(ack-eliciting, not in-flight) already, based on my reading. Unfortunately, the very first description is incorrect, which is potentially confusing, so see

7.6.  <ins> When a sender establishes loss of all ack-eliciting packets sent over a long enough duration, the network is considered to be experiencing </ins> persistent congestion. 7.6.1.


at a time, reducing an exponential-time attack into a linear-time attack. <del> 7.2. </del>  QPACK mitigates but does not completely prevent attacks modeled on CRIME (CRIME) by forcing a guess to match an entire field line, connection by different web origins (RFC6454) are made by mutually distrustful entities. <del> 7.3. </del>  Users of HTTP that require confidentiality for header or trailer fields can use values with entropy sufficient to make guessing field name might occur for shorter values more quickly or with higher probability than for longer values. <del> 7.4. </del>  Implementations can also choose to protect sensitive fields by not compressing them and instead encoding their value as literals. Note that these criteria for deciding to use a never-indexed literal representation will evolve over time as new attacks are discovered. <del> 7.5. </del>  There is no currently known attack against a static Huffman encoding. A study has shown that using a static Huffman encoding table created attacker could not take advantage of this information leakage to recover any meaningful amount of information (see PETAL). <del> 7.6. </del>  An attacker can try to cause an endpoint to exhaust its memory. QPACK is designed to limit both the peak and stable amounts of memory new streams, reading only from the encoder stream, or closing the connection. <del> 7.7. </del>  An implementation of QPACK needs to ensure that large values for integers, long encoding for integers, or long string literals do not

Magnus Nystrom : not be able (based on no current known attack against static Huffman encodings) to mount the previously described "probing" attack? If so, adding a sentence at the end of the section along the lines of "Thus, the previously mentioned probing attack is not known to be applicable for any fields where static Huffman encoding is used."
This section is not related to the probing attack. I think some of the confusion here is lost formatting when importing from RFC 7541. The TOC should be:

at a time, reducing an exponential-time attack into a linear-time attack.  <ins> 7.1.1. </ins> QPACK mitigates but does not completely prevent attacks modeled on CRIME (CRIME) by forcing a guess to match an entire field line, connection by different web origins (RFC6454) are made by mutually distrustful entities.  <ins> 7.1.2. </ins> Users of HTTP that require confidentiality for header or trailer fields can use values with entropy sufficient to make guessing field name might occur for shorter values more quickly or with higher probability than for longer values.  <ins> 7.1.3. </ins> Implementations can also choose to protect sensitive fields by not compressing them and instead encoding their value as literals. Note that these criteria for deciding to use a never-indexed literal representation will evolve over time as new attacks are discovered.  <ins> 7.2. </ins> There is no currently known attack against a static Huffman encoding. A study has shown that using a static Huffman encoding table created attacker could not take advantage of this information leakage to recover any meaningful amount of information (see PETAL).  <ins> 7.3. </ins> An attacker can try to cause an endpoint to exhaust its memory. QPACK is designed to limit both the peak and stable amounts of memory new streams, reading only from the encoder stream, or closing the connection.  <ins> 7.4. </ins> An implementation of QPACK needs to ensure that large values for integers, long encoding for integers, or long string literals do not


The "https" scheme associates authority with possession of a certificate that the client considers to be trustworthy for the host <del> identified by the authority component of the URL.  If a server presents a certificate and proof that it controls the corresponding private key, then a client will accept a secured TLS session with that server as being authoritative for all origins with the "https" scheme and a host identified in the certificate. </del>  A client MAY attempt access to a resource with an "https" URI by resolving the host identifier to an IP address, establishing a QUIC Once a connection exists to a server endpoint, this connection MAY be reused for requests with multiple different URI authority components. <del> In general, a server is considered authoritative for all URIs with the "https" scheme for which the hostname in the URI is present in the authenticated certificate provided by the server, either as the CN field of the certificate subject or as a dNSName in the subjectAltName field of the certificate; see RFC6125.  For a host that is an IP address, the client MUST verify that the address appears as an iPAddress in the subjectAltName field of the certificate.  If the hostname or address is not present in the certificate, the client MUST NOT consider the server authoritative for origins containing that hostname or address.  See Section 4.3 of SEMANTICS for more detail on authoritative access. </del> Clients SHOULD NOT open more than one HTTP/3 connection to a given host and port pair, where the host is derived from a URI, a selected alternative service (ALTSVC), or a configured proxy.  A client MAY

The definition of how you determine a certificate matches an origin isn't specific to reuse, so it belongs in the description of establishing a connection. 3.4 now just says that the certificate needs to also be authoritative for any other origins you want to reuse the connection for. The issue also suggested making this easier to understand, so I'd appreciate suggestions for greater clarity without losing the specifics.
Hilarie Orman wrote: >The only problem that I have is that the authentication model for QUIC-HTTP is not explicitly spelled out. The only discussion is in section 3.4 Connection Reuse, and although that section may be technically correct, I find it hard to understand.
For server authentication, which I think is what you're getting at here, 3.2 also mentions the concept of establishing authority, but doesn't go into much detail. I suspect the specifics of comparing a certificate should move to 3.2. I'd appreciate suggestions for making it easier to understand.
Seems fine.This LGTM. It's clear.

The "https" scheme associates authority with possession of a certificate that the client considers to be trustworthy for the host  <ins> identified by the authority component of the URL. If a server presents a valid certificate and proof that it controls the corresponding private key, then a client will accept a secured TLS session with that server as being authoritative for all origins with the "https" scheme and a host identified in the certificate. The host must be listed either as the CN field of the certificate subject or as a dNSName in the subjectAltName field of the certificate; see RFC6125.  For a host that is an IP address, the client MUST verify that the address appears as an iPAddress in the subjectAltName field of the certificate. If the hostname or address is not present in the certificate, the client MUST NOT consider the server authoritative for origins containing that hostname or address.  See Section 4.3 of SEMANTICS for more detail on authoritative access. </ins> A client MAY attempt access to a resource with an "https" URI by resolving the host identifier to an IP address, establishing a QUIC Once a connection exists to a server endpoint, this connection MAY be reused for requests with multiple different URI authority components.  Clients SHOULD NOT open more than one HTTP/3 connection to a given host and port pair, where the host is derived from a URI, a selected alternative service (ALTSVC), or a configured proxy.  A client MAY


7.6. <del> When a sender establishes loss of all ack-eliciting packets sent over a long enough duration, the network is considered to be experiencing persistent congestion. </del>  7.6.1. 7.6.2. <del> A sender establishes persistent congestion on receiving an </del>  acknowledgement if at least two ack-eliciting packets are declared lost, and: <del> all packets, across all packet number spaces, sent between these two send times are declared lost; </del>  the duration between the send times of these two packets exceeds the persistent congestion duration (pc-duration); and a prior RTT sample existed when both packets were sent.  The persistent congestion period SHOULD NOT start until there is at least one RTT sample.  Before the first RTT sample, a sender arms its PTO timer based on the initial RTT (pto-handshake), which could be

Removes the word ack-eliciting in the descriptive text to persistent congestion. It also contains minor text changes in the following of that section.
I'm taking the suggested changes and this PR. Thanks, NAME !
On the mailing list Timo Völker asked some questions about persistent congestion and why it was only applicable to in-flight packets. During the explanation, I realized that it should probably be ack-eliciting packets, not in-flight. UPDATE: It already is ack-eliciting in all but one spot. I wrote PR to fix that one spot.
To have the context, I quote your explanation. I think ack-eliciting makes more sense here than in-flight. At least, I understand that for the persistent congestion detection the first and the last packet need to be ack-eliciting. But why does this have to be true for packets sent between these two ack-eliciting packets? Here is an example for the edge case. The loss of three out of 50 packets let the sender consider this a persistent congestion. In general, I don't see a real issue here. But, I would like to understand why it is helpful to ignore the fact that non-ack-eliciting packets, sent between two lost ack-eliciting packets, have been acknowledged.
This absolutely should not be in flight packets. This may not cause a major issue as it stands but it is not technically correct.
The text is largely correct(ack-eliciting, not in-flight) already, based on my reading. Unfortunately, the very first description is incorrect, which is potentially confusing, so see
Looking good, one further suggestion.

7.6.  <ins> When a sender establishes loss of all packets sent over a long enough duration, the network is considered to be experiencing persistent congestion. </ins> 7.6.1. 7.6.2.  <ins> A sender establishes persistent congestion after the receipt of an </ins> acknowledgement if at least two ack-eliciting packets are declared lost, and:  <ins> all packets, across all packet number spaces, sent between the send times of two ack-eliciting packets are declared lost; </ins> the duration between the send times of these two packets exceeds the persistent congestion duration (pc-duration); and a prior RTT sample existed when both packets were sent. <ins> These two packets MUST be ack-eliciting, since a receiver is required to acknowledge only ack-eliciting packets within its maximum ack delay; see Section 13.2 of QUIC-TRANSPORT. </ins> The persistent congestion period SHOULD NOT start until there is at least one RTT sample.  Before the first RTT sample, a sender arms its PTO timer based on the initial RTT (pto-handshake), which could be


7.6.2. A sender establishes persistent congestion after the receipt of an <del> acknowledgement if at least two ack-eliciting packets are declared </del>  lost, and: all packets, across all packet number spaces, sent between the <del> send times of two ack-eliciting packets are declared lost; </del>  the duration between the send times of these two packets exceeds the persistent congestion duration (pc-duration); and <del> a prior RTT sample existed when both packets were sent. </del>  These two packets MUST be ack-eliciting, since a receiver is required to acknowledge only ack-eliciting packets within its maximum ack

NAME rightly noted lack of clarity (), which this PR attempts to fix. There's a new MUST here, but I think it was again implicit. EDIT: Removed this MUST since NAME notes correctly that this is a definition, not an action.

7.6.2. A sender establishes persistent congestion after the receipt of an  <ins> acknowledgement if two packets that are ack-eliciting are declared </ins> lost, and: all packets, across all packet number spaces, sent between the  <ins> send times of these two packets are declared lost; </ins> the duration between the send times of these two packets exceeds the persistent congestion duration (pc-duration); and  <ins> a prior RTT sample existed when these two packets were sent. </ins> These two packets MUST be ack-eliciting, since a receiver is required to acknowledge only ack-eliciting packets within its maximum ack


support for one of these versions and can expect that clients ignore the value. <del> [[RFC editor: please remove the remainder of this section before publication.]] The version number for the final version of this specification (0x00000001), is reserved for the version of the protocol that is published as an RFC. Version numbers used to identify IETF drafts are created by adding the draft number to 0xff000000.  For example, draft-ietf-quic- transport-13 would be identified as 0xff00000d. Implementors are encouraged to register version numbers of QUIC that they are using for private experimentation on the GitHub wiki at <https://github.com/quicwg/base-drafts/wiki/QUIC-Versions>. </del> 16. QUIC packets and frames commonly use a variable-length encoding for

Note: Don't merge until the last minute.
I want to do more than just approve this, but this'll have to do

support for one of these versions and can expect that clients ignore the value.  16. QUIC packets and frames commonly use a variable-length encoding for


first Initial packet. This secret is determined by using HKDF-Extract (see Section 2.2 of <del> HKDF) with a salt of 0xafbfec289993d24c9e9786f19c6111e04390a899 and a </del>  IKM of the Destination Connection ID field.  This produces an intermediate pseudorandom key (PRK) that is used to derive two separate secrets for sending and receiving. output of AEAD_AES_128_GCM (AEAD) used with the following inputs: The secret key, K, is 128 bits equal to <del> 0xccce187ed09a09d05728155a6cb96be1. </del>  <del> The nonce, N, is 96 bits equal to 0xe54930f97f2136f0530a8c1c. </del>  The plaintext, P, is empty. The secret key and the nonce are values derived by calling HKDF- Expand-Label using <del> 0x8b0d37eb8535022ebc8d76a207d80df22646ec06dc809642c30a8baa2baaff4c as </del>  the secret, with labels being "quic key" and "quic iv" (protection- keys). 10. <del> This document registers the quic_transport_parameters extension found in quic_parameters in the TLS ExtensionType Values Registry TLS- REGISTRIES. </del>  <del> The Recommended column is to be marked Yes. The TLS 1.3 Column is to include CH and EE. </del>

Also, examples use final version numbers. IMPORTANT: the TLS extension codepoint needs to be updated, both in the text and in the examples (which will result in changes to authentication tags and the affected ciphertext).
I plugged the test vectors into the quiche test suite and it checks out URL
Hey there! We are currently implementing the Initial packet encryption process and it successfully works with the client Initial example from the appendix A.2 of the QUIC-TLS draft. However, when running tests with values for the server Initial example from the appendix A.3, tests regarding the parsing, header sampling, header protection and final encryption result all succeed, except for the header protection mask: the value we generate is: , while the draft explicitly specifies it should be something completely different: . Note that the value we generate is indeed used later on for the header protection and final encryption result, which both reveal successful. Therefore, three possibilities we thought of, in ascending order of probability: Our implementation is completely broken. The mask value provided by the current version of the specification (A.3) is correct, but all others are incorrect. Only the mask value is incorrect, while the rest is correct. We think the last option is the most probable one, as it could just mean it was forgotten to be updated in a recent draft version. If you think this is the case as well, then I would gladly open up a PR in order to close this, I just wanted to make sure beforehand. Otherwise, I would happily accept a tad of guidance towards a correct implementation. Thanks in advance, Paul.
Investigated a bit more. Looking at the result of a , we can see that: 2ebdfd53a is indeed meant to update example vectors, but does it only partially it seems: while 2ebdfd53a is for other secrets, but did it completely. So it should confirm our hypothesis.
hi NAME Our editors are taking some slight respite, we'll respond soon.
No problem NAME I was just adding some more information I forgot to mention yesterday. Thanks for the heads up :+1:
It seems to me that the header is not correct. Thoughts?
For me the header looks fine: it is consistent with the final result. What looks wrong to you NAME
I'll assign this to NAME since I think that is his code.
I can confirm this, but it won't matter as -33 will have new values entirely. The mask value should be 56fb1381e7 for the second example.
NAME would you link this issue to the PR that will update the values for -33 so it gets closed then?
New values seem to work in my testing.

first Initial packet. This secret is determined by using HKDF-Extract (see Section 2.2 of  <ins> HKDF) with a salt of 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a and a </ins> IKM of the Destination Connection ID field.  This produces an intermediate pseudorandom key (PRK) that is used to derive two separate secrets for sending and receiving. output of AEAD_AES_128_GCM (AEAD) used with the following inputs: The secret key, K, is 128 bits equal to  <ins> 0xbe0c690b9f66575a1d766b54e368c84e. </ins>  <ins> The nonce, N, is 96 bits equal to 0x461599d35d632bf2239825bb. </ins> The plaintext, P, is empty. The secret key and the nonce are values derived by calling HKDF- Expand-Label using  <ins> 0xd9c9943e6101fd200021506bcc02814c73030f25c79d71ce876eca876e6fca8e as </ins> the secret, with labels being "quic key" and "quic iv" (protection- keys). 10.  <ins> IANA has registered a codepoint of 57 (or 0x39) for the quic_transport_parameters extension (defined in quic_parameters) in the TLS ExtensionType Values Registry TLS-REGISTRIES. </ins>  <ins> The Recommended column for this extension is marked Yes. The TLS 1.3 Column includes CH and EE. </ins>


on a single byte, or 2 bytes of frames for a 2-byte packet number encoding. <del> The sampled ciphertext for a packet with a short header can be determined by the following pseudocode: </del>  For example, for a packet with a short header, an 8-byte connection ID, and protected with AEAD_AES_128_GCM, the sample takes bytes 13 to 28 inclusive (using zero-based indexing). <del> A packet with a long header is sampled in the same way, noting that multiple QUIC packets might be included in the same UDP datagram and that each one is handled separately. </del>  5.4.3.

Changes the pseudo-code of header protection to use , because that value would be known to the QUIC stack by the time it tries to obtain the header protection sample. Existing pseudo code includes the logic necessary for skipping entire packet header, which is a distraction to the readers.
Tracking issue for
NAME wrote in : >The header protection sampling (HPS) is quite simple, as it is just selecting 16 bytes out of the encrypted payload, but we think the current description is really confusing. >Indeed, section 5.4.2 explains the process with pseudocodes computing an offset relative to the beginning of an entire packet which payload is the encrypted one. However, when applying the AEAD function, one cannot do it in-place, so additional allocation must be reserved in any case. Therefore, one most probably has access to the encrypted payload separately from the unprotected packet, headers and payload. >Thus, this PR proposes two things, split in different commits: >Clearly and explicitly specify the two already-existing pseudocodes sample from an entire packet. Add another pseudocode explaining how the sampling can be achieved for both short-header and long-header packets. We have not changed or removed any existing content however, as we believe this draft could use some more explanations and clarifications here and there, rather than less with confusing elements, even if it means a bit of duplication. >Please tell us if there is any form of typo, wrong wording, grammatical issue or just general mistake, especially concerning the new pseudocode. Let's please keep the discussion here (on this issue) and then we'll dispatch based on the outcome.
This is a fine addition in theory -- it's purely additive -- but I'm not certain that it really helps implementers. The PR will need some editorializing if we do want to take it, but I've not seen a ton of interest in taking this PR in from others. If anyone else wants this in, please speak up.
Well I understand your concern NAME but in what way does it not help implementers exactly? When we worked on these functions, we spent several hours being completely wrong in our understanding of the specification, while both parts are actually quite simple. Therefore, we believe it should be just as simple to understand from the first reading. That's why I proposed such a clarification, but also because if it happened to us, then there's a good chance some will get confused as well. For the first part, although pseudocodes used as a designation of the sampled array, the first sentence of the section states that the sampling operates on the ciphertext. This confused us quite a bit, hence the first commit in order to avoid being cryptic. For the second one, the idea of pseudocodes in the first place is to help implementers, right? From our understanding, the proposed new one should provide a simple and more pragmatic way to achieve the sampling. Obviously I might be wrong in saying this, but then let me understand why I am so.
I have a mixed feeling about the proposal. From implementation standpoint, I agree that it helps people understand the design if we clarify the sampling offset relative to encrypted data. In fact, the first paragraphs of that section already states that. OTOH, only the sender can implemented by using that property. The receiver has to determine the starting offset of the sample relative to the starting offset of the packet number, because the starting offset of encrypted data cannot be determined until the length of packet number is determined as part of header unprotection. Therefore, I think it is beneficial to retain current definition of packet protection which is talks about offsets relative to packets. I do not agree with this statement for two reasons. The first reason is that, as stated above, receivers would never have access to encrypted payload before unprotecting the header. The second reason is that I tend to assume senders to do AEAD encryption in-place. They prepare a MTU-sized buffer, writes unprotected packet header, then fills the payload bytes, leaving at least 16 bytes of extra space at the end to hold the AEAD tag. Then, it mutates the buffer using AEAD, then using header protection. That's how quicly works, and I would be surprised if we are the only one implementation taking that approach. To summarize, the proposed description of the alternative approach only works on the sender-side, and it is unclear to me if it helps many implementers.
Yes you are right NAME when receiving, one cannot determine the limit between the protected packet number and the payload. However, it seems to me that if one has the ability to parse a series of bytes into a form of packet structure with a sort of best-effort method, then they could start by doing so and it would result in a partially parsed packet where the packet number length would be completely wrong. But if that wrong packet number length (the header field + 1) is made to be consistent with the parsed packet number's bytes length () during that parsing, then it would not interfere with the header protection sampling because is the offset relative to the (wrong) start of the encrypted payload required in order to consider the packet number length to be the maximum value 4, which is exactly what needs to be achieved for the sampling. This is the strategy we have adopted in our implementation and from our tests, it works for both the sender's and the receiver's sides. Of course that strategy does not work for the rest of operations required to unprotect the packer number field by considering the array of bytes and then decrypt the payload from its deduced correct start. Correct me if I'm still wrong here. I was not aware it was possible, sorry to have been wrongly assertive. It's just that the AEAD implementation we used does not work in-place, so I just assumed it would probably be the case elsewhere too. I suppose you wouldn't be, especially for implementations having performance as a critical objective. But then, I would also be surprised if we were the only implementation not doing it in-place. Is "helping some implementers" not enough for such a specification or would it add too much noise? I just assumed RFCs were meant to be as unambiguous, clear and easy-to-implement as possible, hence this proposal being additive.
NAME I'm not sure if I follow, but I assume that your point can be generalized as that the receiver will know where the packet number field begins, and use that to figure out where the sample is. I agree with that. Yes, this issue can be viewed as finding the right balance between helpful to some vs. adding noise to others. But taking a step back, I tend to think that the current example is confusing to everybody, because it replicates how an endpoint should parse entire packet header, where people would already have parsed all the plaintext part of the packet header before trying to unprotect the header. Hence filed . I'm not sure if the PR fully resolves NAME concern, but anyways.
You're right, because it assumes the implementation to have a form of access to where the payload could start, but probably does not. I still think both approaches could be presented without too much confusion, but oh well
I like this. Thanks NAME like this clarification -- it's much easier to follow.

on a single byte, or 2 bytes of frames for a 2-byte packet number encoding.  <ins> The sampled ciphertext can be determined by the following pseudocode: where the packet number offset of a short header packet can be calculated as: and the packet number offset of a long header packet can be calculated as: </ins> For example, for a packet with a short header, an 8-byte connection ID, and protected with AEAD_AES_128_GCM, the sample takes bytes 13 to 28 inclusive (using zero-based indexing).  <ins> Multiple QUIC packets might be included in the same UDP datagram. Each packet is handled separately. </ins> 5.4.3.


acknowledgement if two packets that are ack-eliciting are declared lost, and: <del> all packets, across all packet number spaces, sent between the send times of these two packets are declared lost; </del>  the duration between the send times of these two packets exceeds the persistent congestion duration (pc-duration); and

Persistent congestion means that no packets between two ack-eliciting packets was received by the peer. This was codified as all packets between those boundary packets being lost. However, non-ack-eliciting packets can be often never declared as lost, since loss declaration requires a packet to be in-flight (URL). What we really want to say is that none of the packets between these boundary packets were acknowledged. This PR makes this change. Note that the example below the definition already follows this; packets in between the boundary packets are noted as "not acknowledged" rather than marked as lost. (Problem also noted here: URL)

acknowledgement if two packets that are ack-eliciting are declared lost, and:  <ins> across all packet number spaces, none of the packets sent between the send times of these two packets are acknowledged; </ins> the duration between the send times of these two packets exceeds the persistent congestion duration (pc-duration); and


Indicates that x can be any length from A to B; A can be omitted to indicate a minimum of zero bits and B can be omitted to indicate no set upper limit; values in this format always end on <del> an octet boundary </del>  Indicates that x has a fixed value of C Indicates that x can be any length from A to B; A can be omitted to indicate a minimum of zero bits and B can be omitted to indicate no set upper limit; values in this format always end on <del> an octet boundary </del>  Indicates that x has a fixed value of C with the length described by ?, as above (RFC6265) MAY be split into separate field lines, each with one or more cookie-pairs, before compression.  If a decompressed field section contains multiple cookie field lines, these MUST be <del> concatenated into a single octet string using the two-octet delimiter </del>  of 0x3b, 0x20 (the ASCII string "; ") before being passed into a context other than HTTP/2 or HTTP/3, such as an HTTP/1.1 connection, or a generic HTTP server application.

Maybe leftovers from , or newly added text.
I'm sure the document was touched by his noodly appendage of the Octocat :octocat:
How did those slip through!?Yes, I think these are mostly new / imported from other docs.Thanks, NAME !

Indicates that x can be any length from A to B; A can be omitted to indicate a minimum of zero bits and B can be omitted to indicate no set upper limit; values in this format always end on  <ins> an byte boundary </ins> Indicates that x has a fixed value of C Indicates that x can be any length from A to B; A can be omitted to indicate a minimum of zero bits and B can be omitted to indicate no set upper limit; values in this format always end on  <ins> an byte boundary </ins> Indicates that x has a fixed value of C with the length described by ?, as above (RFC6265) MAY be split into separate field lines, each with one or more cookie-pairs, before compression.  If a decompressed field section contains multiple cookie field lines, these MUST be  <ins> concatenated into a single byte string using the two-byte delimiter </ins> of 0x3b, 0x20 (the ASCII string "; ") before being passed into a context other than HTTP/2 or HTTP/3, such as an HTTP/1.1 connection, or a generic HTTP server application.