package tls
import (
"bytes"
"context"
"crypto"
"crypto/ecdh"
"crypto/hmac"
"crypto/rsa"
"errors"
"hash"
"time"
)
type clientHandshakeStateTLS13 struct {
c *Conn
ctx context .Context
serverHello *serverHelloMsg
hello *clientHelloMsg
ecdheKey *ecdh .PrivateKey
session *SessionState
earlySecret []byte
binderKey []byte
certReq *certificateRequestMsgTLS13
usingPSK bool
sentDummyCCS bool
suite *cipherSuiteTLS13
transcript hash .Hash
masterSecret []byte
trafficSecret []byte
}
func (hs *clientHandshakeStateTLS13 ) handshake () error {
c := hs .c
if needFIPS () {
return errors .New ("tls: internal error: TLS 1.3 reached in FIPS mode" )
}
if c .handshakes > 0 {
c .sendAlert (alertProtocolVersion )
return errors .New ("tls: server selected TLS 1.3 in a renegotiation" )
}
if hs .ecdheKey == 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
}
c .isHandshakeComplete .Store (true )
return nil
}
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 .extendedMasterSecret ||
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
}
func (hs *clientHandshakeStateTLS13 ) sendDummyChangeCipherSpec () error {
if hs .c .quic != nil {
return nil
}
if hs .sentDummyCCS {
return nil
}
hs .sentDummyCCS = true
return hs .c .writeChangeCipherRecord ()
}
func (hs *clientHandshakeStateTLS13 ) processHelloRetryRequest () error {
c := hs .c
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
}
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 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 sentID , _ := curveIDForCurve (hs .ecdheKey .Curve ()); sentID == curveID {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: server sent an unnecessary HelloRetryRequest key_share" )
}
if _ , ok := curveForCurveID (curveID ); !ok {
c .sendAlert (alertInternalError )
return errors .New ("tls: CurvePreferences includes unsupported curve" )
}
key , err := generateECDHEKey (c .config .rand (), curveID )
if err != nil {
c .sendAlert (alertInternalError )
return err
}
hs .ecdheKey = key
hs .hello .keyShares = []keyShare {{group : curveID , data : key .PublicKey ().Bytes ()}}
}
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 {
ticketAge := c .config .time ().Sub (time .Unix (int64 (hs .session .createdAt ), 0 ))
hs .hello .pskIdentities [0 ].obfuscatedTicketAge = uint32 (ticketAge /time .Millisecond ) + 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 , 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 {
hs .hello .pskIdentities = nil
hs .hello .pskBinders = nil
}
}
if hs .hello .earlyData {
hs .hello .earlyData = false
c .quicRejectedEarlyData ()
}
if _ , err := hs .c .writeHandshakeRecord (hs .hello , hs .transcript ); err != nil {
return err
}
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 sentID , _ := curveIDForCurve (hs .ecdheKey .Curve ()); hs .serverHello .serverShare .group != sentID {
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 .peerCertificates
c .activeCertHandles = hs .session .activeCertHandles
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
peerKey , err := hs .ecdheKey .Curve ().NewPublicKey (hs .serverHello .serverShare .data )
if err != nil {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: invalid server key share" )
}
sharedKey , err := hs .ecdheKey .ECDH (peerKey )
if err != 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 , QUICEncryptionLevelHandshake , clientSecret )
serverSecret := hs .suite .deriveSecret (handshakeSecret ,
serverHandshakeTrafficLabel , hs .transcript )
c .in .setTrafficSecret (hs .suite , QUICEncryptionLevelHandshake , serverSecret )
if c .quic != nil {
if c .hand .Len () != 0 {
c .sendAlert (alertUnexpectedMessage )
}
c .quicSetWriteSecret (QUICEncryptionLevelHandshake , hs .suite .id , clientSecret )
c .quicSetReadSecret (QUICEncryptionLevelHandshake , hs .suite .id , 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 , c .quic != nil ); err != nil {
c .sendAlert (alertNoApplicationProtocol )
return err
}
c .clientProtocol = encryptedExtensions .alpnProtocol
if c .quic != nil {
if encryptedExtensions .quicTransportParameters == nil {
c .sendAlert (alertMissingExtension )
return errors .New ("tls: server did not send a quic_transport_parameters extension" )
}
c .quicSetTransportParameters (encryptedExtensions .quicTransportParameters )
} else {
if encryptedExtensions .quicTransportParameters != nil {
c .sendAlert (alertUnsupportedExtension )
return errors .New ("tls: server sent an unexpected quic_transport_parameters extension" )
}
}
if !hs .hello .earlyData && encryptedExtensions .earlyData {
c .sendAlert (alertUnsupportedExtension )
return errors .New ("tls: server sent an unexpected early_data extension" )
}
if hs .hello .earlyData && !encryptedExtensions .earlyData {
c .quicRejectedEarlyData ()
}
if encryptedExtensions .earlyData {
if hs .session .cipherSuite != c .cipherSuite {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: server accepted 0-RTT with the wrong cipher suite" )
}
if hs .session .alpnProtocol != c .clientProtocol {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: server accepted 0-RTT with the wrong ALPN" )
}
}
return nil
}
func (hs *clientHandshakeStateTLS13 ) readServerCertificate () error {
c := hs .c
if hs .usingPSK {
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
}
msg , err = c .readHandshake (nil )
if err != nil {
return err
}
certVerify , ok := msg .(*certificateVerifyMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (certVerify , msg )
}
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
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
}
hs .trafficSecret = hs .suite .deriveSecret (hs .masterSecret ,
clientApplicationTrafficLabel , hs .transcript )
serverSecret := hs .suite .deriveSecret (hs .masterSecret ,
serverApplicationTrafficLabel , hs .transcript )
c .in .setTrafficSecret (hs .suite , QUICEncryptionLevelApplication , 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 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 {
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 , QUICEncryptionLevelApplication , hs .trafficSecret )
if !c .config .SessionTicketsDisabled && c .config .ClientSessionCache != nil {
c .resumptionSecret = hs .suite .deriveSecret (hs .masterSecret ,
resumptionLabel , hs .transcript )
}
if c .quic != nil {
if c .hand .Len () != 0 {
c .sendAlert (alertUnexpectedMessage )
}
c .quicSetWriteSecret (QUICEncryptionLevelApplication , hs .suite .id , hs .trafficSecret )
}
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
}
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" )
}
if c .quic != nil && msg .maxEarlyData != 0 && msg .maxEarlyData != 0xffffffff {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: invalid early data for QUIC connection" )
}
cipherSuite := cipherSuiteTLS13ByID (c .cipherSuite )
if cipherSuite == nil || c .resumptionSecret == nil {
return c .sendAlert (alertInternalError )
}
psk := cipherSuite .expandLabel (c .resumptionSecret , "resumption" ,
msg .nonce , cipherSuite .hash .Size ())
session , err := c .sessionState ()
if err != nil {
c .sendAlert (alertInternalError )
return err
}
session .secret = psk
session .useBy = uint64 (c .config .time ().Add (lifetime ).Unix ())
session .ageAdd = msg .ageAdd
session .EarlyData = c .quic != nil && msg .maxEarlyData == 0xffffffff
cs := &ClientSessionState {ticket : msg .label , session : session }
if cacheKey := c .clientSessionCacheKey (); cacheKey != "" {
c .config .ClientSessionCache .Put (cacheKey , cs )
}
return nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .