package tls
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
"errors"
"fmt"
"hash"
"io"
"time"
)
type serverHandshakeState struct {
c *Conn
ctx context .Context
clientHello *clientHelloMsg
hello *serverHelloMsg
suite *cipherSuite
ecdheOk bool
ecSignOk bool
rsaDecryptOk bool
rsaSignOk bool
sessionState *SessionState
finishedHash finishedHash
masterSecret []byte
cert *Certificate
}
func (c *Conn ) serverHandshake (ctx context .Context ) error {
clientHello , err := c .readClientHello (ctx )
if err != nil {
return err
}
if c .vers == VersionTLS13 {
hs := serverHandshakeStateTLS13 {
c : c ,
ctx : ctx ,
clientHello : clientHello ,
}
return hs .handshake ()
}
hs := serverHandshakeState {
c : c ,
ctx : ctx ,
clientHello : clientHello ,
}
return hs .handshake ()
}
func (hs *serverHandshakeState ) handshake () error {
c := hs .c
if err := hs .processClientHello (); err != nil {
return err
}
c .buffering = true
if err := hs .checkForResumption (); err != nil {
return err
}
if hs .sessionState != nil {
if err := hs .doResumeHandshake (); err != nil {
return err
}
if err := hs .establishKeys (); err != nil {
return err
}
if err := hs .sendSessionTicket (); err != nil {
return err
}
if err := hs .sendFinished (c .serverFinished [:]); err != nil {
return err
}
if _ , err := c .flush (); err != nil {
return err
}
c .clientFinishedIsFirst = false
if err := hs .readFinished (nil ); err != nil {
return err
}
} else {
if err := hs .pickCipherSuite (); err != nil {
return err
}
if err := hs .doFullHandshake (); err != nil {
return err
}
if err := hs .establishKeys (); err != nil {
return err
}
if err := hs .readFinished (c .clientFinished [:]); err != nil {
return err
}
c .clientFinishedIsFirst = true
c .buffering = true
if err := hs .sendSessionTicket (); err != nil {
return err
}
if err := hs .sendFinished (nil ); err != nil {
return err
}
if _ , err := c .flush (); err != nil {
return err
}
}
c .ekm = ekmFromMasterSecret (c .vers , hs .suite , hs .masterSecret , hs .clientHello .random , hs .hello .random )
c .isHandshakeComplete .Store (true )
return nil
}
func (c *Conn ) readClientHello (ctx context .Context ) (*clientHelloMsg , error ) {
msg , err := c .readHandshake (nil )
if err != nil {
return nil , err
}
clientHello , ok := msg .(*clientHelloMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return nil , unexpectedMessageError (clientHello , msg )
}
var configForClient *Config
originalConfig := c .config
if c .config .GetConfigForClient != nil {
chi := clientHelloInfo (ctx , c , clientHello )
if configForClient , err = c .config .GetConfigForClient (chi ); err != nil {
c .sendAlert (alertInternalError )
return nil , err
} else if configForClient != nil {
c .config = configForClient
}
}
c .ticketKeys = originalConfig .ticketKeys (configForClient )
clientVersions := clientHello .supportedVersions
if len (clientHello .supportedVersions ) == 0 {
clientVersions = supportedVersionsFromMax (clientHello .vers )
}
c .vers , ok = c .config .mutualVersion (roleServer , clientVersions )
if !ok {
c .sendAlert (alertProtocolVersion )
return nil , fmt .Errorf ("tls: client offered only unsupported versions: %x" , clientVersions )
}
c .haveVers = true
c .in .version = c .vers
c .out .version = c .vers
return clientHello , nil
}
func (hs *serverHandshakeState ) processClientHello () error {
c := hs .c
hs .hello = new (serverHelloMsg )
hs .hello .vers = c .vers
foundCompression := false
for _ , compression := range hs .clientHello .compressionMethods {
if compression == compressionNone {
foundCompression = true
break
}
}
if !foundCompression {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: client does not support uncompressed connections" )
}
hs .hello .random = make ([]byte , 32 )
serverRandom := hs .hello .random
maxVers := c .config .maxSupportedVersion (roleServer )
if maxVers >= VersionTLS12 && c .vers < maxVers || testingOnlyForceDowngradeCanary {
if c .vers == VersionTLS12 {
copy (serverRandom [24 :], downgradeCanaryTLS12 )
} else {
copy (serverRandom [24 :], downgradeCanaryTLS11 )
}
serverRandom = serverRandom [:24 ]
}
_ , err := io .ReadFull (c .config .rand (), serverRandom )
if err != nil {
c .sendAlert (alertInternalError )
return err
}
if len (hs .clientHello .secureRenegotiation ) != 0 {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: initial handshake had non-empty renegotiation extension" )
}
hs .hello .extendedMasterSecret = hs .clientHello .extendedMasterSecret
hs .hello .secureRenegotiationSupported = hs .clientHello .secureRenegotiationSupported
hs .hello .compressionMethod = compressionNone
if len (hs .clientHello .serverName ) > 0 {
c .serverName = hs .clientHello .serverName
}
selectedProto , err := negotiateALPN (c .config .NextProtos , hs .clientHello .alpnProtocols , false )
if err != nil {
c .sendAlert (alertNoApplicationProtocol )
return err
}
hs .hello .alpnProtocol = selectedProto
c .clientProtocol = selectedProto
hs .cert , err = c .config .getCertificate (clientHelloInfo (hs .ctx , c , hs .clientHello ))
if err != nil {
if err == errNoCertificates {
c .sendAlert (alertUnrecognizedName )
} else {
c .sendAlert (alertInternalError )
}
return err
}
if hs .clientHello .scts {
hs .hello .scts = hs .cert .SignedCertificateTimestamps
}
hs .ecdheOk = supportsECDHE (c .config , hs .clientHello .supportedCurves , hs .clientHello .supportedPoints )
if hs .ecdheOk && len (hs .clientHello .supportedPoints ) > 0 {
hs .hello .supportedPoints = []uint8 {pointFormatUncompressed }
}
if priv , ok := hs .cert .PrivateKey .(crypto .Signer ); ok {
switch priv .Public ().(type ) {
case *ecdsa .PublicKey :
hs .ecSignOk = true
case ed25519 .PublicKey :
hs .ecSignOk = true
case *rsa .PublicKey :
hs .rsaSignOk = true
default :
c .sendAlert (alertInternalError )
return fmt .Errorf ("tls: unsupported signing key type (%T)" , priv .Public ())
}
}
if priv , ok := hs .cert .PrivateKey .(crypto .Decrypter ); ok {
switch priv .Public ().(type ) {
case *rsa .PublicKey :
hs .rsaDecryptOk = true
default :
c .sendAlert (alertInternalError )
return fmt .Errorf ("tls: unsupported decryption key type (%T)" , priv .Public ())
}
}
return nil
}
func negotiateALPN (serverProtos , clientProtos []string , quic bool ) (string , error ) {
if len (serverProtos ) == 0 || len (clientProtos ) == 0 {
if quic && len (serverProtos ) != 0 {
return "" , fmt .Errorf ("tls: client did not request an application protocol" )
}
return "" , nil
}
var http11fallback bool
for _ , s := range serverProtos {
for _ , c := range clientProtos {
if s == c {
return s , nil
}
if s == "h2" && c == "http/1.1" {
http11fallback = true
}
}
}
if http11fallback {
return "" , nil
}
return "" , fmt .Errorf ("tls: client requested unsupported application protocols (%s)" , clientProtos )
}
func supportsECDHE (c *Config , supportedCurves []CurveID , supportedPoints []uint8 ) bool {
supportsCurve := false
for _ , curve := range supportedCurves {
if c .supportsCurve (curve ) {
supportsCurve = true
break
}
}
supportsPointFormat := false
for _ , pointFormat := range supportedPoints {
if pointFormat == pointFormatUncompressed {
supportsPointFormat = true
break
}
}
if len (supportedPoints ) == 0 {
supportsPointFormat = true
}
return supportsCurve && supportsPointFormat
}
func (hs *serverHandshakeState ) pickCipherSuite () error {
c := hs .c
preferenceOrder := cipherSuitesPreferenceOrder
if !hasAESGCMHardwareSupport || !aesgcmPreferred (hs .clientHello .cipherSuites ) {
preferenceOrder = cipherSuitesPreferenceOrderNoAES
}
configCipherSuites := c .config .cipherSuites ()
preferenceList := make ([]uint16 , 0 , len (configCipherSuites ))
for _ , suiteID := range preferenceOrder {
for _ , id := range configCipherSuites {
if id == suiteID {
preferenceList = append (preferenceList , id )
break
}
}
}
hs .suite = selectCipherSuite (preferenceList , hs .clientHello .cipherSuites , hs .cipherSuiteOk )
if hs .suite == nil {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: no cipher suite supported by both client and server" )
}
c .cipherSuite = hs .suite .id
for _ , id := range hs .clientHello .cipherSuites {
if id == TLS_FALLBACK_SCSV {
if hs .clientHello .vers < c .config .maxSupportedVersion (roleServer ) {
c .sendAlert (alertInappropriateFallback )
return errors .New ("tls: client using inappropriate protocol fallback" )
}
break
}
}
return nil
}
func (hs *serverHandshakeState ) cipherSuiteOk (c *cipherSuite ) bool {
if c .flags &suiteECDHE != 0 {
if !hs .ecdheOk {
return false
}
if c .flags &suiteECSign != 0 {
if !hs .ecSignOk {
return false
}
} else if !hs .rsaSignOk {
return false
}
} else if !hs .rsaDecryptOk {
return false
}
if hs .c .vers < VersionTLS12 && c .flags &suiteTLS12 != 0 {
return false
}
return true
}
func (hs *serverHandshakeState ) checkForResumption () error {
c := hs .c
if c .config .SessionTicketsDisabled {
return nil
}
var sessionState *SessionState
if c .config .UnwrapSession != nil {
ss , err := c .config .UnwrapSession (hs .clientHello .sessionTicket , c .connectionStateLocked ())
if err != nil {
return err
}
if ss == nil {
return nil
}
sessionState = ss
} else {
plaintext := c .config .decryptTicket (hs .clientHello .sessionTicket , c .ticketKeys )
if plaintext == nil {
return nil
}
ss , err := ParseSessionState (plaintext )
if err != nil {
return nil
}
sessionState = ss
}
createdAt := time .Unix (int64 (sessionState .createdAt ), 0 )
if c .config .time ().Sub (createdAt ) > maxSessionTicketLifetime {
return nil
}
if c .vers != sessionState .version {
return nil
}
cipherSuiteOk := false
for _ , id := range hs .clientHello .cipherSuites {
if id == sessionState .cipherSuite {
cipherSuiteOk = true
break
}
}
if !cipherSuiteOk {
return nil
}
suite := selectCipherSuite ([]uint16 {sessionState .cipherSuite },
c .config .cipherSuites (), hs .cipherSuiteOk )
if suite == nil {
return nil
}
sessionHasClientCerts := len (sessionState .peerCertificates ) != 0
needClientCerts := requiresClientCert (c .config .ClientAuth )
if needClientCerts && !sessionHasClientCerts {
return nil
}
if sessionHasClientCerts && c .config .ClientAuth == NoClientCert {
return nil
}
if sessionHasClientCerts && c .config .time ().After (sessionState .peerCertificates [0 ].NotAfter ) {
return nil
}
if sessionHasClientCerts && c .config .ClientAuth >= VerifyClientCertIfGiven &&
len (sessionState .verifiedChains ) == 0 {
return nil
}
if !sessionState .extMasterSecret && hs .clientHello .extendedMasterSecret {
return nil
}
if sessionState .extMasterSecret && !hs .clientHello .extendedMasterSecret {
return errors .New ("tls: session supported extended_master_secret but client does not" )
}
c .peerCertificates = sessionState .peerCertificates
c .ocspResponse = sessionState .ocspResponse
c .scts = sessionState .scts
c .verifiedChains = sessionState .verifiedChains
c .extMasterSecret = sessionState .extMasterSecret
hs .sessionState = sessionState
hs .suite = suite
c .didResume = true
return nil
}
func (hs *serverHandshakeState ) doResumeHandshake () error {
c := hs .c
hs .hello .cipherSuite = hs .suite .id
c .cipherSuite = hs .suite .id
hs .hello .sessionId = hs .clientHello .sessionId
hs .hello .ticketSupported = true
hs .finishedHash = newFinishedHash (c .vers , hs .suite )
hs .finishedHash .discardHandshakeBuffer ()
if err := transcriptMsg (hs .clientHello , &hs .finishedHash ); err != nil {
return err
}
if _ , err := hs .c .writeHandshakeRecord (hs .hello , &hs .finishedHash ); err != nil {
return err
}
if c .config .VerifyConnection != nil {
if err := c .config .VerifyConnection (c .connectionStateLocked ()); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
}
hs .masterSecret = hs .sessionState .secret
return nil
}
func (hs *serverHandshakeState ) doFullHandshake () error {
c := hs .c
if hs .clientHello .ocspStapling && len (hs .cert .OCSPStaple ) > 0 {
hs .hello .ocspStapling = true
}
hs .hello .ticketSupported = hs .clientHello .ticketSupported && !c .config .SessionTicketsDisabled
hs .hello .cipherSuite = hs .suite .id
hs .finishedHash = newFinishedHash (hs .c .vers , hs .suite )
if c .config .ClientAuth == NoClientCert {
hs .finishedHash .discardHandshakeBuffer ()
}
if err := transcriptMsg (hs .clientHello , &hs .finishedHash ); err != nil {
return err
}
if _ , err := hs .c .writeHandshakeRecord (hs .hello , &hs .finishedHash ); err != nil {
return err
}
certMsg := new (certificateMsg )
certMsg .certificates = hs .cert .Certificate
if _ , err := hs .c .writeHandshakeRecord (certMsg , &hs .finishedHash ); err != nil {
return err
}
if hs .hello .ocspStapling {
certStatus := new (certificateStatusMsg )
certStatus .response = hs .cert .OCSPStaple
if _ , err := hs .c .writeHandshakeRecord (certStatus , &hs .finishedHash ); err != nil {
return err
}
}
keyAgreement := hs .suite .ka (c .vers )
skx , err := keyAgreement .generateServerKeyExchange (c .config , hs .cert , hs .clientHello , hs .hello )
if err != nil {
c .sendAlert (alertHandshakeFailure )
return err
}
if skx != nil {
if _ , err := hs .c .writeHandshakeRecord (skx , &hs .finishedHash ); err != nil {
return err
}
}
var certReq *certificateRequestMsg
if c .config .ClientAuth >= RequestClientCert {
certReq = new (certificateRequestMsg )
certReq .certificateTypes = []byte {
byte (certTypeRSASign ),
byte (certTypeECDSASign ),
}
if c .vers >= VersionTLS12 {
certReq .hasSignatureAlgorithm = true
certReq .supportedSignatureAlgorithms = supportedSignatureAlgorithms ()
}
if c .config .ClientCAs != nil {
certReq .certificateAuthorities = c .config .ClientCAs .Subjects ()
}
if _ , err := hs .c .writeHandshakeRecord (certReq , &hs .finishedHash ); err != nil {
return err
}
}
helloDone := new (serverHelloDoneMsg )
if _ , err := hs .c .writeHandshakeRecord (helloDone , &hs .finishedHash ); err != nil {
return err
}
if _ , err := c .flush (); err != nil {
return err
}
var pub crypto .PublicKey
msg , err := c .readHandshake (&hs .finishedHash )
if err != nil {
return err
}
if c .config .ClientAuth >= RequestClientCert {
certMsg , ok := msg .(*certificateMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (certMsg , msg )
}
if err := c .processCertsFromClient (Certificate {
Certificate : certMsg .certificates ,
}); err != nil {
return err
}
if len (certMsg .certificates ) != 0 {
pub = c .peerCertificates [0 ].PublicKey
}
msg , err = c .readHandshake (&hs .finishedHash )
if err != nil {
return err
}
}
if c .config .VerifyConnection != nil {
if err := c .config .VerifyConnection (c .connectionStateLocked ()); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
}
ckx , ok := msg .(*clientKeyExchangeMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (ckx , msg )
}
preMasterSecret , err := keyAgreement .processClientKeyExchange (c .config , hs .cert , ckx , c .vers )
if err != nil {
c .sendAlert (alertHandshakeFailure )
return err
}
if hs .hello .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 .clientHello .random , hs .hello .random )
}
if err := c .config .writeKeyLog (keyLogLabelTLS12 , hs .clientHello .random , hs .masterSecret ); err != nil {
c .sendAlert (alertInternalError )
return err
}
if len (c .peerCertificates ) > 0 {
msg , err = c .readHandshake (nil )
if err != nil {
return err
}
certVerify , ok := msg .(*certificateVerifyMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (certVerify , msg )
}
var sigType uint8
var sigHash crypto .Hash
if c .vers >= VersionTLS12 {
if !isSupportedSignatureAlgorithm (certVerify .signatureAlgorithm , certReq .supportedSignatureAlgorithms ) {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: client certificate used with invalid signature algorithm" )
}
sigType , sigHash , err = typeAndHashFromSignatureScheme (certVerify .signatureAlgorithm )
if err != nil {
return c .sendAlert (alertInternalError )
}
} else {
sigType , sigHash , err = legacyTypeAndHashFromPublicKey (pub )
if err != nil {
c .sendAlert (alertIllegalParameter )
return err
}
}
signed := hs .finishedHash .hashForClientCertificate (sigType , sigHash )
if err := verifyHandshakeSignature (sigType , pub , sigHash , signed , certVerify .signature ); err != nil {
c .sendAlert (alertDecryptError )
return errors .New ("tls: invalid signature by the client certificate: " + err .Error())
}
if err := transcriptMsg (certVerify , &hs .finishedHash ); err != nil {
return err
}
}
hs .finishedHash .discardHandshakeBuffer ()
return nil
}
func (hs *serverHandshakeState ) establishKeys () error {
c := hs .c
clientMAC , serverMAC , clientKey , serverKey , clientIV , serverIV :=
keysFromMasterSecret (c .vers , hs .suite , hs .masterSecret , hs .clientHello .random , hs .hello .random , hs .suite .macLen , hs .suite .keyLen , hs .suite .ivLen )
var clientCipher , serverCipher any
var clientHash , serverHash hash .Hash
if hs .suite .aead == nil {
clientCipher = hs .suite .cipher (clientKey , clientIV , true )
clientHash = hs .suite .mac (clientMAC )
serverCipher = hs .suite .cipher (serverKey , serverIV , false )
serverHash = hs .suite .mac (serverMAC )
} else {
clientCipher = hs .suite .aead (clientKey , clientIV )
serverCipher = hs .suite .aead (serverKey , serverIV )
}
c .in .prepareCipherSpec (c .vers , clientCipher , clientHash )
c .out .prepareCipherSpec (c .vers , serverCipher , serverHash )
return nil
}
func (hs *serverHandshakeState ) 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
}
clientFinished , ok := msg .(*finishedMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (clientFinished , msg )
}
verify := hs .finishedHash .clientSum (hs .masterSecret )
if len (verify ) != len (clientFinished .verifyData ) ||
subtle .ConstantTimeCompare (verify , clientFinished .verifyData ) != 1 {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: client's Finished message is incorrect" )
}
if err := transcriptMsg (clientFinished , &hs .finishedHash ); err != nil {
return err
}
copy (out , verify )
return nil
}
func (hs *serverHandshakeState ) sendSessionTicket () error {
if !hs .hello .ticketSupported {
return nil
}
c := hs .c
m := new (newSessionTicketMsg )
state , err := c .sessionState ()
if err != nil {
return err
}
state .secret = hs .masterSecret
if hs .sessionState != nil {
state .createdAt = hs .sessionState .createdAt
}
if c .config .WrapSession != nil {
m .ticket , err = c .config .WrapSession (c .connectionStateLocked (), state )
if err != nil {
return err
}
} else {
stateBytes , err := state .Bytes ()
if err != nil {
return err
}
m .ticket , err = c .config .encryptTicket (stateBytes , c .ticketKeys )
if err != nil {
return err
}
}
if _ , err := hs .c .writeHandshakeRecord (m , &hs .finishedHash ); err != nil {
return err
}
return nil
}
func (hs *serverHandshakeState ) sendFinished (out []byte ) error {
c := hs .c
if err := c .writeChangeCipherRecord (); err != nil {
return err
}
finished := new (finishedMsg )
finished .verifyData = hs .finishedHash .serverSum (hs .masterSecret )
if _ , err := hs .c .writeHandshakeRecord (finished , &hs .finishedHash ); err != nil {
return err
}
copy (out , finished .verifyData )
return nil
}
func (c *Conn ) processCertsFromClient (certificate Certificate ) error {
certificates := certificate .Certificate
certs := make ([]*x509 .Certificate , len (certificates ))
var err error
for i , asn1Data := range certificates {
if certs [i ], err = x509 .ParseCertificate (asn1Data ); err != nil {
c .sendAlert (alertBadCertificate )
return errors .New ("tls: failed to parse client certificate: " + err .Error())
}
if certs [i ].PublicKeyAlgorithm == x509 .RSA {
n := certs [i ].PublicKey .(*rsa .PublicKey ).N .BitLen ()
if max , ok := checkKeySize (n ); !ok {
c .sendAlert (alertBadCertificate )
return fmt .Errorf ("tls: client sent certificate containing RSA key larger than %d bits" , max )
}
}
}
if len (certs ) == 0 && requiresClientCert (c .config .ClientAuth ) {
if c .vers == VersionTLS13 {
c .sendAlert (alertCertificateRequired )
} else {
c .sendAlert (alertBadCertificate )
}
return errors .New ("tls: client didn't provide a certificate" )
}
if c .config .ClientAuth >= VerifyClientCertIfGiven && len (certs ) > 0 {
opts := x509 .VerifyOptions {
Roots : c .config .ClientCAs ,
CurrentTime : c .config .time (),
Intermediates : x509 .NewCertPool (),
KeyUsages : []x509 .ExtKeyUsage {x509 .ExtKeyUsageClientAuth },
}
for _ , cert := range certs [1 :] {
opts .Intermediates .AddCert (cert )
}
chains , err := certs [0 ].Verify (opts )
if err != nil {
var errCertificateInvalid x509 .CertificateInvalidError
if errors .As (err , &x509 .UnknownAuthorityError {}) {
c .sendAlert (alertUnknownCA )
} else if errors .As (err , &errCertificateInvalid ) && errCertificateInvalid .Reason == x509 .Expired {
c .sendAlert (alertCertificateExpired )
} else {
c .sendAlert (alertBadCertificate )
}
return &CertificateVerificationError {UnverifiedCertificates : certs , Err : err }
}
c .verifiedChains = chains
}
c .peerCertificates = certs
c .ocspResponse = certificate .OCSPStaple
c .scts = certificate .SignedCertificateTimestamps
if len (certs ) > 0 {
switch certs [0 ].PublicKey .(type ) {
case *ecdsa .PublicKey , *rsa .PublicKey , ed25519 .PublicKey :
default :
c .sendAlert (alertUnsupportedCertificate )
return fmt .Errorf ("tls: client certificate contains an unsupported public key of type %T" , certs [0 ].PublicKey )
}
}
if c .config .VerifyPeerCertificate != nil {
if err := c .config .VerifyPeerCertificate (certificates , c .verifiedChains ); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
}
return nil
}
func clientHelloInfo (ctx context .Context , c *Conn , clientHello *clientHelloMsg ) *ClientHelloInfo {
supportedVersions := clientHello .supportedVersions
if len (clientHello .supportedVersions ) == 0 {
supportedVersions = supportedVersionsFromMax (clientHello .vers )
}
return &ClientHelloInfo {
CipherSuites : clientHello .cipherSuites ,
ServerName : clientHello .serverName ,
SupportedCurves : clientHello .supportedCurves ,
SupportedPoints : clientHello .supportedPoints ,
SignatureSchemes : clientHello .supportedSignatureAlgorithms ,
SupportedProtos : clientHello .alpnProtocols ,
SupportedVersions : supportedVersions ,
Conn : c .conn ,
config : c .config ,
ctx : ctx ,
}
}
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 .