mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-01 19:17:36 +03:00
Go 1.23 adds two new events to QUICConns: QUICStoreSessionEvent and QUICResumeSessionEvent. We added a QUICConfig.EnableStoreSessionEvent flag to control whether the store-session event is provided or not, because receiving this event requires additional action from the caller: the session must be explicitly stored with QUICConn.StoreSession. We did not add a control for whether the resume-session event is provided, because this event requires no action and the caller is expected to ignore unknown events. However, we never documented the expectation that callers ignore unknown events, and quic-go produces an error when receiving an unexpected event. So change the EnableStoreSessionEvent flag to apply to both new events. Fixes #68124 For #63691 Change-Id: I84af487e52b3815f7b648e09884608f8915cd645 Reviewed-on: https://go-review.googlesource.com/c/go/+/594475 Reviewed-by: Marten Seemann <martenseemann@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Roland Shoemaker <roland@golang.org>
913 lines
28 KiB
Go
913 lines
28 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"
|
|
"context"
|
|
"crypto"
|
|
"crypto/hmac"
|
|
"crypto/internal/mlkem768"
|
|
"crypto/rsa"
|
|
"crypto/subtle"
|
|
"errors"
|
|
"hash"
|
|
"slices"
|
|
"time"
|
|
)
|
|
|
|
type clientHandshakeStateTLS13 struct {
|
|
c *Conn
|
|
ctx context.Context
|
|
serverHello *serverHelloMsg
|
|
hello *clientHelloMsg
|
|
keyShareKeys *keySharePrivateKeys
|
|
|
|
session *SessionState
|
|
earlySecret []byte
|
|
binderKey []byte
|
|
|
|
certReq *certificateRequestMsgTLS13
|
|
usingPSK bool
|
|
sentDummyCCS bool
|
|
suite *cipherSuiteTLS13
|
|
transcript hash.Hash
|
|
masterSecret []byte
|
|
trafficSecret []byte // client_application_traffic_secret_0
|
|
|
|
echContext *echContext
|
|
}
|
|
|
|
// handshake requires hs.c, hs.hello, hs.serverHello, hs.keyShareKeys, and,
|
|
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
|
func (hs *clientHandshakeStateTLS13) handshake() error {
|
|
c := hs.c
|
|
|
|
if needFIPS() {
|
|
return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
|
|
}
|
|
|
|
// The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
|
|
// sections 4.1.2 and 4.1.3.
|
|
if c.handshakes > 0 {
|
|
c.sendAlert(alertProtocolVersion)
|
|
return errors.New("tls: server selected TLS 1.3 in a renegotiation")
|
|
}
|
|
|
|
// Consistency check on the presence of a keyShare and its parameters.
|
|
if hs.keyShareKeys == nil || hs.keyShareKeys.ecdhe == nil || len(hs.hello.keyShares) == 0 {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
|
|
if err := hs.checkServerHelloOrHRR(); err != nil {
|
|
return err
|
|
}
|
|
|
|
hs.transcript = hs.suite.hash.New()
|
|
|
|
if err := transcriptMsg(hs.hello, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
if hs.echContext != nil {
|
|
hs.echContext.innerTranscript = hs.suite.hash.New()
|
|
if err := transcriptMsg(hs.echContext.innerHello, hs.echContext.innerTranscript); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.processHelloRetryRequest(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var echRetryConfigList []byte
|
|
if hs.echContext != nil {
|
|
confTranscript := cloneHash(hs.echContext.innerTranscript, hs.suite.hash)
|
|
confTranscript.Write(hs.serverHello.original[:30])
|
|
confTranscript.Write(make([]byte, 8))
|
|
confTranscript.Write(hs.serverHello.original[38:])
|
|
acceptConfirmation := hs.suite.expandLabel(
|
|
hs.suite.extract(hs.echContext.innerHello.random, nil),
|
|
"ech accept confirmation",
|
|
confTranscript.Sum(nil),
|
|
8,
|
|
)
|
|
if subtle.ConstantTimeCompare(acceptConfirmation, hs.serverHello.random[len(hs.serverHello.random)-8:]) == 1 {
|
|
hs.hello = hs.echContext.innerHello
|
|
c.serverName = c.config.ServerName
|
|
hs.transcript = hs.echContext.innerTranscript
|
|
c.echAccepted = true
|
|
|
|
if hs.serverHello.encryptedClientHello != nil {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: unexpected encrypted_client_hello extension in server hello despite ECH being accepted")
|
|
}
|
|
|
|
if hs.hello.serverName == "" && hs.serverHello.serverNameAck {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: unexpected server_name extension in server hello")
|
|
}
|
|
} else {
|
|
hs.echContext.echRejected = true
|
|
// If the server sent us retry configs, we'll return these to
|
|
// the user so they can update their Config.
|
|
echRetryConfigList = hs.serverHello.encryptedClientHello
|
|
}
|
|
}
|
|
|
|
if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.buffering = true
|
|
if err := hs.processServerHello(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.establishHandshakeKeys(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readServerParameters(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readServerCertificate(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readServerFinished(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendClientCertificate(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendClientFinished(); err != nil {
|
|
return err
|
|
}
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if hs.echContext != nil && hs.echContext.echRejected {
|
|
c.sendAlert(alertECHRequired)
|
|
return &ECHRejectionError{echRetryConfigList}
|
|
}
|
|
|
|
c.isHandshakeComplete.Store(true)
|
|
|
|
return nil
|
|
}
|
|
|
|
// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
|
|
// HelloRetryRequest messages. It sets hs.suite.
|
|
func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
|
|
c := hs.c
|
|
|
|
if hs.serverHello.supportedVersion == 0 {
|
|
c.sendAlert(alertMissingExtension)
|
|
return errors.New("tls: server selected TLS 1.3 using the legacy version field")
|
|
}
|
|
|
|
if hs.serverHello.supportedVersion != VersionTLS13 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
|
|
}
|
|
|
|
if hs.serverHello.vers != VersionTLS12 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server sent an incorrect legacy version")
|
|
}
|
|
|
|
if hs.serverHello.ocspStapling ||
|
|
hs.serverHello.ticketSupported ||
|
|
hs.serverHello.extendedMasterSecret ||
|
|
hs.serverHello.secureRenegotiationSupported ||
|
|
len(hs.serverHello.secureRenegotiation) != 0 ||
|
|
len(hs.serverHello.alpnProtocol) != 0 ||
|
|
len(hs.serverHello.scts) != 0 {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
|
|
}
|
|
|
|
if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server did not echo the legacy session ID")
|
|
}
|
|
|
|
if hs.serverHello.compressionMethod != compressionNone {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server selected unsupported compression format")
|
|
}
|
|
|
|
selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
|
|
if hs.suite != nil && selectedSuite != hs.suite {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
|
|
}
|
|
if selectedSuite == nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server chose an unconfigured cipher suite")
|
|
}
|
|
hs.suite = selectedSuite
|
|
c.cipherSuite = hs.suite.id
|
|
|
|
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 *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
|
if hs.c.quic != nil {
|
|
return nil
|
|
}
|
|
if hs.sentDummyCCS {
|
|
return nil
|
|
}
|
|
hs.sentDummyCCS = true
|
|
|
|
return hs.c.writeChangeCipherRecord()
|
|
}
|
|
|
|
// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
|
|
// resends hs.hello, and reads the new ServerHello into hs.serverHello.
|
|
func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
|
c := hs.c
|
|
|
|
// The first ClientHello gets double-hashed into the transcript upon a
|
|
// HelloRetryRequest. (The idea is that the server might offload transcript
|
|
// storage to the client in the cookie.) See RFC 8446, Section 4.4.1.
|
|
chHash := hs.transcript.Sum(nil)
|
|
hs.transcript.Reset()
|
|
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
hs.transcript.Write(chHash)
|
|
if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
var isInnerHello bool
|
|
hello := hs.hello
|
|
if hs.echContext != nil {
|
|
chHash = hs.echContext.innerTranscript.Sum(nil)
|
|
hs.echContext.innerTranscript.Reset()
|
|
hs.echContext.innerTranscript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
hs.echContext.innerTranscript.Write(chHash)
|
|
|
|
if hs.serverHello.encryptedClientHello != nil {
|
|
if len(hs.serverHello.encryptedClientHello) != 8 {
|
|
hs.c.sendAlert(alertDecodeError)
|
|
return errors.New("tls: malformed encrypted client hello extension")
|
|
}
|
|
|
|
confTranscript := cloneHash(hs.echContext.innerTranscript, hs.suite.hash)
|
|
hrrHello := make([]byte, len(hs.serverHello.original))
|
|
copy(hrrHello, hs.serverHello.original)
|
|
hrrHello = bytes.Replace(hrrHello, hs.serverHello.encryptedClientHello, make([]byte, 8), 1)
|
|
confTranscript.Write(hrrHello)
|
|
acceptConfirmation := hs.suite.expandLabel(
|
|
hs.suite.extract(hs.echContext.innerHello.random, nil),
|
|
"hrr ech accept confirmation",
|
|
confTranscript.Sum(nil),
|
|
8,
|
|
)
|
|
if subtle.ConstantTimeCompare(acceptConfirmation, hs.serverHello.encryptedClientHello) == 1 {
|
|
hello = hs.echContext.innerHello
|
|
c.serverName = c.config.ServerName
|
|
isInnerHello = true
|
|
c.echAccepted = true
|
|
}
|
|
}
|
|
|
|
if err := transcriptMsg(hs.serverHello, hs.echContext.innerTranscript); err != nil {
|
|
return err
|
|
}
|
|
} else if hs.serverHello.encryptedClientHello != nil {
|
|
// Unsolicited ECH extension should be rejected
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: unexpected ECH extension in serverHello")
|
|
}
|
|
|
|
// The only HelloRetryRequest extensions we support are key_share and
|
|
// cookie, and clients must abort the handshake if the HRR would not result
|
|
// in any change in the ClientHello.
|
|
if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
|
|
}
|
|
|
|
if hs.serverHello.cookie != nil {
|
|
hello.cookie = hs.serverHello.cookie
|
|
}
|
|
|
|
if hs.serverHello.serverShare.group != 0 {
|
|
c.sendAlert(alertDecodeError)
|
|
return errors.New("tls: received malformed key_share extension")
|
|
}
|
|
|
|
// If the server sent a key_share extension selecting a group, ensure it's
|
|
// a group we advertised but did not send a key share for, and send a key
|
|
// share for it this time.
|
|
if curveID := hs.serverHello.selectedGroup; curveID != 0 {
|
|
if !slices.Contains(hello.supportedCurves, curveID) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server selected unsupported group")
|
|
}
|
|
if slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
|
|
return ks.group == curveID
|
|
}) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
|
}
|
|
// Note: we don't support selecting X25519Kyber768Draft00 in a HRR,
|
|
// because we currently only support it at all when CurvePreferences is
|
|
// empty, which will cause us to also send a key share for it.
|
|
//
|
|
// This will have to change once we support selecting hybrid KEMs
|
|
// without sending key shares for them.
|
|
if _, ok := curveForCurveID(curveID); !ok {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
}
|
|
key, err := generateECDHEKey(c.config.rand(), curveID)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
hs.keyShareKeys = &keySharePrivateKeys{curveID: curveID, ecdhe: key}
|
|
hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
|
|
}
|
|
|
|
if len(hello.pskIdentities) > 0 {
|
|
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
|
if pskSuite == nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
if pskSuite.hash == hs.suite.hash {
|
|
// Update binders and obfuscated_ticket_age.
|
|
ticketAge := c.config.time().Sub(time.Unix(int64(hs.session.createdAt), 0))
|
|
hello.pskIdentities[0].obfuscatedTicketAge = uint32(ticketAge/time.Millisecond) + hs.session.ageAdd
|
|
|
|
transcript := hs.suite.hash.New()
|
|
transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
transcript.Write(chHash)
|
|
if err := transcriptMsg(hs.serverHello, transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := computeAndUpdatePSK(hello, hs.binderKey, transcript, hs.suite.finishedHash); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// Server selected a cipher suite incompatible with the PSK.
|
|
hello.pskIdentities = nil
|
|
hello.pskBinders = nil
|
|
}
|
|
}
|
|
|
|
if hello.earlyData {
|
|
hello.earlyData = false
|
|
c.quicRejectedEarlyData()
|
|
}
|
|
|
|
if isInnerHello {
|
|
// Any extensions which have changed in hello, but are mirrored in the
|
|
// outer hello and compressed, need to be copied to the outer hello, so
|
|
// they can be properly decompressed by the server. For now, the only
|
|
// extension which may have changed is keyShares.
|
|
hs.hello.keyShares = hello.keyShares
|
|
hs.echContext.innerHello = hello
|
|
if err := transcriptMsg(hs.echContext.innerHello, hs.echContext.innerTranscript); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := computeAndUpdateOuterECHExtension(hs.hello, hs.echContext.innerHello, hs.echContext, false); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
hs.hello = hello
|
|
}
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
// serverHelloMsg is not included in the transcript
|
|
msg, err := c.readHandshake(nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
serverHello, ok := msg.(*serverHelloMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(serverHello, msg)
|
|
}
|
|
hs.serverHello = serverHello
|
|
|
|
if err := hs.checkServerHelloOrHRR(); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.didHRR = true
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
|
c := hs.c
|
|
|
|
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return errors.New("tls: server sent two HelloRetryRequest messages")
|
|
}
|
|
|
|
if len(hs.serverHello.cookie) != 0 {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: server sent a cookie in a normal ServerHello")
|
|
}
|
|
|
|
if hs.serverHello.selectedGroup != 0 {
|
|
c.sendAlert(alertDecodeError)
|
|
return errors.New("tls: malformed key_share extension")
|
|
}
|
|
|
|
if hs.serverHello.serverShare.group == 0 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server did not send a key share")
|
|
}
|
|
if !slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
|
|
return ks.group == hs.serverHello.serverShare.group
|
|
}) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server selected unsupported group")
|
|
}
|
|
|
|
if !hs.serverHello.selectedIdentityPresent {
|
|
return nil
|
|
}
|
|
|
|
if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server selected an invalid PSK")
|
|
}
|
|
|
|
if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
|
if pskSuite == nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
if pskSuite.hash != hs.suite.hash {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server selected an invalid PSK and cipher suite pair")
|
|
}
|
|
|
|
hs.usingPSK = true
|
|
c.didResume = true
|
|
c.peerCertificates = hs.session.peerCertificates
|
|
c.activeCertHandles = hs.session.activeCertHandles
|
|
c.verifiedChains = hs.session.verifiedChains
|
|
c.ocspResponse = hs.session.ocspResponse
|
|
c.scts = hs.session.scts
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
|
c := hs.c
|
|
|
|
ecdhePeerData := hs.serverHello.serverShare.data
|
|
if hs.serverHello.serverShare.group == x25519Kyber768Draft00 {
|
|
if len(ecdhePeerData) != x25519PublicKeySize+mlkem768.CiphertextSize {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid server key share")
|
|
}
|
|
ecdhePeerData = hs.serverHello.serverShare.data[:x25519PublicKeySize]
|
|
}
|
|
peerKey, err := hs.keyShareKeys.ecdhe.Curve().NewPublicKey(ecdhePeerData)
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid server key share")
|
|
}
|
|
sharedKey, err := hs.keyShareKeys.ecdhe.ECDH(peerKey)
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid server key share")
|
|
}
|
|
if hs.serverHello.serverShare.group == x25519Kyber768Draft00 {
|
|
if hs.keyShareKeys.kyber == nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
ciphertext := hs.serverHello.serverShare.data[x25519PublicKeySize:]
|
|
kyberShared, err := kyberDecapsulate(hs.keyShareKeys.kyber, ciphertext)
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid Kyber server key share")
|
|
}
|
|
sharedKey = append(sharedKey, kyberShared...)
|
|
}
|
|
c.curveID = hs.serverHello.serverShare.group
|
|
|
|
earlySecret := hs.earlySecret
|
|
if !hs.usingPSK {
|
|
earlySecret = hs.suite.extract(nil, nil)
|
|
}
|
|
|
|
handshakeSecret := hs.suite.extract(sharedKey,
|
|
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
|
|
|
clientSecret := hs.suite.deriveSecret(handshakeSecret,
|
|
clientHandshakeTrafficLabel, hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
|
|
serverSecret := hs.suite.deriveSecret(handshakeSecret,
|
|
serverHandshakeTrafficLabel, hs.transcript)
|
|
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
|
|
|
|
if c.quic != nil {
|
|
if c.hand.Len() != 0 {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
}
|
|
c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret)
|
|
c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret)
|
|
}
|
|
|
|
err = c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
hs.masterSecret = hs.suite.extract(nil,
|
|
hs.suite.deriveSecret(handshakeSecret, "derived", nil))
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) readServerParameters() error {
|
|
c := hs.c
|
|
|
|
msg, err := c.readHandshake(hs.transcript)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(encryptedExtensions, msg)
|
|
}
|
|
|
|
if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol, c.quic != nil); err != nil {
|
|
// RFC 8446 specifies that no_application_protocol is sent by servers, but
|
|
// does not specify how clients handle the selection of an incompatible protocol.
|
|
// RFC 9001 Section 8.1 specifies that QUIC clients send no_application_protocol
|
|
// in this case. Always sending no_application_protocol seems reasonable.
|
|
c.sendAlert(alertNoApplicationProtocol)
|
|
return err
|
|
}
|
|
c.clientProtocol = encryptedExtensions.alpnProtocol
|
|
|
|
if c.quic != nil {
|
|
if encryptedExtensions.quicTransportParameters == nil {
|
|
// RFC 9001 Section 8.2.
|
|
c.sendAlert(alertMissingExtension)
|
|
return errors.New("tls: server did not send a quic_transport_parameters extension")
|
|
}
|
|
c.quicSetTransportParameters(encryptedExtensions.quicTransportParameters)
|
|
} else {
|
|
if encryptedExtensions.quicTransportParameters != nil {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: server sent an unexpected quic_transport_parameters extension")
|
|
}
|
|
}
|
|
|
|
if !hs.hello.earlyData && encryptedExtensions.earlyData {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: server sent an unexpected early_data extension")
|
|
}
|
|
if hs.hello.earlyData && !encryptedExtensions.earlyData {
|
|
c.quicRejectedEarlyData()
|
|
}
|
|
if encryptedExtensions.earlyData {
|
|
if hs.session.cipherSuite != c.cipherSuite {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: server accepted 0-RTT with the wrong cipher suite")
|
|
}
|
|
if hs.session.alpnProtocol != c.clientProtocol {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: server accepted 0-RTT with the wrong ALPN")
|
|
}
|
|
}
|
|
if hs.echContext != nil && !hs.echContext.echRejected && encryptedExtensions.echRetryConfigs != nil {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: server sent ECH retry configs after accepting ECH")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
|
|
c := hs.c
|
|
|
|
// Either a PSK or a certificate is always used, but not both.
|
|
// See RFC 8446, Section 4.1.1.
|
|
if hs.usingPSK {
|
|
// Make sure the connection is still being verified whether or not this
|
|
// is a resumption. Resumptions currently don't reverify certificates so
|
|
// they don't call verifyServerCertificate. See Issue 31641.
|
|
if c.config.VerifyConnection != nil {
|
|
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
c.sendAlert(alertBadCertificate)
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
msg, err := c.readHandshake(hs.transcript)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
certReq, ok := msg.(*certificateRequestMsgTLS13)
|
|
if ok {
|
|
hs.certReq = certReq
|
|
|
|
msg, err = c.readHandshake(hs.transcript)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
certMsg, ok := msg.(*certificateMsgTLS13)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(certMsg, msg)
|
|
}
|
|
if len(certMsg.certificate.Certificate) == 0 {
|
|
c.sendAlert(alertDecodeError)
|
|
return errors.New("tls: received empty certificates message")
|
|
}
|
|
|
|
c.scts = certMsg.certificate.SignedCertificateTimestamps
|
|
c.ocspResponse = certMsg.certificate.OCSPStaple
|
|
|
|
if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
|
|
return err
|
|
}
|
|
|
|
// certificateVerifyMsg is included in the transcript, but not until
|
|
// after we verify the handshake signature, since the state before
|
|
// this message was sent is used.
|
|
msg, err = c.readHandshake(nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
certVerify, ok := msg.(*certificateVerifyMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(certVerify, msg)
|
|
}
|
|
|
|
// See RFC 8446, Section 4.4.3.
|
|
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: certificate used with invalid signature algorithm")
|
|
}
|
|
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
|
|
if err != nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: certificate used with invalid signature algorithm")
|
|
}
|
|
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
|
|
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
|
|
sigHash, signed, certVerify.signature); err != nil {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
|
|
}
|
|
|
|
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) readServerFinished() error {
|
|
c := hs.c
|
|
|
|
// finishedMsg is included in the transcript, but not until after we
|
|
// check the client version, since the state before this message was
|
|
// sent is used during verification.
|
|
msg, err := c.readHandshake(nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
finished, ok := msg.(*finishedMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(finished, msg)
|
|
}
|
|
|
|
expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
|
if !hmac.Equal(expectedMAC, finished.verifyData) {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid server finished hash")
|
|
}
|
|
|
|
if err := transcriptMsg(finished, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Derive secrets that take context through the server Finished.
|
|
|
|
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
|
clientApplicationTrafficLabel, hs.transcript)
|
|
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
|
serverApplicationTrafficLabel, hs.transcript)
|
|
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
|
|
|
|
err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
|
|
c := hs.c
|
|
|
|
if hs.certReq == nil {
|
|
return nil
|
|
}
|
|
|
|
if hs.echContext != nil && hs.echContext.echRejected {
|
|
if _, err := hs.c.writeHandshakeRecord(&certificateMsgTLS13{}, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
cert, err := c.getClientCertificate(&CertificateRequestInfo{
|
|
AcceptableCAs: hs.certReq.certificateAuthorities,
|
|
SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
|
|
Version: c.vers,
|
|
ctx: hs.ctx,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
certMsg := new(certificateMsgTLS13)
|
|
|
|
certMsg.certificate = *cert
|
|
certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
|
|
certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(certMsg, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
// If we sent an empty certificate message, skip the CertificateVerify.
|
|
if len(cert.Certificate) == 0 {
|
|
return nil
|
|
}
|
|
|
|
certVerifyMsg := new(certificateVerifyMsg)
|
|
certVerifyMsg.hasSignatureAlgorithm = true
|
|
|
|
certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms)
|
|
if err != nil {
|
|
// getClientCertificate returned a certificate incompatible with the
|
|
// CertificateRequestInfo supported signature algorithms.
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return err
|
|
}
|
|
|
|
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
|
|
if err != nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
|
|
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
|
|
signOpts := crypto.SignerOpts(sigHash)
|
|
if sigType == signatureRSAPSS {
|
|
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
|
}
|
|
sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: failed to sign handshake: " + err.Error())
|
|
}
|
|
certVerifyMsg.signature = sig
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(certVerifyMsg, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
|
|
c := hs.c
|
|
|
|
finished := &finishedMsg{
|
|
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
|
}
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(finished, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
|
|
|
|
if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
|
|
c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
|
|
resumptionLabel, hs.transcript)
|
|
}
|
|
|
|
if c.quic != nil {
|
|
if c.hand.Len() != 0 {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
}
|
|
c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, hs.trafficSecret)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
|
|
if !c.isClient {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return errors.New("tls: received new session ticket from a client")
|
|
}
|
|
|
|
if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
|
|
return nil
|
|
}
|
|
|
|
// See RFC 8446, Section 4.6.1.
|
|
if msg.lifetime == 0 {
|
|
return nil
|
|
}
|
|
lifetime := time.Duration(msg.lifetime) * time.Second
|
|
if lifetime > maxSessionTicketLifetime {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: received a session ticket with invalid lifetime")
|
|
}
|
|
|
|
// RFC 9001, Section 4.6.1
|
|
if c.quic != nil && msg.maxEarlyData != 0 && msg.maxEarlyData != 0xffffffff {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid early data for QUIC connection")
|
|
}
|
|
|
|
cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
|
|
if cipherSuite == nil || c.resumptionSecret == nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
|
|
psk := cipherSuite.expandLabel(c.resumptionSecret, "resumption",
|
|
msg.nonce, cipherSuite.hash.Size())
|
|
|
|
session := c.sessionState()
|
|
session.secret = psk
|
|
session.useBy = uint64(c.config.time().Add(lifetime).Unix())
|
|
session.ageAdd = msg.ageAdd
|
|
session.EarlyData = c.quic != nil && msg.maxEarlyData == 0xffffffff // RFC 9001, Section 4.6.1
|
|
session.ticket = msg.label
|
|
if c.quic != nil && c.quic.enableSessionEvents {
|
|
c.quicStoreSession(session)
|
|
return nil
|
|
}
|
|
cs := &ClientSessionState{session: session}
|
|
if cacheKey := c.clientSessionCacheKey(); cacheKey != "" {
|
|
c.config.ClientSessionCache.Put(cacheKey, cs)
|
|
}
|
|
|
|
return nil
|
|
}
|