Choose matching credentials if we have ca_names

Mark credentials that are a candidate for issuer matching.
Match only credentials by name if they are a candidate.

This allows us to have credentials in the list that will
be a candidate even if they do not match a requested issuer.

Change-Id: If09b8861f58a5d7d2107eb991f5db7892c23f7e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/71612
Commit-Queue: Bob Beck <bbe@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h
index dc539a9..092b298 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1974,6 +1974,11 @@
 
   CRYPTO_EX_DATA ex_data;
 
+  // must_match_issuer is a flag indicating that this credential should be
+  // considered only when it matches a peer request for a particular issuer via
+  // a negotiation mechanism (such as the certificate_authorities extension).
+  bool must_match_issuer = false;
+
  private:
   friend RefCounted;
   ~ssl_credential_st();
@@ -1989,6 +1994,10 @@
 // The pointers in the result are only valid until |hs| is next mutated.
 bool ssl_get_credential_list(SSL_HANDSHAKE *hs, Array<SSL_CREDENTIAL *> *out);
 
+// ssl_credential_matches_requested_issuers returns true if |cred| is a
+// usable match for any requested issuers in |hs|.
+bool ssl_credential_matches_requested_issuers(SSL_HANDSHAKE *hs,
+                                              const SSL_CREDENTIAL *cred);
 
 // Handshake functions.
 
diff --git a/ssl/ssl_credential.cc b/ssl/ssl_credential.cc
index 357d8f8..5f13ea5 100644
--- a/ssl/ssl_credential.cc
+++ b/ssl/ssl_credential.cc
@@ -61,6 +61,31 @@
   return true;
 }
 
+bool ssl_credential_matches_requested_issuers(SSL_HANDSHAKE *hs,
+                                              const SSL_CREDENTIAL *cred) {
+  if (cred->must_match_issuer) {
+    // If we have names sent by the CA extension, and this
+    // credential matches it, it is good.
+    if (hs->ca_names != nullptr) {
+      for (const CRYPTO_BUFFER *ca_name : hs->ca_names.get()) {
+        if (cred->ChainContainsIssuer(MakeConstSpan(
+                CRYPTO_BUFFER_data(ca_name), CRYPTO_BUFFER_len(ca_name)))) {
+          return true;
+        }
+      }
+    }
+    // TODO(bbe): Other forms of issuer matching go here.
+
+    // If this cred must match a requested issuer and we
+    // get here, we should not use it.
+    return false;
+  }
+
+  // This cred does not need to match a requested issuer, so
+  // it is good to use without a match.
+  return true;
+}
+
 BSSL_NAMESPACE_END
 
 using namespace bssl;
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index fbe94ad..b747215 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -851,7 +851,12 @@
   }
 
   // All currently supported credentials require a signature.
-  return tls1_choose_signature_algorithm(hs, cred, out_sigalg);
+  if (!tls1_choose_signature_algorithm(hs, cred, out_sigalg)) {
+    return false;
+  }
+  // Use this credential if it either matches a requested issuer,
+  // or does not require issuer matching.
+  return ssl_credential_matches_requested_issuers(hs, cred);
 }
 
 static enum ssl_hs_wait_t do_send_client_certificate(SSL_HANDSHAKE *hs) {
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index fcee108..d3cb492 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -226,7 +226,12 @@
   // All currently supported credentials require a signature. If |cred| is a
   // delegated credential, this also checks that the peer supports delegated
   // credentials and matched |dc_cert_verify_algorithm|.
-  return tls1_choose_signature_algorithm(hs, cred, out_sigalg);
+  if (!tls1_choose_signature_algorithm(hs, cred, out_sigalg)) {
+    return false;
+  }
+  // Use this credential if it either matches a requested issuer,
+  // or does not require issuer matching.
+  return ssl_credential_matches_requested_issuers(hs, cred);
 }
 
 static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) {