2009-11-21 23:53:03 +00:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package tls
|
|
|
|
|
|
|
|
import (
|
2012-01-05 17:05:38 +00:00
|
|
|
"bytes"
|
2014-08-29 20:36:30 +01:00
|
|
|
"crypto"
|
2013-07-17 17:33:16 +01:00
|
|
|
"crypto/ecdsa"
|
2009-12-15 23:33:31 +00:00
|
|
|
"crypto/rsa"
|
|
|
|
"crypto/subtle"
|
|
|
|
"crypto/x509"
|
2011-11-02 02:04:37 +00:00
|
|
|
"errors"
|
2014-02-12 16:20:01 +00:00
|
|
|
"fmt"
|
2009-12-15 23:33:31 +00:00
|
|
|
"io"
|
2014-01-22 23:24:03 +00:00
|
|
|
"net"
|
2012-01-05 17:05:38 +00:00
|
|
|
"strconv"
|
2016-02-19 07:25:52 +00:00
|
|
|
"strings"
|
2009-11-21 23:53:03 +00:00
|
|
|
)
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
type clientHandshakeState struct {
|
|
|
|
c *Conn
|
|
|
|
serverHello *serverHelloMsg
|
|
|
|
hello *clientHelloMsg
|
|
|
|
suite *cipherSuite
|
|
|
|
finishedHash finishedHash
|
|
|
|
masterSecret []byte
|
|
|
|
session *ClientSessionState
|
|
|
|
}
|
|
|
|
|
2011-11-02 02:04:37 +00:00
|
|
|
func (c *Conn) clientHandshake() error {
|
2010-07-14 15:40:15 +01:00
|
|
|
if c.config == nil {
|
|
|
|
c.config = defaultConfig()
|
|
|
|
}
|
2010-04-27 06:19:04 +01:00
|
|
|
|
2014-02-21 20:56:41 +00:00
|
|
|
if len(c.config.ServerName) == 0 && !c.config.InsecureSkipVerify {
|
|
|
|
return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
|
|
|
|
}
|
|
|
|
|
2014-08-05 19:36:20 +01:00
|
|
|
nextProtosLength := 0
|
|
|
|
for _, proto := range c.config.NextProtos {
|
|
|
|
if l := len(proto); l == 0 || l > 255 {
|
|
|
|
return errors.New("tls: invalid NextProtos value")
|
|
|
|
} else {
|
|
|
|
nextProtosLength += 1 + l
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nextProtosLength > 0xffff {
|
|
|
|
return errors.New("tls: NextProtos values too large")
|
|
|
|
}
|
|
|
|
|
2009-11-21 23:53:03 +00:00
|
|
|
hello := &clientHelloMsg{
|
2014-01-09 18:38:11 +00:00
|
|
|
vers: c.config.maxVersion(),
|
|
|
|
compressionMethods: []uint8{compressionNone},
|
|
|
|
random: make([]byte, 32),
|
|
|
|
ocspStapling: true,
|
2015-04-16 19:59:22 +01:00
|
|
|
scts: true,
|
2016-02-19 07:25:52 +00:00
|
|
|
serverName: hostnameInSNI(c.config.ServerName),
|
2014-02-24 22:57:51 +00:00
|
|
|
supportedCurves: c.config.curvePreferences(),
|
2014-01-09 18:38:11 +00:00
|
|
|
supportedPoints: []uint8{pointFormatUncompressed},
|
|
|
|
nextProtoNeg: len(c.config.NextProtos) > 0,
|
|
|
|
secureRenegotiation: true,
|
2014-08-05 19:36:20 +01:00
|
|
|
alpnProtocols: c.config.NextProtos,
|
2009-12-15 23:33:31 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 22:09:56 +01:00
|
|
|
possibleCipherSuites := c.config.cipherSuites()
|
|
|
|
hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
|
|
|
|
|
|
|
|
NextCipherSuite:
|
|
|
|
for _, suiteId := range possibleCipherSuites {
|
|
|
|
for _, suite := range cipherSuites {
|
|
|
|
if suite.id != suiteId {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Don't advertise TLS 1.2-only cipher suites unless
|
|
|
|
// we're attempting TLS 1.2.
|
|
|
|
if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
hello.cipherSuites = append(hello.cipherSuites, suiteId)
|
|
|
|
continue NextCipherSuite
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-04 15:51:37 +00:00
|
|
|
_, err := io.ReadFull(c.config.rand(), hello.random)
|
2009-11-21 23:53:03 +00:00
|
|
|
if err != nil {
|
2010-10-11 15:39:56 +01:00
|
|
|
c.sendAlert(alertInternalError)
|
2014-01-22 23:24:03 +00:00
|
|
|
return errors.New("tls: short read from Rand: " + err.Error())
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
|
|
|
|
2013-07-03 00:58:56 +01:00
|
|
|
if hello.vers >= VersionTLS12 {
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
hello.signatureAndHashes = supportedSignatureAlgorithms
|
2013-07-03 00:58:56 +01:00
|
|
|
}
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
var session *ClientSessionState
|
|
|
|
var cacheKey string
|
|
|
|
sessionCache := c.config.ClientSessionCache
|
|
|
|
if c.config.SessionTicketsDisabled {
|
|
|
|
sessionCache = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if sessionCache != nil {
|
|
|
|
hello.ticketSupported = true
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
versOk := candidateSession.vers >= c.config.minVersion() &&
|
|
|
|
candidateSession.vers <= c.config.maxVersion()
|
|
|
|
if versOk && cipherSuiteOk {
|
|
|
|
session = candidateSession
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
c.sendAlert(alertInternalError)
|
|
|
|
return errors.New("tls: short read from Rand: " + err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-26 19:17:29 +00:00
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2009-11-21 23:53:03 +00:00
|
|
|
|
2010-04-27 06:19:04 +01:00
|
|
|
msg, err := c.readHandshake()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
serverHello, ok := msg.(*serverHelloMsg)
|
2009-11-21 23:53:03 +00:00
|
|
|
if !ok {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
|
|
return unexpectedMessageError(serverHello, msg)
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2010-04-27 06:19:04 +01:00
|
|
|
|
2013-06-05 01:02:22 +01:00
|
|
|
vers, ok := c.config.mutualVersion(serverHello.vers)
|
|
|
|
if !ok || vers < VersionTLS10 {
|
2012-01-31 16:22:47 +00:00
|
|
|
// TLS 1.0 is the minimum version supported as a client.
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertProtocolVersion)
|
|
|
|
return fmt.Errorf("tls: server selected unsupported protocol version %x", serverHello.vers)
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2010-04-27 06:19:04 +01:00
|
|
|
c.vers = vers
|
|
|
|
c.haveVers = true
|
2009-11-21 23:53:03 +00:00
|
|
|
|
2015-11-07 12:18:36 +00:00
|
|
|
suite := mutualCipherSuite(hello.cipherSuites, serverHello.cipherSuite)
|
2014-01-22 23:24:03 +00:00
|
|
|
if suite == nil {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertHandshakeFailure)
|
2015-11-07 12:18:36 +00:00
|
|
|
return errors.New("tls: server chose an unconfigured cipher suite")
|
2014-01-22 23:24:03 +00:00
|
|
|
}
|
2013-07-03 00:58:56 +01:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
hs := &clientHandshakeState{
|
|
|
|
c: c,
|
|
|
|
serverHello: serverHello,
|
|
|
|
hello: hello,
|
|
|
|
suite: suite,
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
finishedHash: newFinishedHash(c.vers, suite),
|
2014-01-22 23:24:03 +00:00
|
|
|
session: session,
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
isResume, err := hs.processServerHello()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2011-03-29 22:53:09 +01:00
|
|
|
}
|
|
|
|
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
// No signatures of the handshake are needed in a resumption.
|
|
|
|
// Otherwise, in a full handshake, if we don't have any certificates
|
|
|
|
// configured then we will never send a CertificateVerify message and
|
|
|
|
// thus no signatures are needed in that case either.
|
|
|
|
if isResume || len(c.config.Certificates) == 0 {
|
|
|
|
hs.finishedHash.discardHandshakeBuffer()
|
|
|
|
}
|
|
|
|
|
|
|
|
hs.finishedHash.Write(hs.hello.marshal())
|
|
|
|
hs.finishedHash.Write(hs.serverHello.marshal())
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
if isResume {
|
|
|
|
if err := hs.establishKeys(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := hs.readSessionTicket(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-12 00:40:42 +01:00
|
|
|
if err := hs.readFinished(c.firstFinished[:]); err != nil {
|
2014-01-22 23:24:03 +00:00
|
|
|
return err
|
|
|
|
}
|
2014-08-12 00:40:42 +01:00
|
|
|
if err := hs.sendFinished(nil); err != nil {
|
2014-01-22 23:24:03 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := hs.doFullHandshake(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := hs.establishKeys(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-12 00:40:42 +01:00
|
|
|
if err := hs.sendFinished(c.firstFinished[:]); err != nil {
|
2014-01-22 23:24:03 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := hs.readSessionTicket(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-12 00:40:42 +01:00
|
|
|
if err := hs.readFinished(nil); err != nil {
|
2014-01-22 23:24:03 +00:00
|
|
|
return err
|
|
|
|
}
|
2010-12-15 16:49:55 +00:00
|
|
|
}
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
if sessionCache != nil && hs.session != nil && session != hs.session {
|
|
|
|
sessionCache.Put(cacheKey, hs.session)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.didResume = isResume
|
|
|
|
c.handshakeComplete = true
|
|
|
|
c.cipherSuite = suite.id
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hs *clientHandshakeState) doFullHandshake() error {
|
|
|
|
c := hs.c
|
|
|
|
|
|
|
|
msg, err := c.readHandshake()
|
2010-04-27 06:19:04 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
certMsg, ok := msg.(*certificateMsg)
|
2009-11-21 23:53:03 +00:00
|
|
|
if !ok || len(certMsg.certificates) == 0 {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
|
|
return unexpectedMessageError(certMsg, msg)
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(certMsg.marshal())
|
2009-11-21 23:53:03 +00:00
|
|
|
|
2009-12-15 23:33:31 +00:00
|
|
|
certs := make([]*x509.Certificate, len(certMsg.certificates))
|
2009-11-21 23:53:03 +00:00
|
|
|
for i, asn1Data := range certMsg.certificates {
|
2009-12-15 23:33:31 +00:00
|
|
|
cert, err := x509.ParseCertificate(asn1Data)
|
2009-11-21 23:53:03 +00:00
|
|
|
if err != nil {
|
2010-10-11 15:39:56 +01:00
|
|
|
c.sendAlert(alertBadCertificate)
|
2014-02-12 16:20:01 +00:00
|
|
|
return errors.New("tls: failed to parse certificate from server: " + err.Error())
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2009-12-15 23:33:31 +00:00
|
|
|
certs[i] = cert
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
|
|
|
|
2011-10-13 18:59:13 +01:00
|
|
|
if !c.config.InsecureSkipVerify {
|
2011-04-19 14:57:58 +01:00
|
|
|
opts := x509.VerifyOptions{
|
2012-03-07 18:12:35 +00:00
|
|
|
Roots: c.config.RootCAs,
|
2011-04-25 15:27:36 +01:00
|
|
|
CurrentTime: c.config.time(),
|
2011-04-19 14:57:58 +01:00
|
|
|
DNSName: c.config.ServerName,
|
|
|
|
Intermediates: x509.NewCertPool(),
|
2010-09-20 17:17:31 +01:00
|
|
|
}
|
|
|
|
|
2011-04-19 14:57:58 +01:00
|
|
|
for i, cert := range certs {
|
|
|
|
if i == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
opts.Intermediates.AddCert(cert)
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2011-04-19 14:57:58 +01:00
|
|
|
c.verifiedChains, err = certs[0].Verify(opts)
|
|
|
|
if err != nil {
|
2010-10-11 15:39:56 +01:00
|
|
|
c.sendAlert(alertBadCertificate)
|
2011-04-19 14:57:58 +01:00
|
|
|
return err
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-17 17:33:16 +01:00
|
|
|
switch certs[0].PublicKey.(type) {
|
|
|
|
case *rsa.PublicKey, *ecdsa.PublicKey:
|
|
|
|
break
|
|
|
|
default:
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertUnsupportedCertificate)
|
|
|
|
return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
|
|
|
|
2010-07-21 16:36:01 +01:00
|
|
|
c.peerCertificates = certs
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
if hs.serverHello.ocspStapling {
|
2010-07-14 15:40:15 +01:00
|
|
|
msg, err = c.readHandshake()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cs, ok := msg.(*certificateStatusMsg)
|
|
|
|
if !ok {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
|
|
return unexpectedMessageError(cs, msg)
|
2010-07-14 15:40:15 +01:00
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(cs.marshal())
|
2010-07-14 15:40:15 +01:00
|
|
|
|
|
|
|
if cs.statusType == statusTypeOCSP {
|
|
|
|
c.ocspResponse = cs.response
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-27 06:19:04 +01:00
|
|
|
msg, err = c.readHandshake()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2010-08-16 16:22:22 +01:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
keyAgreement := hs.suite.ka(c.vers)
|
2010-12-16 22:10:50 +00:00
|
|
|
|
|
|
|
skx, ok := msg.(*serverKeyExchangeMsg)
|
|
|
|
if ok {
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(skx.marshal())
|
|
|
|
err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, certs[0], skx)
|
2010-12-16 22:10:50 +00:00
|
|
|
if err != nil {
|
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
msg, err = c.readHandshake()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-29 16:21:32 +01:00
|
|
|
var chainToSend *Certificate
|
2012-03-19 16:34:35 +00:00
|
|
|
var certRequested bool
|
2010-08-16 16:22:22 +01:00
|
|
|
certReq, ok := msg.(*certificateRequestMsg)
|
|
|
|
if ok {
|
2012-03-19 16:34:35 +00:00
|
|
|
certRequested = true
|
|
|
|
|
2012-01-05 17:05:38 +00:00
|
|
|
// 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.
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(certReq.marshal())
|
2012-01-05 17:05:38 +00:00
|
|
|
|
2013-07-17 17:33:16 +01:00
|
|
|
var rsaAvail, ecdsaAvail bool
|
2010-08-16 16:22:22 +01:00
|
|
|
for _, certType := range certReq.certificateTypes {
|
2013-07-17 17:33:16 +01:00
|
|
|
switch certType {
|
|
|
|
case certTypeRSASign:
|
2010-08-16 16:22:22 +01:00
|
|
|
rsaAvail = true
|
2013-07-17 17:33:16 +01:00
|
|
|
case certTypeECDSASign:
|
|
|
|
ecdsaAvail = true
|
2010-08-16 16:22:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-05 17:05:38 +00:00
|
|
|
// We need to search our list of client certs for one
|
2014-08-29 20:36:30 +01:00
|
|
|
// where SignatureAlgorithm is acceptable to the server and the
|
|
|
|
// Issuer is in certReq.certificateAuthorities
|
2012-01-05 17:05:38 +00:00
|
|
|
findCert:
|
2013-05-29 16:21:32 +01:00
|
|
|
for i, chain := range c.config.Certificates {
|
2013-07-17 17:33:16 +01:00
|
|
|
if !rsaAvail && !ecdsaAvail {
|
2012-01-05 17:05:38 +00:00
|
|
|
continue
|
|
|
|
}
|
2010-08-16 16:22:22 +01:00
|
|
|
|
2013-05-29 16:21:32 +01:00
|
|
|
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
|
|
|
|
if j != 0 || x509Cert == nil {
|
|
|
|
if x509Cert, err = x509.ParseCertificate(cert); err != nil {
|
|
|
|
c.sendAlert(alertInternalError)
|
|
|
|
return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
|
|
|
|
}
|
2012-01-05 17:05:38 +00:00
|
|
|
}
|
|
|
|
|
2013-07-17 17:33:16 +01:00
|
|
|
switch {
|
|
|
|
case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA:
|
|
|
|
case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA:
|
|
|
|
default:
|
2013-05-29 16:21:32 +01:00
|
|
|
continue findCert
|
|
|
|
}
|
2012-01-05 17:05:38 +00:00
|
|
|
|
2013-05-29 16:21:32 +01:00
|
|
|
if len(certReq.certificateAuthorities) == 0 {
|
|
|
|
// they gave us an empty list, so just take the
|
2014-08-29 20:36:30 +01:00
|
|
|
// first cert from c.config.Certificates
|
2013-05-29 16:21:32 +01:00
|
|
|
chainToSend = &chain
|
2012-01-05 17:05:38 +00:00
|
|
|
break findCert
|
|
|
|
}
|
2013-05-29 16:21:32 +01:00
|
|
|
|
|
|
|
for _, ca := range certReq.certificateAuthorities {
|
|
|
|
if bytes.Equal(x509Cert.RawIssuer, ca) {
|
|
|
|
chainToSend = &chain
|
|
|
|
break findCert
|
|
|
|
}
|
|
|
|
}
|
2012-01-05 17:05:38 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-16 16:22:22 +01:00
|
|
|
|
|
|
|
msg, err = c.readHandshake()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-27 06:19:04 +01:00
|
|
|
shd, ok := msg.(*serverHelloDoneMsg)
|
2009-11-21 23:53:03 +00:00
|
|
|
if !ok {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
|
|
return unexpectedMessageError(shd, msg)
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(shd.marshal())
|
2009-11-21 23:53:03 +00:00
|
|
|
|
2012-03-19 16:34:35 +00:00
|
|
|
// If the server requested a certificate then we have to send a
|
|
|
|
// Certificate message, even if it's empty because we don't have a
|
|
|
|
// certificate to send.
|
|
|
|
if certRequested {
|
2010-08-16 16:22:22 +01:00
|
|
|
certMsg = new(certificateMsg)
|
2013-05-29 16:21:32 +01:00
|
|
|
if chainToSend != nil {
|
|
|
|
certMsg.certificates = chainToSend.Certificate
|
2012-03-19 16:34:35 +00:00
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(certMsg.marshal())
|
2016-02-26 19:17:29 +00:00
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2010-08-16 16:22:22 +01:00
|
|
|
}
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, certs[0])
|
2009-11-21 23:53:03 +00:00
|
|
|
if err != nil {
|
2010-12-16 22:10:50 +00:00
|
|
|
c.sendAlert(alertInternalError)
|
|
|
|
return err
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2010-12-16 22:10:50 +00:00
|
|
|
if ckx != nil {
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(ckx.marshal())
|
2016-02-26 19:17:29 +00:00
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
|
|
|
|
2013-05-29 16:21:32 +01:00
|
|
|
if chainToSend != nil {
|
2013-09-16 21:39:42 +01:00
|
|
|
certVerify := &certificateVerifyMsg{
|
|
|
|
hasSignatureAndHash: c.vers >= VersionTLS12,
|
|
|
|
}
|
|
|
|
|
2014-08-29 20:36:30 +01:00
|
|
|
key, ok := chainToSend.PrivateKey.(crypto.Signer)
|
|
|
|
if !ok {
|
|
|
|
c.sendAlert(alertInternalError)
|
|
|
|
return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
|
|
|
|
}
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
|
|
|
|
var signatureType uint8
|
2014-08-29 20:36:30 +01:00
|
|
|
switch key.Public().(type) {
|
|
|
|
case *ecdsa.PublicKey:
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
signatureType = signatureECDSA
|
2014-08-29 20:36:30 +01:00
|
|
|
case *rsa.PublicKey:
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
signatureType = signatureRSA
|
2013-07-17 17:33:16 +01:00
|
|
|
default:
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
c.sendAlert(alertInternalError)
|
|
|
|
return fmt.Errorf("tls: failed to sign handshake with client certificate: unknown client certificate key type: %T", key)
|
|
|
|
}
|
|
|
|
|
|
|
|
certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, signatureType)
|
|
|
|
if err != nil {
|
|
|
|
c.sendAlert(alertInternalError)
|
|
|
|
return err
|
2013-07-17 17:33:16 +01:00
|
|
|
}
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
digest, hashFunc, err := hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret)
|
2010-08-16 16:22:22 +01:00
|
|
|
if err != nil {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertInternalError)
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
certVerify.signature, err = key.Sign(c.config.rand(), digest, hashFunc)
|
|
|
|
if err != nil {
|
|
|
|
c.sendAlert(alertInternalError)
|
|
|
|
return err
|
2010-08-16 16:22:22 +01:00
|
|
|
}
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(certVerify.marshal())
|
2016-02-26 19:17:29 +00:00
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2010-08-16 16:22:22 +01:00
|
|
|
}
|
|
|
|
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
|
|
|
|
|
|
|
|
hs.finishedHash.discardHandshakeBuffer()
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hs *clientHandshakeState) establishKeys() error {
|
|
|
|
c := hs.c
|
2010-04-27 06:19:04 +01:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
|
crypto/tls: decouple handshake signatures from the handshake hash.
Prior to TLS 1.2, the handshake had a pleasing property that one could
incrementally hash it and, from that, get the needed hashes for both
the CertificateVerify and Finished messages.
TLS 1.2 introduced negotiation for the signature and hash and it became
possible for the handshake hash to be, say, SHA-384, but for the
CertificateVerify to sign the handshake with SHA-1. The problem is that
one doesn't know in advance which hashes will be needed and thus the
handshake needs to be buffered.
Go ignored this, always kept a single handshake hash, and any signatures
over the handshake had to use that hash.
However, there are a set of servers that inspect the client's offered
signature hash functions and will abort the handshake if one of the
server's certificates is signed with a hash function outside of that
set. https://robertsspaceindustries.com/ is an example of such a server.
Clearly not a lot of thought happened when that server code was written,
but its out there and we have to deal with it.
This change decouples the handshake hash from the CertificateVerify
hash. This lays the groundwork for advertising support for SHA-384 but
doesn't actually make that change in the interests of reviewability.
Updating the advertised hash functions will cause changes in many of the
testdata/ files and some errors might get lost in the noise. This change
only needs to update four testdata/ files: one because a SHA-384-based
handshake is now being signed with SHA-256 and the others because the
TLS 1.2 CertificateRequest message now includes SHA-1.
This change also has the effect of adding support for
client-certificates in SSLv3 servers. However, SSLv3 is now disabled by
default so this should be moot.
It would be possible to avoid much of this change and just support
SHA-384 for the ServerKeyExchange as the SKX only signs over the nonces
and SKX params (a design mistake in TLS). However, that would leave Go
in the odd situation where it advertised support for SHA-384, but would
only use the handshake hash when signing client certificates. I fear
that'll just cause problems in the future.
Much of this code was written by davidben@ for the purposes of testing
BoringSSL.
Partly addresses #9757
Change-Id: I5137a472b6076812af387a5a69fc62c7373cd485
Reviewed-on: https://go-review.googlesource.com/9415
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
2015-04-28 17:13:38 +01:00
|
|
|
keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
|
2014-01-22 23:24:03 +00:00
|
|
|
var clientCipher, serverCipher interface{}
|
|
|
|
var clientHash, serverHash macFunction
|
|
|
|
if hs.suite.cipher != nil {
|
|
|
|
clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */)
|
|
|
|
clientHash = hs.suite.mac(c.vers, clientMAC)
|
|
|
|
serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */)
|
|
|
|
serverHash = hs.suite.mac(c.vers, serverMAC)
|
2013-08-29 22:18:59 +01:00
|
|
|
} else {
|
2014-01-22 23:24:03 +00:00
|
|
|
clientCipher = hs.suite.aead(clientKey, clientIV)
|
|
|
|
serverCipher = hs.suite.aead(serverKey, serverIV)
|
2013-08-29 22:18:59 +01:00
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
|
|
|
|
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
|
2011-09-14 20:32:19 +01:00
|
|
|
c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
|
2014-01-22 23:24:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
2009-11-21 23:53:03 +00:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
func (hs *clientHandshakeState) serverResumedSession() bool {
|
|
|
|
// If the server responded with the same sessionId then it means the
|
|
|
|
// sessionTicket is being used to resume a TLS session.
|
|
|
|
return hs.session != nil && hs.hello.sessionId != nil &&
|
|
|
|
bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId)
|
|
|
|
}
|
2011-03-29 22:53:09 +01:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
func (hs *clientHandshakeState) processServerHello() (bool, error) {
|
|
|
|
c := hs.c
|
|
|
|
|
|
|
|
if hs.serverHello.compressionMethod != compressionNone {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
|
|
return false, errors.New("tls: server selected unsupported compression format")
|
2011-03-29 22:53:09 +01:00
|
|
|
}
|
|
|
|
|
2014-08-05 19:36:20 +01:00
|
|
|
clientDidNPN := hs.hello.nextProtoNeg
|
|
|
|
clientDidALPN := len(hs.hello.alpnProtocols) > 0
|
|
|
|
serverHasNPN := hs.serverHello.nextProtoNeg
|
|
|
|
serverHasALPN := len(hs.serverHello.alpnProtocol) > 0
|
|
|
|
|
|
|
|
if !clientDidNPN && serverHasNPN {
|
2014-01-22 23:24:03 +00:00
|
|
|
c.sendAlert(alertHandshakeFailure)
|
2014-02-12 16:20:01 +00:00
|
|
|
return false, errors.New("server advertised unrequested NPN extension")
|
2014-01-22 23:24:03 +00:00
|
|
|
}
|
2009-11-21 23:53:03 +00:00
|
|
|
|
2014-08-05 19:36:20 +01:00
|
|
|
if !clientDidALPN && serverHasALPN {
|
|
|
|
c.sendAlert(alertHandshakeFailure)
|
|
|
|
return false, errors.New("server advertised unrequested ALPN extension")
|
|
|
|
}
|
|
|
|
|
|
|
|
if serverHasNPN && serverHasALPN {
|
|
|
|
c.sendAlert(alertHandshakeFailure)
|
|
|
|
return false, errors.New("server advertised both NPN and ALPN extensions")
|
|
|
|
}
|
|
|
|
|
|
|
|
if serverHasALPN {
|
|
|
|
c.clientProtocol = hs.serverHello.alpnProtocol
|
|
|
|
c.clientProtocolFallback = false
|
|
|
|
}
|
2015-04-16 19:59:22 +01:00
|
|
|
c.scts = hs.serverHello.scts
|
2014-08-05 19:36:20 +01:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
if hs.serverResumedSession() {
|
|
|
|
// Restore masterSecret and peerCerts from previous state
|
|
|
|
hs.masterSecret = hs.session.masterSecret
|
|
|
|
c.peerCertificates = hs.session.serverCertificates
|
2015-08-05 14:53:56 +01:00
|
|
|
c.verifiedChains = hs.session.verifiedChains
|
2014-01-22 23:24:03 +00:00
|
|
|
return true, nil
|
2013-08-29 22:18:59 +01:00
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2014-08-12 00:40:42 +01:00
|
|
|
func (hs *clientHandshakeState) readFinished(out []byte) error {
|
2014-01-22 23:24:03 +00:00
|
|
|
c := hs.c
|
|
|
|
|
2010-04-27 06:19:04 +01:00
|
|
|
c.readRecord(recordTypeChangeCipherSpec)
|
2014-03-03 14:01:44 +00:00
|
|
|
if err := c.in.error(); err != nil {
|
2012-09-06 08:50:26 +01:00
|
|
|
return err
|
2010-04-27 06:19:04 +01:00
|
|
|
}
|
2009-11-21 23:53:03 +00:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
msg, err := c.readHandshake()
|
2010-04-27 06:19:04 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
serverFinished, ok := msg.(*finishedMsg)
|
2009-11-21 23:53:03 +00:00
|
|
|
if !ok {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
|
|
return unexpectedMessageError(serverFinished, msg)
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
verify := hs.finishedHash.serverSum(hs.masterSecret)
|
2009-11-21 23:53:03 +00:00
|
|
|
if len(verify) != len(serverFinished.verifyData) ||
|
|
|
|
subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertHandshakeFailure)
|
|
|
|
return errors.New("tls: server's Finished message was incorrect")
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
hs.finishedHash.Write(serverFinished.marshal())
|
2014-08-12 00:40:42 +01:00
|
|
|
copy(out, verify)
|
2014-01-22 23:24:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
2009-11-21 23:53:03 +00:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
func (hs *clientHandshakeState) readSessionTicket() error {
|
|
|
|
if !hs.serverHello.ticketSupported {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
c := hs.c
|
|
|
|
msg, err := c.readHandshake()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sessionTicketMsg, ok := msg.(*newSessionTicketMsg)
|
|
|
|
if !ok {
|
2014-02-12 16:20:01 +00:00
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
|
|
return unexpectedMessageError(sessionTicketMsg, msg)
|
2014-01-22 23:24:03 +00:00
|
|
|
}
|
|
|
|
hs.finishedHash.Write(sessionTicketMsg.marshal())
|
|
|
|
|
|
|
|
hs.session = &ClientSessionState{
|
|
|
|
sessionTicket: sessionTicketMsg.ticket,
|
|
|
|
vers: c.vers,
|
|
|
|
cipherSuite: hs.suite.id,
|
|
|
|
masterSecret: hs.masterSecret,
|
|
|
|
serverCertificates: c.peerCertificates,
|
2015-08-05 14:53:56 +01:00
|
|
|
verifiedChains: c.verifiedChains,
|
2014-01-22 23:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-08-12 00:40:42 +01:00
|
|
|
func (hs *clientHandshakeState) sendFinished(out []byte) error {
|
2014-01-22 23:24:03 +00:00
|
|
|
c := hs.c
|
|
|
|
|
2016-02-26 19:17:29 +00:00
|
|
|
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
if hs.serverHello.nextProtoNeg {
|
|
|
|
nextProto := new(nextProtoMsg)
|
|
|
|
proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.nextProtos)
|
|
|
|
nextProto.proto = proto
|
|
|
|
c.clientProtocol = proto
|
|
|
|
c.clientProtocolFallback = fallback
|
|
|
|
|
|
|
|
hs.finishedHash.Write(nextProto.marshal())
|
2016-02-26 19:17:29 +00:00
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, nextProto.marshal()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-01-22 23:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
finished := new(finishedMsg)
|
|
|
|
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
|
|
|
|
hs.finishedHash.Write(finished.marshal())
|
2016-02-26 19:17:29 +00:00
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-12 00:40:42 +01:00
|
|
|
copy(out, finished.verifyData)
|
2010-04-27 06:19:04 +01:00
|
|
|
return nil
|
2009-11-21 23:53:03 +00:00
|
|
|
}
|
2011-03-29 22:53:09 +01:00
|
|
|
|
2014-01-22 23:24:03 +00:00
|
|
|
// clientSessionCacheKey returns a key used to cache sessionTickets that could
|
|
|
|
// be used to resume previously negotiated TLS sessions with a server.
|
|
|
|
func clientSessionCacheKey(serverAddr net.Addr, config *Config) string {
|
|
|
|
if len(config.ServerName) > 0 {
|
|
|
|
return config.ServerName
|
|
|
|
}
|
|
|
|
return serverAddr.String()
|
|
|
|
}
|
|
|
|
|
2014-08-05 19:36:20 +01:00
|
|
|
// mutualProtocol finds the mutual Next Protocol Negotiation or ALPN protocol
|
|
|
|
// given list of possible protocols and a list of the preference order. The
|
|
|
|
// first list must not be empty. It returns the resulting protocol and flag
|
2011-03-29 22:53:09 +01:00
|
|
|
// indicating if the fallback case was reached.
|
2014-08-05 19:36:20 +01:00
|
|
|
func mutualProtocol(protos, preferenceProtos []string) (string, bool) {
|
|
|
|
for _, s := range preferenceProtos {
|
|
|
|
for _, c := range protos {
|
2011-03-29 22:53:09 +01:00
|
|
|
if s == c {
|
|
|
|
return s, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-05 19:36:20 +01:00
|
|
|
return protos[0], true
|
2011-03-29 22:53:09 +01:00
|
|
|
}
|
2016-02-19 07:25:52 +00:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
func hostnameInSNI(name string) string {
|
|
|
|
host := name
|
|
|
|
if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
|
|
|
|
host = host[1 : len(host)-1]
|
|
|
|
}
|
|
|
|
if i := strings.LastIndex(host, "%"); i > 0 {
|
|
|
|
host = host[:i]
|
|
|
|
}
|
|
|
|
if net.ParseIP(host) != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
if len(name) > 0 && name[len(name)-1] == '.' {
|
|
|
|
name = name[:len(name)-1]
|
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|