TLSv1.3: Use the supported_versions extension for version negotiation#1808
TLSv1.3: Use the supported_versions extension for version negotiation#1808mattcaswell wants to merge 15 commits intoopenssl:masterfrom
Conversation
Having them approved and merged first usually does the trick ;-) |
ssl/statem/statem_clnt.c
Outdated
There was a problem hiding this comment.
This isn't properly indented, should be aligned with pkt
|
+1 on the commits that belong in this PR, modulo my one comment |
|
There are problems reported by travis. |
|
I suspect the travis problems are inherited from #1807. I just tried to fix those, but then make update failed. I've just tried again, and we'll see if that fixes it. If it does, I'll rebase this one on top of those changes. |
ssl/t1_lib.c
Outdated
There was a problem hiding this comment.
I guess it might be more compatible to not send it, so I think you should just remove that TODO.
include/openssl/tls1.h
Outdated
There was a problem hiding this comment.
Maybe it should be renamed to TLS1_3_VERSION_DRAFT18.
There was a problem hiding this comment.
I guess we never plan to support multiple versions ...
There was a problem hiding this comment.
Yeah, the idea is that when a new draft comes out, I've just got one define to update, and the version is updated everywhere we use it.
ssl/t1_trce.c
Outdated
There was a problem hiding this comment.
I've tried it from the command line using "-trace" and it works - if that's what you mean. There is no trace "test" at the moment.
ssl/statem/statem_clnt.c
Outdated
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
I think it makes sense to always look at this if it's present.
There was a problem hiding this comment.
I had a discussion with @davidben, about this. I'm now erring towards ignoring this if server version <1.3. There are some weird things about this. What happens if we get a ClientHello.version == TLS1.2, but max version in supported versions list is TLS1.1? What should the ClientHello.version be set to when using this extension and not using TLS1.3, and what do we do if the extension and supported_versions list are conflicting in some way?
There was a problem hiding this comment.
The way I look at this is that the if this extension is send, the client is using it to send it's list of supported protocols. If the server doesn't enable TLS 1.3 support, we can still look at what the clients wants.
In your case hello.version == TLS1.2 and TLS 1.1 in the extension, it looks like it's a client that doesn't support TLS 1.2 or 1.3 and we should look at it like one that supports TLS 1.1. We might decide never to send it (and you just removed the TODO), but other clients might decide to always send this extension.
We should either ignore the hello.version in case this extension is present, or insist that it's set to TLS 1.2.
There was a problem hiding this comment.
I guess that by only sending and checking this when TLS 1.3 is enabled, this is only tested when both sides actually support TLS 1.3, so we probably have no good coverage of this in the test suite.
There was a problem hiding this comment.
There is an explicit test for this in 70-test_sslversions.t - see "Test 6"
There was a problem hiding this comment.
Ah, didn't look at that commit yet.
There was a problem hiding this comment.
I think this issue should be raised on the TLS WG email list.
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
Maybe you want PACKET_as_length_prefixed_1() instead?
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
This overrides what we got from the client hello ...
There was a problem hiding this comment.
Yes. If the client is asking for >=TLS1.3 in s->client_version, but they haven't sent supported_versions, then the maximum we negotiate is TLS1.2. That's as per the spec.
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
Should we check that the hello->version == TLS1_2_VERSION if the extension is present?
There was a problem hiding this comment.
I'm not sure, I can't see anything in the spec about what to do in that case. It does say this:
"If the extension is not present, they MUST negotiate TLS 1.2 or prior as specified in [RFC5246], even if ClientHello.legacy_version is 0x0304 or later."
i.e. if hello->version == TLSv1.3, but supported_versions is not present then negotiate TLS1.2.
I think we might encounter implementations which implement <=TLS1.2 but still send supported_versions....it doesn't say anywhere that you shouldn't do that.
In a conversation I had with @davidben he said "As for servers, the intent was that if you see supported_versions, you totally ignore ClientHello.version and evaluate that other list."
There was a problem hiding this comment.
What you quote there is not what we do, we only look at it if we support TLS 1.3 ...
There was a problem hiding this comment.
Anyway, in Client Hello for legacy_version it says: this field MUST be set to 0x0303, which was the version number for TLS 1.2
There was a problem hiding this comment.
Yes, that's what it says must be sent for a TLS1.3 client, but what should happen if a TLS1.1. client sends it? Presumably it should set it to TLS1.1 because many servers will not understand the extension. Therefore I think we have to ignore the legacy client version altogether if supported_versions is present.
There was a problem hiding this comment.
There's another complication. What client_version should we use in the RSA pre-master secret calculation if we end up negotiating <TLS1.3. The one in ClientHello.version or the maximum one in supported_versions? E.g. if ClientHello.version == TLS1.1, but the client sends TLS1.2 in a supported_versions extension...if we take the policy that we ignore ClientHello.version if supported_versions is present, but an RSA ciphersuite is selected which client_version are we then using?
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
Shouldn't this only be compared when best_vers != 0?
There was a problem hiding this comment.
When best_vers == 0, then version_cmp(s, candidate_vers, best_vers) <= 0 will return false, so we don't hit continue...and so we will then set best_vers = candidate_vers which is what we want - so I think this is correct? That might not be right for when we implement DTLSv1.3 I suppose...but we don't get here if we're using DTLS at the moment.
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
method can be NULL, ssl_method_error() can't deal with that.
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
Can you move that ; to the next line?
There was a problem hiding this comment.
and replace the bare semicolon with continue;
ssl/t1_lib.c
Outdated
There was a problem hiding this comment.
Given the comment on the TLS list, It looks like we need to limit this to TLS1_2_VERSION too.
There was a problem hiding this comment.
There doesn't seem consensus on that yet (ekr didn't like it). So I've not done that at the moment.
There was a problem hiding this comment.
Needs a TODO comment to that effect?
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
Given the comment on the TLS list, we should ignore versions smaller than TLS 1.2 here.
There was a problem hiding this comment.
As above - not done yet.
There was a problem hiding this comment.
needs a TODO comment then?
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
I think this check is not good enough to check that TLS 1.3 (or higher) is enabled, it could have been disabled using SSL_OP_NO_TLSv1_3. I don't think we currently have any function that can tell you if a protocol >= TLS1.3 is supported on the server side, it's really what this function is checking itself.
There was a problem hiding this comment.
I've removed this check, so that supported_versions is always looked at if it is present and takes precedence over legacy_version.
In the 'Files changed' view, choose 'Changes from' in the dropdown menu, and choose a commit, or Shift + click to choose a range of commits. |
Oh...that's nice! |
a8dc41d to
65a49da
Compare
|
Various commits added to address the feedback so far. |
|
The supported_versions discussion on the TLS list looks like it might rumble on for a while. For the moment I've just implemented this so that supported_versions is only added as a client if we are prepared to negotiate TLS1.3. From the server side if it is present, we always look at it, and it takes precedence over the legacy_version field. That I think is the closest interpretation of what the spec currently says. If it changes based on the current discussions, we can update it then. |
test/recipes/70-test_sslvertol.t
Outdated
There was a problem hiding this comment.
I would like to see the following tests:
- We send a TLS 1.4 client hello with the supported_versions extension (and get TLS 1.3)
- We send a TLS 1.3 client hello without the supported_versions extension (and get TLS 1.2)
There was a problem hiding this comment.
By some readings if we send a 1.4 client hello with supported_versions extension, we should receive a fatal alert. But I agree that the behavior when receiving such a client hello should be tested.
There was a problem hiding this comment.
@kaduk Errr....no that's definitely not right. Unless you mean if you set the legacy_version to 1.4? Just sending 1.4 inside the supported_versions extension should be fine and negotiate 1.3 (assuming 1.3 is also there). Obviously sending 1.4 inside supported_versions without 1.3 as well (or anything lower) will fail.
There was a problem hiding this comment.
I meant legacy_version of 1.4 ... does "a 1.4 client hello" mean something else? (If it does, how could we send a 1.4 client hello without the supported_versions extension?)
There was a problem hiding this comment.
Is there a reason for this MaxProtocol?
There was a problem hiding this comment.
Not having it causes the tests to fail. The reason is this check in ssl_choose_server_version()
switch (server_version) {
default:
if (version_cmp(s, client_version, s->version) < 0)
return SSL_R_WRONG_SSL_VERSION;
When renegotiating, s->version is set to TLS1.3, but we always set the legacy version to TLS1.2, so this falls over. However, renegotiation is not allowed in TLS1.3. I've just not got as far as actually disabling it yet. So the reneg tests are restricted to only go up to TLS1.2. I've also just added a TODO to remind us to go back and disable TLS1.3 reneg.
ssl/statem/statem_lib.c
Outdated
|
|
||
| method = vent->smeth(); | ||
| if (ssl_method_error(s, method) == 0) { | ||
| if (method != NULL && ssl_method_error(s, method) == 0) { |
There was a problem hiding this comment.
I guess my comment wasn't good enough. It's smeth that can be NULL. You don't want to call smeth()
c27fabb to
a9e07d7
Compare
|
New commits pushed to address the feedback from @kroeckx |
|
Should we have a target in travis that has enable-tls1_3 in it? |
ssl/t1_lib.c
Outdated
There was a problem hiding this comment.
I'm wondering if it's useful to have a function to start a new extension.
There was a problem hiding this comment.
Maybe. Seems a bit over kill to me.
test/recipes/70-test_sslversions.t
Outdated
There was a problem hiding this comment.
I'm unsure how TLS 1.2 is disabled here.
There was a problem hiding this comment.
I guess NO_TLS1_3 is misleading, because it just does TLS 1.0 and 1.1
There was a problem hiding this comment.
I renamed it to TLS1_1_AND_1_0_ONLY
|
So the changes took fine to me (last 11 commits). I looked at the tests, they look fine for me, but don't mark me as having reviewed them. |
|
Commit added to address the feedback above. I also found a couple of spots where we weren't checking |
We can end up with a NULL SSL_METHOD function if a method has been disabled. If that happens then we shouldn't call vent->smeth().
If supported_versions is present it takes precedence.
Replace a bare ";" with "continue;" for the body of a for loop.
Send a TLS1.4 ClientHello with supported_versions and get TLS1.3 Send a TLS1.3 ClientHello without supported_versions and get TLS1.2
Renegotiation does not exist in TLS1.3, so we need to disable it at some point.
1c7ad27 to
a2d7f79
Compare
|
This has now been rebased on latest master. All other PRs that this one is dependant on have now been merged - so all commits are now in scope for review. |
There were a few places where we weren't checking to see if we were using the draft TLS1.3 version or not.
No need to have a supported versions table and a versions table. They should be the same.
a2d7f79 to
e7e66ad
Compare
|
Just spotted a redundant table in the trace code, so I added a commit to fix that. |
ssl/statem/statem_clnt.c
Outdated
| */ | ||
| if (!WPACKET_put_bytes_u16(pkt, s->client_version) | ||
| if (!WPACKET_put_bytes_u16(pkt, | ||
| (!SSL_IS_DTLS(s) |
There was a problem hiding this comment.
break out that second parameter (the nested ternary operator) into a separate variable, for clarity.
ssl/statem/statem_lib.c
Outdated
There was a problem hiding this comment.
needs a TODO comment then?
| s->client_version = candidate_vers; | ||
| if (version_cmp(s, candidate_vers, best_vers) <= 0) | ||
| continue; | ||
| for (vent = table; |
There was a problem hiding this comment.
not for this PR, but we really should come up with a consistent way of walking through tables: OSS_NELEM or a sentinel
ssl/statem/statem_srvr.c
Outdated
|
|
||
| if (!WPACKET_put_bytes_u16(pkt, s->version) | ||
| /* TODO(TLS1.3): Remove the DRAFT conditional before release */ | ||
| if (!WPACKET_put_bytes_u16(pkt, (s->version == TLS1_3_VERSION) |
There was a problem hiding this comment.
again i think a local variable instead of the nested ?: is better, but here it's not as bad, so that's only a PLEASE not a MUST :)
There was a problem hiding this comment.
Done, although this will get removed anyway before final release when we're not having to worry about draft versions anymore.
ssl/t1_lib.c
Outdated
| return 0; | ||
| } | ||
|
|
||
| if (!SSL_IS_DTLS(s) && s->version >= TLS1_3_VERSION) { |
There was a problem hiding this comment.
hm. I think this is the third time I've seen that kind of if expression ... macro?
ssl/t1_lib.c
Outdated
There was a problem hiding this comment.
Needs a TODO comment to that effect?
| {TLS1_2_VERSION, "TLS 1.2"}, | ||
| {TLS1_3_VERSION, "TLS 1.3"}, | ||
| /* TODO(TLS1.3): Remove this line before release */ | ||
| {TLS1_3_VERSION_DRAFT, TLS1_3_VERSION_DRAFT_TXT}, |
There was a problem hiding this comment.
is this the only place the (stupid, we hates them) _TXT is used? Even if not, everything else in this table has the string constants.
There was a problem hiding this comment.
Yes. The reason is so that I have only one place to update if we go from draft 18 to draft 19, i.e. in tls1.h:
/* TODO(TLS1.3) REMOVE ME: Version indicator for draft -18 */
# define TLS1_3_VERSION_DRAFT 0x7f12
# define TLS1_3_VERSION_DRAFT_TXT "TLS 1.3 (draft 18)"
This is temporary anyway. By the time of release we shouldn't be using a draft version and it all gets deleted.
Added some TODOs, refactored a couple of things and added a SSL_IS_TLS13() macro.
|
New commit pushed addressing feedback. |
|
although, now that you have that SSL_IS_TLS13 macro, my request for a local variable isn't as important. :) your call. |
|
Pushed. Thanks. |
Checklist
Description of change
TLSv1.3 does version negotiation differently to <=TL1.2. The ClientHello.version field is now always set to TLSv1.2, and instead we have a new supported_versions extension that lists the version numbers that we are willing to negotiate. This PR adds support for the new extension. We already have some existing test coverage for version neg in test_ssl_new (02-protocol-version.conf). In addition to that I've added some extra tests in the form of a new TLSProxy test.
This builds on all the previous TLSv1.3 PRs, i.e. #1804, #1805 and #1807. Please ignore the first 6 commits in this PR and only review the last 5 commits!!! (I wish we had a way of not showing those...its kind of annoying)