504 lines
14 KiB
Diff
504 lines
14 KiB
Diff
From a76e6fd7bf698c19a5195506f331c39c35b37e40 Mon Sep 17 00:00:00 2001
|
|
From: Wenkai Lin <linwenkai6@hisilicon.com>
|
|
Date: Thu, 9 Nov 2023 11:23:43 +0800
|
|
Subject: [PATCH 61/63] aead: fix for aes gcm update process
|
|
|
|
According to openssl use mode, in the cipher update stage, the input
|
|
data length should be the same as the output length, however, cached data
|
|
is used for block mode now, this patch is intended to fix the problem.
|
|
|
|
In the aead stream mode, the length of the final packet is 0 and can be
|
|
calculated in the final stage, in the block mode(like asynchronous and
|
|
aad 0-length scenarios), the calculation result must be returned
|
|
to the user in the update stage.
|
|
|
|
However, this adds an additional restriction, user need to set the mac
|
|
before the update stage.
|
|
|
|
Signed-off-by: Wenkai Lin <linwenkai6@hisilicon.com>
|
|
---
|
|
src/uadk_aead.c | 278 +++++++++++++++++++++++++++---------------------
|
|
1 file changed, 159 insertions(+), 119 deletions(-)
|
|
|
|
diff --git a/src/uadk_aead.c b/src/uadk_aead.c
|
|
index 00ba4d2..e27aba5 100644
|
|
--- a/src/uadk_aead.c
|
|
+++ b/src/uadk_aead.c
|
|
@@ -28,6 +28,7 @@
|
|
#include "uadk_utils.h"
|
|
|
|
#define RET_FAIL -1
|
|
+#define STATE_FAIL 0xFFFF
|
|
#define CTX_SYNC_ENC 0
|
|
#define CTX_SYNC_DEC 1
|
|
#define CTX_ASYNC_ENC 2
|
|
@@ -50,7 +51,7 @@ struct aead_priv_ctx {
|
|
unsigned char *data;
|
|
unsigned char iv[AES_GCM_BLOCK_SIZE];
|
|
unsigned char mac[AES_GCM_TAG_LEN];
|
|
- size_t last_update_bufflen;
|
|
+ int taglen;
|
|
};
|
|
|
|
struct aead_engine {
|
|
@@ -267,10 +268,8 @@ static int uadk_e_ctx_init(struct aead_priv_ctx *priv, const unsigned char *ckey
|
|
int ret;
|
|
|
|
ret = uadk_e_init_aead_cipher();
|
|
- if (unlikely(!ret)) {
|
|
- fprintf(stderr, "uadk failed to init aead HW!\n");
|
|
+ if (!ret)
|
|
return 0;
|
|
- }
|
|
|
|
params.type = priv->req.op_type;
|
|
ret = uadk_e_is_env_enabled("aead");
|
|
@@ -296,10 +295,14 @@ static int uadk_e_ctx_init(struct aead_priv_ctx *priv, const unsigned char *ckey
|
|
fprintf(stderr, "uadk engine failed to set ckey!\n");
|
|
goto out;
|
|
}
|
|
- priv->data = malloc(AEAD_BLOCK_SIZE << 1);
|
|
- if (unlikely(!priv->data)) {
|
|
- fprintf(stderr, "uadk engine failed to alloc data!\n");
|
|
- goto out;
|
|
+
|
|
+ if (ASYNC_get_current_job()) {
|
|
+ /* Memory needs to be reserved for both input and output. */
|
|
+ priv->data = malloc(AEAD_BLOCK_SIZE << 1);
|
|
+ if (unlikely(!priv->data)) {
|
|
+ fprintf(stderr, "uadk engine failed to alloc data!\n");
|
|
+ goto out;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -313,10 +316,15 @@ out:
|
|
static int uadk_e_aes_gcm_init(EVP_CIPHER_CTX *ctx, const unsigned char *ckey,
|
|
const unsigned char *iv, int enc)
|
|
{
|
|
- struct aead_priv_ctx *priv =
|
|
- (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
+ struct aead_priv_ctx *priv;
|
|
int ret, ckey_len;
|
|
|
|
+ priv = (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
+ if (!priv) {
|
|
+ fprintf(stderr, "invalid: aead priv ctx is NULL.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
if (unlikely(!ckey))
|
|
return 1;
|
|
|
|
@@ -328,7 +336,6 @@ static int uadk_e_aes_gcm_init(EVP_CIPHER_CTX *ctx, const unsigned char *ckey,
|
|
priv->setup.dalg = 0;
|
|
priv->setup.dmode = 0;
|
|
|
|
- priv->last_update_bufflen = 0;
|
|
priv->req.assoc_bytes = 0;
|
|
priv->req.out_bytes = 0;
|
|
priv->req.data_fmt = WD_FLAT_BUF;
|
|
@@ -339,6 +346,8 @@ static int uadk_e_aes_gcm_init(EVP_CIPHER_CTX *ctx, const unsigned char *ckey,
|
|
|
|
priv->req.mac = priv->mac;
|
|
priv->req.mac_bytes = AES_GCM_TAG_LEN;
|
|
+ priv->taglen = 0;
|
|
+ priv->data = NULL;
|
|
|
|
if (enc)
|
|
priv->req.op_type = WD_CIPHER_ENCRYPTION_DIGEST;
|
|
@@ -355,8 +364,13 @@ static int uadk_e_aes_gcm_init(EVP_CIPHER_CTX *ctx, const unsigned char *ckey,
|
|
|
|
static int uadk_e_aes_gcm_cleanup(EVP_CIPHER_CTX *ctx)
|
|
{
|
|
- struct aead_priv_ctx *priv =
|
|
- (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
+ struct aead_priv_ctx *priv;
|
|
+
|
|
+ priv = (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
+ if (!priv) {
|
|
+ fprintf(stderr, "invalid: aead priv ctx is NULL.\n");
|
|
+ return 0;
|
|
+ }
|
|
|
|
if (priv->sess) {
|
|
wd_aead_free_sess(priv->sess);
|
|
@@ -373,10 +387,15 @@ static int uadk_e_aes_gcm_cleanup(EVP_CIPHER_CTX *ctx)
|
|
|
|
static int uadk_e_aes_gcm_set_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
|
|
{
|
|
- struct aead_priv_ctx *priv =
|
|
- (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
void *ctx_buf = EVP_CIPHER_CTX_buf_noconst(ctx);
|
|
int enc = EVP_CIPHER_CTX_encrypting(ctx);
|
|
+ struct aead_priv_ctx *priv;
|
|
+
|
|
+ priv = (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
+ if (!priv) {
|
|
+ fprintf(stderr, "invalid: aead priv ctx is NULL.\n");
|
|
+ return 0;
|
|
+ }
|
|
|
|
switch (type) {
|
|
case EVP_CTRL_INIT:
|
|
@@ -387,7 +406,7 @@ static int uadk_e_aes_gcm_set_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void
|
|
return 1;
|
|
case EVP_CTRL_GCM_SET_IVLEN:
|
|
if (arg != AES_GCM_IV_LEN) {
|
|
- fprintf(stderr, "gcm only support 12 bytes.\n");
|
|
+ fprintf(stderr, "invalid: aead gcm iv length only support 12B.\n");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
@@ -416,6 +435,7 @@ static int uadk_e_aes_gcm_set_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void
|
|
}
|
|
|
|
memcpy(ctx_buf, ptr, arg);
|
|
+ priv->taglen = arg;
|
|
return 1;
|
|
default:
|
|
fprintf(stderr, "unsupported ctrl type: %d\n", type);
|
|
@@ -423,18 +443,16 @@ static int uadk_e_aes_gcm_set_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void
|
|
}
|
|
}
|
|
|
|
-static int uadk_e_do_aes_gcm_first(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
|
+static int uadk_e_do_aes_gcm_first(struct aead_priv_ctx *priv, unsigned char *out,
|
|
const unsigned char *in, size_t inlen)
|
|
{
|
|
- struct aead_priv_ctx *priv =
|
|
- (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
int ret;
|
|
|
|
priv->req.assoc_bytes = inlen;
|
|
|
|
+ /* Asynchronous jobs use the block mode. */
|
|
if (ASYNC_get_current_job()) {
|
|
- memcpy(priv->data + priv->last_update_bufflen, in, inlen);
|
|
- priv->last_update_bufflen += inlen;
|
|
+ memcpy(priv->data, in, inlen);
|
|
return 1;
|
|
}
|
|
|
|
@@ -442,68 +460,43 @@ static int uadk_e_do_aes_gcm_first(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
|
priv->req.msg_state = AEAD_MSG_FIRST;
|
|
|
|
ret = wd_do_aead_sync(priv->sess, &priv->req);
|
|
- if (ret < 0) {
|
|
- fprintf(stderr, "do sec aead first operation failed, ret:%d!\n", ret);
|
|
+ if (unlikely(ret < 0)) {
|
|
+ fprintf(stderr, "do aead first operation failed, ret: %d!\n", ret);
|
|
return RET_FAIL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
-static int uadk_e_hw_update(struct aead_priv_ctx *priv, unsigned char *out,
|
|
- unsigned char *in, size_t inlen)
|
|
+static int do_aead_sync(struct aead_priv_ctx *priv, unsigned char *out,
|
|
+ const unsigned char *in, size_t inlen)
|
|
{
|
|
int ret;
|
|
|
|
- priv->req.src = in;
|
|
+ /* Due to a hardware limitation, zero-length aad using block mode. */
|
|
+ if (priv->req.assoc_bytes)
|
|
+ priv->req.msg_state = AEAD_MSG_MIDDLE;
|
|
+ else
|
|
+ priv->req.msg_state = AEAD_MSG_BLOCK;
|
|
+
|
|
+ priv->req.src = (unsigned char *)in;
|
|
priv->req.dst = out;
|
|
priv->req.in_bytes = inlen;
|
|
- priv->req.msg_state = AEAD_MSG_MIDDLE;
|
|
+ priv->req.state = 0;
|
|
ret = wd_do_aead_sync(priv->sess, &priv->req);
|
|
- if (ret < 0) {
|
|
- fprintf(stderr, "do sec aead update operation failed, ret:%d!\n", ret);
|
|
+ if (ret < 0 || priv->req.state) {
|
|
+ fprintf(stderr, "do aead update operation failed, ret: %d, state: %u!\n",
|
|
+ ret, priv->req.state);
|
|
return RET_FAIL;
|
|
}
|
|
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int uadk_e_cache_data(struct aead_priv_ctx *priv, const unsigned char *in, size_t inlen)
|
|
-{
|
|
- if (ASYNC_get_current_job() || !priv->req.assoc_bytes) {
|
|
- if (priv->last_update_bufflen + inlen > AEAD_BLOCK_SIZE) {
|
|
- fprintf(stderr, "aead input data length is too long!\n");
|
|
- return RET_FAIL;
|
|
- }
|
|
- memcpy(priv->data + priv->last_update_bufflen, in, inlen);
|
|
- priv->last_update_bufflen += inlen;
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return 1;
|
|
-}
|
|
-
|
|
-static int uadk_e_do_aes_gcm_update(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
|
- const unsigned char *in, size_t inlen)
|
|
-{
|
|
- struct aead_priv_ctx *priv =
|
|
- (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
- int ret;
|
|
-
|
|
- ret = uadk_e_cache_data(priv, in, inlen);
|
|
- if (ret <= 0)
|
|
- return ret;
|
|
-
|
|
- ret = uadk_e_hw_update(priv, out, in, inlen);
|
|
- if (ret < 0)
|
|
- return RET_FAIL;
|
|
-
|
|
return inlen;
|
|
}
|
|
|
|
static void *uadk_e_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)
|
|
@@ -513,6 +506,9 @@ static void *uadk_e_aead_cb(struct wd_aead_req *req, void *data)
|
|
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;
|
|
@@ -523,35 +519,49 @@ static void *uadk_e_aead_cb(struct wd_aead_req *req, void *data)
|
|
return NULL;
|
|
}
|
|
|
|
-static int do_aead_async(struct aead_priv_ctx *priv, struct async_op *op)
|
|
+static int 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 ret = 0;
|
|
- int idx;
|
|
+ int ret;
|
|
|
|
- priv->req.in_bytes = priv->last_update_bufflen - priv->req.assoc_bytes;
|
|
- priv->req.dst = priv->data + AEAD_BLOCK_SIZE;
|
|
+ if (unlikely(priv->req.assoc_bytes + inlen > AEAD_BLOCK_SIZE)) {
|
|
+ fprintf(stderr, "aead input data length is too long!\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
|
|
cb_param = malloc(sizeof(struct uadk_e_cb_info));
|
|
- if (!cb_param) {
|
|
+ if (unlikely(!cb_param)) {
|
|
fprintf(stderr, "failed to alloc cb_param.\n");
|
|
- return ret;
|
|
+ return 0;
|
|
}
|
|
|
|
cb_param->op = op;
|
|
- cb_param->priv = priv;
|
|
+ cb_param->priv = &priv->req;
|
|
priv->req.cb = uadk_e_aead_cb;
|
|
priv->req.cb_param = cb_param;
|
|
+ priv->req.msg_state = AEAD_MSG_BLOCK;
|
|
+ priv->req.state = STATE_FAIL;
|
|
|
|
- ret = async_get_free_task(&idx);
|
|
- if (!ret)
|
|
+ ret = async_get_free_task(&op->idx);
|
|
+ if (unlikely(!ret))
|
|
goto free_cb_param;
|
|
|
|
- op->idx = idx;
|
|
do {
|
|
ret = wd_do_aead_async(priv->sess, &priv->req);
|
|
- if (ret < 0 && ret != -EBUSY) {
|
|
- fprintf(stderr, "do sec aead async failed.\n");
|
|
+ if (unlikely(ret < 0 && ret != -EBUSY)) {
|
|
+ fprintf(stderr, "do aead async operation failed.\n");
|
|
async_free_poll_task(op->idx, 0);
|
|
ret = 0;
|
|
goto free_cb_param;
|
|
@@ -559,65 +569,59 @@ static int do_aead_async(struct aead_priv_ctx *priv, struct async_op *op)
|
|
} 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 = 0;
|
|
+ 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_e_do_aes_gcm_final(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
|
- const unsigned char *in, size_t inlen)
|
|
+static int uadk_e_do_aes_gcm_update(EVP_CIPHER_CTX *ctx, struct aead_priv_ctx *priv,
|
|
+ unsigned char *out, const unsigned char *in, size_t inlen)
|
|
{
|
|
- struct aead_priv_ctx *priv =
|
|
- (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
unsigned char *ctx_buf = EVP_CIPHER_CTX_buf_noconst(ctx);
|
|
struct async_op *op;
|
|
int ret, enc;
|
|
|
|
- op = malloc(sizeof(struct async_op));
|
|
- if (!op)
|
|
- return RET_FAIL;
|
|
-
|
|
- ret = async_setup_async_event_notification(op);
|
|
- if (unlikely(!ret)) {
|
|
- fprintf(stderr, "failed to setup async event notification.\n");
|
|
- free(op);
|
|
- return RET_FAIL;
|
|
+ enc = EVP_CIPHER_CTX_encrypting(ctx);
|
|
+ if (!enc) {
|
|
+ if (priv->taglen == AES_GCM_TAG_LEN) {
|
|
+ memcpy(priv->req.mac, ctx_buf, AES_GCM_TAG_LEN);
|
|
+ } else {
|
|
+ fprintf(stderr, "invalid: aead gcm mac length only support 16B.\n");
|
|
+ return RET_FAIL;
|
|
+ }
|
|
}
|
|
|
|
- if (priv->req.assoc_bytes && !op->job)
|
|
- priv->req.msg_state = AEAD_MSG_END;
|
|
- else
|
|
- priv->req.msg_state = AEAD_MSG_BLOCK;
|
|
-
|
|
- enc = EVP_CIPHER_CTX_encrypting(ctx);
|
|
- if (!enc)
|
|
- memcpy(priv->req.mac, ctx_buf, AES_GCM_TAG_LEN);
|
|
+ if (ASYNC_get_current_job()) {
|
|
+ op = malloc(sizeof(struct async_op));
|
|
+ if (unlikely(!op))
|
|
+ return RET_FAIL;
|
|
|
|
- priv->req.src = priv->data;
|
|
- if (!op->job) {
|
|
- priv->req.in_bytes = priv->last_update_bufflen;
|
|
- priv->req.dst = out;
|
|
- ret = wd_do_aead_sync(priv->sess, &priv->req);
|
|
- if (ret < 0) {
|
|
- fprintf(stderr, "do sec aead final operation failed, ret: %d!\n", ret);
|
|
- goto out;
|
|
+ ret = async_setup_async_event_notification(op);
|
|
+ if (unlikely(!ret)) {
|
|
+ fprintf(stderr, "failed to setup async event notification.\n");
|
|
+ free(op);
|
|
+ return RET_FAIL;
|
|
}
|
|
- } else {
|
|
- ret = do_aead_async(priv, op);
|
|
- if (!ret)
|
|
+
|
|
+ ret = do_aead_async(priv, op, out, in, inlen);
|
|
+ if (unlikely(!ret))
|
|
goto out;
|
|
|
|
- memcpy(out, priv->req.dst + priv->req.assoc_bytes, priv->req.in_bytes);
|
|
+ free(op);
|
|
+ return inlen;
|
|
}
|
|
|
|
- if (enc)
|
|
- memcpy(ctx_buf, priv->req.mac, AES_GCM_TAG_LEN);
|
|
-
|
|
- priv->last_update_bufflen = 0;
|
|
-
|
|
- free(op);
|
|
- return priv->req.in_bytes;
|
|
+ return do_aead_sync(priv, out, in, inlen);
|
|
|
|
out:
|
|
(void)async_clear_async_event_notification();
|
|
@@ -625,19 +629,55 @@ out:
|
|
return RET_FAIL;
|
|
}
|
|
|
|
+static int uadk_e_do_aes_gcm_final(EVP_CIPHER_CTX *ctx, struct aead_priv_ctx *priv,
|
|
+ unsigned char *out, const unsigned char *in, size_t inlen)
|
|
+{
|
|
+ unsigned char *ctx_buf = EVP_CIPHER_CTX_buf_noconst(ctx);
|
|
+ int ret, enc;
|
|
+
|
|
+ enc = EVP_CIPHER_CTX_encrypting(ctx);
|
|
+
|
|
+ if (ASYNC_get_current_job() || !priv->req.assoc_bytes)
|
|
+ goto out;
|
|
+
|
|
+ priv->req.msg_state = AEAD_MSG_END;
|
|
+ priv->req.src = NULL;
|
|
+ priv->req.in_bytes = 0;
|
|
+ priv->req.dst = out;
|
|
+ priv->req.state = 0;
|
|
+ ret = wd_do_aead_sync(priv->sess, &priv->req);
|
|
+ if (ret < 0 || priv->req.state) {
|
|
+ fprintf(stderr, "do aead final operation failed, ret: %d, state: %u!\n",
|
|
+ ret, priv->req.state);
|
|
+ return RET_FAIL;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ if (enc)
|
|
+ memcpy(ctx_buf, priv->req.mac, AES_GCM_TAG_LEN);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int uadk_e_do_aes_gcm(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
|
const unsigned char *in, size_t inlen)
|
|
{
|
|
- int ret;
|
|
+ struct aead_priv_ctx *priv;
|
|
+
|
|
+ priv = (struct aead_priv_ctx *)EVP_CIPHER_CTX_get_cipher_data(ctx);
|
|
+ if (unlikely(!priv)) {
|
|
+ fprintf(stderr, "invalid: aead priv ctx is NULL.\n");
|
|
+ return 0;
|
|
+ }
|
|
|
|
if (in) {
|
|
if (out == NULL)
|
|
- return uadk_e_do_aes_gcm_first(ctx, out, in, inlen);
|
|
+ return uadk_e_do_aes_gcm_first(priv, out, in, inlen);
|
|
|
|
- return uadk_e_do_aes_gcm_update(ctx, out, in, inlen);
|
|
+ return uadk_e_do_aes_gcm_update(ctx, priv, out, in, inlen);
|
|
}
|
|
|
|
- return uadk_e_do_aes_gcm_final(ctx, out, NULL, 0);
|
|
+ return uadk_e_do_aes_gcm_final(ctx, priv, out, NULL, 0);
|
|
}
|
|
|
|
#define UADK_AEAD_DESCR(name, block_size, key_size, iv_len, flags, ctx_size, \
|
|
--
|
|
2.25.1
|
|
|