From 4f4a855d82a889cebcfca150a7a43909bcb6a346 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 18 Jan 2019 19:04:36 +0000 Subject: libgo: update to Go1.12beta2 Reviewed-on: https://go-review.googlesource.com/c/158019 gotools/: * Makefile.am (go_cmd_vet_files): Update for Go1.12beta2 release. (GOTOOLS_TEST_TIMEOUT): Increase to 600. (check-runtime): Export LD_LIBRARY_PATH before computing GOARCH and GOOS. (check-vet): Copy golang.org/x/tools into check-vet-dir. * Makefile.in: Regenerate. gcc/testsuite/: * go.go-torture/execute/names-1.go: Stop using debug/xcoff, which is no longer externally visible. From-SVN: r268084 --- libgo/go/crypto/tls/handshake_client.go | 572 ++++++++++++++++++++------------ 1 file changed, 363 insertions(+), 209 deletions(-) (limited to 'libgo/go/crypto/tls/handshake_client.go') diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go index 32fdc6d6eb1..ca74989f6ed 100644 --- a/libgo/go/crypto/tls/handshake_client.go +++ b/libgo/go/crypto/tls/handshake_client.go @@ -18,6 +18,7 @@ import ( "strconv" "strings" "sync/atomic" + "time" ) type clientHandshakeState struct { @@ -30,28 +31,42 @@ type clientHandshakeState struct { session *ClientSessionState } -func makeClientHello(config *Config) (*clientHelloMsg, error) { +func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { + config := c.config if len(config.ServerName) == 0 && !config.InsecureSkipVerify { - return nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") + return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") } nextProtosLength := 0 for _, proto := range config.NextProtos { if l := len(proto); l == 0 || l > 255 { - return nil, errors.New("tls: invalid NextProtos value") + return nil, nil, errors.New("tls: invalid NextProtos value") } else { nextProtosLength += 1 + l } } - if nextProtosLength > 0xffff { - return nil, errors.New("tls: NextProtos values too large") + return nil, nil, errors.New("tls: NextProtos values too large") + } + + supportedVersions := config.supportedVersions(true) + if len(supportedVersions) == 0 { + return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion") + } + + clientHelloVersion := supportedVersions[0] + // The version at the beginning of the ClientHello was capped at TLS 1.2 + // for compatibility reasons. The supported_versions extension is used + // to negotiate versions now. See RFC 8446, Section 4.2.1. + if clientHelloVersion > VersionTLS12 { + clientHelloVersion = VersionTLS12 } hello := &clientHelloMsg{ - vers: config.maxVersion(), + vers: clientHelloVersion, compressionMethods: []uint8{compressionNone}, random: make([]byte, 32), + sessionId: make([]byte, 32), ocspStapling: true, scts: true, serverName: hostnameInSNI(config.ServerName), @@ -60,7 +75,13 @@ func makeClientHello(config *Config) (*clientHelloMsg, error) { nextProtoNeg: len(config.NextProtos) > 0, secureRenegotiationSupported: true, alpnProtocols: config.NextProtos, + supportedVersions: supportedVersions, } + + if c.handshakes > 0 { + hello.secureRenegotiation = c.clientFinished[:] + } + possibleCipherSuites := config.cipherSuites() hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites)) @@ -82,17 +103,39 @@ NextCipherSuite: _, err := io.ReadFull(config.rand(), hello.random) if err != nil { - return nil, errors.New("tls: short read from Rand: " + err.Error()) + return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) + } + + // A random session ID is used to detect when the server accepted a ticket + // and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as + // a compatibility measure (see RFC 8446, Section 4.1.2). + if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil { + return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) } if hello.vers >= VersionTLS12 { hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms } - return hello, nil + var params ecdheParameters + if hello.supportedVersions[0] == VersionTLS13 { + hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13()...) + + curveID := config.curvePreferences()[0] + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") + } + params, err = generateECDHEParameters(config.rand(), curveID) + if err != nil { + return nil, nil, err + } + hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} + } + + return hello, params, nil } -func (c *Conn) clientHandshake() error { +func (c *Conn) clientHandshake() (err error) { if c.config == nil { c.config = defaultConfig() } @@ -101,112 +144,221 @@ func (c *Conn) clientHandshake() error { // need to be reset. c.didResume = false - hello, err := makeClientHello(c.config) + hello, ecdheParams, err := c.makeClientHello() if err != nil { return err } - if c.handshakes > 0 { - hello.secureRenegotiation = c.clientFinished[:] + cacheKey, session, earlySecret, binderKey := c.loadSession(hello) + if cacheKey != "" && session != nil { + defer func() { + // If we got a handshake failure when resuming a session, throw away + // the session ticket. See RFC 5077, Section 3.2. + // + // RFC 8446 makes no mention of dropping tickets on failure, but it + // does require servers to abort on invalid binders, so we need to + // delete tickets to recover from a corrupted PSK. + if err != nil { + c.config.ClientSessionCache.Put(cacheKey, nil) + } + }() } - var session *ClientSessionState - var cacheKey string - sessionCache := c.config.ClientSessionCache - if c.config.SessionTicketsDisabled { - sessionCache = nil + if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil { + return err } - if sessionCache != nil { - hello.ticketSupported = true + msg, err := c.readHandshake() + if err != nil { + return err } - // Session resumption is not allowed if renegotiating because - // renegotiation is primarily used to allow a client to send a client - // certificate, which would be skipped if session resumption occurred. - if sessionCache != nil && c.handshakes == 0 { - // Try to resume a previously negotiated TLS session, if - // available. - cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) - candidateSession, ok := sessionCache.Get(cacheKey) - if ok { - // Check that the ciphersuite/version used for the - // previous session are still valid. - cipherSuiteOk := false - for _, id := range hello.cipherSuites { - if id == candidateSession.cipherSuite { - cipherSuiteOk = true - break - } - } + serverHello, ok := msg.(*serverHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverHello, msg) + } - versOk := candidateSession.vers >= c.config.minVersion() && - candidateSession.vers <= c.config.maxVersion() - if versOk && cipherSuiteOk { - session = candidateSession - } - } + if err := c.pickTLSVersion(serverHello); err != nil { + return err } - if session != nil { - hello.sessionTicket = session.sessionTicket - // A random session ID is used to detect when the - // server accepted the ticket and is resuming a session - // (see RFC 5077). - hello.sessionId = make([]byte, 16) - if _, err := io.ReadFull(c.config.rand(), hello.sessionId); err != nil { - return errors.New("tls: short read from Rand: " + err.Error()) + if c.vers == VersionTLS13 { + hs := &clientHandshakeStateTLS13{ + c: c, + serverHello: serverHello, + hello: hello, + ecdheParams: ecdheParams, + session: session, + earlySecret: earlySecret, + binderKey: binderKey, } + + // In TLS 1.3, session tickets are delivered after the handshake. + return hs.handshake() } hs := &clientHandshakeState{ - c: c, - hello: hello, - session: session, + c: c, + serverHello: serverHello, + hello: hello, + session: session, } - if err = hs.handshake(); err != nil { + if err := hs.handshake(); err != nil { return err } // If we had a successful handshake and hs.session is different from - // the one already cached - cache a new one - if sessionCache != nil && hs.session != nil && session != hs.session { - sessionCache.Put(cacheKey, hs.session) + // the one already cached - cache a new one. + if cacheKey != "" && hs.session != nil && session != hs.session { + c.config.ClientSessionCache.Put(cacheKey, hs.session) } return nil } -// Does the handshake, either a full one or resumes old session. -// Requires hs.c, hs.hello, and, optionally, hs.session to be set. -func (hs *clientHandshakeState) handshake() error { - c := hs.c +func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + session *ClientSessionState, earlySecret, binderKey []byte) { + if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { + return "", nil, nil, nil + } - // send ClientHello - if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { - return err + hello.ticketSupported = true + + if hello.supportedVersions[0] == VersionTLS13 { + // Require DHE on resumption as it guarantees forward secrecy against + // compromise of the session ticket key. See RFC 8446, Section 4.2.9. + hello.pskModes = []uint8{pskModeDHE} } - msg, err := c.readHandshake() - if err != nil { - return err + // Session resumption is not allowed if renegotiating because + // renegotiation is primarily used to allow a client to send a client + // certificate, which would be skipped if session resumption occurred. + if c.handshakes != 0 { + return "", nil, nil, nil } - var ok bool - if hs.serverHello, ok = msg.(*serverHelloMsg); !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(hs.serverHello, msg) + // Try to resume a previously negotiated TLS session, if available. + cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + session, ok := c.config.ClientSessionCache.Get(cacheKey) + if !ok || session == nil { + return cacheKey, nil, nil, nil } - if err = hs.pickTLSVersion(); err != nil { - return err + // Check that version used for the previous session is still valid. + versOk := false + for _, v := range hello.supportedVersions { + if v == session.vers { + versOk = true + break + } + } + if !versOk { + return cacheKey, nil, nil, nil } - if err = hs.pickCipherSuite(); err != nil { - return err + // Check that the cached server certificate is not expired, and that it's + // valid for the ServerName. This should be ensured by the cache key, but + // protect the application from a faulty ClientSessionCache implementation. + if !c.config.InsecureSkipVerify { + if len(session.verifiedChains) == 0 { + // The original connection had InsecureSkipVerify, while this doesn't. + return cacheKey, nil, nil, nil + } + serverCert := session.serverCertificates[0] + if c.config.time().After(serverCert.NotAfter) { + // Expired certificate, delete the entry. + c.config.ClientSessionCache.Put(cacheKey, nil) + return cacheKey, nil, nil, nil + } + if err := serverCert.VerifyHostname(c.config.ServerName); err != nil { + return cacheKey, nil, nil, nil + } } + if session.vers != VersionTLS13 { + // In TLS 1.2 the cipher suite must match the resumed session. Ensure we + // are still offering it. + if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil { + return cacheKey, nil, nil, nil + } + + hello.sessionTicket = session.sessionTicket + return + } + + // Check that the session ticket is not expired. + if c.config.time().After(session.useBy) { + c.config.ClientSessionCache.Put(cacheKey, nil) + return cacheKey, nil, nil, nil + } + + // In TLS 1.3 the KDF hash must match the resumed session. Ensure we + // offer at least one cipher suite with that hash. + cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite) + if cipherSuite == nil { + return cacheKey, nil, nil, nil + } + cipherSuiteOk := false + for _, offeredID := range hello.cipherSuites { + offeredSuite := cipherSuiteTLS13ByID(offeredID) + if offeredSuite != nil && offeredSuite.hash == cipherSuite.hash { + cipherSuiteOk = true + break + } + } + if !cipherSuiteOk { + return cacheKey, nil, nil, nil + } + + // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1. + ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond) + identity := pskIdentity{ + label: session.sessionTicket, + obfuscatedTicketAge: ticketAge + session.ageAdd, + } + hello.pskIdentities = []pskIdentity{identity} + hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())} + + // Compute the PSK binders. See RFC 8446, Section 4.2.11.2. + psk := cipherSuite.expandLabel(session.masterSecret, "resumption", + session.nonce, cipherSuite.hash.Size()) + earlySecret = cipherSuite.extract(psk, nil) + binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil) + transcript := cipherSuite.hash.New() + transcript.Write(hello.marshalWithoutBinders()) + pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)} + hello.updateBinders(pskBinders) + + return +} + +func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error { + peerVersion := serverHello.vers + if serverHello.supportedVersion != 0 { + peerVersion = serverHello.supportedVersion + } + + vers, ok := c.config.mutualVersion(true, []uint16{peerVersion}) + if !ok { + c.sendAlert(alertProtocolVersion) + return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion) + } + + c.vers = vers + c.haveVers = true + c.in.version = vers + c.out.version = vers + + return nil +} + +// Does the handshake, either a full one or resumes old session. Requires hs.c, +// hs.hello, hs.serverHello, and, optionally, hs.session to be set. +func (hs *clientHandshakeState) handshake() error { + c := hs.c + isResume, err := hs.processServerHello() if err != nil { return err @@ -272,20 +424,6 @@ func (hs *clientHandshakeState) handshake() error { return nil } -func (hs *clientHandshakeState) pickTLSVersion() error { - vers, ok := hs.c.config.mutualVersion(hs.serverHello.vers) - if !ok || vers < VersionTLS10 { - // TLS 1.0 is the minimum version supported as a client. - hs.c.sendAlert(alertProtocolVersion) - return fmt.Errorf("tls: server selected unsupported protocol version %x", hs.serverHello.vers) - } - - hs.c.vers = vers - hs.c.haveVers = true - - return nil -} - func (hs *clientHandshakeState) pickCipherSuite() error { if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil { hs.c.sendAlert(alertHandshakeFailure) @@ -313,53 +451,9 @@ func (hs *clientHandshakeState) doFullHandshake() error { if c.handshakes == 0 { // If this is the first handshake on a connection, process and // (optionally) verify the server's certificates. - certs := make([]*x509.Certificate, len(certMsg.certificates)) - for i, asn1Data := range certMsg.certificates { - cert, err := x509.ParseCertificate(asn1Data) - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to parse certificate from server: " + err.Error()) - } - certs[i] = cert - } - - if !c.config.InsecureSkipVerify { - opts := x509.VerifyOptions{ - Roots: c.config.RootCAs, - CurrentTime: c.config.time(), - DNSName: c.config.ServerName, - Intermediates: x509.NewCertPool(), - } - - for i, cert := range certs { - if i == 0 { - continue - } - opts.Intermediates.AddCert(cert) - } - c.verifiedChains, err = certs[0].Verify(opts) - if err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - if c.config.VerifyPeerCertificate != nil { - if err := c.config.VerifyPeerCertificate(certMsg.certificates, c.verifiedChains); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - switch certs[0].PublicKey.(type) { - case *rsa.PublicKey, *ecdsa.PublicKey: - break - default: - c.sendAlert(alertUnsupportedCertificate) - return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) + if err := c.verifyServerCertificate(certMsg.certificates); err != nil { + return err } - - c.peerCertificates = certs } else { // This is a renegotiation handshake. We require that the // server's identity (i.e. leaf certificate) is unchanged and @@ -393,9 +487,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { } hs.finishedHash.Write(cs.marshal()) - if cs.statusType == statusTypeOCSP { - c.ocspResponse = cs.response - } + c.ocspResponse = cs.response msg, err = c.readHandshake() if err != nil { @@ -427,7 +519,8 @@ func (hs *clientHandshakeState) doFullHandshake() error { certRequested = true hs.finishedHash.Write(certReq.marshal()) - if chainToSend, err = hs.getCertificate(certReq); err != nil { + cri := certificateRequestInfoFromMsg(certReq) + if chainToSend, err = c.getClientCertificate(cri); err != nil { c.sendAlert(alertInternalError) return err } @@ -471,7 +564,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { if chainToSend != nil && len(chainToSend.Certificate) > 0 { certVerify := &certificateVerifyMsg{ - hasSignatureAndHash: c.vers >= VersionTLS12, + hasSignatureAlgorithm: c.vers >= VersionTLS12, } key, ok := chainToSend.PrivateKey.(crypto.Signer) @@ -486,7 +579,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { return err } // SignatureAndHashAlgorithm was introduced in TLS 1.2. - if certVerify.hasSignatureAndHash { + if certVerify.hasSignatureAlgorithm { certVerify.signatureAlgorithm = signatureAlgorithm } digest, err := hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret) @@ -511,7 +604,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { } hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) - if err := c.config.writeKeyLog(hs.hello.random, hs.masterSecret); err != nil { + if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil { c.sendAlert(alertInternalError) return errors.New("tls: failed to write to key log: " + err.Error()) } @@ -553,6 +646,10 @@ func (hs *clientHandshakeState) serverResumedSession() bool { func (hs *clientHandshakeState) processServerHello() (bool, error) { c := hs.c + if err := hs.pickCipherSuite(); err != nil { + return false, err + } + if hs.serverHello.compressionMethod != compressionNone { c.sendAlert(alertUnexpectedMessage) return false, errors.New("tls: server selected unsupported compression format") @@ -626,9 +723,8 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { func (hs *clientHandshakeState) readFinished(out []byte) error { c := hs.c - c.readRecord(recordTypeChangeCipherSpec) - if c.in.err != nil { - return c.in.err + if err := c.readChangeCipherSpec(); err != nil { + return err } msg, err := c.readHandshake() @@ -676,6 +772,7 @@ func (hs *clientHandshakeState) readSessionTicket() error { masterSecret: hs.masterSecret, serverCertificates: c.peerCertificates, verifiedChains: c.verifiedChains, + receivedAt: c.config.time(), } return nil @@ -710,22 +807,72 @@ func (hs *clientHandshakeState) sendFinished(out []byte) error { return nil } +// verifyServerCertificate parses and verifies the provided chain, setting +// c.verifiedChains and c.peerCertificates or sending the appropriate alert. +func (c *Conn) verifyServerCertificate(certificates [][]byte) error { + certs := make([]*x509.Certificate, len(certificates)) + for i, asn1Data := range certificates { + cert, err := x509.ParseCertificate(asn1Data) + if err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("tls: failed to parse certificate from server: " + err.Error()) + } + certs[i] = cert + } + + if !c.config.InsecureSkipVerify { + opts := x509.VerifyOptions{ + Roots: c.config.RootCAs, + CurrentTime: c.config.time(), + DNSName: c.config.ServerName, + Intermediates: x509.NewCertPool(), + } + + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + var err error + c.verifiedChains, err = certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + if c.config.VerifyPeerCertificate != nil { + if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + switch certs[0].PublicKey.(type) { + case *rsa.PublicKey, *ecdsa.PublicKey: + break + default: + c.sendAlert(alertUnsupportedCertificate) + return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) + } + + c.peerCertificates = certs + + return nil +} + // tls11SignatureSchemes contains the signature schemes that we synthesise for // a TLS <= 1.1 connection, based on the supported certificate types. -var tls11SignatureSchemes = []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1} - -const ( - // tls11SignatureSchemesNumECDSA is the number of initial elements of - // tls11SignatureSchemes that use ECDSA. - tls11SignatureSchemesNumECDSA = 3 - // tls11SignatureSchemesNumRSA is the number of trailing elements of - // tls11SignatureSchemes that use RSA. - tls11SignatureSchemesNumRSA = 4 +var ( + tls11SignatureSchemes = []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1} + tls11SignatureSchemesECDSA = tls11SignatureSchemes[:3] + tls11SignatureSchemesRSA = tls11SignatureSchemes[3:] ) -func (hs *clientHandshakeState) getCertificate(certReq *certificateRequestMsg) (*Certificate, error) { - c := hs.c - +// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS +// <= 1.2 CertificateRequest, making an effort to fill in missing information. +func certificateRequestInfoFromMsg(certReq *certificateRequestMsg) *CertificateRequestInfo { var rsaAvail, ecdsaAvail bool for _, certType := range certReq.certificateTypes { switch certType { @@ -736,77 +883,84 @@ func (hs *clientHandshakeState) getCertificate(certReq *certificateRequestMsg) ( } } - if c.config.GetClientCertificate != nil { - var signatureSchemes []SignatureScheme - - if !certReq.hasSignatureAndHash { - // Prior to TLS 1.2, the signature schemes were not - // included in the certificate request message. In this - // case we use a plausible list based on the acceptable - // certificate types. - signatureSchemes = tls11SignatureSchemes - if !ecdsaAvail { - signatureSchemes = signatureSchemes[tls11SignatureSchemesNumECDSA:] + cri := &CertificateRequestInfo{ + AcceptableCAs: certReq.certificateAuthorities, + } + + if !certReq.hasSignatureAlgorithm { + // Prior to TLS 1.2, the signature schemes were not + // included in the certificate request message. In this + // case we use a plausible list based on the acceptable + // certificate types. + switch { + case rsaAvail && ecdsaAvail: + cri.SignatureSchemes = tls11SignatureSchemes + case rsaAvail: + cri.SignatureSchemes = tls11SignatureSchemesRSA + case ecdsaAvail: + cri.SignatureSchemes = tls11SignatureSchemesECDSA + } + return cri + } + + // In TLS 1.2, the signature schemes apply to both the certificate chain and + // the leaf key, while the certificate types only apply to the leaf key. + // See RFC 5246, Section 7.4.4 (where it calls this "somewhat complicated"). + // Filter the signature schemes based on the certificate type. + cri.SignatureSchemes = make([]SignatureScheme, 0, len(certReq.supportedSignatureAlgorithms)) + for _, sigScheme := range certReq.supportedSignatureAlgorithms { + switch signatureFromSignatureScheme(sigScheme) { + case signatureECDSA: + if ecdsaAvail { + cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme) } - if !rsaAvail { - signatureSchemes = signatureSchemes[:len(signatureSchemes)-tls11SignatureSchemesNumRSA] + case signatureRSAPSS, signaturePKCS1v15: + if rsaAvail { + cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme) } - } else { - signatureSchemes = certReq.supportedSignatureAlgorithms } - - return c.config.GetClientCertificate(&CertificateRequestInfo{ - AcceptableCAs: certReq.certificateAuthorities, - SignatureSchemes: signatureSchemes, - }) } - // RFC 4346 on the certificateAuthorities field: A list of the - // distinguished names of acceptable certificate authorities. - // These distinguished names may specify a desired - // distinguished name for a root CA or for a subordinate CA; - // thus, this message can be used to describe both known roots - // and a desired authorization space. If the - // certificate_authorities list is empty then the client MAY - // send any certificate of the appropriate - // ClientCertificateType, unless there is some external - // arrangement to the contrary. + return cri +} + +func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, error) { + if c.config.GetClientCertificate != nil { + return c.config.GetClientCertificate(cri) + } // We need to search our list of client certs for one // where SignatureAlgorithm is acceptable to the server and the - // Issuer is in certReq.certificateAuthorities -findCert: + // Issuer is in AcceptableCAs. for i, chain := range c.config.Certificates { - if !rsaAvail && !ecdsaAvail { + sigOK := false + for _, alg := range signatureSchemesForCertificate(c.vers, &chain) { + if isSupportedSignatureAlgorithm(alg, cri.SignatureSchemes) { + sigOK = true + break + } + } + if !sigOK { continue } + if len(cri.AcceptableCAs) == 0 { + return &chain, nil + } + for j, cert := range chain.Certificate { x509Cert := chain.Leaf - // parse the certificate if this isn't the leaf - // node, or if chain.Leaf was nil + // Parse the certificate if this isn't the leaf node, or if + // chain.Leaf was nil. if j != 0 || x509Cert == nil { var err error if x509Cert, err = x509.ParseCertificate(cert); err != nil { c.sendAlert(alertInternalError) - return nil, errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + return nil, errors.New("tls: failed to parse configured certificate chain #" + strconv.Itoa(i) + ": " + err.Error()) } } - switch { - case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: - case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: - default: - continue findCert - } - - if len(certReq.certificateAuthorities) == 0 { - // they gave us an empty list, so just take the - // first cert from c.config.Certificates - return &chain, nil - } - - for _, ca := range certReq.certificateAuthorities { + for _, ca := range cri.AcceptableCAs { if bytes.Equal(x509Cert.RawIssuer, ca) { return &chain, nil } @@ -845,7 +999,7 @@ func mutualProtocol(protos, preferenceProtos []string) (string, bool) { // hostnameInSNI converts name into an approriate hostname for SNI. // Literal IP addresses and absolute FQDNs are not permitted as SNI values. -// See https://tools.ietf.org/html/rfc6066#section-3. +// See RFC 6066, Section 3. func hostnameInSNI(name string) string { host := name if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { -- cgit v1.2.1