mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-03 20:17:36 +03:00
Looks like the introduction of CCS records in the client second flight gave time to s_server to send NewSessionTicket messages in between the client application data and close_notify. There seems to be no way of turning NewSessionTicket messages off, neither by not sending a psk_key_exchange_modes extension, nor by command line flag. Interleaving the client write like that tickled an issue akin to #18701: on Windows, the client reaches Close() before the last record is drained from the send buffer, the kernel notices and resets the connection, cutting short the last flow. There is no good way of synchronizing this, so we sleep for a RTT before calling close, like in CL 75210. Sigh. Updates #9671 Change-Id: I44dc1cca17b373695b5a18c2741f218af2990bd1 Reviewed-on: https://go-review.googlesource.com/c/147419 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
504 lines
15 KiB
Go
504 lines
15 KiB
Go
// Copyright 2018 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/hmac"
|
|
"crypto/rsa"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"sync/atomic"
|
|
)
|
|
|
|
type serverHandshakeStateTLS13 struct {
|
|
c *Conn
|
|
clientHello *clientHelloMsg
|
|
hello *serverHelloMsg
|
|
sentDummyCCS bool
|
|
suite *cipherSuiteTLS13
|
|
cert *Certificate
|
|
sigAlg SignatureScheme
|
|
handshakeSecret []byte
|
|
trafficSecret []byte // client_application_traffic_secret_0
|
|
transcript hash.Hash
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) handshake() error {
|
|
c := hs.c
|
|
|
|
// For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2.
|
|
if err := hs.processClientHello(); err != nil {
|
|
return err
|
|
}
|
|
c.buffering = true
|
|
if err := hs.sendServerParameters(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendServerCertificate(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendServerFinished(); err != nil {
|
|
return err
|
|
}
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readClientFinished(); err != nil {
|
|
return err
|
|
}
|
|
|
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
|
c := hs.c
|
|
|
|
hs.hello = new(serverHelloMsg)
|
|
|
|
// TLS 1.3 froze the ServerHello.legacy_version field, and uses
|
|
// supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1.
|
|
hs.hello.vers = VersionTLS12
|
|
hs.hello.supportedVersion = c.vers
|
|
|
|
if len(hs.clientHello.supportedVersions) == 0 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
|
|
}
|
|
|
|
if len(hs.clientHello.compressionMethods) != 1 ||
|
|
hs.clientHello.compressionMethods[0] != compressionNone {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: TLS 1.3 client supports illegal compression methods")
|
|
}
|
|
|
|
hs.hello.random = make([]byte, 32)
|
|
if _, err := io.ReadFull(c.config.rand(), hs.hello.random); 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")
|
|
}
|
|
|
|
if hs.clientHello.earlyData {
|
|
return errors.New("tls: early data skipping not implemented") // TODO(filippo)
|
|
}
|
|
|
|
hs.hello.sessionId = hs.clientHello.sessionId
|
|
hs.hello.compressionMethod = compressionNone
|
|
|
|
var preferenceList, supportedList []uint16
|
|
if c.config.PreferServerCipherSuites {
|
|
preferenceList = defaultCipherSuitesTLS13()
|
|
supportedList = hs.clientHello.cipherSuites
|
|
} else {
|
|
preferenceList = hs.clientHello.cipherSuites
|
|
supportedList = defaultCipherSuitesTLS13()
|
|
}
|
|
for _, suiteID := range preferenceList {
|
|
hs.suite = mutualCipherSuiteTLS13(supportedList, suiteID)
|
|
if hs.suite != nil {
|
|
break
|
|
}
|
|
}
|
|
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
|
|
hs.hello.cipherSuite = hs.suite.id
|
|
hs.transcript = hs.suite.hash.New()
|
|
|
|
// Pick the ECDHE group in server preference order, but give priority to
|
|
// groups with a key share, to avoid a HelloRetryRequest round-trip.
|
|
var selectedGroup CurveID
|
|
var clientKeyShare *keyShare
|
|
GroupSelection:
|
|
for _, preferredGroup := range c.config.curvePreferences() {
|
|
for _, ks := range hs.clientHello.keyShares {
|
|
if ks.group == preferredGroup {
|
|
selectedGroup = ks.group
|
|
clientKeyShare = &ks
|
|
break GroupSelection
|
|
}
|
|
}
|
|
if selectedGroup != 0 {
|
|
continue
|
|
}
|
|
for _, group := range hs.clientHello.supportedCurves {
|
|
if group == preferredGroup {
|
|
selectedGroup = group
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if selectedGroup == 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: no ECDHE curve supported by both client and server")
|
|
}
|
|
if clientKeyShare == nil {
|
|
if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
|
|
return err
|
|
}
|
|
clientKeyShare = &hs.clientHello.keyShares[0]
|
|
}
|
|
|
|
if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
}
|
|
params, err := generateECDHEParameters(c.config.rand(), selectedGroup)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()}
|
|
sharedKey := params.SharedKey(clientKeyShare.data)
|
|
if sharedKey == nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid client key share")
|
|
}
|
|
earlySecret := hs.suite.extract(nil, nil)
|
|
hs.handshakeSecret = hs.suite.extract(sharedKey,
|
|
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
|
|
|
// This implements a very simplistic certificate selection strategy for now:
|
|
// getCertificate delegates to the application Config.GetCertificate, or
|
|
// selects based on the server_name only. If the selected certificate's
|
|
// public key does not match the client signature_algorithms, the handshake
|
|
// is aborted. No attention is given to signature_algorithms_cert, and it is
|
|
// not passed to the application Config.GetCertificate. This will need to
|
|
// improve according to RFC 8446, sections 4.4.2.2 and 4.2.3.
|
|
certificate, err := c.config.getCertificate(clientHelloInfo(c, hs.clientHello))
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
supportedAlgs := signatureSchemesForCertificate(certificate)
|
|
if supportedAlgs == nil {
|
|
c.sendAlert(alertInternalError)
|
|
return fmt.Errorf("tls: unsupported certificate key (%T)", certificate.PrivateKey)
|
|
}
|
|
// Pick signature scheme in client preference order, as the server
|
|
// preference order is not configurable.
|
|
for _, preferredAlg := range hs.clientHello.supportedSignatureAlgorithms {
|
|
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
|
hs.sigAlg = preferredAlg
|
|
break
|
|
}
|
|
}
|
|
if hs.sigAlg == 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: client doesn't support selected certificate")
|
|
}
|
|
hs.cert = certificate
|
|
|
|
return nil
|
|
}
|
|
|
|
// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
|
|
// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
|
|
func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
|
if hs.sentDummyCCS {
|
|
return nil
|
|
}
|
|
hs.sentDummyCCS = true
|
|
|
|
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
|
return err
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
|
|
c := hs.c
|
|
|
|
// The first ClientHello gets double-hashed into the transcript upon a
|
|
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
|
hs.transcript.Write(hs.clientHello.marshal())
|
|
chHash := hs.transcript.Sum(nil)
|
|
hs.transcript.Reset()
|
|
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
hs.transcript.Write(chHash)
|
|
|
|
helloRetryRequest := &serverHelloMsg{
|
|
vers: hs.hello.vers,
|
|
random: helloRetryRequestRandom,
|
|
sessionId: hs.hello.sessionId,
|
|
cipherSuite: hs.hello.cipherSuite,
|
|
compressionMethod: hs.hello.compressionMethod,
|
|
supportedVersion: hs.hello.supportedVersion,
|
|
selectedGroup: selectedGroup,
|
|
}
|
|
|
|
hs.transcript.Write(helloRetryRequest.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientHello, ok := msg.(*clientHelloMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(clientHello, msg)
|
|
}
|
|
|
|
if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client sent invalid key share in second ClientHello")
|
|
}
|
|
|
|
if clientHello.earlyData {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client indicated early data in second ClientHello")
|
|
}
|
|
|
|
if illegalClientHelloChange(clientHello, hs.clientHello) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client illegally modified second ClientHello")
|
|
}
|
|
|
|
hs.clientHello = clientHello
|
|
return nil
|
|
}
|
|
|
|
// illegalClientHelloChange returns whether the two ClientHello messages are
|
|
// different, with the exception of the changes allowed before and after a
|
|
// HelloRetryRequest. See RFC 8446, Section 4.1.2.
|
|
func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool {
|
|
if len(ch.supportedVersions) != len(ch1.supportedVersions) ||
|
|
len(ch.cipherSuites) != len(ch1.cipherSuites) ||
|
|
len(ch.supportedCurves) != len(ch1.supportedCurves) ||
|
|
len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) ||
|
|
len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) ||
|
|
len(ch.alpnProtocols) != len(ch1.alpnProtocols) {
|
|
return true
|
|
}
|
|
for i := range ch.supportedVersions {
|
|
if ch.supportedVersions[i] != ch1.supportedVersions[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.cipherSuites {
|
|
if ch.cipherSuites[i] != ch1.cipherSuites[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.supportedCurves {
|
|
if ch.supportedCurves[i] != ch1.supportedCurves[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.supportedSignatureAlgorithms {
|
|
if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.supportedSignatureAlgorithmsCert {
|
|
if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.alpnProtocols {
|
|
if ch.alpnProtocols[i] != ch1.alpnProtocols[i] {
|
|
return true
|
|
}
|
|
}
|
|
return ch.vers != ch1.vers ||
|
|
!bytes.Equal(ch.random, ch1.random) ||
|
|
!bytes.Equal(ch.sessionId, ch1.sessionId) ||
|
|
!bytes.Equal(ch.compressionMethods, ch1.compressionMethods) ||
|
|
ch.nextProtoNeg != ch1.nextProtoNeg ||
|
|
ch.serverName != ch1.serverName ||
|
|
ch.ocspStapling != ch1.ocspStapling ||
|
|
!bytes.Equal(ch.supportedPoints, ch1.supportedPoints) ||
|
|
ch.ticketSupported != ch1.ticketSupported ||
|
|
!bytes.Equal(ch.sessionTicket, ch1.sessionTicket) ||
|
|
ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported ||
|
|
!bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) ||
|
|
ch.scts != ch1.scts ||
|
|
!bytes.Equal(ch.cookie, ch1.cookie) ||
|
|
!bytes.Equal(ch.pskModes, ch1.pskModes)
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
|
|
c := hs.c
|
|
|
|
hs.transcript.Write(hs.clientHello.marshal())
|
|
hs.transcript.Write(hs.hello.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
clientHandshakeTrafficLabel, hs.transcript)
|
|
c.in.setTrafficSecret(hs.suite, clientSecret)
|
|
serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
serverHandshakeTrafficLabel, hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, serverSecret)
|
|
|
|
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
encryptedExtensions := new(encryptedExtensionsMsg)
|
|
|
|
if len(hs.clientHello.alpnProtocols) > 0 {
|
|
if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
|
|
encryptedExtensions.alpnProtocol = selectedProto
|
|
c.clientProtocol = selectedProto
|
|
}
|
|
}
|
|
|
|
hs.transcript.Write(encryptedExtensions.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
|
|
c := hs.c
|
|
|
|
certMsg := new(certificateMsgTLS13)
|
|
|
|
certMsg.certificate = *hs.cert
|
|
certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0
|
|
certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0
|
|
|
|
hs.transcript.Write(certMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
certVerifyMsg := new(certificateVerifyMsg)
|
|
certVerifyMsg.hasSignatureAlgorithm = true
|
|
certVerifyMsg.signatureAlgorithm = hs.sigAlg
|
|
|
|
sigType := signatureFromSignatureScheme(hs.sigAlg)
|
|
sigHash, err := hashFromSignatureScheme(hs.sigAlg)
|
|
if sigType == 0 || err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
h := sigHash.New()
|
|
writeSignedMessage(h, serverSignatureContext, hs.transcript)
|
|
|
|
signOpts := crypto.SignerOpts(sigHash)
|
|
if sigType == signatureRSAPSS {
|
|
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
|
}
|
|
sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), h.Sum(nil), signOpts)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: failed to sign handshake: " + err.Error())
|
|
}
|
|
certVerifyMsg.signature = sig
|
|
|
|
hs.transcript.Write(certVerifyMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
|
|
c := hs.c
|
|
|
|
// See RFC 8446, sections 4.4.4 and 4.4.
|
|
finishedKey := hs.suite.expandLabel(c.out.trafficSecret, "finished", nil, hs.suite.hash.Size())
|
|
verifyData := hmac.New(hs.suite.hash.New, finishedKey)
|
|
verifyData.Write(hs.transcript.Sum(nil))
|
|
finished := &finishedMsg{
|
|
verifyData: verifyData.Sum(nil),
|
|
}
|
|
|
|
hs.transcript.Write(finished.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Derive secrets that take context through the server Finished.
|
|
|
|
masterSecret := hs.suite.extract(nil,
|
|
hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
|
|
|
|
hs.trafficSecret = hs.suite.deriveSecret(masterSecret,
|
|
clientApplicationTrafficLabel, hs.transcript)
|
|
serverSecret := hs.suite.deriveSecret(masterSecret,
|
|
serverApplicationTrafficLabel, hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, serverSecret)
|
|
|
|
err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
c.ekm = hs.suite.exportKeyingMaterial(masterSecret, hs.transcript)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) readClientFinished() error {
|
|
c := hs.c
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
finished, ok := msg.(*finishedMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(finished, msg)
|
|
}
|
|
|
|
finishedKey := hs.suite.expandLabel(c.in.trafficSecret, "finished", nil, hs.suite.hash.Size())
|
|
expectedMAC := hmac.New(hs.suite.hash.New, finishedKey)
|
|
expectedMAC.Write(hs.transcript.Sum(nil))
|
|
if !hmac.Equal(expectedMAC.Sum(nil), finished.verifyData) {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid client finished hash")
|
|
}
|
|
|
|
hs.transcript.Write(finished.marshal())
|
|
|
|
c.in.setTrafficSecret(hs.suite, hs.trafficSecret)
|
|
|
|
return nil
|
|
}
|