mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-03 20:17:36 +03:00
We now have a (well, two, depending on AES hardware support) universal cipher suite preference order, based on their security and performance. Peer and application lists are now treated as filters (and AES hardware support hints) that are applied to this universal order. This removes a complex and nuanced decision from the application's responsibilities, one which we are better equipped to make and which applications usually don't need to have an opinion about. It also lets us worry less about what suites we support or enable, because we can be confident that bad ones won't be selected over good ones. This also moves 3DES suites to InsecureCipherSuites(), even if they are not disabled by default. Just because we can keep them as a last resort it doesn't mean they are secure. Thankfully we had not promised that Insecure means disabled by default. Notable test changes: - TestCipherSuiteCertPreferenceECDSA was testing that we'd pick the right certificate regardless of CipherSuite ordering, which is now completely ignored, as tested by TestCipherSuitePreference. Removed. - The openssl command of TestHandshakeServerExportKeyingMaterial was broken for TLS 1.0 in CL 262857, but its golden file was not regenerated, so the test kept passing. It now broke because the selected suite from the ones in the golden file changed. - In TestAESCipherReordering, "server strongly prefers AES-GCM" is removed because there is no way for a server to express a strong preference anymore; "client prefers AES-GCM and AES-CBC over ChaCha" switched to ChaCha20 when the server lacks AES hardware; and finally "client supports multiple AES-GCM" changed to always prefer AES-128 per the universal preference list. * this is going back on an explicit decision from CL 262857, and while that client order is weird and does suggest a strong dislike for ChaCha20, we have a strong dislike for software AES, so it didn't feel worth making the logic more complex - All Client-* golden files had to be regenerated because the ClientHello cipher suites have changed. (Even when Config.CipherSuites was limited to one suite, the TLS 1.3 default order changed.) Fixes #45430 Fixes #41476 (as 3DES is now always the last resort) Change-Id: If5f5d356c0f8d1f1c7542fb06644a478d6bad1e5 Reviewed-on: https://go-review.googlesource.com/c/go/+/314609 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> Trust: Filippo Valsorda <filippo@golang.org>
861 lines
25 KiB
Go
861 lines
25 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"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"sync/atomic"
|
|
"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 serverHandshakeStateTLS13 struct {
|
|
c *Conn
|
|
ctx context.Context
|
|
clientHello *clientHelloMsg
|
|
hello *serverHelloMsg
|
|
sentDummyCCS bool
|
|
usingPSK bool
|
|
suite *cipherSuiteTLS13
|
|
cert *Certificate
|
|
sigAlg SignatureScheme
|
|
earlySecret []byte
|
|
sharedKey []byte
|
|
handshakeSecret []byte
|
|
masterSecret []byte
|
|
trafficSecret []byte // client_application_traffic_secret_0
|
|
transcript hash.Hash
|
|
clientFinished []byte
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
|
c := hs.c
|
|
|
|
hs.hello = new(serverHelloMsg)
|
|
|
|
// TLS 1.3 froze the ServerHello.legacy_version field, and uses
|
|
// supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1.
|
|
hs.hello.vers = VersionTLS12
|
|
hs.hello.supportedVersion = c.vers
|
|
|
|
if len(hs.clientHello.supportedVersions) == 0 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
|
|
}
|
|
|
|
// 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() {
|
|
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 {
|
|
// 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
|
|
}
|
|
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()
|
|
|
|
// Pick the ECDHE group in server preference order, but give priority to
|
|
// groups with a key share, to avoid a HelloRetryRequest round-trip.
|
|
var selectedGroup CurveID
|
|
var clientKeyShare *keyShare
|
|
GroupSelection:
|
|
for _, preferredGroup := range c.config.curvePreferences() {
|
|
for _, ks := range hs.clientHello.keyShares {
|
|
if ks.group == preferredGroup {
|
|
selectedGroup = ks.group
|
|
clientKeyShare = &ks
|
|
break GroupSelection
|
|
}
|
|
}
|
|
if selectedGroup != 0 {
|
|
continue
|
|
}
|
|
for _, group := range hs.clientHello.supportedCurves {
|
|
if group == preferredGroup {
|
|
selectedGroup = group
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if selectedGroup == 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: no ECDHE curve supported by both client and server")
|
|
}
|
|
if clientKeyShare == nil {
|
|
if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
|
|
return err
|
|
}
|
|
clientKeyShare = &hs.clientHello.keyShares[0]
|
|
}
|
|
|
|
if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
}
|
|
params, err := generateECDHEParameters(c.config.rand(), selectedGroup)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()}
|
|
hs.sharedKey = params.SharedKey(clientKeyShare.data)
|
|
if hs.sharedKey == nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid client key share")
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
plaintext, _ := c.decryptTicket(identity.label)
|
|
if plaintext == nil {
|
|
continue
|
|
}
|
|
sessionState := new(sessionStateTLS13)
|
|
if ok := sessionState.unmarshal(plaintext); !ok {
|
|
continue
|
|
}
|
|
|
|
createdAt := time.Unix(int64(sessionState.createdAt), 0)
|
|
if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
|
|
continue
|
|
}
|
|
|
|
// We don't check the obfuscated ticket age because it's affected by
|
|
// clock skew and it's only a freshness signal useful for shrinking the
|
|
// window for replay attacks, which don't affect us as we don't do 0-RTT.
|
|
|
|
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.certificate.Certificate) != 0
|
|
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
|
if needClientCerts && !sessionHasClientCerts {
|
|
continue
|
|
}
|
|
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
|
continue
|
|
}
|
|
|
|
psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption",
|
|
nil, hs.suite.hash.Size())
|
|
hs.earlySecret = hs.suite.extract(psk, nil)
|
|
binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil)
|
|
// 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")
|
|
}
|
|
transcript.Write(hs.clientHello.marshalWithoutBinders())
|
|
pskBinder := hs.suite.finishedHash(binderKey, transcript)
|
|
if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid PSK binder")
|
|
}
|
|
|
|
c.didResume = true
|
|
if err := c.processCertsFromClient(sessionState.certificate); err != nil {
|
|
return err
|
|
}
|
|
|
|
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.sentDummyCCS {
|
|
return nil
|
|
}
|
|
hs.sentDummyCCS = true
|
|
|
|
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
|
return err
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
|
|
c := hs.c
|
|
|
|
// The first ClientHello gets double-hashed into the transcript upon a
|
|
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
|
hs.transcript.Write(hs.clientHello.marshal())
|
|
chHash := hs.transcript.Sum(nil)
|
|
hs.transcript.Reset()
|
|
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
hs.transcript.Write(chHash)
|
|
|
|
helloRetryRequest := &serverHelloMsg{
|
|
vers: hs.hello.vers,
|
|
random: helloRetryRequestRandom,
|
|
sessionId: hs.hello.sessionId,
|
|
cipherSuite: hs.hello.cipherSuite,
|
|
compressionMethod: hs.hello.compressionMethod,
|
|
supportedVersion: hs.hello.supportedVersion,
|
|
selectedGroup: selectedGroup,
|
|
}
|
|
|
|
hs.transcript.Write(helloRetryRequest.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientHello, ok := msg.(*clientHelloMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(clientHello, msg)
|
|
}
|
|
|
|
if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client sent invalid key share in second ClientHello")
|
|
}
|
|
|
|
if clientHello.earlyData {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client indicated early data in second ClientHello")
|
|
}
|
|
|
|
if illegalClientHelloChange(clientHello, hs.clientHello) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client illegally modified second ClientHello")
|
|
}
|
|
|
|
hs.clientHello = clientHello
|
|
return nil
|
|
}
|
|
|
|
// illegalClientHelloChange 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
|
|
|
|
hs.transcript.Write(hs.clientHello.marshal())
|
|
hs.transcript.Write(hs.hello.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
earlySecret := hs.earlySecret
|
|
if earlySecret == nil {
|
|
earlySecret = hs.suite.extract(nil, nil)
|
|
}
|
|
hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
|
|
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
|
|
|
clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
clientHandshakeTrafficLabel, hs.transcript)
|
|
c.in.setTrafficSecret(hs.suite, clientSecret)
|
|
serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
serverHandshakeTrafficLabel, hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, serverSecret)
|
|
|
|
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
encryptedExtensions := new(encryptedExtensionsMsg)
|
|
|
|
if len(c.config.NextProtos) > 0 && len(hs.clientHello.alpnProtocols) > 0 {
|
|
selectedProto := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos)
|
|
if selectedProto == "" {
|
|
c.sendAlert(alertNoApplicationProtocol)
|
|
return fmt.Errorf("tls: client requested unsupported application protocols (%s)", hs.clientHello.alpnProtocols)
|
|
}
|
|
encryptedExtensions.alpnProtocol = selectedProto
|
|
c.clientProtocol = selectedProto
|
|
}
|
|
|
|
hs.transcript.Write(encryptedExtensions.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) 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()
|
|
}
|
|
|
|
hs.transcript.Write(certReq.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); 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
|
|
|
|
hs.transcript.Write(certMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
certVerifyMsg := new(certificateVerifyMsg)
|
|
certVerifyMsg.hasSignatureAlgorithm = true
|
|
certVerifyMsg.signatureAlgorithm = hs.sigAlg
|
|
|
|
sigType, 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
|
|
|
|
hs.transcript.Write(certVerifyMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
|
|
c := hs.c
|
|
|
|
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
|
|
}
|
|
|
|
// Derive secrets that take context through the server Finished.
|
|
|
|
hs.masterSecret = hs.suite.extract(nil,
|
|
hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
|
|
|
|
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
|
clientApplicationTrafficLabel, hs.transcript)
|
|
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
|
serverApplicationTrafficLabel, hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, serverSecret)
|
|
|
|
err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
c.ekm = hs.suite.exportKeyingMaterial(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
|
|
}
|
|
|
|
// 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,
|
|
}
|
|
hs.transcript.Write(finishedMsg.marshal())
|
|
|
|
if !hs.shouldSendSessionTickets() {
|
|
return nil
|
|
}
|
|
|
|
resumptionSecret := hs.suite.deriveSecret(hs.masterSecret,
|
|
resumptionLabel, hs.transcript)
|
|
|
|
m := new(newSessionTicketMsgTLS13)
|
|
|
|
var certsFromClient [][]byte
|
|
for _, cert := range c.peerCertificates {
|
|
certsFromClient = append(certsFromClient, cert.Raw)
|
|
}
|
|
state := sessionStateTLS13{
|
|
cipherSuite: hs.suite.id,
|
|
createdAt: uint64(c.config.time().Unix()),
|
|
resumptionSecret: resumptionSecret,
|
|
certificate: Certificate{
|
|
Certificate: certsFromClient,
|
|
OCSPStaple: c.ocspResponse,
|
|
SignedCertificateTimestamps: c.scts,
|
|
},
|
|
}
|
|
var err error
|
|
m.label, err = c.encryptTicket(state.marshal())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
|
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); 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()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
certMsg, ok := msg.(*certificateMsgTLS13)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(certMsg, msg)
|
|
}
|
|
hs.transcript.Write(certMsg.marshal())
|
|
|
|
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 {
|
|
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, 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())
|
|
}
|
|
|
|
hs.transcript.Write(certVerify.marshal())
|
|
}
|
|
|
|
// 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
|
|
|
|
msg, err := c.readHandshake()
|
|
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, hs.trafficSecret)
|
|
|
|
return nil
|
|
}
|