From 4bd88135df61ce9844ac5487198c02a49d4ef748 Mon Sep 17 00:00:00 2001
From: Chenghai Huang <huangchenghai2@huawei.com>
Date: Thu, 19 Dec 2024 15:57:48 +0800
Subject: [PATCH 01/10] uadk_provider: add aead alg for uadk_provider in
 openssl3

The following 3 AES algorithms are added:
AES-128-GCM AES-192-GCM AES-256-GCM

Signed-off-by: Chenghai Huang <huangchenghai2@huawei.com>
Signed-off-by: JiangShui Yang <yangjiangshui@h-partners.com>
---
 src/Makefile.am      |    2 +-
 src/uadk_prov.h      |    4 +
 src/uadk_prov_aead.c | 1030 ++++++++++++++++++++++++++++++++++++++++++
 src/uadk_prov_init.c |    7 +
 4 files changed, 1042 insertions(+), 1 deletion(-)
 create mode 100644 src/uadk_prov_aead.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 54c00e7..921305b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -65,7 +65,7 @@ uadk_provider_la_SOURCES=uadk_prov_init.c uadk_async.c uadk_utils.c \
 			 uadk_prov_rsa.c uadk_prov_dh.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_ffc.c uadk_prov_aead.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 8756fc6..ac82245 100644
--- a/src/uadk_prov.h
+++ b/src/uadk_prov.h
@@ -162,6 +162,9 @@ extern const OSSL_DISPATCH uadk_sm4_ecb_functions[FUNC_MAX_NUM];
 extern const OSSL_DISPATCH uadk_sm4_ofb128_functions[FUNC_MAX_NUM];
 extern const OSSL_DISPATCH uadk_sm4_cfb128_functions[FUNC_MAX_NUM];
 extern const OSSL_DISPATCH uadk_sm4_ctr_functions[FUNC_MAX_NUM];
+extern const OSSL_DISPATCH uadk_aes_128_gcm_functions[FUNC_MAX_NUM];
+extern const OSSL_DISPATCH uadk_aes_192_gcm_functions[FUNC_MAX_NUM];
+extern const OSSL_DISPATCH uadk_aes_256_gcm_functions[FUNC_MAX_NUM];
 extern const OSSL_DISPATCH uadk_des_ede3_cbc_functions[];
 extern const OSSL_DISPATCH uadk_des_ede3_ecb_functions[];
 
@@ -178,6 +181,7 @@ extern const OSSL_DISPATCH uadk_sm2_asym_cipher_functions[FUNC_MAX_NUM];
 
 void uadk_prov_destroy_digest(void);
 void uadk_prov_destroy_cipher(void);
+void uadk_prov_destroy_aead(void);
 void uadk_prov_destroy_rsa(void);
 void uadk_prov_destroy_dh(void);
 void uadk_prov_sm2_uninit(void);
diff --git a/src/uadk_prov_aead.c b/src/uadk_prov_aead.c
new file mode 100644
index 0000000..d6e90ca
--- /dev/null
+++ b/src/uadk_prov_aead.c
@@ -0,0 +1,1030 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2023-2024 Huawei Technologies Co.,Ltd. All rights reserved.
+ * Copyright 2023-2024 Linaro ltd.
+ *
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <numa.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include <uadk/wd_aead.h>
+#include <uadk/wd_sched.h>
+#include "uadk.h"
+#include "uadk_async.h"
+#include "uadk_prov.h"
+
+#define MAX_IV_LEN			16
+#define MAX_KEY_LEN			64
+#define MAX_AAD_LEN			0xFFFF
+#define ALG_NAME_SIZE			128
+#define AES_GCM_TAG_LEN			16
+/* The max data length is 16M-512B */
+#define AEAD_BLOCK_SIZE			0xFFFE00
+
+#define UADK_OSSL_FAIL			0
+#define UADK_AEAD_SUCCESS		1
+#define UADK_AEAD_FAIL			(-1)
+
+#define UNINITIALISED_SIZET		((size_t)-1)
+#define IV_STATE_UNINITIALISED		0
+#define IV_STATE_SET			1
+#define KEY_STATE_SET			1
+
+/* Internal flags that can be queried */
+#define PROV_CIPHER_FLAG_AEAD		0x0001
+#define PROV_CIPHER_FLAG_CUSTOM_IV	0x0002
+#define AEAD_FLAGS			(PROV_CIPHER_FLAG_AEAD | PROV_CIPHER_FLAG_CUSTOM_IV)
+
+#define UADK_AEAD_DEF_CTXS		2
+#define UADK_AEAD_OP_NUM		1
+
+struct aead_prov {
+	int pid;
+};
+static struct aead_prov aprov;
+static pthread_mutex_t aead_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum uadk_aead_mode {
+	UNINIT_MODE,
+	ASYNC_MODE,
+	SYNC_MODE
+};
+
+enum aead_tag_status {
+	INIT_TAG,
+	READ_TAG,    /* The MAC has been read. */
+	SET_TAG      /* The MAC has been set to req. */
+};
+
+struct aead_priv_ctx {
+	int nid;
+	char alg_name[ALG_NAME_SIZE];
+	size_t keylen;
+	size_t ivlen;
+	size_t taglen;
+
+	unsigned int enc : 1;
+	unsigned int key_set : 1;     /* Whether key is copied to priv key buffers */
+	unsigned int iv_set : 1;      /* Whether iv is copied to priv iv buffers */
+	enum aead_tag_status tag_set; /* Whether mac is copied to priv mac buffers */
+
+	unsigned char iv[MAX_IV_LEN];
+	unsigned char key[MAX_KEY_LEN];
+	unsigned char buf[AES_GCM_TAG_LEN];       /* mac buffers */
+	unsigned char *data;          /* store input and output when block mode */
+
+	struct wd_aead_sess_setup setup;
+	struct wd_aead_req req;
+	enum uadk_aead_mode mode;
+	handle_t sess;
+};
+
+struct aead_info {
+	int nid;
+	enum wd_cipher_alg alg;
+	enum wd_cipher_mode mode;
+};
+
+static struct aead_info aead_info_table[] = {
+	{ NID_aes_128_gcm, WD_CIPHER_AES, WD_CIPHER_GCM },
+	{ NID_aes_192_gcm, WD_CIPHER_AES, WD_CIPHER_GCM },
+	{ NID_aes_256_gcm, WD_CIPHER_AES, WD_CIPHER_GCM }
+};
+
+static int uadk_aead_poll(void *ctx)
+{
+	__u64 rx_cnt = 0;
+	__u32 recv = 0;
+	/* Poll one packet currently */
+	int expt = 1;
+	int ret;
+
+	do {
+		ret = wd_aead_poll(expt, &recv);
+		if (ret < 0 || recv >= expt)
+			return ret;
+		rx_cnt++;
+	} while (rx_cnt < ENGINE_RECV_MAX_CNT);
+
+	fprintf(stderr, "failed to poll msg: timeout!\n");
+
+	return -ETIMEDOUT;
+}
+
+static void uadk_aead_mutex_infork(void)
+{
+	/* Release the replication lock of the child process */
+	pthread_mutex_unlock(&aead_mutex);
+}
+
+static int uadk_prov_aead_dev_init(struct aead_priv_ctx *priv)
+{
+	struct wd_ctx_nums ctx_set_num;
+	struct wd_ctx_params cparams = {0};
+	int ret = UADK_AEAD_SUCCESS;
+
+	pthread_atfork(NULL, NULL, uadk_aead_mutex_infork);
+	pthread_mutex_lock(&aead_mutex);
+	if (aprov.pid == getpid())
+		goto mutex_unlock;
+
+	cparams.op_type_num = UADK_AEAD_OP_NUM;
+	cparams.ctx_set_num = &ctx_set_num;
+	cparams.bmp = numa_allocate_nodemask();
+	if (!cparams.bmp) {
+		ret = UADK_AEAD_FAIL;
+		fprintf(stderr, "failed to create nodemask!\n");
+		goto mutex_unlock;
+	}
+
+	numa_bitmask_setall(cparams.bmp);
+
+	ctx_set_num.sync_ctx_num = UADK_AEAD_DEF_CTXS;
+	ctx_set_num.async_ctx_num = UADK_AEAD_DEF_CTXS;
+
+	ret = wd_aead_init2_(priv->alg_name, TASK_MIX, SCHED_POLICY_RR, &cparams);
+	if (unlikely(ret)) {
+		ret = UADK_AEAD_FAIL;
+		fprintf(stderr, "failed to init aead!\n");
+		goto free_nodemask;
+	}
+
+	aprov.pid = getpid();
+	async_register_poll_fn(ASYNC_TASK_AEAD, uadk_aead_poll);
+
+free_nodemask:
+	numa_free_nodemask(cparams.bmp);
+mutex_unlock:
+	pthread_mutex_unlock(&aead_mutex);
+	return ret;
+}
+
+static int uadk_prov_aead_ctx_init(struct aead_priv_ctx *priv)
+{
+	struct wd_aead_sess_setup setup = {0};
+	struct sched_params params = {0};
+	int ret;
+
+	if (!priv->key_set || !priv->iv_set) {
+		fprintf(stderr, "key or iv is not set yet!\n");
+		return UADK_AEAD_FAIL;
+	}
+
+	priv->req.iv_bytes = priv->ivlen;
+	priv->req.iv = priv->iv;
+	priv->req.out_bytes = 0;
+	priv->req.mac = priv->buf;
+	priv->req.mac_bytes = priv->taglen;
+
+	ret = uadk_prov_aead_dev_init(priv);
+	if (unlikely(ret < 0))
+		return UADK_AEAD_FAIL;
+
+	/* dec and enc use the same op */
+	params.type = 0;
+	/* Use the default numa parameters */
+	params.numa_id = -1;
+	memcpy(&setup, &priv->setup, sizeof(struct wd_aead_sess_setup));
+	setup.sched_param = &params;
+
+	if (!priv->sess) {
+		priv->sess = wd_aead_alloc_sess(&setup);
+		if (!priv->sess) {
+			fprintf(stderr, "uadk failed to alloc session!\n");
+			return UADK_AEAD_FAIL;
+		}
+
+		ret = wd_aead_set_authsize(priv->sess, priv->taglen);
+		if (ret) {
+			fprintf(stderr, "uadk failed to set authsize!\n");
+			goto free_sess;
+		}
+
+		ret = wd_aead_set_ckey(priv->sess, priv->key, priv->keylen);
+		if (ret) {
+			fprintf(stderr, "uadk failed to set key!\n");
+			goto free_sess;
+		}
+	}
+
+	return UADK_AEAD_SUCCESS;
+
+free_sess:
+	wd_aead_free_sess(priv->sess);
+	priv->sess = 0;
+	return UADK_AEAD_FAIL;
+}
+
+static void *uadk_prov_aead_cb(struct wd_aead_req *req, void *data)
+{
+	struct uadk_e_cb_info *cb_param;
+	struct wd_aead_req *req_origin;
+	struct async_op *op;
+
+	if (!req)
+		return NULL;
+
+	cb_param = req->cb_param;
+	if (!cb_param)
+		return NULL;
+
+	req_origin = cb_param->priv;
+	req_origin->state = req->state;
+
+	op = cb_param->op;
+	if (op && op->job && !op->done) {
+		op->done = 1;
+		async_free_poll_task(op->idx, 1);
+		async_wake_job(op->job);
+	}
+
+	return NULL;
+}
+
+static int do_aes_gcm_prepare(struct aead_priv_ctx *priv)
+{
+	if (priv->mode == UNINIT_MODE) {
+		if (ASYNC_get_current_job())
+			priv->mode = ASYNC_MODE;
+		else
+			priv->mode = SYNC_MODE;
+	}
+
+	if (!priv->enc && priv->tag_set == READ_TAG) {
+		if (likely(priv->taglen == AES_GCM_TAG_LEN)) {
+			memcpy(priv->req.mac, priv->buf, AES_GCM_TAG_LEN);
+			priv->tag_set = SET_TAG;
+		} else {
+			fprintf(stderr, "invalid: aead gcm mac length only support 16B.\n");
+			return UADK_AEAD_FAIL;
+		}
+	}
+
+	return UADK_AEAD_SUCCESS;
+}
+
+static void uadk_do_aead_async_prepare(struct aead_priv_ctx *priv, unsigned char *out,
+				  const unsigned char *in, size_t inlen)
+{
+	priv->req.in_bytes = inlen;
+	/* AAD data is input or output together with plaintext or ciphertext. */
+	if (priv->req.assoc_bytes) {
+		memcpy(priv->data + priv->req.assoc_bytes, in, inlen);
+		priv->req.src = priv->data;
+		priv->req.dst = priv->data + AEAD_BLOCK_SIZE;
+	} else {
+		priv->req.src = (unsigned char *)in;
+		priv->req.dst = out;
+	}
+}
+
+static int uadk_do_aead_sync_inner(struct aead_priv_ctx *priv, unsigned char *out,
+			      const unsigned char *in, size_t inlen, enum wd_aead_msg_state state)
+{
+	int ret;
+
+	if ((state == AEAD_MSG_BLOCK || state == AEAD_MSG_END)
+		&& !priv->enc && priv->tag_set != SET_TAG) {
+		fprintf(stderr, "The tag for synchronous decryption is not set.\n");
+		return UADK_AEAD_FAIL;
+	}
+
+	priv->req.msg_state = state;
+	priv->req.src = (unsigned char *)in;
+	priv->req.dst = out;
+	priv->req.in_bytes = inlen;
+	priv->req.state = 0;
+	ret = wd_do_aead_sync(priv->sess, &priv->req);
+	if (unlikely(ret < 0 || priv->req.state)) {
+		fprintf(stderr, "do aead task failed, msg state: %u, ret: %d, state: %u!\n",
+			state, ret, priv->req.state);
+		return UADK_AEAD_FAIL;
+	}
+
+	return inlen;
+}
+
+static int uadk_do_aead_sync(struct aead_priv_ctx *priv, unsigned char *out,
+			const unsigned char *in, size_t inlen)
+{
+	size_t nbytes, tail, processing_len, max_mid_len;
+	const unsigned char *in_block = in;
+	unsigned char *out_block = out;
+	int ret;
+
+	/* Due to a hardware limitation, zero-length aad using block mode. */
+	if (!priv->req.assoc_bytes)
+		return uadk_do_aead_sync_inner(priv, out, in, inlen, AEAD_MSG_BLOCK);
+
+	tail = inlen % AES_BLOCK_SIZE;
+	nbytes = inlen - tail;
+	max_mid_len = AEAD_BLOCK_SIZE - priv->req.assoc_bytes;
+
+	/* If the data length is not 16-byte aligned, it is split according to the protocol. */
+	while (nbytes > 0) {
+		processing_len = nbytes > max_mid_len ? max_mid_len : nbytes;
+		processing_len -= (processing_len % AES_BLOCK_SIZE);
+
+		ret = uadk_do_aead_sync_inner(priv, out_block, in_block,
+						processing_len, AEAD_MSG_MIDDLE);
+		if (ret < 0)
+			return UADK_AEAD_FAIL;
+		nbytes -= processing_len;
+		in_block = in_block + processing_len;
+		out_block = out_block + processing_len;
+	}
+
+	if (tail) {
+		ret = uadk_do_aead_sync_inner(priv, out_block, in_block, tail, AEAD_MSG_END);
+		if (ret < 0)
+			return UADK_AEAD_FAIL;
+	}
+
+	return inlen;
+}
+
+static int uadk_do_aead_async(struct aead_priv_ctx *priv, struct async_op *op,
+			 unsigned char *out, const unsigned char *in, size_t inlen)
+{
+	struct uadk_e_cb_info *cb_param;
+	int cnt = 0;
+	int ret;
+
+	if (!priv->enc && priv->tag_set != SET_TAG) {
+		fprintf(stderr, "The tag for asynchronous decryption is not set.\n");
+		return UADK_AEAD_FAIL;
+	}
+
+	if (unlikely(priv->req.assoc_bytes + inlen > AEAD_BLOCK_SIZE)) {
+		fprintf(stderr, "aead input data length is too long!\n");
+		return UADK_AEAD_FAIL;
+	}
+
+	uadk_do_aead_async_prepare(priv, out, in, inlen);
+
+	cb_param = malloc(sizeof(struct uadk_e_cb_info));
+	if (unlikely(!cb_param)) {
+		fprintf(stderr, "failed to alloc cb_param.\n");
+		return UADK_AEAD_FAIL;
+	}
+
+	cb_param->op = op;
+	cb_param->priv = &priv->req;
+	priv->req.cb = uadk_prov_aead_cb;
+	priv->req.cb_param = cb_param;
+	priv->req.msg_state = AEAD_MSG_BLOCK;
+	priv->req.state = UADK_AEAD_FAIL;
+
+	ret = async_get_free_task(&op->idx);
+	if (unlikely(!ret))
+		goto free_cb_param;
+
+	do {
+		ret = wd_do_aead_async(priv->sess, &priv->req);
+		if (unlikely(ret < 0)) {
+			if (unlikely(ret != -EBUSY))
+				fprintf(stderr, "do aead async operation failed ret = %d.\n", ret);
+			else if (unlikely(cnt++ > ENGINE_SEND_MAX_CNT))
+				fprintf(stderr, "do aead async operation timeout.\n");
+			else
+				continue;
+
+			async_free_poll_task(op->idx, 0);
+			ret = UADK_AEAD_FAIL;
+			goto free_cb_param;
+		}
+	} while (ret == -EBUSY);
+
+	ret = async_pause_job(priv, op, ASYNC_TASK_AEAD);
+	if (unlikely(!ret || priv->req.state)) {
+		fprintf(stderr, "do aead async job failed, ret: %d, state: %u!\n",
+			ret, priv->req.state);
+		ret = UADK_AEAD_FAIL;
+		goto free_cb_param;
+	}
+
+	if (priv->req.assoc_bytes)
+		memcpy(out, priv->req.dst + priv->req.assoc_bytes, inlen);
+
+free_cb_param:
+	free(cb_param);
+	return ret;
+}
+
+static int uadk_prov_do_aes_gcm_first(struct aead_priv_ctx *priv, unsigned char *out,
+				   const unsigned char *in, size_t inlen)
+{
+	int ret;
+
+	if (inlen > MAX_AAD_LEN) {
+		fprintf(stderr, "the aad len is out of range, aad len = %lu.\n", inlen);
+		return UADK_AEAD_FAIL;
+	}
+
+	priv->req.assoc_bytes = inlen;
+
+	/* Asynchronous jobs use the block mode. */
+	if (priv->mode == ASYNC_MODE || !priv->req.assoc_bytes) {
+		memcpy(priv->data, in, inlen);
+		return UADK_AEAD_SUCCESS;
+	}
+
+	ret = uadk_do_aead_sync_inner(priv, out, in, inlen, AEAD_MSG_FIRST);
+	if (unlikely(ret < 0))
+		return UADK_AEAD_FAIL;
+
+	return UADK_AEAD_SUCCESS;
+}
+
+static int uadk_prov_do_aes_gcm_update(struct aead_priv_ctx *priv, unsigned char *out,
+				    const unsigned char *in, size_t inlen)
+{
+	struct async_op *op;
+	int ret;
+
+	if (priv->mode == ASYNC_MODE) {
+		op = malloc(sizeof(struct async_op));
+		if (unlikely(!op))
+			return UADK_AEAD_FAIL;
+
+		ret = async_setup_async_event_notification(op);
+		if (unlikely(!ret)) {
+			fprintf(stderr, "failed to setup async event notification.\n");
+			goto free_op;
+		}
+
+		ret = uadk_do_aead_async(priv, op, out, in, inlen);
+		if (unlikely(ret < 0)) {
+			fprintf(stderr, "uadk_do_aead_async failed ret = %d.\n", ret);
+			goto free_notification;
+		}
+
+		free(op);
+		return inlen;
+	}
+
+	return uadk_do_aead_sync(priv, out, in, inlen);
+
+free_notification:
+	(void)async_clear_async_event_notification();
+free_op:
+	free(op);
+	return UADK_AEAD_FAIL;
+}
+
+static int uadk_prov_do_aes_gcm_final(struct aead_priv_ctx *priv, unsigned char *out,
+				   const unsigned char *in, size_t inlen)
+{
+	int ret;
+
+	if (priv->mode == ASYNC_MODE || !priv->req.assoc_bytes ||
+	    priv->req.msg_state == AEAD_MSG_END)
+		goto out;
+
+	ret = uadk_do_aead_sync_inner(priv, out, in, inlen, AEAD_MSG_END);
+	if (unlikely(ret < 0))
+		return UADK_AEAD_FAIL;
+
+out:
+	if (priv->enc)
+		memcpy(priv->buf, priv->req.mac, priv->taglen);
+	else
+		priv->tag_set = INIT_TAG;
+
+	priv->mode = UNINIT_MODE;
+	return UADK_AEAD_SUCCESS;
+}
+
+static int uadk_prov_do_aes_gcm(struct aead_priv_ctx *priv, unsigned char *out,
+			       size_t *outl, size_t outsize,
+			       const unsigned char *in, size_t inlen)
+{
+	int ret;
+
+	ret = uadk_prov_aead_ctx_init(priv);
+	if (ret != UADK_AEAD_SUCCESS)
+		return UADK_AEAD_FAIL;
+
+	ret = do_aes_gcm_prepare(priv);
+	if (unlikely(ret < 0))
+		return UADK_AEAD_FAIL;
+
+	if (in) {
+		if (!out)
+			return uadk_prov_do_aes_gcm_first(priv, out, in, inlen);
+
+		return uadk_prov_do_aes_gcm_update(priv, out, in, inlen);
+	}
+
+	return uadk_prov_do_aes_gcm_final(priv, out, NULL, 0);
+}
+
+void uadk_prov_destroy_aead(void)
+{
+	pthread_mutex_lock(&aead_mutex);
+	if (aprov.pid == getpid()) {
+		wd_aead_uninit2();
+		aprov.pid = 0;
+	}
+	pthread_mutex_unlock(&aead_mutex);
+}
+
+static OSSL_FUNC_cipher_encrypt_init_fn uadk_prov_aead_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn uadk_prov_aead_dinit;
+static OSSL_FUNC_cipher_freectx_fn uadk_prov_aead_freectx;
+static OSSL_FUNC_cipher_dupctx_fn uadk_prov_aead_dupctx;
+static OSSL_FUNC_cipher_get_ctx_params_fn uadk_prov_aead_get_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn uadk_prov_aead_gettable_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn uadk_prov_aead_set_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn uadk_prov_aead_settable_ctx_params;
+
+static int uadk_prov_aead_cipher(void *vctx, unsigned char *out, size_t *outl,
+				   size_t outsize, const unsigned char *in,
+				   size_t inl)
+{
+	struct aead_priv_ctx *priv = (struct aead_priv_ctx *)vctx;
+	int ret;
+
+	if (!vctx || !out || !outl)
+		return UADK_OSSL_FAIL;
+
+	if (outsize < inl) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+		return UADK_OSSL_FAIL;
+	}
+
+	ret = uadk_prov_do_aes_gcm(priv, out, outl, outsize, in, inl);
+	if (ret < 0)
+		return UADK_OSSL_FAIL;
+
+	*outl = inl;
+	return UADK_AEAD_SUCCESS;
+}
+
+static int uadk_prov_aead_stream_update(void *vctx, unsigned char *out,
+					  size_t *outl, size_t outsize,
+					  const unsigned char *in, size_t inl)
+{
+	struct aead_priv_ctx *priv = (struct aead_priv_ctx *)vctx;
+	int ret;
+
+	if (!vctx)
+		return UADK_OSSL_FAIL;
+
+	if (outsize < inl) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+		return UADK_OSSL_FAIL;
+	}
+
+	ret = uadk_prov_do_aes_gcm(priv, out, outl, outsize, in, inl);
+	if (ret < 0) {
+		fprintf(stderr, "stream data update failed.\n");
+		return UADK_OSSL_FAIL;
+	}
+
+	*outl = inl;
+	return UADK_AEAD_SUCCESS;
+}
+
+static int uadk_prov_aead_stream_final(void *vctx, unsigned char *out,
+					 size_t *outl, size_t outsize)
+{
+	struct aead_priv_ctx *priv = (struct aead_priv_ctx *)vctx;
+	int ret;
+
+	if (!vctx || !out || !outl)
+		return UADK_OSSL_FAIL;
+
+	ret = uadk_prov_do_aes_gcm(priv, out, outl, outsize, NULL, 0);
+	if (ret < 0) {
+		fprintf(stderr, "stream data final failed, ret = %d\n", ret);
+		return UADK_OSSL_FAIL;
+	}
+
+	*outl = 0;
+	return UADK_AEAD_SUCCESS;
+}
+
+static int uadk_get_aead_info(struct aead_priv_ctx *priv)
+{
+	int aead_counts = ARRAY_SIZE(aead_info_table);
+	int i;
+
+	for (i = 0; i < aead_counts; i++) {
+		if (priv->nid == aead_info_table[i].nid) {
+			priv->setup.calg = aead_info_table[i].alg;
+			priv->setup.cmode = aead_info_table[i].mode;
+			break;
+		}
+	}
+
+	if (unlikely(i == aead_counts)) {
+		fprintf(stderr, "failed to setup the private ctx.\n");
+		return UADK_AEAD_FAIL;
+	}
+
+	return UADK_AEAD_SUCCESS;
+}
+
+static int uadk_prov_aead_init(struct aead_priv_ctx *priv,
+				 const unsigned char *key, size_t keylen,
+				 const unsigned char *iv, size_t ivlen)
+{
+	int ret;
+
+	if (ivlen > MAX_IV_LEN || keylen > MAX_KEY_LEN) {
+		fprintf(stderr, "invalid keylen or ivlen.\n");
+		return UADK_OSSL_FAIL;
+	}
+
+	if (iv) {
+		memcpy(priv->iv, iv, ivlen);
+		priv->iv_set = IV_STATE_SET;
+	}
+
+	ret = uadk_get_aead_info(priv);
+	if (unlikely(ret < 0))
+		return UADK_OSSL_FAIL;
+
+	if (key) {
+		memcpy(priv->key, key, keylen);
+		priv->key_set = KEY_STATE_SET;
+	}
+
+	ret = uadk_prov_aead_dev_init(priv);
+	if (unlikely(ret < 0))
+		return UADK_OSSL_FAIL;
+
+	return UADK_AEAD_SUCCESS;
+}
+
+static int uadk_prov_aead_einit(void *vctx, const unsigned char *key, size_t keylen,
+				  const unsigned char *iv, size_t ivlen,
+				  const OSSL_PARAM params[])
+{
+	struct aead_priv_ctx *priv = (struct aead_priv_ctx *)vctx;
+
+	if (!vctx)
+		return UADK_OSSL_FAIL;
+
+	priv->req.op_type = WD_CIPHER_ENCRYPTION_DIGEST;
+	priv->enc = 1;
+
+	return uadk_prov_aead_init(priv, key, keylen, iv, ivlen);
+}
+
+static int uadk_prov_aead_dinit(void *vctx, const unsigned char *key, size_t keylen,
+				  const unsigned char *iv, size_t ivlen,
+				  const OSSL_PARAM params[])
+{
+	struct aead_priv_ctx *priv = (struct aead_priv_ctx *)vctx;
+
+	if (!vctx)
+		return UADK_OSSL_FAIL;
+
+	priv->req.op_type = WD_CIPHER_DECRYPTION_DIGEST;
+	priv->enc = 0;
+
+	return uadk_prov_aead_init(priv, key, keylen, iv, ivlen);
+}
+
+static const OSSL_PARAM uadk_prov_settable_ctx_params[] = {
+	OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+	OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, NULL),
+	OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+	OSSL_PARAM_END
+};
+
+const OSSL_PARAM *uadk_prov_aead_settable_ctx_params(ossl_unused void *cctx,
+						       ossl_unused void *provctx)
+{
+	return uadk_prov_settable_ctx_params;
+}
+
+static int uadk_prov_aead_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+	struct aead_priv_ctx *priv = (struct aead_priv_ctx *)vctx;
+	const OSSL_PARAM *p;
+	size_t sz = 0;
+	void *vp;
+
+	if (!vctx)
+		return UADK_OSSL_FAIL;
+
+	p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+	if (p) {
+		vp = priv->buf;
+		if (!OSSL_PARAM_get_octet_string(p, &vp, EVP_GCM_TLS_TAG_LEN, &sz)) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+			return UADK_OSSL_FAIL;
+		}
+
+		if (sz == 0 || priv->enc) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG);
+			return UADK_OSSL_FAIL;
+		}
+		priv->tag_set = READ_TAG;
+		priv->taglen = sz;
+	}
+
+	p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+	if (p) {
+		size_t keylen;
+
+		if (!OSSL_PARAM_get_size_t(p, &keylen)) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+			return UADK_OSSL_FAIL;
+		}
+		if (priv->keylen != keylen) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+			return UADK_OSSL_FAIL;
+		}
+	}
+
+	p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_IVLEN);
+	if (p) {
+		if (!OSSL_PARAM_get_size_t(p, &sz)) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+			return UADK_OSSL_FAIL;
+		}
+		if (sz == 0 || sz > priv->ivlen) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+			return UADK_OSSL_FAIL;
+		}
+		priv->ivlen = sz;
+	}
+
+	return UADK_AEAD_SUCCESS;
+}
+
+static const OSSL_PARAM uadk_prov_aead_ctx_params[] = {
+	OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+	OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+	OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
+	OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+	OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0),
+	OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+	OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *uadk_prov_aead_gettable_ctx_params(ossl_unused void *cctx,
+							    ossl_unused void *provctx)
+{
+	return uadk_prov_aead_ctx_params;
+}
+
+static int uadk_prov_aead_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+	struct aead_priv_ctx *priv = (struct aead_priv_ctx *)vctx;
+	OSSL_PARAM *p;
+
+	if (!vctx || !params)
+		return UADK_OSSL_FAIL;
+
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+	if (p && !OSSL_PARAM_set_size_t(p, priv->ivlen)) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+		return UADK_OSSL_FAIL;
+	}
+
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+	if (p && !OSSL_PARAM_set_size_t(p, priv->keylen)) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+		return UADK_OSSL_FAIL;
+	}
+
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN);
+	if (p) {
+		size_t taglen = (priv->taglen != UNINITIALISED_SIZET) ?
+				priv->taglen : AES_GCM_TAG_LEN;
+
+		if (!OSSL_PARAM_set_size_t(p, taglen)) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+			return UADK_OSSL_FAIL;
+		}
+	}
+
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV);
+	if (p) {
+		if (priv->iv_set == IV_STATE_UNINITIALISED)
+			return UADK_OSSL_FAIL;
+		if (priv->ivlen > p->data_size) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+			return UADK_OSSL_FAIL;
+		}
+		if (!OSSL_PARAM_set_octet_string(p, priv->iv, priv->ivlen)
+			&& !OSSL_PARAM_set_octet_ptr(p, &priv->iv, priv->ivlen)) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+			return UADK_OSSL_FAIL;
+		}
+	}
+
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV);
+	if (p) {
+		if (priv->iv_set == IV_STATE_UNINITIALISED)
+			return UADK_OSSL_FAIL;
+		if (priv->ivlen > p->data_size) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+			return UADK_OSSL_FAIL;
+		}
+		if (!OSSL_PARAM_set_octet_string(p, priv->iv, priv->ivlen)
+			&& !OSSL_PARAM_set_octet_ptr(p, &priv->iv, priv->ivlen)) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+			return UADK_OSSL_FAIL;
+		}
+	}
+
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+	if (p) {
+		size_t sz = p->data_size;
+
+		if (sz == 0 || sz > EVP_GCM_TLS_TAG_LEN || !priv->enc
+			|| priv->taglen == UNINITIALISED_SIZET) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG);
+			return UADK_OSSL_FAIL;
+		}
+
+		if (!OSSL_PARAM_set_octet_string(p, priv->buf, sz)) {
+			ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+			return UADK_OSSL_FAIL;
+		}
+	}
+	return UADK_AEAD_SUCCESS;
+}
+
+static const OSSL_PARAM aead_known_gettable_params[] = {
+	OSSL_PARAM_uint(OSSL_CIPHER_PARAM_MODE, NULL),
+	OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+	OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+	OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, NULL),
+	OSSL_PARAM_int(OSSL_CIPHER_PARAM_AEAD, NULL),
+	OSSL_PARAM_int(OSSL_CIPHER_PARAM_CUSTOM_IV, NULL),
+	OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *uadk_prov_aead_gettable_params(ossl_unused void *provctx)
+{
+	return aead_known_gettable_params;
+}
+
+static int uadk_cipher_aead_get_params(OSSL_PARAM params[], unsigned int md,
+					  uint64_t flags, size_t kbits,
+					  size_t blkbits, size_t ivbits)
+{
+	OSSL_PARAM *p;
+
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE);
+	if (p && !OSSL_PARAM_set_uint(p, md)) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+		return UADK_OSSL_FAIL;
+	}
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD);
+	if (p && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_AEAD) != 0)) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+		return UADK_OSSL_FAIL;
+	}
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CUSTOM_IV);
+	if (p && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_CUSTOM_IV) != 0)) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+		return UADK_OSSL_FAIL;
+	}
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+	if (p && !OSSL_PARAM_set_size_t(p, kbits)) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+		return UADK_OSSL_FAIL;
+	}
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE);
+	if (p && !OSSL_PARAM_set_size_t(p, blkbits)) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+		return UADK_OSSL_FAIL;
+	}
+	p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+	if (p && !OSSL_PARAM_set_size_t(p, ivbits)) {
+		ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+		return UADK_OSSL_FAIL;
+	}
+
+	return UADK_AEAD_SUCCESS;
+}
+
+static void *uadk_prov_aead_dupctx(void *ctx)
+{
+	struct aead_priv_ctx *priv, *ret;
+
+	priv = (struct aead_priv_ctx *)ctx;
+	if (!priv)
+		return NULL;
+
+	ret = OPENSSL_memdup(priv, sizeof(*priv));
+	if (!ret)
+		return NULL;
+
+	ret->sess = 0;
+	ret->data = OPENSSL_memdup(priv->data, AEAD_BLOCK_SIZE << 1);
+	if (!ret->data) {
+		OPENSSL_clear_free(ret, sizeof(*ret));
+		return NULL;
+	}
+
+	return ret;
+}
+
+static void uadk_prov_aead_freectx(void *ctx)
+{
+	struct aead_priv_ctx *priv = (struct aead_priv_ctx *)ctx;
+
+	if (!ctx)
+		return;
+
+	if (priv->sess) {
+		wd_aead_free_sess(priv->sess);
+		priv->sess = 0;
+	}
+
+	if (priv->data) {
+		OPENSSL_clear_free(priv->data, AEAD_BLOCK_SIZE << 1);
+		priv->data = NULL;
+	}
+	OPENSSL_clear_free(priv, sizeof(*priv));
+}
+
+#define UADK_AEAD_DESCR(nm, tag_len, key_len, iv_len, blk_size,			\
+			flags, e_nid, algnm, mode)				\
+static OSSL_FUNC_cipher_newctx_fn uadk_##nm##_newctx;				\
+static void *uadk_##nm##_newctx(void *provctx)					\
+{										\
+	struct aead_priv_ctx *ctx = OPENSSL_zalloc(sizeof(*ctx));		\
+	if (!ctx)								\
+		return NULL;							\
+										\
+	ctx->data = OPENSSL_zalloc(AEAD_BLOCK_SIZE << 1);			\
+	if (!ctx->data) {							\
+		OPENSSL_clear_free(ctx, sizeof(*ctx));				\
+		return NULL;							\
+	}									\
+										\
+	ctx->keylen = key_len;							\
+	ctx->ivlen = iv_len;							\
+	ctx->nid = e_nid;							\
+	ctx->taglen = tag_len;							\
+	strncpy(ctx->alg_name, #algnm, ALG_NAME_SIZE - 1);			\
+										\
+	return ctx;								\
+}										\
+static OSSL_FUNC_cipher_get_params_fn uadk_##nm##_get_params;			\
+static int uadk_##nm##_get_params(OSSL_PARAM params[])				\
+{										\
+	return uadk_cipher_aead_get_params(params, mode, flags,			\
+					      key_len, blk_size, iv_len);	\
+}										\
+const OSSL_DISPATCH uadk_##nm##_functions[] = {					\
+	{ OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))uadk_##nm##_newctx },	\
+	{ OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))uadk_prov_aead_freectx },	\
+	{ OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))uadk_prov_aead_dupctx },	\
+	{ OSSL_FUNC_CIPHER_ENCRYPT_INIT,					\
+		(void (*)(void))uadk_prov_aead_einit },				\
+	{ OSSL_FUNC_CIPHER_DECRYPT_INIT,					\
+		(void (*)(void))uadk_prov_aead_dinit },				\
+	{ OSSL_FUNC_CIPHER_UPDATE,						\
+		(void (*)(void))uadk_prov_aead_stream_update },			\
+	{ OSSL_FUNC_CIPHER_FINAL,						\
+		(void (*)(void))uadk_prov_aead_stream_final },			\
+	{ OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))uadk_prov_aead_cipher },	\
+	{ OSSL_FUNC_CIPHER_GET_PARAMS,						\
+		(void (*)(void))uadk_##nm##_get_params },			\
+	{ OSSL_FUNC_CIPHER_GETTABLE_PARAMS,					\
+		(void (*)(void))uadk_prov_aead_gettable_params },		\
+	{ OSSL_FUNC_CIPHER_GET_CTX_PARAMS,					\
+		(void (*)(void))uadk_prov_aead_get_ctx_params },		\
+	{ OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS,					\
+		(void (*)(void))uadk_prov_aead_gettable_ctx_params },		\
+	{ OSSL_FUNC_CIPHER_SET_CTX_PARAMS,					\
+		(void (*)(void))uadk_prov_aead_set_ctx_params },		\
+	{ OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS,					\
+		(void (*)(void))uadk_prov_aead_settable_ctx_params },		\
+	{ 0, NULL }								\
+}
+
+UADK_AEAD_DESCR(aes_128_gcm, AES_GCM_TAG_LEN, 16, 12, 8, AEAD_FLAGS, NID_aes_128_gcm, gcm(aes),
+		EVP_CIPH_GCM_MODE);
+UADK_AEAD_DESCR(aes_192_gcm, AES_GCM_TAG_LEN, 24, 12, 8, AEAD_FLAGS, NID_aes_192_gcm, gcm(aes),
+		EVP_CIPH_GCM_MODE);
+UADK_AEAD_DESCR(aes_256_gcm, AES_GCM_TAG_LEN, 32, 12, 8, AEAD_FLAGS, NID_aes_256_gcm, gcm(aes),
+		EVP_CIPH_GCM_MODE);
diff --git a/src/uadk_prov_init.c b/src/uadk_prov_init.c
index 972f953..772ddbb 100644
--- a/src/uadk_prov_init.c
+++ b/src/uadk_prov_init.c
@@ -137,6 +137,12 @@ const OSSL_ALGORITHM uadk_prov_ciphers_v3[] = {
 	  uadk_aes_192_cfb128_functions, "uadk_provider aes-192-cfb" },
 	{ "AES-256-CFB", UADK_DEFAULT_PROPERTIES,
 	  uadk_aes_256_cfb128_functions, "uadk_provider aes-256-cfb" },
+	{ "AES-128-GCM", UADK_DEFAULT_PROPERTIES,
+	  uadk_aes_128_gcm_functions, "uadk_provider aes-128-gcm" },
+	{ "AES-192-GCM", UADK_DEFAULT_PROPERTIES,
+	  uadk_aes_192_gcm_functions, "uadk_provider aes-192-gcm" },
+	{ "AES-256-GCM", UADK_DEFAULT_PROPERTIES,
+	  uadk_aes_256_gcm_functions, "uadk_provider aes-256-gcm" },
 	{ "SM4-CBC", UADK_DEFAULT_PROPERTIES,
 	  uadk_sm4_cbc_functions, "uadk_provider sm4-cbc" },
 	{ "SM4-ECB", UADK_DEFAULT_PROPERTIES,
@@ -232,6 +238,7 @@ static void uadk_teardown(void *provctx)
 
 	uadk_prov_destroy_digest();
 	uadk_prov_destroy_cipher();
+	uadk_prov_destroy_aead();
 	uadk_prov_destroy_rsa();
 	uadk_prov_sm2_uninit();
 	uadk_prov_dh_uninit();
-- 
2.25.1

