Remove non-namespaced symbols for ctor/dtor of RSA.

Bug: 42220000
Change-Id: I70504fa1747fc8de2196b9d013eca7de6a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/88568
Reviewed-by: Xiangfei Ding <xfding@google.com>
Commit-Queue: Xiangfei Ding <xfding@google.com>
diff --git a/crypto/evp/p_rsa.cc b/crypto/evp/p_rsa.cc
index b156a90..1855cd7 100644
--- a/crypto/evp/p_rsa.cc
+++ b/crypto/evp/p_rsa.cc
@@ -167,7 +167,7 @@
 }
 
 static int rsa_pub_encode_pss(CBB *out, const EVP_PKEY *key) {
-  const RSA *rsa = reinterpret_cast<const RSA *>(key->pkey);
+  const RSAImpl *rsa = reinterpret_cast<const RSAImpl *>(key->pkey);
   CBB spki, algorithm, key_bitstring;
   if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
@@ -194,7 +194,8 @@
     return ret;
   }
 
-  UniquePtr<RSA> rsa(RSA_public_key_from_bytes(CBS_data(key), CBS_len(key)));
+  UniquePtr<RSAImpl> rsa(
+      FromOpaque(RSA_public_key_from_bytes(CBS_data(key), CBS_len(key))));
   if (rsa == nullptr) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     return evp_decode_error;
@@ -206,7 +207,7 @@
 }
 
 static int rsa_priv_encode_pss(CBB *out, const EVP_PKEY *key) {
-  const RSA *rsa = reinterpret_cast<const RSA *>(key->pkey);
+  const RSAImpl *rsa = reinterpret_cast<const RSAImpl *>(key->pkey);
   CBB pkcs8, algorithm, private_key;
   if (!CBB_add_asn1(out, &pkcs8, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&pkcs8, 0 /* version */) ||
@@ -233,7 +234,8 @@
     return ret;
   }
 
-  UniquePtr<RSA> rsa(RSA_private_key_from_bytes(CBS_data(key), CBS_len(key)));
+  UniquePtr<RSAImpl> rsa(
+      FromOpaque(RSA_private_key_from_bytes(CBS_data(key), CBS_len(key))));
   if (rsa == nullptr) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     return evp_decode_error;
@@ -374,7 +376,7 @@
     rctx->pad_mode = RSA_PKCS1_PSS_PADDING;
     // Pick up PSS parameters from the key.
     if (ctx->pkey != nullptr && ctx->pkey->pkey != nullptr) {
-      RSA *rsa = static_cast<RSA *>(ctx->pkey->pkey);
+      RSAImpl *rsa = static_cast<RSAImpl *>(ctx->pkey->pkey);
       const EVP_MD *md = rsa_pss_params_get_md(rsa->pss_params);
       if (md != nullptr) {
         rctx->md = rctx->mgf1md = md;
diff --git a/crypto/evp/print.cc b/crypto/evp/print.cc
index 50d158d..4582fe9 100644
--- a/crypto/evp/print.cc
+++ b/crypto/evp/print.cc
@@ -26,6 +26,8 @@
 #include "../internal.h"
 
 
+using namespace bssl;
+
 static int print_hex(BIO *bp, const uint8_t *data, size_t len, int off) {
   for (size_t i = 0; i < len; i++) {
     if ((i % 15) == 0) {
@@ -98,7 +100,7 @@
 
 // RSA keys.
 
-static int do_rsa_print(BIO *out, const RSA *rsa, int off,
+static int do_rsa_print(BIO *out, const RSAImpl *rsa, int off,
                         int include_private) {
   int mod_len = 0;
   if (rsa->n != nullptr) {
@@ -142,11 +144,11 @@
 }
 
 static int rsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent) {
-  return do_rsa_print(bp, EVP_PKEY_get0_RSA(pkey), indent, 0);
+  return do_rsa_print(bp, FromOpaque(EVP_PKEY_get0_RSA(pkey)), indent, 0);
 }
 
 static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent) {
-  return do_rsa_print(bp, EVP_PKEY_get0_RSA(pkey), indent, 1);
+  return do_rsa_print(bp, FromOpaque(EVP_PKEY_get0_RSA(pkey)), indent, 1);
 }
 
 
diff --git a/crypto/fipsmodule/rsa/internal.h b/crypto/fipsmodule/rsa/internal.h
index 11e6099..602573b 100644
--- a/crypto/fipsmodule/rsa/internal.h
+++ b/crypto/fipsmodule/rsa/internal.h
@@ -23,6 +23,8 @@
 #include "../../internal.h"
 
 
+DECLARE_OPAQUE_STRUCT(rsa_st, RSAImpl)
+
 BSSL_NAMESPACE_BEGIN
 
 // TODO(crbug.com/42290480): Raise this limit. 512-bit RSA was factored in 1999.
@@ -42,9 +44,13 @@
   rsa_pss_sha512,
 };
 
-BSSL_NAMESPACE_END
+// Exported because rsa_test.cc uses this class and links to the .so.
+class OPENSSL_EXPORT RSAImpl : public rsa_st {
+ public:
+  static constexpr bool kAllowUniquePtr = true;
 
-struct rsa_st {
+  ~RSAImpl();
+
   RSA_METHOD *meth;
 
   BIGNUM *n;
@@ -85,12 +91,9 @@
 
   // private_key_frozen is one if the key has been used for a private key
   // operation and may no longer be mutated.
-  unsigned private_key_frozen:1;
+  unsigned private_key_frozen : 1;
 };
 
-
-BSSL_NAMESPACE_BEGIN
-
 #define RSA_PKCS1_PADDING_SIZE 11
 
 // Default implementations of RSA operations.
diff --git a/crypto/fipsmodule/rsa/padding.cc.inc b/crypto/fipsmodule/rsa/padding.cc.inc
index 5f0e41f..a68f14d 100644
--- a/crypto/fipsmodule/rsa/padding.cc.inc
+++ b/crypto/fipsmodule/rsa/padding.cc.inc
@@ -192,7 +192,7 @@
     goto err;
   }
 
-  MSBits = (BN_num_bits(rsa->n) - 1) & 0x7;
+  MSBits = (BN_num_bits(FromOpaque(rsa)->n) - 1) & 0x7;
   emLen = RSA_size(rsa);
   if (EM[0] & (0xFF << MSBits)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_FIRST_OCTET_INVALID);
@@ -281,12 +281,12 @@
   FIPS_service_indicator_lock_state();
   hLen = EVP_MD_size(Hash);
 
-  if (BN_is_zero(rsa->n)) {
+  if (BN_is_zero(FromOpaque(rsa)->n)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_EMPTY_PUBLIC_KEY);
     goto err;
   }
 
-  MSBits = (BN_num_bits(rsa->n) - 1) & 0x7;
+  MSBits = (BN_num_bits(FromOpaque(rsa)->n) - 1) & 0x7;
   emLen = RSA_size(rsa);
   if (MSBits == 0) {
     assert(emLen >= 1);
diff --git a/crypto/fipsmodule/rsa/rsa.cc.inc b/crypto/fipsmodule/rsa/rsa.cc.inc
index 94d5ddb..a3e8fe0 100644
--- a/crypto/fipsmodule/rsa/rsa.cc.inc
+++ b/crypto/fipsmodule/rsa/rsa.cc.inc
@@ -57,7 +57,7 @@
 }
 
 RSA *RSA_new_public_key(const BIGNUM *n, const BIGNUM *e) {
-  RSA *rsa = RSA_new();
+  RSAImpl *rsa = FromOpaque(RSA_new());
   if (rsa == nullptr ||            //
       !bn_dup_into(&rsa->n, n) ||  //
       !bn_dup_into(&rsa->e, e) ||  //
@@ -72,7 +72,7 @@
 RSA *RSA_new_private_key(const BIGNUM *n, const BIGNUM *e, const BIGNUM *d,
                          const BIGNUM *p, const BIGNUM *q, const BIGNUM *dmp1,
                          const BIGNUM *dmq1, const BIGNUM *iqmp) {
-  RSA *rsa = RSA_new();
+  RSAImpl *rsa = FromOpaque(RSA_new());
   if (rsa == nullptr ||                  //
       !bn_dup_into(&rsa->n, n) ||        //
       !bn_dup_into(&rsa->e, e) ||        //
@@ -92,7 +92,7 @@
 
 RSA *RSA_new_private_key_no_crt(const BIGNUM *n, const BIGNUM *e,
                                 const BIGNUM *d) {
-  RSA *rsa = RSA_new();
+  RSAImpl *rsa = FromOpaque(RSA_new());
   if (rsa == nullptr ||            //
       !bn_dup_into(&rsa->n, n) ||  //
       !bn_dup_into(&rsa->e, e) ||  //
@@ -106,7 +106,7 @@
 }
 
 RSA *RSA_new_private_key_no_e(const BIGNUM *n, const BIGNUM *d) {
-  RSA *rsa = RSA_new();
+  RSAImpl *rsa = FromOpaque(RSA_new());
   if (rsa == nullptr) {
     return nullptr;
   }
@@ -123,7 +123,7 @@
 }
 
 RSA *RSA_new_public_key_large_e(const BIGNUM *n, const BIGNUM *e) {
-  RSA *rsa = RSA_new();
+  RSAImpl *rsa = FromOpaque(RSA_new());
   if (rsa == nullptr) {
     return nullptr;
   }
@@ -143,7 +143,7 @@
                                  const BIGNUM *d, const BIGNUM *p,
                                  const BIGNUM *q, const BIGNUM *dmp1,
                                  const BIGNUM *dmq1, const BIGNUM *iqmp) {
-  RSA *rsa = RSA_new();
+  RSAImpl *rsa = FromOpaque(RSA_new());
   if (rsa == nullptr) {
     return nullptr;
   }
@@ -168,7 +168,7 @@
 RSA *RSA_new() { return RSA_new_method(nullptr); }
 
 RSA *RSA_new_method(const ENGINE *engine) {
-  RSA *rsa = NewZeroed<RSA>();
+  RSAImpl *rsa = NewZeroed<RSAImpl>();
   if (rsa == nullptr) {
     return nullptr;
   }
@@ -197,7 +197,7 @@
 }
 
 RSA *RSA_new_method_no_e(const ENGINE *engine, const BIGNUM *n) {
-  RSA *rsa = RSA_new_method(engine);
+  RSAImpl *rsa = FromOpaque(RSA_new_method(engine));
   if (rsa == nullptr || !bn_dup_into(&rsa->n, n)) {
     RSA_free(rsa);
     return nullptr;
@@ -206,111 +206,150 @@
   return rsa;
 }
 
+RSAImpl::~RSAImpl() {
+  // Refcount can be 1 if called by UniquePtr, and 0 if called by RSA_free.
+  BSSL_CHECK(references.load() <= 1);
+
+  if (meth != nullptr && meth->finish != nullptr) {
+    meth->finish(this);
+  }
+  METHOD_unref(meth);
+
+  CRYPTO_free_ex_data(g_rsa_ex_data_class_bss_get(), &ex_data);
+
+  BN_free(n);
+  BN_free(e);
+  BN_free(d);
+  BN_free(p);
+  BN_free(q);
+  BN_free(dmp1);
+  BN_free(dmq1);
+  BN_free(iqmp);
+  rsa_invalidate_key(this);
+  CRYPTO_MUTEX_cleanup(&lock);
+}
+
 void RSA_free(RSA *rsa) {
   if (rsa == nullptr) {
     return;
   }
 
-  if (!CRYPTO_refcount_dec_and_test_zero(&rsa->references)) {
+  auto *impl = FromOpaque(rsa);
+  if (!CRYPTO_refcount_dec_and_test_zero(&impl->references)) {
     return;
   }
-
-  if (rsa->meth != nullptr && rsa->meth->finish != nullptr) {
-    rsa->meth->finish(rsa);
-  }
-  METHOD_unref(rsa->meth);
-
-  CRYPTO_free_ex_data(g_rsa_ex_data_class_bss_get(), &rsa->ex_data);
-
-  BN_free(rsa->n);
-  BN_free(rsa->e);
-  BN_free(rsa->d);
-  BN_free(rsa->p);
-  BN_free(rsa->q);
-  BN_free(rsa->dmp1);
-  BN_free(rsa->dmq1);
-  BN_free(rsa->iqmp);
-  rsa_invalidate_key(rsa);
-  CRYPTO_MUTEX_cleanup(&rsa->lock);
-  Delete(rsa);
+  Delete(impl);
 }
 
 int RSA_up_ref(RSA *rsa) {
-  CRYPTO_refcount_inc(&rsa->references);
+  auto *impl = FromOpaque(rsa);
+  CRYPTO_refcount_inc(&impl->references);
   return 1;
 }
 
-unsigned RSA_bits(const RSA *rsa) { return BN_num_bits(rsa->n); }
+unsigned RSA_bits(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return BN_num_bits(impl->n);
+}
 
-const BIGNUM *RSA_get0_n(const RSA *rsa) { return rsa->n; }
+const BIGNUM *RSA_get0_n(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->n;
+}
 
-const BIGNUM *RSA_get0_e(const RSA *rsa) { return rsa->e; }
+const BIGNUM *RSA_get0_e(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->e;
+}
 
-const BIGNUM *RSA_get0_d(const RSA *rsa) { return rsa->d; }
+const BIGNUM *RSA_get0_d(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->d;
+}
 
-const BIGNUM *RSA_get0_p(const RSA *rsa) { return rsa->p; }
+const BIGNUM *RSA_get0_p(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->p;
+}
 
-const BIGNUM *RSA_get0_q(const RSA *rsa) { return rsa->q; }
+const BIGNUM *RSA_get0_q(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->q;
+}
 
-const BIGNUM *RSA_get0_dmp1(const RSA *rsa) { return rsa->dmp1; }
+const BIGNUM *RSA_get0_dmp1(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->dmp1;
+}
 
-const BIGNUM *RSA_get0_dmq1(const RSA *rsa) { return rsa->dmq1; }
+const BIGNUM *RSA_get0_dmq1(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->dmq1;
+}
 
-const BIGNUM *RSA_get0_iqmp(const RSA *rsa) { return rsa->iqmp; }
+const BIGNUM *RSA_get0_iqmp(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->iqmp;
+}
 
 void RSA_get0_key(const RSA *rsa, const BIGNUM **out_n, const BIGNUM **out_e,
                   const BIGNUM **out_d) {
+  auto *impl = FromOpaque(rsa);
   if (out_n != nullptr) {
-    *out_n = rsa->n;
+    *out_n = impl->n;
   }
   if (out_e != nullptr) {
-    *out_e = rsa->e;
+    *out_e = impl->e;
   }
   if (out_d != nullptr) {
-    *out_d = rsa->d;
+    *out_d = impl->d;
   }
 }
 
 void RSA_get0_factors(const RSA *rsa, const BIGNUM **out_p,
                       const BIGNUM **out_q) {
+  auto *impl = FromOpaque(rsa);
   if (out_p != nullptr) {
-    *out_p = rsa->p;
+    *out_p = impl->p;
   }
   if (out_q != nullptr) {
-    *out_q = rsa->q;
+    *out_q = impl->q;
   }
 }
 
 void RSA_get0_crt_params(const RSA *rsa, const BIGNUM **out_dmp1,
                          const BIGNUM **out_dmq1, const BIGNUM **out_iqmp) {
+  auto *impl = FromOpaque(rsa);
   if (out_dmp1 != nullptr) {
-    *out_dmp1 = rsa->dmp1;
+    *out_dmp1 = impl->dmp1;
   }
   if (out_dmq1 != nullptr) {
-    *out_dmq1 = rsa->dmq1;
+    *out_dmq1 = impl->dmq1;
   }
   if (out_iqmp != nullptr) {
-    *out_iqmp = rsa->iqmp;
+    *out_iqmp = impl->iqmp;
   }
 }
 
 int RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
-  if ((rsa->n == nullptr && n == nullptr) ||
-      (rsa->e == nullptr && e == nullptr)) {
+  auto *impl = FromOpaque(rsa);
+
+  if ((impl->n == nullptr && n == nullptr) ||
+      (impl->e == nullptr && e == nullptr)) {
     return 0;
   }
 
   if (n != nullptr) {
-    BN_free(rsa->n);
-    rsa->n = n;
+    BN_free(impl->n);
+    impl->n = n;
   }
   if (e != nullptr) {
-    BN_free(rsa->e);
-    rsa->e = e;
+    BN_free(impl->e);
+    impl->e = e;
   }
   if (d != nullptr) {
-    BN_free(rsa->d);
-    rsa->d = d;
+    BN_free(impl->d);
+    impl->d = d;
   }
 
   rsa_invalidate_key(rsa);
@@ -318,18 +357,20 @@
 }
 
 int RSA_set0_factors(RSA *rsa, BIGNUM *p, BIGNUM *q) {
-  if ((rsa->p == nullptr && p == nullptr) ||
-      (rsa->q == nullptr && q == nullptr)) {
+  auto *impl = FromOpaque(rsa);
+
+  if ((impl->p == nullptr && p == nullptr) ||
+      (impl->q == nullptr && q == nullptr)) {
     return 0;
   }
 
   if (p != nullptr) {
-    BN_free(rsa->p);
-    rsa->p = p;
+    BN_free(impl->p);
+    impl->p = p;
   }
   if (q != nullptr) {
-    BN_free(rsa->q);
-    rsa->q = q;
+    BN_free(impl->q);
+    impl->q = q;
   }
 
   rsa_invalidate_key(rsa);
@@ -337,23 +378,25 @@
 }
 
 int RSA_set0_crt_params(RSA *rsa, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) {
-  if ((rsa->dmp1 == nullptr && dmp1 == nullptr) ||
-      (rsa->dmq1 == nullptr && dmq1 == nullptr) ||
-      (rsa->iqmp == nullptr && iqmp == nullptr)) {
+  auto *impl = FromOpaque(rsa);
+
+  if ((impl->dmp1 == nullptr && dmp1 == nullptr) ||
+      (impl->dmq1 == nullptr && dmq1 == nullptr) ||
+      (impl->iqmp == nullptr && iqmp == nullptr)) {
     return 0;
   }
 
   if (dmp1 != nullptr) {
-    BN_free(rsa->dmp1);
-    rsa->dmp1 = dmp1;
+    BN_free(impl->dmp1);
+    impl->dmp1 = dmp1;
   }
   if (dmq1 != nullptr) {
-    BN_free(rsa->dmq1);
-    rsa->dmq1 = dmq1;
+    BN_free(impl->dmq1);
+    impl->dmq1 = dmq1;
   }
   if (iqmp != nullptr) {
-    BN_free(rsa->iqmp);
-    rsa->iqmp = iqmp;
+    BN_free(impl->iqmp);
+    impl->iqmp = iqmp;
   }
 
   rsa_invalidate_key(rsa);
@@ -363,8 +406,11 @@
 static int rsa_sign_raw_no_self_test(RSA *rsa, size_t *out_len, uint8_t *out,
                                      size_t max_out, const uint8_t *in,
                                      size_t in_len, int padding) {
-  if (rsa->meth->sign_raw) {
-    return rsa->meth->sign_raw(rsa, out_len, out, max_out, in, in_len, padding);
+  auto *impl = FromOpaque(rsa);
+
+  if (impl->meth->sign_raw) {
+    return impl->meth->sign_raw(rsa, out_len, out, max_out, in, in_len,
+                                padding);
   }
 
   return rsa_default_sign_raw(rsa, out_len, out, max_out, in, in_len, padding);
@@ -377,10 +423,14 @@
                                    padding);
 }
 
-unsigned RSA_size(const RSA *rsa) { return BN_num_bytes(rsa->n); }
+unsigned RSA_size(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return BN_num_bytes(impl->n);
+}
 
 int RSA_is_opaque(const RSA *rsa) {
-  return rsa->meth && (rsa->meth->flags & RSA_FLAG_OPAQUE);
+  auto *impl = FromOpaque(rsa);
+  return impl->meth && (impl->meth->flags & RSA_FLAG_OPAQUE);
 }
 
 int RSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused,
@@ -390,11 +440,13 @@
 }
 
 int RSA_set_ex_data(RSA *rsa, int idx, void *arg) {
-  return CRYPTO_set_ex_data(&rsa->ex_data, idx, arg);
+  auto *impl = FromOpaque(rsa);
+  return CRYPTO_set_ex_data(&impl->ex_data, idx, arg);
 }
 
 void *RSA_get_ex_data(const RSA *rsa, int idx) {
-  return CRYPTO_get_ex_data(&rsa->ex_data, idx);
+  auto *impl = FromOpaque(rsa);
+  return CRYPTO_get_ex_data(&impl->ex_data, idx);
 }
 
 // SSL_SIG_LENGTH is the size of an SSL/TLS (prior to TLS 1.2) signature: it's
@@ -546,15 +598,17 @@
 int bssl::rsa_sign_no_self_test(int hash_nid, const uint8_t *digest,
                                 size_t digest_len, uint8_t *out,
                                 unsigned *out_len, RSA *rsa) {
-  if (rsa->meth->sign) {
+  auto *impl = FromOpaque(rsa);
+
+  if (impl->meth->sign) {
     if (!rsa_check_digest_size(hash_nid, digest_len)) {
       return 0;
     }
     // All supported digest lengths fit in |unsigned|.
     assert(digest_len <= EVP_MAX_MD_SIZE);
     static_assert(EVP_MAX_MD_SIZE <= UINT_MAX, "digest too long");
-    return rsa->meth->sign(hash_nid, digest, (unsigned)digest_len, out, out_len,
-                           rsa);
+    return impl->meth->sign(hash_nid, digest, (unsigned)digest_len, out,
+                            out_len, rsa);
   }
 
   const unsigned rsa_size = RSA_size(rsa);
@@ -619,7 +673,8 @@
 int bssl::rsa_verify_no_self_test(int hash_nid, const uint8_t *digest,
                                   size_t digest_len, const uint8_t *sig,
                                   size_t sig_len, RSA *rsa) {
-  if (rsa->n == nullptr || rsa->e == nullptr) {
+  auto *impl = FromOpaque(rsa);
+  if (impl->n == nullptr || impl->e == nullptr) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
     return 0;
   }
@@ -738,20 +793,21 @@
     return 0;
   }
 
-  if ((key->p != nullptr) != (key->q != nullptr)) {
+  auto *impl = FromOpaque(key);
+  if ((impl->p != nullptr) != (impl->q != nullptr)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_ONLY_ONE_OF_P_Q_GIVEN);
     return 0;
   }
 
-  // |key->d| must be bounded by |key->n|. This ensures bounds on |RSA_bits|
+  // |impl->d| must be bounded by |impl->n|. This ensures bounds on |RSA_bits|
   // translate to bounds on the running time of private key operations.
-  if (key->d != nullptr &&
-      (BN_is_negative(key->d) || BN_cmp(key->d, key->n) >= 0)) {
+  if (impl->d != nullptr &&
+      (BN_is_negative(impl->d) || BN_cmp(impl->d, impl->n) >= 0)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_D_OUT_OF_RANGE);
     return 0;
   }
 
-  if (key->d == nullptr || key->p == nullptr) {
+  if (impl->d == nullptr || impl->p == nullptr) {
     // For a public key, or without p and q, there's nothing that can be
     // checked.
     return 1;
@@ -776,18 +832,18 @@
   // bounds, to avoid a DoS vector in |bn_mul_consttime| below. Note that
   // n was bound by |rsa_check_public_key|. This also implicitly checks p and q
   // are odd, which is a necessary condition for Montgomery reduction.
-  if (BN_is_negative(key->p) ||
-      constant_time_declassify_int(BN_cmp(key->p, key->n) >= 0) ||
-      BN_is_negative(key->q) ||
-      constant_time_declassify_int(BN_cmp(key->q, key->n) >= 0)) {
+  if (BN_is_negative(impl->p) ||
+      constant_time_declassify_int(BN_cmp(impl->p, impl->n) >= 0) ||
+      BN_is_negative(impl->q) ||
+      constant_time_declassify_int(BN_cmp(impl->q, impl->n) >= 0)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_N_NOT_EQUAL_P_Q);
     goto out;
   }
-  if (!bn_mul_consttime(&tmp, key->p, key->q, ctx)) {
+  if (!bn_mul_consttime(&tmp, impl->p, impl->q, ctx)) {
     OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
     goto out;
   }
-  if (BN_cmp(&tmp, key->n) != 0) {
+  if (BN_cmp(&tmp, impl->n) != 0) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_N_NOT_EQUAL_P_Q);
     goto out;
   }
@@ -796,14 +852,14 @@
   // may be unreduced because other implementations use the Euler totient. We
   // simply check that d * e is one mod p-1 and mod q-1. Note d and e were bound
   // by earlier checks in this function.
-  if (!bn_usub_consttime(&pm1, key->p, BN_value_one()) ||
-      !bn_usub_consttime(&qm1, key->q, BN_value_one())) {
+  if (!bn_usub_consttime(&pm1, impl->p, BN_value_one()) ||
+      !bn_usub_consttime(&qm1, impl->q, BN_value_one())) {
     OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
     goto out;
   }
   pm1_bits = BN_num_bits(&pm1);
   qm1_bits = BN_num_bits(&qm1);
-  if (!bn_mul_consttime(&de, key->d, key->e, ctx) ||
+  if (!bn_mul_consttime(&de, impl->d, impl->e, ctx) ||
       !bn_div_consttime(nullptr, &tmp, &de, &pm1, pm1_bits, ctx) ||
       !bn_div_consttime(nullptr, &de, &de, &qm1, qm1_bits, ctx)) {
     OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
@@ -816,20 +872,22 @@
     goto out;
   }
 
-  has_crt_values = key->dmp1 != nullptr;
-  if (has_crt_values != (key->dmq1 != nullptr) ||
-      has_crt_values != (key->iqmp != nullptr)) {
+  has_crt_values = impl->dmp1 != nullptr;
+  if (has_crt_values != (impl->dmq1 != nullptr) ||
+      has_crt_values != (impl->iqmp != nullptr)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_INCONSISTENT_SET_OF_CRT_VALUES);
     goto out;
   }
 
   if (has_crt_values) {
     int dmp1_ok, dmq1_ok, iqmp_ok;
-    if (!check_mod_inverse(&dmp1_ok, key->e, key->dmp1, &pm1, pm1_bits, ctx) ||
-        !check_mod_inverse(&dmq1_ok, key->e, key->dmq1, &qm1, qm1_bits, ctx) ||
+    if (!check_mod_inverse(&dmp1_ok, impl->e, impl->dmp1, &pm1, pm1_bits,
+                           ctx) ||
+        !check_mod_inverse(&dmq1_ok, impl->e, impl->dmq1, &qm1, qm1_bits,
+                           ctx) ||
         // |p| is odd, so |pm1| and |p| have the same bit width. If they didn't,
         // we only need a lower bound anyway.
-        !check_mod_inverse(&iqmp_ok, key->q, key->iqmp, key->p, pm1_bits,
+        !check_mod_inverse(&iqmp_ok, impl->q, impl->iqmp, impl->p, pm1_bits,
                            ctx)) {
       OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
       goto out;
@@ -907,14 +965,15 @@
   //
   // |key->e| may be nullptr if created with |RSA_new_private_key_no_e|.
   enum bn_primality_result_t primality_result;
-  if (key->e == nullptr ||          //
-      BN_num_bits(key->e) <= 16 ||  //
-      BN_num_bits(key->e) > 256 ||  //
-      !BN_is_odd(key->n) ||         //
-      !BN_is_odd(key->e) ||
-      !BN_gcd(&small_gcd, key->n, g_small_factors(), ctx) ||
+  auto *impl = FromOpaque(key);
+  if (impl->e == nullptr ||          //
+      BN_num_bits(impl->e) <= 16 ||  //
+      BN_num_bits(impl->e) > 256 ||  //
+      !BN_is_odd(impl->n) ||         //
+      !BN_is_odd(impl->e) ||
+      !BN_gcd(&small_gcd, impl->n, g_small_factors(), ctx) ||
       !BN_is_one(&small_gcd) ||
-      !BN_enhanced_miller_rabin_primality_test(&primality_result, key->n,
+      !BN_enhanced_miller_rabin_primality_test(&primality_result, impl->n,
                                                BN_prime_checks_for_generation,
                                                ctx, nullptr) ||
       primality_result != bn_non_prime_power_composite) {
@@ -925,7 +984,7 @@
   BN_free(&small_gcd);
   BN_CTX_free(ctx);
 
-  if (!ret || key->d == nullptr || key->p == nullptr) {
+  if (!ret || impl->d == nullptr || impl->p == nullptr) {
     // On a failure or on only a public key, there's nothing else can be
     // checked.
     return ret;
@@ -963,8 +1022,10 @@
 
 int bssl::rsa_private_transform_no_self_test(RSA *rsa, uint8_t *out,
                                              const uint8_t *in, size_t len) {
-  if (rsa->meth->private_transform) {
-    return rsa->meth->private_transform(rsa, out, in, len);
+  auto *impl = FromOpaque(rsa);
+
+  if (impl->meth->private_transform) {
+    return impl->meth->private_transform(rsa, out, in, len);
   }
 
   return rsa_default_private_transform(rsa, out, in, len);
@@ -976,6 +1037,12 @@
   return rsa_private_transform_no_self_test(rsa, out, in, len);
 }
 
-int RSA_flags(const RSA *rsa) { return rsa->flags; }
+int RSA_flags(const RSA *rsa) {
+  auto *impl = FromOpaque(rsa);
+  return impl->flags;
+}
 
-int RSA_test_flags(const RSA *rsa, int flags) { return rsa->flags & flags; }
+int RSA_test_flags(const RSA *rsa, int flags) {
+  auto *impl = FromOpaque(rsa);
+  return impl->flags & flags;
+}
diff --git a/crypto/fipsmodule/rsa/rsa_impl.cc.inc b/crypto/fipsmodule/rsa/rsa_impl.cc.inc
index 77cbca9..150d17a 100644
--- a/crypto/fipsmodule/rsa/rsa_impl.cc.inc
+++ b/crypto/fipsmodule/rsa/rsa_impl.cc.inc
@@ -26,6 +26,7 @@
 
 #include "../../bcm_support.h"
 #include "../../internal.h"
+#include "../../mem_internal.h"
 #include "../bn/internal.h"
 #include "../delocate.h"
 #include "../service_indicator/internal.h"
@@ -39,12 +40,14 @@
               "Max RSA size too big for Montgomery arithmetic");
 
 int bssl::rsa_check_public_key(const RSA *rsa) {
-  if (rsa->n == nullptr) {
+  auto *impl = FromOpaque(rsa);
+
+  if (impl->n == nullptr) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
     return 0;
   }
 
-  unsigned n_bits = BN_num_bits(rsa->n);
+  unsigned n_bits = BN_num_bits(impl->n);
   if (n_bits > OPENSSL_RSA_MAX_MODULUS_BITS) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_MODULUS_TOO_LARGE);
     return 0;
@@ -57,24 +60,24 @@
 
   // RSA moduli must be positive and odd. In addition to being necessary for RSA
   // in general, we cannot setup Montgomery reduction with even moduli.
-  if (!BN_is_odd(rsa->n) || BN_is_negative(rsa->n)) {
+  if (!BN_is_odd(impl->n) || BN_is_negative(impl->n)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_RSA_PARAMETERS);
     return 0;
   }
 
   static const unsigned kMaxExponentBits = 33;
-  if (rsa->e != nullptr) {
+  if (impl->e != nullptr) {
     // Reject e = 1, negative e, and even e. e must be odd to be relatively
     // prime with phi(n).
-    unsigned e_bits = BN_num_bits(rsa->e);
-    if (e_bits < 2 || BN_is_negative(rsa->e) || !BN_is_odd(rsa->e)) {
+    unsigned e_bits = BN_num_bits(impl->e);
+    if (e_bits < 2 || BN_is_negative(impl->e) || !BN_is_odd(impl->e)) {
       OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_E_VALUE);
       return 0;
     }
-    if (rsa->flags & RSA_FLAG_LARGE_PUBLIC_EXPONENT) {
+    if (impl->flags & RSA_FLAG_LARGE_PUBLIC_EXPONENT) {
       // The caller has requested disabling DoS protections. Still, e must be
       // less than n.
-      if (BN_ucmp(rsa->n, rsa->e) <= 0) {
+      if (BN_ucmp(impl->n, impl->e) <= 0) {
         OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_E_VALUE);
         return 0;
       }
@@ -95,9 +98,9 @@
 
       // The upper bound on |e_bits| and lower bound on |n_bits| imply e is
       // bounded by n.
-      assert(BN_ucmp(rsa->n, rsa->e) > 0);
+      assert(BN_ucmp(impl->n, impl->e) > 0);
     }
-  } else if (!(rsa->flags & RSA_FLAG_NO_PUBLIC_EXPONENT)) {
+  } else if (!(impl->flags & RSA_FLAG_NO_PUBLIC_EXPONENT)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
     return 0;
   }
@@ -124,7 +127,7 @@
 // After this function has returned, |rsa| may not be changed. This is needed
 // because |RSA| is a public struct and, additionally, OpenSSL 1.1.0 opaquified
 // it wrong (see https://github.com/openssl/openssl/issues/5158).
-static int freeze_private_key(RSA *rsa, BN_CTX *ctx) {
+static int freeze_private_key(RSAImpl *rsa, BN_CTX *ctx) {
   CRYPTO_MUTEX_lock_read(&rsa->lock);
   int frozen = rsa->private_key_frozen;
   CRYPTO_MUTEX_unlock_read(&rsa->lock);
@@ -222,23 +225,25 @@
 }
 
 void bssl::rsa_invalidate_key(RSA *rsa) {
-  rsa->private_key_frozen = 0;
+  auto *impl = FromOpaque(rsa);
 
-  BN_MONT_CTX_free(rsa->mont_n);
-  rsa->mont_n = nullptr;
-  BN_MONT_CTX_free(rsa->mont_p);
-  rsa->mont_p = nullptr;
-  BN_MONT_CTX_free(rsa->mont_q);
-  rsa->mont_q = nullptr;
+  impl->private_key_frozen = 0;
 
-  BN_free(rsa->d_fixed);
-  rsa->d_fixed = nullptr;
-  BN_free(rsa->dmp1_fixed);
-  rsa->dmp1_fixed = nullptr;
-  BN_free(rsa->dmq1_fixed);
-  rsa->dmq1_fixed = nullptr;
-  BN_free(rsa->iqmp_mont);
-  rsa->iqmp_mont = nullptr;
+  BN_MONT_CTX_free(impl->mont_n);
+  impl->mont_n = nullptr;
+  BN_MONT_CTX_free(impl->mont_p);
+  impl->mont_p = nullptr;
+  BN_MONT_CTX_free(impl->mont_q);
+  impl->mont_q = nullptr;
+
+  BN_free(impl->d_fixed);
+  impl->d_fixed = nullptr;
+  BN_free(impl->dmp1_fixed);
+  impl->dmp1_fixed = nullptr;
+  BN_free(impl->dmq1_fixed);
+  impl->dmq1_fixed = nullptr;
+  BN_free(impl->iqmp_mont);
+  impl->iqmp_mont = nullptr;
 }
 
 int bssl::rsa_default_sign_raw(RSA *rsa, size_t *out_len, uint8_t *out,
@@ -289,12 +294,15 @@
 }
 
 
-static int rsa_mod_exp_crt(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx);
+static int rsa_mod_exp_crt(BIGNUM *r0, const BIGNUM *I, RSAImpl *rsa,
+                           BN_CTX *ctx);
 
 int bssl::rsa_verify_raw_no_self_test(RSA *rsa, size_t *out_len, uint8_t *out,
                                       size_t max_out, const uint8_t *in,
                                       size_t in_len, int padding) {
-  if (rsa->n == nullptr || rsa->e == nullptr) {
+  auto *impl = FromOpaque(rsa);
+
+  if (impl->n == nullptr || impl->e == nullptr) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
     return 0;
   }
@@ -342,14 +350,14 @@
     goto err;
   }
 
-  if (BN_ucmp(f, rsa->n) >= 0) {
+  if (BN_ucmp(f, impl->n) >= 0) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
     goto err;
   }
 
-  if (!BN_MONT_CTX_set_locked(&rsa->mont_n, &rsa->lock, rsa->n, ctx.get()) ||
-      !BN_mod_exp_mont(result, f, rsa->e, &rsa->mont_n->N, ctx.get(),
-                       rsa->mont_n)) {
+  if (!BN_MONT_CTX_set_locked(&impl->mont_n, &impl->lock, impl->n, ctx.get()) ||
+      !BN_mod_exp_mont(result, f, impl->e, &impl->mont_n->N, ctx.get(),
+                       impl->mont_n)) {
     goto err;
   }
 
@@ -393,7 +401,9 @@
 
 int bssl::rsa_default_private_transform(RSA *rsa, uint8_t *out,
                                         const uint8_t *in, size_t len) {
-  if (rsa->n == nullptr || rsa->d == nullptr) {
+  auto *impl = FromOpaque(rsa);
+
+  if (impl->n == nullptr || impl->d == nullptr) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
     return 0;
   }
@@ -410,25 +420,25 @@
   }
 
   // The caller should have ensured this.
-  assert(len == BN_num_bytes(rsa->n));
+  assert(len == BN_num_bytes(impl->n));
   if (BN_bin2bn(in, len, f) == nullptr) {
     return 0;
   }
 
   // The input to the RSA private transform may be secret, but padding is
   // expected to construct a value within range, so we can leak this comparison.
-  if (constant_time_declassify_int(BN_ucmp(f, rsa->n) >= 0)) {
+  if (constant_time_declassify_int(BN_ucmp(f, impl->n) >= 0)) {
     // Usually the padding functions would catch this.
     OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
     return 0;
   }
 
-  if (!freeze_private_key(rsa, ctx.get())) {
+  if (!freeze_private_key(impl, ctx.get())) {
     OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
-  if (rsa->e == nullptr && (rsa->flags & RSA_FLAG_NO_PUBLIC_EXPONENT) == 0) {
+  if (impl->e == nullptr && (impl->flags & RSA_FLAG_NO_PUBLIC_EXPONENT) == 0) {
     // Unless the private key was specifically created with an API like
     // |RSA_new_private_key_no_e|, don't allow RSA keys to be missing the public
     // exponent, which disables some fault attack mitigations. (It should not be
@@ -437,19 +447,19 @@
     return 0;
   }
 
-  if (rsa->p != nullptr && rsa->q != nullptr && rsa->e != nullptr &&
-      rsa->dmp1 != nullptr && rsa->dmq1 != nullptr && rsa->iqmp != nullptr &&
-      // Require that we can reduce |f| by |rsa->p| and |rsa->q| in constant
+  if (impl->p != nullptr && impl->q != nullptr && impl->e != nullptr &&
+      impl->dmp1 != nullptr && impl->dmq1 != nullptr && impl->iqmp != nullptr &&
+      // Require that we can reduce |f| by |impl->p| and |impl->q| in constant
       // time, which requires primes be the same size, rounded to the Montgomery
       // coefficient. (See |mod_montgomery|.) This is not required by RFC 8017,
       // but it is true for keys generated by us and all common implementations.
-      bn_less_than_montgomery_R(rsa->q, rsa->mont_p) &&
-      bn_less_than_montgomery_R(rsa->p, rsa->mont_q)) {
-    if (!rsa_mod_exp_crt(result, f, rsa, ctx.get())) {
+      bn_less_than_montgomery_R(impl->q, impl->mont_p) &&
+      bn_less_than_montgomery_R(impl->p, impl->mont_q)) {
+    if (!rsa_mod_exp_crt(result, f, impl, ctx.get())) {
       return 0;
     }
-  } else if (!BN_mod_exp_mont_consttime(result, f, rsa->d_fixed, rsa->n,
-                                        ctx.get(), rsa->mont_n)) {
+  } else if (!BN_mod_exp_mont_consttime(result, f, impl->d_fixed, impl->n,
+                                        ctx.get(), impl->mont_n)) {
     return 0;
   }
 
@@ -463,11 +473,11 @@
   //
   // This check is cheap assuming |e| is small, which we require in
   // |rsa_check_public_key|.
-  if (rsa->e != nullptr) {
+  if (impl->e != nullptr) {
     BIGNUM *vrfy = BN_CTX_get(ctx.get());
     if (vrfy == nullptr ||
-        !BN_mod_exp_mont(vrfy, result, rsa->e, rsa->n, ctx.get(),
-                         rsa->mont_n) ||
+        !BN_mod_exp_mont(vrfy, result, impl->e, impl->n, ctx.get(),
+                         impl->mont_n) ||
         !constant_time_declassify_int(BN_equal_consttime(vrfy, f))) {
       OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
       return 0;
@@ -479,7 +489,7 @@
   // the result.
   //
   // See Falko Strenzke, "Manger's Attack revisited", ICICS 2010.
-  assert(result->width == rsa->mont_n->N.width);
+  assert(result->width == impl->mont_n->N.width);
   bn_assert_fits_in_bytes(result, len);
   if (!BN_bn2bin_padded(out, len, result)) {
     OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
@@ -520,7 +530,8 @@
   return 1;
 }
 
-static int rsa_mod_exp_crt(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx) {
+static int rsa_mod_exp_crt(BIGNUM *r0, const BIGNUM *I, RSAImpl *rsa,
+                           BN_CTX *ctx) {
   assert(ctx != nullptr);
 
   assert(rsa->n != nullptr);
@@ -559,7 +570,7 @@
       !BN_mod_exp_mont_consttime(r0, r1, rsa->dmp1_fixed, p, ctx,
                                  rsa->mont_p) ||
       // Compute r0 = r0 - m1 mod p. |m1| is reduced mod |q|, not |p|, so we
-      // just run |mod_montgomery| again for simplicity. This could be more
+      // just run |mod_montgomery| again for srsaicity. This could be more
       // efficient with more cases: if |p > q|, |m1| is already reduced. If
       // |p < q| but they have the same bit width, |bn_reduce_once| suffices.
       // However, compared to over 2048 Montgomery multiplications above, this
@@ -724,7 +735,7 @@
 //
 // This function returns one on success and zero on failure. It has a failure
 // probability of about 2^-20.
-static int rsa_generate_key_impl(RSA *rsa, int bits, const BIGNUM *e_value,
+static int rsa_generate_key_impl(RSAImpl *rsa, int bits, const BIGNUM *e_value,
                                  BN_GENCB *cb) {
   if (bits > OPENSSL_RSA_MAX_MODULUS_BITS) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_MODULUS_TOO_LARGE);
@@ -814,7 +825,7 @@
     }
 
     // Calculate d = e^(-1) (mod lcm(p-1, q-1)), per FIPS 186-5. This differs
-    // from typical RSA implementations which use (p-1)*(q-1).
+    // from typical RSA rsaementations which use (p-1)*(q-1).
     //
     // Note this means the size of d might reveal information about p-1 and
     // q-1. However, we do operations with Chinese Remainder Theorem, so we only
@@ -862,7 +873,7 @@
     return 0;
   }
 
-  // Sanity-check that |rsa->n| has the specified size. This is implied by
+  // Sanity-check that |rsa->n| has the specified size. This is rsaied by
   // |generate_prime|'s bounds.
   if (BN_num_bits(rsa->n) != (unsigned)bits) {
     OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
@@ -892,7 +903,7 @@
   *in = nullptr;
 }
 
-static int RSA_generate_key_ex_maybe_fips(RSA *rsa, int bits,
+static int RSA_generate_key_ex_maybe_fips(RSAImpl *rsa, int bits,
                                           const BIGNUM *e_value, BN_GENCB *cb,
                                           int check_fips) {
   boringssl_ensure_rsa_self_test();
@@ -902,7 +913,7 @@
     return 0;
   }
 
-  UniquePtr<RSA> tmp;
+  UniquePtr<RSAImpl> tmp;
 
   // |rsa_generate_key_impl|'s 2^-20 failure probability is too high at scale,
   // so we run the FIPS algorithm four times, bringing it down to 2^-80. We
@@ -912,7 +923,7 @@
   do {
     ERR_clear_error();
     // Generate into scratch space, to avoid leaving partial work on failure.
-    tmp.reset(RSA_new());
+    tmp.reset(FromOpaque(RSA_new()));
     if (tmp == nullptr) {
       return 0;
     }
@@ -955,7 +966,7 @@
 
 int RSA_generate_key_ex(RSA *rsa, int bits, const BIGNUM *e_value,
                         BN_GENCB *cb) {
-  return RSA_generate_key_ex_maybe_fips(rsa, bits, e_value, cb,
+  return RSA_generate_key_ex_maybe_fips(FromOpaque(rsa), bits, e_value, cb,
                                         /*check_fips=*/0);
 }
 
@@ -972,7 +983,8 @@
 
   BIGNUM *e = BN_new();
   int ret = e != nullptr && BN_set_word(e, RSA_F4) &&
-            RSA_generate_key_ex_maybe_fips(rsa, bits, e, cb, /*check_fips=*/1);
+            RSA_generate_key_ex_maybe_fips(FromOpaque(rsa), bits, e, cb,
+                                           /*check_fips=*/1);
   BN_free(e);
 
   if (ret) {
diff --git a/crypto/fipsmodule/self_check/self_check.cc.inc b/crypto/fipsmodule/self_check/self_check.cc.inc
index 2477f36..37da586 100644
--- a/crypto/fipsmodule/self_check/self_check.cc.inc
+++ b/crypto/fipsmodule/self_check/self_check.cc.inc
@@ -192,7 +192,7 @@
       0x89, 0x29, 0xc7, 0x05, 0x27, 0x68, 0x90, 0x15,
   };
 
-  RSA *rsa = RSA_new();
+  RSAImpl *rsa = FromOpaque(RSA_new());
   if (rsa == nullptr ||  //
       !set_bignum(&rsa->n, kN, sizeof(kN)) ||
       !set_bignum(&rsa->e, kE, sizeof(kE)) ||
diff --git a/crypto/rsa/rsa_asn1.cc b/crypto/rsa/rsa_asn1.cc
index 4b99643..93818b5 100644
--- a/crypto/rsa/rsa_asn1.cc
+++ b/crypto/rsa/rsa_asn1.cc
@@ -54,7 +54,7 @@
 }
 
 RSA *RSA_parse_public_key(CBS *cbs) {
-  RSA *ret = RSA_new();
+  RSAImpl *ret = FromOpaque(RSA_new());
   if (ret == nullptr) {
     return nullptr;
   }
@@ -91,9 +91,9 @@
 
 int RSA_marshal_public_key(CBB *cbb, const RSA *rsa) {
   CBB child;
+  const RSAImpl *impl = FromOpaque(rsa);
   if (!CBB_add_asn1(cbb, &child, CBS_ASN1_SEQUENCE) ||
-      !marshal_integer(&child, rsa->n) ||
-      !marshal_integer(&child, rsa->e) ||
+      !marshal_integer(&child, impl->n) || !marshal_integer(&child, impl->e) ||
       !CBB_flush(cbb)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_ENCODE_ERROR);
     return 0;
@@ -120,7 +120,7 @@
 static const uint64_t kVersionTwoPrime = 0;
 
 RSA *RSA_parse_private_key(CBS *cbs) {
-  RSA *ret = RSA_new();
+  RSAImpl *ret = FromOpaque(RSA_new());
   if (ret == nullptr) {
     return nullptr;
   }
@@ -179,18 +179,16 @@
 }
 
 int RSA_marshal_private_key(CBB *cbb, const RSA *rsa) {
+  const RSAImpl *impl = FromOpaque(rsa);
   CBB child;
   if (!CBB_add_asn1(cbb, &child, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&child, kVersionTwoPrime) ||
-      !marshal_integer(&child, rsa->n) ||
-      !marshal_integer(&child, rsa->e) ||
-      !marshal_integer(&child, rsa->d) ||
-      !marshal_integer(&child, rsa->p) ||
-      !marshal_integer(&child, rsa->q) ||
-      !marshal_integer(&child, rsa->dmp1) ||
-      !marshal_integer(&child, rsa->dmq1) ||
-      !marshal_integer(&child, rsa->iqmp) ||
-      !CBB_flush(cbb)) {
+      !marshal_integer(&child, impl->n) || !marshal_integer(&child, impl->e) ||
+      !marshal_integer(&child, impl->d) || !marshal_integer(&child, impl->p) ||
+      !marshal_integer(&child, impl->q) ||
+      !marshal_integer(&child, impl->dmp1) ||
+      !marshal_integer(&child, impl->dmq1) ||
+      !marshal_integer(&child, impl->iqmp) || !CBB_flush(cbb)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_ENCODE_ERROR);
     return 0;
   }
diff --git a/crypto/rsa/rsa_crypt.cc b/crypto/rsa/rsa_crypt.cc
index aa1004d..68fb133 100644
--- a/crypto/rsa/rsa_crypt.cc
+++ b/crypto/rsa/rsa_crypt.cc
@@ -342,7 +342,9 @@
 
 int RSA_encrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
                 const uint8_t *in, size_t in_len, int padding) {
-  if (rsa->n == nullptr || rsa->e == nullptr) {
+  auto *impl = FromOpaque(rsa);
+
+  if (impl->n == nullptr || impl->e == nullptr) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
     return 0;
   }
@@ -396,15 +398,15 @@
     goto err;
   }
 
-  if (BN_ucmp(f, rsa->n) >= 0) {
+  if (BN_ucmp(f, impl->n) >= 0) {
     // usually the padding functions would catch this
     OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
     goto err;
   }
 
-  if (!BN_MONT_CTX_set_locked(&rsa->mont_n, &rsa->lock, rsa->n, ctx.get()) ||
-      !BN_mod_exp_mont(result, f, rsa->e, &rsa->mont_n->N, ctx.get(),
-                       rsa->mont_n)) {
+  if (!BN_MONT_CTX_set_locked(&impl->mont_n, &impl->lock, impl->n, ctx.get()) ||
+      !BN_mod_exp_mont(result, f, impl->e, &impl->mont_n->N, ctx.get(),
+                       impl->mont_n)) {
     goto err;
   }
 
@@ -490,8 +492,9 @@
 
 int RSA_decrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
                 const uint8_t *in, size_t in_len, int padding) {
-  if (rsa->meth->decrypt) {
-    return rsa->meth->decrypt(rsa, out_len, out, max_out, in, in_len, padding);
+  auto *impl = FromOpaque(rsa);
+  if (impl->meth->decrypt) {
+    return impl->meth->decrypt(rsa, out_len, out, max_out, in, in_len, padding);
   }
 
   return rsa_default_decrypt(rsa, out_len, out, max_out, in, in_len, padding);
diff --git a/crypto/rsa/rsa_test.cc b/crypto/rsa/rsa_test.cc
index a0ded6f..bdee9a2 100644
--- a/crypto/rsa/rsa_test.cc
+++ b/crypto/rsa/rsa_test.cc
@@ -34,6 +34,7 @@
 #include "../fipsmodule/bn/internal.h"
 #include "../fipsmodule/rsa/internal.h"
 #include "../internal.h"
+#include "../mem_internal.h"
 #include "../test/test_data.h"
 #include "../test/test_util.h"
 
@@ -576,7 +577,7 @@
 }
 
 TEST(RSATest, GenerateFIPS) {
-  UniquePtr<RSA> rsa(RSA_new());
+  UniquePtr<RSAImpl> rsa(FromOpaque(RSA_new()));
   ASSERT_TRUE(rsa);
 
   // RSA_generate_key_fips may only be used for 2048-, 3072-, and 4096-bit
@@ -595,7 +596,7 @@
   for (const size_t bits : {2048, 3072, 4096}) {
     SCOPED_TRACE(bits);
 
-    rsa.reset(RSA_new());
+    rsa.reset(FromOpaque(RSA_new()));
     ASSERT_TRUE(rsa);
     ASSERT_TRUE(RSA_generate_key_fips(rsa.get(), bits, nullptr));
     EXPECT_EQ(bits, BN_num_bits(rsa->n));
@@ -603,7 +604,7 @@
 }
 
 TEST(RSATest, BadKey) {
-  UniquePtr<RSA> key(RSA_new());
+  UniquePtr<RSAImpl> key(FromOpaque(RSA_new()));
   UniquePtr<BIGNUM> e(BN_new());
   ASSERT_TRUE(key);
   ASSERT_TRUE(e);
@@ -622,13 +623,14 @@
   size_t der_len;
   ASSERT_TRUE(RSA_private_key_to_bytes(&der, &der_len, key.get()));
   UniquePtr<uint8_t> delete_der(der);
-  key.reset(RSA_private_key_from_bytes(der, der_len));
+  key.reset(FromOpaque(RSA_private_key_from_bytes(der, der_len)));
   EXPECT_FALSE(key);
 }
 
 TEST(RSATest, ASN1) {
   // Test that private keys may be decoded.
-  UniquePtr<RSA> rsa(RSA_private_key_from_bytes(kKey1, sizeof(kKey1)));
+  UniquePtr<RSAImpl> rsa(
+      FromOpaque(RSA_private_key_from_bytes(kKey1, sizeof(kKey1))));
   ASSERT_TRUE(rsa);
 
   // Test that the serialization round-trips.
@@ -643,7 +645,7 @@
   delete_der.reset(der);
 
   // Public keys may be parsed back out.
-  rsa.reset(RSA_public_key_from_bytes(der, der_len));
+  rsa.reset(FromOpaque(RSA_public_key_from_bytes(der, der_len)));
   ASSERT_TRUE(rsa);
   EXPECT_FALSE(rsa->p);
   EXPECT_FALSE(rsa->q);
@@ -664,8 +666,8 @@
   ERR_clear_error();
 
   // Public keys with negative moduli are invalid.
-  rsa.reset(
-      RSA_public_key_from_bytes(kEstonianRSAKey, sizeof(kEstonianRSAKey)));
+  rsa.reset(FromOpaque(
+      RSA_public_key_from_bytes(kEstonianRSAKey, sizeof(kEstonianRSAKey))));
   EXPECT_FALSE(rsa);
   ERR_clear_error();
 }
@@ -683,22 +685,22 @@
   ASSERT_TRUE(e);
   ASSERT_TRUE(BN_set_word(e.get(), RSA_F4));
 
-  UniquePtr<RSA> rsa(RSA_new());
+  UniquePtr<RSAImpl> rsa(FromOpaque(RSA_new()));
   ASSERT_TRUE(rsa);
   ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), 1025, e.get(), nullptr));
   EXPECT_EQ(1024u, BN_num_bits(rsa->n));
 
-  rsa.reset(RSA_new());
+  rsa.reset(FromOpaque(RSA_new()));
   ASSERT_TRUE(rsa);
   ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), 1027, e.get(), nullptr));
   EXPECT_EQ(1024u, BN_num_bits(rsa->n));
 
-  rsa.reset(RSA_new());
+  rsa.reset(FromOpaque(RSA_new()));
   ASSERT_TRUE(rsa);
   ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), 1151, e.get(), nullptr));
   EXPECT_EQ(1024u, BN_num_bits(rsa->n));
 
-  rsa.reset(RSA_new());
+  rsa.reset(FromOpaque(RSA_new()));
   ASSERT_TRUE(rsa);
   ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), 1152, e.get(), nullptr));
   EXPECT_EQ(1152u, BN_num_bits(rsa->n));
@@ -750,7 +752,7 @@
       "a54bb61ea5e64b9423102933ea100c12dad809fbf9589515e9d28e867f6b95c2d307f792"
       "cac28c6d7d23f441cb5b62798233db29b5cc0348";
 
-  UniquePtr<RSA> rsa(RSA_new());
+  UniquePtr<RSAImpl> rsa(FromOpaque(RSA_new()));
   ASSERT_TRUE(rsa);
 
   // Missing n or e does not pass.
@@ -895,7 +897,7 @@
 }
 
 TEST(RSATest, KeygenFail) {
-  UniquePtr<RSA> rsa(RSA_new());
+  UniquePtr<RSAImpl> rsa(FromOpaque(RSA_new()));
   ASSERT_TRUE(rsa);
 
   // Cause RSA key generation after a prime has been generated, to test that
@@ -1195,7 +1197,8 @@
 TEST(RSATest, LargeE) {
   // Test an RSA key with large e by swapping d and e in kKey1.
   // Since e is small, e mod (p-1) and e mod (q-1) will simply be e.
-  UniquePtr<RSA> key(RSA_private_key_from_bytes(kKey1, sizeof(kKey1)));
+  UniquePtr<RSAImpl> key(
+      FromOpaque(RSA_private_key_from_bytes(kKey1, sizeof(kKey1))));
   ASSERT_TRUE(key);
   const BIGNUM *n = RSA_get0_n(key.get());
   const BIGNUM *e = RSA_get0_e(key.get());
@@ -1207,14 +1210,15 @@
   // By default, the large exponent is not allowed as e.
   UniquePtr<RSA> pub(RSA_new_public_key(n, /*e=*/d));
   EXPECT_FALSE(pub);
-  UniquePtr<RSA> priv(RSA_new_private_key(n, /*e=*/d, /*d=*/e, p, q,
-                                          /*dmp1=*/e, /*dmq1=*/e, iqmp));
+  UniquePtr<RSAImpl> priv(
+      FromOpaque(RSA_new_private_key(n, /*e=*/d, /*d=*/e, p, q,
+                                     /*dmp1=*/e, /*dmq1=*/e, iqmp)));
   EXPECT_FALSE(priv);
 
   // Constructing such a key piecemeal also would not work. This was only
   // possible with private APIs, so when |RSA| is opaque, this case will be
   // impossible.
-  priv.reset(RSA_new());
+  priv.reset(FromOpaque(RSA_new()));
   ASSERT_TRUE(priv);
   priv->n = BN_dup(n);
   ASSERT_TRUE(priv->n);
@@ -1233,8 +1237,9 @@
   // But the "large e" APIs tolerate it.
   pub.reset(RSA_new_public_key_large_e(n, /*e=*/d));
   ASSERT_TRUE(pub);
-  priv.reset(RSA_new_private_key_large_e(n, /*e=*/d, /*d=*/e, p, q, /*dmp1=*/e,
-                                         /*dmq1=*/e, iqmp));
+  priv.reset(FromOpaque(RSA_new_private_key_large_e(n, /*e=*/d, /*d=*/e, p, q,
+                                                    /*dmp1=*/e,
+                                                    /*dmq1=*/e, iqmp)));
   ASSERT_TRUE(priv);
 
   // Test that operations work correctly.
@@ -1342,7 +1347,8 @@
 
 #if defined(OPENSSL_THREADS)
 TEST(RSATest, Threads) {
-  UniquePtr<RSA> rsa_template(RSA_private_key_from_bytes(kKey1, sizeof(kKey1)));
+  UniquePtr<RSAImpl> rsa_template(
+      FromOpaque(RSA_private_key_from_bytes(kKey1, sizeof(kKey1))));
   ASSERT_TRUE(rsa_template);
 
   const uint8_t kDummyHash[32] = {0};
@@ -1354,7 +1360,7 @@
 
   // RSA keys may be assembled piece-meal and then used in parallel between
   // threads, which requires internal locking to create some derived properties.
-  UniquePtr<RSA> rsa(RSA_new());
+  UniquePtr<RSAImpl> rsa(FromOpaque(RSA_new()));
   rsa->n = BN_dup(rsa_template->n);
   ASSERT_TRUE(rsa->n);
   rsa->e = BN_dup(rsa_template->e);
diff --git a/util/audit_symbols.go b/util/audit_symbols.go
index 586ed8f..9a9c9a9 100644
--- a/util/audit_symbols.go
+++ b/util/audit_symbols.go
@@ -89,7 +89,6 @@
 	// TODO(crbug.com/42220000): Temporary symbols, to be eliminated.
 	regexp.MustCompile(`.*ec_group_st.*`),
 	regexp.MustCompile(`.*evp_pkey_st.*`),
-	regexp.MustCompile(`.*rsa_st.*`),
 }
 
 const (