ssl: Allow inspection of SSL_CREDENTIAL completion status

Before adding the credential to the connection or context, it would be
nice if a builder pattern could inspect if the state of the credential
is operationally ready.

Signed-off-by: Xiangfei Ding <xfding@google.com>
Change-Id: I1a10e0a01d1f2a851d31318730f545006a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/90087
Reviewed-by: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Lily Chen <chlily@google.com>
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h
index 95bf738..8a14395 100644
--- a/include/openssl/prefix_symbols.h
+++ b/include/openssl/prefix_symbols.h
@@ -1836,6 +1836,7 @@
 #pragma redefine_extname SSL_CREDENTIAL_free BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_free))
 #pragma redefine_extname SSL_CREDENTIAL_get_ex_data BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_get_ex_data))
 #pragma redefine_extname SSL_CREDENTIAL_get_ex_new_index BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_get_ex_new_index))
+#pragma redefine_extname SSL_CREDENTIAL_is_complete BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_is_complete))
 #pragma redefine_extname SSL_CREDENTIAL_new_delegated BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_new_delegated))
 #pragma redefine_extname SSL_CREDENTIAL_new_spake2plusv1_client BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_new_spake2plusv1_client))
 #pragma redefine_extname SSL_CREDENTIAL_new_spake2plusv1_server BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_new_spake2plusv1_server))
@@ -4897,6 +4898,7 @@
 #define SSL_CREDENTIAL_free BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_free)
 #define SSL_CREDENTIAL_get_ex_data BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_get_ex_data)
 #define SSL_CREDENTIAL_get_ex_new_index BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_get_ex_new_index)
+#define SSL_CREDENTIAL_is_complete BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_is_complete)
 #define SSL_CREDENTIAL_new_delegated BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_new_delegated)
 #define SSL_CREDENTIAL_new_spake2plusv1_client BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_new_spake2plusv1_client)
 #define SSL_CREDENTIAL_new_spake2plusv1_server BORINGSSL_ADD_PREFIX(SSL_CREDENTIAL_new_spake2plusv1_server)
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 36c3e3a..430273e 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -17,7 +17,7 @@
 #ifndef OPENSSL_HEADER_SSL_H
 #define OPENSSL_HEADER_SSL_H
 
-#include <openssl/base.h>   // IWYU pragma: export
+#include <openssl/base.h>  // IWYU pragma: export
 
 #include <openssl/bio.h>
 #include <openssl/buf.h>
@@ -802,6 +802,14 @@
 // zero, all data referenced by |cred| and |cred| itself are released.
 OPENSSL_EXPORT void SSL_CREDENTIAL_free(SSL_CREDENTIAL *cred);
 
+// SSL_CREDENTIAL_is_complete returns one if the returns one if |cred| has all
+// required properties configured, and zero otherwise.
+//
+// This includes checks on whether the public key is present in a X.509
+// credential and whether a private key or private key method has been set up
+// for such.
+OPENSSL_EXPORT int SSL_CREDENTIAL_is_complete(const SSL_CREDENTIAL *cred);
+
 // SSL_CREDENTIAL_set1_private_key sets |cred|'s private key to |cred|. It
 // returns one on success and zero on failure.
 OPENSSL_EXPORT int SSL_CREDENTIAL_set1_private_key(SSL_CREDENTIAL *cred,
diff --git a/ssl/ssl_credential.cc b/ssl/ssl_credential.cc
index 5206fde..67fa8c3 100644
--- a/ssl/ssl_credential.cc
+++ b/ssl/ssl_credential.cc
@@ -342,6 +342,10 @@
   }
 }
 
+int SSL_CREDENTIAL_is_complete(const SSL_CREDENTIAL *cred) {
+  return cred->IsComplete();
+}
+
 int SSL_CREDENTIAL_set1_private_key(SSL_CREDENTIAL *cred, EVP_PKEY *key) {
   if (!cred->UsesPrivateKey()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 2c8b000..008fd86 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -5546,6 +5546,8 @@
   ASSERT_TRUE(cred);
   bssl::UniquePtr<SSL_CREDENTIAL> cred2(SSL_CREDENTIAL_new_x509());
   ASSERT_TRUE(cred2);
+  ASSERT_FALSE(SSL_CREDENTIAL_is_complete(cred.get()));
+  ASSERT_FALSE(SSL_CREDENTIAL_is_complete(cred2.get()));
 
   SSL_CTX_set_custom_verify(client_ctx.get(), SSL_VERIFY_PEER,
                             AcceptAnyCertificate);
@@ -5579,11 +5581,15 @@
 
   ASSERT_TRUE(SSL_CREDENTIAL_set1_cert_chain(cred2.get(), test_chain.data(),
                                              test_chain.size()));
+  ASSERT_FALSE(SSL_CREDENTIAL_is_complete(cred.get()));
+  ASSERT_FALSE(SSL_CREDENTIAL_is_complete(cred2.get()));
 
   ASSERT_TRUE(SSL_CREDENTIAL_set1_private_key(cred.get(), key.get()));
   ASSERT_TRUE(SSL_CREDENTIAL_set1_private_key(cred2.get(), testkey.get()));
   SSL_CREDENTIAL_set_must_match_issuer(cred.get(), 1);
   SSL_CREDENTIAL_set_must_match_issuer(cred2.get(), 1);
+  ASSERT_TRUE(SSL_CREDENTIAL_is_complete(cred.get()));
+  ASSERT_TRUE(SSL_CREDENTIAL_is_complete(cred2.get()));
   ASSERT_TRUE(SSL_CTX_add1_credential(server_ctx.get(), cred.get()));
   ASSERT_TRUE(SSL_CTX_add1_credential(server_ctx.get(), cred2.get()));
 
@@ -5646,6 +5652,7 @@
   // Add cred3 to the CTX so we have an ubiquitous credential
   bssl::UniquePtr<SSL_CREDENTIAL> cred3(SSL_CREDENTIAL_new_x509());
   ASSERT_TRUE(cred3);
+  ASSERT_FALSE(SSL_CREDENTIAL_is_complete(cred3.get()));
   ASSERT_TRUE(
       SSL_CREDENTIAL_set1_cert_chain(cred3.get(), chain.data(), chain.size()));
   ASSERT_TRUE(SSL_CREDENTIAL_set1_private_key(cred3.get(), key.get()));
@@ -5664,6 +5671,7 @@
   // unknown property 0xbb with 0 bytes of data.
   bssl::UniquePtr<SSL_CREDENTIAL> cred(SSL_CREDENTIAL_new_x509());
   ASSERT_TRUE(cred);
+  ASSERT_FALSE(SSL_CREDENTIAL_is_complete(cred.get()));
   static const uint8_t kTestProperties1[] = {0x00, 0x0b, 0x00, 0x00, 0x00,
                                              0x03, 0xba, 0xdb, 0x0b, 0x00,
                                              0xbb, 0x00, 0x00};