From 8b0bd5f9f9cfd2e2435df8bc8b2aac9379b6b2c9 Mon Sep 17 00:00:00 2001
From: Weili Qian <qianweili@huawei.com>
Date: Thu, 19 Dec 2024 15:58:01 +0800
Subject: [PATCH 05/10] uadk_provider: support ecdh keyexch hardware
 acceleration

Support ECDH key exchange.

Test:
        openssl speed -provider uadk_provider ecdhp192
        openssl speed -provider uadk_provider ecdhp224
        openssl speed -provider uadk_provider ecdhp256
        openssl speed -provider uadk_provider ecdhp384
        openssl speed -provider uadk_provider ecdhp521

Signed-off-by: Weili Qian <qianweili@huawei.com>
Signed-off-by: JiangShui Yang <yangjiangshui@h-partners.com>
---
 src/Makefile.am           |   2 +-
 src/uadk_prov.h           |   1 +
 src/uadk_prov_ecdh_exch.c | 889 ++++++++++++++++++++++++++++++++++++++
 src/uadk_prov_init.c      |   9 +-
 src/uadk_prov_pkey.c      |  68 +++
 src/uadk_prov_pkey.h      |   6 +
 6 files changed, 971 insertions(+), 4 deletions(-)
 create mode 100644 src/uadk_prov_ecdh_exch.c

diff --git a/src/Makefile.am b/src/Makefile.am
index b2e2c06..5a1abe7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -66,7 +66,7 @@ uadk_provider_la_SOURCES=uadk_prov_init.c uadk_async.c uadk_utils.c \
 			 uadk_prov_bio.c uadk_prov_der_writer.c uadk_prov_packet.c \
 			 uadk_prov_pkey.c uadk_prov_sm2.c \
 			 uadk_prov_ffc.c uadk_prov_aead.c \
-			 uadk_prov_ec_kmgmt.c
+			 uadk_prov_ec_kmgmt.c uadk_prov_ecdh_exch.c
 
 uadk_provider_la_LDFLAGS=-module -version-number $(VERSION)
 uadk_provider_la_LIBADD=$(WD_LIBS) -lpthread
diff --git a/src/uadk_prov.h b/src/uadk_prov.h
index 9c310b7..7975884 100644
--- a/src/uadk_prov.h
+++ b/src/uadk_prov.h
@@ -180,6 +180,7 @@ extern const OSSL_DISPATCH uadk_sm2_signature_functions[FUNC_MAX_NUM];
 extern const OSSL_DISPATCH uadk_sm2_asym_cipher_functions[FUNC_MAX_NUM];
 
 extern const OSSL_DISPATCH uadk_ec_keymgmt_functions[FUNC_MAX_NUM];
+extern const OSSL_DISPATCH uadk_ecdh_keyexch_functions[FUNC_MAX_NUM];
 
 void uadk_prov_destroy_digest(void);
 void uadk_prov_destroy_cipher(void);
diff --git a/src/uadk_prov_ecdh_exch.c b/src/uadk_prov_ecdh_exch.c
new file mode 100644
index 0000000..f549d25
--- /dev/null
+++ b/src/uadk_prov_ecdh_exch.c
@@ -0,0 +1,889 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2024 Huawei Technologies Co.,Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/bn.h>
+#include <openssl/kdf.h>
+#include <uadk/wd_ecc.h>
+#include "uadk_async.h"
+#include "uadk_prov.h"
+#include "uadk_prov_der_writer.h"
+#include "uadk_prov_pkey.h"
+
+#define UADK_PROV_MAX_PARAM_LEN		80
+
+enum kdf_type {
+	PROV_ECDH_KDF_NONE = 0,
+	PROV_ECDH_KDF_X9_63
+};
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes EC_KEY structures, so
+ * we use that here too.
+ */
+struct ecdh_ctx {
+	OSSL_LIB_CTX *libctx;
+
+	EC_KEY *k;
+	EC_KEY *peerk;
+
+	/*
+	 * ECDH cofactor mode:
+	 *
+	 * . 0  disabled
+	 * . 1  enabled
+	 * . -1 use cofactor mode set for k
+	 */
+	int cofactor_mode;
+	/* KDF (if any) to use for ECDH */
+	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;
+};
+
+struct ecdh_sess_ctx {
+	EC_KEY *privk;
+	const EC_POINT *pub_key;
+	const BIGNUM *cofactor;
+	const EC_GROUP *group;
+};
+
+UADK_PKEY_KEYEXCH_DESCR(ecdh, ECDH);
+static bool g_keyexch_ecdh_support;
+
+void uadk_prov_keyexch_alg(void)
+{
+	g_keyexch_ecdh_support = uadk_prov_support_algorithm("ecdh");
+}
+
+static size_t ecdh_get_ec_size(const EC_GROUP *group)
+{
+	size_t degree;
+
+	degree = EC_GROUP_get_degree(group);
+
+	return BITS_TO_BYTES(degree);
+}
+
+static int ecdh_param_check(struct ecdh_ctx *pecdhctx, struct ecdh_sess_ctx *sess_ctx)
+{
+	const EC_GROUP *group;
+	int type;
+
+	if (!pecdhctx->k || !pecdhctx->peerk) {
+		fprintf(stderr, "invalid: k or peerk is NULL.\n");
+		return UADK_P_FAIL;
+	}
+
+	sess_ctx->pub_key = EC_KEY_get0_public_key(pecdhctx->peerk);
+	if (!sess_ctx->pub_key) {
+		fprintf(stderr, "invalid: public key is NULL.\n");
+		return UADK_P_FAIL;
+	}
+
+	group = EC_KEY_get0_group(pecdhctx->k);
+	if (!group) {
+		fprintf(stderr, "invalid: group is 0.\n");
+		return UADK_P_FAIL;
+	}
+
+	sess_ctx->cofactor = EC_GROUP_get0_cofactor(group);
+	if (!sess_ctx->cofactor) {
+		fprintf(stderr, "invalid: cofactor is NULL!\n");
+		return UADK_P_FAIL;
+	}
+
+	/* Field GF(2m) is not supported by uadk */
+	type = EC_METHOD_get_field_type(EC_GROUP_method_of(group));
+	if (type != NID_X9_62_prime_field) {
+		fprintf(stderr, "invalid: uadk unsupport Field GF(2m)!\n");
+		return UADK_P_FAIL;
+	}
+
+	sess_ctx->group = group;
+
+	return uadk_prov_ecc_bit_check(group);
+}
+
+static int ecdh_set_privk(struct ecdh_ctx *pecdhctx,
+			  struct ecdh_sess_ctx *sess_ctx)
+{
+	int key_cofactor_mode;
+
+	/*
+	 * The ctx->cofactor_mode flag has precedence over the
+	 * cofactor_mode flag set on ctx->k.
+	 *
+	 * - if ctx->cofactor_mode == -1, use ctx->k directly
+	 * - if ctx->cofactor_mode == key_cofactor_mode, use ctx->k directly
+	 * - if ctx->cofactor_mode != key_cofactor_mode:
+	 *     - if ctx->k->cofactor == 1, the cofactor_mode flag is irrelevant, use
+	 *          ctx->k directly
+	 *     - if ctx->k->cofactor != 1, use a duplicate of ctx->k with the flag
+	 *          set to ctx->cofactor_mode
+	 */
+	key_cofactor_mode = (EC_KEY_get_flags(pecdhctx->k) & EC_FLAG_COFACTOR_ECDH) ?
+				COFACTOR_MODE_ENABLED : COFACTOR_MODE_DISABLED;
+	if (pecdhctx->cofactor_mode != COFACTOR_MODE_USE_KEY &&
+	    pecdhctx->cofactor_mode != key_cofactor_mode &&
+	    !BN_is_one(sess_ctx->cofactor)) {
+		sess_ctx->privk = EC_KEY_dup(pecdhctx->k);
+		if (!sess_ctx->privk)
+			return UADK_P_FAIL;
+
+		if (pecdhctx->cofactor_mode == COFACTOR_MODE_ENABLED)
+			EC_KEY_set_flags(sess_ctx->privk, EC_FLAG_COFACTOR_ECDH);
+		else
+			EC_KEY_clear_flags(sess_ctx->privk, EC_FLAG_COFACTOR_ECDH);
+	} else {
+		sess_ctx->privk = pecdhctx->k;
+	}
+
+	return UADK_P_SUCCESS;
+}
+
+static handle_t ecdh_alloc_sess(EC_KEY *privk)
+{
+	int ret;
+
+	if (!g_keyexch_ecdh_support) {
+		fprintf(stderr, "invalid: hardware not support ecdh!\n");
+		return UADK_P_FAIL;
+	}
+
+	ret = uadk_prov_ecc_init("ecdh");
+	if (!ret) {
+		fprintf(stderr, "failed to init ecdh to compute key!\n");
+		return UADK_P_FAIL;
+	}
+
+	return uadk_prov_ecc_alloc_sess(privk, "ecdh");
+}
+
+static void ecdh_free_sess(handle_t sess)
+{
+	wd_ecc_free_sess(sess);
+}
+
+static int ecdh_init_req(struct ecdh_sess_ctx *sess_ctx,
+			 struct wd_ecc_req *req, handle_t sess)
+{
+	char buf_x[UADK_ECC_MAX_KEY_BYTES];
+	char buf_y[UADK_ECC_MAX_KEY_BYTES];
+	struct wd_ecc_point in_pkey;
+	struct wd_ecc_out *ecdh_out;
+	struct wd_ecc_in *ecdh_in;
+	BIGNUM *pkey_x, *pkey_y;
+	int ret = UADK_P_FAIL;
+	BN_CTX *ctx;
+
+	ctx = BN_CTX_new();
+	if (!ctx)
+		return -ENOMEM;
+
+	BN_CTX_start(ctx);
+	pkey_x = BN_CTX_get(ctx);
+	if (!pkey_x)
+		goto free_ctx;
+
+	pkey_y = BN_CTX_get(ctx);
+	if (!pkey_y)
+		goto free_ctx;
+
+	uadk_prov_get_affine_coordinates(sess_ctx->group, sess_ctx->pub_key, pkey_x, pkey_y, ctx);
+	in_pkey.x.data = buf_x;
+	in_pkey.y.data = buf_y;
+	in_pkey.x.dsize = BN_bn2bin(pkey_x, (unsigned char *)in_pkey.x.data);
+	in_pkey.y.dsize = BN_bn2bin(pkey_y, (unsigned char *)in_pkey.y.data);
+
+	/* Set public key */
+	ecdh_in = wd_ecxdh_new_in(sess, &in_pkey);
+	if (!ecdh_in) {
+		fprintf(stderr, "failed to new ecxdh in\n");
+		goto free_ctx;
+	}
+
+	ecdh_out = wd_ecxdh_new_out(sess);
+	if (!ecdh_out) {
+		fprintf(stderr, "failed to new ecxdh out\n");
+		wd_ecc_del_in(sess, ecdh_in);
+		goto free_ctx;
+	}
+
+	uadk_prov_ecc_fill_req(req, WD_ECXDH_COMPUTE_KEY, ecdh_in, ecdh_out);
+
+	ret = UADK_P_SUCCESS;
+
+free_ctx:
+	BN_CTX_end(ctx);
+	BN_CTX_free(ctx);
+
+	return ret;
+}
+
+static void ecdh_uninit_req(struct wd_ecc_req *req, handle_t sess)
+{
+	wd_ecc_del_in(sess, req->src);
+	wd_ecc_del_out(sess, req->dst);
+}
+
+static int ecdh_get_shared_key(unsigned char *secret,
+			       size_t size, size_t *psecretlen,
+			       struct wd_ecc_req *req)
+{
+	struct wd_ecc_point *shared_key = NULL;
+
+	wd_ecxdh_get_out_params(req->dst, &shared_key);
+	if (!shared_key) {
+		fprintf(stderr, "failed to get ecdh shared key\n");
+		return UADK_P_FAIL;
+	}
+
+	size = size < shared_key->x.dsize ? size : shared_key->x.dsize;
+	*psecretlen = size;
+
+	memcpy(secret, (unsigned char *)shared_key->x.data, size);
+
+	return UADK_P_SUCCESS;
+}
+
+static int ecdh_compute_key(struct ecdh_sess_ctx *sess_ctx,
+			    unsigned char *secret,
+			    size_t *psecretlen, size_t size)
+{
+	struct wd_ecc_req req = {0};
+	handle_t sess;
+	int ret;
+
+	sess = ecdh_alloc_sess(sess_ctx->privk);
+	if (!sess) {
+		fprintf(stderr, "failed to alloc sess to compute key!\n");
+		return UADK_P_FAIL;
+	}
+
+	ret = uadk_prov_ecc_set_private_key(sess, sess_ctx->privk);
+	if (!ret) {
+		fprintf(stderr, "failed to set private key!\n");
+		goto free_sess;
+	}
+
+	ret = ecdh_init_req(sess_ctx, &req, sess);
+	if (!ret) {
+		fprintf(stderr, "failed to init req!\n");
+		goto free_sess;
+	}
+
+	ret = uadk_prov_ecc_crypto(sess, &req, (void *)sess);
+	if (!ret) {
+		fprintf(stderr, "failed to calculate shared key!\n");
+		goto uninit_req;
+	}
+
+	ret = ecdh_get_shared_key(secret, size, psecretlen, &req);
+
+uninit_req:
+	ecdh_uninit_req(&req, sess);
+free_sess:
+	ecdh_free_sess(sess);
+	return ret;
+}
+
+static int ecdh_plain_derive(struct ecdh_ctx *pecdhctx,
+			     unsigned char *secret,
+			     size_t *psecretlen, size_t outlen)
+{
+	struct ecdh_sess_ctx sess_ctx = {0};
+	size_t size, ec_size;
+	int ret;
+
+	ret = ecdh_param_check(pecdhctx, &sess_ctx);
+	if (!ret)
+		return ret;
+
+	ec_size = ecdh_get_ec_size(sess_ctx.group);
+	if (!secret) {
+		*psecretlen = ec_size;
+		return UADK_P_SUCCESS;
+	}
+
+	ret = ecdh_set_privk(pecdhctx, &sess_ctx);
+	if (!ret) {
+		fprintf(stderr, "failed to set private key!\n");
+		return ret;
+	}
+
+	size = outlen < ec_size ? outlen : ec_size;
+	ret = ecdh_compute_key(&sess_ctx, secret, psecretlen, size);
+	if (sess_ctx.privk != pecdhctx->k)
+		EC_KEY_free(sess_ctx.privk);
+
+	return ret;
+}
+
+/* Key derivation function from X9.63/SECG */
+static int ecdh_kdf_X9_63(unsigned char *out, struct ecdh_ctx *pecdhctx,
+			  unsigned char *stmp, size_t stmplen)
+{
+	EVP_KDF *kdf = EVP_KDF_fetch(pecdhctx->libctx, OSSL_KDF_NAME_X963KDF, NULL);
+	const char *mdname = EVP_MD_get0_name(pecdhctx->kdf_md);
+	OSSL_PARAM params[4], *p = params;
+	int ret = UADK_P_FAIL;
+	EVP_KDF_CTX *kctx;
+
+	if (!kdf) {
+		fprintf(stderr, "failed to fetch kdf!\n");
+		return ret;
+	}
+
+	kctx = EVP_KDF_CTX_new(kdf);
+	if (!kctx) {
+		fprintf(stderr, "failed to new kctx!\n");
+		goto free_kdf;
+	}
+
+	*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, (char *)mdname, 0);
+	*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)stmp, stmplen);
+	*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+						(void *)pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen);
+	*p = OSSL_PARAM_construct_end();
+
+	ret = EVP_KDF_derive(kctx, out, pecdhctx->kdf_outlen, params);
+	ret = ret <= 0 ? UADK_P_FAIL : UADK_P_SUCCESS;
+
+	EVP_KDF_CTX_free(kctx);
+
+free_kdf:
+	EVP_KDF_free(kdf);
+
+	return ret;
+}
+
+static int ecdh_X9_63_kdf_derive(struct ecdh_ctx *pecdhctx, unsigned char *secret,
+				 size_t *psecretlen, size_t outlen)
+{
+	unsigned char *stmp;
+	size_t stmplen = 0;
+	int ret;
+
+	if (!secret) {
+		*psecretlen = pecdhctx->kdf_outlen;
+		return UADK_P_SUCCESS;
+	}
+
+	if (outlen < pecdhctx->kdf_outlen) {
+		fprintf(stderr, "invalid: outlen %lu is less than kdf_outlen %lu!\n",
+			outlen, pecdhctx->kdf_outlen);
+		return UADK_P_FAIL;
+	}
+
+	ret = ecdh_plain_derive(pecdhctx, NULL, &stmplen, 0);
+	if (!ret)
+		return ret;
+
+	stmp = OPENSSL_secure_malloc(stmplen);
+	if (!stmp) {
+		fprintf(stderr, "failed to alloc stmp!\n");
+		return UADK_P_FAIL;
+	}
+
+	ret = ecdh_plain_derive(pecdhctx, stmp, &stmplen, stmplen);
+	if (!ret)
+		goto free_stmp;
+
+	ret = ecdh_kdf_X9_63(secret, pecdhctx, stmp, stmplen);
+	if (!ret)
+		goto free_stmp;
+
+	*psecretlen = pecdhctx->kdf_outlen;
+
+ free_stmp:
+	OPENSSL_secure_clear_free(stmp, stmplen);
+	return ret;
+}
+
+static int uadk_keyexch_ecdh_derive(void *vpecdhctx, unsigned char *secret,
+				    size_t *psecretlen, size_t outlen)
+{
+	struct ecdh_ctx *pecdhctx = vpecdhctx;
+
+	if (!pecdhctx) {
+		fprintf(stderr, "invalid: vpecdhctx is NULL to derive!\n");
+		return UADK_P_FAIL;
+	}
+
+	switch (pecdhctx->kdf_type) {
+	case PROV_ECDH_KDF_NONE:
+		return ecdh_plain_derive(pecdhctx, secret, psecretlen, outlen);
+	case PROV_ECDH_KDF_X9_63:
+		return ecdh_X9_63_kdf_derive(pecdhctx, secret, psecretlen, outlen);
+	default:
+		break;
+	}
+
+	return UADK_P_FAIL;
+}
+
+static void *uadk_keyexch_ecdh_newctx(void *provctx)
+{
+	struct ecdh_ctx *pectx;
+
+	pectx = OPENSSL_zalloc(sizeof(*pectx));
+	if (!pectx)
+		return NULL;
+
+	pectx->libctx = prov_libctx_of(provctx);
+	pectx->cofactor_mode = COFACTOR_MODE_USE_KEY;
+	pectx->kdf_type = PROV_ECDH_KDF_NONE;
+
+	return pectx;
+}
+
+static void uadk_keyexch_ecdh_freectx(void *vpecdhctx)
+{
+	struct ecdh_ctx *pecdhctx = vpecdhctx;
+
+	if (!pecdhctx)
+		return;
+
+	EC_KEY_free(pecdhctx->k);
+	EC_KEY_free(pecdhctx->peerk);
+	EVP_MD_free(pecdhctx->kdf_md);
+	OPENSSL_clear_free(pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen);
+	OPENSSL_free(pecdhctx);
+}
+
+static int uadk_keyexch_ecdh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[])
+{
+	struct ecdh_ctx *pecdhctx = vpecdhctx;
+	int ret;
+
+	if (!pecdhctx || !vecdh) {
+		fprintf(stderr, "invalid: pecdhctx or vecdh is to init!\n");
+		return UADK_P_FAIL;
+	}
+
+	if (!EC_KEY_up_ref(vecdh))
+		return UADK_P_FAIL;
+
+	EC_KEY_free(pecdhctx->k);
+	pecdhctx->k = vecdh;
+	pecdhctx->cofactor_mode = COFACTOR_MODE_USE_KEY;
+	pecdhctx->kdf_type = PROV_ECDH_KDF_NONE;
+
+	ret = uadk_keyexch_ecdh_set_ctx_params(pecdhctx, params);
+	if (!ret) {
+		fprintf(stderr, "failed to set_ctx_params!\n");
+		return ret;
+	}
+
+	return uadk_prov_ecc_check_key(pecdhctx->libctx, vecdh, 1);
+}
+
+static int ecdh_match_params(const EC_KEY *privk, const EC_KEY *pubk)
+{
+	const EC_GROUP *group_privk = EC_KEY_get0_group(privk);
+	const EC_GROUP *group_pubk = EC_KEY_get0_group(pubk);
+	int ret = UADK_P_SUCCESS;
+	BN_CTX *ctx;
+
+	ctx = BN_CTX_new_ex(privk->libctx);
+	if (!ctx) {
+		fprintf(stderr, "failed to new ctx!\n");
+		return UADK_P_FAIL;
+	}
+
+	if (group_privk && group_pubk) {
+		if (EC_GROUP_cmp(group_privk, group_pubk, ctx)) {
+			fprintf(stderr, "invalid: privk is not match pubk!\n");
+			ret = UADK_P_FAIL;
+		}
+	}
+
+	BN_CTX_free(ctx);
+
+	return ret;
+}
+
+static int uadk_keyexch_ecdh_set_peer(void *vpecdhctx, void *vecdh)
+{
+	struct ecdh_ctx *pecdhctx = vpecdhctx;
+	int ret;
+
+	if (!pecdhctx || !vecdh) {
+		fprintf(stderr, "invalid: vpecdhctx or vecdh is NULL to set_peer!\n");
+		return UADK_P_FAIL;
+	}
+
+	ret = ecdh_match_params(pecdhctx->k, vecdh);
+	if (!ret)
+		return ret;
+
+	ret = uadk_prov_ecc_check_key(pecdhctx->libctx, vecdh, 1);
+	if (!ret)
+		return ret;
+
+	if (!EC_KEY_up_ref(vecdh))
+		return UADK_P_FAIL;
+
+	EC_KEY_free(pecdhctx->peerk);
+	pecdhctx->peerk = vecdh;
+
+	return UADK_P_SUCCESS;
+}
+
+static void *uadk_keyexch_ecdh_dupctx(void *vpecdhctx)
+{
+	struct ecdh_ctx *srcctx = vpecdhctx;
+	struct ecdh_ctx *dstctx;
+
+	if (!srcctx) {
+		fprintf(stderr, "invalid: source ecdh ctx is NULL!\n");
+		return NULL;
+	}
+
+	dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+	if (!dstctx) {
+		fprintf(stderr, "failed to alloc dst ctx!\n");
+		return NULL;
+	}
+
+	memcpy(dstctx, srcctx, sizeof(*dstctx));
+
+	dstctx->k = NULL;
+	dstctx->peerk = NULL;
+	dstctx->kdf_md = NULL;
+	dstctx->kdf_ukm = NULL;
+
+	/* up-ref all ref-counted objects referenced in dstctx */
+	if (srcctx->k && !EC_KEY_up_ref(srcctx->k))
+		goto err;
+	else
+		dstctx->k = srcctx->k;
+
+	if (srcctx->peerk && !EC_KEY_up_ref(srcctx->peerk))
+		goto err;
+	else
+		dstctx->peerk = srcctx->peerk;
+
+	if (srcctx->kdf_md && !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 && srcctx->kdf_ukmlen > 0) {
+		dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,
+						 srcctx->kdf_ukmlen);
+		if (!dstctx->kdf_ukm)
+			goto err;
+	}
+
+	return dstctx;
+
+err:
+	uadk_keyexch_ecdh_freectx(dstctx);
+	return NULL;
+}
+
+static int ecdh_set_cofactor_mode(struct ecdh_ctx *pectx, const OSSL_PARAM params[])
+{
+	const OSSL_PARAM *p;
+	int mode, ret;
+
+	p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	ret = OSSL_PARAM_get_int(p, &mode);
+	if (!ret)
+		return UADK_P_FAIL;
+
+	if (mode < COFACTOR_MODE_USE_KEY || mode > COFACTOR_MODE_ENABLED)
+		return UADK_P_FAIL;
+
+	pectx->cofactor_mode = mode;
+
+	return UADK_P_SUCCESS;
+}
+
+static int ecdh_get_cofactor_mode(struct ecdh_ctx *pectx, OSSL_PARAM params[])
+{
+	int mode = pectx->cofactor_mode;
+	OSSL_PARAM *p;
+
+	p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	if (mode == COFACTOR_MODE_USE_KEY)
+		/* Check what is the default for pecdhctx->k */
+		mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ?
+			COFACTOR_MODE_ENABLED : COFACTOR_MODE_DISABLED;
+
+	return OSSL_PARAM_set_int(p, mode);
+}
+
+static int ecdh_set_kdf_type(struct ecdh_ctx *pectx, const OSSL_PARAM params[])
+{
+	char name[UADK_PROV_MAX_PARAM_LEN] = {'\0'};
+	const OSSL_PARAM *p;
+	char *str = name;
+	int ret;
+
+	p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	ret = OSSL_PARAM_get_utf8_string(p, &str, sizeof(name));
+	if (!ret)
+		return UADK_P_FAIL;
+
+	if (name[0] == '\0')
+		pectx->kdf_type = PROV_ECDH_KDF_NONE;
+	else if (!strcmp(name, OSSL_KDF_NAME_X963KDF))
+		pectx->kdf_type = PROV_ECDH_KDF_X9_63;
+	else
+		return UADK_P_FAIL;
+
+	return UADK_P_SUCCESS;
+}
+
+static int ecdh_get_kdf_type(struct ecdh_ctx *pectx, OSSL_PARAM params[])
+{
+	const char *kdf_type;
+	OSSL_PARAM *p;
+
+	p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	switch (pectx->kdf_type) {
+	case PROV_ECDH_KDF_NONE:
+		kdf_type = "";
+		break;
+	case PROV_ECDH_KDF_X9_63:
+		kdf_type = OSSL_KDF_NAME_X963KDF;
+		break;
+	default:
+		return UADK_P_FAIL;
+	}
+
+	return OSSL_PARAM_set_utf8_string(p, kdf_type);
+}
+
+static int ecdh_set_kdf_digest(struct ecdh_ctx *pectx, const OSSL_PARAM params[])
+{
+	char mdprops[UADK_PROV_MAX_PARAM_LEN] = {'\0'};
+	char name[UADK_PROV_MAX_PARAM_LEN] = {'\0'};
+	const OSSL_PARAM *p;
+	char *str = name;
+	int ret;
+
+	p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	ret = OSSL_PARAM_get_utf8_string(p, &str, sizeof(name));
+	if (!ret)
+		return UADK_P_FAIL;
+
+	p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);
+	if (p) {
+		str = mdprops;
+		ret = OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops));
+		if (!ret)
+			return UADK_P_FAIL;
+	}
+
+	EVP_MD_free(pectx->kdf_md);
+	pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops);
+	if (!pectx->kdf_md)
+		return UADK_P_FAIL;
+
+	return UADK_P_SUCCESS;
+}
+
+static int ecdh_get_kdf_digest(struct ecdh_ctx *pectx, OSSL_PARAM params[])
+{
+	OSSL_PARAM *p;
+
+	p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	if (!pectx->kdf_md)
+		return OSSL_PARAM_set_utf8_string(p, "");
+
+	return OSSL_PARAM_set_utf8_string(p, EVP_MD_get0_name(pectx->kdf_md));
+}
+
+static int ecdh_set_kdf_outlen(struct ecdh_ctx *pectx, const OSSL_PARAM params[])
+{
+	const OSSL_PARAM *p;
+
+	p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	return OSSL_PARAM_get_size_t(p, &pectx->kdf_outlen);
+}
+
+static int ecdh_get_kdf_outlen(struct ecdh_ctx *pectx, OSSL_PARAM params[])
+{
+	OSSL_PARAM *p;
+
+	p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	return OSSL_PARAM_set_size_t(p, pectx->kdf_outlen);
+}
+
+static int ecdh_set_kdf_ukm(struct ecdh_ctx *pectx, const OSSL_PARAM params[])
+{
+	const OSSL_PARAM *p;
+	void *tmp_ukm = NULL;
+	size_t tmp_ukmlen;
+	int ret;
+
+	p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	ret = OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen);
+	if (!ret)
+		return ret;
+
+	OPENSSL_free(pectx->kdf_ukm);
+	pectx->kdf_ukm = tmp_ukm;
+	pectx->kdf_ukmlen = tmp_ukmlen;
+
+	return UADK_P_SUCCESS;
+}
+
+static int ecdh_get_kdf_ukm(struct ecdh_ctx *pectx, OSSL_PARAM params[])
+{
+	OSSL_PARAM *p;
+
+	p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+	if (!p)
+		return UADK_P_SUCCESS;
+
+	return OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, pectx->kdf_ukmlen);
+}
+
+static int uadk_keyexch_ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[])
+{
+	struct ecdh_ctx *pectx = (struct ecdh_ctx *)vpecdhctx;
+	int ret;
+
+	if (!pectx) {
+		fprintf(stderr, "invalid: pectx is NULL to set_ctx_params!\n");
+		return UADK_P_FAIL;
+	}
+
+	if (!params)
+		return UADK_P_SUCCESS;
+
+	ret = ecdh_set_cofactor_mode(pectx, params);
+	if (!ret)
+		return ret;
+
+	ret = ecdh_set_kdf_type(pectx, params);
+	if (!ret)
+		return ret;
+
+	ret = ecdh_set_kdf_digest(pectx, params);
+	if (!ret)
+		return ret;
+
+	ret = ecdh_set_kdf_outlen(pectx, params);
+	if (!ret)
+		return ret;
+
+	return ecdh_set_kdf_ukm(pectx, params);
+}
+
+static int uadk_keyexch_ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[])
+{
+	struct ecdh_ctx *pectx = vpecdhctx;
+	int ret;
+
+	if (!pectx) {
+		fprintf(stderr, "invalid: pectx is NULL to get_ctx_params!\n");
+		return UADK_P_FAIL;
+	}
+
+	ret = ecdh_get_cofactor_mode(pectx, params);
+	if (!ret)
+		return ret;
+
+	ret = ecdh_get_kdf_type(pectx, params);
+	if (!ret)
+		return ret;
+
+	ret = ecdh_get_kdf_digest(pectx, params);
+	if (!ret)
+		return ret;
+
+	ret = ecdh_get_kdf_outlen(pectx, params);
+	if (!ret)
+		return ret;
+
+	return ecdh_get_kdf_ukm(pectx, params);
+}
+
+static const OSSL_PARAM *uadk_keyexch_ecdh_settable_ctx_params(ossl_unused void *vpecdhctx,
+							       ossl_unused void *provctx)
+{
+	static const OSSL_PARAM known_settable_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_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_END
+	};
+
+	return known_settable_ctx_params;
+}
+
+static const OSSL_PARAM *uadk_keyexch_ecdh_gettable_ctx_params(ossl_unused void *vpecdhctx,
+							       ossl_unused void *provctx)
+{
+	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_END
+	};
+
+	return known_gettable_ctx_params;
+}
diff --git a/src/uadk_prov_init.c b/src/uadk_prov_init.c
index b5d3df5..42e1272 100644
--- a/src/uadk_prov_init.c
+++ b/src/uadk_prov_init.c
@@ -190,6 +190,8 @@ static const OSSL_ALGORITHM uadk_prov_asym_cipher[] = {
 static const OSSL_ALGORITHM uadk_prov_keyexch[] = {
 	{ "DH", UADK_DEFAULT_PROPERTIES,
 	  uadk_dh_keyexch_functions, "UADK DH keyexch implementation"},
+	{ "ECDH", UADK_DEFAULT_PROPERTIES,
+	  uadk_ecdh_keyexch_functions, "uadk_provider ecdh_keyexch" },
 	{ NULL, NULL, NULL }
 };
 
@@ -218,15 +220,16 @@ static const OSSL_ALGORITHM *uadk_query(void *provctx, int operation_id,
 			return uadk_prov_ciphers_v3;
 		return uadk_prov_ciphers_v2;
 	case OSSL_OP_SIGNATURE:
-		(void)uadk_prov_signature_alg();
+		uadk_prov_signature_alg();
 		return uadk_prov_signature;
 	case OSSL_OP_KEYMGMT:
-		(void)uadk_prov_keymgmt_alg();
+		uadk_prov_keymgmt_alg();
 		return uadk_prov_keymgmt;
 	case OSSL_OP_ASYM_CIPHER:
-		(void)uadk_prov_asym_cipher_alg();
+		uadk_prov_asym_cipher_alg();
 		return uadk_prov_asym_cipher;
 	case OSSL_OP_KEYEXCH:
+		uadk_prov_keyexch_alg();
 		return uadk_prov_keyexch;
 	case OSSL_OP_STORE:
 		return prov->query_operation(provctx, operation_id, no_cache);
diff --git a/src/uadk_prov_pkey.c b/src/uadk_prov_pkey.c
index 170c30b..a861551 100644
--- a/src/uadk_prov_pkey.c
+++ b/src/uadk_prov_pkey.c
@@ -29,6 +29,9 @@
 #define PROV_SUPPORT		1
 #define SIGNATURE_TYPE		3
 #define ASYM_CIPHER_TYPE	3
+#define SECURITY_CHECK_DISABLE	0
+#define UADK_PROV_MIN_BITS	112
+#define UADK_PROV_SECURITY_BITS	80
 
 static int p_keymgmt_support_state[KEYMGMT_TYPE];
 static int p_signature_support_state[SIGNATURE_TYPE];
@@ -833,3 +836,68 @@ int uadk_prov_ecc_bit_check(const EC_GROUP *group)
 
 	return UADK_P_FAIL;
 }
+
+/* Currently, disable the security checks in the default provider and uadk provider */
+int uadk_prov_securitycheck_enabled(OSSL_LIB_CTX *ctx)
+{
+	return SECURITY_CHECK_DISABLE;
+}
+
+#ifdef OPENSSL_NO_FIPS_SECURITYCHECKS
+int uadk_prov_ecc_check_key(OSSL_LIB_CTX *ctx, const EC_KEY *ec, int protect)
+{
+	return UADK_P_SUCCESS;
+}
+#else
+int uadk_prov_ecc_check_key(OSSL_LIB_CTX *ctx, const EC_KEY *ec, int protect)
+{
+	const EC_GROUP *group = EC_KEY_get0_group(ec);
+	const char *curve_name;
+	int nid, strength;
+
+	if (!uadk_prov_securitycheck_enabled(ctx))
+		return UADK_P_SUCCESS;
+
+	if (!group) {
+		fprintf(stderr, "invalid: group is NULL!\n");
+		return UADK_P_FAIL;
+	}
+
+	nid = EC_GROUP_get_curve_name(group);
+	if (nid == NID_undef) {
+		fprintf(stderr, "invalid: explicit curves are not allowed in fips mode!\n");
+		return UADK_P_FAIL;
+	}
+
+	curve_name = EC_curve_nid2nist(nid);
+	if (!curve_name) {
+		fprintf(stderr, "invalid: Curve %s is not approved in FIPS mode!\n",
+			curve_name);
+		return UADK_P_FAIL;
+	}
+
+	/*
+	 * For EC the security strength is the (order_bits / 2)
+	 * e.g. P-224 is 112 bits.
+	 */
+	strength = (unsigned int)EC_GROUP_order_bits(group) >> 1;
+	/* The min security strength allowed for legacy verification is 80 bits */
+	if (strength < UADK_PROV_SECURITY_BITS) {
+		fprintf(stderr, "invalid: Curve %s strength %d is not approved in FIPS mode!\n",
+			curve_name, strength);
+		return UADK_P_FAIL;
+	}
+
+	/*
+	 * For signing or key agreement only allow curves with at least 112 bits of
+	 * security strength
+	 */
+	if (protect && strength < UADK_PROV_MIN_BITS) {
+		fprintf(stderr, "invalid: Curve %s strength %d cannot be used for signing\n",
+			curve_name, strength);
+		return UADK_P_FAIL;
+	}
+
+	return UADK_P_SUCCESS;
+}
+#endif /* OPENSSL_NO_FIPS_SECURITYCHECKS */
diff --git a/src/uadk_prov_pkey.h b/src/uadk_prov_pkey.h
index 1d4911c..fbec388 100644
--- a/src/uadk_prov_pkey.h
+++ b/src/uadk_prov_pkey.h
@@ -452,5 +452,11 @@ void uadk_prov_asym_cipher_alg(void);
 int uadk_prov_asym_cipher_get_support_state(int alg_tag);
 int uadk_prov_ecc_init(const char *alg_name);
 int uadk_prov_ecc_bit_check(const EC_GROUP *group);
+bool uadk_prov_support_algorithm(const char *alg);
+int uadk_prov_get_affine_coordinates(const EC_GROUP *group, const EC_POINT *p,
+				     BIGNUM *x, BIGNUM *y, BN_CTX *ctx);
+void uadk_prov_keyexch_alg(void);
+int uadk_prov_securitycheck_enabled(OSSL_LIB_CTX *ctx);
+int uadk_prov_ecc_check_key(OSSL_LIB_CTX *ctx, const EC_KEY *ec, int protect);
 
 #endif
-- 
2.25.1

