Commit 4b216421 authored by Filippo Valsorda's avatar Filippo Valsorda

crypto/tls: implement (*CertificateRequestInfo).SupportsCertificate

Also, add Version to CertificateRequestInfo, as the semantics of
SignatureSchemes change based on version: the ECDSA SignatureSchemes are
only constrained to a specific curve in TLS 1.3.

Fixes #32426

Change-Id: I7a551bea864799e98118349ac2476162893d1ffd
Reviewed-on: https://go-review.googlesource.com/c/go/+/205058
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarAdam Langley <agl@golang.org>
parent dd017384
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package tls package tls
import ( import (
"bytes"
"container/list" "container/list"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
...@@ -407,6 +408,9 @@ type CertificateRequestInfo struct { ...@@ -407,6 +408,9 @@ type CertificateRequestInfo struct {
// SignatureSchemes lists the signature schemes that the server is // SignatureSchemes lists the signature schemes that the server is
// willing to verify. // willing to verify.
SignatureSchemes []SignatureScheme SignatureSchemes []SignatureScheme
// Version is the TLS version that was negotiated for this connection.
Version uint16
} }
// RenegotiationSupport enumerates the different levels of support for TLS // RenegotiationSupport enumerates the different levels of support for TLS
...@@ -1070,6 +1074,38 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error { ...@@ -1070,6 +1074,38 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error {
return nil return nil
} }
// SupportsCertificate returns nil if the provided certificate is supported by
// the server that sent the CertificateRequest. Otherwise, it returns an error
// describing the reason for the incompatibility.
func (cri *CertificateRequestInfo) SupportsCertificate(c *Certificate) error {
if _, err := selectSignatureScheme(cri.Version, c, cri.SignatureSchemes); err != nil {
return err
}
if len(cri.AcceptableCAs) == 0 {
return nil
}
for j, cert := range c.Certificate {
x509Cert := c.Leaf
// Parse the certificate if this isn't the leaf node, or if
// chain.Leaf was nil.
if j != 0 || x509Cert == nil {
var err error
if x509Cert, err = x509.ParseCertificate(cert); err != nil {
return fmt.Errorf("failed to parse certificate #%d in the chain: %w", j, err)
}
}
for _, ca := range cri.AcceptableCAs {
if bytes.Equal(x509Cert.RawIssuer, ca) {
return nil
}
}
}
return errors.New("chain is not signed by an acceptable CA")
}
// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate // BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
// from the CommonName and SubjectAlternateName fields of each of the leaf // from the CommonName and SubjectAlternateName fields of each of the leaf
// certificates. // certificates.
......
...@@ -16,7 +16,6 @@ import ( ...@@ -16,7 +16,6 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
...@@ -518,7 +517,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { ...@@ -518,7 +517,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
certRequested = true certRequested = true
hs.finishedHash.Write(certReq.marshal()) hs.finishedHash.Write(certReq.marshal())
cri := certificateRequestInfoFromMsg(certReq) cri := certificateRequestInfoFromMsg(c.vers, certReq)
if chainToSend, err = c.getClientCertificate(cri); err != nil { if chainToSend, err = c.getClientCertificate(cri); err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err
...@@ -850,7 +849,12 @@ var ( ...@@ -850,7 +849,12 @@ var (
// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS // certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS
// <= 1.2 CertificateRequest, making an effort to fill in missing information. // <= 1.2 CertificateRequest, making an effort to fill in missing information.
func certificateRequestInfoFromMsg(certReq *certificateRequestMsg) *CertificateRequestInfo { func certificateRequestInfoFromMsg(vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo {
cri := &CertificateRequestInfo{
AcceptableCAs: certReq.certificateAuthorities,
Version: vers,
}
var rsaAvail, ecAvail bool var rsaAvail, ecAvail bool
for _, certType := range certReq.certificateTypes { for _, certType := range certReq.certificateTypes {
switch certType { switch certType {
...@@ -861,10 +865,6 @@ func certificateRequestInfoFromMsg(certReq *certificateRequestMsg) *CertificateR ...@@ -861,10 +865,6 @@ func certificateRequestInfoFromMsg(certReq *certificateRequestMsg) *CertificateR
} }
} }
cri := &CertificateRequestInfo{
AcceptableCAs: certReq.certificateAuthorities,
}
if !certReq.hasSignatureAlgorithm { if !certReq.hasSignatureAlgorithm {
// Prior to TLS 1.2, the signature schemes were not // Prior to TLS 1.2, the signature schemes were not
// included in the certificate request message. In this // included in the certificate request message. In this
...@@ -909,43 +909,11 @@ func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, ...@@ -909,43 +909,11 @@ func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate,
return c.config.GetClientCertificate(cri) return c.config.GetClientCertificate(cri)
} }
// We need to search our list of client certs for one for _, chain := range c.config.Certificates {
// where SignatureAlgorithm is acceptable to the server and the if err := cri.SupportsCertificate(&chain); err != nil {
// Issuer is in AcceptableCAs.
for i, chain := range c.config.Certificates {
sigOK := false
for _, alg := range signatureSchemesForCertificate(c.vers, &chain) {
if isSupportedSignatureAlgorithm(alg, cri.SignatureSchemes) {
sigOK = true
break
}
}
if !sigOK {
continue continue
} }
return &chain, nil
if len(cri.AcceptableCAs) == 0 {
return &chain, nil
}
for j, cert := range chain.Certificate {
x509Cert := chain.Leaf
// Parse the certificate if this isn't the leaf node, or if
// chain.Leaf was nil.
if j != 0 || x509Cert == nil {
var err error
if x509Cert, err = x509.ParseCertificate(cert); err != nil {
c.sendAlert(alertInternalError)
return nil, errors.New("tls: failed to parse configured certificate chain #" + strconv.Itoa(i) + ": " + err.Error())
}
}
for _, ca := range cri.AcceptableCAs {
if bytes.Equal(x509Cert.RawIssuer, ca) {
return &chain, nil
}
}
}
} }
// No acceptable certificate found. Don't send a certificate. // No acceptable certificate found. Don't send a certificate.
......
...@@ -526,6 +526,7 @@ func (hs *clientHandshakeStateTLS13) sendClientCertificate() error { ...@@ -526,6 +526,7 @@ func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
cert, err := c.getClientCertificate(&CertificateRequestInfo{ cert, err := c.getClientCertificate(&CertificateRequestInfo{
AcceptableCAs: hs.certReq.certificateAuthorities, AcceptableCAs: hs.certReq.certificateAuthorities,
SignatureSchemes: hs.certReq.supportedSignatureAlgorithms, SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
Version: c.vers,
}) })
if err != nil { if err != nil {
return err return err
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment