[openssl] master update

Matt Caswell matt at openssl.org
Fri Jun 19 09:31:19 UTC 2020


The branch master has been updated
       via  48e971dd9f88933a7f77f5051a8b79b9e17892a9 (commit)
       via  e09f8d256f60fd0af62e510f3eaab9e9936f3a6a (commit)
       via  db9592c1f723841586960912c387a925e4547a26 (commit)
       via  6136ecaa955506ff5f5fcdbc69976914418d561b (commit)
       via  0c13cdf835086a7bd29c32c55e7675e5a8827a31 (commit)
       via  260009d877bfd6fe75aef08ecf4c366127f1f78e (commit)
       via  90929138d73ae46fe2fa3014028ab010043af23e (commit)
       via  3c49e4ff519abee92d9557eca8653ab82cd5787c (commit)
       via  381f3f3bbc930abb98ddc6ddff847f2d55e73a0c (commit)
       via  023b188ca553aa4318d8e7021e3abbbb98833410 (commit)
       via  11a1b341f3bc6a0afe75f9432f623026624fb720 (commit)
       via  9d2d857f135abd281591ee0c2b58e01a710c3cea (commit)
       via  82ec09ec6d4e35ef359a7cb22c0cb46662f18155 (commit)
       via  72bfc9585891cffd29eb683ae5fb3181d62b9d33 (commit)
      from  edeaa96ae6aa9b5e0ba5fe98a7258086767a7887 (commit)


- Log -----------------------------------------------------------------
commit 48e971dd9f88933a7f77f5051a8b79b9e17892a9
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Jun 16 15:30:46 2020 +0100

    Create defines for TLS Group Ids
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit e09f8d256f60fd0af62e510f3eaab9e9936f3a6a
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jun 11 16:47:50 2020 +0100

    Don't send supported groups if no-ec and we're doing DTLS
    
    The supported_groups extension only supported EC groups in DTLS.
    Therefore we shouldn't send it in a no-ec build.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit db9592c1f723841586960912c387a925e4547a26
Author: Matt Caswell <matt at openssl.org>
Date:   Fri May 22 14:11:43 2020 +0100

    Provider a better error message if we fail to copy parameters
    
    If EVP_PKEY_copy_parameters() failed in libssl we did not provide a very
    helpful error message. We provide a better one.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 6136ecaa955506ff5f5fcdbc69976914418d561b
Author: Matt Caswell <matt at openssl.org>
Date:   Fri May 22 14:09:13 2020 +0100

    Make sure we save the copy function when registering a new Keymgmt
    
    If a provider had a "copy" function in the its keymgmt definition we
    were ignoring it.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 0c13cdf835086a7bd29c32c55e7675e5a8827a31
Author: Matt Caswell <matt at openssl.org>
Date:   Thu May 21 17:59:47 2020 +0100

    Write a test provider to test the TLS-GROUPS capability
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 260009d877bfd6fe75aef08ecf4c366127f1f78e
Author: Matt Caswell <matt at openssl.org>
Date:   Thu May 21 16:36:32 2020 +0100

    Update the various SSL group getting and setting functions
    
    A number of these functions returned a NID or an array of NIDs for the
    groups. Now that groups can come from the providers we do not necessarily
    know the NID. Therefore we need to handle this in a clean way.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 90929138d73ae46fe2fa3014028ab010043af23e
Author: Matt Caswell <matt at openssl.org>
Date:   Thu May 21 16:16:41 2020 +0100

    Add some missing OSSL_PKEY_PARAM_GROUP_NAME  documentation
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 3c49e4ff519abee92d9557eca8653ab82cd5787c
Author: Matt Caswell <matt at openssl.org>
Date:   Thu May 21 15:57:35 2020 +0100

    Add documentation about Capabilities
    
    Document the OSSL_PROVIDER_get_capabilities() function as well as the
    provider side support for capabilities.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 381f3f3bbc930abb98ddc6ddff847f2d55e73a0c
Author: Matt Caswell <matt at openssl.org>
Date:   Wed May 20 14:47:39 2020 +0100

    Make EVP_PKEY_CTX_[get|set]_group_name work for ECX too
    
    The previous commits made EVP_PKEY_CTX_[get|set]_group_name work for
    EC and DH keys. We now extend this to ECX. Even though that keys with
    these key types only have one group we still allow it to be explicitly
    set so that we have only one codepath for all keys. Setting the group
    name for these types of keys is optional, but if you do so it must have
    the correct name.
    
    Additionally we enable parameter generation for these keys. Parameters
    aren't actually needed for this key type, but for the same reasons as
    above (to ensure a single codepath for users of these algorithms) we
    enable it anyway.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 023b188ca553aa4318d8e7021e3abbbb98833410
Author: Matt Caswell <matt at openssl.org>
Date:   Wed May 20 14:46:22 2020 +0100

    Make EVP_PKEY_CTX_[get|set]_group_name work for DH too
    
    The previous commit added the EVP_PKEY_CTX_[get|set]_group_name
    functions to work with EC groups. We now extend that to also work for
    DH.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 11a1b341f3bc6a0afe75f9432f623026624fb720
Author: Matt Caswell <matt at openssl.org>
Date:   Tue May 19 15:24:25 2020 +0100

    Make EVP_PKEY_CTX_[get|set]_ec_paramgen_curve_name more generic
    
    We rename these function to EVP_PKEY_CTX_get_group_name and
    EVP_PKEY_CTX_set_group_name so that they can be used for other algorithms
    other than EC.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 9d2d857f135abd281591ee0c2b58e01a710c3cea
Author: Matt Caswell <matt at openssl.org>
Date:   Mon May 18 23:37:18 2020 +0100

    Modify libssl to discover supported groups based on available providers
    
    Now that we have added the TLS-GROUP capability to the default provider
    we can use that to discover the supported group list based on the loaded
    providers.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 82ec09ec6d4e35ef359a7cb22c0cb46662f18155
Author: Matt Caswell <matt at openssl.org>
Date:   Mon May 18 15:13:09 2020 +0100

    Add the OSSL_PROVIDER_get_capabilities() API function
    
    Provide a function to applications to query the capabilities that a
    provider can perform.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

commit 72bfc9585891cffd29eb683ae5fb3181d62b9d33
Author: Matt Caswell <matt at openssl.org>
Date:   Mon May 18 14:11:06 2020 +0100

    Add the concept of "Capabilities" to the default and fips providers
    
    With capabilities we can query a provider about what it can do.
    Initially we support a "TLS-GROUP" capability.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11914)

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

Summary of changes:
 crypto/dh/dh_lib.c                             |   4 +-
 crypto/ec/ec_ameth.c                           |   2 +-
 crypto/ec/ec_backend.c                         |   2 +-
 crypto/ec/ec_ctrl.c                            |  44 +--
 crypto/err/openssl.txt                         |   1 +
 crypto/evp/evp_lib.c                           |  69 ++++
 crypto/evp/keymgmt_meth.c                      |   4 +
 crypto/evp/p_lib.c                             |   2 +-
 crypto/evp/pmeth_gn.c                          |   2 +-
 crypto/evp/pmeth_lib.c                         |   8 +-
 crypto/ffc/ffc_backend.c                       |   2 +-
 crypto/ffc/ffc_params.c                        |   2 +-
 crypto/provider.c                              |   9 +-
 crypto/provider_core.c                         |  14 +
 doc/internal/man3/ossl_provider_new.pod        |  17 +-
 doc/man3/EVP_PKEY_CTX_ctrl.pod                 |  38 +-
 doc/man3/EVP_PKEY_gettable_params.pod          |   2 +-
 doc/man3/OSSL_PROVIDER.pod                     |  22 +-
 doc/man3/SSL_CTX_set1_curves.pod               |  26 +-
 doc/man7/EVP_PKEY-DH.pod                       |   2 +-
 doc/man7/EVP_PKEY-EC.pod                       |   8 +-
 doc/man7/EVP_PKEY-X25519.pod                   |   6 +
 doc/man7/provider-base.pod                     |  78 +++++
 include/internal/provider.h                    |   4 +
 include/internal/tlsgroups.h                   |  49 +++
 include/openssl/core_names.h                   |  16 +-
 include/openssl/core_numbers.h                 |  11 +-
 include/openssl/ec.h                           |   4 -
 include/openssl/evp.h                          |   3 +
 include/openssl/provider.h                     |   4 +
 include/openssl/sslerr.h                       |   1 +
 providers/common/build.info                    |   2 +-
 providers/common/capabilities.c                | 177 ++++++++++
 providers/common/include/prov/providercommon.h |   3 +
 providers/defltprov.c                          |   2 +
 providers/fips/fipsprov.c                      |   1 +
 providers/fips/self_test_data.inc              |   4 +-
 providers/implementations/keymgmt/dh_kmgmt.c   |   6 +-
 providers/implementations/keymgmt/ec_kmgmt.c   |   8 +-
 providers/implementations/keymgmt/ecx_kmgmt.c  |  85 ++++-
 ssl/s3_lib.c                                   |  93 +----
 ssl/ssl_err.c                                  |   2 +
 ssl/ssl_lib.c                                  |  22 +-
 ssl/ssl_local.h                                |  54 +--
 ssl/statem/extensions_clnt.c                   |  14 +-
 ssl/statem/extensions_srvr.c                   |   4 +-
 ssl/statem/statem_srvr.c                       |   4 +-
 ssl/t1_lib.c                                   | 447 ++++++++++++++++++------
 test/acvp_test.c                               |  12 +-
 test/build.info                                |   2 +-
 test/dsatest.c                                 |   2 +-
 test/evp_pkey_provided_test.c                  |  16 +-
 test/filterprov.c                              |   9 +
 test/ssl-tests/20-cert-select.cnf              |   2 -
 test/ssl-tests/20-cert-select.cnf.in           |   2 -
 test/sslapitest.c                              |  58 +++
 test/tls-provider.c                            | 466 +++++++++++++++++++++++++
 util/libcrypto.num                             |   5 +-
 58 files changed, 1596 insertions(+), 362 deletions(-)
 create mode 100644 include/internal/tlsgroups.h
 create mode 100644 providers/common/capabilities.c
 create mode 100644 test/tls-provider.c

diff --git a/crypto/dh/dh_lib.c b/crypto/dh/dh_lib.c
index 3a523c3591..2a3921a137 100644
--- a/crypto/dh/dh_lib.c
+++ b/crypto/dh/dh_lib.c
@@ -500,7 +500,7 @@ int EVP_PKEY_CTX_set_dh_rfc5114(EVP_PKEY_CTX *ctx, int gen)
     if (name == NULL)
         return 0;
 
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_DH_GROUP,
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
                                             (void *)name, 0);
     *p++ = OSSL_PARAM_construct_end();
     return EVP_PKEY_CTX_set_params(ctx, params);
@@ -531,7 +531,7 @@ int EVP_PKEY_CTX_set_dh_nid(EVP_PKEY_CTX *ctx, int nid)
     if (name == NULL)
         return 0;
 
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_DH_GROUP,
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
                                             (void *)name, 0);
     *p++ = OSSL_PARAM_construct_end();
     return EVP_PKEY_CTX_set_params(ctx, params);
diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c
index 6ccaef3815..bde8458274 100644
--- a/crypto/ec/ec_ameth.c
+++ b/crypto/ec/ec_ameth.c
@@ -611,7 +611,7 @@ int ecparams_to_params(const EC_KEY *eckey, OSSL_PARAM_BLD *tmpl)
         if ((curve_name = OBJ_nid2sn(curve_nid)) == NULL)
             return 0;
 
-        if (!OSSL_PARAM_BLD_push_utf8_string(tmpl, OSSL_PKEY_PARAM_EC_NAME, curve_name, 0))
+        if (!OSSL_PARAM_BLD_push_utf8_string(tmpl, OSSL_PKEY_PARAM_GROUP_NAME, curve_name, 0))
             return 0;
     }
 
diff --git a/crypto/ec/ec_backend.c b/crypto/ec/ec_backend.c
index fb6497b084..b12a9411d2 100644
--- a/crypto/ec/ec_backend.c
+++ b/crypto/ec/ec_backend.c
@@ -173,7 +173,7 @@ int ec_key_domparams_fromdata(EC_KEY *ec, const OSSL_PARAM params[])
     if (ec == NULL)
         return 0;
 
-    param_ec_name = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_NAME);
+    param_ec_name = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
     if (param_ec_name == NULL) {
         /* explicit parameters */
 
diff --git a/crypto/ec/ec_ctrl.c b/crypto/ec/ec_ctrl.c
index 9e12b9a159..b47d7b606c 100644
--- a/crypto/ec/ec_ctrl.c
+++ b/crypto/ec/ec_ctrl.c
@@ -421,48 +421,6 @@ int EVP_PKEY_CTX_get0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char **pukm)
     return (int)ukmlen;
 }
 
-int EVP_PKEY_CTX_set_ec_paramgen_curve_name(EVP_PKEY_CTX *ctx,
-                                            const char *name)
-{
-    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
-    OSSL_PARAM *p = params;
-
-    if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    if (name == NULL)
-        return -1;
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_EC_NAME,
-                                            (char *)name, 0);
-    return EVP_PKEY_CTX_set_params(ctx, params);
-}
-
-int EVP_PKEY_CTX_get_ec_paramgen_curve_name(EVP_PKEY_CTX *ctx,
-                                            char *name, size_t namelen)
-{
-    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
-    OSSL_PARAM *p = params;
-
-    if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    if (name == NULL)
-        return -1;
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_EC_NAME,
-                                            name, namelen);
-    if (!EVP_PKEY_CTX_get_params(ctx, params))
-        return -1;
-    return 1;
-}
-
 #ifndef FIPS_MODULE
 int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid)
 {
@@ -483,6 +441,6 @@ int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid)
                                  EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID,
                                  nid, NULL);
 
-    return EVP_PKEY_CTX_set_ec_paramgen_curve_name(ctx, OBJ_nid2sn(nid));
+    return EVP_PKEY_CTX_set_group_name(ctx, OBJ_nid2sn(nid));
 }
 #endif
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 40148e8a2c..515dfc3f11 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -3098,6 +3098,7 @@ SSL_R_CONNECTION_TYPE_NOT_SET:144:connection type not set
 SSL_R_CONTEXT_NOT_DANE_ENABLED:167:context not dane enabled
 SSL_R_COOKIE_GEN_CALLBACK_FAILURE:400:cookie gen callback failure
 SSL_R_COOKIE_MISMATCH:308:cookie mismatch
+SSL_R_COPY_PARAMETERS_FAILED:296:copy parameters failed
 SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED:206:\
 	custom ext handler already installed
 SSL_R_DANE_ALREADY_ENABLED:172:dane already enabled
diff --git a/crypto/evp/evp_lib.c b/crypto/evp/evp_lib.c
index 229485102a..ef978ec6f1 100644
--- a/crypto/evp/evp_lib.c
+++ b/crypto/evp/evp_lib.c
@@ -14,6 +14,7 @@
 #include <openssl/params.h>
 #include <openssl/core_names.h>
 #include <openssl/dh.h>
+#include <openssl/ec.h>
 #include "crypto/evp.h"
 #include "internal/provider.h"
 #include "evp_local.h"
@@ -940,3 +941,71 @@ int EVP_hex2ctrl(int (*cb)(void *ctx, int cmd, void *buf, size_t buflen),
     OPENSSL_free(bin);
     return rv;
 }
+
+int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name)
+{
+    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    OSSL_PARAM *p = params;
+
+    if (ctx == NULL) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+
+    if (!EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
+#ifndef FIPS_MODULE
+        int nid;
+
+        /* Could be a legacy key, try and convert to a ctrl */
+        if (ctx->pmeth != NULL && (nid = OBJ_txt2nid(name)) != NID_undef) {
+# ifndef OPENSSL_NO_DH
+            if (ctx->pmeth->pkey_id == EVP_PKEY_DH)
+                return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH,
+                                         EVP_PKEY_OP_PARAMGEN
+                                         | EVP_PKEY_OP_KEYGEN,
+                                         EVP_PKEY_CTRL_DH_NID, nid, NULL);
+# endif
+# ifndef OPENSSL_NO_EC
+            if (ctx->pmeth->pkey_id == EVP_PKEY_EC)
+                return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                         EVP_PKEY_OP_PARAMGEN|EVP_PKEY_OP_KEYGEN,
+                                         EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID,
+                                         nid, NULL);
+# endif
+        }
+#endif
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+
+    if (name == NULL)
+        return -1;
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                            (char *)name, 0);
+    return EVP_PKEY_CTX_set_params(ctx, params);
+}
+
+int EVP_PKEY_CTX_get_group_name(EVP_PKEY_CTX *ctx, char *name, size_t namelen)
+{
+    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    OSSL_PARAM *p = params;
+
+    if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
+        /* There is no legacy support for this */
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+
+    if (name == NULL)
+        return -1;
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                            name, namelen);
+    if (!EVP_PKEY_CTX_get_params(ctx, params))
+        return -1;
+    return 1;
+}
diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c
index b75d02f136..ab5e00dfba 100644
--- a/crypto/evp/keymgmt_meth.c
+++ b/crypto/evp/keymgmt_meth.c
@@ -124,6 +124,10 @@ static void *keymgmt_from_dispatch(int name_id,
             if (keymgmt->has == NULL)
                 keymgmt->has = OSSL_get_OP_keymgmt_has(fns);
             break;
+        case OSSL_FUNC_KEYMGMT_COPY:
+            if (keymgmt->copy == NULL)
+                keymgmt->copy = OSSL_get_OP_keymgmt_copy(fns);
+            break;
         case OSSL_FUNC_KEYMGMT_VALIDATE:
             if (keymgmt->validate == NULL)
                 keymgmt->validate = OSSL_get_OP_keymgmt_validate(fns);
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index 0b067c8a8c..4dc1e0a5b2 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -1000,7 +1000,7 @@ static int get_ec_curve_name_cb(const OSSL_PARAM params[], void *arg)
 {
     const OSSL_PARAM *p = NULL;
 
-    if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_NAME)) != NULL)
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME)) != NULL)
         return OSSL_PARAM_get_utf8_string(p, arg, 0);
 
     /* If there is no curve name, this is not an EC key */
diff --git a/crypto/evp/pmeth_gn.c b/crypto/evp/pmeth_gn.c
index 411f270b49..1ab309329d 100644
--- a/crypto/evp/pmeth_gn.c
+++ b/crypto/evp/pmeth_gn.c
@@ -228,7 +228,7 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
     {
         char curve_name[OSSL_MAX_NAME_SIZE] = "";
 
-        if (!EVP_PKEY_get_utf8_string_param(*ppkey, OSSL_PKEY_PARAM_EC_NAME,
+        if (!EVP_PKEY_get_utf8_string_param(*ppkey, OSSL_PKEY_PARAM_GROUP_NAME,
                                             curve_name, sizeof(curve_name),
                                             NULL)
             || strcmp(curve_name, "SM2") != 0)
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index dd6556c891..52c304227b 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -605,7 +605,6 @@ int EVP_PKEY_CTX_set_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
     return 0;
 }
 
-#ifndef FIPS_MODULE
 int EVP_PKEY_CTX_get_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 {
     if (EVP_PKEY_CTX_IS_DERIVE_OP(ctx)
@@ -629,6 +628,7 @@ int EVP_PKEY_CTX_get_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
     return 0;
 }
 
+#ifndef FIPS_MODULE
 const OSSL_PARAM *EVP_PKEY_CTX_gettable_params(EVP_PKEY_CTX *ctx)
 {
     if (EVP_PKEY_CTX_IS_DERIVE_OP(ctx)
@@ -1055,16 +1055,16 @@ static int legacy_ctrl_str_to_param(EVP_PKEY_CTX *ctx, const char *name,
         name = OSSL_PKEY_PARAM_FFC_TYPE;
         value = dh_gen_type_id2name(atoi(value));
     } else if (strcmp(name, "dh_param") == 0)
-        name = OSSL_PKEY_PARAM_DH_GROUP;
+        name = OSSL_PKEY_PARAM_GROUP_NAME;
     else if (strcmp(name, "dh_rfc5114") == 0) {
-        name = OSSL_PKEY_PARAM_DH_GROUP;
+        name = OSSL_PKEY_PARAM_GROUP_NAME;
         value = ffc_named_group_from_uid(atoi(value));
     } else if (strcmp(name, "dh_pad") == 0)
         name = OSSL_EXCHANGE_PARAM_PAD;
 # endif
 # ifndef OPENSSL_NO_EC
     else if (strcmp(name, "ec_paramgen_curve") == 0)
-        name = OSSL_PKEY_PARAM_EC_NAME;
+        name = OSSL_PKEY_PARAM_GROUP_NAME;
     else if (strcmp(name, "ecdh_cofactor_mode") == 0)
         name = OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE;
     else if (strcmp(name, "ecdh_kdf_md") == 0)
diff --git a/crypto/ffc/ffc_backend.c b/crypto/ffc/ffc_backend.c
index 49f42d70d0..6e269ebf56 100644
--- a/crypto/ffc/ffc_backend.c
+++ b/crypto/ffc/ffc_backend.c
@@ -27,7 +27,7 @@ int ffc_params_fromdata(FFC_PARAMS *ffc, const OSSL_PARAM params[])
     if (ffc == NULL)
         return 0;
 
-    prm  = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GROUP);
+    prm  = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
     if (prm != NULL) {
         if (prm->data_type != OSSL_PARAM_UTF8_STRING)
             goto err;
diff --git a/crypto/ffc/ffc_params.c b/crypto/ffc/ffc_params.c
index 0796d34337..d70aeea35b 100644
--- a/crypto/ffc/ffc_params.c
+++ b/crypto/ffc/ffc_params.c
@@ -265,7 +265,7 @@ int ffc_params_todata(const FFC_PARAMS *ffc, OSSL_PARAM_BLD *bld,
 
         if (name == NULL
             || !ossl_param_build_set_utf8_string(bld, params,
-                                                 OSSL_PKEY_PARAM_DH_GROUP,
+                                                 OSSL_PKEY_PARAM_GROUP_NAME,
                                                  name))
             return 0;
 #else
diff --git a/crypto/provider.c b/crypto/provider.c
index 02002a5f95..8646aef771 100644
--- a/crypto/provider.c
+++ b/crypto/provider.c
@@ -57,7 +57,6 @@ int OSSL_PROVIDER_get_params(const OSSL_PROVIDER *prov, OSSL_PARAM params[])
     return ossl_provider_get_params(prov, params);
 }
 
-
 const OSSL_ALGORITHM *OSSL_PROVIDER_query_operation(const OSSL_PROVIDER *prov,
                                                     int operation_id,
                                                     int *no_cache)
@@ -70,6 +69,14 @@ void *OSSL_PROVIDER_get0_provider_ctx(const OSSL_PROVIDER *prov)
     return ossl_provider_prov_ctx(prov);
 }
 
+int OSSL_PROVIDER_get_capabilities(const OSSL_PROVIDER *prov,
+                                   const char *capability,
+                                   OSSL_CALLBACK *cb,
+                                   void *arg)
+{
+    return ossl_provider_get_capabilities(prov, capability, cb, arg);
+}
+
 int OSSL_PROVIDER_add_builtin(OPENSSL_CTX *libctx, const char *name,
                               OSSL_provider_init_fn *init_fn)
 {
diff --git a/crypto/provider_core.c b/crypto/provider_core.c
index f7af51a297..cfaa09ff7b 100644
--- a/crypto/provider_core.c
+++ b/crypto/provider_core.c
@@ -70,6 +70,7 @@ struct ossl_provider_st {
     OSSL_provider_teardown_fn *teardown;
     OSSL_provider_gettable_params_fn *gettable_params;
     OSSL_provider_get_params_fn *get_params;
+    OSSL_provider_get_capabilities_fn *get_capabilities;
     OSSL_provider_query_operation_fn *query_operation;
 
     /*
@@ -543,6 +544,10 @@ static int provider_activate(OSSL_PROVIDER *prov)
             prov->get_params =
                 OSSL_get_provider_get_params(provider_dispatch);
             break;
+        case OSSL_FUNC_PROVIDER_GET_CAPABILITIES:
+            prov->get_capabilities =
+                OSSL_get_provider_get_capabilities(provider_dispatch);
+            break;
         case OSSL_FUNC_PROVIDER_QUERY_OPERATION:
             prov->query_operation =
                 OSSL_get_provider_query_operation(provider_dispatch);
@@ -820,6 +825,15 @@ int ossl_provider_get_params(const OSSL_PROVIDER *prov, OSSL_PARAM params[])
         ? 0 : prov->get_params(prov->provctx, params);
 }
 
+int ossl_provider_get_capabilities(const OSSL_PROVIDER *prov,
+                                   const char *capability,
+                                   OSSL_CALLBACK *cb,
+                                   void *arg)
+{
+    return prov->get_capabilities == NULL
+        ? 0 : prov->get_capabilities(prov->provctx, capability, cb, arg);
+}
+
 
 const OSSL_ALGORITHM *ossl_provider_query_operation(const OSSL_PROVIDER *prov,
                                                     int operation_id,
diff --git a/doc/internal/man3/ossl_provider_new.pod b/doc/internal/man3/ossl_provider_new.pod
index 7bc5a38669..6a43c68bea 100644
--- a/doc/internal/man3/ossl_provider_new.pod
+++ b/doc/internal/man3/ossl_provider_new.pod
@@ -14,7 +14,8 @@ ossl_provider_module_name, ossl_provider_module_path,
 ossl_provider_library_context,
 ossl_provider_teardown, ossl_provider_gettable_params,
 ossl_provider_get_params, ossl_provider_query_operation,
-ossl_provider_set_operation_bit, ossl_provider_test_operation_bit
+ossl_provider_set_operation_bit, ossl_provider_test_operation_bit,
+ossl_provider_get_capabilities
 - internal provider routines
 
 =head1 SYNOPSIS
@@ -60,6 +61,10 @@ ossl_provider_set_operation_bit, ossl_provider_test_operation_bit
  void ossl_provider_teardown(const OSSL_PROVIDER *prov);
  const OSSL_PARAM *ossl_provider_gettable_params(const OSSL_PROVIDER *prov);
  int ossl_provider_get_params(const OSSL_PROVIDER *prov, OSSL_PARAM params[]);
+ int ossl_provider_get_capabilities(const OSSL_PROVIDER *prov,
+                                   const char *capability,
+                                   OSSL_CALLBACK *cb,
+                                   void *arg);
  const OSSL_ALGORITHM *ossl_provider_query_operation(const OSSL_PROVIDER *prov,
                                                      int operation_id,
                                                      int *no_cache);
@@ -208,6 +213,12 @@ responder.
 It should treat the given I<OSSL_PARAM> array as described in
 L<OSSL_PARAM(3)>.
 
+ossl_provider_get_capabilities() calls the provider's I<get_capabilities> function,
+if the provider has one. It provides the name of the I<capability> and a
+callback I<cb> parameter to call for each capability that has a matching name in
+the provider. The callback gets passed OSSL_PARAM details about the capability as
+well as the caller supplied argument I<arg>.
+
 ossl_provider_query_operation() calls the provider's
 I<query_operation> function, if the provider has one.
 It should return an array of I<OSSL_ALGORITHM> for the given
@@ -285,6 +296,10 @@ If this function isn't available in the provider, 0 is returned.
 ossl_provider_set_operation_bit() and ossl_provider_test_operation_bit()
 return 1 on success, or 0 on error.
 
+ossl_provider_get_capabilities() returns 1 on success, or 0 on error.
+If this function isn't available in the provider or the provider does not
+support the requested capability then 0 is returned.
+
 =head1 SEE ALSO
 
 L<OSSL_PROVIDER(3)>, L<provider(7)>, L<openssl(1)>
diff --git a/doc/man3/EVP_PKEY_CTX_ctrl.pod b/doc/man3/EVP_PKEY_CTX_ctrl.pod
index db91f01038..1e836fc30e 100644
--- a/doc/man3/EVP_PKEY_CTX_ctrl.pod
+++ b/doc/man3/EVP_PKEY_CTX_ctrl.pod
@@ -9,6 +9,8 @@ EVP_PKEY_CTX_md,
 EVP_PKEY_CTX_set_signature_md,
 EVP_PKEY_CTX_get_signature_md,
 EVP_PKEY_CTX_set_mac_key,
+EVP_PKEY_CTX_set_group_name,
+EVP_PKEY_CTX_get_group_name,
 EVP_PKEY_CTX_set_rsa_padding,
 EVP_PKEY_CTX_get_rsa_padding,
 EVP_PKEY_CTX_set_rsa_pss_saltlen,
@@ -53,8 +55,6 @@ EVP_PKEY_CTX_set_dh_kdf_outlen,
 EVP_PKEY_CTX_get_dh_kdf_outlen,
 EVP_PKEY_CTX_set0_dh_kdf_ukm,
 EVP_PKEY_CTX_get0_dh_kdf_ukm,
-EVP_PKEY_CTX_set_ec_paramgen_curve_name,
-EVP_PKEY_CTX_get_ec_paramgen_curve_name,
 EVP_PKEY_CTX_set_ec_paramgen_curve_nid,
 EVP_PKEY_CTX_set_ec_param_enc,
 EVP_PKEY_CTX_set_ecdh_cofactor_mode,
@@ -88,6 +88,8 @@ EVP_PKEY_CTX_set1_id, EVP_PKEY_CTX_get1_id, EVP_PKEY_CTX_get1_id_len
 
  int EVP_PKEY_CTX_set_mac_key(EVP_PKEY_CTX *ctx, const unsigned char *key,
                               int len);
+ int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name);
+ int EVP_PKEY_CTX_get_group_name(EVP_PKEY_CTX *ctx, char *name, size_t namelen);
 
  #include <openssl/rsa.h>
 
@@ -154,10 +156,6 @@ EVP_PKEY_CTX_set1_id, EVP_PKEY_CTX_get1_id, EVP_PKEY_CTX_get1_id_len
 
  #include <openssl/ec.h>
 
- int EVP_PKEY_CTX_set_ec_paramgen_curve_name(EVP_PKEY_CTX *ctx,
-                                             const char *name);
- int EVP_PKEY_CTX_get_ec_paramgen_curve_name(EVP_PKEY_CTX *ctx,
-                                             char *name, size_t namelen);
  int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid);
  int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx, int param_enc);
  int EVP_PKEY_CTX_set_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx, int cofactor_mode);
@@ -221,6 +219,15 @@ L<EVP_PKEY_new_raw_private_key(3)> or similar functions instead of this macro.
 The EVP_PKEY_CTX_set_mac_key() macro can be used with any of the algorithms
 supported by the L<EVP_PKEY_new_raw_private_key(3)> function.
 
+EVP_PKEY_CTX_set_group_name() sets the group name to I<name> for parameter and
+key generation. For example for EC keys this will set the curve name and for
+DH keys it will set the name of the finite field group.
+
+EVP_PKEY_CTX_get_group_name() finds the group name that's currently
+set with I<ctx>, and writes it to the location that I<name> points at, as long
+as its size I<namelen> is large enough to store that name, including a
+terminating NUL byte.
+
 =head2 RSA parameters
 
 The EVP_PKEY_CTX_set_rsa_padding() function sets the RSA padding mode for I<ctx>.
@@ -524,23 +531,21 @@ by the library and should not be freed by the caller.
 
 =head2 EC parameters
 
-EVP_PKEY_CTX_set_ec_paramgen_curve_name() sets the EC curve to I<name> for EC
-parameter generation.
+Use EVP_PKEY_CTX_set_group_name() (described above) to set the curve name to
+I<name> for parameter and key generation.
 
 EVP_PKEY_CTX_set_ec_paramgen_curve_nid() does the same as
-EVP_PKEY_CTX_set_ec_paramgen_curve_name(), but uses a I<nid> rather than a
-name string.
+EVP_PKEY_CTX_set_group_name(), but is specific to EC and uses a I<nid> rather
+than a name string.
 
-For EC parameter generation, one of EVP_PKEY_CTX_set_ec_paramgen_curve_name()
+For EC parameter generation, one of EVP_PKEY_CTX_set_group_name()
 or EVP_PKEY_CTX_set_ec_paramgen_curve_nid() must be called or an error occurs
 because there is no default curve.
 These function can also be called to set the curve explicitly when
 generating an EC key.
 
-EVP_PKEY_CTX_get_ec_paramgen_curve_name() finds the curve name that's currently
-set with I<ctx>, and writes it to the location that I<name> points at, as long
-as its size I<namelen> is large enough to store that name, including a
-terminating NUL byte.
+EVP_PKEY_CTX_get_group_name() (described above) can be used to obtain the curve
+name that's currently set with I<ctx>.
 
 The EVP_PKEY_CTX_set_ec_param_enc() macro sets the EC parameter encoding to
 I<param_enc> when generating EC parameters or an EC key. The encoding can be
@@ -642,7 +647,8 @@ From OpenSSL 3.0 they are functions.
 EVP_PKEY_CTX_get_rsa_oaep_md_name(), EVP_PKEY_CTX_get_rsa_mgf1_md_name(),
 EVP_PKEY_CTX_set_rsa_mgf1_md_name(), EVP_PKEY_CTX_set_rsa_oaep_md_name(),
 EVP_PKEY_CTX_set_dsa_paramgen_md_props(), EVP_PKEY_CTX_set_dsa_paramgen_gindex(),
-EVP_PKEY_CTX_set_dsa_paramgen_type() and EVP_PKEY_CTX_set_dsa_paramgen_seed()
+EVP_PKEY_CTX_set_dsa_paramgen_type(), EVP_PKEY_CTX_set_dsa_paramgen_seed(),
+EVP_PKEY_CTX_set_group_name() and EVP_PKEY_CTX_get_group_name()
 were added in OpenSSL 3.0.
 
 The EVP_PKEY_CTX_set1_id(), EVP_PKEY_CTX_get1_id() and
diff --git a/doc/man3/EVP_PKEY_gettable_params.pod b/doc/man3/EVP_PKEY_gettable_params.pod
index 87d25c7b99..8f6854a568 100644
--- a/doc/man3/EVP_PKEY_gettable_params.pod
+++ b/doc/man3/EVP_PKEY_gettable_params.pod
@@ -72,7 +72,7 @@ value.
   * is an EC key.
   */
 
- if (!EVP_PKEY_get_utf8_string_param(key, OSSL_PKEY_PARAM_EC_NAME,
+ if (!EVP_PKEY_get_utf8_string_param(key, OSSL_PKEY_PARAM_GROUP_NAME,
                                      curve_name, sizeof(curve_name), &len)) {
    /* Error */
  }
diff --git a/doc/man3/OSSL_PROVIDER.pod b/doc/man3/OSSL_PROVIDER.pod
index d6f0af53c9..63633842fa 100644
--- a/doc/man3/OSSL_PROVIDER.pod
+++ b/doc/man3/OSSL_PROVIDER.pod
@@ -7,7 +7,8 @@ OSSL_PROVIDER, OSSL_PROVIDER_load, OSSL_PROVIDER_unload,
 OSSL_PROVIDER_available, OSSL_PROVIDER_do_all,
 OSSL_PROVIDER_gettable_params, OSSL_PROVIDER_get_params,
 OSSL_PROVIDER_query_operation, OSSL_PROVIDER_get0_provider_ctx,
-OSSL_PROVIDER_add_builtin, OSSL_PROVIDER_name - provider routines
+OSSL_PROVIDER_add_builtin, OSSL_PROVIDER_name,
+OSSL_PROVIDER_get_capabilities - provider routines
 
 =head1 SYNOPSIS
 
@@ -38,6 +39,12 @@ OSSL_PROVIDER_add_builtin, OSSL_PROVIDER_name - provider routines
 
  const char *OSSL_PROVIDER_name(const OSSL_PROVIDER *prov);
 
+ int OSSL_PROVIDER_get_capabilities(const OSSL_PROVIDER *prov,
+                                    const char *capability,
+                                    OSSL_CALLBACK *cb,
+                                    void *arg);
+
+
 =head1 DESCRIPTION
 
 B<OSSL_PROVIDER> is a type that holds internal information about
@@ -104,15 +111,22 @@ have a short lifetime.
 
 OSSL_PROVIDER_name() returns the name of the given provider.
 
+OSSL_PROVIDER_get_capabilities() provides information about the capabilities
+supported by the provider specified in I<prov> with the capability name
+I<capability>. For each capability of that name supported by the provider it
+will call the callback I<cb> and supply a set of B<OSSL_PARAM>s describing the
+capability. It will also pass back the argument I<arg>. For more details about
+capabilities and what they can be used for please see
+L<provider-base(7)/CAPABILTIIES>.
+
 =head1 RETURN VALUES
 
-OSSL_PROVIDER_add() returns 1 on success, or 0 on error.
+OSSL_PROVIDER_add(), OSSL_PROVIDER_unload(), OSSL_PROVIDER_get_params() and
+OSSL_PROVIDER_get_capabilities() return 1 on success, or 0 on error.
 
 OSSL_PROVIDER_load() returns a pointer to a provider object on
 success, or B<NULL> on error.
 
-OSSL_PROVIDER_unload() returns 1 on success, or 0 on error.
-
 OSSL_PROVIDER_available() returns 1 if the named provider is available,
 otherwise 0.
 
diff --git a/doc/man3/SSL_CTX_set1_curves.pod b/doc/man3/SSL_CTX_set1_curves.pod
index b482daace8..3dd0c2a1b4 100644
--- a/doc/man3/SSL_CTX_set1_curves.pod
+++ b/doc/man3/SSL_CTX_set1_curves.pod
@@ -34,7 +34,11 @@ SSL_set1_curves, SSL_set1_curves_list, SSL_get1_curves, SSL_get_shared_curve
 =head1 DESCRIPTION
 
 For all of the functions below that set the supported groups there must be at
-least one group in the list.
+least one group in the list. A number of these functions identify groups via a
+unique integer NID value. However support for some groups may be added by
+external providers. In this case there will be no NID assigned for the group.
+When setting such groups applications should use the "list" form of these
+functions (i.e. SSL_CTX_set1_groups_list() and SSL_set1_groups_list).
 
 SSL_CTX_set1_groups() sets the supported groups for B<ctx> to B<glistlen>
 groups in the array B<glist>. The array consist of all NIDs of groups in
@@ -49,7 +53,8 @@ SSL_CTX_set1_groups_list() sets the supported groups for B<ctx> to
 string B<list>. The string is a colon separated list of group NIDs or
 names, for example "P-521:P-384:P-256:X25519:ffdhe2048". Currently supported
 groups for B<TLSv1.3> are B<P-256>, B<P-384>, B<P-521>, B<X25519>, B<X448>,
-B<ffdhe2048>, B<ffdhe3072>, B<ffdhe4096>, B<ffdhe6144>, B<ffdhe8192>.
+B<ffdhe2048>, B<ffdhe3072>, B<ffdhe4096>, B<ffdhe6144>, B<ffdhe8192>. Support
+for other groups may be added by external providers.
 
 SSL_set1_groups() and SSL_set1_groups_list() are similar except they set
 supported groups for the SSL structure B<ssl>.
@@ -60,17 +65,22 @@ supported groups. The B<groups> parameter can be B<NULL> to simply
 return the number of groups for memory allocation purposes. The
 B<groups> array is in the form of a set of group NIDs in preference
 order. It can return zero if the client did not send a supported groups
-extension.
+extension. If a supported group NID is unknown then the value is set to the
+bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the group.
 
-SSL_get_shared_group() returns shared group B<n> for a server-side
-SSL B<ssl>. If B<n> is -1 then the total number of shared groups is
+SSL_get_shared_group() returns the NID of the shared group B<n> for a
+server-side SSL B<ssl>. If B<n> is -1 then the total number of shared groups is
 returned, which may be zero. Other than for diagnostic purposes,
 most applications will only be interested in the first shared group
 so B<n> is normally set to zero. If the value B<n> is out of range,
-NID_undef is returned.
+NID_undef is returned. If the NID for the shared group is unknown then the value
+is set to the bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the
+group.
 
-SSL_get_negotiated_group() returns the negotiated group on a TLSv1.3 connection
-for key exchange. This can be called by either client or server.
+SSL_get_negotiated_group() returns the NID of the negotiated group on a TLSv1.3
+connection for key exchange. This can be called by either client or server. If
+the NID for the shared group is unknown then the value is set to the bitwise OR
+of TLSEXT_nid_unknown (0x1000000) and the id of the group.
 
 All these functions are implemented as macros.
 
diff --git a/doc/man7/EVP_PKEY-DH.pod b/doc/man7/EVP_PKEY-DH.pod
index 6720417673..f640753bfe 100644
--- a/doc/man7/EVP_PKEY-DH.pod
+++ b/doc/man7/EVP_PKEY-DH.pod
@@ -29,7 +29,7 @@ implementation supports the following:
 
 =over 4
 
-=item "group" (B<OSSL_PKEY_PARAM_DH_GROUP>) <UTF8 string>
+=item "group" (B<OSSL_PKEY_PARAM_GROUP_NAME>) <UTF8 string>
 
 Set or gets a string that associates a B<DH> named safe prime group with known
 values for I<p>, I<q> and I<g>.
diff --git a/doc/man7/EVP_PKEY-EC.pod b/doc/man7/EVP_PKEY-EC.pod
index 85e633ceed..ff074b949d 100644
--- a/doc/man7/EVP_PKEY-EC.pod
+++ b/doc/man7/EVP_PKEY-EC.pod
@@ -16,9 +16,9 @@ The following Import/Export types are available for the built-in EC algorithm:
 
 =over 4
 
-=item "curve-name" (B<OSSL_PKEY_PARAM_EC_NAME>) <utf8 string>
+=item "group" (B<OSSL_PKEY_PARAM_GROUP_NAME>) <utf8 string>
 
-The EC curve name.
+The curve name.
 
 =item "use-cofactor-flag" (B<OSSL_PKEY_PARAM_USE_COFACTOR_ECDH>) <integer>
 
@@ -63,7 +63,7 @@ calling:
 
     EVP_PKEY_keygen_init(gctx);
 
-    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_EC_NAME,
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
                                                  "P-256", 0);
     params[1] = OSSL_PARAM_construct_end();
     EVP_PKEY_CTX_set_params(gctx, params);
@@ -90,7 +90,7 @@ An B<EVP_PKEY> EC CDH (Cofactor Diffie-Hellman) key can be generated with a
 
     EVP_PKEY_keygen_init(gctx);
 
-    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_EC_NAME,
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
                                                  "K-571", 0);
     /*
      * This curve has a cofactor that is not 1 - so setting CDH mode changes
diff --git a/doc/man7/EVP_PKEY-X25519.pod b/doc/man7/EVP_PKEY-X25519.pod
index ebeda8d814..2937f247f5 100644
--- a/doc/man7/EVP_PKEY-X25519.pod
+++ b/doc/man7/EVP_PKEY-X25519.pod
@@ -26,6 +26,12 @@ support the following.
 
 =over 4
 
+=item "group" (B<OSSL_PKEY_PARAM_GROUP_NAME>) <UTF8 string>
+
+This is only supported by X25519 and X448. The group name must be "x25519" or
+"x448" repsectively for those algorithms. This is only present for consistency
+with other key exchange algorithms and is typically not needed.
+
 =item "pub" (B<OSSL_PKEY_PARAM_PUB_KEY>) <octet string>
 
 The public key value.
diff --git a/doc/man7/provider-base.pod b/doc/man7/provider-base.pod
index 081be53a0d..69183cf282 100644
--- a/doc/man7/provider-base.pod
+++ b/doc/man7/provider-base.pod
@@ -74,6 +74,8 @@ provider-base
                                                 int operation_id,
                                                 const int *no_store);
  const OSSL_ITEM *provider_get_reason_strings(void *provctx);
+ int provider_get_capabilities(void *provctx, const char *capability,
+                               OSSL_CALLBACK *cb, void *arg);
 
 =head1 DESCRIPTION
 
@@ -136,6 +138,7 @@ F<libcrypto>):
  provider_get_params            OSSL_FUNC_PROVIDER_GET_PARAMS
  provider_query_operation       OSSL_FUNC_PROVIDER_QUERY_OPERATION
  provider_get_reason_strings    OSSL_FUNC_PROVIDER_GET_REASON_STRINGS
+ provider_get_capabilities      OSSL_FUNC_PROVIDER_GET_CAPABILITIES
 
 =head2 Core functions
 
@@ -229,6 +232,15 @@ provider_get_reason_strings() should return a constant B<OSSL_ITEM>
 array that provides reason strings for reason codes the provider may
 use when reporting errors using core_put_error().
 
+The provider_get_capabilities() function should call the callback I<cb> passing
+it a set of B<OSSL_PARAM>s and the caller supplied argument I<arg>. The
+B<OSSL_PARAM>s should provide details about the capability with the name given
+in the I<capability> argument relevant for the provider context I<provctx>. If a
+provider supports multiple capabilities with the given name then it may call the
+callback multipe times (one for each capability). Capabilities can be useful for
+describing the services that a provider can offer. For further details see the
+L</CAPABILITIES> section below. It should return 1 on success or 0 on error.
+
 None of these functions are mandatory, but a provider is fairly
 useless without at least provider_query_operation(), and
 provider_gettable_params() is fairly useless if not accompanied by
@@ -332,6 +344,72 @@ pointing at the string "foo,bar"
 For more information on handling parameters, see L<OSSL_PARAM(3)> as
 L<OSSL_PARAM_int(3)>.
 
+=head1 CAPABILITIES
+
+Capabilties describe some of the services that a provider can offer.
+Applications can query the capabilities to discover those services.
+
+=head3 "TLS-GROUP" Capability
+
+The "TLS-GROUP" capability can be queried by libssl to discover the list of
+TLS groups that a provider can support. Each group supported can be used for
+key exchange during a TLS handshake. TLS clients can advertise the list of
+TLS groups they support in the supported_groups extension, and TLS servers can
+select a group from the offered list that they also support. In this way a
+provider can add to the list of groups that libssl already supports with
+additional ones.
+
+Each TLS group that a provider supports should be described via the callback
+passed in through the provider_get_capabilities function. Each group should have
+the following details supplied (all are mandatory):
+
+=over 4
+
+=item "tls-group-name" (B<OSSL_CAPABILITY_TLS_GROUP_NAME>) <utf8 string>
+
+The name of the group as given in the IANA TLS Supported Groups registry
+L<https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8>.
+
+=item "tls-group-name-internal" (B<OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL>) <utf8 string>
+
+The name of the group as known by the provider. This could be the same as the
+"tls-group-name", but does not have to be.
+
+=item "tls-group-id" (B<OSSL_CAPABILITY_TLS_GROUP_ID>) <unsigned integer>
+
+The TLS group id value as given in the IANA TLS Supported Groups registry.
+
+=item "tls-group-alg" (B<OSSL_CAPABILITY_TLS_GROUP_ALG>) <utf8 string>
+
+The name of a Key Management algorithm that the provider offers and that should
+be used with this group. Keys created should be able to support key exchange.
+The algorithm must support key and parameter generation as well as the
+key/parameter generation parameter, B<OSSL_PKEY_PARAM_GROUP_NAME>. The group
+name given via "tls-group-name-internal" above will be passed via
+B<OSSL_PKEY_PARAM_GROUP_NAME> when libssl wishes to generate keys/parameters.
+
+=item "tls-group-sec-bits" (B<OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS>) <unsigned integer>
+
+The number of bits of security offered by keys in this group. The number of bits
+should be comparable with the ones given in table 2 and 3 of the NIST SP800-57
+document.
+
+=item "tls-min-tls" (B<OSSL_CAPABILITY_TLS_GROUP_MIN_TLS>) <integer>
+
+=item "tls-max-tls" (B<OSSL_CAPABILITY_TLS_GROUP_MAX_TLS>) <integer>
+
+=item "tls-min-dtls" (B<OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS>) <integer>
+
+=item "tls-max-dtls" (B<OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS>) <integer>
+
+These parameters can be used to describe the minimum and maximum TLS and DTLS
+versions supported by the group. The values equate to the on-the-wire encoding
+of the various TLS versions. For example TLSv1.3 is 0x0304 (772 decimal), and
+TLSv1.2 is 0x0303 (771 decimal). A 0 indicates that there is no defined minimum
+or maximum. A -1 indicates that the group should not be used in that protocol.
+
+=back
+
 =head1 EXAMPLES
 
 This is an example of a simple provider made available as a
diff --git a/include/internal/provider.h b/include/internal/provider.h
index d7c0926a0b..3bfc154283 100644
--- a/include/internal/provider.h
+++ b/include/internal/provider.h
@@ -71,6 +71,10 @@ OPENSSL_CTX *ossl_provider_library_context(const OSSL_PROVIDER *prov);
 void ossl_provider_teardown(const OSSL_PROVIDER *prov);
 const OSSL_PARAM *ossl_provider_gettable_params(const OSSL_PROVIDER *prov);
 int ossl_provider_get_params(const OSSL_PROVIDER *prov, OSSL_PARAM params[]);
+int ossl_provider_get_capabilities(const OSSL_PROVIDER *prov,
+                                   const char *capability,
+                                   OSSL_CALLBACK *cb,
+                                   void *arg);
 const OSSL_ALGORITHM *ossl_provider_query_operation(const OSSL_PROVIDER *prov,
                                                     int operation_id,
                                                     int *no_cache);
diff --git a/include/internal/tlsgroups.h b/include/internal/tlsgroups.h
new file mode 100644
index 0000000000..bb9832e111
--- /dev/null
+++ b/include/internal/tlsgroups.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_INTERNAL_TLSGROUPS_H
+# define OSSL_INTERNAL_TLSGROUPS_H
+
+# define OSSL_TLS_GROUP_ID_sect163k1        0x0001
+# define OSSL_TLS_GROUP_ID_sect163r1        0x0002
+# define OSSL_TLS_GROUP_ID_sect163r2        0x0003
+# define OSSL_TLS_GROUP_ID_sect193r1        0x0004
+# define OSSL_TLS_GROUP_ID_sect193r2        0x0005
+# define OSSL_TLS_GROUP_ID_sect233k1        0x0006
+# define OSSL_TLS_GROUP_ID_sect233r1        0x0007
+# define OSSL_TLS_GROUP_ID_sect239k1        0x0008
+# define OSSL_TLS_GROUP_ID_sect283k1        0x0009
+# define OSSL_TLS_GROUP_ID_sect283r1        0x000A
+# define OSSL_TLS_GROUP_ID_sect409k1        0x000B
+# define OSSL_TLS_GROUP_ID_sect409r1        0x000C
+# define OSSL_TLS_GROUP_ID_sect571k1        0x000D
+# define OSSL_TLS_GROUP_ID_sect571r1        0x000E
+# define OSSL_TLS_GROUP_ID_secp160k1        0x000F
+# define OSSL_TLS_GROUP_ID_secp160r1        0x0010
+# define OSSL_TLS_GROUP_ID_secp160r2        0x0011
+# define OSSL_TLS_GROUP_ID_secp192k1        0x0012
+# define OSSL_TLS_GROUP_ID_secp192r1        0x0013
+# define OSSL_TLS_GROUP_ID_secp224k1        0x0014
+# define OSSL_TLS_GROUP_ID_secp224r1        0x0015
+# define OSSL_TLS_GROUP_ID_secp256k1        0x0016
+# define OSSL_TLS_GROUP_ID_secp256r1        0x0017
+# define OSSL_TLS_GROUP_ID_secp384r1        0x0018
+# define OSSL_TLS_GROUP_ID_secp521r1        0x0019
+# define OSSL_TLS_GROUP_ID_brainpoolP256r1  0x001A
+# define OSSL_TLS_GROUP_ID_brainpoolP384r1  0x001B
+# define OSSL_TLS_GROUP_ID_brainpoolP512r1  0x001C
+# define OSSL_TLS_GROUP_ID_x25519           0x001D
+# define OSSL_TLS_GROUP_ID_x448             0x001E
+# define OSSL_TLS_GROUP_ID_ffdhe2048        0x0100
+# define OSSL_TLS_GROUP_ID_ffdhe3072        0x0101
+# define OSSL_TLS_GROUP_ID_ffdhe4096        0x0102
+# define OSSL_TLS_GROUP_ID_ffdhe6144        0x0103
+# define OSSL_TLS_GROUP_ID_ffdhe8192        0x0104
+
+#endif
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index fa6b7a9547..7da0186392 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -195,6 +195,7 @@ extern "C" {
 #define OSSL_PKEY_PARAM_MGF1_DIGEST         "mgf1-digest"
 #define OSSL_PKEY_PARAM_MGF1_PROPERTIES     "mgf1-properties"
 #define OSSL_PKEY_PARAM_TLS_ENCODED_PT      "tls-encoded-pt"
+#define OSSL_PKEY_PARAM_GROUP_NAME          "group"
 
 /* Diffie-Hellman/DSA public/private key */
 #define OSSL_PKEY_PARAM_PUB_KEY             "pub"
@@ -217,12 +218,10 @@ extern "C" {
 #define OSSL_FFC_PARAM_VALIDATE_PQG         "validate-pqg"
 
 /* Diffie-Hellman params */
-#define OSSL_PKEY_PARAM_DH_GROUP            "group"
 #define OSSL_PKEY_PARAM_DH_GENERATOR        "safeprime-generator"
 #define OSSL_PKEY_PARAM_DH_PRIV_LEN         "priv_len"
 
 /* Elliptic Curve Domain Parameters */
-#define OSSL_PKEY_PARAM_EC_NAME      "curve-name"
 #define OSSL_PKEY_PARAM_EC_PUB_X     "qx"
 #define OSSL_PKEY_PARAM_EC_PUB_Y     "qy"
 
@@ -384,6 +383,19 @@ extern "C" {
 #define OSSL_PKEY_PARAM_RSA_TEST_Q2  "q2"
 #define OSSL_SIGNATURE_PARAM_KAT "kat"
 
+/* Capabilities */
+
+/* TLS-GROUP Capbility */
+#define OSSL_CAPABILITY_TLS_GROUP_NAME              "tls-group-name"
+#define OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL     "tls-group-name-internal"
+#define OSSL_CAPABILITY_TLS_GROUP_ID                "tls-group-id"
+#define OSSL_CAPABILITY_TLS_GROUP_ALG               "tls-group-alg"
+#define OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS     "tls-group-sec-bits"
+#define OSSL_CAPABILITY_TLS_GROUP_MIN_TLS           "tls-min-tls"
+#define OSSL_CAPABILITY_TLS_GROUP_MAX_TLS           "tls-max-tls"
+#define OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS          "tls-min-dtls"
+#define OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS          "tls-max-dtls"
+
 # ifdef __cplusplus
 }
 # endif
diff --git a/include/openssl/core_numbers.h b/include/openssl/core_numbers.h
index f7025d1c1d..667bb21ffb 100644
--- a/include/openssl/core_numbers.h
+++ b/include/openssl/core_numbers.h
@@ -154,20 +154,23 @@ OSSL_CORE_MAKE_FUNC(void, self_test_cb, (OPENSSL_CORE_CTX *ctx, OSSL_CALLBACK **
                                          void **cbarg))
 
 /* Functions provided by the provider to the Core, reserved numbers 1024-1535 */
-# define OSSL_FUNC_PROVIDER_TEARDOWN         1024
+# define OSSL_FUNC_PROVIDER_TEARDOWN           1024
 OSSL_CORE_MAKE_FUNC(void,provider_teardown,(void *provctx))
-# define OSSL_FUNC_PROVIDER_GETTABLE_PARAMS  1025
+# define OSSL_FUNC_PROVIDER_GETTABLE_PARAMS    1025
 OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *,
                     provider_gettable_params,(void *provctx))
-# define OSSL_FUNC_PROVIDER_GET_PARAMS       1026
+# define OSSL_FUNC_PROVIDER_GET_PARAMS         1026
 OSSL_CORE_MAKE_FUNC(int,provider_get_params,(void *provctx,
                                              OSSL_PARAM params[]))
-# define OSSL_FUNC_PROVIDER_QUERY_OPERATION  1027
+# define OSSL_FUNC_PROVIDER_QUERY_OPERATION    1027
 OSSL_CORE_MAKE_FUNC(const OSSL_ALGORITHM *,provider_query_operation,
                     (void *provctx, int operation_id, int *no_store))
 # define OSSL_FUNC_PROVIDER_GET_REASON_STRINGS 1028
 OSSL_CORE_MAKE_FUNC(const OSSL_ITEM *,provider_get_reason_strings,
                     (void *provctx))
+# define OSSL_FUNC_PROVIDER_GET_CAPABILITIES   1029
+OSSL_CORE_MAKE_FUNC(int, provider_get_capabilities, (void *provctx,
+                    const char *capability, OSSL_CALLBACK *cb, void *arg))
 
 /* Operations */
 
diff --git a/include/openssl/ec.h b/include/openssl/ec.h
index 90e109b61e..1302e27bb0 100644
--- a/include/openssl/ec.h
+++ b/include/openssl/ec.h
@@ -1450,10 +1450,6 @@ DEPRECATEDIN_3_0(void EC_KEY_METHOD_get_verify
 #   endif
 #  endif
 
-int EVP_PKEY_CTX_set_ec_paramgen_curve_name(EVP_PKEY_CTX *ctx,
-                                            const char *name);
-int EVP_PKEY_CTX_get_ec_paramgen_curve_name(EVP_PKEY_CTX *ctx,
-                                            char *name, size_t namelen);
 int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid);
 
 #  define EVP_PKEY_CTX_set_ec_param_enc(ctx, flag) \
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 9ce2f5e2ac..2b39d613b0 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -1886,6 +1886,9 @@ int EVP_str2ctrl(int (*cb)(void *ctx, int cmd, void *buf, size_t buflen),
 int EVP_hex2ctrl(int (*cb)(void *ctx, int cmd, void *buf, size_t buflen),
                  void *ctx, int cmd, const char *hex);
 
+int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name);
+int EVP_PKEY_CTX_get_group_name(EVP_PKEY_CTX *ctx, char *name, size_t namelen);
+
 # ifdef  __cplusplus
 }
 # endif
diff --git a/include/openssl/provider.h b/include/openssl/provider.h
index e9a1408675..cb5fc9f8bf 100644
--- a/include/openssl/provider.h
+++ b/include/openssl/provider.h
@@ -29,6 +29,10 @@ int OSSL_PROVIDER_do_all(OPENSSL_CTX *ctx,
 
 const OSSL_PARAM *OSSL_PROVIDER_gettable_params(const OSSL_PROVIDER *prov);
 int OSSL_PROVIDER_get_params(const OSSL_PROVIDER *prov, OSSL_PARAM params[]);
+int OSSL_PROVIDER_get_capabilities(const OSSL_PROVIDER *prov,
+                                   const char *capability,
+                                   OSSL_CALLBACK *cb,
+                                   void *arg);
 
 const OSSL_ALGORITHM *OSSL_PROVIDER_query_operation(const OSSL_PROVIDER *prov,
                                                     int operation_id,
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index bbce792c72..c15a17f96f 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -525,6 +525,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_CONTEXT_NOT_DANE_ENABLED                   167
 # define SSL_R_COOKIE_GEN_CALLBACK_FAILURE                400
 # define SSL_R_COOKIE_MISMATCH                            308
+# define SSL_R_COPY_PARAMETERS_FAILED                     296
 # define SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED       206
 # define SSL_R_DANE_ALREADY_ENABLED                       172
 # define SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL            173
diff --git a/providers/common/build.info b/providers/common/build.info
index c49b090227..14add72dd6 100644
--- a/providers/common/build.info
+++ b/providers/common/build.info
@@ -1,6 +1,6 @@
 SUBDIRS=der
 
 SOURCE[../libcommon.a]=provider_err.c bio_prov.c provider_ctx.c
-$FIPSCOMMON=provider_util.c
+$FIPSCOMMON=provider_util.c capabilities.c
 SOURCE[../libnonfips.a]=$FIPSCOMMON nid_to_name.c
 SOURCE[../libfips.a]=$FIPSCOMMON
diff --git a/providers/common/capabilities.c b/providers/common/capabilities.c
new file mode 100644
index 0000000000..84d2006cee
--- /dev/null
+++ b/providers/common/capabilities.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <openssl/core_numbers.h>
+#include <openssl/core_names.h>
+/* For TLS1_VERSION etc */
+#include <openssl/ssl.h>
+#include <openssl/params.h>
+#include "internal/nelem.h"
+#include "internal/tlsgroups.h"
+#include "prov/providercommon.h"
+
+typedef struct tls_group_constants_st {
+    unsigned int group_id;   /* Group ID */
+    unsigned int secbits;    /* Bits of security */
+    int mintls;              /* Minimum TLS version, -1 unsupported */
+    int maxtls;              /* Maximum TLS version (or 0 for undefined) */
+    int mindtls;             /* Minimum DTLS version, -1 unsupported */
+    int maxdtls;             /* Maximum DTLS version (or 0 for undefined) */
+} TLS_GROUP_CONSTANTS;
+
+static const TLS_GROUP_CONSTANTS group_list[35] = {
+    { OSSL_TLS_GROUP_ID_sect163k1, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect163r1, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect163r2, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect193r1, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect193r2, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect233k1, 112, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect233r1, 112, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect239k1, 112, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect283k1, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect283r1, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect409k1, 192, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect409r1, 192, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect571k1, 256, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_sect571r1, 256, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp160k1, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp160r1, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp160r2, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp192k1, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp192r1, 80, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp224k1, 112, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp224r1, 112, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp256k1, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp256r1, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp384r1, 192, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_secp521r1, 256, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_brainpoolP256r1, 128, TLS1_VERSION, TLS1_2_VERSION,
+      DTLS1_VERSION, DTLS1_2_VERSION },
+    { OSSL_TLS_GROUP_ID_brainpoolP384r1, 192, TLS1_VERSION, TLS1_2_VERSION,
+      DTLS1_VERSION, DTLS1_2_VERSION },
+    { OSSL_TLS_GROUP_ID_brainpoolP512r1, 256, TLS1_VERSION, TLS1_2_VERSION,
+      DTLS1_VERSION, DTLS1_2_VERSION },
+    { OSSL_TLS_GROUP_ID_x25519, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { OSSL_TLS_GROUP_ID_x448, 224, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    /* Security bit values as given by BN_security_bits() */
+    { OSSL_TLS_GROUP_ID_ffdhe2048, 112, TLS1_3_VERSION, 0, -1, -1 },
+    { OSSL_TLS_GROUP_ID_ffdhe3072, 128, TLS1_3_VERSION, 0, -1, -1 },
+    { OSSL_TLS_GROUP_ID_ffdhe4096, 128, TLS1_3_VERSION, 0, -1, -1 },
+    { OSSL_TLS_GROUP_ID_ffdhe6144, 128, TLS1_3_VERSION, 0, -1, -1 },
+    { OSSL_TLS_GROUP_ID_ffdhe8192, 192, TLS1_3_VERSION, 0, -1, -1 },
+};
+
+#define TLS_GROUP_ENTRY(tlsname, realname, algorithm, idx) \
+    { \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME, \
+                               tlsname, \
+                               sizeof(tlsname)), \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL, \
+                               realname, \
+                               sizeof(realname)), \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, \
+                               algorithm, \
+                               sizeof(algorithm)), \
+        OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, \
+                        (unsigned int *)&group_list[idx].group_id), \
+        OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, \
+                        (unsigned int *)&group_list[idx].secbits), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, \
+                        (unsigned int *)&group_list[idx].mintls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, \
+                        (unsigned int *)&group_list[idx].maxtls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, \
+                        (unsigned int *)&group_list[idx].mindtls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, \
+                        (unsigned int *)&group_list[idx].maxdtls), \
+        OSSL_PARAM_END \
+    }
+
+static const OSSL_PARAM param_group_list[][10] = {
+#ifndef OPENSSL_NO_EC
+    TLS_GROUP_ENTRY("sect163k1", "sect163k1", "EC", 0),
+# ifndef FIPS_MODULE
+    TLS_GROUP_ENTRY("sect163r1", "sect163r1", "EC", 1),
+# endif
+    TLS_GROUP_ENTRY("sect163r2", "sect163r2", "EC", 2),
+# ifndef FIPS_MODULE
+    TLS_GROUP_ENTRY("sect193r1", "sect193r1", "EC", 3),
+    TLS_GROUP_ENTRY("sect193r2", "sect193r2", "EC", 4),
+# endif
+    TLS_GROUP_ENTRY("sect233k1", "sect233k1", "EC", 5),
+    TLS_GROUP_ENTRY("sect233r1", "sect233r1", "EC", 6),
+# ifndef FIPS_MODULE
+    TLS_GROUP_ENTRY("sect239k1", "sect239k1", "EC", 7),
+# endif
+    TLS_GROUP_ENTRY("sect283k1", "sect283k1", "EC", 8),
+    TLS_GROUP_ENTRY("sect283r1", "sect283r1", "EC", 9),
+    TLS_GROUP_ENTRY("sect409k1", "sect409k1", "EC", 10),
+    TLS_GROUP_ENTRY("sect409r1", "sect409r1", "EC", 11),
+    TLS_GROUP_ENTRY("sect571k1", "sect571k1", "EC", 12),
+    TLS_GROUP_ENTRY("sect571r1", "sect571r1", "EC", 13),
+# ifndef FIPS_MODULE
+    TLS_GROUP_ENTRY("secp160k1", "secp160k1", "EC", 14),
+    TLS_GROUP_ENTRY("secp160r1", "secp160r1", "EC", 15),
+    TLS_GROUP_ENTRY("secp160r2", "secp160r2", "EC", 16),
+    TLS_GROUP_ENTRY("secp192k1", "secp192k1", "EC", 17),
+# endif
+    TLS_GROUP_ENTRY("secp192r1", "prime192v1", "EC", 18),
+# ifndef FIPS_MODULE
+    TLS_GROUP_ENTRY("secp224k1", "secp224k1", "EC", 19),
+# endif
+    TLS_GROUP_ENTRY("secp224r1", "secp224r1", "EC", 20),
+# ifndef FIPS_MODULE
+    TLS_GROUP_ENTRY("secp256k1", "secp256k1", "EC", 21),
+# endif
+    TLS_GROUP_ENTRY("secp256r1", "prime256v1", "EC", 22),
+    TLS_GROUP_ENTRY("secp384r1", "secp384r1", "EC", 23),
+    TLS_GROUP_ENTRY("secp521r1", "secp521r1", "EC", 24),
+# ifndef FIPS_MODULE
+    TLS_GROUP_ENTRY("brainpoolP256r1", "brainpoolP256r1", "EC", 25),
+    TLS_GROUP_ENTRY("brainpoolP384r1", "brainpoolP384r1", "EC", 26),
+    TLS_GROUP_ENTRY("brainpoolP512r1", "brainpoolP512r1", "EC", 27),
+# endif
+    TLS_GROUP_ENTRY("x25519", "x25519", "X25519", 28),
+    TLS_GROUP_ENTRY("x448", "x448", "X448", 29),
+#endif /* OPENSSL_NO_EC */
+#ifndef OPENSSL_NO_DH
+    /* Security bit values for FFDHE groups are as per RFC 7919 */
+    TLS_GROUP_ENTRY("ffdhe2048", "ffdhe2048", "DH", 30),
+    TLS_GROUP_ENTRY("ffdhe3072", "ffdhe3072", "DH", 31),
+    TLS_GROUP_ENTRY("ffdhe4096", "ffdhe4096", "DH", 32),
+    TLS_GROUP_ENTRY("ffdhe6144", "ffdhe6144", "DH", 33),
+    TLS_GROUP_ENTRY("ffdhe8192", "ffdhe8192", "DH", 34),
+#endif
+};
+
+static int tls_group_capability(OSSL_CALLBACK *cb, void *arg)
+{
+    size_t i;
+
+#if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_DH) && !defined(FIPS_MODULE)
+    assert(OSSL_NELEM(param_group_list) == OSSL_NELEM(group_list));
+#endif
+    for (i = 0; i < OSSL_NELEM(param_group_list); i++)
+        if (!cb(param_group_list[i], arg))
+            return 0;
+
+    return 1;
+}
+
+int provider_get_capabilities(void *provctx, const char *capability,
+                              OSSL_CALLBACK *cb, void *arg)
+{
+    if (strcmp(capability, "TLS-GROUP") == 0)
+        return tls_group_capability(cb, arg);
+
+    /* We don't support this capability */
+    return 0;
+}
diff --git a/providers/common/include/prov/providercommon.h b/providers/common/include/prov/providercommon.h
index 07c5a67f38..b9fcf3d52e 100644
--- a/providers/common/include/prov/providercommon.h
+++ b/providers/common/include/prov/providercommon.h
@@ -8,6 +8,7 @@
  */
 
 #include <openssl/provider.h>
+#include <openssl/core_numbers.h>
 
 const OSSL_CORE_HANDLE *FIPS_get_core_handle(OPENSSL_CTX *ctx);
 
@@ -15,3 +16,5 @@ const char *ossl_prov_util_nid_to_name(int nid);
 
 int cipher_capable_aes_cbc_hmac_sha1(void);
 int cipher_capable_aes_cbc_hmac_sha256(void);
+
+OSSL_provider_get_capabilities_fn provider_get_capabilities;
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 4b15a21c0b..8e27b33fc2 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -550,6 +550,7 @@ static const OSSL_ALGORITHM *deflt_query(void *provctx, int operation_id,
     return NULL;
 }
 
+
 static void deflt_teardown(void *provctx)
 {
     BIO_meth_free(PROV_CTX_get0_core_bio_method(provctx));
@@ -562,6 +563,7 @@ static const OSSL_DISPATCH deflt_dispatch_table[] = {
     { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))deflt_gettable_params },
     { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))deflt_get_params },
     { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))deflt_query },
+    { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, (void (*)(void))provider_get_capabilities },
     { 0, NULL }
 };
 
diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c
index 0484a54f52..9efb6af1c9 100644
--- a/providers/fips/fipsprov.c
+++ b/providers/fips/fipsprov.c
@@ -566,6 +566,7 @@ static const OSSL_DISPATCH fips_dispatch_table[] = {
     { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))fips_gettable_params },
     { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))fips_get_params },
     { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))fips_query },
+    { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, (void (*)(void))provider_get_capabilities },
     { 0, NULL }
 };
 
diff --git a/providers/fips/self_test_data.inc b/providers/fips/self_test_data.inc
index 431e52467c..674806edb2 100644
--- a/providers/fips/self_test_data.inc
+++ b/providers/fips/self_test_data.inc
@@ -739,7 +739,7 @@ static const unsigned char ecdh_peer_pub[] = {
 };
 
 static const ST_KAT_PARAM ecdh_group[] = {
-    ST_KAT_PARAM_UTF8STRING(OSSL_PKEY_PARAM_EC_NAME, ecdh_curve_name),
+    ST_KAT_PARAM_UTF8STRING(OSSL_PKEY_PARAM_GROUP_NAME, ecdh_curve_name),
     ST_KAT_PARAM_END()
 };
 static const ST_KAT_PARAM ecdh_host_key[] = {
@@ -1015,7 +1015,7 @@ static const unsigned char ecd_pub[] = {
 };
 
 static const ST_KAT_PARAM ecdsa_key[] = {
-    ST_KAT_PARAM_UTF8STRING(OSSL_PKEY_PARAM_EC_NAME, ecd_curve_name),
+    ST_KAT_PARAM_UTF8STRING(OSSL_PKEY_PARAM_GROUP_NAME, ecd_curve_name),
     ST_KAT_PARAM_OCTET(OSSL_PKEY_PARAM_PUB_KEY, ecd_pub),
     ST_KAT_PARAM_BIGNUM(OSSL_PKEY_PARAM_PRIV_KEY, ecd_priv),
     ST_KAT_PARAM_END()
diff --git a/providers/implementations/keymgmt/dh_kmgmt.c b/providers/implementations/keymgmt/dh_kmgmt.c
index c81d437dae..82fbdc8bb4 100644
--- a/providers/implementations/keymgmt/dh_kmgmt.c
+++ b/providers/implementations/keymgmt/dh_kmgmt.c
@@ -239,7 +239,7 @@ err:
     OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),                        \
     OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),                               \
     OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),                \
-    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DH_GROUP, NULL, 0)
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0)
 # define DH_IMEXPORTABLE_PUBLIC_KEY                                            \
     OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0)
 # define DH_IMEXPORTABLE_PRIVATE_KEY                                           \
@@ -464,7 +464,7 @@ static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
             return 0;
         }
     }
-    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GROUP);
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
     if (p != NULL) {
         if (p->data_type != OSSL_PARAM_UTF8_STRING
            || ((gctx->group_nid = ffc_named_group_to_uid(p->data)) == NID_undef)) {
@@ -518,7 +518,7 @@ static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
 static const OSSL_PARAM *dh_gen_settable_params(void *provctx)
 {
     static OSSL_PARAM settable[] = {
-        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DH_GROUP, NULL, 0),
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
         OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
         OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_GENERATOR, NULL),
         OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0),
diff --git a/providers/implementations/keymgmt/ec_kmgmt.c b/providers/implementations/keymgmt/ec_kmgmt.c
index d926ec2bd2..0b006047d5 100644
--- a/providers/implementations/keymgmt/ec_kmgmt.c
+++ b/providers/implementations/keymgmt/ec_kmgmt.c
@@ -89,7 +89,7 @@ int domparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl,
         if ((curve_name = ec_curve_nid2name(curve_nid)) == NULL)
             return 0;
         if (!ossl_param_build_set_utf8_string(tmpl, params,
-                                              OSSL_PKEY_PARAM_EC_NAME,
+                                              OSSL_PKEY_PARAM_GROUP_NAME,
                                               curve_name))
 
             return 0;
@@ -412,7 +412,7 @@ int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
 /* IMEXPORT = IMPORT + EXPORT */
 
 # define EC_IMEXPORTABLE_DOM_PARAMETERS                          \
-    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_NAME, NULL, 0)
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0)
 # define EC_IMEXPORTABLE_PUBLIC_KEY                              \
     OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0)
 # define EC_IMEXPORTABLE_PRIVATE_KEY                             \
@@ -699,7 +699,7 @@ static int ec_gen_set_params(void *genctx, const OSSL_PARAM params[])
         if (!OSSL_PARAM_get_int(p, &gctx->ecdh_mode))
             return 0;
     }
-    if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_NAME))
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME))
         != NULL) {
         const char *curve_name = NULL;
         int ret = 0;
@@ -733,7 +733,7 @@ static int ec_gen_set_params(void *genctx, const OSSL_PARAM params[])
 static const OSSL_PARAM *ec_gen_settable_params(void *provctx)
 {
     static OSSL_PARAM settable[] = {
-        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_NAME, NULL, 0),
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
         OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
         OSSL_PARAM_END
     };
diff --git a/providers/implementations/keymgmt/ecx_kmgmt.c b/providers/implementations/keymgmt/ecx_kmgmt.c
index e5c7e8bf3a..813604f7b1 100644
--- a/providers/implementations/keymgmt/ecx_kmgmt.c
+++ b/providers/implementations/keymgmt/ecx_kmgmt.c
@@ -8,6 +8,9 @@
  */
 
 #include <assert.h>
+#include <string.h>
+/* For strcasecmp on Windows */
+#include "e_os.h"
 #include <openssl/core_numbers.h>
 #include <openssl/core_names.h>
 #include <openssl/params.h>
@@ -66,6 +69,7 @@ static OSSL_OP_keymgmt_export_types_fn ecx_imexport_types;
 struct ecx_gen_ctx {
     OPENSSL_CTX *libctx;
     ECX_KEY_TYPE type;
+    int selection;
 };
 
 #ifdef S390X_EC_ASM
@@ -404,12 +408,10 @@ static void *ecx_gen_init(void *provctx, int selection, ECX_KEY_TYPE type)
     OPENSSL_CTX *libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
     struct ecx_gen_ctx *gctx = NULL;
 
-    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
-        return NULL;
-
     if ((gctx = OPENSSL_malloc(sizeof(*gctx))) != NULL) {
         gctx->libctx = libctx;
         gctx->type = type;
+        gctx->selection = selection;
     }
     return gctx;
 }
@@ -434,6 +436,54 @@ static void *ed448_gen_init(void *provctx, int selection)
     return ecx_gen_init(provctx, selection, ECX_KEY_TYPE_ED448);
 }
 
+static int ecx_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+    struct ecx_gen_ctx *gctx = genctx;
+    const OSSL_PARAM *p;
+
+    if (gctx == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
+    if (p != NULL) {
+        const char *groupname = NULL;
+
+        /*
+         * We optionally allow setting a group name - but each algorithm only
+         * support one such name, so all we do is verify that it is the one we
+         * expected.
+         */
+        switch (gctx->type) {
+            case ECX_KEY_TYPE_X25519:
+                groupname = "x25519";
+                break;
+            case ECX_KEY_TYPE_X448:
+                groupname = "x448";
+                break;
+            default:
+                /* We only support this for key exchange at the moment */
+                break;
+        }
+        if (p->data_type != OSSL_PARAM_UTF8_STRING
+                || groupname == NULL
+                || strcasecmp(p->data, groupname) != 0) {
+            ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM *ecx_gen_settable_params(void *provctx)
+{
+    static OSSL_PARAM settable[] = {
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+        OSSL_PARAM_END
+    };
+    return settable;
+}
+
 static void *ecx_gen(struct ecx_gen_ctx *gctx)
 {
     ECX_KEY *key;
@@ -445,6 +495,11 @@ static void *ecx_gen(struct ecx_gen_ctx *gctx)
         ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
+
+    /* If we're doing parameter generation then we just return a blank key */
+    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        return key;
+
     if ((privkey = ecx_key_allocate_privkey(key)) == NULL) {
         ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
         goto err;
@@ -472,6 +527,7 @@ static void *ecx_gen(struct ecx_gen_ctx *gctx)
             goto err;
         break;
     }
+    key->haspubkey = 1;
     return key;
 err:
     ecx_key_free(key);
@@ -548,6 +604,9 @@ static void ecx_gen_cleanup(void *genctx)
         { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ecx_export }, \
         { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ecx_imexport_types }, \
         { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))alg##_gen_init }, \
+        { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))ecx_gen_set_params }, \
+        { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, \
+          (void (*)(void))ecx_gen_settable_params }, \
         { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))alg##_gen }, \
         { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ecx_gen_cleanup }, \
         { 0, NULL } \
@@ -576,6 +635,10 @@ static void *s390x_ecx_keygen25519(struct ecx_gen_ctx *gctx)
         goto err;
     }
 
+    /* If we're doing parameter generation then we just return a blank key */
+    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        return key;
+
     pubkey = key->pubkey;
 
     privkey = ecx_key_allocate_privkey(key);
@@ -593,6 +656,7 @@ static void *s390x_ecx_keygen25519(struct ecx_gen_ctx *gctx)
 
     if (s390x_x25519_mul(pubkey, generator, privkey) != 1)
         goto err;
+    key->haspubkey = 1;
     return key;
  err:
     ecx_key_free(key);
@@ -616,6 +680,10 @@ static void *s390x_ecx_keygen448(struct ecx_gen_ctx *gctx)
         goto err;
     }
 
+    /* If we're doing parameter generation then we just return a blank key */
+    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        return key;
+
     pubkey = key->pubkey;
 
     privkey = ecx_key_allocate_privkey(key);
@@ -632,6 +700,7 @@ static void *s390x_ecx_keygen448(struct ecx_gen_ctx *gctx)
 
     if (s390x_x448_mul(pubkey, generator, privkey) != 1)
         goto err;
+    key->haspubkey = 1;
     return key;
  err:
     ecx_key_free(key);
@@ -662,6 +731,10 @@ static void *s390x_ecd_keygen25519(struct ecx_gen_ctx *gctx)
         goto err;
     }
 
+    /* If we're doing parameter generation then we just return a blank key */
+    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        return key;
+
     pubkey = key->pubkey;
 
     privkey = ecx_key_allocate_privkey(key);
@@ -690,6 +763,7 @@ static void *s390x_ecd_keygen25519(struct ecx_gen_ctx *gctx)
         goto err;
 
     pubkey[31] |= ((x_dst[0] & 0x01) << 7);
+    key->haspubkey = 1;
     return key;
  err:
     ecx_key_free(key);
@@ -723,6 +797,10 @@ static void *s390x_ecd_keygen448(struct ecx_gen_ctx *gctx)
         goto err;
     }
 
+    /* If we're doing parameter generation then we just return a blank key */
+    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        return key;
+
     pubkey = key->pubkey;
 
     privkey = ecx_key_allocate_privkey(key);
@@ -758,6 +836,7 @@ static void *s390x_ecd_keygen448(struct ecx_gen_ctx *gctx)
     pubkey[56] |= ((x_dst[0] & 0x01) << 7);
     EVP_MD_CTX_free(hashctx);
     EVP_MD_free(shake);
+    key->haspubkey = 1;
     return key;
  err:
     ecx_key_free(key);
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 054fc468ed..a7f1e4d83a 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -3650,10 +3650,11 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
                 int *cptr = parg;
 
                 for (i = 0; i < clistlen; i++) {
-                    const TLS_GROUP_INFO *cinf = tls1_group_id_lookup(clist[i]);
+                    const TLS_GROUP_INFO *cinf
+                        = tls1_group_id_lookup(s->ctx, clist[i]);
 
                     if (cinf != NULL)
-                        cptr[i] = cinf->nid;
+                        cptr[i] = tls1_group_id2nid(cinf->group_id, 1);
                     else
                         cptr[i] = TLSEXT_nid_unknown | clist[i];
                 }
@@ -3666,7 +3667,7 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
                                &s->ext.supportedgroups_len, parg, larg);
 
     case SSL_CTRL_SET_GROUPS_LIST:
-        return tls1_set_groups_list(&s->ext.supportedgroups,
+        return tls1_set_groups_list(s->ctx, &s->ext.supportedgroups,
                                     &s->ext.supportedgroups_len, parg);
 
     case SSL_CTRL_GET_SHARED_GROUP:
@@ -3674,11 +3675,11 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
             uint16_t id = tls1_shared_group(s, larg);
 
             if (larg != -1)
-                return tls1_group_id2nid(id);
+                return tls1_group_id2nid(id, 1);
             return id;
         }
     case SSL_CTRL_GET_NEGOTIATED_GROUP:
-        ret = tls1_group_id2nid(s->s3.group_id);
+        ret = tls1_group_id2nid(s->s3.group_id, 1);
         break;
 #endif /* !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH) */
 
@@ -3963,7 +3964,7 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
                                parg, larg);
 
     case SSL_CTRL_SET_GROUPS_LIST:
-        return tls1_set_groups_list(&ctx->ext.supportedgroups,
+        return tls1_set_groups_list(ctx, &ctx->ext.supportedgroups,
                                     &ctx->ext.supportedgroups_len,
                                     parg);
 #endif /* !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH) */
@@ -4764,25 +4765,19 @@ EVP_PKEY *ssl_generate_pkey(SSL *s, EVP_PKEY *pm)
 }
 
 /* Generate a private key from a group ID */
-#if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_EC)
 EVP_PKEY *ssl_generate_pkey_group(SSL *s, uint16_t id)
 {
-    const TLS_GROUP_INFO *ginf = tls1_group_id_lookup(id);
+    const TLS_GROUP_INFO *ginf = tls1_group_id_lookup(s->ctx, id);
     EVP_PKEY_CTX *pctx = NULL;
     EVP_PKEY *pkey = NULL;
-    uint16_t gtype;
-# ifndef OPENSSL_NO_DH
-    DH *dh = NULL;
-# endif
 
     if (ginf == NULL) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP,
                  ERR_R_INTERNAL_ERROR);
         goto err;
     }
-    gtype = ginf->flags & TLS_GROUP_TYPE;
 
-    pctx = EVP_PKEY_CTX_new_from_name(s->ctx->libctx, ginf->keytype,
+    pctx = EVP_PKEY_CTX_new_from_name(s->ctx->libctx, ginf->algorithm,
                                       s->ctx->propq);
 
     if (pctx == NULL) {
@@ -4795,40 +4790,11 @@ EVP_PKEY *ssl_generate_pkey_group(SSL *s, uint16_t id)
                  ERR_R_EVP_LIB);
         goto err;
     }
-# ifndef OPENSSL_NO_DH
-    if (gtype == TLS_GROUP_FFDHE) {
-        if ((pkey = EVP_PKEY_new()) == NULL
-                || (dh = DH_new_by_nid(ginf->nid)) == NULL
-                || !EVP_PKEY_assign(pkey, EVP_PKEY_DH, dh)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP,
-                     ERR_R_EVP_LIB);
-            DH_free(dh);
-            EVP_PKEY_free(pkey);
-            pkey = NULL;
-            goto err;
-        }
-        if (EVP_PKEY_CTX_set_dh_nid(pctx, ginf->nid) <= 0) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP,
-                     ERR_R_EVP_LIB);
-            EVP_PKEY_free(pkey);
-            pkey = NULL;
-            goto err;
-        }
-    }
-#  ifndef OPENSSL_NO_EC
-    else
-#  endif
-# endif
-# ifndef OPENSSL_NO_EC
-    {
-        if (gtype != TLS_GROUP_CURVE_CUSTOM
-                && EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ginf->nid) <= 0) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP,
-                     ERR_R_EVP_LIB);
-            goto err;
-        }
+    if (!EVP_PKEY_CTX_set_group_name(pctx, ginf->realname)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP,
+                 ERR_R_EVP_LIB);
+        goto err;
     }
-# endif
     if (EVP_PKEY_keygen(pctx, &pkey) <= 0) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP,
                  ERR_R_EVP_LIB);
@@ -4840,7 +4806,6 @@ EVP_PKEY *ssl_generate_pkey_group(SSL *s, uint16_t id)
     EVP_PKEY_CTX_free(pctx);
     return pkey;
 }
-#endif
 
 /*
  * Generate parameters from a group ID
@@ -4849,43 +4814,23 @@ EVP_PKEY *ssl_generate_param_group(SSL *s, uint16_t id)
 {
     EVP_PKEY_CTX *pctx = NULL;
     EVP_PKEY *pkey = NULL;
-    const TLS_GROUP_INFO *ginf = tls1_group_id_lookup(id);
-    const char *pkey_ctx_name;
+    const TLS_GROUP_INFO *ginf = tls1_group_id_lookup(s->ctx, id);
 
     if (ginf == NULL)
         goto err;
 
-    if ((ginf->flags & TLS_GROUP_TYPE) == TLS_GROUP_CURVE_CUSTOM) {
-        pkey = EVP_PKEY_new();
-        if (pkey != NULL && EVP_PKEY_set_type(pkey, ginf->nid))
-            return pkey;
-        EVP_PKEY_free(pkey);
-        return NULL;
-    }
-
-    pkey_ctx_name = (ginf->flags & TLS_GROUP_FFDHE) != 0 ? "DH" : "EC";
-    pctx = EVP_PKEY_CTX_new_from_name(s->ctx->libctx, pkey_ctx_name,
+    pctx = EVP_PKEY_CTX_new_from_name(s->ctx->libctx, ginf->algorithm,
                                       s->ctx->propq);
 
     if (pctx == NULL)
         goto err;
     if (EVP_PKEY_paramgen_init(pctx) <= 0)
         goto err;
-# ifndef OPENSSL_NO_DH
-    if (ginf->flags & TLS_GROUP_FFDHE) {
-        if (EVP_PKEY_CTX_set_dh_nid(pctx, ginf->nid) <= 0)
-            goto err;
-    }
-#  ifndef OPENSSL_NO_EC
-    else
-#  endif
-# endif
-# ifndef OPENSSL_NO_EC
-    {
-        if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ginf->nid) <= 0)
-            goto err;
+    if (!EVP_PKEY_CTX_set_group_name(pctx, ginf->realname)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP,
+                 ERR_R_EVP_LIB);
+        goto err;
     }
-# endif
     if (EVP_PKEY_paramgen(pctx, &pkey) <= 0) {
         EVP_PKEY_free(pkey);
         pkey = NULL;
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 30643c33b4..f84b3f94d8 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -109,6 +109,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COOKIE_GEN_CALLBACK_FAILURE),
     "cookie gen callback failure"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COOKIE_MISMATCH), "cookie mismatch"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COPY_PARAMETERS_FAILED),
+    "copy parameters failed"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED),
     "custom ext handler already installed"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_ALREADY_ENABLED),
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 9fb65b6825..cee888944d 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2429,10 +2429,8 @@ long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
     /* For some cases with ctx == NULL perform syntax checks */
     if (ctx == NULL) {
         switch (cmd) {
-#ifndef OPENSSL_NO_EC
         case SSL_CTRL_SET_GROUPS_LIST:
-            return tls1_set_groups_list(NULL, NULL, parg);
-#endif
+            return tls1_set_groups_list(ctx, NULL, NULL, parg);
         case SSL_CTRL_SET_SIGALGS_LIST:
         case SSL_CTRL_SET_CLIENT_SIGALGS_LIST:
             return tls1_set_sigalgs_list(NULL, parg, 0);
@@ -3171,6 +3169,9 @@ SSL_CTX *SSL_CTX_new_with_libctx(OPENSSL_CTX *libctx, const char *propq,
         goto err2;
 
 
+    if (!ssl_load_groups(ret))
+        goto err2;
+
     if (!SSL_CTX_set_ciphersuites(ret, OSSL_default_ciphersuites()))
         goto err;
 
@@ -3326,6 +3327,7 @@ int SSL_CTX_up_ref(SSL_CTX *ctx)
 void SSL_CTX_free(SSL_CTX *a)
 {
     int i;
+    size_t j;
 
     if (a == NULL)
         return;
@@ -3385,10 +3387,16 @@ void SSL_CTX_free(SSL_CTX *a)
     ssl_evp_md_free(a->md5);
     ssl_evp_md_free(a->sha1);
 
-    for (i = 0; i < SSL_ENC_NUM_IDX; i++)
-        ssl_evp_cipher_free(a->ssl_cipher_methods[i]);
-    for (i = 0; i < SSL_MD_NUM_IDX; i++)
-        ssl_evp_md_free(a->ssl_digest_methods[i]);
+    for (j = 0; j < SSL_ENC_NUM_IDX; j++)
+        ssl_evp_cipher_free(a->ssl_cipher_methods[j]);
+    for (j = 0; j < SSL_MD_NUM_IDX; j++)
+        ssl_evp_md_free(a->ssl_digest_methods[j]);
+    for (j = 0; j < a->group_list_len; j++) {
+        OPENSSL_free(a->group_list[j].tlsname);
+        OPENSSL_free(a->group_list[j].realname);
+        OPENSSL_free(a->group_list[j].algorithm);
+    }
+    OPENSSL_free(a->group_list);
 
     OPENSSL_free(a->sigalg_lookup_cache);
 
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index d2e8e23e23..58bc1f99c4 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -807,6 +807,28 @@ int ssl_hmac_final(SSL_HMAC *ctx, unsigned char *md, size_t *len,
                    size_t max_size);
 size_t ssl_hmac_size(const SSL_HMAC *ctx);
 
+typedef struct tls_group_info_st {
+    char *tlsname;           /* Curve Name as in TLS specs */
+    char *realname;          /* Curve Name according to provider */
+    char *algorithm;         /* Algorithm name to fetch */
+    unsigned int secbits;    /* Bits of security (from SP800-57) */
+    uint16_t group_id;       /* Group ID */
+    int mintls;              /* Minimum TLS version, -1 unsupported */
+    int maxtls;              /* Maximum TLS version (or 0 for undefined) */
+    int mindtls;             /* Minimum DTLS version, -1 unsupported */
+    int maxdtls;             /* Maximum DTLS version (or 0 for undefined) */
+} TLS_GROUP_INFO;
+
+/* flags values */
+# define TLS_GROUP_TYPE             0x0000000FU /* Mask for group type */
+# define TLS_GROUP_CURVE_PRIME      0x00000001U
+# define TLS_GROUP_CURVE_CHAR2      0x00000002U
+# define TLS_GROUP_CURVE_CUSTOM     0x00000004U
+# define TLS_GROUP_FFDHE            0x00000008U
+# define TLS_GROUP_ONLY_FOR_TLS1_3  0x00000010U
+
+# define TLS_GROUP_FFDHE_FOR_TLS1_3 (TLS_GROUP_FFDHE|TLS_GROUP_ONLY_FOR_TLS1_3)
+
 struct ssl_ctx_st {
     OPENSSL_CTX *libctx;
 
@@ -1160,6 +1182,10 @@ struct ssl_ctx_st {
 
     /* Cache of all sigalgs we know and whether they are available or not */
     struct sigalg_lookup_st *sigalg_lookup_cache;
+
+    TLS_GROUP_INFO *group_list;
+    size_t group_list_len;
+    size_t group_list_max_len;
 };
 
 typedef struct cert_pkey_st CERT_PKEY;
@@ -1783,24 +1809,6 @@ typedef struct sigalg_lookup_st {
     int enabled;
 } SIGALG_LOOKUP;
 
-typedef struct tls_group_info_st {
-    int nid;                    /* Curve NID */
-    const char *keytype;
-    int secbits;                /* Bits of security (from SP800-57) */
-    uint32_t flags;             /* For group type and applicable TLS versions */
-    uint16_t group_id;          /* Group ID */
-} TLS_GROUP_INFO;
-
-/* flags values */
-# define TLS_GROUP_TYPE             0x0000000FU /* Mask for group type */
-# define TLS_GROUP_CURVE_PRIME      0x00000001U
-# define TLS_GROUP_CURVE_CHAR2      0x00000002U
-# define TLS_GROUP_CURVE_CUSTOM     0x00000004U
-# define TLS_GROUP_FFDHE            0x00000008U
-# define TLS_GROUP_ONLY_FOR_TLS1_3  0x00000010U
-
-# define TLS_GROUP_FFDHE_FOR_TLS1_3 (TLS_GROUP_FFDHE|TLS_GROUP_ONLY_FOR_TLS1_3)
-
 /*
  * Structure containing table entry of certificate info corresponding to
  * CERT_PKEY entries
@@ -2438,6 +2446,7 @@ __owur int ssl_x509err2alert(int type);
 void ssl_sort_cipher_list(void);
 int ssl_load_ciphers(SSL_CTX *ctx);
 __owur int ssl_setup_sig_algs(SSL_CTX *ctx);
+int ssl_load_groups(SSL_CTX *ctx);
 __owur int ssl_fill_hello_random(SSL *s, int server, unsigned char *field,
                                  size_t len, DOWNGRADE dgrd);
 __owur int ssl_generate_master_secret(SSL *s, unsigned char *pms, size_t pmslen,
@@ -2626,16 +2635,17 @@ __owur int ssl_check_srvr_ecc_cert_and_alg(X509 *x, SSL *s);
 
 SSL_COMP *ssl3_comp_find(STACK_OF(SSL_COMP) *sk, int n);
 
-__owur const TLS_GROUP_INFO *tls1_group_id_lookup(uint16_t curve_id);
-__owur int tls1_group_id2nid(uint16_t group_id);
+__owur const TLS_GROUP_INFO *tls1_group_id_lookup(SSL_CTX *ctx, uint16_t curve_id);
+__owur int tls1_group_id2nid(uint16_t group_id, int include_unknown);
 __owur int tls1_check_group_id(SSL *s, uint16_t group_id, int check_own_curves);
 __owur uint16_t tls1_shared_group(SSL *s, int nmatch);
 __owur int tls1_set_groups(uint16_t **pext, size_t *pextlen,
                            int *curves, size_t ncurves);
-__owur int tls1_set_groups_list(uint16_t **pext, size_t *pextlen,
+__owur int tls1_set_groups_list(SSL_CTX *ctx, uint16_t **pext, size_t *pextlen,
                                 const char *str);
 __owur EVP_PKEY *ssl_generate_pkey_group(SSL *s, uint16_t id);
-__owur int tls_valid_group(SSL *s, uint16_t group_id, int version);
+__owur int tls_valid_group(SSL *s, uint16_t group_id, int minversion,
+                           int maxversion);
 __owur EVP_PKEY *ssl_generate_param_group(SSL *s, uint16_t id);
 #  ifndef OPENSSL_NO_EC
 void tls1_get_formatlist(SSL *s, const unsigned char **pformats,
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index ab2d98de60..abff069ec9 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -117,7 +117,7 @@ EXT_RETURN tls_construct_ctos_srp(SSL *s, WPACKET *pkt, unsigned int context,
 #endif
 
 #ifndef OPENSSL_NO_EC
-static int use_ecc(SSL *s, int max_version)
+static int use_ecc(SSL *s, int min_version, int max_version)
 {
     int i, end, ret = 0;
     unsigned long alg_k, alg_a;
@@ -152,7 +152,7 @@ static int use_ecc(SSL *s, int max_version)
     for (j = 0; j < num_groups; j++) {
         uint16_t ctmp = pgroups[j];
 
-        if (tls_valid_group(s, ctmp, max_version)
+        if (tls_valid_group(s, ctmp, min_version, max_version)
                 && tls_group_allowed(s, ctmp, SSL_SECOP_CURVE_SUPPORTED))
             return 1;
     }
@@ -174,7 +174,7 @@ EXT_RETURN tls_construct_ctos_ec_pt_formats(SSL *s, WPACKET *pkt,
                  SSL_F_TLS_CONSTRUCT_CTOS_EC_PT_FORMATS, reason);
         return EXT_RETURN_FAIL;
     }
-    if (!use_ecc(s, max_version))
+    if (!use_ecc(s, min_version, max_version))
         return EXT_RETURN_NOT_SENT;
 
     /* Add TLS extension ECPointFormats to the ClientHello message */
@@ -211,10 +211,10 @@ EXT_RETURN tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt,
     }
 
 #if defined(OPENSSL_NO_EC)
-    if (max_version < TLS1_3_VERSION)
+    if (SSL_IS_DTLS(s) || max_version < TLS1_3_VERSION)
         return EXT_RETURN_NOT_SENT;
 #else
-    if (!use_ecc(s, max_version) && max_version < TLS1_3_VERSION)
+    if (!use_ecc(s, min_version, max_version) && max_version < TLS1_3_VERSION)
         return EXT_RETURN_NOT_SENT;
 #endif
 
@@ -237,7 +237,7 @@ EXT_RETURN tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt,
     for (i = 0; i < num_groups; i++) {
         uint16_t ctmp = pgroups[i];
 
-        if (tls_valid_group(s, ctmp, max_version)
+        if (tls_valid_group(s, ctmp, min_version, max_version)
                 && tls_group_allowed(s, ctmp, SSL_SECOP_CURVE_SUPPORTED)) {
             if (!WPACKET_put_bytes_u16(pkt, ctmp)) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR,
@@ -1907,7 +1907,7 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
     skey = EVP_PKEY_new();
     if (skey == NULL || EVP_PKEY_copy_parameters(skey, ckey) <= 0) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_STOC_KEY_SHARE,
-                 ERR_R_MALLOC_FAILURE);
+                 SSL_R_COPY_PARAMETERS_FAILED);
         return 0;
     }
 
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index 3a0fee6ebc..27ddef9aaf 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1424,6 +1424,7 @@ EXT_RETURN tls_construct_stoc_supported_groups(SSL *s, WPACKET *pkt,
 {
     const uint16_t *groups;
     size_t numgroups, i, first = 1;
+    int version;
 
     /* s->s3.group_id is non zero if we accepted a key_share */
     if (s->s3.group_id == 0)
@@ -1438,10 +1439,11 @@ EXT_RETURN tls_construct_stoc_supported_groups(SSL *s, WPACKET *pkt,
     }
 
     /* Copy group ID if supported */
+    version = SSL_version(s);
     for (i = 0; i < numgroups; i++) {
         uint16_t group = groups[i];
 
-        if (tls_valid_group(s, group, SSL_version(s))
+        if (tls_valid_group(s, group, version, version)
                 && tls_group_allowed(s, group, SSL_SECOP_CURVE_SUPPORTED)) {
             if (first) {
                 /*
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 036bfadbe5..d3913e1b7d 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -3147,7 +3147,7 @@ static int tls_process_cke_dhe(SSL *s, PACKET *pkt)
     ckey = EVP_PKEY_new();
     if (ckey == NULL || EVP_PKEY_copy_parameters(ckey, skey) == 0) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_DHE,
-                 SSL_R_BN_LIB);
+                 SSL_R_COPY_PARAMETERS_FAILED);
         goto err;
     }
 
@@ -3216,7 +3216,7 @@ static int tls_process_cke_ecdhe(SSL *s, PACKET *pkt)
         ckey = EVP_PKEY_new();
         if (ckey == NULL || EVP_PKEY_copy_parameters(ckey, skey) <= 0) {
             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_ECDHE,
-                     ERR_R_EVP_LIB);
+                     SSL_R_COPY_PARAMETERS_FAILED);
             goto err;
         }
 
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index b80ac35d3a..b2752cd03d 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -21,8 +21,10 @@
 #include <openssl/x509v3.h>
 #include <openssl/dh.h>
 #include <openssl/bn.h>
+#include <openssl/provider.h>
 #include "internal/nelem.h"
 #include "internal/evp.h"
+#include "internal/tlsgroups.h"
 #include "ssl_local.h"
 #include <openssl/ct.h>
 
@@ -140,60 +142,54 @@ int tls1_clear(SSL *s)
     return 1;
 }
 
-/*
- * Table of group information.
- */
 #if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_EC)
-static const TLS_GROUP_INFO nid_list[] = {
-# ifndef OPENSSL_NO_EC
-    {NID_sect163k1, "EC", 80, TLS_GROUP_CURVE_CHAR2, 0x0001}, /* sect163k1 (1) */
-    {NID_sect163r1, "EC", 80, TLS_GROUP_CURVE_CHAR2, 0x0002}, /* sect163r1 (2) */
-    {NID_sect163r2, "EC", 80, TLS_GROUP_CURVE_CHAR2, 0x0003}, /* sect163r2 (3) */
-    {NID_sect193r1, "EC", 80, TLS_GROUP_CURVE_CHAR2, 0x0004}, /* sect193r1 (4) */
-    {NID_sect193r2, "EC", 80, TLS_GROUP_CURVE_CHAR2, 0x0005}, /* sect193r2 (5) */
-    {NID_sect233k1, "EC", 112, TLS_GROUP_CURVE_CHAR2, 0x0006}, /* sect233k1 (6) */
-    {NID_sect233r1, "EC", 112, TLS_GROUP_CURVE_CHAR2, 0x0007}, /* sect233r1 (7) */
-    {NID_sect239k1, "EC", 112, TLS_GROUP_CURVE_CHAR2, 0x0008}, /* sect239k1 (8) */
-    {NID_sect283k1, "EC", 128, TLS_GROUP_CURVE_CHAR2, 0x0009}, /* sect283k1 (9) */
-    {NID_sect283r1, "EC", 128, TLS_GROUP_CURVE_CHAR2, 0x000A}, /* sect283r1 (10) */
-    {NID_sect409k1, "EC", 192, TLS_GROUP_CURVE_CHAR2, 0x000B}, /* sect409k1 (11) */
-    {NID_sect409r1, "EC", 192, TLS_GROUP_CURVE_CHAR2, 0x000C}, /* sect409r1 (12) */
-    {NID_sect571k1, "EC", 256, TLS_GROUP_CURVE_CHAR2, 0x000D}, /* sect571k1 (13) */
-    {NID_sect571r1, "EC", 256, TLS_GROUP_CURVE_CHAR2, 0x000E}, /* sect571r1 (14) */
-    {NID_secp160k1, "EC", 80, TLS_GROUP_CURVE_PRIME, 0x000F}, /* secp160k1 (15) */
-    {NID_secp160r1, "EC", 80, TLS_GROUP_CURVE_PRIME, 0x0010}, /* secp160r1 (16) */
-    {NID_secp160r2, "EC", 80, TLS_GROUP_CURVE_PRIME, 0x0011}, /* secp160r2 (17) */
-    {NID_secp192k1, "EC", 80, TLS_GROUP_CURVE_PRIME, 0x0012}, /* secp192k1 (18) */
-    {NID_X9_62_prime192v1, "EC", 80, TLS_GROUP_CURVE_PRIME, 0x0013}, /* secp192r1 (19) */
-    {NID_secp224k1, "EC", 112, TLS_GROUP_CURVE_PRIME, 0x0014}, /* secp224k1 (20) */
-    {NID_secp224r1, "EC", 112, TLS_GROUP_CURVE_PRIME, 0x0015}, /* secp224r1 (21) */
-    {NID_secp256k1, "EC", 128, TLS_GROUP_CURVE_PRIME, 0x0016}, /* secp256k1 (22) */
-    {NID_X9_62_prime256v1, "EC", 128, TLS_GROUP_CURVE_PRIME, 0x0017}, /* secp256r1 (23) */
-    {NID_secp384r1, "EC", 192, TLS_GROUP_CURVE_PRIME, 0x0018}, /* secp384r1 (24) */
-    {NID_secp521r1, "EC", 256, TLS_GROUP_CURVE_PRIME, 0x0019}, /* secp521r1 (25) */
-    {NID_brainpoolP256r1, "EC", 128, TLS_GROUP_CURVE_PRIME, 0x001A}, /* brainpoolP256r1 (26) */
-    {NID_brainpoolP384r1, "EC", 192, TLS_GROUP_CURVE_PRIME, 0x001B}, /* brainpoolP384r1 (27) */
-    {NID_brainpoolP512r1, "EC", 256, TLS_GROUP_CURVE_PRIME, 0x001C}, /* brainpool512r1 (28) */
-    {EVP_PKEY_X25519, "X25519", 128, TLS_GROUP_CURVE_CUSTOM, 0x001D}, /* X25519 (29) */
-    {EVP_PKEY_X448, "X448", 224, TLS_GROUP_CURVE_CUSTOM, 0x001E}, /* X448 (30) */
-# endif /* OPENSSL_NO_EC */
-# ifndef OPENSSL_NO_GOST
-    {NID_id_tc26_gost_3410_2012_256_paramSetA, "GOST_2012_256", 128, TLS_GROUP_CURVE_PRIME, 0x0022}, /* GC256A (34) */
-    {NID_id_tc26_gost_3410_2012_256_paramSetB, "GOST_2012_256", 128, TLS_GROUP_CURVE_PRIME, 0x0023}, /* GC256B (35) */
-    {NID_id_tc26_gost_3410_2012_256_paramSetC, "GOST_2012_256", 128, TLS_GROUP_CURVE_PRIME, 0x0024}, /* GC256C (36) */
-    {NID_id_tc26_gost_3410_2012_256_paramSetD, "GOST_2012_256", 128, TLS_GROUP_CURVE_PRIME, 0x0025}, /* GC256D (37) */
-    {NID_id_tc26_gost_3410_2012_512_paramSetA, "GOST_2012_512", 256, TLS_GROUP_CURVE_PRIME, 0x0026}, /* GC512A (38) */
-    {NID_id_tc26_gost_3410_2012_512_paramSetB, "GOST_2012_512", 256, TLS_GROUP_CURVE_PRIME, 0x0027}, /* GC512B (39) */
-    {NID_id_tc26_gost_3410_2012_512_paramSetC, "GOST_2012_512", 256, TLS_GROUP_CURVE_PRIME, 0x0028}, /* GC512C (40) */
-# endif /* OPENSSL_NO_GOST */
-# ifndef OPENSSL_NO_DH
-    /* Security bit values for FFDHE groups are updated as per RFC 7919 */
-    {NID_ffdhe2048, "DH", 103, TLS_GROUP_FFDHE_FOR_TLS1_3, 0x0100}, /* ffdhe2048 (0x0100) */
-    {NID_ffdhe3072, "DH", 125, TLS_GROUP_FFDHE_FOR_TLS1_3, 0x0101}, /* ffdhe3072 (0x0101) */
-    {NID_ffdhe4096, "DH", 150, TLS_GROUP_FFDHE_FOR_TLS1_3, 0x0102}, /* ffdhe4096 (0x0102) */
-    {NID_ffdhe6144, "DH", 175, TLS_GROUP_FFDHE_FOR_TLS1_3, 0x0103}, /* ffdhe6144 (0x0103) */
-    {NID_ffdhe8192, "DH", 192, TLS_GROUP_FFDHE_FOR_TLS1_3, 0x0104}, /* ffdhe8192 (0x0104) */
-# endif /* OPENSSL_NO_DH */
+/* Legacy NID to group_id mapping. Only works for groups we know about */
+static struct {
+    int nid;
+    uint16_t group_id;
+} nid_to_group[] = {
+    {NID_sect163k1, OSSL_TLS_GROUP_ID_sect163k1},
+    {NID_sect163r1, OSSL_TLS_GROUP_ID_sect163r1},
+    {NID_sect163r2, OSSL_TLS_GROUP_ID_sect163r2},
+    {NID_sect193r1, OSSL_TLS_GROUP_ID_sect193r1},
+    {NID_sect193r2, OSSL_TLS_GROUP_ID_sect193r2},
+    {NID_sect233k1, OSSL_TLS_GROUP_ID_sect233k1},
+    {NID_sect233r1, OSSL_TLS_GROUP_ID_sect233r1},
+    {NID_sect239k1, OSSL_TLS_GROUP_ID_sect239k1},
+    {NID_sect283k1, OSSL_TLS_GROUP_ID_sect283k1},
+    {NID_sect283r1, OSSL_TLS_GROUP_ID_sect283r1},
+    {NID_sect409k1, OSSL_TLS_GROUP_ID_sect409k1},
+    {NID_sect409r1, OSSL_TLS_GROUP_ID_sect409r1},
+    {NID_sect571k1, OSSL_TLS_GROUP_ID_sect571k1},
+    {NID_sect571r1, OSSL_TLS_GROUP_ID_sect571r1},
+    {NID_secp160k1, OSSL_TLS_GROUP_ID_secp160k1},
+    {NID_secp160r1, OSSL_TLS_GROUP_ID_secp160r1},
+    {NID_secp160r2, OSSL_TLS_GROUP_ID_secp160r2},
+    {NID_secp192k1, OSSL_TLS_GROUP_ID_secp192k1},
+    {NID_X9_62_prime192v1, OSSL_TLS_GROUP_ID_secp192r1},
+    {NID_secp224k1, OSSL_TLS_GROUP_ID_secp224k1},
+    {NID_secp224r1, OSSL_TLS_GROUP_ID_secp224r1},
+    {NID_secp256k1, OSSL_TLS_GROUP_ID_secp256k1},
+    {NID_X9_62_prime256v1, OSSL_TLS_GROUP_ID_secp256r1},
+    {NID_secp384r1, OSSL_TLS_GROUP_ID_secp384r1},
+    {NID_secp521r1, OSSL_TLS_GROUP_ID_secp521r1},
+    {NID_brainpoolP256r1, OSSL_TLS_GROUP_ID_brainpoolP256r1},
+    {NID_brainpoolP384r1, OSSL_TLS_GROUP_ID_brainpoolP384r1},
+    {NID_brainpoolP512r1, OSSL_TLS_GROUP_ID_brainpoolP512r1},
+    {EVP_PKEY_X25519, OSSL_TLS_GROUP_ID_x25519},
+    {EVP_PKEY_X448, OSSL_TLS_GROUP_ID_x448},
+    {NID_id_tc26_gost_3410_2012_256_paramSetA, 0x0022},
+    {NID_id_tc26_gost_3410_2012_256_paramSetB, 0x0023},
+    {NID_id_tc26_gost_3410_2012_256_paramSetC, 0x0024},
+    {NID_id_tc26_gost_3410_2012_256_paramSetD, 0x0025},
+    {NID_id_tc26_gost_3410_2012_512_paramSetA, 0x0026},
+    {NID_id_tc26_gost_3410_2012_512_paramSetB, 0x0027},
+    {NID_id_tc26_gost_3410_2012_512_paramSetC, 0x0028},
+    {NID_ffdhe2048, OSSL_TLS_GROUP_ID_ffdhe2048},
+    {NID_ffdhe3072, OSSL_TLS_GROUP_ID_ffdhe3072},
+    {NID_ffdhe4096, OSSL_TLS_GROUP_ID_ffdhe4096},
+    {NID_ffdhe6144, OSSL_TLS_GROUP_ID_ffdhe6144},
+    {NID_ffdhe8192, OSSL_TLS_GROUP_ID_ffdhe8192}
 };
 #endif
 
@@ -241,36 +237,243 @@ static const uint16_t suiteb_curves[] = {
 };
 #endif
 
-const TLS_GROUP_INFO *tls1_group_id_lookup(uint16_t group_id)
+struct provider_group_data_st {
+    SSL_CTX *ctx;
+    OSSL_PROVIDER *provider;
+};
+
+#define TLS_GROUP_LIST_MALLOC_BLOCK_SIZE        10
+static OSSL_CALLBACK add_provider_groups;
+static int add_provider_groups(const OSSL_PARAM params[], void *data)
+{
+    struct provider_group_data_st *pgd = data;
+    SSL_CTX *ctx = pgd->ctx;
+    OSSL_PROVIDER *provider = pgd->provider;
+    const OSSL_PARAM *p;
+    TLS_GROUP_INFO *ginf = NULL;
+    EVP_KEYMGMT *keymgmt;
+    unsigned int gid;
+    int ret = 0;
+
+    if (ctx->group_list_max_len == ctx->group_list_len) {
+        TLS_GROUP_INFO *tmp = NULL;
+
+        if (ctx->group_list_max_len == 0)
+            tmp = OPENSSL_malloc(sizeof(TLS_GROUP_INFO)
+                                 * TLS_GROUP_LIST_MALLOC_BLOCK_SIZE);
+        else
+            tmp = OPENSSL_realloc(ctx->group_list,
+                                  (ctx->group_list_max_len
+                                   + TLS_GROUP_LIST_MALLOC_BLOCK_SIZE)
+                                  * sizeof(TLS_GROUP_INFO));
+        if (tmp == NULL) {
+            SSLerr(0, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        ctx->group_list = tmp;
+        memset(tmp + ctx->group_list_max_len,
+               0,
+               sizeof(TLS_GROUP_INFO) * TLS_GROUP_LIST_MALLOC_BLOCK_SIZE);
+        ctx->group_list_max_len += TLS_GROUP_LIST_MALLOC_BLOCK_SIZE;
+    }
+
+    ginf = &ctx->group_list[ctx->group_list_len];
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_NAME);
+    if (p == NULL || p->data_type != OSSL_PARAM_UTF8_STRING) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+    ginf->tlsname = OPENSSL_strdup(p->data);
+    if (ginf->tlsname == NULL) {
+        SSLerr(0, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL);
+    if (p == NULL || p->data_type != OSSL_PARAM_UTF8_STRING) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+    ginf->realname = OPENSSL_strdup(p->data);
+    if (ginf->realname == NULL) {
+        SSLerr(0, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_ID);
+    if (p == NULL || !OSSL_PARAM_get_uint(p, &gid) || gid > UINT16_MAX) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+    ginf->group_id = (uint16_t)gid;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_ALG);
+    if (p == NULL || p->data_type != OSSL_PARAM_UTF8_STRING) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+    ginf->algorithm = OPENSSL_strdup(p->data);
+    if (ginf->algorithm == NULL) {
+        SSLerr(0, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS);
+    if (p == NULL || !OSSL_PARAM_get_uint(p, &ginf->secbits)) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_MIN_TLS);
+    if (p == NULL || !OSSL_PARAM_get_int(p, &ginf->mintls)) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_MAX_TLS);
+    if (p == NULL || !OSSL_PARAM_get_int(p, &ginf->maxtls)) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS);
+    if (p == NULL || !OSSL_PARAM_get_int(p, &ginf->mindtls)) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS);
+    if (p == NULL || !OSSL_PARAM_get_int(p, &ginf->maxdtls)) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+    /*
+     * Now check that the algorithm is actually usable for our property query
+     * string. Regardless of the result we still return success because we have
+     * successfully processed this group, even though we may decide not to use
+     * it.
+     */
+    ret = 1;
+    keymgmt = EVP_KEYMGMT_fetch(ctx->libctx, ginf->algorithm, ctx->propq);
+    if (keymgmt != NULL) {
+        /*
+         * We have successfully fetched the algorithm - however if the provider
+         * doesn't match this one then we ignore it.
+         *
+         * Note: We're cheating a little here. Technically if the same algorithm
+         * is available from more than one provider then it is undefined which
+         * implementation you will get back. Theoretically this could be
+         * different every time...we assume here that you'll always get the
+         * same one back if you repeat the exact same fetch. Is this a reasonable
+         * assumption to make (in which case perhaps we should document this
+         * behaviour)?
+         */
+        if (EVP_KEYMGMT_provider(keymgmt) == provider) {
+            /* We have a match - so we will use this group */
+            ctx->group_list_len++;
+            ginf = NULL;
+        }
+        EVP_KEYMGMT_free(keymgmt);
+    }
+ err:
+    if (ginf != NULL) {
+        OPENSSL_free(ginf->tlsname);
+        OPENSSL_free(ginf->realname);
+        OPENSSL_free(ginf->algorithm);
+        ginf->tlsname = ginf->realname = NULL;
+    }
+    return ret;
+}
+
+static int discover_provider_groups(OSSL_PROVIDER *provider, void *vctx)
+{
+    struct provider_group_data_st pgd;
+
+    pgd.ctx = vctx;
+    pgd.provider = provider;
+    return OSSL_PROVIDER_get_capabilities(provider, "TLS-GROUP",
+                                          add_provider_groups, &pgd);
+}
+
+int ssl_load_groups(SSL_CTX *ctx)
+{
+    return OSSL_PROVIDER_do_all(ctx->libctx, discover_provider_groups, ctx);
+}
+
+static uint16_t tls1_group_name2id(SSL_CTX *ctx, const char *name)
 {
-#if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_EC)
     size_t i;
+    int nid = NID_undef;
 
-    /* ECC curves from RFC 4492 and RFC 7027 FFDHE group from RFC 8446 */
-    for (i = 0; i < OSSL_NELEM(nid_list); i++) {
-        if (nid_list[i].group_id == group_id)
-            return &nid_list[i];
+    /* See if we can identify a nid for this name */
+#ifndef OPENSSL_NO_EC
+    nid = EC_curve_nist2nid(name);
+#endif
+    if (nid == NID_undef)
+        nid = OBJ_sn2nid(name);
+    if (nid == NID_undef)
+        nid = OBJ_ln2nid(name);
+
+    for (i = 0; i < ctx->group_list_len; i++) {
+        if (strcmp(ctx->group_list[i].tlsname, name) == 0
+                || (nid != NID_undef
+                    && nid == tls1_group_id2nid(ctx->group_list[i].group_id,
+                                                0)))
+            return ctx->group_list[i].group_id;
     }
-#endif /* !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_EC) */
+
+    return 0;
+}
+
+const TLS_GROUP_INFO *tls1_group_id_lookup(SSL_CTX *ctx, uint16_t group_id)
+{
+    size_t i;
+
+    for (i = 0; i < ctx->group_list_len; i++) {
+        if (ctx->group_list[i].group_id == group_id)
+            return &ctx->group_list[i];
+    }
+
     return NULL;
 }
 
 #if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_EC)
-int tls1_group_id2nid(uint16_t group_id)
+int tls1_group_id2nid(uint16_t group_id, int include_unknown)
 {
-    const TLS_GROUP_INFO *ginf = tls1_group_id_lookup(group_id);
+    size_t i;
+
+    if (group_id == 0)
+        return NID_undef;
 
-    return ginf == NULL ? NID_undef : ginf->nid;
+    /*
+     * Return well known Group NIDs - for backwards compatibility. This won't
+     * work for groups we don't know about.
+     */
+    for (i = 0; i < OSSL_NELEM(nid_to_group); i++)
+    {
+        if (nid_to_group[i].group_id == group_id)
+            return nid_to_group[i].nid;
+    }
+    if (!include_unknown)
+        return NID_undef;
+    return TLSEXT_nid_unknown | (int)group_id;
 }
 
 static uint16_t tls1_nid2group_id(int nid)
 {
     size_t i;
 
-    for (i = 0; i < OSSL_NELEM(nid_list); i++) {
-        if (nid_list[i].nid == nid)
-            return nid_list[i].group_id;
+    /*
+     * Return well known Group ids - for backwards compatibility. This won't
+     * work for groups we don't know about.
+     */
+    for (i = 0; i < OSSL_NELEM(nid_to_group); i++)
+    {
+        if (nid_to_group[i].nid == nid)
+            return nid_to_group[i].group_id;
     }
+
     return 0;
 }
 #endif /* !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH) */
@@ -318,36 +521,50 @@ void tls1_get_supported_groups(SSL *s, const uint16_t **pgroups,
 #endif /* !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH) */
 }
 
-int tls_valid_group(SSL *s, uint16_t group_id, int version)
+int tls_valid_group(SSL *s, uint16_t group_id, int minversion, int maxversion)
 {
-    const TLS_GROUP_INFO *ginfo = tls1_group_id_lookup(group_id);
+    const TLS_GROUP_INFO *ginfo = tls1_group_id_lookup(s->ctx, group_id);
+    int ret;
+
+    if (ginfo == NULL)
+        return 0;
 
-    if (version < TLS1_3_VERSION) {
-        if ((ginfo->flags & TLS_GROUP_ONLY_FOR_TLS1_3) != 0)
+    if (SSL_IS_DTLS(s)) {
+        if (ginfo->mindtls < 0 || ginfo->maxdtls < 0)
             return 0;
+        if (ginfo->maxdtls == 0)
+            ret = 1;
+        else
+            ret = DTLS_VERSION_LE(minversion, ginfo->maxdtls);
+        if (ginfo->mindtls > 0)
+            ret &= DTLS_VERSION_GE(maxversion, ginfo->mindtls);
+    } else {
+        if (ginfo->mintls < 0 || ginfo->maxtls < 0)
+            return 0;
+        if (ginfo->maxtls == 0)
+            ret = 1;
+        else
+            ret = (minversion <= ginfo->maxtls);
+        if (ginfo->mintls > 0)
+            ret &= (maxversion >= ginfo->mintls);
     }
-    return 1;
+
+    return ret;
 }
 
 /* See if group is allowed by security callback */
 int tls_group_allowed(SSL *s, uint16_t group, int op)
 {
-    const TLS_GROUP_INFO *ginfo = tls1_group_id_lookup(group);
+    const TLS_GROUP_INFO *ginfo = tls1_group_id_lookup(s->ctx, group);
     unsigned char gtmp[2];
 
     if (ginfo == NULL)
         return 0;
-#ifdef OPENSSL_NO_EC2M
-    if (ginfo->flags & TLS_GROUP_CURVE_CHAR2)
-        return 0;
-#endif
-#ifdef OPENSSL_NO_DH
-    if (ginfo->flags & TLS_GROUP_FFDHE)
-        return 0;
-#endif
+
     gtmp[0] = group >> 8;
     gtmp[1] = group & 0xff;
-    return ssl_security(s, op, ginfo->secbits, ginfo->nid, (void *)gtmp);
+    return ssl_security(s, op, ginfo->secbits,
+                        tls1_group_id2nid(ginfo->group_id, 0), (void *)gtmp);
 }
 
 /* Return 1 if "id" is in "list" */
@@ -469,59 +686,65 @@ err:
 #endif /* !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH) */
 }
 
-#if !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH)
-# define MAX_GROUPLIST   OSSL_NELEM(nid_list)
+/* TODO(3.0): An arbitrary amount for now. Take another look at this */
+# define MAX_GROUPLIST   40
 
 typedef struct {
-    size_t nidcnt;
-    int nid_arr[MAX_GROUPLIST];
-} nid_cb_st;
+    SSL_CTX *ctx;
+    size_t gidcnt;
+    uint16_t gid_arr[MAX_GROUPLIST];
+} gid_cb_st;
 
-static int nid_cb(const char *elem, int len, void *arg)
+static int gid_cb(const char *elem, int len, void *arg)
 {
-    nid_cb_st *narg = arg;
+    gid_cb_st *garg = arg;
     size_t i;
-    int nid = NID_undef;
+    uint16_t gid = 0;
     char etmp[20];
+
     if (elem == NULL)
         return 0;
-    if (narg->nidcnt == MAX_GROUPLIST)
+    if (garg->gidcnt == MAX_GROUPLIST)
         return 0;
     if (len > (int)(sizeof(etmp) - 1))
         return 0;
     memcpy(etmp, elem, len);
     etmp[len] = 0;
-# ifndef OPENSSL_NO_EC
-    nid = EC_curve_nist2nid(etmp);
-# endif
-    if (nid == NID_undef)
-        nid = OBJ_sn2nid(etmp);
-    if (nid == NID_undef)
-        nid = OBJ_ln2nid(etmp);
-    if (nid == NID_undef)
+
+    gid = tls1_group_name2id(garg->ctx, etmp);
+    if (gid == 0)
         return 0;
-    for (i = 0; i < narg->nidcnt; i++)
-        if (narg->nid_arr[i] == nid)
+    for (i = 0; i < garg->gidcnt; i++)
+        if (garg->gid_arr[i] == gid)
             return 0;
-    narg->nid_arr[narg->nidcnt++] = nid;
+    garg->gid_arr[garg->gidcnt++] = gid;
     return 1;
 }
-#endif /* !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH) */
 
-/* Set groups based on a colon separate list */
-int tls1_set_groups_list(uint16_t **pext, size_t *pextlen, const char *str)
+/* Set groups based on a colon separated list */
+int tls1_set_groups_list(SSL_CTX *ctx, uint16_t **pext, size_t *pextlen,
+                         const char *str)
 {
-#if !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH)
-    nid_cb_st ncb;
-    ncb.nidcnt = 0;
-    if (!CONF_parse_list(str, ':', 1, nid_cb, &ncb))
+    gid_cb_st gcb;
+    uint16_t *tmparr;
+
+    gcb.gidcnt = 0;
+    gcb.ctx = ctx;
+    if (!CONF_parse_list(str, ':', 1, gid_cb, &gcb))
         return 0;
     if (pext == NULL)
         return 1;
-    return tls1_set_groups(pext, pextlen, ncb.nid_arr, ncb.nidcnt);
-#else
-    return 0;
-#endif
+
+    /*
+     * gid_cb ensurse there are no duplicates so we can just go ahead and set
+     * the result
+     */
+    tmparr = OPENSSL_memdup(gcb.gid_arr, gcb.gidcnt * sizeof(*tmparr));
+    if (tmparr == NULL)
+        return 0;
+    *pext = tmparr;
+    *pextlen = gcb.gidcnt;
+    return 1;
 }
 
 /* Check a group id matches preferences */
diff --git a/test/acvp_test.c b/test/acvp_test.c
index 0e3e117133..737d2c61bb 100644
--- a/test/acvp_test.c
+++ b/test/acvp_test.c
@@ -120,8 +120,7 @@ static int ecdsa_keygen_test(int id)
 
     if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL))
         || !TEST_int_gt(EVP_PKEY_keygen_init(ctx), 0)
-        || !TEST_true(EVP_PKEY_CTX_set_ec_paramgen_curve_name(ctx,
-                                                              tst->curve_name))
+        || !TEST_true(EVP_PKEY_CTX_set_group_name(ctx, tst->curve_name))
         || !TEST_int_gt(EVP_PKEY_keygen(ctx, &pkey), 0)
         || !TEST_true(pkey_get_bn_bytes(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv,
                                         &priv_len))
@@ -156,7 +155,7 @@ static int ecdsa_create_pkey(EVP_PKEY **pkey, const char *curve_name,
     if (!TEST_ptr(bld = OSSL_PARAM_BLD_new())
         || (curve_name != NULL
             && !TEST_true(OSSL_PARAM_BLD_push_utf8_string(
-                              bld, OSSL_PKEY_PARAM_EC_NAME, curve_name, 0) > 0))
+                              bld, OSSL_PKEY_PARAM_GROUP_NAME, curve_name, 0) > 0))
         || !TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
                                                        OSSL_PKEY_PARAM_PUB_KEY,
                                                        pub, pub_len) > 0)
@@ -252,8 +251,7 @@ static int ecdsa_siggen_test(int id)
 
     if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL))
         || !TEST_int_gt(EVP_PKEY_keygen_init(ctx), 0)
-        || !TEST_true(EVP_PKEY_CTX_set_ec_paramgen_curve_name(ctx,
-                                                              tst->curve_name))
+        || !TEST_true(EVP_PKEY_CTX_set_group_name(ctx, tst->curve_name))
         || !TEST_int_gt(EVP_PKEY_keygen(ctx, &pkey), 0))
         goto err;
 
@@ -903,7 +901,7 @@ static int dh_create_pkey(EVP_PKEY **pkey, const char *group_name,
     if (!TEST_ptr(bld = OSSL_PARAM_BLD_new())
         || (group_name != NULL
             && !TEST_int_gt(OSSL_PARAM_BLD_push_utf8_string(
-                              bld, OSSL_PKEY_PARAM_DH_GROUP,
+                              bld, OSSL_PKEY_PARAM_GROUP_NAME,
                               group_name, 0), 0)))
         goto err;
 
@@ -947,7 +945,7 @@ static int dh_safe_prime_keygen_test(int id)
     OSSL_PARAM params[2];
     const struct dh_safe_prime_keygen_st *tst = &dh_safe_prime_keygen_data[id];
 
-    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_DH_GROUP,
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
                                                  (char *)tst->group_name, 0);
     params[1] = OSSL_PARAM_construct_end();
 
diff --git a/test/build.info b/test/build.info
index 6d7043208a..430931b8e1 100644
--- a/test/build.info
+++ b/test/build.info
@@ -297,7 +297,7 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[param_build_test]=../include ../apps/include
   DEPEND[param_build_test]=../libcrypto.a libtestutil.a
 
-  SOURCE[sslapitest]=sslapitest.c ssltestlib.c filterprov.c
+  SOURCE[sslapitest]=sslapitest.c ssltestlib.c filterprov.c tls-provider.c
   INCLUDE[sslapitest]=../include ../apps/include ..
   DEPEND[sslapitest]=../libcrypto ../libssl libtestutil.a
 
diff --git a/test/dsatest.c b/test/dsatest.c
index 8444ea147a..614a8ea1d8 100644
--- a/test/dsatest.c
+++ b/test/dsatest.c
@@ -282,7 +282,7 @@ static int dsa_keygen_test(void)
                                              &pcount_out))
         || !TEST_int_eq(pcount_out, expected_c)
         || !TEST_false(EVP_PKEY_get_utf8_string_param(key,
-                                                      OSSL_PKEY_PARAM_DH_GROUP,
+                                                      OSSL_PKEY_PARAM_GROUP_NAME,
                                                       group_out,
                                                       sizeof(group_out), &len)))
         goto end;
diff --git a/test/evp_pkey_provided_test.c b/test/evp_pkey_provided_test.c
index ffb56cb3ee..fd3e580d8c 100644
--- a/test/evp_pkey_provided_test.c
+++ b/test/evp_pkey_provided_test.c
@@ -447,7 +447,7 @@ static int test_fromdata_dh_named_group(void)
         || !TEST_ptr(pub = BN_bin2bn(pub_data, sizeof(pub_data), NULL))
         || !TEST_ptr(priv = BN_bin2bn(priv_data, sizeof(priv_data), NULL))
         || !TEST_true(OSSL_PARAM_BLD_push_utf8_string(bld,
-                                                      OSSL_PKEY_PARAM_DH_GROUP,
+                                                      OSSL_PKEY_PARAM_GROUP_NAME,
                                                       group_name, 0))
         || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub))
         || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv))
@@ -464,7 +464,7 @@ static int test_fromdata_dh_named_group(void)
         || !TEST_int_eq(EVP_PKEY_size(pk), 256))
         goto err;
 
-    if (!TEST_true(EVP_PKEY_get_utf8_string_param(pk, OSSL_PKEY_PARAM_DH_GROUP,
+    if (!TEST_true(EVP_PKEY_get_utf8_string_param(pk, OSSL_PKEY_PARAM_GROUP_NAME,
                                                   name_out, sizeof(name_out),
                                                   &len))
         || !TEST_str_eq(name_out, group_name)
@@ -588,7 +588,7 @@ static int test_fromdata_dh_fips186_4(void)
         || !TEST_ptr(pub = BN_bin2bn(pub_data, sizeof(pub_data), NULL))
         || !TEST_ptr(priv = BN_bin2bn(priv_data, sizeof(priv_data), NULL))
         || !TEST_true(OSSL_PARAM_BLD_push_utf8_string(bld,
-                                                      OSSL_PKEY_PARAM_DH_GROUP,
+                                                      OSSL_PKEY_PARAM_GROUP_NAME,
                                                       group_name, 0))
         || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub))
         || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv))
@@ -605,7 +605,7 @@ static int test_fromdata_dh_fips186_4(void)
         || !TEST_int_eq(EVP_PKEY_size(pk), 256))
         goto err;
 
-    if (!TEST_true(EVP_PKEY_get_utf8_string_param(pk, OSSL_PKEY_PARAM_DH_GROUP,
+    if (!TEST_true(EVP_PKEY_get_utf8_string_param(pk, OSSL_PKEY_PARAM_GROUP_NAME,
                                                   name_out, sizeof(name_out),
                                                   &len))
         || !TEST_str_eq(name_out, group_name)
@@ -928,7 +928,7 @@ static int test_fromdata_ec(void)
                                          sizeof(ec_priv_keydata), NULL)))
         goto err;
 
-    if (OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_NAME,
+    if (OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME,
                                         curve, 0) <= 0)
         goto err;
     if (OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY,
@@ -955,12 +955,12 @@ static int test_fromdata_ec(void)
         goto err;
 
     if (!TEST_ptr(gettable = EVP_PKEY_gettable_params(pk))
-        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_EC_NAME))
+        || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_GROUP_NAME))
         || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_PUB_KEY))
         || !TEST_ptr(OSSL_PARAM_locate_const(gettable, OSSL_PKEY_PARAM_PRIV_KEY)))
         goto err;
 
-    if (!EVP_PKEY_get_utf8_string_param(pk, OSSL_PKEY_PARAM_EC_NAME,
+    if (!EVP_PKEY_get_utf8_string_param(pk, OSSL_PKEY_PARAM_GROUP_NAME,
                                         out_curve_name, sizeof(out_curve_name),
                                         &len)
         || !TEST_str_eq(out_curve_name, curve)
@@ -1144,7 +1144,7 @@ static int test_fromdata_dsa_fips186_4(void)
         || !TEST_int_eq(EVP_PKEY_size(pk), 2 + 2 * (3 + sizeof(q_data))))
         goto err;
 
-    if (!TEST_false(EVP_PKEY_get_utf8_string_param(pk, OSSL_PKEY_PARAM_DH_GROUP,
+    if (!TEST_false(EVP_PKEY_get_utf8_string_param(pk, OSSL_PKEY_PARAM_GROUP_NAME,
                                                    name_out, sizeof(name_out),
                                                    &len))
         || !TEST_true(EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_PUB_KEY,
diff --git a/test/filterprov.c b/test/filterprov.c
index 9fe6690e50..c2189e25a8 100644
--- a/test/filterprov.c
+++ b/test/filterprov.c
@@ -66,6 +66,14 @@ static int filter_get_params(void *provctx, OSSL_PARAM params[])
     return OSSL_PROVIDER_get_params(globs->deflt, params);
 }
 
+static int filter_get_capabilities(void *provctx, const char *capability,
+                                   OSSL_CALLBACK *cb, void *arg)
+{
+    struct filter_prov_globals_st *globs = get_globals();
+
+    return OSSL_PROVIDER_get_capabilities(globs->deflt, capability, cb, arg);
+}
+
 static const OSSL_ALGORITHM *filter_query(void *provctx,
                                           int operation_id,
                                           int *no_cache)
@@ -97,6 +105,7 @@ static const OSSL_DISPATCH filter_dispatch_table[] = {
     { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))filter_gettable_params },
     { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))filter_get_params },
     { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))filter_query },
+    { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, (void (*)(void))filter_get_capabilities },
     { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))filter_teardown },
     { 0, NULL }
 };
diff --git a/test/ssl-tests/20-cert-select.cnf b/test/ssl-tests/20-cert-select.cnf
index f788069f56..d1cfcb6f46 100644
--- a/test/ssl-tests/20-cert-select.cnf
+++ b/test/ssl-tests/20-cert-select.cnf
@@ -1715,12 +1715,10 @@ client = 52-TLS 1.3 ECDSA with brainpool-client
 [52-TLS 1.3 ECDSA with brainpool-server]
 Certificate = ${ENV::TEST_CERTS_DIR}/server-ecdsa-brainpoolP256r1-cert.pem
 CipherString = DEFAULT
-Groups = brainpoolP256r1
 PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ecdsa-brainpoolP256r1-key.pem
 
 [52-TLS 1.3 ECDSA with brainpool-client]
 CipherString = DEFAULT
-Groups = brainpoolP256r1
 MaxProtocol = TLSv1.3
 MinProtocol = TLSv1.3
 RequestCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
diff --git a/test/ssl-tests/20-cert-select.cnf.in b/test/ssl-tests/20-cert-select.cnf.in
index 79325e71c1..38cac87cea 100644
--- a/test/ssl-tests/20-cert-select.cnf.in
+++ b/test/ssl-tests/20-cert-select.cnf.in
@@ -890,11 +890,9 @@ my @tests_tls_1_3_non_fips = (
         server =>  {
             "Certificate" => test_pem("server-ecdsa-brainpoolP256r1-cert.pem"),
             "PrivateKey" => test_pem("server-ecdsa-brainpoolP256r1-key.pem"),
-            "Groups" => "brainpoolP256r1",
         },
         client => {
             "RequestCAFile" => test_pem("root-cert.pem"),
-            "Groups" => "brainpoolP256r1",
             "MinProtocol" => "TLSv1.3",
             "MaxProtocol" => "TLSv1.3"
         },
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 4665569054..e0a92b31ae 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -42,6 +42,11 @@
 OSSL_provider_init_fn filter_provider_init;
 int filter_provider_set_filter(int operation, const char *name);
 
+/* Defined in tls-provider.c */
+int tls_provider_init(const OSSL_CORE_HANDLE *handle,
+                      const OSSL_DISPATCH *in,
+                      const OSSL_DISPATCH **out,
+                      void **provctx);
 DEFINE_STACK_OF(OCSP_RESPID)
 DEFINE_STACK_OF(X509)
 DEFINE_STACK_OF(X509_NAME)
@@ -8039,6 +8044,8 @@ static int test_sigalgs_available(int idx)
 
     cctx = SSL_CTX_new_with_libctx(clientctx, NULL, TLS_client_method());
     sctx = SSL_CTX_new_with_libctx(serverctx, NULL, TLS_server_method());
+    if (!TEST_ptr(cctx) || !TEST_ptr(sctx))
+        goto end;
 
     if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
                                        TLS_client_method(),
@@ -8104,6 +8111,45 @@ static int test_sigalgs_available(int idx)
 }
 #endif /* OPENSSL_NO_EC */
 
+#ifndef OPENSSL_NO_TLS1_3
+static int test_pluggable_group(void)
+{
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+    OSSL_PROVIDER *tlsprov = OSSL_PROVIDER_load(libctx, "tls-provider");
+
+    if (!TEST_ptr(tlsprov))
+        goto end;
+
+    if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
+                                       TLS_client_method(),
+                                       TLS1_3_VERSION,
+                                       TLS1_3_VERSION,
+                                       &sctx, &cctx, cert, privkey))
+            || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+                                             NULL, NULL)))
+        goto end;
+
+    if (!TEST_true(SSL_set1_groups_list(serverssl, "xorgroup"))
+            || !TEST_true(SSL_set1_groups_list(clientssl, "xorgroup")))
+        goto end;
+
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
+        goto end;
+
+    testresult = 1;
+
+ end:
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+    OSSL_PROVIDER_unload(tlsprov);
+
+    return testresult;
+}
+#endif
 
 OPT_TEST_DECLARE_USAGE("certfile privkeyfile srpvfile tmpfile provider config\n")
 
@@ -8153,6 +8199,15 @@ int setup_tests(void)
     if (strcmp(modulename, "fips") == 0)
         is_fips = 1;
 
+    /*
+     * We add, but don't load the test "tls-provider". We'll load it when we
+     * need it.
+     */
+    if (!TEST_true(OSSL_PROVIDER_add_builtin(libctx, "tls-provider",
+                                             tls_provider_init)))
+        return 0;
+
+
     if (getenv("OPENSSL_TEST_GETCOUNTS") != NULL) {
 #ifdef OPENSSL_NO_CRYPTO_MDEBUG
         TEST_error("not supported in this build");
@@ -8294,6 +8349,9 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_servername, 10);
 #ifndef OPENSSL_NO_EC
     ADD_ALL_TESTS(test_sigalgs_available, 6);
+#endif
+#ifndef OPENSSL_NO_TLS1_3
+    ADD_TEST(test_pluggable_group);
 #endif
     return 1;
 
diff --git a/test/tls-provider.c b/test/tls-provider.c
new file mode 100644
index 0000000000..4025d9ee16
--- /dev/null
+++ b/test/tls-provider.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/core_names.h>
+#include <openssl/core_numbers.h>
+#include <openssl/rand.h>
+#include <openssl/params.h>
+/* For TLS1_3_VERSION */
+#include <openssl/ssl.h>
+
+int tls_provider_init(const OSSL_CORE_HANDLE *handle,
+                      const OSSL_DISPATCH *in,
+                      const OSSL_DISPATCH **out,
+                      void **provctx);
+
+#define XOR_KEY_SIZE 32
+
+/*
+ * Top secret. This algorithm only works if no one knows what this number is.
+ * Please don't tell anyone what it is.
+ * 
+ * This algorithm is for testing only - don't really use it!
+ */
+static const unsigned char private_constant[XOR_KEY_SIZE] = {
+    0xd3, 0x6b, 0x54, 0xec, 0x5b, 0xac, 0x89, 0x96, 0x8c, 0x2c, 0x66, 0xa5,
+    0x67, 0x0d, 0xe3, 0xdd, 0x43, 0x69, 0xbc, 0x83, 0x3d, 0x60, 0xc7, 0xb8,
+    0x2b, 0x1c, 0x5a, 0xfd, 0xb5, 0xcd, 0xd0, 0xf8
+};
+
+typedef struct xorkey_st {
+    unsigned char privkey[XOR_KEY_SIZE];
+    unsigned char pubkey[XOR_KEY_SIZE];
+    int hasprivkey;
+    int haspubkey;
+} XORKEY;
+
+/* We define a dummy TLS group called "xorgroup" for test purposes */
+
+static unsigned int group_id = 0; /* IANA reserved for private use */
+static unsigned int secbits = 128;
+static unsigned int mintls = TLS1_3_VERSION;
+static unsigned int maxtls = 0;
+static unsigned int mindtls = -1;
+static unsigned int maxdtls = -1;
+
+#define GROUP_NAME "xorgroup"
+#define GROUP_NAME_INTERNAL "xorgroup-int"
+#define ALGORITHM "XOR"
+
+static const OSSL_PARAM xor_group_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME,
+                           GROUP_NAME, sizeof(GROUP_NAME)),
+    OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL,
+                           GROUP_NAME_INTERNAL, sizeof(GROUP_NAME_INTERNAL)),
+    OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, ALGORITHM,
+                           sizeof(ALGORITHM)),
+    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, &group_id),
+    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, &secbits),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, &mintls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, &maxtls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, &mindtls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, &maxdtls),
+    OSSL_PARAM_END
+};
+
+static int tls_prov_get_capabilities(void *provctx, const char *capability,
+                                     OSSL_CALLBACK *cb, void *arg)
+{
+    /* We're only adding one group so we only call the callback once */
+    if (strcmp(capability, "TLS-GROUP") == 0)
+        return cb(xor_group_params, arg);
+
+    /* We don't support this capability */
+    return 0;
+}
+
+/*
+ * Dummy "XOR" Key Exchange algorithm. We just xor the private and public keys
+ * together. Don't use this!
+ */
+
+static OSSL_OP_keyexch_newctx_fn xor_newctx;
+static OSSL_OP_keyexch_init_fn xor_init;
+static OSSL_OP_keyexch_set_peer_fn xor_set_peer;
+static OSSL_OP_keyexch_derive_fn xor_derive;
+static OSSL_OP_keyexch_freectx_fn xor_freectx;
+static OSSL_OP_keyexch_dupctx_fn xor_dupctx;
+
+typedef struct {
+    XORKEY *key;
+    XORKEY *peerkey;
+} PROV_XOR_CTX;
+
+static void *xor_newctx(void *provctx)
+{
+    PROV_XOR_CTX *pxorctx = OPENSSL_zalloc(sizeof(PROV_XOR_CTX));
+
+    if (pxorctx == NULL)
+        return NULL;
+
+    return pxorctx;
+}
+
+static int xor_init(void *vpxorctx, void *vkey)
+{
+    PROV_XOR_CTX *pxorctx = (PROV_XOR_CTX *)vpxorctx;
+
+    if (pxorctx == NULL || vkey == NULL)
+        return 0;
+    pxorctx->key = vkey;
+    return 1;
+}
+
+static int xor_set_peer(void *vpxorctx, void *vpeerkey)
+{
+    PROV_XOR_CTX *pxorctx = (PROV_XOR_CTX *)vpxorctx;
+
+    if (pxorctx == NULL || vpeerkey == NULL)
+        return 0;
+    pxorctx->peerkey = vpeerkey;
+    return 1;
+}
+
+static int xor_derive(void *vpxorctx, unsigned char *secret, size_t *secretlen,
+                      size_t outlen)
+{
+    PROV_XOR_CTX *pxorctx = (PROV_XOR_CTX *)vpxorctx;
+    int i;
+
+    if (pxorctx->key == NULL || pxorctx->peerkey == NULL)
+        return 0;
+
+    *secretlen = XOR_KEY_SIZE;
+    if (secret == NULL)
+        return 1;
+
+    if (outlen < XOR_KEY_SIZE)
+        return 0;
+
+    for (i = 0; i < XOR_KEY_SIZE; i++)
+        secret[i] = pxorctx->key->privkey[i] ^ pxorctx->peerkey->pubkey[i];
+
+    return 1;
+}
+
+static void xor_freectx(void *pxorctx)
+{
+    OPENSSL_free(pxorctx);
+}
+
+static void *xor_dupctx(void *vpxorctx)
+{
+    PROV_XOR_CTX *srcctx = (PROV_XOR_CTX *)vpxorctx;
+    PROV_XOR_CTX *dstctx;
+
+    dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+    if (dstctx == NULL)
+        return NULL;
+
+    *dstctx = *srcctx;
+
+    return dstctx;
+}
+
+static const OSSL_DISPATCH xor_keyexch_functions[] = {
+    { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))xor_newctx },
+    { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))xor_init },
+    { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))xor_derive },
+    { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))xor_set_peer },
+    { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))xor_freectx },
+    { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))xor_dupctx },
+    { 0, NULL }
+};
+
+static const OSSL_ALGORITHM tls_prov_keyexch[] = {
+    { "XOR", "provider=tls-provider", xor_keyexch_functions },
+    { NULL, NULL, NULL }
+};
+
+/* Key Management for the dummy XOR key exchange algorithm */
+
+static OSSL_OP_keymgmt_new_fn xor_newdata;
+static OSSL_OP_keymgmt_free_fn xor_freedata;
+static OSSL_OP_keymgmt_has_fn xor_has;
+static OSSL_OP_keymgmt_copy_fn xor_copy;
+static OSSL_OP_keymgmt_gen_init_fn xor_gen_init;
+static OSSL_OP_keymgmt_gen_set_params_fn xor_gen_set_params;
+static OSSL_OP_keymgmt_gen_settable_params_fn xor_gen_settable_params;
+static OSSL_OP_keymgmt_gen_fn xor_gen;
+static OSSL_OP_keymgmt_gen_cleanup_fn xor_gen_cleanup;
+static OSSL_OP_keymgmt_get_params_fn xor_get_params;
+static OSSL_OP_keymgmt_gettable_params_fn xor_gettable_params;
+static OSSL_OP_keymgmt_set_params_fn xor_set_params;
+static OSSL_OP_keymgmt_settable_params_fn xor_settable_params;
+
+static void *xor_newdata(void *provctx)
+{
+    return OPENSSL_zalloc(sizeof(XORKEY));
+}
+
+static void xor_freedata(void *keydata)
+{
+    OPENSSL_free(keydata);
+}
+
+static int xor_has(void *vkey, int selection)
+{
+    XORKEY *key = vkey;
+    int ok = 0;
+
+    if (key != NULL) {
+        ok = 1;
+
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+            ok = ok && key->haspubkey;
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+            ok = ok && key->hasprivkey;
+    }
+    return ok;
+}
+
+static int xor_copy(void *vtokey, const void *vfromkey, int selection)
+{
+    XORKEY *tokey = vtokey;
+    const XORKEY *fromkey = vfromkey;
+    int ok = 0;
+
+    if (tokey != NULL && fromkey != NULL) {
+        ok = 1;
+
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+            if (fromkey->haspubkey) {
+                memcpy(tokey->pubkey, fromkey->pubkey, XOR_KEY_SIZE);
+                tokey->haspubkey = 1;
+            } else {
+                tokey->haspubkey = 0;
+            }
+        }
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+            if (fromkey->hasprivkey) {
+                memcpy(tokey->privkey, fromkey->privkey, XOR_KEY_SIZE);
+                tokey->hasprivkey = 1;
+            } else {
+                tokey->hasprivkey = 0;
+            }
+        }
+    }
+    return ok;
+}
+
+static ossl_inline int xor_get_params(void *vkey, OSSL_PARAM params[])
+{
+    XORKEY *key = vkey;
+    OSSL_PARAM *p;
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+        && !OSSL_PARAM_set_int(p, XOR_KEY_SIZE))
+        return 0;
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
+        && !OSSL_PARAM_set_int(p, secbits))
+        return 0;
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_TLS_ENCODED_PT)) != NULL) {
+        if (p->data_type != OSSL_PARAM_OCTET_STRING)
+            return 0;
+        p->return_size = XOR_KEY_SIZE;
+        if (p->data != NULL && p->data_size >= XOR_KEY_SIZE)
+            memcpy(p->data, key->pubkey, XOR_KEY_SIZE);
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM xor_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_TLS_ENCODED_PT, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *xor_gettable_params(void)
+{
+    return xor_params;
+}
+
+static int xor_set_params(void *vkey, const OSSL_PARAM params[])
+{
+    XORKEY *key = vkey;
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_TLS_ENCODED_PT);
+    if (p != NULL) {
+        if (p->data_type != OSSL_PARAM_OCTET_STRING
+                || p->data_size != XOR_KEY_SIZE)
+            return 0;
+        memcpy(key->pubkey, p->data, XOR_KEY_SIZE);
+        key->haspubkey = 1;
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM xor_known_settable_params[] = {
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_TLS_ENCODED_PT, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *xor_settable_params(void)
+{
+    return xor_known_settable_params;
+}
+
+struct xor_gen_ctx {
+    int selection;
+    OPENSSL_CTX *libctx;
+};
+
+static void *xor_gen_init(void *provctx, int selection)
+{
+    struct xor_gen_ctx *gctx = NULL;
+
+    if ((selection & (OSSL_KEYMGMT_SELECT_KEYPAIR
+                      | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) == 0)
+        return NULL;
+
+    if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL)
+        gctx->selection = selection;
+
+    /* Our provctx is really just an OPENSSL_CTX */
+    gctx->libctx = (OPENSSL_CTX *)provctx;
+
+    return gctx;
+}
+
+static int xor_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+    struct xor_gen_ctx *gctx = genctx;
+    const OSSL_PARAM *p;
+
+    if (gctx == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
+    if (p != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING
+                || strcmp(p->data, GROUP_NAME_INTERNAL) != 0)
+            return 0;
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM *xor_gen_settable_params(void *provctx)
+{
+    static OSSL_PARAM settable[] = {
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+        OSSL_PARAM_END
+    };
+    return settable;
+}
+
+static void *xor_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+    struct xor_gen_ctx *gctx = genctx;
+    XORKEY *key = OPENSSL_zalloc(sizeof(*key));
+    size_t i;
+
+    if (key == NULL)
+        return NULL;
+
+    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+        if (RAND_bytes_ex(gctx->libctx, key->privkey, XOR_KEY_SIZE) <= 0) {
+            OPENSSL_free(key);
+            return NULL;
+        }
+        for (i = 0; i < XOR_KEY_SIZE; i++)
+            key->pubkey[i] = key->privkey[i] ^ private_constant[i];
+        key->hasprivkey = 1;
+        key->haspubkey = 1;
+    }
+
+    return key;
+}
+
+static void xor_gen_cleanup(void *genctx)
+{
+    OPENSSL_free(genctx);
+}
+
+static const OSSL_DISPATCH xor_keymgmt_functions[] = {
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))xor_newdata },
+    { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))xor_gen_init },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))xor_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+      (void (*)(void))xor_gen_settable_params },
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))xor_gen },
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))xor_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))xor_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))xor_gettable_params },
+    { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))xor_set_params },
+    { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))xor_settable_params },
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))xor_has },
+    { OSSL_FUNC_KEYMGMT_COPY, (void (*)(void))xor_copy },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))xor_freedata },
+    { 0, NULL }
+};
+
+static const OSSL_ALGORITHM tls_prov_keymgmt[] = {
+    { "XOR", "provider=tls-provider", xor_keymgmt_functions },
+    { NULL, NULL, NULL }
+};
+
+static const OSSL_ALGORITHM *tls_prov_query(void *provctx, int operation_id,
+                                            int *no_cache)
+{
+    *no_cache = 0;
+    switch (operation_id) {
+    case OSSL_OP_KEYMGMT:
+        return tls_prov_keymgmt;
+    case OSSL_OP_KEYEXCH:
+        return tls_prov_keyexch;
+    }
+    return NULL;
+}
+
+/* Functions we provide to the core */
+static const OSSL_DISPATCH tls_prov_dispatch_table[] = {
+    { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))OPENSSL_CTX_free },
+    { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))tls_prov_query },
+    { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, (void (*)(void))tls_prov_get_capabilities },
+    { 0, NULL }
+};
+
+int tls_provider_init(const OSSL_CORE_HANDLE *handle,
+                      const OSSL_DISPATCH *in,
+                      const OSSL_DISPATCH **out,
+                      void **provctx)
+{
+    OPENSSL_CTX *libctx = OPENSSL_CTX_new();
+
+    *provctx = libctx;
+
+    /*
+     * Randomise the group_id we're going to use to ensure we don't interoperate
+     * with anything but ourselves.
+     */
+    if (!RAND_bytes_ex(libctx, (unsigned char *)&group_id, sizeof(group_id)))
+        return 0;
+    /*
+     * Ensure group_id is within the IANA Reserved for private use range
+     * (65024-65279)
+     */
+    group_id %= 65279 - 65024;
+    group_id += 65024;
+
+    *out = tls_prov_dispatch_table;
+    return 1;
+}
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 230126ff55..38cc5700d7 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5048,8 +5048,8 @@ CTLOG_new_from_base64_with_libctx       ?	3_0_0	EXIST::FUNCTION:CT
 CTLOG_STORE_new_with_libctx             ?	3_0_0	EXIST::FUNCTION:CT
 EVP_PKEY_set_ex_data                    ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_get_ex_data                    ?	3_0_0	EXIST::FUNCTION:
-EVP_PKEY_CTX_set_ec_paramgen_curve_name ?	3_0_0	EXIST::FUNCTION:EC
-EVP_PKEY_CTX_get_ec_paramgen_curve_name ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_set_group_name             ?	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_CTX_get_group_name             ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_CTX_set_ec_paramgen_curve_nid  ?	3_0_0	EXIST::FUNCTION:EC
 d2i_PrivateKey_ex                       ?	3_0_0	EXIST::FUNCTION:
 d2i_AutoPrivateKey_ex                   ?	3_0_0	EXIST::FUNCTION:
@@ -5099,3 +5099,4 @@ EVP_PKEY_eq                             ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_parameters_eq                  ?	3_0_0	EXIST::FUNCTION:
 OSSL_PROVIDER_query_operation           ?	3_0_0	EXIST::FUNCTION:
 OSSL_PROVIDER_get0_provider_ctx         ?	3_0_0	EXIST::FUNCTION:
+OSSL_PROVIDER_get_capabilities          ?	3_0_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list