Re: [quicwg/base-drafts] Stream0 dt output for merge (#1450)
ekr <notifications@github.com> Fri, 22 June 2018 13:24 UTC
Return-Path: <noreply@github.com>
X-Original-To: quic-issues@ietfa.amsl.com
Delivered-To: quic-issues@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 86C51130E59 for <quic-issues@ietfa.amsl.com>; Fri, 22 Jun 2018 06:24:50 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -8.01
X-Spam-Level:
X-Spam-Status: No, score=-8.01 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, HTML_MESSAGE=0.001, MAILING_LIST_MULTI=-1, RCVD_IN_DNSWL_HI=-5, SPF_PASS=-0.001, T_DKIMWL_WL_HIGH=-0.01] autolearn=ham autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (1024-bit key) header.d=github.com
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id FKDMoaa4VzIZ for <quic-issues@ietfa.amsl.com>; Fri, 22 Jun 2018 06:24:41 -0700 (PDT)
Received: from out-3.smtp.github.com (out-3.smtp.github.com [192.30.252.194]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 9C3C3130E58 for <quic-issues@ietf.org>; Fri, 22 Jun 2018 06:24:40 -0700 (PDT)
Date: Fri, 22 Jun 2018 06:24:39 -0700
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=github.com; s=pf2014; t=1529673879; bh=N2WLWuMJOzC7TWgSOq514e6cmT+bioUzvHsyXfLqFIE=; h=Date:From:Reply-To:To:Cc:In-Reply-To:References:Subject:List-ID: List-Archive:List-Post:List-Unsubscribe:From; b=XYV4R/ut2c7LlJUeAywDZv0ZWuuKBQAtpbZ44WfZ9DA8wCfNEjOyR8azFNwSbdRda 8boWLyuBQLaSwKpUFQRjUBj+ppzJovMfJyEc7x58cfy0s0ni3lXkygagZqpnoMOrpn PLWeFpfE5NnfdlNudpMiW3tESCWGgAVMhNoYRqAo=
From: ekr <notifications@github.com>
Reply-To: quicwg/base-drafts <reply+0166e4ab32280cf74213e84515b674808d68f282edaeb1ff92cf000000011744ba9792a169ce13d67a11@reply.github.com>
To: quicwg/base-drafts <base-drafts@noreply.github.com>
Cc: Subscribed <subscribed@noreply.github.com>
Message-ID: <quicwg/base-drafts/pull/1450/review/130932728@github.com>
In-Reply-To: <quicwg/base-drafts/pull/1450@github.com>
References: <quicwg/base-drafts/pull/1450@github.com>
Subject: Re: [quicwg/base-drafts] Stream0 dt output for merge (#1450)
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary="--==_mimepart_5b2cf8979905f_24772aed49cc6f58898a8"; charset="UTF-8"
Content-Transfer-Encoding: 7bit
Precedence: list
X-GitHub-Sender: ekr
X-GitHub-Recipient: quic-issues
X-GitHub-Reason: subscribed
X-Auto-Response-Suppress: All
X-GitHub-Recipient-Address: quic-issues@ietf.org
Archived-At: <https://mailarchive.ietf.org/arch/msg/quic-issues/ZGAN5khUkMRooKWEIbZ9J7_iO-o>
X-BeenThere: quic-issues@ietf.org
X-Mailman-Version: 2.1.26
List-Id: Notification list for GitHub issues related to the QUIC WG <quic-issues.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/quic-issues>, <mailto:quic-issues-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/quic-issues/>
List-Post: <mailto:quic-issues@ietf.org>
List-Help: <mailto:quic-issues-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/quic-issues>, <mailto:quic-issues-request@ietf.org?subject=subscribe>
X-List-Received-Date: Fri, 22 Jun 2018 13:24:51 -0000
ekr commented on this pull request.
@martinthomson this addresses the comments on the PR. There are two points to call out:
1. There is some substantive back and forth about the model for how to describe the state of the TLS stack and the current encryption level it is reading from. I left some comments describing my view, but it might be easier to discuss live. I don't believe it actually affects bytes on the wire, except for a few points where I think you may have misunderstood, noted in my comments.
I didn't change that text.
2. In a number of places, I opted not to update the Retry text because it seems we are still working through the details on the list.
> @@ -120,6 +124,17 @@ algorithms here that parallel well-known TCP ones. Protocol differences between
QUIC and TCP however contribute to algorithmic differences. We briefly describe
these protocol differences below.
+### Separate Packet Number Spaces
+
+QUIC uses separate packet number spaces for each encryption level, except that
+0-RTT and all generations of 1-RTT keys use the same packet number space.
+Separating the spaces allows the recovery mechanisms to work without special
+cases to avoid spuriously retransmitting un-processable packets.
Done
> @@ -120,6 +124,17 @@ algorithms here that parallel well-known TCP ones. Protocol differences between
QUIC and TCP however contribute to algorithmic differences. We briefly describe
these protocol differences below.
+### Separate Packet Number Spaces
+
+QUIC uses separate packet number spaces for each encryption level, except that
+0-RTT and all generations of 1-RTT keys use the same packet number space.
+Separating the spaces allows the recovery mechanisms to work without special
+cases to avoid spuriously retransmitting un-processable packets.
+Separate packet number spaces do not imply separate paths. Consequently,
Done
> @@ -107,7 +111,7 @@ important to the loss detection and congestion control machinery below.
* Retransmittable packets are those that contain at least one
retransmittable frame.
-* Crypto handshake data is sent on stream 0, and uses the reliability
+* Crypto handshake data is sent in CRYPTO_HS frames, and uses the reliability
I am going to hold this for now. It's a simple sed script once we decide what this should say and CRYPTO_HS is easier to sed.
I disagree with you about the alerts, so let's pick that up on-list.
>
-Handshake packets, which contain STREAM frames for stream 0, are critical to
-QUIC transport and crypto negotiation, so a separate alarm is used for them.
+Data in CRYPTO_HS frames is critical to QUIC transport and crypto negotiation,
+so a more aggressive timeout is used to retransmit it. Below, the word
+handshake packet is used to refer to packets containing CRYPTO_HS frames,
+not packets with the specific long header packet type Handshake.
Leaving as-is
>
+Unlike TLS over TCP, QUIC applications which want to send data do not
+send it through TLS "application_data" records. Rather, they send it
+as QUIC STREAM frames which are then carried in QUIC packets.
+
+
+# Carrying TLS Messages {#carrying-tls}
+
+QUIC carries TLS handshake data in CRYPTO_HS frames, each of which
Leaving as-is.
> +and encrypted under the current TLS encryption level.
+As with TLS over TCP, once TLS handshake data has
+been delivered to QUIC, it is QUIC's responsibility to deliver it
+reliably. Each chunk of data is associated with the then-current TLS
+sending keys, and if QUIC needs to retransmit that data, it MUST use
+the same keys even if TLS has already updated to newer keys.
+
+One important difference between TLS 1.3 records (used with TCP)
+and QUIC CRYPTO_HS frames is that in QUIC multiple frames may appear
+in the same QUIC packet as long as they are associated with the
+same encryption level. For instance, an implementation might
+bundle a Handshake message and an ACK for some Handshake
+data into the same packet.
+
+In general, the rules for which data can appear in packets of which
+encryption level are the same in QUIC as in TLS over TCP:
Rewritten a bit.
> +been delivered to QUIC, it is QUIC's responsibility to deliver it
+reliably. Each chunk of data is associated with the then-current TLS
+sending keys, and if QUIC needs to retransmit that data, it MUST use
+the same keys even if TLS has already updated to newer keys.
+
+One important difference between TLS 1.3 records (used with TCP)
+and QUIC CRYPTO_HS frames is that in QUIC multiple frames may appear
+in the same QUIC packet as long as they are associated with the
+same encryption level. For instance, an implementation might
+bundle a Handshake message and an ACK for some Handshake
+data into the same packet.
+
+In general, the rules for which data can appear in packets of which
+encryption level are the same in QUIC as in TLS over TCP:
+
+- CRYPTO_HS frames MAY appear in packets of any encryption level.
Nothing needed at this time.
> +same encryption level. For instance, an implementation might
+bundle a Handshake message and an ACK for some Handshake
+data into the same packet.
+
+In general, the rules for which data can appear in packets of which
+encryption level are the same in QUIC as in TLS over TCP:
+
+- CRYPTO_HS frames MAY appear in packets of any encryption level.
+- CONNECTION_CLOSE and CRYPTO_CLOSE MAY appear in packets of any
+ encryption level other than 0-RTT.
+- PADDING and PING frames MAY appear in packets of any encryption level.
+- ACK frames MAY appear in packets of any encryption level, but
+ MUST only acknowledge packets which appeared in that encryption
+ level.
+- STREAM frames MUST ONLY appear in the 0-RTT and 1-RTT levels.
+- All other frame types MUST only appear at the 1-RTT levels.
Doing nothing.
> +data into the same packet.
+
+In general, the rules for which data can appear in packets of which
+encryption level are the same in QUIC as in TLS over TCP:
+
+- CRYPTO_HS frames MAY appear in packets of any encryption level.
+- CONNECTION_CLOSE and CRYPTO_CLOSE MAY appear in packets of any
+ encryption level other than 0-RTT.
+- PADDING and PING frames MAY appear in packets of any encryption level.
+- ACK frames MAY appear in packets of any encryption level, but
+ MUST only acknowledge packets which appeared in that encryption
+ level.
+- STREAM frames MUST ONLY appear in the 0-RTT and 1-RTT levels.
+- All other frame types MUST only appear at the 1-RTT levels.
+
+Because packets may be reordered on the wire, QUIC uses the packet
Done
> +
+- CRYPTO_HS frames MAY appear in packets of any encryption level.
+- CONNECTION_CLOSE and CRYPTO_CLOSE MAY appear in packets of any
+ encryption level other than 0-RTT.
+- PADDING and PING frames MAY appear in packets of any encryption level.
+- ACK frames MAY appear in packets of any encryption level, but
+ MUST only acknowledge packets which appeared in that encryption
+ level.
+- STREAM frames MUST ONLY appear in the 0-RTT and 1-RTT levels.
+- All other frame types MUST only appear at the 1-RTT levels.
+
+Because packets may be reordered on the wire, QUIC uses the packet
+type to indicate which level a given packet was encrypted
+under, as shown in {{packet-types-levels}}. When multiple packets of
+different encryption levels need to be sent, endpoints SHOULD use
+coalesced packets to send them in the same UDP datagram.
Done.
> +- All other frame types MUST only appear at the 1-RTT levels.
+
+Because packets may be reordered on the wire, QUIC uses the packet
+type to indicate which level a given packet was encrypted
+under, as shown in {{packet-types-levels}}. When multiple packets of
+different encryption levels need to be sent, endpoints SHOULD use
+coalesced packets to send them in the same UDP datagram.
+
+| Packet Type | Encryption Level |
+|:----------------|:-----------------|
+| Initial | Initial secrets |
+| 0-RTT Protected | 0-RTT |
+| Handshake | Handshake |
+| Retry | N/A |
+| Short Header | 1-RTT |
+{: #packet-types-levels title="Encryption Levels by Packet Type"}
Done.
> +Because packets may be reordered on the wire, QUIC uses the packet
+type to indicate which level a given packet was encrypted
+under, as shown in {{packet-types-levels}}. When multiple packets of
+different encryption levels need to be sent, endpoints SHOULD use
+coalesced packets to send them in the same UDP datagram.
+
+| Packet Type | Encryption Level |
+|:----------------|:-----------------|
+| Initial | Initial secrets |
+| 0-RTT Protected | 0-RTT |
+| Handshake | Handshake |
+| Retry | N/A |
+| Short Header | 1-RTT |
+{: #packet-types-levels title="Encryption Levels by Packet Type"}
+
+{{QUIC-TRANSPORT}}; Section 6.3 shows how packets at the various encryption
Done
>
## Interface to TLS
-As shown in {{schematic}}, the interface from QUIC to TLS consists of four
-primary functions: Handshake, Source Address Validation, Key Ready Events, and
-Secret Export.
+As shown in {{schematic}}, the interface from QUIC to TLS consists of three
+primary functions:
+
+- Sending and receiving handshake messages
+- Rekeying (both in and out)
Changed to "transmit and receive"
>
In order to drive the handshake, TLS depends on being able to send and receive
-handshake messages on stream 0. There are two basic functions on this
+handshake messages. There are two basic functions on this
Done
> -A QUIC server starts the process by providing TLS with stream 0 octets.
-
-Each time that an endpoint receives data on stream 0, it delivers the octets to
-TLS if it is able. Each time that TLS is provided with new data, new handshake
-octets are requested from TLS. TLS might not provide any octets if the
-handshake messages it has received are incomplete or it has no data to send.
-
-At the server, when TLS provides handshake octets, it also needs to indicate
-whether the octets contain a HelloRetryRequest. A HelloRetryRequest MUST always
-be sent in a Retry packet, so the QUIC server needs to know whether the octets
-are a HelloRetryRequest.
+A QUIC server starts the process by providing TLS with the client's
+handshake octets.
+
+At any given time, an endpoint will have a current sending encryption
+level and receiving encryption level. Each encryption level is
I don't think that I agree with this.
Let's take send and receive separately. At any given time, the TLS stack is always sending with a given encryption level. That's just a fact of TLS, whether that's done by doing Rekey(); Send() or Send(Keys). In either case, the data is associated with that level when its sent to QUIC and QUIC needs to maintain that level upon transmission or retransmission.
Receive is more complicated: At any given time, the TLS stack is only prepared to receive packets at a given level. This is also a fact of TLS [0]. As you say, when a packet comes in, it is tagged with an encryption level and there are three cases:
- It's from a prior level, in which case it needs to be consistent with the existing data
- It's from the current level, in which case you process it
- It's from a future level, in which case you (probably) buffer it [I'm ignoring cases in which you packet that's impossibly from the future, like CFIN before you send SH.
Now, you can do this sorting/buffering operation at the TLS layer (like I think picoTLS does) or at the QUIC layer (like Minq does), but I think it's still true that there's one active level
[0] The only case in which this is confusing is 0-RTT rejection where the client is sending with Epoch 1 and the server is trying to receive with Epoch 0 or 2 (depending on how it was rejected). However, neither of these cases applies to QUIC, because you just don't enable the 0-RTT encryption level and so the packets are dropped prior to TLS.
> +
+At any given time, an endpoint will have a current sending encryption
+level and receiving encryption level. Each encryption level is
+associated with a different flow of bytes, which is reliably
+transmitted to the peer in CRYPTO_HS frames. When TLS provides handshake
+octets to be sent, they are appended to the current flow and
+will eventually be transmitted under the then-current key.
+
+When an endpoint receives a QUIC packet containing a CRYPTO_HS frame from
+the network, it proceeds as follows:
+
+- If the packet was in the current receiving encryption level, sequence
+ the data into the input flow as usual. As with STREAM frames,
+ the offset is used to find the proper location in the data sequence.
+ If the result of this process is that new data is available, then
+ it is delivered to TLS.
Done
> +will eventually be transmitted under the then-current key.
+
+When an endpoint receives a QUIC packet containing a CRYPTO_HS frame from
+the network, it proceeds as follows:
+
+- If the packet was in the current receiving encryption level, sequence
+ the data into the input flow as usual. As with STREAM frames,
+ the offset is used to find the proper location in the data sequence.
+ If the result of this process is that new data is available, then
+ it is delivered to TLS.
+
+- If the packet is from a previously installed encryption level, it
+ MUST not contain data which extends past the end of previously
+ received data in that flow. Implementations MUST treat any
+ violations of this requirement as a connection error of type
+ PROTOCOL_VIOLATION.
This doesn't seem right to me. First, the 0-RTT STREAM frame isn't relevant, because this is just about CRYPTO_HS.
As for EOED, here's what happens ordinarily (no loss)
- Recv CH; Rekey current RX to epoch 1
- Receive EOED: Rekey current RX to epoch 2
- Receive CFIN: Rekey current RX to epoch 3
If instead you get CH, CFIN, EOED, here's what happens:
- Recv CH; Rekey current RX to epoch 1
- Receive CFIN: it's in the future, so buffer
- Receive EOED: Rekey current RX to epoch 2
- Process CFIN; Rekey current RX to epoch 3
> Get Handshake
- 1-RTT Keys Ready
- <--- send/receive ---
+ <------------ Initial
+ Rekey rx to 0-RTT keys
Leaving this pending the "current" discussion above.
>
-## QUIC Key Expansion {#key-expansion}
+A server rejects 0-RTT by rejecting 0-RTT at the TLS layer. This results in
+early exporter keys being unavailable, thereby preventing the use of 0-RTT for
+QUIC.
Done.
>
-QUIC uses a system of packet protection secrets, keys and IVs that are modelled
-on the system used in TLS {{!TLS13}}. The secrets that QUIC uses
-as the basis of its key schedule are obtained using TLS exporters (see Section
-7.5 of {{!TLS13}}).
+A client that attempts 0-RTT MUST also consider 0-RTT to be rejected if it
+receives a Version Negotiation packet.
Why? It seems like you could just start sending 0-RTT right then
>
-QUIC uses the Hash-based Key Derivation Function (HKDF) {{!HKDF=RFC5869}} with
-the same hash function negotiated by TLS for key derivation. For example, if
-TLS is using the TLS_AES_128_GCM_SHA256, the SHA-256 hash function is used.
+In TLS over TCP, the HelloRetryRequest feature ({{TLS13}; Section
+4.1.4) can be used to correct a client's incorrect KeyShare extension
+as well as for a stateless round trip check. From the perspective of
Done.
>
-QUIC uses the Hash-based Key Derivation Function (HKDF) {{!HKDF=RFC5869}} with
-the same hash function negotiated by TLS for key derivation. For example, if
-TLS is using the TLS_AES_128_GCM_SHA256, the SHA-256 hash function is used.
+In TLS over TCP, the HelloRetryRequest feature ({{TLS13}; Section
+4.1.4) can be used to correct a client's incorrect KeyShare extension
+as well as for a stateless round trip check. From the perspective of
+QUIC, this just looks like additional messages carried in the Initial
+encryption level. Although it is in principle possible to use this
+feature for address verification in QUIC, QUIC implementations SHOULD
+instead use the Retry feature ({{QUIC-TRANSPORT}}; Section 4.4.2)).
Done
>
-The HKDF-Expand function used by QHKDF-Expand uses the PRF hash function
-negotiated by TLS, except for handshake secrets and keys derived from them (see
-{{handshake-secrets}}).
+If TLS experiences an error, it MUST generate an appropriate alert
+as defined in {{TLS13}}; Section 6) and then provide it to QUIC,
+which sends the alert in a CRYPTO_CLOSE frame. All such alerts are
+"fatal".
Added a reference to TLS 13.
>
-~~~
-struct {
- uint16 length = Length;
- opaque label<6..255> = "QUIC " + Label;
-} QhkdfExpandInfo;
-~~~
+# QUIC Packet Protection {#packet-protection}
+
+As with TLS over TCP, QUIC encrypts packets with keys derived from the TLS
+handshake, using the AEAD algorithm negotiated by TLS.
Left.
> - uint16 length = Length;
- opaque label<6..255> = "QUIC " + Label;
-} QhkdfExpandInfo;
-~~~
+# QUIC Packet Protection {#packet-protection}
+
+As with TLS over TCP, QUIC encrypts packets with keys derived from the TLS
+handshake, using the AEAD algorithm negotiated by TLS.
+
+
+## QUIC Packet Encryption Keys {#encryption-keys}
+
+QUIC derives packet encryption keys in the same way as TLS 1.3:
+Each encryption level/direction pair has a secret value, which
+is then used to derive the traffic keys using as described
+in {{TLS13}}; Section 7.3.
replaced with comma.
>
-For example, assuming a hash function with a 32 octet output, derivation for a
-client packet protection key would use HKDF-Expand with an `info` parameter of
-0x00200851554943206b6579.
+The keys for the Initial encryption level are computed based on
+the client's first Destination Connection Id, as described in
Done.
>
+The keys for the remaining encryption level are computed in the same
+fashion as the corresponding TLS keys (see {{TLS13}}; Section 7),
+except that the label for HKDF-Expand-Label uses the prefix "quic "
+rather than "tls 13". The purpose of this change is to provide key
Done
>
+The keys for the remaining encryption level are computed in the same
+fashion as the corresponding TLS keys (see {{TLS13}}; Section 7),
+except that the label for HKDF-Expand-Label uses the prefix "quic "
+rather than "tls 13". The purpose of this change is to provide key
+separation between TLS and QUIC, so that TLS stacks can avoid
+exposing TLS record protection keys.
My intention was that this be completely separate, actually.
With that said, I think there are a number of options here. I have the action item to talk to the cryptographers.
>
-Packets that carry the TLS handshake (Initial, Retry, and Handshake) are
-protected with a secret derived from the Destination Connection ID field from
-the client's Initial packet. Specifically:
+Initial packets are protected with
+a secret derived from the Destination Connection ID field from the client's
+Initial packet. Specifically:
Rewritten a bit. The Retry thing is still in flux.
> ~~~
The hash function for HKDF when deriving handshake secrets and keys is SHA-256
-{{!SHA=DOI.10.6028/NIST.FIPS.180-4}}. The connection ID used with QHKDF-Expand
-is the connection ID chosen by the client.
+{{!SHA=DOI.10.6028/NIST.FIPS.180-4}}. The connection ID used with
+HKDF-Expand-Label is the connection ID chosen by the client.
changed.
> @@ -743,121 +653,11 @@ 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. In this case, the handshake keys provide no assurance to the client
+ ID field. In this case, the initial keys provide no assurance to the client
Done.
> @@ -873,26 +673,20 @@ from the packet number.
All QUIC packets other than Version Negotiation and Stateless Reset packets are
protected with an AEAD algorithm {{!AEAD}}. Prior to establishing a shared
secret, packets are protected with AEAD_AES_128_GCM and a key derived from the
-client's connection ID (see {{handshake-secrets}}). This provides protection
+client's connection ID (see {{initial-secrets}}). This provides protection
Done.
> against off-path attackers and robustness against QUIC version unaware
middleboxes, but not against on-path attackers.
All ciphersuites currently defined for TLS 1.3 - and therefore QUIC - have a
16-byte authentication tag and produce an output 16 bytes larger than their
input.
-Once TLS has provided a key, the contents of regular QUIC packets immediately
-after any TLS messages have been sent are protected by the AEAD selected by TLS.
-
-The key, K, is either the client packet protection key (client_pp_key\<i>) or
-the server packet protection key (server_pp_key\<i>), derived as defined in
-{{key-expansion}}.
-
-The nonce, N, is formed by combining the packet protection IV (either
-client_pp_iv\<i\> or server_pp_iv\<i\>) with the packet number. The 64 bits
-of the reconstructed QUIC packet number in network byte order is left-padded
-with zeros to the size of the IV. The exclusive OR of the padded packet number
-and the IV forms the AEAD nonce.
+The key and iv for the packet are computed as described in {{encryption-keys}}.
Done
> -Once TLS has provided a key, the contents of regular QUIC packets immediately
-after any TLS messages have been sent are protected by the AEAD selected by TLS.
-
-The key, K, is either the client packet protection key (client_pp_key\<i>) or
-the server packet protection key (server_pp_key\<i>), derived as defined in
-{{key-expansion}}.
-
-The nonce, N, is formed by combining the packet protection IV (either
-client_pp_iv\<i\> or server_pp_iv\<i\>) with the packet number. The 64 bits
-of the reconstructed QUIC packet number in network byte order is left-padded
-with zeros to the size of the IV. The exclusive OR of the padded packet number
-and the IV forms the AEAD nonce.
+The key and iv for the packet are computed as described in {{encryption-keys}}.
+The nonce, N, is formed by combining the packet protection IV with the
+packet number. The 64 bits of the reconstructed QUIC packet number in
+network byte order is left-padded with zeros to the size of the IV.
are
> @@ -1024,144 +795,90 @@ encrypted_pn = ChaCha20(pn_key, counter, nonce, packet_number)
## Receiving Protected Packets
-Once an endpoint successfully receives a packet with a given packet number, it
-MUST discard all packets with higher packet numbers if they cannot be
-successfully unprotected with either the same key, or - if there is a key update
-- the next packet protection key (see {{key-update}}). Similarly, a packet that
-appears to trigger a key update, but cannot be unprotected successfully MUST be
-discarded.
+Once an endpoint successfully receives a packet with a given packet
+number, it MUST discard all packets in the same packet number space
+with higher packet numbers if they cannot be successfully unprotected
+with either the same key, or - if there is a key update - the next
+packet protection key (see {{key-update}}). Similarly, a packet that
+appears to trigger a key update, but cannot be unprotected
+successfully MUST be discarded.
I don't see what this has to do with EOED. Can you show me the packet flow you think has a problem here.
>
-A server might choose to retain 0-RTT packets that arrive before a TLS
-ClientHello. The server can then use those packets once the ClientHello
-arrives. However, the potential for denial of service from buffering 0-RTT
-packets is significant. These packets cannot be authenticated and so might be
-employed by an attacker to exhaust server resources. Limiting the number of
-packets that are saved might be necessary.
+A client MUST only use 0-RTT keys to protect data that is idempotent. A client
+MAY wish to apply additional restrictions on what data it sends prior to the
+completion of the TLS handshake. A client otherwise treats 0-RTT keys as
+equivalent to 1-RTT keys, except that ACKs for that data MUST only be sent with
+1-RTT keys.
Done.
>
-### Retransmission and Acknowledgment of Unprotected Packets
+: 0-RTT data can be acknowledged by the server as it receives it, but any
It's a Note.
>
-Once 1-RTT keys are available to an endpoint, it no longer needs the TLS
-handshake messages that are carried in unprotected packets. However, a server
-might need to retransmit its TLS handshake messages in response to receiving an
-unprotected packet that contains ACK frames. A server MUST process ACK frames
-in unprotected packets until the TLS handshake is reported as complete, or it
-receives an ACK frame in a protected packet that acknowledges all of its
-handshake messages.
+Receiving and verifying the TLS Finished message is critical in
+ensuring the integrity of the TLS handshake. A server MUST NOT use
+1-RTT protected packets from the client prior to verifying the client
+Finished message if its response depends on client authentication.
Removed. I think this is old text, though.
>
-## Key Update {#key-update}
+Once the 1-RTT keys are established and the short header is in use, it
+is possible to update the keys, for instance because of limits on AEAD
+encryption. The KEY_PHASE bit in the short header is used to indicate
I just removed it.
> -server has chosen to use a pre-shared key - the pre-shared key binder (see
-Section 4.2.8 of {{!TLS13}}). Verifying these values provides the server with
-an assurance that the ClientHello has not been modified.
-
-A server could receive packets protected with 0-RTT keys prior to receiving a
-TLS ClientHello. The server MAY retain these packets for later decryption in
-anticipation of receiving a ClientHello.
-
-Receiving and verifying the TLS Finished message is critical in ensuring the
-integrity of the TLS handshake. A server MUST NOT use protected packets from
-the client prior to verifying the client Finished message if its response
-depends on client authentication.
+It is also possible for the attacker to tamper with data that
+is carried in Handshake packets, but because that tampering
+requires modifying TLS handshake messages, that tampering will be
+detected as soon as the TLS handshake completes.
Done.
> @@ -1592,6 +996,11 @@ version of QUIC defined in {{QUIC-TRANSPORT}} is used.
The quic_transport_parameters extension is carried in the ClientHello and the
EncryptedExtensions messages during the handshake.
+While the transport parameters are technically available prior to the
+completion of the handshake, they cannot be fully trusted until the handshake
+completes, and reliance on them should be minimized.
+However, any tampering with the parameters will be detected
+when the handshake completes.
Done
> @@ -1608,14 +1017,13 @@ A small ClientHello that results in a large block of handshake messages from a
server can be used in packet reflection attacks to amplify the traffic generated
by an attacker.
-Certificate caching {{?RFC7924}} can reduce the size of the server's handshake
-messages significantly.
-
-QUIC requires that the packet containing a ClientHello be padded to a minimum
-size. A server is less likely to generate a packet reflection attack if the
-data it sends is a small multiple of this size. A server SHOULD use a
-HelloRetryRequest if the size of the handshake messages it sends is likely to
-significantly exceed the size of the packet containing the ClientHello.
+QUIC includes three defenses against this attack. First, the packet
+containing a ClientHello be padded to a minimum size. Second, if
Done
> @@ -1608,14 +1017,13 @@ A small ClientHello that results in a large block of handshake messages from a
server can be used in packet reflection attacks to amplify the traffic generated
by an attacker.
-Certificate caching {{?RFC7924}} can reduce the size of the server's handshake
-messages significantly.
-
-QUIC requires that the packet containing a ClientHello be padded to a minimum
-size. A server is less likely to generate a packet reflection attack if the
-data it sends is a small multiple of this size. A server SHOULD use a
-HelloRetryRequest if the size of the handshake messages it sends is likely to
-significantly exceed the size of the packet containing the ClientHello.
+QUIC includes three defenses against this attack. First, the packet
+containing a ClientHello be padded to a minimum size. Second, if
+responding to an unverified source address, the server is forbidden to
+send more than three packets in its first flight ({{QUIC-TRANSPORT}};
Done.
> @@ -1608,14 +1017,13 @@ A small ClientHello that results in a large block of handshake messages from a
server can be used in packet reflection attacks to amplify the traffic generated
by an attacker.
-Certificate caching {{?RFC7924}} can reduce the size of the server's handshake
-messages significantly.
-
-QUIC requires that the packet containing a ClientHello be padded to a minimum
-size. A server is less likely to generate a packet reflection attack if the
-data it sends is a small multiple of this size. A server SHOULD use a
-HelloRetryRequest if the size of the handshake messages it sends is likely to
-significantly exceed the size of the packet containing the ClientHello.
+QUIC includes three defenses against this attack. First, the packet
+containing a ClientHello be padded to a minimum size. Second, if
+responding to an unverified source address, the server is forbidden to
+send more than three packets in its first flight ({{QUIC-TRANSPORT}};
+Section 4.4.3). Finally, because ACKs of Handshake packets
Done
> @@ -1608,14 +1017,13 @@ A small ClientHello that results in a large block of handshake messages from a
server can be used in packet reflection attacks to amplify the traffic generated
by an attacker.
-Certificate caching {{?RFC7924}} can reduce the size of the server's handshake
-messages significantly.
-
-QUIC requires that the packet containing a ClientHello be padded to a minimum
-size. A server is less likely to generate a packet reflection attack if the
-data it sends is a small multiple of this size. A server SHOULD use a
-HelloRetryRequest if the size of the handshake messages it sends is likely to
-significantly exceed the size of the packet containing the ClientHello.
+QUIC includes three defenses against this attack. First, the packet
+containing a ClientHello be padded to a minimum size. Second, if
+responding to an unverified source address, the server is forbidden to
+send more than three packets in its first flight ({{QUIC-TRANSPORT}};
+Section 4.4.3). Finally, because ACKs of Handshake packets
+are authenticated, a blind attacker cannot forge them
Done
>
All these packets use the long header and contain the current QUIC version in
the version field.
-In order to prevent tampering by version-unaware middleboxes, handshake packets
-are protected with a connection- and version-specific key, as described in
-{{QUIC-TLS}}. This protection does not provide confidentiality or integrity
-against on-path attackers, but provides some level of protection against
-off-path attackers.
+In order to prevent tampering by version-unaware middleboxes, Initial
+packets are protected with a connection- and version-specific keys
Done
>
### Initial Packet {#packet-initial}
The Initial packet uses long headers with a type value of 0x7F. It carries the
-first cryptographic handshake message sent by the client.
+first CRYPTO_HS frames sent by the client as well as the
+cryptographic messages sent by the server to perform key exchange. The Initial
Done
>
### Initial Packet {#packet-initial}
The Initial packet uses long headers with a type value of 0x7F. It carries the
-first cryptographic handshake message sent by the client.
+first CRYPTO_HS frames sent by the client as well as the
+cryptographic messages sent by the server to perform key exchange. The Initial
+packet is protected by Initial keys as described in {{QUIC-TLS}}.
Done
>
-If the client received a Retry packet and is sending a second Initial packet,
-then it sets the Destination Connection ID to the value from the Source
-Connection ID in the Retry packet. Changing Destination Connection ID also
-results in a change to the keys used to protect the Initial packet.
+~~~
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Token Length (i) ...
I mostly-centered this. Is that what you wanted.
> -results in a change to the keys used to protect the Initial packet.
+~~~
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Token Length (i) ...
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Token (*) ...
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~
+
+Token Length:
+
+: A variable-length integer specifying the length of the Token field, in bytes.
+It may be zero if no token is present. The server MUST send a zero-length
+token.
Done.
> +| Token Length (i) ...
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Token (*) ...
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~
+
+Token Length:
+
+: A variable-length integer specifying the length of the Token field, in bytes.
+It may be zero if no token is present. The server MUST send a zero-length
+token.
+
+Token:
+
+: An optional token blob previously received in either a Retry packet or
+NEW_TOKEN frame.
I would expect it only on the first one, but leaving the text as-is, as this whole design seems a bit in flux.
> +Token Length:
+
+: A variable-length integer specifying the length of the Token field, in bytes.
+It may be zero if no token is present. The server MUST send a zero-length
+token.
+
+Token:
+
+: An optional token blob previously received in either a Retry packet or
+NEW_TOKEN frame.
+
+The client and server use the Initial packet type for any packet that contains
+an initial cryptographic handshake message. This includes all cases where a new
+packet containing the initial cryptographic message needs to be created, this
+includes the packets sent after receiving a Version Negotiation
+({{packet-version}}) or Retry packet ({{packet-retry}}).
Done.
> +Token:
+
+: An optional token blob previously received in either a Retry packet or
+NEW_TOKEN frame.
+
+The client and server use the Initial packet type for any packet that contains
+an initial cryptographic handshake message. This includes all cases where a new
+packet containing the initial cryptographic message needs to be created, this
+includes the packets sent after receiving a Version Negotiation
+({{packet-version}}) or Retry packet ({{packet-retry}}).
+
+A server sends its first Initial packet in response to a client Initial. A
+server may send multiple Initial packets. The cryptographic key exchange could
+require multiple round trips or retransmissions of this data.
+
+The payload of an Initial packet conveys a CRYPTO_HS frame (or frames)
Done.
> +: An optional token blob previously received in either a Retry packet or
+NEW_TOKEN frame.
+
+The client and server use the Initial packet type for any packet that contains
+an initial cryptographic handshake message. This includes all cases where a new
+packet containing the initial cryptographic message needs to be created, this
+includes the packets sent after receiving a Version Negotiation
+({{packet-version}}) or Retry packet ({{packet-retry}}).
+
+A server sends its first Initial packet in response to a client Initial. A
+server may send multiple Initial packets. The cryptographic key exchange could
+require multiple round trips or retransmissions of this data.
+
+The payload of an Initial packet conveys a CRYPTO_HS frame (or frames)
+containing a cryptographic handshake message. The first CRYPTO_HS frame
+send always begins at an offset of 0 (see {{handshake}}). The client's complete
Done
> +The client and server use the Initial packet type for any packet that contains
+an initial cryptographic handshake message. This includes all cases where a new
+packet containing the initial cryptographic message needs to be created, this
+includes the packets sent after receiving a Version Negotiation
+({{packet-version}}) or Retry packet ({{packet-retry}}).
+
+A server sends its first Initial packet in response to a client Initial. A
+server may send multiple Initial packets. The cryptographic key exchange could
+require multiple round trips or retransmissions of this data.
+
+The payload of an Initial packet conveys a CRYPTO_HS frame (or frames)
+containing a cryptographic handshake message. The first CRYPTO_HS frame
+send always begins at an offset of 0 (see {{handshake}}). The client's complete
+first message MUST fit in a single packet (see {{handshake}}). Note that if
+the server sends a HelloRetryRequest, the client will send a second Initial
+packet with a CRYPTO_HS frame starting immediately after the first one.
Did something here.
> +
+A server sends its first Initial packet in response to a client Initial. A
+server may send multiple Initial packets. The cryptographic key exchange could
+require multiple round trips or retransmissions of this data.
+
+The payload of an Initial packet conveys a CRYPTO_HS frame (or frames)
+containing a cryptographic handshake message. The first CRYPTO_HS frame
+send always begins at an offset of 0 (see {{handshake}}). The client's complete
+first message MUST fit in a single packet (see {{handshake}}). Note that if
+the server sends a HelloRetryRequest, the client will send a second Initial
+packet with a CRYPTO_HS frame starting immediately after the first one.
+
+
+#### Connection IDs
+
+When an an Initial packet is sent by a client which has not previously received
Done.
> +server may send multiple Initial packets. The cryptographic key exchange could
+require multiple round trips or retransmissions of this data.
+
+The payload of an Initial packet conveys a CRYPTO_HS frame (or frames)
+containing a cryptographic handshake message. The first CRYPTO_HS frame
+send always begins at an offset of 0 (see {{handshake}}). The client's complete
+first message MUST fit in a single packet (see {{handshake}}). Note that if
+the server sends a HelloRetryRequest, the client will send a second Initial
+packet with a CRYPTO_HS frame starting immediately after the first one.
+
+
+#### Connection IDs
+
+When an an Initial packet is sent by a client which has not previously received
+a Retry packet from the server, it populates the Destination Connection ID field
+with a randomly selected value. This MUST be at least 8 octets in length. Until
OK, though I see a lot of "random" in this document.
> +
+The payload of an Initial packet conveys a CRYPTO_HS frame (or frames)
+containing a cryptographic handshake message. The first CRYPTO_HS frame
+send always begins at an offset of 0 (see {{handshake}}). The client's complete
+first message MUST fit in a single packet (see {{handshake}}). Note that if
+the server sends a HelloRetryRequest, the client will send a second Initial
+packet with a CRYPTO_HS frame starting immediately after the first one.
+
+
+#### Connection IDs
+
+When an an Initial packet is sent by a client which has not previously received
+a Retry packet from the server, it populates the Destination Connection ID field
+with a randomly selected value. This MUST be at least 8 octets in length. Until
+a packet is received from the server, the client MUST use the same random value
+unless it also changes the Source Connection ID (which effectively starts a new
That's not actually correct, because you can use SCID=""
> +containing a cryptographic handshake message. The first CRYPTO_HS frame
+send always begins at an offset of 0 (see {{handshake}}). The client's complete
+first message MUST fit in a single packet (see {{handshake}}). Note that if
+the server sends a HelloRetryRequest, the client will send a second Initial
+packet with a CRYPTO_HS frame starting immediately after the first one.
+
+
+#### Connection IDs
+
+When an an Initial packet is sent by a client which has not previously received
+a Retry packet from the server, it populates the Destination Connection ID field
+with a randomly selected value. This MUST be at least 8 octets in length. Until
+a packet is received from the server, the client MUST use the same random value
+unless it also changes the Source Connection ID (which effectively starts a new
+connection attempt). The randomized Destination Connection ID is used to
+determine packet protection keys.
Done.
>
The client populates the Source Connection ID field with a value of its choosing
and sets the SCIL field to match.
-The first Initial packet that is sent by a client contains a packet number of 0.
-All subsequent packets contain a packet number that is incremented by at least
-one, see ({{packet-numbers}}).
-
-The payload of an Initial packet conveys a STREAM frame (or frames) for stream
-0 containing a cryptographic handshake message. The stream in this packet
-always starts at an offset of 0 (see {{stateless-retry}}) and the complete
-cryptographic handshake message MUST fit in a single packet (see {{handshake}}).
+The Destination Connection ID field in the server's Initial packet
+contains a connection ID that is chosen by the recipient of the packet
I think "a" is fine here.
>
The client populates the Source Connection ID field with a value of its choosing
and sets the SCIL field to match.
-The first Initial packet that is sent by a client contains a packet number of 0.
-All subsequent packets contain a packet number that is incremented by at least
-one, see ({{packet-numbers}}).
-
-The payload of an Initial packet conveys a STREAM frame (or frames) for stream
-0 containing a cryptographic handshake message. The stream in this packet
-always starts at an offset of 0 (see {{stateless-retry}}) and the complete
-cryptographic handshake message MUST fit in a single packet (see {{handshake}}).
+The Destination Connection ID field in the server's Initial packet
+contains a connection ID that is chosen by the recipient of the packet
+(i.e., the client); the Source Connection ID includes the connection
+ID that the sender of the packet wishes to use (see
+{{connection-id}}). The server MUST use consistent Source Connection
Leaving as-is because in flux.
> -
-The payload of an Initial packet conveys a STREAM frame (or frames) for stream
-0 containing a cryptographic handshake message. The stream in this packet
-always starts at an offset of 0 (see {{stateless-retry}}) and the complete
-cryptographic handshake message MUST fit in a single packet (see {{handshake}}).
+The Destination Connection ID field in the server's Initial packet
+contains a connection ID that is chosen by the recipient of the packet
+(i.e., the client); the Source Connection ID includes the connection
+ID that the sender of the packet wishes to use (see
+{{connection-id}}). The server MUST use consistent Source Connection
+IDs during the handshake.
+
+On first receiving an Initial or Retry packet from the server, the
+client uses the Source Connection ID supplied by the server as the
+Destination Connection ID for subsequent packets. The client MUST
+discard any subsequent packets it receives with different
Done.
> +ID that the sender of the packet wishes to use (see
+{{connection-id}}). The server MUST use consistent Source Connection
+IDs during the handshake.
+
+On first receiving an Initial or Retry packet from the server, the
+client uses the Source Connection ID supplied by the server as the
+Destination Connection ID for subsequent packets. The client MUST
+discard any subsequent packets it receives with different
+Source Connection IDs.
+
+#### Tokens
+
+If the client has a suitable token available from a previous connection,
+it SHOULD populate the Token field.
+
+If the client received a Retry packet from the server and sends an
I'm going to leave it as-is for now.
> +
+#### Tokens
+
+If the client has a suitable token available from a previous connection,
+it SHOULD populate the Token field.
+
+If the client received a Retry packet from the server and sends an
+Initial packet in response, then it sets the Destination Connection ID to
+the value from the Source Connection ID in the Retry packet. Changing
+Destination Connection ID also results in a change to the keys used to
+protect the Initial packet. It also sets the Token field to the
+token provided in the Retry.
+
+When a server receives an Initial packet with an address validation
+token, it SHOULD attempt to validate it. If the token is invalid then
+the server SHOULD proceed as if the client did not have a validated
Leaving b/c this design is in flux.
> +
+If the client received a Retry packet from the server and sends an
+Initial packet in response, then it sets the Destination Connection ID to
+the value from the Source Connection ID in the Retry packet. Changing
+Destination Connection ID also results in a change to the keys used to
+protect the Initial packet. It also sets the Token field to the
+token provided in the Retry.
+
+When a server receives an Initial packet with an address validation
+token, it SHOULD attempt to validate it. If the token is invalid then
+the server SHOULD proceed as if the client did not have a validated
+address, including potentially sending a Retry. If the validation
+succeeds, the server SHOULD then allow the handshake to proceed (see
+{{stateless-retry}}).
+
+Note: The rationale for treating the client as unvalidated rather
Done.
> +address, including potentially sending a Retry. If the validation
+succeeds, the server SHOULD then allow the handshake to proceed (see
+{{stateless-retry}}).
+
+Note: The rationale for treating the client as unvalidated rather
+than discarding the packet is that the client might have received
+the token in a previous connection using the NEW_TOKEN message,
+and if the server has lost state, it might be unable to validate
+the token at all, leading to connection failure if the packet
+is discarded.
+
+#### Starting Packet Numbers
+
+The first Initial packet contains a packet number of 0. Each packet sent after
+the Initial packet is associated with a packet number space and its packet
+number increases monotonically in that space (see {{packet-numbers}}).
Good question. Seems like an open issue.
>
### Retry Packet {#packet-retry}
-A Retry packet uses long headers with a type value of 0x7E. It carries
-cryptographic handshake messages and acknowledgments. It is used by a server
-that wishes to perform a stateless retry (see {{stateless-retry}}).
+A Retry packet uses long headers with a type value of 0x7E. It carries an
+address validation token created by the server. It is used by a server that
+wishes to perform a stateless retry (see {{stateless-retry}}).
+
+~~~
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| DCIL(8) | Original Destination Connection ID (*) |
Done.
>
### Retry Packet {#packet-retry}
-A Retry packet uses long headers with a type value of 0x7E. It carries
-cryptographic handshake messages and acknowledgments. It is used by a server
-that wishes to perform a stateless retry (see {{stateless-retry}}).
+A Retry packet uses long headers with a type value of 0x7E. It carries an
+address validation token created by the server. It is used by a server that
+wishes to perform a stateless retry (see {{stateless-retry}}).
+
+~~~
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| DCIL(8) | Original Destination Connection ID (*) |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Retry Token (*)
Done.
>
-After receiving a Retry packet, the client uses a new
-Initial packet containing the next cryptographic handshake message. The client
-retains the state of its cryptographic handshake, but discards all transport
-state. The Initial packet that is generated in response to a Retry packet
-includes STREAM frames on stream 0 that start again at an offset of 0.
+A server MUST only send a Retry in response to a client Initial packet.
Leaving because in flux.
>
-Continuing the cryptographic handshake is necessary to ensure that an attacker
-cannot force a downgrade of any cryptographic parameters. In addition to
-continuing the cryptographic handshake, the client MUST remember the results of
-any version negotiation that occurred (see {{version-negotiation}}). The client
-MAY also retain any observed RTT or congestion state that it has accumulated for
-the flow, but other transport state MUST be discarded.
+If the Original Destination Connection ID field does not match the
+Destination Connection ID from the Initial packet it sent, clients MUST
+discard the packet. This prevents an off-path attacker from injecting
+a Retry packet with a bogus new Source Connection ID.
I prefer the current text.
>
-Continuing the cryptographic handshake is necessary to ensure that an attacker
-cannot force a downgrade of any cryptographic parameters. In addition to
-continuing the cryptographic handshake, the client MUST remember the results of
-any version negotiation that occurred (see {{version-negotiation}}). The client
-MAY also retain any observed RTT or congestion state that it has accumulated for
-the flow, but other transport state MUST be discarded.
+If the Original Destination Connection ID field does not match the
+Destination Connection ID from the Initial packet it sent, clients MUST
Done.
>
-The payload of the Retry packet contains at least two frames. It MUST include a
-STREAM frame on stream 0 with offset 0 containing the server's cryptographic
-stateless retry material. It MUST also include an ACK frame to acknowledge the
-client's Initial packet. It MAY additionally include PADDING frames. The next
-STREAM frame sent by the server will also start at stream offset 0.
+Otherwise, the client SHOULD respond with a new Initial
+packet with the Token field set to the token received in the Retry packet.
I agree, but leaving because the design is in flux.
>
-If the server expects to generate more than three Handshake packets in response
-to an Initial packet, it SHOULD include a PATH_CHALLENGE frame in each Handshake
-packet that it sends. After receiving at least one valid PATH_RESPONSE frame,
-the server can send its remaining Handshake packets. Servers can instead perform
-address validation using a Retry packet; this requires less state on the server,
-but could involve additional computational effort depending on implementation
-choices.
+Servers MUST NOT send more than three packets including Initial and Handshake
Done.
> @@ -694,7 +777,7 @@ Packets protected with 0-RTT keys use a type value of 0x7C. The connection ID
fields for a 0-RTT packet MUST match the values used in the Initial packet
({{packet-initial}}).
-The client can send 0-RTT packets after receiving a Handshake packet
+The client can send 0-RTT packets after receiving an Initial or Handshake packet
Done
> @@ -761,7 +844,7 @@ During the handshake, an endpoint might receive multiple packets with the long
header, and thus be given multiple opportunities to update the Destination
Connection ID it sends. A client MUST only change the value it sends in the
Destination Connection ID in response to the first packet of each type it
-receives from the server (Retry or Handshake); a server MUST set its value based
+receives from the server (Retry or Initial); a server MUST set its value based
on the Initial packet. Any additional changes are not permitted; if subsequent
Leaving, in flux.
> @@ -792,15 +875,36 @@ response to connection migration ({{migration}}). NEW_CONNECTION_ID frames
The packet number is an integer in the range 0 to 2^62-1. The value is used in
determining the cryptographic nonce for packet encryption. Each endpoint
-maintains a separate packet number for sending and receiving. The packet number
-for sending MUST start at zero for the first packet sent and MUST increase by at
-least one after sending a packet.
+maintains a separate packet number for sending and receiving.
+
+Packet numbers are divided into 3 spaces in QUIC:
+
+- Initial space: All Initial packets {{packet-initial}} are in this space.
+- Handshake space: All Handshake packets {{packet-handshake}} are in this space.
+- Application data space: All 0-RTT and 1-RTT encrypted packets
+ {{packet-protected}} are in this space.
+
+As descibed in {{QUIC-TLS}}, each packet type uses different encryption keys.
Done.
> -least one after sending a packet.
+maintains a separate packet number for sending and receiving.
+
+Packet numbers are divided into 3 spaces in QUIC:
+
+- Initial space: All Initial packets {{packet-initial}} are in this space.
+- Handshake space: All Handshake packets {{packet-handshake}} are in this space.
+- Application data space: All 0-RTT and 1-RTT encrypted packets
+ {{packet-protected}} are in this space.
+
+As descibed in {{QUIC-TLS}}, each packet type uses different encryption keys.
+
+Conceptually, a packet number space is the encryption context in which a packet
+can be processed and ACKed. Initial packets can only be sent with
+Initial encryption keys and ACKed in packets which are also Initial packets.
+Similarly Handshake packets can only be sent and acked in Handshake packets.
Done.
>
-A QUIC endpoint MUST NOT reuse a packet number within the same connection (that
-is, under the same cryptographic keys). If the packet number for sending
-reaches 2^62 - 1, the sender MUST close the connection without sending a
-CONNECTION_CLOSE frame or any further packets; an endpoint MAY send a Stateless
-Reset ({{stateless-reset}}) in response to further packets that it receives.
+This enforces cryptographic separation between the data sent in the
+different packet sequence number spaces. As a result, each packet number space
Done.
> cryptographic handshake protocol is in use.
-QUIC provides this stream with reliable, ordered delivery of data. In return,
-the cryptographic handshake provides QUIC with:
+QUIC provides the cryptographic handshake with reliable, ordered
+delivery of data via the CRYPTO_HS frame. In return, the
Done.
> @@ -1118,19 +1227,83 @@ the cryptographic handshake provides QUIC with:
client can receive packets that are addressed with the transport address that
is claimed by the client (see {{address-validation}})
-The initial cryptographic handshake message MUST be sent in a single packet.
-Any second attempt that is triggered by address validation MUST also be sent
-within a single packet. This avoids having to reassemble a message from
-multiple packets. Reassembling messages requires that a server maintain state
-prior to establishing a connection, exposing the server to a denial of service
-risk.
+The CRYPTO_HS frame provides an offset and a length. QUIC functions
+as the record encryption layer for the cryptographic protocol.
Done,
> @@ -1118,19 +1227,83 @@ the cryptographic handshake provides QUIC with:
client can receive packets that are addressed with the transport address that
is claimed by the client (see {{address-validation}})
-The initial cryptographic handshake message MUST be sent in a single packet.
-Any second attempt that is triggered by address validation MUST also be sent
-within a single packet. This avoids having to reassemble a message from
-multiple packets. Reassembling messages requires that a server maintain state
-prior to establishing a connection, exposing the server to a denial of service
-risk.
+The CRYPTO_HS frame provides an offset and a length. QUIC functions
+as the record encryption layer for the cryptographic protocol.
+
+The initial CRYPTO_HS frame MUST be sent in a single packet. Any
+second attempt that is triggered by address validation MUST also be
+sent within a single packet. This avoids having to reassemble a
+message from multiple packets.
I'm not following, so leaving.
>
The first client packet of the cryptographic handshake protocol MUST fit within
a 1232 octet QUIC packet payload. This includes overheads that reduce the space
available to the cryptographic handshake protocol.
-Details of how TLS is integrated with QUIC is provided in more detail in
-{{QUIC-TLS}}.
+The CRYPTO_HS frame can be sent in different packet number spaces.
+CRYPTO_HS frames in each packet number space carry a separate sequence
+of handshake data starting from an offset of 0.
I think I'm happier where it is, so I'm going to leave as-is and let you realize your vision when you want ti.
>
The first client packet of the cryptographic handshake protocol MUST fit within
a 1232 octet QUIC packet payload. This includes overheads that reduce the space
available to the cryptographic handshake protocol.
-Details of how TLS is integrated with QUIC is provided in more detail in
-{{QUIC-TLS}}.
+The CRYPTO_HS frame can be sent in different packet number spaces.
+CRYPTO_HS frames in each packet number space carry a separate sequence
+of handshake data starting from an offset of 0.
+
+## Example Handshake Flows
+
+Details of how TLS is integrated with QUIC are provided in {{QUIC-TLS}},
+but we provide some examples here.
Done
> +
+## Example Handshake Flows
+
+Details of how TLS is integrated with QUIC are provided in {{QUIC-TLS}},
+but we provide some examples here.
+
+{{tls-1rtt-handshake}} provides an overview of the 1-RTT handshake.
+Each line shows a QUIC packet with the packet type and packet
+number shown first, followed by the contents. So, for instance
+the first packet is of type Initial, with packet number 0, and
+contains a CRYPTO_HS frame carrying the ClientHello.
+
+Note that multiple QUIC packets -- even of different encryption levels
+-- may be coalesced into a single UDP datagram (see
+{{packet-coalesce}}, and so this handshake may consist of anywhere
+from 4 to 9 UDP datagrams. For instance, the server's first flight
Done.
> +to increment from its 0-RTT packets.
+
+~~~~
+Client Server
+
+Initial[0]: CRYPTO_HS[CH]
+0-RTT[0]: STREAM[0, "..."] ->
+
+ Initial[0]: CRYPTO_HS[SH] ACK[0]
+ Handshake[0] CRYPTO_HS[EE, CERT, CV, FIN]
+ <- 1-RTT[0]: STREAM[1, "..."] ACK[0]
+
+Initial[1]: ACK[0]
+0-RTT[1]: CRYPTO_HS[EOED]
+Handshake[0]: CRYPTO_HS[FIN], ACK[0]
+1-RTT[2]: STREAM[0, "..."], ACK[0] ->
Thanks. This was just a cut and paste error.
> connection state MUST use the Retry packet ({{packet-retry}}). This packet
-causes a client to reset its transport state and to continue the connection
-attempt with new connection state while maintaining the state of the
-cryptographic handshake.
-
-A server MUST NOT send multiple Retry packets in response to a client handshake
-packet. Thus, any cryptographic handshake message that is sent MUST fit within
-a single packet.
-
-In TLS, the Retry packet type is used to carry the HelloRetryRequest message.
+causes a client to restart the connection attempt and includes the token in the
I don't know what you mean here.
> @@ -1459,68 +1623,69 @@ To send additional data prior to completing the cryptographic handshake, the
server then needs to validate that the client owns the address that it claims.
Source address validation is therefore performed during the establishment of a
-connection. TLS provides the tools that support the feature, but basic
-validation is performed by the core transport protocol.
+connection, by the core transport protocol.
Done.
> -port), a timestamp, and any other supplementary information the server will need
-to validate the token in the future.
-
-The cryptographic handshake is responsible for enacting validation by sending
-the address validation token to the client. A legitimate client will include a
-copy of the token when it attempts to continue the handshake. The cryptographic
-handshake extracts the token then asks QUIC a second time whether the token is
-acceptable. In response, QUIC can either abort the connection or permit it to
-proceed.
+QUIC uses token-based address validation. Any time the server wishes
+to validate a client address, it provides the client with a token. As
+long as the token's authenticity can be checked (see
+{{token-integrity}}) and the client is able to return that token, it
+proves to the server that it received the token.
+
+If QUIC decides to request address validation for the current
Did a slightly more aggressive rewrite.
> +long as the token's authenticity can be checked (see
+{{token-integrity}}) and the client is able to return that token, it
+proves to the server that it received the token.
+
+If QUIC decides to request address validation for the current
+handshake, it encodes the token in a Retry packet. The contents of
+this token are consumed by the server that generates the token, so
+there is no need for a single well-defined format. A token could
+include information about the claimed client address (IP and port), a
+timestamp, and any other supplementary information the server will
+need to validate the token in the future.
+
+The Retry packet is sent to the client and a legitimate client will
+respond with an Initial packet containing the token from the Retry packet
+when it continues the handshake. In response to receiving the token, a
+QUIC server can either abort the connection or permit it to proceed.
Done.
>
A connection MAY be accepted without address validation - or with only limited
validation - but a server SHOULD limit the data it sends toward an unvalidated
address. Successful completion of the cryptographic handshake implicitly
provides proof that the client has received packets from the server.
+The client should allow for additional Retry packets being sent in
+response to Initial packets sent containing a token. There are several
+situations in which the server might not be able to use the previously
+generated token to validate the client's address and must send a new
+Retry. A reasonable limit to the number of tries the client allows
+for, before giving up, is 3. That is, the client SHOULD echo the
+address validation token from a new Retry packet up to 3 times. After
+that, it MAY give up on the connection attempt.
Done, but we need to work out the bigger design.
>
A server MAY provide clients with an address validation token during one
connection that can be used on a subsequent connection. Address validation is
especially important with 0-RTT because a server potentially sends a significant
amount of data to a client in response to 0-RTT data.
-A different type of token is needed when resuming. Unlike the token that is
-created during a handshake, there might be some time between when the token is
-created and when the token is subsequently used. Thus, a resumption token
-SHOULD include an expiration time. It is also unlikely that the client port
-number is the same on two different connections; validating the port is
+The server uses the NEW_TOKEN frame {{frame-new-token}} to provide the client
+with an address validation token that can be used to validate future 0-RTT
+connections. The client may then use this token to validate future 0-RTT
Done.
>
A server MAY provide clients with an address validation token during one
connection that can be used on a subsequent connection. Address validation is
especially important with 0-RTT because a server potentially sends a significant
amount of data to a client in response to 0-RTT data.
-A different type of token is needed when resuming. Unlike the token that is
-created during a handshake, there might be some time between when the token is
-created and when the token is subsequently used. Thus, a resumption token
-SHOULD include an expiration time. It is also unlikely that the client port
-number is the same on two different connections; validating the port is
+The server uses the NEW_TOKEN frame {{frame-new-token}} to provide the client
+with an address validation token that can be used to validate future 0-RTT
+connections. The client may then use this token to validate future 0-RTT
+connections by including it in the Initial packet's header. The client MUST
+NOT use the token provided in a RETRY for future connections.
Done.
>
A server MAY provide clients with an address validation token during one
connection that can be used on a subsequent connection. Address validation is
especially important with 0-RTT because a server potentially sends a significant
amount of data to a client in response to 0-RTT data.
-A different type of token is needed when resuming. Unlike the token that is
-created during a handshake, there might be some time between when the token is
-created and when the token is subsequently used. Thus, a resumption token
-SHOULD include an expiration time. It is also unlikely that the client port
-number is the same on two different connections; validating the port is
+The server uses the NEW_TOKEN frame {{frame-new-token}} to provide the client
+with an address validation token that can be used to validate future 0-RTT
+connections. The client may then use this token to validate future 0-RTT
+connections by including it in the Initial packet's header. The client MUST
+NOT use the token provided in a RETRY for future connections.
+
+Unlike the token that is created for a Retry packet, there might be some time
+between when the token is created and when the token is subsequently used.
+Thus, a resumption token SHOULD include an expiration time. The server may
MAY
> @@ -2676,6 +2832,12 @@ QUIC acknowledgements are irrevocable. Once acknowledged, a packet remains
acknowledged, even if it does not appear in a future ACK frame. This is unlike
TCP SACKs ({{?RFC2018}}).
+It is expected that a sender will reuse the same packet number across different
+packet number spaces. ACK frames only acknowledge the packet numbers that were
+transmitted by the sender in the same packet number space of the packet that the
+ACK was received in. This is an important property to allow for separation
+between the different packet number spaces.
Done,
> @@ -2839,6 +3001,9 @@ is only sending ACK frames will only receive acknowledgements for its packets
if the sender includes them in packets with non-ACK frames. A sender SHOULD
bundle ACK frames with other frames when possible.
+Implementations must be aware of the packet number space of the packet being
+transmitted and only acknowledge packets from that space.
Done.
> @@ -2849,43 +3014,19 @@ received packets in preference to packets received in the past.
### ACK Frames and Packet Protection
-ACK frames that acknowledge protected packets MUST be carried in a packet that
-has an equivalent or greater level of packet protection.
-
-Packets that are protected with 1-RTT keys MUST be acknowledged in packets that
-are also protected with 1-RTT keys.
-
-A packet that is not protected and claims to acknowledge a packet number that
-was sent with packet protection is not valid. An unprotected packet that
-carries acknowledgments for protected packets MUST be discarded in its entirety.
+ACK frames MUST only be carried in a packet that has the same packet
It actually is an interoperability requirement, because if you inadvertantly cross-ACK, you will have failures.
> -messages in the next unprotected packet that it sends, unless it is able to
-acknowledge those packets in later packets protected by 1-RTT keys. At the
-completion of the cryptographic handshake, both peers send unprotected packets
-containing cryptographic handshake messages followed by packets protected by
-1-RTT keys. An endpoint SHOULD acknowledge the unprotected packets that complete
-the cryptographic handshake in a protected packet, because its peer is
-guaranteed to have access to 1-RTT packet protection keys.
-
-For instance, a server acknowledges a TLS ClientHello in the packet that carries
-the TLS ServerHello; similarly, a client can acknowledge a TLS HelloRetryRequest
-in the packet containing a second TLS ClientHello. The complete set of server
-handshake messages (TLS ServerHello through to Finished) might be acknowledged
-by a client in protected packets, because it is certain that the server is able
-to decipher the packet.
-
+Implementations SHOULD aggressively ACK packets containing CRYPTO_HS frames.
Done.
> @@ -2929,6 +3070,34 @@ PATH_CHALLENGE frame previously sent by the endpoint, the endpoint MAY generate
a connection error of type UNSOLICITED_PATH_RESPONSE.
+## NEW_TOKEN frame {#frame-new-token}
+
+An server sends a NEW_TOKEN frame (type=0x21) to provide the client a token to
Done.
> @@ -2929,6 +3070,34 @@ PATH_CHALLENGE frame previously sent by the endpoint, the endpoint MAY generate
a connection error of type UNSOLICITED_PATH_RESPONSE.
+## NEW_TOKEN frame {#frame-new-token}
+
+An server sends a NEW_TOKEN frame (type=0x21) to provide the client a token to
+send in a the header of an Initial packet for a future 0-RTT connection.
Done.
> @@ -2929,6 +3070,34 @@ PATH_CHALLENGE frame previously sent by the endpoint, the endpoint MAY generate
a connection error of type UNSOLICITED_PATH_RESPONSE.
+## NEW_TOKEN frame {#frame-new-token}
+
+An server sends a NEW_TOKEN frame (type=0x21) to provide the client a token to
+send in a the header of an Initial packet for a future 0-RTT connection.
+
+The NEW_TOKEN frame is as follows:
+
+~~~
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Token Length (i) ...
Done.
> +
+Offset:
+
+: A variable-length integer specifying the byte offset in the stream for the
+ data in this CRYPTO_HS frame.
+
+Length:
+
+: A variable-length integer specifying the length of the Crypto Data field in
+ this CRYPTO_HS frame.
+
+Crypto Data:
+
+: The cryptographic message data.
+
+There is a separate flow of cryptographic handshake data in each
Added above.
> +: A variable-length integer specifying the length of the Crypto Data field in
+ this CRYPTO_HS frame.
+
+Crypto Data:
+
+: The cryptographic message data.
+
+There is a separate flow of cryptographic handshake data in each
+encryption level, each of which starts at an offset of 0. This implies
+that each encryption level is treated as a separate CRYPTO_HS stream
+of data.
+
+Unlike STREAM frames, which include a Stream ID indicating to which
+stream the data belongs, the CRYPTO_HS frame carries data for a single
+stream per encryption level. The stream does not have an explicit
+end, so CRYPTO_HS frames do not have a FIN bit.
Hence the text above that forbids stuff extending past the end.
> +that each encryption level is treated as a separate CRYPTO_HS stream
+of data.
+
+Unlike STREAM frames, which include a Stream ID indicating to which
+stream the data belongs, the CRYPTO_HS frame carries data for a single
+stream per encryption level. The stream does not have an explicit
+end, so CRYPTO_HS frames do not have a FIN bit.
+
+
+## CRYPTO_CLOSE Frame {#frame-crypto-close}
+
+The CRYPTO_CLOSE frame (type=0x20) is used to indicate connection failures
+caused by the crypto handshake. It uses the same format as the
+CONNECTION_CLOSE frame ({{frame-connection-close}}), except that the
+error codes are specific to the crypto protocol in use. For TLS 1.3,
+the error code is simply the one-byte TLS alert number.
Sent as left-padded for now. I know MT wants to argue about this whole structure.
> @@ -3064,6 +3293,10 @@ been lost. In general, information is sent again when a packet containing that
information is determined to be lost and sending ceases when a packet
containing that information is acknowledged.
+* Data sent in CRYPTO_HS frames are retransmitted according to the rules in
+ {{QUIC-RECOVERY}}, until either all data has been ACKed or the crypto state
Done.
> @@ -3299,8 +3532,7 @@ The two type bits from a Stream ID therefore identify streams as summarized in
| 0x3 | Server-Initiated, Unidirectional |
{: #stream-id-types title="Stream ID Types"}
-Stream ID 0 (0x0) is a client-initiated, bidirectional stream that is used for
-the cryptographic handshake. Stream 0 MUST NOT be used for application data.
+The first bi-directional stream opened by the client is stream 0.
Left as-is.
> @@ -3893,6 +4102,14 @@ errors is not mandatory, but only because requiring that an endpoint generate
these errors also means that the endpoint needs to maintain the final offset
state for closed streams, which could mean a significant state commitment.
+## Flow control of CRYPTO_HS data {#flow-control-crypto}
+
+Data sent in CRYPTO_HS frames is not flow controlled in the same way as STREAM
+frames. QUIC relies on the cryptographic protocol implementation to avoid
+excessive buffering of data, see {{QUIC-TLS}}. The implementation SHOULD
+provide an interface to QUIC to tell it about its buffering limits so that there
+is no excessive buffering at multiple layers.
Done.
> @@ -4313,6 +4531,15 @@ the range from 0xFE00 to 0xFFFF.
Issue and pull request numbers are listed with a leading octothorp.
+## Since draft-ietf-quic-transport-12
+
+- Enable server to transition connections to a preferred address (#560,#1251).
+- No more stream 0.
Done,
--
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/pull/1450#pullrequestreview-130932728
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Kazuho Oku
- Re: [quicwg/base-drafts] Stream0 dt output for me… Christian Huitema
- Re: [quicwg/base-drafts] Stream0 dt output for me… ianswett
- Re: [quicwg/base-drafts] Stream0 dt output for me… ianswett
- Re: [quicwg/base-drafts] Stream0 dt output for me… Martin Thomson
- Re: [quicwg/base-drafts] Stream0 dt output for me… Martin Thomson
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Martin Thomson
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… Mike Bishop
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… Rui Paulo
- Re: [quicwg/base-drafts] Stream0 dt output for me… ianswett
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… ianswett
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Mike Bishop
- Re: [quicwg/base-drafts] Stream0 dt output for me… Mike Bishop
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Martin Thomson
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Martin Thomson
- Re: [quicwg/base-drafts] Stream0 dt output for me… Martin Thomson
- Re: [quicwg/base-drafts] Stream0 dt output for me… ianswett
- Re: [quicwg/base-drafts] Stream0 dt output for me… Martin Thomson
- Re: [quicwg/base-drafts] Stream0 dt output for me… Martin Thomson
- Re: [quicwg/base-drafts] Stream0 dt output for me… Kazuho Oku
- Re: [quicwg/base-drafts] Stream0 dt output for me… Kazuho Oku
- Re: [quicwg/base-drafts] Stream0 dt output for me… Mike Bishop
- Re: [quicwg/base-drafts] Stream0 dt output for me… Mike Bishop
- Re: [quicwg/base-drafts] Stream0 dt output for me… Nick Banks
- Re: [quicwg/base-drafts] Stream0 dt output for me… Nick Banks
- Re: [quicwg/base-drafts] Stream0 dt output for me… ekr
- Re: [quicwg/base-drafts] Stream0 dt output for me… Christopher Wood
- [quicwg/base-drafts] Stream0 dt output for merge … ekr