-
Notifications
You must be signed in to change notification settings - Fork 205
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
Simplify version negotiation #2133
Conversation
Can you summarise how this differ from the previous failed attempt at this? |
Well, I'm not sure I would say the previous version failed, but in any case, the previous version had two separate mechanisms, one for "compatible" versions and one for "incompatible". This is just the "compatible" piece (with some slightly different mechanisms). See above for my argument for why we don't need the "incompatible" piece. |
I think this may be premature, for the same reasons as the previous proposal was. This will be useful once we start working on QUICv2, but by then odds are we will want to do version negotiation differently, for example to define how some QUICv1 features map to their QUICv2 counterparts. Once we know what that looks like, we'll be able to add you new client transport parameter as an extension. I don't think there's much value in saving one RTT between draft versions (which can be achieved via Alt-Svc) so I still think punting this to when we are closer to QUICv2 is best. |
@DavidSchinazi the problem is that we already have a version negotiation mechanism that is much more complicated than this one, and this is a net simplification. So, if your argument was that we should remove VN entirely, that would be different, but if we're going to have some VN, then it might as well be a simpler one. |
The current VN mechanism has the property of being part of the protocol invariants, whereas the new one only works if the server can parse CRYPTO frames and a TLS 1.3 client hello. Requiring all future versions to be able to parse past versions' transport parameters does not sound simple to me. |
I must say that I was not fond of the positive/negative list juggling in the previous approach, and the potential for missing attack vectors or making implementation mistakses, but I also do find current vneg complex. What is the thinking on downgrade attacks? Consider babyalarm / widely deployed IoT device offering 3 versions A, B, and C. Later it turns out version B is broken. Compromised MITM device plays valid endpoint and selects version B. Real server cannot ignore version B because MITM got in the way. Not sure the current state is much better on this account? I good thing about client publishing versions is that a constrained device can offer versions in order of preference rather than conservatively choosing what is likely supported. Likewise an advanced client could prefer unlikely new version, but offer the common version also. Resolving preferences becomes a bit more involved when the server also has a preference that is not necessarily aligned with the clients. For example servers may prefer AES acceleration over a ChaCha only version (I know TLS is also negotiating crypto - but why, really?). |
This feels like doubling down on the previous idea of mutually-intelligible "compatible" versions -- now you're essentially saying that, while QUIC has stated invariants, there are walled gardens of version sets within those invariants which are all QUIC but are not straightforward to use together. That's trading potential implementation simplification for deployment and maintenance complexity, isn't it? I agree that this is a simplification by reducing an axis of movement. I remain unconvinced that we can guarantee all versions of QUIC used for a given scenario will be "compatible." |
The current VN mechanism has the property of being part of the protocol
invariants, whereas the new one only works if the server can parse CRYPTO
frames and a TLS 1.3 client hello. Requiring all future versions to be able
to parse past versions' transport parameters does not sound simple to me.
As I said in my initial message, that's *not* the property that it
requires. Rather, it requires that all versions that can inter-negotiate be
able to parse each others parameters. It's quite possible with this design
to have two entirely different lines of QUIC versions, one of which has
CRYPTO frames and TLS 1.3, and one of has something entirely different. Can
you describe a practical situation in which this would not be sufficient?
|
On Wed, Dec 12, 2018 at 2:38 PM Mike Bishop ***@***.***> wrote:
This feels like doubling down on the previous idea of
mutually-intelligible "compatible" versions
Yes. My argument is that this is basically the situation we have had with
every TCP-using protocol forever. Nobody thinks you can send an HTTP
request to a SSH server and have that work, but we do expect SSH clients
and servers to be able to negotiate with each other despite some diversity
of versions.
-- now you're essentially saying that, while QUIC has stated invariants,
there are walled gardens of version sets within those invariants which are
all QUIC but are not straightforward to use together.
I view the invariants as primarily for the benefit of intermediaries, not
for the endpoints.
That's trading potential implementation simplification for deployment and
maintenance complexity, isn't it?
I agree that this is a simplification by reducing an axis of movement. I
remain unconvinced that we can guarantee all versions of QUIC used for a
given scenario will be "compatible."
I have two answers to this:
1. The invariant you need to retain isn't that all versions are compatible
but that the client never has to guess whether to send an Initial of type A
or B because the server might not understand A but might understand B (or
vice versa). I've been trying to think of a non-hypothetical situation in
which this might arise that can't be easily dealt with by retaining compat
of the Initial. Do you have one?
2. If, despite the above, we run into a situation where we have that, we
can always implement something like the existing VN mechanism at that point.
|
Having VN out of invariants makes it possible to improve VN in the future, for example if some unforeseen vulnerability is found. |
Except the equivalent of having QUIC version failure is an inability to do a TCP handshake, which does currently work between unrelated applications. However, I think you're right that the larger point is:
To some extent, Google's use of QUIC Crypto is an existence proof. There are servers which speak only QUIC Crypto, and there are servers which speak only TLS-based iQUIC. Even if QUIC Crypto is extinct by the time we ship this draft, the fact that the situation has arisen once suggests that it could again. "Easily dealt with" is pretty dismissive of not only being able to parse old versions' framing, but being able to parlay the handshake contents into a different handshake protocol. |
"Easily dealt with" is pretty dismissive of not only being able to parse
old versions' framing, but being able to parlay the handshake contents into
a different handshake protocol.
I'm not saying that. I'm saying that if we ever want to have totally
incompatible VN, we can design a mechanism much like what is in the current
draft.
|
Then your point (2) was bleeding into (1). I don't disagree that we can, but then we have both client-selects and server-selects models, and I don't think our net complexity has reduced. If we're going to start with one and build the other if/when we need it, let's start with the more general-purpose solution. |
I slightly prefer this to the existing VN mechanism for the reasons outlined above. Its simplicity is a particularly appealing property. Moreover, absent a specific case where we will need to deal with complete incompatibility, which I don't foresee happening, the current generality seems to be a case of YAGNI. (That said, I recognize its relation to the invariants may be concerning. And also I am wrong a lot.) |
This seems like a proposal that needs to go to the list. |
Acknowledged. I will write a note to the list and we can discuss there. |
FWIW, I see two inflexibilities in the Initial packet of QUIC v1 that might make us want to introduce an incompatible version in the future:
The first point is what prohibits I-D: Authenticated Handshake for QUIC from using an Initial packet format that is compatible with version 1. In case we decide to allow "downgrade" from using authenticated Initials to pure-v1, we need incompatible version negotiation. |
I've had a few conversations with @ekr and other folks on this topic and here is a proposal I think might gain consensus: we define version negotiation in QUICv1 as a a non-recoverable error:
|
How would you return that error when there is no established connection to close and no common error format. |
@DavidSchinazi I think I might oppose to the idea due to the following reasons.
This means that clients that prefer a newer version of QUIC cannot connect to a v1 server, unless it uses an Initial packet that is compatible with that of v1. However, as I have pointed out in The other issue is that we'd no longer be able to grease the version field. |
I don't believe that this is correct. In David's proposal, this would work as follows. In v1, we would just specify the VN/fail mechanism. So, if the client sends a v2 Initial, you get VN. However, in v2 we would specify a response similar to that in QUIC currently, namely that the client chooses the new version and then comes back with an Initial. So, if the client sends v2 to a v1-only server, they get VN and fall back to v1, with no change to the v1 server. Note that this does not have an anti-downgrade mechanism, but we can add one retroactively: v2 clients send supported_versions and v2 servers have to check it, so if you are downgraded from v2 -> v1 you will detect it. It's also worth noting that even though in principle QUIC now supports this use case, it requires a round trip from the client to the server, so I'm skeptical that clients will do it until the point where they are very unhappy about QUICv1. |
@ekr @DavidSchinazi Thank you for the clarifications. I'm glad to know that we would have VN-packet-based rejection in the new proposal. I'd assume that we would continue to allow (or encourage) clients to grease the servers. So now it seems to me that the core of the proposal is to omit servers from sending Generally speaking, I agree that compatible version upgrade is preferable, not only because it reduces one round-trip, but because it is easier to deploy than VN-packet-based negotiation (see quicwg/ops-drafts#52). If we can anticipate that having only compatible version upgrade would be fine, I think I have no reason to complain against dropping And that's takes me back to wonder about the inflexibility of the v1 Initial packet design. As I've stated, current design is inflexible in sense that it does not have a way to have extensions outside the ClientHello message. The only way for future versions of QUIC to introduce extensions outside of ClientHello, while retaining compatibility with v1, would be to abuse the Token field. They would define extension slots (like the one we have in TLS handshake messages) using the Token field so that additional data and the token can be transmitted. Would we be fine with that? If we think that's too ugly, maybe we should add a |
I would be supportive of an extension mechanism for client-originated Initial packets. |
#2313 was the accepted resolution here. |
Following the discussions in BKK I've been thinking about version
negotiation a bit, and I think we can improve things while also
simplifying them. The TL;DR version of this is that instead of having
the client send any version and the server correct it, the client
offers a list of versions and the server picks one. This makes
things a lot simpler at the cost of some unnecessary flexibility.
DETAILS
The basic insight is that the current VN process is complicated
because it's got an axis of flexibility we don't need, namely allowing
the client to send an Initial packet with no idea whatsoever of what
the server supports. This means that you have to be able to have the
server blindly generate a VN no matter what the client sends, which
has the following negative consequences:
It burns up a round trip if the client's initial version is not
the one the server wants, even if the client and server are
basically compatible.
We want it to be stateless, so we have this clumsy validation
procedure.
What I propose is a simpler approach, which is pretty much what
all other other IETF protocols that do version negotiation do:
The client sends an Initial packet using a version which is
acceptable to every server for a given application (e.g., Web,
e-mail, etc.). This packet contains a list of the versions
the client supports.
The server responds with its preferred version out of this list.
If the client's Initial packet is totally incomprehensible to
the server, then you get a hard failure (and we repurpose
and simplify VN for this).
This has the following advantages:
All version negotiation happens in one round trip.
Negotiation validation is mostly a matter of checking that the
server-selected version is acceptable [0].
The main cost is that the client needs to be able to safely send an
Initial that can be read by the server, which means that it needs to
agree with the server on Initial format, though this can span multiple
QUIC versions.
Importantly, this doesn't preclude having totally incompatible
versions of QUIC (e.g., with a different CRYPTO handshake). You can
obviously have two disjoint sets of clients and servers that support
QUIC X and QUIC Y. Additionally, the case where you have a server
which supports A and B and clients which either support A or B works
fine. The client's version appears in the header and the server can
demux on that. So, it's not like we're promising that every version of
QUIC has the same CRYPTO values, just that the client knows enough
about the server to know an Initial format it will accept.
What won't work is where the client has no knowledge of the server's
capabilities at all, specifically where (a) the client supports
incompatible versions A and B and (b) the server might support only A
or only B and the client doesn't know which one. It's not clear how
this could happen in practice; it's like the client wanting to speak
either TLS or SSH and not knowing which one the server speaks [1].
[0] You also want to put the server's selected version in the crypto
handshake, so you need to check that the inner and outer versions
match.
[1] If we ever did want this, we could always design it as an
extension later, presumably cribbing from the current VN design.