Subject: [quicwg/base-drafts] Simplify version negotiation (#2133)

From: ekr <[email-masked]>
Date: Wed, 12 Dec 2018 12:44:40 -0800

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.

You can view, comment on, or merge this pull request online at:

  https://github.com/quicwg/base-drafts/pull/2133

-- Commit Summary --

  * Refactor
  * Versions
  * Version error
  * Add vesions back to VE
  * Remove VN from recovery
  * Remove version list from VE

-- File Changes --

    M draft-ietf-quic-recovery.md (12)
    M draft-ietf-quic-transport.md (309)

-- Patch Links --

https://github.com/quicwg/base-drafts/pull/2133.patch
https://github.com/quicwg/base-drafts/pull/2133.diff

-- 
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/2133

---

From: MikkelFJ <[email-masked]>
Date: Wed, 12 Dec 2018 12:52:40 -0800

Can you summarise how this differ from the previous failed attempt at this?

https://github.com/quicwg/base-drafts/pull/1901

-- 
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/2133#issuecomment-446739476

---

From: ekr <[email-masked]>
Date: Wed, 12 Dec 2018 13:30:07 -0800

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.

-- 
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/2133#issuecomment-446751192

---

From: David Schinazi <[email-masked]>
Date: Wed, 12 Dec 2018 14:14:58 -0800

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.

-- 
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/2133#issuecomment-446764639

---

From: ekr <[email-masked]>
Date: Wed, 12 Dec 2018 22:16:15 +0000 (UTC)

@ekr pushed 1 commit.

10bb49aafb4bd160ce9c4acfeee82fc464407b1e  Fix whitespace

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/2133/files/ce1dc114aff8163af2918883062d26f70d698673..10bb49aafb4bd160ce9c4acfeee82fc464407b1e

---

From: ekr <[email-masked]>
Date: Wed, 12 Dec 2018 14:19:24 -0800

@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.

-- 
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/2133#issuecomment-446765814

---

From: David Schinazi <[email-masked]>
Date: Wed, 12 Dec 2018 14:33:30 -0800

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.

-- 
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/2133#issuecomment-446769649

---

From: MikkelFJ <[email-masked]>
Date: Wed, 12 Dec 2018 22:37:12 +0000 (UTC)

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?).

-- 
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/2133#issuecomment-446770619

---

From: Mike Bishop <[email-masked]>
Date: Wed, 12 Dec 2018 14:38:16 -0800

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."

-- 
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/2133#issuecomment-446770917

---

From: ekr <[email-masked]>
Date: Wed, 12 Dec 2018 14:39:47 -0800

> 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?

-- 
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/2133#issuecomment-446771319

---

From: ekr <[email-masked]>
Date: Wed, 12 Dec 2018 14:46:19 -0800

On Wed, Dec 12, 2018 at 2:38 PM Mike Bishop <[email-masked]>
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.

-- 
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/2133#issuecomment-446773113

---

From: MikkelFJ <[email-masked]>
Date: Wed, 12 Dec 2018 14:50:30 -0800

Having VN out of invariants makes it possible to improve VN in the future, for example if some unforeseen vulnerability is found.

-- 
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/2133#issuecomment-446774174

---

From: Mike Bishop <[email-masked]>
Date: Wed, 12 Dec 2018 15:17:16 -0800

> 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.

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:

> 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?

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.

-- 
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/2133#issuecomment-446780845

---

From: ekr <[email-masked]>
Date: Wed, 12 Dec 2018 15:24:08 -0800

"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.

-- 
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/2133#issuecomment-446782388

---

From: Mike Bishop <[email-masked]>
Date: Wed, 12 Dec 2018 15:39:26 -0800

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.

-- 
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/2133#issuecomment-446785758

---

From: Christopher Wood <[email-masked]>
Date: Wed, 12 Dec 2018 16:33:58 -0800

I tend to 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.)

-- 
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/2133#issuecomment-446797838

---

From: Martin Thomson <[email-masked]>
Date: Wed, 12 Dec 2018 22:08:24 -0800

This seems like a proposal that needs to go to the list.

-- 
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/2133#issuecomment-446853498

---

From: ekr <[email-masked]>
Date: Thu, 13 Dec 2018 11:29:32 +0000 (UTC)

Acknowledged. I will write a note to the list and we can discuss there.

-- 
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/2133#issuecomment-446936036

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 13 Dec 2018 18:46:03 -0800

@chris-wood 
> 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.)

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 packet is authenticated using GCM, and it is impossible to introduce another authentication method.
* The only extension point is in the ClientHello extension, which means that the future versions of QUIC cannot signal different information when they retransmit the Initial.

The first point is what prohibits [I-D: Authenticated Handshake for QUIC](https://datatracker.ietf.org/doc/draft-kazuho-quic-authenticated-handshake/) 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.

-- 
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/2133#issuecomment-447194700

---

From: David Schinazi <[email-masked]>
Date: Sat, 22 Dec 2018 03:38:50 -0800

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:
- we keep the invariants as they are today
- in v1 we say that receiving a version negotiation packet is a non-recoverable connection failure
- we remove the list of supported versions from transport parameters in v1
- we punt extra-round-trip-less version negotiation to QUICv2; in other words when we are closer to knowing what QUICv2 looks like we will define a QUICv1 extension and normative text for how to negotiate v1 vs v2 while securing against downgrades

-- 
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/2133#issuecomment-449564252

---

From: MikkelFJ <[email-masked]>
Date: Sat, 22 Dec 2018 03:56:38 -0800

> in v1 we say that receiving a version negotiation packet is a non-recoverable connection failure

How would you return that error when there is no established connection to close and no common error format. 

-- 
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/2133#issuecomment-449565121

---

From: Kazuho Oku <[email-masked]>
Date: Sat, 22 Dec 2018 04:31:32 -0800

@DavidSchinazi I think I might oppose to the idea due to the following reasons.

> * in v1 we say that receiving a version negotiation packet is a non-recoverable connection failure

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 
https://github.com/quicwg/base-drafts/pull/2133#issuecomment-447194700, the Initial packet design of v1 is rather inflexible. I'd assume that there's fair chance that future version of QUIC requiring to use a format that is incompatible from that of v1.

The other issue is that we'd no longer be able to grease the version field.

-- 
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/2133#issuecomment-449566808

---

From: ekr <[email-masked]>
Date: Sat, 22 Dec 2018 06:41:15 -0800

> 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.

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.

-- 
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/2133#issuecomment-449575041

---

From: David Schinazi <[email-masked]>
Date: Sun, 23 Dec 2018 03:05:13 -0800

@kazuho, I agree with @ekr here. The important property is that when a new version of QUIC starts taking shape, that version will define how to VN between it and v1.

-- 
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/2133#issuecomment-449629034

---

From: Kazuho Oku <[email-masked]>
Date: Sun, 23 Dec 2018 16:32:12 -0800

@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 `supported_versions` and clients checking the value. I can understand that the approach works, because checking `supported_versions` is meaningless for a client that only supports one version, and because future versions of the client can interpret the omission of the field as the server supporting only version 1.

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 https://github.com/quicwg/ops-drafts/pull/52).

If we can anticipate that having only compatible version upgrade would be fine, I think I have no reason to complain against dropping `supported_versions` from the QUIC v1.

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](https://github.com/quicwg/base-drafts/pull/2133#issuecomment-447194700). 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 `extensions` field to the client-sent Initial packets, so that future versions of QUIC can use that to contain extensions. QUIC v1 client would set the field to zero-length, and v1 servers would ignore the value of the field.

-- 
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/2133#issuecomment-449672264

---

From: David Schinazi <[email-masked]>
Date: Mon, 24 Dec 2018 03:57:03 -0800

I would be supportive of an extension mechanism for client-originated Initial packets.

-- 
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/2133#issuecomment-449725184