Commit 46d4aa27 authored by Filippo Valsorda's avatar Filippo Valsorda

crypto/tls: implement TLS 1.3 downgrade protection

TLS_FALLBACK_SCSV is extremely fragile in the presence of sparse
supported_version, but gave it the best try I could.

Set the server random canaries but don't check them yet, waiting for the
browsers to clear the way of misbehaving middleboxes.

Updates #9671

Change-Id: Ie55efdec671d639cf1e716acef0c5f103e91a7ce
Reviewed-on: https://go-review.googlesource.com/c/147617Reviewed-by: 's avatarAdam Langley <agl@golang.org>
parent 106db71f
......@@ -189,6 +189,14 @@ var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3.
0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
}
const (
// downgradeCanaryTLS12 or downgradeCanaryTLS11 is embedded in the server
// random as a downgrade protection if the server would be capable of
// negotiating a higher version. See RFC 8446, Section 4.1.3.
downgradeCanaryTLS12 = "DOWNGRD\x01"
downgradeCanaryTLS11 = "DOWNGRD\x00"
)
// ConnectionState records basic TLS details about the connection.
type ConnectionState struct {
Version uint16 // TLS version used by the connection (e.g. VersionTLS12)
......@@ -774,6 +782,14 @@ func (c *Config) supportedVersions(isClient bool) []uint16 {
return versions
}
func (c *Config) maxSupportedVersion(isClient bool) uint16 {
supportedVersions := c.supportedVersions(isClient)
if len(supportedVersions) == 0 {
return 0
}
return supportedVersions[0]
}
// supportedVersionsFromMax returns a list of supported versions derived from a
// legacy maximum version value. Note that only versions supported by this
// library are returned. Any newer peer will use supportedVersions anyway.
......
......@@ -309,7 +309,7 @@ func (test *clientTest) run(t *testing.T, write bool) {
// TODO(filippo): regenerate client tests all at once after CL 146217,
// RSA-PSS and client-side TLS 1.3 are landed.
if !write && !strings.Contains(test.name, "TLSv13") {
t.Skip("recorded client tests are out of date")
t.Skip("recorded server tests are out of date")
}
var clientConn, serverConn net.Conn
......
......@@ -210,7 +210,18 @@ Curves:
}
hs.hello.random = make([]byte, 32)
_, err := io.ReadFull(c.config.rand(), hs.hello.random)
serverRandom := hs.hello.random
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion(false)
if maxVers >= VersionTLS12 && c.vers < maxVers {
if c.vers == VersionTLS12 {
copy(serverRandom[24:], downgradeCanaryTLS12)
} else {
copy(serverRandom[24:], downgradeCanaryTLS11)
}
serverRandom = serverRandom[:24]
}
_, err := io.ReadFull(c.config.rand(), serverRandom)
if err != nil {
c.sendAlert(alertInternalError)
return err
......@@ -299,11 +310,10 @@ func (hs *serverHandshakeState) pickCipherSuite() error {
return errors.New("tls: no cipher suite supported by both client and server")
}
// See RFC 7507.
for _, id := range hs.clientHello.cipherSuites {
if id == TLS_FALLBACK_SCSV {
// The client is doing a fallback connection.
if hs.clientHello.vers < c.config.supportedVersions(false)[0] {
// The client is doing a fallback connection. See RFC 7507.
if hs.clientHello.vers < c.config.maxSupportedVersion(false) {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
......
......@@ -622,6 +622,11 @@ func (test *serverTest) loadData() (flows [][]byte, err error) {
}
func (test *serverTest) run(t *testing.T, write bool) {
// TODO(filippo): regenerate server tests all at once.
if !write && !strings.Contains(test.name, "TLSv13") {
t.Skip("recorded client tests are out of date")
}
checkOpenSSLVersion(t)
var clientConn, serverConn net.Conn
......
......@@ -96,6 +96,27 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error {
return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
}
// Abort if the client is doing a fallback and landing lower than what we
// support. See RFC 7507, which however does not specify the interaction
// with supported_versions. The only difference is that with
// supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
// handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
// it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
// TLS 1.2, because a TLS 1.3 server would abort here. The situation before
// supported_versions was not better because there was just no way to do a
// TLS 1.4 handshake without risking the server selecting TLS 1.3.
for _, id := range hs.clientHello.cipherSuites {
if id == TLS_FALLBACK_SCSV {
// Use c.vers instead of max(supported_versions) because an attacker
// could defeat this by adding an arbitrary high version otherwise.
if c.vers < c.config.maxSupportedVersion(false) {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
break
}
}
if len(hs.clientHello.compressionMethods) != 1 ||
hs.clientHello.compressionMethods[0] != compressionNone {
c.sendAlert(alertIllegalParameter)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment