diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:18:25 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:18:25 +0000 |
commit | 109be507377fe7f6e8819ac94041d3fdcdf6fd2f (patch) | |
tree | 2806a689f8fab4a2ec9fc949830ef270a91d667d /src/crypto/tls/handshake_client_tls13.go | |
parent | Initial commit. (diff) | |
download | golang-1.19-upstream.tar.xz golang-1.19-upstream.zip |
Adding upstream version 1.19.8.upstream/1.19.8upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/crypto/tls/handshake_client_tls13.go')
-rw-r--r-- | src/crypto/tls/handshake_client_tls13.go | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go new file mode 100644 index 0000000..0f2dee4 --- /dev/null +++ b/src/crypto/tls/handshake_client_tls13.go @@ -0,0 +1,704 @@ +// 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" + "hash" + "sync/atomic" + "time" +) + +type clientHandshakeStateTLS13 struct { + c *Conn + ctx context.Context + serverHello *serverHelloMsg + hello *clientHelloMsg + ecdheParams ecdheParameters + + session *ClientSessionState + earlySecret []byte + binderKey []byte + + certReq *certificateRequestMsgTLS13 + usingPSK bool + sentDummyCCS bool + suite *cipherSuiteTLS13 + transcript hash.Hash + masterSecret []byte + trafficSecret []byte // client_application_traffic_secret_0 +} + +// 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 + + if needFIPS() { + return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") + } + + // The server must not select TLS 1.3 in a renegotiation. See RFC 8446, + // sections 4.1.2 and 4.1.3. + if c.handshakes > 0 { + c.sendAlert(alertProtocolVersion) + return errors.New("tls: server selected TLS 1.3 in a renegotiation") + } + + // Consistency check on the presence of a keyShare and its parameters. + if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 { + return c.sendAlert(alertInternalError) + } + + if err := hs.checkServerHelloOrHRR(); err != nil { + return err + } + + hs.transcript = hs.suite.hash.New() + + if err := transcriptMsg(hs.hello, hs.transcript); err != nil { + return err + } + + if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { + if err := hs.sendDummyChangeCipherSpec(); err != nil { + return err + } + if err := hs.processHelloRetryRequest(); err != nil { + return err + } + } + + if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { + return err + } + + c.buffering = true + if err := hs.processServerHello(); err != nil { + return err + } + if err := hs.sendDummyChangeCipherSpec(); err != nil { + return err + } + if err := hs.establishHandshakeKeys(); err != nil { + return err + } + if err := hs.readServerParameters(); err != nil { + return err + } + if err := hs.readServerCertificate(); err != nil { + return err + } + if err := hs.readServerFinished(); err != nil { + return err + } + if err := hs.sendClientCertificate(); err != nil { + return err + } + if err := hs.sendClientFinished(); err != nil { + return err + } + if _, err := c.flush(); err != nil { + return err + } + + 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 + + return hs.c.writeChangeCipherRecord() +} + +// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and +// resends hs.hello, and reads the new ServerHello into hs.serverHello. +func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { + c := hs.c + + // The first ClientHello gets double-hashed into the transcript upon a + // HelloRetryRequest. (The idea is that the server might offload transcript + // storage to the client in the cookie.) See RFC 8446, Section 4.4.1. + chHash := hs.transcript.Sum(nil) + hs.transcript.Reset() + hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) + hs.transcript.Write(chHash) + if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { + return err + } + + // 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 { + hs.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 hs.hello.supportedCurves { + if id == curveID { + curveOK = true + break + } + } + if !curveOK { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected unsupported group") + } + if hs.ecdheParams.CurveID() == curveID { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share") + } + 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.ecdheParams = params + hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} + } + + hs.hello.raw = nil + if len(hs.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) + hs.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) + if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { + return err + } + helloBytes, err := hs.hello.marshalWithoutBinders() + if err != nil { + return err + } + transcript.Write(helloBytes) + pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)} + if err := hs.hello.updateBinders(pskBinders); err != nil { + return err + } + } else { + // Server selected a cipher suite incompatible with the PSK. + hs.hello.pskIdentities = nil + hs.hello.pskBinders = nil + } + } + + if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil { + return err + } + + // serverHelloMsg is not included in the transcript + msg, err := c.readHandshake(nil) + if err != nil { + return err + } + + serverHello, ok := msg.(*serverHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverHello, msg) + } + hs.serverHello = serverHello + + if err := hs.checkServerHelloOrHRR(); err != nil { + return err + } + + return nil +} + +func (hs *clientHandshakeStateTLS13) processServerHello() error { + c := hs.c + + if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { + c.sendAlert(alertUnexpectedMessage) + return errors.New("tls: server sent two HelloRetryRequest messages") + } + + if len(hs.serverHello.cookie) != 0 { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: server sent a cookie in a normal ServerHello") + } + + if hs.serverHello.selectedGroup != 0 { + c.sendAlert(alertDecodeError) + return errors.New("tls: malformed key_share extension") + } + + if hs.serverHello.serverShare.group == 0 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server did not send a key share") + } + if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected unsupported group") + } + + if !hs.serverHello.selectedIdentityPresent { + return nil + } + + if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected an invalid PSK") + } + + if len(hs.hello.pskIdentities) != 1 || hs.session == nil { + return c.sendAlert(alertInternalError) + } + pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite) + if pskSuite == nil { + return c.sendAlert(alertInternalError) + } + if pskSuite.hash != hs.suite.hash { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected an invalid PSK and cipher suite pair") + } + + hs.usingPSK = true + c.didResume = true + c.peerCertificates = hs.session.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 + + sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data) + if sharedKey == nil { + c.sendAlert(alertIllegalParameter) + return errors.New("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(hs.transcript) + if err != nil { + return err + } + + encryptedExtensions, ok := msg.(*encryptedExtensionsMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(encryptedExtensions, msg) + } + + if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil { + c.sendAlert(alertUnsupportedExtension) + return err + } + c.clientProtocol = encryptedExtensions.alpnProtocol + + return nil +} + +func (hs *clientHandshakeStateTLS13) readServerCertificate() error { + c := hs.c + + // Either a PSK or a certificate is always used, but not both. + // See RFC 8446, Section 4.1.1. + if hs.usingPSK { + // Make sure the connection is still being verified whether or not this + // is a resumption. Resumptions currently don't reverify certificates so + // they don't call verifyServerCertificate. See Issue 31641. + if c.config.VerifyConnection != nil { + if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + return nil + } + + msg, err := c.readHandshake(hs.transcript) + if err != nil { + return err + } + + certReq, ok := msg.(*certificateRequestMsgTLS13) + if ok { + hs.certReq = certReq + + msg, err = c.readHandshake(hs.transcript) + if err != nil { + return err + } + } + + certMsg, ok := msg.(*certificateMsgTLS13) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + if len(certMsg.certificate.Certificate) == 0 { + c.sendAlert(alertDecodeError) + return errors.New("tls: received empty certificates message") + } + + c.scts = certMsg.certificate.SignedCertificateTimestamps + c.ocspResponse = certMsg.certificate.OCSPStaple + + if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil { + return err + } + + // certificateVerifyMsg is included in the transcript, but not until + // after we verify the handshake signature, since the state before + // this message was sent is used. + msg, err = c.readHandshake(nil) + if err != nil { + return err + } + + certVerify, ok := msg.(*certificateVerifyMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certVerify, msg) + } + + // See RFC 8446, Section 4.4.3. + if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: certificate used with invalid signature algorithm") + } + sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm) + if err != nil { + return c.sendAlert(alertInternalError) + } + if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: certificate used with invalid signature algorithm") + } + signed := signedMessage(sigHash, serverSignatureContext, hs.transcript) + if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey, + sigHash, signed, certVerify.signature); err != nil { + c.sendAlert(alertDecryptError) + return errors.New("tls: invalid signature by the server certificate: " + err.Error()) + } + + if err := transcriptMsg(certVerify, hs.transcript); err != nil { + return err + } + + return nil +} + +func (hs *clientHandshakeStateTLS13) readServerFinished() error { + c := hs.c + + // finishedMsg is included in the transcript, but not until after we + // check the client version, since the state before this message was + // sent is used during verification. + msg, err := c.readHandshake(nil) + if err != nil { + return err + } + + finished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(finished, msg) + } + + expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript) + if !hmac.Equal(expectedMAC, finished.verifyData) { + c.sendAlert(alertDecryptError) + return errors.New("tls: invalid server finished hash") + } + + if err := transcriptMsg(finished, hs.transcript); err != nil { + return err + } + + // Derive secrets that take context through the server Finished. + + hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret, + clientApplicationTrafficLabel, hs.transcript) + serverSecret := hs.suite.deriveSecret(hs.masterSecret, + serverApplicationTrafficLabel, hs.transcript) + c.in.setTrafficSecret(hs.suite, serverSecret) + + err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript) + + return nil +} + +func (hs *clientHandshakeStateTLS13) sendClientCertificate() error { + c := hs.c + + if hs.certReq == nil { + return nil + } + + cert, err := c.getClientCertificate(&CertificateRequestInfo{ + AcceptableCAs: hs.certReq.certificateAuthorities, + SignatureSchemes: hs.certReq.supportedSignatureAlgorithms, + Version: c.vers, + ctx: hs.ctx, + }) + if err != nil { + return err + } + + certMsg := new(certificateMsgTLS13) + + certMsg.certificate = *cert + certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0 + certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0 + + if _, err := hs.c.writeHandshakeRecord(certMsg, hs.transcript); err != nil { + return err + } + + // If we sent an empty certificate message, skip the CertificateVerify. + if len(cert.Certificate) == 0 { + return nil + } + + certVerifyMsg := new(certificateVerifyMsg) + certVerifyMsg.hasSignatureAlgorithm = true + + certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms) + if err != nil { + // getClientCertificate returned a certificate incompatible with the + // CertificateRequestInfo supported signature algorithms. + c.sendAlert(alertHandshakeFailure) + return err + } + + sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm) + if err != nil { + return c.sendAlert(alertInternalError) + } + + signed := signedMessage(sigHash, clientSignatureContext, hs.transcript) + signOpts := crypto.SignerOpts(sigHash) + if sigType == signatureRSAPSS { + signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} + } + sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts) + if err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to sign handshake: " + err.Error()) + } + certVerifyMsg.signature = sig + + if _, err := hs.c.writeHandshakeRecord(certVerifyMsg, hs.transcript); err != nil { + return err + } + + return nil +} + +func (hs *clientHandshakeStateTLS13) sendClientFinished() error { + c := hs.c + + finished := &finishedMsg{ + verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), + } + + if _, err := hs.c.writeHandshakeRecord(finished, hs.transcript); err != nil { + return err + } + + c.out.setTrafficSecret(hs.suite, hs.trafficSecret) + + if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil { + 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 { + 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 +} |