frame so that it can associate the peer's response with the corresponding PATH_CHALLENGE.  8.2.2. On receiving a PATH_CHALLENGE frame, an endpoint MUST respond by echoing the data contained in the PATH_CHALLENGE frame in a <del> PATH_RESPONSE frame.  A PATH_RESPONSE frame does not need to be sent on the network path where the PATH_CHALLENGE was received; a PATH_RESPONSE can be sent on any network path.  An endpoint MUST NOT delay transmission of a packet containing a PATH_RESPONSE frame unless constrained by congestion control. </del>  An endpoint MUST NOT send more than one PATH_RESPONSE frame in response to one PATH_CHALLENGE frame; see retransmission-of- Path validation succeeds when a PATH_RESPONSE frame is received that contains the data that was sent in a previous PATH_CHALLENGE frame. <del> This validates the path on which the PATH_CHALLENGE was sent. </del>  Receipt of an acknowledgment for a packet containing a PATH_CHALLENGE frame is not adequate validation, since the acknowledgment can be

This enacts the proposal in on . Padding PATHRESPONSE and sending on the same path as PATHCHALLENGE helps ensure that the path MTU in both directions of the path is sufficient for the protocol to function. This is a nasty change; I'm personally quite unhappy about the prospect of implementing it, but it seems like the best option we have.
If we're going to go with a SHOULD on using the same path, then it makes sense to use a SHOULD on the padding piece as well.
The proposed PR bakes the assumption that paths should be symmetric. This might be an OK restriction for V1, but I hate seeing that baked in the code. There are several scenarios in which data can be send one way on a broadcast channel (e.g., satellite or DVB), while the other way is through a slower but symmetric channel. Supporting asymmetry requires testing each direction. The server, instead of changing the address at which it sends merely because it received data from the client on that new path, should perform its own Challenge/Response to validate the path. Since challenges have to be padded, this tests both continuity and MTU. The server learns that a path may exist by receiving packets on it; it only uses that path for sending if the path is validated. I guess this is something we will need to revisit as part of support for multipath.
NAME : It does not assume path symmetry, it tests for our MTU requirement for QUIC of 1200 bytes (URL).
NAME I agree with your concerns, which is why I asked for SHOULDs instead of MUST, but there may be other ways forward as well. That being said, the current proposal seems quite reliable, it's just not very flexible.
NAME By symmetry, I mean the assumption that the pair of source and destination addresses and ports used by the server is exactly the symmetric of the pair used by the client. This is not a completely natural assumption in QUIC, because peers are able to process packets received from any address and port, as long as the crypto works out.
NAME -- ah, ok, I follow. Yes, this is definitely limited to the scope of problem that we have right now. As you say, we might need to revisit this if we are to add use cases that we haven't yet considered.
NAME NAME Merging this, since the plan is to do consensus as part of LC.
In , we added the following sentence, but I think it does not make sense. Generally speaking, a receiver of a datagram cannot verify if the sender sent a full-sized datagram. That is because a datagram as a whole is not authenticated. Instead, we authenticate each of the QUIC packets that constitute the datagram. Therefore, if the sender sends a datagram consisting of multiple QUIC packets, it is (almost always) possible for a MOTS to take one of the QUIC packets, coalesce that with a few octets, and forward that datagram. That forwarded datagram would be smaller than the original datagram. Note also that a sender can send coalesced packet after the handshake too; see .
As a step forward, I think we should do the following: Regarding the sentence we added in , we should either a) change the rule to "MAY discard UDP datagrams less than 1200 bytes that carry ACK-eliciting Initial apckets", or b) delete the sentence, and Considering the fact that there could be other occasions in which we might want to require padding, either within the base drafts, or in some of the extensions, add a note to or somewhere that datagram size is not authenticated and cannot be enforced by the peer.
NAME I hate to make more changes, but I think your point is a good one. If a receiver does the enforcement that we added in -- of enforcing the datagram size -- that makes us vulnerable to this MOTS attack. The newly added text in hasn't shipped yet in a draft, so if we can get agreement, I'd like to take this in before we ship the next draft.
A timely reminder that making changes always results in making more changes and no matter how careful or small, there are almost always problems. This seems like a good proposed tweak, but this has to stop.
Let us discuss what purpose that "MAY discard" rule has and compare it to the issue. What is important for an endpoint is to ensure that its path to the peer is possible to forward packets and supporting at least minimal MTU to make QUIC work. I think that requirement lies on each endpoint to ensure that. Does the client need to enforce the server behavior for that verification? Is that a risk in itself that is greater, equal or less than the MOTS risk the enforcement has?
NAME NAME I totally agree that this has to stop. I'm sorry to have missed this point when working on ; I realized the problem when I was wondering about the MUST vs. SHOULD discussion in . That said, I do think that it would be a good idea to adopt the proposal in this issue. Let me go through the facts first. They are: An encrypted transport has to discard unauthenticated signals to prevent MOTS attackers from disrupting the connection. The size of the datagram is an unauthenticated signal throughout the lifetime of the connection. As noted above, this is the case for 1-RTT packets after handshake confirmation too. Due to these facts, "MUST NOT close, can discard" is a principle that we've always had. The reason we hadn't written it down is because we used to have only one requirement regarding datagram length, that correctly follow the principle, stating that "servers MUST discard non-full-sized datagrams containing Initial packets." But now that we have emerging consensus that datagrams carrying PATHCHALLENGE / PATHRESPONSE MUST be padded to full size (), we need to write down that the receiver cannot enforce that. Realizing that, I started to wonder if the "MAY close" rule that we added in has been correct. As NAME points out, is a change that has not shipped yet. It's a recent change due to an issue discovered during this IETF last call. I do not think we have official consensus yet. Therefore, I tend to think there would be less confusion if the next draft would contain: servers MUST pad datagrams carrying ack-eleciting Initial packets (the basis of ) endpoints MUST pad datagrams carrying PATHCHALLENGE / PATHRESPONSE frames () endpoints MUST NOT close a connection when receiving datagrams with size requirements () rather than having two different of rules for handling small-sized datagrams, of which one does not follow the principle.
Given we explicitly allow coalesced packets for PathMTU, I agree that closing the connection seems like the wrong thing to do. The PR looks sensible.
Agree with Ian. My plea for stopping was not about the fix (the fix here is good), but the original change. We would not have had this problem if we were able to accept that the protocol wasn't absolutely perfect.
NAME has described a way forward on the mailing list for this issue - are we good with that? If so, let's merge, close and do the consensus establishment during IETF LC.
This issue is somewhat similar to the issue with asymmetric path MTUs that NAME discovered in . Consider the following situation: Client and server are communication on one path. The client now probes a new path by sending a PATHCHALLENGE frame on the new path. The server responds with the PATHRESPONSE frame, but sends it on the old path. The client now considers the new path validated, and switches over to that path. If the PMTU in the direction from server to client is now smaller than 1200 bytes, it will never receive a packet from the server and the connection will time out. Even worse, if only small packets (for example ACK-only packets) make it through, this might delay the idle timeout indefinitely. While we could require the client to pad the packet containing the PATH_CHALLENGE to 1200 bytes, this still doesn't help us making sure that the return path also actually supports this packet size.
This is another interesting issue. In a nutshell, post-handshake path validation should confirm that PMTU is no less than 1200 bytes in both directions. But currently we do not require (or recommend) that the probe packets should be padded. Moreover, having such a requirement does not address the problem, because an endpoint is not required to send PATHRESPONSE on the path on which it received a PATHCHALLENGE.
So this issue is similar to with the added tweak that in migration each peer's verification is independent of each other. So the client will only know that it send path is working as currently specified. Only after the server have performed its PATH_CHALLENGE to the client for the new path will it know that it is actually working. Looking at how path validation is defined currently, with the addition of requiring padding to 1200 bytes the result for this case is that the new path will not validate for the server as none of the server's path challenges will arrive at the client and be echoed. Thus the server will declare the path as failed after max(3PTO, 6kInitialRtt). So the client will be able to use the new forward path but unless the old server->client path works the QUIC connection will fail. So I think with a PADDING requirement the slow or indefinite death of the connection will not occur. Looking at how connection migration is defined, the client will verify the path and attempt to migrate the connection by sending non-probe traffic. If the server do not send any non-probe traffic on the new interface until it have done path verification with 1200 bytes the connection migration will fail and the client will never see the server switch to using the new client source address for its communication. Thus, I think the only thing to make this fail consistently is to require padding of the path probes. Possibly one need to clarify that if one sends ACKs outside of path probes that are padded then one can end up in this state if the return path is not MTU capable.
I don't believe this is a PathMTU specific issue. The path could work perfectly in one direction and not in the opposite direction if the PATH_RESPONSE is sent on the old path. I thought this was a known issue for those implementing connection migration? NAME
I never understood why a PATH_RESPONSE was OK to send on the old path. Given that the client clearly wants the server to start using the new path (= a different IP address and port) it seems like the server has a really strong motivation to check that the client can actually receive stuff it sends there.
NAME agree there are only one reason. But the MTU can be verified to avoid failure here. NAME I think sending it back on the return path would avoid some failure cases. However, the peer endpoint still needs to do its own path verification. So do you think one should pad the PATH_RESPONSE carrying QUIC packet to verify the MTU on the return path for the endpoint requesting the path validation?
Why would the peer need to do another path validation? It is still sending to the same endpoint.
I agree that it seems all PATHCHALLENGE packets should be padded. I also cannot remember why it was allowed to respond with a PATHRESPONSE on a different path than the one challenged. I assume because path challenge/response is just treated as a unidirectional validation mechanism. One problem with padding PATH_CHALLENGE packets is in the case of rebinding. If the first/only packet you receive after rebinding is small enough, amplification protection would prevent you from sending a fully padded challenge packet back in response.
NAME because otherwise a malicious client can fake it. So if the client sends the server a PATHCHALLENGE to probe the path, it can spoof the source address, thus the server can't rely on it. So therefore the Server needs to send its PATHCHALLENGE and get a matching response before it has validated that path for transmission. So that requirement will not go away.
The migrating endpoint doesn't send a PATH_CHALLENGE, it just sends data (or whatever) on the new path. The other end sends the challenge, and the migrating endpoints sends the response. (In the case of a NAT, the migrating endpoint may not even know it is migrating, because a NAT rebinding might cause the path validation.)
NAME that is not the only way. Your way will occur when one have a NAT rebinding for example, but a client can test a path prior to migrating to it also. So apparently we are considering two different cases. And in your case the client to server path will not be probed by the client. Thus it become a question if the client needs to probe with a 1200+ packet or can rely on the ACKing of the servers PATH_RESPONSE padded to 1200+ for MTU verification?
The test-before-migrate is a MAY (Section 9.1) and Section 9.2 is pretty clear that a migration is initiated by sending non-probing frames and that a migrating endpoint can do that without first needing to validate the path.
I will note that the client is expected to a path validation with the server when it acts on the server preferred address: Section 9.6.1: URL A client might need to perform a connection migration before it has migrated to the server's preferred address. In this case, the client SHOULD perform path validation to both the original and preferred server address from the client's new address concurrently. So that case exist also here. The point is that we actually have two cases and both needs to be considered when discussing this.
Yes, that migration to the preferred address trips me up, because it reverses the path validation. I don't actually know if anyone has implemented this at all yet.
I think that this is remedied in the same way as we have elsewhere: we can recommend that endpoints pad probing packets to at least 1200 bytes. If any packet containing PATH_CHALLENGE is never less than 1200 bytes, the path will never be valid without the MTU also being sufficient. Rebinding will allow moving to path that is not validated, but I expect that not to be a significant risk. It seems equally likely that the path MTU would spontaneously reduce. And then there is the period where an unvalidated path is used, separate from the rebinding case. Nothing much to be done for that, but there is a firm limit on that, because a path is abandoned when validation fails.
NAME I'm not sure if I agree. I would write down the steps that I think would lead to a problem even when both endpoints pad their probe packets. Would you mind pointing out where I'm wrong? step 1: client sends a full-sized PATHCHALLENGE on a new path step 2: server receives that packet, sends a full-sized PATHRESPONSE on the old path step 3: client receives PATHRESPONSE, and calls the path as being validated step 4: (for some reason; e.g. client sending non-probe packets) server tries to validate the new path, by sending a full-sized PATHCHALLENGE on the new path step 5: client never receives the PATH_CHALLENGE because PMTU in the server -> client direction was sub-1200. step 6: server fails to validate the path
NAME were just discussing the same failure mode. Note also that the PATH_RESPONSE can be on the old path as well, so recommending padding for that won't work. I think that the padding idea is helpful, but not completely good. I'll explain more in a longer posting.
The reason that padding doesn't work is that the PATHRESPONSE can be small or follow a different path. At which point the client thinks that the path is good. If the client migrates, the server will attempt to validate that path. If the server pads to 1200, then its validation will fail. I think that the server switches back to the old path at this point, which might have no effect. The client might have closed the socket by then. At this point, the client continues in its use of the new, broken path. The server either keeps attempting to validate that new path successfully (at an interval determined by how often it retries path validation), or just gives up. If the server keeps going, you get into a weird state where the connection can stutter along. Either way, the connection is no longer useful. This is slightly faster than the case where PATHCHALLENGE is not padded in any way. There, you might end up with an path that appears to be good, until you need to send more data. That might take some time. At least this way you path validation failure within ~3PTO. Padding is also an improvement in that it detects paths that have an MTU that is uniformly less than 1200. Without revisiting the underlying design, I think that we just have to accept this limitation. And I guess we should acknowledge it - I'll add some text on that point.
I agree. I can't think of a way around the limitations here without redesigning the core design. I would suggest moving forward with padding PATH_CHALLENGEs and articulate the potential risks here. Connection migration remains a gift that keeps on giving, and I would expect it to give more as we experiment with it in the field. Let's not lose ourselves in trying to perfect the design of a feature that we are going to learn a lot more about as we deploy QUICv1.
+1 to not doing a redesign in v1. That said, I tend to think that it might be possible to fix this issue with small tweaks. Specifically, I wonder if stating following would address the problem: a sender MUST pad PATHCHALLENGE when receiving PATHCHALLENGE, an endpoint MUST send a padded PATHRESPONSE using the path on which PATHCHALLENGE was received receiving PATHRESPONSE on any path is valid (this is existing requirement) The reason we have the last bullet point is to prevent issues caused by MOTS attackers racing packets. We have to keep them. However, that necessary does not mean that the sender of PATHRESPONSE should be entitled to send PATHRESPONSE on any path. If we tighten the requirement on the sender of PATHRESPONSE, I think that the issue might go away, without causing any regression?
NAME I was literally writing exactly this out, modulo that last point, which I think is a very good addition. This is a small enough tweak even to the text that I think we can pull this off with a minor change. Specifically, the text that will need to change (in addition to requiring padding PATHRESPONSE) is: “A PATHRESPONSE frame does not need to be sent on the network path where the PATHCHALLENGE was received; a PATHRESPONSE can be sent on any network path.” We changed that when we changed validation to be unidirectional. We can retain that property of path validation, but still require PATHRESPONSE to be sent to the same address from which the PATHCHALLENGE was received.
I think NAME suggestion is sensible and simple enough. I will note that that second MUST is unenforceable given the third point, but I think that's ok.
Labeling this as "design" per
Simply note that I think the proposal in appear to be an okay resolution. I would recommend that people consider also so they are aligned.
LGTM, with a note regarding MUST vs. SHOULD for padding. As I pointed out in , datagram size is not authenticated, and therefore receivers can never enforce the use of padding (though they MAY discard packets that do not meet the padding requirement). If we go with a MUST, we should be clear about that.I think this is an improvement and is clear. So I am fine with going forward with this change.

frame so that it can associate the peer's response with the corresponding PATH_CHALLENGE. <ins> An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame to at least the smallest allowed maximum datagram size of 1200 bytes. Sending UDP datagrams of this size ensures that the network path from the endpoint to the peer can be used for QUIC; see datagram-size. </ins> 8.2.2. On receiving a PATH_CHALLENGE frame, an endpoint MUST respond by echoing the data contained in the PATH_CHALLENGE frame in a  <ins> PATH_RESPONSE frame.  An endpoint MUST NOT delay transmission of a packet containing a PATH_RESPONSE frame unless constrained by congestion control. A PATH_RESPONSE frame MUST be sent on the network path where the PATH_CHALLENGE was received.  This ensures that path validation by a peer only succeeds if the path is functional in both directions. This requirement MUST NOT be enforced by the endpoint that initiates path validation as that would enable an attack on migration; see off- path-forward. An endpoint MUST expand datagrams that contain a PATH_RESPONSE frame to at least the smallest allowed maximum datagram size of 1200 bytes. This verifies that the path is able to carry datagrams of this size in both directions. </ins> An endpoint MUST NOT send more than one PATH_RESPONSE frame in response to one PATH_CHALLENGE frame; see retransmission-of- Path validation succeeds when a PATH_RESPONSE frame is received that contains the data that was sent in a previous PATH_CHALLENGE frame.  <ins> A PATH_RESPONSE frame received on any network path validates the path on which the PATH_CHALLENGE was sent. </ins> Receipt of an acknowledgment for a packet containing a PATH_CHALLENGE frame is not adequate validation, since the acknowledgment can be


fields; this document uses "fields" for generality. A name-value pair sent as part of an HTTP field section.  See <del> Section 5 of SEMANTICS. </del>  Data associated with a field name, composed from all field line values with that field name in that section, concatenated together

Section 1.1: HTTP field line: A name-value pair sent as part of an HTTP field section. See Section 5 of [SEMANTICS]. I would guess that this can be refined to point to section 5.4.
Proposed change is "editorial".

fields; this document uses "fields" for generality. A name-value pair sent as part of an HTTP field section.  See  <ins> Section 5.4 and Section 5.6 of SEMANTICS. </ins> Data associated with a field name, composed from all field line values with that field name in that section, concatenated together


Pseudo-header fields are not HTTP fields.  Endpoints MUST NOT generate pseudo-header fields other than those defined in this <del> document, except as negotiated via an extension; see extensions. </del>  Pseudo-header fields are only valid in the context in which they are defined.  Pseudo-header fields defined for requests MUST NOT appear

Taken from NAME suggestion on the list.
On the mailing list NAME raised a question about pseudo-headers. That discussion seemed to identify that HTTP/3 oversteps the mark by saying that psuedo-headers are extensible. We can make an editorial change to bring this back to something equivalent to what RFC 7540 says. See .
Conversation evolved, I''ve created a PR that takes Mike's suggestion.

Pseudo-header fields are not HTTP fields.  Endpoints MUST NOT generate pseudo-header fields other than those defined in this  <ins> document; however, an extension could negotiate a modification of this restriction; see extensions. </ins> Pseudo-header fields are only valid in the context in which they are defined.  Pseudo-header fields defined for requests MUST NOT appear


(indicating whether the string is Huffman-coded), followed by the Length encoded as a 7-bit prefix integer, and finally Length bytes of data.  When Huffman encoding is enabled, the Huffman table from <del> Appendix B of RFC7541 is used without modification. </del>  This document expands the definition of string literals by permitting them to begin other than on a byte boundary.  An "N-bit prefix string <del> literal" begins with the same Huffman flag, followed by the length encoded as an (N-1)-bit prefix integer.  The prefix size, N, can have a value between 2 and 8 inclusive.  The remainder of the string literal is unmodified. </del>  A string literal without a prefix length noted is an 8-bit prefix string literal and follows the definitions in RFC7541 without

Section 4.1.2: Although this is summary of the definition in HPACK, where this issue is clear I think this document should describe things corretly. HPACK defines string literals to begin on a byte boundary. They begin with a single bit flag, denoted as 'H' in this document (indicating whether the string is Huffman-coded), followed by the Length encoded as a 7-bit prefix integer, and finally Length bytes of data. When Huffman encoding is enabled, the Huffman table from Appendix B of [RFC7541] is used without modification. The length field as described here, provides an uncertainty if it is the data pre huffman (if applied) encoding or post encoding. HPACK is clear that it is the later. Can you please clarify that the length if the length of the encoded string that is encoded using N-bit prefix integer encoding. "An "N-bit prefix string literal" begins with the same Huffman flag, followed by the length encoded as an (N-1)-bit prefix integer." This definition if it is the byte or the prefix field that begins with the huffman flag. I would guess that what is meant is that a 6-bit prefix string literal would have a start byte of "??Hxxxxx" rather than "H??xxxxx".
This would be an "editorial" change.
Yes, the latter is correct. A prefixed string literal does not begin on a byte boundary; the first bit (when it begins) is the Huffman bit, and the remainder is a prefixed integer.

(indicating whether the string is Huffman-coded), followed by the Length encoded as a 7-bit prefix integer, and finally Length bytes of data.  When Huffman encoding is enabled, the Huffman table from  <ins> Appendix B of RFC7541 is used without modification and Length indicates the size of the string after encoding. </ins> This document expands the definition of string literals by permitting them to begin other than on a byte boundary.  An "N-bit prefix string  <ins> literal" begins mid-byte, with the first (8-N) bits allocated to a previous field.  The string uses one bit for the Huffman flag, followed by the Length encoded as an (N-1)-bit prefix integer.  The prefix size, N, can have a value between 2 and 8 inclusive.  The remainder of the string literal is unmodified. </ins> A string literal without a prefix length noted is an 8-bit prefix string literal and follows the definitions in RFC7541 without


of the error code.  An expert reviewer is advised to examine new registrations for possible duplication with existing error codes. Use of existing registrations is to be encouraged, but not mandated.  In addition to common fields as described in iana-policy, this registry includes two additional fields.  Permanent registrations in A brief description of the error code semantics. The entries in iana-error-table are registered by this document.  Additionally, each code of the format "0x1f * N + 0x21" for non- negative integer values of N (that is, 0x21, 0x40, ..., through

This provides guidance to experts about avoiding overlap of error codes with HTTP/2 and explains why the codes in the document are where they are.
Section 11.2.3: URL A question as this registry has defined error codes starting at 0x0100 which is outside of the Standards Action / IESG approval range, there are no concerns that it will be specification required to add new code points directly adjecent to these being registered? Secondly, based on what being done in other registries should the be reserved in this registry?
Thanks. I think this arises from the error code reshuffling. I'm not so worried about adjacency so long as new error HTTP/3 codes don't conflict with HTTP/2 error codes (which was the point of shifting the range). So resevering the HTTP/2 codes seems sensible to me.
Yes, we should reserve the HTTP/2 error codes. However, the original intent was that the error codes assigned by the document be within the Standards Action space. Perhaps it would be cleaner to say: 0x00 to 0x3f: Reserved 0x40 to 0x7ff: Standards Action 0x800+: Specification Required
WfM
OK, I completely missed some developments here. Why are we using the 2 byte space for error codes? We can't we start at 0, match the HTTP/2 values, and reserve those that don't map properly?
This happened in the big error code shuffle of URL Folks seemed to want to avoid overlap with H2 in case of errors that might happen for a converting proxy.
I see the request there was to avoid overlap. That was prior to moving to using varints. I think that having these in the 0x00-0x3f space would be worth doing, if only so that the registration policies are consistent. The shift by 0x100 was excessive. 0x20 would have been plenty.
The H3 error codes always squatted in the 0x100 space (HTTPMALFORMEDFRAME...), so I think there's always been a problem. Having consistent policies is sensible, but moving the error codes again is going to be slightly painful.
If you don't want to renumber (which is entirely reasonable), then these error codes are just taken from the specification required space. I don't see any real problem with that.
I'm no expert in the area of registration. If some short term pain makes it easier in the long term then that seems reasonable. Ultimately the HTTP WG is going to be taking care of HTTP/3 in future, does NAME have an opinion?
No strong opinion, but given that the primary use case for these is debugging http stacks, not exposure to applications, on the face of it I don't think alignment is worth the last-minute pain.
So it sounds like our options on the table are: Leave it. It's weird, but not wrong. Renumber, which is going to make implementers sad. Rejigger the boundaries of how things are registered. (2) is right out, I think. Any arguments for (3), or do we just grit our teeth and walk around the odd lump in the spec from now on?
I think that it's fine to leave it.
NAME do you still require changes after these explanations?
I don't require any change here. I was noting the inconsistency in the registration policy. I think leaving things are acceptable. What I think might be worth it is to note the intention to avoid registrations overlapping with HTTP/2. That is simply to ensure that IETF + IESG knows what to consider in future registrations.
OK, if any of the editors feels like adding a PR on this, please do so. Otherwise, we can close this when we are publishing the next revisions.
(In any event, it will be "editorial".)
So I think resolves the last aspects of this issue by providing basic guidance.
minor things, but LGTM.

of the error code.  An expert reviewer is advised to examine new registrations for possible duplication with existing error codes. Use of existing registrations is to be encouraged, but not mandated. <ins> Use of values that are registered in the "HTTP/2 Error Code" registry is discouraged. </ins> In addition to common fields as described in iana-policy, this registry includes two additional fields.  Permanent registrations in A brief description of the error code semantics. The entries in iana-error-table are registered by this document. <ins> These error codes were selected from the range that operates on a Specification Required policy to avoid collisions with HTTP/2 error codes. </ins> Additionally, each code of the format "0x1f * N + 0x21" for non- negative integer values of N (that is, 0x21, 0x40, ..., through


(IPv4), the DF bit MUST be set if possible, to prevent fragmentation on the path.  14.1. A client MUST expand the payload of all UDP datagrams carrying datagram with a payload that is smaller than the smallest allowed maximum datagram size of 1200 bytes.  A server MAY also immediately close the connection by sending a CONNECTION_CLOSE frame with an <del> error code of PROTOCOL_VIOLATION; see immediate-close-hs.  When a client receives an ack-eliciting Initial packet that is carried in a UDP datagram with a payload that is less than 1200 bytes, that client MAY close the connection by sending a CONNECTION_CLOSE frame. </del>  The server MUST also limit the number of bytes it sends before validating the address of the client; see address-validation.

by adding generic "MUST NOT close, MAY discard packets" rule.
?
NAME NAME Merging this, since the plan is to do consensus as part of LC.
In , we added the following sentence, but I think it does not make sense. Generally speaking, a receiver of a datagram cannot verify if the sender sent a full-sized datagram. That is because a datagram as a whole is not authenticated. Instead, we authenticate each of the QUIC packets that constitute the datagram. Therefore, if the sender sends a datagram consisting of multiple QUIC packets, it is (almost always) possible for a MOTS to take one of the QUIC packets, coalesce that with a few octets, and forward that datagram. That forwarded datagram would be smaller than the original datagram. Note also that a sender can send coalesced packet after the handshake too; see .
As a step forward, I think we should do the following: Regarding the sentence we added in , we should either a) change the rule to "MAY discard UDP datagrams less than 1200 bytes that carry ACK-eliciting Initial apckets", or b) delete the sentence, and Considering the fact that there could be other occasions in which we might want to require padding, either within the base drafts, or in some of the extensions, add a note to or somewhere that datagram size is not authenticated and cannot be enforced by the peer.
NAME I hate to make more changes, but I think your point is a good one. If a receiver does the enforcement that we added in -- of enforcing the datagram size -- that makes us vulnerable to this MOTS attack. The newly added text in hasn't shipped yet in a draft, so if we can get agreement, I'd like to take this in before we ship the next draft.
A timely reminder that making changes always results in making more changes and no matter how careful or small, there are almost always problems. This seems like a good proposed tweak, but this has to stop.
Let us discuss what purpose that "MAY discard" rule has and compare it to the issue. What is important for an endpoint is to ensure that its path to the peer is possible to forward packets and supporting at least minimal MTU to make QUIC work. I think that requirement lies on each endpoint to ensure that. Does the client need to enforce the server behavior for that verification? Is that a risk in itself that is greater, equal or less than the MOTS risk the enforcement has?
NAME NAME I totally agree that this has to stop. I'm sorry to have missed this point when working on ; I realized the problem when I was wondering about the MUST vs. SHOULD discussion in . That said, I do think that it would be a good idea to adopt the proposal in this issue. Let me go through the facts first. They are: An encrypted transport has to discard unauthenticated signals to prevent MOTS attackers from disrupting the connection. The size of the datagram is an unauthenticated signal throughout the lifetime of the connection. As noted above, this is the case for 1-RTT packets after handshake confirmation too. Due to these facts, "MUST NOT close, can discard" is a principle that we've always had. The reason we hadn't written it down is because we used to have only one requirement regarding datagram length, that correctly follow the principle, stating that "servers MUST discard non-full-sized datagrams containing Initial packets." But now that we have emerging consensus that datagrams carrying PATHCHALLENGE / PATHRESPONSE MUST be padded to full size (), we need to write down that the receiver cannot enforce that. Realizing that, I started to wonder if the "MAY close" rule that we added in has been correct. As NAME points out, is a change that has not shipped yet. It's a recent change due to an issue discovered during this IETF last call. I do not think we have official consensus yet. Therefore, I tend to think there would be less confusion if the next draft would contain: servers MUST pad datagrams carrying ack-eleciting Initial packets (the basis of ) endpoints MUST pad datagrams carrying PATHCHALLENGE / PATHRESPONSE frames () endpoints MUST NOT close a connection when receiving datagrams with size requirements () rather than having two different of rules for handling small-sized datagrams, of which one does not follow the principle.
Given we explicitly allow coalesced packets for PathMTU, I agree that closing the connection seems like the wrong thing to do. The PR looks sensible.
Agree with Ian. My plea for stopping was not about the fix (the fix here is good), but the original change. We would not have had this problem if we were able to accept that the protocol wasn't absolutely perfect.
NAME has described a way forward on the mailing list for this issue - are we good with that? If so, let's merge, close and do the consensus establishment during IETF LC.
This seems sensible.

(IPv4), the DF bit MUST be set if possible, to prevent fragmentation on the path. <ins> Datagrams are required to be of a minimum size under some conditions. However, the size of the datagram is not authenticated.  Therefore, an endpoint MUST NOT close a connection when it receives a datagram that does not meet size constraints, though the endpoint MAY discard such datagrams. </ins> 14.1. A client MUST expand the payload of all UDP datagrams carrying datagram with a payload that is smaller than the smallest allowed maximum datagram size of 1200 bytes.  A server MAY also immediately close the connection by sending a CONNECTION_CLOSE frame with an  <ins> error code of PROTOCOL_VIOLATION; see immediate-close-hs. </ins> The server MUST also limit the number of bytes it sends before validating the address of the client; see address-validation.


Resumed connections over the same network MAY use the previous connection's final smoothed RTT value as the resumed connection's initial RTT.  When no previous RTT is available, the initial RTT <del> SHOULD be set to 333ms, resulting in a 1 second initial timeout, as recommended in RFC6298. </del>  A connection MAY use the delay between sending a PATH_CHALLENGE and receiving a PATH_RESPONSE to set the initial RTT (see kInitialRtt in Endpoints SHOULD abandon path validation based on a timer.  When setting this timer, implementations are cautioned that the new path could have a longer round-trip time than the original.  A value of <del> three times the larger of the current Probe Timeout (PTO) or the initial timeout (that is, 2*kInitialRtt) as defined in QUIC-RECOVERY is RECOMMENDED.  That is: </del>  This timeout allows for multiple PTOs to expire prior to failing path validation, so that loss of a single PATH_CHALLENGE or PATH_RESPONSE

Address validation can use 3*PTO, just taking the maximum of the old and new path PTO. The idea of initial timeout is easily replaced in recovery by instead describing the effect on the PTO. The net effect is the same, except that new paths often have to include the maximum ACK delay, which this doesn't incorporate.
A quick look in the reference did not show what units of time you are using in the protocol - maybe I missed it?
is set to 333ms. Not sure why a max between two values needs to define the unit in the first place though.
is not defined in -transport, and Stewart was (probably) only reviewing the document in isolation.
NAME a max needs to ensure both values use the same units. But provided that they do, I agree.
Maybe it's easier to say that these are all physical quantities, or numbers with units. In the specification, we use seconds, milliseconds, microseconds. That's fine, because we know how to align the units when we make calculations (e.g., max).
PR proposes removing the text in question. Of course, it doesn't matter what time units are in use in this case. I'm fairly confident that where units matter (i.e., we base the value on a constant), the units are explicit. Here, the units are part of the definition of the variables.
As Martin points out, the offending text was removed. Not seeing anything to be done here , so I'm closing the issue.
says "initial timeout (that is, )". But I don't think that we have this initial timeout concept in recovery any more; we . Should this just be now? The result is that the is meaningless as the PTO is when initialized. Or is this the PTO on the pre-existing path?
I think this is the case. Notice "current." Therefore I belive that the formula should be .
Changing 2kInitialRtt to 3kInitialRtt and 6kInitialRtt to 9kInitialRtt seems like the simplest fix since we don't define an initial_PTO variable anywhere in the documents.
I assume that the choice between and is purely editorial. Either works for me.
I prefer with some changes that I've noted on the PR.
I think this is correct. LGTM, but waiting for NAME to take a look.

Resumed connections over the same network MAY use the previous connection's final smoothed RTT value as the resumed connection's initial RTT.  When no previous RTT is available, the initial RTT  <ins> SHOULD be set to 333ms.  This results in handshakes starting with a PTO of 1 second, as recommended for TCP's initial retransmission timeout; see Section 2 of RFC6298. </ins> A connection MAY use the delay between sending a PATH_CHALLENGE and receiving a PATH_RESPONSE to set the initial RTT (see kInitialRtt in Endpoints SHOULD abandon path validation based on a timer.  When setting this timer, implementations are cautioned that the new path could have a longer round-trip time than the original.  A value of  <ins> three times the larger of the current Probe Timeout (PTO) or the PTO for the new path (that is, using kInitialRtt as defined in QUIC- RECOVERY) is RECOMMENDED. </ins> This timeout allows for multiple PTOs to expire prior to failing path validation, so that loss of a single PATH_CHALLENGE or PATH_RESPONSE


For example:  2. Streams in QUIC provide a lightweight, ordered byte-stream

are encoded as variable-length integers; see Section 16. A QUIC endpoint MUST NOT reuse a stream ID within a connection. This was a common problem throughout the text. I found the use of (0x1) and similar confusing in the text. Where you use this notation you mean the bit manipulated by the mask 0x1 (and similar). It would be helpful to the reader to be a little more pedantic in the use of the term in each case. We will get to it later, in the comments but a lot of readers would be helped by the use of the classic ASCII art style representation of bit fields.
slightly more text

For example: <ins> When a single-bit field is referenced in prose, the position of that field can be clarified by using the value of the byte that carries the field with the field's value set.  For example, the value 0x80 could be used to refer to the single-bit field in the most significant bit of the byte, such as One-bit Field in fig-ex-format. </ins> 2. Streams in QUIC provide a lightweight, ordered byte-stream


The maximum datagram size is defined as the largest size of UDP payload that can be sent across a network path using a single UDP <del> datagram. QUIC depends upon a minimum IP packet size of at least 1280 bytes. This is the IPv6 minimum size (IPv6) and is also supported by most modern IPv4 networks.  Assuming the minimum IP header size of 40 bytes for IPv6 and 20 bytes for IPv4 and a UDP header size of 8 bytes, this results in a maximum datagram size of 1232 bytes for IPv6 and 1252 bytes for IPv4. The maximum datagram size MUST be at least 1200 bytes. </del>  Any maximum datagram size larger than 1200 bytes can be discovered using Path Maximum Transmission Unit Discovery (PMTUD; see pmtud) or

On endpoints.
This is the IPv6 minimum size ([IPv6]) and is also supported by most modern IPv4 networks. Assuming the minimum IP header size of 40 bytes for IPv6 and 20 bytes for IPv4 and a UDP header size of 8 bytes, this results in a maximum datagram size of 1232 bytes for IPv6 and 1252 bytes for IPv4. The previous two paras are quite confusing. I think the first para refers to the max packet size in the network layer, but the second para is not clear, nor is the next para.
The paragraph preceding the first is this: So it is clear what the second paragraph means. QUIC doesn't operate if the network is incapable of carrying a datagram payload of 1200 bytes. I think that rewording that way might help. What is less clear is where the extra 32 (or 52) bytes go. In that we've been very generous in providing for header extensions or UDP extensions. But I don't see a need to explain that aspect.
I found this to still be very confusing, FWIW. It's unclear why the limit would be 1200 bytes when the text very clearly explains that at least 1232 can be relied upon, or alternatively why the text claims "QUIC assumes a minimum IP packet size of at least 1280 bytes." when in fact it appears to only mandate a 32 bytes smaller figure. It reads like someone forgot to update a constant. NAME above suggests that this was intentional, to leave space for unspecified future use by lower layers. I think some allusion to this is justified, just to clarify the otherwise seeming inconsistency.
A sentence to tie it together but LGTM

The maximum datagram size is defined as the largest size of UDP payload that can be sent across a network path using a single UDP  <ins> datagram.  QUIC MUST NOT be used if the network path cannot support a maximum datagram size of at least 1200 bytes. QUIC assumes a minimum IP packet size of at least 1280 bytes.  This is the IPv6 minimum size (IPv6) and is also supported by most modern IPv4 networks.  Assuming the minimum IP header size of 40 bytes for IPv6 and 20 bytes for IPv4 and a UDP header size of 8 bytes, this results in a maximum datagram size of 1232 bytes for IPv6 and 1252 bytes for IPv4.  Thus, modern IPv4 and all IPv6 network paths will be able to support QUIC. </ins> Any maximum datagram size larger than 1200 bytes can be discovered using Path Maximum Transmission Unit Discovery (PMTUD; see pmtud) or


RETIRE_CONNECTION_ID frame (frame-retire-connection-id). An endpoint that receives packets that it cannot process sends a <del> packet in the following layout: </del>  This design ensures that a stateless reset packet is - to the extent possible - indistinguishable from a regular packet with a short

Fixed Bits (2) = 1, Unpredictable Bits (38..), Stateless Reset Token (128), } When I first met this my reaction was that I was sure it sends a lot more bits on the wire than this, but so far I have not see a packet definition nor a pointer to a packet definition. Please could you clarify what is happening?
This seems like a nit. I guess the only thing being that this is the first time the notation introduced in Section 1.3 is used. And now it is Section 10.3. I'll add a cross reference.

RETIRE_CONNECTION_ID frame (frame-retire-connection-id). An endpoint that receives packets that it cannot process sends a  <ins> packet in the following layout (see notation): </ins> This design ensures that a stateless reset packet is - to the extent possible - indistinguishable from a regular packet with a short


Each frame begins with a Frame Type, indicating its type, followed by additional type-dependent fields: <del> The frame types defined in this specification are listed in frame- types.  The Frame Type in ACK, STREAM, MAX_STREAMS, STREAMS_BLOCKED, and CONNECTION_CLOSE frames is used to carry other frame-specific flags.  For all other frames, the Frame Type field simply identifies the frame.  These frames are explained in more detail in frame- formats. </del>  The "Pkts" column in frame-types lists the types of packets that each frame type could appear in, indicated by the following characters: An endpoint MUST treat the receipt of a frame of unknown type as a connection error of type FRAME_ENCODING_ERROR. <del> All QUIC frames are idempotent in this version of QUIC.  That is, a valid frame does not cause undesirable side effects or errors when received more than once. </del>  The Frame Type field uses a variable-length integer encoding (see integer-encoding) with one exception.  To ensure simple and efficient

Less detail above, but a pointer to what follows.

Table 3: Frame Types The notation used in the table took a while to figure out. A little more explanation, and possibly before the table itself might make it clearer.
One suggestion, but LGTM

Each frame begins with a Frame Type, indicating its type, followed by additional type-dependent fields:  <ins> frame-types lists and summarizes information about each frame type that is defined in this specification.  A description of this summary is included after the table. The format and semantics of each frame type are explained in more detail in frame-formats.  The remainder of this section provides a summary of important and general information. The Frame Type in ACK, STREAM, MAX_STREAMS, STREAMS_BLOCKED, and CONNECTION_CLOSE frames is used to carry other frame-specific flags. For all other frames, the Frame Type field simply identifies the frame. </ins> The "Pkts" column in frame-types lists the types of packets that each frame type could appear in, indicated by the following characters: An endpoint MUST treat the receipt of a frame of unknown type as a connection error of type FRAME_ENCODING_ERROR.  <ins> All frames are idempotent in this version of QUIC.  That is, a valid frame does not cause undesirable side effects or errors when received more than once. </ins> The Frame Type field uses a variable-length integer encoding (see integer-encoding) with one exception.  To ensure simple and efficient


The transport protocol described by this document.  QUIC is a name, not an acronym. <del> A complete processable unit of QUIC that can be encapsulated in a UDP datagram.  Multiple QUIC packets can be encapsulated in a single UDP datagram. A QUIC packet that contains frames other than ACK, PADDING, and CONNECTION_CLOSE.  These cause a recipient to send an acknowledgment; see sending-acknowledgements. </del> An entity that can participate in a QUIC connection by generating, receiving, and processing QUIC packets.  There are only two types of endpoint in QUIC: client and server. The endpoint that accepts a QUIC connection.  When used without qualification, the tuple of IP version, IP address, and UDP port number that represents one end of a network path.

I struggled with this a little. Happy to take suggestions.
I'm going to say that the IP Packet vs. QUIC Packet text is not necessary. We carefully qualify where there is any potential for confusion.
On the other issue, it might be worth simply stating the terminology upfront. Something like

Datagram Size There is some confusion in the text between packets, frames and datagrams. That could be helped by earlier clarification.
This is a duplicate of .
12.4. Frames and Frame Types Packet Payload { Frame (8..) ..., } Version Negotiation, Stateless Reset, and Retry packets do not contain frames. At this point (page 82) I am still not sure what a packet or a frame is and have no idea what is going on on the wire.
Probably a duplicate of . These terms should be added to the definitions, with possibly some additional explanatory text in Section 12.
Section 1 is the first place that you introduce the term "Frame". I got very confused about frames and packets, and I thing the taxonomy needs to be clearly explained. You have UDP/IP packets that carry one or more QUIC PDUs, but the frame/packet notation made it unclear what was being discussed and whether the terms were being used consistently. Some clarification would be useful, or if it is in the text then making it more obvious would help.
Perhaps "frame" and "datagram" should be added as defined terms in Section 1.2. On a related note, the defined term is "QUIC packet" but in the text we don't always specify what type of "packet." Perhaps we should define "packet" as generally referring to QUIC, with a note that IP packets will be indicated as such. (I believe all instances of "packet" that refer to IP explicitly say "IP packet," but we should verify that.)
One small suggestion, which you can ignore if you don't like it.

The transport protocol described by this document.  QUIC is a name, not an acronym.  An entity that can participate in a QUIC connection by generating, receiving, and processing QUIC packets.  There are only two types of endpoint in QUIC: client and server. The endpoint that accepts a QUIC connection. <ins> A complete processable unit of QUIC that can be encapsulated in a UDP datagram.  One or more QUIC packets can be encapsulated in a single UDP datagram. A QUIC packet that contains frames other than ACK, PADDING, and CONNECTION_CLOSE.  These cause a recipient to send an acknowledgment; see sending-acknowledgements. A unit of structured protocol information.  There are multiple frame types, each of which carries different information.  Frames are contained in QUIC packets. </ins> When used without qualification, the tuple of IP version, IP address, and UDP port number that represents one end of a network path.


As a result, the size of the packet number encoding is at least one bit more than the base-2 logarithm of the number of contiguous <del> unacknowledged packet numbers, including the new packet. For example, if an endpoint has received an acknowledgment for packet 0xabe8bc, sending a packet with a number of 0xac5c02 requires a packet number encoding with 16 bits or more; whereas the 24-bit packet number encoding is needed to send a packet with a number of 0xace8fe. </del>  At a receiver, protection of the packet number is removed prior to recovering the full packet number.  The full packet number is then Once header protection is removed, the packet number is decoded by finding the packet number value that is closest to the next expected packet.  The next expected packet is the highest received packet <del> number plus one.  For example, if the highest successfully authenticated packet had a packet number of 0xa82f30ea, then a packet containing a 16-bit value of 0x9b32 will be decoded as 0xa82f9b32. Example pseudo-code for packet number decoding can be found in sample-packet-number-decoding. </del>  17.2.

I took some liberties with data types here, but... pseudocode. I do wonder if the slice notation is sufficiently clear; I was hesitant to just do bit-shifts and masks because the number of bytes returned is key. An alternative would be simply to return the number of bytes, I suppose.
bit more than the base-2 logarithm of the number of contiguous unacknowledged packet numbers, including the new packet. That is really hard to parse. 0xabe8bc, sending a packet with a number of 0xac5c02 requires a packet number encoding with 16 bits or more; whereas the 24-bit packet number encoding is needed to send a packet with a number of 0xace8fe. and so is that. Indeed I found the remaining paras of the section difficult to follow.
I agree that first sentence is hard to parse.
I also agree regarding the first. As this is an alternative description, and I don't have anything better to offer, I plan not to change anything unless someone offers something good. The second seems fine.
I wonder if the real takeaway here is that these "examples in passing" assume the reader is deeply following along with the algorithm in their head; I know I don't, but I'm also not implementing packetization. We already have an appendix that illustrates packet number decoding; perhaps we should move one or both paragraphs to an appendix that illustrates selecting an encoding, with these as the examples. Reading the rest of the section, is "protection of the packet number" the term we've used elsewhere? Shouldn't that be "header protection," perhaps with a cross-ref to -tls?

As a result, the size of the packet number encoding is at least one bit more than the base-2 logarithm of the number of contiguous  <ins> unacknowledged packet numbers, including the new packet.  Pseudo-code and examples for packet number encoding can be found in sample- packet-number-encoding. </ins> At a receiver, protection of the packet number is removed prior to recovering the full packet number.  The full packet number is then Once header protection is removed, the packet number is decoded by finding the packet number value that is closest to the next expected packet.  The next expected packet is the highest received packet  <ins> number plus one.  Pseudo-code and an example for packet number decoding can be found in sample-packet-number-decoding. </ins> 17.2.


encode 6, 14, 30, or 62 bit values respectively.  integer-summary summarizes the encoding properties. <del> For example, the eight byte sequence c2 19 7c 5e ff 14 e8 8c (in hexadecimal) decodes to the decimal value 151288809941952652; the four byte sequence 9d 7f 3e 7d decodes to 494878333; the two byte sequence 7b bd decodes to 15293; and the single byte 25 decodes to 37 (as does the two byte sequence 40 25). </del>  Versions (versions) and packet numbers sent in the header (packet- encoding) are described using integers, but do not use this encoding.

if we decide we need/want it.
And yes, this handwaves over coercing the mask into the right size integer for the number of bytes you read.
When you do that, it's probably worth copying the "Code Components" language from -recovery.
hexadecimal) decodes to the decimal value 151288809941952652; the four byte sequence 9d 7f 3e 7d decodes to 494878333; the two byte sequence 7b bd decodes to 15293; and the single byte 25 decodes to 37 (as does the two byte sequence 40 25). The Some hex & decimal indication would help. A picture is worth a thousand words and save the reader drawing this out themselves to understand it and getting confused. The conversion pseudo code would be helpful, possibly in an appendix.
I don't think that any change is needed here. It says "in hexadecimal" and "decimal value" right there.
We could put comma separators in the decimal values (i.e. 494878333 => 494,878,333) so they're more readily parsed as decimals rather than digit sequences. And an appendix with a varint decoder might not be the worst idea.

encode 6, 14, 30, or 62 bit values respectively.  integer-summary summarizes the encoding properties.  <ins> Examples and a sample decoding algorithm are shown in sample-varint. </ins> Versions (versions) and packet numbers sent in the header (packet- encoding) are described using integers, but do not use this encoding.


connections.  An endpoint that wishes to communicate a fatal connection error MUST use a CONNECTION_CLOSE frame if it is able. <del> To support this process, a token is sent by endpoints.  The token is carried in the Stateless Reset Token field of a NEW_CONNECTION_ID frame.  Servers can also specify a stateless_reset_token transport parameter during the handshake that applies to the connection ID that it selected during the handshake; clients cannot use this transport parameter because their transport parameters do not have confidentiality protection.  These tokens are protected by encryption, so only client and server know their value.  Tokens are invalidated when their associated connection ID is retired via a RETIRE_CONNECTION_ID frame (frame-retire-connection-id). </del>  An endpoint that receives packets that it cannot process sends a packet in the following layout (see notation):

This was a little obtuse; it would benefit from a clearer description of what the token is and how it is used.
Thanks Mike. I don't think that I need to worry about "easy to regenerate" (there is a lot of text on that later). The "exchanges" comment is unmodified text, but it is worth fixing.
Having read to the end of section 10 I made a note that I was still trying to figure out what a reset token actually looked like.
Addressed in .

10.3.1. Detecting a Stateless Reset bytes of the UDP datagram. I find this rather opaque given that so far we do not know what a packet looks like, and I cannot see what the encoding of this is. To add to the confusion over packets and frames we now have datagrams.
The encoding of a packet is specifically not relevant at this point. I think that the question is whether it is reasonable to expect that readers have internalized : There is a reasonable criticism here in that the level of detail in the stateless reset description is too much too early. It would be nicer if some of the more detailed stuff here were deferred until later. Unfortunately, it is hard to extricate this from the other more generic stuff in Section 10. So I'm not seeing any real way to improve this without making things worse in other ways.
A few suggestions, but I think this is adequate to resolve the issue.

connections.  An endpoint that wishes to communicate a fatal connection error MUST use a CONNECTION_CLOSE frame if it is able.  <ins> To support this process, an endpoint issues a stateless reset token, which is a 16-byte value that is hard to guess.  If the peer subsequently receives a stateless reset, which is a UDP datagram that ends in that stateless reset token, the peer will immediately end the connection. A stateless reset token is specific to a connection ID.  An endpoint issues a stateless reset token by including the value in the Stateless Reset Token field of a NEW_CONNECTION_ID frame.  Servers can also issue a stateless_reset_token transport parameter during the handshake that applies to the connection ID that it selected during the handshake.  These exchanges are protected by encryption, so only client and server know their value.  Note that clients cannot use the stateless_reset_token transport parameter because their transport parameters do not have confidentiality protection. Tokens are invalidated when their associated connection ID is retired via a RETIRE_CONNECTION_ID frame (frame-retire-connection-id). </ins> An endpoint that receives packets that it cannot process sends a packet in the following layout (see notation):


version.  New QUIC versions SHOULD define a new salt value used in calculating initial secrets.  10. This document registers the quic_transport_parameters extension found

I'm opposed to saying that values from a PRNG shouldn't be exposed. But we can at least say that we need a good CS-PRNG to get many of the security properties claimed by the protocol.
Dear QUIC designers, I've been recently following the discussion on PATHCHALLENGE/PATHRESPONSE attack scenarios, and for what it matters, I am also working on QUIC-like capabilities above TCP. In my design, upon thinking on connection migration attack vectors, I've been worried about another type of exploit. It is more critical in my opinion than bandwidth amplification, and it also affects QUIC. So, regarding QUIC, when a connection migration is initiated, from the current specification[0], "An endpoint can migrate a connection to a new local address by sending packets containing non-probing frames from that address." Sending a non-probing packet would trigger a PATHCHALLENGE message from the other peer in an implementation following those specs to enter in path validation. Essentially, it means that a client can, on-demand, trigger a PATHCHALLENGE from the server. In the specification[1], the PATHCHALLENGE is containing an unpredictable payload. More formally, it is meant to be indistinguishable from the output of a random function. In practice, there are different ways to compute this "unpredictable" payload, with critical differences. The naive approach is to use unsecure randomness random(), and then putting it in the PATHCHALLENGE. Another approach would be to use secure randomness to satisfy the current specifications. That is, putting the output of SecureRandom() to the PATHCHALLENGE. Ironically, the second approach is worrying me. That is, the current protocol specifications may provide incentives to implement a protocol channel to allow any client to query the server's PRNG output on-demand, and with probably a consequent amount of randomness bandwidth. We know from history that this is not a great idea, especially is the presence of potential weaknesses within the PRNG (intentional or not). The most appealing example is the backdoored Dual EC PRNG, for which the "backdoor holder" would only need to bruteforce 16 bits to recover the internal state and predict any new outcome. This is possible as soon as enough of the Duac EC output is observed. In the case of Dual EC, and assuming that the PATHCHALLENGE contains 16 bytes of randomness, the attacker would only need to trigger 3 PATHCHALLENGE to predict the PRNG's next outcomes (assuming P256 base points). Of course, Dual EC is an extreme case, for which the randomness could still be available from other protocol materials (e.g., ConnIDs or keys), but we should anyway avoid specifying a protocol that would offer Secure Randomness easily, and not under the peer's control. I suggest to specify what "unpredictability" means and how to compute it and iterate over multiple "unpredictable" payloads. A goal would be to make clear that we want as few as possible outputs from SecureRandom() to avoid for e.g., a client to have an oracle access to the Server's PRNG. A solution could be to use a simple Hash function and R, 16 bytes of randomness only known by the server and global to all sessions. Then a PATHCHALLENGE could contain, with SESSIONSERCRET the shared derived secret of the session: Challenge1 = H(SESSIONSECRET R) Then, the next one: Challenge2 = H(Challenge1 R) ... Challengei = H(Challenge_i-1 R) This maintains unpredictability, assuming a cryptographic hash function and R secret. I guess that other constructions are possible as well, using HKDF for example. I would also suggest looking into other places for which "unpredictability" is needed, and evaluate whether it could provide good oracle access to the other peer, and if we can be more exigent into the specifications to make sure to avoid those issues. [0] URL [1] URL
Interesting! Given that the TLS ServerHello random field is , is this really a new vector? I would expect the same code to be used to produce the challenge body.
Yes, that's a good point! That's the reason why I said we should look into other places for which "unpredictability" is needed. For the 32 bytes random field of the server hello, a similar strategy could be employed to reduce the output of the PRNG. The protocol security essentially depends on assumptions such as a good PRNG, the underlyng untractable problem behind the asymetric cryptography that enables the symmetric key exchange, and the cryptographic assumptions of the H function used. If we can reduce the PRNG outptut size, and fill what is missing from pseudorandom derivations based on cryptographic assumptions that we should anyway trust, then it seems as a win to me. I understand the current approach "if PRNG bad, we're doomed, so better assume it good". I am not totally in favor of that mindset though. Weaknesses may appear and disappear in the PRNG used; it is not as static as the other security property we rely on.
I have very little sympathy for this viewpoint. As Chris says, we expose randomness that might be sourced from a "secure" PRNG in many other places in the protocol. There are implementation strategies that can be used to isolate PRNG usage for keys from other uses (OpenSSL employs one such technique) and there are hardening methods, such as the one described in RFC 8937. But if the request is to highlight the risk, then we can do that. It's not like we've been parsimonious with security considerations up until now.
I prefer the term "cryptographic RNG" rather than "cryptographically secure PRNG" or "CSPRNG" (which suggests that only certain kinds of pseudorandom generators are allowed). For instance, I define "cryptographic RNGs" as follows:
This is exactly what I suggest to discuss in the Security Considerations section. I was unaware of RFC 8937. It seems to cover the same concerns that I have and produce a similar recommendation. I think it would be valuable to QUIC stacks to pay attention to this issue, and eventually apply RFC 8937 on every source of randomness within QUIC.

version.  New QUIC versions SHOULD define a new salt value used in calculating initial secrets. <ins> 9.7. QUIC depends on endpoints being able to generate secure random numbers, both directly for protocol values such as the connection ID, and transitively via TLS.  See RFC4086 for guidance on secure random number generation. </ins> 10. This document registers the quic_transport_parameters extension found


The latency spin bit enables passive latency monitoring from observation points on the network path throughout the duration of a <del> connection.  The spin bit is only present in the short packet header, since it is possible to measure the initial RTT of a connection by observing the handshake.  Therefore, the spin bit is available after version negotiation and connection establishment are completed.  On- path measurement and use of the latency spin bit is further discussed in QUIC-MANAGEABILITY. </del>  The spin bit is an OPTIONAL feature of QUIC.  A QUIC stack that chooses to support the spin bit MUST implement it as specified in An endpoint resets the spin value for a network path to zero when changing the connection ID being used on that network path. <del> With this mechanism, the server reflects the spin value received, while the client 'spins' it after one RTT.  On-path observers can measure the time between two spin bit toggle events to estimate the end-to-end RTT of a connection. </del> 18. The extension_data field of the quic_transport_parameters extension

while the client 'spins' it after one RTT. On-path observers can measure the time between two spin bit toggle events to estimate the end-to-end RTT of a connection. It would be clearer to the reader if the explanation of operation proceeded the detail.
This is fine, but I find that description to be less than ideal. I won't try to suggest an improvement though. It isn't so bad.

The latency spin bit enables passive latency monitoring from observation points on the network path throughout the duration of a  <ins> connection.  The server reflects the spin value received, while the client 'spins' it after one RTT.  On-path observers can measure the time between two spin bit toggle events to estimate the end-to-end RTT of a connection. The spin bit is only present in the short packet header, since it is possible to measure the initial RTT of a connection by observing the handshake.  Therefore, the spin bit is available after version negotiation and connection establishment are completed.  On-path measurement and use of the latency spin bit is further discussed in QUIC-MANAGEABILITY. </ins> The spin bit is an OPTIONAL feature of QUIC.  A QUIC stack that chooses to support the spin bit MUST implement it as specified in An endpoint resets the spin value for a network path to zero when changing the connection ID being used on that network path.  18. The extension_data field of the quic_transport_parameters extension