package tls
import (
)
type clientHandshakeState struct {
c *Conn
ctx context.Context
serverHello *serverHelloMsg
hello *clientHelloMsg
suite *cipherSuite
finishedHash finishedHash
masterSecret []byte
session *ClientSessionState
}
func ( *Conn) () (*clientHelloMsg, ecdheParameters, error) {
:= .config
if len(.ServerName) == 0 && !.InsecureSkipVerify {
return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
}
:= 0
for , := range .NextProtos {
if := len(); == 0 || > 255 {
return nil, nil, errors.New("tls: invalid NextProtos value")
} else {
+= 1 +
}
}
if > 0xffff {
return nil, nil, errors.New("tls: NextProtos values too large")
}
:= .supportedVersions(roleClient)
if len() == 0 {
return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
}
:= .maxSupportedVersion(roleClient)
if > VersionTLS12 {
= VersionTLS12
}
:= &clientHelloMsg{
vers: ,
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
sessionId: make([]byte, 32),
ocspStapling: true,
scts: true,
serverName: hostnameInSNI(.ServerName),
supportedCurves: .curvePreferences(),
supportedPoints: []uint8{pointFormatUncompressed},
secureRenegotiationSupported: true,
alpnProtocols: .NextProtos,
supportedVersions: ,
}
if .handshakes > 0 {
.secureRenegotiation = .clientFinished[:]
}
:= cipherSuitesPreferenceOrder
if !hasAESGCMHardwareSupport {
= cipherSuitesPreferenceOrderNoAES
}
:= .cipherSuites()
.cipherSuites = make([]uint16, 0, len())
for , := range {
:= mutualCipherSuite(, )
if == nil {
continue
}
if .vers < VersionTLS12 && .flags&suiteTLS12 != 0 {
continue
}
.cipherSuites = append(.cipherSuites, )
}
, := io.ReadFull(.rand(), .random)
if != nil {
return nil, nil, errors.New("tls: short read from Rand: " + .Error())
}
if , := io.ReadFull(.rand(), .sessionId); != nil {
return nil, nil, errors.New("tls: short read from Rand: " + .Error())
}
if .vers >= VersionTLS12 {
.supportedSignatureAlgorithms = supportedSignatureAlgorithms
}
var ecdheParameters
if .supportedVersions[0] == VersionTLS13 {
if hasAESGCMHardwareSupport {
.cipherSuites = append(.cipherSuites, defaultCipherSuitesTLS13...)
} else {
.cipherSuites = append(.cipherSuites, defaultCipherSuitesTLS13NoAES...)
}
:= .curvePreferences()[0]
if , := curveForCurveID(); != X25519 && ! {
return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
}
, = generateECDHEParameters(.rand(), )
if != nil {
return nil, nil,
}
.keyShares = []keyShare{{group: , data: .PublicKey()}}
}
return , , nil
}
func ( *Conn) ( context.Context) ( error) {
if .config == nil {
.config = defaultConfig()
}
.didResume = false
, , := .makeClientHello()
if != nil {
return
}
.serverName = .serverName
, , , := .loadSession()
if != "" && != nil {
defer func() {
if != nil {
.config.ClientSessionCache.Put(, nil)
}
}()
}
if , := .writeRecord(recordTypeHandshake, .marshal()); != nil {
return
}
, := .readHandshake()
if != nil {
return
}
, := .(*serverHelloMsg)
if ! {
.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(, )
}
if := .pickTLSVersion(); != nil {
return
}
:= .config.maxSupportedVersion(roleClient)
:= string(.random[24:]) == downgradeCanaryTLS12
:= string(.random[24:]) == downgradeCanaryTLS11
if == VersionTLS13 && .vers <= VersionTLS12 && ( || ) ||
== VersionTLS12 && .vers <= VersionTLS11 && {
.sendAlert(alertIllegalParameter)
return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox")
}
if .vers == VersionTLS13 {
:= &clientHandshakeStateTLS13{
c: ,
ctx: ,
serverHello: ,
hello: ,
ecdheParams: ,
session: ,
earlySecret: ,
binderKey: ,
}
return .handshake()
}
:= &clientHandshakeState{
c: ,
ctx: ,
serverHello: ,
hello: ,
session: ,
}
if := .handshake(); != nil {
return
}
if != "" && .session != nil && != .session {
.config.ClientSessionCache.Put(, .session)
}
return nil
}
func ( *Conn) ( *clientHelloMsg) ( string,
*ClientSessionState, , []byte) {
if .config.SessionTicketsDisabled || .config.ClientSessionCache == nil {
return "", nil, nil, nil
}
.ticketSupported = true
if .supportedVersions[0] == VersionTLS13 {
.pskModes = []uint8{pskModeDHE}
}
if .handshakes != 0 {
return "", nil, nil, nil
}
= clientSessionCacheKey(.conn.RemoteAddr(), .config)
, := .config.ClientSessionCache.Get()
if ! || == nil {
return , nil, nil, nil
}
:= false
for , := range .supportedVersions {
if == .vers {
= true
break
}
}
if ! {
return , nil, nil, nil
}
if !.config.InsecureSkipVerify {
if len(.verifiedChains) == 0 {
return , nil, nil, nil
}
:= .serverCertificates[0]
if .config.time().After(.NotAfter) {
.config.ClientSessionCache.Put(, nil)
return , nil, nil, nil
}
if := .VerifyHostname(.config.ServerName); != nil {
return , nil, nil, nil
}
}
if .vers != VersionTLS13 {
if mutualCipherSuite(.cipherSuites, .cipherSuite) == nil {
return , nil, nil, nil
}
.sessionTicket = .sessionTicket
return
}
if .config.time().After(.useBy) {
.config.ClientSessionCache.Put(, nil)
return , nil, nil, nil
}
:= cipherSuiteTLS13ByID(.cipherSuite)
if == nil {
return , nil, nil, nil
}
:= false
for , := range .cipherSuites {
:= cipherSuiteTLS13ByID()
if != nil && .hash == .hash {
= true
break
}
}
if ! {
return , nil, nil, nil
}
:= uint32(.config.time().Sub(.receivedAt) / time.Millisecond)
:= pskIdentity{
label: .sessionTicket,
obfuscatedTicketAge: + .ageAdd,
}
.pskIdentities = []pskIdentity{}
.pskBinders = [][]byte{make([]byte, .hash.Size())}
:= .expandLabel(.masterSecret, "resumption",
.nonce, .hash.Size())
= .extract(, nil)
= .deriveSecret(, resumptionBinderLabel, nil)
:= .hash.New()
.Write(.marshalWithoutBinders())
:= [][]byte{.finishedHash(, )}
.updateBinders()
return
}
func ( *Conn) ( *serverHelloMsg) error {
:= .vers
if .supportedVersion != 0 {
= .supportedVersion
}
, := .config.mutualVersion(roleClient, []uint16{})
if ! {
.sendAlert(alertProtocolVersion)
return fmt.Errorf("tls: server selected unsupported protocol version %x", )
}
.vers =
.haveVers = true
.in.version =
.out.version =
return nil
}
func ( *clientHandshakeState) () error {
:= .c
, := .processServerHello()
if != nil {
return
}
.finishedHash = newFinishedHash(.vers, .suite)
if || (len(.config.Certificates) == 0 && .config.GetClientCertificate == nil) {
.finishedHash.discardHandshakeBuffer()
}
.finishedHash.Write(.hello.marshal())
.finishedHash.Write(.serverHello.marshal())
.buffering = true
.didResume =
if {
if := .establishKeys(); != nil {
return
}
if := .readSessionTicket(); != nil {
return
}
if := .readFinished(.serverFinished[:]); != nil {
return
}
.clientFinishedIsFirst = false
if .config.VerifyConnection != nil {
if := .config.VerifyConnection(.connectionStateLocked()); != nil {
.sendAlert(alertBadCertificate)
return
}
}
if := .sendFinished(.clientFinished[:]); != nil {
return
}
if , := .flush(); != nil {
return
}
} else {
if := .doFullHandshake(); != nil {
return
}
if := .establishKeys(); != nil {
return
}
if := .sendFinished(.clientFinished[:]); != nil {
return
}
if , := .flush(); != nil {
return
}
.clientFinishedIsFirst = true
if := .readSessionTicket(); != nil {
return
}
if := .readFinished(.serverFinished[:]); != nil {
return
}
}
.ekm = ekmFromMasterSecret(.vers, .suite, .masterSecret, .hello.random, .serverHello.random)
atomic.StoreUint32(&.handshakeStatus, 1)
return nil
}
func ( *clientHandshakeState) () error {
if .suite = mutualCipherSuite(.hello.cipherSuites, .serverHello.cipherSuite); .suite == nil {
.c.sendAlert(alertHandshakeFailure)
return errors.New("tls: server chose an unconfigured cipher suite")
}
.c.cipherSuite = .suite.id
return nil
}
func ( *clientHandshakeState) () error {
:= .c
, := .readHandshake()
if != nil {
return
}
, := .(*certificateMsg)
if ! || len(.certificates) == 0 {
.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(, )
}
.finishedHash.Write(.marshal())
, = .readHandshake()
if != nil {
return
}
, := .(*certificateStatusMsg)
if {
if !.serverHello.ocspStapling {
.sendAlert(alertUnexpectedMessage)
return errors.New("tls: received unexpected CertificateStatus message")
}
.finishedHash.Write(.marshal())
.ocspResponse = .response
, = .readHandshake()
if != nil {
return
}
}
if .handshakes == 0 {
if := .verifyServerCertificate(.certificates); != nil {
return
}
} else {
if !bytes.Equal(.peerCertificates[0].Raw, .certificates[0]) {
.sendAlert(alertBadCertificate)
return errors.New("tls: server's identity changed during renegotiation")
}
}
:= .suite.ka(.vers)
, := .(*serverKeyExchangeMsg)
if {
.finishedHash.Write(.marshal())
= .processServerKeyExchange(.config, .hello, .serverHello, .peerCertificates[0], )
if != nil {
.sendAlert(alertUnexpectedMessage)
return
}
, = .readHandshake()
if != nil {
return
}
}
var *Certificate
var bool
, := .(*certificateRequestMsg)
if {
= true
.finishedHash.Write(.marshal())
:= certificateRequestInfoFromMsg(.ctx, .vers, )
if , = .getClientCertificate(); != nil {
.sendAlert(alertInternalError)
return
}
, = .readHandshake()
if != nil {
return
}
}
, := .(*serverHelloDoneMsg)
if ! {
.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(, )
}
.finishedHash.Write(.marshal())
if {
= new(certificateMsg)
.certificates = .Certificate
.finishedHash.Write(.marshal())
if , := .writeRecord(recordTypeHandshake, .marshal()); != nil {
return
}
}
, , := .generateClientKeyExchange(.config, .hello, .peerCertificates[0])
if != nil {
.sendAlert(alertInternalError)
return
}
if != nil {
.finishedHash.Write(.marshal())
if , := .writeRecord(recordTypeHandshake, .marshal()); != nil {
return
}
}
if != nil && len(.Certificate) > 0 {
:= &certificateVerifyMsg{}
, := .PrivateKey.(crypto.Signer)
if ! {
.sendAlert(alertInternalError)
return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", .PrivateKey)
}
var uint8
var crypto.Hash
if .vers >= VersionTLS12 {
, := selectSignatureScheme(.vers, , .supportedSignatureAlgorithms)
if != nil {
.sendAlert(alertIllegalParameter)
return
}
, , = typeAndHashFromSignatureScheme()
if != nil {
return .sendAlert(alertInternalError)
}
.hasSignatureAlgorithm = true
.signatureAlgorithm =
} else {
, , = legacyTypeAndHashFromPublicKey(.Public())
if != nil {
.sendAlert(alertIllegalParameter)
return
}
}
:= .finishedHash.hashForClientCertificate(, , .masterSecret)
:= crypto.SignerOpts()
if == signatureRSAPSS {
= &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: }
}
.signature, = .Sign(.config.rand(), , )
if != nil {
.sendAlert(alertInternalError)
return
}
.finishedHash.Write(.marshal())
if , := .writeRecord(recordTypeHandshake, .marshal()); != nil {
return
}
}
.masterSecret = masterFromPreMasterSecret(.vers, .suite, , .hello.random, .serverHello.random)
if := .config.writeKeyLog(keyLogLabelTLS12, .hello.random, .masterSecret); != nil {
.sendAlert(alertInternalError)
return errors.New("tls: failed to write to key log: " + .Error())
}
.finishedHash.discardHandshakeBuffer()
return nil
}
func ( *clientHandshakeState) () error {
:= .c
, , , , , :=
keysFromMasterSecret(.vers, .suite, .masterSecret, .hello.random, .serverHello.random, .suite.macLen, .suite.keyLen, .suite.ivLen)
var , any
var , hash.Hash
if .suite.cipher != nil {
= .suite.cipher(, , false )
= .suite.mac()
= .suite.cipher(, , true )
= .suite.mac()
} else {
= .suite.aead(, )
= .suite.aead(, )
}
.in.prepareCipherSpec(.vers, , )
.out.prepareCipherSpec(.vers, , )
return nil
}
func ( *clientHandshakeState) () bool {
return .session != nil && .hello.sessionId != nil &&
bytes.Equal(.serverHello.sessionId, .hello.sessionId)
}
func ( *clientHandshakeState) () (bool, error) {
:= .c
if := .pickCipherSuite(); != nil {
return false,
}
if .serverHello.compressionMethod != compressionNone {
.sendAlert(alertUnexpectedMessage)
return false, errors.New("tls: server selected unsupported compression format")
}
if .handshakes == 0 && .serverHello.secureRenegotiationSupported {
.secureRenegotiation = true
if len(.serverHello.secureRenegotiation) != 0 {
.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
}
}
if .handshakes > 0 && .secureRenegotiation {
var [24]byte
copy([:], .clientFinished[:])
copy([12:], .serverFinished[:])
if !bytes.Equal(.serverHello.secureRenegotiation, [:]) {
.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: incorrect renegotiation extension contents")
}
}
if := checkALPN(.hello.alpnProtocols, .serverHello.alpnProtocol); != nil {
.sendAlert(alertUnsupportedExtension)
return false,
}
.clientProtocol = .serverHello.alpnProtocol
.scts = .serverHello.scts
if !.serverResumedSession() {
return false, nil
}
if .session.vers != .vers {
.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: server resumed a session with a different version")
}
if .session.cipherSuite != .suite.id {
.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: server resumed a session with a different cipher suite")
}
.masterSecret = .session.masterSecret
.peerCertificates = .session.serverCertificates
.verifiedChains = .session.verifiedChains
.ocspResponse = .session.ocspResponse
if len(.scts) == 0 && len(.session.scts) != 0 {
.scts = .session.scts
}
return true, nil
}
func ( []string, string) error {
if == "" {
return nil
}
if len() == 0 {
return errors.New("tls: server advertised unrequested ALPN extension")
}
for , := range {
if == {
return nil
}
}
return errors.New("tls: server selected unadvertised ALPN protocol")
}
func ( *clientHandshakeState) ( []byte) error {
:= .c
if := .readChangeCipherSpec(); != nil {
return
}
, := .readHandshake()
if != nil {
return
}
, := .(*finishedMsg)
if ! {
.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(, )
}
:= .finishedHash.serverSum(.masterSecret)
if len() != len(.verifyData) ||
subtle.ConstantTimeCompare(, .verifyData) != 1 {
.sendAlert(alertHandshakeFailure)
return errors.New("tls: server's Finished message was incorrect")
}
.finishedHash.Write(.marshal())
copy(, )
return nil
}
func ( *clientHandshakeState) () error {
if !.serverHello.ticketSupported {
return nil
}
:= .c
, := .readHandshake()
if != nil {
return
}
, := .(*newSessionTicketMsg)
if ! {
.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(, )
}
.finishedHash.Write(.marshal())
.session = &ClientSessionState{
sessionTicket: .ticket,
vers: .vers,
cipherSuite: .suite.id,
masterSecret: .masterSecret,
serverCertificates: .peerCertificates,
verifiedChains: .verifiedChains,
receivedAt: .config.time(),
ocspResponse: .ocspResponse,
scts: .scts,
}
return nil
}
func ( *clientHandshakeState) ( []byte) error {
:= .c
if , := .writeRecord(recordTypeChangeCipherSpec, []byte{1}); != nil {
return
}
:= new(finishedMsg)
.verifyData = .finishedHash.clientSum(.masterSecret)
.finishedHash.Write(.marshal())
if , := .writeRecord(recordTypeHandshake, .marshal()); != nil {
return
}
copy(, .verifyData)
return nil
}
func ( *Conn) ( [][]byte) error {
:= make([]*x509.Certificate, len())
for , := range {
, := x509.ParseCertificate()
if != nil {
.sendAlert(alertBadCertificate)
return errors.New("tls: failed to parse certificate from server: " + .Error())
}
[] =
}
if !.config.InsecureSkipVerify {
:= x509.VerifyOptions{
Roots: .config.RootCAs,
CurrentTime: .config.time(),
DNSName: .config.ServerName,
Intermediates: x509.NewCertPool(),
}
for , := range [1:] {
.Intermediates.AddCert()
}
var error
.verifiedChains, = [0].Verify()
if != nil {
.sendAlert(alertBadCertificate)
return
}
}
switch [0].PublicKey.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
break
default:
.sendAlert(alertUnsupportedCertificate)
return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", [0].PublicKey)
}
.peerCertificates =
if .config.VerifyPeerCertificate != nil {
if := .config.VerifyPeerCertificate(, .verifiedChains); != nil {
.sendAlert(alertBadCertificate)
return
}
}
if .config.VerifyConnection != nil {
if := .config.VerifyConnection(.connectionStateLocked()); != nil {
.sendAlert(alertBadCertificate)
return
}
}
return nil
}
func ( context.Context, uint16, *certificateRequestMsg) *CertificateRequestInfo {
:= &CertificateRequestInfo{
AcceptableCAs: .certificateAuthorities,
Version: ,
ctx: ,
}
var , bool
for , := range .certificateTypes {
switch {
case certTypeRSASign:
= true
case certTypeECDSASign:
= true
}
}
if !.hasSignatureAlgorithm {
switch {
case && :
.SignatureSchemes = []SignatureScheme{
ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
}
case :
.SignatureSchemes = []SignatureScheme{
PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
}
case :
.SignatureSchemes = []SignatureScheme{
ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
}
}
return
}
.SignatureSchemes = make([]SignatureScheme, 0, len(.supportedSignatureAlgorithms))
for , := range .supportedSignatureAlgorithms {
, , := typeAndHashFromSignatureScheme()
if != nil {
continue
}
switch {
case signatureECDSA, signatureEd25519:
if {
.SignatureSchemes = append(.SignatureSchemes, )
}
case signatureRSAPSS, signaturePKCS1v15:
if {
.SignatureSchemes = append(.SignatureSchemes, )
}
}
}
return
}
func ( *Conn) ( *CertificateRequestInfo) (*Certificate, error) {
if .config.GetClientCertificate != nil {
return .config.GetClientCertificate()
}
for , := range .config.Certificates {
if := .SupportsCertificate(&); != nil {
continue
}
return &, nil
}
return new(Certificate), nil
}
func ( net.Addr, *Config) string {
if len(.ServerName) > 0 {
return .ServerName
}
return .String()
}
func ( string) string {
:=
if len() > 0 && [0] == '[' && [len()-1] == ']' {
= [1 : len()-1]
}
if := strings.LastIndex(, "%"); > 0 {
= [:]
}
if net.ParseIP() != nil {
return ""
}
for len() > 0 && [len()-1] == '.' {
= [:len()-1]
}
return
}