Turn SSL_CTX into an opaque struct

Bug: 500444613
Change-Id: Idfcba901b410900cb7fce4eeb4c25a995d198f10
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/96207
Reviewed-by: Rudolf Polzer <rpolzer@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/d1_srtp.cc b/ssl/d1_srtp.cc
index 25759d7..ab1f73d 100644
--- a/ssl/d1_srtp.cc
+++ b/ssl/d1_srtp.cc
@@ -88,7 +88,7 @@
 }
 
 int SSL_CTX_set_srtp_profiles(SSL_CTX *ctx, const char *profiles) {
-  return ssl_ctx_make_profiles(profiles, &ctx->srtp_profiles);
+  return ssl_ctx_make_profiles(profiles, &FromOpaque(ctx)->srtp_profiles);
 }
 
 int SSL_set_srtp_profiles(SSL *ssl, const char *profiles) {
diff --git a/ssl/encrypted_client_hello.cc b/ssl/encrypted_client_hello.cc
index e288f3c..444de9b 100644
--- a/ssl/encrypted_client_hello.cc
+++ b/ssl/encrypted_client_hello.cc
@@ -1064,6 +1064,7 @@
 }
 
 int SSL_CTX_set1_ech_keys(SSL_CTX *ctx, SSL_ECH_KEYS *keys) {
+  auto *ctx_impl = FromOpaque(ctx);
   bool has_retry_config = false;
   for (const auto &config : FromOpaque(keys)->configs) {
     if (config->is_retry_config()) {
@@ -1076,8 +1077,8 @@
     return 0;
   }
   UniquePtr<SSLECHKeys> owned_keys = UpRef(FromOpaque(keys));
-  MutexWriteLock lock(&ctx->lock);
-  ctx->ech_keys.swap(owned_keys);
+  MutexWriteLock lock(&ctx_impl->lock);
+  ctx_impl->ech_keys.swap(owned_keys);
   return 1;
 }
 
diff --git a/ssl/extensions.cc b/ssl/extensions.cc
index fb067c6..3697e7a 100644
--- a/ssl/extensions.cc
+++ b/ssl/extensions.cc
@@ -3336,7 +3336,7 @@
     return true;
   }
 
-  const SSL_CTX *ctx = hs->ssl->ctx.get();
+  const SSLContext *ctx = hs->ssl->ctx.get();
   const size_t num_algs = ctx->cert_compression_algs.size();
 
   CBS alg_ids;
@@ -4925,7 +4925,7 @@
 static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_ticket_keys(
     SSL_HANDSHAKE *hs, Array<uint8_t> *out, Span<const uint8_t> ticket) {
   assert(ticket.size() >= SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH);
-  SSL_CTX *ctx = hs->ssl->session_ctx.get();
+  SSLContext *ctx = hs->ssl->session_ctx.get();
 
   // Rotate the ticket key if necessary.
   if (!ssl_ctx_rotate_ticket_encryption_key(ctx)) {
diff --git a/ssl/internal.h b/ssl/internal.h
index e360f0f..85dde3e 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -57,6 +57,7 @@
 
 
 DECLARE_OPAQUE_STRUCT(ssl_credential_st, SSLCredential)
+DECLARE_OPAQUE_STRUCT(ssl_ctx_st, SSLContext)
 DECLARE_OPAQUE_STRUCT(ssl_ech_keys_st, SSLECHKeys)
 
 BSSL_NAMESPACE_BEGIN
@@ -2739,11 +2740,11 @@
   bool (*ssl_auto_chain_if_needed)(SSL_HANDSHAKE *hs);
   // ssl_ctx_new does any necessary initialisation of |ctx|. It returns true on
   // success or false on error.
-  bool (*ssl_ctx_new)(SSL_CTX *ctx);
+  bool (*ssl_ctx_new)(SSLContext *ctx);
   // ssl_ctx_free frees anything created by |ssl_ctx_new|.
-  void (*ssl_ctx_free)(SSL_CTX *ctx);
+  void (*ssl_ctx_free)(SSLContext *ctx);
   // ssl_ctx_flush_cached_client_CA drops any cached |X509_NAME|s from |ctx|.
-  void (*ssl_ctx_flush_cached_client_CA)(SSL_CTX *ssl);
+  void (*ssl_ctx_flush_cached_client_CA)(SSLContext *ssl);
 };
 
 // ssl_crypto_x509_method provides the |SSL_X509_METHOD| functions using
@@ -3573,7 +3574,7 @@
 bool ssl_encrypt_ticket(SSL_HANDSHAKE *hs, CBB *out,
                         const SSL_SESSION *session);
 
-bool ssl_ctx_rotate_ticket_encryption_key(SSL_CTX *ctx);
+bool ssl_ctx_rotate_ticket_encryption_key(SSLContext *ctx);
 
 // ssl_session_new returns a newly-allocated blank |SSL_SESSION| or nullptr on
 // error.
@@ -3847,7 +3848,7 @@
 // ssl_can_read returns whether |ssl| is allowed to read.
 bool ssl_can_read(const SSL *ssl);
 
-OPENSSL_timeval ssl_ctx_get_current_time(const SSL_CTX *ctx);
+OPENSSL_timeval ssl_ctx_get_current_time(const SSLContext *ctx);
 
 // ssl_reset_error_state resets state for |SSL_get_error|.
 void ssl_reset_error_state(SSL *ssl);
@@ -3880,16 +3881,18 @@
   const bssl::SSL_X509_METHOD *x509_method;
 };
 
-struct ssl_ctx_st : public bssl::RefCounted<ssl_ctx_st> {
-  explicit ssl_ctx_st(const SSL_METHOD *ssl_method);
-  ssl_ctx_st(const ssl_ctx_st &) = delete;
-  ssl_ctx_st &operator=(const ssl_ctx_st &) = delete;
+BSSL_NAMESPACE_BEGIN
+class SSLContext : public ssl_ctx_st, public RefCounted<SSLContext> {
+ public:
+  explicit SSLContext(const SSL_METHOD *ssl_method);
+  SSLContext(const SSLContext &) = delete;
+  SSLContext &operator=(const SSLContext &) = delete;
 
-  const bssl::SSL_PROTOCOL_METHOD *method = nullptr;
-  const bssl::SSL_X509_METHOD *x509_method = nullptr;
+  const SSL_PROTOCOL_METHOD *method = nullptr;
+  const SSL_X509_METHOD *x509_method = nullptr;
 
   // lock is used to protect various operations on this object.
-  mutable bssl::Mutex lock;
+  mutable Mutex lock;
 
   // conf_max_version is the maximum acceptable protocol version configured by
   // |SSL_CTX_set_max_proto_version|. Note this version is normalized in DTLS
@@ -3909,7 +3912,7 @@
   // quic_method is the method table corresponding to the QUIC hooks.
   const SSL_QUIC_METHOD *quic_method = nullptr;
 
-  bssl::UniquePtr<bssl::SSLCipherPreferenceList> cipher_list;
+  UniquePtr<SSLCipherPreferenceList> cipher_list;
 
   X509_STORE *cert_store = nullptr;
   LHASH_OF(SSL_SESSION) *sessions = nullptr;
@@ -3974,17 +3977,17 @@
   void (*info_callback)(const SSL *ssl, int type, int value) = nullptr;
 
   // what we put in client cert requests
-  bssl::UniquePtr<STACK_OF(CRYPTO_BUFFER)> client_CA;
+  UniquePtr<STACK_OF(CRYPTO_BUFFER)> client_CA;
 
   // cached_x509_client_CA is a cache of parsed versions of the elements of
   // |client_CA|.
   STACK_OF(X509_NAME) *cached_x509_client_CA = nullptr;
 
   // What we put in client hello in the CA extension.
-  bssl::UniquePtr<STACK_OF(CRYPTO_BUFFER)> CA_names;
+  UniquePtr<STACK_OF(CRYPTO_BUFFER)> CA_names;
 
   // What we request in the trust_anchors extension.
-  std::optional<bssl::Array<uint8_t>> requested_trust_anchors;
+  std::optional<Array<uint8_t>> requested_trust_anchors;
 
   // Default values to use in SSL structures follow (these are copied by
   // SSL_new)
@@ -3996,7 +3999,7 @@
   uint32_t mode = SSL_MODE_NO_AUTO_CHAIN;
   uint32_t max_cert_list = SSL_MAX_CERT_LIST_DEFAULT;
 
-  bssl::UniquePtr<bssl::CERT> cert;
+  UniquePtr<CERT> cert;
 
   // callback that allows applications to peek at protocol messages
   void (*msg_callback)(int is_write, int version, int content_type,
@@ -4038,8 +4041,8 @@
   // first handshake and |ticket_key_prev| may be NULL at any time.
   // Automatically generated ticket keys are rotated as needed at handshake
   // time. Hence, all access must be synchronized through |lock|.
-  bssl::UniquePtr<bssl::TicketKey> ticket_key_current;
-  bssl::UniquePtr<bssl::TicketKey> ticket_key_prev;
+  UniquePtr<TicketKey> ticket_key_current;
+  UniquePtr<TicketKey> ticket_key_prev;
 
   // Callback to support customisation of ticket key setting
   int (*ticket_key_cb)(SSL *ssl, uint8_t *name, uint8_t *iv,
@@ -4047,7 +4050,7 @@
 
   // Server-only: psk_identity_hint is the default identity hint to send in
   // PSK-based key exchanges.
-  bssl::UniquePtr<char> psk_identity_hint;
+  UniquePtr<char> psk_identity_hint;
 
   unsigned (*psk_client_callback)(SSL *ssl, const char *hint, char *identity,
                                   unsigned max_identity_len, uint8_t *psk,
@@ -4089,26 +4092,26 @@
 
   // For a client, this contains the list of supported protocols in wire
   // format.
-  bssl::Array<uint8_t> alpn_client_proto_list;
+  Array<uint8_t> alpn_client_proto_list;
 
   // SRTP profiles we are willing to do from RFC 5764
-  bssl::UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> srtp_profiles;
+  UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> srtp_profiles;
 
   // Defined compression algorithms for certificates.
-  bssl::Vector<bssl::CertCompressionAlg> cert_compression_algs;
+  Vector<CertCompressionAlg> cert_compression_algs;
 
   // Supported group values and flags inherited by SSL structure
-  bssl::Array<uint16_t> supported_group_list;
-  bssl::Array<uint32_t> supported_group_list_flags;
+  Array<uint16_t> supported_group_list;
+  Array<uint32_t> supported_group_list_flags;
 
   // channel_id_private is the client's Channel ID private key, or null if
   // Channel ID should not be offered on this connection.
-  bssl::UniquePtr<EVP_PKEY> channel_id_private;
+  UniquePtr<EVP_PKEY> channel_id_private;
 
   // ech_keys contains the server's list of ECHConfig values and associated
   // private keys. This list may be swapped out at any time, so all access must
   // be synchronized through |lock|.
-  bssl::UniquePtr<bssl::SSLECHKeys> ech_keys;
+  UniquePtr<SSLECHKeys> ech_keys;
 
   // keylog_callback, if not NULL, is the key logging callback. See
   // |SSL_CTX_set_keylog_callback|.
@@ -4121,7 +4124,7 @@
 
   // pool is used for all |CRYPTO_BUFFER|s in case we wish to share certificate
   // memory.
-  bssl::UniquePtr<CRYPTO_BUFFER_POOL> pool;
+  UniquePtr<CRYPTO_BUFFER_POOL> pool;
 
   // ticket_aead_method contains function pointers for opening and sealing
   // session tickets.
@@ -4138,13 +4141,13 @@
 
   // verify_sigalgs, if not empty, is the set of signature algorithms
   // accepted from the peer in decreasing order of preference.
-  bssl::Array<uint16_t> verify_sigalgs;
+  Array<uint16_t> verify_sigalgs;
 
   // accepted_peer_cert_types inherited by SSL struct.
-  bssl::InplaceVector<uint8_t, bssl::kNumCertTypes> accepted_peer_cert_types;
+  InplaceVector<uint8_t, kNumCertTypes> accepted_peer_cert_types;
 
   // available_client_cert_types inherited by SSL struct.
-  bssl::InplaceVector<uint8_t, bssl::kNumCertTypes> available_client_cert_types;
+  InplaceVector<uint8_t, kNumCertTypes> available_client_cert_types;
 
   // retain_only_sha256_of_client_certs is true if we should compute the SHA256
   // hash of the peer's certificate and then discard it to save memory and
@@ -4204,11 +4207,12 @@
 
  private:
   friend RefCounted;
-  ~ssl_ctx_st();
+  ~SSLContext();
 };
+BSSL_NAMESPACE_END
 
 struct ssl_st {
-  explicit ssl_st(SSL_CTX *ctx_arg);
+  explicit ssl_st(bssl::SSLContext *ctx_arg);
   ssl_st(const ssl_st &) = delete;
   ssl_st &operator=(const ssl_st &) = delete;
   ~ssl_st();
@@ -4258,11 +4262,11 @@
 
   void (*info_callback)(const SSL *ssl, int type, int value) = nullptr;
 
-  bssl::UniquePtr<SSL_CTX> ctx;
+  bssl::UniquePtr<bssl::SSLContext> ctx;
 
-  // session_ctx is the |SSL_CTX| used for the session cache and related
+  // session_ctx is the |SSLContext| used for the session cache and related
   // settings.
-  bssl::UniquePtr<SSL_CTX> session_ctx;
+  bssl::UniquePtr<bssl::SSLContext> session_ctx;
 
   // extra application data
   CRYPTO_EX_DATA ex_data;
diff --git a/ssl/ssl_asn1.cc b/ssl/ssl_asn1.cc
index ad43e77..84b00a7 100644
--- a/ssl/ssl_asn1.cc
+++ b/ssl/ssl_asn1.cc
@@ -835,10 +835,11 @@
 
 SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in, size_t in_len,
                                     const SSL_CTX *ctx) {
+  auto *ctx_impl = FromOpaque(ctx);
   CBS cbs;
   CBS_init(&cbs, in, in_len);
   UniquePtr<SSL_SESSION> ret =
-      SSL_SESSION_parse(&cbs, ctx->x509_method, ctx->pool.get());
+      SSL_SESSION_parse(&cbs, ctx_impl->x509_method, ctx_impl->pool.get());
   if (!ret) {
     return nullptr;
   }
diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc
index fb11a19..d66cad8 100644
--- a/ssl/ssl_cert.cc
+++ b/ssl/ssl_cert.cc
@@ -552,8 +552,8 @@
 int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs,
                               size_t num_certs, EVP_PKEY *privkey,
                               const SSL_PRIVATE_KEY_METHOD *privkey_method) {
-  return cert_set_chain_and_key(ctx->cert.get(), certs, num_certs, privkey,
-                                privkey_method);
+  return cert_set_chain_and_key(FromOpaque(ctx)->cert.get(), certs, num_certs,
+                                privkey, privkey_method);
 }
 
 void SSL_certs_clear(SSL *ssl) {
@@ -568,7 +568,7 @@
 }
 
 const STACK_OF(CRYPTO_BUFFER) *SSL_CTX_get0_chain(const SSL_CTX *ctx) {
-  return ctx->cert->legacy_credential->chain.get();
+  return FromOpaque(ctx)->cert->legacy_credential->chain.get();
 }
 
 const STACK_OF(CRYPTO_BUFFER) *SSL_get0_chain(const SSL *ssl) {
@@ -585,7 +585,7 @@
     return 0;
   }
 
-  return ssl_set_cert(ctx->cert.get(), std::move(buffer));
+  return ssl_set_cert(FromOpaque(ctx)->cert.get(), std::move(buffer));
 }
 
 int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
@@ -599,7 +599,7 @@
 
 void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg),
                          void *arg) {
-  ssl_cert_set_cert_cb(ctx->cert.get(), cb, arg);
+  ssl_cert_set_cert_cb(FromOpaque(ctx)->cert.get(), cb, arg);
 }
 
 void SSL_set_cert_cb(SSL *ssl, int (*cb)(SSL *ssl, void *arg), void *arg) {
@@ -628,8 +628,9 @@
 int SSL_CTX_set_signed_cert_timestamp_list(SSL_CTX *ctx, const uint8_t *list,
                                            size_t list_len) {
   UniquePtr<CRYPTO_BUFFER> buf(CRYPTO_BUFFER_new(list, list_len, nullptr));
-  return buf != nullptr && SSL_CREDENTIAL_set1_signed_cert_timestamp_list(
-                               ctx->cert->legacy_credential.get(), buf.get());
+  return buf != nullptr &&
+         SSL_CREDENTIAL_set1_signed_cert_timestamp_list(
+             FromOpaque(ctx)->cert->legacy_credential.get(), buf.get());
 }
 
 int SSL_set_signed_cert_timestamp_list(SSL *ssl, const uint8_t *list,
@@ -647,8 +648,9 @@
                               size_t response_len) {
   UniquePtr<CRYPTO_BUFFER> buf(
       CRYPTO_BUFFER_new(response, response_len, nullptr));
-  return buf != nullptr && SSL_CREDENTIAL_set1_ocsp_response(
-                               ctx->cert->legacy_credential.get(), buf.get());
+  return buf != nullptr &&
+         SSL_CREDENTIAL_set1_ocsp_response(
+             FromOpaque(ctx)->cert->legacy_credential.get(), buf.get());
 }
 
 int SSL_set_ocsp_response(SSL *ssl, const uint8_t *response,
@@ -664,8 +666,9 @@
 }
 
 void SSL_CTX_set0_client_CAs(SSL_CTX *ctx, STACK_OF(CRYPTO_BUFFER) *name_list) {
-  ctx->x509_method->ssl_ctx_flush_cached_client_CA(ctx);
-  ctx->client_CA.reset(name_list);
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->x509_method->ssl_ctx_flush_cached_client_CA(ctx_impl);
+  ctx_impl->client_CA.reset(name_list);
 }
 
 void SSL_set0_client_CAs(SSL *ssl, STACK_OF(CRYPTO_BUFFER) *name_list) {
diff --git a/ssl/ssl_credential.cc b/ssl/ssl_credential.cc
index b1090a2..173b851 100644
--- a/ssl/ssl_credential.cc
+++ b/ssl/ssl_credential.cc
@@ -664,7 +664,7 @@
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
-  return ctx->cert->credentials.Push(UpRef(cred_impl));
+  return FromOpaque(ctx)->cert->credentials.Push(UpRef(cred_impl));
 }
 
 int SSL_add1_credential(SSL *ssl, const SSL_CREDENTIAL *cred) {
diff --git a/ssl/ssl_file.cc b/ssl/ssl_file.cc
index e2df63d..4621290 100644
--- a/ssl/ssl_file.cc
+++ b/ssl/ssl_file.cc
@@ -27,6 +27,8 @@
 #include "internal.h"
 
 
+using namespace bssl;
+
 static int xname_cmp(const X509_NAME *const *a, const X509_NAME *const *b) {
   return X509_NAME_cmp(*a, *b);
 }
@@ -37,7 +39,7 @@
   // duplicates. This implementation preserves that behavior, but only sorts at
   // the end, to avoid a quadratic running time. Existing duplicates in |out|
   // are preserved, but do not introduce new duplicates.
-  bssl::UniquePtr<STACK_OF(X509_NAME)> to_append(sk_X509_NAME_new(xname_cmp));
+  UniquePtr<STACK_OF(X509_NAME)> to_append(sk_X509_NAME_new(xname_cmp));
   if (to_append == nullptr) {
     return 0;
   }
@@ -53,8 +55,7 @@
   sk_X509_NAME_sort(out);
   bool first = true;
   for (;;) {
-    bssl::UniquePtr<X509> x509(
-        PEM_read_bio_X509(bio, nullptr, nullptr, nullptr));
+    UniquePtr<X509> x509(PEM_read_bio_X509(bio, nullptr, nullptr, nullptr));
     if (x509 == nullptr) {
       if (first && !allow_empty) {
         return 0;
@@ -73,9 +74,8 @@
       continue;
     }
 
-    bssl::UniquePtr<X509_NAME> copy(X509_NAME_dup(subject));
-    if (copy == nullptr ||
-        !bssl::PushToStack(to_append.get(), std::move(copy))) {
+    UniquePtr<X509_NAME> copy(X509_NAME_dup(subject));
+    if (copy == nullptr || !PushToStack(to_append.get(), std::move(copy))) {
       return 0;
     }
   }
@@ -84,14 +84,14 @@
   sk_X509_NAME_sort(to_append.get());
   size_t num = sk_X509_NAME_num(to_append.get());
   for (size_t i = 0; i < num; i++) {
-    bssl::UniquePtr<X509_NAME> name(sk_X509_NAME_value(to_append.get(), i));
+    UniquePtr<X509_NAME> name(sk_X509_NAME_value(to_append.get(), i));
     sk_X509_NAME_set(to_append.get(), i, nullptr);
     if (i + 1 < num &&
         X509_NAME_cmp(name.get(), sk_X509_NAME_value(to_append.get(), i + 1)) ==
             0) {
       continue;
     }
-    if (!bssl::PushToStack(out, std::move(name))) {
+    if (!PushToStack(out, std::move(name))) {
       return 0;
     }
   }
@@ -107,11 +107,11 @@
 }
 
 STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     return nullptr;
   }
-  bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
+  UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
   if (ret == nullptr ||  //
       !add_bio_cert_subjects_to_stack(ret.get(), in.get(),
                                       /*allow_empty=*/false)) {
@@ -122,7 +122,7 @@
 
 int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *out,
                                         const char *file) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     return 0;
   }
@@ -130,14 +130,14 @@
 }
 
 int SSL_use_certificate_file(SSL *ssl, const char *file, int type) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
   }
 
   int reason_code;
-  bssl::UniquePtr<X509> x;
+  UniquePtr<X509> x;
   if (type == SSL_FILETYPE_ASN1) {
     reason_code = ERR_R_ASN1_LIB;
     x.reset(d2i_X509_bio(in.get(), nullptr));
@@ -160,14 +160,14 @@
 }
 
 int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
   }
 
   int reason_code;
-  bssl::UniquePtr<RSA> rsa;
+  UniquePtr<RSA> rsa;
   if (type == SSL_FILETYPE_ASN1) {
     reason_code = ERR_R_ASN1_LIB;
     rsa.reset(d2i_RSAPrivateKey_bio(in.get(), nullptr));
@@ -189,14 +189,14 @@
 }
 
 int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
   }
 
   int reason_code;
-  bssl::UniquePtr<EVP_PKEY> pkey;
+  UniquePtr<EVP_PKEY> pkey;
   if (type == SSL_FILETYPE_PEM) {
     reason_code = ERR_R_PEM_LIB;
     pkey.reset(PEM_read_bio_PrivateKey(
@@ -219,21 +219,23 @@
 }
 
 int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  auto *ctx_impl = FromOpaque(ctx);
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
   }
 
   int reason_code;
-  bssl::UniquePtr<X509> x;
+  UniquePtr<X509> x;
   if (type == SSL_FILETYPE_ASN1) {
     reason_code = ERR_R_ASN1_LIB;
     x.reset(d2i_X509_bio(in.get(), nullptr));
   } else if (type == SSL_FILETYPE_PEM) {
     reason_code = ERR_R_PEM_LIB;
-    x.reset(PEM_read_bio_X509(in.get(), nullptr, ctx->default_passwd_callback,
-                              ctx->default_passwd_callback_userdata));
+    x.reset(PEM_read_bio_X509(in.get(), nullptr,
+                              ctx_impl->default_passwd_callback,
+                              ctx_impl->default_passwd_callback_userdata));
   } else {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
     return 0;
@@ -248,22 +250,23 @@
 }
 
 int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  auto *ctx_impl = FromOpaque(ctx);
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
   }
 
   int reason_code;
-  bssl::UniquePtr<RSA> rsa;
+  UniquePtr<RSA> rsa;
   if (type == SSL_FILETYPE_ASN1) {
     reason_code = ERR_R_ASN1_LIB;
     rsa.reset(d2i_RSAPrivateKey_bio(in.get(), nullptr));
   } else if (type == SSL_FILETYPE_PEM) {
     reason_code = ERR_R_PEM_LIB;
     rsa.reset(PEM_read_bio_RSAPrivateKey(
-        in.get(), nullptr, ctx->default_passwd_callback,
-        ctx->default_passwd_callback_userdata));
+        in.get(), nullptr, ctx_impl->default_passwd_callback,
+        ctx_impl->default_passwd_callback_userdata));
   } else {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
     return 0;
@@ -277,19 +280,20 @@
 }
 
 int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  auto *ctx_impl = FromOpaque(ctx);
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
   }
 
   int reason_code;
-  bssl::UniquePtr<EVP_PKEY> pkey;
+  UniquePtr<EVP_PKEY> pkey;
   if (type == SSL_FILETYPE_PEM) {
     reason_code = ERR_R_PEM_LIB;
-    pkey.reset(PEM_read_bio_PrivateKey(in.get(), nullptr,
-                                       ctx->default_passwd_callback,
-                                       ctx->default_passwd_callback_userdata));
+    pkey.reset(PEM_read_bio_PrivateKey(
+        in.get(), nullptr, ctx_impl->default_passwd_callback,
+        ctx_impl->default_passwd_callback_userdata));
   } else if (type == SSL_FILETYPE_ASN1) {
     reason_code = ERR_R_ASN1_LIB;
     pkey.reset(d2i_PrivateKey_bio(in.get(), nullptr));
@@ -310,15 +314,16 @@
 // by a sequence of CA certificates that should be sent to the peer in the
 // Certificate message.
 int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) {
-  bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
+  auto *ctx_impl = FromOpaque(ctx);
+  UniquePtr<BIO> in(BIO_new_file(file, "rb"));
   if (in == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
   }
 
-  bssl::UniquePtr<X509> x(
-      PEM_read_bio_X509_AUX(in.get(), nullptr, ctx->default_passwd_callback,
-                            ctx->default_passwd_callback_userdata));
+  UniquePtr<X509> x(PEM_read_bio_X509_AUX(
+      in.get(), nullptr, ctx_impl->default_passwd_callback,
+      ctx_impl->default_passwd_callback_userdata));
   if (x == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB);
     return 0;
@@ -331,9 +336,9 @@
   // If we could set up our certificate, now proceed to the CA certificates.
   SSL_CTX_clear_chain_certs(ctx);
   for (;;) {
-    bssl::UniquePtr<X509> ca(
-        PEM_read_bio_X509(in.get(), nullptr, ctx->default_passwd_callback,
-                          ctx->default_passwd_callback_userdata));
+    UniquePtr<X509> ca(
+        PEM_read_bio_X509(in.get(), nullptr, ctx_impl->default_passwd_callback,
+                          ctx_impl->default_passwd_callback_userdata));
     if (ca == nullptr) {
       break;
     }
@@ -352,17 +357,17 @@
 }
 
 void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) {
-  ctx->default_passwd_callback = cb;
+  FromOpaque(ctx)->default_passwd_callback = cb;
 }
 
 pem_password_cb *SSL_CTX_get_default_passwd_cb(const SSL_CTX *ctx) {
-  return ctx->default_passwd_callback;
+  return FromOpaque(ctx)->default_passwd_callback;
 }
 
 void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *data) {
-  ctx->default_passwd_callback_userdata = data;
+  FromOpaque(ctx)->default_passwd_callback_userdata = data;
 }
 
 void *SSL_CTX_get_default_passwd_cb_userdata(const SSL_CTX *ctx) {
-  return ctx->default_passwd_callback_userdata;
+  return FromOpaque(ctx)->default_passwd_callback_userdata;
 }
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index 89702ea..491c717 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <iterator>
+#include <utility>
 
 #include <assert.h>
 #include <limits.h>
@@ -230,7 +231,7 @@
                     const_cast<SSL *>(ssl), ssl->msg_callback_arg);
 }
 
-OPENSSL_timeval ssl_ctx_get_current_time(const SSL_CTX *ctx) {
+OPENSSL_timeval ssl_ctx_get_current_time(const SSLContext *ctx) {
   if (ctx->current_time_cb != nullptr) {
     // TODO(davidben): Update current_time_cb to use OPENSSL_timeval. See
     // https://crbug.com/boringssl/155.
@@ -268,7 +269,9 @@
 #endif
 }
 
-void SSL_CTX_set_handoff_mode(SSL_CTX *ctx, bool on) { ctx->handoff = on; }
+void SSL_CTX_set_handoff_mode(SSL_CTX *ctx, bool on) {
+  FromOpaque(ctx)->handoff = on;
+}
 
 static bool ssl_can_renegotiate(const SSL *ssl) {
   if (ssl->server || SSL_is_dtls(ssl)) {
@@ -347,8 +350,9 @@
 
 void SSL_CTX_set_aes_hw_override_for_testing(SSL_CTX *ctx,
                                              bool override_value) {
-  ctx->aes_hw_override = true;
-  ctx->aes_hw_override_value = override_value;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->aes_hw_override = true;
+  ctx_impl->aes_hw_override_value = override_value;
 }
 
 void SSL_set_aes_hw_override_for_testing(SSL *ssl, bool override_value) {
@@ -374,7 +378,7 @@
   return Span(a->session_id) == b->session_id ? 0 : 1;
 }
 
-ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method)
+bssl::SSLContext::SSLContext(const SSL_METHOD *ssl_method)
     : RefCounted(CheckSubClass()),
       method(ssl_method->method),
       x509_method(ssl_method->x509_method),
@@ -395,7 +399,7 @@
   CRYPTO_new_ex_data(&ex_data);
 }
 
-ssl_ctx_st::~ssl_ctx_st() {
+bssl::SSLContext::~SSLContext() {
   // Free the internal session cache. Note that this calls the caller-supplied
   // remove callback, so we must do it before clearing ex_data. (See ticket
   // [openssl.org #212].)
@@ -413,7 +417,7 @@
     return nullptr;
   }
 
-  UniquePtr<SSL_CTX> ret = MakeUnique<SSL_CTX>(method);
+  UniquePtr<SSLContext> ret = MakeUnique<SSLContext>(method);
   if (!ret) {
     return nullptr;
   }
@@ -452,17 +456,17 @@
 }
 
 int SSL_CTX_up_ref(SSL_CTX *ctx) {
-  ctx->UpRefInternal();
+  FromOpaque(ctx)->UpRefInternal();
   return 1;
 }
 
 void SSL_CTX_free(SSL_CTX *ctx) {
   if (ctx != nullptr) {
-    ctx->DecRefInternal();
+    FromOpaque(ctx)->DecRefInternal();
   }
 }
 
-ssl_st::ssl_st(SSL_CTX *ctx_arg)
+ssl_st::ssl_st(SSLContext *ctx_arg)
     : method(ctx_arg->method),
       max_send_fragment(ctx_arg->max_send_fragment),
       msg_callback(ctx_arg->msg_callback),
@@ -489,12 +493,13 @@
 }
 
 SSL *SSL_new(SSL_CTX *ctx) {
-  if (ctx == nullptr) {
+  auto *ctx_impl = FromOpaque(ctx);
+  if (ctx_impl == nullptr) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NULL_SSL_CTX);
     return nullptr;
   }
 
-  UniquePtr<SSL> ssl = MakeUnique<SSL>(ctx);
+  UniquePtr<SSL> ssl = MakeUnique<SSL>(ctx_impl);
   if (ssl == nullptr) {
     return nullptr;
   }
@@ -503,63 +508,64 @@
   if (ssl->config == nullptr) {
     return nullptr;
   }
-  ssl->config->conf_min_version = ctx->conf_min_version;
-  ssl->config->conf_max_version = ctx->conf_max_version;
+  ssl->config->conf_min_version = ctx_impl->conf_min_version;
+  ssl->config->conf_max_version = ctx_impl->conf_max_version;
 
-  ssl->config->cert = ssl_cert_dup(ctx->cert.get());
+  ssl->config->cert = ssl_cert_dup(ctx_impl->cert.get());
   if (ssl->config->cert == nullptr) {
     return nullptr;
   }
 
-  ssl->config->verify_mode = ctx->verify_mode;
-  ssl->config->verify_callback = ctx->default_verify_callback;
-  ssl->config->custom_verify_callback = ctx->custom_verify_callback;
+  ssl->config->verify_mode = ctx_impl->verify_mode;
+  ssl->config->verify_callback = ctx_impl->default_verify_callback;
+  ssl->config->custom_verify_callback = ctx_impl->custom_verify_callback;
   ssl->config->retain_only_sha256_of_client_certs =
-      ctx->retain_only_sha256_of_client_certs;
-  ssl->config->permute_extensions = ctx->permute_extensions;
-  ssl->config->aes_hw_override = ctx->aes_hw_override;
-  ssl->config->aes_hw_override_value = ctx->aes_hw_override_value;
-  ssl->config->compliance_policy = ctx->compliance_policy;
+      ctx_impl->retain_only_sha256_of_client_certs;
+  ssl->config->permute_extensions = ctx_impl->permute_extensions;
+  ssl->config->aes_hw_override = ctx_impl->aes_hw_override;
+  ssl->config->aes_hw_override_value = ctx_impl->aes_hw_override_value;
+  ssl->config->compliance_policy = ctx_impl->compliance_policy;
 
-  if (!ssl->config->supported_group_list.CopyFrom(ctx->supported_group_list) ||
+  if (!ssl->config->supported_group_list.CopyFrom(
+          ctx_impl->supported_group_list) ||
       !ssl->config->supported_group_list_flags.CopyFrom(
-          ctx->supported_group_list_flags) ||
+          ctx_impl->supported_group_list_flags) ||
       !ssl->config->alpn_client_proto_list.CopyFrom(
-          ctx->alpn_client_proto_list) ||
-      !ssl->config->verify_sigalgs.CopyFrom(ctx->verify_sigalgs) ||
+          ctx_impl->alpn_client_proto_list) ||
+      !ssl->config->verify_sigalgs.CopyFrom(ctx_impl->verify_sigalgs) ||
       !ssl->config->accepted_peer_cert_types.TryCopyFrom(
-          ctx->accepted_peer_cert_types) ||
+          ctx_impl->accepted_peer_cert_types) ||
       !ssl->config->available_client_cert_types.TryCopyFrom(
-          ctx->available_client_cert_types)) {
+          ctx_impl->available_client_cert_types)) {
     return nullptr;
   }
 
-  if (ctx->requested_trust_anchors) {
+  if (ctx_impl->requested_trust_anchors) {
     ssl->config->requested_trust_anchors.emplace();
     if (!ssl->config->requested_trust_anchors->CopyFrom(
-            *ctx->requested_trust_anchors)) {
+            *ctx_impl->requested_trust_anchors)) {
       return nullptr;
     }
   }
 
-  if (ctx->psk_identity_hint) {
+  if (ctx_impl->psk_identity_hint) {
     ssl->config->psk_identity_hint.reset(
-        OPENSSL_strdup(ctx->psk_identity_hint.get()));
+        OPENSSL_strdup(ctx_impl->psk_identity_hint.get()));
     if (ssl->config->psk_identity_hint == nullptr) {
       return nullptr;
     }
   }
-  ssl->config->psk_client_callback = ctx->psk_client_callback;
-  ssl->config->psk_server_callback = ctx->psk_server_callback;
+  ssl->config->psk_client_callback = ctx_impl->psk_client_callback;
+  ssl->config->psk_server_callback = ctx_impl->psk_server_callback;
 
-  ssl->config->channel_id_enabled = ctx->channel_id_enabled;
-  ssl->config->channel_id_private = UpRef(ctx->channel_id_private);
+  ssl->config->channel_id_enabled = ctx_impl->channel_id_enabled;
+  ssl->config->channel_id_private = UpRef(ctx_impl->channel_id_private);
 
   ssl->config->signed_cert_timestamps_enabled =
-      ctx->signed_cert_timestamps_enabled;
-  ssl->config->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
-  ssl->config->handoff = ctx->handoff;
-  ssl->quic_method = ctx->quic_method;
+      ctx_impl->signed_cert_timestamps_enabled;
+  ssl->config->ocsp_stapling_enabled = ctx_impl->ocsp_stapling_enabled;
+  ssl->config->handoff = ctx_impl->handoff;
+  ssl->quic_method = ctx_impl->quic_method;
 
   if (!ssl->method->ssl_new(ssl.get()) ||
       !ssl->ctx->x509_method->ssl_new(ssl->s3->hs.get())) {
@@ -1107,7 +1113,7 @@
 }
 
 void SSL_CTX_set_early_data_enabled(SSL_CTX *ctx, int enabled) {
-  ctx->enable_early_data = !!enabled;
+  FromOpaque(ctx)->enable_early_data = !!enabled;
 }
 
 void SSL_set_early_data_enabled(SSL *ssl, int enabled) {
@@ -1322,16 +1328,20 @@
 }
 
 uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
-  ctx->options |= options;
-  return ctx->options;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->options |= options;
+  return ctx_impl->options;
 }
 
 uint32_t SSL_CTX_clear_options(SSL_CTX *ctx, uint32_t options) {
-  ctx->options &= ~options;
-  return ctx->options;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->options &= ~options;
+  return ctx_impl->options;
 }
 
-uint32_t SSL_CTX_get_options(const SSL_CTX *ctx) { return ctx->options; }
+uint32_t SSL_CTX_get_options(const SSL_CTX *ctx) {
+  return FromOpaque(ctx)->options;
+}
 
 uint32_t SSL_set_options(SSL *ssl, uint32_t options) {
   ssl->options |= options;
@@ -1346,16 +1356,18 @@
 uint32_t SSL_get_options(const SSL *ssl) { return ssl->options; }
 
 uint32_t SSL_CTX_set_mode(SSL_CTX *ctx, uint32_t mode) {
-  ctx->mode |= mode;
-  return ctx->mode;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->mode |= mode;
+  return ctx_impl->mode;
 }
 
 uint32_t SSL_CTX_clear_mode(SSL_CTX *ctx, uint32_t mode) {
-  ctx->mode &= ~mode;
-  return ctx->mode;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->mode &= ~mode;
+  return ctx_impl->mode;
 }
 
-uint32_t SSL_CTX_get_mode(const SSL_CTX *ctx) { return ctx->mode; }
+uint32_t SSL_CTX_get_mode(const SSL_CTX *ctx) { return FromOpaque(ctx)->mode; }
 
 uint32_t SSL_set_mode(SSL *ssl, uint32_t mode) {
   ssl->mode |= mode;
@@ -1370,7 +1382,7 @@
 uint32_t SSL_get_mode(const SSL *ssl) { return ssl->mode; }
 
 void SSL_CTX_set1_buffer_pool(SSL_CTX *ctx, CRYPTO_BUFFER_POOL *pool) {
-  ctx->pool = UpRef(pool);
+  FromOpaque(ctx)->pool = UpRef(pool);
 }
 
 void SSL_CTX_set0_buffer_pool(SSL_CTX *ctx, CRYPTO_BUFFER_POOL *pool) {
@@ -1426,7 +1438,8 @@
 
 int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const uint8_t *sid_ctx,
                                    size_t sid_ctx_len) {
-  return set_session_id_context(ctx->cert.get(), sid_ctx, sid_ctx_len);
+  return set_session_id_context(FromOpaque(ctx)->cert.get(), sid_ctx,
+                                sid_ctx_len);
 }
 
 int SSL_set_session_id_context(SSL *ssl, const uint8_t *sid_ctx,
@@ -1619,7 +1632,7 @@
 int SSL_CTX_check_private_key(const SSL_CTX *ctx) {
   // There is no need to actually check consistency because inconsistent values
   // can never be configured.
-  return has_cert_and_key(ctx->cert->legacy_credential.get());
+  return has_cert_and_key(FromOpaque(ctx)->cert->legacy_credential.get());
 }
 
 int SSL_check_private_key(const SSL *ssl) {
@@ -1685,14 +1698,14 @@
 }
 
 size_t SSL_CTX_get_max_cert_list(const SSL_CTX *ctx) {
-  return ctx->max_cert_list;
+  return FromOpaque(ctx)->max_cert_list;
 }
 
 void SSL_CTX_set_max_cert_list(SSL_CTX *ctx, size_t max_cert_list) {
   if (max_cert_list > kMaxHandshakeSize) {
     max_cert_list = kMaxHandshakeSize;
   }
-  ctx->max_cert_list = (uint32_t)max_cert_list;
+  FromOpaque(ctx)->max_cert_list = (uint32_t)max_cert_list;
 }
 
 size_t SSL_get_max_cert_list(const SSL *ssl) { return ssl->max_cert_list; }
@@ -1711,7 +1724,7 @@
   if (max_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) {
     max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH;
   }
-  ctx->max_send_fragment = (uint16_t)max_send_fragment;
+  FromOpaque(ctx)->max_send_fragment = (uint16_t)max_send_fragment;
 
   return 1;
 }
@@ -1745,32 +1758,30 @@
 }
 
 size_t SSL_CTX_sess_number(const SSL_CTX *ctx) {
-  MutexReadLock lock(&ctx->lock);
-  return lh_SSL_SESSION_num_items(ctx->sessions);
+  auto *ctx_impl = FromOpaque(ctx);
+  MutexReadLock lock(&ctx_impl->lock);
+  return lh_SSL_SESSION_num_items(ctx_impl->sessions);
 }
 
 unsigned long SSL_CTX_sess_set_cache_size(SSL_CTX *ctx, unsigned long size) {
-  unsigned long ret = ctx->session_cache_size;
-  ctx->session_cache_size = size;
-  return ret;
+  return std::exchange(FromOpaque(ctx)->session_cache_size, size);
 }
 
 unsigned long SSL_CTX_sess_get_cache_size(const SSL_CTX *ctx) {
-  return ctx->session_cache_size;
+  return FromOpaque(ctx)->session_cache_size;
 }
 
 int SSL_CTX_set_session_cache_mode(SSL_CTX *ctx, int mode) {
-  int ret = ctx->session_cache_mode;
-  ctx->session_cache_mode = mode;
-  return ret;
+  return std::exchange(FromOpaque(ctx)->session_cache_mode, mode);
 }
 
 int SSL_CTX_get_session_cache_mode(const SSL_CTX *ctx) {
-  return ctx->session_cache_mode;
+  return FromOpaque(ctx)->session_cache_mode;
 }
 
 
 int SSL_CTX_get_tlsext_ticket_keys(SSL_CTX *ctx, void *out, size_t len) {
+  auto *ctx_impl = FromOpaque(ctx);
   if (out == nullptr) {
     return 48;
   }
@@ -1781,19 +1792,20 @@
 
   // The default ticket keys are initialized lazily. Trigger a key
   // rotation to initialize them.
-  if (!ssl_ctx_rotate_ticket_encryption_key(ctx)) {
+  if (!ssl_ctx_rotate_ticket_encryption_key(ctx_impl)) {
     return 0;
   }
 
   uint8_t *out_bytes = reinterpret_cast<uint8_t *>(out);
-  MutexReadLock lock(&ctx->lock);
-  OPENSSL_memcpy(out_bytes, ctx->ticket_key_current->name, 16);
-  OPENSSL_memcpy(out_bytes + 16, ctx->ticket_key_current->hmac_key, 16);
-  OPENSSL_memcpy(out_bytes + 32, ctx->ticket_key_current->aes_key, 16);
+  MutexReadLock lock(&ctx_impl->lock);
+  OPENSSL_memcpy(out_bytes, ctx_impl->ticket_key_current->name, 16);
+  OPENSSL_memcpy(out_bytes + 16, ctx_impl->ticket_key_current->hmac_key, 16);
+  OPENSSL_memcpy(out_bytes + 32, ctx_impl->ticket_key_current->aes_key, 16);
   return 1;
 }
 
 int SSL_CTX_set_tlsext_ticket_keys(SSL_CTX *ctx, const void *in, size_t len) {
+  auto *ctx_impl = FromOpaque(ctx);
   if (in == nullptr) {
     return 48;
   }
@@ -1812,8 +1824,8 @@
   // Disable automatic key rotation for manually-configured keys. This is now
   // the caller's responsibility.
   key->next_rotation_tv_sec = 0;
-  ctx->ticket_key_current = std::move(key);
-  ctx->ticket_key_prev.reset();
+  ctx_impl->ticket_key_current = std::move(key);
+  ctx_impl->ticket_key_prev.reset();
   return 1;
 }
 
@@ -1821,7 +1833,7 @@
     SSL_CTX *ctx,
     int (*callback)(SSL *ssl, uint8_t *key_name, uint8_t *iv,
                     EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, int encrypt)) {
-  ctx->ticket_key_cb = callback;
+  FromOpaque(ctx)->ticket_key_cb = callback;
   return 1;
 }
 
@@ -1921,9 +1933,10 @@
 int SSL_CTX_set1_group_ids_with_flags(SSL_CTX *ctx, const uint16_t *group_ids,
                                       const uint32_t *flags,
                                       size_t num_group_ids) {
+  auto *ctx_impl = FromOpaque(ctx);
   return set_group_ids_and_flags(group_ids, flags, num_group_ids,
-                                 &ctx->supported_group_list,
-                                 &ctx->supported_group_list_flags);
+                                 &ctx_impl->supported_group_list,
+                                 &ctx_impl->supported_group_list_flags);
 }
 
 int SSL_set1_group_ids(SSL *ssl, const uint16_t *group_ids,
@@ -1971,9 +1984,11 @@
 }
 
 int SSL_CTX_set1_groups(SSL_CTX *ctx, const int *groups, size_t num_groups) {
-  return ssl_nids_to_group_ids(&ctx->supported_group_list,
+  auto *ctx_impl = FromOpaque(ctx);
+  return ssl_nids_to_group_ids(&ctx_impl->supported_group_list,
                                Span(groups, num_groups)) &&
-         ctx->supported_group_list_flags.Init(ctx->supported_group_list.size());
+         ctx_impl->supported_group_list_flags.Init(
+             ctx_impl->supported_group_list.size());
 }
 
 int SSL_set1_groups(SSL *ssl, const int *groups, size_t num_groups) {
@@ -2031,8 +2046,10 @@
 }
 
 int SSL_CTX_set1_groups_list(SSL_CTX *ctx, const char *groups) {
-  return ssl_str_to_group_ids(&ctx->supported_group_list, groups) &&
-         ctx->supported_group_list_flags.Init(ctx->supported_group_list.size());
+  auto *ctx_impl = FromOpaque(ctx);
+  return ssl_str_to_group_ids(&ctx_impl->supported_group_list, groups) &&
+         ctx_impl->supported_group_list_flags.Init(
+             ctx_impl->supported_group_list.size());
 }
 
 int SSL_set1_groups_list(SSL *ssl, const char *groups) {
@@ -2097,14 +2114,15 @@
 int SSL_set_tmp_dh(SSL *ssl, const DH *dh) { return 1; }
 
 STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx) {
-  return ctx->cipher_list->ciphers.get();
+  return FromOpaque(ctx)->cipher_list->ciphers.get();
 }
 
 int SSL_CTX_cipher_in_group(const SSL_CTX *ctx, size_t i) {
-  if (i >= sk_SSL_CIPHER_num(ctx->cipher_list->ciphers.get())) {
+  auto *ctx_impl = FromOpaque(ctx);
+  if (i >= sk_SSL_CIPHER_num(ctx_impl->cipher_list->ciphers.get())) {
     return 0;
   }
-  return ctx->cipher_list->in_group_flags[i];
+  return ctx_impl->cipher_list->in_group_flags[i];
 }
 
 STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *ssl) {
@@ -2139,16 +2157,20 @@
 }
 
 int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) {
-  const bool has_aes_hw = ctx->aes_hw_override ? ctx->aes_hw_override_value
-                                               : EVP_has_aes_hardware();
-  return ssl_create_cipher_list(&ctx->cipher_list, has_aes_hw, str,
+  auto *ctx_impl = FromOpaque(ctx);
+  const bool has_aes_hw = ctx_impl->aes_hw_override
+                              ? ctx_impl->aes_hw_override_value
+                              : EVP_has_aes_hardware();
+  return ssl_create_cipher_list(&ctx_impl->cipher_list, has_aes_hw, str,
                                 false /* not strict */);
 }
 
 int SSL_CTX_set_strict_cipher_list(SSL_CTX *ctx, const char *str) {
-  const bool has_aes_hw = ctx->aes_hw_override ? ctx->aes_hw_override_value
-                                               : EVP_has_aes_hardware();
-  return ssl_create_cipher_list(&ctx->cipher_list, has_aes_hw, str,
+  auto *ctx_impl = FromOpaque(ctx);
+  const bool has_aes_hw = ctx_impl->aes_hw_override
+                              ? ctx_impl->aes_hw_override_value
+                              : EVP_has_aes_hardware();
+  return ssl_create_cipher_list(&ctx_impl->cipher_list, has_aes_hw, str,
                                 true /* strict */);
 }
 
@@ -2198,8 +2220,9 @@
 void SSL_CTX_set_custom_verify(
     SSL_CTX *ctx, int mode,
     enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)) {
-  ctx->verify_mode = mode;
-  ctx->custom_verify_callback = callback;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->verify_mode = mode;
+  ctx_impl->custom_verify_callback = callback;
 }
 
 void SSL_set_custom_verify(
@@ -2213,7 +2236,7 @@
 }
 
 void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) {
-  ctx->signed_cert_timestamps_enabled = true;
+  FromOpaque(ctx)->signed_cert_timestamps_enabled = true;
 }
 
 void SSL_enable_signed_cert_timestamps(SSL *ssl) {
@@ -2224,7 +2247,7 @@
 }
 
 void SSL_CTX_enable_ocsp_stapling(SSL_CTX *ctx) {
-  ctx->ocsp_stapling_enabled = true;
+  FromOpaque(ctx)->ocsp_stapling_enabled = true;
 }
 
 void SSL_enable_ocsp_stapling(SSL *ssl) {
@@ -2280,12 +2303,12 @@
 
 int SSL_CTX_set_tlsext_servername_callback(
     SSL_CTX *ctx, int (*callback)(SSL *ssl, int *out_alert, void *arg)) {
-  ctx->servername_callback = callback;
+  FromOpaque(ctx)->servername_callback = callback;
   return 1;
 }
 
 int SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg) {
-  ctx->servername_arg = arg;
+  FromOpaque(ctx)->servername_arg = arg;
   return 1;
 }
 
@@ -2349,8 +2372,9 @@
     SSL_CTX *ctx,
     int (*cb)(SSL *ssl, const uint8_t **out, unsigned *out_len, void *arg),
     void *arg) {
-  ctx->next_protos_advertised_cb = cb;
-  ctx->next_protos_advertised_cb_arg = arg;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->next_protos_advertised_cb = cb;
+  ctx_impl->next_protos_advertised_cb_arg = arg;
 }
 
 void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx,
@@ -2359,8 +2383,9 @@
                                                 const uint8_t *in,
                                                 unsigned in_len, void *arg),
                                       void *arg) {
-  ctx->next_proto_select_cb = cb;
-  ctx->next_proto_select_cb_arg = arg;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->next_proto_select_cb = cb;
+  ctx_impl->next_proto_select_cb_arg = arg;
 }
 
 int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const uint8_t *protos,
@@ -2371,7 +2396,7 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL_LIST);
     return 1;
   }
-  return ctx->alpn_client_proto_list.CopyFrom(span) ? 0 : 1;
+  return FromOpaque(ctx)->alpn_client_proto_list.CopyFrom(span) ? 0 : 1;
 }
 
 int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, size_t protos_len) {
@@ -2392,8 +2417,9 @@
                                           uint8_t *out_len, const uint8_t *in,
                                           unsigned in_len, void *arg),
                                 void *arg) {
-  ctx->alpn_select_cb = cb;
-  ctx->alpn_select_cb_arg = arg;
+  auto *ctx_impl = FromOpaque(ctx);
+  ctx_impl->alpn_select_cb = cb;
+  ctx_impl->alpn_select_cb_arg = arg;
 }
 
 void SSL_get0_alpn_selected(const SSL *ssl, const uint8_t **out_data,
@@ -2411,7 +2437,7 @@
 }
 
 void SSL_CTX_set_allow_unknown_alpn_protos(SSL_CTX *ctx, int enabled) {
-  ctx->allow_unknown_alpn_protos = !!enabled;
+  FromOpaque(ctx)->allow_unknown_alpn_protos = !!enabled;
 }
 
 int SSL_add_application_settings(SSL *ssl, const uint8_t *proto,
@@ -2456,7 +2482,8 @@
                                      ssl_cert_decompression_func_t decompress) {
   assert(compress != nullptr || decompress != nullptr);
 
-  for (const auto &alg : ctx->cert_compression_algs) {
+  auto *ctx_impl = FromOpaque(ctx);
+  for (const auto &alg : ctx_impl->cert_compression_algs) {
     if (alg.alg_id == alg_id) {
       return 0;
     }
@@ -2466,11 +2493,11 @@
   alg.alg_id = alg_id;
   alg.compress = compress;
   alg.decompress = decompress;
-  return ctx->cert_compression_algs.Push(alg);
+  return ctx_impl->cert_compression_algs.Push(alg);
 }
 
 void SSL_CTX_set_tls_channel_id_enabled(SSL_CTX *ctx, int enabled) {
-  ctx->channel_id_enabled = !!enabled;
+  FromOpaque(ctx)->channel_id_enabled = !!enabled;
 }
 
 int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx) {
@@ -2496,7 +2523,7 @@
     return 0;
   }
 
-  ctx->channel_id_private = UpRef(private_key);
+  FromOpaque(ctx)->channel_id_private = UpRef(private_key);
   return 1;
 }
 
@@ -2559,7 +2586,7 @@
 }
 
 EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx) {
-  return ctx->cert->legacy_credential->privkey.get();
+  return FromOpaque(ctx)->cert->legacy_credential->privkey.get();
 }
 
 const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl) {
@@ -2578,11 +2605,11 @@
 int SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; }
 
 void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) {
-  ctx->quiet_shutdown = (mode != 0);
+  FromOpaque(ctx)->quiet_shutdown = (mode != 0);
 }
 
 int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx) {
-  return ctx->quiet_shutdown;
+  return FromOpaque(ctx)->quiet_shutdown;
 }
 
 void SSL_set_quiet_shutdown(SSL *ssl, int mode) {
@@ -2632,18 +2659,19 @@
   }
 
   // One cannot change the X.509 callbacks during a connection.
-  if (ssl->ctx->x509_method != ctx->x509_method) {
+  auto *ctx_impl = FromOpaque(ctx);
+  if (ssl->ctx->x509_method != ctx_impl->x509_method) {
     assert(0);
     return nullptr;
   }
 
-  UniquePtr<CERT> new_cert = ssl_cert_dup(ctx->cert.get());
+  UniquePtr<CERT> new_cert = ssl_cert_dup(ctx_impl->cert.get());
   if (!new_cert) {
     return nullptr;
   }
 
   ssl->config->cert = std::move(new_cert);
-  ssl->ctx = UpRef(ctx);
+  ssl->ctx = UpRef(ctx_impl);
   ssl->enable_early_data = ssl->ctx->enable_early_data;
 
   return ssl->ctx.get();
@@ -2679,10 +2707,11 @@
 }
 
 int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method) {
-  if (ctx->method->is_dtls) {
+  auto *ctx_impl = FromOpaque(ctx);
+  if (ctx_impl->method->is_dtls) {
     return 0;
   }
-  ctx->quic_method = quic_method;
+  ctx_impl->quic_method = quic_method;
   return 1;
 }
 
@@ -2716,11 +2745,11 @@
 }
 
 int SSL_CTX_set_ex_data(SSL_CTX *ctx, int idx, void *data) {
-  return CRYPTO_set_ex_data(&ctx->ex_data, idx, data);
+  return CRYPTO_set_ex_data(&FromOpaque(ctx)->ex_data, idx, data);
 }
 
 void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) {
-  return CRYPTO_get_ex_data(&ctx->ex_data, idx);
+  return CRYPTO_get_ex_data(&FromOpaque(ctx)->ex_data, idx);
 }
 
 int SSL_want(const SSL *ssl) {
@@ -2771,7 +2800,8 @@
 }
 
 int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) {
-  return use_psk_identity_hint(&ctx->psk_identity_hint, identity_hint);
+  return use_psk_identity_hint(&FromOpaque(ctx)->psk_identity_hint,
+                               identity_hint);
 }
 
 int SSL_use_psk_identity_hint(SSL *ssl, const char *identity_hint) {
@@ -2817,7 +2847,7 @@
     SSL_CTX *ctx, unsigned (*cb)(SSL *ssl, const char *hint, char *identity,
                                  unsigned max_identity_len, uint8_t *psk,
                                  unsigned max_psk_len)) {
-  ctx->psk_client_callback = cb;
+  FromOpaque(ctx)->psk_client_callback = cb;
 }
 
 void SSL_set_psk_server_callback(SSL *ssl,
@@ -2833,18 +2863,18 @@
 void SSL_CTX_set_psk_server_callback(
     SSL_CTX *ctx, unsigned (*cb)(SSL *ssl, const char *identity, uint8_t *psk,
                                  unsigned max_psk_len)) {
-  ctx->psk_server_callback = cb;
+  FromOpaque(ctx)->psk_server_callback = cb;
 }
 
 void SSL_CTX_set_msg_callback(SSL_CTX *ctx,
                               void (*cb)(int write_p, int version,
                                          int content_type, const void *buf,
                                          size_t len, SSL *ssl, void *arg)) {
-  ctx->msg_callback = cb;
+  FromOpaque(ctx)->msg_callback = cb;
 }
 
 void SSL_CTX_set_msg_callback_arg(SSL_CTX *ctx, void *arg) {
-  ctx->msg_callback_arg = arg;
+  FromOpaque(ctx)->msg_callback_arg = arg;
 }
 
 void SSL_set_msg_callback(SSL *ssl,
@@ -2860,18 +2890,18 @@
 
 void SSL_CTX_set_keylog_callback(SSL_CTX *ctx,
                                  void (*cb)(const SSL *ssl, const char *line)) {
-  ctx->keylog_callback = cb;
+  FromOpaque(ctx)->keylog_callback = cb;
 }
 
 void (*SSL_CTX_get_keylog_callback(const SSL_CTX *ctx))(const SSL *ssl,
                                                         const char *line) {
-  return ctx->keylog_callback;
+  return FromOpaque(ctx)->keylog_callback;
 }
 
 void SSL_CTX_set_current_time_cb(SSL_CTX *ctx,
                                  void (*cb)(const SSL *ssl,
                                             struct timeval *out_clock)) {
-  ctx->current_time_cb = cb;
+  FromOpaque(ctx)->current_time_cb = cb;
 }
 
 int SSL_can_release_private_key(const SSL *ssl) {
@@ -2913,16 +2943,16 @@
 void SSL_CTX_set_select_certificate_cb(
     SSL_CTX *ctx,
     enum ssl_select_cert_result_t (*cb)(const SSL_CLIENT_HELLO *)) {
-  ctx->select_certificate_cb = cb;
+  FromOpaque(ctx)->select_certificate_cb = cb;
 }
 
 void SSL_CTX_set_dos_protection_cb(SSL_CTX *ctx,
                                    int (*cb)(const SSL_CLIENT_HELLO *)) {
-  ctx->dos_protection_cb = cb;
+  FromOpaque(ctx)->dos_protection_cb = cb;
 }
 
 void SSL_CTX_set_reverify_on_resume(SSL_CTX *ctx, int enabled) {
-  ctx->reverify_on_resume = !!enabled;
+  FromOpaque(ctx)->reverify_on_resume = !!enabled;
 }
 
 void SSL_set_enforce_rsa_key_usage(SSL *ssl, int enabled) {
@@ -3145,15 +3175,15 @@
 }
 
 void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, int enabled) {
-  ctx->retain_only_sha256_of_client_certs = !!enabled;
+  FromOpaque(ctx)->retain_only_sha256_of_client_certs = !!enabled;
 }
 
 void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled) {
-  ctx->grease_enabled = !!enabled;
+  FromOpaque(ctx)->grease_enabled = !!enabled;
 }
 
 void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled) {
-  ctx->permute_extensions = !!enabled;
+  FromOpaque(ctx)->permute_extensions = !!enabled;
 }
 
 void SSL_set_permute_extensions(SSL *ssl, int enabled) {
@@ -3168,7 +3198,7 @@
 }
 
 void SSL_CTX_set_false_start_allowed_without_alpn(SSL_CTX *ctx, int allowed) {
-  ctx->false_start_allowed_without_alpn = !!allowed;
+  FromOpaque(ctx)->false_start_allowed_without_alpn = !!allowed;
 }
 
 int SSL_used_hello_retry_request(const SSL *ssl) {
@@ -3278,7 +3308,7 @@
 
 void SSL_CTX_set_ticket_aead_method(SSL_CTX *ctx,
                                     const SSL_TICKET_AEAD_METHOD *aead_method) {
-  ctx->ticket_aead_method = aead_method;
+  FromOpaque(ctx)->ticket_aead_method = aead_method;
 }
 
 SSL_SESSION *SSL_process_tls13_new_session_ticket(SSL *ssl, const uint8_t *buf,
@@ -3312,11 +3342,13 @@
 int SSL_CTX_set_num_tickets(SSL_CTX *ctx, size_t num_tickets) {
   num_tickets = std::min(num_tickets, kMaxTickets);
   static_assert(kMaxTickets <= 0xff, "Too many tickets.");
-  ctx->num_tickets = static_cast<uint8_t>(num_tickets);
+  FromOpaque(ctx)->num_tickets = static_cast<uint8_t>(num_tickets);
   return 1;
 }
 
-size_t SSL_CTX_get_num_tickets(const SSL_CTX *ctx) { return ctx->num_tickets; }
+size_t SSL_CTX_get_num_tickets(const SSL_CTX *ctx) {
+  return FromOpaque(ctx)->num_tickets;
+}
 
 int SSL_set_tlsext_status_type(SSL *ssl, int type) {
   if (!ssl->config) {
@@ -3355,12 +3387,12 @@
 
 int SSL_CTX_set_tlsext_status_cb(SSL_CTX *ctx,
                                  int (*callback)(SSL *ssl, void *arg)) {
-  ctx->legacy_ocsp_callback = callback;
+  FromOpaque(ctx)->legacy_ocsp_callback = callback;
   return 1;
 }
 
 int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg) {
-  ctx->legacy_ocsp_callback_arg = arg;
+  FromOpaque(ctx)->legacy_ocsp_callback_arg = arg;
   return 1;
 }
 
@@ -3422,7 +3454,7 @@
     "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:"
     "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
 
-static int Configure(SSL_CTX *ctx) {
+static int Configure(SSLContext *ctx) {
   ctx->compliance_policy = ssl_compliance_policy_fips_202205;
 
   return
@@ -3475,7 +3507,7 @@
     "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:"
     "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
 
-static int Configure(SSL_CTX *ctx) {
+static int Configure(SSLContext *ctx) {
   ctx->compliance_policy = ssl_compliance_policy_wpa3_192_202304;
 
   return SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION) &&
@@ -3502,7 +3534,7 @@
 
 namespace cnsa202407 {
 
-static int Configure(SSL_CTX *ctx) {
+static int Configure(SSLContext *ctx) {
   ctx->compliance_policy = ssl_compliance_policy_cnsa_202407;
   return 1;
 }
@@ -3533,7 +3565,7 @@
     "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:"
     "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
 
-static int Configure(SSL_CTX *ctx) {
+static int Configure(SSLContext *ctx) {
   ctx->compliance_policy = ssl_compliance_policy_cnsa1_202603;
 
   return SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION) &&
@@ -3572,7 +3604,7 @@
     SSL_SIGN_RSA_PKCS1_SHA384,
 };
 
-static int Configure(SSL_CTX *ctx) {
+static int Configure(SSLContext *ctx) {
   ctx->compliance_policy = ssl_compliance_policy_cnsa2_202603;
 
   return SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION) &&
@@ -3597,24 +3629,25 @@
 
 int SSL_CTX_set_compliance_policy(SSL_CTX *ctx,
                                   enum ssl_compliance_policy_t policy) {
+  auto *ctx_impl = FromOpaque(ctx);
   switch (policy) {
     case ssl_compliance_policy_fips_202205:
-      return fips202205::Configure(ctx);
+      return fips202205::Configure(ctx_impl);
     case ssl_compliance_policy_wpa3_192_202304:
-      return wpa202304::Configure(ctx);
+      return wpa202304::Configure(ctx_impl);
     case ssl_compliance_policy_cnsa_202407:
-      return cnsa202407::Configure(ctx);
+      return cnsa202407::Configure(ctx_impl);
     case ssl_compliance_policy_cnsa1_202603:
-      return cnsa1_202603::Configure(ctx);
+      return cnsa1_202603::Configure(ctx_impl);
     case ssl_compliance_policy_cnsa2_202603:
-      return cnsa2_202603::Configure(ctx);
+      return cnsa2_202603::Configure(ctx_impl);
     default:
       return 0;
   }
 }
 
 enum ssl_compliance_policy_t SSL_CTX_get_compliance_policy(const SSL_CTX *ctx) {
-  return ctx->compliance_policy;
+  return FromOpaque(ctx)->compliance_policy;
 }
 
 int SSL_set_compliance_policy(SSL *ssl, enum ssl_compliance_policy_t policy) {
@@ -3659,7 +3692,7 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TRUST_ANCHOR_LIST);
     return 0;
   }
-  return ctx->cert->available_trust_anchors.CopyFrom(span);
+  return FromOpaque(ctx)->cert->available_trust_anchors.CopyFrom(span);
 }
 
 int SSL_set1_available_trust_anchors(SSL *ssl, const uint8_t *ids,
@@ -3686,7 +3719,7 @@
   if (!copy.CopyFrom(span)) {
     return 0;
   }
-  ctx->requested_trust_anchors = std::move(copy);
+  FromOpaque(ctx)->requested_trust_anchors = std::move(copy);
   return 1;
 }
 
@@ -3742,7 +3775,7 @@
 
 int SSL_CTX_set1_accepted_peer_cert_types(SSL_CTX *ctx, const uint8_t *values,
                                           size_t num_values) {
-  return set1_cert_types(&ctx->accepted_peer_cert_types,
+  return set1_cert_types(&FromOpaque(ctx)->accepted_peer_cert_types,
                          Span(values, num_values));
 }
 
@@ -3758,7 +3791,7 @@
 int SSL_CTX_set1_available_client_cert_types(SSL_CTX *ctx,
                                              const uint8_t *values,
                                              size_t num_values) {
-  return set1_cert_types(&ctx->available_client_cert_types,
+  return set1_cert_types(&FromOpaque(ctx)->available_client_cert_types,
                          Span(values, num_values));
 }
 
diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc
index b48becd..c489290 100644
--- a/ssl/ssl_privkey.cc
+++ b/ssl/ssl_privkey.cc
@@ -456,8 +456,8 @@
     return 0;
   }
 
-  return SSL_CREDENTIAL_set1_private_key(ctx->cert->legacy_credential.get(),
-                                         pkey);
+  return SSL_CREDENTIAL_set1_private_key(
+      FromOpaque(ctx)->cert->legacy_credential.get(), pkey);
 }
 
 int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *der,
@@ -489,7 +489,7 @@
 void SSL_CTX_set_private_key_method(SSL_CTX *ctx,
                                     const SSL_PRIVATE_KEY_METHOD *key_method) {
   BSSL_CHECK(SSL_CREDENTIAL_set_private_key_method(
-      ctx->cert->legacy_credential.get(), key_method));
+      FromOpaque(ctx)->cert->legacy_credential.get(), key_method));
 }
 
 static constexpr size_t kMaxSignatureAlgorithmNameLen = 24;
@@ -654,7 +654,7 @@
 int SSL_CTX_set_signing_algorithm_prefs(SSL_CTX *ctx, const uint16_t *prefs,
                                         size_t num_prefs) {
   return SSL_CREDENTIAL_set1_signing_algorithm_prefs(
-      ctx->cert->legacy_credential.get(), prefs, num_prefs);
+      FromOpaque(ctx)->cert->legacy_credential.get(), prefs, num_prefs);
 }
 
 int SSL_set_signing_algorithm_prefs(SSL *ssl, const uint16_t *prefs,
@@ -947,7 +947,8 @@
 
 int SSL_CTX_set_verify_algorithm_prefs(SSL_CTX *ctx, const uint16_t *prefs,
                                        size_t num_prefs) {
-  return set_sigalg_prefs(&ctx->verify_sigalgs, Span(prefs, num_prefs));
+  return set_sigalg_prefs(&FromOpaque(ctx)->verify_sigalgs,
+                          Span(prefs, num_prefs));
 }
 
 int SSL_set_verify_algorithm_prefs(SSL *ssl, const uint16_t *prefs,
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index 4190b32..8948d98 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -42,8 +42,8 @@
 
 static ExDataClass g_ex_data_class(/*with_app_data=*/true);
 
-static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *session);
-static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session);
+static void SSL_SESSION_list_remove(SSLContext *ctx, SSL_SESSION *session);
+static void SSL_SESSION_list_add(SSLContext *ctx, SSL_SESSION *session);
 
 UniquePtr<SSL_SESSION> ssl_session_new(const SSL_X509_METHOD *x509_method) {
   return MakeUnique<SSL_SESSION>(x509_method);
@@ -265,7 +265,7 @@
   return true;
 }
 
-bool ssl_ctx_rotate_ticket_encryption_key(SSL_CTX *ctx) {
+bool ssl_ctx_rotate_ticket_encryption_key(SSLContext *ctx) {
   OPENSSL_timeval now = ssl_ctx_get_current_time(ctx);
   {
     // Avoid acquiring a write lock in the common case (i.e. a non-default key
@@ -329,7 +329,7 @@
 
   // Initialize HMAC and cipher contexts. If callback present it does all the
   // work otherwise use generated values from parent ctx.
-  SSL_CTX *tctx = hs->ssl->session_ctx.get();
+  SSLContext *tctx = hs->ssl->session_ctx.get();
   uint8_t iv[EVP_MAX_IV_LENGTH];
   uint8_t key_name[16];
   if (tctx->ticket_key_cb != nullptr) {
@@ -628,7 +628,7 @@
   return ssl_hs_ok;
 }
 
-static bool remove_session(SSL_CTX *ctx, SSL_SESSION *session, bool lock) {
+static bool remove_session(SSLContext *ctx, SSL_SESSION *session, bool lock) {
   if (session == nullptr || session->session_id.empty()) {
     return false;
   }
@@ -669,7 +669,7 @@
 }
 
 // locked by SSL_CTX in the calling function
-static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *session) {
+static void SSL_SESSION_list_remove(SSLContext *ctx, SSL_SESSION *session) {
   if (session->next == nullptr || session->prev == nullptr) {
     return;
   }
@@ -697,7 +697,7 @@
   session->prev = session->next = nullptr;
 }
 
-static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session) {
+static void SSL_SESSION_list_add(SSLContext *ctx, SSL_SESSION *session) {
   if (session->next != nullptr && session->prev != nullptr) {
     SSL_SESSION_list_remove(ctx, session);
   }
@@ -715,7 +715,7 @@
   }
 }
 
-static bool add_session_locked(SSL_CTX *ctx, UniquePtr<SSL_SESSION> session) {
+static bool add_session_locked(SSLContext *ctx, UniquePtr<SSL_SESSION> session) {
   SSL_SESSION *new_session = session.get();
   SSL_SESSION *old_session;
   if (!lh_SSL_SESSION_insert(ctx->sessions, &old_session, new_session)) {
@@ -761,7 +761,7 @@
 }
 
 void ssl_update_cache(SSL *ssl) {
-  SSL_CTX *ctx = ssl->session_ctx.get();
+  SSLContext *ctx = ssl->session_ctx.get();
   SSL_SESSION *session = ssl->s3->established_session.get();
   int mode = SSL_is_server(ssl) ? SSL_SESS_CACHE_SERVER : SSL_SESS_CACHE_CLIENT;
   if (!SSL_SESSION_is_resumable(session) ||
@@ -838,7 +838,7 @@
 }
 
 SSL_SESSION *SSL_SESSION_new(const SSL_CTX *ctx) {
-  return ssl_session_new(ctx->x509_method).release();
+  return ssl_session_new(FromOpaque(ctx)->x509_method).release();
 }
 
 int SSL_SESSION_up_ref(SSL_SESSION *session) {
@@ -1099,13 +1099,14 @@
 }
 
 int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *session) {
+  auto *ctx_impl = FromOpaque(ctx);
   UniquePtr<SSL_SESSION> owned_session = UpRef(session);
-  MutexWriteLock lock(&ctx->lock);
-  return add_session_locked(ctx, std::move(owned_session));
+  MutexWriteLock lock(&ctx_impl->lock);
+  return add_session_locked(ctx_impl, std::move(owned_session));
 }
 
 int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *session) {
-  return remove_session(ctx, session, /*lock=*/true);
+  return remove_session(FromOpaque(ctx), session, /*lock=*/true);
 }
 
 int SSL_set_session(SSL *ssl, SSL_SESSION *session) {
@@ -1130,9 +1131,7 @@
     timeout = SSL_DEFAULT_SESSION_TIMEOUT;
   }
 
-  uint32_t old_timeout = ctx->session_timeout;
-  ctx->session_timeout = timeout;
-  return old_timeout;
+  return std::exchange(FromOpaque(ctx)->session_timeout, timeout);
 }
 
 uint32_t SSL_CTX_get_timeout(const SSL_CTX *ctx) {
@@ -1140,15 +1139,15 @@
     return 0;
   }
 
-  return ctx->session_timeout;
+  return FromOpaque(ctx)->session_timeout;
 }
 
 void SSL_CTX_set_session_psk_dhe_timeout(SSL_CTX *ctx, uint32_t timeout) {
-  ctx->session_psk_dhe_timeout = timeout;
+  FromOpaque(ctx)->session_psk_dhe_timeout = timeout;
 }
 
 typedef struct timeout_param_st {
-  SSL_CTX *ctx;
+  SSLContext *ctx;
   uint64_t time;
   LHASH_OF(SSL_SESSION) *cache;
 } TIMEOUT_PARAM;
@@ -1172,53 +1171,53 @@
 }
 
 void SSL_CTX_flush_sessions(SSL_CTX *ctx, uint64_t time) {
+  auto *ctx_impl = FromOpaque(ctx);
   TIMEOUT_PARAM tp;
-
-  tp.ctx = ctx;
-  tp.cache = ctx->sessions;
+  tp.ctx = ctx_impl;
+  tp.cache = ctx_impl->sessions;
   if (tp.cache == nullptr) {
     return;
   }
   tp.time = time;
-  MutexWriteLock lock(&ctx->lock);
+  MutexWriteLock lock(&ctx_impl->lock);
   lh_SSL_SESSION_doall_arg(tp.cache, timeout_doall_arg, &tp);
 }
 
 void SSL_CTX_sess_set_new_cb(SSL_CTX *ctx,
                              int (*cb)(SSL *ssl, SSL_SESSION *session)) {
-  ctx->new_session_cb = cb;
+  FromOpaque(ctx)->new_session_cb = cb;
 }
 
 int (*SSL_CTX_sess_get_new_cb(SSL_CTX *ctx))(SSL *ssl, SSL_SESSION *session) {
-  return ctx->new_session_cb;
+  return FromOpaque(ctx)->new_session_cb;
 }
 
 void SSL_CTX_sess_set_remove_cb(SSL_CTX *ctx,
                                 void (*cb)(SSL_CTX *ctx,
                                            SSL_SESSION *session)) {
-  ctx->remove_session_cb = cb;
+  FromOpaque(ctx)->remove_session_cb = cb;
 }
 
 void (*SSL_CTX_sess_get_remove_cb(SSL_CTX *ctx))(SSL_CTX *ctx,
                                                  SSL_SESSION *session) {
-  return ctx->remove_session_cb;
+  return FromOpaque(ctx)->remove_session_cb;
 }
 
 void SSL_CTX_sess_set_get_cb(SSL_CTX *ctx,
                              SSL_SESSION *(*cb)(SSL *ssl, const uint8_t *id,
                                                 int id_len, int *out_copy)) {
-  ctx->get_session_cb = cb;
+  FromOpaque(ctx)->get_session_cb = cb;
 }
 
 SSL_SESSION *(*SSL_CTX_sess_get_get_cb(SSL_CTX *ctx))(SSL *ssl,
                                                       const uint8_t *id,
                                                       int id_len,
                                                       int *out_copy) {
-  return ctx->get_session_cb;
+  return FromOpaque(ctx)->get_session_cb;
 }
 
 void SSL_CTX_set_resumption_across_names_enabled(SSL_CTX *ctx, int enabled) {
-  ctx->resumption_across_names_enabled = !!enabled;
+  FromOpaque(ctx)->resumption_across_names_enabled = !!enabled;
 }
 
 void SSL_set_resumption_across_names_enabled(SSL *ssl, int enabled) {
@@ -1227,10 +1226,10 @@
 
 void SSL_CTX_set_info_callback(SSL_CTX *ctx, void (*cb)(const SSL *ssl,
                                                         int type, int value)) {
-  ctx->info_callback = cb;
+  FromOpaque(ctx)->info_callback = cb;
 }
 
 void (*SSL_CTX_get_info_callback(SSL_CTX *ctx))(const SSL *ssl, int type,
                                                 int value) {
-  return ctx->info_callback;
+  return FromOpaque(ctx)->info_callback;
 }
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index a91a626..bb9f71f 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -666,9 +666,10 @@
     ASSERT_TRUE(ctx);
 
     ASSERT_TRUE(SSL_CTX_set1_groups_list(ctx.get(), t.rule));
-    ASSERT_EQ(t.expected.size(), ctx->supported_group_list.size());
+    ASSERT_EQ(t.expected.size(),
+              FromOpaque(ctx.get())->supported_group_list.size());
     for (size_t i = 0; i < t.expected.size(); i++) {
-      EXPECT_EQ(t.expected[i], ctx->supported_group_list[i]);
+      EXPECT_EQ(t.expected[i], FromOpaque(ctx.get())->supported_group_list[i]);
     }
   }
 
@@ -691,13 +692,15 @@
     bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
     ASSERT_TRUE(ctx);
     // The new context is populated with the default group list.
-    EXPECT_THAT(ctx->supported_group_list, ElementsAreArray(kDefaults));
+    EXPECT_THAT(FromOpaque(ctx.get())->supported_group_list,
+                ElementsAreArray(kDefaults));
 
     // Set some other list to check that it is set away from the default.
     const uint16_t kArbitraryGroupIds[] = {SSL_GROUP_X25519};
     ASSERT_TRUE(SSL_CTX_set1_group_ids(ctx.get(), kArbitraryGroupIds,
                                        std::size(kArbitraryGroupIds)));
-    EXPECT_THAT(ctx->supported_group_list, Not(ElementsAreArray(kDefaults)));
+    EXPECT_THAT(FromOpaque(ctx.get())->supported_group_list,
+                Not(ElementsAreArray(kDefaults)));
 
     bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
     ASSERT_TRUE(ssl);
@@ -708,7 +711,8 @@
     ASSERT_TRUE(SSL_set1_group_ids(ssl.get(), nullptr, 0));
     EXPECT_THAT(ssl->config->supported_group_list, ElementsAreArray(kDefaults));
     ASSERT_TRUE(SSL_CTX_set1_group_ids(ctx.get(), nullptr, 0));
-    EXPECT_THAT(ctx->supported_group_list, ElementsAreArray(kDefaults));
+    EXPECT_THAT(FromOpaque(ctx.get())->supported_group_list,
+                ElementsAreArray(kDefaults));
   }
 
   // Test the NID APIs.
@@ -716,13 +720,15 @@
     bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
     ASSERT_TRUE(ctx);
     // The new context is populated with the default group list.
-    EXPECT_THAT(ctx->supported_group_list, ElementsAreArray(kDefaults));
+    EXPECT_THAT(FromOpaque(ctx.get())->supported_group_list,
+                ElementsAreArray(kDefaults));
 
     // Set some other list to check that it is set away from the default.
     const int kArbitraryNids[] = {NID_X9_62_prime256v1};
     ASSERT_TRUE(SSL_CTX_set1_groups(ctx.get(), kArbitraryNids,
                                     std::size(kArbitraryNids)));
-    EXPECT_THAT(ctx->supported_group_list, Not(ElementsAreArray(kDefaults)));
+    EXPECT_THAT(FromOpaque(ctx.get())->supported_group_list,
+                Not(ElementsAreArray(kDefaults)));
 
     bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
     ASSERT_TRUE(ssl);
@@ -733,7 +739,8 @@
     ASSERT_TRUE(SSL_set1_groups(ssl.get(), nullptr, 0));
     EXPECT_THAT(ssl->config->supported_group_list, ElementsAreArray(kDefaults));
     ASSERT_TRUE(SSL_CTX_set1_groups(ctx.get(), nullptr, 0));
-    EXPECT_THAT(ctx->supported_group_list, ElementsAreArray(kDefaults));
+    EXPECT_THAT(FromOpaque(ctx.get())->supported_group_list,
+                ElementsAreArray(kDefaults));
   }
 }
 
@@ -3119,15 +3126,16 @@
 // order.
 static bool CacheEquals(SSL_CTX *ctx,
                         const std::vector<SSL_SESSION *> &expected) {
+  auto *ctx_impl = FromOpaque(ctx);
   // Check the linked list.
-  SSL_SESSION *ptr = ctx->session_cache_head;
+  SSL_SESSION *ptr = ctx_impl->session_cache_head;
   for (SSL_SESSION *session : expected) {
     if (ptr != session) {
       return false;
     }
     // TODO(davidben): This is an absurd way to denote the end of the list.
     if (ptr->next ==
-        reinterpret_cast<SSL_SESSION *>(&ctx->session_cache_tail)) {
+        reinterpret_cast<SSL_SESSION *>(&ctx_impl->session_cache_tail)) {
       ptr = nullptr;
     } else {
       ptr = ptr->next;
@@ -3139,7 +3147,7 @@
 
   // Check the hash table.
   std::vector<SSL_SESSION *> actual, expected_copy;
-  lh_SSL_SESSION_doall_arg(ctx->sessions, AppendSession, &actual);
+  lh_SSL_SESSION_doall_arg(ctx_impl->sessions, AppendSession, &actual);
   expected_copy = expected;
 
   std::sort(actual.begin(), actual.end());
@@ -6974,7 +6982,8 @@
       continue;
     }
 
-    ExpectSigAlgsEqual(test.expected, ctx->cert->legacy_credential->sigalgs);
+    ExpectSigAlgsEqual(test.expected,
+                       FromOpaque(ctx.get())->cert->legacy_credential->sigalgs);
   }
 }
 
@@ -7035,7 +7044,8 @@
       continue;
     }
 
-    ExpectSigAlgsEqual(test.expected, ctx->cert->legacy_credential->sigalgs);
+    ExpectSigAlgsEqual(test.expected,
+                       FromOpaque(ctx.get())->cert->legacy_credential->sigalgs);
   }
 }
 
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc
index d472fe1..565f030 100644
--- a/ssl/ssl_versions.cc
+++ b/ssl/ssl_versions.cc
@@ -349,19 +349,23 @@
 using namespace bssl;
 
 int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) {
-  return set_min_version(ctx->method, &ctx->conf_min_version, version);
+  auto *ctx_impl = FromOpaque(ctx);
+  return set_min_version(ctx_impl->method, &ctx_impl->conf_min_version,
+                         version);
 }
 
 int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) {
-  return set_max_version(ctx->method, &ctx->conf_max_version, version);
+  auto *ctx_impl = FromOpaque(ctx);
+  return set_max_version(ctx_impl->method, &ctx_impl->conf_max_version,
+                         version);
 }
 
 uint16_t SSL_CTX_get_min_proto_version(const SSL_CTX *ctx) {
-  return ctx->conf_min_version;
+  return FromOpaque(ctx)->conf_min_version;
 }
 
 uint16_t SSL_CTX_get_max_proto_version(const SSL_CTX *ctx) {
-  return ctx->conf_max_version;
+  return FromOpaque(ctx)->conf_max_version;
 }
 
 int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
diff --git a/ssl/ssl_x509.cc b/ssl/ssl_x509.cc
index ec4c1e1..4ac2460 100644
--- a/ssl/ssl_x509.cc
+++ b/ssl/ssl_x509.cc
@@ -25,8 +25,8 @@
 #include <openssl/stack.h>
 #include <openssl/x509.h>
 
-#include "../crypto/internal.h"
 #include "../crypto/bytestring/internal.h"
+#include "../crypto/internal.h"
 #include "internal.h"
 
 
@@ -41,7 +41,7 @@
 
 // check_ssl_ctx_x509_method acts like |check_ssl_x509_method|, but for an
 // |SSL_CTX|.
-static void check_ssl_ctx_x509_method(const SSL_CTX *ctx) {
+static void check_ssl_ctx_x509_method(const SSLContext *ctx) {
   assert(ctx == nullptr || ctx->x509_method == &ssl_crypto_x509_method);
 }
 
@@ -211,7 +211,7 @@
   }
 
   SSL *const ssl = hs->ssl;
-  SSL_CTX *ssl_ctx = ssl->ctx.get();
+  SSLContext *ssl_ctx = ssl->ctx.get();
   X509_STORE *verify_store = ssl_ctx->cert_store;
   if (hs->config->cert->verify_store != nullptr) {
     verify_store = hs->config->cert->verify_store;
@@ -330,18 +330,18 @@
   return SSL_set1_chain(ssl, chain.get());
 }
 
-static void ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {
+static void ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(SSLContext *ctx) {
   sk_X509_NAME_pop_free(ctx->cached_x509_client_CA, X509_NAME_free);
   ctx->cached_x509_client_CA = nullptr;
 }
 
-static bool ssl_crypto_x509_ssl_ctx_new(SSL_CTX *ctx) {
+static bool ssl_crypto_x509_ssl_ctx_new(SSLContext *ctx) {
   ctx->cert_store = X509_STORE_new();
   ctx->param = X509_VERIFY_PARAM_new();
   return (ctx->cert_store != nullptr && ctx->param != nullptr);
 }
 
-static void ssl_crypto_x509_ssl_ctx_free(SSL_CTX *ctx) {
+static void ssl_crypto_x509_ssl_ctx_free(SSLContext *ctx) {
   ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(ctx);
   X509_VERIFY_PARAM_free(ctx->param);
   X509_STORE_free(ctx->cert_store);
@@ -411,8 +411,9 @@
 }
 
 int SSL_CTX_set_purpose(SSL_CTX *ctx, int purpose) {
-  check_ssl_ctx_x509_method(ctx);
-  return X509_VERIFY_PARAM_set_purpose(ctx->param, purpose);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return X509_VERIFY_PARAM_set_purpose(ctx_impl->param, purpose);
 }
 
 int SSL_set_purpose(SSL *ssl, int purpose) {
@@ -424,8 +425,9 @@
 }
 
 int SSL_CTX_set_trust(SSL_CTX *ctx, int trust) {
-  check_ssl_ctx_x509_method(ctx);
-  return X509_VERIFY_PARAM_set_trust(ctx->param, trust);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return X509_VERIFY_PARAM_set_trust(ctx_impl->param, trust);
 }
 
 int SSL_set_trust(SSL *ssl, int trust) {
@@ -437,8 +439,9 @@
 }
 
 int SSL_CTX_set1_param(SSL_CTX *ctx, const X509_VERIFY_PARAM *param) {
-  check_ssl_ctx_x509_method(ctx);
-  return X509_VERIFY_PARAM_set1(ctx->param, param);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return X509_VERIFY_PARAM_set1(ctx_impl->param, param);
 }
 
 int SSL_set1_param(SSL *ssl, const X509_VERIFY_PARAM *param) {
@@ -450,8 +453,9 @@
 }
 
 X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
-  return ctx->param;
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return ctx_impl->param;
 }
 
 X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) {
@@ -482,19 +486,22 @@
 }
 
 int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
-  return ctx->verify_mode;
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return ctx_impl->verify_mode;
 }
 
 int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
-  return X509_VERIFY_PARAM_get_depth(ctx->param);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return X509_VERIFY_PARAM_get_depth(ctx_impl->param);
 }
 
 int (*SSL_CTX_get_verify_callback(const SSL_CTX *ctx))(
     int ok, X509_STORE_CTX *store_ctx) {
-  check_ssl_ctx_x509_method(ctx);
-  return ctx->default_verify_callback;
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return ctx_impl->default_verify_callback;
 }
 
 void SSL_set_verify(SSL *ssl, int mode,
@@ -519,32 +526,37 @@
 
 void SSL_CTX_set_cert_verify_callback(
     SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *store_ctx, void *arg), void *arg) {
-  check_ssl_ctx_x509_method(ctx);
-  ctx->app_verify_callback = cb;
-  ctx->app_verify_arg = arg;
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  ctx_impl->app_verify_callback = cb;
+  ctx_impl->app_verify_arg = arg;
 }
 
 void SSL_CTX_set_verify(SSL_CTX *ctx, int mode,
                         int (*cb)(int, X509_STORE_CTX *)) {
-  check_ssl_ctx_x509_method(ctx);
-  ctx->verify_mode = mode;
-  ctx->default_verify_callback = cb;
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  ctx_impl->verify_mode = mode;
+  ctx_impl->default_verify_callback = cb;
 }
 
 void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) {
-  check_ssl_ctx_x509_method(ctx);
-  X509_VERIFY_PARAM_set_depth(ctx->param, depth);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  X509_VERIFY_PARAM_set_depth(ctx_impl->param, depth);
 }
 
 int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
-  return X509_STORE_set_default_paths(ctx->cert_store);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return X509_STORE_set_default_paths(ctx_impl->cert_store);
 }
 
 int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *ca_file,
                                   const char *ca_dir) {
-  check_ssl_ctx_x509_method(ctx);
-  return X509_STORE_load_locations(ctx->cert_store, ca_file, ca_dir);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return X509_STORE_load_locations(ctx_impl->cert_store, ca_file, ca_dir);
 }
 
 long SSL_get_verify_result(const SSL *ssl) {
@@ -557,14 +569,16 @@
 }
 
 X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
-  return ctx->cert_store;
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return ctx_impl->cert_store;
 }
 
 void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store) {
-  check_ssl_ctx_x509_method(ctx);
-  X509_STORE_free(ctx->cert_store);
-  ctx->cert_store = store;
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  X509_STORE_free(ctx_impl->cert_store);
+  ctx_impl->cert_store = store;
 }
 
 static int ssl_use_certificate(CERT *cert, X509 *x) {
@@ -590,8 +604,9 @@
 }
 
 int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
-  check_ssl_ctx_x509_method(ctx);
-  return ssl_use_certificate(ctx->cert.get(), x);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return ssl_use_certificate(ctx_impl->cert.get(), x);
 }
 
 // ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
@@ -632,9 +647,10 @@
 }
 
 X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
-  MutexWriteLock lock(&ctx->lock);
-  return ssl_cert_get0_leaf(ctx->cert.get());
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  MutexWriteLock lock(&ctx_impl->lock);
+  return ssl_cert_get0_leaf(ctx_impl->cert.get());
 }
 
 static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
@@ -661,8 +677,9 @@
 }
 
 int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
-  check_ssl_ctx_x509_method(ctx);
-  if (!ssl_cert_set1_chain(ctx->cert.get(), chain)) {
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  if (!ssl_cert_set1_chain(ctx_impl->cert.get(), chain)) {
     return 0;
   }
   sk_X509_pop_free(chain, X509_free);
@@ -670,8 +687,9 @@
 }
 
 int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
-  check_ssl_ctx_x509_method(ctx);
-  return ssl_cert_set1_chain(ctx->cert.get(), chain);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return ssl_cert_set1_chain(ctx_impl->cert.get(), chain);
 }
 
 int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
@@ -695,17 +713,20 @@
 }
 
 int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  check_ssl_ctx_x509_method(ctx);
-  return ssl_cert_add0_chain_cert(ctx->cert.get(), x509);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return ssl_cert_add0_chain_cert(ctx_impl->cert.get(), x509);
 }
 
 int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  check_ssl_ctx_x509_method(ctx);
-  return ssl_cert_add1_chain_cert(ctx->cert.get(), x509);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return ssl_cert_add1_chain_cert(ctx_impl->cert.get(), x509);
 }
 
 int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  check_ssl_ctx_x509_method(ctx);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
   return SSL_CTX_add0_chain_cert(ctx, x509);
 }
 
@@ -726,12 +747,14 @@
 }
 
 int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
   return SSL_CTX_set0_chain(ctx, nullptr);
 }
 
 int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
   return SSL_CTX_clear_chain_certs(ctx);
 }
 
@@ -770,14 +793,15 @@
 }
 
 int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
-  check_ssl_ctx_x509_method(ctx);
-  MutexWriteLock lock(&ctx->lock);
-  if (!ssl_cert_cache_chain_certs(ctx->cert.get())) {
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  MutexWriteLock lock(&ctx_impl->lock);
+  if (!ssl_cert_cache_chain_certs(ctx_impl->cert.get())) {
     *out_chain = nullptr;
     return 0;
   }
 
-  *out_chain = ctx->cert->x509_chain;
+  *out_chain = ctx_impl->cert->x509_chain;
   return 1;
 }
 
@@ -871,9 +895,10 @@
 }
 
 void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list) {
-  check_ssl_ctx_x509_method(ctx);
-  ctx->x509_method->ssl_ctx_flush_cached_client_CA(ctx);
-  set_client_CA_list(&ctx->client_CA, name_list, ctx->pool.get());
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  ctx_impl->x509_method->ssl_ctx_flush_cached_client_CA(ctx_impl);
+  set_client_CA_list(&ctx_impl->client_CA, name_list, ctx_impl->pool.get());
   sk_X509_NAME_pop_free(name_list, X509_NAME_free);
 }
 
@@ -936,13 +961,14 @@
 }
 
 STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) {
-  check_ssl_ctx_x509_method(ctx);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
   // This is a logically const operation that may be called on multiple threads,
   // so it needs to lock around updating |cached_x509_client_CA|.
-  MutexWriteLock lock(&ctx->lock);
+  MutexWriteLock lock(&ctx_impl->lock);
   return buffer_names_to_x509(
-      ctx->client_CA.get(),
-      const_cast<STACK_OF(X509_NAME) **>(&ctx->cached_x509_client_CA));
+      ctx_impl->client_CA.get(),
+      const_cast<STACK_OF(X509_NAME) **>(&ctx_impl->cached_x509_client_CA));
 }
 
 static int add_client_CA(UniquePtr<STACK_OF(CRYPTO_BUFFER)> *names, X509 *x509,
@@ -997,12 +1023,13 @@
 }
 
 int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x509) {
-  check_ssl_ctx_x509_method(ctx);
-  if (!add_client_CA(&ctx->client_CA, x509, ctx->pool.get())) {
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  if (!add_client_CA(&ctx_impl->client_CA, x509, ctx_impl->pool.get())) {
     return 0;
   }
 
-  ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(ctx);
+  ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(ctx_impl);
   return 1;
 }
 
@@ -1037,10 +1064,11 @@
 void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx,
                                 int (*cb)(SSL *ssl, X509 **out_x509,
                                           EVP_PKEY **out_pkey)) {
-  check_ssl_ctx_x509_method(ctx);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
   // Emulate the old client certificate callback with the new one.
   SSL_CTX_set_cert_cb(ctx, do_client_cert_cb, nullptr);
-  ctx->client_cert_cb = cb;
+  ctx_impl->client_cert_cb = cb;
 }
 
 static int set_cert_store(X509_STORE **store_ptr, X509_STORE *new_store,
@@ -1064,13 +1092,15 @@
 }
 
 int SSL_CTX_set0_verify_cert_store(SSL_CTX *ctx, X509_STORE *store) {
-  check_ssl_ctx_x509_method(ctx);
-  return set_cert_store(&ctx->cert->verify_store, store, 0);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return set_cert_store(&ctx_impl->cert->verify_store, store, 0);
 }
 
 int SSL_CTX_set1_verify_cert_store(SSL_CTX *ctx, X509_STORE *store) {
-  check_ssl_ctx_x509_method(ctx);
-  return set_cert_store(&ctx->cert->verify_store, store, 1);
+  auto *ctx_impl = FromOpaque(ctx);
+  check_ssl_ctx_x509_method(ctx_impl);
+  return set_cert_store(&ctx_impl->cert->verify_store, store, 1);
 }
 
 int SSL_set0_verify_cert_store(SSL *ssl, X509_STORE *store) {
diff --git a/ssl/test/test_state.cc b/ssl/test/test_state.cc
index 79d0fdf..37ce3da 100644
--- a/ssl/test/test_state.cc
+++ b/ssl/test/test_state.cc
@@ -77,7 +77,7 @@
 }
 
 void CopySessions(SSL_CTX *dst, const SSL_CTX *src) {
-  lh_SSL_SESSION_doall_arg(src->sessions, ssl_ctx_add_session, dst);
+  lh_SSL_SESSION_doall_arg(FromOpaque(src)->sessions, ssl_ctx_add_session, dst);
 }
 
 static void push_session(SSL_SESSION *session, void *arg) {
@@ -97,7 +97,7 @@
     return false;
   }
   std::vector<SSL_SESSION *> sessions;
-  lh_SSL_SESSION_doall_arg(ctx->sessions, push_session, &sessions);
+  lh_SSL_SESSION_doall_arg(FromOpaque(ctx)->sessions, push_session, &sessions);
   for (const auto &sess : sessions) {
     if (!ssl_session_serialize(sess, &ctx_sessions)) {
       return false;
@@ -120,8 +120,8 @@
     return false;
   }
   while (CBS_len(&sessions)) {
-    UniquePtr<SSL_SESSION> session =
-        SSL_SESSION_parse(&sessions, ctx->x509_method, ctx->pool.get());
+    UniquePtr<SSL_SESSION> session = SSL_SESSION_parse(
+        &sessions, FromOpaque(ctx)->x509_method, FromOpaque(ctx)->pool.get());
     if (!session) {
       return false;
     }
@@ -167,7 +167,8 @@
   }
   if (CBS_len(&pending_session)) {
     state->pending_session =
-        SSL_SESSION_parse(&pending_session, ctx->x509_method, ctx->pool.get());
+        SSL_SESSION_parse(&pending_session, FromOpaque(ctx)->x509_method,
+                          FromOpaque(ctx)->pool.get());
     if (!state->pending_session) {
       return nullptr;
     }
diff --git a/ssl/tls_method.cc b/ssl/tls_method.cc
index 72ec4c1..46e7a6c 100644
--- a/ssl/tls_method.cc
+++ b/ssl/tls_method.cc
@@ -165,9 +165,9 @@
 static bool ssl_noop_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
   return true;
 }
-static bool ssl_noop_x509_ssl_ctx_new(SSL_CTX *ctx) { return true; }
-static void ssl_noop_x509_ssl_ctx_free(SSL_CTX *ctx) {}
-static void ssl_noop_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {}
+static bool ssl_noop_x509_ssl_ctx_new(SSLContext *ctx) { return true; }
+static void ssl_noop_x509_ssl_ctx_free(SSLContext *ctx) {}
+static void ssl_noop_x509_ssl_ctx_flush_cached_client_CA(SSLContext *ctx) {}
 
 const SSL_X509_METHOD ssl_noop_x509_method = {
     ssl_noop_x509_check_client_CA_names,