clientHello *clientHelloMsg
hello *serverHelloMsg
suite *cipherSuite
- ecdhOk bool
+ ecdheOk bool
ecSignOk bool
rsaDecryptOk bool
rsaSignOk bool
if len(clientHello.supportedVersions) == 0 {
clientVersions = supportedVersionsFromMax(clientHello.vers)
}
- c.vers, ok = c.config.mutualVersion(false, clientVersions)
+ c.vers, ok = c.config.mutualVersion(clientVersions)
if !ok {
c.sendAlert(alertProtocolVersion)
return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
hs.hello = new(serverHelloMsg)
hs.hello.vers = c.vers
- supportedCurve := false
- preferredCurves := c.config.curvePreferences()
-Curves:
- for _, curve := range hs.clientHello.supportedCurves {
- for _, supported := range preferredCurves {
- if supported == curve {
- supportedCurve = true
- break Curves
- }
- }
- }
-
- supportedPointFormat := false
- for _, pointFormat := range hs.clientHello.supportedPoints {
- if pointFormat == pointFormatUncompressed {
- supportedPointFormat = true
- break
- }
- }
- hs.ecdhOk = supportedCurve && supportedPointFormat
-
foundCompression := false
// We only support null compression, so check that the client offered it.
for _, compression := range hs.clientHello.compressionMethods {
hs.hello.random = make([]byte, 32)
serverRandom := hs.hello.random
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
- maxVers := c.config.maxSupportedVersion(false)
+ maxVers := c.config.maxSupportedVersion()
if maxVers >= VersionTLS12 && c.vers < maxVers {
if c.vers == VersionTLS12 {
copy(serverRandom[24:], downgradeCanaryTLS12)
hs.hello.alpnProtocol = selectedProto
c.clientProtocol = selectedProto
}
- } else {
- // Although sending an empty NPN extension is reasonable, Firefox has
- // had a bug around this. Best to send nothing at all if
- // c.config.NextProtos is empty. See
- // https://golang.org/issue/5445.
- if hs.clientHello.nextProtoNeg && len(c.config.NextProtos) > 0 {
- hs.hello.nextProtoNeg = true
- hs.hello.nextProtos = c.config.NextProtos
- }
}
hs.cert, err = c.config.getCertificate(clientHelloInfo(c, hs.clientHello))
if err != nil {
- c.sendAlert(alertInternalError)
+ 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 {
+ // Although omitting the ec_point_formats extension is permitted, some
+ // old OpenSSL version will refuse to handshake if not present.
+ //
+ // Per RFC 4492, section 5.1.2, implementations MUST support the
+ // uncompressed point format. See golang.org/issue/31943.
+ hs.hello.supportedPoints = []uint8{pointFormatUncompressed}
+ }
+
if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
switch priv.Public().(type) {
case *ecdsa.PublicKey:
return nil
}
+// supportsECDHE returns whether ECDHE key exchanges can be used with this
+// pre-TLS 1.3 client.
+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
+ }
+ }
+
+ return supportsCurve && supportsPointFormat
+}
+
func (hs *serverHandshakeState) pickCipherSuite() error {
c := hs.c
supportedList = c.config.cipherSuites()
}
- for _, id := range preferenceList {
- if hs.setCipherSuite(id, supportedList, c.vers) {
- break
- }
- }
-
+ hs.suite = selectCipherSuite(preferenceList, supportedList, hs.cipherSuiteOk)
if hs.suite == nil {
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: no cipher suite supported by both client and server")
for _, id := range hs.clientHello.cipherSuites {
if id == TLS_FALLBACK_SCSV {
// The client is doing a fallback connection. See RFC 7507.
- if hs.clientHello.vers < c.config.maxSupportedVersion(false) {
+ if hs.clientHello.vers < c.config.maxSupportedVersion() {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
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
+}
+
// checkForResumption reports whether we should perform resumption on this connection.
func (hs *serverHandshakeState) checkForResumption() bool {
c := hs.c
}
// Check that we also support the ciphersuite from the session.
- if !hs.setCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers) {
+ hs.suite = selectCipherSuite([]uint16{hs.sessionState.cipherSuite},
+ c.config.cipherSuites(), hs.cipherSuiteOk)
+ if hs.suite == nil {
return false
}
}
if c.vers >= VersionTLS12 {
certReq.hasSignatureAlgorithm = true
- certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithmsTLS12
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
}
// An empty list of certificateAuthorities signals to
return unexpectedMessageError(certVerify, msg)
}
- // Determine the signature type.
- _, sigType, hashFunc, err := pickSignatureAlgorithm(pub, []SignatureScheme{certVerify.signatureAlgorithm}, certReq.supportedSignatureAlgorithms, c.vers)
- if err != nil {
- c.sendAlert(alertIllegalParameter)
- return err
+ 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, err := hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret)
- if err == nil {
- err = verifyHandshakeSignature(sigType, pub, hashFunc, signed, certVerify.signature)
- }
- if err != nil {
- c.sendAlert(alertBadCertificate)
- return errors.New("tls: could not validate signature of connection nonces: " + err.Error())
+ signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret)
+ 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())
}
hs.finishedHash.Write(certVerify.marshal())
return err
}
- if hs.hello.nextProtoNeg {
- msg, err := c.readHandshake()
- if err != nil {
- return err
- }
- nextProto, ok := msg.(*nextProtoMsg)
- if !ok {
- c.sendAlert(alertUnexpectedMessage)
- return unexpectedMessageError(nextProto, msg)
- }
- hs.finishedHash.Write(nextProto.marshal())
- c.clientProtocol = nextProto.proto
- }
-
msg, err := c.readHandshake()
if err != nil {
return err
chains, err := certs[0].Verify(opts)
if err != nil {
c.sendAlert(alertBadCertificate)
- return errors.New("tls: failed to verify client's certificate: " + err.Error())
+ return errors.New("tls: failed to verify client certificate: " + err.Error())
}
c.verifiedChains = chains
case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
default:
c.sendAlert(alertUnsupportedCertificate)
- return fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey)
+ return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
}
c.peerCertificates = certs
return nil
}
-// setCipherSuite sets a cipherSuite with the given id as the serverHandshakeState
-// suite if that cipher suite is acceptable to use.
-// It returns a bool indicating if the suite was set.
-func (hs *serverHandshakeState) setCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16) bool {
- for _, supported := range supportedCipherSuites {
- if id != supported {
- continue
- }
- candidate := cipherSuiteByID(id)
- if candidate == nil {
- continue
- }
- // Don't select a ciphersuite which we can't
- // support for this client.
- if candidate.flags&suiteECDHE != 0 {
- if !hs.ecdhOk {
- continue
- }
- if candidate.flags&suiteECSign != 0 {
- if !hs.ecSignOk {
- continue
- }
- } else if !hs.rsaSignOk {
- continue
- }
- } else if !hs.rsaDecryptOk {
- continue
- }
- if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 {
- continue
- }
- hs.suite = candidate
- return true
- }
- return false
-}
-
func clientHelloInfo(c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
supportedVersions := clientHello.supportedVersions
if len(clientHello.supportedVersions) == 0 {
SupportedProtos: clientHello.alpnProtocols,
SupportedVersions: supportedVersions,
Conn: c.conn,
+ config: c.config,
}
}