crypto/tls: don't select ECDSA ciphersuites with only an RSA certificate.

47ec7a68b1a2 added support for ECDSA ciphersuites but didn't alter the
cipher suite selection to take that into account. Thus Go servers could
try and select an ECDSA cipher suite while only having an RSA
certificate, leading to connection failures.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/13239053
This commit is contained in:
Adam Langley 2013-09-17 13:30:36 -04:00
parent 8eaa99cd77
commit 679f6ca983
3 changed files with 77 additions and 37 deletions

View File

@ -34,6 +34,19 @@ type keyAgreement interface {
generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
} }
const (
// suiteECDH indicates that the cipher suite involves elliptic curve
// Diffie-Hellman. This means that it should only be selected when the
// client indicates that it supports ECC with a curve and point format
// that we're happy with.
suiteECDHE = 1 << iota
// suiteECDSA indicates that the cipher suite involves an ECDSA
// signature and therefore may only be selected when the server's
// certificate is ECDSA. If this is not set then the cipher suite is
// RSA based.
suiteECDSA
)
// A cipherSuite is a specific combination of key agreement, cipher and MAC // A cipherSuite is a specific combination of key agreement, cipher and MAC
// function. All cipher suites currently assume RSA key agreement. // function. All cipher suites currently assume RSA key agreement.
type cipherSuite struct { type cipherSuite struct {
@ -43,31 +56,29 @@ type cipherSuite struct {
macLen int macLen int
ivLen int ivLen int
ka func(version uint16) keyAgreement ka func(version uint16) keyAgreement
// If elliptic is set, a server will only consider this ciphersuite if // flags is a bitmask of the suite* values, above.
// the ClientHello indicated that the client supports an elliptic curve flags int
// and point format that we can handle. cipher func(key, iv []byte, isRead bool) interface{}
elliptic bool mac func(version uint16, macKey []byte) macFunction
cipher func(key, iv []byte, isRead bool) interface{} aead func(key, fixedNonce []byte) cipher.AEAD
mac func(version uint16, macKey []byte) macFunction
aead func(key, fixedNonce []byte) cipher.AEAD
} }
var cipherSuites = []*cipherSuite{ var cipherSuites = []*cipherSuite{
// Ciphersuite order is chosen so that ECDHE comes before plain RSA // Ciphersuite order is chosen so that ECDHE comes before plain RSA
// and RC4 comes before AES (because of the Lucky13 attack). // and RC4 comes before AES (because of the Lucky13 attack).
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, true, nil, nil, aeadAESGCM}, {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, true, nil, nil, aeadAESGCM}, {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1, nil}, {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, true, 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, true, cipherAES, macSHA1, nil}, {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, true, cipherAES, macSHA1, nil}, {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1, nil}, {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, true, cipherAES, macSHA1, nil}, {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1, nil}, {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1, nil}, {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, false, cipherAES, macSHA1, nil}, {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1, nil}, {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1, nil}, {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
} }
func cipherRC4(key, iv []byte, isRead bool) interface{} { func cipherRC4(key, iv []byte, isRead bool) interface{} {

View File

@ -23,10 +23,12 @@ type serverHandshakeState struct {
hello *serverHelloMsg hello *serverHelloMsg
suite *cipherSuite suite *cipherSuite
ellipticOk bool ellipticOk bool
ecdsaOk bool
sessionState *sessionState sessionState *sessionState
finishedHash finishedHash finishedHash finishedHash
masterSecret []byte masterSecret []byte
certsFromClient [][]byte certsFromClient [][]byte
cert *Certificate
} }
// serverHandshake performs a TLS handshake as a server. // serverHandshake performs a TLS handshake as a server.
@ -167,6 +169,16 @@ Curves:
hs.hello.nextProtos = config.NextProtos hs.hello.nextProtos = config.NextProtos
} }
if len(config.Certificates) == 0 {
return false, c.sendAlert(alertInternalError)
}
hs.cert = &config.Certificates[0]
if len(hs.clientHello.serverName) > 0 {
hs.cert = config.getCertificateForName(hs.clientHello.serverName)
}
_, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
if hs.checkForResumption() { if hs.checkForResumption() {
return true, nil return true, nil
} }
@ -181,7 +193,7 @@ Curves:
} }
for _, id := range preferenceList { for _, id := range preferenceList {
if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk); hs.suite != nil { if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil {
break break
} }
} }
@ -222,7 +234,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, c.config.cipherSuites(), hs.ellipticOk) hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.ellipticOk, hs.ecdsaOk)
if hs.suite == nil { if hs.suite == nil {
return false return false
} }
@ -264,15 +276,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
config := hs.c.config config := hs.c.config
c := hs.c c := hs.c
if len(config.Certificates) == 0 { if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
return c.sendAlert(alertInternalError)
}
cert := &config.Certificates[0]
if len(hs.clientHello.serverName) > 0 {
cert = config.getCertificateForName(hs.clientHello.serverName)
}
if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
hs.hello.ocspStapling = true hs.hello.ocspStapling = true
} }
@ -282,20 +286,20 @@ func (hs *serverHandshakeState) doFullHandshake() error {
c.writeRecord(recordTypeHandshake, hs.hello.marshal()) c.writeRecord(recordTypeHandshake, hs.hello.marshal())
certMsg := new(certificateMsg) certMsg := new(certificateMsg)
certMsg.certificates = cert.Certificate certMsg.certificates = hs.cert.Certificate
hs.finishedHash.Write(certMsg.marshal()) hs.finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal())
if hs.hello.ocspStapling { if hs.hello.ocspStapling {
certStatus := new(certificateStatusMsg) certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP certStatus.statusType = statusTypeOCSP
certStatus.response = cert.OCSPStaple certStatus.response = hs.cert.OCSPStaple
hs.finishedHash.Write(certStatus.marshal()) hs.finishedHash.Write(certStatus.marshal())
c.writeRecord(recordTypeHandshake, certStatus.marshal()) c.writeRecord(recordTypeHandshake, certStatus.marshal())
} }
keyAgreement := hs.suite.ka(c.vers) keyAgreement := hs.suite.ka(c.vers)
skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello) skx, err := keyAgreement.generateServerKeyExchange(config, hs.cert, hs.clientHello, hs.hello)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
@ -419,7 +423,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.finishedHash.Write(certVerify.marshal()) hs.finishedHash.Write(certVerify.marshal())
} }
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers) preMasterSecret, err := keyAgreement.processClientKeyExchange(config, hs.cert, ckx, c.vers)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
@ -601,7 +605,7 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (c
// 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, supportedCipherSuites []uint16, ellipticOk bool) *cipherSuite { func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk, ecdsaOk bool) *cipherSuite {
for _, supported := range supportedCipherSuites { for _, supported := range supportedCipherSuites {
if id == supported { if id == supported {
var candidate *cipherSuite var candidate *cipherSuite
@ -617,7 +621,10 @@ func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipti
} }
// Don't select a ciphersuite which we can't // Don't select a ciphersuite which we can't
// support for this client. // support for this client.
if candidate.elliptic && !ellipticOk { if (candidate.flags&suiteECDHE != 0) && !ellipticOk {
continue
}
if (candidate.flags&suiteECDSA != 0) != ecdsaOk {
continue continue
} }
return candidate return candidate

View File

@ -302,6 +302,28 @@ func TestClientAuthECDSA(t *testing.T) {
} }
} }
// TestCipherSuiteCertPreferance ensures that we select an RSA ciphersuite with
// an RSA certificate and an ECDSA ciphersuite with an ECDSA certificate.
func TestCipherSuiteCertPreferance(t *testing.T) {
var config = *testConfig
config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}
config.MaxVersion = VersionTLS11
config.PreferServerCipherSuites = true
testServerScript(t, "CipherSuiteCertPreference", tls11ECDHEAESServerScript, &config, nil)
config = *testConfig
config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}
config.Certificates = []Certificate{
Certificate{
Certificate: [][]byte{testECDSACertificate},
PrivateKey: testECDSAPrivateKey,
},
}
config.BuildNameToCertificate()
config.PreferServerCipherSuites = true
testServerScript(t, "CipherSuiteCertPreference2", ecdheECDSAAESServerScript, &config, nil)
}
func TestTLS11Server(t *testing.T) { func TestTLS11Server(t *testing.T) {
var config = *testConfig var config = *testConfig
config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA} config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}