[PATCH 01/12] uadk_provider: fix cipher oiv reset and dupctx defects
From: Zhushuai Yin <yinzhushuai@huawei.com> When re-initializing a CBC/CFB/OFB cipher context with iv==NULL, the context did not restore the original IV (oiv), causing subsequent operations to use a stale or zeroed IV and produce wrong ciphertext. The provider also lacked a dupctx implementation, so EVP_CIPHER_CTX_copy() silently shallow-copied the context, leading to double-free or use-after-free when either copy was freed. Back up oiv on first init and reset iv to oiv on iv==NULL re-init for CBC/CFB/OFB modes, aligning with the OpenSSL default provider. Implement dupctx to deep-copy the cipher context and register OSSL_FUNC_CIPHER_DUPCTX to properly support EVP_CIPHER_CTX_copy(). Verified on board: Bug1/Bug2 fixed, 7 algorithms round-trip and sync/async speed OK without regression. Signed-off-by: Zhushuai Yin <yinzhushuai@huawei.com> --- src/uadk_prov_cipher.c | 80 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/src/uadk_prov_cipher.c b/src/uadk_prov_cipher.c index b6c1dbb..f77c475 100644 --- a/src/uadk_prov_cipher.c +++ b/src/uadk_prov_cipher.c @@ -127,6 +127,11 @@ struct cipher_priv_ctx { size_t ivlen; size_t bufsz; /* Number of bytes in buf */ char alg_name[ALG_NAME_SIZE]; + /* + * Original IV saved at first init; used to reset iv on iv==NULL + * re-init (CBC/CFB/OFB), matching OpenSSL default oiv semantics. + */ + unsigned char oiv[IV_LEN]; }; struct cipher_info { @@ -476,6 +481,7 @@ static int uadk_prov_cipher_init(struct cipher_priv_ctx *priv, if (iv) { memcpy(priv->iv, iv, ivlen); + memcpy(priv->oiv, iv, ivlen); priv->iv_set = 1; } @@ -483,6 +489,26 @@ static int uadk_prov_cipher_init(struct cipher_priv_ctx *priv, if (unlikely(!ret)) return UADK_P_FAIL; + /* + * Re-init without IV: reset iv to the original (oiv) for CBC/CFB/OFB + * modes, matching OpenSSL ciphercommon.c semantics. + * CTR (counter increments) and XTS (tweak) are excluded. + */ + if (iv == NULL && priv->iv_set) { + switch (priv->setup.mode) { + case WD_CIPHER_CBC: + case WD_CIPHER_CBC_CS1: + case WD_CIPHER_CBC_CS2: + case WD_CIPHER_CBC_CS3: + case WD_CIPHER_CFB: + case WD_CIPHER_OFB: + memcpy(priv->iv, priv->oiv, priv->ivlen); + break; + default: + break; + } + } + if (key) { memcpy(priv->key, key, keylen); priv->key_set = 1; @@ -922,6 +948,7 @@ void uadk_prov_destroy_cipher(void) static OSSL_FUNC_cipher_encrypt_init_fn uadk_prov_cipher_einit; static OSSL_FUNC_cipher_decrypt_init_fn uadk_prov_cipher_dinit; static OSSL_FUNC_cipher_freectx_fn uadk_prov_cipher_freectx; +static OSSL_FUNC_cipher_dupctx_fn uadk_prov_cipher_dupctx; static OSSL_FUNC_cipher_get_ctx_params_fn uadk_prov_cipher_get_ctx_params; static OSSL_FUNC_cipher_gettable_ctx_params_fn uadk_prov_cipher_gettable_ctx_params; static OSSL_FUNC_cipher_set_ctx_params_fn uadk_prov_cipher_set_ctx_params; @@ -1326,8 +1353,8 @@ static int uadk_prov_cipher_get_ctx_params(void *vctx, OSSL_PARAM params[]) return UADK_P_FAIL; } p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV); - if (p != NULL && !OSSL_PARAM_set_octet_string(p, priv->iv, priv->ivlen) && - !OSSL_PARAM_set_octet_ptr(p, &priv->iv, priv->ivlen)) { + if (p != NULL && !OSSL_PARAM_set_octet_string(p, priv->oiv, priv->ivlen) && + !OSSL_PARAM_set_octet_ptr(p, &priv->oiv, priv->ivlen)) { UADK_ERR("failed to set cipher octet parameter: param iv.\n"); return UADK_P_FAIL; } @@ -1417,6 +1444,54 @@ static int ossl_cipher_generic_get_params(OSSL_PARAM params[], unsigned int md, return UADK_P_SUCCESS; } +static void *uadk_prov_cipher_dupctx(void *ctx) +{ + struct cipher_priv_ctx *src = (struct cipher_priv_ctx *)ctx; + struct cipher_priv_ctx *dst; + + if (ctx == NULL) + return NULL; + + dst = OPENSSL_memdup(src, sizeof(*src)); + if (dst == NULL) + return NULL; + + /* WD session handle cannot be shared; force re-alloc on next HW call. */ + dst->sess = 0; + + /* sw_cipher is a fetched EVP_CIPHER; share via up_ref. */ + if (src->sw_cipher) { + if (EVP_CIPHER_up_ref(src->sw_cipher) != 1) { + UADK_ERR("cipher dupctx: EVP_CIPHER_up_ref failed.\n"); + dst->sw_cipher = NULL; + goto err; + } + } + + /* sw_ctx carries mid-stream IV/counter state; deep-copy it. */ + if (src->sw_ctx) { + dst->sw_ctx = EVP_CIPHER_CTX_new(); + if (dst->sw_ctx == NULL) { + UADK_ERR("cipher dupctx: EVP_CIPHER_CTX_new failed.\n"); + goto err; + } + if (EVP_CIPHER_CTX_copy(dst->sw_ctx, src->sw_ctx) != 1) { + UADK_ERR("cipher dupctx: EVP_CIPHER_CTX_copy failed.\n"); + EVP_CIPHER_CTX_free(dst->sw_ctx); + dst->sw_ctx = NULL; + goto err; + } + } + + return dst; + +err: + EVP_CIPHER_free(dst->sw_cipher); + OPENSSL_free(dst); + + return NULL; +} + static void uadk_prov_cipher_freectx(void *ctx) { struct cipher_priv_ctx *priv = (struct cipher_priv_ctx *)ctx; @@ -1488,6 +1563,7 @@ static int uadk_##nm##_get_params(OSSL_PARAM params[]) \ const OSSL_DISPATCH uadk_##nm##_functions[] = { \ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))uadk_##nm##_newctx }, \ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))uadk_prov_cipher_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))uadk_prov_cipher_dupctx }, \ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, \ (void (*)(void))uadk_prov_cipher_einit }, \ { OSSL_FUNC_CIPHER_DECRYPT_INIT, \ -- 2.43.0
From: Zhushuai Yin <yinzhushuai@huawei.com> The uadk_provider lacked TLS CBC record processing, so TLS handshakes and record encryption with CBC suites fell back to the software default provider and lost hardware acceleration. Existing cipher context management also had freectx heap mismatch, dupctx double-free, and a broken soft-fallback oneshot path that prevented correct TLS CBC operation even when the offload path was reached. Add TLS CBC record processing (constant-time padding/MAC/explicit-IV strip) mirroring the OpenSSL default provider so that CBC suites run end-to-end on hardware. Fix freectx heap mismatch, dupctx double-free, and the soft-fallback oneshot path to ensure correct context lifecycle and fallback behavior under TLS usage. Verified: 16 CBC suites across TLS1.0-1.2 all PASS, no regression. Signed-off-by: Zhushuai Yin <yinzhushuai@huawei.com> --- src/uadk_prov_cipher.c | 472 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 469 insertions(+), 3 deletions(-) diff --git a/src/uadk_prov_cipher.c b/src/uadk_prov_cipher.c index f77c475..c9ae006 100644 --- a/src/uadk_prov_cipher.c +++ b/src/uadk_prov_cipher.c @@ -19,10 +19,13 @@ #include <stdio.h> #include <stdbool.h> #include <string.h> +#include <limits.h> #include <dlfcn.h> #include <numa.h> #include <openssl/core_names.h> #include <openssl/proverr.h> +#include <openssl/prov_ssl.h> +#include <openssl/rand.h> #include <uadk/wd_cipher.h> #include <uadk/wd_sched.h> #include "uadk.h" @@ -51,6 +54,9 @@ #define UADK_CIPHER_DEF_CTXS 2 #define UADK_CIPHER_OP_NUM 1 +/* Maximum CBC padding bytes including length byte (mirrors OpenSSL MAX_PADDING) */ +#define TLS_MAX_PADDING 256 + /* OSSL_CIPHER_PARAM_CTS_MODE Values */ # define OSSL_CIPHER_CTS_MODE_CS1 "CS1" # define OSSL_CIPHER_CTS_MODE_CS2 "CS2" @@ -132,6 +138,12 @@ struct cipher_priv_ctx { * re-init (CBC/CFB/OFB), matching OpenSSL default oiv semantics. */ unsigned char oiv[IV_LEN]; + + /* TLS CBC record-mode state (mirrors OpenSSL default provider) */ + unsigned int tlsversion; /* 0 = not TLS, else SSL3/TLS1/.../TLS1_3 */ + size_t tlsmacsize; /* MAC size to strip/add in TLS record mode */ + unsigned char *tlsmac; /* pointer to MAC extracted on decrypt */ + int alloced; /* whether tlsmac was OPENSSL_malloc'd */ }; struct cipher_info { @@ -517,9 +529,6 @@ static int uadk_prov_cipher_init(struct cipher_priv_ctx *priv, priv->switch_flag = 0; priv->switch_threshold = SMALL_PACKET_OFFLOAD_THRESHOLD_DEFAULT; - if (uadk_get_sw_offload_state()) - uadk_create_cipher_soft_ctx(priv); - ret = uadk_prov_cipher_dev_init(priv); if (unlikely(ret <= 0)) { UADK_ERR("cipher switch to soft init!\n"); @@ -1024,6 +1033,385 @@ static int uadk_prov_cipher_block_decrypto(struct cipher_priv_ctx *priv, unsigne return UADK_P_SUCCESS; } +/* + * Constant-time helpers for TLS CBC padding/MAC removal. + * OpenSSL's constant_time.h is not public; we reimplement the few + * operations needed here in a branch-free way. + */ +#define UADK_SIZE_T_BITS (sizeof(size_t) * CHAR_BIT) + +static ossl_inline size_t uadk_ct_ge_s(size_t a, size_t b) +{ + /* Returns (size_t)-1 if a >= b, 0 otherwise. */ + size_t lt = ((a - b) >> (UADK_SIZE_T_BITS - 1)) & 1; + + return (size_t)(0 - (1 - lt)); +} + +static ossl_inline unsigned char uadk_ct_ge_u8(size_t a, size_t b) +{ + return (unsigned char)uadk_ct_ge_s(a, b); +} + +static ossl_inline size_t uadk_ct_eq_s(size_t a, size_t b) +{ + /* Returns (size_t)-1 if a == b, 0 otherwise. */ + size_t neq = ((a - b) | (b - a)) >> (UADK_SIZE_T_BITS - 1); + + return (size_t)(0 - (1 - neq)); +} + +static ossl_inline unsigned char uadk_ct_eq_u8(size_t a, size_t b) +{ + return (unsigned char)uadk_ct_eq_s(a, b); +} + +static ossl_inline size_t uadk_ct_lt_s(size_t a, size_t b) +{ + /* Returns (size_t)-1 if a < b, 0 otherwise. */ + size_t lt = ((a - b) >> (UADK_SIZE_T_BITS - 1)) & 1; + + return (size_t)(0 - lt); +} + +static ossl_inline unsigned char uadk_ct_select_u8(unsigned char mask, + unsigned char a, + unsigned char b) +{ + /* mask is 0x00 or 0xff. Returns a when mask set, b otherwise. */ + return (unsigned char)((a & mask) | (b & ~mask)); +} + +/* + * Context for constant-time MAC extraction. Bundles the scan parameters, + * MAC window, and working buffers so helpers stay within 5 params. + */ +struct uadk_cbc_mac_ctx { + const unsigned char *recdata; + size_t origreclen; + size_t macsize; + size_t mac_start; + size_t mac_end; + size_t scan_start; + size_t rotate_offset; + size_t good; + unsigned char randmac[EVP_MAX_MD_SIZE]; + unsigned char rotated_mac[EVP_MAX_MD_SIZE]; +}; + +/* + * Scan recdata in constant time, collecting MAC bytes into rotated_mac + * and recording the rotation offset. + */ +static void uadk_cbc_mac_rotate(struct uadk_cbc_mac_ctx *ctx) +{ + size_t i, j, in_mac = 0; + + ctx->rotate_offset = 0; + memset(ctx->rotated_mac, 0, ctx->macsize); + for (i = ctx->scan_start, j = 0; i < ctx->origreclen; i++) { + size_t mac_started = uadk_ct_eq_s(i, ctx->mac_start); + size_t mac_ended = uadk_ct_lt_s(i, ctx->mac_end); + unsigned char b = ctx->recdata[i]; + + in_mac |= mac_started; + in_mac &= mac_ended; + ctx->rotate_offset |= j & mac_started; + ctx->rotated_mac[j++] |= b & in_mac; + j &= uadk_ct_lt_s(j, ctx->macsize); + } +} + +/* + * Rotate rotated_mac by rotate_offset and substitute random bytes where + * padding was bad (good == 0). Result written to out. + */ +static void uadk_cbc_mac_select(struct uadk_cbc_mac_ctx *ctx, + unsigned char *out) +{ + size_t rotate_offset = ctx->macsize - ctx->rotate_offset; + size_t i, j; + + memset(out, 0, ctx->macsize); + rotate_offset &= uadk_ct_lt_s(rotate_offset, ctx->macsize); + for (i = 0; i < ctx->macsize; i++) { + for (j = 0; j < ctx->macsize; j++) + out[j] |= ctx->rotated_mac[i] & + uadk_ct_eq_u8(j, rotate_offset); + rotate_offset++; + rotate_offset &= uadk_ct_lt_s(rotate_offset, ctx->macsize); + out[i] = uadk_ct_select_u8((unsigned char)(ctx->good & 0xff), + out[i], ctx->randmac[i]); + } +} + +/* + * Constant-time copy of the trailing MAC bytes of recdata into a freshly + * allocated buffer, with a random MAC substituted if good is 0. + * Mirrors ssl3_cbc_copy_mac() from ssl/record/tls_pad.c. + */ +static int uadk_cipher_cbc_copy_mac(struct cipher_priv_ctx *priv, + size_t *reclen, size_t origreclen, + unsigned char *recdata, size_t good) +{ + struct uadk_cbc_mac_ctx ctx = {0}; + size_t macsize = priv->tlsmacsize; + size_t mac_end = *reclen; + + if (macsize == 0) { + if (good == 0) + return UADK_P_FAIL; + return UADK_P_SUCCESS; + } + + *reclen -= macsize; + + if (priv->blksize == 1) { + priv->tlsmac = &recdata[*reclen]; + priv->alloced = 0; + return UADK_P_SUCCESS; + } + + if (RAND_bytes(ctx.randmac, (int)macsize) <= 0) + return UADK_P_FAIL; + + priv->tlsmac = OPENSSL_malloc(macsize); + if (priv->tlsmac == NULL) + return UADK_P_FAIL; + priv->alloced = 1; + + ctx.recdata = recdata; + ctx.origreclen = origreclen; + ctx.macsize = macsize; + ctx.mac_start = mac_end - macsize; + ctx.mac_end = mac_end; + ctx.good = good; + if (origreclen > macsize + TLS_MAX_PADDING) + ctx.scan_start = origreclen - (macsize + TLS_MAX_PADDING); + + uadk_cbc_mac_rotate(&ctx); + uadk_cbc_mac_select(&ctx, priv->tlsmac); + + return UADK_P_SUCCESS; +} + +/* + * Check TLS 1.x padding in constant time and return the good mask. + * Updates *reclen to remove padding. + */ +static size_t uadk_tls1_remove_padding(const unsigned char *buf, size_t *reclen, + size_t overhead) +{ + size_t padding_length = buf[*reclen - 1]; + size_t to_check = TLS_MAX_PADDING; + unsigned char mask; + unsigned char b; + size_t good; + size_t i; + + good = uadk_ct_ge_s(*reclen, overhead + padding_length); + if (to_check > *reclen) + to_check = *reclen; + for (i = 0; i < to_check; i++) { + mask = uadk_ct_ge_u8(padding_length, i); + b = buf[*reclen - 1 - i]; + good &= ~(mask & (padding_length ^ b)); + } + good = uadk_ct_eq_s(0xff, good & 0xff); + *reclen -= good & (padding_length + 1); + + return good; +} + +/* Strip SSLv3 padding and copy MAC. */ +static int uadk_ssl3_remove_padding(struct cipher_priv_ctx *priv, + unsigned char *buf, size_t *reclen, + size_t origreclen) +{ + size_t overhead = 1 + priv->tlsmacsize; + size_t padding_length; + size_t good; + + if (overhead > *reclen) + return UADK_P_FAIL; + padding_length = buf[*reclen - 1]; + good = uadk_ct_ge_s(*reclen, padding_length + overhead); + good &= uadk_ct_ge_s(priv->blksize, padding_length + 1); + *reclen -= good & (padding_length + 1); + + return uadk_cipher_cbc_copy_mac(priv, reclen, origreclen, buf, good); +} + +/* Strip TLS 1.x padding (if block cipher) and copy MAC. */ +static int uadk_tls1_remove_padding_and_mac(struct cipher_priv_ctx *priv, + unsigned char *buf, size_t *reclen, + size_t origreclen) +{ + size_t blksz = priv->blksize; + size_t overhead = ((blksz == 1) ? 0 : 1) + priv->tlsmacsize; + size_t good; + + if (overhead > *reclen) + return UADK_P_FAIL; + + if (blksz != 1) + good = uadk_tls1_remove_padding(buf, reclen, overhead); + else + good = (size_t)-1; + + return uadk_cipher_cbc_copy_mac(priv, reclen, origreclen, buf, good); +} + +/* + * Strip TLS CBC padding in constant time and extract the MAC. Handles + * explicit-IV stripping for TLS >= 1.1, then SSLv3 or TLS 1.x padding + * and MAC extraction. On decrypt, *buflen is the plaintext length. + */ +static int uadk_cipher_tls1_unpad_mac(struct cipher_priv_ctx *priv, + unsigned char *buf, size_t *buflen, + size_t origreclen) +{ + unsigned int ver = priv->tlsversion; + size_t blksz = priv->blksize; + size_t reclen = *buflen; + int ret; + + if (priv->alloced) { + OPENSSL_free(priv->tlsmac); + priv->alloced = 0; + priv->tlsmac = NULL; + } + + if (ver == TLS1_2_VERSION || ver == DTLS1_2_VERSION || + ver == TLS1_1_VERSION || ver == DTLS1_VERSION || + ver == DTLS1_BAD_VER) { + if (reclen < blksz) + return UADK_P_FAIL; + buf += blksz; + reclen -= blksz; + origreclen -= blksz; + } + + if (ver == SSL3_VERSION) + ret = uadk_ssl3_remove_padding(priv, buf, &reclen, origreclen); + else + ret = uadk_tls1_remove_padding_and_mac(priv, buf, &reclen, + origreclen); + + if (ret != UADK_P_SUCCESS) + return UADK_P_FAIL; + + *buflen = reclen; + + return UADK_P_SUCCESS; +} + +/* Add TLS padding (RFC 5246) for encryption. Updates *inl to padded length. */ +static int uadk_tls_add_padding(struct cipher_priv_ctx *priv, + unsigned char *out, size_t *inl, + size_t outsize) +{ + size_t blksz = priv->blksize; + size_t padnum = blksz - (*inl % blksz); + unsigned char padval = (unsigned char)(padnum - 1); + size_t loop; + + if (outsize < *inl + padnum) { + UADK_ERR("invalid: tls cipher encrypt buffer too small.\n"); + return UADK_P_FAIL; + } + if (padnum > TLS_MAX_PADDING) { + UADK_ERR("invalid: tls cipher padding too large.\n"); + return UADK_P_FAIL; + } + if (priv->tlsversion == SSL3_VERSION) { + if (padnum > 1) + memset(out + *inl, 0, padnum - 1); + *(out + *inl + padnum - 1) = padval; + } else { + for (loop = *inl; loop < *inl + padnum; loop++) + out[loop] = padval; + } + *inl += padnum; + + return UADK_P_SUCCESS; +} + +/* + * Encrypt/decrypt in-place via HW, falling back to soft cipher on + * failure. Uses pad=0 one-shot; TLS padding/MAC is stripped afterwards + * by uadk_cipher_tls1_unpad_mac. + */ +static int uadk_tls_cipher_hw_or_soft(struct cipher_priv_ctx *priv, + unsigned char *out, size_t *outl, + size_t outsize, size_t inl) +{ + int ret; + + ret = uadk_prov_hw_cipher(priv, out, outl, outsize, out, inl); + if (ret == UADK_P_SUCCESS) + return UADK_P_SUCCESS; + + if (priv->sw_cipher) { + ret = uadk_prov_cipher_soft_oneshot(priv, out, outl, out, inl); + if (ret == UADK_P_SUCCESS) + return UADK_P_SUCCESS; + } + + return UADK_P_FAIL; +} + +/* + * Process a TLS 1.x CBC record in one shot: encrypt (append padding + + * encrypt in place) or decrypt (decrypt in place, strip explicit + * IV/padding, extract MAC via OSSL_CIPHER_PARAM_TLS_MAC). + */ +static int uadk_prov_cipher_tls_block_update(struct cipher_priv_ctx *priv, + unsigned char *out, size_t *outl, + size_t outsize, size_t inl) +{ + size_t blksz = priv->blksize; + size_t origreclen = inl; + int ret; + + if (!priv->pad || outsize < inl) { + UADK_ERR("invalid: tls cipher update requires in-place buffer.\n"); + return UADK_P_FAIL; + } + + if (priv->enc) { + ret = uadk_tls_add_padding(priv, out, &inl, outsize); + if (ret != UADK_P_SUCCESS) + return UADK_P_FAIL; + } + + if ((inl % blksz) != 0) { + UADK_ERR("invalid: tls cipher record not block-aligned.\n"); + return UADK_P_FAIL; + } + + ret = uadk_tls_cipher_hw_or_soft(priv, out, outl, outsize, inl); + if (ret != UADK_P_SUCCESS) + return UADK_P_FAIL; + + if (priv->alloced) { + OPENSSL_free(priv->tlsmac); + priv->alloced = 0; + priv->tlsmac = NULL; + } + + *outl = inl; + + if (!priv->enc) { + ret = uadk_cipher_tls1_unpad_mac(priv, out, outl, origreclen); + if (ret != UADK_P_SUCCESS) { + UADK_ERR("tls cipher unpad/mac extract failed.\n"); + return UADK_P_FAIL; + } + } + + return UADK_P_SUCCESS; +} static int uadk_prov_cipher_block_final(void *vctx, unsigned char *out, size_t *outl, size_t outsize) @@ -1034,6 +1422,12 @@ static int uadk_prov_cipher_block_final(void *vctx, unsigned char *out, if (!vctx || !out || !outl) return UADK_P_FAIL; + /* TLS record mode is one-shot per update; final is an error there. */ + if (priv->tlsversion > 0) { + UADK_ERR("cipher final is invalid in TLS record mode.\n"); + return UADK_P_FAIL; + } + if (priv->switch_flag == UADK_DO_SOFT) return uadk_prov_cipher_soft_final(priv, out, outl); @@ -1064,6 +1458,15 @@ static int uadk_prov_cipher_block_update(void *vctx, unsigned char *output, return UADK_P_FAIL; } + if (priv->tlsversion > 0) { + if (input != output) { + UADK_ERR("invalid: tls cipher update requires in-place buffer.\n"); + return UADK_P_FAIL; + } + return uadk_prov_cipher_tls_block_update(priv, output, outl, + outsize, inl); + } + return uadk_prov_do_cipher(priv, output, outl, outsize, input, inl); } @@ -1247,6 +1650,8 @@ static const OSSL_PARAM uadk_prov_settable_ctx_params[] = { OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), OSSL_PARAM_uint(OSSL_CIPHER_PARAM_PADDING, NULL), OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS_VERSION, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL), OSSL_PARAM_END }; @@ -1326,6 +1731,42 @@ static int uadk_prov_cipher_set_ctx_params(void *vctx, const OSSL_PARAM params[] ALG_NAME_SIZE - 1); } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION); + if (p != NULL) { + unsigned int ver; + + if (!OSSL_PARAM_get_uint(p, &ver)) { + UADK_ERR("failed to get cipher uint parameter: tls version.\n"); + return UADK_P_FAIL; + } + switch (ver) { + case SSL3_VERSION: + case TLS1_VERSION: + case TLS1_1_VERSION: + case TLS1_2_VERSION: + case DTLS1_VERSION: + case DTLS1_2_VERSION: + case DTLS1_BAD_VER: + priv->tlsversion = ver; + break; + default: + UADK_ERR("invalid: unsupported tls version: 0x%x.\n", ver); + return UADK_P_FAIL; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE); + if (p != NULL) { + size_t macsize; + + if (!OSSL_PARAM_get_size_t(p, &macsize) || + macsize > EVP_MAX_MD_SIZE) { + UADK_ERR("failed to get cipher size parameter: tls mac size.\n"); + return UADK_P_FAIL; + } + priv->tlsmacsize = macsize; + } + return UADK_P_SUCCESS; } @@ -1373,6 +1814,12 @@ static int uadk_prov_cipher_get_ctx_params(void *vctx, OSSL_PARAM params[]) return UADK_P_FAIL; } } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC); + if (p != NULL + && !OSSL_PARAM_set_octet_ptr(p, priv->tlsmac, priv->tlsmacsize)) { + UADK_ERR("failed to set cipher octet parameter: tls mac.\n"); + return UADK_P_FAIL; + } return UADK_P_SUCCESS; } @@ -1383,6 +1830,7 @@ static const OSSL_PARAM uadk_prov_default_ctx_params[] = { OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0), OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0), OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0), + { OSSL_CIPHER_PARAM_TLS_MAC, OSSL_PARAM_OCTET_PTR, NULL, 0, OSSL_PARAM_UNMODIFIED }, OSSL_PARAM_END }; @@ -1456,6 +1904,14 @@ static void *uadk_prov_cipher_dupctx(void *ctx) if (dst == NULL) return NULL; + /* + * tlsmac is a heap buffer owned solely by the source context. + * Sharing it would double-free; force the duplicate to start + * with no MAC buffer. + */ + dst->tlsmac = NULL; + dst->alloced = 0; + /* WD session handle cannot be shared; force re-alloc on next HW call. */ dst->sess = 0; @@ -1508,6 +1964,12 @@ static void uadk_prov_cipher_freectx(void *ctx) if (priv->sess) wd_cipher_free_sess(priv->sess); + if (priv->alloced) { + OPENSSL_free(priv->tlsmac); + priv->tlsmac = NULL; + priv->alloced = 0; + } + OPENSSL_clear_free(priv, sizeof(*priv)); } @@ -1552,6 +2014,10 @@ static void *uadk_##nm##_newctx(void *provctx) \ strncpy(ctx->alg_name, #algnm, ALG_NAME_SIZE - 1); \ if (strcmp(#typ, "block") == 0) \ ctx->pad = 1; \ + \ + if (uadk_get_sw_offload_state()) \ + uadk_create_cipher_soft_ctx(ctx); \ + \ return ctx; \ } \ static OSSL_FUNC_cipher_get_params_fn uadk_##nm##_get_params; \ -- 2.43.0
From: Zhushuai Yin <yinzhushuai@huawei.com> BN_bn2binpad() can return negative on failure, but the RSA private decrypt and public verify paths only checked for zero (!len), causing a negative length to be treated as success and leading to buffer overread and incorrect results. DH_set0_key() return values were unchecked, leaking BN objects on failure. The SM2 pkey registration compared ret against zero instead of UADK_E_SUCCESS, mismatching the function's actual success code. In the async cipher and digest paths, the callback did not propagate hardware req->state, and the async pause_job error branch fell through to free_cb_param without distinguishing a pause_job failure (which leaks the poll task slot allocated by async_get_free_task) from a hardware error reported via req->state. Check BN_bn2binpad() return for <= 0 in RSA decrypt/verify and fall back to soft on failure. Check DH_set0_key() return and free the orphaned BN on failure. Compare against UADK_E_SUCCESS in SM2 pkey registration. Propagate req->state in cipher and digest callbacks, check state in sync paths, and free the poll task slot via async_free_poll_task() when async_pause_job returns zero before jumping to the cleanup label. Signed-off-by: Zhushuai Yin <yinzhushuai@huawei.com> --- src/uadk_cipher.c | 13 ++++++++++++- src/uadk_dh.c | 23 +++++++++++++++++------ src/uadk_digest.c | 22 ++++++++++++++++++---- src/uadk_pkey.c | 2 +- src/uadk_rsa.c | 6 +++--- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/uadk_cipher.c b/src/uadk_cipher.c index bbe6e19..8e738e1 100644 --- a/src/uadk_cipher.c +++ b/src/uadk_cipher.c @@ -551,6 +551,7 @@ static int uadk_e_cipher_cleanup(EVP_CIPHER_CTX *ctx) static void *uadk_e_cipher_cb(struct wd_cipher_req *req, void *data) { struct uadk_e_cb_info *cb_param; + struct cipher_priv_ctx *priv; struct async_op *op; if (!req) @@ -560,6 +561,10 @@ static void *uadk_e_cipher_cb(struct wd_cipher_req *req, void *data) if (!cb_param) return NULL; + priv = cb_param->priv; + if (priv) + priv->req.state = req->state; + op = cb_param->op; if (op && op->job && !op->done) { op->done = 1; @@ -575,7 +580,7 @@ static int do_cipher_sync(struct cipher_priv_ctx *priv) int ret; ret = wd_do_cipher_sync(priv->sess, &priv->req); - if (ret) + if (ret || priv->req.state) return 0; return 1; @@ -621,6 +626,12 @@ static int do_cipher_async(struct cipher_priv_ctx *priv, struct async_op *op) } while (ret == -EBUSY); ret = async_pause_job(priv, op, ASYNC_TASK_CIPHER); + if (!ret || priv->req.state) { + if (!ret) + async_free_poll_task(op->idx, 0); + ret = 0; + goto free_cb_param; + } free_cb_param: free(cb_param); diff --git a/src/uadk_dh.c b/src/uadk_dh.c index be3b41c..dc8db21 100644 --- a/src/uadk_dh.c +++ b/src/uadk_dh.c @@ -813,12 +813,23 @@ static int dh_soft_set_pkey(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) const BIGNUM *old_pub = DH_get0_pub_key(dh); const BIGNUM *old_priv = DH_get0_priv_key(dh); - if (old_pub != pub_key && old_priv != priv_key) - DH_set0_key(dh, pub_key, priv_key); - else if (old_pub != pub_key) - DH_set0_key(dh, pub_key, NULL); - else if (old_priv != priv_key) - DH_set0_key(dh, NULL, priv_key); + if (old_pub != pub_key && old_priv != priv_key) { + if (!DH_set0_key(dh, pub_key, priv_key)) { + BN_free(pub_key); + BN_free(priv_key); + return UADK_E_FAIL; + } + } else if (old_pub != pub_key) { + if (!DH_set0_key(dh, pub_key, NULL)) { + BN_free(pub_key); + return UADK_E_FAIL; + } + } else if (old_priv != priv_key) { + if (!DH_set0_key(dh, NULL, priv_key)) { + BN_free(priv_key); + return UADK_E_FAIL; + } + } return UADK_E_SUCCESS; } diff --git a/src/uadk_digest.c b/src/uadk_digest.c index 72d96b3..2721ff1 100644 --- a/src/uadk_digest.c +++ b/src/uadk_digest.c @@ -50,7 +50,8 @@ #define MAX_DIGEST_LENGTH 64 #define DIGEST_BLOCK_SIZE (512 * 1024) -/* copied form openssl/include/internal/sm3.h +/* + * copied form openssl/include/internal/sm3.h * OpenSSL 3.0 has no <openssl/sm3.h> */ # define SM3_DIGEST_LENGTH 32 @@ -189,7 +190,8 @@ static EVP_MD *uadk_sha256; static EVP_MD *uadk_sha384; static EVP_MD *uadk_sha512; -/* OpenSSL 3.0 has no app_datasize, need set manually, +/* + * OpenSSL 3.0 has no app_datasize, need set manually, * check crypto/evp/legacy_md5.c: md5_md as example. */ #define SET_APP_DATASIZE(ctx_type) \ @@ -800,6 +802,7 @@ static void *uadk_e_digest_cb(void *data) { struct wd_digest_req *req = (struct wd_digest_req *)data; struct uadk_e_cb_info *cb_param; + struct digest_priv_ctx *priv; struct async_op *op; if (!req) @@ -809,6 +812,10 @@ static void *uadk_e_digest_cb(void *data) if (!cb_param) return NULL; + priv = cb_param->priv; + if (priv) + priv->req.state = req->state; + op = cb_param->op; if (op && op->job && !op->done) { op->done = 1; @@ -824,7 +831,7 @@ static int do_digest_sync(struct digest_priv_ctx *priv) int ret; ret = wd_do_digest_sync(priv->sess, &priv->req); - if (ret) { + if (ret || priv->req.state) { fprintf(stderr, "do sec digest sync failed, switch to soft digest.\n"); return 0; } @@ -873,6 +880,12 @@ static int do_digest_async(struct digest_priv_ctx *priv, struct async_op *op) } while (ret == -EBUSY); ret = async_pause_job(priv, op, ASYNC_TASK_DIGEST); + if (!ret || priv->req.state) { + if (!ret) + async_free_poll_task(op->idx, 0); + ret = 0; + goto free_cb_param; + } free_cb_param: free(cb_param); @@ -1034,7 +1047,8 @@ static int uadk_e_digest_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from) if (t->state != SEC_DIGEST_INIT) { t->is_stream_copy = true; - /* Length that the hardware has processed should be equal to + /* + * Length that the hardware has processed should be equal to * total input data length minus software cache data length. */ t->req.long_data_len = t->total_data_len - t->last_update_bufflen; diff --git a/src/uadk_pkey.c b/src/uadk_pkey.c index 9bb6a80..e6592e3 100644 --- a/src/uadk_pkey.c +++ b/src/uadk_pkey.c @@ -657,7 +657,7 @@ static int get_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, switch (nid) { case EVP_PKEY_SM2: ret = uadk_sm2_create_pmeth(&pkey_meth); - if (!ret) { + if (ret != UADK_E_SUCCESS) { fprintf(stderr, "failed to register sm2 pmeth.\n"); return 0; } diff --git a/src/uadk_rsa.c b/src/uadk_rsa.c index e718853..8e5a3f1 100644 --- a/src/uadk_rsa.c +++ b/src/uadk_rsa.c @@ -1518,7 +1518,7 @@ static int uadk_e_rsa_keygen(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb) } ret = rsa_primes_gen(bits, e, bn_param->p, bn_param->q, cb); - if (!ret) { + if (ret != UADK_E_SUCCESS) { ret = UADK_DO_SOFT; goto free_sess; } @@ -1717,7 +1717,7 @@ static int uadk_e_rsa_private_decrypt(int flen, const unsigned char *from, } len = BN_bn2binpad(dec_bn, from_buf, num_bytes); - if (!len) { + if (len <= 0) { ret = UADK_DO_SOFT; goto free_dec_bn; } @@ -1916,7 +1916,7 @@ static int uadk_e_rsa_public_verify(int flen, const unsigned char *from, } len = BN_bn2binpad(verify_bn, from_buf, num_bytes); - if (!len) { + if (len <= 0) { ret = UADK_DO_SOFT; goto free_verify_bn; } -- 2.43.0
From: Zhushuai Yin <yinzhushuai@huawei.com> In the provider async paths, the async_pause_job failure branch used a single goto that either returned failure or jumped to cleanup, conflating the two cases and causing poll task slot leaks when pause_job returned zero. BN_bn2binpad() in the ECC private key conversion could return negative, which was stored directly into prikey.dsize and passed to the hardware, causing invalid key setup. The digest final path did not handle SWITCH_SOFT, treating a soft-switch return as a hard failure and breaking digest fallback. Split the async_pause_job error branch in the cipher, digest, DH and pkey provider paths to goto free_poll_task on ret==0 and return failure on req->state, preventing poll task slot leaks. Check BN_bn2binpad() return for negative in ECC private key setup and return UADK_P_FAIL. Handle UADK_DIGEST_SWITCH_SOFT in the digest final path by falling through to the sync error path instead of returning failure. Signed-off-by: Zhushuai Yin <yinzhushuai@huawei.com> --- src/uadk_prov_cipher.c | 6 ++++-- src/uadk_prov_dh.c | 2 +- src/uadk_prov_digest.c | 12 ++++++++---- src/uadk_prov_pkey.c | 10 ++++++++-- src/uadk_prov_rsa_sign.c | 2 +- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/uadk_prov_cipher.c b/src/uadk_prov_cipher.c index c9ae006..caff75b 100644 --- a/src/uadk_prov_cipher.c +++ b/src/uadk_prov_cipher.c @@ -563,7 +563,7 @@ static int uadk_do_cipher_sync(struct cipher_priv_ctx *priv) int ret; ret = wd_do_cipher_sync(priv->sess, &priv->req); - if (ret) + if (ret || priv->req.state) return UADK_P_FAIL; return UADK_P_SUCCESS; @@ -602,7 +602,9 @@ static int uadk_do_cipher_async(struct cipher_priv_ctx *priv, struct async_op *o } while (true); ret = async_pause_job(priv, op, ASYNC_TASK_CIPHER); - if (!ret || priv->req.state) + if (!ret) + goto free_poll_task; + if (priv->req.state) return UADK_P_FAIL; return UADK_P_SUCCESS; diff --git a/src/uadk_prov_dh.c b/src/uadk_prov_dh.c index 004b60e..870f299 100644 --- a/src/uadk_prov_dh.c +++ b/src/uadk_prov_dh.c @@ -941,7 +941,7 @@ static int uadk_prov_dh_do_crypto(struct uadk_dh_sess *dh_sess) ret = async_pause_job(dh_sess, &op, ASYNC_TASK_DH); if (!ret) - goto err; + goto free_poll_task; ret = dh_sess->req.status; if (ret) diff --git a/src/uadk_prov_digest.c b/src/uadk_prov_digest.c index 7488fc6..3334662 100644 --- a/src/uadk_prov_digest.c +++ b/src/uadk_prov_digest.c @@ -623,7 +623,7 @@ static int uadk_do_digest_sync(struct digest_priv_ctx *priv) return UADK_DIGEST_FAIL; ret = wd_do_digest_sync(priv->sess, &priv->req); - if (ret) { + if (ret || priv->req.state) { UADK_ERR("do sec digest sync failed, switch to soft digest.\n"); return UADK_DIGEST_FAIL; } @@ -666,7 +666,9 @@ static int uadk_do_digest_async(struct digest_priv_ctx *priv, struct async_op *o } while (true); ret = async_pause_job(priv, op, ASYNC_TASK_DIGEST); - if (!ret || priv->req.state) + if (!ret) + goto free_poll_task; + if (priv->req.state) return UADK_DIGEST_FAIL; return UADK_DIGEST_SUCCESS; @@ -693,7 +695,9 @@ static int uadk_digest_final(struct digest_priv_ctx *priv, unsigned char *digest } ret = uadk_digest_ctx_init(priv); - if (ret != UADK_DIGEST_SUCCESS) + if (ret == UADK_DIGEST_SWITCH_SOFT) + goto sync_err; + else if (ret != UADK_DIGEST_SUCCESS) return UADK_DIGEST_FAIL; priv->req.in = priv->data; @@ -722,7 +726,7 @@ static int uadk_digest_final(struct digest_priv_ctx *priv, unsigned char *digest sync_err: if (priv->state == SEC_DIGEST_INIT) { - ret = uadk_digest_soft_work(priv, priv->req.in_bytes, digest); + ret = uadk_digest_soft_work(priv, priv->last_update_bufflen, digest); } else { ret = UADK_DIGEST_FAIL; UADK_ERR("do sec digest final failed.\n"); diff --git a/src/uadk_prov_pkey.c b/src/uadk_prov_pkey.c index 68750bd..0d392ca 100644 --- a/src/uadk_prov_pkey.c +++ b/src/uadk_prov_pkey.c @@ -427,7 +427,7 @@ int uadk_prov_ecc_crypto(handle_t sess, struct wd_ecc_req *req, void *usr) ret = async_pause_job(usr, &op, ASYNC_TASK_ECC); if (ret == 0) - goto err; + goto free_poll_task; if (req->status) goto err; @@ -715,6 +715,7 @@ int uadk_prov_ecc_set_private_key(handle_t sess, const EC_KEY *ec) struct wd_dtb prikey; const BIGNUM *d; int buflen, ret; + int tmplen; size_t degree; d = EC_KEY_get0_private_key(ec); @@ -733,7 +734,12 @@ int uadk_prov_ecc_set_private_key(handle_t sess, const EC_KEY *ec) buflen = BITS_TO_BYTES(degree); ecc_key = wd_ecc_get_key(sess); prikey.data = (void *)prikey_bin; - prikey.dsize = BN_bn2binpad(d, prikey_bin, buflen); + tmplen = BN_bn2binpad(d, prikey_bin, buflen); + if (tmplen < 0) { + UADK_ERR("failed to convert ecc private key\n"); + return UADK_P_FAIL; + } + prikey.dsize = tmplen; ret = wd_ecc_set_prikey(ecc_key, &prikey); if (ret) { diff --git a/src/uadk_prov_rsa_sign.c b/src/uadk_prov_rsa_sign.c index e9f9651..370dcc1 100644 --- a/src/uadk_prov_rsa_sign.c +++ b/src/uadk_prov_rsa_sign.c @@ -349,7 +349,7 @@ static int verify_trans_bn(struct uadk_rsa_sess *rsa_sess, unsigned char *from_b goto verify_end; *len = BN_bn2binpad(verify_bn, from_buf, num_bytes); - if (*len == 0) + if (*len <= 0) ret = UADK_P_FAIL; verify_end: -- 2.43.0
From: Zhushuai Yin <yinzhushuai@huawei.com> When no SEC device is available and uadk_provider falls back to software computation, the HMAC soft path allocates a fresh OSSL_LIB_CTX per HMAC ctx and tears it down on every final. Each allocation forces OpenSSL to repopulate the OBJ/method name tables, showing up as OBJ_obj2txt / OPENSSL_LH_doall_arg / OSSL_LIB_CTX_free hotspots in flame graphs (nginx + no-device scenario accounts for ~8% of total time). Additionally, EVP_MAC_fetch with the provider libctx and NULL property recursively dispatches back to uadk_prov_hmac_newctx, causing stack overflow and coredump. The fix uses "provider=default" property filter, mirroring uadk_prov_aead.c's soft-path pattern. The OSSL_OP_MAC query case also lacks the version-check break gate present in OSSL_OP_DIGEST, so uadk_provider claims HMAC even when no device exists and sw_offload is enabled, adding per-request dispatch overhead. The _alg() calls for pkey operations are moved after version check to avoid unnecessary UACCE device list traversal when falling back to default provider. This patch: - Reuse the provider libctx with "provider=default" property filter instead of allocating a per-ctx OSSL_LIB_CTX in HMAC soft fallback - Add version-check break to OSSL_OP_MAC, aligning with OSSL_OP_DIGEST - Move _alg() calls after version check in OSSL_OP_SIGNATURE/KEYMGMT/ ASYM_CIPHER/KEYEXCH to skip device traversal when not needed - Fix uadk_unquery OSSL_OP_MAC to use needs_version_check pattern, avoiding unconditional OPENSSL_free on default provider's array Signed-off-by: Zhushuai Yin <yinzhushuai@huawei.com> --- src/uadk_prov_hmac.c | 37 +++++++++++++++++++------------------ src/uadk_prov_init.c | 25 ++++++++++++++----------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/uadk_prov_hmac.c b/src/uadk_prov_hmac.c index 3f7de9e..83b0809 100644 --- a/src/uadk_prov_hmac.c +++ b/src/uadk_prov_hmac.c @@ -76,7 +76,6 @@ struct hmac_priv_ctx { size_t total_data_len; size_t switch_threshold; OSSL_LIB_CTX *libctx; - OSSL_LIB_CTX *soft_libctx; EVP_MAC_CTX *soft_ctx; EVP_MAC *soft_md; handle_t sess; @@ -124,12 +123,6 @@ static int uadk_create_hmac_soft_ctx(struct hmac_priv_ctx *priv) if (priv->soft_md) return UADK_P_SUCCESS; - priv->soft_libctx = OSSL_LIB_CTX_new(); - if (!priv->soft_libctx) { - UADK_ERR("new soft libctx failed.\n"); - return UADK_P_FAIL; - } - switch (priv->alg_id) { case NID_md5: case NID_sm3: @@ -140,7 +133,24 @@ static int uadk_create_hmac_soft_ctx(struct hmac_priv_ctx *priv) case NID_sha512: case NID_sha512_224: case NID_sha512_256: - priv->soft_md = EVP_MAC_fetch(priv->soft_libctx, "HMAC", NULL); + /* + * Reuse the provider libctx (already bootstrapped once at + * provider load and warm for the process lifetime) instead of + * allocating a fresh OSSL_LIB_CTX per HMAC ctx. A fresh libctx + * would force OpenSSL to repopulate the OBJ/method tables on + * every fetch and tear them down on every final, which shows up + * as OBJ_obj2txt / OPENSSL_LH_doall_arg / OSSL_LIB_CTX_free + * hotspots in flame graphs (nginx + no-device case). + * + * Must use "provider=default" property to explicitly fetch from + * the default provider, NOT uadk_provider itself. The provider + * libctx has "?provider=uadk_provider" as default property (see + * uadk_prov_init.c uadk_set_default_prov), so without this filter + * the fetch would recursively dispatch back to + * uadk_prov_hmac_newctx -> stack overflow -> coredump. + * This mirrors uadk_prov_aead.c's soft-path pattern. + */ + priv->soft_md = EVP_MAC_fetch(priv->libctx, "HMAC", "provider=default"); break; default: break; @@ -148,7 +158,7 @@ static int uadk_create_hmac_soft_ctx(struct hmac_priv_ctx *priv) if (unlikely(!priv->soft_md)) { UADK_ERR("hmac soft fetch failed.\n"); - goto free_libctx; + return UADK_P_FAIL; } priv->soft_ctx = EVP_MAC_CTX_new(priv->soft_md); @@ -162,9 +172,6 @@ static int uadk_create_hmac_soft_ctx(struct hmac_priv_ctx *priv) free_mac_md: EVP_MAC_free(priv->soft_md); priv->soft_md = NULL; -free_libctx: - OSSL_LIB_CTX_free(priv->soft_libctx); - priv->soft_libctx = NULL; return UADK_P_FAIL; } @@ -236,11 +243,6 @@ static void hmac_soft_cleanup(struct hmac_priv_ctx *priv) priv->soft_md = NULL; } - if (priv->soft_libctx) { - OSSL_LIB_CTX_free(priv->soft_libctx); - priv->soft_libctx = NULL; - } - priv->switch_flag = 0; } @@ -827,7 +829,6 @@ static void *uadk_prov_hmac_dupctx(void *hctx) goto free_ctx; if (dst_ctx->soft_ctx) { - dst_ctx->soft_libctx = NULL; dst_ctx->soft_ctx = EVP_MAC_CTX_dup(src_ctx->soft_ctx); if (!dst_ctx->soft_ctx) { UADK_ERR("create soft_ctx failed in ctx copy.\n"); diff --git a/src/uadk_prov_init.c b/src/uadk_prov_init.c index 38d7426..6ab2c73 100644 --- a/src/uadk_prov_init.c +++ b/src/uadk_prov_init.c @@ -731,6 +731,9 @@ static const OSSL_ALGORITHM *uadk_query(void *provctx, int operation_id, break; return uadk_generate_digests_array(); case OSSL_OP_MAC: + ver = uadk_prov_digest_version(); + if (!ver && uadk_get_sw_offload_state()) + break; return uadk_generate_hmac_array(); case OSSL_OP_CIPHER: ver = uadk_prov_cipher_version(); @@ -740,35 +743,35 @@ static const OSSL_ALGORITHM *uadk_query(void *provctx, int operation_id, return uadk_generate_cipher_array_v2(); return uadk_generate_cipher_array_v3(); case OSSL_OP_SIGNATURE: - uadk_prov_signature_alg(); ver = uadk_prov_pkey_version(); if (!ver && uadk_get_sw_offload_state()) break; - else if (ver == HW_ASYM_ENC_V2) + uadk_prov_signature_alg(); + if (ver == HW_ASYM_ENC_V2) return uadk_generate_signature_array_v2(); return uadk_generate_signature_array_v3(); case OSSL_OP_KEYMGMT: - uadk_prov_keymgmt_alg(); ver = uadk_prov_pkey_version(); if (!ver && uadk_get_sw_offload_state()) break; - else if (ver == HW_ASYM_ENC_V2) + uadk_prov_keymgmt_alg(); + if (ver == HW_ASYM_ENC_V2) return uadk_generate_keymgmt_array_v2(); return uadk_generate_keymgmt_array_v3(); case OSSL_OP_ASYM_CIPHER: - uadk_prov_asym_cipher_alg(); ver = uadk_prov_pkey_version(); if (!ver && uadk_get_sw_offload_state()) break; - else if (ver == HW_ASYM_ENC_V2) + uadk_prov_asym_cipher_alg(); + if (ver == HW_ASYM_ENC_V2) return uadk_generate_asym_cipher_array_v2(); return uadk_generate_asym_cipher_array_v3(); case OSSL_OP_KEYEXCH: - uadk_prov_keyexch_alg(); ver = uadk_prov_pkey_version(); if (!ver && uadk_get_sw_offload_state()) break; - else if (ver == HW_ASYM_ENC_V2) + uadk_prov_keyexch_alg(); + if (ver == HW_ASYM_ENC_V2) return uadk_generate_keyexch_array_v2(); return uadk_generate_keyexch_array_v3(); default: @@ -829,9 +832,9 @@ static void uadk_unquery(void *provctx, int operation_id, needs_version_check = 1; break; case OSSL_OP_MAC: - if (algs) - OPENSSL_free((void *)algs); - return; + ver = uadk_prov_digest_version(); + needs_version_check = 1; + break; default: break; } -- 2.43.0
From: Zhushuai Yin <yinzhushuai@huawei.com> When the SEC processing cores are disabled during an async cipher or digest operation, wd_do_cipher_async/send still succeeds because the doorbell is an MMIO write to the QM (Queue Manager), which is an independent hardware module from the processing cores. However, the poll thread fails to receive a CQE (cores disabled, no one writes CQE, phase never flips) and retries ASYNC_MAX_RETRY_CNT times before giving up. The callback is never invoked because wd_cipher_poll_ctx/ wd_digest_poll_ctx returns an error before calling the callback, so priv->req.state remains 0. async_pause_job returns a positive value (the async fd exists), and the existing check (!ret || priv->req.state) evaluates to false, so the soft fallback is skipped. For cipher, the output buffer stays unfilled and the encrypt-decrypt round-trip fails with ret=252. For digest, the output buffer stays unfilled and the final hash value is incorrect (silent error in first-block mode, already-fail in stream mode). Pre-priv->req.state with STATE_FAIL before sending the async request, matching the existing pattern in AEAD, RSA, DH, and ECC. When the callback is invoked successfully, it overwrites req.state with the actual HW result (0 on success). When the callback is never invoked (poll thread gives up), req.state remains STATE_FAIL (non-zero), so the existing !ret || priv->req.state check correctly triggers soft fallback. Signed-off-by: Zhushuai Yin <yinzhushuai@huawei.com> --- src/uadk_cipher.c | 3 +++ src/uadk_digest.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/uadk_cipher.c b/src/uadk_cipher.c index 8e738e1..5c28d62 100644 --- a/src/uadk_cipher.c +++ b/src/uadk_cipher.c @@ -55,6 +55,8 @@ struct sw_cipher_t { const EVP_CIPHER *(*get_cipher)(void); }; +#define STATE_FAIL 0xFFFF + struct cipher_priv_ctx { handle_t sess; struct wd_cipher_sess_setup setup; @@ -603,6 +605,7 @@ static int do_cipher_async(struct cipher_priv_ctx *priv, struct async_op *op) cb_param->priv = priv; priv->req.cb = uadk_e_cipher_cb; priv->req.cb_param = cb_param; + priv->req.state = STATE_FAIL; ret = async_get_free_task(&idx); if (!ret) diff --git a/src/uadk_digest.c b/src/uadk_digest.c index 2721ff1..75cc5d9 100644 --- a/src/uadk_digest.c +++ b/src/uadk_digest.c @@ -49,6 +49,7 @@ #define SHA_SMALL_PACKET_OFFLOAD_THRESHOLD_DEFAULT (512) #define MAX_DIGEST_LENGTH 64 #define DIGEST_BLOCK_SIZE (512 * 1024) +#define STATE_FAIL 0xFFFF /* * copied form openssl/include/internal/sm3.h @@ -856,6 +857,7 @@ static int do_digest_async(struct digest_priv_ctx *priv, struct async_op *op) cb_param->priv = priv; priv->req.cb = uadk_e_digest_cb; priv->req.cb_param = cb_param; + priv->req.state = STATE_FAIL; ret = async_get_free_task(&idx); if (!ret) -- 2.43.0
From: Zhushuai Yin <yinzhushuai@huawei.com> STATE_FAIL (0xFFFF) was locally defined in three files (aead/cipher/digest) and RSA/DH/ECC used hardcoded -1 for req.status. UADK_E_POLL_SUCCESS and UADK_E_POLL_FAIL were locally defined in both uadk_dh.c and uadk_rsa.c, while uadk_cipher.c and uadk_digest.c used hardcoded 0/-1 for the same purpose. This duplication makes it easy to miss the pre-set when adding new async paths. Move all four macros to uadk.h with documentation: - STATE_FAIL / STATUS_FAIL: async send error sentinels - UADK_E_POLL_SUCCESS / UADK_E_POLL_FAIL: async poll return values Remove duplicate local definitions from aead/cipher/digest/dh/rsa, and replace the hardcoded 0/-1 in cipher/digest/aead poll functions with the macros for consistency. Signed-off-by: Zhushuai Yin <yinzhushuai@huawei.com> --- src/uadk.h | 15 +++++++++++++++ src/uadk_aead.c | 3 +-- src/uadk_cipher.c | 6 ++---- src/uadk_dh.c | 4 +--- src/uadk_digest.c | 5 ++--- src/uadk_pkey.c | 2 +- src/uadk_rsa.c | 4 +--- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/uadk.h b/src/uadk.h index 2c52643..a6db774 100644 --- a/src/uadk.h +++ b/src/uadk.h @@ -29,6 +29,21 @@ #define UADK_INIT_FAIL 2 #define UADK_DEVICE_ERROR 3 +/* + * Async send error sentinel: pre-set before wd_*_async() so that + * callback-not-invoked cases (poll thread gives up) are caught by + * the existing req.state/status != 0 soft-fallback check. + * + * STATE_FAIL (0xFFFF) is for __u16 state fields (cipher/digest/aead). + * STATUS_FAIL (-1) is for int status fields (RSA/DH/ECC). + */ +#define STATE_FAIL 0xFFFF +#define STATUS_FAIL (-1) + +/* Async poll return values */ +#define UADK_E_POLL_SUCCESS 0 +#define UADK_E_POLL_FAIL (-1) + enum { HW_V2, HW_V3, diff --git a/src/uadk_aead.c b/src/uadk_aead.c index 20e0631..a026284 100644 --- a/src/uadk_aead.c +++ b/src/uadk_aead.c @@ -30,7 +30,6 @@ #define UADK_E_SUCCESS 1 #define UADK_E_FAIL 0 #define RET_FAIL (-1) -#define STATE_FAIL 0xFFFF #define CTX_SYNC_ENC 0 #define CTX_SYNC_DEC 1 #define CTX_ASYNC_ENC 2 @@ -114,7 +113,7 @@ static int uadk_e_aead_poll(void *ctx) do { ret = wd_aead_poll_ctx(idx, expt, &recv); if (!ret && recv == expt) - return 0; + return UADK_E_POLL_SUCCESS; else if (ret == -EAGAIN) rx_cnt++; else diff --git a/src/uadk_cipher.c b/src/uadk_cipher.c index 5c28d62..2b1537c 100644 --- a/src/uadk_cipher.c +++ b/src/uadk_cipher.c @@ -55,8 +55,6 @@ struct sw_cipher_t { const EVP_CIPHER *(*get_cipher)(void); }; -#define STATE_FAIL 0xFFFF - struct cipher_priv_ctx { handle_t sess; struct wd_cipher_sess_setup setup; @@ -323,11 +321,11 @@ static int uadk_e_cipher_poll(void *ctx) do { ret = wd_cipher_poll_ctx(idx, expt, &recv); if (!ret && recv == expt) - return 0; + return UADK_E_POLL_SUCCESS; else if (ret == -EAGAIN) rx_cnt++; else - return -1; + return UADK_E_POLL_FAIL; } while (rx_cnt < ENGINE_RECV_MAX_CNT); fprintf(stderr, "failed to recv msg: timeout!\n"); diff --git a/src/uadk_dh.c b/src/uadk_dh.c index dc8db21..ab8a305 100644 --- a/src/uadk_dh.c +++ b/src/uadk_dh.c @@ -48,8 +48,6 @@ #define UADK_DO_SOFT (-0xE0) #define UADK_E_SUCCESS 1 #define UADK_E_FAIL 0 -#define UADK_E_POLL_SUCCESS 0 -#define UADK_E_POLL_FAIL (-1) #define UADK_E_INIT_SUCCESS 0 #define ENV_ENABLED 1 #define KEY_GEN_BY_ENGINE 1 @@ -746,7 +744,7 @@ static int dh_do_async(struct uadk_dh_sess *dh_sess, struct async_op *op) cb_param->priv = &dh_sess->req; dh_sess->req.cb = uadk_e_dh_cb; dh_sess->req.cb_param = cb_param; - dh_sess->req.status = -1; + dh_sess->req.status = STATUS_FAIL; ret = async_get_free_task(&idx); if (!ret) goto free_cb_param; diff --git a/src/uadk_digest.c b/src/uadk_digest.c index 75cc5d9..29448b7 100644 --- a/src/uadk_digest.c +++ b/src/uadk_digest.c @@ -49,7 +49,6 @@ #define SHA_SMALL_PACKET_OFFLOAD_THRESHOLD_DEFAULT (512) #define MAX_DIGEST_LENGTH 64 #define DIGEST_BLOCK_SIZE (512 * 1024) -#define STATE_FAIL 0xFFFF /* * copied form openssl/include/internal/sm3.h @@ -403,11 +402,11 @@ static int uadk_e_digest_poll(void *ctx) do { ret = wd_digest_poll_ctx(CTX_ASYNC, expt, &recv); if (!ret && recv == expt) - return 0; + return UADK_E_POLL_SUCCESS; else if (ret == -EAGAIN) rx_cnt++; else - return -1; + return UADK_E_POLL_FAIL; } while (rx_cnt < ENGINE_RECV_MAX_CNT); fprintf(stderr, "failed to recv msg: timeout!\n"); diff --git a/src/uadk_pkey.c b/src/uadk_pkey.c index e6592e3..1fbee88 100644 --- a/src/uadk_pkey.c +++ b/src/uadk_pkey.c @@ -335,7 +335,7 @@ static int uadk_ecc_do_async(handle_t sess, struct wd_ecc_req *req, cb_param->priv = req; req->cb_param = cb_param; req->cb = uadk_e_ecc_cb; - req->status = -1; + req->status = STATUS_FAIL; ret = async_get_free_task(&idx); if (!ret) goto free_cb_param; diff --git a/src/uadk_rsa.c b/src/uadk_rsa.c index 8e5a3f1..8774d5d 100644 --- a/src/uadk_rsa.c +++ b/src/uadk_rsa.c @@ -49,8 +49,6 @@ #define UADK_E_SUCCESS 1 #define UADK_E_FAIL 0 #define UADK_DO_SOFT (-0xE0) -#define UADK_E_POLL_SUCCESS 0 -#define UADK_E_POLL_FAIL (-1) #define UADK_E_INIT_SUCCESS 0 #define CHECK_PADDING_FAIL (-1) #define ENV_ENABLED 1 @@ -1198,7 +1196,7 @@ static int rsa_do_async(struct uadk_rsa_sess *rsa_sess, struct async_op *op) cb_param->priv = &rsa_sess->req; rsa_sess->req.cb = uadk_e_rsa_cb; rsa_sess->req.cb_param = cb_param; - rsa_sess->req.status = -1; + rsa_sess->req.status = STATUS_FAIL; ret = async_get_free_task(&idx); if (!ret) goto free_cb_param; -- 2.43.0
Wrap the aes_128_xts and aes_256_xts UADK_CIPHER_DESCR definitions in uadk_prov_cipher.c to keep lines within the 80-column limit. Signed-off-by: Chenghai Huang <huangchenghai2@huawei.com> --- src/uadk_prov_cipher.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/uadk_prov_cipher.c b/src/uadk_prov_cipher.c index caff75b..5c9dfe8 100644 --- a/src/uadk_prov_cipher.c +++ b/src/uadk_prov_cipher.c @@ -2065,8 +2065,10 @@ UADK_CIPHER_DESCR(aes_256_cts, 1, 32, 16, 0, ID_aes_256_cts, cbc-cs1(aes), EVP_C UADK_CIPHER_DESCR(aes_128_ecb, 16, 16, 0, 0, ID_aes_128_ecb, ecb(aes), EVP_CIPH_ECB_MODE, block); UADK_CIPHER_DESCR(aes_192_ecb, 16, 24, 0, 0, ID_aes_192_ecb, ecb(aes), EVP_CIPH_ECB_MODE, block); UADK_CIPHER_DESCR(aes_256_ecb, 16, 32, 0, 0, ID_aes_256_ecb, ecb(aes), EVP_CIPH_ECB_MODE, block); -UADK_CIPHER_DESCR(aes_128_xts, 1, 32, 16, PROV_CIPHER_FLAG_CUSTOM_IV, ID_aes_128_xts, xts(aes), EVP_CIPH_XTS_MODE, stream); -UADK_CIPHER_DESCR(aes_256_xts, 1, 64, 16, PROV_CIPHER_FLAG_CUSTOM_IV, ID_aes_256_xts, xts(aes), EVP_CIPH_XTS_MODE, stream); +UADK_CIPHER_DESCR(aes_128_xts, 1, 32, 16, PROV_CIPHER_FLAG_CUSTOM_IV, + ID_aes_128_xts, xts(aes), EVP_CIPH_XTS_MODE, stream); +UADK_CIPHER_DESCR(aes_256_xts, 1, 64, 16, PROV_CIPHER_FLAG_CUSTOM_IV, + ID_aes_256_xts, xts(aes), EVP_CIPH_XTS_MODE, stream); UADK_CIPHER_DESCR(sm4_cbc, 16, 16, 16, 0, ID_sm4_cbc, cbc(sm4), EVP_CIPH_CBC_MODE, block); UADK_CIPHER_DESCR(sm4_ecb, 16, 16, 0, 0, ID_sm4_ecb, ecb(sm4), EVP_CIPH_ECB_MODE, block); UADK_CIPHER_DESCR(des_ede3_cbc, 8, 24, 8, 0, ID_des_ede3_cbc, cbc(des), EVP_CIPH_CBC_MODE, block); -- 2.43.0
From: Weili Qian <qianweili@huawei.com> X25519_KEYBITS (256) was used for both hardware session setup and get_params, but OpenSSL expects 253 (RFC 7748 clamps the top 3 bits of the X25519 scalar). Add a separate X25519_BITS (253) macro for get_params, keeping X25519_KEYBITS for uadk_prov_ecx_alloc_sess(). Signed-off-by: Weili Qian <qianweili@huawei.com> --- src/uadk_prov_ecx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/uadk_prov_ecx.c b/src/uadk_prov_ecx.c index 1818219..3515573 100644 --- a/src/uadk_prov_ecx.c +++ b/src/uadk_prov_ecx.c @@ -38,6 +38,7 @@ #define X25519_KEYBITS 256 #define X25519_KEYLEN 32 #define X25519_SECURITY_BITS 128 +#define X25519_BITS 253 #define ECX_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_KEYPAIR) @@ -1484,7 +1485,7 @@ static const char *uadk_keymgmt_x25519_query_operation_name(int operation_id) static int uadk_keymgmt_x25519_get_params(void *key, OSSL_PARAM params[]) { - return uadk_prov_ecx_get_params(key, params, X25519_KEYBITS, X25519_SECURITY_BITS, + return uadk_prov_ecx_get_params(key, params, X25519_BITS, X25519_SECURITY_BITS, X25519_KEYLEN); } -- 2.43.0
From: Weili Qian <qianweili@huawei.com> The DH key exchange set_ctx_params callback did not handle OSSL_KDF_PARAM_CEK_ALG, so the CEK algorithm passed by the upper layer was silently ignored, causing X9.42 KDF to produce incorrect results. Add uadk_prov_dh_locate_kdf_cekalg() to parse the parameter and store it in pdhctx->kdf_cekalg. Also fix the kdf_pad error path to goto end so the mutex is properly unlocked. Signed-off-by: Weili Qian <qianweili@huawei.com> --- src/uadk_prov_dh.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/uadk_prov_dh.c b/src/uadk_prov_dh.c index 870f299..d78cd88 100644 --- a/src/uadk_prov_dh.c +++ b/src/uadk_prov_dh.c @@ -2069,6 +2069,33 @@ static int uadk_prov_dh_locate_kdf_pad(PROV_DH_KEYEXCH_CTX *pdhctx, const OSSL_P return UADK_P_SUCCESS; } +static int uadk_prov_dh_locate_kdf_cekalg(PROV_DH_KEYEXCH_CTX *pdhctx, + const OSSL_PARAM params[]) +{ + char name[DH_MAX_PARAM_LEN + 1] = {'\0'}; + const OSSL_PARAM *p; + char *str; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CEK_ALG); + if (p != NULL) { + str = name; + + OPENSSL_free(pdhctx->kdf_cekalg); + pdhctx->kdf_cekalg = NULL; + + if (p->data != NULL && p->data_size != 0) { + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name))) + return UADK_P_FAIL; + + pdhctx->kdf_cekalg = OPENSSL_strdup(name); + if (pdhctx->kdf_cekalg == NULL) + return UADK_P_FAIL; + } + } + + return UADK_P_SUCCESS; +} + static int uadk_keyexch_dh_set_ctx_params(void *dhctx, const OSSL_PARAM params[]) { PROV_DH_KEYEXCH_CTX *pdhctx = (PROV_DH_KEYEXCH_CTX *)dhctx; @@ -2109,8 +2136,14 @@ static int uadk_keyexch_dh_set_ctx_params(void *dhctx, const OSSL_PARAM params[] } ret = uadk_prov_dh_locate_kdf_pad(pdhctx, params); - if (ret == UADK_P_FAIL) + if (ret == UADK_P_FAIL) { UADK_ERR("failed to locate kdf pad\n"); + goto end; + } + + ret = uadk_prov_dh_locate_kdf_cekalg(pdhctx, params); + if (ret == UADK_P_FAIL) + UADK_ERR("failed to locate kdf cekalg\n"); end: pthread_mutex_unlock(&dh_mutex); -- 2.43.0
From: Weili Qian <qianweili@huawei.com> The ECX key exchange used reverse_bytes() to convert endianness directly on the key buffer. Since ECX_KEY objects are shared via dupctx, in-place reversal corrupts the key for all reference holders if the operation fails between the forward and reverse-back. Switch to the non-destructive reverse_bytes_ex() variant which writes reversed bytes into a separate stack buffer, leaving the original key untouched. Also cleanse the temporary private key buffer after use. Signed-off-by: Weili Qian <qianweili@huawei.com> --- src/uadk_ecx.c | 58 ++++++++++++---------------------- src/uadk_prov_ecx.c | 77 +++++++++------------------------------------ 2 files changed, 35 insertions(+), 100 deletions(-) diff --git a/src/uadk_ecx.c b/src/uadk_ecx.c index fbab680..3a33968 100644 --- a/src/uadk_ecx.c +++ b/src/uadk_ecx.c @@ -217,6 +217,14 @@ static int reverse_bytes(unsigned char *to_buf, __u32 size) return UADK_E_SUCCESS; } +static void reverse_bytes_ex(unsigned char *src_buf, unsigned char *dst_buf, __u32 size) +{ + __u32 i; + + for (i = 0; i < size; i++) + dst_buf[i] = src_buf[size - i - 1]; +} + static int x25519_init(EVP_PKEY_CTX *ctx) { struct wd_ecc_sess_setup setup = {0}; @@ -612,22 +620,18 @@ static int ecx_compkey_init_iot(struct ecx_ctx *ecx_ctx, struct wd_ecc_req *req, struct ecx_key *peer_ecx_key, struct ecx_key *ecx_key) { + unsigned char pubkey_be[ECX_MAX_KEYLEN]; __u32 key_size = ecx_ctx->key_size; char buf_y[ECX_MAX_KEYLEN] = {0}; handle_t sess = ecx_ctx->sess; struct wd_ecc_point in_pubkey; struct wd_ecc_out *ecx_out; struct wd_ecc_in *ecx_in; - int ret; - /* Trans public key from little-endian to big-endian */ - ret = reverse_bytes(peer_ecx_key->pubkey, key_size); - if(!ret) { - fprintf(stderr, "failed to trans public key\n"); - return UADK_E_FAIL; - } + /* Trans public key from little-endian to big-endian into a temp buffer */ + reverse_bytes_ex(peer_ecx_key->pubkey, pubkey_be, key_size); - in_pubkey.x.data = (char *)peer_ecx_key->pubkey; + in_pubkey.x.data = (char *)pubkey_be; in_pubkey.x.dsize = key_size; in_pubkey.y.data = buf_y; in_pubkey.y.dsize = 1; @@ -641,27 +645,14 @@ static int ecx_compkey_init_iot(struct ecx_ctx *ecx_ctx, struct wd_ecc_req *req, ecx_out = wd_ecxdh_new_out(sess); if (!ecx_out) { fprintf(stderr, "failed to new ecxdh out\n"); - ret = UADK_E_FAIL; - goto del_in; + wd_ecc_del_in(sess, ecx_in); + return UADK_E_FAIL; } uadk_ecc_fill_req(req, WD_ECXDH_COMPUTE_KEY, ecx_in, ecx_out); - /* Trans public key from big-endian to little-endian */ - ret = reverse_bytes(peer_ecx_key->pubkey, key_size); - if (!ret) { - fprintf(stderr, "failed to trans public key\n"); - goto del_out; - } - - return ret; - -del_out: - wd_ecc_del_out(sess, ecx_out); -del_in: - wd_ecc_del_in(sess, ecx_in); + return UADK_E_SUCCESS; - return ret; } static void ecx_compkey_uninit_iot(handle_t sess, struct wd_ecc_req *req) @@ -673,35 +664,26 @@ static void ecx_compkey_uninit_iot(handle_t sess, struct wd_ecc_req *req) static int ecx_derive_set_private_key(struct ecx_ctx *ecx_ctx, struct ecx_key *ecx_key) { + unsigned char prikey_be[ECX_MAX_KEYLEN]; int key_size = ecx_ctx->key_size; handle_t sess = ecx_ctx->sess; struct wd_ecc_key *ecc_key; struct wd_dtb prikey; int ret; - /* Trans private key from little-endian to big-endian */ - ret = reverse_bytes(ecx_key->privkey, key_size); - if (!ret) { - fprintf(stderr, "failed to trans private key\n"); - return UADK_E_FAIL; - } + /* Trans private key from little-endian to big-endian into a temp buffer */ + reverse_bytes_ex(ecx_key->privkey, prikey_be, key_size); - prikey.data = (char *)ecx_key->privkey; + prikey.data = (char *)prikey_be; prikey.dsize = ecx_ctx->key_size; ecc_key = wd_ecc_get_key(sess); ret = wd_ecc_set_prikey(ecc_key, &prikey); + OPENSSL_cleanse(prikey_be, sizeof(prikey_be)); if (ret) { fprintf(stderr, "failed to set ecc prikey, ret = %d\n", ret); return UADK_E_FAIL; } - /* Trans private key from big-endian to little-endian */ - ret = reverse_bytes(ecx_key->privkey, key_size); - if (!ret) { - fprintf(stderr, "failed to trans private key\n"); - return UADK_E_FAIL; - } - return UADK_E_SUCCESS; } diff --git a/src/uadk_prov_ecx.c b/src/uadk_prov_ecx.c index 3515573..27e9b86 100644 --- a/src/uadk_prov_ecx.c +++ b/src/uadk_prov_ecx.c @@ -676,29 +676,12 @@ static int uadk_prov_reverse_bytes(unsigned char *to_buf, __u32 size) return UADK_P_SUCCESS; } -static int uadk_prov_reverse_bytes_ex(unsigned char *src_buf, unsigned char *dst_buf, __u32 size) +static void uadk_prov_reverse_bytes_ex(unsigned char *src_buf, unsigned char *dst_buf, __u32 size) { __u32 i; - if (size == 0) { - UADK_ERR("invalid size, size = %u\n", size); - return UADK_P_FAIL; - } - - if (src_buf == NULL) { - UADK_ERR("src_buf is NULL\n"); - return UADK_P_FAIL; - } - - if (dst_buf == NULL) { - UADK_ERR("dst_buf is NULL\n"); - return UADK_P_FAIL; - } - for (i = 0; i < size; i++) dst_buf[i] = src_buf[size - i - 1]; - - return UADK_P_SUCCESS; } static int uadk_prov_ecx_set_pkey(PROV_ECX_KEYMGMT_CTX *gctx, struct wd_ecc_req *req, @@ -719,12 +702,8 @@ static int uadk_prov_ecx_set_pkey(PROV_ECX_KEYMGMT_CTX *gctx, struct wd_ecc_req } /* Trans public key from big-endian to little-endian */ - ret = uadk_prov_reverse_bytes_ex((unsigned char *)pubkey->x.data, - ecx_key->pubkey, pubkey->x.dsize); - if (ret == UADK_P_FAIL) { - UADK_ERR("failed to transform pubkey\n"); - return ret; - } + uadk_prov_reverse_bytes_ex((unsigned char *)pubkey->x.data, + ecx_key->pubkey, pubkey->x.dsize); /* Trans private key from big-endian to little-endian */ ret = uadk_prov_reverse_bytes(ecx_key->privkey, gctx->keylen); if (ret == UADK_P_FAIL) { @@ -1060,21 +1039,17 @@ static int uadk_keyexch_x448_set_peer(void *vecxctx, void *vkey) static int uadk_prov_ecx_compkey_init_iot(PROV_ECX_KEYEXCH_CTX *ecxctx, struct wd_ecc_req *req) { + unsigned char pubkey_be[ECX_MAX_KEYLEN]; char buffer_y[ECX_MAX_KEYLEN] = {0}; handle_t sess = ecxctx->sess; struct wd_ecc_point in_pubkey; struct wd_ecc_out *ecx_out; struct wd_ecc_in *ecx_in; - int ret; - /* Trans public key from little-endian to big-endian */ - ret = uadk_prov_reverse_bytes(ecxctx->peerkey->pubkey, ecxctx->keylen); - if (ret == UADK_P_FAIL) { - UADK_ERR("failed to trans public key\n"); - return UADK_P_FAIL; - } + /* Trans public key from little-endian to big-endian into a temp buffer */ + uadk_prov_reverse_bytes_ex(ecxctx->peerkey->pubkey, pubkey_be, ecxctx->keylen); - in_pubkey.x.data = (char *)ecxctx->peerkey->pubkey; + in_pubkey.x.data = (char *)pubkey_be; in_pubkey.x.dsize = ecxctx->keylen; in_pubkey.y.data = buffer_y; in_pubkey.y.dsize = 1; @@ -1088,27 +1063,14 @@ static int uadk_prov_ecx_compkey_init_iot(PROV_ECX_KEYEXCH_CTX *ecxctx, struct w ecx_out = wd_ecxdh_new_out(sess); if (ecx_out == NULL) { UADK_ERR("failed to new ecxdh out\n"); - ret = UADK_P_FAIL; - goto del_ecx_in; + wd_ecc_del_in(sess, ecx_in); + return UADK_P_FAIL; } uadk_prov_ecc_fill_req(req, WD_ECXDH_COMPUTE_KEY, ecx_in, ecx_out); - /* Trans public key from big-endian to little-endian */ - ret = uadk_prov_reverse_bytes(ecxctx->peerkey->pubkey, ecxctx->keylen); - if (ret == UADK_P_FAIL) { - UADK_ERR("failed to trans peer public key\n"); - goto del_ecx_out; - } - - return ret; - -del_ecx_out: - wd_ecc_del_out(sess, ecx_out); -del_ecx_in: - wd_ecc_del_in(sess, ecx_in); + return UADK_P_SUCCESS; - return ret; } static void uadk_prov_ecx_compkey_uninit_iot(PROV_ECX_KEYEXCH_CTX *ecxctx, struct wd_ecc_req *req) @@ -1119,34 +1081,25 @@ static void uadk_prov_ecx_compkey_uninit_iot(PROV_ECX_KEYEXCH_CTX *ecxctx, struc static int uadk_prov_ecx_derive_set_prikey(PROV_ECX_KEYEXCH_CTX *ecxctx) { + unsigned char prikey_be[ECX_MAX_KEYLEN]; handle_t sess = ecxctx->sess; struct wd_ecc_key *ecc_key; struct wd_dtb prikey; int ret; - /* Trans private key from little-endian to big-endian */ - ret = uadk_prov_reverse_bytes(ecxctx->key->privkey, ecxctx->keylen); - if (!ret) { - UADK_ERR("failed to trans private key\n"); - return UADK_P_FAIL; - } + /* Trans private key from little-endian to big-endian into a temp buffer */ + uadk_prov_reverse_bytes_ex(ecxctx->key->privkey, prikey_be, ecxctx->keylen); - prikey.data = (char *)ecxctx->key->privkey; + prikey.data = (char *)prikey_be; prikey.dsize = ecxctx->keylen; ecc_key = wd_ecc_get_key(sess); ret = wd_ecc_set_prikey(ecc_key, &prikey); + OPENSSL_cleanse(prikey_be, sizeof(prikey_be)); if (ret) { UADK_ERR("failed to set ecc prikey, ret = %d\n", ret); return UADK_P_FAIL; } - /* Trans private key from big-endian to little-endian */ - ret = uadk_prov_reverse_bytes(ecxctx->key->privkey, ecxctx->keylen); - if (ret == UADK_P_FAIL) { - UADK_ERR("failed to trans private key\n"); - return UADK_P_FAIL; - } - return UADK_P_SUCCESS; } -- 2.43.0
From: Weili Qian <qianweili@huawei.com> uadk_provider mirrors the default provider's signature context and delegates its software fallbacks (DO_SOFT) to the default provider functions. On OpenSSL 3.4+ the default provider routes digest signing through EVP_PKEY_OP_SIGNMSG/VERIFYMSG, disallows SHA1 for message signing, and defaults PSS saltlen to AUTO_DIGEST_MAX (3.1+), so the 3.2-era operation values and defaults in uadk diverge from the default provider behavior on 3.4+. Align RSA/ECDSA signature behavior with the OpenSSL default provider: - Set digest_sign_init operation to EVP_PKEY_OP_SIGNMSG and digest_verify_init operation to EVP_PKEY_OP_VERIFYMSG on OpenSSL 3.4+ to match default provider routing. - Exclude EVP_PKEY_OP_SIGNMSG from sha1_allowed in RSA and ECDSA, so SHA1 is disallowed for message signing (not just direct signing). - Include EVP_PKEY_OP_SIGNMSG in ECDSA ecc_check_key to validate private key for message signing operations. - Change default PSS saltlen from RSA_PSS_SALTLEN_AUTO to RSA_PSS_SALTLEN_AUTO_DIGEST_MAX on OpenSSL 3.1+ to comply with FIPS 186-4 section 5.5 (sLen <= hLen). - Temporarily present EVP_PKEY_OP_SIGN/VERIFY to the internal sign/verify call in digest_sign_final/digest_verify_final, so a DO_SOFT fallback takes the default provider's digest path instead of re-hashing the finalized digest, which fails with an update-after-final error. Signed-off-by: Weili Qian <qianweili@huawei.com> --- src/uadk_prov_ecdsa.c | 22 ++++++++++++++-- src/uadk_prov_rsa_sign.c | 56 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/uadk_prov_ecdsa.c b/src/uadk_prov_ecdsa.c index 44f64f6..032bc2a 100644 --- a/src/uadk_prov_ecdsa.c +++ b/src/uadk_prov_ecdsa.c @@ -237,7 +237,11 @@ static int ecdsa_digest_get_approved_nid(struct ecdsa_ctx *ctx, const EVP_MD *md int mdnid = ecdsa_digest_get_nid(md); #ifndef OPENSSL_NO_FIPS_SECURITYCHECKS - int sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN); + int sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + && ctx->operation != EVP_PKEY_OP_SIGNMSG +#endif + ); if (uadk_prov_securitycheck_enabled(ctx->libctx)) { if (mdnid == NID_undef || (mdnid == NID_sha1 && !sha1_allowed)) @@ -325,7 +329,13 @@ static int ecdsa_signverify_init(void *vctx, void *ec, if (eckey) { if (!uadk_prov_ecc_check_key(ctx->libctx, eckey, - operation == EVP_PKEY_OP_SIGN)) +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + (operation == EVP_PKEY_OP_SIGN || + operation == EVP_PKEY_OP_SIGNMSG) +#else + operation == EVP_PKEY_OP_SIGN +#endif + )) return UADK_P_FAIL; if (!EC_KEY_up_ref(ec)) @@ -894,7 +904,11 @@ err: static int uadk_signature_ecdsa_digest_sign_init(void *vctx, const char *mdname, void *ec, const OSSL_PARAM params[]) { +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + return ecdsa_digest_singverify_init(vctx, mdname, ec, params, EVP_PKEY_OP_SIGNMSG); +#else return ecdsa_digest_singverify_init(vctx, mdname, ec, params, EVP_PKEY_OP_SIGN); +#endif } static int ecdsa_digest_signverify_update(void *vctx, const unsigned char *data, @@ -943,7 +957,11 @@ static int uadk_signature_ecdsa_digest_sign_final(void *vctx, unsigned char *sig static int uadk_signature_ecdsa_digest_verify_init(void *vctx, const char *mdname, void *ec, const OSSL_PARAM params[]) { +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + return ecdsa_digest_singverify_init(vctx, mdname, ec, params, EVP_PKEY_OP_VERIFYMSG); +#else return ecdsa_digest_singverify_init(vctx, mdname, ec, params, EVP_PKEY_OP_VERIFY); +#endif } static int uadk_signature_ecdsa_digest_verify_update(void *vctx, const unsigned char *data, diff --git a/src/uadk_prov_rsa_sign.c b/src/uadk_prov_rsa_sign.c index 370dcc1..96e3094 100644 --- a/src/uadk_prov_rsa_sign.c +++ b/src/uadk_prov_rsa_sign.c @@ -520,7 +520,11 @@ static int rsa_setup_md(struct PROV_RSA_SIG_CTX *ctx, const char *mdname, if (mdname != NULL) { EVP_MD *md = EVP_MD_fetch(ctx->libctx, mdname, mdprops); - int sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN); + int sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + && ctx->operation != EVP_PKEY_OP_SIGNMSG +#endif + ); int md_nid = ossl_digest_rsa_sign_get_md_nid(ctx->libctx, md, sha1_allowed); size_t mdname_len = strlen(mdname); bool md_ret = (!md) || (md_nid <= 0); @@ -647,7 +651,11 @@ static int uadk_rsa_signverify_init(void *vprsactx, void *vrsa, ctx->operation = operation; /* Maximum for sign, auto for verify */ +#if OPENSSL_VERSION_NUMBER >= 0x30100000L + ctx->saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX; +#else ctx->saltlen = RSA_PSS_SALTLEN_AUTO; +#endif ctx->min_saltlen = -1; ctx->flag_allow_oneshot = 1; ctx->flag_allow_final = 1; @@ -1239,7 +1247,11 @@ static void *uadk_signature_rsa_newctx(void *provctx, const char *propq) priv->flag_allow_md = 1; priv->propq = propq_copy; /* Maximum for sign, auto for verify */ +#if OPENSSL_VERSION_NUMBER >= 0x30100000L + priv->saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX; +#else priv->saltlen = RSA_PSS_SALTLEN_AUTO; +#endif priv->min_saltlen = -1; return priv; @@ -1377,7 +1389,11 @@ static int uadk_rsa_setup_md(struct PROV_RSA_SIG_CTX *ctx, const char *mdname, if (mdname != NULL) { md = EVP_MD_fetch(ctx->libctx, mdname, mdprops); - sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN); + sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + && ctx->operation != EVP_PKEY_OP_SIGNMSG +#endif + ); md_nid = uadk_digest_rsa_sign_get_md_nid(ctx->libctx, md, sha1_allowed); mdname_len = strlen(mdname); if (!md || md_nid <= 0 || @@ -1458,8 +1474,13 @@ error: static int uadk_signature_rsa_digest_sign_init(void *vprsactx, const char *mdname, void *vrsa, const OSSL_PARAM params[]) { +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + return uadk_signature_rsa_digest_signverify_init(vprsactx, mdname, vrsa, + params, EVP_PKEY_OP_SIGNMSG); +#else return uadk_signature_rsa_digest_signverify_init(vprsactx, mdname, vrsa, params, EVP_PKEY_OP_SIGN); +#endif } static int uadk_signature_rsa_digest_sign_update(void *vprsactx, @@ -1579,6 +1600,7 @@ static int uadk_signature_rsa_digest_sign_final(void *vprsactx, unsigned char *s struct PROV_RSA_SIG_CTX *priv = (struct PROV_RSA_SIG_CTX *)vprsactx; unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int dlen = 0; + int operation; int ret; if (!priv) @@ -1602,7 +1624,18 @@ static int uadk_signature_rsa_digest_sign_final(void *vprsactx, unsigned char *s priv->flag_allow_md = 1; - ret = uadk_signature_rsa_sign(vprsactx, sig, siglen, sigsize, digest, (size_t)dlen); + /* + * We are called with the final digest. Presenting SIGNMSG would make + * the default provider's rsa_sign() take the message path and hash + * the digest again through the already finalized mdctx. Temporarily + * present a plain SIGN so a DO_SOFT fallback takes the digest path + * (rsa_sign_directly). + */ + operation = priv->operation; + priv->operation = EVP_PKEY_OP_SIGN; + ret = uadk_signature_rsa_sign(vprsactx, sig, siglen, sigsize, + digest, (size_t)dlen); + priv->operation = operation; if (sig != NULL) { priv->flag_allow_update = 0; priv->flag_allow_oneshot = 0; @@ -1615,8 +1648,13 @@ static int uadk_signature_rsa_digest_sign_final(void *vprsactx, unsigned char *s static int uadk_signature_rsa_digest_verify_init(void *vprsactx, const char *mdname, void *vrsa, const OSSL_PARAM params[]) { +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + return uadk_signature_rsa_digest_signverify_init(vprsactx, mdname, vrsa, + params, EVP_PKEY_OP_VERIFYMSG); +#else return uadk_signature_rsa_digest_signverify_init(vprsactx, mdname, vrsa, params, EVP_PKEY_OP_VERIFY); +#endif } static int uadk_signature_rsa_digest_verify_update(void *vprsactx, const unsigned char *data, @@ -1636,6 +1674,7 @@ static int uadk_signature_rsa_digest_verify_final(void *vprsactx, const unsigned struct PROV_RSA_SIG_CTX *priv = (struct PROV_RSA_SIG_CTX *)vprsactx; unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int dlen = 0; + int operation; int ret; if (!priv) @@ -1653,7 +1692,16 @@ static int uadk_signature_rsa_digest_verify_final(void *vprsactx, const unsigned priv->flag_allow_md = 1; - ret = uadk_signature_rsa_verify(vprsactx, sig, siglen, digest, (size_t)dlen); + /* + * Same as in uadk_signature_rsa_digest_sign_final(): we are called + * with the final digest, so present a plain VERIFY operation to keep + * a DO_SOFT fallback on the default provider's digest verify path. + */ + operation = priv->operation; + priv->operation = EVP_PKEY_OP_VERIFY; + ret = uadk_signature_rsa_verify(vprsactx, sig, siglen, + digest, (size_t)dlen); + priv->operation = operation; priv->flag_allow_update = 0; priv->flag_allow_final = 0; -- 2.43.0
participants (1)
-
Chenghai Huang