QUIC packet and framing add at least 36 bytes of overhead to the ClientHello message.  That overhead increases if the client chooses a <del> connection ID without zero length.  Overheads also do not include the token or a connection ID longer than 8 bytes, both of which might be required if a server sends a Retry packet. </del>  A typical TLS ClientHello can easily fit into a 1200-byte packet. However, in addition to the overheads added by QUIC, there are Session resumption allows servers to link activity on the original connection with the resumed connection, which might be a privacy issue for clients.  Clients can choose not to enable resumption to <del> avoid creating this correlation.  Client SHOULD NOT reuse tickets as </del>  that allows entities other than the server to correlate connections; see Section C.4 of TLS13. Packets protected with Initial secrets (initial-secrets) are not authenticated, meaning that an attacker could spoof packets with the intent to disrupt a connection.  To limit these attacks, Initial <del> packet protection keys can be discarded more aggressively than other </del>  keys. The successful use of Handshake packets indicates that no more

Too fast! Thanks.
This will require rewrapping, which I can do if you prefer.

QUIC packet and framing add at least 36 bytes of overhead to the ClientHello message.  That overhead increases if the client chooses a  <ins> source connection ID longer than zero bytes.  Overheads also do not include the token or a destination connection ID longer than 8 bytes, both of which might be required if a server sends a Retry packet. </ins> A typical TLS ClientHello can easily fit into a 1200-byte packet. However, in addition to the overheads added by QUIC, there are Session resumption allows servers to link activity on the original connection with the resumed connection, which might be a privacy issue for clients.  Clients can choose not to enable resumption to  <ins> avoid creating this correlation.  Clients SHOULD NOT reuse tickets as </ins> that allows entities other than the server to correlate connections; see Section C.4 of TLS13. Packets protected with Initial secrets (initial-secrets) are not authenticated, meaning that an attacker could spoof packets with the intent to disrupt a connection.  To limit these attacks, Initial  <ins> packet protection keys are discarded more aggressively than other </ins> keys. The successful use of Handshake packets indicates that no more


endpoint.  The connection ID is used by endpoints and the intermediaries that support them to ensure that each QUIC packet can be delivered to the correct instance of an endpoint.  At the <del> endpoint, the connection ID is used to identify which QUIC connection the packet is intended for. </del>  The connection ID is chosen by each endpoint using version-specific methods.  Packets for the same QUIC connection might use different

Issue raised by NAME via : OLD:/At the endpoint, the connection ID is used to identify which QUIC connection the packet is intended for./ NEW:/At the endpoint, the connection ID is used to identify the QUIC connection for which the packet is intended./
Sounds good. I'll do that too. I have read Version-Independent Properties of QUIC (-30) in the WGLC and think this is ready, I found 3 editorial comments that might need consideration after the WGLC: (i) I suggest the introduction for this spec starts abruptly. Is it worth considering one sentence top explain what QUIC is, in case someone read this document first? - One possible solution could be to move the text in section 3 to section 1? (To me, the section 3 text does not need to come after the Section 2 definitions.) (ii) Does this read better with /for/ moved within the sentence? OLD:/At the endpoint, the connection ID is used to identify which QUIC connection the packet is intended for./ NEW:/At the endpoint, the connection ID is used to identify the QUIC connection for which the packet is intended./ (iii) I see the desire to emphasise the “NOT”, but I query if this is correct usage of a RFC2119 keyword in Appendix A?: /The following statements are NOT guaranteed to be true for every QUIC version:/ Best wishes, Gorry

endpoint.  The connection ID is used by endpoints and the intermediaries that support them to ensure that each QUIC packet can be delivered to the correct instance of an endpoint.  At the  <ins> endpoint, the connection ID is used to identify the QUIC connection for which the packet is intended. </ins> The connection ID is chosen by each endpoint using version-specific methods.  Packets for the same QUIC connection might use different


If an endpoint has cause to expect that IP packets with an ECT codepoint might be dropped by a faulty network element, the endpoint could set an ECT codepoint for only the first ten outgoing packets on <del> a path, or for a period of three RTTs.  If all packets marked with non-zero ECN codepoints are subsequently lost, it can disable marking on the assumption that the marking causes in loss. </del>  An endpoint thus attempts to use ECN and validates this for each new connection, when switching to a server's preferred address, and on

I tried to just make a note here saying "if you don't send many packets, you won't send many packets", but then decided that 3PTO is probably more sensible. As it is, 3 RTT is the value that the PTO is initialized to (validation almost always happens on a new path). And the loss of a single packet just doesn't cut it. Even 3 packets (the first packet, plus two on PTO, assuming two are sent on PTO) is not that strong, but at least it isn't insanely tight.
URL and URL describe ECN validation and they say : dropped by a faulty network element, an endpoint could set the ECT(0) codepoint for only the first ten outgoing packets on a path, or for a period of three RTTs, whichever occurs first. Initial PTO timeout is also 3RTT. If client does not get ACK for its first flight Initial, after 3RTT later, it resends Initial without ECT set because client is now in "UNKNOWN" state. If the first client Initial is really lost, ECN validation fails. It seems to me that it would be better to lengthen timeout or ensure enough packets are sent not limited by the timeout?
It seems like this particular problem was introduced by reducing the initial RTT from 500ms to 333ms (in ).
We use 3*PTO everywhere else, so suggesting it here makes sense to me.

If an endpoint has cause to expect that IP packets with an ECT codepoint might be dropped by a faulty network element, the endpoint could set an ECT codepoint for only the first ten outgoing packets on  <ins> a path, or for a period of three PTOs (see Section 6.2 of QUIC- RECOVERY).  If all packets marked with non-zero ECN codepoints are subsequently lost, it can disable marking on the assumption that the marking causes in loss. </ins> An endpoint thus attempts to use ECN and validates this for each new connection, when switching to a server's preferred address, and on


7. <del> It is possible that middleboxes could use traits of a specific </del>  version of QUIC and assume that when other versions of QUIC exhibit similar traits the same underlying semantic is being expressed. There are potentially many such traits; see bad-assumptions.  Some

I can't stop re-reading this text. There isn't much of it, but that makes it easier to spot little annoyances like this.

7.  <ins> It is possible that middleboxes could observe traits of a specific </ins> version of QUIC and assume that when other versions of QUIC exhibit similar traits the same underlying semantic is being expressed. There are potentially many such traits; see bad-assumptions.  Some


4.5.6. <del> The literal field line without name reference representation encodes a field name and a field value as string literals. </del>  This representation begins with the '001' three-bit pattern.  The fourth bit is the 'N' bit as described in literal-name-reference.

This saves bytes (that's the most important reason). This uses something positive rather than something negative to define the instruction. Yes, it means that there are two "Literal"s in the name of an instruction. But now it says what is inside rather than what isn't.
NAME I can't solve all the problems :) I think that in HPACK "literal" was reserved for the value more than the name. Maybe this only highlights that...
I understand. This commit at least solves some of them, thank you for the improvement!
Beauty is in the eye of the beholder. What am I to do with all the "wonr" variable names?
Without Name Reference might be better replaced as "With Literal Name" (Damn my enter key...)
Also consider changing on line 1640, but GitHub won't let me comment that far from a change.I think this is an improvement. It has always bothered me that "literal field line" is used in sitations when only part of the field line is literal, and this change does not address that. An alternative would be: insert with literal name => insert literal field line (or literal entry) literal field line with name reference => field line with name reference and literal value literal field line with literal name => literal field lineSeems like a sensible change.

4.5.6.  <ins> The literal field line with literal name representation encodes a field name and a field value as string literals. </ins> This representation begins with the '001' three-bit pattern.  The fourth bit is the 'N' bit as described in literal-name-reference.


described in transport-parameter-grease, some identifiers are reserved in order to exercise this requirement.  New transport parameters can be registered according to the rules in iana-transport-parameters.

Some advice.
The spec currently says Assume now that the client does store a transport parameter it doesn't understand. At the time when it actually establishes a 0-RTT connection, the implementation was updated (or new extensions were activated), and now it understands the TP. This would lead to unintended consequences, especially if this was a TP that it was prohibited from saving in the first place. I think the sentence would be clearer if it said MUST NOT. Not sure if that is an editorial change though.
There is an unstated assumption here: that the client doesn't change its opinion about transport parameters between two connections. This applies to other transport parameters. For instance, a client might like a stream limit it is given when initially connecting, but might change its mind when trying 0-RTT (maybe the limit is too small to bother with 0-RTT). A MUST NOT in this case is worse than not answering the question. Let's say that the client initially doesn't support maxudppayload_size as it always sends 1200 byte datagrams. As it cannot process this when it first connects, it doesn't store the value. Later, when it attempts to connect it has been updated to understand this transport parameter. As it has been prohibited from storing the value, it can't read the value that it might have otherwise saved as an opaque value. So it attempts 0-RTT and assumes that it can send 1400 byte packets safely, when it should know that it can't. Generally, this doesn't happen because defaults for transport parameters are the least permissive value. New transport parameters generally have defaults that are safe to use in 0-RTT in case the original value was not remembered. However, we don't require that. (This is a bad example, as we explicitly don't worry about violating the datagram size limit in 0-RTT, but I didn't have a more evocative example.)
NAME with NAME explanation, does anything need to be done still in your opinion?
NAME example seems to be an argument for requiring clients to store unknown TPs, and checking their status (should have been saved for 0-RTT or shouldn't have been saved) when restoring them. Is that something we should require?
Or it is an argument for you to throw out resumption information if you have added new capabilities that might depend on stuff you previously threw out.
I think it's an argument for discarding resumption information when adding new capabilities. FWIW, Chrome doesn't persist resumption information to disk for security reasons, but this has the side benefit that this information does not persist across restarts, including binary updates. One exception I can imagine is a client which gets an experiment config push that doesn't require a restart, enabling or disabling a new extension feature. However, in that case, not saving unknown TPs still seems safest, since if the extension depended upon the unknown TP, then it's just not activated. I'm worried maxudppayload_size is a bad example because it's part of the core spec and it's not similar to extensions which add new frames or change the behavior of existing frames.
Either interpretation fixes the issue, but I would agree that resumption information shouldn't be used when the implementation changes behavior. But it could store all unknown TPs, and then reinterpret later. The current text allows both, which seems adequate. This isn't important enough to warrant a design change. I'm fine with the current text, but I'd be open to an editorial fix if someone sent a PR.
If the client stores the value, then gains the capability to interpret it, it also gains the capability to know it should be ignoring any cached value. I'm not too concerned about that variant. If the client doesn't store the value but the server is still enforcing it, that's handled by making the default the least permissive value. If we don't state that assumption, perhaps we should.
I'd be happy with an editorial PR here to clarify some of this, but I'm not feeling confident enough to write it.

described in transport-parameter-grease, some identifiers are reserved in order to exercise this requirement. <ins> A client that does not understand a transport parameter can discard it and attempt 0-RTT on subsequent connections.  However, if the client adds support for a discarded transport parameter, it risks violating the constraints that the transport parameter establishes if it attempts 0-RTT.  New transport parameters can avoid this problem by setting a default of the most conservative value. </ins> New transport parameters can be registered according to the rules in iana-transport-parameters.


Errors that result in the connection being unusable, such as an obvious violation of protocol semantics or corruption of state that affects an entire connection, MUST be signaled using a <del> CONNECTION_CLOSE frame (frame-connection-close).  An endpoint MAY close the connection in this manner even if the error only affects a single stream. </del>  Application-specific protocol errors are signaled using the CONNECTION_CLOSE frame with a frame type of 0x1d.  Errors that are

The spec says: First of all, I have no idea what a "corruption of state" is supposed to be. More importantly, the second sentence seems to suggest that if an error only affects a single stream, it might not be required to send a connection-level error. I'm thinking of a stream-level flow control violation.
The challenge here is that the transport doesn't define how streams can be used by application layer protocols, but seems to want to strongly encourage how an application uses error codes. An example of "corruption of state" is something like a QPACK error that renders the HTTP/3 connection unusable because the QPACK state cannot be assured. In contrast, an application might treat receiving a header with an uppercase field name as a stream error that otherwise would not affect the rest of the H3 connection.
Anything remain to be done here?
It seems like NAME is interpreting this sentence as only applying to application protocol errors. If that's the intention, we should make that clear:
It's not exclusive to the application. Another example of corrupt state at the transport layer is CRYPTOBUFFEREXCEEDED -- you had to drop some bytes, so now your TLS handshake state is corrupted. As to the second piece, I think you're reading that backward. A stream error MAY always be promoted to a connection error if the implementation decides it's severe enough; it doesn't say anything about deciding to downplay errors if they're localized. If an error is defined to be a connection error, it's a connection error. If it's defined to be a stream error, however, you're always allowed to decide it's the last straw and walk away.
QUIC transport does not have stream-level errors, though applications can. As such, the last sentence references a circumstance that does not exist in the transport draft. So the best solution seems to be to remove the last sentence.
NAME : to your two points: "corruption of state that affects an entire connection" is clear that if the connection state is affected then it should result in a connection error agree that the second sentence seems unnecessary entirely, since stream-level errors are always treated as connection errors within the transport.

Errors that result in the connection being unusable, such as an obvious violation of protocol semantics or corruption of state that affects an entire connection, MUST be signaled using a  <ins> CONNECTION_CLOSE frame (frame-connection-close). </ins> Application-specific protocol errors are signaled using the CONNECTION_CLOSE frame with a frame type of 0x1d.  Errors that are


a path, or for a period of three PTOs (see Section 6.2 of QUIC- RECOVERY).  If all packets marked with non-zero ECN codepoints are subsequently lost, it can disable marking on the assumption that the <del> marking causes in loss. </del>  An endpoint thus attempts to use ECN and validates this for each new connection, when switching to a server's preferred address, and on

Section 13.4.2: If all packets marked with non-zero ECN codepoints are subsequently lost, it can disable marking on the assumption that the marking causes in loss. s/in/the?

a path, or for a period of three PTOs (see Section 6.2 of QUIC- RECOVERY).  If all packets marked with non-zero ECN codepoints are subsequently lost, it can disable marking on the assumption that the  <ins> marking caused the loss. </ins> An endpoint thus attempts to use ECN and validates this for each new connection, when switching to a server's preferred address, and on


reaching a state where key updates are not possible. For AEAD_AES_128_GCM and AEAD_AES_256_GCM, the confidentiality limit <del> is 2^25 encrypted packets; see gcm-bounds.  For </del>  AEAD_CHACHA20_POLY1305, the confidentiality limit is greater than the number of possible packets (2^62) and so can be disregarded.  For <del> AEAD_AES_128_CCM, the confidentiality limit is 2^23.5 encrypted </del>  packets; see ccm-bounds.  Applying a limit reduces the probability that an attacker can distinguish the AEAD in use from a random permutation; see AEBounds, ROBUST, and GCM-MU. AEAD_LIMIT_REACHED and not process any more packets. For AEAD_AES_128_GCM and AEAD_AES_256_GCM, the integrity limit is <del> 2^54 invalid packets; see gcm-bounds.  For AEAD_CHACHA20_POLY1305, </del>  the integrity limit is 2^36 invalid packets; see AEBounds.  For <del> AEAD_AES_128_CCM, the integrity limit is 2^23.5 invalid packets; see </del>  ccm-bounds.  Applying this limit reduces the probability that an attacker can successfully forge a packet; see AEBounds, ROBUST, and GCM-MU.  Future analyses and specifications MAY relax confidentiality or integrity limits for an AEAD.

The analysis describes conditions where higher limits might be allowed, and provides a formula in each case that can be used to determine other limits more readily. The number of changes I've made here are somewhat frightening. This is why I resisted doing this. This will need careful double-checking. I think that this is better, but I'm not sure that I feel happy about having spent the time. The actual numbers don't concern me much, anyone can subtract 2, but the changes to the structure really need to be looked at. Some of the changes are editorial, but they still need extra eyes.
cc NAME
This issue is in response to NAME 's URL . PR introduces anti-forgery limits for packet lengths up to 2^14 as those were inherited from the maximum TLS record size. Since NAME mentioned a way to compute limits for those sizes, we should use that and put the limit in the document, instead of leaving that as an exercise to the reader.
In case it helps: https://chris-URL
Anyone care to prepare a PR for this one?
Without a PR, this will stay as is...
Leaving things as-is is unsafe, as the current document allows sending QUIC packets of size 2^15 without providing bounds required for the security properties promised by the protocol. If we don't have anyone willing to the analysis for 2^16 (I don't consider myself competent enough to get this right), then we should disallow sending or receiving QUIC packets longer than 2^14. Otherwise we're shipping a protocol that is not safe.
So I talked this over with NAME and we both agree that this is not unsafe enough to block publication based on: Getting this right is hard enough that I'm concerned that we'll make another mistake. The number of mistakes we made already is enough that I don't want to put this in a spec without multiple rounds of checking. NAME points out that a change in targets also changes some MUST-level requirements and so wouldn't be a purely editorial change, so I'm going to remove the "editorial" tag and suggest that we just not take this.
Sounds like a nice to have that would benefit from more time and experience. Marking as "future-version" for later consideration but otherwise closing this to clear our landing path.
Hi NAME NAME NAME did I miss a recent change to our process? Since when are we closing non-editorial issues without a call for consensus?
I believe the process says that design issues cannot be closed without a call for consensus and this was never marked as a design issue. NAME created a flowchart at some point, but I'm failing to find it. Given the difficulty of deploying even 4k packets, I don't foresee this being a practical problem in the near future.
The flow chart is . Closing an issue that's marked neither editorial nor design isn't a scenario that's allowed according to that diagram.
I agree with David that this doesn't seem like the right resolution. I also agree with MT that we should not attempt to adjust the limits for bigger packets now. Given Ian's comment I think the correct resolution is to simply forbid packets > 2^14. If/when someone does the analysis for larger packet sizes, that document can simply lift this restriction and replace it with whatever the new rules are. This is explicitly contemplates in S 6.6. "Future analyses and specifications MAY relax confidentiality or integrity limits for an AEAD." Note that this would actually make the spec slightly shorter because you could remove the Note about how this applies to 2^14 not 2^16.
Hmm.. Thinking about this a little more We can certainly prohibit sending > 2^14 packets, but we if we prohibit receiving it, then we'll need an extension to let you send them. And if we don't, then the receiver will not be able to properly enforce the limits on 2^16. So, maybe not so easy...
We could solve this by slightly tweaking the definition of the transport parameter. OLD TEXT: NEW TEXT: If/when someone ever figures out anti-forgery limits for packet sizes above 16384, they can define an extension that negotiates that feature.
I had forgotten about this parameter. I don't think we even need an extension. We say: Then if we figure out new anti-forgery limits we remove these MUSTs. Only new implementations will send maxudppayloadsize > 16384 and only if both implementations are new will they send packets > 16384. That means that (1) sending implementations will enforce the right AEAD limits (2) only implementations which know about the new receive limits will offer maxudppayloadsize > 16384 and older implementations can just reject longer packets out of hand which means that they don't contribute to the forgery limit.
That sounds good to me.
I wold like more people to take a look at this. To me personally, this seems like a simple enough change, but it is a design change... Since we really want to start IETF LC today (because the longer we delay, the more editorial issues are being opened again, perpetuating the cycle), I propose we convert this issue to an IETF LC issue and resolve it together with anything else that gets raised during that.
Reducing the max packet size in QUIC v1 is fine by me, given I don't expect to exceed 16384 bytes.
NAME making this an IETF LC issue is fine if you prefer it that way. Can you please reopen this issue and replace the "future-version" GitHub tag with "ietf-lc"?
Done. I hope we can resolve this quickly.
Thanks. I hope so as well. Would you like me to send a PR or should I wait for IETF LC to start?
Let's wait for some more comments on the issue. The editors were swamped.
After thinking about this a lot, I prefer not to do anything. For several reasons, but mostly because I think that a mechanical protection is an overreaction to this situation. The target limits are arbitrary. The degradation here is at worst an increase in attacker advantage from 2^-57 to 2^-53. That's for CCM. The GCM and Poly1305 numbers scale linearly, so the change is to 2^-55. I could see a way to sharpen the text, but mandating use of the UDP size limit to do this seems unnecessary. Suggesting that usage - for the paranoid - would be OK.
If you're fairly confident in your analysis that 2^16 byte packets are safe given certain bounds, can we instead put those bounds in the spec? If you're not confident in said bounds, then I'm uncomfortable shipping a protocol whose security bounds we are not confident in. At the end of the day, either we know the bounds or we don't, I'm not understanding what a middle-ground looks like here.
I think I am with David here. Specifically: I think the document should provide a traffic limit and an analysis that those limits provide security at least at a given level. It seems like we have three choices (1) restrict the packet size (2) lower the traffic limits (3) provide an analysis with a weaker guarantee, given that as MT points out, the guarantee is somewhat arbitrary
Labeling this as "design" based on
I am moving this back to "Consensus Emerging" so we don't forget to call it out during the IETF LC.
Thanks, NAME I checked the derivations and produced the same results. This PR is exactly what I was hoping for, thanks for writing it. As far as I can tell, it's correct. It also fully addresses my concerns from .A few minor points.

reaching a state where key updates are not possible. For AEAD_AES_128_GCM and AEAD_AES_256_GCM, the confidentiality limit  <ins> is 2^23 encrypted packets; see gcm-bounds.  For </ins> AEAD_CHACHA20_POLY1305, the confidentiality limit is greater than the number of possible packets (2^62) and so can be disregarded.  For  <ins> AEAD_AES_128_CCM, the confidentiality limit is 2^21.5 encrypted </ins> packets; see ccm-bounds.  Applying a limit reduces the probability that an attacker can distinguish the AEAD in use from a random permutation; see AEBounds, ROBUST, and GCM-MU. AEAD_LIMIT_REACHED and not process any more packets. For AEAD_AES_128_GCM and AEAD_AES_256_GCM, the integrity limit is  <ins> 2^52 invalid packets; see gcm-bounds.  For AEAD_CHACHA20_POLY1305, </ins> the integrity limit is 2^36 invalid packets; see AEBounds.  For  <ins> AEAD_AES_128_CCM, the integrity limit is 2^21.5 invalid packets; see </ins> ccm-bounds.  Applying this limit reduces the probability that an attacker can successfully forge a packet; see AEBounds, ROBUST, and GCM-MU. <ins> Endpoints that limit the size of packets MAY use higher confidentiality and integrity limits; see aead-analysis for details. </ins> Future analyses and specifications MAY relax confidentiality or integrity limits for an AEAD.


A sender SHOULD restart its PTO timer every time an ack-eliciting packet is sent or acknowledged, when the handshake is confirmed (Section 4.1.2 of QUIC-TLS), or when Initial or Handshake keys are <del> discarded (Section 9 of QUIC-TLS).  This ensures the PTO is always </del>  set based on the latest estimate of the round-trip time and for the correct packet across packet number spaces. 6.4. <del> When packet protection keys are discarded (see Section 4.8 of QUIC- </del>  TLS), all packets that were sent with those keys can no longer be acknowledged because their acknowledgements cannot be processed anymore.  The sender MUST discard all recovery state associated with

So there are a couple of section references in the doc that points the wrong section in other documents. Section 6.2.1: "A sender SHOULD restart its PTO timer every time an ack-eliciting packet is sent or acknowledged, when the handshake is confirmed (Section 4.1.2 of [QUIC-TLS]), or when Initial or Handshake keys are discarded (Section 9 of [QUIC-TLS])", should that last ref be to section 4.9 in QUIC-TLS?"Section 6.4: "see Section 4.8 of [QUIC-TLS])" This is now Section 4.9.Section B.2: maxdatagramsize: The sender's current maximum payload size. Does not include UDP or IP overhead. The max datagram size is used for congestion window computations. An endpoint sets the value of this variable based on its PMTU (see Section 14.1 of [QUIC-TRANSPORT]), with a minimum value of 1200 bytes." Should that Section 14.1 reference be to 14.2.
Thank you for catching these, NAME

A sender SHOULD restart its PTO timer every time an ack-eliciting packet is sent or acknowledged, when the handshake is confirmed (Section 4.1.2 of QUIC-TLS), or when Initial or Handshake keys are  <ins> discarded (Section 4.9 of QUIC-TLS).  This ensures the PTO is always </ins> set based on the latest estimate of the round-trip time and for the correct packet across packet number spaces. 6.4.  <ins> When packet protection keys are discarded (see Section 4.9 of QUIC- </ins> TLS), all packets that were sent with those keys can no longer be acknowledged because their acknowledgements cannot be processed anymore.  The sender MUST discard all recovery state associated with


attacker. Future versions of QUIC may have different requirements for the <del> lengths of connection IDs larger than version 1.  In particular, connection IDs might not be required to have a minimum lengths, and might be longer than 20 bytes.  A server therefore MUST respond with a Version Negotiation packets for all lengths of connection IDs allowed by the layout defined here. </del>  The remainder of the Version Negotiation packet is a list of 32-bit versions that the server supports.

Could we also maybe add a pointer to the requirement that a DCID in an Initial must be eight bytes to Section 17.2? Because at the moment, it's buried in Section 7.2, where I didn't find it.
I've taken this change manually, after reverting the change to the invariants doc. After looking at this more closely, the invariants doc does not include specifics of how to generate a Version Negotiation packet and this takes an awkward step in that direction. Rather than start to import the rules into invariants piecemeal like this, I think that it is cleaner to leave the rules for generating a Version Negotiation packet as version-specific, which is already clearly the case.
Addendum: I missed the bit about this being a blanket "version-specific rules for packets". I've changed this to "version-specific rules for connection IDs", to reflect the context better. And to avoid tripping up on the packet size rules in version 1.
Since future QUIC versions might have different CID length requirements for Initial packets (or however that packet type will be called in that version), we already require servers to be able to send Version Negotiation packets with CIDs longer than 20 bytes (which is the maximum length that v1 allows). We need to clarify that this also applies to the minimum CID length, which is 8 byte for the DCID in v1. A future version of QUIC might relax this limit, so a server also needs to generate a VN packet if it receives a packet with an unknown version and a shorter DCID, otherwise it won't be possible to roll out such a QUIC version. This is coming out of a Slack discussion with NAME and NAME as well as recent tests on the interop runner. It turns out that 2 server implementations on the interop runner didn't get this right, and at least one server implementation that's not on the interop runner, but is running QUIC in production.
I think the CID specific aspect is fine. It is unclear what other aspects that are specified by the protocol and are not invariant here.

attacker. Future versions of QUIC may have different requirements for the  <ins> lengths of connection IDs.  In particular, connection IDs might have a smaller minimum length or a greater maximum length.  Version- specific rules for QUIC packets therefore MUST NOT influence a server decision about whether to send a Version Negotiation packet. </ins> The remainder of the Version Negotiation packet is a list of 32-bit versions that the server supports.


increased in either slow start or congestion avoidance.  This can happen due to insufficient application data or flow control limits. <del> A sender can use the pipeACK method described in Section 4.3 of RFC7661 to determine if the congestion window is sufficiently utilized. </del> A sender that paces packets (see pacing) might delay sending packets and not fully utilize the congestion window due to this delay.  A sender SHOULD NOT consider itself application limited if it would

by removing a paragraph mentioning pipeACK and moving RFC3168 to a normative reference.
In Recovery-31 there are several references that I judge as normative and not informative ones. Section 7.8: "A sender MAY use the pipeACK method described in Section 4.3 of [RFC7661] to determine if the congestion window is sufficiently utilized." A normative reference necessary to follow if one selects to use the optional method.Sectiom 7.3.2: "Implementations MAY reduce the congestion window immediately upon entering a recovery period or use other mechanisms, such as Proportional Rate Reduction ([PRR]), to reduce the congestion window more gradually." Another case of a suggested mechanism, which if one is to follow it requires one to read that PPR reference. Secondly, why the alternative reference style.Section 8.3: "Markings can be treated as equivalent to loss ([RFC3168]), but other responses can be specified, such as ([RFC8511]) or ([RFC8311])." So RFC3168 is the normative specification for the treat it equal to loss. RFC8511 is the TCP alternative back-off an experimental specification for the alternative. And RFC8311 is the process for performing other experiments with alternative responses. I judge all necessary depending on what one intendeds to do. So to my understanding this results in two downrefs to experimental specifications. I think that is acceptable in the context they are used in this document. But to be tried in IETF last call. However, I think I don't want to generally recommend them to be acceptable as downrefs so I have no intentions to promote them for the Downref registry.
Moving discussion from the PR to the issue. NAME : I'm not the expert on what should be normative, but these were intentionally marked as informative references since they're all TCP specific RFCs. The pipeACK reference in particular seems like an odd one. If this is what the chairs want, then I'm ok with it as an editor, but these choices were intentional. NAME : I see what NAME is saying, but I also see some wiggle room in RFC 3967, Section 1.1: When a reference to an example is made, such a reference need not be normative. For example, text such as "an algorithm such as the one specified in [RFCxxxx] would be acceptable" indicates an informative reference, since that cited algorithm is just one of several possible algorithms that could be used. I am also somewhat concerned that trying to get the downrefs into the registry is more likely to run into trouble (Sec 3) This procedure should not be used if the proper step is to move the document to which the reference is being made into the appropriate category. It is not intended as an easy way out of normal process. Rather, the procedure is intended for dealing with specific cases where putting particular documents into the required category is problematic and unlikely ever to happen.
Thanks for digging up the relevant text, NAME Based on that text from RFC 3967, it doesn't seem like we need to make these references normative. I agree that the pipeACK reference is odd, but I think that's probably because of the use of MAY there. I'm happy to replace the MAY with a can -- it is meant to present an option, not a permission -- and make that reference informative as well. NAME : thoughts?
I think at last RFC3168 should be referenced normatively somewhere (probably section 7.1 though) because you need to know what the ECN bits are in order to do anything with it. I guess because of section 7.1. RFC8311 should also be a normative reference.
NAME RFC3168 is already a normative reference in the transport doc, which in my view is appropriate.
Was wondering about this as well given the transport doc is of course a normative reference of this doc. However, as this doc uses terms like CE also directly itself I think the right practice is to also have it as a normative reference here, and as you have to read it no matter what, it definitely shouldn't hurt.
talks about ECN-CE markings, but really all of Section 7 talks about a response to an increase in ECN-CE count by the peer, which is defined in the transport doc. This does point to a small editorial issue. The text in Section 4.1 says: This really should be:
Good point on the editorial issue.
With that change, the only thing we're left with is . This does require an understanding of ECN, and it has RFC 2119 keywords, but it's really an optional mechanism that is suggested. We could remove the RFC 2119 words there, since they're really unnecessary.
My point is that RFC3168 should be normative because not matter what you need to understand what an ECN-CE mark is and that is defined/specified in RFC3168.
Mirja has a point, see URL
-transport is a normative reference. So 3168 is already (transitively) a normative reference. The question is whether this specific text requires anything from 3168 to understand. I don't see that it does, especially given the suggested change, which talks about a concept defined in -transport, not 3168.
has made these editorial changes; NAME can you see if anything else needs to be done here?
I will note that it was never my intention to let any of theses Experimental or informational TCP specs to be added into the downref registry by this last call. So, the pipeack reference needs to be reformulated to not be a normative one. As currently stated it is a clear normative reference per IESG Statement ([URL]) , and especially "Note 1: Even references that are relevant only for optional features must be classified as normative if they meet the above conditions for normative references. " You need to find a formulation that normative describes what the implementation MAY do, and then you can exemplify with the TCP version. Then one in Section 7.3.2 I can accept as still being informative as the MAY statement is related to the primary aspect of the sentence and the reference to PPR is exemplifying. Still this is very much on the border and may still generate a comment in IESG. For RFC 3168 I don't understand why not making it to a normative one. There are no issues with its status as it is proposed standard. And simply the security consideration section still implies that you need to know what a ECN-CE mark is to interpret this. So please make this one normative.
I'm ok with RFC 3168 being normative, particularly since it's already normative in transport, but would like to avoid making the others normative.
NAME if pipeack is not normative then a reformulation is necessary of that sentence in Section 7.8 to not state is as a mechanism that MAY be implemented.
The sentence currently has a can, not a MAY: "A sender can use the pipeACK method described in Section 4.3 of {{?RFC7661}} to determine if the congestion window is sufficiently utilized." But I sent out PR to make it clearer that pipeACK is an example, PTAL and see if that helps.
NAME what PR proposes removing that first sentence do resolve the pipeack to me. So merging , and moving RFC3168 to normative will resolve this issue to my understanding.
Thanks! I'll update PR to include moving RFC3168 to normative.
NAME NAME : I believe this is purely editorial, but I'd like to confirm.
We've meandered to a route that AFAICT makes reference types accurate to their existing intent, or avoids them becoming normative. I'm happy for this to be editorials.

increased in either slow start or congestion avoidance.  This can happen due to insufficient application data or flow control limits.  A sender that paces packets (see pacing) might delay sending packets and not fully utilize the congestion window due to this delay.  A sender SHOULD NOT consider itself application limited if it would


The keys used for packet protection are computed from the TLS secrets using the KDF provided by TLS.  In TLS 1.3, the HKDF-Expand-Label function described in Section 7.1 of TLS13 is used, using the hash <del> function from the negotiated cipher suite.  Other versions of TLS MUST provide a similar function in order to be used with QUIC. </del>  The current encryption level secret and the label "quic key" are input to the KDF to produce the AEAD key; the label "quic iv" is used The secret used by clients to construct Initial packets uses the PRK and the label "client in" as input to the HKDF-Expand-Label function <del> to produce a 32 byte secret; packets constructed by the server use the same process with the label "server in".  The hash function for HKDF when deriving initial secrets and keys is SHA-256 SHA. </del>  This process in pseudocode is:

This was, I think clear, but the use of HKDF-Expand-Label and the encoding of labels into octets could be even more explicit.
Section 5.2: The secret used by clients to construct Initial packets uses the PRK and the label "client in" as input to the HKDF-Expand-Label function to produce a 32 byte secret; packets constructed by the server use the same process with the label "server in". The hash function for HKDF when deriving initial secrets and keys is SHA-256 [SHA]. Looking at RFC 5689 the HKDF-Expand function defines what is referenced as label as "info" input. It is a string. It is unclear that this is an ASCII string, but based on what is written it looks like something where each character is an octet is assumed, thus no UTF-16 or UTF-32 strings would work as intended. If I am correct I think the draft can be improved by being explicit like in TLS that the label used as input into the functions as ASCII. So adding something like this sentence from RFC8446 is likely appropriate: The labels specified in this document are all ASCII strings and do not include a trailing NUL byte.
Actually I think I understand what the issue is: Lets look at these three paragraphs in Section 5.2: This secret is determined by using HKDF-Extract (see Section 2.2 of [HKDF]) with a salt of 0xafbfec289993d24c9e9786f19c6111e04390a899 and a 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. The secret used by clients to construct Initial packets uses the PRK and the label "client in" as input to the HKDF-Expand-Label function to produce a 32 byte secret; packets constructed by the server use the same process with the label "server in". The hash function for HKDF when deriving initial secrets and keys is SHA-256 [SHA]. The HKDF-Expand-Label function defined in TLS 1.3 MUST be used for Initial packets even where the TLS versions offered do not include TLS 1.3. I got the impression that the HKDF-Expand function was the one from RFC5689 and not the one from TLS1.3. So I think what is needed to clarify this is that the second paragraph needs to explicitly provide the HKDF-Expand-label function with section and document reference. I still think it should be made clear that the labels are ASCII, but that is clear in TLS 1.3 compared to RFC5689.
Based on , labeling this as "editorial"
Changes looks good, resolve the issue.
nits

The keys used for packet protection are computed from the TLS secrets using the KDF provided by TLS.  In TLS 1.3, the HKDF-Expand-Label function described in Section 7.1 of TLS13 is used, using the hash  <ins> function from the negotiated cipher suite.  Note that labels, which are described using strings, are encoded as bytes using ASCII ASCII without quotes or any trailing NUL byte.  Other versions of TLS MUST provide a similar function in order to be used with QUIC. </ins> The current encryption level secret and the label "quic key" are input to the KDF to produce the AEAD key; the label "quic iv" is used The secret used by clients to construct Initial packets uses the PRK and the label "client in" as input to the HKDF-Expand-Label function  <ins> from TLS TLS13 to produce a 32-byte secret.  Packets constructed by the server use the same process with the label "server in".  The hash function for HKDF when deriving initial secrets and keys is SHA-256 SHA. </ins> This process in pseudocode is:


Removing header protection only differs in the order in which the packet number length (pn_length) is determined.  fig-sample shows an example long header packet (Initial) and a short header packet. fig-sample shows the fields in each header that are covered by header protection and the portion of the protected packet AES is defined in AES. This algorithm samples 16 bytes from the packet ciphertext.  This <del> value is used as the input to AES-ECB.  In pseudocode: </del>  5.4.4. a sequence of 32-bit little-endian integers. The encryption mask is produced by invoking ChaCha20 to protect 5 <del> zero bytes.  In pseudocode: </del>  5.5.

This could have been more explicit, so now it is. Doubly so.
Section 5.4.1, Figure 6: mask = headerprotection(hpkey, sample) I think it is a bit unclear that the headerprotection function is what is producing the stream cipher cipher text that will be masked and xored with the header bits. Especially as the instantiations of these functions for AESGCM (Section 5.4.3) and ChaCha20 (Section 5.4.4) is not defined as instantiation of that function, only as providing the same variable output. I think this could be clarified in both places so that the linkage between the general application and the stream cipher producing function is clear.
Looks good and resolve my issue.
indubitably

Removing header protection only differs in the order in which the packet number length (pn_length) is determined. <ins> Specific header protection functions are defined based on the selected cipher suite; see hp-aes and hp-chacha. </ins> fig-sample shows an example long header packet (Initial) and a short header packet. fig-sample shows the fields in each header that are covered by header protection and the portion of the protected packet AES is defined in AES. This algorithm samples 16 bytes from the packet ciphertext.  This  <ins> value is used as the input to AES-ECB.  In pseudocode, the header protection function is defined as: </ins> 5.4.4. a sequence of 32-bit little-endian integers. The encryption mask is produced by invoking ChaCha20 to protect 5  <ins> zero bytes.  In pseudocode, the header protection function is defined as: </ins> 5.5.


2.1. TLS provides two endpoints with a way to establish a means of <del> communication over an untrusted medium (that is, the Internet) that ensures that messages they exchange cannot be observed, modified, or forged. </del>  Internally, TLS is a layered protocol, with the structure shown in tls-layers. A 0-RTT handshake, in which the client uses information it has previously learned about the server to send Application Data immediately.  This Application Data can be replayed by an attacker <del> so it MUST NOT carry a self-contained trigger for any non- idempotent action. </del>  A simplified TLS handshake with 0-RTT application data is shown in tls-full. Destination Connection ID it uses in response to an Initial packet from the server. <del> The Destination Connection ID is of arbitrary length, and it could be zero length if the server sends a Retry packet with a zero- length Source Connection ID field.  In this case, the Initial keys provide no assurance to the client that the server received its packet; the client has to rely on the exchange that included the Retry packet for that property. </del>  test-vectors contains sample Initial packets.

These are all small, but neat improvements inspired by the feedback in .
Here are some minor editorial issues: Section 2.1: "TLS provides two endpoints with a way to establish a means of communication over an untrusted medium (that is, the Internet) that ensures that messages they exchange cannot be observed, modified, or forged." I think "observed" as used here is the wrong word. Because the encrypted form of the clear-text message will be possible to observe. Please reformulate to give actual security properties. Section 2.1: "A 0-RTT handshake, in which the client uses information it has previously learned about the server to send Application Data immediately. This Application Data can be replayed by an attacker so it MUST NOT carry a self-contained trigger for any non-idempotent action." Please do not use RFC2119 words "MUST NOT" in overview. I understand the need for emphasis on this. However, I assume this is actually specified normatively elsewhere. So simply using other words to provide the emphasis and possibly a reference to the section defining this normatively. Section 5.2: "Note: The Destination Connection ID is of arbitrary length, and it could be zero length if the server sends a Retry packet with a zero-length Source Connection ID field." To my understanding the DCID is not arbitrary length, it is of any octet length between 0 and 160 bits. Does the Initial processing function need to deal with non QUIC version-1 long header packets?
Based on , labeling this as "editorial"
I think the PR resolves my issues nicely.
I liked that we dropped the MUST NOT, it's already somewhere else. I also like that we point back to quic-trans for the dcid URL

2.1. TLS provides two endpoints with a way to establish a means of  <ins> communication over an untrusted medium (that is, the Internet).  TLS enables authentication of peers and provides confidentiality and integrity protection for messages that endpoints exchange. </ins> Internally, TLS is a layered protocol, with the structure shown in tls-layers. A 0-RTT handshake, in which the client uses information it has previously learned about the server to send Application Data immediately.  This Application Data can be replayed by an attacker  <ins> so 0-RTT is not suitable for carrying instructions that might initiate any non-idempotent action. </ins> A simplified TLS handshake with 0-RTT application data is shown in tls-full. Destination Connection ID it uses in response to an Initial packet from the server.  <ins> The Destination Connection ID field could be any length up to 20 bytes, including zero length if the server sends a Retry packet with a zero-length Source Connection ID field.  After a Retry, the Initial keys provide the client no assurance that the server received its packet, so the client has to rely on the exchange that included the Retry packet to validate the server address; see Section 8.1 of QUIC-TRANSPORT. </ins> test-vectors contains sample Initial packets.


resolving the host identifier to an IP address, establishing a QUIC connection to that address on the indicated port, and sending an HTTP/3 request message targeting the URI to the server over that <del> secured connection. </del>  Connectivity problems (e.g., blocking UDP) can result in QUIC connection establishment failure; clients SHOULD attempt to use TCP- An HTTP origin advertises the availability of an equivalent HTTP/3 endpoint via the Alt-Svc HTTP response header field or the HTTP/2 <del> ALTSVC frame (ALTSVC), using the Application Layer Protocol Negotiation (ALPN; see RFC7301) token defined in connection- establishment. </del>  For example, an origin could indicate in an HTTP response that HTTP/3 was available on UDP port 50781 at the same hostname by including the

Section 3.2: "A client MAY attempt access to a resource with an "https" URI by resolving the host identifier to an IP address, establishing a QUIC connection to that address on the indicated port, and sending an HTTP/3 request message targeting the URI to the server over that secured connection." draft-ietf-quic-tls states in Section 8.1: "Unless another mechanism is used for agreeing on an application protocol, endpoints MUST use ALPN for this purpose." Looking at Section 3.2 with Section 3.1 deleted I think there is an important aspect missing which is expressed in first sentence of Section 3.1: "HTTP/3 uses the token "h3" to identify itself in ALPN and Alt-Svc." In Section 3.3: "QUIC connections are established as described in [QUIC-TRANSPORT]. During connection establishment, HTTP/3 support is indicated by selecting the ALPN token "h3" in the TLS handshake. Support for other application-layer protocols MAY be offered in the same handshake." So what I interpret is that the inclusion of the "h3" ALPN token will be required in the clients messages when establishing an QUIC connection with the intention of communicating using HTTP/3 according to this specification. So I think there are two changes needed here. First that there needs to be a first paragraph in Section 3.2 to state that HTTP/3 uses "h3" as token in ALPN and Alt-Svc as a lead in, secondly that the sentence first referenced above needs to state that ALPN = "h3" will be given in the QUIC-TLS handshake. If I understand this correctly, a QUIC connection that has been established using "h3" ALPN token, i.e. client tells it interested in this application and where the server choice that token in the handshake will be using HTTP/3 according to this specification. It is possible for other ALPN tokens to be application foo using HTTP/3. Finally do I interpret things correctly that "h3" is not strictly required but then both client and server need to know that yes, this connection establishment is for the purpose of HTTP/3?
If some other mechanism is used for agreeing on protocol, ALPN can be omitted, yes. I'm not convinced that the first paragraph of 3.[2->1] is the appropriate place to put the token statement; the section is about discovering an HTTP/3 endpoint given a URL, and the definition of the ALPN token is incidental there. I've added the token to 3.[2->1] in the paragraph about the connection; see what you think.
I think the proposed text does clarify the issue and makes the default and alternative clear. So this resolves my issue.
Based on , labeling this as "editorial"

resolving the host identifier to an IP address, establishing a QUIC connection to that address on the indicated port, and sending an HTTP/3 request message targeting the URI to the server over that  <ins> secured connection.  Unless some other mechanism is used to select HTTP/3, the token "h3" is used in the Application Layer Protocol Negotiation (ALPN; see RFC7301) extension during the TLS handshake. </ins> Connectivity problems (e.g., blocking UDP) can result in QUIC connection establishment failure; clients SHOULD attempt to use TCP- An HTTP origin advertises the availability of an equivalent HTTP/3 endpoint via the Alt-Svc HTTP response header field or the HTTP/2  <ins> ALTSVC frame (ALTSVC), using the "h3" ALPN token. </ins> For example, an origin could indicate in an HTTP response that HTTP/3 was available on UDP port 50781 at the same hostname by including the


samples, estimated using a mean variation. The calculation of smoothed_rtt uses RTT samples after adjusting them <del> for acknowledgement delays.  These delays are computed using the ACK Delay field of the ACK frame as described in Section 19.3 of QUIC- </del>  TRANSPORT. The peer might report acknowledgement delays that are larger than the cases, an endpoint SHOULD subtract such local delays from its RTT sample until the handshake is confirmed. <del> smoothed_rtt and rttvar are computed as follows, similar to RFC6298. </del>  <del> When there are no samples for a network path, and on the first RTT sample for the network path: </del>  <del> Before any RTT samples are available, the initial RTT is used as rtt_sample.  On the first RTT sample for the network path, that sample is used as rtt_sample.  This ensures that the first measurement erases the history of any persisted or default values. </del>  On subsequent RTT samples, smoothed_rtt and rttvar evolve as follows: The packet is unacknowledged, in-flight, and was sent prior to an acknowledged packet. <del> Either its packet number is kPacketThreshold smaller than an </del>  acknowledged packet (packet-threshold), or it was sent long enough in the past (time-threshold). 7. <del> This document specifies a congestion controller for QUIC similar to TCP NewReno (RFC6582). </del>  The signals QUIC provides for congestion control are generic and are <del> designed to support different algorithms.  Endpoints can unilaterally choose a different algorithm to use, such as Cubic (RFC8312). </del>  <del> If an endpoint uses a different controller than that specified in this document, the chosen controller MUST conform to the congestion </del>  control guidelines specified in Section 3.1 of RFC8085. Similar to TCP, packets containing only ACK frames do not count

Addresses .
This issue will list a number of minor editorial things that should be checked and likely addressed with some editorial fixes: Section 5.3: "These delays are computed using the ACK Delay field of the ACK frame as described in Section 19.3 of [QUIC-TRANSPORT]." This text implies that the computation of the delay is described in Section 19.3 of QUIC-TRANSPORT. However, I don't think it is described the calculation, only the definition of the field. Please reformulate to imply only field definition not calculation. Section 5.3: "On the first RTT sample for the network path, that sample is used as rttsample. This ensures that the first measurement erases the history of any persisted or default values." I think this intended to cover cases where path changes are detected. But it is not well expressed. Does this need a reformulation and a reference into QUIC-TRANSPORT where it states when such a reset should occur? Section 6.1: "Either its packet number is kPacketThreshold smaller than an acknowledged packet" Nitpicking, assuming no PN gaps in transmission. Does that need a note? Section 6.2.4: "An endpoint MAY send up to two full-sized datagrams containing ack-eliciting packets, to avoid an expensive consecutive PTO expiration due to a single lost datagram or transmit data from multiple packet number spaces." This last part, I assume intended to generate additional datagrams to prevent single loss to impact. However, due to coalescing this is unclear in this context. Section 7: "Endpoints can unilaterally choose a different algorithm to use, such as Cubic ([RFC8312])." I think this sentence should be explicit that in needs to be sender-based algorithms. I think we need to have the requirement on performing congestion control on the sender unless there has been explicit agreement with the receiver that they will do it. Section 7.2: "Endpoints SHOULD use an initial congestion window of 10 times the maximum datagram size (maxdatagramsize), limited to the larger of 14720 bytes or twice the maximum datagram size." "maximum datagram size" is not used in Transport there need to be alignment here of what name to use. Transport has Maximum Packet Size, which appears to match the definition in the appendix of maxdatagram_size.
Thanks for your careful review, NAME -- these are good finds! I've addressed your comments below and in . I'll address point 6 in a separate PR. Section 19.3 has the following text, which does tell how to compute acknowledgement delay from the ACK Delay field: I've replaced "computed" with "decoded" in the recovery document to make it clearer. Coalescing applies to packets, not to datagrams. We've explicitly stated that a sender could send 2 datagrams to avoid any effects of coalescing. I think the text is pretty clear on this: "An endpoint MAY send up to two full-sized datagrams containing ack-eliciting packets". I've added "sender-side" to make the controller more explicit. To your point about making this a sender requirement, that is covered in the transport doc (end of ):
Based on and , labeling this as "editorial"
I think these issues are resolved once the two PRs are completely ready.
In Section 5.3: "When there are no samples for a network path, and on the first RTT sample for the network path: smoothedrtt = rttsample rttvar = rttsample / 2 Before any RTT samples are available, the initial RTT is used as rttsample. On the first RTT sample for the network path, that sample is used as rttsample. This ensures that the first measurement erases the history of any persisted or default values." First of all this text uses "rttsample" while other places with the exception of the pseudo code uses latestrtt variable. There are two issues with that. First that the value of rttsample is state dependent. If there exist an latestrtt then that is used, if not the initial rtt. As the initial RTT is defined in Section 6.2.2 I think a forward reference is in place. Finally, should this calculation in cases where latestrtt exists be using minackdelay?
I like to add that there are some structural issues in Section 6.2.1 and 6.2.2 where the initial value depends on both section.
Based on , labeling this as "editorial"
The PR resolves this issue.
OK, thanks GitHub for saving and duplicating my comments...Looks good and appear to resolve the uncertainties I had.

samples, estimated using a mean variation. The calculation of smoothed_rtt uses RTT samples after adjusting them  <ins> for acknowledgement delays.  These delays are decoded from the ACK Delay field of ACK frames as described in Section 19.3 of QUIC- </ins> TRANSPORT. The peer might report acknowledgement delays that are larger than the cases, an endpoint SHOULD subtract such local delays from its RTT sample until the handshake is confirmed.  <ins> Similar to RFC6298, smoothed_rtt and rttvar are computed as follows. </ins>  <ins> An endpoint initializes the RTT estimator during connection establishment and when the estimator is reset during connection migration; see Section 9.4 of QUIC-TRANSPORT.  Before any RTT samples are available for a new path or when the estimator is reset, the estimator is initialized using the initial RTT; see pto-handshake. </ins>  <ins> smoothed_rtt and rttvar are initialized as follows, where kInitialRtt contains the initial RTT value: RTT samples for the network path are recorded in latest_rtt; see latest-rtt.  On the first RTT sample after initialization, the estimator is reset using that sample.  This ensures that the estimator retains no history of past samples. On the first RTT sample after initialization, smoothed_rtt and rttvar are set as follows: </ins> On subsequent RTT samples, smoothed_rtt and rttvar evolve as follows: The packet is unacknowledged, in-flight, and was sent prior to an acknowledged packet.  <ins> The packet was sent kPacketThreshold packets before an </ins> acknowledged packet (packet-threshold), or it was sent long enough in the past (time-threshold). 7.  <ins> This document specifies a sender-side congestion controller for QUIC similar to TCP NewReno (RFC6582). </ins> The signals QUIC provides for congestion control are generic and are  <ins> designed to support different sender-side algorithms.  A sender can unilaterally choose a different algorithm to use, such as Cubic (RFC8312). </ins>  <ins> If a sender uses a different controller than that specified in this document, the chosen controller MUST conform to the congestion </ins> control guidelines specified in Section 3.1 of RFC8085. Similar to TCP, packets containing only ACK frames do not count


by the peer that are greater than the peer's max_ack_delay are attributed to unintentional but potentially repeating delays, such as scheduler latency at the peer or loss of previous acknowledgements.  Therefore, these extra delays are considered effectively part of path delay and incorporated into the RTT estimate.

Works for me, so I consider this issue resolved.
In Section 5.3: I don't understand the motivation behind this statement. Assuming that the peer is measuring their actual ackdelay, i.e. it has a sample for arrivaltime[psnhighestacked] and then when creating the ack and calculating the ackdelay it will do time(now) - arrivaltime[psnhighestacked] and that is then larger than maxackdelay. Then this has nothing to do with the path, only with the peer implementation and its capability of waking up to report the ack when additional reception is not forcing it to ACK earlier than maxackdelay. Thus, by capping the use of ackdelay to maxack_delay then in certain transmission conditions the RTT will be inflated, and in others not. I would guess that the impact of this is in most cases minimal. However, from a factual stand point it appear to me as non true statement. Does this needs be changed?
Right, it is not a direct function of the path, but it is included in the path delay as represented by the RTT estimates. I'm not sure of a better way to say this, but NAME might have a suggestion.
NAME : Yes, that is correct, and that is exactly the intention. What is not intentional delay at the peer is considered part of the "path". The model we have is as follows. A sender wants to separate intentional delays at the peer from unintentional delays, since it can then do a better estimate for loss detection by not counting the ack delay in the EWMA. This leads to an issue where the peer can intentionally or unintentionally misreport actual ack delays, causing the estimated RTT to be incorrectly small. To avoid this, we have two checks: all RTT samples, after adjusting for ack delay, are limited to being the measured min RTT, and any reported ack delay has to be smaller than the peer's promised max ack delay. The question here is not whether the unintentional delay belongs in the path or not, but whether it ought to be removed from the path RTT, since we explicitly remove the reported ack delay from the RTT sample. Since this is unintentional (or uncontrolled) delay, we consider it part of the path, and part of the EWMA.
NAME do these explanation address your issue? Or are text changes required?
NAME my point is that an error in timers waking up the receiver to send an ACK will affect the time of the transmission of the ACK. However, in those cases where at the time it wakes up to construct the ACK it correctly reads the time and accurately represent the time between the time of packet reception and transmission of ACK in the ACK_DELAY field the RTT is correctly estimated. It is the timing of the feedback loop that is a bit in error as it was longer than max ACK delay. To attempt to answer NAME I think there is an difference in opinion if this affects the RTT estimate or not in a negative way. And thus I like to better understand if I there is any substance here or not. Likely there is not, but let continue the discussion a little bit more.
This design is very much intentional. For example, if a peer specifies a max ack delay of 10 ms but always delays ACKs 100ms, then IF one always used the peer's ackdelay without capping it at maxack_delay, it could result in repeated spurious PTOs. Re-reading the text, I do believe it says the right thing:
Ok, I can understand the intention here to say that you need to behave as promised, and to do otherwise gives a to big advantage to the misbehaving peers therefore we will include any time beyond max ACK delay in the RTT. But, that is not quite what the motivation text says. So I don't see any need for a technical change. I think it would be good to adjust the motivation so that it more correctly reflects the reasons.
SG, I added a sentence, PTAL at
Marking this "editorial" based on
Issue resolved by editorial PR.
One nit

by the peer that are greater than the peer's max_ack_delay are attributed to unintentional but potentially repeating delays, such as scheduler latency at the peer or loss of previous acknowledgements. <ins> Excess delays could also be due to a non-compliant receiver. </ins> Therefore, these extra delays are considered effectively part of path delay and incorporated into the RTT estimate.


server from repeated client probes, the PTO backoff is not reset at a client that is not yet certain that the server has finished validating the client's address.  That is, a client does not reset <del> the PTO backoff factor on receiving acknowledgements until the handshake is confirmed; see Section 4.1.2 of QUIC-TLS. </del>  This exponential reduction in the sender's rate is important because consecutive PTOs might be caused by loss of packets or

If you receive a handshake ACK, then the server has validated the client's address, so it's really only Initial ACKs.
I think this is good.
There is an inconsistency in URL w.r.t. resetting PTO backoff factor. Section 6.2.1: This suggests that for a client to be certain that the server has finished validating the client's address, the handshake should have been confirmed. However, the pseudo code uses PeerCompletedAddressValidation to determine whether to reset pto-count and PeerCompletedAddressValidation also checks for Handshake ACK received. That is not what the text in 6.2.1 says, as, according to URL, the handshake is confirmed when HANDSHAKEDONE is received or when a 1-RTT ack is received. That is a subtle difference. As kazu noted in the discussion on Slack, Sec 8.1 of QUIC says: "Once the server has successfully processed a Handshake packet from the client, it can consider the client address to have been validated.", so, the client can consider that the server validated the client address when either ACK in Handshake or HANDSHAKEDONE in 1-RTT is received. (See Figure 5 of TLS.) Hence, the pseudocode seems to be correct and that means the text in 6.2.1 should be corrected.
Thanks for filing this, I think the new text is simpler anyway.

server from repeated client probes, the PTO backoff is not reset at a client that is not yet certain that the server has finished validating the client's address.  That is, a client does not reset  <ins> the PTO backoff factor on receiving acknowledgements in Initial packets. </ins> This exponential reduction in the sender's rate is important because consecutive PTOs might be caused by loss of packets or


Prior to making requests for an origin whose scheme is not "https", the client MUST ensure the server is willing to serve that scheme. <del> If the client intends to make requests for an origin whose scheme is "http", this means that it MUST obtain a valid "http-opportunistic" response for the origin as described in RFC8164 prior to making any such requests.  Other schemes might define other mechanisms. </del>  3.3.

Text was already given the thumbs-up on the issue, so merging without waiting for review.
Section 3.2.2 states: "Prior to making requests for an origin whose scheme is not "https", the client MUST ensure the server is willing to serve that scheme. If the client intends to make requests for an origin whose scheme is "http", this means that it MUST obtain a valid http-opportunistic response for the origin as described in [RFC8164] prior to making any such requests. Other schemes might define other mechanisms." The first issue is that this text make normative use of RFC8164 to handle "http" scheme. RFC8164 is an experimental RFC. Thus we have a downref issue. I think it would be inappropriate to elevate RFC8164 status by allowing a downref here. I think one option would be to move the specification of the usage of RFC 8164 for the "http" URI scheme in HTTP/3 into its own experimental RFC. Another potential option is to simply inform about the ongoing experiment without specifying that it can be done with HTTP/3. Secondly, I am uncertain about the scope of the first sentence in that paragraph. Is there any other URI schemes other than "http" that this is applicable for? If it is which type of URI schemes is it applicable for? For clearly it is not applicable to the "rtsp" scheme that isn't using HTTP at all.
Second point first: HTTP doesn't restrict the scheme of URIs a client can request; URI schemes define what protocols can be used to access them, which might or might not include HTTP. For example, RFC7540 says: ":scheme" is not restricted to "http" and "https" schemed URIs. A proxy or gateway can translate requests for non-HTTP schemes, enabling the use of HTTP to interact with non-HTTP services. To the first point, I feel like not mentioning that RFC8164 can be done with HTTP/3 means it's irrelevant to include in this document, and the requirement to verify the peer can serve a scheme is necessary. Perhaps something like:
The proposed text looks good. I think that resolves it and you can move RFC8164 to be a informational ref. So I think this resolves my issue.
Based on this resolution, labeling this as "editorial"

Prior to making requests for an origin whose scheme is not "https", the client MUST ensure the server is willing to serve that scheme.  <ins> For origins whose scheme is "http", an experimental method to accomplish this is described in RFC8164.  Other mechanisms might be defined for various schemes in the future. </ins> 3.3.


6.4. <del> An endpoint always sends packets that are protected with the newest keys.  Keys used for packet protection can be discarded immediately after switching to newer keys. </del>  Packets with higher packet numbers MUST be protected with either the same or newer packet protection keys than packets with lower packet

In this context, "newer" was confusing as the code permits the creation of the next set of packet protection keys when switching over. As the goal of the text was to ensure that old keys not be used, using that formulation avoids any confusion that might be caused from not having a reference against which to decide which is "newer".
Section 6.4: An endpoint always sends packets that are protected with the newest keys. Keys used for packet protection can be discarded immediately after switching to newer keys. I do get confused by the first sentence reference to "newest" key. So the previous section talks about the requirement that one retain the current and next key. If one have created the next key, then that key is newest. I don't think this is intention, but the text can be interpret that one need to switch to the next key as soon as one is allowed if one have created it. The only question is when it is created. If I would guess the above section 6.4 sentence is to ensure that one never uses the previous key, if a key change has occurred. However, one should not be forced to switch to the next key unless the other side has switched or the key has exhausted its protection life time. However, I think the current formulation can result in that an outside attacker that can inject a packet with a toggled key-phase bit can force an endpoint to switch its key, even if the peer hasn't initiated a key update. This simply by forcing it to generate it, and then have the packet integrity check to fail for the injected packet. Because now there is a newer "next key" in store.
Never using old keys is a nice way of putting this. I'll make that change.
On the last point (which I glossed over initially, as it relates to a different concept), the attacker can at best force the installation of the next set of read keys and only if the receiver is willing to do so. Flipping the key phase bit has the effect of causing a receiver to select the previous or next read keys. If the receiver generates keys on demand (which is not a great idea, but some implementations do...), then it will generate those keys. That key generation should be a one-off event (we recommend saving keys in ). A bit flip cannot result in any change to any other state (other than the global anti-forgery counter, which is a recent addition). Updates only occur as a result of successfully removing packet protection and a bit flip will cause the integrity check to fail and the packet to be dropped with no other state changes. The point about "newer" is well taken though. I didn't really think that updates to read keys could affect write keys (that's not how my code works), but as this is a lock-step protocol, that seems obvious in retrospect.
NAME do these explanations plus 52c5f4f41bb1a1128af8f52398224cd2c9f83e45 address your issue? Or are further text changes required?
Mostly, I think one could use the previous sections discussion of "current" and "next" to clarify this. For example: OLD: An endpoint never sends packets that are protected with old keys. New: An endpoint never sends packets that are protected with old keys (previous key) only the current key.
I did that in , but somehow it slipped the net and I failed to create the pull request. Sorry about that. I'll make the recommended change (or something like it).
NAME request was quite clear, so you should merge this and close the issue.I think these say the same thing, but the new text is shorter, which I always like.

6.4.  <ins> An endpoint never sends packets that are protected with old keys. Only the current keys are used.  Keys used for protecting packets can be discarded immediately after switching to newer keys. </ins> Packets with higher packet numbers MUST be protected with either the same or newer packet protection keys than packets with lower packet


terms of the granularity of padding, how padding is arranged in relation to the information that is being protected, whether padding is applied in the case of packet loss, and how an implementation <del> might control padding.  Redundant padding could even be counterproductive. </del>  To mitigate attacks that rely on compression, disabling or limiting compression might be preferable to padding as a countermeasure. Use of padding can result in less protection than might seem <del> immediately obvious.  At best, padding only makes it more difficult for an attacker to infer length information by increasing the number of frames an attacker has to observe.  Incorrectly implemented padding schemes can be easily defeated.  In particular, randomized padding with a predictable distribution provides very little protection; similarly, padding payloads to a fixed size exposes information as payload sizes cross the fixed-sized boundary, which could be possible if an attacker can control plaintext. </del>  10.8.

Section 6.2.3: URL These streams have no semantics, and can be sent when application-layer padding is desired. So although a bit unclear I assume the intention here is that one can grab the next available unidirectional stream, send the stream type with a reserved value. After that that endpoint may send arbitrary data at any time while the connection lasts and close it down. I think that could be slightly more explained. What is the cause of this issue is the "Application-layer padding". So I assume that an HTTP/3 implementation could have certain API to request this padding and what behavior it has. However, can this be anything other than generally padding out the QUIC connection. I think from this level it might be hard to have any more detailed control over where the padding data goes. The QUIC scheduler will do what it is programmed to do when it has multiple streams to send and split that into different packets in an implementation dependent fashion. So I wonder what impression people get of this padding feature? It appears hard to use for privacy preserving features as that needs to more consistently ensure that an outside observer can't detect patterns. So it will basically be for keep-alive and consuming more bits, possibly to probe if the application can take the step to the next quality level. So I would think the properties of this should be slightly more explained to not give false hopes.
HTTP traffic patterns can be quite bursty with a lot of cessation. The possible privacy preserving feature comes from an endpoint that can continue to drive application traffic over some time window. Allowing an application to pump the QUIC connection, and have the transport just deal with it without any special configuration seems attractive. In other words, if the transport is not application-limited then it can always keep the congestion window full. A scheduler can always demote such streams to behind all other streams. However, a receiver that doesn't immediately drain such streams will cause app limitation, which could affect the privacy targets. We might want to document that.
Ok that is one aspect that would benefit from clarification. I think some words on the properties that application level requested usage of reserved stream has would also be good.
NAME does the PR address your issue?
This is editorial because we're just explaining a bit more how an endpoint might use what HTTP/3 provides.
NAME Yes, this I think provides sufficient context for the padding and combined with the risks of applying it badly. So resolved.

terms of the granularity of padding, how padding is arranged in relation to the information that is being protected, whether padding is applied in the case of packet loss, and how an implementation  <ins> might control padding. Reserved stream types can be used to give the appearance of sending traffic even when the connection is idle.  Because HTTP traffic often occurs in bursts, apparent traffic can be used to obscure the timing or duration of such bursts, even to the point of appearing to send a constant stream of data.  However, as such traffic is still flow controlled by the receiver, a failure to promptly drain such streams and provide additional flow control credit can limit the sender's ability to send real traffic. </ins> To mitigate attacks that rely on compression, disabling or limiting compression might be preferable to padding as a countermeasure. Use of padding can result in less protection than might seem  <ins> immediately obvious.  Redundant padding could even be counterproductive.  At best, padding only makes it more difficult for an attacker to infer length information by increasing the number of frames an attacker has to observe.  Incorrectly implemented padding schemes can be easily defeated.  In particular, randomized padding with a predictable distribution provides very little protection; similarly, padding payloads to a fixed size exposes information as payload sizes cross the fixed-sized boundary, which could be possible if an attacker can control plaintext. </ins> 10.8.


has already ended, the server MAY still abruptly terminate the stream or MAY take no action. <del> When a server sends CANCEL_PUSH, it is indicating that it will not be fulfilling a promise.  The client cannot expect the corresponding promise to be fulfilled, unless it has already received and processed the promised response.  A server SHOULD send a CANCEL_PUSH frame even if it has opened the corresponding stream. </del>  Sending a CANCEL_PUSH frame has no direct effect on the state of existing push streams.  A client SHOULD NOT send a CANCEL_PUSH frame

In Section 7.2.3: URL A server SHOULD send a CANCELPUSH frame even if it has opened the corresponding stream. Is the intention that this sentence should have this meaning? "A server that has opened a stream for a PUSH and is unable to fulfill this promise SHOULD send a CANCEL_PUSH frame with the corresponding Push ID." I think as currently formulated it is missing the criteria as well when this may occur. Secondly the alternative is also not clear. I assume this is a SHOULD as the stream can be reset with an error code? Does that need to be added and which error will be used? Especially as the next paragraph only discusses that the client SHOULD abort reading. Can't the server terminate the stream from its side? If only the later is allowed, I think that should be made explicit but appears unnecessary.
To the second question, stating clearly that the server can abort the Push stream with H3REQUESTCANCELLED seems like a good improvement.
Looking at the larger paragraph, the point is that there are two possible states when a server decides not to fulfill a push: a. No stream has been created; CANCELPUSH is sufficient. b. A stream has been created. The server could simply reset the stream; this is valid, but risks the client processing the RESET before learning the Push ID, so it won't know which Push ID is no longer being fulfilled. Better if the server both resets the stream and sends a CANCELPUSH. You're right that this could be reworded to emphasize that you always send CANCEL_PUSH if you're not fulfilling a promise, and also close the stream if you've opened one.
This seems like the right framing. But everything seems to correspond.

has already ended, the server MAY still abruptly terminate the stream or MAY take no action.  <ins> A server sends CANCEL_PUSH to indicate that it will not be fulfilling a promise which was previously sent.  The client cannot expect the corresponding promise to be fulfilled, unless it has already received and processed the promised response.  Regardless of whether a push stream has been opened, a server SHOULD send a CANCEL_PUSH frame when it determines that promise will not be fulfilled.  If a stream has already been opened, the server can abort sending on the stream with an error code of H3_REQUEST_CANCELLED. </ins> Sending a CANCEL_PUSH frame has no direct effect on the state of existing push streams.  A client SHOULD NOT send a CANCEL_PUSH frame


connection error of type H3_FRAME_UNEXPECTED (errors).  In particular, a DATA frame before any HEADERS frame, or a HEADERS or DATA frame after the trailing HEADERS frame is considered invalid.  A server MAY send one or more PUSH_PROMISE frames (frame-push- promise) before, after, or interleaved with the frames of a response Frame types of the format "0x1f * N + 0x21" for non-negative integer values of N are reserved to exercise the requirement that unknown types be ignored (extensions).  These frames have no semantics, and <del> can be sent on any open stream when application-layer padding is desired.  They MAY also be sent on open streams in connections where no data is currently being transferred.  Endpoints MUST NOT consider these frames to have any meaning upon receipt. </del>  The payload and length of the frames are selected in any manner the implementation chooses.

NAME does this address your concerns?
Section 7.2.8: URL These frames have no semantics, and can be sent on any open stream when application-layer padding is desired. So I am uncertain if it is acceptable to shoehorn in a reserved frame after a frame that is supposed to be the last and then result in a close of the frame? Based on what is written I would lean to the interpretation that it is allowed, as the frame sender has decided to not close the stream yet, and thus it is open. If this is correct interpretation than it is up to the editor if they like to clarify more. Else something needs to be done.
The best example here is sending a reserved frame after a Request stream is opened by the client. Perhaps it is useful to call that out in section 4.1. is a bit loose. You can't send frames on qpack streams for instance, or as the first bytes on a push stream. Maybe we can rephrase it as, "Can be sent on any stream where frames are permitted to be sent"
There are no frames that close the stream, because stream closure is handled by the transport. SETTINGS is special in that it is required to be first, and is covered by : It would hypothetically apply to a future frame type which is required to come at the end of the stream as well, but we have no such frames at this time. ( does impose specific ordering on DATA and HEADERS frames relative to each other, but says nothing about other frame types.) NAME point is well-taken, and I'll take that text.
NAME does the PR address your issue?
Yes, I think this PR clarifies things a bit and are sufficient to resolve my issue.
Labeling this as "editorial" based on

connection error of type H3_FRAME_UNEXPECTED (errors).  In particular, a DATA frame before any HEADERS frame, or a HEADERS or DATA frame after the trailing HEADERS frame is considered invalid. <ins> Other frame types, especially unknown frame types, might be permitted subject to their own rules; see extensions. </ins> A server MAY send one or more PUSH_PROMISE frames (frame-push- promise) before, after, or interleaved with the frames of a response Frame types of the format "0x1f * N + 0x21" for non-negative integer values of N are reserved to exercise the requirement that unknown types be ignored (extensions).  These frames have no semantics, and  <ins> MAY be sent on any stream where frames are allowed to be sent.  This enables their use for application-layer padding.  Endpoints MUST NOT consider these frames to have any meaning upon receipt. </ins> The payload and length of the frames are selected in any manner the implementation chooses.


Section 22.1.1 of QUIC-TRANSPORT. The initial allocations in these registries created in this document <del> are all assigned permanent status and list as contact both the IESG (iesg@ietf.org) and the HTTP working group (ietf-http-wg@w3.org). </del>  11.2.1.

NAME see .
Section 11.2: URL "The initial allocations in these registries created in this document are all assigned permanent status and list as contact both the IESG (EMAIL) and the HTTP working group (EMAIL)." From my perspective it is better to have IETF as change controller. In practice that would mean that any chartered WG to work on this registry would by completing the IETF process be allowed to introduce changes to the registry. Having HTTP Working Group as contact is fine. So I would propose to change this to: "The initial allocations in these registries created in this document are all assigned permanent status and list as change controller the IETF and the HTTP working group (EMAIL) as contact."

Section 22.1.1 of QUIC-TRANSPORT. The initial allocations in these registries created in this document  <ins> are all assigned permanent status and list as contact both the IETF and the HTTP working group (ietf-http-wg@w3.org). </ins> 11.2.1.


A server MAY send one or more PUSH_PROMISE frames (frame-push- promise) before, after, or interleaved with the frames of a response message.  These PUSH_PROMISE frames are not part of the response; see <del> server-push for more details.  These frames are not permitted in pushed responses; a pushed response that includes PUSH_PROMISE frames </del>  MUST be treated as a connection error of type H3_FRAME_UNEXPECTED. Frames of unknown types (extensions), including reserved frames "Hypertext Transfer Protocol (HTTP) Field Name Registry" maintained at <https://www.iana.org/assignments/http-fields/>.  As in previous versions of HTTP, field names are strings containing a subset of ASCII characters that are compared in a case-insensitive fashion.  Properties of HTTP field names and values are discussed in The following settings are defined in HTTP/3: <del> The default value is unlimited.  See header-formatting for usage. </del>  Setting identifiers of the format "0x1f * N + 0x21" for non-negative integer values of N are reserved to exercise the requirement that

These are some minor editorial things that should be considered to be addressed: Section 4.1: "A server MAY send one or more PUSHPROMISE frames (Section 7.2.5) before, after, or interleaved with the frames of a response message. These PUSHPROMISE frames are not part of the response; see Section 4.4 for more details. These frames are not permitted in pushed responses; a pushed response that includes PUSHPROMISE frames MUST be treated as a connection error of type H3FRAMEUNEXPECTED." Especially the third sentence was hard to parse. Took me a while to realize that it tries to express that PUSHPROMISE frames are not allowed in pushed responses. I think it is primarily a language thing. However, isn't this dependent on stream type. So PUSHPROMISE are allowed on the client-initiated bi-directional stream but not on the server-initiated unidirectional stream. Section 4.1.1: "For a listing of registered HTTP fields, see the "Hypertext Transfer Protocol (HTTP) Field Name Registry" maintained at URL" To avoid more confusion like the one I had in IETF last call. Can you add an Editors note (to be removed before RFC publication) that this registry does not yet exist, and is dependent on approval of [SEMANTICS]? Section 7.2.4.1: "SETTINGSMAXFIELDSECTION_SIZE (0x6): The default value is unlimited. See Section 4.1.1 for usage." As the actual discussion of usage of this setting is in Section 4.1.1.3 wouldn't that be better to reference?
I'll take a look at cleaning it up, but I'm hesitant to simply say "can't be used on unidirectional streams" because an extension might create a unidirectional stream that does allow PUSH_PROMISE frames for whatever reason. Maybe specifically stating that they can't be sent on Push Streams would work? Good suggestion. Good suggestion; probably we moved this text to subsections after the reference was written.
My personal view has been that PUSHPROMISE is part of a request message. And so it has to abide by the rules there. Any attempt to change that requires a semantic extension to H3. Which is what URL precisely does in order to allow PUSHPROMISE on a new stream type.

A server MAY send one or more PUSH_PROMISE frames (frame-push- promise) before, after, or interleaved with the frames of a response message.  These PUSH_PROMISE frames are not part of the response; see  <ins> server-push for more details.  PUSH_PROMISE frames are not permitted on push streams; a pushed response that includes PUSH_PROMISE frames </ins> MUST be treated as a connection error of type H3_FRAME_UNEXPECTED. Frames of unknown types (extensions), including reserved frames "Hypertext Transfer Protocol (HTTP) Field Name Registry" maintained at <https://www.iana.org/assignments/http-fields/>. <ins> *Note:* This registry will not exist until SEMANTICS is approved. *RFC Editor*, please remove this note prior to publication. </ins> As in previous versions of HTTP, field names are strings containing a subset of ASCII characters that are compared in a case-insensitive fashion.  Properties of HTTP field names and values are discussed in The following settings are defined in HTTP/3:  <ins> The default value is unlimited.  See header-size-constraints for usage. </ins> Setting identifiers of the format "0x1f * N + 0x21" for non-negative integer values of N are reserved to exercise the requirement that


The date of last update to the registration.  Contact details for the registrant. Supplementary notes about the registration. permanent. All registrations in this document are assigned a permanent status <del> and list as contact the IETF (quic@ietf.org). </del>  22.2. Section 22.1.1 of QUIC-TRANSPORT. The initial allocations in these registries created in this document <del> are all assigned permanent status and list as contact both the IETF and the HTTP working group (ietf-http-wg@w3.org). </del>  11.2.1.

At NAME suggestion this adds a "change controller" field to registries and splits between a change controller (IETF) and contact (QUIC WG). Not really sure if this is worth a design label...
Section 22.1.1: It is quite common that one may want to separate who has the change control over an registration entry vs who is the contact. Change control can often be assigned to an organization rather than an individual while a contact information may be an individual or a specific contact address for the organization. Contact information has a tendency to get stale, and in many case the contact if a person may no longer represent the organization that made the registration. Thus, my suggestion would be to add a separation between contact and change controller. In fact just for the registration in the Transport doc it makes sense to make the change controller "IETF" and have the contact be "QUIC WG".
This also applies to permanent registrations in Section 22.1.4
NAME does the PR address your issue?
Change looks good and implements what I proposed.
Labeling as "editorial" based on

The date of last update to the registration. <ins> The entity that is responsible for the definition of the registration. </ins> Contact details for the registrant. Supplementary notes about the registration. permanent. All registrations in this document are assigned a permanent status  <ins> and list a change controller of the IETF and a contact of the QUIC working group (quic@ietf.org). </ins> 22.2. Section 22.1.1 of QUIC-TRANSPORT. The initial allocations in these registries created in this document  <ins> are all assigned permanent status and list a change controller of the IETF and a contact of the HTTP working group (ietf-http-wg@w3.org). </ins> 11.2.1.