mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-04 12:37:35 +03:00
Support for Ed25519 certificates was added in CL 175478, this wires them up into the TLS stack according to RFC 8422 (TLS 1.2) and RFC 8446 (TLS 1.3). RFC 8422 also specifies support for TLS 1.0 and 1.1, and I initially implemented that, but even OpenSSL doesn't take the complexity, so I just dropped it. It would have required keeping a buffer of the handshake transcript in order to do the direct Ed25519 signatures. We effectively need to support TLS 1.2 because it shares ClientHello signature algorithms with TLS 1.3. While at it, reordered the advertised signature algorithms in the rough order we would want to use them, also based on what curves have fast constant-time implementations. Client and client auth tests changed because of the change in advertised signature algorithms in ClientHello and CertificateRequest. Fixes #25355 Change-Id: I9fdd839afde4fd6b13fcbc5cc7017fd8c35085ee Reviewed-on: https://go-review.googlesource.com/c/go/+/177698 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
1014 lines
30 KiB
Go
1014 lines
30 KiB
Go
// 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 (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/ed25519"
|
|
"crypto/rsa"
|
|
"crypto/subtle"
|
|
"crypto/x509"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type clientHandshakeState struct {
|
|
c *Conn
|
|
serverHello *serverHelloMsg
|
|
hello *clientHelloMsg
|
|
suite *cipherSuite
|
|
finishedHash finishedHash
|
|
masterSecret []byte
|
|
session *ClientSessionState
|
|
}
|
|
|
|
func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
|
|
config := c.config
|
|
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
|
|
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, nil, errors.New("tls: invalid NextProtos value")
|
|
} else {
|
|
nextProtosLength += 1 + l
|
|
}
|
|
}
|
|
if nextProtosLength > 0xffff {
|
|
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: clientHelloVersion,
|
|
compressionMethods: []uint8{compressionNone},
|
|
random: make([]byte, 32),
|
|
sessionId: make([]byte, 32),
|
|
ocspStapling: true,
|
|
scts: true,
|
|
serverName: hostnameInSNI(config.ServerName),
|
|
supportedCurves: config.curvePreferences(),
|
|
supportedPoints: []uint8{pointFormatUncompressed},
|
|
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))
|
|
|
|
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 {
|
|
break
|
|
}
|
|
hello.cipherSuites = append(hello.cipherSuites, suiteId)
|
|
break
|
|
}
|
|
}
|
|
|
|
_, err := io.ReadFull(config.rand(), hello.random)
|
|
if err != nil {
|
|
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
|
|
}
|
|
|
|
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() (err error) {
|
|
if c.config == nil {
|
|
c.config = defaultConfig()
|
|
}
|
|
|
|
// This may be a renegotiation handshake, in which case some fields
|
|
// need to be reset.
|
|
c.didResume = false
|
|
|
|
hello, ecdheParams, err := c.makeClientHello()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
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)
|
|
}
|
|
}()
|
|
}
|
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
serverHello, ok := msg.(*serverHelloMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(serverHello, msg)
|
|
}
|
|
|
|
if err := c.pickTLSVersion(serverHello); err != nil {
|
|
return err
|
|
}
|
|
|
|
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,
|
|
serverHello: serverHello,
|
|
hello: hello,
|
|
session: session,
|
|
}
|
|
|
|
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 cacheKey != "" && hs.session != nil && session != hs.session {
|
|
c.config.ClientSessionCache.Put(cacheKey, hs.session)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
|
|
|
|
// 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 && c.config.GetClientCertificate == nil) {
|
|
hs.finishedHash.discardHandshakeBuffer()
|
|
}
|
|
|
|
hs.finishedHash.Write(hs.hello.marshal())
|
|
hs.finishedHash.Write(hs.serverHello.marshal())
|
|
|
|
c.buffering = true
|
|
if isResume {
|
|
if err := hs.establishKeys(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readSessionTicket(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readFinished(c.serverFinished[:]); err != nil {
|
|
return err
|
|
}
|
|
c.clientFinishedIsFirst = false
|
|
if err := hs.sendFinished(c.clientFinished[:]); err != nil {
|
|
return err
|
|
}
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := hs.doFullHandshake(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.establishKeys(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendFinished(c.clientFinished[:]); err != nil {
|
|
return err
|
|
}
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
c.clientFinishedIsFirst = true
|
|
if err := hs.readSessionTicket(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readFinished(c.serverFinished[:]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random)
|
|
c.didResume = isResume
|
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeState) pickCipherSuite() error {
|
|
if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil {
|
|
hs.c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: server chose an unconfigured cipher suite")
|
|
}
|
|
|
|
hs.c.cipherSuite = hs.suite.id
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeState) doFullHandshake() error {
|
|
c := hs.c
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
certMsg, ok := msg.(*certificateMsg)
|
|
if !ok || len(certMsg.certificates) == 0 {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(certMsg, msg)
|
|
}
|
|
hs.finishedHash.Write(certMsg.marshal())
|
|
|
|
if c.handshakes == 0 {
|
|
// If this is the first handshake on a connection, process and
|
|
// (optionally) verify the server's certificates.
|
|
if err := c.verifyServerCertificate(certMsg.certificates); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// This is a renegotiation handshake. We require that the
|
|
// server's identity (i.e. leaf certificate) is unchanged and
|
|
// thus any previous trust decision is still valid.
|
|
//
|
|
// See https://mitls.org/pages/attacks/3SHAKE for the
|
|
// motivation behind this requirement.
|
|
if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) {
|
|
c.sendAlert(alertBadCertificate)
|
|
return errors.New("tls: server's identity changed during renegotiation")
|
|
}
|
|
}
|
|
|
|
msg, err = c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cs, ok := msg.(*certificateStatusMsg)
|
|
if ok {
|
|
// RFC4366 on Certificate Status Request:
|
|
// The server MAY return a "certificate_status" message.
|
|
|
|
if !hs.serverHello.ocspStapling {
|
|
// If a server returns a "CertificateStatus" message, then the
|
|
// server MUST have included an extension of type "status_request"
|
|
// with empty "extension_data" in the extended server hello.
|
|
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return errors.New("tls: received unexpected CertificateStatus message")
|
|
}
|
|
hs.finishedHash.Write(cs.marshal())
|
|
|
|
c.ocspResponse = cs.response
|
|
|
|
msg, err = c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
keyAgreement := hs.suite.ka(c.vers)
|
|
|
|
skx, ok := msg.(*serverKeyExchangeMsg)
|
|
if ok {
|
|
hs.finishedHash.Write(skx.marshal())
|
|
err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx)
|
|
if err != nil {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return err
|
|
}
|
|
|
|
msg, err = c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var chainToSend *Certificate
|
|
var certRequested bool
|
|
certReq, ok := msg.(*certificateRequestMsg)
|
|
if ok {
|
|
certRequested = true
|
|
hs.finishedHash.Write(certReq.marshal())
|
|
|
|
cri := certificateRequestInfoFromMsg(certReq)
|
|
if chainToSend, err = c.getClientCertificate(cri); err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
msg, err = c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
shd, ok := msg.(*serverHelloDoneMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(shd, msg)
|
|
}
|
|
hs.finishedHash.Write(shd.marshal())
|
|
|
|
// 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 {
|
|
certMsg = new(certificateMsg)
|
|
certMsg.certificates = chainToSend.Certificate
|
|
hs.finishedHash.Write(certMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0])
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
if ckx != nil {
|
|
hs.finishedHash.Write(ckx.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if chainToSend != nil && len(chainToSend.Certificate) > 0 {
|
|
certVerify := &certificateVerifyMsg{
|
|
hasSignatureAlgorithm: c.vers >= VersionTLS12,
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(key.Public(), certReq.supportedSignatureAlgorithms, hs.hello.supportedSignatureAlgorithms, c.vers)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
// SignatureAndHashAlgorithm was introduced in TLS 1.2.
|
|
if certVerify.hasSignatureAlgorithm {
|
|
certVerify.signatureAlgorithm = signatureAlgorithm
|
|
}
|
|
signed, err := hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
signOpts := crypto.SignerOpts(hashFunc)
|
|
if sigType == signatureRSAPSS {
|
|
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: hashFunc}
|
|
}
|
|
certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
hs.finishedHash.Write(certVerify.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
|
|
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())
|
|
}
|
|
|
|
hs.finishedHash.discardHandshakeBuffer()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeState) establishKeys() error {
|
|
c := hs.c
|
|
|
|
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
|
|
keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
|
|
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)
|
|
} else {
|
|
clientCipher = hs.suite.aead(clientKey, clientIV)
|
|
serverCipher = hs.suite.aead(serverKey, serverIV)
|
|
}
|
|
|
|
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
|
|
c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
|
|
return nil
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
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")
|
|
}
|
|
|
|
if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported {
|
|
c.secureRenegotiation = true
|
|
if len(hs.serverHello.secureRenegotiation) != 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
|
|
}
|
|
}
|
|
|
|
if c.handshakes > 0 && c.secureRenegotiation {
|
|
var expectedSecureRenegotiation [24]byte
|
|
copy(expectedSecureRenegotiation[:], c.clientFinished[:])
|
|
copy(expectedSecureRenegotiation[12:], c.serverFinished[:])
|
|
if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return false, errors.New("tls: incorrect renegotiation extension contents")
|
|
}
|
|
}
|
|
|
|
clientDidNPN := hs.hello.nextProtoNeg
|
|
clientDidALPN := len(hs.hello.alpnProtocols) > 0
|
|
serverHasNPN := hs.serverHello.nextProtoNeg
|
|
serverHasALPN := len(hs.serverHello.alpnProtocol) > 0
|
|
|
|
if !clientDidNPN && serverHasNPN {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return false, errors.New("tls: server advertised unrequested NPN extension")
|
|
}
|
|
|
|
if !clientDidALPN && serverHasALPN {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return false, errors.New("tls: server advertised unrequested ALPN extension")
|
|
}
|
|
|
|
if serverHasNPN && serverHasALPN {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return false, errors.New("tls: server advertised both NPN and ALPN extensions")
|
|
}
|
|
|
|
if serverHasALPN {
|
|
c.clientProtocol = hs.serverHello.alpnProtocol
|
|
c.clientProtocolFallback = false
|
|
}
|
|
c.scts = hs.serverHello.scts
|
|
|
|
if !hs.serverResumedSession() {
|
|
return false, nil
|
|
}
|
|
|
|
if hs.session.vers != c.vers {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return false, errors.New("tls: server resumed a session with a different version")
|
|
}
|
|
|
|
if hs.session.cipherSuite != hs.suite.id {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return false, errors.New("tls: server resumed a session with a different cipher suite")
|
|
}
|
|
|
|
// Restore masterSecret and peerCerts from previous state
|
|
hs.masterSecret = hs.session.masterSecret
|
|
c.peerCertificates = hs.session.serverCertificates
|
|
c.verifiedChains = hs.session.verifiedChains
|
|
return true, nil
|
|
}
|
|
|
|
func (hs *clientHandshakeState) readFinished(out []byte) error {
|
|
c := hs.c
|
|
|
|
if err := c.readChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
serverFinished, ok := msg.(*finishedMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(serverFinished, msg)
|
|
}
|
|
|
|
verify := hs.finishedHash.serverSum(hs.masterSecret)
|
|
if len(verify) != len(serverFinished.verifyData) ||
|
|
subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: server's Finished message was incorrect")
|
|
}
|
|
hs.finishedHash.Write(serverFinished.marshal())
|
|
copy(out, verify)
|
|
return nil
|
|
}
|
|
|
|
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 {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(sessionTicketMsg, msg)
|
|
}
|
|
hs.finishedHash.Write(sessionTicketMsg.marshal())
|
|
|
|
hs.session = &ClientSessionState{
|
|
sessionTicket: sessionTicketMsg.ticket,
|
|
vers: c.vers,
|
|
cipherSuite: hs.suite.id,
|
|
masterSecret: hs.masterSecret,
|
|
serverCertificates: c.peerCertificates,
|
|
verifiedChains: c.verifiedChains,
|
|
receivedAt: c.config.time(),
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeState) sendFinished(out []byte) error {
|
|
c := hs.c
|
|
|
|
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
|
|
return err
|
|
}
|
|
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())
|
|
if _, err := c.writeRecord(recordTypeHandshake, nextProto.marshal()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
finished := new(finishedMsg)
|
|
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
|
|
hs.finishedHash.Write(finished.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
return err
|
|
}
|
|
copy(out, finished.verifyData)
|
|
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 _, cert := range certs[1:] {
|
|
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, ed25519.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}
|
|
tls11SignatureSchemesECDSA = tls11SignatureSchemes[:3]
|
|
tls11SignatureSchemesRSA = tls11SignatureSchemes[3:]
|
|
)
|
|
|
|
// 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, ecAvail bool
|
|
for _, certType := range certReq.certificateTypes {
|
|
switch certType {
|
|
case certTypeRSASign:
|
|
rsaAvail = true
|
|
case certTypeECDSASign:
|
|
ecAvail = true
|
|
}
|
|
}
|
|
|
|
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 && ecAvail:
|
|
cri.SignatureSchemes = tls11SignatureSchemes
|
|
case rsaAvail:
|
|
cri.SignatureSchemes = tls11SignatureSchemesRSA
|
|
case ecAvail:
|
|
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, signatureEd25519:
|
|
if ecAvail {
|
|
cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
|
|
}
|
|
case signatureRSAPSS, signaturePKCS1v15:
|
|
if rsaAvail {
|
|
cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 AcceptableCAs.
|
|
for i, chain := range c.config.Certificates {
|
|
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.
|
|
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 configured certificate chain #" + strconv.Itoa(i) + ": " + err.Error())
|
|
}
|
|
}
|
|
|
|
for _, ca := range cri.AcceptableCAs {
|
|
if bytes.Equal(x509Cert.RawIssuer, ca) {
|
|
return &chain, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// No acceptable certificate found. Don't send a certificate.
|
|
return new(Certificate), nil
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
|
|
// 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
|
|
// indicating if the fallback case was reached.
|
|
func mutualProtocol(protos, preferenceProtos []string) (string, bool) {
|
|
for _, s := range preferenceProtos {
|
|
for _, c := range protos {
|
|
if s == c {
|
|
return s, false
|
|
}
|
|
}
|
|
}
|
|
|
|
return protos[0], true
|
|
}
|
|
|
|
// hostnameInSNI converts name into an approriate hostname for SNI.
|
|
// Literal IP addresses and absolute FQDNs are not permitted as SNI values.
|
|
// See RFC 6066, 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 ""
|
|
}
|
|
for len(name) > 0 && name[len(name)-1] == '.' {
|
|
name = name[:len(name)-1]
|
|
}
|
|
return name
|
|
}
|