From 14accaf7f39c9be6d280a957eb15e12a6778a778 Mon Sep 17 00:00:00 2001 From: "Jacob H. Haven" Date: Thu, 19 Mar 2015 04:01:57 -0700 Subject: [PATCH] crypto/tls: make use of crypto.Signer and crypto.Decrypter This change replaces all direct ECDSA/RSA sign and decrypt operations with calls through the crypto.Signer and crypto.Decrypter interfaces. This is a follow-up to https://go-review.googlesource.com/#/c/3900/ which added crypto.Decrypter and implemented it for RSA. Change-Id: Ie0f3928448b285f329efcd3a93ca3fd5e3b3e42d Reviewed-on: https://go-review.googlesource.com/7804 Reviewed-by: Adam Langley --- common.go | 8 +++--- handshake_server.go | 60 ++++++++++++++++++++++++++++++++++----------- key_agreement.go | 40 ++++++++++++++---------------- 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/common.go b/common.go index 584a361..ec3e997 100644 --- a/common.go +++ b/common.go @@ -489,10 +489,10 @@ func (c *Config) BuildNameToCertificate() { type Certificate struct { Certificate [][]byte // PrivateKey contains the private key corresponding to the public key - // in Leaf. For a server, this must be a *rsa.PrivateKey or - // *ecdsa.PrivateKey. For a client doing client authentication, this - // can be any type that implements crypto.Signer (which includes RSA - // and ECDSA private keys). + // in Leaf. For a server, this must implement either crypto.Decrypter + // (implemented by RSA private keys) or crypto.Signer (which includes + // RSA and ECDSA private keys). For a client doing client authentication, + // this can be any type that implements crypto.Signer. PrivateKey crypto.PrivateKey // OCSPStaple contains an optional OCSP response which will be served // to clients that request it. diff --git a/handshake_server.go b/handshake_server.go index c7c1f1e..8b31c7c 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -25,6 +25,9 @@ type serverHandshakeState struct { suite *cipherSuite ellipticOk bool ecdsaOk bool + rsaOk bool + signOk bool + decryptOk bool sessionState *sessionState finishedHash finishedHash masterSecret []byte @@ -197,7 +200,28 @@ Curves: } } - _, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey) + if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok { + hs.signOk = true + switch priv.Public().(type) { + case *ecdsa.PublicKey: + hs.ecdsaOk = true + case *rsa.PublicKey: + hs.rsaOk = true + default: + c.sendAlert(alertInternalError) + return false, fmt.Errorf("crypto/tls: unsupported signing key type (%T)", priv.Public()) + } + } + if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok { + hs.decryptOk = true + switch priv.Public().(type) { + case *rsa.PublicKey: + hs.rsaOk = true + default: + c.sendAlert(alertInternalError) + return false, fmt.Errorf("crypto/tls: unsupported decryption key type (%T)", priv.Public()) + } + } if hs.checkForResumption() { return true, nil @@ -213,7 +237,7 @@ Curves: } for _, id := range preferenceList { - if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil { + if hs.setCipherSuite(id, supportedList, c.vers) { break } } @@ -272,8 +296,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.sessionState.vers, hs.ellipticOk, hs.ecdsaOk) - if hs.suite == nil { + if !hs.setCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers) { return false } @@ -649,9 +672,10 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (c return nil, nil } -// tryCipherSuite returns a cipherSuite with the given id if that cipher suite -// is acceptable to use. -func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16, ellipticOk, ecdsaOk bool) *cipherSuite { +// setCipherSuite sets a cipherSuite with the given id as the serverHandshakeState +// suite if that cipher suite is acceptable to use. +// It returns a bool indicating if the suite was set. +func (hs *serverHandshakeState) setCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16) bool { for _, supported := range supportedCipherSuites { if id == supported { var candidate *cipherSuite @@ -667,18 +691,26 @@ func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version } // Don't select a ciphersuite which we can't // support for this client. - if (candidate.flags&suiteECDHE != 0) && !ellipticOk { - continue - } - if (candidate.flags&suiteECDSA != 0) != ecdsaOk { + if candidate.flags&suiteECDHE != 0 { + if !hs.ellipticOk || !hs.signOk { + continue + } + if candidate.flags&suiteECDSA != 0 { + if !hs.ecdsaOk { + continue + } + } else if !hs.rsaOk { + continue + } + } else if !hs.decryptOk || !hs.rsaOk { continue } if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 { continue } - return candidate + hs.suite = candidate + return true } } - - return nil + return false } diff --git a/key_agreement.go b/key_agreement.go index 0974fc6..d57d933 100644 --- a/key_agreement.go +++ b/key_agreement.go @@ -31,12 +31,6 @@ func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certif } func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { - preMasterSecret := make([]byte, 48) - _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) - if err != nil { - return nil, err - } - if len(ckx.ciphertext) < 2 { return nil, errClientKeyExchange } @@ -49,8 +43,12 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certifi } ciphertext = ckx.ciphertext[2:] } - - err = rsa.DecryptPKCS1v15SessionKey(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret) + priv, ok := cert.PrivateKey.(crypto.Decrypter) + if !ok { + return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter") + } + // Perform contant time RSA PKCS#1 v1.5 decryption + preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48}) if err != nil { return nil, err } @@ -239,30 +237,30 @@ NextCandidate: if err != nil { return nil, err } + + priv, ok := cert.PrivateKey.(crypto.Signer) + if !ok { + return nil, errors.New("tls: certificate private key does not implement crypto.Signer") + } var sig []byte switch ka.sigType { case signatureECDSA: - privKey, ok := cert.PrivateKey.(*ecdsa.PrivateKey) + _, ok := priv.Public().(*ecdsa.PublicKey) if !ok { - return nil, errors.New("ECDHE ECDSA requires an ECDSA server private key") + return nil, errors.New("ECDHE ECDSA requires an ECDSA server key") } - r, s, err := ecdsa.Sign(config.rand(), privKey, digest) - if err != nil { - return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) - } - sig, err = asn1.Marshal(ecdsaSignature{r, s}) case signatureRSA: - privKey, ok := cert.PrivateKey.(*rsa.PrivateKey) + _, ok := priv.Public().(*rsa.PublicKey) if !ok { - return nil, errors.New("ECDHE RSA requires a RSA server private key") - } - sig, err = rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest) - if err != nil { - return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + return nil, errors.New("ECDHE RSA requires a RSA server key") } default: return nil, errors.New("unknown ECDHE signature algorithm") } + sig, err = priv.Sign(config.rand(), digest, hashFunc) + if err != nil { + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + } skx := new(serverKeyExchangeMsg) sigAndHashLen := 0