package tls
import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/internal/fips140/mlkem"
"crypto/internal/fips140/tls13"
"crypto/internal/hpke"
"crypto/rsa"
"crypto/subtle"
"crypto/tls/internal/fips140tls"
"crypto/x509"
"errors"
"fmt"
"hash"
"internal/byteorder"
"internal/godebug"
"io"
"net"
"slices"
"strconv"
"strings"
"time"
)
type clientHandshakeState struct {
c *Conn
ctx context .Context
serverHello *serverHelloMsg
hello *clientHelloMsg
suite *cipherSuite
finishedHash finishedHash
masterSecret []byte
session *SessionState
ticket []byte
}
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
func (c *Conn ) makeClientHello () (*clientHelloMsg , *keySharePrivateKeys , *echClientContext , error ) {
config := c .config
if len (config .ServerName ) == 0 && !config .InsecureSkipVerify {
return nil , nil , nil , errors .New ("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config" )
}
nextProtosLength := 0
for _ , proto := range config .NextProtos {
if l := len (proto ); l == 0 || l > 255 {
return nil , nil , nil , errors .New ("tls: invalid NextProtos value" )
} else {
nextProtosLength += 1 + l
}
}
if nextProtosLength > 0xffff {
return nil , nil , nil , errors .New ("tls: NextProtos values too large" )
}
supportedVersions := config .supportedVersions (roleClient )
if len (supportedVersions ) == 0 {
return nil , nil , nil , errors .New ("tls: no supported versions satisfy MinVersion and MaxVersion" )
}
maxVersion := config .maxSupportedVersion (roleClient )
hello := &clientHelloMsg {
vers : maxVersion ,
compressionMethods : []uint8 {compressionNone },
random : make ([]byte , 32 ),
extendedMasterSecret : true ,
ocspStapling : true ,
scts : true ,
serverName : hostnameInSNI (config .ServerName ),
supportedCurves : config .curvePreferences (maxVersion ),
supportedPoints : []uint8 {pointFormatUncompressed },
secureRenegotiationSupported : true ,
alpnProtocols : config .NextProtos ,
supportedVersions : supportedVersions ,
}
if hello .vers > VersionTLS12 {
hello .vers = VersionTLS12
}
if c .handshakes > 0 {
hello .secureRenegotiation = c .clientFinished [:]
}
preferenceOrder := cipherSuitesPreferenceOrder
if !hasAESGCMHardwareSupport {
preferenceOrder = cipherSuitesPreferenceOrderNoAES
}
configCipherSuites := config .cipherSuites ()
hello .cipherSuites = make ([]uint16 , 0 , len (configCipherSuites ))
for _ , suiteId := range preferenceOrder {
suite := mutualCipherSuite (configCipherSuites , suiteId )
if suite == nil {
continue
}
if maxVersion < VersionTLS12 && suite .flags &suiteTLS12 != 0 {
continue
}
hello .cipherSuites = append (hello .cipherSuites , suiteId )
}
_ , err := io .ReadFull (config .rand (), hello .random )
if err != nil {
return nil , nil , nil , errors .New ("tls: short read from Rand: " + err .Error())
}
if c .quic == nil {
hello .sessionId = make ([]byte , 32 )
if _ , err := io .ReadFull (config .rand (), hello .sessionId ); err != nil {
return nil , nil , nil , errors .New ("tls: short read from Rand: " + err .Error())
}
}
if maxVersion >= VersionTLS12 {
hello .supportedSignatureAlgorithms = supportedSignatureAlgorithms ()
}
if testingOnlyForceClientHelloSignatureAlgorithms != nil {
hello .supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
}
var keyShareKeys *keySharePrivateKeys
if hello .supportedVersions [0 ] == VersionTLS13 {
if len (hello .supportedVersions ) == 1 {
hello .cipherSuites = nil
}
if fips140tls .Required () {
hello .cipherSuites = append (hello .cipherSuites , defaultCipherSuitesTLS13FIPS ...)
} else if hasAESGCMHardwareSupport {
hello .cipherSuites = append (hello .cipherSuites , defaultCipherSuitesTLS13 ...)
} else {
hello .cipherSuites = append (hello .cipherSuites , defaultCipherSuitesTLS13NoAES ...)
}
if len (hello .supportedCurves ) == 0 {
return nil , nil , nil , errors .New ("tls: no supported elliptic curves for ECDHE" )
}
curveID := hello .supportedCurves [0 ]
keyShareKeys = &keySharePrivateKeys {curveID : curveID }
if curveID == X25519MLKEM768 {
keyShareKeys .ecdhe , err = generateECDHEKey (config .rand (), X25519 )
if err != nil {
return nil , nil , nil , err
}
seed := make ([]byte , mlkem .SeedSize )
if _ , err := io .ReadFull (config .rand (), seed ); err != nil {
return nil , nil , nil , err
}
keyShareKeys .mlkem , err = mlkem .NewDecapsulationKey768 (seed )
if err != nil {
return nil , nil , nil , err
}
mlkemEncapsulationKey := keyShareKeys .mlkem .EncapsulationKey ().Bytes ()
x25519EphemeralKey := keyShareKeys .ecdhe .PublicKey ().Bytes ()
hello .keyShares = []keyShare {
{group : X25519MLKEM768 , data : append (mlkemEncapsulationKey , x25519EphemeralKey ...)},
}
if slices .Contains (hello .supportedCurves , X25519 ) {
hello .keyShares = append (hello .keyShares , keyShare {group : X25519 , data : x25519EphemeralKey })
}
} else {
if _ , ok := curveForCurveID (curveID ); !ok {
return nil , nil , nil , errors .New ("tls: CurvePreferences includes unsupported curve" )
}
keyShareKeys .ecdhe , err = generateECDHEKey (config .rand (), curveID )
if err != nil {
return nil , nil , nil , err
}
hello .keyShares = []keyShare {{group : curveID , data : keyShareKeys .ecdhe .PublicKey ().Bytes ()}}
}
}
if c .quic != nil {
p , err := c .quicGetTransportParameters ()
if err != nil {
return nil , nil , nil , err
}
if p == nil {
p = []byte {}
}
hello .quicTransportParameters = p
}
var ech *echClientContext
if c .config .EncryptedClientHelloConfigList != nil {
if c .config .MinVersion != 0 && c .config .MinVersion < VersionTLS13 {
return nil , nil , nil , errors .New ("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated" )
}
if c .config .MaxVersion != 0 && c .config .MaxVersion <= VersionTLS12 {
return nil , nil , nil , errors .New ("tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated" )
}
echConfigs , err := parseECHConfigList (c .config .EncryptedClientHelloConfigList )
if err != nil {
return nil , nil , nil , err
}
echConfig := pickECHConfig (echConfigs )
if echConfig == nil {
return nil , nil , nil , errors .New ("tls: EncryptedClientHelloConfigList contains no valid configs" )
}
ech = &echClientContext {config : echConfig }
hello .encryptedClientHello = []byte {1 }
hello .supportedPoints = nil
hello .ticketSupported = false
hello .secureRenegotiationSupported = false
hello .extendedMasterSecret = false
echPK , err := hpke .ParseHPKEPublicKey (ech .config .KemID , ech .config .PublicKey )
if err != nil {
return nil , nil , nil , err
}
suite , err := pickECHCipherSuite (ech .config .SymmetricCipherSuite )
if err != nil {
return nil , nil , nil , err
}
ech .kdfID , ech .aeadID = suite .KDFID , suite .AEADID
info := append ([]byte ("tls ech\x00" ), ech .config .raw ...)
ech .encapsulatedKey , ech .hpkeContext , err = hpke .SetupSender (ech .config .KemID , suite .KDFID , suite .AEADID , echPK , info )
if err != nil {
return nil , nil , nil , err
}
}
return hello , keyShareKeys , ech , nil
}
type echClientContext struct {
config *echConfig
hpkeContext *hpke .Sender
encapsulatedKey []byte
innerHello *clientHelloMsg
innerTranscript hash .Hash
kdfID uint16
aeadID uint16
echRejected bool
}
func (c *Conn ) clientHandshake (ctx context .Context ) (err error ) {
if c .config == nil {
c .config = defaultConfig ()
}
c .didResume = false
hello , keyShareKeys , ech , err := c .makeClientHello ()
if err != nil {
return err
}
session , earlySecret , binderKey , err := c .loadSession (hello )
if err != nil {
return err
}
if session != nil {
defer func () {
if err != nil {
if cacheKey := c .clientSessionCacheKey (); cacheKey != "" {
c .config .ClientSessionCache .Put (cacheKey , nil )
}
}
}()
}
if ech != nil {
ech .innerHello = hello .clone ()
hello .serverName = string (ech .config .PublicName )
hello .random = make ([]byte , 32 )
_, err = io .ReadFull (c .config .rand (), hello .random )
if err != nil {
return errors .New ("tls: short read from Rand: " + err .Error())
}
if err := computeAndUpdateOuterECHExtension (hello , ech .innerHello , ech , true ); err != nil {
return err
}
}
c .serverName = hello .serverName
if _ , err := c .writeHandshakeRecord (hello , nil ); err != nil {
return err
}
if hello .earlyData {
suite := cipherSuiteTLS13ByID (session .cipherSuite )
transcript := suite .hash .New ()
if err := transcriptMsg (hello , transcript ); err != nil {
return err
}
earlyTrafficSecret := earlySecret .ClientEarlyTrafficSecret (transcript )
c .quicSetWriteSecret (QUICEncryptionLevelEarly , suite .id , earlyTrafficSecret )
}
msg , err := c .readHandshake (nil )
if err != nil {
return err
}
serverHello , ok := msg .(*serverHelloMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (serverHello , msg )
}
if err := c .pickTLSVersion (serverHello ); err != nil {
return err
}
maxVers := c .config .maxSupportedVersion (roleClient )
tls12Downgrade := string (serverHello .random [24 :]) == downgradeCanaryTLS12
tls11Downgrade := string (serverHello .random [24 :]) == downgradeCanaryTLS11
if maxVers == VersionTLS13 && c .vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade ) ||
maxVers == VersionTLS12 && c .vers <= VersionTLS11 && tls11Downgrade {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox" )
}
if c .vers == VersionTLS13 {
hs := &clientHandshakeStateTLS13 {
c : c ,
ctx : ctx ,
serverHello : serverHello ,
hello : hello ,
keyShareKeys : keyShareKeys ,
session : session ,
earlySecret : earlySecret ,
binderKey : binderKey ,
echContext : ech ,
}
return hs .handshake ()
}
hs := &clientHandshakeState {
c : c ,
ctx : ctx ,
serverHello : serverHello ,
hello : hello ,
session : session ,
}
return hs .handshake ()
}
func (c *Conn ) loadSession (hello *clientHelloMsg ) (
session *SessionState , earlySecret *tls13 .EarlySecret , binderKey []byte , err error ) {
if c .config .SessionTicketsDisabled || c .config .ClientSessionCache == nil {
return nil , nil , nil , nil
}
echInner := bytes .Equal (hello .encryptedClientHello , []byte {1 })
hello .ticketSupported = true && !echInner
if hello .supportedVersions [0 ] == VersionTLS13 {
hello .pskModes = []uint8 {pskModeDHE }
}
if c .handshakes != 0 {
return nil , nil , nil , nil
}
cacheKey := c .clientSessionCacheKey ()
if cacheKey == "" {
return nil , nil , nil , nil
}
cs , ok := c .config .ClientSessionCache .Get (cacheKey )
if !ok || cs == nil {
return nil , nil , nil , nil
}
session = cs .session
versOk := false
for _ , v := range hello .supportedVersions {
if v == session .version {
versOk = true
break
}
}
if !versOk {
return nil , nil , nil , nil
}
if c .config .time ().After (session .peerCertificates [0 ].NotAfter ) {
c .config .ClientSessionCache .Put (cacheKey , nil )
return nil , nil , nil , nil
}
if !c .config .InsecureSkipVerify {
if len (session .verifiedChains ) == 0 {
return nil , nil , nil , nil
}
if err := session .peerCertificates [0 ].VerifyHostname (c .config .ServerName ); err != nil {
return nil , nil , nil , nil
}
}
if session .version != VersionTLS13 {
if mutualCipherSuite (hello .cipherSuites , session .cipherSuite ) == nil {
return nil , nil , nil , nil
}
hello .sessionTicket = session .ticket
return
}
if c .config .time ().After (time .Unix (int64 (session .useBy ), 0 )) {
c .config .ClientSessionCache .Put (cacheKey , nil )
return nil , nil , nil , nil
}
cipherSuite := cipherSuiteTLS13ByID (session .cipherSuite )
if cipherSuite == nil {
return nil , nil , nil , nil
}
cipherSuiteOk := false
for _ , offeredID := range hello .cipherSuites {
offeredSuite := cipherSuiteTLS13ByID (offeredID )
if offeredSuite != nil && offeredSuite .hash == cipherSuite .hash {
cipherSuiteOk = true
break
}
}
if !cipherSuiteOk {
return nil , nil , nil , nil
}
if c .quic != nil {
if c .quic .enableSessionEvents {
c .quicResumeSession (session )
}
if session .EarlyData && mutualCipherSuiteTLS13 (hello .cipherSuites , session .cipherSuite ) != nil {
for _ , alpn := range hello .alpnProtocols {
if alpn == session .alpnProtocol {
hello .earlyData = true
break
}
}
}
}
ticketAge := c .config .time ().Sub (time .Unix (int64 (session .createdAt ), 0 ))
identity := pskIdentity {
label : session .ticket ,
obfuscatedTicketAge : uint32 (ticketAge /time .Millisecond ) + session .ageAdd ,
}
hello .pskIdentities = []pskIdentity {identity }
hello .pskBinders = [][]byte {make ([]byte , cipherSuite .hash .Size ())}
earlySecret = tls13 .NewEarlySecret (cipherSuite .hash .New , session .secret )
binderKey = earlySecret .ResumptionBinderKey ()
transcript := cipherSuite .hash .New ()
if err := computeAndUpdatePSK (hello , binderKey , transcript , cipherSuite .finishedHash ); err != nil {
return nil , nil , nil , err
}
return
}
func (c *Conn ) pickTLSVersion (serverHello *serverHelloMsg ) error {
peerVersion := serverHello .vers
if serverHello .supportedVersion != 0 {
peerVersion = serverHello .supportedVersion
}
vers , ok := c .config .mutualVersion (roleClient , []uint16 {peerVersion })
if !ok {
c .sendAlert (alertProtocolVersion )
return fmt .Errorf ("tls: server selected unsupported protocol version %x" , peerVersion )
}
c .vers = vers
c .haveVers = true
c .in .version = vers
c .out .version = vers
return nil
}
func (hs *clientHandshakeState ) handshake () error {
c := hs .c
isResume , err := hs .processServerHello ()
if err != nil {
return err
}
hs .finishedHash = newFinishedHash (c .vers , hs .suite )
if isResume || (len (c .config .Certificates ) == 0 && c .config .GetClientCertificate == nil ) {
hs .finishedHash .discardHandshakeBuffer ()
}
if err := transcriptMsg (hs .hello , &hs .finishedHash ); err != nil {
return err
}
if err := transcriptMsg (hs .serverHello , &hs .finishedHash ); err != nil {
return err
}
c .buffering = true
c .didResume = isResume
if isResume {
if err := hs .establishKeys (); err != nil {
return err
}
if err := hs .readSessionTicket (); err != nil {
return err
}
if err := hs .readFinished (c .serverFinished [:]); err != nil {
return err
}
c .clientFinishedIsFirst = false
if c .config .VerifyConnection != nil {
if err := c .config .VerifyConnection (c .connectionStateLocked ()); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
}
if err := hs .sendFinished (c .clientFinished [:]); err != nil {
return err
}
if _ , err := c .flush (); err != nil {
return err
}
} else {
if err := hs .doFullHandshake (); err != nil {
return err
}
if err := hs .establishKeys (); err != nil {
return err
}
if err := hs .sendFinished (c .clientFinished [:]); err != nil {
return err
}
if _ , err := c .flush (); err != nil {
return err
}
c .clientFinishedIsFirst = true
if err := hs .readSessionTicket (); err != nil {
return err
}
if err := hs .readFinished (c .serverFinished [:]); err != nil {
return err
}
}
if err := hs .saveSessionTicket (); err != nil {
return err
}
c .ekm = ekmFromMasterSecret (c .vers , hs .suite , hs .masterSecret , hs .hello .random , hs .serverHello .random )
c .isHandshakeComplete .Store (true )
return nil
}
func (hs *clientHandshakeState ) pickCipherSuite () error {
if hs .suite = mutualCipherSuite (hs .hello .cipherSuites , hs .serverHello .cipherSuite ); hs .suite == nil {
hs .c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: server chose an unconfigured cipher suite" )
}
if hs .c .config .CipherSuites == nil && !fips140tls .Required () && rsaKexCiphers [hs .suite .id ] {
tlsrsakex .Value ()
tlsrsakex .IncNonDefault ()
}
if hs .c .config .CipherSuites == nil && !fips140tls .Required () && tdesCiphers [hs .suite .id ] {
tls3des .Value ()
tls3des .IncNonDefault ()
}
hs .c .cipherSuite = hs .suite .id
return nil
}
func (hs *clientHandshakeState ) doFullHandshake () error {
c := hs .c
msg , err := c .readHandshake (&hs .finishedHash )
if err != nil {
return err
}
certMsg , ok := msg .(*certificateMsg )
if !ok || len (certMsg .certificates ) == 0 {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (certMsg , msg )
}
msg , err = c .readHandshake (&hs .finishedHash )
if err != nil {
return err
}
cs , ok := msg .(*certificateStatusMsg )
if ok {
if !hs .serverHello .ocspStapling {
c .sendAlert (alertUnexpectedMessage )
return errors .New ("tls: received unexpected CertificateStatus message" )
}
c .ocspResponse = cs .response
msg , err = c .readHandshake (&hs .finishedHash )
if err != nil {
return err
}
}
if c .handshakes == 0 {
if err := c .verifyServerCertificate (certMsg .certificates ); err != nil {
return err
}
} else {
if !bytes .Equal (c .peerCertificates [0 ].Raw , certMsg .certificates [0 ]) {
c .sendAlert (alertBadCertificate )
return errors .New ("tls: server's identity changed during renegotiation" )
}
}
keyAgreement := hs .suite .ka (c .vers )
skx , ok := msg .(*serverKeyExchangeMsg )
if ok {
err = keyAgreement .processServerKeyExchange (c .config , hs .hello , hs .serverHello , c .peerCertificates [0 ], skx )
if err != nil {
c .sendAlert (alertIllegalParameter )
return err
}
if len (skx .key ) >= 3 && skx .key [0 ] == 3 {
c .curveID = CurveID (byteorder .BEUint16 (skx .key [1 :]))
}
msg , err = c .readHandshake (&hs .finishedHash )
if err != nil {
return err
}
}
var chainToSend *Certificate
var certRequested bool
certReq , ok := msg .(*certificateRequestMsg )
if ok {
certRequested = true
cri := certificateRequestInfoFromMsg (hs .ctx , c .vers , certReq )
if chainToSend , err = c .getClientCertificate (cri ); err != nil {
c .sendAlert (alertInternalError )
return err
}
msg , err = c .readHandshake (&hs .finishedHash )
if err != nil {
return err
}
}
shd , ok := msg .(*serverHelloDoneMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (shd , msg )
}
if certRequested {
certMsg = new (certificateMsg )
certMsg .certificates = chainToSend .Certificate
if _ , err := hs .c .writeHandshakeRecord (certMsg , &hs .finishedHash ); err != nil {
return err
}
}
preMasterSecret , ckx , err := keyAgreement .generateClientKeyExchange (c .config , hs .hello , c .peerCertificates [0 ])
if err != nil {
c .sendAlert (alertInternalError )
return err
}
if ckx != nil {
if _ , err := hs .c .writeHandshakeRecord (ckx , &hs .finishedHash ); err != nil {
return err
}
}
if hs .serverHello .extendedMasterSecret {
c .extMasterSecret = true
hs .masterSecret = extMasterFromPreMasterSecret (c .vers , hs .suite , preMasterSecret ,
hs .finishedHash .Sum ())
} else {
hs .masterSecret = masterFromPreMasterSecret (c .vers , hs .suite , preMasterSecret ,
hs .hello .random , hs .serverHello .random )
}
if err := c .config .writeKeyLog (keyLogLabelTLS12 , hs .hello .random , hs .masterSecret ); err != nil {
c .sendAlert (alertInternalError )
return errors .New ("tls: failed to write to key log: " + err .Error())
}
if chainToSend != nil && len (chainToSend .Certificate ) > 0 {
certVerify := &certificateVerifyMsg {}
key , ok := chainToSend .PrivateKey .(crypto .Signer )
if !ok {
c .sendAlert (alertInternalError )
return fmt .Errorf ("tls: client certificate private key of type %T does not implement crypto.Signer" , chainToSend .PrivateKey )
}
var sigType uint8
var sigHash crypto .Hash
if c .vers >= VersionTLS12 {
signatureAlgorithm , err := selectSignatureScheme (c .vers , chainToSend , certReq .supportedSignatureAlgorithms )
if err != nil {
c .sendAlert (alertIllegalParameter )
return err
}
sigType , sigHash , err = typeAndHashFromSignatureScheme (signatureAlgorithm )
if err != nil {
return c .sendAlert (alertInternalError )
}
certVerify .hasSignatureAlgorithm = true
certVerify .signatureAlgorithm = signatureAlgorithm
} else {
sigType , sigHash , err = legacyTypeAndHashFromPublicKey (key .Public ())
if err != nil {
c .sendAlert (alertIllegalParameter )
return err
}
}
signed := hs .finishedHash .hashForClientCertificate (sigType , sigHash )
signOpts := crypto .SignerOpts (sigHash )
if sigType == signatureRSAPSS {
signOpts = &rsa .PSSOptions {SaltLength : rsa .PSSSaltLengthEqualsHash , Hash : sigHash }
}
certVerify .signature , err = key .Sign (c .config .rand (), signed , signOpts )
if err != nil {
c .sendAlert (alertInternalError )
return err
}
if _ , err := hs .c .writeHandshakeRecord (certVerify , &hs .finishedHash ); err != nil {
return err
}
}
hs .finishedHash .discardHandshakeBuffer ()
return nil
}
func (hs *clientHandshakeState ) establishKeys () error {
c := hs .c
clientMAC , serverMAC , clientKey , serverKey , clientIV , serverIV :=
keysFromMasterSecret (c .vers , hs .suite , hs .masterSecret , hs .hello .random , hs .serverHello .random , hs .suite .macLen , hs .suite .keyLen , hs .suite .ivLen )
var clientCipher , serverCipher any
var clientHash , serverHash hash .Hash
if hs .suite .cipher != nil {
clientCipher = hs .suite .cipher (clientKey , clientIV , false )
clientHash = hs .suite .mac (clientMAC )
serverCipher = hs .suite .cipher (serverKey , serverIV , true )
serverHash = hs .suite .mac (serverMAC )
} else {
clientCipher = hs .suite .aead (clientKey , clientIV )
serverCipher = hs .suite .aead (serverKey , serverIV )
}
c .in .prepareCipherSpec (c .vers , serverCipher , serverHash )
c .out .prepareCipherSpec (c .vers , clientCipher , clientHash )
return nil
}
func (hs *clientHandshakeState ) serverResumedSession () bool {
return hs .session != nil && hs .hello .sessionId != nil &&
bytes .Equal (hs .serverHello .sessionId , hs .hello .sessionId )
}
func (hs *clientHandshakeState ) processServerHello () (bool , error ) {
c := hs .c
if err := hs .pickCipherSuite (); err != nil {
return false , err
}
if hs .serverHello .compressionMethod != compressionNone {
c .sendAlert (alertUnexpectedMessage )
return false , errors .New ("tls: server selected unsupported compression format" )
}
if c .handshakes == 0 && hs .serverHello .secureRenegotiationSupported {
c .secureRenegotiation = true
if len (hs .serverHello .secureRenegotiation ) != 0 {
c .sendAlert (alertHandshakeFailure )
return false , errors .New ("tls: initial handshake had non-empty renegotiation extension" )
}
}
if c .handshakes > 0 && c .secureRenegotiation {
var expectedSecureRenegotiation [24 ]byte
copy (expectedSecureRenegotiation [:], c .clientFinished [:])
copy (expectedSecureRenegotiation [12 :], c .serverFinished [:])
if !bytes .Equal (hs .serverHello .secureRenegotiation , expectedSecureRenegotiation [:]) {
c .sendAlert (alertHandshakeFailure )
return false , errors .New ("tls: incorrect renegotiation extension contents" )
}
}
if err := checkALPN (hs .hello .alpnProtocols , hs .serverHello .alpnProtocol , false ); err != nil {
c .sendAlert (alertUnsupportedExtension )
return false , err
}
c .clientProtocol = hs .serverHello .alpnProtocol
c .scts = hs .serverHello .scts
if !hs .serverResumedSession () {
return false , nil
}
if hs .session .version != c .vers {
c .sendAlert (alertHandshakeFailure )
return false , errors .New ("tls: server resumed a session with a different version" )
}
if hs .session .cipherSuite != hs .suite .id {
c .sendAlert (alertHandshakeFailure )
return false , errors .New ("tls: server resumed a session with a different cipher suite" )
}
if hs .session .extMasterSecret != hs .serverHello .extendedMasterSecret {
c .sendAlert (alertHandshakeFailure )
return false , errors .New ("tls: server resumed a session with a different EMS extension" )
}
hs .masterSecret = hs .session .secret
c .extMasterSecret = hs .session .extMasterSecret
c .peerCertificates = hs .session .peerCertificates
c .activeCertHandles = hs .c .activeCertHandles
c .verifiedChains = hs .session .verifiedChains
c .ocspResponse = hs .session .ocspResponse
if len (c .scts ) == 0 && len (hs .session .scts ) != 0 {
c .scts = hs .session .scts
}
return true , nil
}
func checkALPN(clientProtos []string , serverProto string , quic bool ) error {
if serverProto == "" {
if quic && len (clientProtos ) > 0 {
return errors .New ("tls: server did not select an ALPN protocol" )
}
return nil
}
if len (clientProtos ) == 0 {
return errors .New ("tls: server advertised unrequested ALPN extension" )
}
for _ , proto := range clientProtos {
if proto == serverProto {
return nil
}
}
return errors .New ("tls: server selected unadvertised ALPN protocol" )
}
func (hs *clientHandshakeState ) readFinished (out []byte ) error {
c := hs .c
if err := c .readChangeCipherSpec (); err != nil {
return err
}
msg , err := c .readHandshake (nil )
if err != nil {
return err
}
serverFinished , ok := msg .(*finishedMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (serverFinished , msg )
}
verify := hs .finishedHash .serverSum (hs .masterSecret )
if len (verify ) != len (serverFinished .verifyData ) ||
subtle .ConstantTimeCompare (verify , serverFinished .verifyData ) != 1 {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: server's Finished message was incorrect" )
}
if err := transcriptMsg (serverFinished , &hs .finishedHash ); err != nil {
return err
}
copy (out , verify )
return nil
}
func (hs *clientHandshakeState ) readSessionTicket () error {
if !hs .serverHello .ticketSupported {
return nil
}
c := hs .c
if !hs .hello .ticketSupported {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: server sent unrequested session ticket" )
}
msg , err := c .readHandshake (&hs .finishedHash )
if err != nil {
return err
}
sessionTicketMsg , ok := msg .(*newSessionTicketMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (sessionTicketMsg , msg )
}
hs .ticket = sessionTicketMsg .ticket
return nil
}
func (hs *clientHandshakeState ) saveSessionTicket () error {
if hs .ticket == nil {
return nil
}
c := hs .c
cacheKey := c .clientSessionCacheKey ()
if cacheKey == "" {
return nil
}
session := c .sessionState ()
session .secret = hs .masterSecret
session .ticket = hs .ticket
cs := &ClientSessionState {session : session }
c .config .ClientSessionCache .Put (cacheKey , cs )
return nil
}
func (hs *clientHandshakeState ) sendFinished (out []byte ) error {
c := hs .c
if err := c .writeChangeCipherRecord (); err != nil {
return err
}
finished := new (finishedMsg )
finished .verifyData = hs .finishedHash .clientSum (hs .masterSecret )
if _ , err := hs .c .writeHandshakeRecord (finished , &hs .finishedHash ); err != nil {
return err
}
copy (out , finished .verifyData )
return nil
}
const defaultMaxRSAKeySize = 8192
var tlsmaxrsasize = godebug .New ("tlsmaxrsasize" )
func checkKeySize(n int ) (max int , ok bool ) {
if v := tlsmaxrsasize .Value (); v != "" {
if max , err := strconv .Atoi (v ); err == nil {
if (n <= max ) != (n <= defaultMaxRSAKeySize ) {
tlsmaxrsasize .IncNonDefault ()
}
return max , n <= max
}
}
return defaultMaxRSAKeySize , n <= defaultMaxRSAKeySize
}
func (c *Conn ) verifyServerCertificate (certificates [][]byte ) error {
activeHandles := make ([]*activeCert , len (certificates ))
certs := make ([]*x509 .Certificate , len (certificates ))
for i , asn1Data := range certificates {
cert , err := globalCertCache .newCert (asn1Data )
if err != nil {
c .sendAlert (alertBadCertificate )
return errors .New ("tls: failed to parse certificate from server: " + err .Error())
}
if cert .cert .PublicKeyAlgorithm == x509 .RSA {
n := cert .cert .PublicKey .(*rsa .PublicKey ).N .BitLen ()
if max , ok := checkKeySize (n ); !ok {
c .sendAlert (alertBadCertificate )
return fmt .Errorf ("tls: server sent certificate containing RSA key larger than %d bits" , max )
}
}
activeHandles [i ] = cert
certs [i ] = cert .cert
}
echRejected := c .config .EncryptedClientHelloConfigList != nil && !c .echAccepted
if echRejected {
if c .config .EncryptedClientHelloRejectionVerify != nil {
if err := c .config .EncryptedClientHelloRejectionVerify (c .connectionStateLocked ()); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
} else {
opts := x509 .VerifyOptions {
Roots : c .config .RootCAs ,
CurrentTime : c .config .time (),
DNSName : c .serverName ,
Intermediates : x509 .NewCertPool (),
}
for _ , cert := range certs [1 :] {
opts .Intermediates .AddCert (cert )
}
chains , err := certs [0 ].Verify (opts )
if err != nil {
c .sendAlert (alertBadCertificate )
return &CertificateVerificationError {UnverifiedCertificates : certs , Err : err }
}
c .verifiedChains , err = fipsAllowedChains (chains )
if err != nil {
c .sendAlert (alertBadCertificate )
return &CertificateVerificationError {UnverifiedCertificates : certs , Err : err }
}
}
} else if !c .config .InsecureSkipVerify {
opts := x509 .VerifyOptions {
Roots : c .config .RootCAs ,
CurrentTime : c .config .time (),
DNSName : c .config .ServerName ,
Intermediates : x509 .NewCertPool (),
}
for _ , cert := range certs [1 :] {
opts .Intermediates .AddCert (cert )
}
chains , err := certs [0 ].Verify (opts )
if err != nil {
c .sendAlert (alertBadCertificate )
return &CertificateVerificationError {UnverifiedCertificates : certs , Err : err }
}
c .verifiedChains , err = fipsAllowedChains (chains )
if err != nil {
c .sendAlert (alertBadCertificate )
return &CertificateVerificationError {UnverifiedCertificates : certs , Err : err }
}
}
switch certs [0 ].PublicKey .(type ) {
case *rsa .PublicKey , *ecdsa .PublicKey , ed25519 .PublicKey :
break
default :
c .sendAlert (alertUnsupportedCertificate )
return fmt .Errorf ("tls: server's certificate contains an unsupported type of public key: %T" , certs [0 ].PublicKey )
}
c .activeCertHandles = activeHandles
c .peerCertificates = certs
if c .config .VerifyPeerCertificate != nil && !echRejected {
if err := c .config .VerifyPeerCertificate (certificates , c .verifiedChains ); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
}
if c .config .VerifyConnection != nil && !echRejected {
if err := c .config .VerifyConnection (c .connectionStateLocked ()); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
}
return nil
}
func certificateRequestInfoFromMsg(ctx context .Context , vers uint16 , certReq *certificateRequestMsg ) *CertificateRequestInfo {
cri := &CertificateRequestInfo {
AcceptableCAs : certReq .certificateAuthorities ,
Version : vers ,
ctx : ctx ,
}
var rsaAvail , ecAvail bool
for _ , certType := range certReq .certificateTypes {
switch certType {
case certTypeRSASign :
rsaAvail = true
case certTypeECDSASign :
ecAvail = true
}
}
if !certReq .hasSignatureAlgorithm {
switch {
case rsaAvail && ecAvail :
cri .SignatureSchemes = []SignatureScheme {
ECDSAWithP256AndSHA256 , ECDSAWithP384AndSHA384 , ECDSAWithP521AndSHA512 ,
PKCS1WithSHA256 , PKCS1WithSHA384 , PKCS1WithSHA512 , PKCS1WithSHA1 ,
}
case rsaAvail :
cri .SignatureSchemes = []SignatureScheme {
PKCS1WithSHA256 , PKCS1WithSHA384 , PKCS1WithSHA512 , PKCS1WithSHA1 ,
}
case ecAvail :
cri .SignatureSchemes = []SignatureScheme {
ECDSAWithP256AndSHA256 , ECDSAWithP384AndSHA384 , ECDSAWithP521AndSHA512 ,
}
}
return cri
}
cri .SignatureSchemes = make ([]SignatureScheme , 0 , len (certReq .supportedSignatureAlgorithms ))
for _ , sigScheme := range certReq .supportedSignatureAlgorithms {
sigType , _ , err := typeAndHashFromSignatureScheme (sigScheme )
if err != nil {
continue
}
switch sigType {
case signatureECDSA , signatureEd25519 :
if ecAvail {
cri .SignatureSchemes = append (cri .SignatureSchemes , sigScheme )
}
case signatureRSAPSS , signaturePKCS1v15 :
if rsaAvail {
cri .SignatureSchemes = append (cri .SignatureSchemes , sigScheme )
}
}
}
return cri
}
func (c *Conn ) getClientCertificate (cri *CertificateRequestInfo ) (*Certificate , error ) {
if c .config .GetClientCertificate != nil {
return c .config .GetClientCertificate (cri )
}
for _ , chain := range c .config .Certificates {
if err := cri .SupportsCertificate (&chain ); err != nil {
continue
}
return &chain , nil
}
return new (Certificate ), nil
}
func (c *Conn ) clientSessionCacheKey () string {
if len (c .config .ServerName ) > 0 {
return c .config .ServerName
}
if c .conn != nil {
return c .conn .RemoteAddr ().String ()
}
return ""
}
func hostnameInSNI(name string ) string {
host := name
if len (host ) > 0 && host [0 ] == '[' && host [len (host )-1 ] == ']' {
host = host [1 : len (host )-1 ]
}
if i := strings .LastIndex (host , "%" ); i > 0 {
host = host [:i ]
}
if net .ParseIP (host ) != nil {
return ""
}
for len (name ) > 0 && name [len (name )-1 ] == '.' {
name = name [:len (name )-1 ]
}
return name
}
func computeAndUpdatePSK(m *clientHelloMsg , binderKey []byte , transcript hash .Hash , finishedHash func ([]byte , hash .Hash ) []byte ) error {
helloBytes , err := m .marshalWithoutBinders ()
if err != nil {
return err
}
transcript .Write (helloBytes )
pskBinders := [][]byte {finishedHash (binderKey , transcript )}
return m .updateBinders (pskBinders )
}
The pages are generated with Golds v0.7.3 . (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 @zigo_101 (reachable from the left QR code) to get the latest news of Golds .