[openssl] master update

shane.lontis at oracle.com shane.lontis at oracle.com
Tue Aug 11 10:42:10 UTC 2020


The branch master has been updated
       via  31d2daecb384475da13c4bf7c76a2dde0077b2f2 (commit)
       via  116d2510f7e95d1e6f4a6b9db2df1ec8d11b61fb (commit)
       via  627c220311bcd3bd6f4f745b76ce6a548bed4629 (commit)
      from  36b778fbb785ef19dc2476798ccb8233a8a16075 (commit)


- Log -----------------------------------------------------------------
commit 31d2daecb384475da13c4bf7c76a2dde0077b2f2
Author: Shane Lontis <shane.lontis at oracle.com>
Date:   Tue Aug 4 11:39:49 2020 +1000

    Add DHX serialization
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/12575)

commit 116d2510f7e95d1e6f4a6b9db2df1ec8d11b61fb
Author: Shane Lontis <shane.lontis at oracle.com>
Date:   Tue Aug 4 11:21:21 2020 +1000

    Add dh_kdf support to provider
    
    Similiar to ecdh this supports the legacy kdf inside the provider dh key exchange.
    The supporting EVP_PKEY_CTX macros have been changed into mehtods and moved into dh_ctrl.c
    New kdfs such as SSKDF should be done as a seperate pass after doing the derive.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/12575)

commit 627c220311bcd3bd6f4f745b76ce6a548bed4629
Author: Shane Lontis <shane.lontis at oracle.com>
Date:   Tue Aug 4 11:15:18 2020 +1000

    Add DHX support to keymanager
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/12575)

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

Summary of changes:
 crypto/dh/build.info                               |   5 +-
 crypto/dh/dh_ameth.c                               |  41 +-
 crypto/dh/dh_ctrl.c                                | 566 +++++++++++++++++++++
 crypto/dh/dh_kdf.c                                 |  74 +--
 crypto/dh/dh_lib.c                                 | 192 -------
 crypto/evp/p_lib.c                                 |   2 +
 crypto/evp/pmeth_lib.c                             |  23 +-
 doc/man3/EVP_PKEY_CTX_ctrl.pod                     | 124 ++---
 include/crypto/dh.h                                |   6 +
 include/openssl/dh.h                               |  53 +-
 providers/defltprov.c                              |   1 +
 providers/deserializers.inc                        |   1 +
 providers/fips/fipsprov.c                          |   1 +
 providers/implementations/exchange/dh_exch.c       | 302 ++++++++++-
 .../implementations/include/prov/implementations.h |   2 +
 providers/implementations/keymgmt/dh_kmgmt.c       |  72 ++-
 .../serializers/deserialize_der2key.c              |   1 +
 .../implementations/serializers/serializer_dh.c    |   5 +
 .../serializers/serializer_dh_priv.c               |   6 +-
 .../serializers/serializer_dh_pub.c                |   6 +-
 .../implementations/serializers/serializer_local.h |   1 +
 providers/serializers.inc                          |  10 +
 test/evp_libctx_test.c                             |  88 ++++
 test/serdes_test.c                                 |   5 +
 util/libcrypto.num                                 |  10 +
 25 files changed, 1246 insertions(+), 351 deletions(-)
 create mode 100644 crypto/dh/dh_ctrl.c

diff --git a/crypto/dh/build.info b/crypto/dh/build.info
index 656e6ea828..887ef78b0b 100644
--- a/crypto/dh/build.info
+++ b/crypto/dh/build.info
@@ -1,10 +1,11 @@
 LIBS=../../libcrypto
 
-$COMMON=dh_lib.c dh_key.c dh_group_params.c dh_check.c dh_backend.c dh_gen.c
+$COMMON=dh_lib.c dh_key.c dh_group_params.c dh_check.c dh_backend.c dh_gen.c \
+        dh_kdf.c
 
 SOURCE[../../libcrypto]=$COMMON\
         dh_asn1.c dh_err.c \
-        dh_ameth.c dh_pmeth.c dh_prn.c dh_rfc5114.c dh_kdf.c dh_meth.c
+        dh_ameth.c dh_pmeth.c dh_prn.c dh_rfc5114.c dh_meth.c dh_ctrl.c
 IF[{- !$disabled{'deprecated-0.9.8'} -}]
   SOURCE[../../libcrypto]=dh_depr.c
 ENDIF
diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c
index d5e5f72517..011bc5ad03 100644
--- a/crypto/dh/dh_ameth.c
+++ b/crypto/dh/dh_ameth.c
@@ -35,9 +35,19 @@
 static DH *d2i_dhp(const EVP_PKEY *pkey, const unsigned char **pp,
                    long length)
 {
-    if (pkey->ameth == &dhx_asn1_meth)
-        return d2i_DHxparams(NULL, pp, length);
-    return d2i_DHparams(NULL, pp, length);
+    DH *dh = NULL;
+    int is_dhx = (pkey->ameth == &dhx_asn1_meth);
+
+    if (is_dhx)
+        dh = d2i_DHxparams(NULL, pp, length);
+    else
+        dh = d2i_DHparams(NULL, pp, length);
+
+    if (dh != NULL) {
+        DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+        DH_set_flags(dh, is_dhx ? DH_FLAG_TYPE_DHX : DH_FLAG_TYPE_DH);
+    }
+    return dh;
 }
 
 static int i2d_dhp(const EVP_PKEY *pkey, const DH *a, unsigned char **pp)
@@ -101,7 +111,6 @@ static int dh_pub_decode(EVP_PKEY *pkey, const X509_PUBKEY *pubkey)
     ASN1_INTEGER_free(public_key);
     DH_free(dh);
     return 0;
-
 }
 
 static int dh_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
@@ -548,12 +557,16 @@ err:
     return rv;
 }
 
-static int dh_pkey_import_from(const OSSL_PARAM params[], void *vpctx)
+static int dh_pkey_import_from_type(const OSSL_PARAM params[], void *vpctx,
+                                    int type)
 {
     EVP_PKEY_CTX *pctx = vpctx;
     EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(pctx);
     DH *dh = dh_new_with_libctx(pctx->libctx);
 
+    DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+    DH_set_flags(dh, type == EVP_PKEY_DH ? DH_FLAG_TYPE_DH : DH_FLAG_TYPE_DHX);
+
     if (dh == NULL) {
         ERR_raise(ERR_LIB_DH, ERR_R_MALLOC_FAILURE);
         return 0;
@@ -561,13 +574,23 @@ static int dh_pkey_import_from(const OSSL_PARAM params[], void *vpctx)
 
     if (!dh_ffc_params_fromdata(dh, params)
         || !dh_key_fromdata(dh, params)
-        || !EVP_PKEY_assign_DH(pkey, dh)) {
+        || !EVP_PKEY_assign(pkey, type, dh)) {
         DH_free(dh);
         return 0;
     }
     return 1;
 }
 
+static int dh_pkey_import_from(const OSSL_PARAM params[], void *vpctx)
+{
+    return dh_pkey_import_from_type(params, vpctx, EVP_PKEY_DH);
+}
+
+static int dhx_pkey_import_from(const OSSL_PARAM params[], void *vpctx)
+{
+    return dh_pkey_import_from_type(params, vpctx, EVP_PKEY_DHX);
+}
+
 const EVP_PKEY_ASN1_METHOD dh_asn1_meth = {
     EVP_PKEY_DH,
     EVP_PKEY_DH,
@@ -649,7 +672,11 @@ const EVP_PKEY_ASN1_METHOD dhx_asn1_meth = {
 
     0,
     dh_pkey_public_check,
-    dh_pkey_param_check
+    dh_pkey_param_check,
+    0, 0, 0, 0,
+    dh_pkey_dirty_cnt,
+    dh_pkey_export_to,
+    dhx_pkey_import_from,
 };
 
 #ifndef OPENSSL_NO_CMS
diff --git a/crypto/dh/dh_ctrl.c b/crypto/dh/dh_ctrl.c
new file mode 100644
index 0000000000..6fddd271a8
--- /dev/null
+++ b/crypto/dh/dh_ctrl.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright 1995-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
+ */
+
+/*
+ * DH low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <stdio.h>
+#include "crypto/evp.h"
+#include <openssl/bn.h>
+#include <openssl/engine.h>
+#include <openssl/obj_mac.h>
+#include <openssl/core_names.h>
+#include "internal/cryptlib.h"
+#include "internal/refcount.h"
+#include "crypto/dh.h"
+#include "dh_local.h"
+
+static int dh_paramgen_check(EVP_PKEY_CTX *ctx)
+{
+    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 key type not DH return error */
+    if (ctx->pmeth != NULL
+        && ctx->pmeth->pkey_id != EVP_PKEY_DH
+        && ctx->pmeth->pkey_id != EVP_PKEY_DHX)
+        return -1;
+    return 1;
+}
+
+static int dh_param_derive_check(EVP_PKEY_CTX *ctx)
+{
+    if (ctx == NULL || !EVP_PKEY_CTX_IS_DERIVE_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 key type not DH return error */
+    if (ctx->pmeth != NULL
+        && ctx->pmeth->pkey_id != EVP_PKEY_DH
+        && ctx->pmeth->pkey_id != EVP_PKEY_DHX)
+        return -1;
+    return 1;
+}
+
+int EVP_PKEY_CTX_set_dh_paramgen_gindex(EVP_PKEY_CTX *ctx, int gindex)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+
+    if ((ret = dh_paramgen_check(ctx)) <= 0)
+        return ret;
+
+    *p++ = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_FFC_GINDEX, &gindex);
+    *p = OSSL_PARAM_construct_end();
+
+    return EVP_PKEY_CTX_set_params(ctx, params);
+}
+
+int EVP_PKEY_CTX_set_dh_paramgen_seed(EVP_PKEY_CTX *ctx,
+                                      const unsigned char *seed,
+                                      size_t seedlen)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+
+    if ((ret = dh_paramgen_check(ctx)) <= 0)
+        return ret;
+
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_FFC_SEED,
+                                             (void *)seed, seedlen);
+    *p = OSSL_PARAM_construct_end();
+
+    return EVP_PKEY_CTX_set_params(ctx, params);
+}
+
+int EVP_PKEY_CTX_set_dh_paramgen_type(EVP_PKEY_CTX *ctx, int typ)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+    const char *name;
+
+    if ((ret = dh_paramgen_check(ctx)) <= 0)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.keymgmt.genctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
+                                 EVP_PKEY_CTRL_DH_PARAMGEN_TYPE, typ, NULL);
+
+    name = dh_gen_type_id2name(typ);
+    if (name == NULL)
+        return 0;
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE,
+                                            (char *) name, 0);
+    *p = OSSL_PARAM_construct_end();
+
+    return EVP_PKEY_CTX_set_params(ctx, params);
+}
+
+int EVP_PKEY_CTX_set_dh_paramgen_prime_len(EVP_PKEY_CTX *ctx, int pbits)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+    size_t bits = pbits;
+
+    if ((ret = dh_paramgen_check(ctx)) <= 0)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.keymgmt.genctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
+                                 EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, pbits,
+                                 NULL);
+    *p++ = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_FFC_PBITS, &bits);
+    *p = OSSL_PARAM_construct_end();
+    return EVP_PKEY_CTX_set_params(ctx, params);
+}
+
+int EVP_PKEY_CTX_set_dh_paramgen_subprime_len(EVP_PKEY_CTX *ctx, int qbits)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+    size_t bits2 = qbits;
+
+    if ((ret = dh_paramgen_check(ctx)) <= 0)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.keymgmt.genctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
+                                 EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN, qbits,
+                                 NULL);
+    *p++ = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_FFC_QBITS, &bits2);
+    *p = OSSL_PARAM_construct_end();
+
+    return EVP_PKEY_CTX_set_params(ctx, params);
+}
+
+int EVP_PKEY_CTX_set_dh_paramgen_generator(EVP_PKEY_CTX *ctx, int gen)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+
+    if ((ret = dh_paramgen_check(ctx)) <= 0)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.keymgmt.genctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
+                                 EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR, gen, NULL);
+    *p++ = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_DH_GENERATOR, &gen);
+    *p = OSSL_PARAM_construct_end();
+
+    return EVP_PKEY_CTX_set_params(ctx, params);
+}
+
+int EVP_PKEY_CTX_set_dh_rfc5114(EVP_PKEY_CTX *ctx, int gen)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+    const char *name;
+
+    if ((ret = dh_paramgen_check(ctx)) <= 0)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.keymgmt.genctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_PARAMGEN,
+                                 EVP_PKEY_CTRL_DH_RFC5114, gen, NULL);
+    name = ffc_named_group_from_uid(gen);
+    if (name == NULL)
+        return 0;
+
+    *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);
+}
+
+int EVP_PKEY_CTX_set_dhx_rfc5114(EVP_PKEY_CTX *ctx, int gen)
+{
+    return EVP_PKEY_CTX_set_dh_rfc5114(ctx, gen);
+}
+
+int EVP_PKEY_CTX_set_dh_nid(EVP_PKEY_CTX *ctx, int nid)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+    const char *name;
+
+    if ((ret = dh_paramgen_check(ctx)) <= 0)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.keymgmt.genctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH,
+                                 EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN,
+                                 EVP_PKEY_CTRL_DH_NID, nid, NULL);
+    name = ffc_named_group_from_uid(nid);
+    if (name == NULL)
+        return 0;
+
+    *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);
+}
+
+int EVP_PKEY_CTX_set_dh_kdf_type(EVP_PKEY_CTX *ctx, int kdf)
+{
+    int ret;
+    const char *kdf_type;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_DH_KDF_TYPE, kdf, NULL);
+    switch (kdf) {
+        case EVP_PKEY_DH_KDF_NONE:
+            kdf_type = "";
+            break;
+        case EVP_PKEY_DH_KDF_X9_42:
+            kdf_type = OSSL_KDF_NAME_X942KDF;
+            break;
+        default:
+            return -2;
+    }
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
+                                            /*
+                                             * Cast away the const. This is read
+                                             * only so should be safe
+                                             */
+                                            (char *)kdf_type, 0);
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+
+    return ret;
+}
+
+int EVP_PKEY_CTX_get_dh_kdf_type(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    char kdf_type[80]; /* 80 should be big enough */
+    OSSL_PARAM params[2], *p = params;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_DH_KDF_TYPE, -2, NULL);
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
+                                            kdf_type, sizeof(kdf_type));
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    if (kdf_type[0] == '\0')
+        return EVP_PKEY_DH_KDF_NONE;
+    else if (strcmp(kdf_type, OSSL_KDF_NAME_X942KDF) == 0)
+        return EVP_PKEY_DH_KDF_X9_42;
+
+    return -1;
+}
+
+int EVP_PKEY_CTX_set0_dh_kdf_oid(EVP_PKEY_CTX *ctx, ASN1_OBJECT *oid)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+    const char *oid_name;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_DH_KDF_OID, 0, (void *)(oid));
+    oid_name = OBJ_nid2sn(OBJ_obj2nid(oid));
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CEK_ALG,
+                                            (char *)oid_name, 0);
+    *p = OSSL_PARAM_construct_end();
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+
+    return ret;
+}
+
+int EVP_PKEY_CTX_get0_dh_kdf_oid(EVP_PKEY_CTX *ctx, ASN1_OBJECT **oid)
+{
+    int ret, nid;
+    OSSL_PARAM params[2], *p = params;
+    char oid_name[80]; /* 80 should be big enough */
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_GET_DH_KDF_OID, 0, (void *)(oid));
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CEK_ALG,
+                                            oid_name, sizeof(oid_name));
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+    nid = OBJ_sn2nid(oid_name);
+    if (nid == NID_undef)
+        nid = OBJ_ln2nid(oid_name);
+    *oid = (nid == NID_undef ? NULL : OBJ_nid2obj(nid));
+    return *oid != NULL;
+}
+
+int EVP_PKEY_CTX_set_dh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+    const char *md_name = NULL;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_DH_KDF_MD, 0, (void *)(md));
+    md_name = (md == NULL) ? "" : EVP_MD_name(md);
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
+                                            /*
+                                             * Cast away the const. This is read
+                                             * only so should be safe
+                                             */
+                                            (char *)md_name, 0);
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+    return ret;
+}
+
+int EVP_PKEY_CTX_get_dh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD **pmd)
+{
+    int ret;
+    char name[80] = "";  /* 80 should be big enough */
+    OSSL_PARAM params[2], *p = params;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_GET_DH_KDF_MD, 0, (void *)(pmd));
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
+                                            name, sizeof(name));
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    /* May be NULL meaning "unknown" */
+    *pmd = EVP_get_digestbyname(name);
+
+    return 1;
+}
+
+int EVP_PKEY_CTX_set_dh_kdf_outlen(EVP_PKEY_CTX *ctx, int inlen)
+{
+    int ret;
+    size_t len = inlen;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_DH_KDF_OUTLEN, inlen, NULL);
+    if (inlen <= 0) {
+        /*
+         * This would ideally be -1 or 0, but we have to retain compatibility
+         * with legacy behaviour of EVP_PKEY_CTX_ctrl() which returned -2 if
+         * in <= 0
+         */
+        return -2;
+    }
+
+    *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
+                                       &len);
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+    return ret;
+}
+
+int EVP_PKEY_CTX_get_dh_kdf_outlen(EVP_PKEY_CTX *ctx, int *plen)
+{
+    int ret;
+    size_t len = UINT_MAX;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN, 0,
+                                 (void *)(plen));
+    *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
+                                       &len);
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    if (len > INT_MAX)
+        return -1;
+
+    *plen = (int)len;
+
+    return 1;
+}
+
+int EVP_PKEY_CTX_set0_dh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int len)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_DH_KDF_UKM, len, (void *)(ukm));
+
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM,
+                                            /*
+                                             * Cast away the const. This is read
+                                             * only so should be safe
+                                             */
+                                            (void *)ukm,
+                                            (size_t)len);
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+    if (ret == 1)
+        OPENSSL_free(ukm);
+    return ret;
+}
+
+int EVP_PKEY_CTX_get0_dh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char **pukm)
+{
+    int ret;
+    size_t ukmlen;
+    OSSL_PARAM params[3], *p = params;
+
+    ret = dh_param_derive_check(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_GET_DH_KDF_UKM, 0, (void *)(pukm));
+
+    *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_EXCHANGE_PARAM_KDF_UKM,
+                                          (void **)pukm, 0);
+    *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_UKM_LEN,
+                                       &ukmlen);
+    *p = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    if (ukmlen > INT_MAX)
+        return -1;
+
+    return (int)ukmlen;
+}
diff --git a/crypto/dh/dh_kdf.c b/crypto/dh/dh_kdf.c
index 1b8a320db1..0b1e5881c3 100644
--- a/crypto/dh/dh_kdf.c
+++ b/crypto/dh/dh_kdf.c
@@ -14,40 +14,34 @@
 #include "internal/deprecated.h"
 
 #include "e_os.h"
+#include "e_os.h"
+#include <string.h>
+#include <openssl/core_names.h>
+#include <openssl/dh.h>
+#include <openssl/evp.h>
+#include <openssl/asn1.h>
+#include <openssl/kdf.h>
+#include <internal/provider.h>
+#include <crypto/dh.h>
 
-#ifndef OPENSSL_NO_CMS
-# include <string.h>
-# include <openssl/core_names.h>
-# include <openssl/dh.h>
-# include <openssl/evp.h>
-# include <openssl/asn1.h>
-# include <openssl/kdf.h>
-# include <internal/provider.h>
-
-int DH_KDF_X9_42(unsigned char *out, size_t outlen,
-                 const unsigned char *Z, size_t Zlen,
-                 ASN1_OBJECT *key_oid,
-                 const unsigned char *ukm, size_t ukmlen, const EVP_MD *md)
+/* Key derivation function from X9.63/SECG */
+int dh_KDF_X9_42_asn1(unsigned char *out, size_t outlen,
+                      const unsigned char *Z, size_t Zlen,
+                      const char *cek_alg,
+                      const unsigned char *ukm, size_t ukmlen, const EVP_MD *md,
+                      OPENSSL_CTX *libctx, const char *propq)
 {
-    int ret = 0, nid;
+    int ret = 0;
     EVP_KDF_CTX *kctx = NULL;
     EVP_KDF *kdf = NULL;
-    const char *oid_sn;
     OSSL_PARAM params[5], *p = params;
     const char *mdname = EVP_MD_name(md);
-    const OSSL_PROVIDER *prov = EVP_MD_provider(md);
-    OPENSSL_CTX *provctx = ossl_provider_library_context(prov);
 
-    nid = OBJ_obj2nid(key_oid);
-    if (nid == NID_undef)
-        return 0;
-    oid_sn = OBJ_nid2sn(nid);
-    if (oid_sn == NULL)
-        return 0;
-
-    kdf = EVP_KDF_fetch(provctx, OSSL_KDF_NAME_X942KDF, NULL);
-    if ((kctx = EVP_KDF_CTX_new(kdf)) == NULL)
+    kdf = EVP_KDF_fetch(libctx, OSSL_KDF_NAME_X942KDF, propq);
+    kctx = EVP_KDF_CTX_new(kdf);
+    if (kctx == NULL)
         goto err;
+
     *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
                                             (char *)mdname, 0);
     *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
@@ -56,13 +50,35 @@ int DH_KDF_X9_42(unsigned char *out, size_t outlen,
         *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_UKM,
                                                  (unsigned char *)ukm, ukmlen);
     *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CEK_ALG,
-                                            (char *)oid_sn, 0);
+                                            (char *)cek_alg, 0);
     *p = OSSL_PARAM_construct_end();
     ret = EVP_KDF_CTX_set_params(kctx, params) > 0
-        && EVP_KDF_derive(kctx, out, outlen) > 0;
+          && EVP_KDF_derive(kctx, out, outlen) > 0;
 err:
     EVP_KDF_CTX_free(kctx);
     EVP_KDF_free(kdf);
     return ret;
 }
-#endif /* OPENSSL_NO_CMS */
+
+#if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_CMS)
+int DH_KDF_X9_42(unsigned char *out, size_t outlen,
+                 const unsigned char *Z, size_t Zlen,
+                 ASN1_OBJECT *key_oid,
+                 const unsigned char *ukm, size_t ukmlen, const EVP_MD *md)
+{
+    int nid;
+    const char *key_alg = NULL;
+    const OSSL_PROVIDER *prov = EVP_MD_provider(md);
+    OPENSSL_CTX *libctx = ossl_provider_library_context(prov);
+
+    nid = OBJ_obj2nid(key_oid);
+    if (nid == NID_undef)
+        return 0;
+    key_alg = OBJ_nid2sn(nid);
+    if (key_alg == NULL)
+        return 0;
+
+    return dh_KDF_X9_42_asn1(out, outlen, Z, Zlen, key_alg,
+                             ukm, ukmlen, md, libctx, NULL);
+}
+#endif /* !defined(FIPS_MODULE) && !defined(OPENSSL_NO_CMS) */
diff --git a/crypto/dh/dh_lib.c b/crypto/dh/dh_lib.c
index 2a3921a137..a6f4e64137 100644
--- a/crypto/dh/dh_lib.c
+++ b/crypto/dh/dh_lib.c
@@ -344,195 +344,3 @@ int dh_ffc_params_fromdata(DH *dh, const OSSL_PARAM params[])
     }
     return ret;
 }
-
-static int dh_paramgen_check(EVP_PKEY_CTX *ctx)
-{
-    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 key type not DH return error */
-    if (ctx->pmeth != NULL
-        && ctx->pmeth->pkey_id != EVP_PKEY_DH
-        && ctx->pmeth->pkey_id != EVP_PKEY_DHX)
-        return -1;
-    return 1;
-}
-
-int EVP_PKEY_CTX_set_dh_paramgen_gindex(EVP_PKEY_CTX *ctx, int gindex)
-{
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-    *p++ = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_FFC_GINDEX, &gindex);
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, params);
-}
-
-int EVP_PKEY_CTX_set_dh_paramgen_seed(EVP_PKEY_CTX *ctx,
-                                      const unsigned char *seed,
-                                      size_t seedlen)
-{
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-    *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_FFC_SEED,
-                                             (void *)seed, seedlen);
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, params);
-}
-
-int EVP_PKEY_CTX_set_dh_paramgen_type(EVP_PKEY_CTX *ctx, int typ)
-{
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *name;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-#if !defined(FIPS_MODULE)
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_PARAMGEN_TYPE, typ, NULL);
-#endif
-
-    name = dh_gen_type_id2name(typ);
-    if (name == NULL)
-        return 0;
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE,
-                                            (char *) name, 0);
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, params);
-}
-
-int EVP_PKEY_CTX_set_dh_paramgen_prime_len(EVP_PKEY_CTX *ctx, int pbits)
-{
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    size_t bits = pbits;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-#if !defined(FIPS_MODULE)
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, pbits,
-                                 NULL);
-#endif
-    *p++ = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_FFC_PBITS, &bits);
-    *p++ = OSSL_PARAM_construct_end();
-    return EVP_PKEY_CTX_set_params(ctx, params);
-}
-
-int EVP_PKEY_CTX_set_dh_paramgen_subprime_len(EVP_PKEY_CTX *ctx, int qbits)
-{
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    size_t bits2 = qbits;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-#if !defined(FIPS_MODULE)
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN, qbits,
-                                 NULL);
-#endif
-    *p++ = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_FFC_QBITS, &bits2);
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, params);
-}
-
-int EVP_PKEY_CTX_set_dh_paramgen_generator(EVP_PKEY_CTX *ctx, int gen)
-{
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-#if !defined(FIPS_MODULE)
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR, gen, NULL);
-#endif
-
-    *p++ = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_DH_GENERATOR, &gen);
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, params);
-}
-
-int EVP_PKEY_CTX_set_dh_rfc5114(EVP_PKEY_CTX *ctx, int gen)
-{
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *name;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-#if !defined(FIPS_MODULE)
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_RFC5114, gen, NULL);
-#endif
-    name = ffc_named_group_from_uid(gen);
-    if (name == NULL)
-        return 0;
-
-    *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);
-}
-
-int EVP_PKEY_CTX_set_dhx_rfc5114(EVP_PKEY_CTX *ctx, int gen)
-{
-    return EVP_PKEY_CTX_set_dh_rfc5114(ctx, gen);
-}
-
-int EVP_PKEY_CTX_set_dh_nid(EVP_PKEY_CTX *ctx, int nid)
-{
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *name;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-#if !defined(FIPS_MODULE)
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        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
-    name = ffc_named_group_from_uid(nid);
-    if (name == NULL)
-        return 0;
-
-    *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/evp/p_lib.c b/crypto/evp/p_lib.c
index 2563cd97ca..b057669a2d 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -1001,6 +1001,8 @@ int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name)
 #ifndef OPENSSL_NO_DH
         else if (strcasecmp(name, "DH") == 0)
             type = EVP_PKEY_DH;
+        else if (strcasecmp(name, "X9.42 DH") == 0)
+            type = EVP_PKEY_DHX;
 #endif
 #ifndef OPENSSL_NO_DSA
         else if (strcasecmp(name, "DSA") == 0)
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index 6f2f52912b..ba85a8c143 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -150,7 +150,6 @@ static int is_legacy_alg(int id, const char *keytype)
      * support
      */
     case EVP_PKEY_SM2:
-    case EVP_PKEY_DHX:
     case EVP_PKEY_CMAC:
     case EVP_PKEY_HMAC:
     case EVP_PKEY_SIPHASH:
@@ -1040,6 +1039,28 @@ static int legacy_ctrl_to_param(EVP_PKEY_CTX *ctx, int keytype, int optype,
         return -2;
 
 # ifndef OPENSSL_NO_DH
+    if (keytype == EVP_PKEY_DHX) {
+        switch (cmd) {
+        case EVP_PKEY_CTRL_DH_KDF_TYPE:
+            return EVP_PKEY_CTX_set_dh_kdf_type(ctx, p1);
+        case EVP_PKEY_CTRL_DH_KDF_MD:
+            return EVP_PKEY_CTX_set_dh_kdf_md(ctx, p2);
+        case EVP_PKEY_CTRL_DH_KDF_OUTLEN:
+            return EVP_PKEY_CTX_set_dh_kdf_outlen(ctx, p1);
+        case EVP_PKEY_CTRL_DH_KDF_UKM:
+            return EVP_PKEY_CTX_set0_dh_kdf_ukm(ctx, p2, p1);
+        case EVP_PKEY_CTRL_DH_KDF_OID:
+            return EVP_PKEY_CTX_set0_dh_kdf_oid(ctx, p2);
+        case EVP_PKEY_CTRL_GET_DH_KDF_MD:
+            return EVP_PKEY_CTX_get_dh_kdf_md(ctx, p2);
+        case EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN:
+            return EVP_PKEY_CTX_get_dh_kdf_outlen(ctx, p2);
+        case EVP_PKEY_CTRL_GET_DH_KDF_UKM:
+            return EVP_PKEY_CTX_get0_dh_kdf_ukm(ctx, p2);
+        case EVP_PKEY_CTRL_GET_DH_KDF_OID:
+            return EVP_PKEY_CTX_get0_dh_kdf_oid(ctx, p2);
+        }
+    }
     if (keytype == EVP_PKEY_DH) {
         switch (cmd) {
             case EVP_PKEY_CTRL_DH_PAD:
diff --git a/doc/man3/EVP_PKEY_CTX_ctrl.pod b/doc/man3/EVP_PKEY_CTX_ctrl.pod
index bd98a2f1d7..9e6b6de443 100644
--- a/doc/man3/EVP_PKEY_CTX_ctrl.pod
+++ b/doc/man3/EVP_PKEY_CTX_ctrl.pod
@@ -369,41 +369,39 @@ negotiated protocol version. Otherwise it should be left unset.
 
 =head2 DSA parameters
 
-The EVP_PKEY_CTX_set_dsa_paramgen_bits() method sets the number of bits used
-for DSA parameter generation to I<nbits>. If not specified, 2048 is used.
+EVP_PKEY_CTX_set_dsa_paramgen_bits() sets the number of bits used for DSA
+parameter generation to B<nbits>. If not specified, 2048 is used.
 
-The EVP_PKEY_CTX_set_dsa_paramgen_q_bits() method sets the number of bits in the
-subprime parameter I<q> for DSA parameter generation to I<qbits>. If not
-specified, 224 is used. If a digest function is specified below, this parameter
-is ignored and instead, the number of bits in I<q> matches the size of the
-digest.
+EVP_PKEY_CTX_set_dsa_paramgen_q_bits() sets the number of bits in the subprime
+parameter I<q> for DSA parameter generation to I<qbits>. If not specified, 224
+is used. If a digest function is specified below, this parameter is ignored and
+instead, the number of bits in I<q> matches the size of the digest.
 
-The EVP_PKEY_CTX_set_dsa_paramgen_md() method sets the digest function used for
-DSA parameter generation to I<md>. If not specified, one of SHA-1, SHA-224, or
+EVP_PKEY_CTX_set_dsa_paramgen_md() sets the digest function used for DSA
+parameter generation to I<md>. If not specified, one of SHA-1, SHA-224, or
 SHA-256 is selected to match the bit length of I<q> above.
 
-The EVP_PKEY_CTX_set_dsa_paramgen_md_props() method sets the digest function
-used for DSA parameter generation using I<md_name> and I<md_properties> to
-retrieve the digest from a provider.
+EVP_PKEY_CTX_set_dsa_paramgen_md_props() sets the digest function used for DSA
+parameter generation using I<md_name> and I<md_properties> to retrieve the
+digest from a provider.
 If not specified, I<md_name> will be set to one of SHA-1, SHA-224, or
 SHA-256 depending on the bit length of I<q> above. I<md_properties> is a
 property query string that has a default value of '' if not specified.
 
-The EVP_PKEY_CTX_set_dsa_paramgen_gindex() method sets the I<gindex> used by
-the generator G. The default value is -1 which uses unverifiable g, otherwise
-a positive value uses verifiable g. This value must be saved if key validation
-of g is required, since it is not part of a persisted key.
+EVP_PKEY_CTX_set_dsa_paramgen_gindex() sets the I<gindex> used by the generator
+G. The default value is -1 which uses unverifiable g, otherwise a positive value
+uses verifiable g. This value must be saved if key validation of g is required,
+since it is not part of a persisted key.
 
-The EVP_PKEY_CTX_set_dsa_paramgen_seed() method sets the I<seed> to use for
-generation rather than using a randomly generated value for the seed. This is
-useful for testing purposes only and can fail if the seed does not produce
-primes for both p & q on its first iteration. This value must be saved if
-key validation of p, q, and verifiable g are required, since it is not part of
-a persisted key.
+EVP_PKEY_CTX_set_dsa_paramgen_seed() sets the I<seed> to use for generation
+rather than using a randomly generated value for the seed. This is useful for
+testing purposes only and can fail if the seed does not produce primes for both
+p & q on its first iteration. This value must be saved if key validation of
+p, q, and verifiable g are required, since it is not part of a persisted key.
 
-The EVP_PKEY_CTX_set_dsa_paramgen_type() method sets the generation type to
-use FIPS186-4 generation if I<name> is "fips186_4", or FIPS186-2 generation if
-I<name> is "fips186_2". The default value is "fips186_4".
+EVP_PKEY_CTX_set_dsa_paramgen_type() sets the generation type to use FIPS186-4
+generation if I<name> is "fips186_4", or FIPS186-2 generation if I<name> is
+"fips186_2". The default value is "fips186_4".
 
 =head2 DH parameters
 
@@ -445,17 +443,16 @@ Uses a safe prime generator g (PKCS#3 format).
 
 The default is B<DH_PARAMGEN_TYPE_GENERATOR>.
 
-The EVP_PKEY_CTX_set_dh_paramgen_gindex() method sets the I<gindex> used by
-the generator G. The default value is -1 which uses unverifiable g, otherwise
-a positive value uses verifiable g. This value must be saved if key validation
-of g is required, since it is not part of a persisted key.
+EVP_PKEY_CTX_set_dh_paramgen_gindex() sets the I<gindex> used by the generator G.
+The default value is -1 which uses unverifiable g, otherwise a positive value
+uses verifiable g. This value must be saved if key validation of g is required,
+since it is not part of a persisted key.
 
-The EVP_PKEY_CTX_set_dh_paramgen_seed() method sets the I<seed> to use for
-generation rather than using a randomly generated value for the seed. This is
-useful for testing purposes only and can fail if the seed does not produce
-primes for both p & q on its first iteration. This value must be saved if
-key validation of p, q, and verifiable g are required, since it is not part of
-a persisted key.
+EVP_PKEY_CTX_set_dh_paramgen_seed() sets the I<seed> to use for generation
+rather than using a randomly generated value for the seed. This is useful for
+testing purposes only and can fail if the seed does not produce primes for both
+p & q on its first iteration. This value must be saved if key validation of p, q,
+and verifiable g are required, since it is not part of a persisted key.
 
 The EVP_PKEY_CTX_set_dh_pad() function sets the DH padding mode.
 If I<pad> is 1 the shared secret is padded with zeros up to the size of the DH
@@ -486,47 +483,47 @@ EVP_PKEY_derive() is the output of the KDF instead of the DH shared secret.
 The KDF output is typically used as a Key Encryption Key (KEK) that in turn
 encrypts a Content Encryption Key (CEK).
 
-The EVP_PKEY_CTX_set_dh_kdf_type() macro sets the key derivation function type
-to I<kdf> for DH key derivation. Possible values are B<EVP_PKEY_DH_KDF_NONE>
-and B<EVP_PKEY_DH_KDF_X9_42> which uses the key derivation specified in RFC2631
+EVP_PKEY_CTX_set_dh_kdf_type() sets the key derivation function type to I<kdf>
+for DH key derivation. Possible values are B<EVP_PKEY_DH_KDF_NONE> and
+B<EVP_PKEY_DH_KDF_X9_42> which uses the key derivation specified in RFC2631
 (based on the keying algorithm described in X9.42). When using key derivation,
 the I<kdf_oid>, I<kdf_md> and I<kdf_outlen> parameters must also be specified.
 
-The EVP_PKEY_CTX_get_dh_kdf_type() macro gets the key derivation function type
-for I<ctx> used for DH key derivation. Possible values are B<EVP_PKEY_DH_KDF_NONE>
-and B<EVP_PKEY_DH_KDF_X9_42>.
+EVP_PKEY_CTX_get_dh_kdf_type() gets the key derivation function type for I<ctx>
+used for DH key derivation. Possible values are B<EVP_PKEY_DH_KDF_NONE> and
+B<EVP_PKEY_DH_KDF_X9_42>.
 
-The EVP_PKEY_CTX_set0_dh_kdf_oid() macro sets the key derivation function
-object identifier to I<oid> for DH key derivation. This OID should identify
-the algorithm to be used with the Content Encryption Key.
+EVP_PKEY_CTX_set0_dh_kdf_oid() sets the key derivation function object
+identifier to I<oid> for DH key derivation. This OID should identify the
+algorithm to be used with the Content Encryption Key.
 The library takes ownership of the object identifier so the caller should not
 free the original memory pointed to by I<oid>.
 
-The EVP_PKEY_CTX_get0_dh_kdf_oid() macro gets the key derivation function oid
-for I<ctx> used for DH key derivation. The resulting pointer is owned by the
-library and should not be freed by the caller.
+EVP_PKEY_CTX_get0_dh_kdf_oid() gets the key derivation function oid for I<ctx>
+used for DH key derivation. The resulting pointer is owned by the library and
+should not be freed by the caller.
 
-The EVP_PKEY_CTX_set_dh_kdf_md() macro sets the key derivation function
-message digest to I<md> for DH key derivation. Note that RFC2631 specifies
-that this digest should be SHA1 but OpenSSL tolerates other digests.
+EVP_PKEY_CTX_set_dh_kdf_md() sets the key derivation function message digest to
+I<md> for DH key derivation. Note that RFC2631 specifies that this digest should
+be SHA1 but OpenSSL tolerates other digests.
 
-The EVP_PKEY_CTX_get_dh_kdf_md() macro gets the key derivation function
-message digest for I<ctx> used for DH key derivation.
+EVP_PKEY_CTX_get_dh_kdf_md() gets the key derivation function message digest for
+I<ctx> used for DH key derivation.
 
-The EVP_PKEY_CTX_set_dh_kdf_outlen() macro sets the key derivation function
-output length to I<len> for DH key derivation.
+EVP_PKEY_CTX_set_dh_kdf_outlen() sets the key derivation function output length
+to I<len> for DH key derivation.
 
-The EVP_PKEY_CTX_get_dh_kdf_outlen() macro gets the key derivation function
-output length for I<ctx> used for DH key derivation.
+EVP_PKEY_CTX_get_dh_kdf_outlen() gets the key derivation function output length
+for I<ctx> used for DH key derivation.
 
-The EVP_PKEY_CTX_set0_dh_kdf_ukm() macro sets the user key material to
-I<ukm> and its length to I<len> for DH key derivation. This parameter is optional
-and corresponds to the partyAInfo field in RFC2631 terms. The specification
+EVP_PKEY_CTX_set0_dh_kdf_ukm() sets the user key material to I<ukm> and its
+length to I<len> for DH key derivation. This parameter is optional and
+corresponds to the partyAInfo field in RFC2631 terms. The specification
 requires that it is 512 bits long but this is not enforced by OpenSSL.
 The library takes ownership of the user key material so the caller should not
 free the original memory pointed to by I<ukm>.
 
-The EVP_PKEY_CTX_get0_dh_kdf_ukm() macro gets the user key material for I<ctx>.
+EVP_PKEY_CTX_get0_dh_kdf_ukm() gets the user key material for I<ctx>.
 The return value is the user key material length. The resulting pointer is owned
 by the library and should not be freed by the caller.
 
@@ -641,7 +638,12 @@ EVP_PKEY_CTX_set_rsa_mgf1_md(), EVP_PKEY_CTX_set_rsa_oaep_md(),
 EVP_PKEY_CTX_get_rsa_oaep_md(), EVP_PKEY_CTX_set0_rsa_oaep_label(),
 EVP_PKEY_CTX_get0_rsa_oaep_label(), EVP_PKEY_CTX_set_rsa_pss_saltlen(),
 EVP_PKEY_CTX_get_rsa_pss_saltlen(), EVP_PKEY_CTX_set_dsa_paramgen_bits(),
-EVP_PKEY_CTX_set_dsa_paramgen_q_bits() and EVP_PKEY_CTX_set_dsa_paramgen_md()
+EVP_PKEY_CTX_set_dsa_paramgen_q_bits(), EVP_PKEY_CTX_set_dsa_paramgen_md().
+EVP_PKEY_CTX_set_dh_kdf_type(), EVP_PKEY_CTX_get_dh_kdf_type(),
+EVP_PKEY_CTX_set0_dh_kdf_oid(), EVP_PKEY_CTX_get0_dh_kdf_oid(),
+EVP_PKEY_CTX_set_dh_kdf_md(), EVP_PKEY_CTX_get_dh_kdf_md(),
+EVP_PKEY_CTX_set_dh_kdf_outlen(), EVP_PKEY_CTX_get_dh_kdf_outlen(),
+EVP_PKEY_CTX_set0_dh_kdf_ukm() and EVP_PKEY_CTX_get0_dh_kdf_ukm()
 were macros in OpenSSL 1.1.1 and below.
 From OpenSSL 3.0 they are functions.
 
diff --git a/include/crypto/dh.h b/include/crypto/dh.h
index 22847dd1e0..fa0d70dbd4 100644
--- a/include/crypto/dh.h
+++ b/include/crypto/dh.h
@@ -35,3 +35,9 @@ const DH_METHOD *dh_get_method(const DH *dh);
 
 int dh_buf2key(DH *key, const unsigned char *buf, size_t len);
 size_t dh_key2buf(const DH *dh, unsigned char **pbuf, size_t size, int alloc);
+
+int dh_KDF_X9_42_asn1(unsigned char *out, size_t outlen,
+                      const unsigned char *Z, size_t Zlen,
+                      const char *cek_alg,
+                      const unsigned char *ukm, size_t ukmlen, const EVP_MD *md,
+                      OPENSSL_CTX *libctx, const char *propq);
diff --git a/include/openssl/dh.h b/include/openssl/dh.h
index 4907bc6567..4b456cff16 100644
--- a/include/openssl/dh.h
+++ b/include/openssl/dh.h
@@ -40,6 +40,10 @@ extern "C" {
 
 #  define DH_FLAG_CACHE_MONT_P     0x01
 
+#  define DH_FLAG_TYPE_MASK             0xF000
+#  define DH_FLAG_TYPE_DH               0x0000
+#  define DH_FLAG_TYPE_DHX              0x1000
+
 #  ifndef OPENSSL_NO_DEPRECATED_1_1_0
 /*
  * Does nothing. Previously this switched off constant time behaviour.
@@ -282,45 +286,16 @@ int EVP_PKEY_CTX_set_dh_rfc5114(EVP_PKEY_CTX *ctx, int gen);
 int EVP_PKEY_CTX_set_dhx_rfc5114(EVP_PKEY_CTX *ctx, int gen);
 int EVP_PKEY_CTX_set_dh_pad(EVP_PKEY_CTX *ctx, int pad);
 
-#  define EVP_PKEY_CTX_set_dh_kdf_type(ctx, kdf) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_DH_KDF_TYPE, kdf, NULL)
-
-#  define EVP_PKEY_CTX_get_dh_kdf_type(ctx) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_DH_KDF_TYPE, -2, NULL)
-
-#  define EVP_PKEY_CTX_set0_dh_kdf_oid(ctx, oid) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_DH_KDF_OID, 0, (void *)(oid))
-
-#  define EVP_PKEY_CTX_get0_dh_kdf_oid(ctx, poid) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX,  EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_GET_DH_KDF_OID, 0, (void *)(poid))
-
-#  define EVP_PKEY_CTX_set_dh_kdf_md(ctx, md) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_DH_KDF_MD, 0, (void *)(md))
-
-#  define EVP_PKEY_CTX_get_dh_kdf_md(ctx, pmd) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_GET_DH_KDF_MD, 0, (void *)(pmd))
-
-#  define EVP_PKEY_CTX_set_dh_kdf_outlen(ctx, len) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_DH_KDF_OUTLEN, len, NULL)
-
-#  define EVP_PKEY_CTX_get_dh_kdf_outlen(ctx, plen) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN, 0, (void *)(plen))
-
-#  define EVP_PKEY_CTX_set0_dh_kdf_ukm(ctx, p, plen) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX,  EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_DH_KDF_UKM, plen, (void *)(p))
-
-#  define EVP_PKEY_CTX_get0_dh_kdf_ukm(ctx, p) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX,  EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_GET_DH_KDF_UKM, 0, (void *)(p))
+int EVP_PKEY_CTX_set_dh_kdf_type(EVP_PKEY_CTX *ctx, int kdf);
+int EVP_PKEY_CTX_get_dh_kdf_type(EVP_PKEY_CTX *ctx);
+int EVP_PKEY_CTX_set0_dh_kdf_oid(EVP_PKEY_CTX *ctx, ASN1_OBJECT *oid);
+int EVP_PKEY_CTX_get0_dh_kdf_oid(EVP_PKEY_CTX *ctx, ASN1_OBJECT **oid);
+int EVP_PKEY_CTX_set_dh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
+int EVP_PKEY_CTX_get_dh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD **md);
+int EVP_PKEY_CTX_set_dh_kdf_outlen(EVP_PKEY_CTX *ctx, int len);
+int EVP_PKEY_CTX_get_dh_kdf_outlen(EVP_PKEY_CTX *ctx, int *len);
+int EVP_PKEY_CTX_set0_dh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int len);
+int EVP_PKEY_CTX_get0_dh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char **ukm);
 
 #  define EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN     (EVP_PKEY_ALG_CTRL + 1)
 #  define EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR     (EVP_PKEY_ALG_CTRL + 2)
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 00d1800c24..c34c539726 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -374,6 +374,7 @@ static const OSSL_ALGORITHM deflt_asym_cipher[] = {
 static const OSSL_ALGORITHM deflt_keymgmt[] = {
 #ifndef OPENSSL_NO_DH
     { "DH:dhKeyAgreement", "provider=default", dh_keymgmt_functions },
+    { "DHX:X9.42 DH:dhpublicnumber", "provider=default", dhx_keymgmt_functions },
 #endif
 #ifndef OPENSSL_NO_DSA
     { "DSA:dsaEncryption", "provider=default", dsa_keymgmt_functions },
diff --git a/providers/deserializers.inc b/providers/deserializers.inc
index e11056ee9e..2e363143b8 100644
--- a/providers/deserializers.inc
+++ b/providers/deserializers.inc
@@ -13,6 +13,7 @@
 
 #ifndef OPENSSL_NO_DH
     DESER("DH", "yes", "der", der_to_dh_deserializer_functions),
+    DESER("DHX", "yes", "der", der_to_dhx_deserializer_functions),
 #endif
 #ifndef OPENSSL_NO_DSA
     DESER("DSA", "yes", "der", der_to_dsa_deserializer_functions),
diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c
index 4711a99b01..b29cae509c 100644
--- a/providers/fips/fipsprov.c
+++ b/providers/fips/fipsprov.c
@@ -447,6 +447,7 @@ static const OSSL_ALGORITHM fips_asym_cipher[] = {
 static const OSSL_ALGORITHM fips_keymgmt[] = {
 #ifndef OPENSSL_NO_DH
     { "DH:dhKeyAgreement", FIPS_DEFAULT_PROPERTIES, dh_keymgmt_functions },
+    { "DHX:X9.42 DH:dhpublicnumber", FIPS_DEFAULT_PROPERTIES, dhx_keymgmt_functions },
 #endif
 #ifndef OPENSSL_NO_DSA
     { "DSA", FIPS_DEFAULT_PROPERTIES, dsa_keymgmt_functions },
diff --git a/providers/implementations/exchange/dh_exch.c b/providers/implementations/exchange/dh_exch.c
index c0cb378c12..3c3bd4dd38 100644
--- a/providers/implementations/exchange/dh_exch.c
+++ b/providers/implementations/exchange/dh_exch.c
@@ -13,10 +13,12 @@
  */
 #include "internal/deprecated.h"
 
+#include <string.h>
 #include <openssl/crypto.h>
 #include <openssl/core_dispatch.h>
 #include <openssl/core_names.h>
 #include <openssl/dh.h>
+#include <openssl/err.h>
 #include <openssl/params.h>
 #include "prov/implementations.h"
 #include "prov/provider_ctx.h"
@@ -30,6 +32,23 @@ static OSSL_FUNC_keyexch_freectx_fn dh_freectx;
 static OSSL_FUNC_keyexch_dupctx_fn dh_dupctx;
 static OSSL_FUNC_keyexch_set_ctx_params_fn dh_set_ctx_params;
 static OSSL_FUNC_keyexch_settable_ctx_params_fn dh_settable_ctx_params;
+static OSSL_FUNC_keyexch_get_ctx_params_fn dh_get_ctx_params;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn dh_gettable_ctx_params;
+
+/*
+ * This type is only really used to handle some legacy related functionality.
+ * If you need to use other KDF's (such as SSKDF) just use PROV_DH_KDF_NONE
+ * here and then create and run a KDF after the key is derived.
+ * Note that X942 has 2 variants of key derivation:
+ *   (1) DH_KDF_X9_42_ASN1 - which contains an ANS1 encoded object that has
+ *   the counter embedded in it.
+ *   (2) DH_KDF_X941_CONCAT - which is the same as ECDH_X963_KDF (which can be
+ *       done by creating a "X963KDF".
+ */
+enum kdf_type {
+    PROV_DH_KDF_NONE = 0,
+    PROV_DH_KDF_X9_42_ASN1
+};
 
 /*
  * What's passed as an actual key is defined by the KEYMGMT interface.
@@ -42,6 +61,18 @@ typedef struct {
     DH *dh;
     DH *dhpeer;
     unsigned int pad : 1;
+
+    /* DH KDF */
+    /* KDF (if any) to use for DH */
+    enum kdf_type kdf_type;
+    /* Message digest to use for key derivation */
+    EVP_MD *kdf_md;
+    /* User key material */
+    unsigned char *kdf_ukm;
+    size_t kdf_ukmlen;
+    /* KDF output length */
+    size_t kdf_outlen;
+    char *kdf_cekalg;
 } PROV_DH_CTX;
 
 static void *dh_newctx(void *provctx)
@@ -51,6 +82,7 @@ static void *dh_newctx(void *provctx)
     if (pdhctx == NULL)
         return NULL;
     pdhctx->libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
+    pdhctx->kdf_type = PROV_DH_KDF_NONE;
     return pdhctx;
 }
 
@@ -62,6 +94,7 @@ static int dh_init(void *vpdhctx, void *vdh)
         return 0;
     DH_free(pdhctx->dh);
     pdhctx->dh = vdh;
+    pdhctx->kdf_type = PROV_DH_KDF_NONE;
     return 1;
 }
 
@@ -76,8 +109,9 @@ static int dh_set_peer(void *vpdhctx, void *vdh)
     return 1;
 }
 
-static int dh_derive(void *vpdhctx, unsigned char *secret, size_t *secretlen,
-                     size_t outlen)
+static int dh_plain_derive(void *vpdhctx,
+                           unsigned char *secret, size_t *secretlen,
+                           size_t outlen)
 {
     PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
     int ret;
@@ -108,12 +142,74 @@ static int dh_derive(void *vpdhctx, unsigned char *secret, size_t *secretlen,
     return 1;
 }
 
+static int dh_X9_42_kdf_derive(void *vpdhctx, unsigned char *secret,
+                               size_t *secretlen, size_t outlen)
+{
+    PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+    unsigned char *stmp = NULL;
+    size_t stmplen;
+    int ret = 0;
+
+    if (secret == NULL) {
+        *secretlen = pdhctx->kdf_outlen;
+        return 1;
+    }
+
+    if (pdhctx->kdf_outlen > outlen)
+        return 0;
+    if (!dh_plain_derive(pdhctx, NULL, &stmplen, 0))
+        return 0;
+    if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    if (!dh_plain_derive(pdhctx, stmp, &stmplen, stmplen))
+        goto err;
+
+    /* Do KDF stuff */
+    if (pdhctx->kdf_type == PROV_DH_KDF_X9_42_ASN1) {
+        if (!dh_KDF_X9_42_asn1(secret, pdhctx->kdf_outlen,
+                               stmp, stmplen,
+                               pdhctx->kdf_cekalg,
+                               pdhctx->kdf_ukm,
+                               pdhctx->kdf_ukmlen,
+                               pdhctx->kdf_md,
+                               pdhctx->libctx, NULL))
+            goto err;
+    }
+    *secretlen = pdhctx->kdf_outlen;
+    ret = 1;
+err:
+    OPENSSL_secure_clear_free(stmp, stmplen);
+    return ret;
+}
+
+static int dh_derive(void *vpdhctx, unsigned char *secret,
+                     size_t *psecretlen, size_t outlen)
+{
+    PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+
+    switch (pdhctx->kdf_type) {
+        case PROV_DH_KDF_NONE:
+            return dh_plain_derive(pdhctx, secret, psecretlen, outlen);
+        case PROV_DH_KDF_X9_42_ASN1:
+            return dh_X9_42_kdf_derive(pdhctx, secret, psecretlen, outlen);
+        default:
+            break;
+    }
+    return 0;
+}
+
+
 static void dh_freectx(void *vpdhctx)
 {
     PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
 
+    OPENSSL_free(pdhctx->kdf_cekalg);
     DH_free(pdhctx->dh);
     DH_free(pdhctx->dhpeer);
+    EVP_MD_free(pdhctx->kdf_md);
+    OPENSSL_clear_free(pdhctx->kdf_ukm, pdhctx->kdf_ukmlen);
 
     OPENSSL_free(pdhctx);
 }
@@ -128,18 +224,40 @@ static void *dh_dupctx(void *vpdhctx)
         return NULL;
 
     *dstctx = *srcctx;
-    if (dstctx->dh != NULL && !DH_up_ref(dstctx->dh)) {
-        OPENSSL_free(dstctx);
-        return NULL;
-    }
+    dstctx->dh = NULL;
+    dstctx->dhpeer = NULL;
+    dstctx->kdf_md = NULL;
+    dstctx->kdf_ukm = NULL;
+    dstctx->kdf_cekalg = NULL;
 
-    if (dstctx->dhpeer != NULL && !DH_up_ref(dstctx->dhpeer)) {
-        DH_free(dstctx->dh);
-        OPENSSL_free(dstctx);
-        return NULL;
+    if (dstctx->dh != NULL && !DH_up_ref(srcctx->dh))
+        goto err;
+    else
+        dstctx->dh = srcctx->dh;
+
+    if (dstctx->dhpeer != NULL && !DH_up_ref(srcctx->dhpeer))
+        goto err;
+    else
+        dstctx->dhpeer = srcctx->dhpeer;
+
+    if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md))
+        goto err;
+    else
+        dstctx->kdf_md = srcctx->kdf_md;
+
+    /* Duplicate UKM data if present */
+    if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) {
+        dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,
+                                         srcctx->kdf_ukmlen);
+        if (dstctx->kdf_ukm == NULL)
+            goto err;
     }
+    dstctx->kdf_cekalg = OPENSSL_strdup(srcctx->kdf_cekalg);
 
     return dstctx;
+err:
+    dh_freectx(dstctx);
+    return NULL;
 }
 
 static int dh_set_ctx_params(void *vpdhctx, const OSSL_PARAM params[])
@@ -147,27 +265,180 @@ static int dh_set_ctx_params(void *vpdhctx, const OSSL_PARAM params[])
     PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
     const OSSL_PARAM *p;
     unsigned int pad;
+    char name[80] = { '\0' }; /* should be big enough */
+    char *str = NULL;
 
     if (pdhctx == NULL || params == NULL)
         return 0;
 
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+    if (p != NULL) {
+        str = name;
+        if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+            return 0;
+
+        if (name[0] == '\0')
+            pdhctx->kdf_type = PROV_DH_KDF_NONE;
+        else if (strcmp(name, OSSL_KDF_NAME_X942KDF) == 0)
+            pdhctx->kdf_type = PROV_DH_KDF_X9_42_ASN1;
+        else
+            return 0;
+    }
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+    if (p != NULL) {
+        char mdprops[80] = { '\0' }; /* should be big enough */
+
+        str = name;
+        if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+            return 0;
+
+        str = mdprops;
+        p = OSSL_PARAM_locate_const(params,
+                                    OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);
+
+        if (p != NULL) {
+            if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
+                return 0;
+        }
+
+        EVP_MD_free(pdhctx->kdf_md);
+        pdhctx->kdf_md = EVP_MD_fetch(pdhctx->libctx, name, mdprops);
+
+        if (pdhctx->kdf_md == NULL)
+            return 0;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+    if (p != NULL) {
+        size_t outlen;
+
+        if (!OSSL_PARAM_get_size_t(p, &outlen))
+            return 0;
+        pdhctx->kdf_outlen = outlen;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+    if (p != NULL) {
+        void *tmp_ukm = NULL;
+        size_t tmp_ukmlen;
+
+        OPENSSL_free(pdhctx->kdf_ukm);
+        pdhctx->kdf_ukm = NULL;
+        pdhctx->kdf_ukmlen = 0;
+        /* ukm is an optional field so it can be NULL */
+        if (p->data != NULL && p->data_size != 0) {
+            if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen))
+                return 0;
+            pdhctx->kdf_ukm = tmp_ukm;
+            pdhctx->kdf_ukmlen = tmp_ukmlen;
+        }
+    }
+
     p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_PAD);
-    if (p == NULL || !OSSL_PARAM_get_uint(p, &pad))
-        return 0;
-    pdhctx->pad = pad ? 1 : 0;
+    if (p != NULL) {
+        if (!OSSL_PARAM_get_uint(p, &pad))
+            return 0;
+        pdhctx->pad = pad ? 1 : 0;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CEK_ALG);
+    if (p != NULL) {
+        str = name;
+        if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+            return 0;
+        pdhctx->kdf_cekalg = OPENSSL_strdup(name);
+    }
     return 1;
 }
 
 static const OSSL_PARAM known_settable_ctx_params[] = {
     OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_PAD, NULL),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0),
     OSSL_PARAM_END
 };
 
-static const OSSL_PARAM *dh_settable_ctx_params(void *provctx)
+static const OSSL_PARAM *dh_settable_ctx_params(ossl_unused void *provctx)
 {
     return known_settable_ctx_params;
 }
 
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+    OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+    OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR,
+                    NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_UKM_LEN, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *dh_gettable_ctx_params(ossl_unused void *provctx)
+{
+    return known_gettable_ctx_params;
+}
+
+static int dh_get_ctx_params(void *vpdhctx, OSSL_PARAM params[])
+{
+    PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+    OSSL_PARAM *p;
+
+    if (pdhctx == NULL || params == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+    if (p != NULL) {
+        const char *kdf_type = NULL;
+
+        switch (pdhctx->kdf_type) {
+            case PROV_DH_KDF_NONE:
+                kdf_type = "";
+                break;
+            case PROV_DH_KDF_X9_42_ASN1:
+                kdf_type = OSSL_KDF_NAME_X942KDF;
+                break;
+            default:
+                return 0;
+        }
+
+        if (!OSSL_PARAM_set_utf8_string(p, kdf_type))
+            return 0;
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+    if (p != NULL
+            && !OSSL_PARAM_set_utf8_string(p, pdhctx->kdf_md == NULL
+                                           ? ""
+                                           : EVP_MD_name(pdhctx->kdf_md))){
+        return 0;
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+    if (p != NULL && !OSSL_PARAM_set_size_t(p, pdhctx->kdf_outlen))
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+    if (p != NULL && !OSSL_PARAM_set_octet_ptr(p, pdhctx->kdf_ukm, 0))
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM_LEN);
+    if (p != NULL && !OSSL_PARAM_set_size_t(p, pdhctx->kdf_ukmlen))
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_CEK_ALG);
+    if (p != NULL
+            && !OSSL_PARAM_set_utf8_string(p, pdhctx->kdf_cekalg == NULL
+                                           ? "" :  pdhctx->kdf_cekalg))
+        return 0;
+
+    return 1;
+}
+
 const OSSL_DISPATCH dh_keyexch_functions[] = {
     { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))dh_newctx },
     { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))dh_init },
@@ -178,5 +449,8 @@ const OSSL_DISPATCH dh_keyexch_functions[] = {
     { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))dh_set_ctx_params },
     { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
       (void (*)(void))dh_settable_ctx_params },
+    { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))dh_get_ctx_params },
+    { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
+      (void (*)(void))dh_gettable_ctx_params },
     { 0, NULL }
 };
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index 9e3ef4d79c..76053e22b0 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -267,6 +267,7 @@ extern const OSSL_DISPATCH crngt_functions[];
 
 /* Key management */
 extern const OSSL_DISPATCH dh_keymgmt_functions[];
+extern const OSSL_DISPATCH dhx_keymgmt_functions[];
 extern const OSSL_DISPATCH dsa_keymgmt_functions[];
 extern const OSSL_DISPATCH rsa_keymgmt_functions[];
 extern const OSSL_DISPATCH rsapss_keymgmt_functions[];
@@ -364,6 +365,7 @@ extern const OSSL_DISPATCH ec_pub_pem_serializer_functions[];
 extern const OSSL_DISPATCH ec_param_pem_serializer_functions[];
 
 extern const OSSL_DISPATCH der_to_dh_deserializer_functions[];
+extern const OSSL_DISPATCH der_to_dhx_deserializer_functions[];
 extern const OSSL_DISPATCH der_to_dsa_deserializer_functions[];
 extern const OSSL_DISPATCH msblob_to_dsa_deserializer_functions[];
 extern const OSSL_DISPATCH pvk_to_dsa_deserializer_functions[];
diff --git a/providers/implementations/keymgmt/dh_kmgmt.c b/providers/implementations/keymgmt/dh_kmgmt.c
index 73fcb3fc41..0ea6ce7784 100644
--- a/providers/implementations/keymgmt/dh_kmgmt.c
+++ b/providers/implementations/keymgmt/dh_kmgmt.c
@@ -29,6 +29,7 @@
 static OSSL_FUNC_keymgmt_new_fn dh_newdata;
 static OSSL_FUNC_keymgmt_free_fn dh_freedata;
 static OSSL_FUNC_keymgmt_gen_init_fn dh_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn dhx_gen_init;
 static OSSL_FUNC_keymgmt_gen_set_template_fn dh_gen_set_template;
 static OSSL_FUNC_keymgmt_gen_set_params_fn dh_gen_set_params;
 static OSSL_FUNC_keymgmt_gen_settable_params_fn dh_gen_settable_params;
@@ -73,6 +74,7 @@ struct dh_gen_ctx {
     const char *mdprops;
     OSSL_CALLBACK *cb;
     void *cbarg;
+    int dh_type;
 };
 
 typedef struct dh_name2id_st{
@@ -131,7 +133,26 @@ static int dh_key_todata(DH *dh, OSSL_PARAM_BLD *bld, OSSL_PARAM params[])
 
 static void *dh_newdata(void *provctx)
 {
-    return dh_new_with_libctx(PROV_LIBRARY_CONTEXT_OF(provctx));
+    DH *dh = NULL;
+
+    dh = dh_new_with_libctx(PROV_LIBRARY_CONTEXT_OF(provctx));
+    if (dh != NULL) {
+        DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+        DH_set_flags(dh, DH_FLAG_TYPE_DH);
+    }
+    return dh;
+}
+
+static void *dhx_newdata(void *provctx)
+{
+    DH *dh = NULL;
+
+    dh = dh_new_with_libctx(PROV_LIBRARY_CONTEXT_OF(provctx));
+    if (dh != NULL) {
+        DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+        DH_set_flags(dh, DH_FLAG_TYPE_DHX);
+    }
+    return dh;
 }
 
 static void dh_freedata(void *keydata)
@@ -399,7 +420,7 @@ static int dh_validate(void *keydata, int selection)
     return ok;
 }
 
-static void *dh_gen_init(void *provctx, int selection)
+static void *dh_gen_init_base(void *provctx, int selection, int type)
 {
     OPENSSL_CTX *libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
     struct dh_gen_ctx *gctx = NULL;
@@ -419,10 +440,21 @@ static void *dh_gen_init(void *provctx, int selection)
         gctx->hindex = 0;
         gctx->pcounter = -1;
         gctx->generator = DH_GENERATOR_2;
+        gctx->dh_type = type;
     }
     return gctx;
 }
 
+static void *dh_gen_init(void *provctx, int selection)
+{
+    return dh_gen_init_base(provctx, selection, DH_FLAG_TYPE_DH);
+}
+
+static void *dhx_gen_init(void *provctx, int selection)
+{
+   return dh_gen_init_base(provctx, selection, DH_FLAG_TYPE_DHX);
+}
+
 static int dh_gen_set_template(void *genctx, void *templ)
 {
     struct dh_gen_ctx *gctx = genctx;
@@ -624,6 +656,9 @@ static void *dh_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
         if (DH_generate_key(dh) <= 0)
             goto end;
     }
+    DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+    DH_set_flags(dh, gctx->dh_type);
+
     ret = 1;
 end:
     if (ret <= 0) {
@@ -683,3 +718,36 @@ const OSSL_DISPATCH dh_keymgmt_functions[] = {
     { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))dh_export_types },
     { 0, NULL }
 };
+
+/* For any DH key, we use the "DH" algorithms regardless of sub-type. */
+static const char *dhx_query_operation_name(int operation_id)
+{
+    return "DH";
+}
+
+const OSSL_DISPATCH 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_SETTABLE_PARAMS,
+      (void (*)(void))dh_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 },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))dh_freedata },
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))dh_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))dh_gettable_params },
+    { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))dh_set_params },
+    { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))dh_settable_params },
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))dh_has },
+    { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))dh_match },
+    { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))dh_validate },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))dh_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))dh_import_types },
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))dh_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))dh_export_types },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+      (void (*)(void))dhx_query_operation_name },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/deserialize_der2key.c b/providers/implementations/serializers/deserialize_der2key.c
index d765ad8fab..702119b74c 100644
--- a/providers/implementations/serializers/deserialize_der2key.c
+++ b/providers/implementations/serializers/deserialize_der2key.c
@@ -221,6 +221,7 @@ static int der2key_export_object(void *vctx,
 
 #ifndef OPENSSL_NO_DH
 IMPLEMENT_NEWCTX("DH", DH, dh, EVP_PKEY_get1_DH, DH_free);
+IMPLEMENT_NEWCTX("DHX", DHX, dhx, EVP_PKEY_get1_DH, DH_free);
 #endif
 #ifndef OPENSSL_NO_DSA
 IMPLEMENT_NEWCTX("DSA", DSA, dsa, EVP_PKEY_get1_DSA, DSA_free);
diff --git a/providers/implementations/serializers/serializer_dh.c b/providers/implementations/serializers/serializer_dh.c
index 03bb874a64..d63c8402f9 100644
--- a/providers/implementations/serializers/serializer_dh.c
+++ b/providers/implementations/serializers/serializer_dh.c
@@ -149,3 +149,8 @@ int ossl_prov_dh_priv_to_der(const void *dh, unsigned char **pder)
     return ret;
 }
 
+
+int ossl_prov_dh_type_to_evp(const DH *dh)
+{
+    return DH_test_flags(dh, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH;
+}
diff --git a/providers/implementations/serializers/serializer_dh_priv.c b/providers/implementations/serializers/serializer_dh_priv.c
index fe11834c28..185f836110 100644
--- a/providers/implementations/serializers/serializer_dh_priv.c
+++ b/providers/implementations/serializers/serializer_dh_priv.c
@@ -153,7 +153,8 @@ static int dh_priv_der(void *vctx, void *dh, OSSL_CORE_BIO *cout,
     ctx->sc.cb = cb;
     ctx->sc.cbarg = cbarg;
 
-    ret = ossl_prov_write_priv_der_from_obj(out, dh, EVP_PKEY_DH,
+    ret = ossl_prov_write_priv_der_from_obj(out, dh,
+                                            ossl_prov_dh_type_to_evp(dh),
                                             ossl_prov_prepare_dh_params,
                                             ossl_prov_dh_priv_to_der,
                                             &ctx->sc);
@@ -198,7 +199,8 @@ static int dh_pem_priv(void *vctx, void *dh, OSSL_CORE_BIO *cout,
     ctx->sc.cb = cb;
     ctx->sc.cbarg = cbarg;
 
-    ret = ossl_prov_write_priv_pem_from_obj(out, dh, EVP_PKEY_DH,
+    ret = ossl_prov_write_priv_pem_from_obj(out, dh,
+                                            ossl_prov_dh_type_to_evp(dh),
                                             ossl_prov_prepare_dh_params,
                                             ossl_prov_dh_priv_to_der,
                                             &ctx->sc);
diff --git a/providers/implementations/serializers/serializer_dh_pub.c b/providers/implementations/serializers/serializer_dh_pub.c
index 6ca79c9444..b79f1df216 100644
--- a/providers/implementations/serializers/serializer_dh_pub.c
+++ b/providers/implementations/serializers/serializer_dh_pub.c
@@ -80,7 +80,8 @@ static int dh_pub_der(void *ctx, void *dh, OSSL_CORE_BIO *cout,
     if (out == NULL)
         return 0;
 
-    ret = ossl_prov_write_pub_der_from_obj(out, dh, EVP_PKEY_DH,
+    ret = ossl_prov_write_pub_der_from_obj(out, dh,
+                                           ossl_prov_dh_type_to_evp(dh),
                                            ossl_prov_prepare_dh_params,
                                            ossl_prov_dh_pub_to_der);
     BIO_free(out);
@@ -120,7 +121,8 @@ static int dh_pub_pem(void *ctx, void *dh, OSSL_CORE_BIO *cout,
     if (out == NULL)
         return 0;
 
-    ret = ossl_prov_write_pub_pem_from_obj(out, dh, EVP_PKEY_DH,
+    ret = ossl_prov_write_pub_pem_from_obj(out, dh,
+                                           ossl_prov_dh_type_to_evp(dh),
                                            ossl_prov_prepare_dh_params,
                                            ossl_prov_dh_pub_to_der);
     BIO_free(out);
diff --git a/providers/implementations/serializers/serializer_local.h b/providers/implementations/serializers/serializer_local.h
index 49ec8882c6..14c6073246 100644
--- a/providers/implementations/serializers/serializer_local.h
+++ b/providers/implementations/serializers/serializer_local.h
@@ -64,6 +64,7 @@ int ossl_prov_prepare_dh_params(const void *dh, int nid,
                                 void **pstr, int *pstrtype);
 int ossl_prov_dh_pub_to_der(const void *dh, unsigned char **pder);
 int ossl_prov_dh_priv_to_der(const void *dh, unsigned char **pder);
+int ossl_prov_dh_type_to_evp(const DH *dh);
 
 #ifndef OPENSSL_NO_EC
 void ecx_get_new_free_import(ECX_KEY_TYPE type,
diff --git a/providers/serializers.inc b/providers/serializers.inc
index 3143ebbec5..749c1bbb67 100644
--- a/providers/serializers.inc
+++ b/providers/serializers.inc
@@ -35,6 +35,16 @@
     SER("DH", "yes", "pem", "private", dh_priv_pem_serializer_functions),
     SER("DH", "yes", "pem", "public", dh_pub_pem_serializer_functions),
     SER("DH", "yes", "pem", "parameters", dh_param_pem_serializer_functions),
+
+    SER("DHX", "yes", "text", "private", dh_priv_text_serializer_functions),
+    SER("DHX", "yes", "text", "public", dh_pub_text_serializer_functions),
+    SER("DHX", "yes", "text", "parameters", dh_param_text_serializer_functions),
+    SER("DHX", "yes", "der", "private", dh_priv_der_serializer_functions),
+    SER("DHX", "yes", "der", "public", dh_pub_der_serializer_functions),
+    SER("DHX", "yes", "der", "parameters", dh_param_der_serializer_functions),
+    SER("DHX", "yes", "pem", "private", dh_priv_pem_serializer_functions),
+    SER("DHX", "yes", "pem", "public", dh_pub_pem_serializer_functions),
+    SER("DHX", "yes", "pem", "parameters", dh_param_pem_serializer_functions),
 #endif
 
 #ifndef OPENSSL_NO_DSA
diff --git a/test/evp_libctx_test.c b/test/evp_libctx_test.c
index 7421e1e3ca..3b20dad74b 100644
--- a/test/evp_libctx_test.c
+++ b/test/evp_libctx_test.c
@@ -25,6 +25,7 @@
 #include <openssl/dsa.h>
 #include <openssl/dh.h>
 #include <openssl/safestack.h>
+#include <openssl/x509.h>
 #include "testutil.h"
 #include "internal/nelem.h"
 #include "crypto/bn_dh.h"   /* _bignum_ffdhe2048_p */
@@ -203,6 +204,92 @@ static int test_dh_safeprime_param_keygen(int tstid)
     };
     return do_dh_param_keygen(tstid, bn);
 }
+
+static int dhx_cert_load(void)
+{
+    int ret = 0;
+    X509 *cert = NULL;
+    BIO *bio = NULL;
+
+    static const unsigned char dhx_cert[] = {
+        0x30,0x82,0x03,0xff,0x30,0x82,0x02,0xe7,0xa0,0x03,0x02,0x01,0x02,0x02,0x09,0x00,
+        0xdb,0xf5,0x4d,0x22,0xa0,0x7a,0x67,0xa6,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,
+        0xf7,0x0d,0x01,0x01,0x05,0x05,0x00,0x30,0x44,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,
+        0x04,0x06,0x13,0x02,0x55,0x4b,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0a,0x0c,
+        0x0d,0x4f,0x70,0x65,0x6e,0x53,0x53,0x4c,0x20,0x47,0x72,0x6f,0x75,0x70,0x31,0x1d,
+        0x30,0x1b,0x06,0x03,0x55,0x04,0x03,0x0c,0x14,0x54,0x65,0x73,0x74,0x20,0x53,0x2f,
+        0x4d,0x49,0x4d,0x45,0x20,0x52,0x53,0x41,0x20,0x52,0x6f,0x6f,0x74,0x30,0x1e,0x17,
+        0x0d,0x31,0x33,0x30,0x38,0x30,0x32,0x31,0x34,0x34,0x39,0x32,0x39,0x5a,0x17,0x0d,
+        0x32,0x33,0x30,0x36,0x31,0x31,0x31,0x34,0x34,0x39,0x32,0x39,0x5a,0x30,0x44,0x31,
+        0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x4b,0x31,0x16,0x30,0x14,
+        0x06,0x03,0x55,0x04,0x0a,0x0c,0x0d,0x4f,0x70,0x65,0x6e,0x53,0x53,0x4c,0x20,0x47,
+        0x72,0x6f,0x75,0x70,0x31,0x1d,0x30,0x1b,0x06,0x03,0x55,0x04,0x03,0x0c,0x14,0x54,
+        0x65,0x73,0x74,0x20,0x53,0x2f,0x4d,0x49,0x4d,0x45,0x20,0x45,0x45,0x20,0x44,0x48,
+        0x20,0x23,0x31,0x30,0x82,0x01,0xb6,0x30,0x82,0x01,0x2b,0x06,0x07,0x2a,0x86,0x48,
+        0xce,0x3e,0x02,0x01,0x30,0x82,0x01,0x1e,0x02,0x81,0x81,0x00,0xd4,0x0c,0x4a,0x0c,
+        0x04,0x72,0x71,0x19,0xdf,0x59,0x19,0xc5,0xaf,0x44,0x7f,0xca,0x8e,0x2b,0xf0,0x09,
+        0xf5,0xd3,0x25,0xb1,0x73,0x16,0x55,0x89,0xdf,0xfd,0x07,0xaf,0x19,0xd3,0x7f,0xd0,
+        0x07,0xa2,0xfe,0x3f,0x5a,0xf1,0x01,0xc6,0xf8,0x2b,0xef,0x4e,0x6d,0x03,0x38,0x42,
+        0xa1,0x37,0xd4,0x14,0xb4,0x00,0x4a,0xb1,0x86,0x5a,0x83,0xce,0xb9,0x08,0x0e,0xc1,
+        0x99,0x27,0x47,0x8d,0x0b,0x85,0xa8,0x82,0xed,0xcc,0x0d,0xb9,0xb0,0x32,0x7e,0xdf,
+        0xe8,0xe4,0xf6,0xf6,0xec,0xb3,0xee,0x7a,0x11,0x34,0x65,0x97,0xfc,0x1a,0xb0,0x95,
+        0x4b,0x19,0xb9,0xa6,0x1c,0xd9,0x01,0x32,0xf7,0x35,0x7c,0x2d,0x5d,0xfe,0xc1,0x85,
+        0x70,0x49,0xf8,0xcc,0x99,0xd0,0xbe,0xf1,0x5a,0x78,0xc8,0x03,0x02,0x81,0x80,0x69,
+        0x00,0xfd,0x66,0xf2,0xfc,0x15,0x8b,0x09,0xb8,0xdc,0x4d,0xea,0xaa,0x79,0x55,0xf9,
+        0xdf,0x46,0xa6,0x2f,0xca,0x2d,0x8f,0x59,0x2a,0xad,0x44,0xa3,0xc6,0x18,0x2f,0x95,
+        0xb6,0x16,0x20,0xe3,0xd3,0xd1,0x8f,0x03,0xce,0x71,0x7c,0xef,0x3a,0xc7,0x44,0x39,
+        0x0e,0xe2,0x1f,0xd8,0xd3,0x89,0x2b,0xe7,0x51,0xdc,0x12,0x48,0x4c,0x18,0x4d,0x99,
+        0x12,0x06,0xe4,0x17,0x02,0x03,0x8c,0x24,0x05,0x8e,0xa6,0x85,0xf2,0x69,0x1b,0xe1,
+        0x6a,0xdc,0xe2,0x04,0x3a,0x01,0x9d,0x64,0xbe,0xfe,0x45,0xf9,0x44,0x18,0x71,0xbd,
+        0x2d,0x3e,0x7a,0x6f,0x72,0x7d,0x1a,0x80,0x42,0x57,0xae,0x18,0x6f,0x91,0xd6,0x61,
+        0x03,0x8a,0x1c,0x89,0x73,0xc7,0x56,0x41,0x03,0xd3,0xf8,0xed,0x65,0xe2,0x85,0x02,
+        0x15,0x00,0x89,0x94,0xab,0x10,0x67,0x45,0x41,0xad,0x63,0xc6,0x71,0x40,0x8d,0x6b,
+        0x9e,0x19,0x5b,0xa4,0xc7,0xf5,0x03,0x81,0x84,0x00,0x02,0x81,0x80,0x2f,0x5b,0xde,
+        0x72,0x02,0x36,0x6b,0x00,0x5e,0x24,0x7f,0x14,0x2c,0x18,0x52,0x42,0x97,0x4b,0xdb,
+        0x6e,0x15,0x50,0x3c,0x45,0x3e,0x25,0xf3,0xb7,0xc5,0x6e,0xe5,0x52,0xe7,0xc4,0xfb,
+        0xf4,0xa5,0xf0,0x39,0x12,0x7f,0xbc,0x54,0x1c,0x93,0xb9,0x5e,0xee,0xe9,0x14,0xb0,
+        0xdf,0xfe,0xfc,0x36,0xe4,0xf2,0xaf,0xfb,0x13,0xc8,0xdf,0x18,0x94,0x1d,0x40,0xb9,
+        0x71,0xdd,0x4c,0x9c,0xa7,0x03,0x52,0x02,0xb5,0xed,0x71,0x80,0x3e,0x23,0xda,0x28,
+        0xe5,0xab,0xe7,0x6f,0xf2,0x0a,0x0e,0x00,0x5b,0x7d,0xc6,0x4b,0xd7,0xc7,0xb2,0xc3,
+        0xba,0x62,0x7f,0x70,0x28,0xa0,0x9d,0x71,0x13,0x70,0xd1,0x9f,0x32,0x2f,0x3e,0xd2,
+        0xcd,0x1b,0xa4,0xc6,0x72,0xa0,0x74,0x5d,0x71,0xef,0x03,0x43,0x6e,0xa3,0x60,0x30,
+        0x5e,0x30,0x0c,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,
+        0x0e,0x06,0x03,0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x05,0xe0,0x30,
+        0x1d,0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,0x14,0x0b,0x5a,0x4d,0x5f,0x7d,0x25,
+        0xc7,0xf2,0x9d,0xc1,0xaa,0xb7,0x63,0x82,0x2f,0xfa,0x8f,0x32,0xe7,0xc0,0x30,0x1f,
+        0x06,0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xdf,0x7e,0x5e,0x88,0x05,
+        0x24,0x33,0x08,0xdd,0x22,0x81,0x02,0x97,0xcc,0x9a,0xb7,0xb1,0x33,0x27,0x30,0x30,
+        0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x05,0x05,0x00,0x03,0x82,
+        0x01,0x01,0x00,0x5a,0xf2,0x63,0xef,0xd3,0x16,0xd7,0xf5,0xaa,0xdd,0x12,0x00,0x36,
+        0x00,0x21,0xa2,0x7b,0x08,0xd6,0x3b,0x9f,0x62,0xac,0x53,0x1f,0xed,0x4c,0xd1,0x15,
+        0x34,0x65,0x71,0xee,0x96,0x07,0xa6,0xef,0xb2,0xde,0xd8,0xbb,0x35,0x6e,0x2c,0xe2,
+        0xd1,0x26,0xef,0x7e,0x94,0xe2,0x88,0x51,0xa4,0x6c,0xaa,0x27,0x2a,0xd3,0xb6,0xc2,
+        0xf7,0xea,0xc3,0x0b,0xa9,0xb5,0x28,0x37,0xa2,0x63,0x08,0xe4,0x88,0xc0,0x1b,0x16,
+        0x1b,0xca,0xfd,0x8a,0x07,0x32,0x29,0xa7,0x53,0xb5,0x2d,0x30,0xe4,0xf5,0x16,0xc3,
+        0xe3,0xc2,0x4c,0x30,0x5d,0x35,0x80,0x1c,0xa2,0xdb,0xe3,0x4b,0x51,0x0d,0x4c,0x60,
+        0x5f,0xb9,0x46,0xac,0xa8,0x46,0xa7,0x32,0xa7,0x9c,0x76,0xf8,0xe9,0xb5,0x19,0xe2,
+        0x0c,0xe1,0x0f,0xc6,0x46,0xe2,0x38,0xa7,0x87,0x72,0x6d,0x6c,0xbc,0x88,0x2f,0x9d,
+        0x2d,0xe5,0xd0,0x7d,0x1e,0xc7,0x5d,0xf8,0x7e,0xb4,0x0b,0xa6,0xf9,0x6c,0xe3,0x7c,
+        0xb2,0x70,0x6e,0x75,0x9b,0x1e,0x63,0xe1,0x4d,0xb2,0x81,0xd3,0x55,0x38,0x94,0x1a,
+        0x7a,0xfa,0xbf,0x01,0x18,0x70,0x2d,0x35,0xd3,0xe3,0x10,0x7a,0x9a,0xa7,0x8f,0xf3,
+        0xbd,0x56,0x55,0x5e,0xd8,0xbd,0x4e,0x16,0x76,0xd0,0x48,0x4c,0xf9,0x51,0x54,0xdf,
+        0x2d,0xb0,0xc9,0xaa,0x5e,0x42,0x38,0x50,0xbf,0x0f,0xc0,0xd9,0x84,0x44,0x4b,0x42,
+        0x24,0xec,0x14,0xa3,0xde,0x11,0xdf,0x58,0x7f,0xc2,0x4d,0xb2,0xd5,0x42,0x78,0x6e,
+        0x52,0x3e,0xad,0xc3,0x5f,0x04,0xc4,0xe6,0x31,0xaa,0x81,0x06,0x8b,0x13,0x4b,0x3c,
+        0x0e,0x6a,0xb1
+    };
+
+    if (!TEST_ptr(bio = BIO_new_mem_buf(dhx_cert, sizeof(dhx_cert)))
+        || !TEST_ptr(cert = X509_new_with_libctx(libctx, NULL))
+        || !TEST_ptr(d2i_X509_bio(bio, &cert)))
+        goto err;
+    ret = 1;
+err:
+    X509_free(cert);
+    BIO_free(bio);
+    return ret;
+}
+
 #endif /* OPENSSL_NO_DH */
 
 static int test_cipher_reinit(int test_id)
@@ -401,6 +488,7 @@ int setup_tests(void)
 #endif
 #ifndef OPENSSL_NO_DH
     ADD_ALL_TESTS(test_dh_safeprime_param_keygen, 3 * 3 * 3);
+    ADD_TEST(dhx_cert_load);
 #endif
 
     if (!TEST_ptr(cipher_names = sk_OPENSSL_CSTRING_new(name_cmp)))
diff --git a/test/serdes_test.c b/test/serdes_test.c
index 43d64155f5..f4afe53eac 100644
--- a/test/serdes_test.c
+++ b/test/serdes_test.c
@@ -740,6 +740,8 @@ static int test_public_via_MSBLOB(const char *type, EVP_PKEY *key)
 #ifndef OPENSSL_NO_DH
 DOMAIN_KEYS(DH);
 IMPLEMENT_TEST_SUITE(DH, "DH")
+DOMAIN_KEYS(DHX);
+IMPLEMENT_TEST_SUITE(DHX, "X9.42 DH")
 #endif
 #ifndef OPENSSL_NO_DSA
 DOMAIN_KEYS(DSA);
@@ -804,6 +806,7 @@ int setup_tests(void)
     TEST_info("Generating keys...");
 #ifndef OPENSSL_NO_DH
     MAKE_DOMAIN_KEYS(DH, "DH", NULL);
+    MAKE_DOMAIN_KEYS(DHX, "X9.42 DH", NULL);
 #endif
 #ifndef OPENSSL_NO_DSA
     MAKE_DOMAIN_KEYS(DSA, "DSA", DSA_params);
@@ -822,6 +825,7 @@ int setup_tests(void)
     if (ok) {
 #ifndef OPENSSL_NO_DH
         ADD_TEST_SUITE(DH);
+        ADD_TEST_SUITE(DHX);
 #endif
 #ifndef OPENSSL_NO_DSA
         ADD_TEST_SUITE(DSA);
@@ -854,6 +858,7 @@ void cleanup_tests(void)
 {
 #ifndef OPENSSL_NO_DH
     FREE_DOMAIN_KEYS(DH);
+    FREE_DOMAIN_KEYS(DHX);
 #endif
 #ifndef OPENSSL_NO_DSA
     FREE_DOMAIN_KEYS(DSA);
diff --git a/util/libcrypto.num b/util/libcrypto.num
index a3fd0ddc31..f441232582 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5240,3 +5240,13 @@ EVP_PKEY_CTX_set_scrypt_N               ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_CTX_set_scrypt_r               ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_CTX_set_scrypt_p               ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_CTX_set_scrypt_maxmem_bytes    ?	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_CTX_set_dh_kdf_type            ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_get_dh_kdf_type            ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_set0_dh_kdf_oid            ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_get0_dh_kdf_oid            ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_set_dh_kdf_md              ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_get_dh_kdf_md              ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_set_dh_kdf_outlen          ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_get_dh_kdf_outlen          ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_set0_dh_kdf_ukm            ?	3_0_0	EXIST::FUNCTION:DH
+EVP_PKEY_CTX_get0_dh_kdf_ukm            ?	3_0_0	EXIST::FUNCTION:DH


More information about the openssl-commits mailing list