-
Notifications
You must be signed in to change notification settings - Fork 203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compatible version upgrade #1901
Changes from 1 commit
4dc5e0d
7483395
f997b10
f12becd
4235f75
0e0df73
48ae25f
87bcb71
847445b
7533873
2b02ccd
34b19da
a2c5c6a
0de8a0c
8ffb306
67a815e
4edf5b3
4417e03
8f69ad1
7c4b04d
270243e
0143d41
bbf55a5
a4f4c9f
9d50417
3f330f6
f0b5080
4578287
aa3ddc1
d59f429
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1169,23 +1169,29 @@ Connection ID fields in a packet that the client sent. If this check fails, the | |
packet MUST be discarded. | ||
|
||
Once the Version Negotiation packet is determined to be valid, the client then | ||
selects an acceptable protocol version from the list provided by the server. | ||
The client then attempts to create a connection using that version. Though the | ||
content of the Initial packet the client sends might not change in response to | ||
version negotiation, a client MUST increase the packet number it uses on every | ||
packet it sends. Packets MUST continue to use long headers ({{long-header}}) | ||
and MUST include the new negotiated protocol version. | ||
selects an acceptable protocol version from the list provided by the server. The | ||
client MUST choose the version that it most prefers from those supported by the | ||
server. The server will abort the connection attempt if the client chooses in a | ||
manner inconsistent from the preference order included with the client transport | ||
parameters (see {{version-validation}}). | ||
|
||
The client then attempts to create a connection using the version it selects. | ||
Though the content of the Initial packet the client sends might not change in | ||
response to version negotiation, a client MUST increase the packet number it | ||
uses on every packet it sends. Packets MUST continue to use long headers | ||
({{long-header}}) and MUST include the new negotiated protocol version. | ||
|
||
The client MUST use the long header format and include its selected version on | ||
all packets until it has 1-RTT keys and it has received a packet from the server | ||
which is not a Version Negotiation packet. | ||
|
||
A client MUST NOT change the version it uses unless it is in response to a | ||
Version Negotiation packet from the server. Once a client receives a packet | ||
from the server which is not a Version Negotiation packet, it MUST discard other | ||
Version Negotiation packets on the same connection. Similarly, a client MUST | ||
ignore a Version Negotiation packet if it has already received and acted on a | ||
Version Negotiation packet. | ||
Version Negotiation packet from the server, or if the server picks a different, | ||
but compatible version (see {{version-upgrade}}). Once a client receives a | ||
packet from the server which is not a Version Negotiation packet, it MUST | ||
discard other Version Negotiation packets on the same connection. Similarly, a | ||
client MUST ignore a Version Negotiation packet if it has already received and | ||
acted on a Version Negotiation packet. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it not theoretically possible that a client (that supports A,B,C) sends version A, and the server supports version {B, C} and responds with a version negotiation packet. THEN the server is updated to support only {C}. The client responds to the VN with B, and then gets a new VN in response, and would want to then try C? It's a pretty crappy experience and extremely small edge case, but I think possible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is exactly the type of edge case I had in mind when talking about #504 (now quicwg/ops-drafts#28). I don't think that we can fix that here, though this compatible version upgrade will make that a lot easier: if B and C are compatible, you can retain enough support for B that you can upgrade from B to C. It's not perfect, because you still have to send Version Negotiation in response to B if the client doesn't support C, but it could get you further in an incremental roll-back scenario. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, in that case the client should abort and possibly make a completely new connection attempt a limited number of times. Otherwise you can get into a loop between two round robin load balanced servers where you never terminate version negotiation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The client is not allowed to respond to version {C} because it is only allow to handle one VN packet (which is important to avoid loops). But as I wrote, a client could make a new connection attempt which amounts to the same thing, so stricly speaking the connection would fail on timeout as the second VN packet is ignored. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generally speaking, a server cluster will indicate a common subset of versions that it supports in VN. When adding support for a new version, each of the server start accepting the new version when found in Initial. After that completes, the next would be to modify each server to start indicating the use of the new version in VN. In case of removing the support for an old version, we will remove that version from the VN list, then stop accepting Initial with that version. |
||
|
||
A client MUST ignore a Version Negotiation packet that lists the client's chosen | ||
version. | ||
|
@@ -1197,9 +1203,8 @@ as a result, see {{retry-0rtt-pn}}. | |
The format of the packet that a client sends might be different in the new | ||
version. In this case, the client generates a new packet that conforms to the | ||
selected version. Though different versions might convey information about | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aren't we requiring the client to always regenerate a packet, now that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh, I missed that one. I squashed the other text, but missed that one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah! It's a gift to proof-readers 👍 |
||
versions differently, a client MUST NOT change either the list of versions it | ||
claims to support. Similarly, a client MUST NOT change what it reports as the | ||
version it first attempted to use. | ||
versions differently, a client MUST send both its first attempted version and | ||
the list of versions it claims to support in preference order. | ||
|
||
Version negotiation packets have no cryptographic protection. Keeping | ||
version-related information consistent between versions is critical for | ||
|
@@ -1210,15 +1215,18 @@ validation of the choice of version (see {{version-validation}}). | |
|
||
The first packet sent by a client uses a QUIC version selected by the client. | ||
In this version of QUIC, this packet also includes transport parameters. Those | ||
transport parameters include a list of QUIC versions the client supports. | ||
transport parameters includes the version the client first attempts to use, plus | ||
martinthomson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
a list of QUIC versions the client supports in the order that the client prefers | ||
them. | ||
|
||
A server that understands the version selected by the client can extract the | ||
list of QUIC versions and select an alternative version from the list of | ||
supported versions. A server MAY choose a compatible version from that list and | ||
continue the handshake with that version. The server sends its first packet as | ||
though it was continuing with the version it selects. | ||
supported versions. A server MAY choose version from that list that is | ||
martinthomson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
compatible with the version selected by the client and continue the handshake | ||
with that version. The server sends its first packet as though it was | ||
continuing with the version it selects. | ||
|
||
A QUIC version is compatible with this version if the cryptographic handshake | ||
A QUIC version is compatible with another version if the cryptographic handshake | ||
message sent in the first packet can be used in both versions. A compatible | ||
version is also able to identify and acknowledge the first packet sent by the | ||
client in some fashion. Other QUIC versions might have different constraints in | ||
|
@@ -1235,6 +1243,10 @@ A server MUST NOT send a Version Negotiation packet if it prefers a version that | |
is not compatible with the version the client initially chose; a server has to | ||
allow the client to choose between versions that are not compatible. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need some text what a client is supposed to do when it receives a VNP that includes a compatible version to the one it initially selected? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. Because the server might not support every compatible version. If the client offers A, where B is compatible, the server can still send Version Negotiation in response with B because it doesn't understand A. |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if the client wants to use a specific other version if possible, but it does not want to do it in-flight within the current connection, for example to keep the client simple? It cannot omit that version from TP because then the current version will continue. It cannot include that version in TP because then it might get in-flight version change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What’s the use case for this? I don’t really see the point of “keeping the client simple” here, this is not one of the complex areas of the protocol. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you implement each version as a separate binary or in a separate process. You could send on the that other version instead and hope the best, but that could result in many unnecessary version negotations if that version is not (yet) widely supported. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you were to implement each version in a different process, then this would not work, yes. Unless you wanted to complicate the hand-off from a simple There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we have some text that says what to do if the server's supported list doesn't overlap at all with the client's? Should it still send a VN, even though it knows the client won't be able to continue the connection? I didn't see any text talking about that case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's caught by the generic version negotiation text. But I note that we don't have that. #1917. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since you wrote this, new text appeared requiring a connection abort if a VN with no overlap is received. If the server has implicit knowledge about what the client supports it doesn't change anything - either it goes forward with a compatible version or it sends a VN packet. |
||
A client MUST abort the connection attempt with a VERSION_NEGOTIATION_ERROR if | ||
the server chooses a compatible version upgrade to the version that the client | ||
first attempted. | ||
|
||
|
||
## Using Reserved Versions | ||
|
||
|
@@ -1438,12 +1450,12 @@ available. Each endpoint validates the value provided by its peer. Successful | |
validation of transport parameters MUST be completed before a connection is | ||
considered successfully established. | ||
|
||
A client also includes a list of supported versions along with its transport | ||
parameters. Transport parameters are carried in the first packet the client | ||
sends. Sending the list of supported versions enables upgrades between | ||
compatible versions, as described in {{version-upgrade}}. These parameters also | ||
enable validation of version negotiation by the server (see | ||
{{version-validation}}). | ||
A client also includes information about the QUIC versions it supports along | ||
with its transport parameters. Transport parameters are carried in the first | ||
packet the client sends. Sending a list of supported versions enables upgrades | ||
between compatible versions, as described in {{version-upgrade}}. These | ||
parameters are used by a server to validate the outcome of version negotiation | ||
(see {{version-validation}}). | ||
|
||
A transport parameter MUST appear at most once in a given transport parameters | ||
extension. An endpoint MUST treat receipt of duplicate transport parameters as | ||
|
@@ -1510,30 +1522,55 @@ version downgrade are possible. In the first, an attacker replaces the QUIC | |
version in the Initial packet. In the second, a fake Version Negotiation packet | ||
is sent by an attacker. | ||
|
||
To protect against these attacks, the transport parameters includes the complete | ||
list of versions that a client is willing to use, with the version it used for | ||
sending the first packet in the first entry. Including this information in the | ||
cryptographic handshake provides it with integrity protection, and allows the | ||
server to detect version downgrade attacks. | ||
To protect against these attacks, the transport parameters includes both the | ||
original version the client uses with its first packet, a complete list of | ||
martinthomson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
versions that a client is willing to use in preference order. Including this | ||
information in the cryptographic handshake provides it with integrity | ||
protection, and allows the server to detect version downgrade attacks. | ||
|
||
The client MUST include the first QUIC version it attempts to use in the | ||
original_version field of its transport parameters. A server MUST abort the | ||
connection attempt with a VERSION_NEGOTIATION_ERROR if it supports the version | ||
in the original_version field and that version does not match the version of the | ||
Initial packet from the client. | ||
|
||
The client MUST include the versions it claims to support in preference order in | ||
the supported_versions field of its transport parameters. A server MUST abort | ||
the connection attempt with a VERSION_NEGOTIATION_ERROR if the Initial packet | ||
from the client contains a version that is less preferred by the client than any | ||
version it supports, unless that version also matches the original_version | ||
field. | ||
|
||
<!-- Editor's note: should I include this algorithm? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 I find it useful. |
||
if original_version == current_version: | ||
return OK # No version negotiation, great. | ||
for v in supported_versions: | ||
if v == current_version: | ||
return OK # Version negotiation, but all good. | ||
if server_supports(v): | ||
return BAD # Downgrade attack attempt thwarted! | ||
return BAD # Client claims it doesn't support this version! | ||
--> | ||
|
||
The client MUST include the first QUIC version it attempts to use as the first | ||
entry of the supported_versions list in the transport parameters. A server MUST | ||
close the connection attempt with a VERSION_NEGOTIATION_ERROR if it supports the | ||
first entry in the list it receives and that version does not match the version | ||
of the QUIC packet. The server MAY choose to upgrade to a compatible version, | ||
as defined in {{version-upgrade}}. | ||
After validating the version parameters from the client, a server MAY choose to | ||
upgrade to a compatible version, as defined in {{version-upgrade}}, even if that | ||
version is less preferred by the client. | ||
|
||
Different QUIC versions might define transport parameters, or their equivalent, | ||
using a different format. However, the version field in the QUIC packet header | ||
is authenticated using transport parameters. The position and the format of the | ||
version fields in transport parameters therefore MUST either be identical across | ||
different QUIC versions, or be unambiguously different to ensure no confusion | ||
about their interpretation. One way that a new format could be introduced is to | ||
define a TLS extension with a different codepoint. | ||
using a different format. However, the negotiated version is validated using | ||
these parameters, so they require that equivalent information is conveyed. | ||
|
||
If a new version uses transport parameters with a similar encoding, the position | ||
and the format of the version fields in transport parameters MUST either be | ||
identical across different QUIC versions, or be unambiguously different to | ||
ensure no confusion about their interpretation. If the same sequence of octets | ||
could be interpreted differently based on the assumed version, this might be | ||
exploited to attack version negotiation. One way that a new format could be | ||
introduced is to define a TLS extension with a different codepoint. | ||
|
||
Transport parameters for QUIC versions that are compatible with this QUIC | ||
version MUST provide transport parameters definitions that retain both a list of | ||
versions the client supports, as well as the one that it first attempts to use. | ||
version MUST provide transport parameters definitions that retain equivalents | ||
for the original_version and supported_versions fields. | ||
|
||
|
||
# Address Validation | ||
|
@@ -3836,6 +3873,7 @@ language from Section 3 of {{!TLS13=RFC8446}}. | |
struct { | ||
select (Handshake.msg_type) { | ||
case client_hello: | ||
QuicVersion original_version; | ||
QuicVersion supported_versions<4..2^8-4>; | ||
}; | ||
TransportParameter parameters<0..2^16-1>; | ||
|
@@ -3854,14 +3892,24 @@ language from Section 3 of {{!TLS13=RFC8446}}. | |
The `extension_data` field of the quic_transport_parameters extension defined in | ||
{{QUIC-TLS}} contains a TransportParameters value. | ||
|
||
A client includes a list of its supported versions in its transport parameters. | ||
This supports validation of the negotiated versions as defined in | ||
{{version-validation}}. | ||
|
||
QUIC encodes transport parameters into a sequence of octets, which are then | ||
included in the cryptographic handshake. | ||
|
||
|
||
## Version Validation Fields | ||
|
||
A client includes the QUIC version it first attempts to connect with in the | ||
original_version field of its transport parameters. | ||
|
||
The supported_versions field is a list of the versions the client supports. | ||
This list MUST be ordered starting with the version the client most prefers and | ||
ending with the least-prefered version. This supports validation of the | ||
negotiated versions as defined in {{version-validation}}. | ||
|
||
Though different QUIC versions might express these values differently, a client | ||
MUST not change these values in response to Version Negotiation. | ||
|
||
|
||
## Transport Parameter Definitions {#transport-parameter-definitions} | ||
|
||
<!-- TODO: reorganize this section --> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this saying that even if the client creates an entirely new connection state it needs to increase the packet number still? I assume that wasn't intended. Perhaps
Though the content of the Initial packet the client sends might not change in response to version negotiation, a client MUST increase the packet number it uses on every packet it sends.
should be changed to something likeIf the content of the Initial packet the client sends does not change in response to version negotiation, a client MUST increase the packet number it uses on every packet it sends.
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll take the suggestion, which is better than what we have. In practice, the packet will probably change - it's a different version of the protocol after all.