]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/tls: allow the server to enforce its ciphersuite preferences.
authorAdam Langley <agl@golang.org>
Tue, 22 Jan 2013 15:10:38 +0000 (10:10 -0500)
committerAdam Langley <agl@golang.org>
Tue, 22 Jan 2013 15:10:38 +0000 (10:10 -0500)
Previously, Go TLS servers always took the client's preferences into
account when selecting a ciphersuite. This change adds the option of
using the server's preferences, which can be expressed by setting
tls.Config.CipherSuites.

This mirrors Apache's SSLHonorCipherOrder directive.

R=golang-dev, nightlyone, bradfitz, ality
CC=golang-dev
https://golang.org/cl/7163043

src/pkg/crypto/tls/common.go
src/pkg/crypto/tls/handshake_server.go
src/pkg/crypto/tls/handshake_server_test.go

index cfe2f2227fb104ec32dd565e736f3d74be50ada2..a888df762db389ba0100189192d4ed70a4caafeb 100644 (file)
@@ -184,6 +184,12 @@ type Config struct {
        // is nil, TLS uses a list of suites supported by the implementation.
        CipherSuites []uint16
 
+       // PreferServerCipherSuites controls whether the server selects the
+       // client's most preferred ciphersuite, or the server's most preferred
+       // ciphersuite. If true then the server's preference, as expressed in
+       // the order of elements in CipherSuites, is used.
+       PreferServerCipherSuites bool
+
        // SessionTicketsDisabled may be set to true to disable session ticket
        // (resumption) support.
        SessionTicketsDisabled bool
index d84103450204c5ca6551247ca382dd4181285cd7..730991016aaed5399365ec23ef8aee7701c2a486 100644 (file)
@@ -180,8 +180,17 @@ Curves:
                return true, nil
        }
 
-       for _, id := range hs.clientHello.cipherSuites {
-               if hs.suite = c.tryCipherSuite(id, hs.ellipticOk); hs.suite != nil {
+       var preferenceList, supportedList []uint16
+       if c.config.PreferServerCipherSuites {
+               preferenceList = c.config.cipherSuites()
+               supportedList = hs.clientHello.cipherSuites
+       } else {
+               preferenceList = hs.clientHello.cipherSuites
+               supportedList = c.config.cipherSuites()
+       }
+
+       for _, id := range preferenceList {
+               if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk); hs.suite != nil {
                        break
                }
        }
@@ -222,7 +231,7 @@ func (hs *serverHandshakeState) checkForResumption() bool {
        }
 
        // Check that we also support the ciphersuite from the session.
-       hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, hs.ellipticOk)
+       hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.ellipticOk)
        if hs.suite == nil {
                return false
        }
@@ -568,8 +577,8 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*
 
 // tryCipherSuite returns a cipherSuite with the given id if that cipher suite
 // is acceptable to use.
-func (c *Conn) tryCipherSuite(id uint16, ellipticOk bool) *cipherSuite {
-       for _, supported := range c.config.cipherSuites() {
+func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk bool) *cipherSuite {
+       for _, supported := range supportedCipherSuites {
                if id == supported {
                        var candidate *cipherSuite
 
index 6d2e28b4023c1078c0508820929f3643b9d1715b..bf8cbe3ae6b91e97e898f966a1fc3e6e21a25fa4 100644 (file)
@@ -125,6 +125,50 @@ func TestClose(t *testing.T) {
        }
 }
 
+func testHandshake(clientConfig, serverConfig *Config) (state ConnectionState, err error) {
+       c, s := net.Pipe()
+       go func() {
+               cli := Client(c, clientConfig)
+               cli.Handshake()
+               c.Close()
+       }()
+       server := Server(s, serverConfig)
+       err = server.Handshake()
+       if err == nil {
+               state = server.ConnectionState()
+       }
+       s.Close()
+       return
+}
+
+func TestCipherSuitePreference(t *testing.T) {
+       serverConfig := &Config{
+               CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+               Certificates: testConfig.Certificates,
+       }
+       clientConfig := &Config{
+               CipherSuites:       []uint16{TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_RC4_128_SHA},
+               InsecureSkipVerify: true,
+       }
+       state, err := testHandshake(clientConfig, serverConfig)
+       if err != nil {
+               t.Fatalf("handshake failed: %s", err)
+       }
+       if state.CipherSuite != TLS_RSA_WITH_AES_128_CBC_SHA {
+               // By default the server should use the client's preference.
+               t.Fatalf("Client's preference was not used, got %x", state.CipherSuite)
+       }
+
+       serverConfig.PreferServerCipherSuites = true
+       state, err = testHandshake(clientConfig, serverConfig)
+       if err != nil {
+               t.Fatalf("handshake failed: %s", err)
+       }
+       if state.CipherSuite != TLS_RSA_WITH_RC4_128_SHA {
+               t.Fatalf("Server's preference was not used, got %x", state.CipherSuite)
+       }
+}
+
 func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config, peers []*x509.Certificate) {
        c, s := net.Pipe()
        srv := Server(s, config)