Turn SSL_CREDENTIAL into a namespaced opaque struct

Bug: 500444613
Change-Id: Ib9834e7a999b028a3b86caaa3c919f5d99ddd2f7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/92627
Auto-Submit: David Benjamin <davidben@google.com>
Commit-Queue: Rudolf Polzer <rpolzer@google.com>
Reviewed-by: Rudolf Polzer <rpolzer@google.com>
Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/ssl/extensions.cc b/ssl/extensions.cc
index 9eddd24..5292268 100644
--- a/ssl/extensions.cc
+++ b/ssl/extensions.cc
@@ -3296,7 +3296,7 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_CREDENTIAL_LIST);
     return false;
   }
-  SSL_CREDENTIAL *cred = creds[0].get();
+  SSLCredential *cred = creds[0].get();
   assert(cred->type == SSLCredentialType::kSPAKE2PlusV1Client);
 
   hs->pake_prover = MakeUnique<spake2plus::Prover>();
@@ -4979,8 +4979,7 @@
 }
 
 bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs,
-                                     const SSL_CREDENTIAL *cred,
-                                     uint16_t *out) {
+                                     const SSLCredential *cred, uint16_t *out) {
   SSL *const ssl = hs->ssl;
   if (!cred->UsesPrivateKey()) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 31cb867..689375c 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -1264,7 +1264,7 @@
   return ssl_hs_ok;
 }
 
-static bool check_credential(SSL_HANDSHAKE *hs, const SSL_CREDENTIAL *cred,
+static bool check_credential(SSL_HANDSHAKE *hs, const SSLCredential *cred,
                              uint16_t *out_sigalg) {
   bool cert_type_ok = false;
   if (hs->client_cert_type == TLSEXT_cert_type_x509) {
@@ -1332,14 +1332,14 @@
     }
   }
 
-  Array<SSL_CREDENTIAL *> creds;
+  Array<SSLCredential *> creds;
   if (!ssl_get_full_credential_list(hs, &creds)) {
     return ssl_hs_error;
   }
 
   // Select the credential, if any, to use.
   bool may_proceed_anonymously = true;
-  for (SSL_CREDENTIAL *cred : creds) {
+  for (SSLCredential *cred : creds) {
     if (!cred->UsesPrivateKey()) {
       // Non-certificate credentials (e.g. PSKs) do not participate in deciding
       // whether to error or proceed anonymously.
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 1129d11..af3a401 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -223,7 +223,7 @@
 };
 
 static TLS12ServerParams choose_params(SSL_HANDSHAKE *hs,
-                                       const SSL_CREDENTIAL *cred,
+                                       const SSLCredential *cred,
                                        Span<const uint8_t> allowed_cert_types,
                                        const STACK_OF(SSL_CIPHER) *client_pref,
                                        bool has_ecdhe_group) {
@@ -744,7 +744,7 @@
   if (client_pref == nullptr) {
     return ssl_hs_error;
   }
-  Array<SSL_CREDENTIAL *> creds;
+  Array<SSLCredential *> creds;
   if (!ssl_get_full_credential_list(hs, &creds)) {
     return ssl_hs_error;
   }
@@ -762,7 +762,7 @@
                            client_pref.get(), has_ecdhe_group);
   } else {
     // Select the first credential which works.
-    for (SSL_CREDENTIAL *cred : creds) {
+    for (SSLCredential *cred : creds) {
       ERR_clear_error();
       params = choose_params(hs, cred, *allowed_cert_types, client_pref.get(),
                              has_ecdhe_group);
diff --git a/ssl/internal.h b/ssl/internal.h
index 2fbe2d0..af0afe3 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -48,7 +48,6 @@
 #include "../crypto/mem_internal.h"
 #include "../crypto/spake2plus/internal.h"
 
-
 #if defined(OPENSSL_WINDOWS)
 // Windows defines struct timeval in winsock2.h.
 #include <winsock2.h>
@@ -57,6 +56,8 @@
 #endif
 
 
+DECLARE_OPAQUE_STRUCT(ssl_credential_st, SSLCredential)
+
 BSSL_NAMESPACE_BEGIN
 
 struct SSL_CONFIG;
@@ -1227,7 +1228,7 @@
 
 struct SSLImportedPSK {
   static constexpr bool kAllowUniquePtr = true;
-  UniquePtr<SSL_CREDENTIAL> credential;
+  UniquePtr<SSLCredential> credential;
   Array<uint8_t> imported_identity;
   InplaceVector<uint8_t, SSL_MAX_MD_SIZE> ipskx;
   uint16_t protocol = 0;
@@ -1240,7 +1241,7 @@
 // |DTLS1_3_VERSION|) of the target protocol. It returns the imported PSK on
 // success and std::nullopt on error.
 std::optional<SSLImportedPSK> tls13_derive_imported_psk(const SSL_HANDSHAKE *hs,
-                                                        SSL_CREDENTIAL *cred,
+                                                        SSLCredential *cred,
                                                         uint16_t protocol,
                                                         const EVP_MD *hkdf_md);
 
@@ -1248,7 +1249,7 @@
 // imported identity for the specified target protocol and target KDF. This
 // allows matching against PSK identities without deriving imported PSK keys.
 bool tls13_compare_imported_psk_identity(Span<const uint8_t> id,
-                                         const SSL_CREDENTIAL *cred,
+                                         const SSLCredential *cred,
                                          uint16_t protocol,
                                          const EVP_MD *hkdf_md);
 
@@ -1414,18 +1415,17 @@
   kRawPublicKey,
 };
 
-BSSL_NAMESPACE_END
-
-// SSL_CREDENTIAL is exported to C, so it must be defined outside the namespace.
-struct ssl_credential_st : public bssl::RefCounted<ssl_credential_st> {
-  explicit ssl_credential_st(bssl::SSLCredentialType type);
-  ssl_credential_st(const ssl_credential_st &) = delete;
-  ssl_credential_st &operator=(const ssl_credential_st &) = delete;
+class SSLCredential : public ssl_credential_st,
+                      public RefCounted<SSLCredential> {
+ public:
+  explicit SSLCredential(SSLCredentialType type);
+  SSLCredential(const SSLCredential &) = delete;
+  SSLCredential &operator=(const SSLCredential &) = delete;
 
   // Dup returns a copy of the credential, or nullptr on error. The |ex_data|
   // values are not copied. This is only used on the legacy credential, whose
   // |ex_data| is inaccessible.
-  bssl::UniquePtr<SSL_CREDENTIAL> Dup() const;
+  UniquePtr<SSLCredential> Dup() const;
 
   // ClearCertAndKey erases any certificate and private key on the credential.
   void ClearCertAndKey();
@@ -1445,8 +1445,7 @@
   // certificates unmodified. It returns true on success and false on error. If
   // |discard_key_on_mismatch| is true and the private key is inconsistent with
   // the new leaf certificate, it is silently discarded.
-  bool SetLeafCert(bssl::UniquePtr<CRYPTO_BUFFER> leaf,
-                   bool discard_key_on_mismatch);
+  bool SetLeafCert(UniquePtr<CRYPTO_BUFFER> leaf, bool discard_key_on_mismatch);
 
   // ClearIntermediateCerts clears intermediate certificates in the certificate
   // chain, while preserving the leaf.
@@ -1455,23 +1454,23 @@
   // AppendIntermediateCert appends |cert| to the certificate chain. If there is
   // no leaf certificate configured, it leaves a placeholder null in |chain|. It
   // returns one on success and zero on error.
-  bool AppendIntermediateCert(bssl::UniquePtr<CRYPTO_BUFFER> cert);
+  bool AppendIntermediateCert(UniquePtr<CRYPTO_BUFFER> cert);
 
   // ChainContainsIssuer returns true if |dn| is a byte for byte match with the
   // issuer of any certificate in |chain|, false otherwise.
-  bool ChainContainsIssuer(bssl::Span<const uint8_t> dn) const;
+  bool ChainContainsIssuer(Span<const uint8_t> dn) const;
 
   // type is the credential type and determines which other fields apply.
-  bssl::SSLCredentialType type;
+  SSLCredentialType type;
 
   // pubkey is the cached public key of the credential. Unlike |privkey|, it is
   // always present and is extracted from the certificate, delegated credential,
   // etc.
-  bssl::UniquePtr<EVP_PKEY> pubkey;
+  UniquePtr<EVP_PKEY> pubkey;
 
   // privkey is the private key of the credential. It may be omitted in favor of
   // |key_method|.
-  bssl::UniquePtr<EVP_PKEY> privkey;
+  UniquePtr<EVP_PKEY> privkey;
 
   // key_method, if non-null, is a set of callbacks to call for private key
   // operations.
@@ -1483,7 +1482,7 @@
   //
   // In delegated credentials, this field is not configurable and is instead
   // computed from the dc_cert_verify_algorithm field.
-  bssl::Array<uint16_t> sigalgs;
+  Array<uint16_t> sigalgs;
 
   // chain contains the certificate chain, with the leaf at the beginning. The
   // first element of |chain| may be nullptr to indicate that the leaf
@@ -1491,36 +1490,36 @@
   //   If |chain| != nullptr -> len(chain) >= 1
   //   If |chain[0]| == nullptr -> len(chain) >= 2.
   //   |chain[1..]| != nullptr
-  bssl::UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
+  UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
 
   // dc is the DelegatedCredential structure, if this is a delegated credential.
-  bssl::UniquePtr<CRYPTO_BUFFER> dc;
+  UniquePtr<CRYPTO_BUFFER> dc;
 
   // dc_algorithm is the signature scheme of the signature over the delegated
   // credential itself, made by the end-entity certificate's public key.
   uint16_t dc_algorithm = 0;
 
   // Signed certificate timestamp list to be sent to the client, if requested
-  bssl::UniquePtr<CRYPTO_BUFFER> signed_cert_timestamp_list;
+  UniquePtr<CRYPTO_BUFFER> signed_cert_timestamp_list;
 
   // OCSP response to be sent to the client, if requested.
-  bssl::UniquePtr<CRYPTO_BUFFER> ocsp_response;
+  UniquePtr<CRYPTO_BUFFER> ocsp_response;
 
   // SPAKE2+-specific information.
-  bssl::Array<uint8_t> pake_context;
-  bssl::Array<uint8_t> client_identity;
-  bssl::Array<uint8_t> server_identity;
-  bssl::Array<uint8_t> password_verifier_w0;
-  bssl::Array<uint8_t> password_verifier_w1;  // server-only
-  bssl::Array<uint8_t> registration_record;   // client-only
+  Array<uint8_t> pake_context;
+  Array<uint8_t> client_identity;
+  Array<uint8_t> server_identity;
+  Array<uint8_t> password_verifier_w0;
+  Array<uint8_t> password_verifier_w1;  // server-only
+  Array<uint8_t> registration_record;   // client-only
   mutable std::atomic<uint32_t> pake_limit;
 
   // External-PSK-specific information. epskx is the HKDF-Extract-ed value, from
   // Section 5.1 of RFC 9258.
-  bssl::Array<uint8_t> epskx;
-  bssl::Array<uint8_t> epsk_id;
+  Array<uint8_t> epskx;
+  Array<uint8_t> epsk_id;
   const EVP_MD *epsk_md = nullptr;
-  bssl::Array<uint8_t> epsk_context;
+  Array<uint8_t> epsk_context;
 
   // Checks whether there are still permitted PAKE attempts remaining, without
   // changing the counter.
@@ -1536,7 +1535,7 @@
 
   // trust_anchor_id, if non-empty, is the trust anchor ID for the root of the
   // chain in |chain|.
-  bssl::Array<uint8_t> trust_anchor_id;
+  Array<uint8_t> trust_anchor_id;
 
   CRYPTO_EX_DATA ex_data;
 
@@ -1549,11 +1548,9 @@
 
  private:
   friend RefCounted;
-  ~ssl_credential_st();
+  ~SSLCredential();
 };
 
-BSSL_NAMESPACE_BEGIN
-
 // ssl_get_full_credential_list computes |hs|'s full credential list, including
 // the legacy credential. On success, it writes it to |*out| and returns true.
 // Otherwise, it returns false. The credential list may be empty, in which case
@@ -1567,13 +1564,13 @@
 //
 // The pointers in the result are only valid until |hs| is next mutated.
 bool ssl_get_full_credential_list(SSL_HANDSHAKE *hs,
-                                  Array<SSL_CREDENTIAL *> *out);
+                                  Array<SSLCredential *> *out);
 
 // ssl_credential_matches_requested_issuers returns true if |cred| is a
 // usable match for any requested issuers in |hs|, and false with an error
 // otherwise.
 bool ssl_credential_matches_requested_issuers(SSL_HANDSHAKE *hs,
-                                              const SSL_CREDENTIAL *cred);
+                                              const SSLCredential *cred);
 
 // ssl_check_tls13_credential_ignoring_issuer returns true if |cred| is usable
 // as the certificate in a TLS 1.3 handshake, ignoring the issuer check.
@@ -1583,7 +1580,7 @@
 // returned.
 bool ssl_check_tls13_credential_ignoring_issuer(
     SSL_HANDSHAKE *hs, Span<const uint8_t> allowed_cert_types,
-    const SSL_CREDENTIAL *cred, uint16_t *out_sigalg);
+    const SSLCredential *cred, uint16_t *out_sigalg);
 
 
 // Client certificate type & Server certificate type.
@@ -1944,7 +1941,7 @@
   Array<uint8_t> certificate_types;
 
   // credential is the credential we are using for the handshake.
-  UniquePtr<SSL_CREDENTIAL> credential;
+  UniquePtr<SSLCredential> credential;
 
   // peer_pubkey is the public key parsed from the peer's leaf certificate.
   UniquePtr<EVP_PKEY> peer_pubkey;
@@ -2497,7 +2494,7 @@
 // with |cred| based on the peer's preferences and the algorithms supported. It
 // returns true on success and false on error.
 bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs,
-                                     const SSL_CREDENTIAL *cred, uint16_t *out);
+                                     const SSLCredential *cred, uint16_t *out);
 
 // tls12_add_verify_sigalgs adds the signature algorithms acceptable for the
 // peer signature to |out|. It returns true on success and false on error.
@@ -2529,12 +2526,12 @@
 
   // credentials is the list of credentials to select between. Elements of this
   // array immutable.
-  Vector<UniquePtr<SSL_CREDENTIAL>> credentials;
+  Vector<UniquePtr<SSLCredential>> credentials;
 
   // legacy_credential is the credential configured by the legacy
   // non-credential-based APIs. If IsComplete() returns true, it is appended to
   // the list of credentials.
-  UniquePtr<SSL_CREDENTIAL> legacy_credential;
+  UniquePtr<SSLCredential> legacy_credential;
 
   // x509_method contains pointers to functions that might deal with |X509|
   // compatibility, or might be a no-op, depending on the application.
@@ -3833,6 +3830,8 @@
 //
 // The following types are exported to C code as public typedefs, so they must
 // be defined outside of the namespace.
+//
+// TODO(crbug.com/500444613): Move these to the bssl namespace.
 
 // ssl_method_st backs the public |SSL_METHOD| type. It is a compatibility
 // structure to support the legacy version-locked methods.
diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc
index 2330a95..400a6da 100644
--- a/ssl/ssl_cert.cc
+++ b/ssl/ssl_cert.cc
@@ -38,7 +38,7 @@
 BSSL_NAMESPACE_BEGIN
 
 CERT::CERT(const SSL_X509_METHOD *x509_method_arg)
-    : legacy_credential(MakeUnique<SSL_CREDENTIAL>(SSLCredentialType::kX509)),
+    : legacy_credential(MakeUnique<SSLCredential>(SSLCredentialType::kX509)),
       x509_method(x509_method_arg) {}
 
 CERT::~CERT() { x509_method->cert_free(this); }
diff --git a/ssl/ssl_credential.cc b/ssl/ssl_credential.cc
index bbb4e96..6c23b52 100644
--- a/ssl/ssl_credential.cc
+++ b/ssl/ssl_credential.cc
@@ -37,7 +37,7 @@
 }
 
 bool ssl_get_full_credential_list(SSL_HANDSHAKE *hs,
-                                  Array<SSL_CREDENTIAL *> *out) {
+                                  Array<SSLCredential *> *out) {
   CERT *cert = hs->config->cert.get();
   // Finish filling in the legacy credential if needed.
   if (!cert->x509_method->ssl_auto_chain_if_needed(hs)) {
@@ -64,7 +64,7 @@
 }
 
 bool ssl_credential_matches_requested_issuers(SSL_HANDSHAKE *hs,
-                                              const SSL_CREDENTIAL *cred) {
+                                              const SSLCredential *cred) {
   if (!cred->must_match_issuer) {
     // This credential does not need to match a requested issuer, so
     // it is good to use without a match.
@@ -115,18 +115,14 @@
   }
 }
 
-BSSL_NAMESPACE_END
-
-using namespace bssl;
-
 static ExDataClass g_ex_data_class;
 
-ssl_credential_st::ssl_credential_st(SSLCredentialType type_arg)
+SSLCredential::SSLCredential(SSLCredentialType type_arg)
     : RefCounted(CheckSubClass()), type(type_arg) {
   CRYPTO_new_ex_data(&ex_data);
 }
 
-ssl_credential_st::~ssl_credential_st() {
+SSLCredential::~SSLCredential() {
   CRYPTO_free_ex_data(&g_ex_data_class, &ex_data);
 }
 
@@ -135,9 +131,9 @@
   return const_cast<CRYPTO_BUFFER *>(buffer);
 }
 
-UniquePtr<SSL_CREDENTIAL> ssl_credential_st::Dup() const {
+UniquePtr<SSLCredential> SSLCredential::Dup() const {
   assert(type == SSLCredentialType::kX509);
-  UniquePtr<SSL_CREDENTIAL> ret = MakeUnique<SSL_CREDENTIAL>(type);
+  UniquePtr<SSLCredential> ret = MakeUnique<SSLCredential>(type);
   if (ret == nullptr) {
     return nullptr;
   }
@@ -164,14 +160,14 @@
   return ret;
 }
 
-void ssl_credential_st::ClearCertAndKey() {
+void SSLCredential::ClearCertAndKey() {
   pubkey = nullptr;
   privkey = nullptr;
   key_method = nullptr;
   chain = nullptr;
 }
 
-bool ssl_credential_st::UsesX509() const {
+bool SSLCredential::UsesX509() const {
   switch (type) {
     case SSLCredentialType::kX509:
     case SSLCredentialType::kDelegated:
@@ -185,7 +181,7 @@
   abort();
 }
 
-bool ssl_credential_st::UsesPrivateKey() const {
+bool SSLCredential::UsesPrivateKey() const {
   switch (type) {
     case SSLCredentialType::kX509:
     case SSLCredentialType::kDelegated:
@@ -199,7 +195,7 @@
   abort();
 }
 
-bool ssl_credential_st::IsComplete() const {
+bool SSLCredential::IsComplete() const {
   // APIs like |SSL_use_certificate| and |SSL_set1_chain| configure the leaf and
   // other certificates separately. It is possible for |chain| have a null leaf.
   if (UsesX509() && (sk_CRYPTO_BUFFER_num(chain.get()) == 0 ||
@@ -220,8 +216,8 @@
   return true;
 }
 
-bool ssl_credential_st::SetLeafCert(UniquePtr<CRYPTO_BUFFER> leaf,
-                                    bool discard_key_on_mismatch) {
+bool SSLCredential::SetLeafCert(UniquePtr<CRYPTO_BUFFER> leaf,
+                                bool discard_key_on_mismatch) {
   if (!UsesX509()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return false;
@@ -273,7 +269,7 @@
   return true;
 }
 
-void ssl_credential_st::ClearIntermediateCerts() {
+void SSLCredential::ClearIntermediateCerts() {
   if (chain == nullptr) {
     return;
   }
@@ -283,7 +279,7 @@
   }
 }
 
-bool ssl_credential_st::ChainContainsIssuer(Span<const uint8_t> dn) const {
+bool SSLCredential::ChainContainsIssuer(Span<const uint8_t> dn) const {
   if (UsesX509()) {
     // TODO(bbe) This is used for matching a chain by CA name for the CA
     // extension. If we require a chain to be present, we could remove any
@@ -304,11 +300,9 @@
   return false;
 }
 
-bool ssl_credential_st::HasPAKEAttempts() const {
-  return pake_limit.load() != 0;
-}
+bool SSLCredential::HasPAKEAttempts() const { return pake_limit.load() != 0; }
 
-bool ssl_credential_st::ClaimPAKEAttempt() const {
+bool SSLCredential::ClaimPAKEAttempt() const {
   uint32_t current = pake_limit.load(std::memory_order_relaxed);
   for (;;) {
     if (current == 0) {
@@ -322,13 +316,13 @@
   return true;
 }
 
-void ssl_credential_st::RestorePAKEAttempt() const {
+void SSLCredential::RestorePAKEAttempt() const {
   // This should not overflow because it will only be paired with
   // ClaimPAKEAttempt.
   pake_limit.fetch_add(1);
 }
 
-bool ssl_credential_st::AppendIntermediateCert(UniquePtr<CRYPTO_BUFFER> cert) {
+bool SSLCredential::AppendIntermediateCert(UniquePtr<CRYPTO_BUFFER> cert) {
   if (!UsesX509()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return false;
@@ -344,8 +338,12 @@
   return PushToStack(chain.get(), std::move(cert));
 }
 
+BSSL_NAMESPACE_END
+
+using namespace bssl;
+
 SSL_CREDENTIAL *SSL_CREDENTIAL_new_x509() {
-  return New<SSL_CREDENTIAL>(SSLCredentialType::kX509);
+  return New<SSLCredential>(SSLCredentialType::kX509);
 }
 
 SSL_CREDENTIAL *SSL_CREDENTIAL_new_pre_shared_key(
@@ -356,7 +354,7 @@
     return nullptr;
   }
 
-  auto cred = MakeUnique<SSL_CREDENTIAL>(SSLCredentialType::kPreSharedKey);
+  auto cred = MakeUnique<SSLCredential>(SSLCredentialType::kPreSharedKey);
   size_t epskx_len;
   if (cred == nullptr ||
       // Precompute epskx, to avoid recomputing it on every use of the
@@ -374,7 +372,7 @@
 }
 
 SSL_CREDENTIAL *SSL_CREDENTIAL_new_delegated() {
-  return New<SSL_CREDENTIAL>(SSLCredentialType::kDelegated);
+  return New<SSLCredential>(SSLCredentialType::kDelegated);
 }
 
 SSL_CREDENTIAL *SSL_CREDENTIAL_new_raw_public_key(EVP_PKEY *pkey) {
@@ -382,8 +380,8 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY);
     return nullptr;
   }
-  UniquePtr<SSL_CREDENTIAL> cred =
-      MakeUnique<SSL_CREDENTIAL>(SSLCredentialType::kRawPublicKey);
+  UniquePtr<SSLCredential> cred =
+      MakeUnique<SSLCredential>(SSLCredentialType::kRawPublicKey);
   if (cred == nullptr) {
     return nullptr;
   }
@@ -398,8 +396,8 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY);
     return nullptr;
   }
-  UniquePtr<SSL_CREDENTIAL> cred =
-      MakeUnique<SSL_CREDENTIAL>(SSLCredentialType::kRawPublicKey);
+  UniquePtr<SSLCredential> cred =
+      MakeUnique<SSLCredential>(SSLCredentialType::kRawPublicKey);
   if (cred == nullptr) {
     return nullptr;
   }
@@ -408,63 +406,69 @@
   return cred.release();
 }
 
-void SSL_CREDENTIAL_up_ref(SSL_CREDENTIAL *cred) { cred->UpRefInternal(); }
+void SSL_CREDENTIAL_up_ref(SSL_CREDENTIAL *cred) {
+  FromOpaque(cred)->UpRefInternal();
+}
 
 void SSL_CREDENTIAL_free(SSL_CREDENTIAL *cred) {
   if (cred != nullptr) {
-    cred->DecRefInternal();
+    FromOpaque(cred)->DecRefInternal();
   }
 }
 
 int SSL_CREDENTIAL_is_complete(const SSL_CREDENTIAL *cred) {
-  return cred->IsComplete();
+  return FromOpaque(cred)->IsComplete();
 }
 
 int SSL_CREDENTIAL_set1_private_key(SSL_CREDENTIAL *cred, EVP_PKEY *key) {
-  if (!cred->UsesPrivateKey()) {
+  auto *cred_impl = FromOpaque(cred);
+  if (!cred_impl->UsesPrivateKey()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
 
   // If the public half has been configured, check |key| matches. |pubkey| will
   // have been extracted from the certificate, delegated credential, etc.
-  if (cred->pubkey != nullptr &&
-      !ssl_compare_public_and_private_key(cred->pubkey.get(), key)) {
+  if (cred_impl->pubkey != nullptr &&
+      !ssl_compare_public_and_private_key(cred_impl->pubkey.get(), key)) {
     return 0;
   }
 
-  cred->privkey = UpRef(key);
-  cred->key_method = nullptr;
+  cred_impl->privkey = UpRef(key);
+  cred_impl->key_method = nullptr;
   return 1;
 }
 
 int SSL_CREDENTIAL_set_private_key_method(
     SSL_CREDENTIAL *cred, const SSL_PRIVATE_KEY_METHOD *key_method) {
-  if (!cred->UsesPrivateKey()) {
+  auto *cred_impl = FromOpaque(cred);
+  if (!cred_impl->UsesPrivateKey()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
 
-  cred->privkey = nullptr;
-  cred->key_method = key_method;
+  cred_impl->privkey = nullptr;
+  cred_impl->key_method = key_method;
   return 1;
 }
 
 int SSL_CREDENTIAL_set1_cert_chain(SSL_CREDENTIAL *cred,
                                    CRYPTO_BUFFER *const *certs,
                                    size_t num_certs) {
-  if (!cred->UsesX509() || num_certs == 0) {
+  auto *cred_impl = FromOpaque(cred);
+  if (!cred_impl->UsesX509() || num_certs == 0) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
 
-  if (!cred->SetLeafCert(UpRef(certs[0]), /*discard_key_on_mismatch=*/false)) {
+  if (!cred_impl->SetLeafCert(UpRef(certs[0]),
+                              /*discard_key_on_mismatch=*/false)) {
     return 0;
   }
 
-  cred->ClearIntermediateCerts();
+  cred_impl->ClearIntermediateCerts();
   for (size_t i = 1; i < num_certs; i++) {
-    if (!cred->AppendIntermediateCert(UpRef(certs[i]))) {
+    if (!cred_impl->AppendIntermediateCert(UpRef(certs[i]))) {
       return 0;
     }
   }
@@ -474,7 +478,8 @@
 
 int SSL_CREDENTIAL_set1_delegated_credential(SSL_CREDENTIAL *cred,
                                              CRYPTO_BUFFER *dc) {
-  if (cred->type != SSLCredentialType::kDelegated) {
+  auto *cred_impl = FromOpaque(cred);
+  if (cred_impl->type != SSLCredentialType::kDelegated) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
@@ -510,35 +515,38 @@
     return 0;
   }
 
-  if (!cred->sigalgs.CopyFrom(Span(&dc_cert_verify_algorithm, 1))) {
+  if (!cred_impl->sigalgs.CopyFrom(Span(&dc_cert_verify_algorithm, 1))) {
     return 0;
   }
 
-  if (cred->privkey != nullptr &&
-      !ssl_compare_public_and_private_key(pubkey.get(), cred->privkey.get())) {
+  if (cred_impl->privkey != nullptr &&
+      !ssl_compare_public_and_private_key(pubkey.get(),
+                                          cred_impl->privkey.get())) {
     return 0;
   }
 
-  cred->dc = UpRef(dc);
-  cred->pubkey = std::move(pubkey);
-  cred->dc_algorithm = algorithm;
+  cred_impl->dc = UpRef(dc);
+  cred_impl->pubkey = std::move(pubkey);
+  cred_impl->dc_algorithm = algorithm;
   return 1;
 }
 
 int SSL_CREDENTIAL_set1_ocsp_response(SSL_CREDENTIAL *cred,
                                       CRYPTO_BUFFER *ocsp) {
-  if (!cred->UsesX509()) {
+  auto *cred_impl = FromOpaque(cred);
+  if (!cred_impl->UsesX509()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
 
-  cred->ocsp_response = UpRef(ocsp);
+  cred_impl->ocsp_response = UpRef(ocsp);
   return 1;
 }
 
 int SSL_CREDENTIAL_set1_signed_cert_timestamp_list(SSL_CREDENTIAL *cred,
                                                    CRYPTO_BUFFER *sct_list) {
-  if (!cred->UsesX509()) {
+  auto *cred_impl = FromOpaque(cred);
+  if (!cred_impl->UsesX509()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
@@ -550,7 +558,7 @@
     return 0;
   }
 
-  cred->signed_cert_timestamp_list = UpRef(sct_list);
+  cred_impl->signed_cert_timestamp_list = UpRef(sct_list);
   return 1;
 }
 
@@ -567,13 +575,13 @@
       Span(server_identity, server_identity_len));
 }
 
-static UniquePtr<SSL_CREDENTIAL> ssl_credential_new_spake2plusv1(
+static UniquePtr<SSLCredential> ssl_credential_new_spake2plusv1(
     SSLCredentialType type, Span<const uint8_t> context,
     Span<const uint8_t> client_identity, Span<const uint8_t> server_identity,
     uint32_t limit) {
   assert(type == SSLCredentialType::kSPAKE2PlusV1Client ||
          type == SSLCredentialType::kSPAKE2PlusV1Server);
-  auto cred = MakeUnique<SSL_CREDENTIAL>(type);
+  auto cred = MakeUnique<SSLCredential>(type);
   if (cred == nullptr) {
     return nullptr;
   }
@@ -600,7 +608,7 @@
     return nullptr;
   }
 
-  UniquePtr<SSL_CREDENTIAL> cred = ssl_credential_new_spake2plusv1(
+  UniquePtr<SSLCredential> cred = ssl_credential_new_spake2plusv1(
       SSLCredentialType::kSPAKE2PlusV1Client, Span(context, context_len),
       Span(client_identity, client_identity_len),
       Span(server_identity, server_identity_len), error_limit);
@@ -628,7 +636,7 @@
     return nullptr;
   }
 
-  UniquePtr<SSL_CREDENTIAL> cred = ssl_credential_new_spake2plusv1(
+  UniquePtr<SSLCredential> cred = ssl_credential_new_spake2plusv1(
       SSLCredentialType::kSPAKE2PlusV1Server, Span(context, context_len),
       Span(client_identity, client_identity_len),
       Span(server_identity, server_identity_len), rate_limit);
@@ -646,23 +654,25 @@
 }
 
 int SSL_CTX_add1_credential(SSL_CTX *ctx, SSL_CREDENTIAL *cred) {
-  if (!cred->IsComplete()) {
+  auto *cred_impl = FromOpaque(cred);
+  if (!cred_impl->IsComplete()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
-  return ctx->cert->credentials.Push(UpRef(cred));
+  return ctx->cert->credentials.Push(UpRef(cred_impl));
 }
 
 int SSL_add1_credential(SSL *ssl, SSL_CREDENTIAL *cred) {
+  auto *cred_impl = FromOpaque(cred);
   if (ssl->config == nullptr) {
     return 0;
   }
 
-  if (!cred->IsComplete()) {
+  if (!cred_impl->IsComplete()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
-  return ssl->config->cert->credentials.Push(UpRef(cred));
+  return ssl->config->cert->credentials.Push(UpRef(cred_impl));
 }
 
 const SSL_CREDENTIAL *SSL_get0_selected_credential(const SSL *ssl) {
@@ -680,26 +690,27 @@
 }
 
 int SSL_CREDENTIAL_set_ex_data(SSL_CREDENTIAL *cred, int idx, void *arg) {
-  return CRYPTO_set_ex_data(&cred->ex_data, idx, arg);
+  return CRYPTO_set_ex_data(&FromOpaque(cred)->ex_data, idx, arg);
 }
 
 void *SSL_CREDENTIAL_get_ex_data(const SSL_CREDENTIAL *cred, int idx) {
-  return CRYPTO_get_ex_data(&cred->ex_data, idx);
+  return CRYPTO_get_ex_data(&FromOpaque(cred)->ex_data, idx);
 }
 
 void SSL_CREDENTIAL_set_must_match_issuer(SSL_CREDENTIAL *cred, int match) {
-  cred->must_match_issuer = !!match;
+  FromOpaque(cred)->must_match_issuer = !!match;
 }
 
 int SSL_CREDENTIAL_set1_trust_anchor_id(SSL_CREDENTIAL *cred, const uint8_t *id,
                                         size_t id_len) {
+  auto *cred_impl = FromOpaque(cred);
   // For now, this is only valid for X.509.
-  if (!cred->UsesX509()) {
+  if (!cred_impl->UsesX509()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
 
-  if (!cred->trust_anchor_id.CopyFrom(Span(id, id_len))) {
+  if (!cred_impl->trust_anchor_id.CopyFrom(Span(id, id_len))) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
   }
@@ -709,8 +720,9 @@
 
 int SSL_CREDENTIAL_set1_certificate_properties(
     SSL_CREDENTIAL *cred, CRYPTO_BUFFER *cert_property_list) {
+  auto *cred_impl = FromOpaque(cred);
   // For now, this is only valid for X.509.
-  if (!cred->UsesX509()) {
+  if (!cred_impl->UsesX509()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
@@ -756,7 +768,7 @@
       OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TRUST_ANCHOR_LIST);
       return 0;
     }
-    if (!SSL_CREDENTIAL_set1_trust_anchor_id(cred,
+    if (!SSL_CREDENTIAL_set1_trust_anchor_id(cred_impl,
                                              CBS_data(&trust_anchor.value()),
                                              CBS_len(&trust_anchor.value()))) {
       return 0;
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index e3c42a9..de4e79e 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -1590,7 +1590,7 @@
   return SSL_pending(ssl) != 0 || !ssl->s3->read_buffer.empty();
 }
 
-static bool has_cert_and_key(const SSL_CREDENTIAL *cred) {
+static bool has_cert_and_key(const SSLCredential *cred) {
   // TODO(davidben): If |cred->key_method| is set, that should be fine too.
   if (cred->privkey == nullptr) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc
index 945e78b..190eed4 100644
--- a/ssl/ssl_privkey.cc
+++ b/ssl/ssl_privkey.cc
@@ -211,7 +211,7 @@
     SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
     uint16_t sigalg, Span<const uint8_t> in) {
   SSL *const ssl = hs->ssl;
-  const SSL_CREDENTIAL *const cred = hs->credential.get();
+  const SSLCredential *const cred = hs->credential.get();
   SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
   Array<uint8_t> spki;
   if (hints) {
@@ -299,7 +299,7 @@
                                                       size_t max_out,
                                                       Span<const uint8_t> in) {
   SSL *const ssl = hs->ssl;
-  const SSL_CREDENTIAL *const cred = hs->credential.get();
+  const SSLCredential *const cred = hs->credential.get();
   assert(!hs->can_release_private_key);
   if (cred->key_method != nullptr) {
     enum ssl_private_key_result_t ret;
@@ -597,19 +597,20 @@
 int SSL_CREDENTIAL_set1_signing_algorithm_prefs(SSL_CREDENTIAL *cred,
                                                 const uint16_t *prefs,
                                                 size_t num_prefs) {
-  if (!cred->UsesPrivateKey()) {
+  auto *cred_impl = FromOpaque(cred);
+  if (!cred_impl->UsesPrivateKey()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
 
   // Delegated credentials are constrained to a single algorithm, so there is no
   // need to configure this.
-  if (cred->type == SSLCredentialType::kDelegated) {
+  if (cred_impl->type == SSLCredentialType::kDelegated) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
 
-  return set_sigalg_prefs(&cred->sigalgs, Span(prefs, num_prefs));
+  return set_sigalg_prefs(&cred_impl->sigalgs, Span(prefs, num_prefs));
 }
 
 int SSL_CTX_set_signing_algorithm_prefs(SSL_CTX *ctx, const uint16_t *prefs,
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 3b7bb11..3476f3e 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -5675,7 +5675,8 @@
                   CRYPTO_BUFFER_len(subject_buf.get())));
 #if !defined(BORINGSSL_SHARED_LIBRARY)
   ASSERT_FALSE(
-      cred->ChainContainsIssuer(Span(CRYPTO_BUFFER_data(subject_buf.get()),
+      FromOpaque(cred.get())
+          ->ChainContainsIssuer(Span(CRYPTO_BUFFER_data(subject_buf.get()),
                                      CRYPTO_BUFFER_len(subject_buf.get()))));
 #endif
 
@@ -5684,7 +5685,8 @@
 
 #if !defined(BORINGSSL_SHARED_LIBRARY)
   ASSERT_TRUE(
-      cred->ChainContainsIssuer(Span(CRYPTO_BUFFER_data(subject_buf.get()),
+      FromOpaque(cred.get())
+          ->ChainContainsIssuer(Span(CRYPTO_BUFFER_data(subject_buf.get()),
                                      CRYPTO_BUFFER_len(subject_buf.get()))));
 #endif
 
diff --git a/ssl/ssl_x509.cc b/ssl/ssl_x509.cc
index 74d2959..30bae84 100644
--- a/ssl/ssl_x509.cc
+++ b/ssl/ssl_x509.cc
@@ -296,7 +296,7 @@
   // Only build a chain if the feature isn't disabled, the legacy credential
   // exists but has no intermediates configured.
   SSL *ssl = hs->ssl;
-  SSL_CREDENTIAL *cred = hs->config->cert->legacy_credential.get();
+  SSLCredential *cred = hs->config->cert->legacy_credential.get();
   if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) || !cred->IsComplete() ||
       sk_CRYPTO_BUFFER_num(cred->chain.get()) != 1) {
     return true;
@@ -599,7 +599,7 @@
 static int ssl_cert_cache_leaf_cert(CERT *cert) {
   assert(cert->x509_method);
 
-  const SSL_CREDENTIAL *cred = cert->legacy_credential.get();
+  const SSLCredential *cred = cert->legacy_credential.get();
   if (cert->x509_leaf != nullptr || cred->chain == nullptr) {
     return 1;
   }
@@ -745,7 +745,7 @@
 static int ssl_cert_cache_chain_certs(CERT *cert) {
   assert(cert->x509_method);
 
-  const SSL_CREDENTIAL *cred = cert->legacy_credential.get();
+  const SSLCredential *cred = cert->legacy_credential.get();
   if (cert->x509_chain != nullptr || cred->chain == nullptr ||
       sk_CRYPTO_BUFFER_num(cred->chain.get()) < 2) {
     return 1;
diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc
index 30b1d8f..177336e 100644
--- a/ssl/tls13_both.cc
+++ b/ssl/tls13_both.cc
@@ -437,7 +437,7 @@
 
 bool tls13_add_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  const SSL_CREDENTIAL *cred = hs->credential.get();
+  const SSLCredential *cred = hs->credential.get();
 
   ScopedCBB cbb;
   CBB *body, body_storage, certificate_list;
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index 0942604..67b6321 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -959,7 +959,7 @@
   return ssl_hs_ok;
 }
 
-static bool check_credential(SSL_HANDSHAKE *hs, const SSL_CREDENTIAL *cred,
+static bool check_credential(SSL_HANDSHAKE *hs, const SSLCredential *cred,
                              uint16_t *out_sigalg) {
   bool cert_type_ok = false;
   if (hs->client_cert_type == TLSEXT_cert_type_x509) {
@@ -1008,14 +1008,14 @@
     }
   }
 
-  Array<SSL_CREDENTIAL *> creds;
+  Array<SSLCredential *> creds;
   if (!ssl_get_full_credential_list(hs, &creds)) {
     return ssl_hs_error;
   }
 
   // Select the credential, if any, to use.
   bool may_proceed_anonymously = true;
-  for (SSL_CREDENTIAL *cred : creds) {
+  for (SSLCredential *cred : creds) {
     if (!cred->UsesPrivateKey()) {
       // Non-certificate credentials (e.g. PSKs) do not participate in deciding
       // whether to error or proceed anonymously.
diff --git a/ssl/tls13_enc.cc b/ssl/tls13_enc.cc
index 08f2909..d03a76f 100644
--- a/ssl/tls13_enc.cc
+++ b/ssl/tls13_enc.cc
@@ -610,9 +610,10 @@
   }
 }
 
-std::optional<SSLImportedPSK> tls13_derive_imported_psk(
-    const SSL_HANDSHAKE *hs, SSL_CREDENTIAL *cred, uint16_t protocol,
-    const EVP_MD *hkdf_md) {
+std::optional<SSLImportedPSK> tls13_derive_imported_psk(const SSL_HANDSHAKE *hs,
+                                                        SSLCredential *cred,
+                                                        uint16_t protocol,
+                                                        const EVP_MD *hkdf_md) {
   assert(cred->type == SSLCredentialType::kPreSharedKey);
 
   std::optional<uint16_t> target_kdf = hkdf_md_to_kdf_id(hkdf_md);
@@ -665,7 +666,7 @@
 }
 
 bool tls13_compare_imported_psk_identity(Span<const uint8_t> id,
-                                         const SSL_CREDENTIAL *cred,
+                                         const SSLCredential *cred,
                                          uint16_t protocol,
                                          const EVP_MD *hkdf_md) {
   assert(cred->type == SSLCredentialType::kPreSharedKey);
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index 88ad22b..376e1a3 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -259,7 +259,7 @@
 
 bool ssl_check_tls13_credential_ignoring_issuer(
     SSL_HANDSHAKE *hs, Span<const uint8_t> allowed_cert_types,
-    const SSL_CREDENTIAL *cred, uint16_t *out_sigalg) {
+    const SSLCredential *cred, uint16_t *out_sigalg) {
   assert(!allowed_cert_types.empty());
   const auto is_cert_type_allowed = [&](uint8_t cert_type) {
     return std::find(allowed_cert_types.begin(), allowed_cert_types.end(),
@@ -296,7 +296,7 @@
 
 static bool check_signature_credential(SSL_HANDSHAKE *hs,
                                        Span<const uint8_t> allowed_cert_types,
-                                       const SSL_CREDENTIAL *cred,
+                                       const SSLCredential *cred,
                                        uint16_t *out_sigalg) {
   return ssl_check_tls13_credential_ignoring_issuer(hs, allowed_cert_types,
                                                     cred, out_sigalg) &&
@@ -306,7 +306,7 @@
 }
 
 static bool check_pake_credential(SSL_HANDSHAKE *hs,
-                                  const SSL_CREDENTIAL *cred) {
+                                  const SSLCredential *cred) {
   assert(cred->type == SSLCredentialType::kSPAKE2PlusV1Server);
   // Look for a client PAKE share that matches |cred|.
   if (hs->pake_share == nullptr ||
@@ -320,7 +320,7 @@
   return true;
 }
 
-static bool check_psk_credential(SSL_HANDSHAKE *hs, const SSL_CREDENTIAL *cred,
+static bool check_psk_credential(SSL_HANDSHAKE *hs, const SSLCredential *cred,
                                  const std::optional<SSLOfferedPSKs> &psks) {
   assert(cred->type == SSLCredentialType::kPreSharedKey);
   SSL *const ssl = hs->ssl;
@@ -397,7 +397,7 @@
   }
 
   // Select the credential to use.
-  Array<SSL_CREDENTIAL *> creds;
+  Array<SSLCredential *> creds;
   if (!ssl_get_full_credential_list(hs, &creds)) {
     return ssl_hs_error;
   }
@@ -412,7 +412,7 @@
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
-  for (SSL_CREDENTIAL *cred : creds) {
+  for (SSLCredential *cred : creds) {
     ERR_clear_error();
     if (cred->type == SSLCredentialType::kSPAKE2PlusV1Server) {
       if (check_pake_credential(hs, cred)) {