[openssl] master update

shane.lontis at oracle.com shane.lontis at oracle.com
Sat Aug 22 04:57:40 UTC 2020


The branch master has been updated
       via  c0f39ded68ba0929698a8773e63e9806ec9e5c74 (commit)
      from  a02c715c183382aa3038fc4d7d463b17e62a24ff (commit)


- Log -----------------------------------------------------------------
commit c0f39ded68ba0929698a8773e63e9806ec9e5c74
Author: Shane Lontis <shane.lontis at oracle.com>
Date:   Sat Aug 22 14:55:41 2020 +1000

    Add Explicit EC parameter support to providers.
    
    This was added for backward compatability.
    Added EC_GROUP_new_from_params() that supports explicit curve parameters.
    
    This fixes the 15-test_genec.t TODO.
    
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/12604)

-----------------------------------------------------------------------

Summary of changes:
 crypto/ec/ec_ameth.c                               |  62 +---
 crypto/ec/ec_asn1.c                                |  71 +---
 crypto/ec/ec_backend.c                             | 205 ++++++++--
 crypto/ec/ec_err.c                                 |   8 +
 crypto/ec/ec_lib.c                                 | 411 ++++++++++++++++++++-
 crypto/err/openssl.txt                             |   7 +
 crypto/evp/pmeth_lib.c                             |  20 +-
 doc/man3/EC_GROUP_new.pod                          |  18 +-
 doc/man3/EVP_PKEY_fromdata.pod                     |   2 +-
 doc/man7/EVP_PKEY-EC.pod                           |  84 ++++-
 include/crypto/ec.h                                |   6 +-
 include/openssl/core.h                             |   6 +-
 include/openssl/core_names.h                       |  22 ++
 include/openssl/ec.h                               |  14 +
 include/openssl/ecerr.h                            |   7 +
 .../implementations/encode_decode/encoder_ec.c     | 190 ++++++++--
 providers/implementations/keymgmt/ec_kmgmt.c       | 391 +++++++++++++++-----
 test/ectest.c                                      | 224 ++++++++++-
 test/endecode_test.c                               | 264 ++++++++++++-
 test/recipes/15-test_genec.t                       |  22 +-
 util/libcrypto.num                                 |   1 +
 21 files changed, 1709 insertions(+), 326 deletions(-)

diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c
index 6cd0bf6279..8840d57188 100644
--- a/crypto/ec/ec_ameth.c
+++ b/crypto/ec/ec_ameth.c
@@ -606,43 +606,6 @@ size_t ec_pkey_dirty_cnt(const EVP_PKEY *pkey)
     return pkey->pkey.ec->dirty_cnt;
 }
 
-static ossl_inline
-int ecparams_to_params(const EC_KEY *eckey, OSSL_PARAM_BLD *tmpl)
-{
-    const EC_GROUP *ecg;
-    int curve_nid;
-
-    if (eckey == NULL)
-        return 0;
-
-    ecg = EC_KEY_get0_group(eckey);
-    if (ecg == NULL)
-        return 0;
-
-    curve_nid = EC_GROUP_get_curve_name(ecg);
-
-    if (curve_nid == NID_undef) {
-        /* explicit parameters */
-
-        /*
-         * TODO(3.0): should we support explicit parameters curves?
-         */
-        return 0;
-    } else {
-        /* named curve */
-        const char *curve_name = NULL;
-
-        if ((curve_name = OBJ_nid2sn(curve_nid)) == NULL)
-            return 0;
-
-        if (!OSSL_PARAM_BLD_push_utf8_string(tmpl, OSSL_PKEY_PARAM_GROUP_NAME,
-                                             curve_name, 0))
-            return 0;
-    }
-
-    return 1;
-}
-
 static
 int ec_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
                       EVP_KEYMGMT *to_keymgmt, OPENSSL_CTX *libctx,
@@ -650,7 +613,7 @@ int ec_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
 {
     const EC_KEY *eckey = NULL;
     const EC_GROUP *ecg = NULL;
-    unsigned char *pub_key_buf = NULL;
+    unsigned char *pub_key_buf = NULL, *gen_buf = NULL;
     size_t pub_key_buflen;
     OSSL_PARAM_BLD *tmpl;
     OSSL_PARAM *params = NULL;
@@ -676,8 +639,17 @@ int ec_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     if (tmpl == NULL)
         return 0;
 
+    /*
+     * EC_POINT_point2buf() can generate random numbers in some
+     * implementations so we need to ensure we use the correct libctx.
+     */
+    bnctx = BN_CTX_new_ex(libctx);
+    if (bnctx == NULL)
+        goto err;
+    BN_CTX_start(bnctx);
+
     /* export the domain parameters */
-    if (!ecparams_to_params(eckey, tmpl))
+    if (!ec_group_todata(ecg, tmpl, NULL, libctx, propq, bnctx, &gen_buf))
         goto err;
     selection |= OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
 
@@ -685,14 +657,6 @@ int ec_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     pub_point = EC_KEY_get0_public_key(eckey);
 
     if (pub_point != NULL) {
-        /*
-         * EC_POINT_point2buf() can generate random numbers in some
-         * implementations so we need to ensure we use the correct libctx.
-         */
-        bnctx = BN_CTX_new_ex(libctx);
-        if (bnctx == NULL)
-            goto err;
-
         /* convert pub_point to a octet string according to the SECG standard */
         if ((pub_key_buflen = EC_POINT_point2buf(ecg, pub_point,
                                                  POINT_CONVERSION_COMPRESSED,
@@ -779,6 +743,8 @@ int ec_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     OSSL_PARAM_BLD_free(tmpl);
     OSSL_PARAM_BLD_free_params(params);
     OPENSSL_free(pub_key_buf);
+    OPENSSL_free(gen_buf);
+    BN_CTX_end(bnctx);
     BN_CTX_free(bnctx);
     return rv;
 }
@@ -794,7 +760,7 @@ static int ec_pkey_import_from(const OSSL_PARAM params[], void *vpctx)
         return 0;
     }
 
-    if (!ec_key_domparams_fromdata(ec, params)
+    if (!ec_group_fromdata(ec, params)
         || !ec_key_otherparams_fromdata(ec, params)
         || !ec_key_fromdata(ec, params, 1)
         || !EVP_PKEY_assign_EC_KEY(pkey, ec)) {
diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c
index 654a12ad60..879ff9faa2 100644
--- a/crypto/ec/ec_asn1.c
+++ b/crypto/ec/ec_asn1.c
@@ -23,75 +23,6 @@
 
 #ifndef FIPS_MODULE
 
-int EC_GROUP_get_basis_type(const EC_GROUP *group)
-{
-    int i;
-
-    if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field)
-        /* everything else is currently not supported */
-        return 0;
-
-    /* Find the last non-zero element of group->poly[] */
-    for (i = 0;
-         i < (int)OSSL_NELEM(group->poly) && group->poly[i] != 0;
-         i++)
-        continue;
-
-    if (i == 4)
-        return NID_X9_62_ppBasis;
-    else if (i == 2)
-        return NID_X9_62_tpBasis;
-    else
-        /* everything else is currently not supported */
-        return 0;
-}
-
-#ifndef OPENSSL_NO_EC2M
-int EC_GROUP_get_trinomial_basis(const EC_GROUP *group, unsigned int *k)
-{
-    if (group == NULL)
-        return 0;
-
-    if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field
-        || !((group->poly[0] != 0) && (group->poly[1] != 0)
-             && (group->poly[2] == 0))) {
-        ECerr(EC_F_EC_GROUP_GET_TRINOMIAL_BASIS,
-              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-        return 0;
-    }
-
-    if (k)
-        *k = group->poly[1];
-
-    return 1;
-}
-
-int EC_GROUP_get_pentanomial_basis(const EC_GROUP *group, unsigned int *k1,
-                                   unsigned int *k2, unsigned int *k3)
-{
-    if (group == NULL)
-        return 0;
-
-    if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field
-        || !((group->poly[0] != 0) && (group->poly[1] != 0)
-             && (group->poly[2] != 0) && (group->poly[3] != 0)
-             && (group->poly[4] == 0))) {
-        ECerr(EC_F_EC_GROUP_GET_PENTANOMIAL_BASIS,
-              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-        return 0;
-    }
-
-    if (k1)
-        *k1 = group->poly[3];
-    if (k2)
-        *k2 = group->poly[2];
-    if (k3)
-        *k3 = group->poly[1];
-
-    return 1;
-}
-#endif
-
 /* some structures needed for the asn1 encoding */
 typedef struct x9_62_pentanomial_st {
     int32_t k1;
@@ -444,7 +375,7 @@ static int ec_asn1_group2curve(const EC_GROUP *group, X9_62_CURVE *curve)
 }
 
 ECPARAMETERS *EC_GROUP_get_ecparameters(const EC_GROUP *group,
-                                               ECPARAMETERS *params)
+                                        ECPARAMETERS *params)
 {
     size_t len = 0;
     ECPARAMETERS *ret = NULL;
diff --git a/crypto/ec/ec_backend.c b/crypto/ec/ec_backend.c
index 2277c2c724..1599e2b1f3 100644
--- a/crypto/ec/ec_backend.c
+++ b/crypto/ec/ec_backend.c
@@ -10,15 +10,178 @@
 #include <openssl/core_names.h>
 #include <openssl/objects.h>
 #include <openssl/params.h>
+#include <openssl/err.h>
 #include "crypto/bn.h"
 #include "crypto/ec.h"
+#include "ec_local.h"
+#include "e_os.h"
+#include "internal/param_build_set.h"
+
+/* Mapping between a flag and a name */
+static const OSSL_ITEM encoding_nameid_map[] = {
+    { OPENSSL_EC_EXPLICIT_CURVE, OSSL_PKEY_EC_ENCODING_EXPLICIT },
+    { OPENSSL_EC_NAMED_CURVE, OSSL_PKEY_EC_ENCODING_GROUP },
+};
+
+int ec_encoding_name2id(const char *name)
+{
+    size_t i, sz;
+
+    /* Return the default value if there is no name */
+    if (name == NULL)
+        return OPENSSL_EC_NAMED_CURVE;
+
+    for (i = 0, sz = OSSL_NELEM(encoding_nameid_map); i < sz; i++) {
+        if (strcasecmp(name, encoding_nameid_map[i].ptr) == 0)
+            return encoding_nameid_map[i].id;
+    }
+    return -1;
+}
+
+static char *ec_param_encoding_id2name(int id)
+{
+    size_t i, sz;
+
+    for (i = 0, sz = OSSL_NELEM(encoding_nameid_map); i < sz; i++) {
+        if (id == (int)encoding_nameid_map[i].id)
+            return encoding_nameid_map[i].ptr;
+    }
+    return NULL;
+}
+
+int ec_group_todata(const EC_GROUP *group, OSSL_PARAM_BLD *tmpl,
+                    OSSL_PARAM params[], OPENSSL_CTX *libctx, const char *propq,
+                    BN_CTX *bnctx, unsigned char **genbuf)
+{
+    int ret = 0, curve_nid, encoding_flag;
+    const char *field_type, *encoding_name;
+    const BIGNUM *cofactor, *order;
+    BIGNUM *p = NULL, *a = NULL, *b = NULL;
+    point_conversion_form_t genform;
+    const EC_POINT *genpt;
+    unsigned char *seed = NULL;
+    size_t genbuf_len, seed_len;
+
+    if (group == NULL) {
+        ECerr(0,EC_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    encoding_flag = EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE;
+    encoding_name = ec_param_encoding_id2name(encoding_flag);
+    if (encoding_name == NULL
+        || !ossl_param_build_set_utf8_string(tmpl, params,
+                                             OSSL_PKEY_PARAM_EC_ENCODING,
+                                             encoding_name)) {
+        ECerr(0, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+
+    curve_nid = EC_GROUP_get_curve_name(group);
+    if (curve_nid == NID_undef) {
+        /* explicit curve */
+        int fid = EC_GROUP_get_field_type(group);
+
+        if (fid == NID_X9_62_prime_field) {
+            field_type = SN_X9_62_prime_field;
+        } else if (fid == NID_X9_62_characteristic_two_field) {
+            field_type = SN_X9_62_characteristic_two_field;
+        } else {
+            ECerr(0, EC_R_INVALID_FIELD);
+            return 0;
+        }
+
+        p = BN_CTX_get(bnctx);
+        a = BN_CTX_get(bnctx);
+        b = BN_CTX_get(bnctx);
+        if (b == NULL) {
+            ECerr(0, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        if (!EC_GROUP_get_curve(group, p, a, b, bnctx)) {
+            ECerr(0, EC_R_INVALID_CURVE);
+            goto err;
+        }
+
+        order = EC_GROUP_get0_order(group);
+        if (order == NULL) {
+            ECerr(0, EC_R_INVALID_GROUP_ORDER);
+            goto err;
+        }
+        genpt = EC_GROUP_get0_generator(group);
+        if (genpt == NULL) {
+            ECerr(0, EC_R_INVALID_GENERATOR);
+            goto err;
+        }
+        genform = EC_GROUP_get_point_conversion_form(group);
+        genbuf_len = EC_POINT_point2buf(group, genpt, genform, genbuf, bnctx);
+        if (genbuf_len == 0) {
+            ECerr(0, EC_R_INVALID_GENERATOR);
+            goto err;
+        }
+
+        if (!ossl_param_build_set_utf8_string(tmpl, params,
+                                              OSSL_PKEY_PARAM_EC_FIELD_TYPE,
+                                              field_type)
+            || !ossl_param_build_set_bn(tmpl, params, OSSL_PKEY_PARAM_EC_P, p)
+            || !ossl_param_build_set_bn(tmpl, params, OSSL_PKEY_PARAM_EC_A, a)
+            || !ossl_param_build_set_bn(tmpl, params, OSSL_PKEY_PARAM_EC_B, b)
+            || !ossl_param_build_set_bn(tmpl, params, OSSL_PKEY_PARAM_EC_ORDER,
+                                        order)
+            || !ossl_param_build_set_octet_string(tmpl, params,
+                                                  OSSL_PKEY_PARAM_EC_GENERATOR,
+                                                  *genbuf, genbuf_len)) {
+            ECerr(0, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        cofactor = EC_GROUP_get0_cofactor(group);
+        if (cofactor != NULL
+            && !ossl_param_build_set_bn(tmpl, params,
+                                        OSSL_PKEY_PARAM_EC_COFACTOR, cofactor)) {
+            ECerr(0, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        seed = EC_GROUP_get0_seed(group);
+        seed_len = EC_GROUP_get_seed_len(group);
+        if (seed != NULL
+            && seed_len > 0
+            && !ossl_param_build_set_octet_string(tmpl, params,
+                                                  OSSL_PKEY_PARAM_EC_SEED,
+                                                  seed, seed_len)) {
+            ECerr(0, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+#ifdef OPENSSL_NO_EC2M
+        if (fid == NID_X9_62_characteristic_two_field) {
+            ECerr(0, EC_R_GF2M_NOT_SUPPORTED);
+            goto err;
+        }
+#endif
+    } else {
+        /* named curve */
+        const char *curve_name = curve_name = ec_curve_nid2name(curve_nid);
+
+        if (curve_name == NULL
+            || !ossl_param_build_set_utf8_string(tmpl, params,
+                                                 OSSL_PKEY_PARAM_GROUP_NAME,
+                                                 curve_name)) {
+            ECerr(0, EC_R_INVALID_CURVE);
+            goto err;
+        }
+    }
+    ret = 1;
+err:
+    return ret;
+}
 
 /*
  * The intention with the "backend" source file is to offer backend support
  * for legacy backends (EVP_PKEY_ASN1_METHOD and EVP_PKEY_METHOD) and provider
  * implementations alike.
  */
-
 int ec_set_ecdh_cofactor_mode(EC_KEY *ec, int mode)
 {
     const EC_GROUP *ecg = EC_KEY_get0_group(ec);
@@ -163,52 +326,27 @@ int ec_key_fromdata(EC_KEY *ec, const OSSL_PARAM params[], int include_private)
     return ok;
 }
 
-int ec_key_domparams_fromdata(EC_KEY *ec, const OSSL_PARAM params[])
+int ec_group_fromdata(EC_KEY *ec, const OSSL_PARAM params[])
 {
-    const OSSL_PARAM *param_ec_name;
-    EC_GROUP *ecg = NULL;
-    char *curve_name = NULL;
     int ok = 0;
+    EC_GROUP *group = NULL;
 
     if (ec == NULL)
         return 0;
 
-    param_ec_name = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
-    if (param_ec_name == NULL) {
-        /* explicit parameters */
-
-        /*
-         * TODO(3.0): should we support explicit parameters curves?
-         */
-        return 0;
-    } else {
-        /* named curve */
-        int curve_nid;
-
-        if (!OSSL_PARAM_get_utf8_string(param_ec_name, &curve_name, 0)
-                || curve_name == NULL
-                || (curve_nid = ec_curve_name2nid(curve_name)) == NID_undef)
-            goto err;
-
-        if ((ecg = EC_GROUP_new_by_curve_name_with_libctx(ec_key_get_libctx(ec),
-                                                          ec_key_get0_propq(ec),
-                                                          curve_nid)) == NULL)
-            goto err;
-    }
+     group = EC_GROUP_new_from_params(params, ec_key_get_libctx(ec),
+                                      ec_key_get0_propq(ec));
 
-    if (!EC_KEY_set_group(ec, ecg))
+    if (!EC_KEY_set_group(ec, group))
         goto err;
 
     /*
      * TODO(3.0): if the group has changed, should we invalidate the private and
      * public key?
      */
-
     ok = 1;
-
- err:
-    OPENSSL_free(curve_name);
-    EC_GROUP_free(ecg);
+err:
+    EC_GROUP_free(group);
     return ok;
 }
 
@@ -227,5 +365,6 @@ int ec_key_otherparams_fromdata(EC_KEY *ec, const OSSL_PARAM params[])
             || !ec_set_ecdh_cofactor_mode(ec, mode))
             return 0;
     }
+
     return 1;
 }
diff --git a/crypto/ec/ec_err.c b/crypto/ec/ec_err.c
index afb2696285..7112cbc21f 100644
--- a/crypto/ec/ec_err.c
+++ b/crypto/ec/ec_err.c
@@ -44,7 +44,10 @@ static const ERR_STRING_DATA EC_str_reasons[] = {
     "i2d ecpkparameters failure"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INCOMPATIBLE_OBJECTS),
     "incompatible objects"},
+    {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_A), "invalid a"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_ARGUMENT), "invalid argument"},
+    {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_B), "invalid b"},
+    {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_COFACTOR), "invalid cofactor"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_COMPRESSED_POINT),
     "invalid compressed point"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_COMPRESSION_BIT),
@@ -55,14 +58,19 @@ static const ERR_STRING_DATA EC_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_ENCODING), "invalid encoding"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_FIELD), "invalid field"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_FORM), "invalid form"},
+    {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_GENERATOR), "invalid generator"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_GROUP_ORDER), "invalid group order"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_KEY), "invalid key"},
+    {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_NAMED_GROUP_CONVERSION),
+    "invalid named group conversion"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_OUTPUT_LENGTH),
     "invalid output length"},
+    {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_P), "invalid p"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_PEER_KEY), "invalid peer key"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_PENTANOMIAL_BASIS),
     "invalid pentanomial basis"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_PRIVATE_KEY), "invalid private key"},
+    {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_SEED), "invalid seed"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_INVALID_TRINOMIAL_BASIS),
     "invalid trinomial basis"},
     {ERR_PACK(ERR_LIB_EC, 0, EC_R_KDF_PARAMETER_ERROR), "kdf parameter error"},
diff --git a/crypto/ec/ec_lib.c b/crypto/ec/ec_lib.c
index a0c007a13e..d9298f62d0 100644
--- a/crypto/ec/ec_lib.c
+++ b/crypto/ec/ec_lib.c
@@ -15,11 +15,14 @@
 #include "internal/deprecated.h"
 
 #include <string.h>
-
+#include <openssl/params.h>
+#include <openssl/core_names.h>
 #include <openssl/err.h>
 #include <openssl/opensslv.h>
-
+#include "crypto/ec.h"
+#include "internal/nelem.h"
 #include "ec_local.h"
+#include "e_os.h" /* strcasecmp */
 
 /* functions for EC_GROUP objects */
 
@@ -1317,3 +1320,407 @@ int ec_point_blind_coordinates(const EC_GROUP *group, EC_POINT *p, BN_CTX *ctx)
 
     return group->meth->blind_coordinates(group, p, ctx);
 }
+
+int EC_GROUP_get_basis_type(const EC_GROUP *group)
+{
+    int i;
+
+    if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field)
+        /* everything else is currently not supported */
+        return 0;
+
+    /* Find the last non-zero element of group->poly[] */
+    for (i = 0;
+         i < (int)OSSL_NELEM(group->poly) && group->poly[i] != 0;
+         i++)
+        continue;
+
+    if (i == 4)
+        return NID_X9_62_ppBasis;
+    else if (i == 2)
+        return NID_X9_62_tpBasis;
+    else
+        /* everything else is currently not supported */
+        return 0;
+}
+
+#ifndef OPENSSL_NO_EC2M
+int EC_GROUP_get_trinomial_basis(const EC_GROUP *group, unsigned int *k)
+{
+    if (group == NULL)
+        return 0;
+
+    if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field
+        || !((group->poly[0] != 0) && (group->poly[1] != 0)
+             && (group->poly[2] == 0))) {
+        ECerr(EC_F_EC_GROUP_GET_TRINOMIAL_BASIS,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    if (k)
+        *k = group->poly[1];
+
+    return 1;
+}
+
+int EC_GROUP_get_pentanomial_basis(const EC_GROUP *group, unsigned int *k1,
+                                   unsigned int *k2, unsigned int *k3)
+{
+    if (group == NULL)
+        return 0;
+
+    if (EC_GROUP_get_field_type(group) != NID_X9_62_characteristic_two_field
+        || !((group->poly[0] != 0) && (group->poly[1] != 0)
+             && (group->poly[2] != 0) && (group->poly[3] != 0)
+             && (group->poly[4] == 0))) {
+        ECerr(EC_F_EC_GROUP_GET_PENTANOMIAL_BASIS,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    if (k1)
+        *k1 = group->poly[3];
+    if (k2)
+        *k2 = group->poly[2];
+    if (k3)
+        *k3 = group->poly[1];
+
+    return 1;
+}
+#endif
+
+/*
+ * Check if the explicit parameters group matches any built-in curves.
+ *
+ * We create a copy of the group just built, so that we can remove optional
+ * fields for the lookup: we do this to avoid the possibility that one of
+ * the optional parameters is used to force the library into using a less
+ * performant and less secure EC_METHOD instead of the specialized one.
+ * In any case, `seed` is not really used in any computation, while a
+ * cofactor different from the one in the built-in table is just
+ * mathematically wrong anyway and should not be used.
+ */
+static EC_GROUP *ec_group_explicit_to_named(const EC_GROUP *group,
+                                            OPENSSL_CTX *libctx,
+                                            const char *propq,
+                                            BN_CTX *ctx)
+{
+    EC_GROUP *ret_group = NULL, *dup = NULL;
+    int curve_name_nid;
+
+    const EC_POINT *point = EC_GROUP_get0_generator(group);
+    const BIGNUM *order = EC_GROUP_get0_order(group);
+    int no_seed = (EC_GROUP_get0_seed(group) == NULL);
+
+    if ((dup = EC_GROUP_dup(group)) == NULL
+            || EC_GROUP_set_seed(dup, NULL, 0) != 1
+            || !EC_GROUP_set_generator(dup, point, order, NULL))
+        goto err;
+    if ((curve_name_nid = ec_curve_nid_from_params(dup, ctx)) != NID_undef) {
+        /*
+         * The input explicit parameters successfully matched one of the
+         * built-in curves: often for built-in curves we have specialized
+         * methods with better performance and hardening.
+         *
+         * In this case we replace the `EC_GROUP` created through explicit
+         * parameters with one created from a named group.
+         */
+
+#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
+        /*
+         * NID_wap_wsg_idm_ecid_wtls12 and NID_secp224r1 are both aliases for
+         * the same curve, we prefer the SECP nid when matching explicit
+         * parameters as that is associated with a specialized EC_METHOD.
+         */
+        if (curve_name_nid == NID_wap_wsg_idm_ecid_wtls12)
+            curve_name_nid = NID_secp224r1;
+#endif /* !def(OPENSSL_NO_EC_NISTP_64_GCC_128) */
+
+        ret_group = EC_GROUP_new_by_curve_name_with_libctx(libctx, propq,
+                                                           curve_name_nid);
+        if (ret_group == NULL)
+            goto err;
+
+        /*
+         * Set the flag so that EC_GROUPs created from explicit parameters are
+         * serialized using explicit parameters by default.
+         */
+        EC_GROUP_set_asn1_flag(ret_group, OPENSSL_EC_EXPLICIT_CURVE);
+
+        /*
+         * If the input params do not contain the optional seed field we make
+         * sure it is not added to the returned group.
+         *
+         * The seed field is not really used inside libcrypto anyway, and
+         * adding it to parsed explicit parameter keys would alter their DER
+         * encoding output (because of the extra field) which could impact
+         * applications fingerprinting keys by their DER encoding.
+         */
+        if (no_seed) {
+            if (EC_GROUP_set_seed(ret_group, NULL, 0) != 1)
+                goto err;
+        }
+    } else {
+        ret_group = (EC_GROUP *)group;
+    }
+    EC_GROUP_free(dup);
+    return ret_group;
+err:
+    EC_GROUP_free(dup);
+    EC_GROUP_free(ret_group);
+    return NULL;
+}
+
+static int ec_encoding_param2id(const OSSL_PARAM *p, int *id)
+{
+    const char *name = NULL;
+    int status = 0;
+
+    switch (p->data_type) {
+    case OSSL_PARAM_UTF8_STRING:
+        /* The OSSL_PARAM functions have no support for this */
+        name = p->data;
+        status = (name != NULL);
+        break;
+    case OSSL_PARAM_UTF8_PTR:
+        status = OSSL_PARAM_get_utf8_ptr(p, &name);
+        break;
+    }
+    if (status) {
+        int i = ec_encoding_name2id(name);
+
+        if (i >= 0) {
+            *id = i;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static EC_GROUP *group_new_from_name(const OSSL_PARAM *p,
+                                     OPENSSL_CTX *libctx, const char *propq)
+{
+    int ok = 0, nid;
+    const char *curve_name = NULL;
+
+    switch (p->data_type) {
+    case OSSL_PARAM_UTF8_STRING:
+        /* The OSSL_PARAM functions have no support for this */
+        curve_name = p->data;
+        ok = (curve_name != NULL);
+        break;
+    case OSSL_PARAM_UTF8_PTR:
+        ok = OSSL_PARAM_get_utf8_ptr(p, &curve_name);
+        break;
+    }
+
+    if (ok) {
+        nid = ec_curve_name2nid(curve_name);
+        if (nid == NID_undef) {
+            ECerr(0, EC_R_INVALID_CURVE);
+            return NULL;
+        } else {
+            return EC_GROUP_new_by_curve_name_with_libctx(libctx, propq, nid);
+        }
+    }
+    return NULL;
+}
+
+EC_GROUP *EC_GROUP_new_from_params(const OSSL_PARAM params[],
+                                   OPENSSL_CTX *libctx, const char *propq)
+{
+    const OSSL_PARAM *ptmp, *pa, *pb;
+    int ok = 0;
+    EC_GROUP *group = NULL, *named_group = NULL;
+    BIGNUM *p = NULL, *a = NULL, *b = NULL, *order = NULL, *cofactor = NULL;
+    EC_POINT *point = NULL;
+    int field_bits = 0;
+    int is_prime_field = 1;
+    BN_CTX *bnctx = NULL;
+    const unsigned char *buf = NULL;
+    int encoding_flag = -1;
+
+    ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_ENCODING);
+    if (ptmp != NULL && !ec_encoding_param2id(ptmp, &encoding_flag)) {
+        ECerr(0, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+
+    ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
+    if (ptmp != NULL) {
+        group = group_new_from_name(ptmp, libctx, propq);
+        if (group != NULL)
+            EC_GROUP_set_asn1_flag(group, encoding_flag);
+        else
+            ECerr(0, ERR_R_EC_LIB);
+        return group;
+    }
+    bnctx = BN_CTX_new_ex(libctx);
+    if (bnctx == NULL) {
+        ECerr(0, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    BN_CTX_start(bnctx);
+
+    p = BN_CTX_get(bnctx);
+    a = BN_CTX_get(bnctx);
+    b = BN_CTX_get(bnctx);
+    order = BN_CTX_get(bnctx);
+    if (order == NULL) {
+        ECerr(0, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_FIELD_TYPE);
+    if (ptmp == NULL || ptmp->data_type != OSSL_PARAM_UTF8_STRING) {
+        ECerr(0, EC_R_INVALID_FIELD);
+        goto err;
+    }
+    if (strcasecmp(ptmp->data, SN_X9_62_prime_field) == 0) {
+        is_prime_field = 1;
+    } else if (strcasecmp(ptmp->data, SN_X9_62_characteristic_two_field) == 0) {
+        is_prime_field = 0;
+    } else {
+        /* Invalid field */
+        ECerr(0, EC_R_UNSUPPORTED_FIELD);
+        goto err;
+    }
+
+    pa = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_A);
+    if (!OSSL_PARAM_get_BN(pa, &a)) {
+        ECerr(0, EC_R_INVALID_A);
+        goto err;
+    }
+    pb = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_B);
+    if (!OSSL_PARAM_get_BN(pb, &b)) {
+        ECerr(0, EC_R_INVALID_B);
+        goto err;
+    }
+
+    /* extract the prime number or irreducible polynomial */
+    ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_P);
+    if (!OSSL_PARAM_get_BN(ptmp, &p)) {
+        ECerr(0, EC_R_INVALID_P);
+        goto err;
+    }
+
+    if (is_prime_field) {
+        if (BN_is_negative(p) || BN_is_zero(p)) {
+            ECerr(0, EC_R_INVALID_P);
+            goto err;
+        }
+        field_bits = BN_num_bits(p);
+        if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) {
+            ECerr(0, EC_R_FIELD_TOO_LARGE);
+            goto err;
+        }
+
+        /* create the EC_GROUP structure */
+        group = EC_GROUP_new_curve_GFp(p, a, b, bnctx);
+    } else {
+#ifdef OPENSSL_NO_EC2M
+        ECerr(0, EC_R_GF2M_NOT_SUPPORTED);
+        goto err;
+#else
+        /* create the EC_GROUP structure */
+        group = EC_GROUP_new_curve_GF2m(p, a, b, NULL);
+        if (group != NULL) {
+            field_bits = EC_GROUP_get_degree(group);
+            if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) {
+                ECerr(0, EC_R_FIELD_TOO_LARGE);
+                goto err;
+            }
+        }
+#endif /* OPENSSL_NO_EC2M */
+    }
+
+    if (group == NULL) {
+        ECerr(0, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    /* Optional seed */
+    ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_SEED);
+    if (ptmp != NULL) {
+        if (ptmp->data_type != OSSL_PARAM_OCTET_STRING) {
+            ECerr(0, EC_R_INVALID_SEED);
+            goto err;
+        }
+        if (!EC_GROUP_set_seed(group, ptmp->data, ptmp->data_size))
+            goto err;
+    }
+
+    /* generator base point */
+    ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_GENERATOR);
+    if (ptmp == NULL
+        || ptmp->data_type != OSSL_PARAM_OCTET_STRING) {
+        ECerr(0, EC_R_INVALID_GENERATOR);
+        goto err;
+    }
+    buf = (const unsigned char *)(ptmp->data);
+    if ((point = EC_POINT_new(group)) == NULL)
+        goto err;
+    EC_GROUP_set_point_conversion_form(group,
+                                       (point_conversion_form_t)buf[0] & ~0x01);
+    if (!EC_POINT_oct2point(group, point, buf, ptmp->data_size, bnctx)) {
+        ECerr(0, EC_R_INVALID_GENERATOR);
+        goto err;
+    }
+
+    /* order */
+    ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_ORDER);
+    if (!OSSL_PARAM_get_BN(ptmp, &order)
+        || (BN_is_negative(order) || BN_is_zero(order))
+        || (BN_num_bits(order) > (int)field_bits + 1)) { /* Hasse bound */
+        ECerr(0, EC_R_INVALID_GROUP_ORDER);
+        goto err;
+    }
+
+    /* Optional cofactor */
+    ptmp = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_COFACTOR);
+    if (ptmp != NULL) {
+        cofactor = BN_CTX_get(bnctx);
+        if (cofactor == NULL || !OSSL_PARAM_get_BN(ptmp, &cofactor)) {
+            ECerr(0, EC_R_INVALID_COFACTOR);
+            goto err;
+        }
+    }
+
+    /* set the generator, order and cofactor (if present) */
+    if (!EC_GROUP_set_generator(group, point, order, cofactor)) {
+        ECerr(0, EC_R_INVALID_GENERATOR);
+        goto err;
+    }
+
+    named_group = ec_group_explicit_to_named(group, libctx, propq, bnctx);
+    if (named_group == NULL) {
+        ECerr(0, EC_R_INVALID_NAMED_GROUP_CONVERSION);
+        goto err;
+    }
+    if (named_group == group) {
+        /*
+         * If we did not find a named group then the encoding should be explicit
+         * if it was specified
+         */
+        if (encoding_flag == OPENSSL_EC_NAMED_CURVE) {
+            ECerr(0, EC_R_INVALID_ENCODING);
+            goto err;
+        }
+        EC_GROUP_set_asn1_flag(group, OPENSSL_EC_EXPLICIT_CURVE);
+    } else {
+        EC_GROUP_free(group);
+        group = named_group;
+    }
+    ok = 1;
+ err:
+    if (!ok) {
+        EC_GROUP_free(group);
+        group = NULL;
+    }
+    EC_POINT_free(point);
+    BN_CTX_end(bnctx);
+    BN_CTX_free(bnctx);
+
+    return group;
+}
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 3a5a5b5692..b530098d2f 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2421,7 +2421,10 @@ EC_R_GF2M_NOT_SUPPORTED:147:gf2m not supported
 EC_R_GROUP2PKPARAMETERS_FAILURE:120:group2pkparameters failure
 EC_R_I2D_ECPKPARAMETERS_FAILURE:121:i2d ecpkparameters failure
 EC_R_INCOMPATIBLE_OBJECTS:101:incompatible objects
+EC_R_INVALID_A:168:invalid a
 EC_R_INVALID_ARGUMENT:112:invalid argument
+EC_R_INVALID_B:169:invalid b
+EC_R_INVALID_COFACTOR:171:invalid cofactor
 EC_R_INVALID_COMPRESSED_POINT:110:invalid compressed point
 EC_R_INVALID_COMPRESSION_BIT:109:invalid compression bit
 EC_R_INVALID_CURVE:141:invalid curve
@@ -2430,12 +2433,16 @@ EC_R_INVALID_DIGEST_TYPE:138:invalid digest type
 EC_R_INVALID_ENCODING:102:invalid encoding
 EC_R_INVALID_FIELD:103:invalid field
 EC_R_INVALID_FORM:104:invalid form
+EC_R_INVALID_GENERATOR:173:invalid generator
 EC_R_INVALID_GROUP_ORDER:122:invalid group order
 EC_R_INVALID_KEY:116:invalid key
+EC_R_INVALID_NAMED_GROUP_CONVERSION:174:invalid named group conversion
 EC_R_INVALID_OUTPUT_LENGTH:161:invalid output length
+EC_R_INVALID_P:172:invalid p
 EC_R_INVALID_PEER_KEY:133:invalid peer key
 EC_R_INVALID_PENTANOMIAL_BASIS:132:invalid pentanomial basis
 EC_R_INVALID_PRIVATE_KEY:123:invalid private key
+EC_R_INVALID_SEED:175:invalid seed
 EC_R_INVALID_TRINOMIAL_BASIS:137:invalid trinomial basis
 EC_R_KDF_PARAMETER_ERROR:148:kdf parameter error
 EC_R_KEYS_NOT_SET:140:keys not set
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index fe2cc689f6..7fd5339537 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -1277,24 +1277,6 @@ int EVP_PKEY_CTX_ctrl_uint64(EVP_PKEY_CTX *ctx, int keytype, int optype,
 static int legacy_ctrl_str_to_param(EVP_PKEY_CTX *ctx, const char *name,
                                     const char *value)
 {
-
-    /* Special cases that we intercept */
-# ifndef OPENSSL_NO_EC
-    /*
-     * We don't support encoding settings for providers, i.e. the only
-     * possible encoding is "named_curve", so we simply fail when something
-     * else is given, and otherwise just pretend all is fine.
-     */
-    if (strcmp(name, "ec_param_enc") == 0) {
-        if (strcmp(value, "named_curve") == 0) {
-            return 1;
-        } else {
-            ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-            return -2;
-        }
-    }
-# endif
-
     if (strcmp(name, "md") == 0)
         name = OSSL_ALG_PARAM_DIGEST;
     else if (strcmp(name, "rsa_padding_mode") == 0)
@@ -1352,6 +1334,8 @@ static int legacy_ctrl_str_to_param(EVP_PKEY_CTX *ctx, const char *name,
         name = OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE;
     else if (strcmp(name, "ecdh_kdf_md") == 0)
         name = OSSL_EXCHANGE_PARAM_KDF_DIGEST;
+    else if (strcmp(name, "ec_param_enc") == 0)
+        name = OSSL_PKEY_PARAM_EC_ENCODING;
 # endif
     else if (strcmp(name, "N") == 0)
         name = OSSL_KDF_PARAM_SCRYPT_N;
diff --git a/doc/man3/EC_GROUP_new.pod b/doc/man3/EC_GROUP_new.pod
index dbead817ec..918e504ce8 100644
--- a/doc/man3/EC_GROUP_new.pod
+++ b/doc/man3/EC_GROUP_new.pod
@@ -4,9 +4,10 @@
 
 EC_GROUP_get_ecparameters,
 EC_GROUP_get_ecpkparameters,
-EC_GROUP_new,
+EC_GROUP_new_from_params,
 EC_GROUP_new_from_ecparameters,
 EC_GROUP_new_from_ecpkparameters,
+EC_GROUP_new,
 EC_GROUP_free,
 EC_GROUP_clear_free,
 EC_GROUP_new_curve_GFp,
@@ -26,6 +27,8 @@ objects
 
  #include <openssl/ec.h>
 
+ EC_GROUP *EC_GROUP_new_from_params(const OSSL_PARAM params[],
+                                    OPENSSL_CTX *libctx, const char *propq);
  EC_GROUP *EC_GROUP_new_from_ecparameters(const ECPARAMETERS *params);
  EC_GROUP *EC_GROUP_new_from_ecpkparameters(const ECPKPARAMETERS *params);
  void EC_GROUP_free(EC_GROUP *group);
@@ -93,6 +96,14 @@ used.
 It is then necessary to call EC_GROUP_set_curve() to set the curve parameters.
 Applications should instead use one of the other EC_GROUP_new_* constructors.
 
+EC_GROUP_new_from_params() creates a group with parameters specified by I<params>.
+The library context I<libctx> (see L<OPENSSL_CTX(3)>) and property query string
+I<propq> are used to fetch algorithms from providers.
+I<params> may be either a list of explicit params or a named group,
+The values for I<ctx> and I<propq> may be NULL.
+The I<params> that can be used are described in
+L<B<EVP_PKEY-EC>(7)|EVP_PKEY-EC(7)/Common EC parameters>.
+
 EC_GROUP_new_from_ecparameters() will create a group from the
 specified I<params> and
 EC_GROUP_new_from_ecpkparameters() will create a group from the specific PK
@@ -177,7 +188,7 @@ EC_GROUP_get_curve_GF2m() return 1 on success or 0 on error.
 L<crypto(7)>, L<EC_GROUP_copy(3)>,
 L<EC_POINT_new(3)>, L<EC_POINT_add(3)>, L<EC_KEY_new(3)>,
 L<EC_GFp_simple_method(3)>, L<d2i_ECPKParameters(3)>,
-L<OPENSSL_CTX(3)>
+L<OPENSSL_CTX(3)>, L<EVP_PKEY-EC(7)>
 
 =head1 HISTORY
 
@@ -187,7 +198,8 @@ L<OPENSSL_CTX(3)>
 
 EC_GROUP_new() was deprecated in OpenSSL 3.0.
 
-EC_GROUP_new_by_curve_name_with_libctx() was added in OpenSSL 3.0.
+EC_GROUP_new_by_curve_name_with_libctx() and EC_GROUP_new_from_params() were
+added in OpenSSL 3.0.
 
 =item *
 
diff --git a/doc/man3/EVP_PKEY_fromdata.pod b/doc/man3/EVP_PKEY_fromdata.pod
index e3003674e3..e3e6d89f8c 100644
--- a/doc/man3/EVP_PKEY_fromdata.pod
+++ b/doc/man3/EVP_PKEY_fromdata.pod
@@ -149,7 +149,7 @@ TODO Write a set of cookbook documents and link to them.
      0x47
  };
  const OSSL_PARAM params[] = {
-     OSSL_PARAM_utf8_string("curve-name", "prime256v1"),
+     OSSL_PARAM_utf8_string("group", "prime256v1"),
      OSSL_PARAM_BN("priv", priv, sizeof(priv)),
      OSSL_PARAM_BN("pub", pub, sizeof(pub)),
      OSSL_PARAM_END
diff --git a/doc/man7/EVP_PKEY-EC.pod b/doc/man7/EVP_PKEY-EC.pod
index ff074b949d..e423a52971 100644
--- a/doc/man7/EVP_PKEY-EC.pod
+++ b/doc/man7/EVP_PKEY-EC.pod
@@ -12,7 +12,15 @@ The B<EC> keytype is implemented in OpenSSL's default provider.
 
 =head2 Common EC parameters
 
-The following Import/Export types are available for the built-in EC algorithm:
+The normal way of specifying domain parameters for an EC curve is via the
+curve name "group". For curves with no curve name, explicit parameters can be
+used that specify "field-type", "p", "a", "b", "generator" and "order".
+Explicit parameters are supported for backwards compability reasons, but they
+are not compliant with multiple standards (including RFC5915) which only allow
+named curves.
+
+The following KeyGen/Gettable/Import/Export types are available for the
+built-in EC algorithm:
 
 =over 4
 
@@ -20,12 +28,56 @@ The following Import/Export types are available for the built-in EC algorithm:
 
 The curve name.
 
+=item "field-type" (B<OSSL_PKEY_PARAM_EC_FIELD_TYPE>) <utf8 string>
+
+The value should be either "prime-field" or "characteristic-two-field",
+which correspond to prime field Fp and binary field F2^m.
+
+=item "p" (B<OSSL_PKEY_PARAM_EC_P>) <unsigned integer>
+
+For a curve over Fp I<p> is the prime for the field. For a curve over F2^m I<p>
+represents the irreducible polynomial - each bit represents a term in the
+polynomial. Therefore, there will either be three or five bits set dependent on
+whether the polynomial is a trinomial or a pentanomial.
+
+=item "a" (B<OSSL_PKEY_PARAM_EC_A>) <unsigned integer>
+
+=item "b" (B<OSSL_PKEY_PARAM_EC_B>) <unsigned integer>
+
+=item "seed" (B<OSSL_PKEY_PARAM_EC_SEED>) <octet string>
+
+I<a> and I<b> represents the coefficients of the curve
+For Fp:   y^2 mod p = x^3 +ax + b mod p OR
+For F2^m: y^2 + xy = x^3 + ax^2 + b
+
+I<seed> is an optional value that is for information purposes only.
+It represents the random number seed used to generate the coefficient I<b> from a
+random number.
+
+=item "generator" (B<OSSL_PKEY_PARAM_EC_GENERATOR>) <octet string>
+
+=item "order" (B<OSSL_PKEY_PARAM_EC_ORDER>) <unsigned integer>
+
+=item "cofactor" (B<OSSL_PKEY_PARAM_EC_COFACTOR>) <unsigned integer>
+
+The I<generator> is a well defined point on the curve chosen for cryptographic
+operations. The encoding conforms with Sec. 2.3.3 of the SECG SEC 1 ("Elliptic Curve
+Cryptography") standard. See EC_POINT_oct2point().
+Integers used for point multiplications will be between 0 and
+I<order> - 1.
+I<cofactor> is an optional value.
+I<order> multiplied by the I<cofactor> gives the number of points on the curve.
+
 =item "use-cofactor-flag" (B<OSSL_PKEY_PARAM_USE_COFACTOR_ECDH>) <integer>
 
 Enable Cofactor DH (ECC CDH) if this value is 1, otherwise it uses normal EC DH
 if the value is zero. The cofactor variant multiplies the shared secret by the
 EC curve's cofactor (note for some curves the cofactor is 1).
 
+=item "encoding" (B<OSSL_PKEY_PARAM_EC_ENCODING>) <utf8 string>
+
+Set the format used for serializing the EC group parameters.
+Valid values are "explicit" or "named_curve". The default value is "named_curve".
 
 See also L<EVP_KEYEXCH-ECDH(7)> for the related
 B<OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE> parameter that can be set on a
@@ -46,6 +98,36 @@ exchange message for the TLS protocol.
 
 =back
 
+The following Gettable types are also available for the built-in EC algorithm:
+
+=over 4
+
+=item "basis-type" (B<OSSL_PKEY_PARAM_EC_CHAR2_TYPE>) <utf8 string>
+
+Supports the values "tpBasis" for a trinomial or "ppBasis" for a pentanomial.
+This field is only used for a binary field F2^m.
+
+=item "m" (B<OSSL_PKEY_PARAM_EC_CHAR2_M>) <integer>
+
+=item "tp" (B<OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS>) <integer>
+
+=item "k1" (B<OSSL_PKEY_PARAM_EC_CHAR2_PP_K1>) <integer>
+
+=item "k2" (B<OSSL_PKEY_PARAM_EC_CHAR2_PP_K2>) <integer>
+
+=item "k3" (B<OSSL_PKEY_PARAM_EC_CHAR2_PP_K3>) <integer>
+
+These fields are only used for a binary field F2^m.
+I<m> is the degree of the binary field.
+
+I<tp> is the middle bit of a trinomial so its value must be in the
+range m > tp > 0.
+
+I<k1>, I<k2> and I<k3> are used to get the middle bits of a pentanomial such
+that m > k3 > k2 > k1 > 0
+
+=back
+
 =head1 EXAMPLES
 
 An B<EVP_PKEY> context can be obtained by calling:
diff --git a/include/crypto/ec.h b/include/crypto/ec.h
index 9901141bb2..587f7a39fc 100644
--- a/include/crypto/ec.h
+++ b/include/crypto/ec.h
@@ -59,10 +59,14 @@ const char *ec_curve_nid2name(int nid);
 int ec_curve_name2nid(const char *name);
 
 /* Backend support */
+int ec_group_todata(const EC_GROUP *group, OSSL_PARAM_BLD *tmpl,
+                    OSSL_PARAM params[], OPENSSL_CTX *libctx, const char *propq,
+                    BN_CTX *bnctx, unsigned char **genbuf);
+int ec_group_fromdata(EC_KEY *ec, const OSSL_PARAM params[]);
 int ec_key_fromdata(EC_KEY *ecx, const OSSL_PARAM params[], int include_private);
-int ec_key_domparams_fromdata(EC_KEY *ecx, const OSSL_PARAM params[]);
 int ec_key_otherparams_fromdata(EC_KEY *ec, const OSSL_PARAM params[]);
 int ec_set_ecdh_cofactor_mode(EC_KEY *ec, int mode);
+int ec_encoding_name2id(const char *name);
 
 # endif /* OPENSSL_NO_EC */
 #endif
diff --git a/include/openssl/core.h b/include/openssl/core.h
index e77c2ba22f..80ba32d9bf 100644
--- a/include/openssl/core.h
+++ b/include/openssl/core.h
@@ -107,18 +107,18 @@ struct ossl_param_st {
 # define OSSL_PARAM_REAL                 3
 /*-
  * OSSL_PARAM_UTF8_STRING
- * is a printable string.  Is expteced to be printed as it is.
+ * is a printable string.  It is expected to be printed as it is.
  */
 # define OSSL_PARAM_UTF8_STRING          4
 /*-
  * OSSL_PARAM_OCTET_STRING
- * is a string of bytes with no further specification.  Is expected to be
+ * is a string of bytes with no further specification.  It is expected to be
  * printed as a hexdump.
  */
 # define OSSL_PARAM_OCTET_STRING         5
 /*-
  * OSSL_PARAM_UTF8_PTR
- * is a pointer to a printable string.  Is expteced to be printed as it is.
+ * is a pointer to a printable string.  It is expected to be printed as it is.
  *
  * The difference between this and OSSL_PARAM_UTF8_STRING is that only pointers
  * are manipulated for this type.
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 7538d9ce93..bf6dd2e96f 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -269,6 +269,22 @@ extern "C" {
 #define OSSL_PKEY_PARAM_EC_PUB_X     "qx"
 #define OSSL_PKEY_PARAM_EC_PUB_Y     "qy"
 
+/* Elliptic Curve Explicit Domain Parameters */
+#define OSSL_PKEY_PARAM_EC_FIELD_TYPE     "field-type"
+#define OSSL_PKEY_PARAM_EC_P              "p"
+#define OSSL_PKEY_PARAM_EC_A              "a"
+#define OSSL_PKEY_PARAM_EC_B              "b"
+#define OSSL_PKEY_PARAM_EC_GENERATOR      "generator"
+#define OSSL_PKEY_PARAM_EC_ORDER          "order"
+#define OSSL_PKEY_PARAM_EC_COFACTOR       "cofactor"
+#define OSSL_PKEY_PARAM_EC_SEED           "seed"
+#define OSSL_PKEY_PARAM_EC_CHAR2_M        "m"
+#define OSSL_PKEY_PARAM_EC_CHAR2_TYPE     "basis-type"
+#define OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS "tp"
+#define OSSL_PKEY_PARAM_EC_CHAR2_PP_K1    "k1"
+#define OSSL_PKEY_PARAM_EC_CHAR2_PP_K2    "k2"
+#define OSSL_PKEY_PARAM_EC_CHAR2_PP_K3    "k3"
+
 /* Elliptic Curve Key Parameters */
 #define OSSL_PKEY_PARAM_USE_COFACTOR_FLAG "use-cofactor-flag"
 #define OSSL_PKEY_PARAM_USE_COFACTOR_ECDH \
@@ -352,6 +368,12 @@ extern "C" {
 #define OSSL_PKEY_PARAM_FFC_DIGEST       OSSL_PKEY_PARAM_DIGEST
 #define OSSL_PKEY_PARAM_FFC_DIGEST_PROPS OSSL_PKEY_PARAM_PROPERTIES
 
+#define OSSL_PKEY_PARAM_EC_ENCODING      "encoding" /* utf8_string */
+
+/* OSSL_PKEY_PARAM_EC_ENCODING values */
+#define OSSL_PKEY_EC_ENCODING_EXPLICIT  "explicit"
+#define OSSL_PKEY_EC_ENCODING_GROUP     "named_curve"
+
 /* Key Exchange parameters */
 #define OSSL_EXCHANGE_PARAM_PAD                   "pad" /* uint */
 #define OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE "ecdh-cofactor-mode" /* int */
diff --git a/include/openssl/ec.h b/include/openssl/ec.h
index 822e3e9b28..9db898cfed 100644
--- a/include/openssl/ec.h
+++ b/include/openssl/ec.h
@@ -47,6 +47,7 @@ typedef enum {
     POINT_CONVERSION_HYBRID = 6
 } point_conversion_form_t;
 
+#  include <openssl/params.h>
 #  ifndef OPENSSL_NO_DEPRECATED_3_0
 typedef struct ec_method_st EC_METHOD;
 #  endif
@@ -379,6 +380,19 @@ EC_GROUP *EC_GROUP_new_curve_GF2m(const BIGNUM *p, const BIGNUM *a,
                                   const BIGNUM *b, BN_CTX *ctx);
 #  endif
 
+/**
+ * Creates a EC_GROUP object with a curve specified by parameters.
+ * The parameters may be explicit or a named curve,
+ *  \param  params A list of parameters describing the group.
+ *  \param  libctx The associated library context or NULL for the default
+ *                 context
+ *  \param  propq  A property query string
+ *  \return newly created EC_GROUP object with specified parameters or NULL
+ *          if an error occurred
+ */
+EC_GROUP *EC_GROUP_new_from_params(const OSSL_PARAM params[],
+                                   OPENSSL_CTX *libctx, const char *propq);
+
 /**
  * Creates a EC_GROUP object with a curve specified by a NID
  *  \param  libctx The associated library context or NULL for the default
diff --git a/include/openssl/ecerr.h b/include/openssl/ecerr.h
index b12e222510..bbed2b4b7c 100644
--- a/include/openssl/ecerr.h
+++ b/include/openssl/ecerr.h
@@ -243,7 +243,10 @@ int ERR_load_EC_strings(void);
 #  define EC_R_GROUP2PKPARAMETERS_FAILURE                  120
 #  define EC_R_I2D_ECPKPARAMETERS_FAILURE                  121
 #  define EC_R_INCOMPATIBLE_OBJECTS                        101
+#  define EC_R_INVALID_A                                   168
 #  define EC_R_INVALID_ARGUMENT                            112
+#  define EC_R_INVALID_B                                   169
+#  define EC_R_INVALID_COFACTOR                            171
 #  define EC_R_INVALID_COMPRESSED_POINT                    110
 #  define EC_R_INVALID_COMPRESSION_BIT                     109
 #  define EC_R_INVALID_CURVE                               141
@@ -252,12 +255,16 @@ int ERR_load_EC_strings(void);
 #  define EC_R_INVALID_ENCODING                            102
 #  define EC_R_INVALID_FIELD                               103
 #  define EC_R_INVALID_FORM                                104
+#  define EC_R_INVALID_GENERATOR                           173
 #  define EC_R_INVALID_GROUP_ORDER                         122
 #  define EC_R_INVALID_KEY                                 116
+#  define EC_R_INVALID_NAMED_GROUP_CONVERSION              174
 #  define EC_R_INVALID_OUTPUT_LENGTH                       161
+#  define EC_R_INVALID_P                                   172
 #  define EC_R_INVALID_PEER_KEY                            133
 #  define EC_R_INVALID_PENTANOMIAL_BASIS                   132
 #  define EC_R_INVALID_PRIVATE_KEY                         123
+#  define EC_R_INVALID_SEED                                175
 #  define EC_R_INVALID_TRINOMIAL_BASIS                     137
 #  define EC_R_KDF_PARAMETER_ERROR                         148
 #  define EC_R_KEYS_NOT_SET                                140
diff --git a/providers/implementations/encode_decode/encoder_ec.c b/providers/implementations/encode_decode/encoder_ec.c
index 8c256fe448..ee64e2f802 100644
--- a/providers/implementations/encode_decode/encoder_ec.c
+++ b/providers/implementations/encode_decode/encoder_ec.c
@@ -23,22 +23,134 @@ void ec_get_new_free_import(OSSL_FUNC_keymgmt_new_fn **ec_new,
     *ec_import = ossl_prov_get_keymgmt_import(ec_keymgmt_functions);
 }
 
-static int ossl_prov_print_ec_param(BIO *out, const EC_GROUP *group)
+static int ossl_prov_print_ec_param_explicit_curve(BIO *out,
+                                                   const EC_GROUP *group,
+                                                   BN_CTX *ctx)
 {
-    const char *curve_name;
-    int curve_nid = EC_GROUP_get_curve_name(group);
+    const char *plabel = "Prime:";
+    BIGNUM *p = NULL, *a = NULL, *b = NULL;
 
-    /* TODO(3.0): Explicit parameters are currently not supported */
-    if (curve_nid == NID_undef)
+    p = BN_CTX_get(ctx);
+    a = BN_CTX_get(ctx);
+    b = BN_CTX_get(ctx);
+    if (b == NULL
+        || !EC_GROUP_get_curve(group, p, a, b, ctx))
         return 0;
 
-    if (BIO_printf(out, "%s: %s\n", "ASN1 OID", OBJ_nid2sn(curve_nid)) <= 0)
+    if (EC_GROUP_get_field_type(group) == NID_X9_62_characteristic_two_field) {
+        int basis_type = EC_GROUP_get_basis_type(group);
+
+        /* print the 'short name' of the base type OID */
+        if (basis_type == NID_undef
+            || BIO_printf(out, "Basis Type: %s\n", OBJ_nid2sn(basis_type)) <= 0)
+            return 0;
+        plabel = "Polynomial:";
+    }
+    return ossl_prov_print_labeled_bignum(out, plabel, p)
+           && ossl_prov_print_labeled_bignum(out, "A:   ", a)
+           && ossl_prov_print_labeled_bignum(out, "B:   ", b);
+}
+
+static int ossl_prov_print_ec_param_explicit_gen(BIO *out,
+                                                 const EC_GROUP *group,
+                                                 BN_CTX *ctx)
+{
+    const EC_POINT *point = NULL;
+    BIGNUM *gen = NULL;
+    const char *glabel = NULL;
+    point_conversion_form_t form;
+
+    form = EC_GROUP_get_point_conversion_form(group);
+    point = EC_GROUP_get0_generator(group);
+    gen = BN_CTX_get(ctx);
+
+    if (gen == NULL
+        || point == NULL
+        || EC_POINT_point2bn(group, point, form, gen, ctx) == NULL)
+        return 0;
+
+    if (gen != NULL) {
+        switch (form) {
+        case POINT_CONVERSION_COMPRESSED:
+           glabel = "Generator (compressed):";
+           break;
+        case POINT_CONVERSION_UNCOMPRESSED:
+            glabel = "Generator (uncompressed):";
+            break;
+        case POINT_CONVERSION_HYBRID:
+            glabel = "Generator (hybrid):";
+            break;
+        default:
+            return 0;
+        }
+        return ossl_prov_print_labeled_bignum(out, glabel, gen);
+    }
+    return 1;
+}
+
+/* Print explicit parameters */
+static int ossl_prov_print_ec_param_explicit(BIO *out, const EC_GROUP *group,
+                                             OPENSSL_CTX *libctx)
+{
+    int ret = 0, tmp_nid;
+    BN_CTX *ctx = NULL;
+    const BIGNUM *order = NULL, *cofactor = NULL;
+    const unsigned char *seed;
+    size_t seed_len = 0;
+
+    ctx = BN_CTX_new_ex(libctx);
+    if (ctx == NULL)
         return 0;
+    BN_CTX_start(ctx);
+
+    tmp_nid = EC_GROUP_get_field_type(group);
+    order = EC_GROUP_get0_order(group);
+    if (order == NULL)
+        goto err;
+
+    seed = EC_GROUP_get0_seed(group);
+    if (seed != NULL)
+        seed_len = EC_GROUP_get_seed_len(group);
+    cofactor = EC_GROUP_get0_cofactor(group);
+
+    /* print the 'short name' of the field type */
+    if (BIO_printf(out, "Field Type: %s\n", OBJ_nid2sn(tmp_nid)) <= 0
+        || !ossl_prov_print_ec_param_explicit_curve(out, group, ctx)
+        || !ossl_prov_print_ec_param_explicit_gen(out, group, ctx)
+        || !ossl_prov_print_labeled_bignum(out, "Order: ", order)
+        || (cofactor != NULL
+            && !ossl_prov_print_labeled_bignum(out, "Cofactor: ", cofactor))
+        || (seed != NULL
+            && !ossl_prov_print_labeled_buf(out, "Seed:", seed, seed_len)))
+        goto err;
+    ret = 1;
+err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    return ret;
+}
+
+static int ossl_prov_print_ec_param(BIO *out, const EC_GROUP *group,
+                                    OPENSSL_CTX *libctx)
+{
+    if (EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE) {
+        const char *curve_name;
+        int curve_nid = EC_GROUP_get_curve_name(group);
 
-    /* TODO(3.0): Only named curves are currently supported */
-    curve_name = EC_curve_nid2nist(curve_nid);
-    return (curve_name == NULL
-            || BIO_printf(out, "%s: %s\n", "NIST CURVE", curve_name) > 0);
+        /* Explicit parameters */
+        if (curve_nid == NID_undef)
+            return 0;
+
+        if (BIO_printf(out, "%s: %s\n", "ASN1 OID", OBJ_nid2sn(curve_nid)) <= 0)
+            return 0;
+
+        /* TODO(3.0): Only named curves are currently supported */
+        curve_name = EC_curve_nid2nist(curve_nid);
+        return (curve_name == NULL
+                || BIO_printf(out, "%s: %s\n", "NIST CURVE", curve_name) > 0);
+    } else {
+        return ossl_prov_print_ec_param_explicit(out, group, libctx);
+    }
 }
 
 int ossl_prov_print_eckey(BIO *out, EC_KEY *eckey, enum ec_print_type type)
@@ -94,7 +206,7 @@ int ossl_prov_print_eckey(BIO *out, EC_KEY *eckey, enum ec_print_type type)
     if (pub != NULL
         && !ossl_prov_print_labeled_buf(out, "pub:", pub, pub_len))
         goto err;
-    ret = ossl_prov_print_ec_param(out, group);
+    ret = ossl_prov_print_ec_param(out, group, ec_key_get_libctx(eckey));
 err:
     OPENSSL_clear_free(priv, priv_len);
     OPENSSL_free(pub);
@@ -104,32 +216,60 @@ null_err:
     goto err;
 }
 
-int ossl_prov_prepare_ec_params(const void *eckey, int nid,
-                                void **pstr, int *pstrtype)
+static int ossl_prov_prepare_ec_explicit_params(const void *eckey,
+                                                void **pstr, int *pstrtype)
 {
-    int curve_nid;
-    const EC_GROUP *group = EC_KEY_get0_group(eckey);
-    ASN1_OBJECT *params;
+    ASN1_STRING *params = ASN1_STRING_new();
 
-    if (group == NULL
-        || ((curve_nid = EC_GROUP_get_curve_name(group)) == NID_undef)
-        || ((params = OBJ_nid2obj(curve_nid)) == NULL)) {
-        /* TODO(3.0): Explicit curves are not supported */
+    if (params == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
         return 0;
     }
 
-    if (OBJ_length(params) == 0) {
-        /* Some curves might not have an associated OID */
-        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_OID);
-        ASN1_OBJECT_free(params);
+    params->length = i2d_ECParameters(eckey, &params->data);
+    if (params->length <= 0) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        ASN1_STRING_free(params);
         return 0;
     }
 
+    *pstrtype = V_ASN1_SEQUENCE;
     *pstr = params;
-    *pstrtype = V_ASN1_OBJECT;
     return 1;
 }
 
+int ossl_prov_prepare_ec_params(const void *eckey, int nid,
+                                void **pstr, int *pstrtype)
+{
+    int curve_nid;
+    const EC_GROUP *group = EC_KEY_get0_group(eckey);
+    ASN1_OBJECT *params = NULL;
+
+    if (group == NULL)
+        return 0;
+    curve_nid = EC_GROUP_get_curve_name(group);
+    if (curve_nid != NID_undef) {
+        params = OBJ_nid2obj(curve_nid);
+        if (params == NULL)
+            return 0;
+    }
+
+    if (curve_nid != NID_undef
+        && (EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE)) {
+        if (OBJ_length(params) == 0) {
+            /* Some curves might not have an associated OID */
+            ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_OID);
+            ASN1_OBJECT_free(params);
+            return 0;
+        }
+        *pstr = params;
+        *pstrtype = V_ASN1_OBJECT;
+        return 1;
+    } else {
+        return ossl_prov_prepare_ec_explicit_params(eckey, pstr, pstrtype);
+    }
+}
+
 int ossl_prov_ec_pub_to_der(const void *eckey, unsigned char **pder)
 {
     return i2o_ECPublicKey(eckey, pder);
diff --git a/providers/implementations/keymgmt/ec_kmgmt.c b/providers/implementations/keymgmt/ec_kmgmt.c
index 7fa23b1a6c..cb5e2291da 100644
--- a/providers/implementations/keymgmt/ec_kmgmt.c
+++ b/providers/implementations/keymgmt/ec_kmgmt.c
@@ -13,6 +13,8 @@
  */
 #include "internal/deprecated.h"
 
+#include "e_os.h" /* strcasecmp */
+#include <string.h>
 #include <openssl/core_dispatch.h>
 #include <openssl/core_names.h>
 #include <openssl/bn.h>
@@ -64,41 +66,6 @@ const char *ec_query_operation_name(int operation_id)
     return NULL;
 }
 
-static ossl_inline
-int domparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl,
-                        OSSL_PARAM params[])
-{
-    const EC_GROUP *ecg;
-    int curve_nid;
-
-    if (ec == NULL)
-        return 0;
-
-    ecg = EC_KEY_get0_group(ec);
-    if (ecg == NULL)
-        return 0;
-
-    curve_nid = EC_GROUP_get_curve_name(ecg);
-
-    if (curve_nid == NID_undef) {
-        /* TODO(3.0): should we support explicit parameters curves? */
-        return 0;
-    } else {
-        /* named curve */
-        const char *curve_name = NULL;
-
-        if ((curve_name = ec_curve_nid2name(curve_nid)) == NULL)
-            return 0;
-        if (!ossl_param_build_set_utf8_string(tmpl, params,
-                                              OSSL_PKEY_PARAM_GROUP_NAME,
-                                              curve_name))
-
-            return 0;
-    }
-
-    return 1;
-}
-
 /*
  * Callers of key_to_params MUST make sure that domparams_to_params is also
  * called!
@@ -319,7 +286,7 @@ int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
     /*
      * In this implementation, we can export/import only keydata in the
      * following combinations:
-     *   - domain parameters only
+     *   - domain parameters (+optional other params)
      *   - public key with associated domain parameters (+optional other params)
      *   - private key with associated public key and domain parameters
      *         (+optional other params)
@@ -327,19 +294,16 @@ int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
      * This means:
      *   - domain parameters must always be requested
      *   - private key must be requested alongside public key
-     *   - other parameters must be requested only alongside a key
+     *   - other parameters are always optional
      */
     if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
         return 0;
     if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
             && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0)
         return 0;
-    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0
-            && (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
-        return 0;
 
     if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
-        ok = ok && ec_key_domparams_fromdata(ec, params);
+        ok = ok && ec_group_fromdata(ec, params);
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
         int include_private =
             selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
@@ -359,7 +323,8 @@ int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
     EC_KEY *ec = keydata;
     OSSL_PARAM_BLD *tmpl;
     OSSL_PARAM *params = NULL;
-    unsigned char *pub_key = NULL;
+    unsigned char *pub_key = NULL, *genbuf = NULL;
+    BN_CTX *bnctx = NULL;
     int ok = 1;
 
     if (ec == NULL)
@@ -368,7 +333,7 @@ int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
     /*
      * In this implementation, we can export/import only keydata in the
      * following combinations:
-     *   - domain parameters only
+     *   - domain parameters (+optional other params)
      *   - public key with associated domain parameters (+optional other params)
      *   - private key with associated public key and domain parameters
      *         (+optional other params)
@@ -376,7 +341,7 @@ int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
      * This means:
      *   - domain parameters must always be requested
      *   - private key must be requested alongside public key
-     *   - other parameters must be requested only alongside a key
+     *   - other parameters are always optional
      */
     if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
         return 0;
@@ -391,8 +356,14 @@ int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
     if (tmpl == NULL)
         return 0;
 
-    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
-        ok = ok && domparams_to_params(ec, tmpl, NULL);
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+        bnctx = BN_CTX_new_ex(ec_key_get_libctx(ec));
+        BN_CTX_start(bnctx);
+        ok = ok && (bnctx != NULL);
+        ok = ok && ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL,
+                                   ec_key_get_libctx(ec), ec_key_get0_propq(ec),
+                                   bnctx, &genbuf);
+    }
 
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
         int include_private =
@@ -409,18 +380,31 @@ int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
     OSSL_PARAM_BLD_free_params(params);
     OSSL_PARAM_BLD_free(tmpl);
     OPENSSL_free(pub_key);
+    OPENSSL_free(genbuf);
+    BN_CTX_end(bnctx);
+    BN_CTX_free(bnctx);
     return ok;
 }
 
 /* IMEXPORT = IMPORT + EXPORT */
 
-# define EC_IMEXPORTABLE_DOM_PARAMETERS                          \
-    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0)
-# define EC_IMEXPORTABLE_PUBLIC_KEY                              \
+# define EC_IMEXPORTABLE_DOM_PARAMETERS                                        \
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),               \
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),              \
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_FIELD_TYPE, NULL, 0),            \
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_P, NULL, 0),                              \
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_A, NULL, 0),                              \
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_B, NULL, 0),                              \
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_GENERATOR, NULL, 0),            \
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_ORDER, NULL, 0),                          \
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_COFACTOR, NULL, 0),                       \
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0)
+
+# define EC_IMEXPORTABLE_PUBLIC_KEY                                            \
     OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0)
-# define EC_IMEXPORTABLE_PRIVATE_KEY                             \
+# define EC_IMEXPORTABLE_PRIVATE_KEY                                           \
     OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
-# define EC_IMEXPORTABLE_OTHER_PARAMETERS                        \
+# define EC_IMEXPORTABLE_OTHER_PARAMETERS                                      \
     OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL)
 
 /*
@@ -465,19 +449,81 @@ const OSSL_PARAM *ec_export_types(int selection)
     return ec_imexport_types(selection);
 }
 
+static int ec_get_ecm_params(const EC_GROUP *group, OSSL_PARAM params[])
+{
+#ifdef OPENSSL_NO_EC2M
+    return 1;
+#else
+    int ret = 0, m;
+    unsigned int k1 = 0, k2 = 0, k3 = 0;
+    int basis_nid;
+    const char *basis_name = NULL;
+    int fid = EC_GROUP_get_field_type(group);
+
+    if (fid != NID_X9_62_characteristic_two_field)
+        return 1;
+
+    basis_nid = EC_GROUP_get_basis_type(group);
+    if (basis_nid == NID_X9_62_tpBasis)
+        basis_name = SN_X9_62_tpBasis;
+    else if (basis_nid == NID_X9_62_ppBasis)
+        basis_name = SN_X9_62_ppBasis;
+    else
+        goto err;
+
+    m = EC_GROUP_get_degree(group);
+    if (!ossl_param_build_set_int(NULL, params, OSSL_PKEY_PARAM_EC_CHAR2_M, m)
+        || !ossl_param_build_set_utf8_string(NULL, params,
+                                             OSSL_PKEY_PARAM_EC_CHAR2_TYPE,
+                                             basis_name))
+        goto err;
+
+    if (basis_nid == NID_X9_62_tpBasis) {
+        if (!EC_GROUP_get_trinomial_basis(group, &k1)
+            || !ossl_param_build_set_int(NULL, params,
+                                         OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS,
+                                         (int)k1))
+            goto err;
+    } else {
+        if (!EC_GROUP_get_pentanomial_basis(group, &k1, &k2, &k3)
+            || !ossl_param_build_set_int(NULL, params,
+                                         OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, (int)k1)
+            || !ossl_param_build_set_int(NULL, params,
+                                         OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, (int)k2)
+            || !ossl_param_build_set_int(NULL, params,
+                                         OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, (int)k3))
+            goto err;
+    }
+    ret = 1;
+err:
+    return ret;
+#endif /* OPENSSL_NO_EC2M */
+}
+
 static
 int ec_get_params(void *key, OSSL_PARAM params[])
 {
-    int ret;
+    int ret = 0;
     EC_KEY *eck = key;
     const EC_GROUP *ecg = NULL;
     OSSL_PARAM *p;
-    unsigned char *pub_key = NULL;
+    unsigned char *pub_key = NULL, *genbuf = NULL;
+    OPENSSL_CTX *libctx;
+    const char *propq;
+    BN_CTX *bnctx = NULL;
 
     ecg = EC_KEY_get0_group(eck);
     if (ecg == NULL)
         return 0;
 
+    libctx = ec_key_get_libctx(eck);
+    propq = ec_key_get0_propq(eck);
+
+    bnctx = BN_CTX_new_ex(libctx);
+    if (bnctx == NULL)
+        return 0;
+    BN_CTX_start(bnctx);
+
     if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
         && !OSSL_PARAM_set_int(p, ECDSA_size(eck)))
         return 0;
@@ -537,32 +583,45 @@ int ec_get_params(void *key, OSSL_PARAM params[])
             return 0;
     }
     if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_TLS_ENCODED_PT)) != NULL) {
-        BN_CTX *ctx = BN_CTX_new_ex(ec_key_get_libctx(key));
-
-        if (ctx == NULL)
-            return 0;
         p->return_size = EC_POINT_point2oct(EC_KEY_get0_group(key),
                                             EC_KEY_get0_public_key(key),
                                             POINT_CONVERSION_UNCOMPRESSED,
-                                            p->data, p->return_size, ctx);
-        BN_CTX_free(ctx);
+                                            p->data, p->return_size, bnctx);
         if (p->return_size == 0)
-            return 0;
+            goto err;
     }
 
-    ret = domparams_to_params(eck, NULL, params)
+    ret = ec_get_ecm_params(ecg, params)
+          && ec_group_todata(ecg, NULL, params, libctx, propq, bnctx, &genbuf)
           && key_to_params(eck, NULL, params, 1, &pub_key)
           && otherparams_to_params(eck, NULL, params);
+err:
+    OPENSSL_free(genbuf);
     OPENSSL_free(pub_key);
+    BN_CTX_end(bnctx);
+    BN_CTX_free(bnctx);
     return ret;
 }
 
+#ifndef OPENSSL_NO_EC2M
+# define EC2M_GETTABLE_DOM_PARAMS                                              \
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_M, NULL),                      \
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_CHAR2_TYPE, NULL, 0),        \
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS, NULL),               \
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, NULL),                  \
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, NULL),                  \
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, NULL),
+#else
+# define EC2M_GETTABLE_DOM_PARAMS
+#endif
+
 static const OSSL_PARAM ec_known_gettable_params[] = {
     OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
     OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
     OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
     OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_TLS_ENCODED_PT, NULL, 0),
     EC_IMEXPORTABLE_DOM_PARAMETERS,
+    EC2M_GETTABLE_DOM_PARAMS
     EC_IMEXPORTABLE_PUBLIC_KEY,
     OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0),
     OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0),
@@ -643,9 +702,15 @@ int ec_validate(void *keydata, int selection)
 
 struct ec_gen_ctx {
     OPENSSL_CTX *libctx;
-    EC_GROUP *gen_group;
+    char *group_name;
+    char *encoding;
+    char *field_type;
+    BIGNUM *p, *a, *b, *order, *cofactor;
+    unsigned char *gen, *seed;
+    size_t gen_len, seed_len;
     int selection;
     int ecdh_mode;
+    EC_GROUP *gen_group;
 };
 
 static void *ec_gen_init(void *provctx, int selection)
@@ -658,19 +723,18 @@ static void *ec_gen_init(void *provctx, int selection)
 
     if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
         gctx->libctx = libctx;
-        gctx->gen_group = NULL;
         gctx->selection = selection;
         gctx->ecdh_mode = 0;
     }
     return gctx;
 }
 
-static int ec_gen_set_group(void *genctx, int nid)
+static int ec_gen_set_group(void *genctx, const EC_GROUP *src)
 {
     struct ec_gen_ctx *gctx = genctx;
     EC_GROUP *group;
 
-    group = EC_GROUP_new_by_curve_name_with_libctx(gctx->libctx, NULL, nid);
+    group = EC_GROUP_dup(src);
     if (group == NULL) {
         ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
         return 0;
@@ -679,6 +743,7 @@ static int ec_gen_set_group(void *genctx, int nid)
     gctx->gen_group = group;
     return 1;
 }
+
 static int ec_gen_set_template(void *genctx, void *templ)
 {
     struct ec_gen_ctx *gctx = genctx;
@@ -689,48 +754,144 @@ static int ec_gen_set_template(void *genctx, void *templ)
         return 0;
     if ((ec_group = EC_KEY_get0_group(ec)) == NULL)
         return 0;
-    return ec_gen_set_group(gctx, EC_GROUP_get_curve_name(ec_group));
+    return ec_gen_set_group(gctx, ec_group);
+}
+
+#define COPY_INT_PARAM(params, key, val)                                       \
+p = OSSL_PARAM_locate_const(params, key);                                      \
+if (p != NULL && !OSSL_PARAM_get_int(p, &val))                                 \
+    goto err;
+
+#define COPY_UTF8_PARAM(params, key, val)                                      \
+p = OSSL_PARAM_locate_const(params, key);                                      \
+if (p != NULL) {                                                               \
+    if (p->data_type != OSSL_PARAM_UTF8_STRING)                                \
+        goto err;                                                              \
+    OPENSSL_free(val);                                                         \
+    val = OPENSSL_strdup(p->data);                                             \
+    if (val == NULL)                                                           \
+        goto err;                                                              \
+}
+
+#define COPY_OCTET_PARAM(params, key, val, len)                                \
+p = OSSL_PARAM_locate_const(params, key);                                      \
+if (p != NULL) {                                                               \
+    if (p->data_type != OSSL_PARAM_OCTET_STRING)                               \
+        goto err;                                                              \
+    OPENSSL_free(val);                                                         \
+    len = p->data_size;                                                        \
+    val = OPENSSL_memdup(p->data, p->data_size);                               \
+    if (val == NULL)                                                           \
+        goto err;                                                              \
+}
+
+#define COPY_BN_PARAM(params, key, bn)                                         \
+p = OSSL_PARAM_locate_const(params, key);                                      \
+if (p != NULL) {                                                               \
+    if (bn == NULL)                                                            \
+        bn = BN_new();                                                         \
+    if (bn == NULL || !OSSL_PARAM_get_BN(p, &bn))                              \
+        goto err;                                                              \
 }
 
 static int ec_gen_set_params(void *genctx, const OSSL_PARAM params[])
 {
+    int ret = 0;
     struct ec_gen_ctx *gctx = genctx;
     const OSSL_PARAM *p;
+    EC_GROUP *group = NULL;
 
-    if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH))
-        != NULL) {
-        if (!OSSL_PARAM_get_int(p, &gctx->ecdh_mode))
-            return 0;
-    }
-    if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME))
-        != NULL) {
-        const char *curve_name = NULL;
-        int ret = 0;
-
-        switch (p->data_type) {
-        case OSSL_PARAM_UTF8_STRING:
-            /* The OSSL_PARAM functions have no support for this */
-            curve_name = p->data;
-            ret = (curve_name != NULL);
-            break;
-        case OSSL_PARAM_UTF8_PTR:
-            ret = OSSL_PARAM_get_utf8_ptr(p, &curve_name);
-            break;
-        }
+    COPY_INT_PARAM(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, gctx->ecdh_mode);
 
-        if (ret) {
-            int nid = ec_curve_name2nid(curve_name);
+    COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_GROUP_NAME, gctx->group_name);
+    COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_FIELD_TYPE, gctx->field_type);
+    COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_ENCODING, gctx->encoding);
 
-            if (nid == NID_undef) {
-                ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
-                ret = 0;
-            } else {
-                ret = ec_gen_set_group(gctx, nid);
-            }
-        }
-        return ret;
+    COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_P, gctx->p);
+    COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_A, gctx->a);
+    COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_B, gctx->b);
+    COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_ORDER, gctx->order);
+    COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_COFACTOR, gctx->cofactor);
+
+    COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_EC_SEED, gctx->seed, gctx->seed_len);
+    COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_EC_GENERATOR, gctx->gen,
+                     gctx->gen_len);
+
+    ret = 1;
+err:
+    EC_GROUP_free(group);
+    return ret;
+}
+
+static int ec_gen_set_group_from_params(struct ec_gen_ctx *gctx)
+{
+    int ret = 0;
+    OSSL_PARAM_BLD *bld;
+    OSSL_PARAM *params = NULL;
+    EC_GROUP *group = NULL;
+
+    bld = OSSL_PARAM_BLD_new();
+    if (bld == NULL)
+        return 0;
+
+    if (gctx->encoding != NULL
+        && !OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_ENCODING,
+                                            gctx->encoding, 0))
+        goto err;
+
+    if (gctx->group_name != NULL) {
+        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME,
+                                             gctx->group_name, 0))
+            goto err;
+        /* Ignore any other parameters if there is a group name */
+        goto build;
+    } else if (gctx->field_type != NULL) {
+        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_FIELD_TYPE,
+                                             gctx->field_type, 0))
+            goto err;
+    } else {
+        goto err;
     }
-    return 1;
+    if (gctx->p == NULL
+        || gctx->a == NULL
+        || gctx->b == NULL
+        || gctx->order == NULL
+        || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, gctx->p)
+        || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, gctx->a)
+        || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, gctx->b)
+        || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_ORDER, gctx->order))
+        goto err;
+
+    if (gctx->cofactor != NULL
+        && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR,
+                                   gctx->cofactor))
+        goto err;
+
+    if (gctx->seed != NULL
+        && !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_SEED,
+                                             gctx->seed, gctx->seed_len))
+        goto err;
+
+    if (gctx->gen == NULL
+        || !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR,
+                                             gctx->gen, gctx->gen_len))
+        goto err;
+build:
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (params == NULL)
+        goto err;
+    group = EC_GROUP_new_from_params(params, gctx->libctx, NULL);
+    if (group == NULL)
+        goto err;
+
+    EC_GROUP_free(gctx->gen_group);
+    gctx->gen_group = group;
+
+    ret = 1;
+err:
+    OSSL_PARAM_BLD_free_params(params);
+    OSSL_PARAM_BLD_free(bld);
+    return ret;
 }
 
 static const OSSL_PARAM *ec_gen_settable_params(void *provctx)
@@ -738,6 +899,15 @@ static const OSSL_PARAM *ec_gen_settable_params(void *provctx)
     static OSSL_PARAM settable[] = {
         OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
         OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_FIELD_TYPE, NULL, 0),
+        OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_P, NULL, 0),
+        OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_A, NULL, 0),
+        OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_B, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_GENERATOR, NULL, 0),
+        OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_ORDER, NULL, 0),
+        OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_COFACTOR, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0),
         OSSL_PARAM_END
     };
 
@@ -760,14 +930,27 @@ static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
 {
     struct ec_gen_ctx *gctx = genctx;
     EC_KEY *ec = NULL;
-    int ret = 1;                 /* Start optimistically */
+    int ret = 0;
 
     if (gctx == NULL
         || (ec = EC_KEY_new_with_libctx(gctx->libctx, NULL)) == NULL)
         return NULL;
 
+    if (gctx->gen_group == NULL) {
+        if (!ec_gen_set_group_from_params(gctx))
+            goto err;
+    } else {
+        if (gctx->encoding) {
+            int flags = ec_encoding_name2id(gctx->encoding);
+            if (flags < 0)
+                goto err;
+            EC_GROUP_set_asn1_flag(gctx->gen_group, flags);
+        }
+    }
+
     /* We must always assign a group, no matter what */
     ret = ec_gen_assign_group(ec, gctx->gen_group);
+
     /* Whether you want it or not, you get a keypair, not just one half */
     if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
         ret = ret && EC_KEY_generate_key(ec);
@@ -777,7 +960,7 @@ static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
 
     if (ret)
         return ec;
-
+err:
     /* Something went wrong, throw the key away */
     EC_KEY_free(ec);
     return NULL;
@@ -791,6 +974,16 @@ static void ec_gen_cleanup(void *genctx)
         return;
 
     EC_GROUP_free(gctx->gen_group);
+    BN_free(gctx->p);
+    BN_free(gctx->a);
+    BN_free(gctx->b);
+    BN_free(gctx->order);
+    BN_free(gctx->cofactor);
+    OPENSSL_free(gctx->group_name);
+    OPENSSL_free(gctx->field_type);;
+    OPENSSL_free(gctx->encoding);
+    OPENSSL_free(gctx->seed);
+    OPENSSL_free(gctx->gen);
     OPENSSL_free(gctx);
 }
 
diff --git a/test/ectest.c b/test/ectest.c
index 3678c42f71..9088fd166a 100644
--- a/test/ectest.c
+++ b/test/ectest.c
@@ -2340,6 +2340,225 @@ static int ec_point_hex2point_test(int id)
     return ret;
 }
 
+static int do_test_custom_explicit_fromdata(EC_GROUP *group, BN_CTX *ctx,
+                                            unsigned char *gen, int gen_size)
+{
+    int ret = 0, i_out;
+    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;
+    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
+
+    p = BN_CTX_get(ctx);
+    a = BN_CTX_get(ctx);
+    b = BN_CTX_get(ctx);
+
+    if (!TEST_ptr(b)
+        || !TEST_ptr(bld = OSSL_PARAM_BLD_new()))
+        goto err;
+
+    if (EC_GROUP_get_field_type(group) == NID_X9_62_prime_field) {
+        field_name = SN_X9_62_prime_field;
+    } else {
+        field_name = SN_X9_62_characteristic_two_field;
+#ifndef OPENSSL_NO_EC2M
+        if (EC_GROUP_get_basis_type(group) == NID_X9_62_tpBasis) {
+            basis_name = SN_X9_62_tpBasis;
+            if (!TEST_true(EC_GROUP_get_trinomial_basis(group, &k1)))
+                goto err;
+        } else {
+            basis_name = SN_X9_62_ppBasis;
+            if (!TEST_true(EC_GROUP_get_pentanomial_basis(group, &k1, &k2, &k3)))
+                goto err;
+        }
+#endif /* OPENSSL_NO_EC2M */
+    }
+    if (!TEST_true(EC_GROUP_get_curve(group, p, a, b, ctx))
+        || !TEST_true(OSSL_PARAM_BLD_push_utf8_string(bld,
+                          OSSL_PKEY_PARAM_EC_FIELD_TYPE, field_name, 0))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, p))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, a))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, b)))
+        goto err;
+
+    if (EC_GROUP_get0_seed(group) != NULL) {
+        if (!TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
+                           OSSL_PKEY_PARAM_EC_SEED, EC_GROUP_get0_seed(group),
+                           EC_GROUP_get_seed_len(group))))
+            goto err;
+    }
+    if (EC_GROUP_get0_cofactor(group) != NULL) {
+        if (!TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR,
+                                              EC_GROUP_get0_cofactor(group))))
+            goto err;
+    }
+
+    if (!TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
+                       OSSL_PKEY_PARAM_EC_GENERATOR, gen, gen_size))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_ORDER,
+                                             EC_GROUP_get0_order(group))))
+        goto err;
+
+    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_param_fromdata_init(pctx), 0)
+        || !TEST_int_gt(EVP_PKEY_fromdata(pctx, &pkeyparam, params), 0))
+        goto err;
+
+    /*- Check that all the set values are retrievable -*/
+
+    /* There should be no match to a group name since the generator changed */
+    if (!TEST_false(EVP_PKEY_get_utf8_string_param(pkeyparam,
+                        OSSL_PKEY_PARAM_GROUP_NAME, name, sizeof(name),
+                        &name_len)))
+        goto err;
+
+    /* The encoding should be explicit as it has no group */
+    if (!TEST_true(EVP_PKEY_get_utf8_string_param(pkeyparam,
+                       OSSL_PKEY_PARAM_EC_ENCODING,
+                       name, sizeof(name), &name_len))
+        || !TEST_str_eq(name, OSSL_PKEY_EC_ENCODING_EXPLICIT))
+        goto err;
+
+    if (!TEST_true(EVP_PKEY_get_utf8_string_param(pkeyparam,
+                       OSSL_PKEY_PARAM_EC_FIELD_TYPE, name, sizeof(name),
+                       &name_len))
+        || !TEST_str_eq(name, field_name))
+        goto err;
+
+    if (!TEST_true(EVP_PKEY_get_octet_string_param(pkeyparam,
+                       OSSL_PKEY_PARAM_EC_GENERATOR, buf, sizeof(buf), &buf_len))
+        || !TEST_mem_eq(buf, (int)buf_len, gen, gen_size))
+        goto err;
+
+    if (!TEST_true(EVP_PKEY_get_bn_param(pkeyparam, OSSL_PKEY_PARAM_EC_P, &p_out))
+        || !TEST_BN_eq(p_out, p)
+        || !TEST_true(EVP_PKEY_get_bn_param(pkeyparam, OSSL_PKEY_PARAM_EC_A,
+                                            &a_out))
+        || !TEST_BN_eq(a_out, a)
+        || !TEST_true(EVP_PKEY_get_bn_param(pkeyparam, OSSL_PKEY_PARAM_EC_B,
+                                            &b_out))
+        || !TEST_BN_eq(b_out, b)
+        || !TEST_true(EVP_PKEY_get_bn_param(pkeyparam, OSSL_PKEY_PARAM_EC_ORDER,
+                                            &order_out))
+        || !TEST_BN_eq(order_out, EC_GROUP_get0_order(group)))
+        goto err;
+
+    if (EC_GROUP_get0_cofactor(group) != NULL) {
+        if (!TEST_true(EVP_PKEY_get_bn_param(pkeyparam,
+                           OSSL_PKEY_PARAM_EC_COFACTOR, &cofactor_out))
+            || !TEST_BN_eq(cofactor_out, EC_GROUP_get0_cofactor(group)))
+            goto err;
+    }
+    if (EC_GROUP_get0_seed(group) != NULL) {
+        if (!TEST_true(EVP_PKEY_get_octet_string_param(pkeyparam,
+                           OSSL_PKEY_PARAM_EC_SEED, buf, sizeof(buf), &buf_len))
+            || !TEST_mem_eq(buf, buf_len, EC_GROUP_get0_seed(group),
+                            EC_GROUP_get_seed_len(group)))
+            goto err;
+    }
+
+    if (EC_GROUP_get_field_type(group) == NID_X9_62_prime_field) {
+        /* No extra fields should be set for a prime field */
+        if (!TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                            OSSL_PKEY_PARAM_EC_CHAR2_M, &i_out))
+            || !TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                               OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS, &i_out))
+            || !TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                               OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, &i_out))
+            || !TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                               OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, &i_out))
+            || !TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                               OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, &i_out))
+            || !TEST_false(EVP_PKEY_get_utf8_string_param(pkeyparam,
+                               OSSL_PKEY_PARAM_EC_CHAR2_TYPE, name, sizeof(name),
+                               &name_len)))
+            goto err;
+    } else {
+#ifndef OPENSSL_NO_EC2M
+        if (!TEST_true(EVP_PKEY_get_int_param(pkeyparam,
+                           OSSL_PKEY_PARAM_EC_CHAR2_M, &i_out))
+            || !TEST_int_eq(EC_GROUP_get_degree(group), i_out)
+            || !TEST_true(EVP_PKEY_get_utf8_string_param(pkeyparam,
+                              OSSL_PKEY_PARAM_EC_CHAR2_TYPE, name, sizeof(name),
+                              &name_len))
+            || !TEST_str_eq(name, basis_name))
+            goto err;
+
+        if (EC_GROUP_get_basis_type(group) == NID_X9_62_tpBasis) {
+            if (!TEST_true(EVP_PKEY_get_int_param(pkeyparam,
+                               OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS, &i_out))
+                || !TEST_int_eq(k1, i_out)
+                || !TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                                   OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, &i_out))
+                || !TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                                   OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, &i_out))
+                || !TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                                   OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, &i_out)))
+                goto err;
+        } else {
+            if (!TEST_false(EVP_PKEY_get_int_param(pkeyparam,
+                                OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS, &i_out))
+                || !TEST_true(EVP_PKEY_get_int_param(pkeyparam,
+                                  OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, &i_out))
+                || !TEST_int_eq(k1, i_out)
+                || !TEST_true(EVP_PKEY_get_int_param(pkeyparam,
+                                  OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, &i_out))
+                || !TEST_int_eq(k2, i_out)
+                || !TEST_true(EVP_PKEY_get_int_param(pkeyparam,
+                                  OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, &i_out))
+                || !TEST_int_eq(k3, i_out))
+                goto err;
+        }
+#endif /* OPENSSL_NO_EC2M */
+    }
+    if (!TEST_ptr(gettable = EVP_PKEY_gettable_params(pkeyparam))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_GROUP_NAME))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_ENCODING))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_FIELD_TYPE))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_P))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_A))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_B))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_GENERATOR))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_ORDER))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_COFACTOR))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_SEED))
+#ifndef OPENSSL_NO_EC2M
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_CHAR2_M))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_CHAR2_TYPE))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_CHAR2_PP_K1))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_CHAR2_PP_K2))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_CHAR2_PP_K3))
+#endif
+        )
+        goto err;
+    ret = 1;
+err:
+    BN_free(order_out);
+    BN_free(cofactor_out);
+    BN_free(a_out);
+    BN_free(b_out);
+    BN_free(p_out);
+    OSSL_PARAM_BLD_free_params(params);
+    OSSL_PARAM_BLD_free(bld);
+    EVP_PKEY_free(pkeyparam);
+    EVP_PKEY_CTX_free(pctx);
+    return ret;
+}
+
 /*
  * check the EC_METHOD respects the supplied EC_GROUP_set_generator G
  */
@@ -2406,14 +2625,17 @@ static int custom_generator_test(int id)
         || !TEST_mem_eq(b1, bsize, b2, bsize))
         goto err;
 
+    if (!do_test_custom_explicit_fromdata(group, ctx, b1, bsize))
+        goto err;
+
     ret = 1;
 
  err:
-    BN_CTX_end(ctx);
     EC_POINT_free(Q1);
     EC_POINT_free(Q2);
     EC_POINT_free(G2);
     EC_GROUP_free(group);
+    BN_CTX_end(ctx);
     BN_CTX_free(ctx);
     OPENSSL_free(b1);
     OPENSSL_free(b2);
diff --git a/test/endecode_test.c b/test/endecode_test.c
index 5f3fa3d220..0a2f5d0b40 100644
--- a/test/endecode_test.c
+++ b/test/endecode_test.c
@@ -12,7 +12,9 @@
 #include <openssl/pem.h>
 #include <openssl/rsa.h>
 #include <openssl/x509.h>
+#include <openssl/core_names.h>
 #include <openssl/params.h>
+#include <openssl/param_build.h>
 #include <openssl/encoder.h>
 #include <openssl/decoder.h>
 
@@ -21,6 +23,21 @@
 
 #include "testutil.h"
 
+#ifndef OPENSSL_NO_EC
+static BN_CTX *bnctx = NULL;
+static OSSL_PARAM_BLD *bld_prime_nc = NULL;
+static OSSL_PARAM_BLD *bld_prime = NULL;
+static OSSL_PARAM *ec_explicit_prime_params_nc = NULL;
+static OSSL_PARAM *ec_explicit_prime_params_explicit = NULL;
+
+# ifndef OPENSSL_NO_EC2M
+static OSSL_PARAM_BLD *bld_tri_nc = NULL;
+static OSSL_PARAM_BLD *bld_tri = NULL;
+static OSSL_PARAM *ec_explicit_tri_params_nc = NULL;
+static OSSL_PARAM *ec_explicit_tri_params_explicit = NULL;
+# endif
+#endif
+
 /*
  * TODO(3.0) Modify PEM_write_bio_PrivateKey_traditional() to handle
  * provider side EVP_PKEYs (which don't necessarily have an ameth)
@@ -75,7 +92,6 @@ static EVP_PKEY *make_key(const char *type, EVP_PKEY *template,
     return pkey;
 }
 
-
 /* Main test driver */
 
 /*
@@ -754,6 +770,16 @@ IMPLEMENT_TEST_SUITE_PVK(DSA, "DSA")
 #ifndef OPENSSL_NO_EC
 DOMAIN_KEYS(EC);
 IMPLEMENT_TEST_SUITE(EC, "EC")
+DOMAIN_KEYS(ECExplicitPrimeNamedCurve);
+IMPLEMENT_TEST_SUITE(ECExplicitPrimeNamedCurve, "EC")
+DOMAIN_KEYS(ECExplicitPrime2G);
+IMPLEMENT_TEST_SUITE(ECExplicitPrime2G, "EC")
+# ifndef OPENSSL_NO_EC2M
+DOMAIN_KEYS(ECExplicitTriNamedCurve);
+IMPLEMENT_TEST_SUITE(ECExplicitTriNamedCurve, "EC")
+DOMAIN_KEYS(ECExplicitTri2G);
+IMPLEMENT_TEST_SUITE(ECExplicitTri2G, "EC")
+# endif
 KEYS(ED25519);
 IMPLEMENT_TEST_SUITE(ED25519, "ED25519")
 KEYS(ED448);
@@ -774,6 +800,189 @@ IMPLEMENT_TEST_SUITE_PVK(RSA, "RSA")
 # endif
 #endif
 
+#ifndef OPENSSL_NO_EC
+/* Explicit parameters that match a named curve */
+static int do_create_ec_explicit_prime_params(OSSL_PARAM_BLD *bld,
+                                              const unsigned char *gen,
+                                              size_t gen_len)
+{
+    BIGNUM *a, *b, *prime, *order;
+
+    /* Curve prime256v1 */
+    static const unsigned char prime_data[] = {
+        0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff
+    };
+    static const unsigned char a_data[] = {
+        0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xfc
+    };
+    static const unsigned char b_data[] = {
+        0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7,
+        0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98, 0x86, 0xbc,
+        0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, 0xb0, 0xf6,
+        0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b
+    };
+    static const unsigned char seed[] = {
+        0xc4, 0x9d, 0x36, 0x08, 0x86, 0xe7, 0x04, 0x93,
+        0x6a, 0x66, 0x78, 0xe1, 0x13, 0x9d, 0x26, 0xb7,
+        0x81, 0x9f, 0x7e, 0x90
+    };
+    static const unsigned char order_data[] = {
+        0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
+        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e,
+        0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51
+    };
+    return TEST_ptr(a = BN_CTX_get(bnctx))
+           && TEST_ptr(b = BN_CTX_get(bnctx))
+           && TEST_ptr(prime = BN_CTX_get(bnctx))
+           && TEST_ptr(order = BN_CTX_get(bnctx))
+           && TEST_ptr(BN_bin2bn(prime_data, sizeof(prime_data), prime))
+           && TEST_ptr(BN_bin2bn(a_data, sizeof(a_data), a))
+           && TEST_ptr(BN_bin2bn(b_data, sizeof(b_data), b))
+           && TEST_ptr(BN_bin2bn(order_data, sizeof(order_data), order))
+           && TEST_true(OSSL_PARAM_BLD_push_utf8_string(bld,
+                            OSSL_PKEY_PARAM_EC_FIELD_TYPE, SN_X9_62_prime_field,
+                            0))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, prime))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, a))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, b))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld,
+                            OSSL_PKEY_PARAM_EC_ORDER, order))
+           && TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
+                            OSSL_PKEY_PARAM_EC_GENERATOR, gen, gen_len))
+           && TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
+                            OSSL_PKEY_PARAM_EC_SEED, seed, sizeof(seed)))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR,
+                                               BN_value_one()));
+}
+
+static int create_ec_explicit_prime_params_namedcurve(OSSL_PARAM_BLD *bld)
+{
+    static const unsigned char prime256v1_gen[] = {
+        0x04,
+        0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47,
+        0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2,
+        0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0,
+        0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96,
+        0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b,
+        0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16,
+        0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+        0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5
+    };
+    return do_create_ec_explicit_prime_params(bld, prime256v1_gen,
+                                              sizeof(prime256v1_gen));
+}
+
+static int create_ec_explicit_prime_params(OSSL_PARAM_BLD *bld)
+{
+    /* 2G */
+    static const unsigned char prime256v1_gen2[] = {
+        0x04,
+        0xe4, 0x97, 0x08, 0xbe, 0x7d, 0xfa, 0xa2, 0x9a,
+        0xa3, 0x12, 0x6f, 0xe4, 0xe7, 0xd0, 0x25, 0xe3,
+        0x4a, 0xc1, 0x03, 0x15, 0x8c, 0xd9, 0x33, 0xc6,
+        0x97, 0x42, 0xf5, 0xdc, 0x97, 0xb9, 0xd7, 0x31,
+        0xe9, 0x7d, 0x74, 0x3d, 0x67, 0x6a, 0x3b, 0x21,
+        0x08, 0x9c, 0x31, 0x73, 0xf8, 0xc1, 0x27, 0xc9,
+        0xd2, 0xa0, 0xa0, 0x83, 0x66, 0xe0, 0xc9, 0xda,
+        0xa8, 0xc6, 0x56, 0x2b, 0x94, 0xb1, 0xae, 0x55
+    };
+    return do_create_ec_explicit_prime_params(bld, prime256v1_gen2,
+                                              sizeof(prime256v1_gen2));
+}
+
+# ifndef OPENSSL_NO_EC2M
+static int do_create_ec_explicit_trinomial_params(OSSL_PARAM_BLD *bld,
+                                                  const unsigned char *gen,
+                                                  size_t gen_len)
+{
+    BIGNUM *a, *b, *poly, *order, *cofactor;
+    /* sect233k1 characteristic-two-field tpBasis */
+    static const unsigned char poly_data[] = {
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    };
+    static const unsigned char a_data[] = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    };
+    static const unsigned char b_data[] = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+    };
+    static const unsigned char order_data[] = {
+        0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x06, 0x9D, 0x5B, 0xB9, 0x15, 0xBC, 0xD4, 0x6E, 0xFB,
+        0x1A, 0xD5, 0xF1, 0x73, 0xAB, 0xDF
+    };
+    static const unsigned char cofactor_data[]= {
+        0x4
+    };
+    return TEST_ptr(a = BN_CTX_get(bnctx))
+           && TEST_ptr(b = BN_CTX_get(bnctx))
+           && TEST_ptr(poly = BN_CTX_get(bnctx))
+           && TEST_ptr(order = BN_CTX_get(bnctx))
+           && TEST_ptr(cofactor = BN_CTX_get(bnctx))
+           && TEST_ptr(BN_bin2bn(poly_data, sizeof(poly_data), poly))
+           && TEST_ptr(BN_bin2bn(a_data, sizeof(a_data), a))
+           && TEST_ptr(BN_bin2bn(b_data, sizeof(b_data), b))
+           && TEST_ptr(BN_bin2bn(order_data, sizeof(order_data), order))
+           && TEST_ptr(BN_bin2bn(cofactor_data, sizeof(cofactor_data), cofactor))
+           && TEST_true(OSSL_PARAM_BLD_push_utf8_string(bld,
+                            OSSL_PKEY_PARAM_EC_FIELD_TYPE,
+                            SN_X9_62_characteristic_two_field, 0))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, poly))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, a))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, b))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld,
+                            OSSL_PKEY_PARAM_EC_ORDER, order))
+           && TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
+                            OSSL_PKEY_PARAM_EC_GENERATOR, gen, gen_len))
+           && TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR,
+                                               cofactor));
+}
+
+static int create_ec_explicit_trinomial_params_namedcurve(OSSL_PARAM_BLD *bld)
+{
+    static const unsigned char gen[] = {
+        0x04,
+        0x01, 0x72, 0x32, 0xBA, 0x85, 0x3A, 0x7E, 0x73, 0x1A, 0xF1, 0x29, 0xF2,
+        0x2F, 0xF4, 0x14, 0x95, 0x63, 0xA4, 0x19, 0xC2, 0x6B, 0xF5, 0x0A, 0x4C,
+        0x9D, 0x6E, 0xEF, 0xAD, 0x61, 0x26,
+        0x01, 0xDB, 0x53, 0x7D, 0xEC, 0xE8, 0x19, 0xB7, 0xF7, 0x0F, 0x55, 0x5A,
+        0x67, 0xC4, 0x27, 0xA8, 0xCD, 0x9B, 0xF1, 0x8A, 0xEB, 0x9B, 0x56, 0xE0,
+        0xC1, 0x10, 0x56, 0xFA, 0xE6, 0xA3
+    };
+    return do_create_ec_explicit_trinomial_params(bld, gen, sizeof(gen));
+}
+
+static int create_ec_explicit_trinomial_params(OSSL_PARAM_BLD *bld)
+{
+    static const unsigned char gen2[] = {
+        0x04,
+        0x00, 0xd7, 0xba, 0xd0, 0x26, 0x6c, 0x31, 0x6a, 0x78, 0x76, 0x01, 0xd1,
+        0x32, 0x4b, 0x8f, 0x30, 0x29, 0x2d, 0x78, 0x30, 0xca, 0x43, 0xaa, 0xf0,
+        0xa2, 0x5a, 0xd4, 0x0f, 0xb3, 0xf4,
+        0x00, 0x85, 0x4b, 0x1b, 0x8d, 0x50, 0x10, 0xa5, 0x1c, 0x80, 0xf7, 0x86,
+        0x40, 0x62, 0x4c, 0x87, 0xd1, 0x26, 0x7a, 0x9c, 0x5c, 0xe9, 0x82, 0x29,
+        0xd1, 0x67, 0x70, 0x41, 0xea, 0xcb
+    };
+    return do_create_ec_explicit_trinomial_params(bld, gen2, sizeof(gen2));
+}
+# endif /* OPENSSL_NO_EC2M */
+#endif /* OPENSSL_NO_EC */
+
 int setup_tests(void)
 {
     int ok = 1;
@@ -803,7 +1012,28 @@ int setup_tests(void)
         OSSL_PARAM_END
     };
 
+#ifndef OPENSSL_NO_EC
+    if (!TEST_ptr(bnctx = BN_CTX_new_ex(NULL))
+        || !TEST_ptr(bld_prime_nc = OSSL_PARAM_BLD_new())
+        || !TEST_ptr(bld_prime = OSSL_PARAM_BLD_new())
+        || !create_ec_explicit_prime_params_namedcurve(bld_prime_nc)
+        || !create_ec_explicit_prime_params(bld_prime)
+        || !TEST_ptr(ec_explicit_prime_params_nc = OSSL_PARAM_BLD_to_param(bld_prime_nc))
+        || !TEST_ptr(ec_explicit_prime_params_explicit = OSSL_PARAM_BLD_to_param(bld_prime))
+# ifndef OPENSSL_NO_EC2M
+        || !TEST_ptr(bld_tri_nc = OSSL_PARAM_BLD_new())
+        || !TEST_ptr(bld_tri = OSSL_PARAM_BLD_new())
+        || !create_ec_explicit_trinomial_params_namedcurve(bld_tri_nc)
+        || !create_ec_explicit_trinomial_params(bld_tri)
+        || !TEST_ptr(ec_explicit_tri_params_nc = OSSL_PARAM_BLD_to_param(bld_tri_nc))
+        || !TEST_ptr(ec_explicit_tri_params_explicit = OSSL_PARAM_BLD_to_param(bld_tri))
+# endif
+        )
+        return 0;
+#endif
+
     TEST_info("Generating keys...");
+
 #ifndef OPENSSL_NO_DH
     MAKE_DOMAIN_KEYS(DH, "DH", NULL);
     MAKE_DOMAIN_KEYS(DHX, "X9.42 DH", NULL);
@@ -813,6 +1043,12 @@ int setup_tests(void)
 #endif
 #ifndef OPENSSL_NO_EC
     MAKE_DOMAIN_KEYS(EC, "EC", EC_params);
+    MAKE_DOMAIN_KEYS(ECExplicitPrimeNamedCurve, "EC", ec_explicit_prime_params_nc);
+    MAKE_DOMAIN_KEYS(ECExplicitPrime2G, "EC", ec_explicit_prime_params_explicit);
+# ifndef OPENSSL_NO_EC2M
+    MAKE_DOMAIN_KEYS(ECExplicitTriNamedCurve, "EC", ec_explicit_tri_params_nc);
+    MAKE_DOMAIN_KEYS(ECExplicitTri2G, "EC", ec_explicit_tri_params_explicit);
+# endif
     MAKE_KEYS(ED25519, "ED25519", NULL);
     MAKE_KEYS(ED448, "ED448", NULL);
     MAKE_KEYS(X25519, "X25519", NULL);
@@ -836,6 +1072,12 @@ int setup_tests(void)
 #endif
 #ifndef OPENSSL_NO_EC
         ADD_TEST_SUITE(EC);
+        ADD_TEST_SUITE(ECExplicitPrimeNamedCurve);
+        ADD_TEST_SUITE(ECExplicitPrime2G);
+# ifndef OPENSSL_NO_EC2M
+        ADD_TEST_SUITE(ECExplicitTriNamedCurve);
+        ADD_TEST_SUITE(ECExplicitTri2G);
+# endif
         ADD_TEST_SUITE(ED25519);
         ADD_TEST_SUITE(ED448);
         ADD_TEST_SUITE(X25519);
@@ -856,6 +1098,20 @@ int setup_tests(void)
 
 void cleanup_tests(void)
 {
+#ifndef OPENSSL_NO_EC
+    OSSL_PARAM_BLD_free_params(ec_explicit_prime_params_nc);
+    OSSL_PARAM_BLD_free_params(ec_explicit_prime_params_explicit);
+    OSSL_PARAM_BLD_free(bld_prime_nc);
+    OSSL_PARAM_BLD_free(bld_prime);
+# ifndef OPENSSL_NO_EC2M
+    OSSL_PARAM_BLD_free_params(ec_explicit_tri_params_nc);
+    OSSL_PARAM_BLD_free_params(ec_explicit_tri_params_explicit);
+    OSSL_PARAM_BLD_free(bld_tri_nc);
+    OSSL_PARAM_BLD_free(bld_tri);
+# endif
+    BN_CTX_free(bnctx);
+#endif /* OPENSSL_NO_EC */
+
 #ifndef OPENSSL_NO_DH
     FREE_DOMAIN_KEYS(DH);
     FREE_DOMAIN_KEYS(DHX);
@@ -865,6 +1121,12 @@ void cleanup_tests(void)
 #endif
 #ifndef OPENSSL_NO_EC
     FREE_DOMAIN_KEYS(EC);
+    FREE_DOMAIN_KEYS(ECExplicitPrimeNamedCurve);
+    FREE_DOMAIN_KEYS(ECExplicitPrime2G);
+# ifndef OPENSSL_NO_EC2M
+    FREE_DOMAIN_KEYS(ECExplicitTriNamedCurve);
+    FREE_DOMAIN_KEYS(ECExplicitTri2G);
+# endif
     FREE_KEYS(ED25519);
     FREE_KEYS(ED448);
     FREE_KEYS(X25519);
diff --git a/test/recipes/15-test_genec.t b/test/recipes/15-test_genec.t
index 20ddd4026d..2dfed387ca 100644
--- a/test/recipes/15-test_genec.t
+++ b/test/recipes/15-test_genec.t
@@ -29,30 +29,12 @@ sub supported_pass {
     ok(run(@_), $str);
 }
 
-sub unsupported_pass {
-    my $str = shift;
- TODO: {
-        local $TODO = "Currently not supported";
-
-        ok(run(@_), $str);
-    }
-}
-
 sub supported_fail {
     my $str = shift;
 
     ok(!run(@_), $str);
 }
 
-sub unsupported_fail {
-    my $str = shift;
- TODO: {
-        local $TODO = "Currently not supported";
-
-        ok(!run(@_), $str);
-    }
-}
-
 setup("test_genec");
 
 plan skip_all => "This test is unsupported in a no-ec build"
@@ -183,7 +165,7 @@ push(@curve_list, @curve_aliases);
 my %params_encodings =
     (
      'named_curve'      => \&supported_pass,
-     'explicit'         => \&unsupported_pass
+     'explicit'         => \&supported_pass
     );
 
 my @output_formats = ('PEM', 'DER');
@@ -273,7 +255,7 @@ subtest "test curves that only support explicit parameters encoding" => sub {
     my %params_encodings =
         (
          'named_curve'      => \&supported_fail,
-         'explicit'         => \&unsupported_pass
+         'explicit'         => \&supported_pass
         );
 
     foreach my $curvename (@explicit_only_curves) {
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 2d85c13eef..549ab0a7c7 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5271,3 +5271,4 @@ OSSL_STORE_INFO_get1_PUBKEY             ?	3_0_0	EXIST::FUNCTION:
 PEM_read_bio_PUBKEY_ex                  ?	3_0_0	EXIST::FUNCTION:
 PEM_read_PUBKEY_ex                      ?	3_0_0	EXIST::FUNCTION:STDIO
 PEM_read_bio_Parameters_ex              ?	3_0_0	EXIST::FUNCTION:
+EC_GROUP_new_from_params                ?	3_0_0	EXIST::FUNCTION:EC


More information about the openssl-commits mailing list