]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/tls: don't select TLS 1.2 cipher suites in prior versions.
authorAdam Langley <agl@golang.org>
Thu, 26 Sep 2013 21:09:56 +0000 (17:09 -0400)
committerAdam Langley <agl@golang.org>
Thu, 26 Sep 2013 21:09:56 +0000 (17:09 -0400)
AES-GCM cipher suites are only defined for TLS 1.2, although there's
nothing really version specific about them. However, development
versions of NSS (meaning Firefox and Chrome) have an issue where
they'll advertise TLS 1.2-only cipher suites in a TLS 1.1 ClientHello
but then balk when the server selects one.

This change causes Go clients not to advertise TLS 1.2 cipher suites
unless TLS 1.2 is being used, and prevents servers from selecting them
unless TLS 1.2 has been negotiated.

https://code.google.com/p/chromium/issues/detail?id=297151
https://bugzilla.mozilla.org/show_bug.cgi?id=919677

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/13573047

src/pkg/crypto/tls/cipher_suites.go
src/pkg/crypto/tls/handshake_client.go
src/pkg/crypto/tls/handshake_server.go
src/pkg/crypto/tls/handshake_server_test.go

index 9a95cf82a9213fae63a19396e5a58cbd8022d5ba..39a51459d288fc516248af612c6fabb0a8989258 100644 (file)
@@ -45,6 +45,9 @@ const (
        // certificate is ECDSA. If this is not set then the cipher suite is
        // RSA based.
        suiteECDSA
+       // suiteTLS12 indicates that the cipher suite should only be advertised
+       // and accepted when using TLS 1.2.
+       suiteTLS12
 )
 
 // A cipherSuite is a specific combination of key agreement, cipher and MAC
@@ -66,8 +69,8 @@ type cipherSuite struct {
 var cipherSuites = []*cipherSuite{
        // Ciphersuite order is chosen so that ECDHE comes before plain RSA
        // and RC4 comes before AES (because of the Lucky13 attack).
-       {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE, nil, nil, aeadAESGCM},
-       {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA, nil, nil, aeadAESGCM},
+       {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
+       {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
        {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
        {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherRC4, macSHA1, nil},
        {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
index bfd70a2ecce5ed6dffd2e62ba61849d03cca45c6..9cd57c55f4f4783f2e08e5e74af62bb9621c4f5f 100644 (file)
@@ -23,7 +23,6 @@ func (c *Conn) clientHandshake() error {
 
        hello := &clientHelloMsg{
                vers:               c.config.maxVersion(),
-               cipherSuites:       c.config.cipherSuites(),
                compressionMethods: []uint8{compressionNone},
                random:             make([]byte, 32),
                ocspStapling:       true,
@@ -33,6 +32,25 @@ func (c *Conn) clientHandshake() error {
                nextProtoNeg:       len(c.config.NextProtos) > 0,
        }
 
+       possibleCipherSuites := c.config.cipherSuites()
+       hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
+
+NextCipherSuite:
+       for _, suiteId := range possibleCipherSuites {
+               for _, suite := range cipherSuites {
+                       if suite.id != suiteId {
+                               continue
+                       }
+                       // Don't advertise TLS 1.2-only cipher suites unless
+                       // we're attempting TLS 1.2.
+                       if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
+                               continue
+                       }
+                       hello.cipherSuites = append(hello.cipherSuites, suiteId)
+                       continue NextCipherSuite
+               }
+       }
+
        t := uint32(c.config.time().Unix())
        hello.random[0] = byte(t >> 24)
        hello.random[1] = byte(t >> 16)
index 7c318555ba4f10c8d09d86586602898e22c1d509..1db10f289d2c67f350132701fa7e2ee2a45d977e 100644 (file)
@@ -193,7 +193,7 @@ Curves:
        }
 
        for _, id := range preferenceList {
-               if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil {
+               if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil {
                        break
                }
        }
@@ -234,7 +234,7 @@ func (hs *serverHandshakeState) checkForResumption() bool {
        }
 
        // Check that we also support the ciphersuite from the session.
-       hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.ellipticOk, hs.ecdsaOk)
+       hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers, hs.ellipticOk, hs.ecdsaOk)
        if hs.suite == nil {
                return false
        }
@@ -605,7 +605,7 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (c
 
 // tryCipherSuite returns a cipherSuite with the given id if that cipher suite
 // is acceptable to use.
-func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk, ecdsaOk bool) *cipherSuite {
+func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16, ellipticOk, ecdsaOk bool) *cipherSuite {
        for _, supported := range supportedCipherSuites {
                if id == supported {
                        var candidate *cipherSuite
@@ -627,6 +627,9 @@ func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipti
                        if (candidate.flags&suiteECDSA != 0) != ecdsaOk {
                                continue
                        }
+                       if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 {
+                               continue
+                       }
                        return candidate
                }
        }
index 31bcc785f50a78421b6a7855f3dcabc8efaf8597..c08eba7f17ce91996130ab68bf6083830e4b4840 100644 (file)
@@ -104,6 +104,53 @@ func TestNoCompressionOverlap(t *testing.T) {
        testClientHelloFailure(t, clientHello, alertHandshakeFailure)
 }
 
+func TestTLS12OnlyCipherSuites(t *testing.T) {
+       // Test that a Server doesn't select a TLS 1.2-only cipher suite when
+       // the client negotiates TLS 1.1.
+       var zeros [32]byte
+
+       clientHello := &clientHelloMsg{
+               vers:   VersionTLS11,
+               random: zeros[:],
+               cipherSuites: []uint16{
+                       // The Server, by default, will use the client's
+                       // preference order. So the GCM cipher suite
+                       // will be selected unless it's excluded because
+                       // of the version in this ClientHello.
+                       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+                       TLS_RSA_WITH_RC4_128_SHA,
+               },
+               compressionMethods: []uint8{compressionNone},
+               supportedCurves:    []uint16{curveP256, curveP384, curveP521},
+               supportedPoints:    []uint8{pointFormatUncompressed},
+       }
+
+       c, s := net.Pipe()
+       var reply interface{}
+       var clientErr error
+       go func() {
+               cli := Client(c, testConfig)
+               cli.vers = clientHello.vers
+               cli.writeRecord(recordTypeHandshake, clientHello.marshal())
+               reply, clientErr = cli.readHandshake()
+               c.Close()
+       }()
+       config := *testConfig
+       config.CipherSuites = clientHello.cipherSuites
+       Server(s, &config).Handshake()
+       s.Close()
+       if clientErr != nil {
+               t.Fatal(clientErr)
+       }
+       serverHello, ok := reply.(*serverHelloMsg)
+       if !ok {
+               t.Fatalf("didn't get ServerHello message in reply. Got %v\n", reply)
+       }
+       if s := serverHello.cipherSuite; s != TLS_RSA_WITH_RC4_128_SHA {
+               t.Fatalf("bad cipher suite from server: %x", s)
+       }
+}
+
 func TestAlertForwarding(t *testing.T) {
        c, s := net.Pipe()
        go func() {