Use handshake parameters to decide if cert/key are available

Whether the host has a valid certificate or private key may depend on
the handshake parameters and not just its configuration. For example,
negotiating the delegated credential extension (see
https://tools.ietf.org/html/draft-ietf-tls-subcerts) requires an
alternate private key for the handshake.

Change-Id: I11cea1d11e731aa4018d980c010b8d8ebaa64c31
Reviewed-on: https://boringssl-review.googlesource.com/c/33664
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 0274dc2..4d57ae5 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -1219,7 +1219,7 @@
     }
   }
 
-  if (!ssl_has_certificate(hs->config)) {
+  if (!ssl_has_certificate(hs)) {
     // Without a client certificate, the handshake buffer may be released.
     hs->transcript.FreeBuffer();
   }
@@ -1386,12 +1386,12 @@
 static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
-  if (!hs->cert_request || !ssl_has_certificate(hs->config)) {
+  if (!hs->cert_request || !ssl_has_certificate(hs)) {
     hs->state = state_send_client_finished;
     return ssl_hs_ok;
   }
 
-  assert(ssl_has_private_key(hs->config));
+  assert(ssl_has_private_key(hs));
   ScopedCBB cbb;
   CBB body, child;
   if (!ssl->method->init_message(ssl, cbb.get(), &body,
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 1572096..15ba2b0 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -303,7 +303,7 @@
   uint32_t mask_k = 0;
   uint32_t mask_a = 0;
 
-  if (ssl_has_certificate(hs->config)) {
+  if (ssl_has_certificate(hs)) {
     mask_a |= ssl_cipher_auth_mask_for_key(hs->local_pubkey.get());
     if (EVP_PKEY_id(hs->local_pubkey.get()) == EVP_PKEY_RSA) {
       mask_k |= SSL_kRSA;
@@ -868,7 +868,7 @@
   ScopedCBB cbb;
 
   if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
-    if (!ssl_has_certificate(hs->config)) {
+    if (!ssl_has_certificate(hs)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
       return ssl_hs_error;
     }
@@ -974,7 +974,7 @@
 
   // Add a signature.
   if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
-    if (!ssl_has_private_key(hs->config)) {
+    if (!ssl_has_private_key(hs)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
diff --git a/ssl/internal.h b/ssl/internal.h
index bc45b50..9846966 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -919,8 +919,8 @@
 
 // Private key operations.
 
-// ssl_has_private_key returns whether |cfg| has a private key configured.
-bool ssl_has_private_key(const SSL_CONFIG *cfg);
+// ssl_has_private_key returns whether |hs| has a private key configured.
+bool ssl_has_private_key(const SSL_HANDSHAKE *hs);
 
 // ssl_private_key_* perform the corresponding operation on
 // |SSL_PRIVATE_KEY_METHOD|. If there is a custom private key configured, they
@@ -1173,7 +1173,7 @@
 
 // ssl_has_certificate returns whether a certificate and private key are
 // configured.
-bool ssl_has_certificate(const SSL_CONFIG *cfg);
+bool ssl_has_certificate(const SSL_HANDSHAKE *hs);
 
 // ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
 // by a TLS Certificate message. On success, it advances |cbs| and returns
diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc
index 37d6501..9551810 100644
--- a/ssl/ssl_cert.cc
+++ b/ssl/ssl_cert.cc
@@ -324,10 +324,10 @@
   return true;
 }
 
-bool ssl_has_certificate(const SSL_CONFIG *cfg) {
-  return cfg->cert->chain != nullptr &&
-         sk_CRYPTO_BUFFER_value(cfg->cert->chain.get(), 0) != nullptr &&
-         ssl_has_private_key(cfg);
+bool ssl_has_certificate(const SSL_HANDSHAKE *hs) {
+  return hs->config->cert->chain != nullptr &&
+         sk_CRYPTO_BUFFER_value(hs->config->cert->chain.get(), 0) != nullptr &&
+         ssl_has_private_key(hs);
 }
 
 bool ssl_parse_cert_chain(uint8_t *out_alert,
@@ -395,7 +395,7 @@
 }
 
 bool ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb) {
-  if (!ssl_has_certificate(hs->config)) {
+  if (!ssl_has_certificate(hs)) {
     return CBB_add_u24(cbb, 0);
   }
 
@@ -728,7 +728,7 @@
 
 bool ssl_on_certificate_selected(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_has_certificate(hs->config)) {
+  if (!ssl_has_certificate(hs)) {
     // Nothing to do.
     return true;
   }
diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc
index e716c9a..16888b9 100644
--- a/ssl/ssl_privkey.cc
+++ b/ssl/ssl_privkey.cc
@@ -133,8 +133,9 @@
   return NULL;
 }
 
-bool ssl_has_private_key(const SSL_CONFIG *cfg) {
-  return cfg->cert->privatekey != nullptr || cfg->cert->key_method != nullptr;
+bool ssl_has_private_key(const SSL_HANDSHAKE *hs) {
+  return (hs->config->cert->privatekey != nullptr ||
+          hs->config->cert->key_method != nullptr);
 }
 
 static bool pkey_supports_algorithm(const SSL *ssl, EVP_PKEY *pkey,
diff --git a/ssl/ssl_x509.cc b/ssl/ssl_x509.cc
index eb3a38b..841482f 100644
--- a/ssl/ssl_x509.cc
+++ b/ssl/ssl_x509.cc
@@ -448,7 +448,7 @@
   // Only build a chain if there are no intermediates configured and the feature
   // isn't disabled.
   if ((hs->ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
-      !ssl_has_certificate(hs->config) || hs->config->cert->chain == NULL ||
+      !ssl_has_certificate(hs) || hs->config->cert->chain == NULL ||
       sk_CRYPTO_BUFFER_num(hs->config->cert->chain.get()) > 1) {
     return 1;
   }
@@ -1223,7 +1223,8 @@
     assert(ssl->config);
     return -1;
   }
-  if (ssl_has_certificate(ssl->config.get()) ||
+
+  if (ssl_has_certificate(ssl->s3->hs.get()) ||
       ssl->ctx->client_cert_cb == NULL) {
     return 1;
   }
diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc
index f6e359c..605942a 100644
--- a/ssl/tls13_both.cc
+++ b/ssl/tls13_both.cc
@@ -441,7 +441,7 @@
     return false;
   }
 
-  if (!ssl_has_certificate(hs->config)) {
+  if (!ssl_has_certificate(hs)) {
     return ssl_add_message_cbb(ssl, cbb.get());
   }
 
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index 0ab4021..e7d6dae 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -685,7 +685,7 @@
 
 static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs) {
   // Don't send CertificateVerify if there is no certificate.
-  if (!ssl_has_certificate(hs->config)) {
+  if (!ssl_has_certificate(hs)) {
     hs->tls13_state = state_complete_second_flight;
     return ssl_hs_ok;
   }
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index efdb602..562fecb 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -662,7 +662,7 @@
 
   // Send the server Certificate message, if necessary.
   if (!ssl->s3->session_reused) {
-    if (!ssl_has_certificate(hs->config)) {
+    if (!ssl_has_certificate(hs)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
       return ssl_hs_error;
     }