diff options
Diffstat (limited to 'src/pkg/crypto/tls/handshake_server.go')
-rw-r--r-- | src/pkg/crypto/tls/handshake_server.go | 121 |
1 files changed, 85 insertions, 36 deletions
diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index 823730c60..c9ccf675c 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -6,9 +6,11 @@ package tls import ( "crypto" + "crypto/ecdsa" "crypto/rsa" "crypto/subtle" "crypto/x509" + "encoding/asn1" "errors" "io" ) @@ -21,10 +23,12 @@ type serverHandshakeState struct { hello *serverHelloMsg suite *cipherSuite ellipticOk bool + ecdsaOk bool sessionState *sessionState finishedHash finishedHash masterSecret []byte certsFromClient [][]byte + cert *Certificate } // serverHandshake performs a TLS handshake as a server. @@ -98,7 +102,7 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { if !ok { return false, c.sendAlert(alertUnexpectedMessage) } - c.vers, ok = mutualVersion(hs.clientHello.vers) + c.vers, ok = config.mutualVersion(hs.clientHello.vers) if !ok { return false, c.sendAlert(alertProtocolVersion) } @@ -156,11 +160,25 @@ Curves: if len(hs.clientHello.serverName) > 0 { c.serverName = hs.clientHello.serverName } - if hs.clientHello.nextProtoNeg { + // Although sending an empty NPN extension is reasonable, Firefox has + // had a bug around this. Best to send nothing at all if + // config.NextProtos is empty. See + // https://code.google.com/p/go/issues/detail?id=5445. + if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 { hs.hello.nextProtoNeg = true 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() { return true, nil } @@ -175,7 +193,7 @@ Curves: } for _, id := range preferenceList { - if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk); hs.suite != nil { + if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil { break } } @@ -199,7 +217,7 @@ func (hs *serverHandshakeState) checkForResumption() bool { if hs.sessionState.vers > hs.clientHello.vers { return false } - if vers, ok := mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers { + if vers, ok := c.config.mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers { return false } @@ -216,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.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers, hs.ellipticOk, hs.ecdsaOk) if hs.suite == nil { return false } @@ -258,15 +276,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { config := hs.c.config c := hs.c - if len(config.Certificates) == 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 { + if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 { hs.hello.ocspStapling = true } @@ -276,20 +286,20 @@ func (hs *serverHandshakeState) doFullHandshake() error { c.writeRecord(recordTypeHandshake, hs.hello.marshal()) certMsg := new(certificateMsg) - certMsg.certificates = cert.Certificate + certMsg.certificates = hs.cert.Certificate hs.finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) if hs.hello.ocspStapling { certStatus := new(certificateStatusMsg) certStatus.statusType = statusTypeOCSP - certStatus.response = cert.OCSPStaple + certStatus.response = hs.cert.OCSPStaple hs.finishedHash.Write(certStatus.marshal()) c.writeRecord(recordTypeHandshake, certStatus.marshal()) } - keyAgreement := hs.suite.ka() - skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello) + keyAgreement := hs.suite.ka(c.vers) + skx, err := keyAgreement.generateServerKeyExchange(config, hs.cert, hs.clientHello, hs.hello) if err != nil { c.sendAlert(alertHandshakeFailure) return err @@ -302,7 +312,14 @@ func (hs *serverHandshakeState) doFullHandshake() error { if config.ClientAuth >= RequestClientCert { // Request a client certificate certReq := new(certificateRequestMsg) - certReq.certificateTypes = []byte{certTypeRSASign} + certReq.certificateTypes = []byte{ + byte(certTypeRSASign), + byte(certTypeECDSASign), + } + if c.vers >= VersionTLS12 { + certReq.hasSignatureAndHash = true + certReq.signatureAndHashes = supportedClientCertSignatureAlgorithms + } // An empty list of certificateAuthorities signals to // the client that it may send any certificate in response @@ -320,7 +337,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { hs.finishedHash.Write(helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal()) - var pub *rsa.PublicKey // public key for client auth, if any + var pub crypto.PublicKey // public key for client auth, if any msg, err := c.readHandshake() if err != nil { @@ -365,7 +382,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { // If we received a client cert in response to our certificate request message, // the client will send us a certificateVerifyMsg immediately after the - // clientKeyExchangeMsg. This message is a MD5SHA1 digest of all preceding + // clientKeyExchangeMsg. This message is a digest of all preceding // handshake-layer messages that is signed using the private key corresponding // to the client's certificate. This allows us to verify that the client is in // possession of the private key of the certificate. @@ -379,10 +396,25 @@ func (hs *serverHandshakeState) doFullHandshake() error { return c.sendAlert(alertUnexpectedMessage) } - digest := make([]byte, 0, 36) - digest = hs.finishedHash.serverMD5.Sum(digest) - digest = hs.finishedHash.serverSHA1.Sum(digest) - err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) + switch key := pub.(type) { + case *ecdsa.PublicKey: + ecdsaSig := new(ecdsaSignature) + if _, err = asn1.Unmarshal(certVerify.signature, ecdsaSig); err != nil { + break + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + err = errors.New("ECDSA signature contained zero or negative values") + break + } + digest, _, _ := hs.finishedHash.hashForClientCertificate(signatureECDSA) + if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) { + err = errors.New("ECDSA verification failure") + break + } + case *rsa.PublicKey: + digest, hashFunc, _ := hs.finishedHash.hashForClientCertificate(signatureRSA) + err = rsa.VerifyPKCS1v15(key, hashFunc, digest, certVerify.signature) + } if err != nil { c.sendAlert(alertBadCertificate) return errors.New("could not validate signature of connection nonces: " + err.Error()) @@ -391,7 +423,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { 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 { c.sendAlert(alertHandshakeFailure) return err @@ -407,12 +439,20 @@ func (hs *serverHandshakeState) establishKeys() error { clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromMasterSecret(c.vers, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) - clientCipher := hs.suite.cipher(clientKey, clientIV, true /* for reading */) - clientHash := hs.suite.mac(c.vers, clientMAC) - c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) + var clientCipher, serverCipher interface{} + var clientHash, serverHash macFunction - serverCipher := hs.suite.cipher(serverKey, serverIV, false /* not for reading */) - serverHash := hs.suite.mac(c.vers, serverMAC) + if hs.suite.aead == nil { + clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */) + clientHash = hs.suite.mac(c.vers, clientMAC) + serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */) + serverHash = hs.suite.mac(c.vers, serverMAC) + } else { + clientCipher = hs.suite.aead(clientKey, clientIV) + serverCipher = hs.suite.aead(serverKey, serverIV) + } + + c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) return nil @@ -502,7 +542,7 @@ func (hs *serverHandshakeState) sendFinished() error { // processCertsFromClient takes a chain of client certificates either from a // Certificates message or from a sessionState and verifies them. It returns // the public key of the leaf certificate. -func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*rsa.PublicKey, error) { +func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) { c := hs.c hs.certsFromClient = certificates @@ -549,8 +589,11 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (* } if len(certs) > 0 { - pub, ok := certs[0].PublicKey.(*rsa.PublicKey) - if !ok { + var pub crypto.PublicKey + switch key := certs[0].PublicKey.(type) { + case *ecdsa.PublicKey, *rsa.PublicKey: + pub = key + default: return nil, c.sendAlert(alertUnsupportedCertificate) } c.peerCertificates = certs @@ -562,7 +605,7 @@ 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, supportedCipherSuites []uint16, ellipticOk 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 @@ -578,7 +621,13 @@ func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipti } // Don't select a ciphersuite which we can't // support for this client. - if candidate.elliptic && !ellipticOk { + if (candidate.flags&suiteECDHE != 0) && !ellipticOk { + continue + } + if (candidate.flags&suiteECDSA != 0) != ecdsaOk { + continue + } + if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 { continue } return candidate |