mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-01 19:17:36 +03:00
* crypto/tls: Add hybrid post-quantum key agreement (#13) * import: client-side KEM from cloudflare/go * import: server-side KEM from cloudflare/go * fix: modify test to get rid of CFEvents. Note: uTLS does not promise any server-side functionality, and this change is made to be able to conduct unit tests which requires both side to be able to handle KEM Curves. Co-authored-by: Christopher Wood <caw@heapingbits.net> Co-Authored-By: Bas Westerbaan <bas@westerbaan.name> ---- Based on: * crypto/tls: Add hybrid post-quantum key agreement Adds X25519Kyber512Draft00, X25519Kyber768Draft00, and P256Kyber768Draft00 hybrid post-quantum key agreements with temporary group identifiers. The hybrid post-quantum key exchanges uses plain X{25519,448} instead of HPKE, which we assume will be more likely to be adopted. The order is chosen to match CECPQ2. Not enabled by default. Adds CFEvents to detect `HelloRetryRequest`s and to signal which key agreement was used. Co-authored-by: Christopher Wood <caw@heapingbits.net> [bas, 1.20.1: also adds P256Kyber768Draft00] [pwu, 1.20.4: updated circl to v1.3.3, moved code to cfevent.go] * crypto: add support for CIRCL signature schemes * only partially port the commit from cloudflare/go. We would stick to the official x509 at the cost of incompatibility. Co-Authored-By: Bas Westerbaan <bas@westerbaan.name> Co-Authored-By: Christopher Patton <3453007+cjpatton@users.noreply.github.com> Co-Authored-By: Peter Wu <peter@lekensteyn.nl> * crypto/tls: add new X25519Kyber768Draft00 code point Ported from cloudflare/go to support the upcoming new post-quantum keyshare. ---- * Point tls.X25519Kyber768Draft00 to the new 0x6399 identifier while the old 0xfe31 identifier is available as tls.X25519Kyber768Draft00Old. * Make sure that the kem.PrivateKey can always be mapped to the CurveID that was linked to it. This is needed since we now have two ID aliasing to the same scheme, and clients need to be able to detect whether the key share presented by the server actually matches the key share that the client originally sent. * Update tests, add the new identifier and remove unnecessary code. Link: https://mailarchive.ietf.org/arch/msg/tls/HAWpNpgptl--UZNSYuvsjB-Pc2k/ Link: https://datatracker.ietf.org/doc/draft-tls-westerbaan-xyber768d00/02/ Co-Authored-By: Peter Wu <peter@lekensteyn.nl> Co-Authored-By: Bas Westerbaan <bas@westerbaan.name> --------- Co-authored-by: Bas Westerbaan <bas@westerbaan.name> Co-authored-by: Christopher Patton <3453007+cjpatton@users.noreply.github.com> Co-authored-by: Peter Wu <peter@lekensteyn.nl>
1001 lines
29 KiB
Go
1001 lines
29 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"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"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
|
|
earlyData bool
|
|
suite *cipherSuiteTLS13
|
|
cert *Certificate
|
|
sigAlg SignatureScheme
|
|
selectedGroup CurveID
|
|
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
|
|
|
|
if needFIPS() {
|
|
return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
|
|
}
|
|
|
|
// 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
|
|
}
|
|
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 && curveIdToCirclScheme(selectedGroup) == nil && !ok {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
}
|
|
if kem := curveIdToCirclScheme(selectedGroup); kem != nil {
|
|
ct, ss, alert, err := encapsulateForKem(kem, c.config.rand(), clientKeyShare.data)
|
|
if err != nil {
|
|
c.sendAlert(alert)
|
|
return fmt.Errorf("%s encap: %w", kem.Name(), err)
|
|
}
|
|
hs.hello.serverShare = keyShare{group: selectedGroup, data: ct}
|
|
hs.sharedKey = ss
|
|
} else {
|
|
key, err := generateECDHEKey(c.config.rand(), selectedGroup)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
hs.hello.serverShare = keyShare{group: selectedGroup, data: key.PublicKey().Bytes()}
|
|
peerKey, err := key.Curve().NewPublicKey(clientKeyShare.data)
|
|
if err == nil {
|
|
hs.sharedKey, _ = key.ECDH(peerKey)
|
|
}
|
|
}
|
|
if hs.sharedKey == nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid client key share")
|
|
}
|
|
|
|
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 {
|
|
if hs.clientHello.quicTransportParameters == nil {
|
|
// RFC 9001 Section 8.2.
|
|
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
|
|
}
|
|
|
|
hs.earlySecret = hs.suite.extract(sessionState.secret, 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")
|
|
}
|
|
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.suite.deriveSecret(hs.earlySecret, clientEarlyTrafficLabel, 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) 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 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 _, err := hs.c.writeHandshakeRecord(helloRetryRequest, hs.transcript); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// clientHelloMsg is not included in the transcript.
|
|
msg, err := c.readHandshake(nil)
|
|
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
|
|
|
|
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 = 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, QUICEncryptionLevelHandshake, clientSecret)
|
|
serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
serverHandshakeTrafficLabel, 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 _, 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 = c.config.supportedSignatureAlgorithms() // [UTLS] ported from cloudflare/go
|
|
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.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, 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.suite.deriveSecret(hs.masterSecret,
|
|
resumptionLabel, hs.transcript)
|
|
|
|
if !hs.shouldSendSessionTickets() {
|
|
return nil
|
|
}
|
|
return c.sendSessionTicket(false)
|
|
}
|
|
|
|
func (c *Conn) sendSessionTicket(earlyData bool) 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 := suite.expandLabel(c.resumptionSecret, "resumption",
|
|
nil, suite.hash.Size())
|
|
|
|
m := new(newSessionTicketMsgTLS13)
|
|
|
|
state, err := c.sessionState()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state.secret = psk
|
|
state.EarlyData = earlyData
|
|
if c.config.WrapSession != nil {
|
|
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)
|
|
_, err = c.config.rand().Read(ageAdd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.ageAdd = binary.LittleEndian.Uint32(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, c.config.supportedSignatureAlgorithms()) { // [UTLS] ported from cloudflare/go
|
|
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
|
|
}
|