crypto/tls: allow the server to enforce its ciphersuite preferences.
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
This commit is contained in:
parent
ada990689f
commit
9d5e7dd72f
@ -184,6 +184,12 @@ type Config struct {
|
|||||||
// is nil, TLS uses a list of suites supported by the implementation.
|
// is nil, TLS uses a list of suites supported by the implementation.
|
||||||
CipherSuites []uint16
|
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
|
// SessionTicketsDisabled may be set to true to disable session ticket
|
||||||
// (resumption) support.
|
// (resumption) support.
|
||||||
SessionTicketsDisabled bool
|
SessionTicketsDisabled bool
|
||||||
|
@ -180,8 +180,17 @@ Curves:
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, id := range hs.clientHello.cipherSuites {
|
var preferenceList, supportedList []uint16
|
||||||
if hs.suite = c.tryCipherSuite(id, hs.ellipticOk); hs.suite != nil {
|
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
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,7 +231,7 @@ func (hs *serverHandshakeState) checkForResumption() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check that we also support the ciphersuite from the session.
|
// 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 {
|
if hs.suite == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -568,8 +577,8 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*
|
|||||||
|
|
||||||
// tryCipherSuite returns a cipherSuite with the given id if that cipher suite
|
// tryCipherSuite returns a cipherSuite with the given id if that cipher suite
|
||||||
// is acceptable to use.
|
// is acceptable to use.
|
||||||
func (c *Conn) tryCipherSuite(id uint16, ellipticOk bool) *cipherSuite {
|
func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk bool) *cipherSuite {
|
||||||
for _, supported := range c.config.cipherSuites() {
|
for _, supported := range supportedCipherSuites {
|
||||||
if id == supported {
|
if id == supported {
|
||||||
var candidate *cipherSuite
|
var candidate *cipherSuite
|
||||||
|
|
||||||
|
@ -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) {
|
func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config, peers []*x509.Certificate) {
|
||||||
c, s := net.Pipe()
|
c, s := net.Pipe()
|
||||||
srv := Server(s, config)
|
srv := Server(s, config)
|
||||||
|
Loading…
Reference in New Issue
Block a user