mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-01 19:17:36 +03:00
This makes three related changes that work particularly well together and would require significant extra work to do separately: it replaces X25519Kyber768Draft00 with X25519MLKEM768, it makes CurvePreferences ordering crypto/tls-selected, and applies a preference to PQ key exchange methods over key shares (to mitigate downgrades). TestHandshakeServerUnsupportedKeyShare was removed because we are not rejecting unsupported key shares anymore (nor do we select them, and rejecting them actively is a MAY). It would have been nice to keep the test to check we still continue successfully, but testClientHelloFailure is broken in the face of any server-side behavior which requires writing any other messages back to the client, or reading them. Updates #69985 Fixes #69393 Change-Id: I58de76f5b8742a9bd4543fd7907c48e038507b19 Reviewed-on: https://go-review.googlesource.com/c/go/+/630775 Reviewed-by: Roland Shoemaker <roland@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Auto-Submit: Filippo Valsorda <filippo@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1147 lines
35 KiB
Go
1147 lines
35 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/fips140/hkdf"
|
|
"crypto/internal/fips140/mlkem"
|
|
"crypto/internal/fips140/tls13"
|
|
"crypto/internal/hpke"
|
|
"crypto/rsa"
|
|
"crypto/tls/internal/fips140tls"
|
|
"errors"
|
|
"hash"
|
|
"internal/byteorder"
|
|
"io"
|
|
"slices"
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
// maxClientPSKIdentities is the number of client PSK identities the server will
|
|
// attempt to validate. It will ignore the rest not to let cheap ClientHello
|
|
// messages cause too much work in session ticket decryption attempts.
|
|
const maxClientPSKIdentities = 5
|
|
|
|
type echServerContext struct {
|
|
hpkeContext *hpke.Receipient
|
|
configID uint8
|
|
ciphersuite echCipher
|
|
transcript hash.Hash
|
|
// inner indicates that the initial client_hello we recieved contained an
|
|
// encrypted_client_hello extension that indicated it was an "inner" hello.
|
|
// We don't do any additional processing of the hello in this case, so all
|
|
// fields above are unset.
|
|
inner bool
|
|
}
|
|
|
|
type serverHandshakeStateTLS13 struct {
|
|
c *Conn
|
|
ctx context.Context
|
|
clientHello *clientHelloMsg
|
|
hello *serverHelloMsg
|
|
sentDummyCCS bool
|
|
usingPSK bool
|
|
earlyData bool
|
|
suite *cipherSuiteTLS13
|
|
cert *Certificate
|
|
sigAlg SignatureScheme
|
|
earlySecret *tls13.EarlySecret
|
|
sharedKey []byte
|
|
handshakeSecret *tls13.HandshakeSecret
|
|
masterSecret *tls13.MasterSecret
|
|
trafficSecret []byte // client_application_traffic_secret_0
|
|
transcript hash.Hash
|
|
clientFinished []byte
|
|
echContext *echServerContext
|
|
}
|
|
|
|
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
|
|
}
|
|
if err := hs.checkForResumption(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.pickCertificate(); 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
|
|
}
|
|
// Note that at this point we could start sending application data without
|
|
// waiting for the client's second flight, but the application might not
|
|
// expect the lack of replay protection of the ClientHello parameters.
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readClientCertificate(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readClientFinished(); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.isHandshakeComplete.Store(true)
|
|
|
|
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")
|
|
}
|
|
|
|
// Abort if the client is doing a fallback and landing lower than what we
|
|
// support. See RFC 7507, which however does not specify the interaction
|
|
// with supported_versions. The only difference is that with
|
|
// supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
|
|
// handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
|
|
// it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
|
|
// TLS 1.2, because a TLS 1.3 server would abort here. The situation before
|
|
// supported_versions was not better because there was just no way to do a
|
|
// TLS 1.4 handshake without risking the server selecting TLS 1.3.
|
|
for _, id := range hs.clientHello.cipherSuites {
|
|
if id == TLS_FALLBACK_SCSV {
|
|
// Use c.vers instead of max(supported_versions) because an attacker
|
|
// could defeat this by adding an arbitrary high version otherwise.
|
|
if c.vers < c.config.maxSupportedVersion(roleServer) {
|
|
c.sendAlert(alertInappropriateFallback)
|
|
return errors.New("tls: client using inappropriate protocol fallback")
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
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 && c.quic != nil {
|
|
if len(hs.clientHello.pskIdentities) == 0 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: early_data without pre_shared_key")
|
|
}
|
|
} else if hs.clientHello.earlyData {
|
|
// See RFC 8446, Section 4.2.10 for the complicated behavior required
|
|
// here. The scenario is that a different server at our address offered
|
|
// to accept early data in the past, which we can't handle. For now, all
|
|
// 0-RTT enabled session tickets need to expire before a Go server can
|
|
// replace a server or join a pool. That's the same requirement that
|
|
// applies to mixing or replacing with any TLS 1.2 server.
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: client sent unexpected early data")
|
|
}
|
|
|
|
hs.hello.sessionId = hs.clientHello.sessionId
|
|
hs.hello.compressionMethod = compressionNone
|
|
|
|
preferenceList := defaultCipherSuitesTLS13
|
|
if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
|
|
preferenceList = defaultCipherSuitesTLS13NoAES
|
|
}
|
|
if fips140tls.Required() {
|
|
preferenceList = defaultCipherSuitesTLS13FIPS
|
|
}
|
|
for _, suiteID := range preferenceList {
|
|
hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, 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()
|
|
|
|
// First, if a post-quantum key exchange is available, use one. See
|
|
// draft-ietf-tls-key-share-prediction-01, Section 4 for why this must be
|
|
// first.
|
|
//
|
|
// Second, if the client sent a key share for a group we support, use that,
|
|
// to avoid a HelloRetryRequest round-trip.
|
|
//
|
|
// Finally, pick in our fixed preference order.
|
|
preferredGroups := c.config.curvePreferences(c.vers)
|
|
preferredGroups = slices.DeleteFunc(preferredGroups, func(group CurveID) bool {
|
|
return !slices.Contains(hs.clientHello.supportedCurves, group)
|
|
})
|
|
if len(preferredGroups) == 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: no key exchanges supported by both client and server")
|
|
}
|
|
hasKeyShare := func(group CurveID) bool {
|
|
for _, ks := range hs.clientHello.keyShares {
|
|
if ks.group == group {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
sort.SliceStable(preferredGroups, func(i, j int) bool {
|
|
return hasKeyShare(preferredGroups[i]) && !hasKeyShare(preferredGroups[j])
|
|
})
|
|
sort.SliceStable(preferredGroups, func(i, j int) bool {
|
|
return isPQKeyExchange(preferredGroups[i]) && !isPQKeyExchange(preferredGroups[j])
|
|
})
|
|
selectedGroup := preferredGroups[0]
|
|
|
|
var clientKeyShare *keyShare
|
|
for _, ks := range hs.clientHello.keyShares {
|
|
if ks.group == selectedGroup {
|
|
clientKeyShare = &ks
|
|
break
|
|
}
|
|
}
|
|
if clientKeyShare == nil {
|
|
ks, err := hs.doHelloRetryRequest(selectedGroup)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
clientKeyShare = ks
|
|
}
|
|
c.curveID = selectedGroup
|
|
|
|
ecdhGroup := selectedGroup
|
|
ecdhData := clientKeyShare.data
|
|
if selectedGroup == X25519MLKEM768 {
|
|
ecdhGroup = X25519
|
|
if len(ecdhData) != mlkem.EncapsulationKeySize768+x25519PublicKeySize {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid X25519MLKEM768 client key share")
|
|
}
|
|
ecdhData = ecdhData[mlkem.EncapsulationKeySize768:]
|
|
}
|
|
if _, ok := curveForCurveID(ecdhGroup); !ok {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
}
|
|
key, err := generateECDHEKey(c.config.rand(), ecdhGroup)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
hs.hello.serverShare = keyShare{group: selectedGroup, data: key.PublicKey().Bytes()}
|
|
peerKey, err := key.Curve().NewPublicKey(ecdhData)
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid client key share")
|
|
}
|
|
hs.sharedKey, err = key.ECDH(peerKey)
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid client key share")
|
|
}
|
|
if selectedGroup == X25519MLKEM768 {
|
|
k, err := mlkem.NewEncapsulationKey768(clientKeyShare.data[:mlkem.EncapsulationKeySize768])
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid X25519MLKEM768 client key share")
|
|
}
|
|
ciphertext, mlkemSharedSecret := k.Encapsulate()
|
|
// draft-kwiatkowski-tls-ecdhe-mlkem-02, Section 3.1.3: "For
|
|
// X25519MLKEM768, the shared secret is the concatenation of the ML-KEM
|
|
// shared secret and the X25519 shared secret. The shared secret is 64
|
|
// bytes (32 bytes for each part)."
|
|
hs.sharedKey = append(mlkemSharedSecret, hs.sharedKey...)
|
|
// draft-kwiatkowski-tls-ecdhe-mlkem-02, Section 3.1.2: "When the
|
|
// X25519MLKEM768 group is negotiated, the server's key exchange value
|
|
// is the concatenation of an ML-KEM ciphertext returned from
|
|
// encapsulation to the client's encapsulation key, and the server's
|
|
// ephemeral X25519 share."
|
|
hs.hello.serverShare.data = append(ciphertext, hs.hello.serverShare.data...)
|
|
}
|
|
|
|
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, c.quic != nil)
|
|
if err != nil {
|
|
c.sendAlert(alertNoApplicationProtocol)
|
|
return err
|
|
}
|
|
c.clientProtocol = selectedProto
|
|
|
|
if c.quic != nil {
|
|
// RFC 9001 Section 4.2: Clients MUST NOT offer TLS versions older than 1.3.
|
|
for _, v := range hs.clientHello.supportedVersions {
|
|
if v < VersionTLS13 {
|
|
c.sendAlert(alertProtocolVersion)
|
|
return errors.New("tls: client offered TLS version older than TLS 1.3")
|
|
}
|
|
}
|
|
// RFC 9001 Section 8.2.
|
|
if hs.clientHello.quicTransportParameters == nil {
|
|
c.sendAlert(alertMissingExtension)
|
|
return errors.New("tls: client did not send a quic_transport_parameters extension")
|
|
}
|
|
c.quicSetTransportParameters(hs.clientHello.quicTransportParameters)
|
|
} else {
|
|
if hs.clientHello.quicTransportParameters != nil {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: client sent an unexpected quic_transport_parameters extension")
|
|
}
|
|
}
|
|
|
|
c.serverName = hs.clientHello.serverName
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) checkForResumption() error {
|
|
c := hs.c
|
|
|
|
if c.config.SessionTicketsDisabled {
|
|
return nil
|
|
}
|
|
|
|
modeOK := false
|
|
for _, mode := range hs.clientHello.pskModes {
|
|
if mode == pskModeDHE {
|
|
modeOK = true
|
|
break
|
|
}
|
|
}
|
|
if !modeOK {
|
|
return nil
|
|
}
|
|
|
|
if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid or missing PSK binders")
|
|
}
|
|
if len(hs.clientHello.pskIdentities) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for i, identity := range hs.clientHello.pskIdentities {
|
|
if i >= maxClientPSKIdentities {
|
|
break
|
|
}
|
|
|
|
var sessionState *SessionState
|
|
if c.config.UnwrapSession != nil {
|
|
var err error
|
|
sessionState, err = c.config.UnwrapSession(identity.label, c.connectionStateLocked())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if sessionState == nil {
|
|
continue
|
|
}
|
|
} else {
|
|
plaintext := c.config.decryptTicket(identity.label, c.ticketKeys)
|
|
if plaintext == nil {
|
|
continue
|
|
}
|
|
var err error
|
|
sessionState, err = ParseSessionState(plaintext)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if sessionState.version != VersionTLS13 {
|
|
continue
|
|
}
|
|
|
|
createdAt := time.Unix(int64(sessionState.createdAt), 0)
|
|
if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
|
|
continue
|
|
}
|
|
|
|
pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite)
|
|
if pskSuite == nil || pskSuite.hash != hs.suite.hash {
|
|
continue
|
|
}
|
|
|
|
// PSK connections don't re-establish client certificates, but carry
|
|
// them over in the session ticket. Ensure the presence of client certs
|
|
// in the ticket is consistent with the configured requirements.
|
|
sessionHasClientCerts := len(sessionState.peerCertificates) != 0
|
|
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
|
if needClientCerts && !sessionHasClientCerts {
|
|
continue
|
|
}
|
|
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
|
continue
|
|
}
|
|
if sessionHasClientCerts && c.config.time().After(sessionState.peerCertificates[0].NotAfter) {
|
|
continue
|
|
}
|
|
if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
|
|
len(sessionState.verifiedChains) == 0 {
|
|
continue
|
|
}
|
|
|
|
if c.quic != nil && c.quic.enableSessionEvents {
|
|
if err := c.quicResumeSession(sessionState); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
hs.earlySecret = tls13.NewEarlySecret(hs.suite.hash.New, sessionState.secret)
|
|
binderKey := hs.earlySecret.ResumptionBinderKey()
|
|
// Clone the transcript in case a HelloRetryRequest was recorded.
|
|
transcript := cloneHash(hs.transcript, hs.suite.hash)
|
|
if transcript == nil {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: internal error: failed to clone hash")
|
|
}
|
|
clientHelloBytes, err := hs.clientHello.marshalWithoutBinders()
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
transcript.Write(clientHelloBytes)
|
|
pskBinder := hs.suite.finishedHash(binderKey, transcript)
|
|
if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid PSK binder")
|
|
}
|
|
|
|
if c.quic != nil && hs.clientHello.earlyData && i == 0 &&
|
|
sessionState.EarlyData && sessionState.cipherSuite == hs.suite.id &&
|
|
sessionState.alpnProtocol == c.clientProtocol {
|
|
hs.earlyData = true
|
|
|
|
transcript := hs.suite.hash.New()
|
|
if err := transcriptMsg(hs.clientHello, transcript); err != nil {
|
|
return err
|
|
}
|
|
earlyTrafficSecret := hs.earlySecret.ClientEarlyTrafficSecret(transcript)
|
|
c.quicSetReadSecret(QUICEncryptionLevelEarly, hs.suite.id, earlyTrafficSecret)
|
|
}
|
|
|
|
c.didResume = true
|
|
c.peerCertificates = sessionState.peerCertificates
|
|
c.ocspResponse = sessionState.ocspResponse
|
|
c.scts = sessionState.scts
|
|
c.verifiedChains = sessionState.verifiedChains
|
|
|
|
hs.hello.selectedIdentityPresent = true
|
|
hs.hello.selectedIdentity = uint16(i)
|
|
hs.usingPSK = true
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
|
|
// interfaces implemented by standard library hashes to clone the state of in
|
|
// to a new instance of h. It returns nil if the operation fails.
|
|
func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash {
|
|
// Recreate the interface to avoid importing encoding.
|
|
type binaryMarshaler interface {
|
|
MarshalBinary() (data []byte, err error)
|
|
UnmarshalBinary(data []byte) error
|
|
}
|
|
marshaler, ok := in.(binaryMarshaler)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
state, err := marshaler.MarshalBinary()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
out := h.New()
|
|
unmarshaler, ok := out.(binaryMarshaler)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
if err := unmarshaler.UnmarshalBinary(state); err != nil {
|
|
return nil
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) pickCertificate() error {
|
|
c := hs.c
|
|
|
|
// Only one of PSK and certificates are used at a time.
|
|
if hs.usingPSK {
|
|
return nil
|
|
}
|
|
|
|
// signature_algorithms is required in TLS 1.3. See RFC 8446, Section 4.2.3.
|
|
if len(hs.clientHello.supportedSignatureAlgorithms) == 0 {
|
|
return c.sendAlert(alertMissingExtension)
|
|
}
|
|
|
|
certificate, 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
|
|
}
|
|
hs.sigAlg, err = selectSignatureScheme(c.vers, certificate, hs.clientHello.supportedSignatureAlgorithms)
|
|
if err != nil {
|
|
// getCertificate returned a certificate that is unsupported or
|
|
// incompatible with the client's signature algorithms.
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return err
|
|
}
|
|
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.c.quic != nil {
|
|
return nil
|
|
}
|
|
if hs.sentDummyCCS {
|
|
return nil
|
|
}
|
|
hs.sentDummyCCS = true
|
|
|
|
return hs.c.writeChangeCipherRecord()
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) (*keyShare, error) {
|
|
c := hs.c
|
|
|
|
// The first ClientHello gets double-hashed into the transcript upon a
|
|
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
|
if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil {
|
|
return nil, err
|
|
}
|
|
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,
|
|
}
|
|
|
|
if hs.echContext != nil {
|
|
// Compute the acceptance message.
|
|
helloRetryRequest.encryptedClientHello = make([]byte, 8)
|
|
confTranscript := cloneHash(hs.transcript, hs.suite.hash)
|
|
if err := transcriptMsg(helloRetryRequest, confTranscript); err != nil {
|
|
return nil, err
|
|
}
|
|
acceptConfirmation := tls13.ExpandLabel(hs.suite.hash.New,
|
|
hkdf.Extract(hs.suite.hash.New, hs.clientHello.random, nil),
|
|
"hrr ech accept confirmation",
|
|
confTranscript.Sum(nil),
|
|
8,
|
|
)
|
|
helloRetryRequest.encryptedClientHello = acceptConfirmation
|
|
}
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(helloRetryRequest, hs.transcript); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// clientHelloMsg is not included in the transcript.
|
|
msg, err := c.readHandshake(nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
clientHello, ok := msg.(*clientHelloMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return nil, unexpectedMessageError(clientHello, msg)
|
|
}
|
|
|
|
if hs.echContext != nil {
|
|
if len(clientHello.encryptedClientHello) == 0 {
|
|
c.sendAlert(alertMissingExtension)
|
|
return nil, errors.New("tls: second client hello missing encrypted client hello extension")
|
|
}
|
|
|
|
echType, echCiphersuite, configID, encap, payload, err := parseECHExt(clientHello.encryptedClientHello)
|
|
if err != nil {
|
|
c.sendAlert(alertDecodeError)
|
|
return nil, errors.New("tls: client sent invalid encrypted client hello extension")
|
|
}
|
|
|
|
if echType == outerECHExt && hs.echContext.inner || echType == innerECHExt && !hs.echContext.inner {
|
|
c.sendAlert(alertDecodeError)
|
|
return nil, errors.New("tls: unexpected switch in encrypted client hello extension type")
|
|
}
|
|
|
|
if echType == outerECHExt {
|
|
if echCiphersuite != hs.echContext.ciphersuite || configID != hs.echContext.configID || len(encap) != 0 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return nil, errors.New("tls: second client hello encrypted client hello extension does not match")
|
|
}
|
|
|
|
encodedInner, err := decryptECHPayload(hs.echContext.hpkeContext, clientHello.original, payload)
|
|
if err != nil {
|
|
c.sendAlert(alertDecryptError)
|
|
return nil, errors.New("tls: failed to decrypt second client hello encrypted client hello extension payload")
|
|
}
|
|
|
|
echInner, err := decodeInnerClientHello(clientHello, encodedInner)
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return nil, errors.New("tls: client sent invalid encrypted client hello extension")
|
|
}
|
|
|
|
clientHello = echInner
|
|
}
|
|
}
|
|
|
|
if len(clientHello.keyShares) != 1 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return nil, errors.New("tls: client didn't send one key share in second ClientHello")
|
|
}
|
|
ks := &clientHello.keyShares[0]
|
|
|
|
if ks.group != selectedGroup {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return nil, errors.New("tls: client sent unexpected key share in second ClientHello")
|
|
}
|
|
|
|
if clientHello.earlyData {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return nil, errors.New("tls: client indicated early data in second ClientHello")
|
|
}
|
|
|
|
if illegalClientHelloChange(clientHello, hs.clientHello) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return nil, errors.New("tls: client illegally modified second ClientHello")
|
|
}
|
|
|
|
c.didHRR = true
|
|
hs.clientHello = clientHello
|
|
return ks, nil
|
|
}
|
|
|
|
// illegalClientHelloChange reports 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.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
|
|
|
|
if hs.echContext != nil {
|
|
copy(hs.hello.random[32-8:], make([]byte, 8))
|
|
echTranscript := cloneHash(hs.transcript, hs.suite.hash)
|
|
echTranscript.Write(hs.clientHello.original)
|
|
if err := transcriptMsg(hs.hello, echTranscript); err != nil {
|
|
return err
|
|
}
|
|
// compute the acceptance message
|
|
acceptConfirmation := tls13.ExpandLabel(hs.suite.hash.New,
|
|
hkdf.Extract(hs.suite.hash.New, hs.clientHello.random, nil),
|
|
"ech accept confirmation",
|
|
echTranscript.Sum(nil),
|
|
8,
|
|
)
|
|
copy(hs.hello.random[32-8:], acceptConfirmation)
|
|
}
|
|
|
|
if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
earlySecret := hs.earlySecret
|
|
if earlySecret == nil {
|
|
earlySecret = tls13.NewEarlySecret(hs.suite.hash.New, nil)
|
|
}
|
|
hs.handshakeSecret = earlySecret.HandshakeSecret(hs.sharedKey)
|
|
|
|
clientSecret := hs.handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript)
|
|
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
|
|
serverSecret := hs.handshakeSecret.ServerHandshakeTrafficSecret(hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
|
|
|
|
if c.quic != nil {
|
|
if c.hand.Len() != 0 {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
}
|
|
c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret)
|
|
c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret)
|
|
}
|
|
|
|
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)
|
|
encryptedExtensions.alpnProtocol = c.clientProtocol
|
|
|
|
if c.quic != nil {
|
|
p, err := c.quicGetTransportParameters()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
encryptedExtensions.quicTransportParameters = p
|
|
encryptedExtensions.earlyData = hs.earlyData
|
|
}
|
|
|
|
// If client sent ECH extension, but we didn't accept it,
|
|
// send retry configs, if available.
|
|
if len(hs.c.config.EncryptedClientHelloKeys) > 0 && len(hs.clientHello.encryptedClientHello) > 0 && hs.echContext == nil {
|
|
encryptedExtensions.echRetryConfigs, err = buildRetryConfigList(hs.c.config.EncryptedClientHelloKeys)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(encryptedExtensions, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) requestClientCert() bool {
|
|
return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
|
|
c := hs.c
|
|
|
|
// Only one of PSK and certificates are used at a time.
|
|
if hs.usingPSK {
|
|
return nil
|
|
}
|
|
|
|
if hs.requestClientCert() {
|
|
// Request a client certificate
|
|
certReq := new(certificateRequestMsgTLS13)
|
|
certReq.ocspStapling = true
|
|
certReq.scts = true
|
|
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
|
|
if c.config.ClientCAs != nil {
|
|
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
|
|
}
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(certReq, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
if _, err := hs.c.writeHandshakeRecord(certMsg, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
certVerifyMsg := new(certificateVerifyMsg)
|
|
certVerifyMsg.hasSignatureAlgorithm = true
|
|
certVerifyMsg.signatureAlgorithm = hs.sigAlg
|
|
|
|
sigType, sigHash, err := typeAndHashFromSignatureScheme(hs.sigAlg)
|
|
if err != nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
|
|
signed := signedMessage(sigHash, 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(), signed, signOpts)
|
|
if err != nil {
|
|
public := hs.cert.PrivateKey.(crypto.Signer).Public()
|
|
if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS &&
|
|
rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS
|
|
c.sendAlert(alertHandshakeFailure)
|
|
} else {
|
|
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 *serverHandshakeStateTLS13) sendServerFinished() 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
|
|
}
|
|
|
|
// Derive secrets that take context through the server Finished.
|
|
|
|
hs.masterSecret = hs.handshakeSecret.MasterSecret()
|
|
|
|
hs.trafficSecret = hs.masterSecret.ClientApplicationTrafficSecret(hs.transcript)
|
|
serverSecret := hs.masterSecret.ServerApplicationTrafficSecret(hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
|
|
|
|
if c.quic != nil {
|
|
if c.hand.Len() != 0 {
|
|
// TODO: Handle this in setTrafficSecret?
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
}
|
|
c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, 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(hs.masterSecret, hs.transcript)
|
|
|
|
// If we did not request client certificates, at this point we can
|
|
// precompute the client finished and roll the transcript forward to send
|
|
// session tickets in our first flight.
|
|
if !hs.requestClientCert() {
|
|
if err := hs.sendSessionTickets(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool {
|
|
if hs.c.config.SessionTicketsDisabled {
|
|
return false
|
|
}
|
|
|
|
// QUIC tickets are sent by QUICConn.SendSessionTicket, not automatically.
|
|
if hs.c.quic != nil {
|
|
return false
|
|
}
|
|
|
|
// Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9.
|
|
for _, pskMode := range hs.clientHello.pskModes {
|
|
if pskMode == pskModeDHE {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
|
|
c := hs.c
|
|
|
|
hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
|
finishedMsg := &finishedMsg{
|
|
verifyData: hs.clientFinished,
|
|
}
|
|
if err := transcriptMsg(finishedMsg, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.resumptionSecret = hs.masterSecret.ResumptionMasterSecret(hs.transcript)
|
|
|
|
if !hs.shouldSendSessionTickets() {
|
|
return nil
|
|
}
|
|
return c.sendSessionTicket(false, nil)
|
|
}
|
|
|
|
func (c *Conn) sendSessionTicket(earlyData bool, extra [][]byte) error {
|
|
suite := cipherSuiteTLS13ByID(c.cipherSuite)
|
|
if suite == nil {
|
|
return errors.New("tls: internal error: unknown cipher suite")
|
|
}
|
|
// ticket_nonce, which must be unique per connection, is always left at
|
|
// zero because we only ever send one ticket per connection.
|
|
psk := tls13.ExpandLabel(suite.hash.New, c.resumptionSecret, "resumption",
|
|
nil, suite.hash.Size())
|
|
|
|
m := new(newSessionTicketMsgTLS13)
|
|
|
|
state := c.sessionState()
|
|
state.secret = psk
|
|
state.EarlyData = earlyData
|
|
state.Extra = extra
|
|
if c.config.WrapSession != nil {
|
|
var err error
|
|
m.label, err = c.config.WrapSession(c.connectionStateLocked(), state)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
stateBytes, err := state.Bytes()
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
m.label, err = c.config.encryptTicket(stateBytes, c.ticketKeys)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
|
|
|
|
// ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1
|
|
// The value is not stored anywhere; we never need to check the ticket age
|
|
// because 0-RTT is not supported.
|
|
ageAdd := make([]byte, 4)
|
|
if _, err := c.config.rand().Read(ageAdd); err != nil {
|
|
return err
|
|
}
|
|
m.ageAdd = byteorder.LEUint32(ageAdd)
|
|
|
|
if earlyData {
|
|
// RFC 9001, Section 4.6.1
|
|
m.maxEarlyData = 0xffffffff
|
|
}
|
|
|
|
if _, err := c.writeHandshakeRecord(m, nil); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
|
|
c := hs.c
|
|
|
|
if !hs.requestClientCert() {
|
|
// Make sure the connection is still being verified whether or not
|
|
// the server requested a client certificate.
|
|
if c.config.VerifyConnection != nil {
|
|
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
c.sendAlert(alertBadCertificate)
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// If we requested a client certificate, then the client must send a
|
|
// certificate message. If it's empty, no CertificateVerify is sent.
|
|
|
|
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 err := c.processCertsFromClient(certMsg.certificate); err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.config.VerifyConnection != nil {
|
|
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
|
c.sendAlert(alertBadCertificate)
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(certMsg.certificate.Certificate) != 0 {
|
|
// 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: client 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: client certificate used with invalid signature algorithm")
|
|
}
|
|
signed := signedMessage(sigHash, clientSignatureContext, 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 client certificate: " + err.Error())
|
|
}
|
|
|
|
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// If we waited until the client certificates to send session tickets, we
|
|
// are ready to do it now.
|
|
if err := hs.sendSessionTickets(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) readClientFinished() error {
|
|
c := hs.c
|
|
|
|
// finishedMsg is not included in the transcript.
|
|
msg, err := c.readHandshake(nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
finished, ok := msg.(*finishedMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(finished, msg)
|
|
}
|
|
|
|
if !hmac.Equal(hs.clientFinished, finished.verifyData) {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid client finished hash")
|
|
}
|
|
|
|
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
|
|
|
|
return nil
|
|
}
|