package tls
import (
"bytes"
"crypto"
"crypto/hmac"
"crypto/rsa"
"errors"
"hash"
"sync/atomic"
"time"
)
type clientHandshakeStateTLS13 struct {
c *Conn
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
}
func (hs *clientHandshakeStateTLS13 ) handshake () error {
c := hs .c
if c .handshakes > 0 {
c .sendAlert (alertProtocolVersion )
return errors .New ("tls: server selected TLS 1.3 in a renegotiation" )
}
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 ()
hs .transcript .Write (hs .hello .marshal ())
if bytes .Equal (hs .serverHello .random , helloRetryRequestRandom ) {
if err := hs .sendDummyChangeCipherSpec (); err != nil {
return err
}
if err := hs .processHelloRetryRequest (); err != nil {
return err
}
}
hs .transcript .Write (hs .serverHello .marshal ())
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
}
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
}
func (hs *clientHandshakeStateTLS13 ) sendDummyChangeCipherSpec () error {
if hs .sentDummyCCS {
return nil
}
hs .sentDummyCCS = true
_ , err := hs .c .writeRecord (recordTypeChangeCipherSpec , []byte {1 })
return err
}
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 )
hs .transcript .Write (hs .serverHello .marshal ())
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 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 {
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 )
transcript .Write (hs .serverHello .marshal ())
transcript .Write (hs .hello .marshalWithoutBinders ())
pskBinders := [][]byte {hs .suite .finishedHash (hs .binderKey , transcript )}
hs .hello .updateBinders (pskBinders )
} else {
hs .hello .pskIdentities = nil
hs .hello .pskBinders = nil
}
}
hs .transcript .Write (hs .hello .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , hs .hello .marshal ()); err != nil {
return err
}
msg , err := c .readHandshake ()
if err != nil {
return err
}
serverHello , ok := msg .(*serverHelloMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (serverHello , msg )
}
hs .serverHello = serverHello
if err := hs .checkServerHelloOrHRR (); err != nil {
return err
}
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 ()
if err != nil {
return err
}
encryptedExtensions , ok := msg .(*encryptedExtensionsMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (encryptedExtensions , msg )
}
hs .transcript .Write (encryptedExtensions .marshal ())
if encryptedExtensions .alpnProtocol != "" {
if len (hs .hello .alpnProtocols ) == 0 {
c .sendAlert (alertUnsupportedExtension )
return errors .New ("tls: server advertised unrequested ALPN extension" )
}
if mutualProtocol ([]string {encryptedExtensions .alpnProtocol }, hs .hello .alpnProtocols ) == "" {
c .sendAlert (alertUnsupportedExtension )
return errors .New ("tls: server selected unadvertised ALPN protocol" )
}
c .clientProtocol = encryptedExtensions .alpnProtocol
}
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 ()
if err != nil {
return err
}
certReq , ok := msg .(*certificateRequestMsgTLS13 )
if ok {
hs .transcript .Write (certReq .marshal ())
hs .certReq = certReq
msg , err = c .readHandshake ()
if err != nil {
return err
}
}
certMsg , ok := msg .(*certificateMsgTLS13 )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (certMsg , msg )
}
if len (certMsg .certificate .Certificate ) == 0 {
c .sendAlert (alertDecodeError )
return errors .New ("tls: received empty certificates message" )
}
hs .transcript .Write (certMsg .marshal ())
c .scts = certMsg .certificate .SignedCertificateTimestamps
c .ocspResponse = certMsg .certificate .OCSPStaple
if err := c .verifyServerCertificate (certMsg .certificate .Certificate ); err != nil {
return err
}
msg , err = c .readHandshake ()
if err != nil {
return err
}
certVerify , ok := msg .(*certificateVerifyMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (certVerify , msg )
}
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())
}
hs .transcript .Write (certVerify .marshal ())
return nil
}
func (hs *clientHandshakeStateTLS13 ) readServerFinished () error {
c := hs .c
msg , err := c .readHandshake ()
if err != nil {
return err
}
finished , ok := msg .(*finishedMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (finished , msg )
}
expectedMAC := hs .suite .finishedHash (c .in .trafficSecret , hs .transcript )
if !hmac .Equal (expectedMAC , finished .verifyData ) {
c .sendAlert (alertDecryptError )
return errors .New ("tls: invalid server finished hash" )
}
hs .transcript .Write (finished .marshal ())
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 ,
})
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
hs .transcript .Write (certMsg .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , certMsg .marshal ()); 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
hs .transcript .Write (certVerifyMsg .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , certVerifyMsg .marshal ()); 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 ),
}
hs .transcript .Write (finished .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , finished .marshal ()); 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
}
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 )
}
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
}
The pages are generated with Golds v0.3.6 . (GOOS=darwin 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 .