Adding more options for signing digest fallback.

Allow configuring digest preferences for the private key. Some
smartcards have limited support for signing digests, notably Windows
CAPI keys and old Estonian smartcards. Chromium used the supports_digest
hook in SSL_PRIVATE_KEY_METHOD to limit such keys to SHA1. However,
detecting those keys was a heuristic, so some SHA256-capable keys
authenticating to SHA256-only servers regressed in the switch to
BoringSSL. Replace this mechanism with an API to configure digest
preference order. This way heuristically-detected SHA1-only keys may be
configured by Chromium as SHA1-preferring rather than SHA1-requiring.

In doing so, clean up the shared_sigalgs machinery somewhat.

BUG=468076

Change-Id: I996a2df213ae4d8b4062f0ab85b15262ca26f3c6
Reviewed-on: https://boringssl-review.googlesource.com/5755
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 14f9383..be40cf2 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -774,6 +774,14 @@
                                              const uint8_t *response,
                                              size_t response_len);
 
+/* SSL_set_private_key_digest_prefs copies |num_digests| NIDs from |digest_nids|
+ * into |ssl|. These digests will be used, in decreasing order of preference,
+ * when signing with |ssl|'s private key. It returns one on success and zero on
+ * error. */
+OPENSSL_EXPORT int SSL_set_private_key_digest_prefs(SSL *ssl,
+                                                    const int *digest_nids,
+                                                    size_t num_digests);
+
 
 /* Certificate and private key convenience functions. */
 
@@ -852,10 +860,6 @@
    * key used by |ssl|. */
   int (*type)(SSL *ssl);
 
-  /* supports_digest returns one if the key used by |ssl| supports signing
-   * digests of type |md| and zero otherwise. */
-  int (*supports_digest)(SSL *ssl, const EVP_MD *md);
-
   /* max_signature_len returns the maximum length of a signature signed by the
    * key used by |ssl|. This must be a constant value for a given |ssl|. */
   size_t (*max_signature_len)(SSL *ssl);
@@ -890,7 +894,7 @@
                                                  size_t *out_len, size_t max_out);
 } SSL_PRIVATE_KEY_METHOD;
 
-/* SSL_use_private_key_method configures a custom private key on |ssl|.
+/* SSL_set_private_key_method configures a custom private key on |ssl|.
  * |key_method| must remain valid for the lifetime of |ssl|. */
 OPENSSL_EXPORT void SSL_set_private_key_method(
     SSL *ssl, const SSL_PRIVATE_KEY_METHOD *key_method);
diff --git a/ssl/internal.h b/ssl/internal.h
index 8f477bc..8655bca 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -455,8 +455,6 @@
 
 int ssl_private_key_type(SSL *ssl);
 
-int ssl_private_key_supports_digest(SSL *ssl, const EVP_MD *md);
-
 size_t ssl_private_key_max_signature_len(SSL *ssl);
 
 enum ssl_private_key_result_t ssl_private_key_sign(
@@ -757,17 +755,17 @@
    * |SSL_CTX_set_tmp_ecdh_callback|. */
   EC_KEY *(*ecdh_tmp_cb)(SSL *ssl, int is_export, int keysize);
 
-  /* signature algorithms peer reports: e.g. supported signature
-   * algorithms extension for server or as part of a certificate
-   * request for client. */
-  uint8_t *peer_sigalgs;
-  /* Size of above array */
+  /* peer_sigalgs are the algorithm/hash pairs that the peer supports. These
+   * are taken from the contents of signature algorithms extension for a server
+   * or from the CertificateRequest for a client. */
+  TLS_SIGALGS *peer_sigalgs;
+  /* peer_sigalgslen is the number of entries in |peer_sigalgs|. */
   size_t peer_sigalgslen;
 
-  /* Signature algorithms shared by client and server: cached
-   * because these are used most often. */
-  TLS_SIGALGS *shared_sigalgs;
-  size_t shared_sigalgslen;
+  /* digest_nids, if non-NULL, is the set of digests supported by |privatekey|
+   * in decreasing order of preference. */
+  int *digest_nids;
+  size_t num_digest_nids;
 
   /* Certificate setup callback: if set is called whenever a
    * certificate may be required (client or server). the callback
@@ -1296,7 +1294,7 @@
 uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version);
 
 uint32_t ssl_get_algorithm_prf(SSL *s);
-int tls1_process_sigalgs(SSL *s, const CBS *sigalgs);
+int tls1_parse_peer_sigalgs(SSL *s, const CBS *sigalgs);
 
 /* tls1_choose_signing_digest returns a digest for use with |ssl|'s private key
  * based on the peer's preferences the digests supported. */
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index f2539ee..559db72 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1363,17 +1363,12 @@
 
   if (SSL_USE_SIGALGS(s)) {
     CBS supported_signature_algorithms;
-    if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms)) {
+    if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) ||
+        !tls1_parse_peer_sigalgs(s, &supported_signature_algorithms)) {
       ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       goto err;
     }
-
-    if (!tls1_process_sigalgs(s, &supported_signature_algorithms)) {
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SIGNATURE_ALGORITHMS_ERROR);
-      goto err;
-    }
   }
 
   /* get the CA RDNs */
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 1506f63..a9e8d77 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -238,7 +238,7 @@
 
   ssl_cert_clear_certs(c);
   OPENSSL_free(c->peer_sigalgs);
-  OPENSSL_free(c->shared_sigalgs);
+  OPENSSL_free(c->digest_nids);
 
   OPENSSL_free(c);
 }
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index fb6fdf6..ccd3858 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -310,6 +310,21 @@
   ssl->cert->key_method = key_method;
 }
 
+int SSL_set_private_key_digest_prefs(SSL *ssl, const int *digest_nids,
+                                     size_t num_digests) {
+  OPENSSL_free(ssl->cert->digest_nids);
+
+  ssl->cert->num_digest_nids = 0;
+  ssl->cert->digest_nids = BUF_memdup(digest_nids, num_digests*sizeof(int));
+  if (ssl->cert->digest_nids == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  ssl->cert->num_digest_nids = num_digests;
+  return 1;
+}
+
 int ssl_has_private_key(SSL *ssl) {
   return ssl->cert->privatekey != NULL || ssl->cert->key_method != NULL;
 }
@@ -321,13 +336,6 @@
   return EVP_PKEY_id(ssl->cert->privatekey);
 }
 
-int ssl_private_key_supports_digest(SSL *ssl, const EVP_MD *md) {
-  if (ssl->cert->key_method != NULL) {
-    return ssl->cert->key_method->supports_digest(ssl, md);
-  }
-  return EVP_PKEY_supports_digest(ssl->cert->privatekey, md);
-}
-
 size_t ssl_private_key_max_signature_len(SSL *ssl) {
   if (ssl->cert->key_method != NULL) {
     return ssl->cert->key_method->max_signature_len(ssl);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 747b7f6..caa0cc4 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1266,33 +1266,15 @@
   ssl->cert->peer_sigalgs = NULL;
   ssl->cert->peer_sigalgslen = 0;
 
-  OPENSSL_free(ssl->cert->shared_sigalgs);
-  ssl->cert->shared_sigalgs = NULL;
-  ssl->cert->shared_sigalgslen = 0;
-
   if (contents == NULL) {
     return 1;
   }
 
   CBS supported_signature_algorithms;
   if (!CBS_get_u16_length_prefixed(contents, &supported_signature_algorithms) ||
-      CBS_len(contents) != 0) {
-    return 0;
-  }
-
-  /* Ensure the signature algorithms are non-empty. It contains a list of
-   * SignatureAndHashAlgorithms which are two bytes each. */
-  if (CBS_len(&supported_signature_algorithms) == 0 ||
-      (CBS_len(&supported_signature_algorithms) % 2) != 0 ||
-      !tls1_process_sigalgs(ssl, &supported_signature_algorithms)) {
-    return 0;
-  }
-
-  /* It's a fatal error if the signature_algorithms extension is received and
-   * there are no shared algorithms. */
-  if (ssl->cert->peer_sigalgs && !ssl->cert->shared_sigalgs) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_SIGATURE_ALGORITHMS);
-    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+      CBS_len(contents) != 0 ||
+      CBS_len(&supported_signature_algorithms) == 0 ||
+      !tls1_parse_peer_sigalgs(ssl, &supported_signature_algorithms)) {
     return 0;
   }
 
@@ -2852,93 +2834,52 @@
   }
 }
 
-/* Given preference and allowed sigalgs set shared sigalgs */
-static int tls12_do_shared_sigalgs(TLS_SIGALGS *shsig, const uint8_t *pref,
-                                   size_t preflen, const uint8_t *allow,
-                                   size_t allowlen) {
-  const uint8_t *ptmp, *atmp;
-  size_t i, j, nmatch = 0;
+OPENSSL_COMPILE_ASSERT(sizeof(TLS_SIGALGS) == 2,
+    sizeof_tls_sigalgs_is_not_two);
 
-  for (i = 0, ptmp = pref; i < preflen; i += 2, ptmp += 2) {
-    /* Skip disabled hashes or signature algorithms */
-    if (tls12_get_hash(ptmp[0]) == NULL ||
-        tls12_get_pkey_type(ptmp[1]) == -1) {
-      continue;
-    }
-
-    for (j = 0, atmp = allow; j < allowlen; j += 2, atmp += 2) {
-      if (ptmp[0] == atmp[0] && ptmp[1] == atmp[1]) {
-        nmatch++;
-        if (shsig) {
-          shsig->rhash = ptmp[0];
-          shsig->rsign = ptmp[1];
-          shsig++;
-        }
-
-        break;
-      }
-    }
-  }
-
-  return nmatch;
-}
-
-/* Set shared signature algorithms for SSL structures */
-static int tls1_set_shared_sigalgs(SSL *s) {
-  const uint8_t *pref, *allow, *conf;
-  size_t preflen, allowlen, conflen;
-  size_t nmatch;
-  TLS_SIGALGS *salgs = NULL;
-  CERT *c = s->cert;
-
-  OPENSSL_free(c->shared_sigalgs);
-  c->shared_sigalgs = NULL;
-  c->shared_sigalgslen = 0;
-
-  conflen = tls12_get_psigalgs(s, &conf);
-
-  if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
-    pref = conf;
-    preflen = conflen;
-    allow = c->peer_sigalgs;
-    allowlen = c->peer_sigalgslen;
-  } else {
-    allow = conf;
-    allowlen = conflen;
-    pref = c->peer_sigalgs;
-    preflen = c->peer_sigalgslen;
-  }
-
-  nmatch = tls12_do_shared_sigalgs(NULL, pref, preflen, allow, allowlen);
-  if (!nmatch) {
-    return 1;
-  }
-
-  salgs = OPENSSL_malloc(nmatch * sizeof(TLS_SIGALGS));
-  if (!salgs) {
-    return 0;
-  }
-
-  nmatch = tls12_do_shared_sigalgs(salgs, pref, preflen, allow, allowlen);
-  c->shared_sigalgs = salgs;
-  c->shared_sigalgslen = nmatch;
-  return 1;
-}
-
-/* Set preferred digest for each key type */
-int tls1_process_sigalgs(SSL *s, const CBS *sigalgs) {
-  CERT *c = s->cert;
-
+int tls1_parse_peer_sigalgs(SSL *ssl, const CBS *in_sigalgs) {
   /* Extension ignored for inappropriate versions */
-  if (!SSL_USE_SIGALGS(s)) {
+  if (!SSL_USE_SIGALGS(ssl)) {
     return 1;
   }
 
-  if (CBS_len(sigalgs) % 2 != 0 ||
-      !CBS_stow(sigalgs, &c->peer_sigalgs, &c->peer_sigalgslen) ||
-      !tls1_set_shared_sigalgs(s)) {
+  CERT *const cert = ssl->cert;
+  OPENSSL_free(cert->peer_sigalgs);
+  cert->peer_sigalgs = NULL;
+  cert->peer_sigalgslen = 0;
+
+  size_t num_sigalgs = CBS_len(in_sigalgs);
+
+  if (num_sigalgs % 2 != 0) {
     return 0;
   }
+  num_sigalgs /= 2;
+
+  /* supported_signature_algorithms in the certificate request is
+   * allowed to be empty. */
+  if (num_sigalgs == 0) {
+    return 1;
+  }
+
+  /* This multiplication doesn't overflow because sizeof(TLS_SIGALGS) is two
+   * (statically asserted above) and we just divided |num_sigalgs| by two. */
+  cert->peer_sigalgs = OPENSSL_malloc(num_sigalgs * sizeof(TLS_SIGALGS));
+  if (cert->peer_sigalgs == NULL) {
+    return 0;
+  }
+  cert->peer_sigalgslen = num_sigalgs;
+
+  CBS sigalgs;
+  CBS_init(&sigalgs, CBS_data(in_sigalgs), CBS_len(in_sigalgs));
+
+  size_t i;
+  for (i = 0; i < num_sigalgs; i++) {
+    TLS_SIGALGS *const sigalg = &cert->peer_sigalgs[i];
+    if (!CBS_get_u8(&sigalgs, &sigalg->rhash) ||
+        !CBS_get_u8(&sigalgs, &sigalg->rsign)) {
+      return 0;
+    }
+  }
 
   return 1;
 }
@@ -2946,17 +2887,31 @@
 const EVP_MD *tls1_choose_signing_digest(SSL *ssl) {
   CERT *cert = ssl->cert;
   int type = ssl_private_key_type(ssl);
-  size_t i;
+  size_t i, j;
 
-  /* Select the first shared digest supported by our key. */
-  for (i = 0; i < cert->shared_sigalgslen; i++) {
-    const EVP_MD *md = tls12_get_hash(cert->shared_sigalgs[i].rhash);
-    if (md == NULL ||
-        tls12_get_pkey_type(cert->shared_sigalgs[i].rsign) != type ||
-        !ssl_private_key_supports_digest(ssl, md)) {
-      continue;
+  static const int kDefaultDigestList[] = {NID_sha256, NID_sha384, NID_sha512,
+                                           NID_sha224, NID_sha1};
+
+  const int *digest_nids = kDefaultDigestList;
+  size_t num_digest_nids =
+      sizeof(kDefaultDigestList) / sizeof(kDefaultDigestList[0]);
+  if (cert->digest_nids != NULL) {
+    digest_nids = cert->digest_nids;
+    num_digest_nids = cert->num_digest_nids;
+  }
+
+  for (i = 0; i < num_digest_nids; i++) {
+    const int digest_nid = digest_nids[i];
+    for (j = 0; j < cert->peer_sigalgslen; j++) {
+      const EVP_MD *md = tls12_get_hash(cert->peer_sigalgs[j].rhash);
+      if (md == NULL ||
+          digest_nid != EVP_MD_type(md) ||
+          tls12_get_pkey_type(cert->peer_sigalgs[j].rsign) != type) {
+        continue;
+      }
+
+      return md;
     }
-    return md;
   }
 
   /* If no suitable digest may be found, default to SHA-1. */
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index c30eb55..9e30bfc 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -45,6 +45,7 @@
 #include <openssl/ssl.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "../../crypto/test/scoped_types.h"
@@ -143,10 +144,6 @@
   return EVP_PKEY_id(GetTestState(ssl)->private_key.get());
 }
 
-static int AsyncPrivateKeySupportsDigest(SSL *ssl, const EVP_MD *md) {
-  return EVP_PKEY_supports_digest(GetTestState(ssl)->private_key.get(), md);
-}
-
 static size_t AsyncPrivateKeyMaxSignatureLen(SSL *ssl) {
   return EVP_PKEY_size(GetTestState(ssl)->private_key.get());
 }
@@ -214,15 +211,44 @@
 
 static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = {
     AsyncPrivateKeyType,
-    AsyncPrivateKeySupportsDigest,
     AsyncPrivateKeyMaxSignatureLen,
     AsyncPrivateKeySign,
     AsyncPrivateKeySignComplete,
 };
 
+template<typename T>
+struct Free {
+  void operator()(T *buf) {
+    free(buf);
+  }
+};
+
 static bool InstallCertificate(SSL *ssl) {
   const TestConfig *config = GetConfigPtr(ssl);
   TestState *test_state = GetTestState(ssl);
+
+  if (!config->digest_prefs.empty()) {
+    std::unique_ptr<char, Free<char>> digest_prefs(
+        strdup(config->digest_prefs.c_str()));
+    char *tokptr = nullptr;
+    std::vector<int> digest_list;
+
+    for (;;) {
+      char *token = strtok_r(digest_list.empty() ? digest_prefs.get() : nullptr,
+                             ",", &tokptr);
+      if (token == nullptr) {
+        break;
+      }
+
+      digest_list.push_back(EVP_MD_type(EVP_get_digestbyname(token)));
+    }
+
+    if (!SSL_set_private_key_digest_prefs(ssl, digest_list.data(),
+                                          digest_list.size())) {
+      return false;
+    }
+  }
+
   if (!config->key_file.empty()) {
     if (config->use_async_private_key) {
       test_state->private_key = LoadPrivateKey(config->key_file.c_str());
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 039d164..77be9f6 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -191,6 +191,7 @@
 	SRTPProtectionProfile      uint16                // the negotiated DTLS-SRTP protection profile
 	TLSUnique                  []byte                // the tls-unique channel binding
 	SCTList                    []byte                // signed certificate timestamp list
+	ClientCertSignatureHash    uint8                 // TLS id of the hash used by the client to sign the handshake
 }
 
 // ClientAuthType declares the policy the server will follow for
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index ed016e0..39bdfda 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -50,6 +50,11 @@
 	// firstFinished contains the first Finished hash sent during the
 	// handshake. This is the "tls-unique" channel binding value.
 	firstFinished [12]byte
+	// clientCertSignatureHash contains the TLS hash id for the hash that
+	// was used by the client to sign the handshake with a client
+	// certificate. This is only set by a server and is zero if no client
+	// certificates were used.
+	clientCertSignatureHash uint8
 
 	clientRandom, serverRandom [32]byte
 	masterSecret               [48]byte
@@ -1349,6 +1354,7 @@
 		state.SRTPProtectionProfile = c.srtpProtectionProfile
 		state.TLSUnique = c.firstFinished[:]
 		state.SCTList = c.sctList
+		state.ClientCertSignatureHash = c.clientCertSignatureHash
 	}
 
 	return state
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index d23bf71..068dff9 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -685,6 +685,7 @@
 			if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) {
 				return errors.New("tls: unsupported hash function for client certificate")
 			}
+			c.clientCertSignatureHash = signatureAndHash.hash
 		} 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/runner.go b/ssl/test/runner/runner.go
index 819f1ea..269a955 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -159,11 +159,17 @@
 	expectedOCSPResponse []uint8
 	// expectedSCTList, if not nil, is the expected SCT list to be received.
 	expectedSCTList []uint8
+	// expectedClientCertSignatureHash, if not zero, is the TLS id of the
+	// hash function that the client should have used when signing the
+	// handshake with a client certificate.
+	expectedClientCertSignatureHash uint8
 	// messageLen is the length, in bytes, of the test message that will be
 	// sent.
 	messageLen int
 	// messageCount is the number of test messages that will be sent.
 	messageCount int
+	// digestPrefs is the list of digest preferences from the client.
+	digestPrefs string
 	// certFile is the path to the certificate to use for the server.
 	certFile string
 	// keyFile is the path to the private key to use for the server.
@@ -340,6 +346,10 @@
 		return fmt.Errorf("SCT list mismatch")
 	}
 
+	if expected := test.expectedClientCertSignatureHash; expected != 0 && expected != connState.ClientCertSignatureHash {
+		return fmt.Errorf("expected client to sign handshake with hash %d, but got %d", expected, connState.ClientCertSignatureHash)
+	}
+
 	if test.exportKeyingMaterial > 0 {
 		actual := make([]byte, test.exportKeyingMaterial)
 		if _, err := io.ReadFull(tlsConn, actual); err != nil {
@@ -525,6 +535,10 @@
 		panic("expectResumeRejected without resumeSession in " + test.name)
 	}
 
+	if test.testType != clientTest && test.expectedClientCertSignatureHash != 0 {
+		panic("expectedClientCertSignatureHash non-zero with serverTest in " + test.name)
+	}
+
 	listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}})
 	if err != nil {
 		panic(err)
@@ -554,6 +568,11 @@
 		}
 	}
 
+	if test.digestPrefs != "" {
+		flags = append(flags, "-digest-prefs")
+		flags = append(flags, test.digestPrefs)
+	}
+
 	if test.protocol == dtls {
 		flags = append(flags, "-dtls")
 	}
@@ -3903,6 +3922,73 @@
 		shouldFail:    true,
 		expectedError: ":WRONG_SIGNATURE_TYPE:",
 	})
+
+	// Test that the agreed upon digest respects the client preferences and
+	// the server digests.
+	testCases = append(testCases, testCase{
+		name: "Agree-Digest-Fallback",
+		config: Config{
+			ClientAuth: RequireAnyClientCert,
+			SignatureAndHashes: []signatureAndHash{
+				{signatureRSA, hashSHA512},
+				{signatureRSA, hashSHA1},
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+		},
+		digestPrefs:                     "SHA256",
+		expectedClientCertSignatureHash: hashSHA1,
+	})
+	testCases = append(testCases, testCase{
+		name: "Agree-Digest-SHA256",
+		config: Config{
+			ClientAuth: RequireAnyClientCert,
+			SignatureAndHashes: []signatureAndHash{
+				{signatureRSA, hashSHA1},
+				{signatureRSA, hashSHA256},
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+		},
+		digestPrefs:                     "SHA256,SHA1",
+		expectedClientCertSignatureHash: hashSHA256,
+	})
+	testCases = append(testCases, testCase{
+		name: "Agree-Digest-SHA1",
+		config: Config{
+			ClientAuth: RequireAnyClientCert,
+			SignatureAndHashes: []signatureAndHash{
+				{signatureRSA, hashSHA1},
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+		},
+		digestPrefs:                     "SHA512,SHA256,SHA1",
+		expectedClientCertSignatureHash: hashSHA1,
+	})
+	testCases = append(testCases, testCase{
+		name: "Agree-Digest-Default",
+		config: Config{
+			ClientAuth: RequireAnyClientCert,
+			SignatureAndHashes: []signatureAndHash{
+				{signatureRSA, hashSHA256},
+				{signatureECDSA, hashSHA256},
+				{signatureRSA, hashSHA1},
+				{signatureECDSA, hashSHA1},
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+		},
+		expectedClientCertSignatureHash: hashSHA256,
+	})
 }
 
 // timeouts is the retransmit schedule for BoringSSL. It doubles and
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index fe6dc93..1c42b2e 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -99,6 +99,7 @@
 };
 
 const Flag<std::string> kStringFlags[] = {
+  { "-digest-prefs", &TestConfig::digest_prefs },
   { "-key-file", &TestConfig::key_file },
   { "-cert-file", &TestConfig::cert_file },
   { "-expect-server-name", &TestConfig::expected_server_name },
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 76ee312..9dea8e9 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -24,6 +24,7 @@
   bool is_dtls = false;
   bool resume = false;
   bool fallback_scsv = false;
+  std::string digest_prefs;
   std::string key_file;
   std::string cert_file;
   std::string expected_server_name;