Skip to content
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

Describe a new version negotiation mechanism which allows for #1755

Closed
wants to merge 1 commit into from

Conversation

ekr
Copy link
Collaborator

@ekr ekr commented Sep 17, 2018

no-cost negotiation as long as the client and server support
a common version. It also removes a lot of text.

no-cost negotiation as long as the client and server support
a common version. It also removes a lot of text.
Copy link
Contributor

@ianswett ianswett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the approach, though I want to read it a few more times to make sure nothing from the old text is lost.

  1. Should there be an issue for this? 2) Should this be discussed in NY?

@ekr
Copy link
Collaborator Author

ekr commented Sep 17, 2018

@ianswett: yes, we should discuss in NY.

I will defer to the chairs for the need for an issue.

@kazuho
Copy link
Member

kazuho commented Sep 17, 2018

Am I correct in assuming that resumption will be forbidden when the proposed mechanism is used?

I ask this because if we forbid that, the only things that need to be processed in a different version becomes the Initial packet and the CRYPTO frame.

@ekr
Copy link
Collaborator Author

ekr commented Sep 17, 2018

@kazuho: I would think the client would be allowed to resume but the server would have to reject resumption/0-RTT if it changed version

@RyanTheOptimist
Copy link
Contributor

I think an issue for this would be a good idea.

I'm not sure that I understand what constraints on the two versions are required in order to allow this approach to work. Consider the case where only two versions are supported, A and B. The client sends an INITIAL packet containing the ClientHello (or the like) encoded with A, but indicates that it also supports B. The server and client both prefer B, so the server decide to upgrade to B. Are we guaranteed that everything needed for B will be in the A ClientHello? Could the TLS handshake mapping change between A and B is some way that would cause problems here? What about 0-RTT data? What if between A and B we changed the stream mapping / numbering? The client would have created (and sent) streams allocated with the logic of A but which would be incorrect for B? It seems really hard to guarantee that there are no such changes between A and B.

I think there are simpler solutions to the problem this PR intends to solve:

  1. Advertise supported versions. For the HTTP/QUIC use case, Alt-Svc already gives us a mechanism to advertise QUIC versions so we're not in a position where the client doesn't know the server's versions.
  2. Use two connections. Start a connection with versions A and send 0-RTT streams. When the client learns that the server supports a preferred version, it should start a new connection using version B, but not send new requests on this connection until/unless the handshake completes. Once that happens, the client should send new requests on B and close A once it become idle. This avoids waiting an RTT to learn about the new version and avoids the complexity of trying to upgrade an existing connection from one version to another.

@kazuho
Copy link
Member

kazuho commented Sep 17, 2018

@ekr

@kazuho: I would think the client would be allowed to resume but the server would have to reject resumption/0-RTT if it changed version

Thank you for the clarification. Makes sense to me.

I like the idea behind the PR; to open the possibility of future versions of QUIC accepting v1 Initial packets without spending extra round-trip. Current approach does not allow that, and that will hinder us from introducing v2.

@kazuho
Copy link
Member

kazuho commented Sep 17, 2018

@RyanatGoogle

I'm not sure that I understand what constraints on the two versions are required in order to allow this approach to work.

FWIW, my assumption is that the only constraint would be that the TLS protocol version needs to be compatible.

Consider the case where you want to send a QUICv1 Initial packet that can be upgraded to v3. From the Initial packet, the v3 server needs to extract the initial flight of the TLS handshake (i.e. ClientHello) and the elements that would have existed in the v3's Initial packet. It is obvious that the TLS handshake transcript can be extracted. The other elements can be transmitted as a TLS ClientHello extension.

to support (most likely the oldest common version).

* A list of versions which the client supports in the
"supported_versions" transport parameter).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing paren


* A packet version which indicates which version it has been encoded
with. This SHOULD be a version which the client expects most servers
to support (most likely the oldest common version).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop the parenthetical here.

If the server understands the packet version -- even if it is not its
most preferred version -- then it deprotects the packet and selects
the most preferred version out of the client's supported versions
list, and responds with that version. This allows a client and server
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aside from sentence construction here, which needs work, I don't understand how you believe this should work. If you believe that a server can continue a connection, how can it continue from an Initial taken from a different version?

@MikeBishop
Copy link
Contributor

I'm not strongly opposed to this as an optimization, but it doesn't feel necessary for QUICv1. You remove a lot of text, but it's adding a second layer of version negotiation -- I would expect that it needs more text, and I'm not convinced the text you removed is obsoleted by adding a second way to negotiate.

@martinthomson
Copy link
Member

The big loser here is the validation of version negotiation. I think that most of that is stuff we want to lose. By having all the client's supported versions available to the server we lose a lot of the complication that the text was addressing. I think that we do need new text to describe the properties we are looking to obtain and how we obtain them, but that should be short. It appears as though the proposed text is a little too lean (the rationale for having transport parameter format stability is gone, for instance).

@kazuho
Copy link
Member

kazuho commented Sep 27, 2018

The big loser here is the validation of version negotiation. I think that most of that is stuff we want to lose. By having all the client's supported versions available to the server we lose a lot of the complication that the text was addressing.

IIUC, the risk here is that if we have a client and server that support both QUIC v1 and vX (a future version of QUIC that does not use TLS), we cannot detect downgrade attack that forces the peers to use v1 instead of vX.

Are we fine with that?

If not, I might prefer retaining the current approach in v1 and consider introducing the proposed approach in v2 (my understanding is that this type of version negotiation can be introduced afterwards, like we introduced supported_versions in TLS 1.3).

@mikkelfj
Copy link
Contributor

Any risk of a downgrade attack sounds like a big loose. Especially if the only concern is to save a few messages during a few hours of deployment of a new server. Otherwise, if it can be done safely, by all means...

The IoT scenario is very real. For example, the author of the new BearSSL is concerned about the cost of TLS 1.3 on constrained devices and believe TLS 1.2 will live on. This will consequently lead to a different version of QUIC for such devices, if he is right.

@ianswett
Copy link
Contributor

Kazuho, I think this approach only works if the server supports both the version the client tries intitially and another version the client offers.

The client can choose to only offer alternate versions it prefers, but does not know if the server supports. I believe that mitigates the downgrade attack? If the client tries v1 and the server only supports v0, the server is going to have to send a VN anyway I think?

@kazuho
Copy link
Member

kazuho commented Sep 28, 2018

@ianswett The case I am concerned is like below.

Client and server supports both v1 and vX.

Client prefers using vX, and sends Initial using vX format. Middlebox intercepts that, and injects a Retry that only allows v1. In response, client sends Initial using v1 format. TP will still include v1 and vX.

Now the server sees that the client supports both v1 and vX, but does not have a clue that the client preferred using vX. Hence the handshake succeeds using v1. At this point, downgrade attack succeeds.

The key observation here is that it is only the client that knows what the initially tried version is. Therefore, we currently require the client to verify if there has been an attempt to downgrade. The proposed change removes that requirement without a replacement.

If we want to let the server select the version, I think that we should require the client to send it's original version number using TP so that the server can determine if there was a downgrade attack.

@ianswett
Copy link
Contributor

nit: Above, you meant the middlebox injects a VN, not Retry, correct?

We could force the client to list the initially chosen version first or last as well, if we don't want another transport parameter.

My thinking was the client would only list other versions it likes as well or better than the version it initially tried. ie: it'd try 1, but also list 2.

In my mind, the question of timing comes down to whether we think this will be helpful to upgrade from draft IETF versions to the 'final' v1 . I'd like to think the frames are not likely to change prior to v1 so that feature won't need to be exercised, but that's probably wishful thinking.

@MikeBishop
Copy link
Contributor

MikeBishop commented Sep 28, 2018

I think we don't want to remove the client's initially-offered version or the server's list of other supported versions -- they're there for valid reasons and are still needed. The client's original version is used for the server to ensure that the VN wasn't injected. The server's other supported versions are useful for the client to remember and pick a more-preferred version next time (especially if that more-preferred version uses a different handshake).

This optimization should strictly add to the VN information in the handshake, with a list of compatible version the server could switch to in its response.

(The suggestion that such compatible versions would include near-final draft versions and the final RFC is definitely intriguing from a deployment standpoint, incidentally.)

{{iana-transport-parameters}}.


### Version Negotiation Validation {#version-validation}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build failure is because you've removed this section without updating the other places in the draft that reference it. (Incidentally, the pre-commit hooks should have blocked you from committing something that doesn't build -- can you run make update to make sure that's installed?)

@mikkelfj
Copy link
Contributor

Guidelines on how to decide which version is preferable? Is that a local configuration? Or list order in TP? An IoT device might be able to speak all versions, but have vastly different preferences from a high powered server.

@MikeBishop
Copy link
Contributor

I think preference is always in the hands of the one who switches, with ability to constrain options on the other party. So I'd frame it as:

  • Client begins with the version it thinks is most likely to be supported
  • Includes a list of compatible versions it considers equally-or-more preferred
  • Server either chooses the most preferred from the set of (used, offered), or sends VN if there's no overlap
  • Client learns from VN or the transport parameters the set of versions the server is offering; if it sees something it likes better, it remembers to offer that in the future.

There's not necessarily an obligation to disclose every version you're capable of speaking; you might only disclose the ones you want a peer to use.

@martinthomson
Copy link
Member

martinthomson commented Sep 28, 2018

If we want to let the server select the version, I think that we should require the client to send it's original version number using TP so that the server can determine if there was a downgrade attack.

This does require that it send its original version number. The client lists all its supported versions. We can - as is customary - insist that the client puts its preferences first. But a consequence of this design is that the server ultimately decides which version is used.

@kazuho
Copy link
Member

kazuho commented Sep 28, 2018

@ianswett

nit: Above, you meant the middlebox injects a VN, not Retry, correct?

Yes. Thank you for noticing that.

My thinking was the client would only list other versions it likes as well or better than the version it initially tried. ie: it'd try 1, but also list 2.

That's certainly an option, but then you cannot detect downgrade attacks that happen between the wire-compatible versions too.

IMO, it is beneficial to define two version negotiation mechanisms (i.e. one uses VN and one uses the proposed method) as independent as possible, and that leads me to think that the client should include all the versions it can use in the TP, regardless of what the interaction using VN was.

In my mind, the question of timing comes down to whether we think this will be helpful to upgrade from draft IETF versions to the 'final' v1 . I'd like to think the frames are not likely to change prior to v1 so that feature won't need to be exercised, but that's probably wishful thinking.

That makes sense. Thank you for pointing that out.

@martinduke
Copy link
Contributor

This is an interesting idea, but it also strikes me as the kind of minor optimization that shouldn't make the cut at this stage in the process. I understand @kazuho 's point that it might ease v2 transition, but the VN code has been stable for a long time and there are lots of things to think about here.

This also puts version choice in the hands of the server, rather than client. I'm not sure how I feel about that. If clients are picking a version based on their resource constraints, then this is a bad thing.

@kazuho
Copy link
Member

kazuho commented Sep 28, 2018

@martinduke FWIW, my view is that the downgrade attack protection in the current VN design is corrupt. I have filed #1810 that explains the issue.

martinthomson added a commit that referenced this pull request Oct 19, 2018
As discussed in #1758, we only need to allow rate limiting.

However, the particular example I draw on will become critical if we
adopt the proposed changes to version negotiation in #1755 (or something
like it), because the only packet a server can confidently respond to
with Version Negotiation in that case is the Initial.  If we adopt an
iteration of #1755, then this text would need to be expanded to explain
that also.

Closes #1758.
martinthomson added a commit that referenced this pull request Oct 24, 2018
This introduces the design that proposed by ekr prior to the NY interim.
In this design, the client advertises the list of versions it supports
in its transport parameters.  The server is permitted to select a
"compatible" version and proceed as though the client selected that
version.

The main advantage of this approach is seamless upgrade between
compatible versions without the extra round-trip imposed by Version
Negotiation.  This will be especially useful when we move from
pre-release versions (0xff0000xx) to the final version.

Closes #1773, #1755.
@martinthomson
Copy link
Member

See #1901 for a less bitrotten version of this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants