[openssl] master update

tomas at openssl.org tomas at openssl.org
Mon Apr 26 17:52:20 UTC 2021


The branch master has been updated
       via  f1ffaaeece5efb7d2f4859a59e3164edf9b4b769 (commit)
      from  6c9bc258d2e9e7b500236a1c696da1f384f0b907 (commit)


- Log -----------------------------------------------------------------
commit f1ffaaeece5efb7d2f4859a59e3164edf9b4b769
Author: Shane Lontis <shane.lontis at oracle.com>
Date:   Thu Apr 15 18:25:17 2021 +1000

    Fixes related to separation of DH and DHX types
    
    Fix dh_rfc5114 option in genpkey.
    
    Fixes #14145
    Fixes #13956
    Fixes #13952
    Fixes #13871
    Fixes #14054
    Fixes #14444
    
    Updated documentation for app to indicate what options are available for
    DH and DHX keys.
    
    DH and DHX now have different keymanager gen_set_params() methods.
    
    Added CHANGES entry to indicate the breaking change.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14883)

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

Summary of changes:
 CHANGES.md                                         |   7 +
 crypto/dh/dh_pmeth.c                               |  43 ++---
 crypto/evp/ctrl_params_translate.c                 | 104 +++++++-----
 crypto/evp/dh_support.c                            |  29 +++-
 crypto/evp/p_lib.c                                 |  33 +++-
 crypto/ffc/ffc_dh.c                                |   4 +-
 doc/man1/openssl-genpkey.pod.in                    | 186 +++++++++++++++++----
 doc/man7/EVP_PKEY-DH.pod                           |  60 ++++---
 doc/man7/EVP_PKEY-FFC.pod                          |  36 ++--
 include/crypto/dh.h                                |   2 +-
 providers/implementations/keymgmt/dh_kmgmt.c       | 125 ++++++++++----
 test/recipes/15-test_gendh.t                       |  33 +---
 test/recipes/15-test_gendhparam.t                  | 170 +++++++++++++++++++
 test/recipes/20-test_dhparam_check.t               |  14 +-
 .../dh5114_1_pkcs3.pem => valid/dh_5114_1.pem}     |   0
 .../dh5114_2_pkcs3.pem => valid/dh_5114_2.pem}     |   0
 .../dh5114_3_pkcs3.pem => valid/dh_5114_3.pem}     |   0
 .../dh5114_2_pkcs3.pem => valid/dhx_5114_2.pem}    |  10 +-
 18 files changed, 629 insertions(+), 227 deletions(-)
 create mode 100644 test/recipes/15-test_gendhparam.t
 rename test/recipes/20-test_dhparam_check_data/{invalid/dh5114_1_pkcs3.pem => valid/dh_5114_1.pem} (100%)
 copy test/recipes/20-test_dhparam_check_data/{invalid/dh5114_2_pkcs3.pem => valid/dh_5114_2.pem} (100%)
 rename test/recipes/20-test_dhparam_check_data/{invalid/dh5114_3_pkcs3.pem => valid/dh_5114_3.pem} (100%)
 rename test/recipes/20-test_dhparam_check_data/{invalid/dh5114_2_pkcs3.pem => valid/dhx_5114_2.pem} (71%)

diff --git a/CHANGES.md b/CHANGES.md
index d2d9e01f35..480c4091a9 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -23,6 +23,13 @@ OpenSSL 3.0
 
 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx]
 
+ * For the key types DH and DHX the allowed settable parameters are now different.
+   Previously (in 1.1.1) these conflicting parameters were allowed, but will now
+   result in errors. See EVP_PKEY-DH(7) for further details. This affects the
+   behaviour of openssl-genpkey(1) for DH parameter generation.
+
+   *Shane Lontis*
+
  * The default manual page suffix ($MANSUFFIX) has been changed to "ossl"
 
    *Matt Caswell*
diff --git a/crypto/dh/dh_pmeth.c b/crypto/dh/dh_pmeth.c
index affe40a53c..78d46aba22 100644
--- a/crypto/dh/dh_pmeth.c
+++ b/crypto/dh/dh_pmeth.c
@@ -35,7 +35,6 @@ typedef struct {
     int pad;
     /* message digest used for parameter generation */
     const EVP_MD *md;
-    int rfc5114_param;
     int param_nid;
     /* Keygen callback info */
     int gentmp[2];
@@ -98,7 +97,6 @@ static int pkey_dh_copy(EVP_PKEY_CTX *dst, const EVP_PKEY_CTX *src)
     dctx->paramgen_type = sctx->paramgen_type;
     dctx->pad = sctx->pad;
     dctx->md = sctx->md;
-    dctx->rfc5114_param = sctx->rfc5114_param;
     dctx->param_nid = sctx->param_nid;
 
     dctx->kdf_type = sctx->kdf_type;
@@ -156,11 +154,11 @@ static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
     case EVP_PKEY_CTRL_DH_RFC5114:
         if (p1 < 1 || p1 > 3 || dctx->param_nid != NID_undef)
             return -2;
-        dctx->rfc5114_param = p1;
+        dctx->param_nid = p1;
         return 1;
 
     case EVP_PKEY_CTRL_DH_NID:
-        if (p1 <= 0 || dctx->rfc5114_param != 0)
+        if (p1 <= 0 || dctx->param_nid != NID_undef)
             return -2;
         dctx->param_nid = p1;
         return 1;
@@ -233,11 +231,12 @@ static int pkey_dh_ctrl_str(EVP_PKEY_CTX *ctx,
     }
     if (strcmp(type, "dh_rfc5114") == 0) {
         DH_PKEY_CTX *dctx = ctx->data;
-        int len;
-        len = atoi(value);
-        if (len < 0 || len > 3)
+        int id;
+
+        id = atoi(value);
+        if (id < 0 || id > 3)
             return -2;
-        dctx->rfc5114_param = len;
+        dctx->param_nid = id;
         return 1;
     }
     if (strcmp(type, "dh_param") == 0) {
@@ -331,36 +330,16 @@ static int pkey_dh_paramgen(EVP_PKEY_CTX *ctx,
     /*
      * Look for a safe prime group for key establishment. Which uses
      * either RFC_3526 (modp_XXXX) or RFC_7919 (ffdheXXXX).
+     * RFC_5114 is also handled here for param_nid = (1..3)
      */
     if (dctx->param_nid != NID_undef) {
+        int type = dctx->param_nid <= 3 ? EVP_PKEY_DHX : EVP_PKEY_DH;
+
         if ((dh = DH_new_by_nid(dctx->param_nid)) == NULL)
             return 0;
-        EVP_PKEY_assign(pkey, EVP_PKEY_DH, dh);
-        return 1;
-    }
-
-#ifndef FIPS_MODULE
-    if (dctx->rfc5114_param) {
-        switch (dctx->rfc5114_param) {
-        case 1:
-            dh = DH_get_1024_160();
-            break;
-
-        case 2:
-            dh = DH_get_2048_224();
-            break;
-
-        case 3:
-            dh = DH_get_2048_256();
-            break;
-
-        default:
-            return -2;
-        }
-        EVP_PKEY_assign(pkey, EVP_PKEY_DHX, dh);
+        EVP_PKEY_assign(pkey, type, dh);
         return 1;
     }
-#endif /* FIPS_MODULE */
 
     if (ctx->pkey_gencb != NULL) {
         pcb = BN_GENCB_new();
diff --git a/crypto/evp/ctrl_params_translate.c b/crypto/evp/ctrl_params_translate.c
index 8f4ffd3bc4..f48e723c33 100644
--- a/crypto/evp/ctrl_params_translate.c
+++ b/crypto/evp/ctrl_params_translate.c
@@ -977,7 +977,7 @@ static int fix_oid(enum state state,
     return ret;
 }
 
-/* EVP_PKEY_CTRL_DH_NID, ...??? */
+/* EVP_PKEY_CTRL_DH_NID */
 static int fix_dh_nid(enum state state,
                       const struct translation_st *translation,
                       struct translation_ctx_st *ctx)
@@ -987,7 +987,7 @@ static int fix_dh_nid(enum state state,
     if ((ret = default_check(state, translation, ctx)) <= 0)
         return ret;
 
-    /* This is currently only settable */
+    /* This is only settable */
     if (ctx->action_type != SET)
         return 0;
 
@@ -997,16 +997,30 @@ static int fix_dh_nid(enum state state,
         ctx->p1 = 0;
     }
 
-    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+    return default_fixup_args(state, translation, ctx);
+}
+
+/* EVP_PKEY_CTRL_DH_RFC5114 */
+static int fix_dh_nid5114(enum state state,
+                          const struct translation_st *translation,
+                          struct translation_ctx_st *ctx)
+{
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
         return ret;
 
-    if (state == PRE_PARAMS_TO_CTRL) {
-        ctx->p1 =
-            ossl_ffc_named_group_get_uid(ossl_ffc_name_to_dh_named_group(ctx->p2));
-        ctx->p2 = NULL;
+    /* This is only settable */
+    if (ctx->action_type != SET)
+        return 0;
+
+    if (state == PRE_CTRL_STR_TO_PARAMS) {
+        ctx->p2 = (char *)ossl_ffc_named_group_get_name
+            (ossl_ffc_uid_to_dh_named_group(atoi(ctx->p2)));
+        ctx->p1 = 0;
     }
 
-    return ret;
+    return default_fixup_args(state, translation, ctx);
 }
 
 /* EVP_PKEY_CTRL_DH_PARAMGEN_TYPE */
@@ -1019,24 +1033,16 @@ static int fix_dh_paramgen_type(enum state state,
     if ((ret = default_check(state, translation, ctx)) <= 0)
         return ret;
 
-    /* This is currently only settable */
+    /* This is only settable */
     if (ctx->action_type != SET)
         return 0;
 
-    if (state == PRE_CTRL_TO_PARAMS) {
-        ctx->p2 = (char *)ossl_dh_gen_type_id2name(ctx->p1);
-        ctx->p1 = 0;
-    }
-
-    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
-        return ret;
-
-    if (state == PRE_PARAMS_TO_CTRL) {
-        ctx->p1 = ossl_dh_gen_type_name2id(ctx->p2);
-        ctx->p2 = NULL;
+    if (state == PRE_CTRL_STR_TO_PARAMS) {
+        ctx->p2 = (char *)ossl_dh_gen_type_id2name(atoi(ctx->p2));
+        ctx->p1 = strlen(ctx->p2);
     }
 
-    return ret;
+    return default_fixup_args(state, translation, ctx);
 }
 
 /* EVP_PKEY_CTRL_EC_PARAM_ENC */
@@ -1927,35 +1933,47 @@ static const struct translation_st evp_pkey_ctx_translations[] = {
       EVP_PKEY_CTRL_GET_DH_KDF_OID, NULL, NULL,
       OSSL_KDF_PARAM_CEK_ALG, OSSL_PARAM_UTF8_STRING, fix_oid },
 
-    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_DERIVE,
-      EVP_PKEY_CTRL_DH_PAD, "dh_pad", NULL,
-      OSSL_EXCHANGE_PARAM_PAD, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    /* DHX Keygen Parameters that are shared with DH */
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_TYPE, "dh_paramgen_type", NULL,
+      OSSL_PKEY_PARAM_FFC_TYPE, OSSL_PARAM_UTF8_STRING, fix_dh_paramgen_type },
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, "dh_paramgen_prime_len", NULL,
+      OSSL_PKEY_PARAM_FFC_PBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_PARAMGEN  | EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_DH_NID, "dh_param", NULL,
+      OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_UTF8_STRING, NULL },
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_PARAMGEN  | EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_DH_RFC5114, "dh_rfc5114", NULL,
+      OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_UTF8_STRING, fix_dh_nid5114 },
 
+    /* DH Keygen Parameters that are shared with DHX */
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_TYPE, "dh_paramgen_type", NULL,
+      OSSL_PKEY_PARAM_FFC_TYPE, OSSL_PARAM_UTF8_STRING, fix_dh_paramgen_type },
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, "dh_paramgen_prime_len", NULL,
+      OSSL_PKEY_PARAM_FFC_PBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
     { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN,
       EVP_PKEY_CTRL_DH_NID, "dh_param", NULL,
       OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_UTF8_STRING, fix_dh_nid },
-    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
-      EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, NULL, NULL,
-      OSSL_PKEY_PARAM_FFC_PBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
-    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
-      EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN, "dh_paramgen_subprime_len", NULL,
-      OSSL_PKEY_PARAM_FFC_QBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN  | EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_DH_RFC5114, "dh_rfc5114", NULL,
+      OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_UTF8_STRING, fix_dh_nid5114 },
+
+    /* DH specific Keygen Parameters */
     { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
       EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR, "dh_paramgen_generator", NULL,
       OSSL_PKEY_PARAM_DH_GENERATOR, OSSL_PARAM_INTEGER, NULL },
-    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
-      EVP_PKEY_CTRL_DH_PARAMGEN_TYPE, "dh_paramgen_type", NULL,
-      OSSL_PKEY_PARAM_FFC_TYPE, OSSL_PARAM_UTF8_STRING, fix_dh_paramgen_type },
- /*
-  * This is know to be incorrect, will be fixed and enabled when the
-  * underlying code is corrected.
-  * Until then, we simply don't support it here.
-  */
-#if 0
-    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
-      EVP_PKEY_CTRL_DH_RFC5114, "dh_rfc5114", NULL,
-      OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_INTEGER, NULL },
-#endif
+
+    /* DHX specific Keygen Parameters */
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN, "dh_paramgen_subprime_len", NULL,
+      OSSL_PKEY_PARAM_FFC_QBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_DH_PAD, "dh_pad", NULL,
+      OSSL_EXCHANGE_PARAM_PAD, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
 
     /*-
      * DSA
diff --git a/crypto/evp/dh_support.c b/crypto/evp/dh_support.c
index 7e0256bd00..87296ffbee 100644
--- a/crypto/evp/dh_support.c
+++ b/crypto/evp/dh_support.c
@@ -15,14 +15,25 @@
 typedef struct dh_name2id_st{
     const char *name;
     int id;
+    int type;
 } DH_GENTYPE_NAME2ID;
 
-static const DH_GENTYPE_NAME2ID dhtype2id[]=
+/* Indicates that the paramgen_type can be used for either DH or DHX */
+#define TYPE_ANY -1
+#ifndef OPENSSL_NO_DH
+# define TYPE_DH    DH_FLAG_TYPE_DH
+# define TYPE_DHX   DH_FLAG_TYPE_DHX
+#else
+# define TYPE_DH    0
+# define TYPE_DHX   0
+#endif
+
+static const DH_GENTYPE_NAME2ID dhtype2id[] =
 {
-    { "fips186_4", DH_PARAMGEN_TYPE_FIPS_186_4 },
-    { "fips186_2", DH_PARAMGEN_TYPE_FIPS_186_2 },
-    { "group", DH_PARAMGEN_TYPE_GROUP },
-    { "generator", DH_PARAMGEN_TYPE_GENERATOR }
+    { "group", DH_PARAMGEN_TYPE_GROUP, TYPE_ANY },
+    { "generator", DH_PARAMGEN_TYPE_GENERATOR, TYPE_DH },
+    { "fips186_4", DH_PARAMGEN_TYPE_FIPS_186_4, TYPE_DHX },
+    { "fips186_2", DH_PARAMGEN_TYPE_FIPS_186_2, TYPE_DHX },
 };
 
 const char *ossl_dh_gen_type_id2name(int id)
@@ -36,13 +47,17 @@ const char *ossl_dh_gen_type_id2name(int id)
     return NULL;
 }
 
-int ossl_dh_gen_type_name2id(const char *name)
+#ifndef OPENSSL_NO_DH
+int ossl_dh_gen_type_name2id(const char *name, int type)
 {
     size_t i;
 
     for (i = 0; i < OSSL_NELEM(dhtype2id); ++i) {
-        if (strcmp(dhtype2id[i].name, name) == 0)
+        if ((dhtype2id[i].type == TYPE_ANY
+             || type == dhtype2id[i].type)
+            && strcmp(dhtype2id[i].name, name) == 0)
             return dhtype2id[i].id;
     }
     return -1;
 }
+#endif
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index daa0f617d8..3af7e17bee 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -890,13 +890,38 @@ IMPLEMENT_ECX_VARIANT(ED448)
 
 # if !defined(OPENSSL_NO_DH) && !defined(OPENSSL_NO_DEPRECATED_3_0)
 
-int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key)
+int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *dhkey)
 {
-    int type = DH_get0_q(key) == NULL ? EVP_PKEY_DH : EVP_PKEY_DHX;
-    int ret = EVP_PKEY_assign(pkey, type, key);
+    int ret, type;
+
+    /*
+     * ossl_dh_is_named_safe_prime_group() returns 1 for named safe prime groups
+     * related to ffdhe and modp (which cache q = (p - 1) / 2),
+     * and returns 0 for all other dh parameter generation types including
+     * RFC5114 named groups.
+     *
+     * The EVP_PKEY_DH type is used for dh parameter generation types:
+     *  - named safe prime groups related to ffdhe and modp
+     *  - safe prime generator
+     *
+     * The type EVP_PKEY_DHX is used for dh parameter generation types
+     *  - fips186-4 and fips186-2
+     *  - rfc5114 named groups.
+     *
+     * The EVP_PKEY_DH type is used to save PKCS#3 data than can be stored
+     * without a q value.
+     * The EVP_PKEY_DHX type is used to save X9.42 data that requires the
+     * q value to be stored.
+     */
+    if (ossl_dh_is_named_safe_prime_group(dhkey))
+        type = EVP_PKEY_DH;
+    else
+        type = DH_get0_q(dhkey) == NULL ? EVP_PKEY_DH : EVP_PKEY_DHX;
+
+    ret = EVP_PKEY_assign(pkey, type, dhkey);
 
     if (ret)
-        DH_up_ref(key);
+        DH_up_ref(dhkey);
     return ret;
 }
 
diff --git a/crypto/ffc/ffc_dh.c b/crypto/ffc/ffc_dh.c
index 17888e9291..e9f597c46c 100644
--- a/crypto/ffc/ffc_dh.c
+++ b/crypto/ffc/ffc_dh.c
@@ -113,9 +113,7 @@ const DH_NAMED_GROUP *ossl_ffc_numbers_to_dh_named_group(const BIGNUM *p,
         if (BN_cmp(p, dh_named_groups[i].p) == 0
             && BN_cmp(g, dh_named_groups[i].g) == 0
             /* Verify q is correct if it exists */
-            && ((q != NULL && BN_cmp(q, dh_named_groups[i].q) == 0)
-                /* Do not match RFC 5114 groups without q */
-                || (q == NULL && dh_named_groups[i].uid > 3)))
+            && (q == NULL || BN_cmp(q, dh_named_groups[i].q) == 0))
             return &dh_named_groups[i];
     }
     return NULL;
diff --git a/doc/man1/openssl-genpkey.pod.in b/doc/man1/openssl-genpkey.pod.in
index 7f4ebef439..aa08b01f4f 100644
--- a/doc/man1/openssl-genpkey.pod.in
+++ b/doc/man1/openssl-genpkey.pod.in
@@ -63,7 +63,7 @@ name accepted by EVP_get_cipherbyname() is acceptable such as B<des3>.
 
 =item B<-algorithm> I<alg>
 
-Public key algorithm to use such as RSA, DSA or DH. If used this option must
+Public key algorithm to use such as RSA, DSA, DH or DHX. If used this option must
 precede any B<-pkeyopt> options. The options B<-paramfile> and B<-algorithm>
 are mutually exclusive. Engines may add algorithms in addition to the standard
 built-in ones.
@@ -74,11 +74,8 @@ X25519, X448, ED25519 and ED448.
 Valid built-in algorithm names for parameter generation (see the B<-genparam>
 option) are DH, DSA and EC.
 
-Note that the algorithm name X9.42 DH may be used as a synonym for the DH
-algorithm. These are identical and do not indicate the type of parameters that
-will be generated. Use the B<dh_paramgen_type> option to indicate whether PKCS#3
-or X9.42 DH parameters are required. See L</DH Parameter Generation Options>
-below for more details.
+Note that the algorithm name X9.42 DH may be used as a synonym for DHX keys and
+PKCS#3 refers to DH Keys. Some options are not shared between DH and DHX keys.
 
 =item B<-pkeyopt> I<opt>:I<value>
 
@@ -182,6 +179,18 @@ B<named_curve> or B<explicit>. The default value is B<named_curve>.
 
 =back
 
+=head2 DH Key Generation Options
+
+=over 4
+
+=item B<group>:I<name>
+
+The B<paramfile> option is not required if a named group is used here.
+See the L</DH Parameter Generation Options> section below.
+
+=back
+
+
 =head1 PARAMETER GENERATION OPTIONS
 
 The options supported by each algorithm and indeed each implementation of an
@@ -214,7 +223,6 @@ ignored. If not set, then a digest will be used that gives an output matching
 the number of bits in B<q>, i.e. B<sha1> if q length is 160, B<sha224> if it 224
 or B<sha256> if it is 256.
 
-
 =item B<properties>:I<query>
 
 The I<digest> property I<query> string to use when fetching a digest from a provider.
@@ -243,36 +251,128 @@ generate valid primes.
 
 =head2 DH Parameter Generation Options
 
+For most use cases it is recommended to use the B<group> option rather than
+the B<type> options. Note that the B<group> option is not used by default if
+no parameter generation options are specified.
+
 =over 4
 
+=item B<group>:I<name>
+
+=item B<dh_param>:I<name>
+
+Use a named DH group to select constant values for the DH parameters.
+All other options will be ignored if this value is set.
+
+Valid values that are associated with the B<algorithm> of B<"DH"> are:
+"ffdhe2048", "ffdhe3072", "ffdhe4096", "ffdhe6144", "ffdhe8192",
+"modp_1536", "modp_2048", "modp_3072", "modp_4096", "modp_6144", "modp_8192".
+
+Valid values that are associated with the B<algorithm> of B<"DHX"> are the
+RFC5114 names "dh_1024_160", "dh_2048_224", "dh_2048_256".
+
+=item B<dh_rfc5114>:I<num>
+
+If this option is set, then the appropriate RFC5114 parameters are used
+instead of generating new parameters. The value I<num> can be one of
+1, 2 or 3 that are equivalant to using the option B<group> with one of
+"dh_1024_160", "dh_2048_224" or "dh_2048_256".
+All other options will be ignored if this value is set.
+
+=item B<pbits>:I<numbits>
+
 =item B<dh_paramgen_prime_len>:I<numbits>
 
 The number of bits in the prime parameter I<p>. The default is 2048.
 
+=item B<qbits>:I<numbits>
+
 =item B<dh_paramgen_subprime_len>:I<numbits>
 
-The number of bits in the sub prime parameter I<q>. The default is 256 if the
-prime is at least 2048 bits long or 160 otherwise. Only relevant if used in
-conjunction with the B<dh_paramgen_type> option to generate X9.42 DH parameters.
+The number of bits in the sub prime parameter I<q>. The default is 224.
+Only relevant if used in conjunction with the B<dh_paramgen_type> option to
+generate DHX parameters.
+
+=item B<safeprime-generator>:I<value>
 
 =item B<dh_paramgen_generator>:I<value>
 
 The value to use for the generator I<g>. The default is 2.
+The B<algorithm> option must be B<"DH"> for this parameter to be used.
+
+=item B<type>:I<string>
+
+The type name of DH parameters to generate. Valid values are:
+
+=over 4
+
+=item "generator"
+
+Use a safe prime generator with the option B<safeprime_generator>
+The B<algorithm> option must be B<"DH">.
+
+=item "fips186_4"
+
+FIPS186-4 parameter generation.
+The B<algorithm> option must be B<"DHX">.
+
+=item "fips186_2"
+
+FIPS186-4 parameter generation.
+The B<algorithm> option must be B<"DHX">.
+
+=item "group"
+
+Can be used with the option B<pbits> to select one of
+"ffdhe2048", "ffdhe3072", "ffdhe4096", "ffdhe6144" or "ffdhe8192".
+The B<algorithm> option must be B<"DH">.
+
+=item "default"
+
+Selects a default type based on the B<algorithm>. This is used by the
+OpenSSL default provider to set the type for backwards compatability.
+If B<algorithm> is B<"DH"> then B<"generator"> is used.
+If B<algorithm> is B<"DHX"> then B<"fips186_2"> is used.
+
+=back
 
 =item B<dh_paramgen_type>:I<value>
 
-The type of DH parameters to generate. Use 0 for PKCS#3 DH and 1 for X9.42 DH.
-The default is 0.
+The type of DH parameters to generate. Valid values are 0, 1, 2 or 3
+which correspond to setting the option B<type> to
+"generator", "fips186_2", "fips186_4" or "group".
 
-=item B<dh_rfc5114>:I<num>
+=item B<digest>:I<digest>
 
-If this option is set, then the appropriate RFC5114 parameters are used
-instead of generating new parameters. The value I<num> can be one of
-1, 2 or 3 corresponding to RFC5114 DH parameters consisting of
-1024 bit group with 160 bit subgroup, 2048 bit group with 224 bit subgroup
-and 2048 bit group with 256 bit subgroup as mentioned in RFC5114 sections
-2.1, 2.2 and 2.3 respectively. If present this overrides all other DH parameter
-options.
+The digest to use during parameter generation. Must be one of B<sha1>, B<sha224>
+or B<sha256>. If set, then the number of bits in B<qbits> will match the output
+size of the specified digest and the B<qbits> parameter will be
+ignored. If not set, then a digest will be used that gives an output matching
+the number of bits in B<q>, i.e. B<sha1> if q length is 160, B<sha224> if it is
+224 or B<sha256> if it is 256.
+This is only used by "fips186_4" and "fips186_2" key generation.
+
+=item B<properties>:I<query>
+
+The I<digest> property I<query> string to use when fetching a digest from a provider.
+This is only used by "fips186_4" and "fips186_2" key generation.
+
+=item B<gindex>:I<index>
+
+The index to use for canonical generation and verification of the generator g.
+Set this to a positive value ranging from 0..255 to use this mode. Larger values
+will only use the bottom byte.
+This I<index> must then be reused during key validation to verify the value of g.
+If this value is not set then g is not verifiable. The default value is -1.
+This is only used by "fips186_4" and "fips186_2" key generation.
+
+=item B<hexseed>:I<seed>
+
+The seed I<seed> data to use instead of generating a random seed internally.
+This should be used for testing purposes only. This will either produced fixed
+values for the generated parameters OR it will fail if the seed did not
+generate valid primes.
+This is only used by "fips186_4" and "fips186_2" key generation.
 
 =back
 
@@ -313,25 +413,49 @@ Generate DSA key from parameters:
 
  openssl genpkey -paramfile dsap.pem -out dsakey.pem
 
-Generate 2048 bit DH parameters:
+Generate 4096 bit DH Key using safe prime group ffdhe4096:
+
+ openssl genpkey -algorithm DH -out dhkey.pem -pkeyopt group:ffdhe4096
+
+Generate 2048 bit X9.42 DH key with 256 bit subgroup using RFC5114 group3:
+
+ openssl genpkey -algorithm DHX -out dhkey.pem -pkeyopt dh_rfc5114:3
+
+Generate a DH key using a DH parameters file:
+
+ openssl genpkey -paramfile dhp.pem -out dhkey.pem
+
+Output DH parameters for safe prime group ffdhe2048:
+
+ openssl genpkey -genparam -algorithm DH -out dhp.pem -pkeyopt group:ffdhe2048
+
+Output 2048 bit X9.42 DH parameters with 224 bit subgroup using RFC5114 group2:
+
+ openssl genpkey -genparam -algorithm DHX -out dhp.pem -pkeyopt dh_rfc5114:2
+
+Output 2048 bit X9.42 DH parameters with 224 bit subgroup using FIP186-4 keygen:
+
+ openssl genpkey -genparam -algorithm DHX -out dhp.pem -text \
+     -pkeyopt pbits:2048 -pkeyopt qbits:224 -pkeyopt digest:SHA256 \
+     -pkeyopt gindex:1 -pkeyopt dh_paramgen_type:2
+
+Output 1024 bit X9.42 DH parameters with 160 bit subgroup using FIP186-2 keygen:
+
+ openssl genpkey -genparam -algorithm DHX -out dhp.pem -text \
+     -pkeyopt pbits:1024 -pkeyopt qbits:160 -pkeyopt digest:SHA1 \
+     -pkeyopt gindex:1 -pkeyopt dh_paramgen_type:1
+
+Output 2048 bit DH parameters:
 
  openssl genpkey -genparam -algorithm DH -out dhp.pem \
      -pkeyopt dh_paramgen_prime_len:2048
 
-Generate 2048 bit X9.42 DH parameters:
+Output 2048 bit DH parameters using a generator:
 
  openssl genpkey -genparam -algorithm DH -out dhpx.pem \
      -pkeyopt dh_paramgen_prime_len:2048 \
      -pkeyopt dh_paramgen_type:1
 
-Output RFC5114 2048 bit DH parameters with 224 bit subgroup:
-
- openssl genpkey -genparam -algorithm DH -out dhp.pem -pkeyopt dh_rfc5114:2
-
-Generate DH key from parameters:
-
- openssl genpkey -paramfile dhp.pem -out dhkey.pem
-
 Generate EC parameters:
 
  openssl genpkey -genparam -algorithm EC -out ecp.pem \
@@ -367,7 +491,7 @@ The B<-engine> option was deprecated in OpenSSL 3.0.
 
 =head1 COPYRIGHT
 
-Copyright 2006-2020 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2006-2021 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
diff --git a/doc/man7/EVP_PKEY-DH.pod b/doc/man7/EVP_PKEY-DH.pod
index 72d20f6f1c..60865a7120 100644
--- a/doc/man7/EVP_PKEY-DH.pod
+++ b/doc/man7/EVP_PKEY-DH.pod
@@ -2,7 +2,8 @@
 
 =head1 NAME
 
-EVP_PKEY-DH, EVP_KEYMGMT-DH - EVP_PKEY DH keytype and algorithm support
+EVP_PKEY-DH, EVP_PKEY-DHX, EVP_KEYMGMT-DH
+- EVP_PKEY DH and DHX keytype and algorithm support
 
 =head1 DESCRIPTION
 
@@ -14,25 +15,30 @@ applications that cannot be upgraded to use the approved safe-prime groups.
 
 See L<EVP_PKEY-FFC(7)> for more information about FFC keys.
 
-For B<DH> that is not a named group the FIPS186-4 standard specifies that the
+The B<DH> key type uses PKCS#3 format which saves p and g, but not the 'q' value.
+The B<DHX> key type uses X9.42 format which saves the value of 'q' and this
+must be used for FIPS186-4.
+
+For B<DHX> that is not a named group the FIPS186-4 standard specifies that the
 values used for FFC parameter generation are also required for parameter
 validation. This means that optional FFC domain parameter values for
-I<seed>, I<pcounter> and I<gindex> may need to be stored for validation purposes.
-For B<DH> the I<seed> and I<pcounter> can be stored in ASN1 data
-(but the I<gindex> is not).
+I<seed>, I<pcounter> and I<gindex> or I<hindex> may need to be stored for
+validation purposes.
+For B<DHX> the I<seed> and I<pcounter> can be stored in ASN1 data
+(but the I<gindex> or I<hindex> can not be stored).
 
-=head2 DH parameters
+=head2 DH and DHX domain parameters
 
 In addition to the common FCC parameters that all FFC keytypes should support
-(see L<EVP_PKEY-FFC(7)/FFC parameters>) the B<DH> keytype
-implementation supports the following:
+(see L<EVP_PKEY-FFC(7)/FFC parameters>) the B<DHX> and B<DH> keytype
+implementations support the following:
 
 =over 4
 
 =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>.
+Sets or gets a string that associates a B<DH> or B<DHX> named safe prime group
+with known values for I<p>, I<q> and I<g>.
 
 The following values can be used by the OpenSSL's default and FIPS providers:
 "ffdhe2048", "ffdhe3072", "ffdhe4096", "ffdhe6144", "ffdhe8192",
@@ -41,31 +47,46 @@ The following values can be used by the OpenSSL's default and FIPS providers:
 The following additional values can also be used by OpenSSL's default provider:
 "modp_1536", "dh_1024_160", "dh_2048_224", "dh_2048_256".
 
-DH named groups can be easily validated since the parameters are well known.
+DH/DHX named groups can be easily validated since the parameters are well known.
 For protocols that only transfer I<p> and I<g> the value of I<q> can also be
 retrieved.
 
-=item "safeprime-generator" (B<OSSL_PKEY_PARAM_DH_GENERATOR>) <integer>
+=back
 
-Used for DH generation of safe primes using the old generator code.
-It is recommended to use a named safe prime group instead, if domain parameter
-validation is required. The default value is 2.
+=head2 DH and DHX additional parameters
 
-These are not named safe prime groups so setting this value for the OpenSSL FIPS
-provider will instead choose a named safe prime group based on the size of I<p>.
+=over 4
 
 =item "encoded-pub-key" (B<OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY>) <octet string>
 
 Used for getting and setting the encoding of the DH public key used in a key
 exchange message for the TLS protocol.
+See EVP_PKEY_set1_encoded_public_key() and EVP_PKEY_get1_encoded_public_key().
+
+=back
+
+=head2 DH additional domain parameters
+
+=over 4
+
+=item "safeprime-generator" (B<OSSL_PKEY_PARAM_DH_GENERATOR>) <integer>
+
+Used for DH generation of safe primes using the old safe prime generator code.
+The default value is 2.
+It is recommended to use a named safe prime group instead, if domain parameter
+validation is required. 
+
+Randomly generated safe primes are not allowed by FIPS, so setting this value
+for the OpenSSL FIPS provider will instead choose a named safe prime group
+based on the size of I<p>.
 
 =back
 
-=head2 DH domain parameter / key generation parameters
+=head2 DH and DHX domain parameter / key generation parameters
 
 In addition to the common FFC key generation parameters that all FFC key types
 should support (see L<EVP_PKEY-FFC(7)/FFC key generation parameters>) the
-B<DH> keytype implementation supports the following:
+B<DH> and B<DHX> keytype implementation supports the following:
 
 =over 4
 
@@ -91,6 +112,7 @@ type.
 =item "generator"
 
 A safe prime generator. See the "safeprime-generator" type above.
+This is only valid for B<DH> keys.
 
 =back
 
diff --git a/doc/man7/EVP_PKEY-FFC.pod b/doc/man7/EVP_PKEY-FFC.pod
index 7e238d7b44..e345580ec1 100644
--- a/doc/man7/EVP_PKEY-FFC.pod
+++ b/doc/man7/EVP_PKEY-FFC.pod
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-EVP_PKEY-FFC - EVP_PKEY DSA and DH shared FFC parameters.
+EVP_PKEY-FFC - EVP_PKEY DSA and DH/DHX shared FFC parameters.
 
 =head1 DESCRIPTION
 
@@ -11,9 +11,9 @@ cryptography using finite field mathematics. DSA is an example of FFC and
 Diffie-Hellman key establishment algorithms specified in SP800-56A can also be
 implemented as FFC.
 
-The B<DSA> and B<DH> keytypes are implemented in OpenSSL's default and FIPS
-providers.
-The implementations support the basic DSA and DH keys, containing the public
+The B<DSA>, B<DH> and B<DHX> keytypes are implemented in OpenSSL's default and
+FIPS providers.
+The implementations support the basic DSA, DH and DHX keys, containing the public
 and private keys I<pub> and I<priv> as well as the three main domain parameters
 I<p>, I<q> and I<g>.
 
@@ -26,10 +26,14 @@ For B<DH> the I<seed> and I<pcounter> can be stored in ASN1 data
 (but the I<gindex> is not). For B<DSA> however, these fields are not stored in
 the ASN1 data so they need to be stored externally if validation is required.
 
+The B<DH> key type uses PKCS#3 format which saves p and g, but not the 'q' value.
+The B<DHX> key type uses X9.42 format which saves the value of 'q' and this
+must be used for FIPS186-4.
+
 =head2 FFC parameters
 
 In addition to the common parameters that all keytypes should support (see
-L<provider-keymgmt(7)/Common parameters>), the B<DSA> and B<DH> keytype
+L<provider-keymgmt(7)/Common parameters>), the B<DSA>, B<DH> and B<DHX> keytype
 implementations support the following.
 
 =over 4
@@ -42,18 +46,30 @@ The public key value.
 
 The private key value.
 
-=item "p" (B<OSSL_PKEY_PARAM_FFC_P>) <unsigned integer>
+=back
 
-A DSA or Diffie-Hellman prime "p" value.
+=head2 FFC DSA, DH and DHX domain parameters
 
-=item "q" (B<OSSL_PKEY_PARAM_FFC_Q>) <unsigned integer>
+=over 4
 
-A DSA or Diffie-Hellman prime "q" value.
+=item "p" (B<OSSL_PKEY_PARAM_FFC_P>) <unsigned integer>
+
+A DSA or Diffie-Hellman prime "p" value.
 
 =item "g" (B<OSSL_PKEY_PARAM_FFC_G>) <unsigned integer>
 
 A DSA or Diffie-Hellman generator "g" value.
 
+=back
+
+=head2 FFC DSA and DHX domain parameters
+
+=over 4
+
+=item "q" (B<OSSL_PKEY_PARAM_FFC_Q>) <unsigned integer>
+
+A DSA or Diffie-Hellman prime "q" value.
+
 =item "seed" (B<OSSL_PKEY_PARAM_FFC_SEED>) <octet string>
 
 An optional domain parameter I<seed> value used during generation and validation
@@ -88,7 +104,7 @@ An optional informational cofactor parameter that should equal to (p - 1) / q.
 
 =head2 FFC key generation parameters
 
-The following key generation types are available for DSA and DH algorithms:
+The following key generation types are available for DSA and DHX algorithms:
 
 =over 4
 
diff --git a/include/crypto/dh.h b/include/crypto/dh.h
index ff7c65a468..8613f9038e 100644
--- a/include/crypto/dh.h
+++ b/include/crypto/dh.h
@@ -26,7 +26,7 @@ int ossl_dh_generate_public_key(BN_CTX *ctx, const DH *dh,
                                 const BIGNUM *priv_key, BIGNUM *pub_key);
 int ossl_dh_get_named_group_uid_from_size(int pbits);
 const char *ossl_dh_gen_type_id2name(int id);
-int ossl_dh_gen_type_name2id(const char *name);
+int ossl_dh_gen_type_name2id(const char *name, int type);
 void ossl_dh_cache_named_group(DH *dh);
 int ossl_dh_is_named_safe_prime_group(const DH *dh);
 
diff --git a/providers/implementations/keymgmt/dh_kmgmt.c b/providers/implementations/keymgmt/dh_kmgmt.c
index b3678c5e2a..c4cda447bf 100644
--- a/providers/implementations/keymgmt/dh_kmgmt.c
+++ b/providers/implementations/keymgmt/dh_kmgmt.c
@@ -92,7 +92,7 @@ static int dh_gen_type_name2id_w_default(const char *name, int type)
 #endif
     }
 
-    return ossl_dh_gen_type_name2id(name);
+    return ossl_dh_gen_type_name2id(name, type);
 }
 
 static void *dh_newdata(void *provctx)
@@ -488,7 +488,7 @@ static int dh_set_gen_seed(struct dh_gen_ctx *gctx, unsigned char *seed,
     return 1;
 }
 
-static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
+static int dh_gen_common_set_params(void *genctx, const OSSL_PARAM params[])
 {
     struct dh_gen_ctx *gctx = genctx;
     const OSSL_PARAM *p;
@@ -498,7 +498,6 @@ static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
     if (params == NULL)
         return 1;
 
-
     p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_TYPE);
     if (p != NULL) {
         if (p->data_type != OSSL_PARAM_UTF8_STRING
@@ -519,11 +518,59 @@ static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
             ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
             return 0;
         }
-        gctx->gen_type = DH_PARAMGEN_TYPE_GROUP;
     }
-    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GENERATOR);
-    if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->generator))
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PBITS)) != NULL
+        && !OSSL_PARAM_get_size_t(p, &gctx->pbits))
+        return 0;
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_PRIV_LEN);
+    if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->priv_len))
+        return 0;
+    return 1;
+}
+
+static const OSSL_PARAM *dh_gen_settable_params(ossl_unused void *genctx,
+                                                ossl_unused void *provctx)
+{
+    static const OSSL_PARAM dh_gen_settable[] = {
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, 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_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL),
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_GENERATOR, NULL),
+        OSSL_PARAM_END
+    };
+    return dh_gen_settable;
+}
+
+static const OSSL_PARAM *dhx_gen_settable_params(ossl_unused void *genctx,
+                                                 ossl_unused void *provctx)
+{
+    static const OSSL_PARAM dhx_gen_settable[] = {
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, 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_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL),
+        OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_QBITS, NULL),
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST, NULL, 0),
+        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST_PROPS, NULL, 0),
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+        OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+        OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+        OSSL_PARAM_END
+    };
+    return dhx_gen_settable;
+}
+
+static int dhx_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+    struct dh_gen_ctx *gctx = genctx;
+    const OSSL_PARAM *p;
+
+    if (!dh_gen_common_set_params(genctx, params))
         return 0;
+
+    /* Parameters related to fips186-4 and fips186-2 */
     p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_GINDEX);
     if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->gindex))
         return 0;
@@ -538,10 +585,6 @@ static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
         && (p->data_type != OSSL_PARAM_OCTET_STRING
             || !dh_set_gen_seed(gctx, p->data, p->data_size)))
             return 0;
-
-    if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PBITS)) != NULL
-        && !OSSL_PARAM_get_size_t(p, &gctx->pbits))
-        return 0;
     if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_QBITS)) != NULL
         && !OSSL_PARAM_get_size_t(p, &gctx->qbits))
         return 0;
@@ -563,31 +606,41 @@ static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
         if (gctx->mdprops == NULL)
             return 0;
     }
-    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_PRIV_LEN);
-    if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->priv_len))
+
+    /* Parameters that are not allowed for DHX */
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GENERATOR);
+    if (p != NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_UNSUPPORTED);
         return 0;
+    }
     return 1;
 }
 
-static const OSSL_PARAM *dh_gen_settable_params(ossl_unused void *genctx,
-                                                ossl_unused void *provctx)
+static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
 {
-    static OSSL_PARAM settable[] = {
-        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),
-        OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL),
-        OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_QBITS, NULL),
-        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST, NULL, 0),
-        OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST_PROPS, NULL, 0),
-        OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
-        OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
-        OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
-        OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
-        OSSL_PARAM_END
-    };
-    return settable;
+    struct dh_gen_ctx *gctx = genctx;
+    const OSSL_PARAM *p;
+
+    if (!dh_gen_common_set_params(genctx, params))
+        return 0;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GENERATOR);
+    if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->generator))
+        return 0;
+
+    /* Parameters that are not allowed for DH */
+    if (OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_GINDEX) != NULL
+        || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PCOUNTER) != NULL
+        || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_H) != NULL
+        || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_SEED) != NULL
+        || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_QBITS) != NULL
+        || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST) != NULL
+        || OSSL_PARAM_locate_const(params,
+                                   OSSL_PKEY_PARAM_FFC_DIGEST_PROPS) != NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    return 1;
 }
 
 static int dh_gencb(int p, int n, BN_GENCB *cb)
@@ -612,6 +665,14 @@ static void *dh_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
     if (!ossl_prov_is_running() || gctx == NULL)
         return NULL;
 
+    /*
+     * If a group name is selected then the type is group regardless of what the
+     * the user selected. This overrides rather than errors for backwards
+     * compatibility.
+     */
+    if (gctx->group_nid != NID_undef)
+        gctx->gen_type = DH_PARAMGEN_TYPE_GROUP;
+
     /* For parameter generation - If there is a group name just create it */
     if (gctx->gen_type == DH_PARAMGEN_TYPE_GROUP
             && gctx->ffc_params == NULL) {
@@ -765,9 +826,9 @@ const OSSL_DISPATCH ossl_dhx_keymgmt_functions[] = {
     { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))dhx_newdata },
     { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))dhx_gen_init },
     { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))dh_gen_set_template },
-    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))dh_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))dhx_gen_set_params },
     { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
-      (void (*)(void))dh_gen_settable_params },
+      (void (*)(void))dhx_gen_settable_params },
     { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))dh_gen },
     { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))dh_gen_cleanup },
     { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))dh_load },
diff --git a/test/recipes/15-test_gendh.t b/test/recipes/15-test_gendh.t
index 87dd73f438..39112f1bfe 100644
--- a/test/recipes/15-test_gendh.t
+++ b/test/recipes/15-test_gendh.t
@@ -18,34 +18,7 @@ setup("test_gendh");
 
 plan skip_all => "This test is unsupported in a no-dh build" if disabled("dh");
 
-plan tests => 13;
-
-ok(run(app([ 'openssl', 'genpkey', '-genparam',
-             '-algorithm', 'DH',
-             '-pkeyopt', 'gindex:1',
-             '-pkeyopt', 'type:fips186_4',
-             '-text'])),
-   "genpkey DH params fips186_4 with verifiable g");
-
-ok(run(app([ 'openssl', 'genpkey', '-genparam',
-             '-algorithm', 'DH',
-             '-pkeyopt', 'type:fips186_4',
-             '-text'])),
-   "genpkey DH params fips186_4 with unverifiable g");
-
-ok(run(app([ 'openssl', 'genpkey', '-genparam',
-             '-algorithm', 'DH',
-             '-pkeyopt', 'pbits:2048',
-             '-pkeyopt', 'qbits:224',
-             '-pkeyopt', 'digest:SHA512-224',
-             '-pkeyopt', 'type:fips186_4'])),
-   "genpkey DH params fips186_4 with truncated SHA");
-
-ok(run(app([ 'openssl', 'genpkey', '-genparam',
-             '-algorithm', 'DH',
-             '-pkeyopt', 'type:fips186_2',
-             '-text'])),
-   "genpkey DH params fips186_2");
+plan tests => 9;
 
 ok(run(app([ 'openssl', 'genpkey', '-algorithm', 'DH',
              '-pkeyopt', 'type:group',
@@ -59,7 +32,7 @@ ok(run(app([ 'openssl', 'genpkey', '-algorithm', 'DH',
    "genpkey DH group ffdhe2048");
 
 ok(run(app([ 'openssl', 'genpkey', '-genparam',
-             '-algorithm', 'DH',
+             '-algorithm', 'DHX',
              '-pkeyopt', 'gindex:1',
              '-pkeyopt', 'type:fips186_4',
              '-out', 'dhgen.pem' ])),
@@ -70,7 +43,7 @@ ok(run(app([ 'openssl', 'genpkey', '-genparam',
 ok(run(app([ 'openssl', 'genpkey',
              '-paramfile', 'dhgen.pem',
              '-pkeyopt', 'gindex:1',
-             '-pkeyopt', 'hexseed:0102030405060708090A0B0C0D0E0F1011121314',
+             '-pkeyopt', 'hexseed:ed2927f2139eb61495d6641efda1243f93ebe482b5bfc2c755a53825',
              '-pkeyopt', 'pcounter:25',
              '-text' ])),
    "genpkey DH fips186_4 with PEM params");
diff --git a/test/recipes/15-test_gendhparam.t b/test/recipes/15-test_gendhparam.t
new file mode 100644
index 0000000000..b5fe644889
--- /dev/null
+++ b/test/recipes/15-test_gendhparam.t
@@ -0,0 +1,170 @@
+#! /usr/bin/env perl
+# Copyright 2021 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
+
+use strict;
+use warnings;
+
+use OpenSSL::Test;
+use OpenSSL::Test::Utils;
+
+setup("test_gendhparam");
+
+my @testdata = (
+    {
+        algorithm => 'DHX',
+        pkeyopts => [ "type:fips186_4", 'digest:SHA256', 'gindex:1' ],
+        expect => [ 'BEGIN X9.42 DH PARAMETERS', 'gindex:', 'pcounter:', 'SEED:' ],
+        message   => 'DH fips186_4 param gen with verifiable g',
+    },
+    {
+        algorithm => 'DH',
+        pkeyopts => [ "type:fips186_4", 'digest:SHA256', 'gindex:1' ],
+        expect => [ 'ERROR' ],
+        message   => 'fips186_4 param gen should fail if DHX is not used',
+    },
+    {
+        algorithm => 'DHX',
+        pkeyopts => [ "type:fips186_4", 'digest:SHA512-224', 'gindex:1' ],
+        expect => [ 'BEGIN X9.42 DH PARAMETERS', 'gindex:', 'pcounter:', 'SEED:' ],
+        message   => 'DH fips186_4 param gen with verifiable g and truncated digest',
+    },
+    {
+        algorithm => 'DHX',
+        pkeyopts => [ 'type:fips186_2', 'pbits:1024', 'qbits:160' ],
+        expect => [ 'BEGIN X9.42 DH PARAMETERS', 'h:', 'pcounter:', 'SEED:' ],
+        message   => 'DHX fips186_2 param gen with a selected p and q size with unverifyable g',
+    },
+    {
+        algorithm => 'DHX',
+        pkeyopts => [ 'type:fips186_2', 'dh_paramgen_prime_len:1024', 'dh_paramgen_subprime_len:160' ],
+        message   => 'DHX fips186_2 param gen with a selected p and q size using aliased',
+        expect => [ "BEGIN X9.42 DH PARAMETERS" ],
+    },
+    {
+        algorithm => 'DH',
+        pkeyopts => [ 'type:fips186_2', 'dh_paramgen_prime_len:1024', 'dh_paramgen_subprime_len:160' ],
+        message   => 'DH fips186_2 param gen with a selected p and q size using aliases should fail',
+        expect => [ "ERROR" ],
+    },
+    {
+        algorithm => 'DH',
+        pkeyopts => [ 'group:ffdhe2048'],
+        expect => [ 'BEGIN DH PARAMETERS', 'GROUP:' ],
+        message   => 'DH named group ffdhe selection',
+    },
+    {
+        algorithm => 'DH',
+        pkeyopts => [ 'dh_param:ffdhe8192'],
+        expect => [ 'BEGIN DH PARAMETERS', 'GROUP:' ],
+        message   => 'DH named group ffdhe selection using alias',
+    },
+    {
+        algorithm => 'DH',
+        pkeyopts => [ 'group:modp_3072'],
+        expect => [ 'BEGIN DH PARAMETERS', 'GROUP:' ],
+        message   => 'DH named group modp selection',
+    },
+    {
+        algorithm => 'DH',
+        pkeyopts => [ 'dh_param:modp_4096'],
+        message   => 'DH named group modp selection using alias',
+        expect => [ 'BEGIN DH PARAMETERS', 'GROUP:'],
+    },
+    {
+        algorithm => 'DHX',
+        pkeyopts => [ 'group:dh_2048_256' ],
+        expect => [ 'BEGIN X9.42 DH PARAMETERS', 'GROUP:' ],
+        message   => 'DHX RFC5114 named group selection',
+    },
+    {
+        algorithm => 'DHX',
+        pkeyopts => [ 'dh_param:dh_2048_224' ],
+        expect => [ 'BEGIN X9.42 DH PARAMETERS', 'GROUP:' ],
+        message   => 'DHX RFC5114 named group selection using alias',
+    },
+    {
+        algorithm => 'DHX',
+        pkeyopts => [ 'dh_rfc5114:2'],
+        expect => [ 'BEGIN X9.42 DH PARAMETERS', 'GROUP:' ],
+        message   => 'DHX RFC5114 named group selection using an id',
+    },
+    {
+        algorithm => 'DHX',
+        pkeyopts => [ 'dh_rfc5114:1', 'dh_paramgen_type:1' ],
+        expect => [ 'BEGIN X9.42 DH PARAMETERS', 'GROUP:' ],
+        message   => 'DHX paramgen_type is ignored if the group is set',
+    },
+    {
+        algorithm => 'DH',
+        pkeyopts => [ 'dh_rfc5114:1', 'dh_paramgen_type:1' ],
+        expect => [ 'ERROR' ],
+        message   => "Setting dh_paramgen_type to fips186 should fail for DH keys",
+    },
+# These tests using the safeprime generator were removed as they are slow..
+#    {
+#        algorithm => 'DH',
+#        pkeyopts => [ 'type:generator', 'safeprime-generator:5'],
+#        expect => [ 'BEGIN DH PARAMETERS', 'G:    5' ],
+#        message   => 'DH safe prime generator',
+#    },
+#    {
+#        algorithm => 'DH',
+#        pkeyopts => [ 'dh_paramgen_type:0', 'dh_paramgen_generator:5'],
+#        expect => [ 'BEGIN DH PARAMETERS', 'G:    5' ],
+#        message   => 'DH safe prime generator using an alias',
+#    },
+     {
+        algorithm => 'DHX',
+        pkeyopts => [ 'type:generator', 'safeprime-generator:5'],
+        expect => [ 'ERROR' ],
+        message   => 'safe prime generator should fail for DHX',
+    },
+);
+
+plan skip_all => "DH isn't supported in this build" if disabled("dh");
+
+plan tests => scalar @testdata;
+
+foreach my $test (@testdata) {
+    my $alg = $test->{algorithm};
+    my $msg = $test->{message};
+    my @testargs = @{ $test->{pkeyopts} };
+    my @expected = @{ $test->{expect} };
+    my @pkeyopts= ();
+    foreach (@testargs) {
+        push(@pkeyopts, '-pkeyopt');
+        push(@pkeyopts, $_);
+    }
+    my @lines = run(app(['openssl', 'genpkey', '-genparam',
+                          '-algorithm', $alg, '-text', @pkeyopts]),
+                    capture => 1);
+    ok(compareline(\@lines, \@expected), $msg);
+}
+
+# Check that the stdout output matches the expected value.
+sub compareline {
+    my ($ref_lines, $ref_expected) = @_;
+    my @lines = @$ref_lines;
+    my @expected = @$ref_expected;
+
+    if (@lines == 0 and $expected[0] eq 'ERROR') {
+        return 1;
+    }
+    print "-----------------\n";
+    foreach (@lines) {
+        print $_;
+    }
+    print "-----------------\n";
+    foreach my $ex (@expected) {
+        if ( !grep { index($_, $ex) >= 0 }  @lines) {
+            print "ERROR: Cannot find: $ex\n";
+            return 0;
+        }
+    }
+    return 1;
+}
diff --git a/test/recipes/20-test_dhparam_check.t b/test/recipes/20-test_dhparam_check.t
index f3882ad2b3..b929afb326 100644
--- a/test/recipes/20-test_dhparam_check.t
+++ b/test/recipes/20-test_dhparam_check.t
@@ -28,16 +28,10 @@ TESTDIR=test/recipes/20-test_dhparam_check_data/valid
 rm -rf $TESTDIR
 mkdir -p $TESTDIR
 
-#TODO(3.0): These 3 currently create invalid output - see issue #14145
-./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt dh_rfc5114:1 -out $TESTDIR/dh5114_1.pem
-./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt dh_rfc5114:2 -out $TESTDIR/dh5114_2.pem
-./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt dh_rfc5114:3 -out $TESTDIR/dh5114_3.pem
-
-#TODO(3.0): These 4 currently create invalid output - see issue #14145
-./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt pbits:1024 -pkeyopt type:fips186_2 -out $TESTDIR/dh_p1024_t1862.pem
-./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt pbits:2048 -pkeyopt type:fips186_2 -out $TESTDIR/dh_p2048_t1862.pem
-./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt pbits:2048 -pkeyopt type:fips186_4 -out $TESTDIR/dh_p2048_t1864.pem
-./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt pbits:3072 -pkeyopt type:fips186_2 -out $TESTDIR/dh_p3072_t1862.pem
+./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt dh_rfc5114:1 -out $TESTDIR/dh_5114_1.pem
+./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt dh_rfc5114:2 -out $TESTDIR/dh_5114_2.pem
+./util/opensslwrap.sh genpkey -genparam -algorithm DH -pkeyopt dh_rfc5114:3 -out $TESTDIR/dh_5114_3.pem
+./util/opensslwrap.sh genpkey -genparam -algorithm DHX -pkeyopt dh_rfc5114:2 -out $TESTDIR/dhx_5114_2.pem
 
 ./util/opensslwrap.sh genpkey -genparam -algorithm DHX -pkeyopt pbits:1024 -pkeyopt qbits:160 -pkeyopt type:fips186_2 -out $TESTDIR/dhx_p1024_q160_t1862.pem
 ./util/opensslwrap.sh genpkey -genparam -algorithm DHX -pkeyopt pbits:1024 -pkeyopt qbits:224 -pkeyopt type:fips186_2 -out $TESTDIR/dhx_p1024_q224_t1862.pem
diff --git a/test/recipes/20-test_dhparam_check_data/invalid/dh5114_1_pkcs3.pem b/test/recipes/20-test_dhparam_check_data/valid/dh_5114_1.pem
similarity index 100%
rename from test/recipes/20-test_dhparam_check_data/invalid/dh5114_1_pkcs3.pem
rename to test/recipes/20-test_dhparam_check_data/valid/dh_5114_1.pem
diff --git a/test/recipes/20-test_dhparam_check_data/invalid/dh5114_2_pkcs3.pem b/test/recipes/20-test_dhparam_check_data/valid/dh_5114_2.pem
similarity index 100%
copy from test/recipes/20-test_dhparam_check_data/invalid/dh5114_2_pkcs3.pem
copy to test/recipes/20-test_dhparam_check_data/valid/dh_5114_2.pem
diff --git a/test/recipes/20-test_dhparam_check_data/invalid/dh5114_3_pkcs3.pem b/test/recipes/20-test_dhparam_check_data/valid/dh_5114_3.pem
similarity index 100%
rename from test/recipes/20-test_dhparam_check_data/invalid/dh5114_3_pkcs3.pem
rename to test/recipes/20-test_dhparam_check_data/valid/dh_5114_3.pem
diff --git a/test/recipes/20-test_dhparam_check_data/invalid/dh5114_2_pkcs3.pem b/test/recipes/20-test_dhparam_check_data/valid/dhx_5114_2.pem
similarity index 71%
rename from test/recipes/20-test_dhparam_check_data/invalid/dh5114_2_pkcs3.pem
rename to test/recipes/20-test_dhparam_check_data/valid/dhx_5114_2.pem
index d1fadc1a90..8887cb174b 100644
--- a/test/recipes/20-test_dhparam_check_data/invalid/dh5114_2_pkcs3.pem
+++ b/test/recipes/20-test_dhparam_check_data/valid/dhx_5114_2.pem
@@ -1,5 +1,5 @@
------BEGIN DH PARAMETERS-----
-MIICDgKCAQEArRB+HpEjqdDWYPqnlVnFH6INZOVoO5/RtUsVl7YdCnXm+hQd+VpW
+-----BEGIN X9.42 DH PARAMETERS-----
+MIICKQKCAQEArRB+HpEjqdDWYPqnlVnFH6INZOVoO5/RtUsVl7YdCnXm+hQd+VpW
 26+aPEB7od8V6z1oijCcGA4d5rhaEnSgpm0/gVKtasISkDfJ7e/aTfjZHo/vVbc5
 S3rVt9C2wSIHyfmNEe002/bGugssi7wnvmoA4KC5xJcIs7+KMXCRiDaBKGEwvImF
 2xYC5xRBXZMwJ4Jzx94x79xzEPcSH9WgdBWYfZrcCkhtzfk6zEQyg4cxXXXhmMZB
@@ -9,6 +9,6 @@ vnuJmYyvdIZqCM/k/+OmgkpOELmm8N2SHwGnDEr6q3OddwDCn1LFfbF8YgqGUr5e
 kAGo1mrXwXZpEBmZAkr00CcnWsE0i7inYtBSG8mK4kcVBCLqHtQJk51U2nRgzbX2
 xrJQcXy+8YDrNBGOmNEZUppF1vg0Vm4wJeMWozDvu3eobwwasVsFGuPUKMj4rLcK
 gTcVC47rEOGD7dGZY93Z4mPkdwWJ72qiHn9fL/OBtTnM40CdE81Wavu0jWwBkYHh
-vP6UswJp7f5y/ptqpL17Wg8ccc//TBnEGOH27AF5gbwIfypwZbOEuJDTGR8r+gIC
-AOA=
------END DH PARAMETERS-----
+vP6UswJp7f5y/ptqpL17Wg8ccc//TBnEGOH27AF5gbwIfypwZbOEuJDTGR8r+gId
+AIAcDTTFjZP+mXF3EB+AU1pHOM68vziambNjces=
+-----END X9.42 DH PARAMETERS-----


More information about the openssl-commits mailing list