]> Cypherpunks.ru repositories - gostls13.git/commitdiff
crypto/tls: implement Certificate.SupportedSignatureAlgorithms
authorFilippo Valsorda <filippo@golang.org>
Mon, 4 Nov 2019 02:04:48 +0000 (21:04 -0500)
committerFilippo Valsorda <filippo@golang.org>
Tue, 12 Nov 2019 01:09:06 +0000 (01:09 +0000)
This will let applications stop crypto/tls from using a certificate key
with an algorithm that is not supported by its crypto.Signer, like
hardware backed keys that can't do RSA-PSS.

Fixes #28660

Change-Id: I294cc06bddf813fff35c5107540c4a1788e1dace
Reviewed-on: https://go-review.googlesource.com/c/go/+/205062
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
src/crypto/tls/auth.go
src/crypto/tls/auth_test.go
src/crypto/tls/common.go
src/crypto/tls/tls_test.go

index 3ae2256620bef552096774410edbf91144655e5d..e8a500e876da35c7561c3a2dea32390428ff2cfc 100644 (file)
@@ -154,7 +154,8 @@ func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash c
 }
 
 // signatureSchemesForCertificate returns the list of supported SignatureSchemes
-// for a given certificate, based on the public key and the protocol version.
+// for a given certificate, based on the public key and the protocol version,
+// and optionally filtered by its explicit SupportedSignatureAlgorithms.
 //
 // This function must be kept in sync with supportedSignatureAlgorithms.
 func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
@@ -163,31 +164,33 @@ func signatureSchemesForCertificate(version uint16, cert *Certificate) []Signatu
                return nil
        }
 
+       var sigAlgs []SignatureScheme
        switch pub := priv.Public().(type) {
        case *ecdsa.PublicKey:
                if version != VersionTLS13 {
                        // In TLS 1.2 and earlier, ECDSA algorithms are not
                        // constrained to a single curve.
-                       return []SignatureScheme{
+                       sigAlgs = []SignatureScheme{
                                ECDSAWithP256AndSHA256,
                                ECDSAWithP384AndSHA384,
                                ECDSAWithP521AndSHA512,
                                ECDSAWithSHA1,
                        }
+                       break
                }
                switch pub.Curve {
                case elliptic.P256():
-                       return []SignatureScheme{ECDSAWithP256AndSHA256}
+                       sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
                case elliptic.P384():
-                       return []SignatureScheme{ECDSAWithP384AndSHA384}
+                       sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
                case elliptic.P521():
-                       return []SignatureScheme{ECDSAWithP521AndSHA512}
+                       sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
                default:
                        return nil
                }
        case *rsa.PublicKey:
                if version != VersionTLS13 {
-                       return []SignatureScheme{
+                       sigAlgs = []SignatureScheme{
                                // Temporarily disable RSA-PSS in TLS 1.2, see Issue 32425.
                                // PSSWithSHA256,
                                // PSSWithSHA384,
@@ -197,18 +200,30 @@ func signatureSchemesForCertificate(version uint16, cert *Certificate) []Signatu
                                PKCS1WithSHA512,
                                PKCS1WithSHA1,
                        }
+                       break
                }
                // TLS 1.3 dropped support for PKCS#1 v1.5 in favor of RSA-PSS.
-               return []SignatureScheme{
+               sigAlgs = []SignatureScheme{
                        PSSWithSHA256,
                        PSSWithSHA384,
                        PSSWithSHA512,
                }
        case ed25519.PublicKey:
-               return []SignatureScheme{Ed25519}
+               sigAlgs = []SignatureScheme{Ed25519}
        default:
                return nil
        }
+
+       if cert.SupportedSignatureAlgorithms != nil {
+               var filteredSigAlgs []SignatureScheme
+               for _, sigAlg := range sigAlgs {
+                       if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
+                               filteredSigAlgs = append(filteredSigAlgs, sigAlg)
+                       }
+               }
+               return filteredSigAlgs
+       }
+       return sigAlgs
 }
 
 // selectSignatureScheme picks a SignatureScheme from the peer's preference list
@@ -216,7 +231,7 @@ func signatureSchemesForCertificate(version uint16, cert *Certificate) []Signatu
 // versions that support signature algorithms, so TLS 1.2 and 1.3.
 func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
        supportedAlgs := signatureSchemesForCertificate(vers, c)
-       if supportedAlgs == nil {
+       if len(supportedAlgs) == 0 {
                return 0, unsupportedCertificateError(c)
        }
        if len(peerAlgs) == 0 && vers == VersionTLS12 {
@@ -266,5 +281,9 @@ func unsupportedCertificateError(cert *Certificate) error {
                return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
        }
 
+       if cert.SupportedSignatureAlgorithms != nil {
+               return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
+       }
+
        return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
 }
index 52ddf18d6f223a1703749d715d00d5d384fcc46e..7edb003d9b248cc5f24e8d9f3bd2fdd9e738c7a1 100644 (file)
@@ -14,6 +14,11 @@ func TestSignatureSelection(t *testing.T) {
                Certificate: [][]byte{testRSACertificate},
                PrivateKey:  testRSAPrivateKey,
        }
+       pkcs1Cert := &Certificate{
+               Certificate:                  [][]byte{testRSACertificate},
+               PrivateKey:                   testRSAPrivateKey,
+               SupportedSignatureAlgorithms: []SignatureScheme{PKCS1WithSHA1, PKCS1WithSHA256},
+       }
        ecdsaCert := &Certificate{
                Certificate: [][]byte{testP256Certificate},
                PrivateKey:  testP256PrivateKey,
@@ -35,6 +40,7 @@ func TestSignatureSelection(t *testing.T) {
                {rsaCert, []SignatureScheme{PKCS1WithSHA1, PKCS1WithSHA256}, VersionTLS12, PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1},
                {rsaCert, []SignatureScheme{PKCS1WithSHA512, PKCS1WithSHA1}, VersionTLS12, PKCS1WithSHA512, signaturePKCS1v15, crypto.SHA512},
                {rsaCert, []SignatureScheme{PSSWithSHA256, PKCS1WithSHA256}, VersionTLS12, PKCS1WithSHA256, signaturePKCS1v15, crypto.SHA256},
+               {pkcs1Cert, []SignatureScheme{PSSWithSHA256, PKCS1WithSHA256}, VersionTLS12, PKCS1WithSHA256, signaturePKCS1v15, crypto.SHA256},
                {rsaCert, []SignatureScheme{PSSWithSHA384, PKCS1WithSHA1}, VersionTLS13, PSSWithSHA384, signatureRSAPSS, crypto.SHA384},
                {ecdsaCert, []SignatureScheme{ECDSAWithSHA1}, VersionTLS12, ECDSAWithSHA1, signatureECDSA, crypto.SHA1},
                {ecdsaCert, []SignatureScheme{ECDSAWithP256AndSHA256}, VersionTLS12, ECDSAWithP256AndSHA256, signatureECDSA, crypto.SHA256},
@@ -70,6 +76,12 @@ func TestSignatureSelection(t *testing.T) {
                }
        }
 
+       brokenCert := &Certificate{
+               Certificate:                  [][]byte{testRSACertificate},
+               PrivateKey:                   testRSAPrivateKey,
+               SupportedSignatureAlgorithms: []SignatureScheme{Ed25519},
+       }
+
        badTests := []struct {
                cert        *Certificate
                peerSigAlgs []SignatureScheme
@@ -80,6 +92,8 @@ func TestSignatureSelection(t *testing.T) {
                {rsaCert, []SignatureScheme{0}, VersionTLS12},
                {ed25519Cert, []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithSHA1}, VersionTLS12},
                {ecdsaCert, []SignatureScheme{Ed25519}, VersionTLS12},
+               {brokenCert, []SignatureScheme{Ed25519}, VersionTLS12},
+               {brokenCert, []SignatureScheme{PKCS1WithSHA256}, VersionTLS12},
                // RFC 5246, Section 7.4.1.4.1, says to only consider {sha1,ecdsa} as
                // default when the extension is missing, and RFC 8422 does not update
                // it. Anyway, if a stack supports Ed25519 it better support sigalgs.
@@ -92,6 +106,7 @@ func TestSignatureSelection(t *testing.T) {
                {ecdsaCert, []SignatureScheme{ECDSAWithP384AndSHA384}, VersionTLS13},
                // TLS 1.3 does not support PKCS1v1.5 or SHA-1.
                {rsaCert, []SignatureScheme{PKCS1WithSHA256}, VersionTLS13},
+               {pkcs1Cert, []SignatureScheme{PSSWithSHA256, PKCS1WithSHA256}, VersionTLS13},
                {ecdsaCert, []SignatureScheme{ECDSAWithSHA1}, VersionTLS13},
        }
 
index 2c1ff277185f3e14fcdff7a5d092b05e91a3905b..04f93da5ef83a723a5bc832e18cfa9db8f862fa6 100644 (file)
@@ -1174,6 +1174,9 @@ type Certificate struct {
        // For a server up to TLS 1.2, it can also implement crypto.Decrypter with
        // an RSA PublicKey.
        PrivateKey crypto.PrivateKey
+       // SupportedSignatureAlgorithms is an optional list restricting what
+       // signature algorithms the PrivateKey can be used for.
+       SupportedSignatureAlgorithms []SignatureScheme
        // OCSPStaple contains an optional OCSP response which will be served
        // to clients that request it.
        OCSPStaple []byte
index fc9cf1df0d32f0541dc5a849e218f5becee0a4a7..f3a286052c2237ded86cf72aa183d6023c383023 100644 (file)
@@ -6,6 +6,7 @@ package tls
 
 import (
        "bytes"
+       "crypto"
        "crypto/x509"
        "encoding/json"
        "errors"
@@ -1051,6 +1052,11 @@ func TestClientHelloInfo_SupportsCertificate(t *testing.T) {
                Certificate: [][]byte{testRSACertificate},
                PrivateKey:  testRSAPrivateKey,
        }
+       pkcs1Cert := &Certificate{
+               Certificate:                  [][]byte{testRSACertificate},
+               PrivateKey:                   testRSAPrivateKey,
+               SupportedSignatureAlgorithms: []SignatureScheme{PKCS1WithSHA1, PKCS1WithSHA256},
+       }
        ecdsaCert := &Certificate{
                // ECDSA P-256 certificate
                Certificate: [][]byte{testP256Certificate},
@@ -1084,6 +1090,10 @@ func TestClientHelloInfo_SupportsCertificate(t *testing.T) {
                        SignatureSchemes:  []SignatureScheme{ECDSAWithP384AndSHA384},
                        SupportedVersions: []uint16{VersionTLS13},
                }, "signature algorithms"},
+               {pkcs1Cert, &ClientHelloInfo{
+                       SignatureSchemes:  []SignatureScheme{PSSWithSHA256, ECDSAWithP256AndSHA256},
+                       SupportedVersions: []uint16{VersionTLS13},
+               }, "signature algorithms"},
 
                {rsaCert, &ClientHelloInfo{
                        CipherSuites:      []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
@@ -1204,3 +1214,38 @@ func TestClientHelloInfo_SupportsCertificate(t *testing.T) {
                }
        }
 }
+
+type brokenSigner struct{ crypto.Signer }
+
+func (s brokenSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
+       // Replace opts with opts.HashFunc(), so rsa.PSSOptions are discarded.
+       return s.Signer.Sign(rand, digest, opts.HashFunc())
+}
+
+// TestPKCS1OnlyCert uses a client certificate with a broken crypto.Signer that
+// always makes PKCS#1 v1.5 signatures, so can't be used with RSA-PSS.
+func TestPKCS1OnlyCert(t *testing.T) {
+       clientConfig := testConfig.Clone()
+       clientConfig.Certificates = []Certificate{{
+               Certificate: [][]byte{testRSACertificate},
+               PrivateKey:  brokenSigner{testRSAPrivateKey},
+       }}
+       serverConfig := testConfig.Clone()
+       serverConfig.MaxVersion = VersionTLS12 // TLS 1.3 doesn't support PKCS#1 v1.5
+       serverConfig.ClientAuth = RequireAnyClientCert
+
+       // If RSA-PSS is selected, the handshake should fail.
+       if _, _, err := testHandshake(t, clientConfig, serverConfig); err == nil {
+               // RSA-PSS is temporarily disabled in TLS 1.2. See Issue 32425.
+               // t.Fatal("expected broken certificate to cause connection to fail")
+       }
+
+       clientConfig.Certificates[0].SupportedSignatureAlgorithms =
+               []SignatureScheme{PKCS1WithSHA1, PKCS1WithSHA256}
+
+       // But if the certificate restricts supported algorithms, RSA-PSS should not
+       // be selected, and the handshake should succeed.
+       if _, _, err := testHandshake(t, clientConfig, serverConfig); err != nil {
+               t.Error(err)
+       }
+}