crypto/tls: implement TLS 1.3 version negotiation
RFC 8446 recommends using the supported_versions extension to negotiate
lower versions as well, so begin by implementing it to negotiate the
currently supported versions.
Note that pickTLSVersion was incorrectly negotiating the ServerHello
version down on the client. If the server had illegally sent a version
higher than the ClientHello version, the client would have just
downgraded it, hopefully failing later in the handshake.
In TestGetConfigForClient, we were hitting the record version check
because the server would select TLS 1.1, the handshake would fail on the
client which required TLS 1.2, which would then send a TLS 1.0 record
header on its fatal alert (not having negotiated a version), while the
server would expect a TLS 1.1 header at that point. Now, the client gets
to communicate the minimum version through the extension and the
handshake fails on the server.
Updates #9671
Change-Id: Ie33c7124c0c769f62e10baad51cbed745c424e5b
Reviewed-on: https://go-review.googlesource.com/c/146217
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go
index 2c916e8..ae793e2d 100644
--- a/src/crypto/tls/handshake_server.go
+++ b/src/crypto/tls/handshake_server.go
@@ -135,14 +135,19 @@
}
}
- c.vers, ok = c.config.mutualVersion(hs.clientHello.vers)
+ clientVersions := hs.clientHello.supportedVersions
+ if len(hs.clientHello.supportedVersions) == 0 {
+ clientVersions = supportedVersionsFromMax(hs.clientHello.vers)
+ }
+ c.vers, ok = c.config.mutualVersion(false, clientVersions)
if !ok {
c.sendAlert(alertProtocolVersion)
- return false, fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers)
+ return false, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
}
c.haveVers = true
hs.hello = new(serverHelloMsg)
+ hs.hello.vers = c.vers
supportedCurve := false
preferredCurves := c.config.curvePreferences()
@@ -179,7 +184,6 @@
return false, errors.New("tls: client does not support uncompressed connections")
}
- hs.hello.vers = c.vers
hs.hello.random = make([]byte, 32)
_, err = io.ReadFull(c.config.rand(), hs.hello.random)
if err != nil {
@@ -272,7 +276,7 @@
for _, id := range hs.clientHello.cipherSuites {
if id == TLS_FALLBACK_SCSV {
// The client is doing a fallback connection.
- if hs.clientHello.vers < c.config.maxVersion() {
+ if hs.clientHello.vers < c.config.supportedVersions(false)[0] {
c.sendAlert(alertInappropriateFallback)
return false, errors.New("tls: client using inappropriate protocol fallback")
}
@@ -762,19 +766,14 @@
return false
}
-// suppVersArray is the backing array of ClientHelloInfo.SupportedVersions
-var suppVersArray = [...]uint16{VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30}
-
func (hs *serverHandshakeState) clientHelloInfo() *ClientHelloInfo {
if hs.cachedClientHelloInfo != nil {
return hs.cachedClientHelloInfo
}
- var supportedVersions []uint16
- if hs.clientHello.vers > VersionTLS12 {
- supportedVersions = suppVersArray[:]
- } else if hs.clientHello.vers >= VersionSSL30 {
- supportedVersions = suppVersArray[VersionTLS12-hs.clientHello.vers:]
+ supportedVersions := hs.clientHello.supportedVersions
+ if len(hs.clientHello.supportedVersions) == 0 {
+ supportedVersions = supportedVersionsFromMax(hs.clientHello.vers)
}
hs.cachedClientHelloInfo = &ClientHelloInfo{