mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 08:31:30 +00:00
1033 lines
32 KiB
Go
1033 lines
32 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/rsa"
|
|
"crypto/subtle"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
circlKem "github.com/cloudflare/circl/kem"
|
|
)
|
|
|
|
type clientHandshakeStateTLS13 struct {
|
|
c *Conn
|
|
ctx context.Context
|
|
serverHello *serverHelloMsg
|
|
hello *clientHelloMsg
|
|
helloInner *clientHelloMsg
|
|
keySharePrivate clientKeySharePrivate
|
|
|
|
session *ClientSessionState
|
|
earlySecret []byte
|
|
binderKey []byte
|
|
selectedGroup CurveID
|
|
|
|
certReq *certificateRequestMsgTLS13
|
|
usingPSK bool
|
|
sentDummyCCS bool
|
|
suite *cipherSuiteTLS13
|
|
transcript hash.Hash
|
|
transcriptInner hash.Hash
|
|
masterSecret []byte
|
|
trafficSecret []byte // client_application_traffic_secret_0
|
|
|
|
hsTimings CFEventTLS13ClientHandshakeTimingInfo
|
|
}
|
|
|
|
// processDelegatedCredentialFromServer unmarshals the DelegatedCredential
|
|
// offered by the server (if present) and validates it using the peer's
|
|
// certificate.
|
|
func (hs *clientHandshakeStateTLS13) processDelegatedCredentialFromServer(rawDC []byte, certVerifyMsg *certificateVerifyMsg) error {
|
|
c := hs.c
|
|
|
|
var dc *DelegatedCredential
|
|
var err error
|
|
if rawDC != nil {
|
|
// Assert that support for the DC extension was indicated by the client.
|
|
if !hs.hello.delegatedCredentialSupported {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return errors.New("tls: got Delegated Credential extension without indication")
|
|
}
|
|
|
|
dc, err = UnmarshalDelegatedCredential(rawDC)
|
|
if err != nil {
|
|
c.sendAlert(alertDecodeError)
|
|
return fmt.Errorf("tls: Delegated Credential: %s", err)
|
|
}
|
|
|
|
if !isSupportedSignatureAlgorithm(dc.cred.expCertVerfAlgo, supportedSignatureAlgorithmsDC) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: Delegated Credential used with invalid signature algorithm")
|
|
}
|
|
if !isSupportedSignatureAlgorithm(dc.algorithm, c.config.supportedSignatureAlgorithms()) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: Delegated Credential signed with unsupported signature algorithm")
|
|
}
|
|
}
|
|
|
|
if dc != nil {
|
|
if !dc.Validate(c.peerCertificates[0], false, c.config.time(), certVerifyMsg) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid Delegated Credential")
|
|
}
|
|
}
|
|
|
|
c.verifiedDC = dc
|
|
|
|
return nil
|
|
}
|
|
|
|
// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and,
|
|
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
|
func (hs *clientHandshakeStateTLS13) handshake() error {
|
|
c := hs.c
|
|
|
|
// 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.keySharePrivate == nil || len(hs.hello.keyShares) != 1 {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
|
|
if err := hs.checkServerHelloOrHRR(); err != nil {
|
|
return err
|
|
}
|
|
|
|
hs.transcript = hs.suite.hash.New()
|
|
hs.transcript.Write(hs.hello.marshal())
|
|
|
|
// When offering ECH, we don't know whether ECH was accepted or rejected
|
|
// until we get the server's response. Compute the transcript of both the
|
|
// inner and outer handshake until we know.
|
|
if c.ech.offered {
|
|
hs.transcriptInner = hs.suite.hash.New()
|
|
hs.transcriptInner.Write(hs.helloInner.marshal())
|
|
}
|
|
|
|
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.processHelloRetryRequest(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Check for ECH acceptance confirmation.
|
|
if c.ech.offered {
|
|
echAcceptConfTranscript := cloneHash(hs.transcriptInner, hs.suite.hash)
|
|
if echAcceptConfTranscript == nil {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: internal error: failed to clone hash")
|
|
}
|
|
|
|
sh := hs.serverHello.marshal()
|
|
echAcceptConfTranscript.Write(sh[:30])
|
|
echAcceptConfTranscript.Write(zeros[:8])
|
|
echAcceptConfTranscript.Write(sh[38:])
|
|
echAcceptConf := hs.suite.expandLabel(
|
|
hs.suite.extract(hs.helloInner.random, nil),
|
|
echAcceptConfLabel,
|
|
echAcceptConfTranscript.Sum(nil),
|
|
8)
|
|
|
|
if subtle.ConstantTimeCompare(hs.serverHello.random[24:], echAcceptConf) == 1 {
|
|
c.ech.accepted = true
|
|
hs.hello = hs.helloInner
|
|
hs.transcript = hs.transcriptInner
|
|
}
|
|
}
|
|
|
|
hs.transcript.Write(hs.serverHello.marshal())
|
|
|
|
// Resolve the server name now that ECH acceptance has been determined.
|
|
//
|
|
// NOTE(cjpatton): Currently the client sends the same ALPN extension in the
|
|
// ClientHelloInner and ClientHelloOuter. If that changes, then we'll need
|
|
// to resolve ALPN here as well.
|
|
c.serverName = hs.hello.serverName
|
|
|
|
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 := hs.abortIfRequired(); err != nil {
|
|
return err
|
|
}
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.handleCFEvent(hs.hsTimings)
|
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
|
|
|
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.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.sentDummyCCS {
|
|
return nil
|
|
}
|
|
hs.sentDummyCCS = true
|
|
|
|
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
|
return err
|
|
}
|
|
|
|
// 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
|
|
|
|
c.handleCFEvent(CFEventTLS13HRR{})
|
|
|
|
// 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)
|
|
hs.transcript.Write(hs.serverHello.marshal())
|
|
|
|
// Determine which ClientHello message was consumed by the server. If ECH
|
|
// was offered, this may be the ClientHelloInner or ClientHelloOuter.
|
|
hello := hs.hello
|
|
isInner := false
|
|
if c.ech.offered {
|
|
chHash = hs.transcriptInner.Sum(nil)
|
|
hs.transcriptInner.Reset()
|
|
hs.transcriptInner.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
hs.transcriptInner.Write(chHash)
|
|
|
|
// Check for ECH acceptance confirmation.
|
|
if hs.serverHello.ech != nil {
|
|
if len(hs.serverHello.ech) != 8 {
|
|
c.sendAlert(alertDecodeError)
|
|
return errors.New("tls: ech: hrr: malformed acceptance signal")
|
|
}
|
|
|
|
echAcceptConfHRRTranscript := cloneHash(hs.transcriptInner, hs.suite.hash)
|
|
if echAcceptConfHRRTranscript == nil {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: internal error: failed to clone hash")
|
|
}
|
|
|
|
echAcceptConfHRR := echEncodeAcceptConfHelloRetryRequest(hs.serverHello.marshal())
|
|
echAcceptConfHRRTranscript.Write(echAcceptConfHRR)
|
|
echAcceptConfHRRSignal := hs.suite.expandLabel(
|
|
hs.suite.extract(hs.helloInner.random, nil),
|
|
echAcceptConfHRRLabel,
|
|
echAcceptConfHRRTranscript.Sum(nil),
|
|
8)
|
|
|
|
if subtle.ConstantTimeCompare(hs.serverHello.ech, echAcceptConfHRRSignal) == 1 {
|
|
hello = hs.helloInner
|
|
isInner = true
|
|
}
|
|
}
|
|
|
|
hs.transcriptInner.Write(hs.serverHello.marshal())
|
|
}
|
|
|
|
// 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 {
|
|
curveOK := false
|
|
for _, id := range hello.supportedCurves {
|
|
if id == curveID {
|
|
curveOK = true
|
|
break
|
|
}
|
|
}
|
|
if !curveOK {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server selected unsupported group")
|
|
}
|
|
if clientKeySharePrivateCurveID(hs.keySharePrivate) == curveID {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
|
}
|
|
if scheme := curveIdToCirclScheme(curveID); scheme != nil {
|
|
pk, sk, err := generateKemKeyPair(scheme, c.config.rand())
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return fmt.Errorf("HRR generateKemKeyPair %s: %w",
|
|
scheme.Name(), err)
|
|
}
|
|
packedPk, err := pk.MarshalBinary()
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return fmt.Errorf("HRR pack circl public key %s: %w",
|
|
scheme.Name(), err)
|
|
}
|
|
hs.keySharePrivate = sk
|
|
hello.keyShares = []keyShare{{group: curveID, data: packedPk}}
|
|
} else {
|
|
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
}
|
|
params, err := generateECDHEParameters(c.config.rand(), curveID)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
hs.keySharePrivate = params
|
|
hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
|
|
}
|
|
}
|
|
|
|
hello.raw = nil
|
|
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 := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond)
|
|
hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd
|
|
|
|
transcript := hs.suite.hash.New()
|
|
transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
transcript.Write(chHash)
|
|
transcript.Write(hs.serverHello.marshal())
|
|
transcript.Write(hello.marshalWithoutBinders())
|
|
pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)}
|
|
hello.updateBinders(pskBinders)
|
|
} else {
|
|
// Server selected a cipher suite incompatible with the PSK.
|
|
hello.pskIdentities = nil
|
|
hello.pskBinders = nil
|
|
}
|
|
}
|
|
|
|
if isInner {
|
|
hs.helloInner = hello
|
|
hs.transcriptInner.Write(hs.helloInner.marshal())
|
|
if err := c.echUpdateClientHelloOuter(hs.hello, hs.helloInner, nil); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
hs.hello = hello
|
|
}
|
|
|
|
if c.ech.offered && testingECHIllegalHandleAfterHRR {
|
|
hs.hello.raw = nil
|
|
|
|
// Change the cipher suite and config id and set an encapsulated key in
|
|
// the updated ClientHello. This will trigger a server abort because the
|
|
// cipher suite and config id are supposed to match the previous
|
|
// ClientHello and the encapsulated key is supposed to be empty.
|
|
var ech echClientOuter
|
|
_, kdf, aead := c.ech.sealer.Suite().Params()
|
|
ech.handle.suite.kdfId = uint16(kdf) ^ 0xff
|
|
ech.handle.suite.aeadId = uint16(aead) ^ 0xff
|
|
ech.handle.configId = c.ech.configId ^ 0xff
|
|
ech.handle.enc = []byte{1, 2, 3, 4, 5}
|
|
ech.payload = []byte{1, 2, 3, 4, 5}
|
|
hs.hello.ech = ech.marshal()
|
|
}
|
|
|
|
if testingECHTriggerBypassAfterHRR {
|
|
hs.hello.raw = nil
|
|
|
|
// Don't send the ECH extension in the updated ClientHello. This will
|
|
// trigger a server abort, since this is illegal.
|
|
hs.hello.ech = nil
|
|
}
|
|
|
|
if testingECHTriggerBypassBeforeHRR {
|
|
hs.hello.raw = nil
|
|
|
|
// Send a dummy ECH extension in the updated ClientHello. This will
|
|
// trigger a server abort, since no ECH extension was sent in the
|
|
// previous ClientHello.
|
|
var err error
|
|
hs.hello.ech, err = echGenerateGreaseExt(c.config.rand())
|
|
if err != nil {
|
|
return fmt.Errorf("tls: ech: failed to generate grease ECH: %s", err)
|
|
}
|
|
}
|
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
serverHello, ok := msg.(*serverHelloMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(serverHello, msg)
|
|
}
|
|
hs.serverHello = serverHello
|
|
|
|
if err := hs.checkServerHelloOrHRR(); err != nil {
|
|
return err
|
|
}
|
|
|
|
hs.transcript.Write(hs.hello.marshal())
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
|
c := hs.c
|
|
|
|
defer func() {
|
|
hs.hsTimings.ProcessServerHello = hs.hsTimings.elapsedTime()
|
|
}()
|
|
|
|
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 hs.serverHello.serverShare.group != clientKeySharePrivateCurveID(hs.keySharePrivate) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: server selected unsupported group")
|
|
}
|
|
|
|
c.handleCFEvent(CFEventTLSNegotiatedNamedKEX{
|
|
KEX: hs.serverHello.serverShare.group,
|
|
})
|
|
|
|
if !hs.serverHello.selectedIdentityPresent {
|
|
return nil
|
|
}
|
|
|
|
// Per the rules of draft-ietf-tls-esni-13, Section 6.1, the server is not
|
|
// permitted to resume a connection connection in the outer handshake. If
|
|
// ECH is rejected and the client-facing server replies with a
|
|
// "pre_shared_key" extension in its ServerHello, then the client MUST abort
|
|
// the handshake with an "illegal_parameter" alert.
|
|
if c.ech.offered && !c.ech.accepted {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: ech: client-facing server offered PSK after ECH rejection")
|
|
}
|
|
|
|
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.serverCertificates
|
|
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
|
|
|
|
var sharedKey []byte
|
|
if params, ok := hs.keySharePrivate.(ecdheParameters); ok {
|
|
sharedKey = params.SharedKey(hs.serverHello.serverShare.data)
|
|
} else if sk, ok := hs.keySharePrivate.(circlKem.PrivateKey); ok {
|
|
var err error
|
|
sharedKey, err = sk.Scheme().Decapsulate(sk, hs.serverHello.serverShare.data)
|
|
if err != nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return fmt.Errorf("%s decaps: %w", sk.Scheme().Name(), err)
|
|
}
|
|
}
|
|
|
|
if sharedKey == nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return fmt.Errorf("tls: invalid server key share")
|
|
}
|
|
|
|
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, clientSecret)
|
|
serverSecret := hs.suite.deriveSecret(handshakeSecret,
|
|
serverHandshakeTrafficLabel, hs.transcript)
|
|
c.in.setTrafficSecret(hs.suite, 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()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(encryptedExtensions, msg)
|
|
}
|
|
hs.transcript.Write(encryptedExtensions.marshal())
|
|
|
|
if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil {
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return err
|
|
}
|
|
c.clientProtocol = encryptedExtensions.alpnProtocol
|
|
|
|
if c.ech.offered && len(encryptedExtensions.ech) > 0 {
|
|
if !c.ech.accepted {
|
|
// If the server rejects ECH, then it may send retry configurations.
|
|
// If present, we must check them for syntactic correctness and
|
|
// abort if they are not correct.
|
|
c.ech.retryConfigs = encryptedExtensions.ech
|
|
if _, err = UnmarshalECHConfigs(c.ech.retryConfigs); err != nil {
|
|
c.sendAlert(alertDecodeError)
|
|
return fmt.Errorf("tls: ech: failed to parse retry configs: %s", err)
|
|
}
|
|
} else {
|
|
// Retry configs must not be sent in the inner handshake.
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: ech: got retry configs after ECH acceptance")
|
|
}
|
|
}
|
|
|
|
hs.hsTimings.ReadEncryptedExtensions = hs.hsTimings.elapsedTime()
|
|
|
|
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()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
certReq, ok := msg.(*certificateRequestMsgTLS13)
|
|
if ok {
|
|
hs.transcript.Write(certReq.marshal())
|
|
|
|
hs.certReq = certReq
|
|
|
|
msg, err = c.readHandshake()
|
|
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")
|
|
}
|
|
hs.transcript.Write(certMsg.marshal())
|
|
|
|
hs.hsTimings.ReadCertificate = hs.hsTimings.elapsedTime()
|
|
|
|
c.scts = certMsg.certificate.SignedCertificateTimestamps
|
|
c.ocspResponse = certMsg.certificate.OCSPStaple
|
|
|
|
if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err = c.readHandshake()
|
|
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, c.config.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")
|
|
}
|
|
if certMsg.delegatedCredential {
|
|
if err := hs.processDelegatedCredentialFromServer(certMsg.certificate.DelegatedCredential, certVerify); err != nil {
|
|
return err // alert sent
|
|
}
|
|
}
|
|
|
|
pk := c.peerCertificates[0].PublicKey
|
|
if c.verifiedDC != nil {
|
|
pk = c.verifiedDC.cred.publicKey
|
|
}
|
|
|
|
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
|
|
if err := verifyHandshakeSignature(sigType, pk,
|
|
sigHash, signed, certVerify.signature); err != nil {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
|
|
}
|
|
|
|
hs.transcript.Write(certVerify.marshal())
|
|
|
|
hs.hsTimings.ReadCertificateVerify = hs.hsTimings.elapsedTime()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) readServerFinished() 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)
|
|
}
|
|
|
|
hs.hsTimings.ReadServerFinished = hs.hsTimings.elapsedTime()
|
|
|
|
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")
|
|
}
|
|
|
|
hs.transcript.Write(finished.marshal())
|
|
|
|
// 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, 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 certificateRequestInfo(certReq *certificateRequestMsgTLS13, vers uint16, ctx context.Context) *CertificateRequestInfo {
|
|
cri := &CertificateRequestInfo{
|
|
SupportsDelegatedCredential: certReq.supportDelegatedCredential,
|
|
SignatureSchemes: certReq.supportedSignatureAlgorithms,
|
|
SignatureSchemesDC: certReq.supportedSignatureAlgorithmsDC,
|
|
AcceptableCAs: certReq.certificateAuthorities,
|
|
Version: vers,
|
|
ctx: ctx,
|
|
}
|
|
|
|
return cri
|
|
}
|
|
|
|
// getClientDelegatedCredential will return a Delegated Credential pair (a
|
|
// Delegated Credential and its private key) for the given CertificateRequestInfo,
|
|
// defaulting to the first element of cert.DelegatedCredentialPair.
|
|
// The returned Delegated Credential could be invalid for usage in the handshake.
|
|
// Returns an error if there are no delegated credentials or if the one found
|
|
// cannot be used for the current connection.
|
|
func getClientDelegatedCredential(cri *CertificateRequestInfo, cert *Certificate) (*DelegatedCredentialPair, error) {
|
|
if len(cert.DelegatedCredentials) == 0 {
|
|
return nil, errors.New("no Delegated Credential found")
|
|
}
|
|
|
|
for _, dcPair := range cert.DelegatedCredentials {
|
|
// If the client sent the signature_algorithms in the DC extension, ensure it supports
|
|
// schemes we can use with this delegated credential.
|
|
if len(cri.SignatureSchemesDC) > 0 {
|
|
if _, err := selectSignatureSchemeDC(VersionTLS13, dcPair.DC, cri.SignatureSchemes, cri.SignatureSchemesDC); err == nil {
|
|
return &dcPair, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// No delegated credential can be returned.
|
|
return nil, errors.New("no valid Delegated Credential found")
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
|
|
c := hs.c
|
|
|
|
if hs.certReq == nil {
|
|
return nil
|
|
}
|
|
|
|
cri := certificateRequestInfo(hs.certReq, c.vers, hs.ctx)
|
|
|
|
cert, err := c.getClientCertificate(cri)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var dcPair *DelegatedCredentialPair
|
|
if hs.certReq.supportDelegatedCredential && len(hs.certReq.supportedSignatureAlgorithmsDC) > 0 {
|
|
// getClientDelegatedCredential selects a delegated credential that the server has advertised support for, if possible.
|
|
if delegatedCredentialPair, err := getClientDelegatedCredential(cri, cert); err == nil {
|
|
if delegatedCredentialPair.DC != nil && delegatedCredentialPair.PrivateKey != nil {
|
|
var err error
|
|
// Even if the Delegated Credential has already been marshalled, be sure it is the correct one.
|
|
if delegatedCredentialPair.DC.raw, err = delegatedCredentialPair.DC.Marshal(); err == nil {
|
|
dcPair = delegatedCredentialPair
|
|
cert.DelegatedCredential = dcPair.DC.raw
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
certMsg := new(certificateMsgTLS13)
|
|
|
|
certMsg.certificate = *cert
|
|
certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
|
|
certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
|
|
certMsg.delegatedCredential = hs.certReq.supportDelegatedCredential && len(cert.DelegatedCredential) > 0
|
|
|
|
hs.transcript.Write(certMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
hs.hsTimings.WriteCertificate = hs.hsTimings.elapsedTime()
|
|
|
|
// If we sent an empty certificate message, skip the CertificateVerify.
|
|
if len(cert.Certificate) == 0 {
|
|
return nil
|
|
}
|
|
|
|
certVerifyMsg := new(certificateVerifyMsg)
|
|
certVerifyMsg.hasSignatureAlgorithm = true
|
|
|
|
var sigAlgorithm SignatureScheme
|
|
suppSigAlgo := hs.certReq.supportedSignatureAlgorithms
|
|
sigAlgorithm, err = selectSignatureScheme(c.vers, cert, suppSigAlgo)
|
|
if err != nil {
|
|
// getClientCertificate returned a certificate incompatible with the
|
|
// CertificateRequestInfo supported signature algorithms.
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return err
|
|
}
|
|
|
|
if certMsg.delegatedCredential {
|
|
suppSigAlgo = hs.certReq.supportedSignatureAlgorithmsDC
|
|
if dcPair == nil || dcPair.DC == nil {
|
|
cert.DelegatedCredential = nil
|
|
} else {
|
|
sigAlgorithm = dcPair.DC.cred.expCertVerfAlgo
|
|
cert.PrivateKey = dcPair.PrivateKey
|
|
}
|
|
}
|
|
|
|
certVerifyMsg.signatureAlgorithm = sigAlgorithm
|
|
|
|
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
|
|
|
|
hs.transcript.Write(certVerifyMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
hs.hsTimings.WriteCertificateVerify = hs.hsTimings.elapsedTime()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
|
|
c := hs.c
|
|
|
|
finished := &finishedMsg{
|
|
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
|
}
|
|
|
|
hs.transcript.Write(finished.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
hs.hsTimings.WriteClientFinished = hs.hsTimings.elapsedTime()
|
|
|
|
c.out.setTrafficSecret(hs.suite, hs.trafficSecret)
|
|
|
|
if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil && !c.config.ECHEnabled {
|
|
c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
|
|
resumptionLabel, hs.transcript)
|
|
}
|
|
|
|
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 || c.config.ECHEnabled {
|
|
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")
|
|
}
|
|
|
|
cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
|
|
if cipherSuite == nil || c.resumptionSecret == nil {
|
|
return c.sendAlert(alertInternalError)
|
|
}
|
|
|
|
// Save the resumption_master_secret and nonce instead of deriving the PSK
|
|
// to do the least amount of work on NewSessionTicket messages before we
|
|
// know if the ticket will be used. Forward secrecy of resumed connections
|
|
// is guaranteed by the requirement for pskModeDHE.
|
|
session := &ClientSessionState{
|
|
sessionTicket: msg.label,
|
|
vers: c.vers,
|
|
cipherSuite: c.cipherSuite,
|
|
masterSecret: c.resumptionSecret,
|
|
serverCertificates: c.peerCertificates,
|
|
verifiedChains: c.verifiedChains,
|
|
receivedAt: c.config.time(),
|
|
nonce: msg.nonce,
|
|
useBy: c.config.time().Add(lifetime),
|
|
ageAdd: msg.ageAdd,
|
|
ocspResponse: c.ocspResponse,
|
|
scts: c.scts,
|
|
}
|
|
|
|
cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
|
|
c.config.ClientSessionCache.Put(cacheKey, session)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *clientHandshakeStateTLS13) abortIfRequired() error {
|
|
c := hs.c
|
|
if c.ech.offered && !c.ech.accepted {
|
|
// If ECH was rejected, then abort the handshake.
|
|
c.sendAlert(alertECHRequired)
|
|
return errors.New("tls: ech: rejected")
|
|
}
|
|
return nil
|
|
}
|