Commit 54d175c7d5 for openssl.org

commit 54d175c7d5f476e2a2c93c050dc145f484c93fbd
Author: Dmitry Belyavskiy <beldmit@gmail.com>
Date:   Wed Jan 14 17:59:38 2026 +0100

    Disabling explicit EC curves encoding

    In case the parameters don't exactly match the well-known ones

    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Simo Sorce <simo@redhat.com>
    (Merged from https://github.com/openssl/openssl/pull/29639)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 33444738e9..c915b6b217 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -93,7 +93,7 @@ jobs:
       run: echo "FIPS_VENDOR=CI" >> VERSION.dat
     - name: config
       # enable-quic is on by default, but we leave it here to check we're testing the explicit enable somewhere
-      run: CC=gcc ./config --strict-warnings --banner=Configured enable-demos enable-h3demo enable-sslkeylog enable-fips enable-quic enable-lms && perl configdata.pm --dump
+      run: CC=gcc ./config --strict-warnings --banner=Configured enable-demos enable-h3demo enable-ec_explicit_curves enable-sslkeylog enable-fips enable-quic enable-lms && perl configdata.pm --dump
     - name: make
       run: make -s -j4
     - name: get cpu info
@@ -414,7 +414,7 @@ jobs:
         sudo cat /proc/sys/vm/mmap_rnd_bits
         sudo sysctl -w vm.mmap_rnd_bits=28
     - name: config
-      run: ./config --strict-warnings --banner=Configured --debug enable-demos enable-h3demo enable-asan enable-ubsan enable-rc5 enable-md2 enable-ec_nistp_64_gcc_128 enable-fips enable-lms && perl configdata.pm --dump
+      run: ./config --strict-warnings --banner=Configured --debug enable-demos enable-h3demo enable-asan enable-ec_explicit_curves enable-ubsan enable-rc5 enable-md2 enable-ec_nistp_64_gcc_128 enable-fips enable-lms && perl configdata.pm --dump
     - name: make
       run: make -s -j4
     - name: get cpu info
@@ -443,7 +443,7 @@ jobs:
         sudo cat /proc/sys/vm/mmap_rnd_bits
         sudo sysctl -w vm.mmap_rnd_bits=28
     - name: config
-      run: ./config --strict-warnings --banner=Configured --debug -DPEDANTIC -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION enable-asan enable-ubsan enable-rc5 enable-md2 enable-ec_nistp_64_gcc_128 enable-weak-ssl-ciphers enable-nextprotoneg && perl configdata.pm --dump
+      run: ./config --strict-warnings --banner=Configured --debug -DPEDANTIC -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION enable-asan enable-ec_explicit_curves enable-ubsan enable-rc5 enable-md2 enable-ec_nistp_64_gcc_128 enable-weak-ssl-ciphers enable-nextprotoneg && perl configdata.pm --dump
     - name: make
       run: make -s -j4
     - name: get cpu info
@@ -474,7 +474,7 @@ jobs:
         sudo sysctl -w vm.mmap_rnd_bits=28
     - name: config
       # --debug -O1 is to produce a debug build that runs in a reasonable amount of time
-      run: CC=clang ./config --strict-warnings --banner=Configured --debug no-shared -O1 -fsanitize=memory -DOSSL_SANITIZE_MEMORY -fno-optimize-sibling-calls enable-rc5 enable-md2 enable-ec_nistp_64_gcc_128 enable-fips enable-lms no-slh-dsa && perl configdata.pm --dump
+      run: CC=clang ./config --strict-warnings --banner=Configured --debug no-shared -O1 -fsanitize=memory -DOSSL_SANITIZE_MEMORY -fno-optimize-sibling-calls enable-rc5 enable-md2 enable-ec_nistp_64_gcc_128 enable-ec_explicit_curves enable-fips enable-lms no-slh-dsa && perl configdata.pm --dump
     - name: make
       run: make -s -j4
     - name: get cpu info
@@ -563,7 +563,7 @@ jobs:
     - name: install extra config support
       run: sudo apt-get -y install libsctp-dev abigail-tools libzstd-dev zstd
     - name: config
-      run: ./config --strict-warnings --banner=Configured enable-demos enable-h3demo enable-ktls enable-fips enable-lms enable-egd enable-ec_nistp_64_gcc_128 enable-md2 enable-rc5 enable-sctp enable-weak-ssl-ciphers enable-trace enable-zlib enable-zstd && perl configdata.pm --dump
+      run: ./config --strict-warnings --banner=Configured enable-demos enable-h3demo enable-ec_explicit_curves enable-ktls enable-fips enable-lms enable-egd enable-ec_nistp_64_gcc_128 enable-md2 enable-rc5 enable-sctp enable-weak-ssl-ciphers enable-trace enable-zlib enable-zstd && perl configdata.pm --dump
     - name: make
       run: make -s -j4
     - name: get cpu info
@@ -613,7 +613,7 @@ jobs:
     - name: checkout fuzz/corpora submodule
       run: git submodule update --init --depth 1 fuzz/corpora
     - name: config
-      run: ./config --strict-warnings --banner=Configured --debug enable-demos enable-h3demo no-shared enable-crypto-mdebug enable-rc5 enable-md2 enable-weak-ssl-ciphers enable-zlib enable-ec_nistp_64_gcc_128 no-fips && perl configdata.pm --dump
+      run: ./config --strict-warnings --banner=Configured --debug enable-demos enable-h3demo no-shared enable-crypto-mdebug enable-rc5 enable-md2 enable-weak-ssl-ciphers enable-zlib enable-ec_nistp_64_gcc_128 enable-ec_explicit_curves no-fips && perl configdata.pm --dump
     - name: make
       run: make -s -j4
     - name: get cpu info
diff --git a/CHANGES.md b/CHANGES.md
index ff960d0dcc..dfe4b0d2d3 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -45,6 +45,12 @@ OpenSSL 4.0

    *Shane Lontis*

+ * Support of explicit EC curves was disabled by default, an error will occur if
+   an explicit EC curve doesn't match any known one. New configuration option,
+   `enable-ec_explicit_curves` is added.
+
+   *Dmitry Belyavskiy*
+
  * Removed configure options can now only be disabled. You may continue to use
    `disable-<feature>`, which will remain supported. Using `enable-<feature>`
    for a removed feature is no longer permitted.
diff --git a/Configure b/Configure
index 3b8f2e4587..7682185697 100755
--- a/Configure
+++ b/Configure
@@ -466,6 +466,7 @@ my @disablables = (
     "dtls",
     "ec",
     "ec2m",
+    "ec_explicit_curves",
     "ec_nistp_64_gcc_128",
     "ecdh",
     "ecdsa",
@@ -601,6 +602,7 @@ our %disabled = ( # "what"         => "comment"
                   "demos"               => "default",
                   "h3demo"              => "default",
                   "hqinterop"           => "default",
+                  "ec_explicit_curves" => "default",
                   "ec_nistp_64_gcc_128" => "default",
                   "egd"                 => "default",
                   "engine"              => "default",
@@ -654,7 +656,7 @@ my @disable_cascades = (
     "zstd"              => [ "zstd-dynamic" ],
     "des"               => [ "mdc2" ],
     "deprecated"        => [ "tls-deprecated-ec" ],
-    "ec"                => [ qw(ec2m ecdsa ecdh sm2 gost ecx tls-deprecated-ec) ],
+    "ec"                => [ qw(ec2m ec_explicit_curves ecdsa ecdh sm2 gost ecx tls-deprecated-ec) ],
     "dgram"             => [ "dtls", "quic", "sctp" ],
     "sock"              => [ "dgram", "tfo" ],
     "dtls"              => [ @dtls ],
diff --git a/INSTALL.md b/INSTALL.md
index 9884caabe9..252bc13e7c 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -771,6 +771,12 @@ Disable legacy TLS EC groups that were deprecated in RFC8422.  These are the
 Koblitz curves, B<secp160r1>, B<secp160r2>, B<secp192r1>, B<secp224r1>, and the
 binary Elliptic curves that would also be disabled by C<no-ec2m>.

+### enable-ec_expicit_curves
+
+Enable support for explictitly specified elliptic curves not matching the
+well-known ones. Until this option is on, such curves can't be instantiated
+from ASN.1 formats.
+
 ### enable-ec_nistp_64_gcc_128

 Enable support for optimised implementations of some commonly used NIST
diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c
index 01a3933713..c6deacc1a7 100644
--- a/crypto/ec/ec_asn1.c
+++ b/crypto/ec/ec_asn1.c
@@ -888,6 +888,14 @@ EC_GROUP *d2i_ECPKParameters(EC_GROUP **a, const unsigned char **in, long len)

     if (params->type == ECPKPARAMETERS_TYPE_EXPLICIT)
         group->decoded_from_explicit_params = 1;
+#ifdef OPENSSL_NO_EC_EXPLICIT_CURVES
+    if (EC_GROUP_check_named_curve(group, 0, NULL) == NID_undef) {
+        EC_GROUP_free(group);
+        ECPKPARAMETERS_free(params);
+        ERR_raise(ERR_LIB_EC, EC_R_UNKNOWN_GROUP);
+        return NULL;
+    }
+#endif

     if (a) {
         EC_GROUP_free(*a);
@@ -948,6 +956,13 @@ EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len)
         goto err;
     }

+#ifdef OPENSSL_NO_EC_EXPLICIT_CURVES
+    if (EC_GROUP_check_named_curve(ret->group, 0, NULL) == NID_undef) {
+        ERR_raise(ERR_LIB_EC, EC_R_UNKNOWN_GROUP);
+        goto err;
+    }
+#endif
+
     ret->version = priv_key->version;

     if (priv_key->privateKey) {
diff --git a/crypto/ec/ec_lib.c b/crypto/ec/ec_lib.c
index b041ecd0f7..079a02b59f 100644
--- a/crypto/ec/ec_lib.c
+++ b/crypto/ec/ec_lib.c
@@ -1577,7 +1577,9 @@ EC_GROUP *EC_GROUP_new_from_params(const OSSL_PARAM params[],
     int is_prime_field = 1;
     BN_CTX *bnctx = NULL;
     const unsigned char *buf = NULL;
+#ifndef OPENSSL_NO_EC_EXPLICIT_CURVES
     int encoding_flag = -1;
+#endif
 #endif

     /* This is the simple named group case */
@@ -1752,6 +1754,12 @@ EC_GROUP *EC_GROUP_new_from_params(const OSSL_PARAM params[],
         goto err;
     }
     if (named_group == group) {
+#ifdef OPENSSL_NO_EC_EXPLICIT_CURVES
+        if (EC_GROUP_check_named_curve(group, 0, NULL) == NID_undef) {
+            ERR_raise(ERR_LIB_EC, EC_R_UNKNOWN_GROUP);
+            goto err;
+        }
+#else
         /*
          * If we did not find a named group then the encoding should be explicit
          * if it was specified
@@ -1767,6 +1775,7 @@ EC_GROUP *EC_GROUP_new_from_params(const OSSL_PARAM params[],
             goto err;
         }
         EC_GROUP_set_asn1_flag(group, OPENSSL_EC_EXPLICIT_CURVE);
+#endif
     } else {
         EC_GROUP_free(group);
         group = named_group;
diff --git a/test/ectest.c b/test/ectest.c
index 9fca45bad4..c4d2c97ea7 100644
--- a/test/ectest.c
+++ b/test/ectest.c
@@ -2555,6 +2555,7 @@ err:
     return r;
 }

+#ifndef OPENSSL_NO_EC_EXPLICIT_CURVES
 /*-
  * random 256-bit explicit parameters curve, cofactor absent
  * order:    0x0c38d96a9f892b88772ec2e39614a82f4f (132 bit)
@@ -2644,6 +2645,7 @@ err:
     EC_GROUP_free(group);
     return ret;
 }
+#endif

 /*-
  * For named curves, test that:
@@ -2887,23 +2889,28 @@ err:
 static int do_test_custom_explicit_fromdata(EC_GROUP *group, BN_CTX *ctx,
     unsigned char *gen, size_t gen_size)
 {
-    int ret = 0, i_out;
+    int ret = 0;
     EVP_PKEY_CTX *pctx = NULL;
     EVP_PKEY *pkeyparam = NULL;
     OSSL_PARAM_BLD *bld = NULL;
     const char *field_name;
     OSSL_PARAM *params = NULL;
-    const OSSL_PARAM *gettable;
     BIGNUM *p, *a, *b;
     BIGNUM *p_out = NULL, *a_out = NULL, *b_out = NULL;
     BIGNUM *order_out = NULL, *cofactor_out = NULL;
+#ifndef OPENSSL_NO_EC_EXPLICIT_CURVES
+    const OSSL_PARAM *gettable;
+    int i_out;
     char name[80];
     unsigned char buf[1024];
     size_t buf_len, name_len;
 #ifndef OPENSSL_NO_EC2M
-    unsigned int k1 = 0, k2 = 0, k3 = 0;
     const char *basis_name = NULL;
 #endif
+#endif
+#ifndef OPENSSL_NO_EC2M
+    unsigned int k1 = 0, k2 = 0, k3 = 0;
+#endif

     p = BN_CTX_get(ctx);
     a = BN_CTX_get(ctx);
@@ -2919,11 +2926,15 @@ static int do_test_custom_explicit_fromdata(EC_GROUP *group, BN_CTX *ctx,
         field_name = SN_X9_62_characteristic_two_field;
 #ifndef OPENSSL_NO_EC2M
         if (EC_GROUP_get_basis_type(group) == NID_X9_62_tpBasis) {
+#ifndef OPENSSL_NO_EC_EXPLICIT_CURVES
             basis_name = SN_X9_62_tpBasis;
+#endif
             if (!TEST_true(EC_GROUP_get_trinomial_basis(group, &k1)))
                 goto err;
         } else {
+#ifndef OPENSSL_NO_EC_EXPLICIT_CURVES
             basis_name = SN_X9_62_ppBasis;
+#endif
             if (!TEST_true(EC_GROUP_get_pentanomial_basis(group, &k1, &k2, &k3)))
                 goto err;
         }
@@ -2958,11 +2969,19 @@ static int do_test_custom_explicit_fromdata(EC_GROUP *group, BN_CTX *ctx,
     if (!TEST_ptr(params = OSSL_PARAM_BLD_to_param(bld))
         || !TEST_ptr(pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL))
         || !TEST_int_gt(EVP_PKEY_fromdata_init(pctx), 0)
+#ifdef OPENSSL_NO_EC_EXPLICIT_CURVES
+        || !TEST_int_le(EVP_PKEY_fromdata(pctx, &pkeyparam,
+                            EVP_PKEY_KEY_PARAMETERS, params),
+            0)
+#else
         || !TEST_int_gt(EVP_PKEY_fromdata(pctx, &pkeyparam,
                             EVP_PKEY_KEY_PARAMETERS, params),
-            0))
+            0)
+#endif
+    )
         goto err;

+#ifndef OPENSSL_NO_EC_EXPLICIT_CURVES
     /*- Check that all the set values are retrievable -*/

     /* There should be no match to a group name since the generator changed */
@@ -3096,6 +3115,7 @@ static int do_test_custom_explicit_fromdata(EC_GROUP *group, BN_CTX *ctx,
 #endif
     )
         goto err;
+#endif
     ret = 1;
 err:
     BN_free(order_out);
@@ -3218,12 +3238,15 @@ static int custom_params_test(int id)
     EC_KEY *eckey1 = NULL, *eckey2 = NULL;
     EVP_PKEY *pkey1 = NULL, *pkey2 = NULL;
     EVP_PKEY_CTX *pctx1 = NULL, *pctx2 = NULL, *dctx = NULL;
-    size_t sslen, t, bsize;
+    size_t bsize;
     unsigned char *pub1 = NULL, *pub2 = NULL;
     OSSL_PARAM_BLD *param_bld = NULL;
     OSSL_PARAM *params1 = NULL, *params2 = NULL;
+#ifndef OPENSSL_NO_EC_EXPLICIT_CURVES
     const unsigned char *export = NULL;
     size_t export_size = 0;
+    size_t sslen, t;
+#endif
     EVP_SKEY *skey = NULL;

     /* Do some setup */
@@ -3391,6 +3414,16 @@ static int custom_params_test(int id)
         goto err;
     eckey2 = NULL; /* ownership passed to pkey2 */

+#ifdef OPENSSL_NO_EC_EXPLICIT_CURVES
+    /* Compute keyexchange in both directions - fail with custom params */
+    if (!TEST_ptr(pctx1 = EVP_PKEY_CTX_new(pkey1, NULL))
+        || !TEST_int_le(EVP_PKEY_derive_init(pctx1), 0))
+        goto err;
+    if (!TEST_ptr(pctx2 = EVP_PKEY_CTX_new(pkey2, NULL))
+        || !TEST_int_le(EVP_PKEY_derive_init(pctx2), 0))
+        goto err;
+
+#else
     /* Compute keyexchange in both directions */
     if (!TEST_ptr(pctx1 = EVP_PKEY_CTX_new(pkey1, NULL))
         || !TEST_int_eq(EVP_PKEY_derive_init(pctx1), 1)
@@ -3474,7 +3507,7 @@ static int custom_params_test(int id)
         /* compare with previous result */
         || !TEST_mem_eq(export, export_size, buf2, sslen))
         goto err;
-
+#endif
     ret = 1;

 err:
@@ -3561,7 +3594,9 @@ int setup_tests(void)

     ADD_TEST(parameter_test);
     ADD_TEST(ossl_parameter_test);
+#ifndef OPENSSL_NO_EC_EXPLICIT_CURVES
     ADD_TEST(cofactor_range_test);
+#endif
     ADD_ALL_TESTS(cardinality_test, (int)crv_len);
     ADD_TEST(prime_field_tests);
 #ifndef OPENSSL_NO_EC2M
diff --git a/test/evp_test.c b/test/evp_test.c
index c8920b6f73..7ce3be9779 100644
--- a/test/evp_test.c
+++ b/test/evp_test.c
@@ -5433,8 +5433,16 @@ top:
     pkey = NULL;
 start:
     if (strcmp(pp->key, "PrivateKey") == 0) {
+        int unsupported = 0;
+
         pkey = PEM_read_bio_PrivateKey_ex(t->s.key, NULL, 0, NULL, libctx, NULL);
-        if (pkey == NULL && !key_unsupported()) {
+        if (pkey == NULL)
+            unsupported = key_unsupported();
+#ifdef OPENSSL_NO_EC_EXPLICIT_CURVES
+        if (strcmp(pp->value, "EC_EXPLICIT") == 0)
+            unsupported = 1;
+#endif
+        if (pkey == NULL && !unsupported) {
             EVP_PKEY_free(pkey);
             TEST_info("Can't read private key %s", pp->value);
             TEST_openssl_errors();