Open jameshartig opened 2 years ago
For refs, RFC8446:
4.2.1. Supported Versions ...... A server which negotiates a version of TLS prior to TLS 1.3 MUST set ServerHello.version and MUST NOT send the "supported_versions" extension.
This is technically the client so I don't think that applies. But I noticed it does say:
Servers MUST be prepared to receive ClientHellos that include this extension but do not include 0x0304 in the list of versions.
So it appears that Google is in the wrong here since they support TLS 1.2 but don't expect to negotiate 1.2 if that extension is present.
However, if a server doesn't support TLS 1.3, they wouldn't know about this extension.
@rolandshoemaker @golang/security
This looks like server intolerance for the supported_versions
extension in TLS 1.2 in BoringSSL. @davidben @agl can you have a look?
Odd! I can repro it with the script, but I'm pretty sure we test this case with BoringSSL, and it doesn't repro with other servers that use BoringSSL. This may be a bug in some code we have in Google's server. Searching around there...
Ah, I see the issue. It's a combination of sending supported_versions, not including the ECDSA version of those ciphers, but including ECDSA in signature algorithms. (Doesn't look like Go lets you configure that one.) You should probably be including the ECDSA versions anyway, so hopefully that'll resolve it for you? Though I acknowledge the google.com behavior isn't great.
Why this happens is that determining ECDSA support in TLS is bit of a mess and has migrated over time:
The TLS 1.3 behavior is cleaner but the combination of all three modes leaves a bit of a mess: google.com resolves SNI, ECDSA, etc., all at once, when we see the ClientHello. At that point, it's not quite yet known which TLS version we'll be using. So we assume that any client which sends supported_versions is at least new enough for the signature_algorithms extension to be accurate and assume the TLS 1.3 check is sufficient. So, with this ClientHello, we see ecdsa_secp256r1_sha256 in the signature algorithms extension and assume this must be a new enough client to speak ECDSA and configure the ECDSA certificate.
From there, the handshake continues, we negotiate TLS 1.2 via supported_versions, and find there are no acceptable cipher suites for an ECDSA certificate. Thus the handshake fails with handshake_failure. More robust would be for BoringSSL to support configuring both certificates and internally select it, since the selection logic is far too messy to expect a caller to implement.
You should probably be including the ECDSA versions anyway, so hopefully that'll resolve it for you?
Ah that's good to know! I appreciate the through investigation. For this one case, we actually are purposefully not including ECDSA so we can test the RSA certificate we've deployed to a GCP load balancer. We have 2 different configs one that includes ECDSA ciphers and one that doesn't.
Not sure where we go from here. Should we be able to configure the signature algorithms in Go or is this squarely a bug on Google's end and there's no Go change necessary?
Besides Google, do we envision problems with TLS 1.2 servers by sending supported_versions
?
Thank you for the investigation @davidben! The certificate compatibility logic is indeed a major mess, our own implementation was one of the most painful things I had to write.
We could add some logic to not send ECDSA in signature_algorithms
when not sending ECDSA cipher suites and not offering TLS 1.3, but I feel like that's propagating complexity in the wrong direction. I would argue that this is a server bug, as the server is negotiating TLS 1.2 so it should check the intersection of cipher suites, signature algorithms, supported curves, and point compression, like it would if there was no supported_versions
.
As usual, Go doing something a little different is helpful in greasing the ecosystem, if we can afford it :)
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
If you make a TLS 1.2 ClientHello to some servers and include a
supported_versions
extension the server will report back a handshake failure. I was comparing why Go failed butopenssl s_client ... -tls1_2 -cipher 'ECDHE-RSA-AES128-GCM-SHA256'
worked and I figured out that the difference is that Go sends thesupported_versions
and openssl doesn't.It seems that
supported_versions
is a TLS 1.3 extension so we probably shouldn't be including it when the MaxVersion is less than 1.3.Here's a way to reproduce it against google.com:
I realize the above is a contrived example but the fact that Google has this problem (and all GCP load balancers) seems to justify that it's not a random server that's misbehaving.
What did you expect to see?
I expected the program not to panic.
What did you see instead?
Instead I get
panic: Get "https://google.com/": remote error: tls: handshake failure
.If I change
marshal
inhandshake_messages.go
to:Then the above program no longer errors.