Add tests for signature algorithm negotiation.
Change-Id: I5a263734560997b774014b5742877aa4b2940664
Reviewed-on: https://boringssl-review.googlesource.com/2289
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index a4bdef8..01b7581 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -129,8 +129,12 @@
// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
const (
+ hashMD5 uint8 = 1
hashSHA1 uint8 = 2
+ hashSHA224 uint8 = 3
hashSHA256 uint8 = 4
+ hashSHA384 uint8 = 5
+ hashSHA512 uint8 = 6
)
// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
@@ -346,6 +350,11 @@
// protection profiles to offer in DTLS-SRTP.
SRTPProtectionProfiles []uint16
+ // SignatureAndHashes, if not nil, overrides the default set of
+ // supported signature and hash algorithms to advertise in
+ // CertificateRequest.
+ SignatureAndHashes []signatureAndHash
+
// Bugs specifies optional misbehaviour to be used for testing other
// implementations.
Bugs ProtocolBugs
@@ -541,6 +550,14 @@
// SendSRTPProtectionProfile, if non-zero, is the SRTP profile that the
// server sends in the ServerHello instead of the negotiated one.
SendSRTPProtectionProfile uint16
+
+ // NoSignatureAndHashes, if true, causes the client to omit the
+ // signature and hashes extension.
+ //
+ // For a server, it will cause an empty list to be sent in the
+ // CertificateRequest message. None the less, the configured set will
+ // still be enforced.
+ NoSignatureAndHashes bool
}
func (c *Config) serverInit() {
@@ -655,6 +672,20 @@
return &c.Certificates[0]
}
+func (c *Config) signatureAndHashesForServer() []signatureAndHash {
+ if c != nil && c.SignatureAndHashes != nil {
+ return c.SignatureAndHashes
+ }
+ return supportedClientCertSignatureAlgorithms
+}
+
+func (c *Config) signatureAndHashesForClient() []signatureAndHash {
+ if c != nil && c.SignatureAndHashes != nil {
+ return c.SignatureAndHashes
+ }
+ return supportedSKXSignatureAlgorithms
+}
+
// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
// from the CommonName and SubjectAlternateName fields of each of the leaf
// certificates.
@@ -806,3 +837,12 @@
func unexpectedMessageError(wanted, got interface{}) error {
return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
}
+
+func isSupportedSignatureAndHash(sigHash signatureAndHash, sigHashes []signatureAndHash) bool {
+ for _, s := range sigHashes {
+ if s == sigHash {
+ return true
+ }
+ }
+ return false
+}
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 71712a9..c4dff89 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -129,8 +129,8 @@
return errors.New("tls: short read from Rand: " + err.Error())
}
- if hello.vers >= VersionTLS12 {
- hello.signatureAndHashes = supportedSKXSignatureAlgorithms
+ if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes {
+ hello.signatureAndHashes = c.config.signatureAndHashesForClient()
}
var session *ClientSessionState
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index bd6f702..32814d3 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -467,7 +467,9 @@
}
if c.vers >= VersionTLS12 {
certReq.hasSignatureAndHash = true
- certReq.signatureAndHashes = supportedClientCertSignatureAlgorithms
+ if !config.Bugs.NoSignatureAndHashes {
+ certReq.signatureAndHashes = config.signatureAndHashesForServer()
+ }
}
// An empty list of certificateAuthorities signals to
@@ -567,6 +569,9 @@
var signatureAndHash signatureAndHash
if certVerify.hasSignatureAndHash {
signatureAndHash = certVerify.signatureAndHash
+ if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) {
+ return errors.New("tls: unsupported hash function for client certificate")
+ }
} else {
// Before TLS 1.2 the signature algorithm was implicit
// from the key type, and only one hash per signature
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go
index 47f34cb..d59168f 100644
--- a/ssl/test/runner/key_agreement.go
+++ b/ssl/test/runner/key_agreement.go
@@ -12,7 +12,6 @@
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
- "crypto/sha256"
"crypto/x509"
"encoding/asn1"
"errors"
@@ -125,28 +124,20 @@
return md5sha1
}
-// sha256Hash implements TLS 1.2's hash function.
-func sha256Hash(slices [][]byte) []byte {
- h := sha256.New()
- for _, slice := range slices {
- h.Write(slice)
- }
- return h.Sum(nil)
-}
-
// hashForServerKeyExchange hashes the given slices and returns their digest
// and the identifier of the hash function used. The hashFunc argument is only
// used for >= TLS 1.2 and precisely identifies the hash function to use.
func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) {
if version >= VersionTLS12 {
- switch hashFunc {
- case hashSHA256:
- return sha256Hash(slices), crypto.SHA256, nil
- case hashSHA1:
- return sha1Hash(slices), crypto.SHA1, nil
- default:
- return nil, crypto.Hash(0), errors.New("tls: unknown hash function used by peer")
+ hash, err := lookupTLSHash(hashFunc)
+ if err != nil {
+ return nil, 0, err
}
+ h := hash.New()
+ for _, slice := range slices {
+ h.Write(slice)
+ }
+ return h.Sum(nil), hash, nil
}
if sigType == signatureECDSA {
return sha1Hash(slices), crypto.SHA1, nil
@@ -307,6 +298,10 @@
if len(sig) < 2 {
return errServerKeyExchange
}
+
+ if !isSupportedSignatureAndHash(signatureAndHash{ka.sigType, tls12HashId}, config.signatureAndHashesForClient()) {
+ return errors.New("tls: unsupported hash function for ServerKeyExchange")
+ }
}
sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) {
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go
index d45c080..75a8933 100644
--- a/ssl/test/runner/prf.go
+++ b/ssl/test/runner/prf.go
@@ -185,6 +185,27 @@
return
}
+// lookupTLSHash looks up the corresponding crypto.Hash for a given
+// TLS hash identifier.
+func lookupTLSHash(hash uint8) (crypto.Hash, error) {
+ switch hash {
+ case hashMD5:
+ return crypto.MD5, nil
+ case hashSHA1:
+ return crypto.SHA1, nil
+ case hashSHA224:
+ return crypto.SHA224, nil
+ case hashSHA256:
+ return crypto.SHA256, nil
+ case hashSHA384:
+ return crypto.SHA384, nil
+ case hashSHA512:
+ return crypto.SHA512, nil
+ default:
+ return 0, errors.New("tls: unsupported hash algorithm")
+ }
+}
+
func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
if version >= VersionTLS12 {
newHash := sha256.New
@@ -331,11 +352,13 @@
return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), crypto.MD5SHA1, nil
}
if h.version >= VersionTLS12 {
- if signatureAndHash.hash != hashSHA256 {
- return nil, 0, errors.New("tls: unsupported hash function for client certificate")
+ hashAlg, err := lookupTLSHash(signatureAndHash.hash)
+ if err != nil {
+ return nil, 0, err
}
- digest := sha256.Sum256(h.buffer)
- return digest[:], crypto.SHA256, nil
+ hash := hashAlg.New()
+ hash.Write(h.buffer)
+ return hash.Sum(nil), hashAlg, nil
}
if signatureAndHash.signature == signatureECDSA {
return h.server.Sum(nil), crypto.SHA1, nil
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 8c661a6..dca0479 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2030,6 +2030,114 @@
})
}
+var testHashes = []struct {
+ name string
+ id uint8
+}{
+ {"SHA1", hashSHA1},
+ {"SHA224", hashSHA224},
+ {"SHA256", hashSHA256},
+ {"SHA384", hashSHA384},
+ {"SHA512", hashSHA512},
+}
+
+func addSigningHashTests() {
+ // Make sure each hash works. Include some fake hashes in the list and
+ // ensure they're ignored.
+ for _, hash := range testHashes {
+ testCases = append(testCases, testCase{
+ name: "SigningHash-ClientAuth-" + hash.name,
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, 42},
+ {signatureRSA, hash.id},
+ {signatureRSA, 255},
+ },
+ },
+ flags: []string{
+ "-cert-file", rsaCertificateFile,
+ "-key-file", rsaKeyFile,
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SigningHash-ServerKeyExchange-Sign-" + hash.name,
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, 42},
+ {signatureRSA, hash.id},
+ {signatureRSA, 255},
+ },
+ },
+ })
+ }
+
+ // Test that hash resolution takes the signature type into account.
+ testCases = append(testCases, testCase{
+ name: "SigningHash-ClientAuth-SignatureType",
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ SignatureAndHashes: []signatureAndHash{
+ {signatureECDSA, hashSHA512},
+ {signatureRSA, hashSHA384},
+ {signatureECDSA, hashSHA1},
+ },
+ },
+ flags: []string{
+ "-cert-file", rsaCertificateFile,
+ "-key-file", rsaKeyFile,
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SigningHash-ServerKeyExchange-SignatureType",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ SignatureAndHashes: []signatureAndHash{
+ {signatureECDSA, hashSHA512},
+ {signatureRSA, hashSHA384},
+ {signatureECDSA, hashSHA1},
+ },
+ },
+ })
+
+ // Test that, if the list is missing, the peer falls back to SHA-1.
+ testCases = append(testCases, testCase{
+ name: "SigningHash-ClientAuth-Fallback",
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, hashSHA1},
+ },
+ Bugs: ProtocolBugs{
+ NoSignatureAndHashes: true,
+ },
+ },
+ flags: []string{
+ "-cert-file", rsaCertificateFile,
+ "-key-file", rsaKeyFile,
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SigningHash-ServerKeyExchange-Fallback",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, hashSHA1},
+ },
+ Bugs: ProtocolBugs{
+ NoSignatureAndHashes: true,
+ },
+ },
+ })
+}
+
func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
defer wg.Done()
@@ -2088,6 +2196,7 @@
addExtendedMasterSecretTests()
addRenegotiationTests()
addDTLSReplayTests()
+ addSigningHashTests()
for _, async := range []bool{false, true} {
for _, splitHandshake := range []bool{false, true} {
for _, protocol := range []protocol{tls, dtls} {