Add dh_kdf support to provider
authorShane Lontis <shane.lontis@oracle.com>
Tue, 4 Aug 2020 01:21:21 +0000 (11:21 +1000)
committerShane Lontis <shane.lontis@oracle.com>
Tue, 11 Aug 2020 10:39:19 +0000 (20:39 +1000)
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@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/12575)

crypto/dh/build.info
crypto/dh/dh_ctrl.c [new file with mode: 0644]
crypto/dh/dh_kdf.c
crypto/dh/dh_lib.c
crypto/evp/pmeth_lib.c
doc/man3/EVP_PKEY_CTX_ctrl.pod
include/crypto/dh.h
include/openssl/dh.h
providers/implementations/exchange/dh_exch.c
test/evp_libctx_test.c

index 656e6ea828cc47eebea2a16889f7ca545c2a8344..887ef78b0befcaa1fa99b44459aa7bb8d4ccaf6c 100644 (file)
@@ -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_ctrl.c b/crypto/dh/dh_ctrl.c
new file mode 100644 (file)
index 0000000..6fddd27
--- /dev/null
@@ -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;
+}
index 1b8a320db1c76faa0359bfa30cad7ed46920fcb3..0b1e5881c33406d6ae1a297d7168422127c0f0a6 100644 (file)
 #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) */
index 2a3921a1371cee85a6cfe7d0632d4417386406d7..a6f4e641370f8af70d35841e07c37cddeba06ffd 100644 (file)
@@ -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);
-}
index 6f2f52912b1a13d03c304ca6b8f0990735a73be8..ba85a8c1434c1519c8087be2278d3a6121b083f6 100644 (file)
@@ -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:
index bd98a2f1d7cac78c7a57bc6504bdfc67e9a0d053..85f2b64cc5a590898a78108968214edea9563408 100644 (file)
@@ -486,47 +486,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
+The EVP_PKEY_CTX_set_dh_kdf_type() method 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
+The EVP_PKEY_CTX_get_dh_kdf_type() method 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
+The EVP_PKEY_CTX_set0_dh_kdf_oid() method 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
+The EVP_PKEY_CTX_get0_dh_kdf_oid() method 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
+The EVP_PKEY_CTX_set_dh_kdf_md() method 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
+The EVP_PKEY_CTX_get_dh_kdf_md() method 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
+The EVP_PKEY_CTX_set_dh_kdf_outlen() method 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
+The EVP_PKEY_CTX_get_dh_kdf_outlen() method 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
+The EVP_PKEY_CTX_set0_dh_kdf_ukm() method 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>.
+The EVP_PKEY_CTX_get0_dh_kdf_ukm() method 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 +641,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.
 
index 22847dd1e01fd34125658d3947813ee90ebaf3d5..fa0d70dbd417a4f1260c4084005638604aeb6a3f 100644 (file)
@@ -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);
index 1a02f8a330b9ba49589a8a9529d854fba2d059b0..4b456cff16e4084fc25a68d1e3f0c3d7ee820012 100644 (file)
@@ -286,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)
index c0cb378c12f739f310bf8976436ec3c1abbef86d..3c3bd4dd38a4a2921fad60d76853203017a88208 100644 (file)
  */
 #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 }
 };
index 7421e1e3ca5edc90c0a82c31cb448b9242e0f4bc..3b20dad74b383bd9dc752484935c4f11e1b6ad48 100644 (file)
@@ -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)))