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

Bug: 42220000
Change-Id: If289dbc99f402d3a8f55083d179c30126a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/88431
Reviewed-by: Xiangfei Ding <xfding@google.com>
Commit-Queue: Xiangfei Ding <xfding@google.com>
diff --git a/crypto/ec/ec_asn1.cc b/crypto/ec/ec_asn1.cc
index 14f2f57..a34b410 100644
--- a/crypto/ec/ec_asn1.cc
+++ b/crypto/ec/ec_asn1.cc
@@ -30,6 +30,7 @@
 #include "../bytestring/internal.h"
 #include "../fipsmodule/ec/internal.h"
 #include "../internal.h"
+#include "../mem_internal.h"
 #include "internal.h"
 
 
@@ -108,7 +109,7 @@
     return nullptr;
   }
 
-  UniquePtr<EC_KEY> ret(EC_KEY_new());
+  UniquePtr<ECKey> ret(FromOpaque(EC_KEY_new()));
   if (ret == nullptr || !EC_KEY_set_group(ret.get(), group)) {
     return nullptr;
   }
@@ -177,7 +178,9 @@
 
 int EC_KEY_marshal_private_key(CBB *cbb, const EC_KEY *key,
                                unsigned enc_flags) {
-  if (key == nullptr || key->group == nullptr || key->priv_key == nullptr) {
+  const ECKey *key_impl = FromOpaque(key);
+  if (key_impl == nullptr || key_impl->group == nullptr ||
+      key_impl->priv_key == nullptr) {
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
@@ -187,8 +190,8 @@
       !CBB_add_asn1_uint64(&ec_private_key, 1 /* version */) ||
       !CBB_add_asn1(&ec_private_key, &private_key, CBS_ASN1_OCTETSTRING) ||
       !BN_bn2cbb_padded(&private_key,
-                        BN_num_bytes(EC_GROUP_get0_order(key->group)),
-                        EC_KEY_get0_private_key(key))) {
+                        BN_num_bytes(EC_GROUP_get0_order(key_impl->group)),
+                        EC_KEY_get0_private_key(key_impl))) {
     OPENSSL_PUT_ERROR(EC, EC_R_ENCODE_ERROR);
     return 0;
   }
@@ -196,7 +199,7 @@
   if (!(enc_flags & EC_PKEY_NO_PARAMETERS)) {
     CBB child;
     if (!CBB_add_asn1(&ec_private_key, &child, kParametersTag) ||
-        !EC_KEY_marshal_curve_name(&child, key->group) ||
+        !EC_KEY_marshal_curve_name(&child, key_impl->group) ||
         !CBB_flush(&ec_private_key)) {
       OPENSSL_PUT_ERROR(EC, EC_R_ENCODE_ERROR);
       return 0;
@@ -204,15 +207,15 @@
   }
 
   // TODO(fork): replace this flexibility with sensible default?
-  if (!(enc_flags & EC_PKEY_NO_PUBKEY) && key->pub_key != nullptr) {
+  if (!(enc_flags & EC_PKEY_NO_PUBKEY) && key_impl->pub_key != nullptr) {
     CBB child, public_key;
     if (!CBB_add_asn1(&ec_private_key, &child, kPublicKeyTag) ||
         !CBB_add_asn1(&child, &public_key, CBS_ASN1_BITSTRING) ||
         // As in a SubjectPublicKeyInfo, the byte-encoded public key is then
         // encoded as a BIT STRING with bits ordered as in the DER encoding.
         !CBB_add_u8(&public_key, 0 /* padding */) ||
-        !EC_POINT_point2cbb(&public_key, key->group, key->pub_key,
-                            key->conv_form, nullptr) ||
+        !EC_POINT_point2cbb(&public_key, key_impl->group, key_impl->pub_key,
+                            key_impl->conv_form, nullptr) ||
         !CBB_flush(&ec_private_key)) {
       OPENSSL_PUT_ERROR(EC, EC_R_ENCODE_ERROR);
       return 0;
@@ -475,24 +478,26 @@
 }
 
 int i2d_ECParameters(const EC_KEY *key, uint8_t **outp) {
-  if (key == nullptr || key->group == nullptr) {
+  const ECKey *key_impl = FromOpaque(key);
+  if (key_impl == nullptr || key_impl->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return -1;
   }
   return I2DFromCBB(
       /*initial_capacity=*/16, outp, [&](CBB *cbb) -> bool {
-        return EC_KEY_marshal_curve_name(cbb, key->group);
+        return EC_KEY_marshal_curve_name(cbb, key_impl->group);
       });
 }
 
 EC_KEY *o2i_ECPublicKey(EC_KEY **keyp, const uint8_t **inp, long len) {
-  EC_KEY *ret = nullptr;
+  ECKey *ret = nullptr;
 
-  if (keyp == nullptr || *keyp == nullptr || (*keyp)->group == nullptr) {
+  if (keyp == nullptr || *keyp == nullptr ||
+      FromOpaque(*keyp)->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return nullptr;
   }
-  ret = *keyp;
+  ret = FromOpaque(*keyp);
   if (ret->pub_key == nullptr &&
       (ret->pub_key = EC_POINT_new(ret->group)) == nullptr) {
     return nullptr;
@@ -512,12 +517,13 @@
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
+  const ECKey *key_impl = FromOpaque(key);
   // No initial capacity because |EC_POINT_point2cbb| will internally reserve
   // the right size in one shot, so it's best to leave this at zero.
   int ret = I2DFromCBB(
       /*initial_capacity=*/0, outp, [&](CBB *cbb) -> bool {
-        return EC_POINT_point2cbb(cbb, key->group, key->pub_key, key->conv_form,
-                                  nullptr);
+        return EC_POINT_point2cbb(cbb, key_impl->group, key_impl->pub_key,
+                                  key_impl->conv_form, nullptr);
       });
   // Historically, this function used the wrong return value on error.
   return ret > 0 ? ret : 0;
diff --git a/crypto/ecdh/ecdh.cc b/crypto/ecdh/ecdh.cc
index 4ac218c..cbd363d 100644
--- a/crypto/ecdh/ecdh.cc
+++ b/crypto/ecdh/ecdh.cc
@@ -32,11 +32,11 @@
                      const EC_KEY *priv_key,
                      void *(*kdf)(const void *in, size_t inlen, void *out,
                                   size_t *out_len)) {
-  if (priv_key->priv_key == nullptr) {
+  if (FromOpaque(priv_key)->priv_key == nullptr) {
     OPENSSL_PUT_ERROR(ECDH, ECDH_R_NO_PRIVATE_VALUE);
     return -1;
   }
-  const EC_SCALAR *const priv = &priv_key->priv_key->scalar;
+  const EC_SCALAR *const priv = &FromOpaque(priv_key)->priv_key->scalar;
   const EC_GROUP *const group = EC_KEY_get0_group(priv_key);
   if (EC_GROUP_cmp(group, pub_key->group, nullptr) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
diff --git a/crypto/ecdsa/ecdsa_asn1.cc b/crypto/ecdsa/ecdsa_asn1.cc
index a49543f..d4d0b4b 100644
--- a/crypto/ecdsa/ecdsa_asn1.cc
+++ b/crypto/ecdsa/ecdsa_asn1.cc
@@ -76,9 +76,10 @@
 
 int ECDSA_sign(int type, const uint8_t *digest, size_t digest_len, uint8_t *sig,
                unsigned int *out_sig_len, const EC_KEY *eckey) {
-  if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
-    return eckey->ecdsa_meth->sign(digest, digest_len, sig, out_sig_len,
-                                   (EC_KEY *)eckey /* cast away const */);
+  const ECKey *eckey_impl = FromOpaque(eckey);
+  if (eckey_impl->ecdsa_meth && eckey_impl->ecdsa_meth->sign) {
+    return eckey_impl->ecdsa_meth->sign(digest, digest_len, sig, out_sig_len,
+                                        (EC_KEY *)eckey /* cast away const */);
   }
 
   *out_sig_len = 0;
diff --git a/crypto/fipsmodule/ec/ec_key.cc.inc b/crypto/fipsmodule/ec/ec_key.cc.inc
index 8d7d086..9831423 100644
--- a/crypto/fipsmodule/ec/ec_key.cc.inc
+++ b/crypto/fipsmodule/ec/ec_key.cc.inc
@@ -57,7 +57,7 @@
 EC_KEY *EC_KEY_new() { return EC_KEY_new_method(nullptr); }
 
 EC_KEY *EC_KEY_new_method(const ENGINE *engine) {
-  EC_KEY *ret = NewZeroed<EC_KEY>();
+  ECKey *ret = NewZeroed<ECKey>();
   if (ret == nullptr) {
     return nullptr;
   }
@@ -87,7 +87,7 @@
 }
 
 EC_KEY *EC_KEY_new_by_curve_name(int nid) {
-  EC_KEY *ret = EC_KEY_new();
+  ECKey *ret = FromOpaque(EC_KEY_new());
   if (ret == nullptr) {
     return nullptr;
   }
@@ -99,29 +99,37 @@
   return ret;
 }
 
+ECKey::~ECKey() {
+  // Refcount can be 1 when called by UniquePtr, and 0 when called by
+  // EC_KEY_free.
+  BSSL_CHECK(references.load() <= 1);
+
+  if (ecdsa_meth) {
+    if (ecdsa_meth->finish) {
+      ecdsa_meth->finish(this);
+    }
+    METHOD_unref(ecdsa_meth);
+  }
+
+  CRYPTO_free_ex_data(g_ec_ex_data_class_bss_get(), &ex_data);
+
+  EC_GROUP_free(group);
+  EC_POINT_free(pub_key);
+  ec_wrapped_scalar_free(priv_key);
+}
+
 void EC_KEY_free(EC_KEY *r) {
   if (r == nullptr) {
     return;
   }
 
-  if (!CRYPTO_refcount_dec_and_test_zero(&r->references)) {
+  auto *impl = FromOpaque(r);
+
+  if (!CRYPTO_refcount_dec_and_test_zero(&impl->references)) {
     return;
   }
 
-  if (r->ecdsa_meth) {
-    if (r->ecdsa_meth->finish) {
-      r->ecdsa_meth->finish(r);
-    }
-    METHOD_unref(r->ecdsa_meth);
-  }
-
-  CRYPTO_free_ex_data(g_ec_ex_data_class_bss_get(), &r->ex_data);
-
-  EC_GROUP_free(r->group);
-  EC_POINT_free(r->pub_key);
-  ec_wrapped_scalar_free(r->priv_key);
-
-  Delete(r);
+  Delete(impl);
 }
 
 EC_KEY *EC_KEY_dup(const EC_KEY *src) {
@@ -130,87 +138,100 @@
     return nullptr;
   }
 
-  EC_KEY *ret = EC_KEY_new();
+  ECKey *ret = FromOpaque(EC_KEY_new());
   if (ret == nullptr) {
     return nullptr;
   }
 
-  if ((src->group != nullptr && !EC_KEY_set_group(ret, src->group)) ||
-      (src->pub_key != nullptr && !EC_KEY_set_public_key(ret, src->pub_key)) ||
-      (src->priv_key != nullptr &&
-       !EC_KEY_set_private_key(ret, EC_KEY_get0_private_key(src)))) {
+  auto *impl = FromOpaque(src);
+  if ((impl->group != nullptr && !EC_KEY_set_group(ret, impl->group)) ||
+      (impl->pub_key != nullptr &&
+       !EC_KEY_set_public_key(ret, impl->pub_key)) ||
+      (impl->priv_key != nullptr &&
+       !EC_KEY_set_private_key(ret, EC_KEY_get0_private_key(impl)))) {
     EC_KEY_free(ret);
     return nullptr;
   }
 
-  ret->enc_flag = src->enc_flag;
-  ret->conv_form = src->conv_form;
+  ret->enc_flag = impl->enc_flag;
+  ret->conv_form = impl->conv_form;
   return ret;
 }
 
 int EC_KEY_up_ref(EC_KEY *r) {
-  CRYPTO_refcount_inc(&r->references);
+  auto *impl = FromOpaque(r);
+  CRYPTO_refcount_inc(&impl->references);
   return 1;
 }
 
 int EC_KEY_is_opaque(const EC_KEY *key) {
-  return key->ecdsa_meth && (key->ecdsa_meth->flags & ECDSA_FLAG_OPAQUE);
+  auto *impl = FromOpaque(key);
+  return impl->ecdsa_meth && (impl->ecdsa_meth->flags & ECDSA_FLAG_OPAQUE);
 }
 
-const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key) { return key->group; }
+const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key) {
+  auto *impl = FromOpaque(key);
+  return impl->group;
+}
 
 int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group) {
-  // If |key| already has a group, it is an error to switch to another one.
-  if (key->group != nullptr) {
-    if (EC_GROUP_cmp(key->group, group, nullptr) != 0) {
+  auto *impl = FromOpaque(key);
+
+  // If |impl| already has a group, it is an error to switch to another one.
+  if (impl->group != nullptr) {
+    if (EC_GROUP_cmp(impl->group, group, nullptr) != 0) {
       OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
       return 0;
     }
     return 1;
   }
 
-  assert(key->priv_key == nullptr);
-  assert(key->pub_key == nullptr);
+  assert(impl->priv_key == nullptr);
+  assert(impl->pub_key == nullptr);
 
-  EC_GROUP_free(key->group);
-  key->group = EC_GROUP_dup(group);
-  return key->group != nullptr;
+  EC_GROUP_free(impl->group);
+  impl->group = EC_GROUP_dup(group);
+  return impl->group != nullptr;
 }
 
 const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key) {
-  return key->priv_key != nullptr ? &key->priv_key->bignum : nullptr;
+  auto *impl = FromOpaque(key);
+  return impl->priv_key != nullptr ? &impl->priv_key->bignum : nullptr;
 }
 
 int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key) {
-  if (key->group == nullptr) {
+  auto *impl = FromOpaque(key);
+  if (impl->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
     return 0;
   }
 
-  EC_WRAPPED_SCALAR *scalar = ec_wrapped_scalar_new(key->group);
+  EC_WRAPPED_SCALAR *scalar = ec_wrapped_scalar_new(impl->group);
   if (scalar == nullptr) {
     return 0;
   }
-  if (!ec_bignum_to_scalar(key->group, &scalar->scalar, priv_key) ||
+  if (!ec_bignum_to_scalar(impl->group, &scalar->scalar, priv_key) ||
       // Zero is not a valid private key, so it is safe to leak the result of
       // this comparison.
       constant_time_declassify_int(
-          ec_scalar_is_zero(key->group, &scalar->scalar))) {
+          ec_scalar_is_zero(impl->group, &scalar->scalar))) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_PRIVATE_KEY);
     ec_wrapped_scalar_free(scalar);
     return 0;
   }
-  ec_wrapped_scalar_free(key->priv_key);
-  key->priv_key = scalar;
+  ec_wrapped_scalar_free(impl->priv_key);
+  impl->priv_key = scalar;
   return 1;
 }
 
 const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key) {
-  return key->pub_key;
+  auto *impl = FromOpaque(key);
+  return impl->pub_key;
 }
 
 int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key) {
-  if (key->group == nullptr) {
+  auto *impl = FromOpaque(key);
+  if (impl->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
     return 0;
   }
@@ -221,43 +242,51 @@
   }
 
   if (pub_key != nullptr &&
-      EC_GROUP_cmp(key->group, pub_key->group, nullptr) != 0) {
+      EC_GROUP_cmp(impl->group, pub_key->group, nullptr) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
     return 0;
   }
 
-  EC_POINT_free(key->pub_key);
-  key->pub_key = EC_POINT_dup(pub_key, key->group);
-  return (key->pub_key == nullptr) ? 0 : 1;
+  EC_POINT_free(impl->pub_key);
+  impl->pub_key = EC_POINT_dup(pub_key, impl->group);
+  return (impl->pub_key == nullptr) ? 0 : 1;
 }
 
-unsigned int EC_KEY_get_enc_flags(const EC_KEY *key) { return key->enc_flag; }
+unsigned int EC_KEY_get_enc_flags(const EC_KEY *key) {
+  auto *impl = FromOpaque(key);
+  return impl->enc_flag;
+}
 
 void EC_KEY_set_enc_flags(EC_KEY *key, unsigned int flags) {
-  key->enc_flag = flags;
+  auto *impl = FromOpaque(key);
+  impl->enc_flag = flags;
 }
 
 point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key) {
-  return key->conv_form;
+  auto *impl = FromOpaque(key);
+  return impl->conv_form;
 }
 
 void EC_KEY_set_conv_form(EC_KEY *key, point_conversion_form_t cform) {
-  key->conv_form = cform;
+  auto *impl = FromOpaque(key);
+  impl->conv_form = cform;
 }
 
 int EC_KEY_check_key(const EC_KEY *eckey) {
-  if (!eckey || !eckey->group || !eckey->pub_key) {
+  auto *impl = FromOpaque(eckey);
+
+  if (!eckey || !impl->group || !impl->pub_key) {
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
 
-  if (EC_POINT_is_at_infinity(eckey->group, eckey->pub_key)) {
+  if (EC_POINT_is_at_infinity(impl->group, impl->pub_key)) {
     OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY);
     return 0;
   }
 
   // Test whether the public key is on the elliptic curve.
-  if (!EC_POINT_is_on_curve(eckey->group, eckey->pub_key, nullptr)) {
+  if (!EC_POINT_is_on_curve(impl->group, impl->pub_key, nullptr)) {
     OPENSSL_PUT_ERROR(EC, EC_R_POINT_IS_NOT_ON_CURVE);
     return 0;
   }
@@ -266,17 +295,17 @@
   //
   // NOTE: this is a FIPS pair-wise consistency check for the ECDH case. See SP
   // 800-56Ar3, page 36.
-  if (eckey->priv_key != nullptr) {
+  if (impl->priv_key != nullptr) {
     EC_JACOBIAN point;
-    if (!ec_point_mul_scalar_base(eckey->group, &point,
-                                  &eckey->priv_key->scalar)) {
+    if (!ec_point_mul_scalar_base(impl->group, &point,
+                                  &impl->priv_key->scalar)) {
       OPENSSL_PUT_ERROR(EC, ERR_R_EC_LIB);
       return 0;
     }
     // Leaking this comparison only leaks whether |eckey|'s public key was
     // correct.
     if (!constant_time_declassify_int(ec_GFp_simple_points_equal(
-            eckey->group, &point, &eckey->pub_key->raw))) {
+            impl->group, &point, &impl->pub_key->raw))) {
       OPENSSL_PUT_ERROR(EC, EC_R_INVALID_PRIVATE_KEY);
       return 0;
     }
@@ -286,25 +315,27 @@
 }
 
 int EC_KEY_check_fips(const EC_KEY *key) {
+  auto *impl = FromOpaque(key);
+
   int ret = 0;
   FIPS_service_indicator_lock_state();
 
-  if (!EC_KEY_check_key(key)) {
+  if (!EC_KEY_check_key(impl)) {
     goto end;
   }
 
-  if (key->priv_key) {
+  if (impl->priv_key) {
     uint8_t digest[SHA256_DIGEST_LENGTH] = {0};
     uint8_t sig[ECDSA_MAX_FIXED_LEN];
     size_t sig_len;
     if (!ecdsa_sign_fixed(digest, sizeof(digest), sig, &sig_len, sizeof(sig),
-                          key)) {
+                          impl)) {
       goto end;
     }
     if (boringssl_fips_break_test("ECDSA_PWCT")) {
       digest[0] = ~digest[0];
     }
-    if (!ecdsa_verify_fixed(digest, sizeof(digest), sig, sig_len, key)) {
+    if (!ecdsa_verify_fixed(digest, sizeof(digest), sig, sig_len, impl)) {
       OPENSSL_PUT_ERROR(EC, EC_R_PUBLIC_KEY_VALIDATION_FAILED);
       goto end;
     }
@@ -315,7 +346,7 @@
 end:
   FIPS_service_indicator_unlock_state();
   if (ret) {
-    EC_KEY_keygen_verify_service_indicator(key);
+    EC_KEY_keygen_verify_service_indicator(impl);
   }
 
   return ret;
@@ -323,14 +354,16 @@
 
 int EC_KEY_set_public_key_affine_coordinates(EC_KEY *key, const BIGNUM *x,
                                              const BIGNUM *y) {
-  if (!key || !key->group || !x || !y) {
+  auto *impl = FromOpaque(key);
+
+  if (!key || !impl->group || !x || !y) {
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
 
-  UniquePtr<EC_POINT> point(EC_POINT_new(key->group));
+  UniquePtr<EC_POINT> point(EC_POINT_new(impl->group));
   if (point == nullptr ||
-      !EC_POINT_set_affine_coordinates_GFp(key->group, point.get(), x, y,
+      !EC_POINT_set_affine_coordinates_GFp(impl->group, point.get(), x, y,
                                            nullptr) ||
       !EC_KEY_set_public_key(key, point.get()) ||  //
       !EC_KEY_check_key(key)) {
@@ -341,34 +374,40 @@
 }
 
 int EC_KEY_oct2key(EC_KEY *key, const uint8_t *in, size_t len, BN_CTX *ctx) {
-  if (key->group == nullptr) {
+  auto *impl = FromOpaque(key);
+
+  if (impl->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
     return 0;
   }
 
-  UniquePtr<EC_POINT> point(EC_POINT_new(key->group));
+  UniquePtr<EC_POINT> point(EC_POINT_new(impl->group));
   return point != nullptr &&
-         EC_POINT_oct2point(key->group, point.get(), in, len, ctx) &&
+         EC_POINT_oct2point(impl->group, point.get(), in, len, ctx) &&
          EC_KEY_set_public_key(key, point.get());
 }
 
 size_t EC_KEY_key2buf(const EC_KEY *key, point_conversion_form_t form,
                       uint8_t **out_buf, BN_CTX *ctx) {
-  if (key == nullptr || key->pub_key == nullptr || key->group == nullptr) {
+  auto *impl = FromOpaque(key);
+
+  if (impl == nullptr || impl->pub_key == nullptr || impl->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
     return 0;
   }
 
-  return EC_POINT_point2buf(key->group, key->pub_key, form, out_buf, ctx);
+  return EC_POINT_point2buf(impl->group, impl->pub_key, form, out_buf, ctx);
 }
 
 int EC_KEY_oct2priv(EC_KEY *key, const uint8_t *in, size_t len) {
-  if (key->group == nullptr) {
+  auto *impl = FromOpaque(key);
+
+  if (impl->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
     return 0;
   }
 
-  if (len != BN_num_bytes(EC_GROUP_get0_order(key->group))) {
+  if (len != BN_num_bytes(EC_GROUP_get0_order(impl->group))) {
     OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
     return 0;
   }
@@ -381,12 +420,14 @@
 }
 
 size_t EC_KEY_priv2oct(const EC_KEY *key, uint8_t *out, size_t max_out) {
-  if (key->group == nullptr || key->priv_key == nullptr) {
+  auto *impl = FromOpaque(key);
+
+  if (impl->group == nullptr || impl->priv_key == nullptr) {
     OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
     return 0;
   }
 
-  size_t len = BN_num_bytes(EC_GROUP_get0_order(key->group));
+  size_t len = BN_num_bytes(EC_GROUP_get0_order(impl->group));
   if (out == nullptr) {
     return len;
   }
@@ -397,7 +438,7 @@
   }
 
   size_t bytes_written;
-  ec_scalar_to_bytes(key->group, out, &bytes_written, &key->priv_key->scalar);
+  ec_scalar_to_bytes(impl->group, out, &bytes_written, &impl->priv_key->scalar);
   assert(bytes_written == len);
   return len;
 }
@@ -425,7 +466,9 @@
 }
 
 int EC_KEY_generate_key(EC_KEY *key) {
-  if (key == nullptr || key->group == nullptr) {
+  auto *impl = FromOpaque(key);
+
+  if (impl == nullptr || impl->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
@@ -434,18 +477,19 @@
   // FIPS 186-5, A.2.2, repeating the process on failure.
 
   // Check the group order is large enough. See step 1 of FIPS 186-5, A.2.2.
-  if (EC_GROUP_order_bits(key->group) < 224) {
+  if (EC_GROUP_order_bits(impl->group) < 224) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_GROUP_ORDER);
     return 0;
   }
 
   static const uint8_t kDefaultAdditionalData[32] = {0};
-  EC_WRAPPED_SCALAR *priv_key = ec_wrapped_scalar_new(key->group);
-  EC_POINT *pub_key = EC_POINT_new(key->group);
+  EC_WRAPPED_SCALAR *priv_key = ec_wrapped_scalar_new(impl->group);
+  EC_POINT *pub_key = EC_POINT_new(impl->group);
   if (priv_key == nullptr || pub_key == nullptr ||
-      !ec_random_nonzero_scalar(key->group, &priv_key->scalar,
+      !ec_random_nonzero_scalar(impl->group, &priv_key->scalar,
                                 kDefaultAdditionalData) ||
-      !ec_point_mul_scalar_base(key->group, &pub_key->raw, &priv_key->scalar)) {
+      !ec_point_mul_scalar_base(impl->group, &pub_key->raw,
+                                &priv_key->scalar)) {
     EC_POINT_free(pub_key);
     ec_wrapped_scalar_free(priv_key);
     return 0;
@@ -459,29 +503,31 @@
   // discussion in the bug.
   CONSTTIME_DECLASSIFY(&pub_key->raw, sizeof(pub_key->raw));
 
-  ec_wrapped_scalar_free(key->priv_key);
-  key->priv_key = priv_key;
-  EC_POINT_free(key->pub_key);
-  key->pub_key = pub_key;
+  ec_wrapped_scalar_free(impl->priv_key);
+  impl->priv_key = priv_key;
+  EC_POINT_free(impl->pub_key);
+  impl->pub_key = pub_key;
   return 1;
 }
 
 int EC_KEY_generate_key_fips(EC_KEY *eckey) {
-  if (eckey == nullptr || eckey->group == nullptr) {
+  auto *impl = FromOpaque(eckey);
+
+  if (impl == nullptr || impl->group == nullptr) {
     OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
 
   boringssl_ensure_ecc_self_test();
 
-  if (EC_KEY_generate_key(eckey) && EC_KEY_check_fips(eckey)) {
+  if (EC_KEY_generate_key(impl) && EC_KEY_check_fips(impl)) {
     return 1;
   }
 
-  EC_POINT_free(eckey->pub_key);
-  ec_wrapped_scalar_free(eckey->priv_key);
-  eckey->pub_key = nullptr;
-  eckey->priv_key = nullptr;
+  EC_POINT_free(impl->pub_key);
+  ec_wrapped_scalar_free(impl->priv_key);
+  impl->pub_key = nullptr;
+  impl->priv_key = nullptr;
   return 0;
 }
 
@@ -493,11 +539,15 @@
 }
 
 int EC_KEY_set_ex_data(EC_KEY *d, int idx, void *arg) {
-  return CRYPTO_set_ex_data(&d->ex_data, idx, arg);
+  auto *impl = FromOpaque(d);
+
+  return CRYPTO_set_ex_data(&impl->ex_data, idx, arg);
 }
 
 void *EC_KEY_get_ex_data(const EC_KEY *d, int idx) {
-  return CRYPTO_get_ex_data(&d->ex_data, idx);
+  auto *impl = FromOpaque(d);
+
+  return CRYPTO_get_ex_data(&impl->ex_data, idx);
 }
 
 void EC_KEY_set_asn1_flag(EC_KEY *key, int flag) {}
diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc
index 406c496..0a35b19 100644
--- a/crypto/fipsmodule/ec/ec_test.cc
+++ b/crypto/fipsmodule/ec/ec_test.cc
@@ -31,6 +31,7 @@
 #include <openssl/span.h>
 
 #include "../../ec/internal.h"
+#include "../../mem_internal.h"
 #include "../../test/file_test.h"
 #include "../../test/test_util.h"
 #include "../bn/internal.h"
@@ -455,7 +456,8 @@
 }
 
 TEST(ECTest, PointAtInfinity) {
-  UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+  UniquePtr<ECKey> key(
+      FromOpaque(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)));
   ASSERT_TRUE(key);
 
   UniquePtr<EC_POINT> inf(EC_POINT_new(key->group));
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h
index 32322e4..aff9a3e 100644
--- a/crypto/fipsmodule/ec/internal.h
+++ b/crypto/fipsmodule/ec/internal.h
@@ -27,6 +27,8 @@
 #include "../bn/internal.h"
 
 
+DECLARE_OPAQUE_STRUCT(ec_key_st, ECKey)
+
 BSSL_NAMESPACE_BEGIN
 
 // EC internals.
@@ -699,9 +701,14 @@
   EC_SCALAR scalar;
 } EC_WRAPPED_SCALAR;
 
-BSSL_NAMESPACE_END
+// Exported, as the destructor is used by ec_test.cc.
+class OPENSSL_EXPORT ECKey : public ec_key_st {
+ public:
+  static constexpr bool kAllowUniquePtr = true;
 
-struct ec_key_st {
+  // Used by ec_test.cc.
+  OPENSSL_EXPORT ~ECKey();
+
   EC_GROUP *group;
 
   // Ideally |pub_key| would be an |EC_AFFINE| so serializing it does not pay an
@@ -720,5 +727,7 @@
   CRYPTO_EX_DATA ex_data;
 } /* EC_KEY */;
 
+BSSL_NAMESPACE_END
+
 
 #endif  // OPENSSL_HEADER_CRYPTO_FIPSMODULE_EC_INTERNAL_H
diff --git a/crypto/fipsmodule/ecdh/ecdh.cc.inc b/crypto/fipsmodule/ecdh/ecdh.cc.inc
index ca0c47a..81c5482 100644
--- a/crypto/fipsmodule/ecdh/ecdh.cc.inc
+++ b/crypto/fipsmodule/ecdh/ecdh.cc.inc
@@ -34,11 +34,11 @@
                           const EC_KEY *priv_key) {
   boringssl_ensure_ecc_self_test();
 
-  if (priv_key->priv_key == nullptr) {
+  if (FromOpaque(priv_key)->priv_key == nullptr) {
     OPENSSL_PUT_ERROR(ECDH, ECDH_R_NO_PRIVATE_VALUE);
     return 0;
   }
-  const EC_SCALAR *const priv = &priv_key->priv_key->scalar;
+  const EC_SCALAR *const priv = &FromOpaque(priv_key)->priv_key->scalar;
   const EC_GROUP *const group = EC_KEY_get0_group(priv_key);
   if (EC_GROUP_cmp(group, pub_key->group, nullptr) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
diff --git a/crypto/fipsmodule/ecdsa/ecdsa.cc.inc b/crypto/fipsmodule/ecdsa/ecdsa.cc.inc
index 7ff46f9..463fc66 100644
--- a/crypto/fipsmodule/ecdsa/ecdsa.cc.inc
+++ b/crypto/fipsmodule/ecdsa/ecdsa.cc.inc
@@ -185,17 +185,19 @@
     const uint8_t *digest, size_t digest_len, uint8_t *sig, size_t *out_sig_len,
     size_t max_sig_len, const EC_KEY *eckey, const uint8_t *nonce,
     size_t nonce_len) {
-  if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
+  const ECKey *impl = FromOpaque(eckey);
+
+  if (impl->ecdsa_meth && impl->ecdsa_meth->sign) {
     OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED);
     return 0;
   }
 
-  const EC_GROUP *group = EC_KEY_get0_group(eckey);
-  if (group == nullptr || eckey->priv_key == nullptr) {
+  const EC_GROUP *group = EC_KEY_get0_group(impl);
+  if (group == nullptr || impl->priv_key == nullptr) {
     OPENSSL_PUT_ERROR(ECDSA, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
-  const EC_SCALAR *priv_key = &eckey->priv_key->scalar;
+  const EC_SCALAR *priv_key = &impl->priv_key->scalar;
 
   EC_SCALAR k;
   if (!ec_scalar_from_bytes(group, &k, nonce, nonce_len)) {
@@ -209,20 +211,22 @@
 int bssl::ecdsa_sign_fixed(const uint8_t *digest, size_t digest_len,
                            uint8_t *sig, size_t *out_sig_len,
                            size_t max_sig_len, const EC_KEY *eckey) {
+  const ECKey *impl = FromOpaque(eckey);
+
   boringssl_ensure_ecc_self_test();
 
-  if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
+  if (impl->ecdsa_meth && impl->ecdsa_meth->sign) {
     OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED);
     return 0;
   }
 
-  const EC_GROUP *group = EC_KEY_get0_group(eckey);
-  if (group == nullptr || eckey->priv_key == nullptr) {
+  const EC_GROUP *group = EC_KEY_get0_group(impl);
+  if (group == nullptr || impl->priv_key == nullptr) {
     OPENSSL_PUT_ERROR(ECDSA, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
   const BIGNUM *order = EC_GROUP_get0_order(group);
-  const EC_SCALAR *priv_key = &eckey->priv_key->scalar;
+  const EC_SCALAR *priv_key = &impl->priv_key->scalar;
 
   // Generate the ECDSA per-message secret number by rejection sampling. This
   // function implements FIPS 186-5, A.3.2, repeating the process on failure.
diff --git a/util/audit_symbols.go b/util/audit_symbols.go
index 5fa64c9..2d3c266 100644
--- a/util/audit_symbols.go
+++ b/util/audit_symbols.go
@@ -88,7 +88,6 @@
 
 	// TODO(crbug.com/42220000): Temporary symbols, to be eliminated.
 	regexp.MustCompile(`.*ec_group_st.*`),
-	regexp.MustCompile(`.*ec_key_st.*`),
 	regexp.MustCompile(`.*evp_pkey_st.*`),
 	regexp.MustCompile(`.*rsa_st.*`),
 	regexp.MustCompile(`.*x509_store_st.*`),