summaryrefslogtreecommitdiff
path: root/libgo/go/crypto/tls/handshake_client.go
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2019-01-18 19:04:36 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2019-01-18 19:04:36 +0000
commit4f4a855d82a889cebcfca150a7a43909bcb6a346 (patch)
treef12bae0781920fa34669fe30b6f4615a86d9fb80 /libgo/go/crypto/tls/handshake_client.go
parent225220d668dafb8262db7012bced688acbe63b33 (diff)
downloadgcc-4f4a855d82a889cebcfca150a7a43909bcb6a346.tar.gz
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
Diffstat (limited to 'libgo/go/crypto/tls/handshake_client.go')
-rw-r--r--libgo/go/crypto/tls/handshake_client.go572
1 files changed, 363 insertions, 209 deletions
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] == ']' {