mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-04 04:27:36 +03:00
We now have a (well, two, depending on AES hardware support) universal cipher suite preference order, based on their security and performance. Peer and application lists are now treated as filters (and AES hardware support hints) that are applied to this universal order. This removes a complex and nuanced decision from the application's responsibilities, one which we are better equipped to make and which applications usually don't need to have an opinion about. It also lets us worry less about what suites we support or enable, because we can be confident that bad ones won't be selected over good ones. This also moves 3DES suites to InsecureCipherSuites(), even if they are not disabled by default. Just because we can keep them as a last resort it doesn't mean they are secure. Thankfully we had not promised that Insecure means disabled by default. Notable test changes: - TestCipherSuiteCertPreferenceECDSA was testing that we'd pick the right certificate regardless of CipherSuite ordering, which is now completely ignored, as tested by TestCipherSuitePreference. Removed. - The openssl command of TestHandshakeServerExportKeyingMaterial was broken for TLS 1.0 in CL 262857, but its golden file was not regenerated, so the test kept passing. It now broke because the selected suite from the ones in the golden file changed. - In TestAESCipherReordering, "server strongly prefers AES-GCM" is removed because there is no way for a server to express a strong preference anymore; "client prefers AES-GCM and AES-CBC over ChaCha" switched to ChaCha20 when the server lacks AES hardware; and finally "client supports multiple AES-GCM" changed to always prefer AES-128 per the universal preference list. * this is going back on an explicit decision from CL 262857, and while that client order is weird and does suggest a strong dislike for ChaCha20, we have a strong dislike for software AES, so it didn't feel worth making the logic more complex - All Client-* golden files had to be regenerated because the ClientHello cipher suites have changed. (Even when Config.CipherSuites was limited to one suite, the TLS 1.3 default order changed.) Fixes #45430 Fixes #41476 (as 3DES is now always the last resort) Change-Id: If5f5d356c0f8d1f1c7542fb06644a478d6bad1e5 Reviewed-on: https://go-review.googlesource.com/c/go/+/314609 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> Trust: Filippo Valsorda <filippo@golang.org>
849 lines
23 KiB
Go
849 lines
23 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 (
|
|
"context"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/ed25519"
|
|
"crypto/rsa"
|
|
"crypto/subtle"
|
|
"crypto/x509"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// serverHandshakeState contains details of a server handshake in progress.
|
|
// It's discarded once the handshake has completed.
|
|
type serverHandshakeState struct {
|
|
c *Conn
|
|
ctx context.Context
|
|
clientHello *clientHelloMsg
|
|
hello *serverHelloMsg
|
|
suite *cipherSuite
|
|
ecdheOk bool
|
|
ecSignOk bool
|
|
rsaDecryptOk bool
|
|
rsaSignOk bool
|
|
sessionState *sessionState
|
|
finishedHash finishedHash
|
|
masterSecret []byte
|
|
cert *Certificate
|
|
}
|
|
|
|
// serverHandshake performs a TLS handshake as a server.
|
|
func (c *Conn) serverHandshake(ctx context.Context) error {
|
|
clientHello, err := c.readClientHello(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.vers == VersionTLS13 {
|
|
hs := serverHandshakeStateTLS13{
|
|
c: c,
|
|
ctx: ctx,
|
|
clientHello: clientHello,
|
|
}
|
|
return hs.handshake()
|
|
}
|
|
|
|
hs := serverHandshakeState{
|
|
c: c,
|
|
ctx: ctx,
|
|
clientHello: clientHello,
|
|
}
|
|
return hs.handshake()
|
|
}
|
|
|
|
func (hs *serverHandshakeState) handshake() error {
|
|
c := hs.c
|
|
|
|
if err := hs.processClientHello(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// For an overview of TLS handshaking, see RFC 5246, Section 7.3.
|
|
c.buffering = true
|
|
if hs.checkForResumption() {
|
|
// The client has included a session ticket and so we do an abbreviated handshake.
|
|
c.didResume = true
|
|
if err := hs.doResumeHandshake(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.establishKeys(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendSessionTicket(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendFinished(c.serverFinished[:]); err != nil {
|
|
return err
|
|
}
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
c.clientFinishedIsFirst = false
|
|
if err := hs.readFinished(nil); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// The client didn't include a session ticket, or it wasn't
|
|
// valid so we do a full handshake.
|
|
if err := hs.pickCipherSuite(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.doFullHandshake(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.establishKeys(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readFinished(c.clientFinished[:]); err != nil {
|
|
return err
|
|
}
|
|
c.clientFinishedIsFirst = true
|
|
c.buffering = true
|
|
if err := hs.sendSessionTicket(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendFinished(nil); err != nil {
|
|
return err
|
|
}
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
|
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
|
|
|
return nil
|
|
}
|
|
|
|
// readClientHello reads a ClientHello message and selects the protocol version.
|
|
func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clientHello, ok := msg.(*clientHelloMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return nil, unexpectedMessageError(clientHello, msg)
|
|
}
|
|
|
|
var configForClient *Config
|
|
originalConfig := c.config
|
|
if c.config.GetConfigForClient != nil {
|
|
chi := clientHelloInfo(ctx, c, clientHello)
|
|
if configForClient, err = c.config.GetConfigForClient(chi); err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return nil, err
|
|
} else if configForClient != nil {
|
|
c.config = configForClient
|
|
}
|
|
}
|
|
c.ticketKeys = originalConfig.ticketKeys(configForClient)
|
|
|
|
clientVersions := clientHello.supportedVersions
|
|
if len(clientHello.supportedVersions) == 0 {
|
|
clientVersions = supportedVersionsFromMax(clientHello.vers)
|
|
}
|
|
c.vers, ok = c.config.mutualVersion(clientVersions)
|
|
if !ok {
|
|
c.sendAlert(alertProtocolVersion)
|
|
return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
|
|
}
|
|
c.haveVers = true
|
|
c.in.version = c.vers
|
|
c.out.version = c.vers
|
|
|
|
return clientHello, nil
|
|
}
|
|
|
|
func (hs *serverHandshakeState) processClientHello() error {
|
|
c := hs.c
|
|
|
|
hs.hello = new(serverHelloMsg)
|
|
hs.hello.vers = c.vers
|
|
|
|
foundCompression := false
|
|
// We only support null compression, so check that the client offered it.
|
|
for _, compression := range hs.clientHello.compressionMethods {
|
|
if compression == compressionNone {
|
|
foundCompression = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !foundCompression {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: client does not support uncompressed connections")
|
|
}
|
|
|
|
hs.hello.random = make([]byte, 32)
|
|
serverRandom := hs.hello.random
|
|
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
|
|
maxVers := c.config.maxSupportedVersion()
|
|
if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
|
|
if c.vers == VersionTLS12 {
|
|
copy(serverRandom[24:], downgradeCanaryTLS12)
|
|
} else {
|
|
copy(serverRandom[24:], downgradeCanaryTLS11)
|
|
}
|
|
serverRandom = serverRandom[:24]
|
|
}
|
|
_, err := io.ReadFull(c.config.rand(), serverRandom)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
if len(hs.clientHello.secureRenegotiation) != 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: initial handshake had non-empty renegotiation extension")
|
|
}
|
|
|
|
hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
|
|
hs.hello.compressionMethod = compressionNone
|
|
if len(hs.clientHello.serverName) > 0 {
|
|
c.serverName = hs.clientHello.serverName
|
|
}
|
|
|
|
if len(c.config.NextProtos) > 0 && len(hs.clientHello.alpnProtocols) > 0 {
|
|
selectedProto := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos)
|
|
if selectedProto == "" {
|
|
c.sendAlert(alertNoApplicationProtocol)
|
|
return fmt.Errorf("tls: client requested unsupported application protocols (%s)", hs.clientHello.alpnProtocols)
|
|
}
|
|
hs.hello.alpnProtocol = selectedProto
|
|
c.clientProtocol = selectedProto
|
|
}
|
|
|
|
hs.cert, err = c.config.getCertificate(clientHelloInfo(hs.ctx, c, hs.clientHello))
|
|
if err != nil {
|
|
if err == errNoCertificates {
|
|
c.sendAlert(alertUnrecognizedName)
|
|
} else {
|
|
c.sendAlert(alertInternalError)
|
|
}
|
|
return err
|
|
}
|
|
if hs.clientHello.scts {
|
|
hs.hello.scts = hs.cert.SignedCertificateTimestamps
|
|
}
|
|
|
|
hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
|
|
|
|
if hs.ecdheOk {
|
|
// Although omitting the ec_point_formats extension is permitted, some
|
|
// old OpenSSL version will refuse to handshake if not present.
|
|
//
|
|
// Per RFC 4492, section 5.1.2, implementations MUST support the
|
|
// uncompressed point format. See golang.org/issue/31943.
|
|
hs.hello.supportedPoints = []uint8{pointFormatUncompressed}
|
|
}
|
|
|
|
if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
|
|
switch priv.Public().(type) {
|
|
case *ecdsa.PublicKey:
|
|
hs.ecSignOk = true
|
|
case ed25519.PublicKey:
|
|
hs.ecSignOk = true
|
|
case *rsa.PublicKey:
|
|
hs.rsaSignOk = true
|
|
default:
|
|
c.sendAlert(alertInternalError)
|
|
return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
|
|
}
|
|
}
|
|
if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
|
|
switch priv.Public().(type) {
|
|
case *rsa.PublicKey:
|
|
hs.rsaDecryptOk = true
|
|
default:
|
|
c.sendAlert(alertInternalError)
|
|
return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// supportsECDHE returns whether ECDHE key exchanges can be used with this
|
|
// pre-TLS 1.3 client.
|
|
func supportsECDHE(c *Config, supportedCurves []CurveID, supportedPoints []uint8) bool {
|
|
supportsCurve := false
|
|
for _, curve := range supportedCurves {
|
|
if c.supportsCurve(curve) {
|
|
supportsCurve = true
|
|
break
|
|
}
|
|
}
|
|
|
|
supportsPointFormat := false
|
|
for _, pointFormat := range supportedPoints {
|
|
if pointFormat == pointFormatUncompressed {
|
|
supportsPointFormat = true
|
|
break
|
|
}
|
|
}
|
|
|
|
return supportsCurve && supportsPointFormat
|
|
}
|
|
|
|
func (hs *serverHandshakeState) pickCipherSuite() error {
|
|
c := hs.c
|
|
|
|
preferenceOrder := cipherSuitesPreferenceOrder
|
|
if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
|
|
preferenceOrder = cipherSuitesPreferenceOrderNoAES
|
|
}
|
|
|
|
configCipherSuites := c.config.cipherSuites()
|
|
preferenceList := make([]uint16, 0, len(configCipherSuites))
|
|
for _, suiteID := range preferenceOrder {
|
|
for _, id := range configCipherSuites {
|
|
if id == suiteID {
|
|
preferenceList = append(preferenceList, id)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
hs.suite = selectCipherSuite(preferenceList, hs.clientHello.cipherSuites, hs.cipherSuiteOk)
|
|
if hs.suite == nil {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: no cipher suite supported by both client and server")
|
|
}
|
|
c.cipherSuite = hs.suite.id
|
|
|
|
for _, id := range hs.clientHello.cipherSuites {
|
|
if id == TLS_FALLBACK_SCSV {
|
|
// The client is doing a fallback connection. See RFC 7507.
|
|
if hs.clientHello.vers < c.config.maxSupportedVersion() {
|
|
c.sendAlert(alertInappropriateFallback)
|
|
return errors.New("tls: client using inappropriate protocol fallback")
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool {
|
|
if c.flags&suiteECDHE != 0 {
|
|
if !hs.ecdheOk {
|
|
return false
|
|
}
|
|
if c.flags&suiteECSign != 0 {
|
|
if !hs.ecSignOk {
|
|
return false
|
|
}
|
|
} else if !hs.rsaSignOk {
|
|
return false
|
|
}
|
|
} else if !hs.rsaDecryptOk {
|
|
return false
|
|
}
|
|
if hs.c.vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// checkForResumption reports whether we should perform resumption on this connection.
|
|
func (hs *serverHandshakeState) checkForResumption() bool {
|
|
c := hs.c
|
|
|
|
if c.config.SessionTicketsDisabled {
|
|
return false
|
|
}
|
|
|
|
plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket)
|
|
if plaintext == nil {
|
|
return false
|
|
}
|
|
hs.sessionState = &sessionState{usedOldKey: usedOldKey}
|
|
ok := hs.sessionState.unmarshal(plaintext)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
createdAt := time.Unix(int64(hs.sessionState.createdAt), 0)
|
|
if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
|
|
return false
|
|
}
|
|
|
|
// Never resume a session for a different TLS version.
|
|
if c.vers != hs.sessionState.vers {
|
|
return false
|
|
}
|
|
|
|
cipherSuiteOk := false
|
|
// Check that the client is still offering the ciphersuite in the session.
|
|
for _, id := range hs.clientHello.cipherSuites {
|
|
if id == hs.sessionState.cipherSuite {
|
|
cipherSuiteOk = true
|
|
break
|
|
}
|
|
}
|
|
if !cipherSuiteOk {
|
|
return false
|
|
}
|
|
|
|
// Check that we also support the ciphersuite from the session.
|
|
hs.suite = selectCipherSuite([]uint16{hs.sessionState.cipherSuite},
|
|
c.config.cipherSuites(), hs.cipherSuiteOk)
|
|
if hs.suite == nil {
|
|
return false
|
|
}
|
|
|
|
sessionHasClientCerts := len(hs.sessionState.certificates) != 0
|
|
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
|
if needClientCerts && !sessionHasClientCerts {
|
|
return false
|
|
}
|
|
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (hs *serverHandshakeState) doResumeHandshake() error {
|
|
c := hs.c
|
|
|
|
hs.hello.cipherSuite = hs.suite.id
|
|
c.cipherSuite = hs.suite.id
|
|
// We echo the client's session ID in the ServerHello to let it know
|
|
// that we're doing a resumption.
|
|
hs.hello.sessionId = hs.clientHello.sessionId
|
|
hs.hello.ticketSupported = hs.sessionState.usedOldKey
|
|
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
|
|
hs.finishedHash.discardHandshakeBuffer()
|
|
hs.finishedHash.Write(hs.clientHello.marshal())
|
|
hs.finishedHash.Write(hs.hello.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := c.processCertsFromClient(Certificate{
|
|
Certificate: hs.sessionState.certificates,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.config.VerifyConnection != nil {
|
|
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
c.sendAlert(alertBadCertificate)
|
|
return err
|
|
}
|
|
}
|
|
|
|
hs.masterSecret = hs.sessionState.masterSecret
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeState) doFullHandshake() error {
|
|
c := hs.c
|
|
|
|
if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
|
|
hs.hello.ocspStapling = true
|
|
}
|
|
|
|
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
|
|
hs.hello.cipherSuite = hs.suite.id
|
|
|
|
hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
|
|
if c.config.ClientAuth == NoClientCert {
|
|
// No need to keep a full record of the handshake if client
|
|
// certificates won't be used.
|
|
hs.finishedHash.discardHandshakeBuffer()
|
|
}
|
|
hs.finishedHash.Write(hs.clientHello.marshal())
|
|
hs.finishedHash.Write(hs.hello.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
certMsg := new(certificateMsg)
|
|
certMsg.certificates = hs.cert.Certificate
|
|
hs.finishedHash.Write(certMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if hs.hello.ocspStapling {
|
|
certStatus := new(certificateStatusMsg)
|
|
certStatus.response = hs.cert.OCSPStaple
|
|
hs.finishedHash.Write(certStatus.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
keyAgreement := hs.suite.ka(c.vers)
|
|
skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
|
|
if err != nil {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return err
|
|
}
|
|
if skx != nil {
|
|
hs.finishedHash.Write(skx.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var certReq *certificateRequestMsg
|
|
if c.config.ClientAuth >= RequestClientCert {
|
|
// Request a client certificate
|
|
certReq = new(certificateRequestMsg)
|
|
certReq.certificateTypes = []byte{
|
|
byte(certTypeRSASign),
|
|
byte(certTypeECDSASign),
|
|
}
|
|
if c.vers >= VersionTLS12 {
|
|
certReq.hasSignatureAlgorithm = true
|
|
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
|
|
}
|
|
|
|
// An empty list of certificateAuthorities signals to
|
|
// the client that it may send any certificate in response
|
|
// to our request. When we know the CAs we trust, then
|
|
// we can send them down, so that the client can choose
|
|
// an appropriate certificate to give to us.
|
|
if c.config.ClientCAs != nil {
|
|
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
|
|
}
|
|
hs.finishedHash.Write(certReq.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
helloDone := new(serverHelloDoneMsg)
|
|
hs.finishedHash.Write(helloDone.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
|
|
var pub crypto.PublicKey // public key for client auth, if any
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If we requested a client certificate, then the client must send a
|
|
// certificate message, even if it's empty.
|
|
if c.config.ClientAuth >= RequestClientCert {
|
|
certMsg, ok := msg.(*certificateMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(certMsg, msg)
|
|
}
|
|
hs.finishedHash.Write(certMsg.marshal())
|
|
|
|
if err := c.processCertsFromClient(Certificate{
|
|
Certificate: certMsg.certificates,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
if len(certMsg.certificates) != 0 {
|
|
pub = c.peerCertificates[0].PublicKey
|
|
}
|
|
|
|
msg, err = c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if c.config.VerifyConnection != nil {
|
|
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
c.sendAlert(alertBadCertificate)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Get client key exchange
|
|
ckx, ok := msg.(*clientKeyExchangeMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(ckx, msg)
|
|
}
|
|
hs.finishedHash.Write(ckx.marshal())
|
|
|
|
preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
|
|
if err != nil {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return err
|
|
}
|
|
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
|
|
if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
// If we received a client cert in response to our certificate request message,
|
|
// the client will send us a certificateVerifyMsg immediately after the
|
|
// clientKeyExchangeMsg. This message is a digest of all preceding
|
|
// handshake-layer messages that is signed using the private key corresponding
|
|
// to the client's certificate. This allows us to verify that the client is in
|
|
// possession of the private key of the certificate.
|
|
if len(c.peerCertificates) > 0 {
|
|
msg, err = c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
certVerify, ok := msg.(*certificateVerifyMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(certVerify, msg)
|
|
}
|
|
|
|
var sigType uint8
|
|
var sigHash crypto.Hash
|
|
if c.vers >= VersionTLS12 {
|
|
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client certificate used with invalid signature algorithm")
|
|
}
|
|
sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
|
|
if err != nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
} else {
|
|
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return err
|
|
}
|
|
}
|
|
|
|
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret)
|
|
if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
|
}
|
|
|
|
hs.finishedHash.Write(certVerify.marshal())
|
|
}
|
|
|
|
hs.finishedHash.discardHandshakeBuffer()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeState) establishKeys() error {
|
|
c := hs.c
|
|
|
|
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
|
|
keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
|
|
|
|
var clientCipher, serverCipher interface{}
|
|
var clientHash, serverHash hash.Hash
|
|
|
|
if hs.suite.aead == nil {
|
|
clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
|
|
clientHash = hs.suite.mac(clientMAC)
|
|
serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
|
|
serverHash = hs.suite.mac(serverMAC)
|
|
} else {
|
|
clientCipher = hs.suite.aead(clientKey, clientIV)
|
|
serverCipher = hs.suite.aead(serverKey, serverIV)
|
|
}
|
|
|
|
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
|
|
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeState) readFinished(out []byte) error {
|
|
c := hs.c
|
|
|
|
if err := c.readChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
clientFinished, ok := msg.(*finishedMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(clientFinished, msg)
|
|
}
|
|
|
|
verify := hs.finishedHash.clientSum(hs.masterSecret)
|
|
if len(verify) != len(clientFinished.verifyData) ||
|
|
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: client's Finished message is incorrect")
|
|
}
|
|
|
|
hs.finishedHash.Write(clientFinished.marshal())
|
|
copy(out, verify)
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeState) sendSessionTicket() error {
|
|
// ticketSupported is set in a resumption handshake if the
|
|
// ticket from the client was encrypted with an old session
|
|
// ticket key and thus a refreshed ticket should be sent.
|
|
if !hs.hello.ticketSupported {
|
|
return nil
|
|
}
|
|
|
|
c := hs.c
|
|
m := new(newSessionTicketMsg)
|
|
|
|
createdAt := uint64(c.config.time().Unix())
|
|
if hs.sessionState != nil {
|
|
// If this is re-wrapping an old key, then keep
|
|
// the original time it was created.
|
|
createdAt = hs.sessionState.createdAt
|
|
}
|
|
|
|
var certsFromClient [][]byte
|
|
for _, cert := range c.peerCertificates {
|
|
certsFromClient = append(certsFromClient, cert.Raw)
|
|
}
|
|
state := sessionState{
|
|
vers: c.vers,
|
|
cipherSuite: hs.suite.id,
|
|
createdAt: createdAt,
|
|
masterSecret: hs.masterSecret,
|
|
certificates: certsFromClient,
|
|
}
|
|
var err error
|
|
m.ticket, err = c.encryptTicket(state.marshal())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hs.finishedHash.Write(m.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeState) sendFinished(out []byte) error {
|
|
c := hs.c
|
|
|
|
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
|
|
return err
|
|
}
|
|
|
|
finished := new(finishedMsg)
|
|
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
|
|
hs.finishedHash.Write(finished.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
copy(out, finished.verifyData)
|
|
|
|
return nil
|
|
}
|
|
|
|
// processCertsFromClient takes a chain of client certificates either from a
|
|
// Certificates message or from a sessionState and verifies them. It returns
|
|
// the public key of the leaf certificate.
|
|
func (c *Conn) processCertsFromClient(certificate Certificate) error {
|
|
certificates := certificate.Certificate
|
|
certs := make([]*x509.Certificate, len(certificates))
|
|
var err error
|
|
for i, asn1Data := range certificates {
|
|
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
|
|
c.sendAlert(alertBadCertificate)
|
|
return errors.New("tls: failed to parse client certificate: " + err.Error())
|
|
}
|
|
}
|
|
|
|
if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
|
|
c.sendAlert(alertBadCertificate)
|
|
return errors.New("tls: client didn't provide a certificate")
|
|
}
|
|
|
|
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
|
|
opts := x509.VerifyOptions{
|
|
Roots: c.config.ClientCAs,
|
|
CurrentTime: c.config.time(),
|
|
Intermediates: x509.NewCertPool(),
|
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}
|
|
|
|
for _, cert := range certs[1:] {
|
|
opts.Intermediates.AddCert(cert)
|
|
}
|
|
|
|
chains, err := certs[0].Verify(opts)
|
|
if err != nil {
|
|
c.sendAlert(alertBadCertificate)
|
|
return errors.New("tls: failed to verify client certificate: " + err.Error())
|
|
}
|
|
|
|
c.verifiedChains = chains
|
|
}
|
|
|
|
c.peerCertificates = certs
|
|
c.ocspResponse = certificate.OCSPStaple
|
|
c.scts = certificate.SignedCertificateTimestamps
|
|
|
|
if len(certs) > 0 {
|
|
switch certs[0].PublicKey.(type) {
|
|
case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
|
|
default:
|
|
c.sendAlert(alertUnsupportedCertificate)
|
|
return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
|
|
}
|
|
}
|
|
|
|
if c.config.VerifyPeerCertificate != nil {
|
|
if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
|
|
c.sendAlert(alertBadCertificate)
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
|
|
supportedVersions := clientHello.supportedVersions
|
|
if len(clientHello.supportedVersions) == 0 {
|
|
supportedVersions = supportedVersionsFromMax(clientHello.vers)
|
|
}
|
|
|
|
return &ClientHelloInfo{
|
|
CipherSuites: clientHello.cipherSuites,
|
|
ServerName: clientHello.serverName,
|
|
SupportedCurves: clientHello.supportedCurves,
|
|
SupportedPoints: clientHello.supportedPoints,
|
|
SignatureSchemes: clientHello.supportedSignatureAlgorithms,
|
|
SupportedProtos: clientHello.alpnProtocols,
|
|
SupportedVersions: supportedVersions,
|
|
Conn: c.conn,
|
|
config: c.config,
|
|
ctx: ctx,
|
|
}
|
|
}
|