Use constant curve-specific groups whenever possible

Also remove unnecessary EC_GROUP_free calls. EC_GROUP_free is only
necessary in codepaths where arbitrary groups are possible.

Bug: 20
Change-Id: I3dfb7f07b890ab002ba8a302724d8bc671590cfe
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60932
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/ec_extra/ec_asn1.c b/crypto/ec_extra/ec_asn1.c
index 1cd9799..fb12e48 100644
--- a/crypto/ec_extra/ec_asn1.c
+++ b/crypto/ec_extra/ec_asn1.c
@@ -94,7 +94,6 @@
   }
 
   // Parse the optional parameters field.
-  EC_GROUP *inner_group = NULL;
   EC_KEY *ret = NULL;
   BIGNUM *priv_key = NULL;
   if (CBS_peek_asn1_tag(&ec_private_key, kParametersTag)) {
@@ -107,7 +106,7 @@
       OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
       goto err;
     }
-    inner_group = EC_KEY_parse_parameters(&child);
+    const EC_GROUP *inner_group = EC_KEY_parse_parameters(&child);
     if (inner_group == NULL) {
       goto err;
     }
@@ -189,13 +188,11 @@
   }
 
   BN_free(priv_key);
-  EC_GROUP_free(inner_group);
   return ret;
 
 err:
   EC_KEY_free(ret);
   BN_free(priv_key);
-  EC_GROUP_free(inner_group);
   return NULL;
 }
 
@@ -353,8 +350,6 @@
   for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kAllGroups); i++) {
     const EC_GROUP *group = kAllGroups[i]();
     if (CBS_mem_equal(&named_curve, group->oid, group->oid_len)) {
-      // TODO(davidben): Remove unnecessary calls to |EC_GROUP_free| within the
-      // library.
       return (EC_GROUP *)group;
     }
   }
@@ -433,8 +428,6 @@
   BN_free(b);
   BN_free(x);
   BN_free(y);
-  // TODO(davidben): Remove unnecessary calls to |EC_GROUP_free| within the
-  // library.
   return (EC_GROUP *)ret;
 }
 
@@ -492,18 +485,16 @@
 
   CBS cbs;
   CBS_init(&cbs, *inp, (size_t)len);
-  EC_GROUP *group = EC_KEY_parse_parameters(&cbs);
+  const EC_GROUP *group = EC_KEY_parse_parameters(&cbs);
   if (group == NULL) {
     return NULL;
   }
 
   EC_KEY *ret = EC_KEY_new();
   if (ret == NULL || !EC_KEY_set_group(ret, group)) {
-    EC_GROUP_free(group);
     EC_KEY_free(ret);
     return NULL;
   }
-  EC_GROUP_free(group);
 
   if (out_key != NULL) {
     EC_KEY_free(*out_key);
diff --git a/crypto/ecdh_extra/ecdh_test.cc b/crypto/ecdh_extra/ecdh_test.cc
index 3948525..ca44375 100644
--- a/crypto/ecdh_extra/ecdh_test.cc
+++ b/crypto/ecdh_extra/ecdh_test.cc
@@ -35,24 +35,23 @@
 #include "../test/wycheproof_util.h"
 
 
-static bssl::UniquePtr<EC_GROUP> GetCurve(FileTest *t, const char *key) {
+static const EC_GROUP *GetCurve(FileTest *t, const char *key) {
   std::string curve_name;
   if (!t->GetAttribute(&curve_name, key)) {
     return nullptr;
   }
 
   if (curve_name == "P-224") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp224r1));
+    return EC_group_p224();
   }
   if (curve_name == "P-256") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(
-        NID_X9_62_prime256v1));
+    return EC_group_p256();
   }
   if (curve_name == "P-384") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp384r1));
+    return EC_group_p384();
   }
   if (curve_name == "P-521") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp521r1));
+    return EC_group_p521();
   }
 
   t->PrintLine("Unknown curve '%s'", curve_name.c_str());
@@ -70,7 +69,7 @@
 
 TEST(ECDHTest, TestVectors) {
   FileTestGTest("crypto/ecdh_extra/ecdh_tests.txt", [](FileTest *t) {
-    bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
+    const EC_GROUP *group = GetCurve(t, "Curve");
     ASSERT_TRUE(group);
     bssl::UniquePtr<BIGNUM> priv_key = GetBIGNUM(t, "Private");
     ASSERT_TRUE(priv_key);
@@ -87,16 +86,16 @@
 
     bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
     ASSERT_TRUE(key);
-    bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
+    bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group));
     ASSERT_TRUE(pub_key);
-    bssl::UniquePtr<EC_POINT> peer_pub_key(EC_POINT_new(group.get()));
+    bssl::UniquePtr<EC_POINT> peer_pub_key(EC_POINT_new(group));
     ASSERT_TRUE(peer_pub_key);
-    ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
+    ASSERT_TRUE(EC_KEY_set_group(key.get(), group));
     ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
-    ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(group.get(), pub_key.get(),
+    ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(group, pub_key.get(),
                                                     x.get(), y.get(), nullptr));
     ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
-        group.get(), peer_pub_key.get(), peer_x.get(), peer_y.get(), nullptr));
+        group, peer_pub_key.get(), peer_x.get(), peer_y.get(), nullptr));
     ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
     ASSERT_TRUE(EC_KEY_check_key(key.get()));
 
@@ -130,7 +129,7 @@
 static void RunWycheproofTest(FileTest *t) {
   t->IgnoreInstruction("encoding");
 
-  bssl::UniquePtr<EC_GROUP> group = GetWycheproofCurve(t, "curve", true);
+  const EC_GROUP *group = GetWycheproofCurve(t, "curve", true);
   ASSERT_TRUE(group);
   bssl::UniquePtr<BIGNUM> priv_key = GetWycheproofBIGNUM(t, "private", false);
   ASSERT_TRUE(priv_key);
@@ -157,10 +156,10 @@
 
   bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
   ASSERT_TRUE(key);
-  ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
+  ASSERT_TRUE(EC_KEY_set_group(key.get(), group));
   ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
 
-  std::vector<uint8_t> actual((EC_GROUP_get_degree(group.get()) + 7) / 8);
+  std::vector<uint8_t> actual((EC_GROUP_get_degree(group) + 7) / 8);
   int ret =
       ECDH_compute_key(actual.data(), actual.size(),
                        EC_KEY_get0_public_key(peer_ec), key.get(), nullptr);
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index 4205402..7e588c5 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -121,7 +121,6 @@
 SSL,178,NO_COMPRESSION_SPECIFIED
 SSL,265,NO_GROUPS_SPECIFIED
 SSL,179,NO_METHOD_SPECIFIED
-SSL,180,NO_P256_SUPPORT
 SSL,181,NO_PRIVATE_KEY_ASSIGNED
 SSL,182,NO_RENEGOTIATION
 SSL,183,NO_REQUIRED_DIGEST
diff --git a/crypto/evp/p_ec.c b/crypto/evp/p_ec.c
index c9f26cb..ed89cc3 100644
--- a/crypto/evp/p_ec.c
+++ b/crypto/evp/p_ec.c
@@ -75,7 +75,7 @@
 typedef struct {
   // message digest
   const EVP_MD *md;
-  EC_GROUP *gen_group;
+  const EC_GROUP *gen_group;
 } EC_PKEY_CTX;
 
 
@@ -111,7 +111,6 @@
     return;
   }
 
-  EC_GROUP_free(dctx->gen_group);
   OPENSSL_free(dctx);
 }
 
@@ -195,11 +194,10 @@
       return 1;
 
     case EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID: {
-      EC_GROUP *group = EC_GROUP_new_by_curve_name(p1);
+      const EC_GROUP *group = EC_GROUP_new_by_curve_name(p1);
       if (group == NULL) {
         return 0;
       }
-      EC_GROUP_free(dctx->gen_group);
       dctx->gen_group = group;
       return 1;
     }
diff --git a/crypto/evp/p_ec_asn1.c b/crypto/evp/p_ec_asn1.c
index 19ff2c0..9a9d463 100644
--- a/crypto/evp/p_ec_asn1.c
+++ b/crypto/evp/p_ec_asn1.c
@@ -94,7 +94,7 @@
 
   // The parameters are a named curve.
   EC_KEY *eckey = NULL;
-  EC_GROUP *group = EC_KEY_parse_curve_name(params);
+  const EC_GROUP *group = EC_KEY_parse_curve_name(params);
   if (group == NULL || CBS_len(params) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     goto err;
@@ -107,12 +107,10 @@
     goto err;
   }
 
-  EC_GROUP_free(group);
   EVP_PKEY_assign_EC_KEY(out, eckey);
   return 1;
 
 err:
-  EC_GROUP_free(group);
   EC_KEY_free(eckey);
   return 0;
 }
@@ -135,15 +133,13 @@
 
 static int eckey_priv_decode(EVP_PKEY *out, CBS *params, CBS *key) {
   // See RFC 5915.
-  EC_GROUP *group = EC_KEY_parse_parameters(params);
+  const EC_GROUP *group = EC_KEY_parse_parameters(params);
   if (group == NULL || CBS_len(params) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
-    EC_GROUP_free(group);
     return 0;
   }
 
   EC_KEY *ec_key = EC_KEY_parse_private_key(key, group);
-  EC_GROUP_free(group);
   if (ec_key == NULL || CBS_len(key) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     EC_KEY_free(ec_key);
diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc
index a40dd16..75e11f8 100644
--- a/crypto/fipsmodule/ec/ec_test.cc
+++ b/crypto/fipsmodule/ec/ec_test.cc
@@ -430,11 +430,8 @@
   EXPECT_FALSE(EC_KEY_set_private_key(key.get(), BN_value_one()));
 
   // Public keys may not be configured without a group.
-  bssl::UniquePtr<EC_GROUP> group(
-      EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
-  ASSERT_TRUE(group);
-  EXPECT_FALSE(
-      EC_KEY_set_public_key(key.get(), EC_GROUP_get0_generator(group.get())));
+  EXPECT_FALSE(EC_KEY_set_public_key(key.get(),
+                                     EC_GROUP_get0_generator(EC_group_p256())));
 }
 
 TEST(ECTest, SetNULLKey) {
@@ -454,16 +451,13 @@
 TEST(ECTest, GroupMismatch) {
   bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_secp384r1));
   ASSERT_TRUE(key);
-  bssl::UniquePtr<EC_GROUP> p256(
-      EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
-  ASSERT_TRUE(p256);
 
   // Changing a key's group is invalid.
-  EXPECT_FALSE(EC_KEY_set_group(key.get(), p256.get()));
+  EXPECT_FALSE(EC_KEY_set_group(key.get(), EC_group_p256()));
 
   // Configuring a public key with the wrong group is invalid.
-  EXPECT_FALSE(
-      EC_KEY_set_public_key(key.get(), EC_GROUP_get0_generator(p256.get())));
+  EXPECT_FALSE(EC_KEY_set_public_key(key.get(),
+                                     EC_GROUP_get0_generator(EC_group_p256())));
 }
 
 TEST(ECTest, EmptyKey) {
@@ -531,15 +525,15 @@
 
 class ECCurveTest : public testing::TestWithParam<int> {
  public:
-  const EC_GROUP *group() const { return group_.get(); }
+  const EC_GROUP *group() const { return group_; }
 
   void SetUp() override {
-    group_.reset(EC_GROUP_new_by_curve_name(GetParam()));
+    group_ = EC_GROUP_new_by_curve_name(GetParam());
     ASSERT_TRUE(group_);
   }
 
  private:
-  bssl::UniquePtr<EC_GROUP> group_;
+  const EC_GROUP *group_;
 };
 
 TEST_P(ECCurveTest, SetAffine) {
@@ -995,24 +989,23 @@
 INSTANTIATE_TEST_SUITE_P(All, ECCurveTest, testing::ValuesIn(AllCurves()),
                          CurveToString);
 
-static bssl::UniquePtr<EC_GROUP> GetCurve(FileTest *t, const char *key) {
+static const EC_GROUP *GetCurve(FileTest *t, const char *key) {
   std::string curve_name;
   if (!t->GetAttribute(&curve_name, key)) {
     return nullptr;
   }
 
   if (curve_name == "P-224") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp224r1));
+    return EC_group_p224();
   }
   if (curve_name == "P-256") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(
-        NID_X9_62_prime256v1));
+    return EC_group_p256();
   }
   if (curve_name == "P-384") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp384r1));
+    return EC_group_p384();
   }
   if (curve_name == "P-521") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp521r1));
+    return EC_group_p521();
   }
 
   t->PrintLine("Unknown curve '%s'", curve_name.c_str());
@@ -1035,7 +1028,7 @@
 
   FileTestGTest("crypto/fipsmodule/ec/ec_scalar_base_mult_tests.txt",
                 [&](FileTest *t) {
-    bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
+    const EC_GROUP *group = GetCurve(t, "Curve");
     ASSERT_TRUE(group);
     bssl::UniquePtr<BIGNUM> n = GetBIGNUM(t, "N");
     ASSERT_TRUE(n);
@@ -1051,25 +1044,24 @@
     ASSERT_TRUE(py);
     auto check_point = [&](const EC_POINT *p) {
       if (is_infinity) {
-        EXPECT_TRUE(EC_POINT_is_at_infinity(group.get(), p));
+        EXPECT_TRUE(EC_POINT_is_at_infinity(group, p));
       } else {
         ASSERT_TRUE(EC_POINT_get_affine_coordinates_GFp(
-            group.get(), p, px.get(), py.get(), ctx.get()));
+            group, p, px.get(), py.get(), ctx.get()));
         EXPECT_EQ(0, BN_cmp(x.get(), px.get()));
         EXPECT_EQ(0, BN_cmp(y.get(), py.get()));
       }
     };
 
-    const EC_POINT *g = EC_GROUP_get0_generator(group.get());
-    bssl::UniquePtr<EC_POINT> p(EC_POINT_new(group.get()));
+    const EC_POINT *g = EC_GROUP_get0_generator(group);
+    bssl::UniquePtr<EC_POINT> p(EC_POINT_new(group));
     ASSERT_TRUE(p);
     // Test single-point multiplication.
-    ASSERT_TRUE(EC_POINT_mul(group.get(), p.get(), n.get(), nullptr, nullptr,
+    ASSERT_TRUE(EC_POINT_mul(group, p.get(), n.get(), nullptr, nullptr,
                              ctx.get()));
     check_point(p.get());
 
-    ASSERT_TRUE(
-        EC_POINT_mul(group.get(), p.get(), nullptr, g, n.get(), ctx.get()));
+    ASSERT_TRUE(EC_POINT_mul(group, p.get(), nullptr, g, n.get(), ctx.get()));
     check_point(p.get());
   });
 }
@@ -1082,7 +1074,7 @@
 
   FileTestGTest("crypto/fipsmodule/ec/ec_scalar_base_mult_tests.txt",
                 [&](FileTest *t) {
-    bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
+    const EC_GROUP *group = GetCurve(t, "Curve");
     ASSERT_TRUE(group);
     bssl::UniquePtr<BIGNUM> n = GetBIGNUM(t, "N");
     ASSERT_TRUE(n);
@@ -1098,41 +1090,40 @@
     ASSERT_TRUE(py);
     auto check_point = [&](const EC_POINT *p) {
       if (is_infinity) {
-        EXPECT_TRUE(EC_POINT_is_at_infinity(group.get(), p));
+        EXPECT_TRUE(EC_POINT_is_at_infinity(group, p));
       } else {
         ASSERT_TRUE(EC_POINT_get_affine_coordinates_GFp(
-            group.get(), p, px.get(), py.get(), ctx.get()));
+            group, p, px.get(), py.get(), ctx.get()));
         EXPECT_EQ(0, BN_cmp(x.get(), px.get()));
         EXPECT_EQ(0, BN_cmp(y.get(), py.get()));
       }
     };
 
-    const EC_POINT *g = EC_GROUP_get0_generator(group.get());
-    bssl::UniquePtr<EC_POINT> p(EC_POINT_new(group.get()));
+    const EC_POINT *g = EC_GROUP_get0_generator(group);
+    bssl::UniquePtr<EC_POINT> p(EC_POINT_new(group));
     ASSERT_TRUE(p);
     bssl::UniquePtr<BIGNUM> a(BN_new()), b(BN_new());
     for (int i = -64; i < 64; i++) {
       SCOPED_TRACE(i);
       ASSERT_TRUE(BN_set_word(a.get(), abs(i)));
       if (i < 0) {
-        ASSERT_TRUE(BN_sub(a.get(), EC_GROUP_get0_order(group.get()), a.get()));
+        ASSERT_TRUE(BN_sub(a.get(), EC_GROUP_get0_order(group), a.get()));
       }
 
       ASSERT_TRUE(BN_copy(b.get(), n.get()));
       ASSERT_TRUE(BN_sub(b.get(), b.get(), a.get()));
       if (BN_is_negative(b.get())) {
-        ASSERT_TRUE(BN_add(b.get(), b.get(), EC_GROUP_get0_order(group.get())));
+        ASSERT_TRUE(BN_add(b.get(), b.get(), EC_GROUP_get0_order(group)));
       }
 
-      ASSERT_TRUE(
-          EC_POINT_mul(group.get(), p.get(), a.get(), g, b.get(), ctx.get()));
+      ASSERT_TRUE(EC_POINT_mul(group, p.get(), a.get(), g, b.get(), ctx.get()));
       check_point(p.get());
 
       EC_SCALAR a_scalar, b_scalar;
-      ASSERT_TRUE(ec_bignum_to_scalar(group.get(), &a_scalar, a.get()));
-      ASSERT_TRUE(ec_bignum_to_scalar(group.get(), &b_scalar, b.get()));
-      ASSERT_TRUE(ec_point_mul_scalar_public(group.get(), &p->raw, &a_scalar,
-                                             &g->raw, &b_scalar));
+      ASSERT_TRUE(ec_bignum_to_scalar(group, &a_scalar, a.get()));
+      ASSERT_TRUE(ec_bignum_to_scalar(group, &b_scalar, b.get()));
+      ASSERT_TRUE(ec_point_mul_scalar_public(group, &p->raw, &a_scalar, &g->raw,
+                                             &b_scalar));
       check_point(p.get());
     }
   });
@@ -1148,31 +1139,31 @@
 
 TEST(ECTest, DeriveFromSecret) {
   struct DeriveTest {
-    int curve;
+    const EC_GROUP *group;
     std::vector<uint8_t> secret;
     std::vector<uint8_t> expected_priv;
     std::vector<uint8_t> expected_pub;
   };
   const DeriveTest kDeriveTests[] = {
-      {NID_X9_62_prime256v1, HexToBytes(""),
+      {EC_group_p256(), HexToBytes(""),
        HexToBytes(
            "b98a86a71efb51ebdac4759937b977e9b0c05224675bb2b6a58ba306e237f4b8"),
        HexToBytes(
            "04fbe6cab439918e00231a2ff073cdc25823998864a9eb36f809095a1a919ece875"
            "a145803fbe89a6cde53936e3c6d9c253ed3d38f5f58cae455c27e95645ceda9")},
-      {NID_X9_62_prime256v1, HexToBytes("123456"),
+      {EC_group_p256(), HexToBytes("123456"),
        HexToBytes(
            "44a72bc62087b88e5ab7126766177ed0d8f1ed09ad066cd746527fc201105a7e"),
        HexToBytes(
            "04ec0555cd76e991fef7f5504343937d0f38696db3360a4854052cb0d84a377a5a0"
            "ff64c352755c28692b4ae085c2b817db9a1eddbd22e9cf39c12751e0870791b")},
-      {NID_X9_62_prime256v1, HexToBytes("00000000000000000000000000000000"),
+      {EC_group_p256(), HexToBytes("00000000000000000000000000000000"),
        HexToBytes(
            "7ca1e2c83e6a5f2c1b3e7d58180226f269930c4b9fbe2a275096079630b7c57d"),
        HexToBytes(
            "0442ef70c8fc0fbe383ed0a0da36f39f9a590f3feebc07863cc858c9a8ef0465731"
            "0408c249bd4d61929c54b71ffe056e6b4fa1eb537039b43d1c175f0ceab0f89")},
-      {NID_X9_62_prime256v1,
+      {EC_group_p256(),
        HexToBytes(
            "de9c9b35543aaa0fba039e34e8ca9695da3225c7161c9e3a8c70356cac28c780"),
        HexToBytes(
@@ -1180,7 +1171,7 @@
        HexToBytes(
            "046741f806b593bf3a3d4a9d76bdcb9b0d7874633cbea8f42c05e78561f7e8ec362"
            "b9b6f1913ded796fbdafe7f210cea897ac22a4e580c06a60f2659fd09f1830f")},
-      {NID_secp384r1, HexToBytes("123456"),
+      {EC_group_p384(), HexToBytes("123456"),
        HexToBytes("95cd90d548997de090c7622708eccb7edc1b1bd78d2422235ad97406dada"
                   "076555309da200096f6e4b36c46002beee89"),
        HexToBytes(
@@ -1191,13 +1182,12 @@
 
   for (const auto &test : kDeriveTests) {
     SCOPED_TRACE(Bytes(test.secret));
-    bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.curve));
-    ASSERT_TRUE(group);
+
     bssl::UniquePtr<EC_KEY> key(EC_KEY_derive_from_secret(
-        group.get(), test.secret.data(), test.secret.size()));
+        test.group, test.secret.data(), test.secret.size()));
     ASSERT_TRUE(key);
 
-    std::vector<uint8_t> priv(BN_num_bytes(EC_GROUP_get0_order(group.get())));
+    std::vector<uint8_t> priv(BN_num_bytes(EC_GROUP_get0_order(test.group)));
     ASSERT_TRUE(BN_bn2bin_padded(priv.data(), priv.size(),
                                  EC_KEY_get0_private_key(key.get())));
     EXPECT_EQ(Bytes(priv), Bytes(test.expected_priv));
@@ -1226,33 +1216,33 @@
     int (*hash_to_curve)(const EC_GROUP *group, EC_POINT *out,
                          const uint8_t *dst, size_t dst_len, const uint8_t *msg,
                          size_t msg_len);
-    int curve_nid;
+    const EC_GROUP *group;
     const char *dst;
     const char *msg;
     const char *x_hex;
     const char *y_hex;
   };
-  static const HashToCurveTest kTests[] = {
+  const HashToCurveTest kTests[] = {
       // See draft-irtf-cfrg-hash-to-curve-16, appendix J.1.1.
-      {&EC_hash_to_curve_p256_xmd_sha256_sswu, NID_X9_62_prime256v1,
+      {&EC_hash_to_curve_p256_xmd_sha256_sswu, EC_group_p256(),
        "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", "",
        "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91"
        "c44247d3e4",
        "8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9a"
        "b5c43e8415"},
-      {&EC_hash_to_curve_p256_xmd_sha256_sswu, NID_X9_62_prime256v1,
+      {&EC_hash_to_curve_p256_xmd_sha256_sswu, EC_group_p256(),
        "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", "abc",
        "0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775da"
        "c4a3388a0f",
        "5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71"
        "ffd424212e"},
-      {&EC_hash_to_curve_p256_xmd_sha256_sswu, NID_X9_62_prime256v1,
+      {&EC_hash_to_curve_p256_xmd_sha256_sswu, EC_group_p256(),
        "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", "abcdef0123456789",
        "65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c15268118"
        "64e544ed80",
        "cad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d"
        "6df9b56ca3"},
-      {&EC_hash_to_curve_p256_xmd_sha256_sswu, NID_X9_62_prime256v1,
+      {&EC_hash_to_curve_p256_xmd_sha256_sswu, EC_group_p256(),
        "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_",
        "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
        "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
@@ -1261,7 +1251,7 @@
        "3dc65a0b5d",
        "98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539"
        "d2bfb3c29e"},
-      {&EC_hash_to_curve_p256_xmd_sha256_sswu, NID_X9_62_prime256v1,
+      {&EC_hash_to_curve_p256_xmd_sha256_sswu, EC_group_p256(),
        "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_",
        "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@@ -1279,25 +1269,25 @@
        "2b0f4757dc"},
 
       // See draft-irtf-cfrg-hash-to-curve-07, appendix G.2.1.
-      {hash_to_curve_p384_sha512_draft07, NID_secp384r1,
+      {hash_to_curve_p384_sha512_draft07, EC_group_p384(),
        "P384_XMD:SHA-512_SSWU_RO_TESTGEN", "",
        "2fc0b9efdd63a8e43b4db88dc12f03c798f6fd91bccac0c9096185"
        "4386e58fdc54fc2a01f0f358759054ce1f9b762025",
        "949b936fabb72cdb02cd7980b86cb6a3adf286658e81301648851d"
        "b8a49d9bec00ccb57698d559fc5960fa5030a8e54b"},
-      {hash_to_curve_p384_sha512_draft07, NID_secp384r1,
+      {hash_to_curve_p384_sha512_draft07, EC_group_p384(),
        "P384_XMD:SHA-512_SSWU_RO_TESTGEN", "abc",
        "4f3338035391e8ce8ce40c974136f0edc97f392ffd44a643338741"
        "8ed1b8c2603487e1688ec151f048fbc6b2c138c92f",
        "152b90aef6558be328a3168855fb1906452e7167b0f7c8a56ff9d4"
        "fa87d6fb522cdf8e409db54418b2c764fd26260757"},
-      {hash_to_curve_p384_sha512_draft07, NID_secp384r1,
+      {hash_to_curve_p384_sha512_draft07, EC_group_p384(),
        "P384_XMD:SHA-512_SSWU_RO_TESTGEN", "abcdef0123456789",
        "e9e5d7ac397e123d060ad44301cbc8eb972f6e64ebcff29dcc9b9a"
        "10357902aace2240c580fec85e5b427d98b4e80703",
        "916cb8963521ad75105be43cc4148e5a5bbb4fcf107f1577e4f7fa"
        "3ca58cd786aa76890c8e687d2353393bc16c78ec4d"},
-      {hash_to_curve_p384_sha512_draft07, NID_secp384r1,
+      {hash_to_curve_p384_sha512_draft07, EC_group_p384(),
        "P384_XMD:SHA-512_SSWU_RO_TESTGEN",
        "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@@ -1319,17 +1309,15 @@
     SCOPED_TRACE(test.dst);
     SCOPED_TRACE(test.msg);
 
-    bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.curve_nid));
-    ASSERT_TRUE(group);
-    bssl::UniquePtr<EC_POINT> p(EC_POINT_new(group.get()));
+    bssl::UniquePtr<EC_POINT> p(EC_POINT_new(test.group));
     ASSERT_TRUE(p);
     ASSERT_TRUE(test.hash_to_curve(
-        group.get(), p.get(), reinterpret_cast<const uint8_t *>(test.dst),
+        test.group, p.get(), reinterpret_cast<const uint8_t *>(test.dst),
         strlen(test.dst), reinterpret_cast<const uint8_t *>(test.msg),
         strlen(test.msg)));
 
     std::vector<uint8_t> buf;
-    ASSERT_TRUE(EncodeECPoint(&buf, group.get(), p.get(),
+    ASSERT_TRUE(EncodeECPoint(&buf, test.group, p.get(),
                               POINT_CONVERSION_UNCOMPRESSED));
     size_t field_len = (buf.size() - 1) / 2;
     EXPECT_EQ(test.x_hex,
@@ -1339,32 +1327,28 @@
   }
 
   // hash-to-curve functions should check for the wrong group.
-  bssl::UniquePtr<EC_GROUP> p224(EC_GROUP_new_by_curve_name(NID_secp224r1));
-  ASSERT_TRUE(p224);
-  bssl::UniquePtr<EC_GROUP> p384(EC_GROUP_new_by_curve_name(NID_secp384r1));
-  ASSERT_TRUE(p384);
   EC_JACOBIAN raw;
-  bssl::UniquePtr<EC_POINT> p_p384(EC_POINT_new(p384.get()));
+  bssl::UniquePtr<EC_POINT> p_p384(EC_POINT_new(EC_group_p384()));
   ASSERT_TRUE(p_p384);
-  bssl::UniquePtr<EC_POINT> p_p224(EC_POINT_new(p224.get()));
+  bssl::UniquePtr<EC_POINT> p_p224(EC_POINT_new(EC_group_p224()));
   ASSERT_TRUE(p_p224);
   static const uint8_t kDST[] = {0, 1, 2, 3};
   static const uint8_t kMessage[] = {4, 5, 6, 7};
   EXPECT_FALSE(ec_hash_to_curve_p384_xmd_sha384_sswu(
-      p224.get(), &raw, kDST, sizeof(kDST), kMessage, sizeof(kMessage)));
+      EC_group_p224(), &raw, kDST, sizeof(kDST), kMessage, sizeof(kMessage)));
   EXPECT_FALSE(EC_hash_to_curve_p384_xmd_sha384_sswu(
-      p224.get(), p_p224.get(), kDST, sizeof(kDST), kMessage,
+      EC_group_p224(), p_p224.get(), kDST, sizeof(kDST), kMessage,
       sizeof(kMessage)));
   EXPECT_FALSE(EC_hash_to_curve_p384_xmd_sha384_sswu(
-      p224.get(), p_p384.get(), kDST, sizeof(kDST), kMessage,
+      EC_group_p224(), p_p384.get(), kDST, sizeof(kDST), kMessage,
       sizeof(kMessage)));
   EXPECT_FALSE(EC_hash_to_curve_p384_xmd_sha384_sswu(
-      p384.get(), p_p224.get(), kDST, sizeof(kDST), kMessage,
+      EC_group_p384(), p_p224.get(), kDST, sizeof(kDST), kMessage,
       sizeof(kMessage)));
 
   // Zero-length DSTs are not allowed.
   EXPECT_FALSE(ec_hash_to_curve_p384_xmd_sha384_sswu(
-      p384.get(), &raw, nullptr, 0, kMessage, sizeof(kMessage)));
+      EC_group_p384(), &raw, nullptr, 0, kMessage, sizeof(kMessage)));
 }
 
 TEST(ECTest, HashToScalar) {
@@ -1372,21 +1356,21 @@
     int (*hash_to_scalar)(const EC_GROUP *group, EC_SCALAR *out,
                           const uint8_t *dst, size_t dst_len,
                           const uint8_t *msg, size_t msg_len);
-    int curve_nid;
+    const EC_GROUP *group;
     const char *dst;
     const char *msg;
     const char *result_hex;
   };
-  static const HashToScalarTest kTests[] = {
-      {&ec_hash_to_scalar_p384_xmd_sha512_draft07, NID_secp384r1,
+  const HashToScalarTest kTests[] = {
+      {&ec_hash_to_scalar_p384_xmd_sha512_draft07, EC_group_p384(),
        "P384_XMD:SHA-512_SCALAR_TEST", "",
        "9687acc2de56c3cf94c0e05b6811a21aa480092254ec0532bdce63"
        "140ecd340f09dc2d45d77e21fb0aa76f7707b8a676"},
-      {&ec_hash_to_scalar_p384_xmd_sha512_draft07, NID_secp384r1,
+      {&ec_hash_to_scalar_p384_xmd_sha512_draft07, EC_group_p384(),
        "P384_XMD:SHA-512_SCALAR_TEST", "abcdef0123456789",
        "8f8076022a68233cbcecaceae68c2068f132724f001caa78619eff"
        "1ffc58fa871db73fe9034fc9cf853c384ed34b5666"},
-      {&ec_hash_to_scalar_p384_xmd_sha512_draft07, NID_secp384r1,
+      {&ec_hash_to_scalar_p384_xmd_sha512_draft07, EC_group_p384(),
        "P384_XMD:SHA-512_SCALAR_TEST",
        "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@@ -1406,25 +1390,22 @@
     SCOPED_TRACE(test.dst);
     SCOPED_TRACE(test.msg);
 
-    bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.curve_nid));
-    ASSERT_TRUE(group);
     EC_SCALAR scalar;
     ASSERT_TRUE(test.hash_to_scalar(
-        group.get(), &scalar, reinterpret_cast<const uint8_t *>(test.dst),
+        test.group, &scalar, reinterpret_cast<const uint8_t *>(test.dst),
         strlen(test.dst), reinterpret_cast<const uint8_t *>(test.msg),
         strlen(test.msg)));
     uint8_t buf[EC_MAX_BYTES];
     size_t len;
-    ec_scalar_to_bytes(group.get(), buf, &len, &scalar);
+    ec_scalar_to_bytes(test.group, buf, &len, &scalar);
     EXPECT_EQ(test.result_hex, EncodeHex(bssl::MakeConstSpan(buf, len)));
   }
 
   // hash-to-scalar functions should check for the wrong group.
-  bssl::UniquePtr<EC_GROUP> p224(EC_GROUP_new_by_curve_name(NID_secp224r1));
-  ASSERT_TRUE(p224);
   EC_SCALAR scalar;
   static const uint8_t kDST[] = {0, 1, 2, 3};
   static const uint8_t kMessage[] = {4, 5, 6, 7};
   EXPECT_FALSE(ec_hash_to_scalar_p384_xmd_sha512_draft07(
-      p224.get(), &scalar, kDST, sizeof(kDST), kMessage, sizeof(kMessage)));
+      EC_group_p224(), &scalar, kDST, sizeof(kDST), kMessage,
+      sizeof(kMessage)));
 }
diff --git a/crypto/fipsmodule/ec/p256-nistz_test.cc b/crypto/fipsmodule/ec/p256-nistz_test.cc
index a53d94e..56eed08 100644
--- a/crypto/fipsmodule/ec/p256-nistz_test.cc
+++ b/crypto/fipsmodule/ec/p256-nistz_test.cc
@@ -109,13 +109,10 @@
   }
 #endif
 
-  bssl::UniquePtr<EC_GROUP> group(
-      EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
-  ASSERT_TRUE(group);
-
+  const EC_GROUP *group = EC_group_p256();
   BN_ULONG order_words[P256_LIMBS];
   ASSERT_TRUE(
-      bn_copy_words(order_words, P256_LIMBS, EC_GROUP_get0_order(group.get())));
+      bn_copy_words(order_words, P256_LIMBS, EC_GROUP_get0_order(group)));
 
   BN_ULONG in[P256_LIMBS], out[P256_LIMBS];
   EC_SCALAR in_scalar, out_scalar, result;
@@ -154,9 +151,9 @@
     // Calculate out*in and confirm that it equals one, modulo the order.
     OPENSSL_memcpy(in_scalar.words, in, sizeof(in));
     OPENSSL_memcpy(out_scalar.words, out, sizeof(out));
-    ec_scalar_to_montgomery(group.get(), &in_scalar, &in_scalar);
-    ec_scalar_to_montgomery(group.get(), &out_scalar, &out_scalar);
-    ec_scalar_mul_montgomery(group.get(), &result, &in_scalar, &out_scalar);
+    ec_scalar_to_montgomery(group, &in_scalar, &in_scalar);
+    ec_scalar_to_montgomery(group, &out_scalar, &out_scalar);
+    ec_scalar_mul_montgomery(group, &result, &in_scalar, &out_scalar);
 
     EXPECT_EQ(0, OPENSSL_memcmp(kOneMont, &result, sizeof(kOneMont)));
 
diff --git a/crypto/fipsmodule/ecdsa/ecdsa_test.cc b/crypto/fipsmodule/ecdsa/ecdsa_test.cc
index b821d0c..39ad0a2 100644
--- a/crypto/fipsmodule/ecdsa/ecdsa_test.cc
+++ b/crypto/fipsmodule/ecdsa/ecdsa_test.cc
@@ -319,17 +319,16 @@
   }
 
   if (curve_name == "P-224") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp224r1));
+    return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p224()));
   }
   if (curve_name == "P-256") {
-    return bssl::UniquePtr<EC_GROUP>(
-        EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+    return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p256()));
   }
   if (curve_name == "P-384") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp384r1));
+    return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p384()));
   }
   if (curve_name == "P-521") {
-    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp521r1));
+    return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p521()));
   }
   if (curve_name == "secp160r1") {
     return NewSecp160r1Group();
diff --git a/crypto/fipsmodule/self_check/self_check.c b/crypto/fipsmodule/self_check/self_check.c
index 525cd16..db61485 100644
--- a/crypto/fipsmodule/self_check/self_check.c
+++ b/crypto/fipsmodule/self_check/self_check.c
@@ -249,11 +249,12 @@
       0x93, 0x8b, 0x74, 0xf2, 0xbc, 0xc5, 0x30, 0x52, 0xb0, 0x77,
   };
 
-  EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+  EC_KEY *ec_key = EC_KEY_new();
   BIGNUM *qx = BN_bin2bn(kQx, sizeof(kQx), NULL);
   BIGNUM *qy = BN_bin2bn(kQy, sizeof(kQy), NULL);
   BIGNUM *d = BN_bin2bn(kD, sizeof(kD), NULL);
   if (ec_key == NULL || qx == NULL || qy == NULL || d == NULL ||
+      !EC_KEY_set_group(ec_key, EC_group_p256()) ||
       !EC_KEY_set_public_key_affine_coordinates(ec_key, qx, qy) ||
       !EC_KEY_set_private_key(ec_key, d)) {
     EC_KEY_free(ec_key);
@@ -411,7 +412,6 @@
 static int boringssl_self_test_ecc(void) {
   int ret = 0;
   EC_KEY *ec_key = NULL;
-  EC_GROUP *ec_group = NULL;
   EC_POINT *ec_point_in = NULL;
   EC_POINT *ec_point_out = NULL;
   BIGNUM *ec_scalar = NULL;
@@ -506,11 +506,7 @@
       0x7c, 0x41, 0x8f, 0xaf, 0x9c, 0x40, 0xaf, 0x2e, 0x4a, 0x0c,
   };
 
-  ec_group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
-  if (ec_group == NULL) {
-    fprintf(stderr, "Failed to create P-256 group.\n");
-    goto err;
-  }
+  const EC_GROUP *ec_group = EC_group_p256();
   ec_point_in = EC_POINT_new(ec_group);
   ec_point_out = EC_POINT_new(ec_group);
   ec_scalar = BN_new();
@@ -535,7 +531,6 @@
   EC_KEY_free(ec_key);
   EC_POINT_free(ec_point_in);
   EC_POINT_free(ec_point_out);
-  EC_GROUP_free(ec_group);
   BN_free(ec_scalar);
   ECDSA_SIG_free(sig);
 
diff --git a/crypto/fipsmodule/service_indicator/service_indicator_test.cc b/crypto/fipsmodule/service_indicator/service_indicator_test.cc
index 8ae52de..27042e0 100644
--- a/crypto/fipsmodule/service_indicator/service_indicator_test.cc
+++ b/crypto/fipsmodule/service_indicator/service_indicator_test.cc
@@ -1497,12 +1497,12 @@
 
   FIPSStatus approved = FIPSStatus::NOT_APPROVED;
 
-  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.nid));
+  const EC_GROUP *group = EC_GROUP_new_by_curve_name(test.nid);
   bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
   bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
   bssl::ScopedEVP_MD_CTX md_ctx;
   ASSERT_TRUE(eckey);
-  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group.get()));
+  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group));
 
   // Generate a generic EC key.
   ASSERT_TRUE(EC_KEY_generate_key(eckey.get()));
@@ -1557,12 +1557,12 @@
 
   FIPSStatus approved = FIPSStatus::NOT_APPROVED;
 
-  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.nid));
+  const EC_GROUP *group = EC_GROUP_new_by_curve_name(test.nid);
   bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
   bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
   bssl::ScopedEVP_MD_CTX md_ctx;
   ASSERT_TRUE(eckey);
-  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group.get()));
+  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group));
 
   // Generate ECDSA signatures for ECDSA verification.
   ASSERT_TRUE(EC_KEY_generate_key(eckey.get()));
@@ -1623,12 +1623,12 @@
   ASSERT_TRUE(EVP_DigestInit(ctx.get(), test.func()));
   ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), kPlaintext, sizeof(kPlaintext)));
 
-  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.nid));
+  const EC_GROUP *group = EC_GROUP_new_by_curve_name(test.nid);
   bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
   bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
   bssl::ScopedEVP_MD_CTX md_ctx;
   ASSERT_TRUE(eckey);
-  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group.get()));
+  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group));
 
   // Generate a generic ec key.
   EC_KEY_generate_key(eckey.get());
@@ -1719,7 +1719,7 @@
 
   FIPSStatus approved = FIPSStatus::NOT_APPROVED;
 
-  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.nid));
+  const EC_GROUP *group = EC_GROUP_new_by_curve_name(test.nid);
   bssl::UniquePtr<EC_KEY> our_key(EC_KEY_new());
   bssl::UniquePtr<EC_KEY> peer_key(EC_KEY_new());
   bssl::ScopedEVP_MD_CTX md_ctx;
@@ -1727,11 +1727,11 @@
   ASSERT_TRUE(peer_key);
 
   // Generate two generic ec key pairs.
-  ASSERT_TRUE(EC_KEY_set_group(our_key.get(), group.get()));
+  ASSERT_TRUE(EC_KEY_set_group(our_key.get(), group));
   ASSERT_TRUE(EC_KEY_generate_key(our_key.get()));
   ASSERT_TRUE(EC_KEY_check_key(our_key.get()));
 
-  ASSERT_TRUE(EC_KEY_set_group(peer_key.get(), group.get()));
+  ASSERT_TRUE(EC_KEY_set_group(peer_key.get(), group));
   ASSERT_TRUE(EC_KEY_generate_key(peer_key.get()));
   ASSERT_TRUE(EC_KEY_check_key(peer_key.get()));
 
diff --git a/crypto/test/wycheproof_util.cc b/crypto/test/wycheproof_util.cc
index 9d31706..573439f 100644
--- a/crypto/test/wycheproof_util.cc
+++ b/crypto/test/wycheproof_util.cc
@@ -106,28 +106,28 @@
   return nullptr;
 }
 
-bssl::UniquePtr<EC_GROUP> GetWycheproofCurve(FileTest *t, const char *key,
-                                             bool instruction) {
+const EC_GROUP *GetWycheproofCurve(FileTest *t, const char *key,
+                                   bool instruction) {
   std::string name;
   bool ok =
       instruction ? t->GetInstruction(&name, key) : t->GetAttribute(&name, key);
   if (!ok) {
     return nullptr;
   }
-  int nid;
   if (name == "secp224r1") {
-    nid = NID_secp224r1;
-  } else if (name == "secp256r1") {
-    nid = NID_X9_62_prime256v1;
-  } else if (name == "secp384r1") {
-    nid = NID_secp384r1;
-  } else if (name == "secp521r1") {
-    nid = NID_secp521r1;
-  } else {
-    t->PrintLine("Unknown curve '%s'", name.c_str());
-    return nullptr;
+    return EC_group_p224();
   }
-  return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(nid));
+  if (name == "secp256r1") {
+    return EC_group_p256();
+  }
+  if (name == "secp384r1") {
+    return EC_group_p384();
+  }
+  if (name == "secp521r1") {
+    return EC_group_p521();
+  }
+  t->PrintLine("Unknown curve '%s'", name.c_str());
+  return nullptr;
 }
 
 bssl::UniquePtr<BIGNUM> GetWycheproofBIGNUM(FileTest *t, const char *key,
diff --git a/crypto/test/wycheproof_util.h b/crypto/test/wycheproof_util.h
index 67e0ed3..8e10420 100644
--- a/crypto/test/wycheproof_util.h
+++ b/crypto/test/wycheproof_util.h
@@ -51,8 +51,8 @@
 
 // GetWycheproofCurve returns a curve using the Wycheproof name, or nullptr on
 // error.
-bssl::UniquePtr<EC_GROUP> GetWycheproofCurve(FileTest *t, const char *key,
-                                             bool instruction);
+const EC_GROUP *GetWycheproofCurve(FileTest *t, const char *key,
+                                   bool instruction);
 
 // GetWycheproofBIGNUM returns a BIGNUM in the Wycheproof format, or nullptr on
 // error.
diff --git a/crypto/trust_token/pmbtoken.c b/crypto/trust_token/pmbtoken.c
index 49eda91..5334a0c 100644
--- a/crypto/trust_token/pmbtoken.c
+++ b/crypto/trust_token/pmbtoken.c
@@ -62,17 +62,13 @@
 
 static const uint8_t kDefaultAdditionalData[32] = {0};
 
-static int pmbtoken_init_method(PMBTOKEN_METHOD *method, int curve_nid,
+static int pmbtoken_init_method(PMBTOKEN_METHOD *method, const EC_GROUP *group,
                                 const uint8_t *h_bytes, size_t h_len,
                                 hash_t_func_t hash_t, hash_s_func_t hash_s,
                                 hash_c_func_t hash_c,
                                 hash_to_scalar_func_t hash_to_scalar,
                                 int prefix_point) {
-  method->group = EC_GROUP_new_by_curve_name(curve_nid);
-  if (method->group == NULL) {
-    return 0;
-  }
-
+  method->group = group;
   method->hash_t = hash_t;
   method->hash_s = hash_s;
   method->hash_c = hash_c;
@@ -1230,7 +1226,7 @@
   };
 
   pmbtoken_exp1_ok = pmbtoken_init_method(
-      &pmbtoken_exp1_method, NID_secp384r1, kH, sizeof(kH),
+      &pmbtoken_exp1_method, EC_group_p384(), kH, sizeof(kH),
       pmbtoken_exp1_hash_t, pmbtoken_exp1_hash_s, pmbtoken_exp1_hash_c,
       pmbtoken_exp1_hash_to_scalar, 1);
 }
@@ -1403,7 +1399,7 @@
   };
 
   pmbtoken_exp2_ok = pmbtoken_init_method(
-      &pmbtoken_exp2_method, NID_secp384r1, kH, sizeof(kH),
+      &pmbtoken_exp2_method, EC_group_p384(), kH, sizeof(kH),
       pmbtoken_exp2_hash_t, pmbtoken_exp2_hash_s, pmbtoken_exp2_hash_c,
       pmbtoken_exp2_hash_to_scalar, 0);
 }
@@ -1577,7 +1573,7 @@
   };
 
   pmbtoken_pst1_ok = pmbtoken_init_method(
-      &pmbtoken_pst1_method, NID_secp384r1, kH, sizeof(kH),
+      &pmbtoken_pst1_method, EC_group_p384(), kH, sizeof(kH),
       pmbtoken_pst1_hash_t, pmbtoken_pst1_hash_s, pmbtoken_pst1_hash_c,
       pmbtoken_pst1_hash_to_scalar, 0);
 }
diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc
index 37fdc85..fd18776 100644
--- a/crypto/trust_token/trust_token_test.cc
+++ b/crypto/trust_token/trust_token_test.cc
@@ -250,9 +250,7 @@
 
 // Test that H in |TRUST_TOKEN_experiment_v1| was computed correctly.
 TEST(TrustTokenTest, HExp1) {
-  const EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp384r1);
-  ASSERT_TRUE(group);
-
+  const EC_GROUP *group = EC_group_p384();
   const uint8_t kHGen[] = "generator";
   const uint8_t kHLabel[] = "PMBTokens Experiment V1 HashH";
 
@@ -272,9 +270,7 @@
 
 // Test that H in |TRUST_TOKEN_experiment_v2_pmb| was computed correctly.
 TEST(TrustTokenTest, HExp2) {
-  const EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp384r1);
-  ASSERT_TRUE(group);
-
+  const EC_GROUP *group = EC_group_p384();
   const uint8_t kHGen[] = "generator";
   const uint8_t kHLabel[] = "PMBTokens Experiment V2 HashH";
 
diff --git a/crypto/trust_token/voprf.c b/crypto/trust_token/voprf.c
index 15939d3..c2ab815 100644
--- a/crypto/trust_token/voprf.c
+++ b/crypto/trust_token/voprf.c
@@ -35,7 +35,7 @@
                                      uint8_t *buf, size_t len);
 
 typedef struct {
-  const EC_GROUP *group;
+  const EC_GROUP *(*group_func)(void);
 
   // hash_to_group implements the HashToGroup operation for VOPRFs. It returns
   // one on success and zero on error.
@@ -47,20 +47,6 @@
 
 static const uint8_t kDefaultAdditionalData[32] = {0};
 
-static int voprf_init_method(VOPRF_METHOD *method, int curve_nid,
-                             hash_to_group_func_t hash_to_group,
-                             hash_to_scalar_func_t hash_to_scalar) {
-  method->group = EC_GROUP_new_by_curve_name(curve_nid);
-  if (method->group == NULL) {
-    return 0;
-  }
-
-  method->hash_to_group = hash_to_group;
-  method->hash_to_scalar = hash_to_scalar;
-
-  return 1;
-}
-
 static int cbb_add_point(CBB *out, const EC_GROUP *group,
                          const EC_AFFINE *point) {
   uint8_t *p;
@@ -117,7 +103,7 @@
 
 static int voprf_calculate_key(const VOPRF_METHOD *method, CBB *out_private,
                                CBB *out_public, const EC_SCALAR *priv) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   EC_JACOBIAN pub;
   EC_AFFINE pub_affine;
   if (!ec_point_mul_scalar_base(group, &pub, priv) ||
@@ -139,7 +125,8 @@
 static int voprf_generate_key(const VOPRF_METHOD *method, CBB *out_private,
                               CBB *out_public) {
   EC_SCALAR priv;
-  if (!ec_random_nonzero_scalar(method->group, &priv, kDefaultAdditionalData)) {
+  if (!ec_random_nonzero_scalar(method->group_func(), &priv,
+                                kDefaultAdditionalData)) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
     return 0;
   }
@@ -162,7 +149,7 @@
       !CBB_add_bytes(&cbb, kKeygenLabel, sizeof(kKeygenLabel)) ||
       !CBB_add_bytes(&cbb, secret, secret_len) ||
       !CBB_finish(&cbb, &buf, &len) ||
-      !method->hash_to_scalar(method->group, &priv, buf, len)) {
+      !method->hash_to_scalar(method->group_func(), &priv, buf, len)) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
     goto err;
   }
@@ -178,7 +165,7 @@
 static int voprf_client_key_from_bytes(const VOPRF_METHOD *method,
                                        TRUST_TOKEN_CLIENT_KEY *key,
                                        const uint8_t *in, size_t len) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   if (!ec_point_from_uncompressed(group, &key->pubs, in, len)) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
     return 0;
@@ -190,7 +177,7 @@
 static int voprf_issuer_key_from_bytes(const VOPRF_METHOD *method,
                                        TRUST_TOKEN_ISSUER_KEY *key,
                                        const uint8_t *in, size_t len) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   if (!ec_scalar_from_bytes(group, &key->xs, in, len)) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
     return 0;
@@ -213,7 +200,7 @@
                                                    size_t msg_len) {
   SHA512_CTX hash_ctx;
 
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   STACK_OF(TRUST_TOKEN_PRETOKEN) *pretokens =
       sk_TRUST_TOKEN_PRETOKEN_new_null();
   if (pretokens == NULL) {
@@ -280,6 +267,7 @@
                                const EC_AFFINE *K1) {
   static const uint8_t kDLEQLabel[] = "DLEQ";
 
+  const EC_GROUP *group = method->group_func();
   int ok = 0;
   CBB cbb;
   CBB_zero(&cbb);
@@ -287,13 +275,13 @@
   size_t len;
   if (!CBB_init(&cbb, 0) ||
       !CBB_add_bytes(&cbb, kDLEQLabel, sizeof(kDLEQLabel)) ||
-      !cbb_add_point(&cbb, method->group, X) ||
-      !cbb_add_point(&cbb, method->group, T) ||
-      !cbb_add_point(&cbb, method->group, W) ||
-      !cbb_add_point(&cbb, method->group, K0) ||
-      !cbb_add_point(&cbb, method->group, K1) ||
+      !cbb_add_point(&cbb, group, X) ||
+      !cbb_add_point(&cbb, group, T) ||
+      !cbb_add_point(&cbb, group, W) ||
+      !cbb_add_point(&cbb, group, K0) ||
+      !cbb_add_point(&cbb, group, K1) ||
       !CBB_finish(&cbb, &buf, &len) ||
-      !method->hash_to_scalar(method->group, out, buf, len)) {
+      !method->hash_to_scalar(group, out, buf, len)) {
     goto err;
   }
 
@@ -311,18 +299,19 @@
                                     const EC_AFFINE *a3) {
   static const uint8_t kChallengeLabel[] = "Challenge";
 
+  const EC_GROUP *group = method->group_func();
   CBB cbb;
   uint8_t transcript[5 * EC_MAX_COMPRESSED + 2 + sizeof(kChallengeLabel) - 1];
   size_t len;
   if (!CBB_init_fixed(&cbb, transcript, sizeof(transcript)) ||
-      !cbb_serialize_point(&cbb, method->group, Bm) ||
-      !cbb_serialize_point(&cbb, method->group, a0) ||
-      !cbb_serialize_point(&cbb, method->group, a1) ||
-      !cbb_serialize_point(&cbb, method->group, a2) ||
-      !cbb_serialize_point(&cbb, method->group, a3) ||
+      !cbb_serialize_point(&cbb, group, Bm) ||
+      !cbb_serialize_point(&cbb, group, a0) ||
+      !cbb_serialize_point(&cbb, group, a1) ||
+      !cbb_serialize_point(&cbb, group, a2) ||
+      !cbb_serialize_point(&cbb, group, a3) ||
       !CBB_add_bytes(&cbb, kChallengeLabel, sizeof(kChallengeLabel) - 1) ||
       !CBB_finish(&cbb, NULL, &len) ||
-      !method->hash_to_scalar(method->group, out, transcript, len)) {
+      !method->hash_to_scalar(group, out, transcript, len)) {
     return 0;
   }
 
@@ -348,7 +337,7 @@
       !CBB_add_bytes(&cbb, CBB_data(points), CBB_len(points)) ||
       !CBB_add_u16(&cbb, (uint16_t)index) ||
       !CBB_finish(&cbb, &buf, &len) ||
-      !method->hash_to_scalar(method->group, out, buf, len)) {
+      !method->hash_to_scalar(method->group_func(), out, buf, len)) {
     goto err;
   }
 
@@ -363,7 +352,7 @@
 static int dleq_generate(const VOPRF_METHOD *method, CBB *cbb,
                          const TRUST_TOKEN_ISSUER_KEY *priv,
                          const EC_JACOBIAN *T, const EC_JACOBIAN *W) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
 
   enum {
     idx_T,
@@ -429,7 +418,7 @@
 static int dleq_verify(const VOPRF_METHOD *method, CBS *cbs,
                        const TRUST_TOKEN_CLIENT_KEY *pub, const EC_JACOBIAN *T,
                        const EC_JACOBIAN *W) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
 
 
   enum {
@@ -488,7 +477,7 @@
 static int voprf_sign_tt(const VOPRF_METHOD *method,
                          const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs,
                          size_t num_requested, size_t num_to_issue) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   if (num_requested < num_to_issue) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR);
     return 0;
@@ -510,7 +499,7 @@
       !Zs ||
       !es ||
       !CBB_init(&batch_cbb, 0) ||
-      !cbb_add_point(&batch_cbb, method->group, &key->pubs)) {
+      !cbb_add_point(&batch_cbb, group, &key->pubs)) {
     goto err;
   }
 
@@ -587,7 +576,7 @@
     const VOPRF_METHOD *method, const TRUST_TOKEN_CLIENT_KEY *key,
     const STACK_OF(TRUST_TOKEN_PRETOKEN) *pretokens, CBS *cbs, size_t count,
     uint32_t key_id) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   if (count > sk_TRUST_TOKEN_PRETOKEN_num(pretokens)) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
     return NULL;
@@ -611,7 +600,7 @@
       Zs == NULL ||
       es == NULL ||
       !CBB_init(&batch_cbb, 0) ||
-      !cbb_add_point(&batch_cbb, method->group, &key->pubs)) {
+      !cbb_add_point(&batch_cbb, group, &key->pubs)) {
     goto err;
   }
 
@@ -721,7 +710,7 @@
 static int compute_composite_seed(const VOPRF_METHOD *method,
                                   uint8_t out[SHA384_DIGEST_LENGTH],
                                   const EC_AFFINE *pub) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   static const uint8_t kSeedDST[] = "Seed-OPRFV1-\x01-P384-SHA384";
 
   SHA512_CTX hash_ctx;
@@ -739,7 +728,7 @@
                                      EC_SCALAR *di, size_t index,
                                      const EC_AFFINE *C, const EC_AFFINE *D) {
   static const uint8_t kCompositeLabel[] = "Composite";
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
 
   if (index > UINT16_MAX) {
     return 0;
@@ -758,7 +747,7 @@
       !CBB_add_bytes(&cbb, kCompositeLabel,
                      sizeof(kCompositeLabel) - 1) ||
       !CBB_finish(&cbb, NULL, &len) ||
-      !method->hash_to_scalar(method->group, di, transcript, len)) {
+      !method->hash_to_scalar(group, di, transcript, len)) {
     return 0;
   }
 
@@ -769,7 +758,7 @@
                           const TRUST_TOKEN_ISSUER_KEY *priv,
                           const EC_SCALAR *r, const EC_JACOBIAN *M,
                           const EC_JACOBIAN *Z) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
 
   enum {
     idx_M,
@@ -820,7 +809,7 @@
 static int verify_proof(const VOPRF_METHOD *method, CBS *cbs,
                         const TRUST_TOKEN_CLIENT_KEY *pub,
                         const EC_JACOBIAN *M, const EC_JACOBIAN *Z) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
 
   enum {
     idx_M,
@@ -873,7 +862,7 @@
                            const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb,
                            CBS *cbs, size_t num_requested, size_t num_to_issue,
                            const EC_SCALAR *proof_scalar) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   if (num_requested < num_to_issue) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR);
     return 0;
@@ -963,7 +952,7 @@
                       const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs,
                       size_t num_requested, size_t num_to_issue) {
   EC_SCALAR proof_scalar;
-  if (!ec_random_nonzero_scalar(method->group, &proof_scalar,
+  if (!ec_random_nonzero_scalar(method->group_func(), &proof_scalar,
                                 kDefaultAdditionalData)) {
     return 0;
   }
@@ -977,8 +966,8 @@
     CBS *cbs, size_t num_requested, size_t num_to_issue,
     const uint8_t *proof_scalar_buf, size_t proof_scalar_len) {
   EC_SCALAR proof_scalar;
-  if (!ec_scalar_from_bytes(method->group, &proof_scalar, proof_scalar_buf,
-                            proof_scalar_len)) {
+  if (!ec_scalar_from_bytes(method->group_func(), &proof_scalar,
+                            proof_scalar_buf, proof_scalar_len)) {
     return 0;
   }
   return voprf_sign_impl(method, key, cbb, cbs, num_requested, num_to_issue,
@@ -989,7 +978,7 @@
     const VOPRF_METHOD *method, const TRUST_TOKEN_CLIENT_KEY *key,
     const STACK_OF(TRUST_TOKEN_PRETOKEN) *pretokens, CBS *cbs, size_t count,
     uint32_t key_id) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   if (count > sk_TRUST_TOKEN_PRETOKEN_num(pretokens)) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
     return NULL;
@@ -1099,7 +1088,7 @@
                       uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE],
                       const uint8_t *token, size_t token_len,
                       int include_message, const uint8_t *msg, size_t msg_len) {
-  const EC_GROUP *group = method->group;
+  const EC_GROUP *group = method->group_func();
   CBS cbs, salt;
   CBS_init(&cbs, token, token_len);
   EC_AFFINE Ws;
@@ -1154,57 +1143,27 @@
       group, out, kHashCLabel, sizeof(kHashCLabel), buf, len);
 }
 
-static int voprf_exp2_ok = 0;
-static VOPRF_METHOD voprf_exp2_method;
-static CRYPTO_once_t voprf_exp2_method_once = CRYPTO_ONCE_INIT;
-
-static void voprf_exp2_init_method_impl(void) {
-  voprf_exp2_ok =
-      voprf_init_method(&voprf_exp2_method, NID_secp384r1,
-                        voprf_exp2_hash_to_group, voprf_exp2_hash_to_scalar);
-}
-
-static int voprf_exp2_init_method(void) {
-  CRYPTO_once(&voprf_exp2_method_once, voprf_exp2_init_method_impl);
-  if (!voprf_exp2_ok) {
-    OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-  return 1;
-}
+static VOPRF_METHOD voprf_exp2_method = {
+    EC_group_p384, voprf_exp2_hash_to_group, voprf_exp2_hash_to_scalar};
 
 int voprf_exp2_generate_key(CBB *out_private, CBB *out_public) {
-  if (!voprf_exp2_init_method()) {
-    return 0;
-  }
-
   return voprf_generate_key(&voprf_exp2_method, out_private, out_public);
 }
 
 int voprf_exp2_derive_key_from_secret(CBB *out_private, CBB *out_public,
                                       const uint8_t *secret,
                                       size_t secret_len) {
-  if (!voprf_exp2_init_method()) {
-    return 0;
-  }
-
   return voprf_derive_key_from_secret(&voprf_exp2_method, out_private,
                                       out_public, secret, secret_len);
 }
 
 int voprf_exp2_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key,
                                      const uint8_t *in, size_t len) {
-  if (!voprf_exp2_init_method()) {
-    return 0;
-  }
   return voprf_client_key_from_bytes(&voprf_exp2_method, key, in, len);
 }
 
 int voprf_exp2_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key,
                                      const uint8_t *in, size_t len) {
-  if (!voprf_exp2_init_method()) {
-    return 0;
-  }
   return voprf_issuer_key_from_bytes(&voprf_exp2_method, key, in, len);
 }
 
@@ -1212,9 +1171,6 @@
                                                  int include_message,
                                                  const uint8_t *msg,
                                                  size_t msg_len) {
-  if (!voprf_exp2_init_method()) {
-    return NULL;
-  }
   return voprf_blind(&voprf_exp2_method, cbb, count, include_message, msg,
                      msg_len);
 }
@@ -1222,7 +1178,7 @@
 int voprf_exp2_sign(const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs,
                     size_t num_requested, size_t num_to_issue,
                     uint8_t private_metadata) {
-  if (!voprf_exp2_init_method() || private_metadata != 0) {
+  if (private_metadata != 0) {
     return 0;
   }
   return voprf_sign_tt(&voprf_exp2_method, key, cbb, cbs, num_requested,
@@ -1233,9 +1189,6 @@
     const TRUST_TOKEN_CLIENT_KEY *key,
     const STACK_OF(TRUST_TOKEN_PRETOKEN) *pretokens, CBS *cbs, size_t count,
     uint32_t key_id) {
-  if (!voprf_exp2_init_method()) {
-    return NULL;
-  }
   return voprf_unblind_tt(&voprf_exp2_method, key, pretokens, cbs, count,
                           key_id);
 }
@@ -1245,9 +1198,6 @@
                     uint8_t *out_private_metadata, const uint8_t *token,
                     size_t token_len, int include_message, const uint8_t *msg,
                     size_t msg_len) {
-  if (!voprf_exp2_init_method()) {
-    return 0;
-  }
   return voprf_read(&voprf_exp2_method, key, out_nonce, token, token_len,
                     include_message, msg, msg_len);
 }
@@ -1269,57 +1219,27 @@
                                            sizeof(kHashCLabel) - 1, buf, len);
 }
 
-static int voprf_pst1_ok = 0;
-static VOPRF_METHOD voprf_pst1_method;
-static CRYPTO_once_t voprf_pst1_method_once = CRYPTO_ONCE_INIT;
-
-static void voprf_pst1_init_method_impl(void) {
-  voprf_pst1_ok =
-      voprf_init_method(&voprf_pst1_method, NID_secp384r1,
-                        voprf_pst1_hash_to_group, voprf_pst1_hash_to_scalar);
-}
-
-static int voprf_pst1_init_method(void) {
-  CRYPTO_once(&voprf_pst1_method_once, voprf_pst1_init_method_impl);
-  if (!voprf_pst1_ok) {
-    OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-  return 1;
-}
+static VOPRF_METHOD voprf_pst1_method = {
+    EC_group_p384, voprf_pst1_hash_to_group, voprf_pst1_hash_to_scalar};
 
 int voprf_pst1_generate_key(CBB *out_private, CBB *out_public) {
-  if (!voprf_pst1_init_method()) {
-    return 0;
-  }
-
   return voprf_generate_key(&voprf_pst1_method, out_private, out_public);
 }
 
 int voprf_pst1_derive_key_from_secret(CBB *out_private, CBB *out_public,
                                       const uint8_t *secret,
                                       size_t secret_len) {
-  if (!voprf_pst1_init_method()) {
-    return 0;
-  }
-
   return voprf_derive_key_from_secret(&voprf_pst1_method, out_private,
                                       out_public, secret, secret_len);
 }
 
 int voprf_pst1_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key,
                                      const uint8_t *in, size_t len) {
-  if (!voprf_pst1_init_method()) {
-    return 0;
-  }
   return voprf_client_key_from_bytes(&voprf_pst1_method, key, in, len);
 }
 
 int voprf_pst1_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key,
                                      const uint8_t *in, size_t len) {
-  if (!voprf_pst1_init_method()) {
-    return 0;
-  }
   return voprf_issuer_key_from_bytes(&voprf_pst1_method, key, in, len);
 }
 
@@ -1327,9 +1247,6 @@
                                                  int include_message,
                                                  const uint8_t *msg,
                                                  size_t msg_len) {
-  if (!voprf_pst1_init_method()) {
-    return NULL;
-  }
   return voprf_blind(&voprf_pst1_method, cbb, count, include_message, msg,
                      msg_len);
 }
@@ -1337,7 +1254,7 @@
 int voprf_pst1_sign(const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs,
                     size_t num_requested, size_t num_to_issue,
                     uint8_t private_metadata) {
-  if (!voprf_pst1_init_method() || private_metadata != 0) {
+  if (private_metadata != 0) {
     return 0;
   }
   return voprf_sign(&voprf_pst1_method, key, cbb, cbs, num_requested,
@@ -1349,7 +1266,7 @@
     const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, size_t num_requested,
     size_t num_to_issue, uint8_t private_metadata,
     const uint8_t *proof_scalar_buf, size_t proof_scalar_len) {
-  if (!voprf_pst1_init_method() || private_metadata != 0) {
+  if (private_metadata != 0) {
     return 0;
   }
   return voprf_sign_with_proof_scalar_for_testing(
@@ -1361,9 +1278,6 @@
     const TRUST_TOKEN_CLIENT_KEY *key,
     const STACK_OF(TRUST_TOKEN_PRETOKEN) *pretokens, CBS *cbs, size_t count,
     uint32_t key_id) {
-  if (!voprf_pst1_init_method()) {
-    return NULL;
-  }
   return voprf_unblind(&voprf_pst1_method, key, pretokens, cbs, count, key_id);
 }
 
@@ -1372,9 +1286,6 @@
                     uint8_t *out_private_metadata, const uint8_t *token,
                     size_t token_len, int include_message, const uint8_t *msg,
                     size_t msg_len) {
-  if (!voprf_pst1_init_method()) {
-    return 0;
-  }
   return voprf_read(&voprf_pst1_method, key, out_nonce, token, token_len,
                     include_message, msg, msg_len);
 }
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 9f758ca..ffbf51e 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -5684,7 +5684,6 @@
 #define SSL_R_NO_CIPHER_MATCH 177
 #define SSL_R_NO_COMPRESSION_SPECIFIED 178
 #define SSL_R_NO_METHOD_SPECIFIED 179
-#define SSL_R_NO_P256_SUPPORT 180
 #define SSL_R_NO_PRIVATE_KEY_ASSIGNED 181
 #define SSL_R_NO_RENEGOTIATION 182
 #define SSL_R_NO_REQUIRED_DIGEST 183
diff --git a/ssl/extensions.cc b/ssl/extensions.cc
index c5b1ed1..05aeb40 100644
--- a/ssl/extensions.cc
+++ b/ssl/extensions.cc
@@ -4102,12 +4102,7 @@
     return false;
   }
 
-  UniquePtr<EC_GROUP> p256(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
-  if (!p256) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
-    return false;
-  }
-
+  const EC_GROUP *p256 = EC_group_p256();
   UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
   UniquePtr<BIGNUM> x(BN_new()), y(BN_new());
   if (!sig || !x || !y) {
@@ -4123,11 +4118,11 @@
   }
 
   UniquePtr<EC_KEY> key(EC_KEY_new());
-  UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
+  UniquePtr<EC_POINT> point(EC_POINT_new(p256));
   if (!key || !point ||
-      !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(),
-                                           y.get(), nullptr) ||
-      !EC_KEY_set_group(key.get(), p256.get()) ||
+      !EC_POINT_set_affine_coordinates_GFp(p256, point.get(), x.get(), y.get(),
+                                           nullptr) ||
+      !EC_KEY_set_group(key.get(), p256) ||
       !EC_KEY_set_public_key(key.get(), point.get())) {
     return false;
   }
diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc
index d932ef3..694bec1 100644
--- a/ssl/ssl_key_share.cc
+++ b/ssl/ssl_key_share.cc
@@ -40,8 +40,8 @@
 
 class ECKeyShare : public SSLKeyShare {
  public:
-  ECKeyShare(int nid, uint16_t group_id)
-      : group_(EC_GROUP_new_by_curve_name(nid)), group_id_(group_id) {}
+  ECKeyShare(const EC_GROUP *group, uint16_t group_id)
+      : group_(group), group_id_(group_id) {}
 
   uint16_t GroupID() const override { return group_id_; }
 
@@ -49,17 +49,16 @@
     assert(!private_key_);
     // Generate a private key.
     private_key_.reset(BN_new());
-    if (!group_ || !private_key_ ||
-        !BN_rand_range_ex(private_key_.get(), 1,
-                          EC_GROUP_get0_order(group_))) {
+    if (!private_key_ ||
+        !BN_rand_range_ex(private_key_.get(), 1, EC_GROUP_get0_order(group_))) {
       return false;
     }
 
     // Compute the corresponding public key and serialize it.
     UniquePtr<EC_POINT> public_key(EC_POINT_new(group_));
     if (!public_key ||
-        !EC_POINT_mul(group_, public_key.get(), private_key_.get(),
-                      nullptr, nullptr, /*ctx=*/nullptr) ||
+        !EC_POINT_mul(group_, public_key.get(), private_key_.get(), nullptr,
+                      nullptr, /*ctx=*/nullptr) ||
         !EC_POINT_point2cbb(out, group_, public_key.get(),
                             POINT_CONVERSION_UNCOMPRESSED, /*ctx=*/nullptr)) {
       return false;
@@ -98,11 +97,10 @@
     }
 
     // Compute the x-coordinate of |peer_key| * |private_key_|.
-    if (!EC_POINT_mul(group_, result.get(), NULL, peer_point.get(),
+    if (!EC_POINT_mul(group_, result.get(), nullptr, peer_point.get(),
                       private_key_.get(), /*ctx=*/nullptr) ||
         !EC_POINT_get_affine_coordinates_GFp(group_, result.get(), x.get(),
-                                             NULL,
-                                             /*ctx=*/nullptr)) {
+                                             nullptr, /*ctx=*/nullptr)) {
       return false;
     }
 
@@ -303,13 +301,13 @@
 UniquePtr<SSLKeyShare> SSLKeyShare::Create(uint16_t group_id) {
   switch (group_id) {
     case SSL_GROUP_SECP224R1:
-      return MakeUnique<ECKeyShare>(NID_secp224r1, SSL_GROUP_SECP224R1);
+      return MakeUnique<ECKeyShare>(EC_group_p224(), SSL_GROUP_SECP224R1);
     case SSL_GROUP_SECP256R1:
-      return MakeUnique<ECKeyShare>(NID_X9_62_prime256v1, SSL_GROUP_SECP256R1);
+      return MakeUnique<ECKeyShare>(EC_group_p256(), SSL_GROUP_SECP256R1);
     case SSL_GROUP_SECP384R1:
-      return MakeUnique<ECKeyShare>(NID_secp384r1, SSL_GROUP_SECP384R1);
+      return MakeUnique<ECKeyShare>(EC_group_p384(), SSL_GROUP_SECP384R1);
     case SSL_GROUP_SECP521R1:
-      return MakeUnique<ECKeyShare>(NID_secp521r1, SSL_GROUP_SECP521R1);
+      return MakeUnique<ECKeyShare>(EC_group_p521(), SSL_GROUP_SECP521R1);
     case SSL_GROUP_X25519:
       return MakeUnique<X25519KeyShare>();
     case SSL_GROUP_X25519_KYBER768_DRAFT00:
diff --git a/tool/speed.cc b/tool/speed.cc
index 089a460..f05be90 100644
--- a/tool/speed.cc
+++ b/tool/speed.cc
@@ -741,14 +741,15 @@
   return true;
 }
 
-static bool SpeedECDHCurve(const std::string &name, int nid,
+static bool SpeedECDHCurve(const std::string &name, const EC_GROUP *group,
                            const std::string &selected) {
   if (!selected.empty() && name.find(selected) == std::string::npos) {
     return true;
   }
 
-  bssl::UniquePtr<EC_KEY> peer_key(EC_KEY_new_by_curve_name(nid));
+  bssl::UniquePtr<EC_KEY> peer_key(EC_KEY_new());
   if (!peer_key ||
+      !EC_KEY_set_group(peer_key.get(), group) ||
       !EC_KEY_generate_key(peer_key.get())) {
     return false;
   }
@@ -769,12 +770,12 @@
 
   TimeResults results;
   if (!TimeFunctionParallel(
-          &results, [nid, peer_value_len, &peer_value]() -> bool {
-            bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid));
-            if (!key || !EC_KEY_generate_key(key.get())) {
+          &results, [group, peer_value_len, &peer_value]() -> bool {
+            bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
+            if (!key || !EC_KEY_set_group(key.get(), group) ||
+                !EC_KEY_generate_key(key.get())) {
               return false;
             }
-            const EC_GROUP *const group = EC_KEY_get0_group(key.get());
             bssl::UniquePtr<EC_POINT> point(EC_POINT_new(group));
             bssl::UniquePtr<EC_POINT> peer_point(EC_POINT_new(group));
             bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
@@ -798,14 +799,15 @@
   return true;
 }
 
-static bool SpeedECDSACurve(const std::string &name, int nid,
+static bool SpeedECDSACurve(const std::string &name, const EC_GROUP *group,
                             const std::string &selected) {
   if (!selected.empty() && name.find(selected) == std::string::npos) {
     return true;
   }
 
-  bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid));
+  bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
   if (!key ||
+      !EC_KEY_set_group(key.get(), group) ||
       !EC_KEY_generate_key(key.get())) {
     return false;
   }
@@ -849,17 +851,17 @@
 }
 
 static bool SpeedECDH(const std::string &selected) {
-  return SpeedECDHCurve("ECDH P-224", NID_secp224r1, selected) &&
-         SpeedECDHCurve("ECDH P-256", NID_X9_62_prime256v1, selected) &&
-         SpeedECDHCurve("ECDH P-384", NID_secp384r1, selected) &&
-         SpeedECDHCurve("ECDH P-521", NID_secp521r1, selected);
+  return SpeedECDHCurve("ECDH P-224", EC_group_p224(), selected) &&
+         SpeedECDHCurve("ECDH P-256", EC_group_p256(), selected) &&
+         SpeedECDHCurve("ECDH P-384", EC_group_p384(), selected) &&
+         SpeedECDHCurve("ECDH P-521", EC_group_p521(), selected);
 }
 
 static bool SpeedECDSA(const std::string &selected) {
-  return SpeedECDSACurve("ECDSA P-224", NID_secp224r1, selected) &&
-         SpeedECDSACurve("ECDSA P-256", NID_X9_62_prime256v1, selected) &&
-         SpeedECDSACurve("ECDSA P-384", NID_secp384r1, selected) &&
-         SpeedECDSACurve("ECDSA P-521", NID_secp521r1, selected);
+  return SpeedECDSACurve("ECDSA P-224", EC_group_p224(), selected) &&
+         SpeedECDSACurve("ECDSA P-256", EC_group_p256(), selected) &&
+         SpeedECDSACurve("ECDSA P-384", EC_group_p384(), selected) &&
+         SpeedECDSACurve("ECDSA P-521", EC_group_p521(), selected);
 }
 
 static bool Speed25519(const std::string &selected) {
@@ -1139,28 +1141,22 @@
 
   TimeResults results;
   {
-    const EC_GROUP *p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
-    if (p256 == NULL) {
-      return false;
-    }
     if (!TimeFunctionParallel(&results, [&]() -> bool {
           EC_JACOBIAN out;
-          return ec_hash_to_curve_p256_xmd_sha256_sswu(
-              p256, &out, kLabel, sizeof(kLabel), input, sizeof(input));
+          return ec_hash_to_curve_p256_xmd_sha256_sswu(EC_group_p256(), &out,
+                                                       kLabel, sizeof(kLabel),
+                                                       input, sizeof(input));
         })) {
       fprintf(stderr, "hash-to-curve failed.\n");
       return false;
     }
     results.Print("hash-to-curve P256_XMD:SHA-256_SSWU_RO_");
 
-    const EC_GROUP *p384 = EC_GROUP_new_by_curve_name(NID_secp384r1);
-    if (p384 == NULL) {
-      return false;
-    }
     if (!TimeFunctionParallel(&results, [&]() -> bool {
           EC_JACOBIAN out;
-          return ec_hash_to_curve_p384_xmd_sha384_sswu(
-              p384, &out, kLabel, sizeof(kLabel), input, sizeof(input));
+          return ec_hash_to_curve_p384_xmd_sha384_sswu(EC_group_p384(), &out,
+                                                       kLabel, sizeof(kLabel),
+                                                       input, sizeof(input));
         })) {
       fprintf(stderr, "hash-to-curve failed.\n");
       return false;
@@ -1170,7 +1166,8 @@
     if (!TimeFunctionParallel(&results, [&]() -> bool {
           EC_SCALAR out;
           return ec_hash_to_scalar_p384_xmd_sha512_draft07(
-              p384, &out, kLabel, sizeof(kLabel), input, sizeof(input));
+              EC_group_p384(), &out, kLabel, sizeof(kLabel), input,
+              sizeof(input));
         })) {
       fprintf(stderr, "hash-to-scalar failed.\n");
       return false;