From 93da1d0eac2292b0b4b6e7fa95385c00b35c9d41 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 21 Jul 2026 10:03:14 -0500 Subject: [PATCH 01/19] linuxkm/lkcapi_aes_glue.c, linuxkm/lkcapi_glue.c, configure.ac: implement linuxkm cmac(aes) glue. --- .wolfssl_known_macro_extras | 1 + configure.ac | 3 + linuxkm/lkcapi_aes_glue.c | 674 ++++++++++++++++++++++++++++++++++++ linuxkm/lkcapi_glue.c | 6 + 4 files changed, 684 insertions(+) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 9f3f45f24de..cb27325f5cf 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -75,6 +75,7 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE CONFIG_CRYPTO_AES CONFIG_CRYPTO_CBC CONFIG_CRYPTO_CCM +CONFIG_CRYPTO_CMAC CONFIG_CRYPTO_CTR CONFIG_CRYPTO_DH CONFIG_CRYPTO_DH_RFC7919_GROUPS diff --git a/configure.ac b/configure.ac index a6be4cdd7cc..662214246fe 100644 --- a/configure.ac +++ b/configure.ac @@ -11633,6 +11633,8 @@ then 'ofb(aes)') test "$ENABLED_AESOFB" != "no" || AC_MSG_ERROR([linuxkm-lkcapi-register ${lkcapi_alg}: AES-OFB implementation not enabled.]) AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_REGISTER_AESOFB" ;; 'ecb(aes)') AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_REGISTER_AESECB -DHAVE_AES_ECB" ;; + 'cmac(aes)') test "$ENABLED_CMAC" != "no" || AC_MSG_ERROR([linuxkm-lkcapi-register ${lkcapi_alg}: AES-CMAC implementation not enabled.]) + AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_REGISTER_AESCMAC -DHAVE_AES_ECB" ;; 'all-aes') test "$ENABLED_AES" != "no" || AC_MSG_ERROR([linuxkm-lkcapi-register ${lkcapi_alg}: AES is disabled.]) AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_REGISTER_AES_ALL" ;; 'sha1') test "$ENABLED_SHA" != "no" || AC_MSG_ERROR([linuxkm-lkcapi-register ${lkcapi_alg}: SHA-1 implementation not enabled.]) @@ -11676,6 +11678,7 @@ then '-ctr(aes)') AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_DONT_REGISTER_AESCTR" ;; '-ofb(aes)') AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_DONT_REGISTER_AESOFB" ;; '-ecb(aes)') AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_DONT_REGISTER_AESECB" ;; + '-cmac(aes)') AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_DONT_REGISTER_AESCMAC" ;; '-all-aes') AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_DONT_REGISTER_AES_ALL" ;; '-sha1') AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_DONT_REGISTER_SHA1" ;; '-sha2') AM_CFLAGS="$AM_CFLAGS -DLINUXKM_LKCAPI_DONT_REGISTER_SHA2" ;; diff --git a/linuxkm/lkcapi_aes_glue.c b/linuxkm/lkcapi_aes_glue.c index bd362226242..f5dd0f757fd 100644 --- a/linuxkm/lkcapi_aes_glue.c +++ b/linuxkm/lkcapi_aes_glue.c @@ -44,6 +44,7 @@ defined(LINUXKM_LKCAPI_REGISTER_AESCTR) || \ defined(LINUXKM_LKCAPI_REGISTER_AESOFB) || \ defined(LINUXKM_LKCAPI_REGISTER_AESECB) || \ + defined(LINUXKM_LKCAPI_REGISTER_AESCMAC) || \ defined(LINUXKM_LKCAPI_REGISTER_AES_ALL) #ifdef NO_AES @@ -95,6 +96,7 @@ #define WOLFKM_AESCTR_NAME "ctr(aes)" #define WOLFKM_AESOFB_NAME "ofb(aes)" #define WOLFKM_AESECB_NAME "ecb(aes)" +#define WOLFKM_AESCMAC_NAME "cmac(aes)" #if defined(WOLFSSL_X86_64_BUILD) && (defined(USE_INTEL_SPEEDUP) || defined(USE_INTEL_SPEEDUP_FOR_AES)) #if !defined(NO_AVX512_SUPPORT) @@ -125,6 +127,7 @@ #define WOLFKM_AESCTR_DRIVER ("ctr-aes" WOLFKM_AES_DRIVER_SUFFIX) #define WOLFKM_AESOFB_DRIVER ("ofb-aes" WOLFKM_AES_DRIVER_SUFFIX) #define WOLFKM_AESECB_DRIVER ("ecb-aes" WOLFKM_AES_DRIVER_SUFFIX) +#define WOLFKM_AESCMAC_DRIVER ("cmac-aes" WOLFKM_AES_DRIVER_SUFFIX) #ifdef HAVE_AES_CBC #if (defined(LINUXKM_LKCAPI_REGISTER_ALL) || \ @@ -252,6 +255,25 @@ #endif #undef LINUXKM_LKCAPI_REGISTER_AESECB #endif +#if defined(WOLFSSL_CMAC) && defined(WOLFSSL_AES_DIRECT) + #if (defined(LINUXKM_LKCAPI_REGISTER_ALL) || \ + defined(LINUXKM_LKCAPI_REGISTER_AES_ALL) || \ + (defined(LINUXKM_LKCAPI_REGISTER_ALL_KCONFIG) && defined(CONFIG_CRYPTO_CMAC))) && \ + !defined(LINUXKM_LKCAPI_DONT_REGISTER_AESCMAC) && \ + !defined(LINUXKM_LKCAPI_REGISTER_AESCMAC) + #define LINUXKM_LKCAPI_REGISTER_AESCMAC + #endif +#else + #if defined(LINUXKM_LKCAPI_REGISTER_ALL_KCONFIG) && defined(CONFIG_CRYPTO_CMAC) && \ + !defined(LINUXKM_LKCAPI_DONT_REGISTER_AESCMAC) + #error Config conflict: target kernel has CONFIG_CRYPTO_CMAC, but module is missing WOLFSSL_CMAC and/or WOLFSSL_AES_DIRECT. + #endif + #undef LINUXKM_LKCAPI_REGISTER_AESCMAC +#endif + +#ifdef LINUXKM_LKCAPI_REGISTER_AESCMAC + #include +#endif #ifdef LINUXKM_LKCAPI_REGISTER_AESCBC static int linuxkm_test_aescbc(void); @@ -283,6 +305,9 @@ #ifdef LINUXKM_LKCAPI_REGISTER_AESECB static int linuxkm_test_aesecb(void); #endif +#ifdef LINUXKM_LKCAPI_REGISTER_AESCMAC + static int linuxkm_test_aescmac(void); +#endif #if defined(LINUXKM_LKCAPI_REGISTER_AESCBC) || \ defined(LINUXKM_LKCAPI_REGISTER_AESCFB) || \ @@ -4981,6 +5006,655 @@ static int linuxkm_test_aesecb(void) { #endif /* LINUXKM_LKCAPI_REGISTER_AESECB */ +#ifdef LINUXKM_LKCAPI_REGISTER_AESCMAC + +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(6,0,0) + #define WC_LINUXKM_CMAC_HAVE_CMACFREE +#endif + +/* The transient-copy design below struct-copies a keyed Cmac, which is sound + * only while the embedded Aes owns no heap allocations in module + * configurations: wc_AesFree()'s only mainstream XFREE() target is the + * GCM-streaming streamData buffer, which the CMAC path never allocates. + * _CRYPTO_CB per-instance contexts would break that invariant. + */ +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) + #error LINUXKM_LKCAPI_REGISTER_AESCMAC is incompatible with WOLF_CRYPTO_CB_FREE. +#endif + +/* Per-tfm state -- a pristine keyed Cmac, built at km_AesCmacSetKey() and + * owned by the tfm, whose lifecycle (unlike a desc's) has a guaranteed + * exit_tfm. descs never own heap: the kernel's desc ctx contract permits + * memcpy, poisoning, and discard-without-final (cf. testmgr_poison() between + * export and import in crypto/testmgr.c), under which any desc-owned + * allocation is structurally leak-prone. + */ +struct km_AesCmacTfmCtx { + Cmac *pristine; +}; + +/* Serialized mid-stream state for .export/.import, and also the entire + * per-desc state, held inline: pure POD, so poison/memcpy/discard are all + * safe by construction. Deliberately excludes all key material (raw key, + * schedule, k1/k2 subkeys) -- ops rematerialize those by struct-copying the + * tfm-owned pristine Cmac, so the blob that transits caller-owned memory + * (e.g. AF_ALG state transport) carries only chaining state. The format is + * private to this driver: export/import always round-trips within a single + * tfm, so no cross-implementation compatibility obtains, and native + * endianness and widths are correct by construction. + */ +struct km_AesCmacExportState { + byte digest[WC_AES_BLOCK_SIZE]; + byte buffer[WC_AES_BLOCK_SIZE]; + word32 bufferSz; + word32 totalSz; +}; + +struct km_AesCmacDescCtx { + struct km_AesCmacExportState st; +}; + +/* The state shuttling reaches into struct Cmac internals by field name -- no + * cryptographic substance, but width-sensitive. Pin the assumptions at + * compile time so any divergence across FIPS-boundary or config variants of + * cmac.h fails the build rather than the runtime. + */ +wc_static_assert(sizeof(((Cmac *)0)->digest) == WC_AES_BLOCK_SIZE); +wc_static_assert(sizeof(((Cmac *)0)->buffer) == WC_AES_BLOCK_SIZE); +wc_static_assert(sizeof(((Cmac *)0)->bufferSz) == sizeof(word32)); +wc_static_assert(sizeof(((Cmac *)0)->totalSz) == sizeof(word32)); +/* No implicit padding: statesize-sized exports must be fully written, lest + * uninitialized kernel bytes leak to userspace through AF_ALG. + */ +wc_static_assert(sizeof(struct km_AesCmacExportState) == + (2 * WC_AES_BLOCK_SIZE) + (2 * sizeof(word32))); +wc_static_assert(sizeof(struct km_AesCmacDescCtx) == + sizeof(struct km_AesCmacExportState)); + +/* full teardown of a live, fully-initialized Cmac. */ +static void km_AesCmacDispose(Cmac **cmac) +{ +#ifdef WC_LINUXKM_CMAC_HAVE_CMACFREE + (void)wc_CmacFree(*cmac); +#else + byte discard[WC_CMAC_TAG_MAX_SZ]; + word32 discard_sz = (word32)sizeof(discard); + (void)wc_CmacFinal(*cmac, discard, &discard_sz); + ForceZero(discard, sizeof(discard)); +#endif + free(*cmac); + *cmac = NULL; +} + +static int km_AesCmacSetKey(struct crypto_shash *tfm, const u8 *key, + unsigned int keylen) +{ + struct km_AesCmacTfmCtx *t_ctx = + (struct km_AesCmacTfmCtx *)crypto_shash_ctx(tfm); + Cmac *new_pristine; + int ret; + + if ((keylen != AES_128_KEY_SIZE) && + (keylen != AES_192_KEY_SIZE) && + (keylen != AES_256_KEY_SIZE)) + { + return -EINVAL; + } + + new_pristine = (Cmac *)malloc(sizeof(*new_pristine)); + if (! new_pristine) + return -ENOMEM; + + ret = wc_InitCmac(new_pristine, key, (word32)keylen, WC_CMAC_AES, + NULL /* unused */); + if (ret != 0) { + /* wc_InitCmac() zeroizes the Cmac before use, but doesn't release the + * embedded Aes on post-wc_AesInit() failures. + */ +#ifdef WC_LINUXKM_CMAC_HAVE_CMACFREE + (void)wc_CmacFree(new_pristine); +#else + /* no wc_CmacFree() in the boundary -- a failed-init Cmac holds no + * allocations in pre-v6 FIPS configurations, so zeroization suffices. + */ + ForceZero(new_pristine, sizeof(*new_pristine)); +#endif + free(new_pristine); + /* fail closed on rekey failure -- the kernel re-flags NEED_KEY, and a + * stale pristine would misattribute subsequent traffic to the old + * key. + */ + if (t_ctx->pristine) + km_AesCmacDispose(&t_ctx->pristine); + return -EINVAL; + } + + if (t_ctx->pristine) + km_AesCmacDispose(&t_ctx->pristine); + t_ctx->pristine = new_pristine; + + return 0; +} + +static void km_AesCmacExitTfm(struct crypto_shash *tfm) +{ + struct km_AesCmacTfmCtx *t_ctx = + (struct km_AesCmacTfmCtx *)crypto_shash_ctx(tfm); + if (t_ctx->pristine) + km_AesCmacDispose(&t_ctx->pristine); + return; +} + +/* Materialize a transient working Cmac: struct copy of the tfm-owned pristine + * (rederiving nothing -- schedule and k1/k2 come along in the image), then + * install the desc's streaming state. The transient shares only the value + * image, never ownership, with the pristine. + */ +static int km_AesCmacMaterialize(struct crypto_shash *tfm, + const struct km_AesCmacExportState *st, + Cmac **cmac_out) +{ + struct km_AesCmacTfmCtx *t_ctx = + (struct km_AesCmacTfmCtx *)crypto_shash_ctx(tfm); + Cmac *cmac; + + if (t_ctx->pristine == NULL) + return -ENOKEY; + + cmac = (Cmac *)malloc(sizeof(*cmac)); + if (! cmac) + return -ENOMEM; + + XMEMCPY(cmac, t_ctx->pristine, sizeof(*cmac)); + + XMEMCPY(cmac->digest, st->digest, WC_AES_BLOCK_SIZE); + XMEMCPY(cmac->buffer, st->buffer, WC_AES_BLOCK_SIZE); + cmac->bufferSz = st->bufferSz; + cmac->totalSz = st->totalSz; + + *cmac_out = cmac; + return 0; +} + +static void km_AesCmacExtract(const Cmac *cmac, + struct km_AesCmacExportState *st) +{ + XMEMCPY(st->digest, cmac->digest, WC_AES_BLOCK_SIZE); + XMEMCPY(st->buffer, cmac->buffer, WC_AES_BLOCK_SIZE); + st->bufferSz = cmac->bufferSz; + st->totalSz = cmac->totalSz; +} + +static int km_AesCmacInit(struct shash_desc *desc) +{ + struct km_AesCmacDescCtx *d_ctx = + (struct km_AesCmacDescCtx *)shash_desc_ctx(desc); + struct km_AesCmacTfmCtx *t_ctx = + (struct km_AesCmacTfmCtx *)crypto_shash_ctx(desc->tfm); + + if (t_ctx->pristine == NULL) + return -ENOKEY; + + /* The all-zero state is exactly the streaming state of a freshly + * wc_InitCmac()ed Cmac. + */ + XMEMSET(&d_ctx->st, 0, sizeof(d_ctx->st)); + + return 0; +} + +/* Note, inefficiency here in Update() is to accommodate the LKCAPI's lack of a + * cleanup hook for the desc ctx. Otherwise, we could cache a heap-allocated + * live Cmac object, accessed via a pointer in the shash_desc_ctx(), and just + * pass it to wc_CmacUpdate() in each call to km_AesCmacUpdate(). If a + * performance requirement materializes, this is still possible, awkwardly, + * e.g. by using kmem_cache, or adding a thread-synchronized linked list + * accessible via the tfm object, assuring deallocation at worst when + * km_AesCmacExitTfm() is called. + */ +static int km_AesCmacUpdate(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct km_AesCmacDescCtx *d_ctx = + (struct km_AesCmacDescCtx *)shash_desc_ctx(desc); + Cmac *cmac; + int ret; + + ret = km_AesCmacMaterialize(desc->tfm, &d_ctx->st, &cmac); + if (ret != 0) + return ret; + + ret = wc_CmacUpdate(cmac, data, len); + if (ret == 0) + km_AesCmacExtract(cmac, &d_ctx->st); + + km_AesCmacDispose(&cmac); + + return (ret == 0) ? 0 : -EINVAL; +} + +static int km_AesCmacFinal(struct shash_desc *desc, u8 *out) +{ + struct km_AesCmacDescCtx *d_ctx = + (struct km_AesCmacDescCtx *)shash_desc_ctx(desc); + Cmac *cmac; + int ret; + + ret = km_AesCmacMaterialize(desc->tfm, &d_ctx->st, &cmac); + if (ret != 0) + return ret; + + { + word32 outSz = WC_AES_BLOCK_SIZE; + ret = wc_CmacFinal(cmac, out, &outSz); + } + + /* wc_CmacFinal() zeroizes and releases the Cmac unconditionally, on all + * supported FIPS and non-FIPS code paths -- only the container allocation + * remains to be freed. + */ + free(cmac); + + /* scrub the spent streaming state. */ + ForceZero(&d_ctx->st, sizeof(d_ctx->st)); + + return (ret == 0) ? 0 : -EINVAL; +} + +static int km_AesCmacFinup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + struct km_AesCmacDescCtx *d_ctx = + (struct km_AesCmacDescCtx *)shash_desc_ctx(desc); + Cmac *cmac; + int ret; + + /* single materialization for the combined update+final. */ + ret = km_AesCmacMaterialize(desc->tfm, &d_ctx->st, &cmac); + if (ret != 0) + return ret; + + ret = wc_CmacUpdate(cmac, data, len); + + if (ret != 0) { + km_AesCmacDispose(&cmac); + return -EINVAL; + } + + { + word32 outSz = WC_AES_BLOCK_SIZE; + ret = wc_CmacFinal(cmac, out, &outSz); + } + free(cmac); + + ForceZero(&d_ctx->st, sizeof(d_ctx->st)); + + return (ret == 0) ? 0 : -EINVAL; +} + +static int km_AesCmacDigest(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + int ret = km_AesCmacInit(desc); + if (ret != 0) + return ret; + return km_AesCmacFinup(desc, data, len, out); +} + +static int km_AesCmacExport(struct shash_desc *desc, void *out) +{ + struct km_AesCmacDescCtx *d_ctx = + (struct km_AesCmacDescCtx *)shash_desc_ctx(desc); + + /* non-destructive -- the desc remains live and can continue streaming. */ + XMEMCPY(out, &d_ctx->st, sizeof(d_ctx->st)); + + return 0; +} + +static int km_AesCmacImport(struct shash_desc *desc, const void *in) +{ + struct km_AesCmacDescCtx *d_ctx = + (struct km_AesCmacDescCtx *)shash_desc_ctx(desc); + const struct km_AesCmacExportState *st = + (const struct km_AesCmacExportState *)in; + + /* An out-of-range bufferSz would underflow the remaining-space + * calculation in the wc_CmacUpdate() buffering path -- reject before + * installing anything. + */ + if (st->bufferSz > WC_AES_BLOCK_SIZE) + return -EINVAL; + + /* Like .init, .import overwrites the desc ctx unconditionally -- the + * prior contents are the caller's responsibility and may be uninitialized + * or deliberately poisoned (c.f. testmgr_poison() in crypto/testmgr.c). + * As pure inline POD, they hold nothing to interpret, free, or zeroize. + */ + XMEMCPY(&d_ctx->st, st, sizeof(d_ctx->st)); + + return 0; +} + +static struct shash_alg cmacAesAlg = +{ + .digestsize = WC_AES_BLOCK_SIZE, + .init = km_AesCmacInit, + .update = km_AesCmacUpdate, + .final = km_AesCmacFinal, + .finup = km_AesCmacFinup, + .digest = km_AesCmacDigest, + .setkey = km_AesCmacSetKey, + .export = km_AesCmacExport, + .import = km_AesCmacImport, + .exit_tfm = km_AesCmacExitTfm, + .descsize = sizeof(struct km_AesCmacDescCtx), + .statesize = sizeof(struct km_AesCmacExportState), + .base = { + .cra_name = WOLFKM_AESCMAC_NAME, + .cra_driver_name = WOLFKM_AESCMAC_DRIVER, + .cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY, + .cra_blocksize = WC_AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct km_AesCmacTfmCtx), + .cra_module = THIS_MODULE + } +}; +static int cmacAesAlg_loaded = 0; + +static int linuxkm_test_aescmac(void) +{ + wc_test_ret_t wc_ret; + int ret = 0; + struct crypto_shash *tfm = NULL; + struct shash_desc *desc = NULL; + struct shash_desc *desc2 = NULL; + struct km_AesCmacExportState export_state; + size_t desc_size = 0; + + /* SP 800-38B / RFC 4493 example vectors */ + static const byte key128[AES_128_KEY_SIZE] = + { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }; + static const byte key256[AES_256_KEY_SIZE] = + { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, + 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, + 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 + }; + static const byte m_vector[40] = + { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 + }; + /* CMAC-AES128, Mlen=128 (first block of m_vector) */ + static const byte tag128_m16[WC_AES_BLOCK_SIZE] = + { + 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c + }; + /* CMAC-AES128, Mlen=320 */ + static const byte tag128_m40[WC_AES_BLOCK_SIZE] = + { + 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 + }; + /* CMAC-AES256, Mlen=320 */ + static const byte tag256_m40[WC_AES_BLOCK_SIZE] = + { + 0xaa, 0xf3, 0xd8, 0xf1, 0xde, 0x56, 0x40, 0xc2, + 0x32, 0xf5, 0xb1, 0x69, 0xb9, 0xc9, 0x11, 0xe6 + }; + byte tag[WC_AES_BLOCK_SIZE]; + + /* First, the wolfCrypt-native KATs. */ + wc_ret = cmac_test(); + if (wc_ret < 0) { + wc_test_render_error_message("cmac_test failed: ", wc_ret); + return WC_TEST_RET_DEC_EC(wc_ret); + } + + /* Now the kernel crypto part. */ + + tfm = crypto_alloc_shash(WOLFKM_AESCMAC_NAME, 0, 0); + if (IS_ERR(tfm)) { + ret = (int)PTR_ERR(tfm); + pr_err("ERROR: allocating shash algorithm %s failed: %d\n", + WOLFKM_AESCMAC_NAME, ret); + tfm = NULL; + goto test_cmac_end; + } + + ret = check_shash_driver_masking(tfm, WOLFKM_AESCMAC_NAME, + WOLFKM_AESCMAC_DRIVER); + if (ret) + goto test_cmac_end; + + if (crypto_shash_digestsize(tfm) != WC_AES_BLOCK_SIZE) { + pr_err("ERROR: shash algorithm %s crypto_shash_digestsize()" + " returned %d but expected %d\n", + WOLFKM_AESCMAC_DRIVER, crypto_shash_digestsize(tfm), + WC_AES_BLOCK_SIZE); + ret = -EINVAL; + goto test_cmac_end; + } + + desc_size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm); + desc = (struct shash_desc *)malloc(desc_size); + if (! desc) { + pr_err("ERROR: malloc failed\n"); + ret = -ENOMEM; + goto test_cmac_end; + } + XMEMSET(desc, 0, desc_size); + desc->tfm = tfm; + + /* Undersized/oversized keys must be rejected at setkey time. */ + if (crypto_shash_setkey(tfm, key256, AES_256_KEY_SIZE - 1) == 0) { + pr_err("ERROR: crypto_shash_setkey for %s accepted an invalid" + " key length.\n", WOLFKM_AESCMAC_NAME); + ret = -EINVAL; + goto test_cmac_end; + } + + ret = crypto_shash_setkey(tfm, key128, sizeof(key128)); + if (ret) { + pr_err("ERROR: crypto_shash_setkey for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + + /* One-shot digest, single-block message. */ + ret = crypto_shash_digest(desc, m_vector, WC_AES_BLOCK_SIZE, tag); + if (ret) { + pr_err("ERROR: crypto_shash_digest for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + if (XMEMCMP(tag, tag128_m16, sizeof(tag)) != 0) { + pr_err("ERROR: %s one-shot KAT mismatch (AES-128, Mlen=128)\n", + WOLFKM_AESCMAC_DRIVER); + ret = LINUXKM_LKCAPI_AES_KAT_MISMATCH_E; + goto test_cmac_end; + } + + /* Incremental init/update/final, with an unaligned split that straddles + * a block boundary, exercising the Cmac buffering path. + */ + ret = crypto_shash_init(desc); + if (ret) { + pr_err("ERROR: crypto_shash_init for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + ret = crypto_shash_update(desc, m_vector, 7); + if (ret == 0) + ret = crypto_shash_update(desc, m_vector + 7, sizeof(m_vector) - 7); + if (ret) { + pr_err("ERROR: crypto_shash_update for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + ret = crypto_shash_final(desc, tag); + if (ret) { + pr_err("ERROR: crypto_shash_final for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + if (XMEMCMP(tag, tag128_m40, sizeof(tag)) != 0) { + pr_err("ERROR: %s incremental KAT mismatch (AES-128, Mlen=320)\n", + WOLFKM_AESCMAC_DRIVER); + ret = LINUXKM_LKCAPI_AES_KAT_MISMATCH_E; + goto test_cmac_end; + } + + /* Re-init over a live mid-stream desc: under the kernel's desc ctx + * contract this discards the prior state without final, which must be + * loss-free by construction (nothing desc-owned to leak). + */ + ret = crypto_shash_init(desc); + if (ret == 0) + ret = crypto_shash_update(desc, m_vector, 7); + if (ret == 0) + ret = crypto_shash_init(desc); + if (ret == 0) + ret = crypto_shash_update(desc, m_vector, sizeof(m_vector)); + if (ret == 0) + ret = crypto_shash_final(desc, tag); + if (ret) { + pr_err("ERROR: shash re-init sequence for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + if (XMEMCMP(tag, tag128_m40, sizeof(tag)) != 0) { + pr_err("ERROR: %s re-init KAT mismatch (AES-128, Mlen=320)\n", + WOLFKM_AESCMAC_DRIVER); + ret = LINUXKM_LKCAPI_AES_KAT_MISMATCH_E; + goto test_cmac_end; + } + + /* Export/import round trip: export desc mid-stream, import into a second, + * deliberately poisoned desc, and finish both independently. This + * validates true cross-desc snapshot semantics, which testmgr's same-desc + * reimport sequencing cannot distinguish from pointer aliasing. + */ + if (crypto_shash_statesize(tfm) != + sizeof(struct km_AesCmacExportState)) + { + pr_err("ERROR: shash algorithm %s crypto_shash_statesize()" + " returned %d but expected %d\n", + WOLFKM_AESCMAC_DRIVER, crypto_shash_statesize(tfm), + (int)sizeof(struct km_AesCmacExportState)); + ret = -EINVAL; + goto test_cmac_end; + } + + desc2 = (struct shash_desc *)malloc(desc_size); + if (! desc2) { + pr_err("ERROR: malloc failed\n"); + ret = -ENOMEM; + goto test_cmac_end; + } + XMEMSET(desc2, 0xa5, desc_size); /* poison, a la testmgr */ + desc2->tfm = tfm; + + ret = crypto_shash_init(desc); + if (ret == 0) + ret = crypto_shash_update(desc, m_vector, 7); + if (ret == 0) + ret = crypto_shash_export(desc, &export_state); + if (ret) { + pr_err("ERROR: shash export sequence for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + + ret = crypto_shash_import(desc2, &export_state); + if (ret == 0) + ret = crypto_shash_update(desc2, m_vector + 7, sizeof(m_vector) - 7); + if (ret == 0) + ret = crypto_shash_final(desc2, tag); + if (ret) { + pr_err("ERROR: shash import sequence for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + if (XMEMCMP(tag, tag128_m40, sizeof(tag)) != 0) { + pr_err("ERROR: %s import-continuation KAT mismatch" + " (AES-128, Mlen=320)\n", WOLFKM_AESCMAC_DRIVER); + ret = LINUXKM_LKCAPI_AES_KAT_MISMATCH_E; + goto test_cmac_end; + } + + /* The exporting desc must remain live and correct. */ + ret = crypto_shash_update(desc, m_vector + 7, sizeof(m_vector) - 7); + if (ret == 0) + ret = crypto_shash_final(desc, tag); + if (ret) { + pr_err("ERROR: shash post-export continuation for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + if (XMEMCMP(tag, tag128_m40, sizeof(tag)) != 0) { + pr_err("ERROR: %s post-export continuation KAT mismatch" + " (AES-128, Mlen=320)\n", WOLFKM_AESCMAC_DRIVER); + ret = LINUXKM_LKCAPI_AES_KAT_MISMATCH_E; + goto test_cmac_end; + } + + /* Malformed state must be rejected before installation. */ + export_state.bufferSz = WC_AES_BLOCK_SIZE + 1; + if (crypto_shash_import(desc2, &export_state) == 0) { + pr_err("ERROR: crypto_shash_import for %s accepted an" + " out-of-range bufferSz.\n", WOLFKM_AESCMAC_NAME); + ret = -EINVAL; + goto test_cmac_end; + } + + /* Rekey with AES-256 and confirm one-shot. */ + ret = crypto_shash_setkey(tfm, key256, sizeof(key256)); + if (ret) { + pr_err("ERROR: crypto_shash_setkey for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + ret = crypto_shash_digest(desc, m_vector, sizeof(m_vector), tag); + if (ret) { + pr_err("ERROR: crypto_shash_digest for %s returned: %d\n", + WOLFKM_AESCMAC_NAME, ret); + goto test_cmac_end; + } + if (XMEMCMP(tag, tag256_m40, sizeof(tag)) != 0) { + pr_err("ERROR: %s one-shot KAT mismatch (AES-256, Mlen=320)\n", + WOLFKM_AESCMAC_DRIVER); + ret = LINUXKM_LKCAPI_AES_KAT_MISMATCH_E; + goto test_cmac_end; + } + +test_cmac_end: + + ForceZero(&export_state, sizeof(export_state)); + if (desc2) { + ForceZero(desc2, desc_size); + free(desc2); + } + if (desc) { + ForceZero(desc, desc_size); + free(desc); + } + if (tfm) + crypto_free_shash(tfm); + + return ret; +} + +#endif /* LINUXKM_LKCAPI_REGISTER_AESCMAC */ + #endif /* LINUXKM_LKCAPI_REGISTER_AES */ #endif /* !WC_SKIP_INCLUDED_C_FILES */ diff --git a/linuxkm/lkcapi_glue.c b/linuxkm/lkcapi_glue.c index a145770a4f8..de7e868d50a 100644 --- a/linuxkm/lkcapi_glue.c +++ b/linuxkm/lkcapi_glue.c @@ -460,6 +460,9 @@ static int linuxkm_lkcapi_register(void) #ifdef LINUXKM_LKCAPI_REGISTER_AESCCM REGISTER_ALG(ccmAesAead, aead, linuxkm_test_aesccm); #endif +#ifdef LINUXKM_LKCAPI_REGISTER_AESCMAC + REGISTER_ALG(cmacAesAlg, shash, linuxkm_test_aescmac); +#endif #ifdef LINUXKM_LKCAPI_REGISTER_AESXTS REGISTER_ALG(xtsAesAlg, skcipher, linuxkm_test_aesxts); #endif @@ -850,6 +853,9 @@ static int linuxkm_lkcapi_unregister(void) #ifdef LINUXKM_LKCAPI_REGISTER_AESCCM_RFC4309 UNREGISTER_ALG(ccmAesAead_rfc4309, aead); #endif +#ifdef LINUXKM_LKCAPI_REGISTER_AESCMAC + UNREGISTER_ALG(cmacAesAlg, shash); +#endif #ifdef LINUXKM_LKCAPI_REGISTER_AESXTS UNREGISTER_ALG(xtsAesAlg, skcipher); #endif From c877dece0253a6f2ea061d71a9a51b8d699a3c99 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 22 Jul 2026 09:51:20 -0500 Subject: [PATCH 02/19] linuxkm/lkcapi_sha_glue.c: fix sha.W lifecycle management to prevent leaks: * separate WC_LINUXKM_SHA_IMPLEMENT() into WC_LINUXKM_SHA1_IMPLEMENT() (no fixes needed) and WC_LINUXKM_SHA2_IMPLEMENT() (with associated new helpers WC_LINUXKM_SHA2_FREE_W(), WC_LINUXKM_SHA2_DECL_W(), WC_LINUXKM_SHA2_PUSH_W(), and WC_LINUXKM_SHA2_POP_W(), that move .W to a stack buffer). * Reimplement SHA-2 one-shot digest callback to use only direct wolfCrypt calls rather than proxy to other callbacks. --- linuxkm/lkcapi_sha_glue.c | 186 +++++++++++++++++++++++++++++++++++-- wolfcrypt/src/sha256.c | 22 ++--- wolfcrypt/src/sha512.c | 12 +-- wolfssl/wolfcrypt/sha256.h | 9 +- wolfssl/wolfcrypt/sha512.h | 6 +- 5 files changed, 208 insertions(+), 27 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index f7b275c4a6a..6c88ba0fc88 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -486,7 +486,7 @@ WC_MAYBE_UNUSED static int sha3_test_once(void) { } #endif -#define WC_LINUXKM_SHA_IMPLEMENT(name, digest_size, block_size, \ +#define WC_LINUXKM_SHA1_IMPLEMENT(name, digest_size, block_size, \ this_cra_name, this_cra_driver_name, \ init_f, update_f, final_f, \ free_f, test_routine) \ @@ -588,6 +588,169 @@ static int linuxkm_test_ ## name(void) { \ \ struct wc_swallow_the_semicolon +#ifdef WOLFSSL_SMALL_STACK_CACHE + /* The glue layer needs to take ownership of the .W working buffer to assure + * it can't leak on abandoned descs, or double-free on export-import cycled + * descs. It's small enough to fit comfortably on the stack, so there's + * almost no overhead associated with this. + * + * Eager allocation of .W in SHA-2 init is to assure no heap operations in + * SHA-2 after init, mitigating an infinite recursion: The wolfCrypt DRBG + * sits atop SHA-2, and when LINUXKM_DRBG_GET_RANDOM_BYTES && + * WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS && CONFIG_SLAB_FREELIST_RANDOM, + * it sits _under_ the kernel heap. + */ + #define WC_LINUXKM_SHA2_FREE_W(s) do { free((s)->W); (s)->W = NULL; } while (0) + #define WC_LINUXKM_SHA2_DECL_W(s, l) wc_static_assert((l) % sizeof (s)->W[0] == 0); \ + typeof((s)->W[0]) w_buf[(l) / sizeof (s)->W[0]] + #define WC_LINUXKM_SHA2_PUSH_W(s) { (s)->W = w_buf + #define WC_LINUXKM_SHA2_POP_W(s) ForceZero(w_buf, sizeof w_buf); (s)->W = NULL; } WC_DO_NOTHING +#else + #define WC_LINUXKM_SHA2_FREE_W(s) WC_DO_NOTHING + #define WC_LINUXKM_SHA2_DECL_W(s, l) struct wc_swallow_the_semicolon + #define WC_LINUXKM_SHA2_PUSH_W(s) { WC_DO_NOTHING + #define WC_LINUXKM_SHA2_POP_W(s) } WC_DO_NOTHING +#endif + +#define WC_LINUXKM_SHA2_IMPLEMENT(name, digest_size, block_size, W_size, \ + this_cra_name, this_cra_driver_name, \ + init_f, update_f, final_f, \ + free_f, test_routine) \ + \ + \ +static int km_ ## name ## _init(struct shash_desc *desc) { \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + \ + int ret = init_f(&ctx-> name ## _state); \ + if (ret == 0) { \ + WC_LINUXKM_SHA2_FREE_W(&ctx-> name ## _state); \ + return 0; \ + } \ + else \ + return -EINVAL; \ +} \ + \ +static int km_ ## name ## _update(struct shash_desc *desc, const u8 *data, \ + unsigned int len) \ +{ \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + int ret; \ + WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + \ + WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ + ret = update_f(&ctx-> name ## _state, data, len); \ + WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + \ + if (ret == 0) \ + return 0; \ + else { \ + free_f(&ctx-> name ## _state); \ + return -EINVAL; \ + } \ +} \ + \ +static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + int ret; \ + WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + \ + WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ + ret = final_f(&ctx-> name ## _state, out); \ + WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + \ + free_f(&ctx-> name ## _state); \ + \ + if (ret == 0) \ + return 0; \ + else \ + return -EINVAL; \ +} \ + \ +static int km_ ## name ## _finup(struct shash_desc *desc, const u8 *data, \ + unsigned int len, u8 *out) \ +{ \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + int ret; \ + WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + \ + WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ + ret = update_f(&ctx-> name ## _state, data, len); \ + WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + \ + if (ret != 0) { \ + free_f(&ctx-> name ## _state); \ + return -EINVAL; \ + } \ + \ + WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ + ret = final_f(&ctx-> name ## _state, out); \ + WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + \ + free_f(&ctx-> name ## _state); \ + \ + if (ret == 0) \ + return 0; \ + else \ + return -EINVAL; \ +} \ + \ +static int km_ ## name ## _digest(struct shash_desc *desc, const u8 *data, \ + unsigned int len, u8 *out) \ +{ \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + int ret; \ + \ + ret = init_f(&ctx-> name ## _state); \ + if (ret != 0) \ + return -EINVAL; \ + \ + ret = update_f(&ctx-> name ## _state, data, len); \ + \ + if (ret == 0) \ + ret = final_f(&ctx-> name ## _state, out); \ + \ + free_f(&ctx-> name ## _state); \ + \ + if (ret == 0) \ + return 0; \ + else \ + return -EINVAL; \ +} \ + \ + \ +static struct shash_alg name ## _alg = \ +{ \ + .digestsize = (digest_size), \ + .init = km_ ## name ## _init, \ + .update = km_ ## name ## _update, \ + .final = km_ ## name ## _final, \ + .finup = km_ ## name ## _finup, \ + .digest = km_ ## name ## _digest, \ + .descsize = sizeof(struct km_sha_state), \ + .base = { \ + .cra_name = (this_cra_name), \ + .cra_driver_name = (this_cra_driver_name), \ + .cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY, \ + .cra_blocksize = (block_size), \ + .cra_module = THIS_MODULE \ + } \ +}; \ +static int name ## _alg_loaded = 0; \ + \ +static int linuxkm_test_ ## name(void) { \ + wc_test_ret_t ret = test_routine(); \ + if (ret >= 0) \ + return check_shash_driver_masking(NULL /* tfm */, this_cra_name, \ + this_cra_driver_name); \ + else { \ + wc_test_render_error_message("linuxkm_test_" #name " failed: ", \ + ret); \ + return WC_TEST_RET_DEC_EC(ret); \ + } \ +} \ + \ +struct wc_swallow_the_semicolon + #define WC_LINUXKM_SHA3_IMPLEMENT(name, digest_size, block_size, \ this_cra_name, this_cra_driver_name, \ init_f, update_f, final_f, \ @@ -699,35 +862,46 @@ static int linuxkm_test_ ## name(void) { \ struct wc_swallow_the_semicolon #ifdef LINUXKM_LKCAPI_REGISTER_SHA1 - WC_LINUXKM_SHA_IMPLEMENT(sha1, WC_SHA_DIGEST_SIZE, WC_SHA_BLOCK_SIZE, + WC_LINUXKM_SHA1_IMPLEMENT(sha1, WC_SHA_DIGEST_SIZE, WC_SHA_BLOCK_SIZE, WOLFKM_SHA1_NAME, WOLFKM_SHA1_DRIVER, wc_InitSha, wc_ShaUpdate, wc_ShaFinal, wc_ShaFree, sha_test); #endif +#ifndef WC_SHA256_W_SIZE + #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) +#endif +#ifndef WC_SHA512_W_SIZE + #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) +#endif + #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_224 - WC_LINUXKM_SHA_IMPLEMENT(sha2_224, WC_SHA224_DIGEST_SIZE, WC_SHA224_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_224, WC_SHA224_DIGEST_SIZE, WC_SHA224_BLOCK_SIZE, + WC_SHA256_W_SIZE, WOLFKM_SHA2_224_NAME, WOLFKM_SHA2_224_DRIVER, wc_InitSha224, wc_Sha224Update, wc_Sha224Final, wc_Sha224Free, sha224_test); #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_256 - WC_LINUXKM_SHA_IMPLEMENT(sha2_256, WC_SHA256_DIGEST_SIZE, WC_SHA256_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_256, WC_SHA256_DIGEST_SIZE, WC_SHA256_BLOCK_SIZE, + WC_SHA256_W_SIZE, WOLFKM_SHA2_256_NAME, WOLFKM_SHA2_256_DRIVER, wc_InitSha256, wc_Sha256Update, wc_Sha256Final, wc_Sha256Free, sha256_test); #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_384 - WC_LINUXKM_SHA_IMPLEMENT(sha2_384, WC_SHA384_DIGEST_SIZE, WC_SHA384_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_384, WC_SHA384_DIGEST_SIZE, WC_SHA384_BLOCK_SIZE, + WC_SHA512_W_SIZE, WOLFKM_SHA2_384_NAME, WOLFKM_SHA2_384_DRIVER, wc_InitSha384, wc_Sha384Update, wc_Sha384Final, wc_Sha384Free, sha384_test); #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_512 - WC_LINUXKM_SHA_IMPLEMENT(sha2_512, WC_SHA512_DIGEST_SIZE, WC_SHA512_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_512, WC_SHA512_DIGEST_SIZE, WC_SHA512_BLOCK_SIZE, + WC_SHA512_W_SIZE, WOLFKM_SHA2_512_NAME, WOLFKM_SHA2_512_DRIVER, wc_InitSha512, wc_Sha512Update, wc_Sha512Final, wc_Sha512Free, sha512_test); diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index 4c3d4fd2ebb..16a56bc7575 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -747,7 +747,7 @@ static int InitSha256(wc_Sha256* sha256) sha256->devCtx = NULL; #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1329,7 +1329,7 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId) #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1482,7 +1482,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->devCtx = NULL; #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1570,12 +1570,12 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, return BAD_FUNC_ARG; #elif defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) word32* W; - W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); if (W == NULL) return MEMORY_E; #else - word32 W[WC_SHA256_BLOCK_SIZE]; + word32 W[WC_SHA256_W_SIZE / sizeof(word32)]; #endif /* Copy context->state[] to working vars */ @@ -1616,7 +1616,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #endif return 0; } -#else +#else /* SHA256_MANY_REGISTERS */ /* SHA256 version that keeps all data in registers */ #define SCHED1(j) (W[j] = *((word32*)&data[j*sizeof(word32)])) #define SCHED(j) ( \ @@ -1644,7 +1644,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef USE_SLOW_SHA256 int j; #endif - word32 W[WC_SHA256_BLOCK_SIZE/sizeof(word32)]; + word32 W[WC_SHA256_W_SIZE / sizeof(word32)]; /* Copy digest to working vars */ S[0] = sha256->digest[0]; @@ -1693,7 +1693,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, return 0; } #endif /* SHA256_MANY_REGISTERS */ -#endif +#endif /* NEED_SOFT_SHA256 */ /* End wc_ software implementation */ #ifdef XTRANSFORM @@ -2484,7 +2484,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef WOLFSSL_SMALL_STACK_CACHE if (sha224->W == NULL) { - sha224->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha224->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha224->heap, DYNAMIC_TYPE_DIGEST); if (sha224->W == NULL) return MEMORY_E; @@ -3054,7 +3054,7 @@ int wc_Sha224_Grow(wc_Sha224* sha224, const byte* in, int inSz) XMEMCPY(dst, src, sizeof(wc_Sha224)); #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + dst->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha224)); @@ -3208,7 +3208,7 @@ int wc_Sha256Copy(wc_Sha256* src, wc_Sha256* dst) #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + dst->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha256)); diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 311ff37e436..6a6bde5a03c 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -1714,7 +1714,7 @@ static int InitSha512_Family(wc_Sha512* sha512, void* heap, int devId, * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - sha512->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + sha512->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, sha512->heap, DYNAMIC_TYPE_DIGEST); if (sha512->W == NULL) return MEMORY_E; @@ -1890,11 +1890,11 @@ static int _Transform_Sha512(wc_Sha512* sha512) return BAD_FUNC_ARG; #elif defined(WOLFSSL_SMALL_STACK) word64* W; - W = (word64*) XMALLOC(sizeof(word64) * 16, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); + W = (word64*)XMALLOC(WC_SHA512_W_SIZE, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); if (W == NULL) return MEMORY_E; #else - word64 W[16]; + word64 W[WC_SHA512_W_SIZE / sizeof(word64)]; #endif /* Copy digest to working vars */ @@ -2759,7 +2759,7 @@ static int InitSha384(wc_Sha384* sha384) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - sha384->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + sha384->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, sha384->heap, DYNAMIC_TYPE_DIGEST); if (sha384->W == NULL) return MEMORY_E; @@ -3117,7 +3117,7 @@ int wc_Sha512Copy(wc_Sha512* src, wc_Sha512* dst) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + dst->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha512)); @@ -3559,7 +3559,7 @@ int wc_Sha384Copy(wc_Sha384* src, wc_Sha384* dst) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA384_BLOCK_SIZE, + dst->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha384)); diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index 8c0f49b30c9..e78f0a80b9e 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -199,7 +199,7 @@ struct wc_Sha256 { int sha_method; #endif -#endif +#endif /* !FREESCALE_LTC_SHA etc. */ void* heap; #ifdef WOLFSSL_PIC32MZ_HASH hashUpdCache cache; /* cache for updates */ @@ -207,9 +207,14 @@ struct wc_Sha256 { #ifdef WOLFSSL_ASYNC_CRYPT WC_ASYNC_DEV asyncDev; #endif /* WOLFSSL_ASYNC_CRYPT */ +#ifdef SHA256_MANY_REGISTERS + #define WC_SHA256_W_SIZE WC_SHA256_BLOCK_SIZE +#else /* !SHA256_MANY_REGISTERS */ + #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) +#endif /* !SHA256_MANY_REGISTERS */ #ifdef WOLFSSL_SMALL_STACK_CACHE word32* W; -#endif /* !FREESCALE_LTC_SHA && !STM32_HASH_SHA2 */ +#endif /* WOLFSSL_SMALL_STACK_CACHE */ #ifdef WOLFSSL_DEVCRYPTO_HASH WC_CRYPTODEV ctx; #endif diff --git a/wolfssl/wolfcrypt/sha512.h b/wolfssl/wolfcrypt/sha512.h index 5f2ea96f212..c8bc8ef0479 100644 --- a/wolfssl/wolfcrypt/sha512.h +++ b/wolfssl/wolfcrypt/sha512.h @@ -166,9 +166,11 @@ struct wc_Sha512 { WC_ASYNC_DEV asyncDev; #endif /* WOLFSSL_ASYNC_CRYPT */ #ifdef WOLFSSL_SMALL_STACK_CACHE + #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) word64* W; -#endif - +#else /* !WOLFSSL_SMALL_STACK_CACHE */ + #define WC_SHA512_W_SIZE (sizeof(word64) * 16) +#endif /* !WOLFSSL_SMALL_STACK_CACHE */ #if defined(WOLFSSL_ESP32_CRYPT) && \ !defined(NO_WOLFSSL_ESP32_CRYPT_HASH) && \ (!defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) || \ From b345813ae67995ba6b9bd13ae8b3470f37f504ac Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 22 Jul 2026 11:17:42 -0500 Subject: [PATCH 03/19] linuxkm/linuxkm_wc_port.h, linuxkm/module_hooks.c: implement magic checks on kernel struct wolfSSL_Mutex in WOLFSSL_LINUXKM_VERBOSE_DEBUG builds. --- linuxkm/linuxkm_wc_port.h | 34 +++++++++++++++++++++++++++++++++- linuxkm/module_hooks.c | 4 ++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index ca4ac907490..3c4360893f3 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -1962,9 +1962,28 @@ typedef struct wolfSSL_Mutex { spinlock_t lock; unsigned long irq_flags; + #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + unsigned int magic; + #endif } wolfSSL_Mutex; - #define WOLFSSL_MUTEX_INITIALIZER(lockname) { .lock =__SPIN_LOCK_UNLOCKED(lockname), .irq_flags = 0 } + #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + #define WC_LINUXKM_SPINLOCK_MAGIC 1702166717U + + #define WOLFSSL_MUTEX_INITIALIZER(lockname) { \ + .lock =__SPIN_LOCK_UNLOCKED(lockname), \ + .irq_flags = 0, \ + .magic = WC_LINUXKM_SPINLOCK_MAGIC \ + } + + #else + + #define WOLFSSL_MUTEX_INITIALIZER(lockname) { \ + .lock =__SPIN_LOCK_UNLOCKED(lockname), \ + .irq_flags = 0 \ + } + + #endif static __always_inline int wc_InitMutex(wolfSSL_Mutex* m) { @@ -1975,12 +1994,21 @@ spin_lock_init(&m->lock); #endif m->irq_flags = 0; + #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + m->magic = WC_LINUXKM_SPINLOCK_MAGIC; + #endif return 0; } static __always_inline int wc_FreeMutex(wolfSSL_Mutex* m) { + #ifdef CONFIG_DEBUG_SPINLOCK + m->lock.magic = 0; + #endif + #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + m->magic = 0; + #endif (void)m; return 0; } @@ -2005,6 +2033,10 @@ static __always_inline int wc_UnLockMutex(wolfSSL_Mutex* m) { + #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + if ((m == NULL) || (m->magic != WC_LINUXKM_SPINLOCK_MAGIC)) + return -1; + #endif spin_unlock_irqrestore(&m->lock, m->irq_flags); return 0; } diff --git a/linuxkm/module_hooks.c b/linuxkm/module_hooks.c index 52d6fb0d0aa..bb64ce959cd 100644 --- a/linuxkm/module_hooks.c +++ b/linuxkm/module_hooks.c @@ -180,6 +180,10 @@ extern int wolfcrypt_benchmark_main(int argc, char** argv); int wc_lkm_LockMutex(wolfSSL_Mutex* m) { unsigned long irq_flags; +#ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + if ((m == NULL) || (m->magic != WC_LINUXKM_SPINLOCK_MAGIC)) + return BAD_FUNC_ARG; +#endif /* first, try the cheap way. */ if (spin_trylock_irqsave(&m->lock, irq_flags)) { m->irq_flags = irq_flags; From 3cdf16a316e1ae891536da0810f46fe331ce5a78 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 22 Jul 2026 11:25:40 -0500 Subject: [PATCH 04/19] linuxkm/lkcapi_sha_glue.c: refactor SHA-3 state around struct km_Sha3TfmCtx, adding km_sha3_init_tfm(), km_sha3_exit_tfm(), km_sha3_alloc_tstate(), and km_sha3_free_tstate(), implementing garbage collection of abandoned shash_desc objects at .exit_tfm. --- linuxkm/lkcapi_sha_glue.c | 173 ++++++++++++++++++++++++++++++-------- 1 file changed, 138 insertions(+), 35 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 6c88ba0fc88..e4f333bb88c 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -433,6 +433,29 @@ #undef LINUXKM_LKCAPI_REGISTER_HASH_DRBG #endif +#ifdef WOLFSSL_SHA3 +/* struct wc_Sha3 won't fit in HASH_MAX_DESCSIZE. */ +struct km_sha3_state { + union { +#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_224 + struct wc_Sha3 sha3_224_state; +#endif +#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_256 + struct wc_Sha3 sha3_256_state; +#endif +#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_384 + struct wc_Sha3 sha3_384_state; +#endif +#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_512 + struct wc_Sha3 sha3_512_state; +#endif + struct wc_Sha3 sha3_state; + }; + /* pointers for the cleanup list */ + struct list_head desc_ent; +}; +#endif /* WOLFSSL_SHA3 */ + struct km_sha_state { union { #ifdef LINUXKM_LKCAPI_REGISTER_SHA1 @@ -451,28 +474,95 @@ struct km_sha_state { struct wc_Sha512 sha2_512_state; #endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_224 - struct wc_Sha3 *sha3_224_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_256 - struct wc_Sha3 *sha3_256_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_384 - struct wc_Sha3 *sha3_384_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_512 - struct wc_Sha3 *sha3_512_state; -#endif #ifdef WOLFSSL_SHA3 - void *sha3_ptr; -#endif + struct km_sha3_state *sha3_state; +#endif /* WOLFSSL_SHA3 */ }; }; +wc_static_assert(sizeof(struct km_sha_state) <= HASH_MAX_DESCSIZE); + #ifdef WOLFSSL_SHA3 -WC_MAYBE_UNUSED static void km_sha3_free_tstate(struct km_sha_state *t_ctx) { - free(t_ctx->sha3_ptr); - t_ctx->sha3_ptr = NULL; + +#ifdef WOLFSSL_LINUXKM_USE_MUTEXES + #error LINUXKM_LKCAPI_REGISTER_SHA3 requires spinlock-based mutexes. +#endif + +/* The kernel list macros provoke "pointer of type `void *' used in arithmetic". */ +PRAGMA_DIAG_PUSH +PRAGMA("GCC diagnostic ignored \"-Wpointer-arith\""); + +struct km_Sha3TfmCtx { + wolfSSL_Mutex desc_list_lock; + struct list_head desc_list; +}; + +WC_MAYBE_UNUSED static int km_sha3_init_tfm(struct crypto_shash *tfm) +{ + struct km_Sha3TfmCtx *t_ctx = (struct km_Sha3TfmCtx *)crypto_shash_ctx(tfm); + if (wc_InitMutex(&t_ctx->desc_list_lock) != 0) + return -EINVAL; + INIT_LIST_HEAD(&t_ctx->desc_list); + return 0; +} + +WC_MAYBE_UNUSED static void km_sha3_exit_tfm(struct crypto_shash *tfm) +{ + struct km_Sha3TfmCtx *t_ctx = (struct km_Sha3TfmCtx *)crypto_shash_ctx(tfm); + struct km_sha3_state *s_ctx_i; + struct km_sha3_state *next_ent; + + /* Don't need to lock the mutex to clean up, because the API contract + * forbids any use of descs at/after exit of the associated TFM -- i.e. the + * list holds only abandoned descs -- and we're deallocating the lock + * besides. Moreover, we definitely don't want to lock, so that the + * iteration and heap operations aren't in a locked context that might make + * desc deallocation awkward or impossible (leak). + */ + list_for_each_entry_safe(s_ctx_i, next_ent, &t_ctx->desc_list, desc_ent) { + list_del(&s_ctx_i->desc_ent); + ForceZero(s_ctx_i, sizeof(*s_ctx_i)); + free(s_ctx_i); + } + (void)wc_FreeMutex(&t_ctx->desc_list_lock); +} + +WC_MAYBE_UNUSED static int km_sha3_alloc_tstate(struct shash_desc *desc) { + struct km_Sha3TfmCtx *t_ctx = + (struct km_Sha3TfmCtx *)crypto_shash_ctx(desc->tfm); + struct km_sha_state *s_ctx = (struct km_sha_state *)shash_desc_ctx(desc); + s_ctx->sha3_state = (struct km_sha3_state *)malloc(sizeof(struct km_sha3_state)); + if (! s_ctx->sha3_state) + return -ENOMEM; + + if (wc_LockMutex(&t_ctx->desc_list_lock) != 0) { + free(s_ctx->sha3_state); + s_ctx->sha3_state = NULL; + return -EINVAL; + } + list_add(&s_ctx->sha3_state->desc_ent, &t_ctx->desc_list); + (void)wc_UnLockMutex(&t_ctx->desc_list_lock); + + return 0; +} + +WC_MAYBE_UNUSED static void km_sha3_free_tstate(struct shash_desc *desc) { + struct km_Sha3TfmCtx *t_ctx = + (struct km_Sha3TfmCtx *)crypto_shash_ctx(desc->tfm); + struct km_sha_state *s_ctx = (struct km_sha_state *)shash_desc_ctx(desc); + + if (s_ctx->sha3_state == NULL) + return; + + if (wc_LockMutex(&t_ctx->desc_list_lock) != 0) + return; + list_del(&s_ctx->sha3_state->desc_ent); + (void)wc_UnLockMutex(&t_ctx->desc_list_lock); + + wc_Sha3_256_Free(&s_ctx->sha3_state->sha3_state); + ForceZero(s_ctx->sha3_state, sizeof *s_ctx->sha3_state); + free(s_ctx->sha3_state); + s_ctx->sha3_state = NULL; } WC_MAYBE_UNUSED static int sha3_test_once(void) { @@ -484,6 +574,9 @@ WC_MAYBE_UNUSED static int sha3_test_once(void) { } return ret; } + +PRAGMA_DIAG_POP + #endif #define WC_LINUXKM_SHA1_IMPLEMENT(name, digest_size, block_size, \ @@ -761,15 +854,14 @@ static int km_ ## name ## _init(struct shash_desc *desc) { \ struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ int ret; \ \ - ctx-> name ## _state = malloc(sizeof *ctx-> name ## _state); \ - if (! ctx-> name ## _state) \ - return -ENOMEM; \ - ret = init_f(ctx-> name ## _state, NULL, INVALID_DEVID); \ + ret = km_sha3_alloc_tstate(desc); \ + if (ret) \ + return ret; \ + ret = init_f(&ctx->sha3_state-> name ## _state, NULL, INVALID_DEVID); \ if (ret == 0) \ return 0; \ else { \ - free(ctx-> name ## _state); \ - ctx-> name ## _state = NULL; \ + km_sha3_free_tstate(desc); \ return -EINVAL; \ } \ } \ @@ -779,13 +871,12 @@ static int km_ ## name ## _update(struct shash_desc *desc, const u8 *data, \ { \ struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ \ - int ret = update_f(ctx-> name ## _state, data, len); \ + int ret = update_f(&ctx->sha3_state-> name ## _state, data, len); \ \ if (ret == 0) \ return 0; \ else { \ - free_f(ctx-> name ## _state); \ - km_sha3_free_tstate(ctx); \ + km_sha3_free_tstate(desc); \ return -EINVAL; \ } \ } \ @@ -793,10 +884,9 @@ static int km_ ## name ## _update(struct shash_desc *desc, const u8 *data, \ static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ \ - int ret = final_f(ctx-> name ## _state, out); \ + int ret = final_f(&ctx->sha3_state-> name ## _state, out); \ \ - free_f(ctx-> name ## _state); \ - km_sha3_free_tstate(ctx); \ + km_sha3_free_tstate(desc); \ if (ret == 0) \ return 0; \ else \ @@ -808,11 +898,10 @@ static int km_ ## name ## _finup(struct shash_desc *desc, const u8 *data, \ { \ struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ \ - int ret = update_f(ctx-> name ## _state, data, len); \ + int ret = update_f(&ctx->sha3_state-> name ## _state, data, len); \ \ if (ret != 0) { \ - free_f(ctx-> name ## _state); \ - km_sha3_free_tstate(ctx); \ + km_sha3_free_tstate(desc); \ return -EINVAL; \ } \ \ @@ -822,14 +911,26 @@ static int km_ ## name ## _finup(struct shash_desc *desc, const u8 *data, \ static int km_ ## name ## _digest(struct shash_desc *desc, const u8 *data, \ unsigned int len, u8 *out) \ { \ - int ret = km_ ## name ## _init(desc); \ + struct km_sha3_state sha3_state; \ + int ret; \ + \ + (void)desc; \ + ret = init_f(&sha3_state. name ## _state, NULL, INVALID_DEVID); \ if (ret != 0) \ - return ret; \ - return km_ ## name ## _finup(desc, data, len, out); \ + return -EINVAL; \ + ret = update_f(&sha3_state. name ## _state, data, len); \ + if (ret == 0) \ + ret = final_f(&sha3_state. name ## _state, out); \ + \ + free_f(&sha3_state. name ## _state); \ + ForceZero(&sha3_state, sizeof sha3_state); \ + \ + return ret == 0 ? 0 : -EINVAL; \ } \ \ static struct shash_alg name ## _alg = \ { \ + .init_tfm = km_sha3_init_tfm, \ .digestsize = (digest_size), \ .init = km_ ## name ## _init, \ .update = km_ ## name ## _update, \ @@ -837,11 +938,13 @@ static struct shash_alg name ## _alg = \ .finup = km_ ## name ## _finup, \ .digest = km_ ## name ## _digest, \ .descsize = sizeof(struct km_sha_state), \ + .exit_tfm = km_sha3_exit_tfm, \ .base = { \ .cra_name = (this_cra_name), \ .cra_driver_name = (this_cra_driver_name), \ .cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY, \ .cra_blocksize = (block_size), \ + .cra_ctxsize = sizeof(struct km_Sha3TfmCtx), \ .cra_module = THIS_MODULE \ } \ }; \ From 9704ae603fb4ff2b5390cabe8326be5a23987db6 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 22 Jul 2026 14:58:01 -0500 Subject: [PATCH 05/19] linuxkm/lkcapi_sha_glue.c: add struct km_sha3_export_state, and implement km_sha3_export() and per-alg _import() methods, and km_sha3_test_export_import(). --- .wolfssl_known_macro_extras | 1 + linuxkm/lkcapi_sha_glue.c | 241 +++++++++++++++++++++++++++++++++++- 2 files changed, 237 insertions(+), 5 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index cb27325f5cf..c8367a23ed8 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -292,6 +292,7 @@ HASH_DIGEST HASH_DataType_8b HASH_IMR_DCIE HASH_IMR_DINIE +HASH_MAX_STATESIZE HAVE_AESGCM_DECRYPT HAVE_BYTEREVERSE64 HAVE_CERTIFICATE_STATUS_V2 diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index e4f333bb88c..5c80978bf89 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -488,9 +488,15 @@ wc_static_assert(sizeof(struct km_sha_state) <= HASH_MAX_DESCSIZE); #error LINUXKM_LKCAPI_REGISTER_SHA3 requires spinlock-based mutexes. #endif -/* The kernel list macros provoke "pointer of type `void *' used in arithmetic". */ +/* The kernel list macros provoke "pointer of type `void *' used in arithmetic", + * and on older kernels, "nested extern declaration of + * `__compiletime_assert_foo'". + */ PRAGMA_DIAG_PUSH PRAGMA("GCC diagnostic ignored \"-Wpointer-arith\""); +PRAGMA("GCC diagnostic ignored \"-Wnested-externs\""); + +#include struct km_Sha3TfmCtx { wolfSSL_Mutex desc_list_lock; @@ -577,6 +583,190 @@ WC_MAYBE_UNUSED static int sha3_test_once(void) { PRAGMA_DIAG_POP +/* Serialized SHA-3 state for .export / .import. This is the canonical + * {core, block, len} form the kernel budgets HASH_MAX_STATESIZE for -- worst + * case sha3-224, 200 + 144 + 1. Deliberately NOT a struct copy of wc_Sha3: + * that carries the full 200-byte t[] plus heap/devId/fn-ptrs, which would both + * blow the statesize budget and ship non-portable, non-state fields across + * descs. s[] and t[] are stored in native byte order -- export/import always + * round-trips within one host, so no canonical encoding is needed. */ +struct km_sha3_export_state { + byte s[sizeof(((struct wc_Sha3 *)0)->s)]; /* KECCAK sponge, 200 bytes */ + byte t[WC_SHA3_224_BLOCK_SIZE]; /* pending block; 144 = max rate + * of the registered SHA-3 + * variants (sha3-224) */ + byte i; /* valid bytes in t[]; always + * < rate <= 144, since + * Sha3Final rejects i >= rate */ +}; + +/* HASH_MAX_STATESIZE added by 2b1a29ce33, kernel 6.16. */ +#ifdef HASH_MAX_STATESIZE +wc_static_assert(sizeof(struct km_sha3_export_state) <= HASH_MAX_STATESIZE); +#endif + +/* Non-destructive: serialize the live sponge into the caller's statesize + * buffer, leaving the desc (and its cleanup-list node) intact for continued + * streaming. Variant-agnostic -- s/t/i live at the same offset in every union + * member, so the generic .sha3_state accessor serves all four. */ +WC_MAYBE_UNUSED static int km_sha3_export(struct shash_desc *desc, void *out) +{ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc); + struct km_sha3_export_state *blob = (struct km_sha3_export_state *)out; + const struct wc_Sha3 *sha3; + + if (ctx->sha3_state == NULL) + return -EINVAL; + sha3 = &ctx->sha3_state->sha3_state; + + /* i < rate <= sizeof(blob->t) always; guard defensively so a corrupted + * live state can't overrun blob->t. */ + if (sha3->i > sizeof(blob->t)) + return -EINVAL; + + XMEMCPY(blob->s, sha3->s, sizeof(blob->s)); + XMEMSET(blob->t, 0, sizeof(blob->t)); + XMEMCPY(blob->t, sha3->t, sha3->i); + blob->i = (byte)sha3->i; + + return 0; +} + +/* Kernel-API export/import test coverage. Exercises the cross-desc path that + * distinguishes real state serialization from pointer aliasing: testmgr's + * reimport divisions are same-desc, so a default memcpy of the desc pointer + * would round-trip within one desc yet double-free across two. Here we export + * mid-stream, import into a distinct poisoned desc, finish BOTH independently, + * and require both to match a one-shot reference -- plus statesize and + * malformed-blob rejection probes. + */ +WC_MAYBE_UNUSED static int km_sha3_test_export_import( + const char *cra_name, const char *cra_driver_name, unsigned int block_size) +{ + int ret; + struct crypto_shash *tfm = NULL; + struct shash_desc *desc = NULL; + struct shash_desc *desc2 = NULL; + struct km_sha3_export_state *blob = NULL; + size_t desc_size = 0; + unsigned int split, i; + byte msg[300]; + byte ref[WC_SHA3_512_DIGEST_SIZE]; + byte tag[WC_SHA3_512_DIGEST_SIZE]; + + for (i = 0; i < (unsigned int)sizeof(msg); i++) + msg[i] = (byte)(i * 7 + 1); + + tfm = crypto_alloc_shash(cra_name, 0, 0); + if (IS_ERR(tfm)) { + ret = (int)PTR_ERR(tfm); + pr_err("error: crypto_alloc_shash(%s) failed: %d\n", cra_name, ret); + return ret; + } + + if (crypto_shash_statesize(tfm) != sizeof(struct km_sha3_export_state)) { + pr_err("error: %s statesize %u != expected %u\n", cra_driver_name, + crypto_shash_statesize(tfm), + (unsigned int)sizeof(struct km_sha3_export_state)); + ret = -EINVAL; + goto out; + } + + desc_size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm); + desc = (struct shash_desc *)malloc(desc_size); + desc2 = (struct shash_desc *)malloc(desc_size); + blob = (struct km_sha3_export_state *)malloc(sizeof(*blob)); + if ((desc == NULL) || (desc2 == NULL) || (blob == NULL)) { + ret = -ENOMEM; + goto out; + } + XMEMSET(desc, 0, desc_size); + desc->tfm = tfm; + + /* Reference digest over the whole message. */ + ret = crypto_shash_init(desc); + if (ret == 0) + ret = crypto_shash_update(desc, msg, sizeof(msg)); + if (ret == 0) + ret = crypto_shash_final(desc, ref); + if (ret) { + pr_err("error: %s reference digest failed: %d\n", cra_driver_name, ret); + goto out; + } + + /* Split leaves block_size/2 unabsorbed bytes, so the export blob carries a + * non-empty partial block for every variant (rate 72..144). + */ + split = block_size + block_size / 2; + + ret = crypto_shash_init(desc); + if (ret == 0) + ret = crypto_shash_update(desc, msg, split); + if (ret == 0) + ret = crypto_shash_export(desc, blob); + if (ret) { + pr_err("error: %s export sequence failed: %d\n", cra_driver_name, ret); + goto out; + } + + /* Import into a poisoned second desc: import must not read prior ctx. */ + XMEMSET(desc2, 0xa5, desc_size); + desc2->tfm = tfm; + ret = crypto_shash_import(desc2, blob); + if (ret == 0) + ret = crypto_shash_update(desc2, msg + split, sizeof(msg) - split); + if (ret == 0) + ret = crypto_shash_final(desc2, tag); + if (ret) { + pr_err("error: %s import sequence failed: %d\n", cra_driver_name, ret); + goto out; + } + if (XMEMCMP(tag, ref, crypto_shash_digestsize(tfm)) != 0) { + pr_err("error: %s import-continuation digest mismatch\n", + cra_driver_name); + ret = -EBADMSG; + goto out; + } + + /* The exporting desc must remain live and independent of desc2. */ + ret = crypto_shash_update(desc, msg + split, sizeof(msg) - split); + if (ret == 0) + ret = crypto_shash_final(desc, tag); + if (ret) { + pr_err("error: %s post-export continuation failed: %d\n", + cra_driver_name, ret); + goto out; + } + if (XMEMCMP(tag, ref, crypto_shash_digestsize(tfm)) != 0) { + pr_err("error: %s post-export digest mismatch\n", cra_driver_name); + ret = -EBADMSG; + goto out; + } + + /* Malformed state (partial length >= rate) must be rejected before any + * allocation or installation. + */ + blob->i = (byte)block_size; + if (crypto_shash_import(desc2, blob) == 0) { + pr_err("error: %s import accepted out-of-range partial length\n", + cra_driver_name); + ret = -EINVAL; + goto out; + } + + ret = 0; + +out: + + free(blob); + free(desc2); + free(desc); + if (tfm) + crypto_free_shash(tfm); + + return ret; +} + #endif #define WC_LINUXKM_SHA1_IMPLEMENT(name, digest_size, block_size, \ @@ -928,6 +1118,41 @@ static int km_ ## name ## _digest(struct shash_desc *desc, const u8 *data, \ return ret == 0 ? 0 : -EINVAL; \ } \ \ +static int km_ ## name ## _import(struct shash_desc *desc, \ + const void *in) \ +{ \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + const struct km_sha3_export_state *blob = \ + (const struct km_sha3_export_state *)in; \ + struct wc_Sha3 *sha3; \ + int ret; \ + \ + if (blob->i >= (block_size)) \ + return -EINVAL; \ + \ + ret = km_sha3_alloc_tstate(desc); \ + if (ret) \ + return ret; \ + \ + sha3 = &ctx->sha3_state-> name ## _state; \ + ret = init_f(sha3, NULL, INVALID_DEVID); \ + if (ret != 0) { \ + km_sha3_free_tstate(desc); \ + return -EINVAL; \ + } \ + \ + XMEMCPY(sha3->s, blob->s, sizeof(sha3->s)); \ + XMEMCPY(sha3->t, blob->t, blob->i); \ + XMEMSET(sha3->t + blob->i, 0, sizeof(sha3->t) - blob->i); \ + sha3->i = blob->i; \ + \ + return 0; \ +} \ + \ +wc_static_assert((block_size) <= \ + sizeof(((struct km_sha3_export_state *)0)->t)); \ + \ + \ static struct shash_alg name ## _alg = \ { \ .init_tfm = km_sha3_init_tfm, \ @@ -938,6 +1163,9 @@ static struct shash_alg name ## _alg = \ .finup = km_ ## name ## _finup, \ .digest = km_ ## name ## _digest, \ .descsize = sizeof(struct km_sha_state), \ + .export = km_sha3_export, \ + .import = km_ ## name ## _import, \ + .statesize = sizeof(struct km_sha3_export_state), \ .exit_tfm = km_sha3_exit_tfm, \ .base = { \ .cra_name = (this_cra_name), \ @@ -952,14 +1180,17 @@ static int name ## _alg_loaded = 0; \ \ static int linuxkm_test_ ## name(void) { \ wc_test_ret_t ret = test_routine(); \ - if (ret >= 0) \ - return check_shash_driver_masking(NULL /* tfm */, this_cra_name, \ - this_cra_driver_name); \ - else { \ + if (ret < 0) { \ wc_test_render_error_message("linuxkm_test_" #name " failed: ", \ ret); \ return WC_TEST_RET_DEC_EC(ret); \ } \ + ret = check_shash_driver_masking(NULL /* tfm */, this_cra_name, \ + this_cra_driver_name); \ + if (ret) \ + return ret; \ + return km_sha3_test_export_import(this_cra_name, this_cra_driver_name, \ + (block_size)); \ } \ \ struct wc_swallow_the_semicolon From 5a1555209c5880b4d3d9a9478a146d0b08ff81eb Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 22 Jul 2026 17:48:59 -0500 Subject: [PATCH 06/19] linuxkm/lkcapi_sha_glue.c: fix export/import for HMAC: * add to struct km_sha_hmac_pstate members desc_list_lock, desc_list, export_list, and export_list_len; * add struct km_sha_hmac_export_state; * add km_hmac_alloc_tstate(), km_hmac_export(), km_hmac_import(), km_hmac_test_export_import(); * and update km_hmac_*() and WC_LINUXKM_HMAC_IMPLEMENT(() to implement. --- linuxkm/lkcapi_sha_glue.c | 573 +++++++++++++++++++++++++++++++++++--- 1 file changed, 542 insertions(+), 31 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 5c80978bf89..560636e6345 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -1269,15 +1269,81 @@ struct wc_swallow_the_semicolon wc_Sha3_512_Free, sha3_test_once); #endif -struct km_sha_hmac_pstate { +struct km_sha_hmac_node { struct Hmac wc_hmac; + /* linkage for the tfm-owned cleanup list */ + struct list_head desc_ent; }; struct km_sha_hmac_state { - struct Hmac *wc_hmac; /* HASH_MAX_DESCSIZE is 368, but sizeof(struct Hmac) is 832 */ + /* HASH_MAX_DESCSIZE is 368, but sizeof(struct Hmac) is 832, so the working + * Hmac lives in a heap node hung off the desc and tracked on the tfm + * cleanup list for garbage collection at .exit_tfm. */ + struct km_sha_hmac_node *node; +}; +struct km_sha_hmac_pstate { + /* keyed, pristine Hmac, deep-copied into each desc's node at .init */ + struct Hmac wc_hmac; + /* desc_list_lock guards BOTH lists below. */ + wolfSSL_Mutex desc_list_lock; + /* cleanup list of live/abandoned desc working nodes (abandonment GC) */ + struct list_head desc_list; + /* bounded ring of .export snapshots; import validates handles against it */ + struct list_head export_list; + unsigned int export_list_len; +}; + +/* Serialized HMAC state for .export / .import. sizeof(struct Hmac) is 832, and + * an HMAC-over-SHA-3 state is two full sponges, so real state cannot fit + * HASH_MAX_STATESIZE (345). .export deep-copies the live Hmac into a snapshot + * node on the tfm's export_list and the blob carries only a validated handle; + * .import validates the handle against THIS tfm's export_list -- never + * dereferencing an unvalidated pointer -- and copies from the snapshot. + */ +#define WC_LINUXKM_HMAC_EXPORT_MAGIC 0x484d4143U /* 'HMAC' */ + +/* Upper bound on live .export snapshots per tfm. Bounds worst-case memory to + * this many nodes (~832B each): without it, algif_hash's export-on-accept lets + * userspace grow the parent's list without limit (close(accept(fd)) in a loop). + * The accept-clone path imports immediately after export, so a snapshot is + * consumed long before it can be evicted; this need only exceed the max + * concurrent in-flight export->import pairs on one tfm (accept drops the sock + * lock between the two). Over-cap merely degrades a stale import to graceful + * -EINVAL, never corruption. Override at build time if a workload needs more. + * + * Note the default expression is runtime-evaluated to scale with host size. + */ +#ifndef WC_LINUXKM_HMAC_EXPORT_LIST_MAX + #define WC_LINUXKM_HMAC_EXPORT_LIST_MAX (nr_cpu_ids * 2) +#endif + +struct km_sha_hmac_export_state { + word32 magic; + struct km_sha_hmac_node *snapshot; }; +wc_static_assert(sizeof(struct km_sha_hmac_state) <= HASH_MAX_DESCSIZE); + +/* HASH_MAX_STATESIZE added by 2b1a29ce33, kernel 6.16. */ +#ifdef HASH_MAX_STATESIZE +wc_static_assert(sizeof(struct km_sha_hmac_export_state) <= HASH_MAX_STATESIZE); +#endif + #ifndef NO_HMAC +#ifdef WOLFSSL_LINUXKM_USE_MUTEXES + #error LINUXKM_LKCAPI_REGISTER_HMAC requires spinlock-based mutexes. +#endif + +/* The kernel list macros provoke "pointer of type `void *' used in arithmetic", + * and on older kernels, "nested extern declaration of + * `__compiletime_assert_foo'". + */ +PRAGMA_DIAG_PUSH +PRAGMA("GCC diagnostic ignored \"-Wpointer-arith\""); +PRAGMA("GCC diagnostic ignored \"-Wnested-externs\""); + +#include + WC_MAYBE_UNUSED static int linuxkm_hmac_setkey_common(struct crypto_shash *tfm, int type, const byte* key, word32 length) { struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(tfm); @@ -1296,43 +1362,109 @@ WC_MAYBE_UNUSED static int linuxkm_hmac_setkey_common(struct crypto_shash *tfm, return -EINVAL; } -WC_MAYBE_UNUSED static void km_hmac_free_tstate(struct km_sha_hmac_state *t_ctx) { - wc_HmacFree(t_ctx->wc_hmac); - free(t_ctx->wc_hmac); - t_ctx->wc_hmac = NULL; +WC_MAYBE_UNUSED static int km_hmac_alloc_tstate(struct shash_desc *desc) { + struct km_sha_hmac_pstate *p_ctx = + (struct km_sha_hmac_pstate *)crypto_shash_ctx(desc->tfm); + struct km_sha_hmac_state *s_ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); + s_ctx->node = (struct km_sha_hmac_node *)malloc(sizeof(struct km_sha_hmac_node)); + if (! s_ctx->node) + return -ENOMEM; + + if (wc_LockMutex(&p_ctx->desc_list_lock) != 0) { + free(s_ctx->node); + s_ctx->node = NULL; + return -EINVAL; + } + list_add(&s_ctx->node->desc_ent, &p_ctx->desc_list); + (void)wc_UnLockMutex(&p_ctx->desc_list_lock); + + return 0; +} + +WC_MAYBE_UNUSED static void km_hmac_free_tstate(struct shash_desc *desc) { + struct km_sha_hmac_pstate *p_ctx = + (struct km_sha_hmac_pstate *)crypto_shash_ctx(desc->tfm); + struct km_sha_hmac_state *s_ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); + + if (s_ctx->node == NULL) + return; + + if (wc_LockMutex(&p_ctx->desc_list_lock) != 0) + return; + list_del(&s_ctx->node->desc_ent); + (void)wc_UnLockMutex(&p_ctx->desc_list_lock); + + /* wc_HmacFree is NOT a no-op: a wc_HmacCopy'd node can own inner/outer hash + * heap (e.g. SMALL_STACK_CACHE W buffers), so it must run before free(). */ + wc_HmacFree(&s_ctx->node->wc_hmac); +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) + ForceZero(s_ctx->node, sizeof *s_ctx->node); +#endif + free(s_ctx->node); + s_ctx->node = NULL; } WC_MAYBE_UNUSED static int km_hmac_init_tfm(struct crypto_shash *tfm) { struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(tfm); int ret = wc_HmacInit(&p_ctx->wc_hmac, NULL /* heap */, INVALID_DEVID); - if (ret == 0) - return 0; - else + if (ret != 0) return -EINVAL; + if (wc_InitMutex(&p_ctx->desc_list_lock) != 0) { + wc_HmacFree(&p_ctx->wc_hmac); + return -EINVAL; + } + INIT_LIST_HEAD(&p_ctx->desc_list); + INIT_LIST_HEAD(&p_ctx->export_list); + p_ctx->export_list_len = 0; + return 0; } WC_MAYBE_UNUSED static void km_hmac_exit_tfm(struct crypto_shash *tfm) { struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(tfm); + struct km_sha_hmac_node *node_i; + struct km_sha_hmac_node *next_ent; + + /* Don't need to lock the mutex to clean up, because the API contract + * forbids any use of descs at/after exit of the associated TFM -- i.e. the + * list holds only abandoned descs -- and we're deallocating the lock + * besides. Moreover, we definitely don't want to lock, so that the + * iteration and heap operations aren't in a locked context that might make + * desc deallocation awkward or impossible (leak). + */ + list_for_each_entry_safe(node_i, next_ent, &p_ctx->desc_list, desc_ent) { + list_del(&node_i->desc_ent); + wc_HmacFree(&node_i->wc_hmac); +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) + ForceZero(node_i, sizeof(*node_i)); +#endif + free(node_i); + } + list_for_each_entry_safe(node_i, next_ent, &p_ctx->export_list, desc_ent) { + list_del(&node_i->desc_ent); + wc_HmacFree(&node_i->wc_hmac); +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) + ForceZero(node_i, sizeof(*node_i)); +#endif + free(node_i); + } wc_HmacFree(&p_ctx->wc_hmac); - return; + (void)wc_FreeMutex(&p_ctx->desc_list_lock); } WC_MAYBE_UNUSED static int km_hmac_init(struct shash_desc *desc) { int ret; - struct km_sha_hmac_state *t_ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); + struct km_sha_hmac_state *s_ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(desc->tfm); - t_ctx->wc_hmac = malloc(sizeof *t_ctx->wc_hmac); - if (! t_ctx->wc_hmac) - return -ENOMEM; + ret = km_hmac_alloc_tstate(desc); + if (ret) + return ret; - ret = wc_HmacCopy(&p_ctx->wc_hmac, t_ctx->wc_hmac); + ret = wc_HmacCopy(&p_ctx->wc_hmac, &s_ctx->node->wc_hmac); if (ret != 0) { - ForceZero(t_ctx->wc_hmac, sizeof *t_ctx->wc_hmac); - free(t_ctx->wc_hmac); - t_ctx->wc_hmac = NULL; + km_hmac_free_tstate(desc); return -EINVAL; } @@ -1344,12 +1476,12 @@ WC_MAYBE_UNUSED static int km_hmac_update(struct shash_desc *desc, const u8 *dat { struct km_sha_hmac_state *ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); - int ret = wc_HmacUpdate(ctx->wc_hmac, data, len); + int ret = wc_HmacUpdate(&ctx->node->wc_hmac, data, len); if (ret == 0) return 0; else { - km_hmac_free_tstate(ctx); + km_hmac_free_tstate(desc); return -EINVAL; } } @@ -1357,9 +1489,9 @@ WC_MAYBE_UNUSED static int km_hmac_update(struct shash_desc *desc, const u8 *dat WC_MAYBE_UNUSED static int km_hmac_final(struct shash_desc *desc, u8 *out) { struct km_sha_hmac_state *ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); - int ret = wc_HmacFinal(ctx->wc_hmac, out); + int ret = wc_HmacFinal(&ctx->node->wc_hmac, out); - km_hmac_free_tstate(ctx); + km_hmac_free_tstate(desc); if (ret == 0) return 0; @@ -1372,10 +1504,10 @@ WC_MAYBE_UNUSED static int km_hmac_finup(struct shash_desc *desc, const u8 *data { struct km_sha_hmac_state *ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); - int ret = wc_HmacUpdate(ctx->wc_hmac, data, len); + int ret = wc_HmacUpdate(&ctx->node->wc_hmac, data, len); if (ret != 0) { - km_hmac_free_tstate(ctx); + km_hmac_free_tstate(desc); return -EINVAL; } @@ -1385,12 +1517,386 @@ WC_MAYBE_UNUSED static int km_hmac_finup(struct shash_desc *desc, const u8 *data WC_MAYBE_UNUSED static int km_hmac_digest(struct shash_desc *desc, const u8 *data, unsigned int len, u8 *out) { - int ret = km_hmac_init(desc); - if (ret != 0) + /* One-shot: no abandonment or export window, so skip the cleanup list. + * sizeof(struct Hmac) is 832 -- too large for the stack (cf. the SHA-3 + * digest's stack state), so use a bare heap Hmac that is always freed + * here rather than a listed node. + */ + struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(desc->tfm); + struct Hmac *h; + int ret; + + h = (struct Hmac *)malloc(sizeof *h); + if (! h) + return -ENOMEM; + + ret = wc_HmacCopy(&p_ctx->wc_hmac, h); + if (ret != 0) { + free(h); + return -EINVAL; + } + ret = wc_HmacUpdate(h, data, len); + if (ret == 0) + ret = wc_HmacFinal(h, out); + + wc_HmacFree(h); +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) + ForceZero(h, sizeof(*h)); +#endif + free(h); + + return ret == 0 ? 0 : -EINVAL; +} + +/* Note that km_hmac_export() is implementing a pseudo-export -- the "out" + * buffer only gets a pointer to the actual deep-copied HMAC state, not a bona + * fide serialization of it, because HASH_MAX_STATESIZE is simply too small to + * accommodate the full state. + */ +WC_MAYBE_UNUSED static int km_hmac_export(struct shash_desc *desc, void *out) +{ + struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(desc->tfm); + struct km_sha_hmac_state *s_ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); + struct km_sha_hmac_export_state *blob = (struct km_sha_hmac_export_state *)out; + struct km_sha_hmac_node *snapshot; + struct km_sha_hmac_node *evicted = NULL; + int ret; + + if (s_ctx->node == NULL) + return -EINVAL; + + /* Snapshot the live state into a fresh node. Allocate and deep-copy + * OUTSIDE the lock -- wc_HmacCopy may allocate inner-hash heap. Copying + * from this desc's own working node needs no lock (a desc is not used + * concurrently); the lock protects the lists, not the nodes. */ + snapshot = (struct km_sha_hmac_node *)malloc(sizeof(struct km_sha_hmac_node)); + if (! snapshot) + return -ENOMEM; + ret = wc_HmacCopy(&s_ctx->node->wc_hmac, &snapshot->wc_hmac); + if (ret != 0) { + ForceZero(snapshot, sizeof(*snapshot)); + free(snapshot); + return -EINVAL; + } + + if (wc_LockMutex(&p_ctx->desc_list_lock) != 0) { + wc_HmacFree(&snapshot->wc_hmac); +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) + ForceZero(snapshot, sizeof(*snapshot)); +#endif + free(snapshot); + return -EINVAL; + } + /* Bound the ring: at capacity, unlink the oldest (list tail) under the lock; + * it is freed below, outside the lock. Unlinking under the lock is what + * lets .import validate-and-copy under the same lock without racing a free. + */ + if (p_ctx->export_list_len >= WC_LINUXKM_HMAC_EXPORT_LIST_MAX) { + evicted = list_last_entry(&p_ctx->export_list, + struct km_sha_hmac_node, desc_ent); + list_del(&evicted->desc_ent); + p_ctx->export_list_len--; + } + /* list_add() prepends, so the tail from list_last_entry() is the oldest. */ + list_add(&snapshot->desc_ent, &p_ctx->export_list); + p_ctx->export_list_len++; + (void)wc_UnLockMutex(&p_ctx->desc_list_lock); + + /* The evicted node is now unlinked and unreachable (any outstanding handle + * to it will fail import validation), so free it outside the lock. + */ + if (evicted != NULL) { + wc_HmacFree(&evicted->wc_hmac); +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) + ForceZero(evicted, sizeof(*evicted)); +#endif + free(evicted); + } + + /* Zero first so no uninitialized padding leaks into the caller's buffer. */ + XMEMSET(blob, 0, sizeof(*blob)); + blob->magic = WC_LINUXKM_HMAC_EXPORT_MAGIC; + blob->snapshot = snapshot; + + return 0; +} + +WC_MAYBE_UNUSED static int km_hmac_import(struct shash_desc *desc, const void *in) +{ + struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(desc->tfm); + struct km_sha_hmac_state *s_ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); + const struct km_sha_hmac_export_state *blob = (const struct km_sha_hmac_export_state *)in; + struct km_sha_hmac_node *snapshot; + struct km_sha_hmac_node *node_i; + struct km_sha_hmac_node *newnode; + int found = 0; + int ret; + + if (blob->magic != WC_LINUXKM_HMAC_EXPORT_MAGIC) + return -EINVAL; + snapshot = blob->snapshot; + if (snapshot == NULL) + return -EINVAL; + + /* Fresh working node, allocated outside the lock; its inner Hmac heap is + * populated by the copy under the lock below. + */ + newnode = (struct km_sha_hmac_node *)malloc(sizeof(struct km_sha_hmac_node)); + if (! newnode) + return -ENOMEM; + + /* Validate the handle AND copy from the snapshot under ONE lock hold, so a + * concurrent export's eviction cannot free the snapshot between the match + * and the copy. A handle from another tfm, an evicted snapshot, or a + * forged/poisoned blob is not a live member -> graceful -EINVAL, with no + * dereference of attacker-influenced memory. + */ + if (wc_LockMutex(&p_ctx->desc_list_lock) != 0) { + ForceZero(newnode, sizeof(*newnode)); + free(newnode); + return -EINVAL; + } + list_for_each_entry(node_i, &p_ctx->export_list, desc_ent) { + if (node_i == snapshot) { + found = 1; + break; + } + } + if (! found) { + (void)wc_UnLockMutex(&p_ctx->desc_list_lock); + ForceZero(newnode, sizeof(*newnode)); /* raw node; nothing to wc_HmacFree */ + free(newnode); + return -EINVAL; + } + ret = wc_HmacCopy(&snapshot->wc_hmac, &newnode->wc_hmac); + if (ret != 0) { + (void)wc_UnLockMutex(&p_ctx->desc_list_lock); + /* Copy-failure cleanup mirrors km_hmac_init (wc_HmacFree then free) -- + * keep the two consistent if wc_HmacCopy's failure contract is revised. + */ + wc_HmacFree(&newnode->wc_hmac); +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) + ForceZero(newnode, sizeof(*newnode)); +#endif + free(newnode); + return -EINVAL; + } + /* Publish: link the working node onto desc_list and into the desc ctx, + * overwriting any poisoned prior pointer without reading it. A real prior + * node orphans onto desc_list and is reaped at exit_tfm. + */ + list_add(&newnode->desc_ent, &p_ctx->desc_list); + s_ctx->node = newnode; + (void)wc_UnLockMutex(&p_ctx->desc_list_lock); + + return 0; +} + +/* Kernel-API export/import test coverage: cross-desc round-trip through a + * poisoned desc; the two rejection cases the design relies on (corrupted + * handle, and a valid handle presented to a different tfm); and eviction of an + * aged-out handle once WC_LINUXKM_HMAC_EXPORT_LIST_MAX exports have intervened. + */ +WC_MAYBE_UNUSED static int km_hmac_test_export_import( + const char *cra_name, const char *cra_driver_name) +{ + int ret; + struct crypto_shash *tfm = NULL; + struct crypto_shash *tfm2 = NULL; + struct shash_desc *desc = NULL; + struct shash_desc *desc2 = NULL; + struct km_sha_hmac_export_state *blob = NULL; + struct km_sha_hmac_export_state old_blob; + size_t desc_size = 0; + unsigned int split, i, dsz; + byte key[32]; + byte msg[300]; + byte ref[WC_SHA3_512_DIGEST_SIZE]; + byte tag[WC_SHA3_512_DIGEST_SIZE]; + + for (i = 0; i < (unsigned int)sizeof(key); i++) + key[i] = (byte)(i + 1); + for (i = 0; i < (unsigned int)sizeof(msg); i++) + msg[i] = (byte)(i * 7 + 1); + + tfm = crypto_alloc_shash(cra_name, 0, 0); + if (IS_ERR(tfm)) { + ret = (int)PTR_ERR(tfm); + pr_err("error: crypto_alloc_shash(%s) failed: %d\n", cra_name, ret); return ret; - return km_hmac_finup(desc, data, len, out); + } + + ret = crypto_shash_setkey(tfm, key, sizeof(key)); + if (ret) { + pr_err("error: %s setkey failed: %d\n", cra_driver_name, ret); + goto out; + } + + if (crypto_shash_statesize(tfm) != sizeof(struct km_sha_hmac_export_state)) { + pr_err("error: %s statesize %u != expected %u\n", cra_driver_name, + crypto_shash_statesize(tfm), + (unsigned int)sizeof(struct km_sha_hmac_export_state)); + ret = -EINVAL; + goto out; + } + + dsz = crypto_shash_digestsize(tfm); + desc_size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm); + desc = (struct shash_desc *)malloc(desc_size); + desc2 = (struct shash_desc *)malloc(desc_size); + blob = (struct km_sha_hmac_export_state *)malloc(sizeof(*blob)); + if ((desc == NULL) || (desc2 == NULL) || (blob == NULL)) { + ret = -ENOMEM; + goto out; + } + XMEMSET(desc, 0, desc_size); + desc->tfm = tfm; + + /* Reference digest over the whole message. */ + ret = crypto_shash_init(desc); + if (ret == 0) + ret = crypto_shash_update(desc, msg, sizeof(msg)); + if (ret == 0) + ret = crypto_shash_final(desc, ref); + if (ret) { + pr_err("error: %s reference digest failed: %d\n", cra_driver_name, ret); + goto out; + } + + /* Export mid-stream, import into a poisoned desc, finish BOTH, require both + * to match the reference. + */ + split = 150; + ret = crypto_shash_init(desc); + if (ret == 0) + ret = crypto_shash_update(desc, msg, split); + if (ret == 0) + ret = crypto_shash_export(desc, blob); + if (ret) { + pr_err("error: %s export sequence failed: %d\n", cra_driver_name, ret); + goto out; + } + + XMEMSET(desc2, 0xa5, desc_size); + desc2->tfm = tfm; + ret = crypto_shash_import(desc2, blob); + if (ret == 0) + ret = crypto_shash_update(desc2, msg + split, sizeof(msg) - split); + if (ret == 0) + ret = crypto_shash_final(desc2, tag); + if (ret) { + pr_err("error: %s import sequence failed: %d\n", cra_driver_name, ret); + goto out; + } + if (XMEMCMP(tag, ref, dsz) != 0) { + pr_err("error: %s import-continuation digest mismatch\n", cra_driver_name); + ret = -EBADMSG; + goto out; + } + + /* Exporting desc stays live and independent. */ + ret = crypto_shash_update(desc, msg + split, sizeof(msg) - split); + if (ret == 0) + ret = crypto_shash_final(desc, tag); + if (ret) { + pr_err("error: %s post-export continuation failed: %d\n", cra_driver_name, ret); + goto out; + } + if (XMEMCMP(tag, ref, dsz) != 0) { + pr_err("error: %s post-export digest mismatch\n", cra_driver_name); + ret = -EBADMSG; + goto out; + } + + /* Corrupted handle (bad magic) must be rejected. */ + ret = crypto_shash_init(desc); + if (ret == 0) + ret = crypto_shash_update(desc, msg, split); + if (ret == 0) + ret = crypto_shash_export(desc, blob); + if (ret) { + pr_err("error: %s re-export failed: %d\n", cra_driver_name, ret); + goto out; + } + old_blob = *blob; + blob->magic ^= 0xffffffffU; + XMEMSET(desc2, 0xa5, desc_size); + desc2->tfm = tfm; + if (crypto_shash_import(desc2, blob) == 0) { + pr_err("error: %s import accepted a corrupted handle magic\n", cra_driver_name); + ret = -EINVAL; + goto out; + } + + /* Valid handle, wrong tfm: snapshot is on tfm's list, not tfm2's. */ + tfm2 = crypto_alloc_shash(cra_name, 0, 0); + if (IS_ERR(tfm2)) { + ret = (int)PTR_ERR(tfm2); + tfm2 = NULL; + pr_err("error: %s second crypto_alloc_shash failed: %d\n", cra_driver_name, ret); + goto out; + } + ret = crypto_shash_setkey(tfm2, key, sizeof(key)); + if (ret) { + pr_err("error: %s tfm2 setkey failed: %d\n", cra_driver_name, ret); + goto out; + } + XMEMSET(desc2, 0xa5, desc_size); + desc2->tfm = tfm2; + if (crypto_shash_import(desc2, &old_blob) == 0) { + pr_err("error: %s cross-tfm import was accepted\n", cra_driver_name); + ret = -EINVAL; + goto out; + } + + /* Eviction: after WC_LINUXKM_HMAC_EXPORT_LIST_MAX further exports, the aged + * handle (old_blob) is evicted and no longer importable, while the newest + * remains valid. + */ + for (i = 0; i < (unsigned int)WC_LINUXKM_HMAC_EXPORT_LIST_MAX; i++) { + ret = crypto_shash_export(desc, blob); + if (ret) { + pr_err("error: %s eviction-fill export failed: %d\n", cra_driver_name, ret); + goto out; + } + } + XMEMSET(desc2, 0xa5, desc_size); + desc2->tfm = tfm; + if (crypto_shash_import(desc2, &old_blob) == 0) { + pr_err("error: %s evicted handle still importable\n", cra_driver_name); + ret = -EINVAL; + goto out; + } + XMEMSET(desc2, 0xa5, desc_size); + desc2->tfm = tfm; + ret = crypto_shash_import(desc2, blob); + if (ret == 0) + ret = crypto_shash_final(desc2, tag); + if (ret) { + pr_err("error: %s newest handle not importable: %d\n", cra_driver_name, ret); + goto out; + } + + /* Finish the still-open exporting desc to free its working node. */ + (void)crypto_shash_final(desc, tag); + + ret = 0; + +out: + + free(blob); + free(desc2); + free(desc); + if (tfm2) + crypto_free_shash(tfm2); + if (tfm) + crypto_free_shash(tfm); + + return ret; } +PRAGMA_DIAG_POP + WC_MAYBE_UNUSED static int hmac_sha3_test_once(void) { static int once = 0; static int ret; @@ -1419,6 +1925,9 @@ static struct shash_alg name ## _alg = \ .final = km_hmac_final, \ .finup = km_hmac_finup, \ .digest = km_hmac_digest, \ + .export = km_hmac_export, \ + .import = km_hmac_import, \ + .statesize = sizeof(struct km_sha_hmac_export_state), \ .setkey = km_ ## name ## _setkey, \ .init_tfm = km_hmac_init_tfm, \ .exit_tfm = km_hmac_exit_tfm, \ @@ -1436,14 +1945,16 @@ static int name ## _alg_loaded = 0; \ \ static int linuxkm_test_ ## name(void) { \ wc_test_ret_t ret = test_routine(); \ - if (ret >= 0) \ - return check_shash_driver_masking(NULL /* tfm */, this_cra_name, \ - this_cra_driver_name); \ - else { \ + if (ret < 0) { \ wc_test_render_error_message("linuxkm_test_" #name " failed: ", \ ret); \ return WC_TEST_RET_DEC_EC(ret); \ } \ + ret = check_shash_driver_masking(NULL /* tfm */, this_cra_name, \ + this_cra_driver_name); \ + if (ret) \ + return ret; \ + return km_hmac_test_export_import(this_cra_name, this_cra_driver_name);\ } \ \ struct wc_swallow_the_semicolon From 6879f222b73e8b313f73464d88b5aabdd07b12e2 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 22 Jul 2026 22:32:12 -0500 Subject: [PATCH 07/19] test/: * fixes for NO_DH; * fixes in test_wc_ed448_import_public() and test_wc_Ed448DecisionCoverage() for FIPS v6; * fixes in tests/api/test_sha3.c for KMAC keysize in FIPS builds. --- tests/api.c | 4 ++++ tests/api/test_dtls.c | 8 ++++++++ tests/api/test_ed448.c | 4 ++++ tests/api/test_evp_pkey.c | 5 +++-- tests/api/test_sha3.c | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/api.c b/tests/api.c index e720a06ce39..25d59274fd1 100644 --- a/tests/api.c +++ b/tests/api.c @@ -30072,8 +30072,10 @@ static int test_wolfSSL_crypto_policy_ciphers(void) found = crypto_policy_cipher_found(ssl, "AES_128", 0); ExpectIntEQ(found, !is_future); +#ifndef NO_DH found = crypto_policy_cipher_found(ssl, "TLS_DHE_RSA_WITH_AES", 1); ExpectIntEQ(found, !is_future); +#endif found = crypto_policy_cipher_found(ssl, "_SHA", -1); ExpectIntEQ(found, !is_future); @@ -30081,8 +30083,10 @@ static int test_wolfSSL_crypto_policy_ciphers(void) found = crypto_policy_cipher_found(ssl, "AES128", 0); ExpectIntEQ(found, !is_future); +#ifndef NO_DH found = crypto_policy_cipher_found(ssl, "DHE-RSA-AES", 1); ExpectIntEQ(found, !is_future); +#endif found = crypto_policy_cipher_found(ssl, "-SHA", -1); ExpectIntEQ(found, !is_future); diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index 502c70e0dc7..0f22fc606ed 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -63,21 +63,28 @@ int test_dtls12_basic_connection_id(void) #endif #endif /* WOLFSSL_AES_128 && WOLFSSL_STATIC_RSA */ #if defined(WOLFSSL_AES_128) +#ifndef NO_DH "DHE-RSA-AES128-SHA256", +#endif "ECDHE-RSA-AES128-SHA256", #ifdef HAVE_AESGCM +#ifndef NO_DH "DHE-RSA-AES128-GCM-SHA256", +#endif "ECDHE-RSA-AES128-GCM-SHA256", #endif #endif /* WOLFSSL_AES_128 */ #endif /* NO_SHA256 */ #endif /* NO_RSA */ #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && !defined(HAVE_FIPS) +#ifndef NO_DH "DHE-RSA-CHACHA20-POLY1305", "DHE-RSA-CHACHA20-POLY1305-OLD", +#endif "ECDHE-RSA-CHACHA20-POLY1305", "ECDHE-RSA-CHACHA20-POLY1305-OLD", #endif +#ifndef NO_DH #ifndef NO_PSK "DHE-PSK-AES128-CBC-SHA256", #ifdef WOLFSSL_AES_256 @@ -88,6 +95,7 @@ int test_dtls12_basic_connection_id(void) #endif "DHE-PSK-AES128-CCM", #endif +#endif /* !NO_DH */ }; size_t i; struct { diff --git a/tests/api/test_ed448.c b/tests/api/test_ed448.c index 2fe404b9c9a..f468b0c0c48 100644 --- a/tests/api/test_ed448.c +++ b/tests/api/test_ed448.c @@ -253,6 +253,7 @@ int test_wc_ed448_import_public(void) ExpectIntEQ(wc_ed448_import_public(in, inlen - 1, &pubKey), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) /* MC/DC: wc_ed448_import_public_ex()'s tri-state length check * (inLen != PUB_KEY_SIZE && inLen != PUB_KEY_SIZE+1 && * inLen != 2*PUB_KEY_SIZE+1) -- close the third operand's FALSE side @@ -297,6 +298,7 @@ int test_wc_ed448_import_public(void) ExpectIntEQ(wc_ed448_import_public_ex(uncompressed, ED448_PUB_KEY_SIZE, &pubKey, 1), 0); } +#endif DoExpectIntEQ(wc_FreeRng(&rng), 0); wc_ed448_free(&pubKey); @@ -1089,6 +1091,7 @@ int test_wc_Ed448DecisionCoverage(void) &key, NULL, 0), 0); ExpectIntEQ(verify, 1); +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) /* type == Ed448ph && inLen != ED448_PREHASH_SIZE -> BAD_LENGTH_E * (sign_hash forwards hashLen as inLen with type Ed448ph). */ sigLen = sizeof(sig); @@ -1103,6 +1106,7 @@ int test_wc_Ed448DecisionCoverage(void) * TRUE, msgLen != PREHASH_SIZE FALSE) for the length operand. */ ExpectIntEQ(wc_ed448ph_verify_hash(sig, sigLen, badhash, sizeof(badhash), &verify, &key, NULL, 0), WC_NO_ERR_TRACE(BAD_LENGTH_E)); +#endif DoExpectIntEQ(wc_FreeRng(&rng), 0); wc_ed448_free(&key); diff --git a/tests/api/test_evp_pkey.c b/tests/api/test_evp_pkey.c index fa1cb1dbaba..6fbfb281c2a 100644 --- a/tests/api/test_evp_pkey.c +++ b/tests/api/test_evp_pkey.c @@ -2498,7 +2498,8 @@ int test_wolfSSL_EVP_PKEY_print_public(void) /* * test DH public key print */ -#if defined(WOLFSSL_DH_EXTRA) && defined(USE_CERT_BUFFERS_2048) +#if !defined(NO_DH) && defined(WOLFSSL_DH_EXTRA) && \ + defined(USE_CERT_BUFFERS_2048) ExpectNotNull(rbio = BIO_new_mem_buf( dh_pub_key_der_2048, sizeof_dh_pub_key_der_2048)); @@ -2555,7 +2556,7 @@ int test_wolfSSL_EVP_PKEY_print_public(void) rbio = NULL; wbio = NULL; -#endif /* WOLFSSL_DH_EXTRA && USE_CERT_BUFFERS_2048 */ +#endif /* !NO_DH && WOLFSSL_DH_EXTRA && USE_CERT_BUFFERS_2048 */ /* to prevent "unused variable" warning */ (void)pkey; diff --git a/tests/api/test_sha3.c b/tests/api/test_sha3.c index 668b69562f4..8f500c8cfc5 100644 --- a/tests/api/test_sha3.c +++ b/tests/api/test_sha3.c @@ -1869,7 +1869,16 @@ int test_wc_Kmac128_api(void) EXPECT_DECLS; #ifdef WOLFSSL_KMAC128 wc_Kmac kmac; +#ifdef HAVE_FIPS + static const byte key[KMAC_FIPS_MIN_KEY] = + { 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01 + }; +#else static const byte key[4] = { 0x00, 0x01, 0x02, 0x03 }; +#endif static const byte msg[4] = { 0x00, 0x01, 0x02, 0x03 }; byte out[200]; byte out2[200]; @@ -1881,9 +1890,16 @@ int test_wc_Kmac128_api(void) INVALID_DEVID), BAD_FUNC_ARG); ExpectIntEQ(wc_InitKmac128(&kmac, key, (word32)sizeof(key), NULL, 4, HEAP_HINT, INVALID_DEVID), BAD_FUNC_ARG); + +#ifdef HAVE_FIPS + ExpectIntEQ(wc_InitKmac128(&kmac, NULL, 0, NULL, 0, HEAP_HINT, + INVALID_DEVID), KMAC_MIN_KEYLEN_E); +#else /* NULL key with zero length is allowed. */ ExpectIntEQ(wc_InitKmac128(&kmac, NULL, 0, NULL, 0, HEAP_HINT, INVALID_DEVID), 0); +#endif + wc_Kmac128_Free(&kmac); /* wc_Kmac128_Update argument checks. */ @@ -2149,7 +2165,16 @@ int test_wc_Kmac256_api(void) EXPECT_DECLS; #ifdef WOLFSSL_KMAC256 wc_Kmac kmac; +#ifdef HAVE_FIPS + static const byte key[KMAC_FIPS_MIN_KEY] = + { 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01 + }; +#else static const byte key[4] = { 0x00, 0x01, 0x02, 0x03 }; +#endif static const byte msg[4] = { 0x00, 0x01, 0x02, 0x03 }; byte out[200]; byte out2[200]; @@ -2161,8 +2186,10 @@ int test_wc_Kmac256_api(void) INVALID_DEVID), BAD_FUNC_ARG); ExpectIntEQ(wc_InitKmac256(&kmac, key, (word32)sizeof(key), NULL, 4, HEAP_HINT, INVALID_DEVID), BAD_FUNC_ARG); +#ifndef HAVE_FIPS ExpectIntEQ(wc_InitKmac256(&kmac, NULL, 0, NULL, 0, HEAP_HINT, INVALID_DEVID), 0); +#endif wc_Kmac256_Free(&kmac); /* wc_Kmac256_Update argument checks. */ @@ -2478,7 +2505,16 @@ int test_wc_Kmac_Copy(void) #ifdef WOLFSSL_KMAC128 wc_Kmac kmac; wc_Kmac copy; +#ifdef HAVE_FIPS + static const byte key[KMAC_FIPS_MIN_KEY] = + { 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01 + }; +#else static const byte key[4] = { 0x00, 0x01, 0x02, 0x03 }; +#endif static const byte msg[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; From 8ada9961424ec5c7ad2076fd2ef17a9934bad8cd Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 08:29:17 -0500 Subject: [PATCH 08/19] linuxkm/: add defined(CONFIG_CRYPTO_FIPS) condition to a couple macro conjunctions to fix constant-folding-provoked compiler warnings around fips_enabled; for clarity, rename internal macros WC_LINUXKM_HAVE_SELFTEST and WC_LINUXKM_HAVE_SELFTEST_FULL to WC_LINUX_CONFIG_SELFTESTS and WC_LINUX_CONFIG_SELFTESTS_FULL respectively. --- linuxkm/linuxkm_wc_port.h | 18 +++++++++--------- linuxkm/lkcapi_ecdh_glue.c | 4 ++-- linuxkm/lkcapi_ecdsa_glue.c | 10 +++++----- linuxkm/lkcapi_glue.c | 23 ++++++++++++----------- linuxkm/lkcapi_sha_glue.c | 4 +++- 5 files changed, 31 insertions(+), 28 deletions(-) diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 3c4360893f3..43412887da9 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -37,23 +37,23 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 16, 0) #if defined(CONFIG_CRYPTO_MANAGER) && !defined(CONFIG_CRYPTO_MANAGER_DISABLE_TESTS) - #define WC_LINUXKM_HAVE_SELFTEST + #define WC_LINUX_CONFIG_SELFTESTS #endif - #if defined(WC_LINUXKM_HAVE_SELFTEST) && defined(CONFIG_CRYPTO_MANAGER_EXTRA_TESTS) - #define WC_LINUXKM_HAVE_SELFTEST_FULL + #if defined(WC_LINUX_CONFIG_SELFTESTS) && defined(CONFIG_CRYPTO_MANAGER_EXTRA_TESTS) + #define WC_LINUX_CONFIG_SELFTESTS_FULL #endif #else /* see Linux 698de822780f */ #if defined(CONFIG_CRYPTO_MANAGER) && defined(CONFIG_CRYPTO_SELFTESTS) - #define WC_LINUXKM_HAVE_SELFTEST + #define WC_LINUX_CONFIG_SELFTESTS #endif /* see Linux ac90aad0e9 */ - #if defined(WC_LINUXKM_HAVE_SELFTEST) && defined(CONFIG_CRYPTO_SELFTESTS_FULL) - #define WC_LINUXKM_HAVE_SELFTEST_FULL + #if defined(WC_LINUX_CONFIG_SELFTESTS) && defined(CONFIG_CRYPTO_SELFTESTS_FULL) + #define WC_LINUX_CONFIG_SELFTESTS_FULL #endif #endif - #if defined(HAVE_FIPS) && defined(LINUXKM_LKCAPI_REGISTER_AESXTS) && defined(WC_LINUXKM_HAVE_SELFTEST_FULL) + #if defined(HAVE_FIPS) && defined(LINUXKM_LKCAPI_REGISTER_AESXTS) && defined(WC_LINUX_CONFIG_SELFTESTS_FULL) /* CONFIG_CRYPTO_MANAGER_EXTRA_TESTS expects AES-XTS-384 to work, even when CONFIG_CRYPTO_FIPS, but FIPS 140-3 only allows AES-XTS-256 and AES-XTS-512. */ #error CONFIG_CRYPTO_MANAGER_EXTRA_TESTS is incompatible with FIPS wolfCrypt AES-XTS -- please reconfigure the target kernel to disable CONFIG_CRYPTO_MANAGER_EXTRA_TESTS/CONFIG_CRYPTO_SELFTESTS_FULL. #endif @@ -64,7 +64,7 @@ * disabled), and kernel module signatures frequently use SHA-1 until quite * recently. */ - #if (defined(WC_LINUXKM_HAVE_SELFTEST) || defined(CONFIG_MODULE_SIG_SHA1)) && \ + #if (defined(WC_LINUX_CONFIG_SELFTESTS) || defined(CONFIG_MODULE_SIG_SHA1)) && \ (((defined(WC_MIN_DIGEST_SIZE_FOR_VERIFY) && (WC_MIN_DIGEST_SIZE_FOR_VERIFY > 20)) || \ (defined(NO_SHA) && !defined(WC_MIN_DIGEST_SIZE_FOR_VERIFY)))) && \ defined(HAVE_ECC) && \ @@ -445,7 +445,7 @@ #include /* Kernel non-FIPS self-test ("testmgr") has a KAT with all-zeros keys. */ - #if defined(WC_LINUXKM_HAVE_SELFTEST) && !defined(HAVE_FIPS) + #if defined(WC_LINUX_CONFIG_SELFTESTS) && !defined(HAVE_FIPS) #define WC_AES_XTS_ALLOW_DUPLICATE_KEYS #endif diff --git a/linuxkm/lkcapi_ecdh_glue.c b/linuxkm/lkcapi_ecdh_glue.c index 9cd05d99e1a..1445bb17764 100644 --- a/linuxkm/lkcapi_ecdh_glue.c +++ b/linuxkm/lkcapi_ecdh_glue.c @@ -908,8 +908,8 @@ static int linuxkm_test_ecdh_nist_driver(const char * driver, */ tfm = crypto_alloc_kpp(driver, 0, 0); if (IS_ERR(tfm)) { - #if defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_MANAGER) && \ - !defined(CONFIG_CRYPTO_MANAGER_DISABLE_TESTS) + #if defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && \ + defined(WC_LINUX_CONFIG_SELFTESTS) if ((PTR_ERR(tfm) == -ENOENT) && fips_enabled) { pr_info("info: skipping unsupported kpp algorithm %s: %d\n", driver, (int)PTR_ERR(tfm)); diff --git a/linuxkm/lkcapi_ecdsa_glue.c b/linuxkm/lkcapi_ecdsa_glue.c index 74cc6eeb7fb..68639ac14bf 100644 --- a/linuxkm/lkcapi_ecdsa_glue.c +++ b/linuxkm/lkcapi_ecdsa_glue.c @@ -554,7 +554,7 @@ static int km_ecdsa_verify(struct crypto_sig *tfm, /* 6 ECDSA (struct sig_testvec) test vectors in crypto/testmgr.h use SHA-1 * (m_size 20). */ -#ifdef WC_LINUXKM_HAVE_SELFTEST +#ifdef WC_LINUX_CONFIG_SELFTESTS wc_static_assert2(WC_MIN_DIGEST_SIZE_FOR_VERIFY <= 20, "WC_MIN_DIGEST_SIZE_FOR_VERIFY must allow SHA-1-sized digests when " "native kernel self-test is enabled."); @@ -676,7 +676,7 @@ static int km_ecdsa_verify(struct akcipher_request *req) /* 6 ECDSA (struct sig_testvec) test vectors in crypto/testmgr.h use SHA-1 * (m_size 20). */ -#ifdef WC_LINUXKM_HAVE_SELFTEST +#ifdef WC_LINUX_CONFIG_SELFTESTS wc_static_assert2(WC_MIN_DIGEST_SIZE_FOR_VERIFY <= 20, "WC_MIN_DIGEST_SIZE_FOR_VERIFY must allow SHA-1-sized digests when " "native kernel self-test is enabled."); @@ -1177,9 +1177,9 @@ static int linuxkm_test_ecdsa_nist_driver(const char * driver, */ tfm = crypto_alloc_akcipher(driver, 0, 0); if (IS_ERR(tfm)) { - #if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)) && \ - defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_MANAGER) && \ - !defined(CONFIG_CRYPTO_MANAGER_DISABLE_TESTS) + #if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)) && \ + defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && \ + defined(WC_LINUX_CONFIG_SELFTESTS) /* ecdsa was not recognized as fips_allowed before linux v6.3 * in kernel crypto/testmgr.c, and the kernel will block * its allocation if fips_enabled is set. */ diff --git a/linuxkm/lkcapi_glue.c b/linuxkm/lkcapi_glue.c index de7e868d50a..fc24bc4d44f 100644 --- a/linuxkm/lkcapi_glue.c +++ b/linuxkm/lkcapi_glue.c @@ -31,7 +31,7 @@ #error LINUXKM_LKCAPI_REGISTER is supported only on Linux kernel versions >= 5.4.0. #endif -#ifdef WC_LINUXKM_HAVE_SELFTEST +#ifdef WC_LINUX_CONFIG_SELFTESTS /* kernel crypto self-test includes test setups that have different expected * results FIPS vs non-FIPS, and the required kernel exported symbol * "fips_enabled" is only available in CONFIG_CRYPTO_FIPS kernels (otherwise @@ -63,7 +63,7 @@ #define WOLFSSL_LINUXKM_LKCAPI_PRIORITY 100000 #endif -#ifdef WC_LINUXKM_HAVE_SELFTEST_FULL +#ifdef WC_LINUX_CONFIG_SELFTESTS_FULL static int disable_setkey_warnings = 0; #else #define disable_setkey_warnings 0 @@ -224,7 +224,7 @@ static wolfSSL_Atomic_Int linuxkm_lkcapi_registering_now = WOLFSSL_ATOMIC_INITIA static int linuxkm_lkcapi_register(void); static int linuxkm_lkcapi_unregister(void); -#if defined(HAVE_FIPS) && defined(WC_LINUXKM_HAVE_SELFTEST) +#if defined(HAVE_FIPS) && defined(WC_LINUX_CONFIG_SELFTESTS) static int enabled_fips = 0; #endif @@ -267,7 +267,7 @@ static ssize_t deinstall_algs_handler(struct kobject *kobj, struct kobj_attribut if (ret != 0) return ret; -#if defined(HAVE_FIPS) && defined(WC_LINUXKM_HAVE_SELFTEST) +#if defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && defined(WC_LINUX_CONFIG_SELFTESTS) if (enabled_fips) { pr_info("wolfCrypt: restoring fips_enabled to off.\n"); enabled_fips = fips_enabled = 0; @@ -340,7 +340,7 @@ static int linuxkm_lkcapi_register(void) if (ret) goto out; -#ifdef WC_LINUXKM_HAVE_SELFTEST_FULL +#ifdef WC_LINUX_CONFIG_SELFTESTS_FULL /* temporarily disable warnings around setkey failures, which are expected * from the crypto fuzzer in FIPS configs, and potentially in others. * unexpected setkey failures are fatal errors returned by the fuzzer. @@ -348,7 +348,8 @@ static int linuxkm_lkcapi_register(void) disable_setkey_warnings = 1; #endif #if !defined(LINUXKM_DONT_FORCE_FIPS_ENABLED) && \ - defined(HAVE_FIPS) && defined(WC_LINUXKM_HAVE_SELFTEST) + defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && \ + defined(WC_LINUX_CONFIG_SELFTESTS) if (! fips_enabled) { /* assert system-wide FIPS status, to disable FIPS-forbidden * test vectors and fuzzing from the CRYPTO_MANAGER. @@ -392,7 +393,7 @@ static int linuxkm_lkcapi_register(void) } \ } while (0) -#if defined(HAVE_FIPS) && defined(WC_LINUXKM_HAVE_SELFTEST) +#if defined(HAVE_FIPS) && defined(WC_LINUX_CONFIG_SELFTESTS) /* Same as above, but allow for option to skip problematic algs that are * not consistently labeled fips_allowed in crypto/testmgr.c, and hence * may be rejected by the kernel at runtime if is_fips is true. */ @@ -574,7 +575,7 @@ static int linuxkm_lkcapi_register(void) #endif /* HAVE_ECC521 */ #elif (LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)) && \ defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && \ - defined(WC_LINUXKM_HAVE_SELFTEST) + defined(WC_LINUX_CONFIG_SELFTESTS) /* * ecdsa was not recognized as fips_allowed before linux v6.3 * in kernel crypto/testmgr.c. @@ -614,7 +615,7 @@ static int linuxkm_lkcapi_register(void) #if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)) && \ defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && \ - defined(WC_LINUXKM_HAVE_SELFTEST) + defined(WC_LINUX_CONFIG_SELFTESTS) #endif #endif /* LINUXKM_LKCAPI_REGISTER_ECDSA */ @@ -636,7 +637,7 @@ static int linuxkm_lkcapi_register(void) * enabled. Failures because of !fips_allowed are skipped over. */ #if defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && \ - defined(WC_LINUXKM_HAVE_SELFTEST) + defined(WC_LINUX_CONFIG_SELFTESTS) #if defined(LINUXKM_ECC192) REGISTER_ALG_OPTIONAL(ecdh_nist_p192, kpp, linuxkm_test_ecdh_nist_p192); #endif /* LINUXKM_ECC192 */ @@ -735,7 +736,7 @@ static int linuxkm_lkcapi_register(void) #undef REGISTER_ALG #undef REGISTER_ALG_OPTIONAL -#ifdef WC_LINUXKM_HAVE_SELFTEST_FULL +#ifdef WC_LINUX_CONFIG_SELFTESTS_FULL disable_setkey_warnings = 0; #endif diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 560636e6345..cf6bfe72e2e 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -1349,7 +1349,9 @@ WC_MAYBE_UNUSED static int linuxkm_hmac_setkey_common(struct crypto_shash *tfm, struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(tfm); int ret; -#if defined(HAVE_FIPS) && (FIPS_VERSION3_LT(6, 0, 0) || defined(CONFIG_CRYPTO_MANAGER_DISABLE_TESTS) || (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0))) +#if defined(HAVE_FIPS) && (FIPS_VERSION3_LT(6, 0, 0) || \ + !defined(WC_LINUX_CONFIG_SELFTESTS) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0))) ret = wc_HmacSetKey(&p_ctx->wc_hmac, type, key, length); #else /* kernel 5.10.x crypto manager expects FIPS-undersized keys to succeed. */ From a25d9c3983dc81017efe255c17fa999e0d59b63b Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 11:37:13 -0500 Subject: [PATCH 09/19] linuxkm/lkcapi_sha_glue.c: add struct km_sha3_state_by_pointer to tighten up SHA-3 allocations, avoiding frivolous struct km_sha_state (with inline wc_Sha512) for SHA-3. --- linuxkm/lkcapi_sha_glue.c | 88 +++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index cf6bfe72e2e..1afc76e3ad2 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -433,29 +433,6 @@ #undef LINUXKM_LKCAPI_REGISTER_HASH_DRBG #endif -#ifdef WOLFSSL_SHA3 -/* struct wc_Sha3 won't fit in HASH_MAX_DESCSIZE. */ -struct km_sha3_state { - union { -#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_224 - struct wc_Sha3 sha3_224_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_256 - struct wc_Sha3 sha3_256_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_384 - struct wc_Sha3 sha3_384_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_512 - struct wc_Sha3 sha3_512_state; -#endif - struct wc_Sha3 sha3_state; - }; - /* pointers for the cleanup list */ - struct list_head desc_ent; -}; -#endif /* WOLFSSL_SHA3 */ - struct km_sha_state { union { #ifdef LINUXKM_LKCAPI_REGISTER_SHA1 @@ -473,16 +450,38 @@ struct km_sha_state { #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_512 struct wc_Sha512 sha2_512_state; #endif - -#ifdef WOLFSSL_SHA3 - struct km_sha3_state *sha3_state; -#endif /* WOLFSSL_SHA3 */ }; }; wc_static_assert(sizeof(struct km_sha_state) <= HASH_MAX_DESCSIZE); #ifdef WOLFSSL_SHA3 +struct km_sha3_state { + union { +#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_224 + struct wc_Sha3 sha3_224_state; +#endif +#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_256 + struct wc_Sha3 sha3_256_state; +#endif +#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_384 + struct wc_Sha3 sha3_384_state; +#endif +#ifdef LINUXKM_LKCAPI_REGISTER_SHA3_512 + struct wc_Sha3 sha3_512_state; +#endif + struct wc_Sha3 sha3_state; + }; + /* pointers for the cleanup list */ + struct list_head desc_ent; +}; + +/* struct wc_Sha3 won't fit in HASH_MAX_DESCSIZE. */ +struct km_sha3_state_by_pointer { + struct km_sha3_state *sha3_state; +}; + +wc_static_assert(sizeof(struct km_sha3_state_by_pointer) <= HASH_MAX_DESCSIZE); #ifdef WOLFSSL_LINUXKM_USE_MUTEXES #error LINUXKM_LKCAPI_REGISTER_SHA3 requires spinlock-based mutexes. @@ -527,6 +526,10 @@ WC_MAYBE_UNUSED static void km_sha3_exit_tfm(struct crypto_shash *tfm) */ list_for_each_entry_safe(s_ctx_i, next_ent, &t_ctx->desc_list, desc_ent) { list_del(&s_ctx_i->desc_ent); + /* Use wc_Sha3_256_Free() as a proxy for unexported wc_Sha3Free() + * (currently a no-op in kernel configs, but that could change). + */ + wc_Sha3_256_Free(&s_ctx_i->sha3_state); ForceZero(s_ctx_i, sizeof(*s_ctx_i)); free(s_ctx_i); } @@ -536,11 +539,17 @@ WC_MAYBE_UNUSED static void km_sha3_exit_tfm(struct crypto_shash *tfm) WC_MAYBE_UNUSED static int km_sha3_alloc_tstate(struct shash_desc *desc) { struct km_Sha3TfmCtx *t_ctx = (struct km_Sha3TfmCtx *)crypto_shash_ctx(desc->tfm); - struct km_sha_state *s_ctx = (struct km_sha_state *)shash_desc_ctx(desc); + struct km_sha3_state_by_pointer *s_ctx = (struct km_sha3_state_by_pointer *)shash_desc_ctx(desc); s_ctx->sha3_state = (struct km_sha3_state *)malloc(sizeof(struct km_sha3_state)); if (! s_ctx->sha3_state) return -ENOMEM; + /* Must zero here to make unconditionally safe for wc_Sha3_256_Free() in + * km_sha3_exit_tfm() (currently a no-op in kernel configs, but that could + * change). + */ + XMEMSET(&s_ctx->sha3_state->sha3_state, 0, sizeof s_ctx->sha3_state->sha3_state); + if (wc_LockMutex(&t_ctx->desc_list_lock) != 0) { free(s_ctx->sha3_state); s_ctx->sha3_state = NULL; @@ -555,7 +564,7 @@ WC_MAYBE_UNUSED static int km_sha3_alloc_tstate(struct shash_desc *desc) { WC_MAYBE_UNUSED static void km_sha3_free_tstate(struct shash_desc *desc) { struct km_Sha3TfmCtx *t_ctx = (struct km_Sha3TfmCtx *)crypto_shash_ctx(desc->tfm); - struct km_sha_state *s_ctx = (struct km_sha_state *)shash_desc_ctx(desc); + struct km_sha3_state_by_pointer *s_ctx = (struct km_sha3_state_by_pointer *)shash_desc_ctx(desc); if (s_ctx->sha3_state == NULL) return; @@ -611,7 +620,7 @@ wc_static_assert(sizeof(struct km_sha3_export_state) <= HASH_MAX_STATESIZE); * member, so the generic .sha3_state accessor serves all four. */ WC_MAYBE_UNUSED static int km_sha3_export(struct shash_desc *desc, void *out) { - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc); + struct km_sha3_state_by_pointer *ctx = (struct km_sha3_state_by_pointer *)shash_desc_ctx(desc); struct km_sha3_export_state *blob = (struct km_sha3_export_state *)out; const struct wc_Sha3 *sha3; @@ -767,7 +776,7 @@ WC_MAYBE_UNUSED static int km_sha3_test_export_import( return ret; } -#endif +#endif /* WOLFSSL_SHA3 */ #define WC_LINUXKM_SHA1_IMPLEMENT(name, digest_size, block_size, \ this_cra_name, this_cra_driver_name, \ @@ -1041,7 +1050,8 @@ struct wc_swallow_the_semicolon \ \ static int km_ ## name ## _init(struct shash_desc *desc) { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct km_sha3_state_by_pointer *ctx = \ + (struct km_sha3_state_by_pointer *)shash_desc_ctx(desc); \ int ret; \ \ ret = km_sha3_alloc_tstate(desc); \ @@ -1059,7 +1069,8 @@ static int km_ ## name ## _init(struct shash_desc *desc) { \ static int km_ ## name ## _update(struct shash_desc *desc, const u8 *data, \ unsigned int len) \ { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct km_sha3_state_by_pointer *ctx = \ + (struct km_sha3_state_by_pointer *)shash_desc_ctx(desc); \ \ int ret = update_f(&ctx->sha3_state-> name ## _state, data, len); \ \ @@ -1072,7 +1083,8 @@ static int km_ ## name ## _update(struct shash_desc *desc, const u8 *data, \ } \ \ static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct km_sha3_state_by_pointer *ctx = \ + (struct km_sha3_state_by_pointer *)shash_desc_ctx(desc); \ \ int ret = final_f(&ctx->sha3_state-> name ## _state, out); \ \ @@ -1086,7 +1098,8 @@ static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ static int km_ ## name ## _finup(struct shash_desc *desc, const u8 *data, \ unsigned int len, u8 *out) \ { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct km_sha3_state_by_pointer *ctx = \ + (struct km_sha3_state_by_pointer *)shash_desc_ctx(desc); \ \ int ret = update_f(&ctx->sha3_state-> name ## _state, data, len); \ \ @@ -1121,7 +1134,8 @@ static int km_ ## name ## _digest(struct shash_desc *desc, const u8 *data, \ static int km_ ## name ## _import(struct shash_desc *desc, \ const void *in) \ { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct km_sha3_state_by_pointer *ctx = \ + (struct km_sha3_state_by_pointer *)shash_desc_ctx(desc); \ const struct km_sha3_export_state *blob = \ (const struct km_sha3_export_state *)in; \ struct wc_Sha3 *sha3; \ @@ -1162,7 +1176,7 @@ static struct shash_alg name ## _alg = \ .final = km_ ## name ## _final, \ .finup = km_ ## name ## _finup, \ .digest = km_ ## name ## _digest, \ - .descsize = sizeof(struct km_sha_state), \ + .descsize = sizeof(struct km_sha3_state_by_pointer), \ .export = km_sha3_export, \ .import = km_ ## name ## _import, \ .statesize = sizeof(struct km_sha3_export_state), \ From 34be5710528026022243eaffdaa15b0c049aa484 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 11:47:06 -0500 Subject: [PATCH 10/19] linuxkm/lkcapi_sha_glue.c: refactor HMAC export/import mechanism: * Eliminate pointer from export blob, replacing it with a TFM-specific random cookie and per-desc serial ID. * Fixes kernel address disclosure risk, handle forgery risk, cross-TFM confusion risk, and heap pointer reuse risk. --- linuxkm/lkcapi_sha_glue.c | 60 +++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 1afc76e3ad2..ddfa70236a0 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -1283,10 +1283,13 @@ struct wc_swallow_the_semicolon wc_Sha3_512_Free, sha3_test_once); #endif +#ifndef NO_HMAC + struct km_sha_hmac_node { struct Hmac wc_hmac; /* linkage for the tfm-owned cleanup list */ struct list_head desc_ent; + word64 desc_id; }; struct km_sha_hmac_state { /* HASH_MAX_DESCSIZE is 368, but sizeof(struct Hmac) is 832, so the working @@ -1304,16 +1307,18 @@ struct km_sha_hmac_pstate { /* bounded ring of .export snapshots; import validates handles against it */ struct list_head export_list; unsigned int export_list_len; + word64 cur_desc_id; + word64 tfm_cookie; }; /* Serialized HMAC state for .export / .import. sizeof(struct Hmac) is 832, and * an HMAC-over-SHA-3 state is two full sponges, so real state cannot fit * HASH_MAX_STATESIZE (345). .export deep-copies the live Hmac into a snapshot - * node on the tfm's export_list and the blob carries only a validated handle; - * .import validates the handle against THIS tfm's export_list -- never - * dereferencing an unvalidated pointer -- and copies from the snapshot. + * node on the tfm's export_list and the blob carries only a desc_id; .import + * looks it up by desc_id, validated using the tfm_cookie, and copies from the + * snapshot. Snapshots are deallocated at exit_tfm. */ -#define WC_LINUXKM_HMAC_EXPORT_MAGIC 0x484d4143U /* 'HMAC' */ +#define WC_LINUXKM_HMAC_EXPORT_MAGIC W64LIT(0x57435F484d414331) /* "WC_HMAC1" */ /* Upper bound on live .export snapshots per tfm. Bounds worst-case memory to * this many nodes (~832B each): without it, algif_hash's export-on-accept lets @@ -1331,8 +1336,9 @@ struct km_sha_hmac_pstate { #endif struct km_sha_hmac_export_state { - word32 magic; - struct km_sha_hmac_node *snapshot; + word64 magic; /* identifies the export as an HMAC handle. */ + word64 tfm_cookie; /* associates the export unambiguously with this TFM. */ + word64 desc_id; /* local to this TFM, allocated serially from zero. */ }; wc_static_assert(sizeof(struct km_sha_hmac_state) <= HASH_MAX_DESCSIZE); @@ -1342,8 +1348,6 @@ wc_static_assert(sizeof(struct km_sha_hmac_state) <= HASH_MAX_DESCSIZE); wc_static_assert(sizeof(struct km_sha_hmac_export_state) <= HASH_MAX_STATESIZE); #endif -#ifndef NO_HMAC - #ifdef WOLFSSL_LINUXKM_USE_MUTEXES #error LINUXKM_LKCAPI_REGISTER_HMAC requires spinlock-based mutexes. #endif @@ -1385,12 +1389,17 @@ WC_MAYBE_UNUSED static int km_hmac_alloc_tstate(struct shash_desc *desc) { s_ctx->node = (struct km_sha_hmac_node *)malloc(sizeof(struct km_sha_hmac_node)); if (! s_ctx->node) return -ENOMEM; + /* Must zero to assure the Hmac object is safe to pass to wc_HmacFree() even + * if init fails. + */ + XMEMSET(s_ctx->node, 0, sizeof *s_ctx->node); if (wc_LockMutex(&p_ctx->desc_list_lock) != 0) { free(s_ctx->node); s_ctx->node = NULL; return -EINVAL; } + s_ctx->node->desc_id = p_ctx->cur_desc_id++; list_add(&s_ctx->node->desc_ent, &p_ctx->desc_list); (void)wc_UnLockMutex(&p_ctx->desc_list_lock); @@ -1411,7 +1420,8 @@ WC_MAYBE_UNUSED static void km_hmac_free_tstate(struct shash_desc *desc) { (void)wc_UnLockMutex(&p_ctx->desc_list_lock); /* wc_HmacFree is NOT a no-op: a wc_HmacCopy'd node can own inner/outer hash - * heap (e.g. SMALL_STACK_CACHE W buffers), so it must run before free(). */ + * heap (e.g. SMALL_STACK_CACHE W buffers), so it must run before free(). + */ wc_HmacFree(&s_ctx->node->wc_hmac); #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) ForceZero(s_ctx->node, sizeof *s_ctx->node); @@ -1433,6 +1443,8 @@ WC_MAYBE_UNUSED static int km_hmac_init_tfm(struct crypto_shash *tfm) INIT_LIST_HEAD(&p_ctx->desc_list); INIT_LIST_HEAD(&p_ctx->export_list); p_ctx->export_list_len = 0; + p_ctx->cur_desc_id = 0; + p_ctx->tfm_cookie = get_random_u64(); return 0; } @@ -1605,7 +1617,7 @@ WC_MAYBE_UNUSED static int km_hmac_export(struct shash_desc *desc, void *out) } /* Bound the ring: at capacity, unlink the oldest (list tail) under the lock; * it is freed below, outside the lock. Unlinking under the lock is what - * lets .import validate-and-copy under the same lock without racing a free. + * lets .import lookup-and-copy under the same lock without racing a free. */ if (p_ctx->export_list_len >= WC_LINUXKM_HMAC_EXPORT_LIST_MAX) { evicted = list_last_entry(&p_ctx->export_list, @@ -1613,13 +1625,14 @@ WC_MAYBE_UNUSED static int km_hmac_export(struct shash_desc *desc, void *out) list_del(&evicted->desc_ent); p_ctx->export_list_len--; } + snapshot->desc_id = p_ctx->cur_desc_id++; /* list_add() prepends, so the tail from list_last_entry() is the oldest. */ list_add(&snapshot->desc_ent, &p_ctx->export_list); p_ctx->export_list_len++; (void)wc_UnLockMutex(&p_ctx->desc_list_lock); /* The evicted node is now unlinked and unreachable (any outstanding handle - * to it will fail import validation), so free it outside the lock. + * to it will fail import lookup), so free it outside the lock. */ if (evicted != NULL) { wc_HmacFree(&evicted->wc_hmac); @@ -1632,7 +1645,8 @@ WC_MAYBE_UNUSED static int km_hmac_export(struct shash_desc *desc, void *out) /* Zero first so no uninitialized padding leaks into the caller's buffer. */ XMEMSET(blob, 0, sizeof(*blob)); blob->magic = WC_LINUXKM_HMAC_EXPORT_MAGIC; - blob->snapshot = snapshot; + blob->tfm_cookie = p_ctx->tfm_cookie; + blob->desc_id = snapshot->desc_id; return 0; } @@ -1642,7 +1656,6 @@ WC_MAYBE_UNUSED static int km_hmac_import(struct shash_desc *desc, const void *i struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(desc->tfm); struct km_sha_hmac_state *s_ctx = (struct km_sha_hmac_state *)shash_desc_ctx(desc); const struct km_sha_hmac_export_state *blob = (const struct km_sha_hmac_export_state *)in; - struct km_sha_hmac_node *snapshot; struct km_sha_hmac_node *node_i; struct km_sha_hmac_node *newnode; int found = 0; @@ -1650,8 +1663,8 @@ WC_MAYBE_UNUSED static int km_hmac_import(struct shash_desc *desc, const void *i if (blob->magic != WC_LINUXKM_HMAC_EXPORT_MAGIC) return -EINVAL; - snapshot = blob->snapshot; - if (snapshot == NULL) + + if (blob->tfm_cookie != p_ctx->tfm_cookie) return -EINVAL; /* Fresh working node, allocated outside the lock; its inner Hmac heap is @@ -1668,32 +1681,30 @@ WC_MAYBE_UNUSED static int km_hmac_import(struct shash_desc *desc, const void *i * dereference of attacker-influenced memory. */ if (wc_LockMutex(&p_ctx->desc_list_lock) != 0) { - ForceZero(newnode, sizeof(*newnode)); free(newnode); return -EINVAL; } list_for_each_entry(node_i, &p_ctx->export_list, desc_ent) { - if (node_i == snapshot) { + if (node_i->desc_id == blob->desc_id) { found = 1; break; } } if (! found) { (void)wc_UnLockMutex(&p_ctx->desc_list_lock); - ForceZero(newnode, sizeof(*newnode)); /* raw node; nothing to wc_HmacFree */ free(newnode); return -EINVAL; } - ret = wc_HmacCopy(&snapshot->wc_hmac, &newnode->wc_hmac); + ret = wc_HmacCopy(&node_i->wc_hmac, &newnode->wc_hmac); if (ret != 0) { (void)wc_UnLockMutex(&p_ctx->desc_list_lock); - /* Copy-failure cleanup mirrors km_hmac_init (wc_HmacFree then free) -- - * keep the two consistent if wc_HmacCopy's failure contract is revised. + /* No need for wc_HmacFree() here -- failed wc_HmacCopy() guarantees no + * allocations are held under the Hmac -- in fact, it leaves the object + * in an indeterminate state that's unsafe to pass to wc_HmacFree(), + * since we aren't zeroing it after the malloc() (zeroing would be + * frivolous for allocations to be handed immediately to wc_HmacCopy()). */ - wc_HmacFree(&newnode->wc_hmac); -#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) ForceZero(newnode, sizeof(*newnode)); -#endif free(newnode); return -EINVAL; } @@ -1701,6 +1712,7 @@ WC_MAYBE_UNUSED static int km_hmac_import(struct shash_desc *desc, const void *i * overwriting any poisoned prior pointer without reading it. A real prior * node orphans onto desc_list and is reaped at exit_tfm. */ + newnode->desc_id = p_ctx->cur_desc_id++; list_add(&newnode->desc_ent, &p_ctx->desc_list); s_ctx->node = newnode; (void)wc_UnLockMutex(&p_ctx->desc_list_lock); From 00a8b5c1f273cc9b25bc8c499b91a326e3c246de Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 17:01:55 -0500 Subject: [PATCH 11/19] wolfssl/wolfcrypt/types.h: add wc_static_assert_if_const(). --- wolfssl/wolfcrypt/types.h | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index af98e43ba30..5d92b74c2c9 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -2247,7 +2247,8 @@ WOLFSSL_API word32 CheckRunTimeSettings(void); #include #endif #if (defined(__cplusplus) && (__cplusplus >= 201703L)) || \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L)) || \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L) && \ + !defined(__GNUC__)) || \ (defined(_MSVC_LANG) && (__cpp_static_assert >= 201411L)) /* native variadic static_assert() */ #define wc_static_assert static_assert @@ -2274,6 +2275,21 @@ WOLFSSL_API word32 CheckRunTimeSettings(void); #ifndef wc_static_assert2 #define wc_static_assert2(expr, msg) _Static_assert(expr, msg) #endif + #ifndef wc_static_assert_if_const + /* wc_static_assert_if_const() allows opportunistic compile-time + * assertions on expr -- if expr is constant at compile time, the + * assert is performed, otherwise it's silently ignored. Useful for + * checking potentially user-supplied macro definitions that may be + * numeric literals or may involve variable references. + */ + #define WC_IS_CONSTEXPR(e) \ + __builtin_types_compatible_p(__typeof__(8 ? \ + ((void *)((long)(e) * 0l)) \ + : (int *)8), int *) + #define wc_static_assert_if_const(expr, msg) \ + wc_static_assert2(__builtin_choose_expr( \ + WC_IS_CONSTEXPR(expr), expr, 1), msg) + #endif #else #ifdef __COUNTER__ #define wc_static_assert(expr) \ @@ -2293,6 +2309,11 @@ WOLFSSL_API word32 CheckRunTimeSettings(void); #define wc_static_assert2(expr, msg) wc_static_assert(expr) #endif +#ifndef wc_static_assert_if_const + #define wc_static_assert_if_const(expr, msg) \ + struct wc_static_assert_dummy_struct +#endif + #ifndef WC_RELAX_LONG_LOOP #define WC_RELAX_LONG_LOOP() WC_DO_NOTHING #endif From 86670c409ef2d9db97bc8ba3b5a20adee550922e Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 17:23:51 -0500 Subject: [PATCH 12/19] wolfcrypt/src/sha{256,512}.c, wolfssl/wolfcrypt/sha{256,512}.h, wolfssl/wolfcrypt/settings.h: * Revert earlier changes adding WC_SHA256_W_SIZE and WC_SHA512_W_SIZE. * Add WC_SHA2_NO_SMALL_STACK to allow the W work buffer to move back onto the stack. * In wolfssl/wolfcrypt/settings.h, define WC_SHA2_NO_SMALL_STACK by default when WOLFSSL_KERNEL_MODE, and add a clause to #undef WOLFSSL_SMALL_STACK when building the SHA-2 implementations. linuxkm/lkcapi_sha_glue.c: * Refactor out superfluous struct km_sha_state. * Clean up some macro dynamics. * Add static asserts on HASH_MAX_STATESIZE to WC_LINUXKM_SHA1_IMPLEMENT() and WC_LINUXKM_SHA2_IMPLEMENT(). --- .wolfssl_known_macro_extras | 1 + linuxkm/lkcapi_sha_glue.c | 173 +++++++++++++++++------------------ wolfcrypt/src/sha256.c | 22 ++--- wolfcrypt/src/sha512.c | 12 +-- wolfssl/wolfcrypt/settings.h | 16 +++- wolfssl/wolfcrypt/sha256.h | 15 ++- wolfssl/wolfcrypt/sha512.h | 12 ++- 7 files changed, 133 insertions(+), 118 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index c8367a23ed8..f98c165ca19 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -746,6 +746,7 @@ WC_RSA_NONBLOCK_TIME WC_RSA_NO_FERMAT_CHECK WC_RTL8735B_NO_DERIVE_CACHE WC_RWLOCK_OPS_INLINE +WC_SHA2_SMALL_STACK WC_SHE_SW_DEFAULT WC_SKIP_INCLUDED_C_FILES WC_SLHDSA_KERNEL_ASM diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index ddfa70236a0..4c2119caac0 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -433,29 +433,19 @@ #undef LINUXKM_LKCAPI_REGISTER_HASH_DRBG #endif -struct km_sha_state { - union { -#ifdef LINUXKM_LKCAPI_REGISTER_SHA1 - struct wc_Sha sha1_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA2_224 - struct wc_Sha256 sha2_224_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA2_256 - struct wc_Sha256 sha2_256_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA2_384 - struct wc_Sha512 sha2_384_state; -#endif -#ifdef LINUXKM_LKCAPI_REGISTER_SHA2_512 - struct wc_Sha512 sha2_512_state; +/* HASH_MAX_STATESIZE added by 2b1a29ce33, kernel 6.16. Before that it was + * implicitly same as HASH_MAX_DESCSIZE. + */ +#ifndef HASH_MAX_STATESIZE + #define HASH_MAX_STATESIZE HASH_MAX_DESCSIZE #endif - }; -}; -wc_static_assert(sizeof(struct km_sha_state) <= HASH_MAX_DESCSIZE); +#if defined(WOLFSSL_SHA3) && \ + (defined(LINUXKM_LKCAPI_REGISTER_SHA3_224) || \ + defined(LINUXKM_LKCAPI_REGISTER_SHA3_256) || \ + defined(LINUXKM_LKCAPI_REGISTER_SHA3_384) || \ + defined(LINUXKM_LKCAPI_REGISTER_SHA3_512)) -#ifdef WOLFSSL_SHA3 struct km_sha3_state { union { #ifdef LINUXKM_LKCAPI_REGISTER_SHA3_224 @@ -609,10 +599,7 @@ struct km_sha3_export_state { * Sha3Final rejects i >= rate */ }; -/* HASH_MAX_STATESIZE added by 2b1a29ce33, kernel 6.16. */ -#ifdef HASH_MAX_STATESIZE wc_static_assert(sizeof(struct km_sha3_export_state) <= HASH_MAX_STATESIZE); -#endif /* Non-destructive: serialize the live sponge into the caller's statesize * buffer, leaving the desc (and its cleanup-list node) intact for continued @@ -776,18 +763,21 @@ WC_MAYBE_UNUSED static int km_sha3_test_export_import( return ret; } -#endif /* WOLFSSL_SHA3 */ +#endif /* WOLFSSL_SHA3 && LINUXKM_LKCAPI_REGISTER_SHA3_* */ -#define WC_LINUXKM_SHA1_IMPLEMENT(name, digest_size, block_size, \ +#define WC_LINUXKM_SHA1_IMPLEMENT(name, s_name, digest_size, block_size, \ this_cra_name, this_cra_driver_name, \ init_f, update_f, final_f, \ free_f, test_routine) \ \ \ +wc_static_assert(sizeof(struct s_name) <= HASH_MAX_DESCSIZE); \ +wc_static_assert(sizeof(struct s_name) <= HASH_MAX_STATESIZE); \ + \ static int km_ ## name ## _init(struct shash_desc *desc) { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ \ - int ret = init_f(&ctx-> name ## _state); \ + int ret = init_f(ctx); \ if (ret == 0) \ return 0; \ else \ @@ -797,24 +787,24 @@ static int km_ ## name ## _init(struct shash_desc *desc) { \ static int km_ ## name ## _update(struct shash_desc *desc, const u8 *data, \ unsigned int len) \ { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ \ - int ret = update_f(&ctx-> name ## _state, data, len); \ + int ret = update_f(ctx, data, len); \ \ if (ret == 0) \ return 0; \ else { \ - free_f(&ctx-> name ## _state); \ + free_f(ctx); \ return -EINVAL; \ } \ } \ \ static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ \ - int ret = final_f(&ctx-> name ## _state, out); \ + int ret = final_f(ctx, out); \ \ - free_f(&ctx-> name ## _state); \ + free_f(ctx); \ \ if (ret == 0) \ return 0; \ @@ -825,12 +815,12 @@ static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ static int km_ ## name ## _finup(struct shash_desc *desc, const u8 *data, \ unsigned int len, u8 *out) \ { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ \ - int ret = update_f(&ctx-> name ## _state, data, len); \ + int ret = update_f(ctx, data, len); \ \ if (ret != 0) { \ - free_f(&ctx-> name ## _state); \ + free_f(ctx); \ return -EINVAL; \ } \ \ @@ -855,7 +845,7 @@ static struct shash_alg name ## _alg = \ .final = km_ ## name ## _final, \ .finup = km_ ## name ## _finup, \ .digest = km_ ## name ## _digest, \ - .descsize = sizeof(struct km_sha_state), \ + .descsize = sizeof(struct s_name), \ .base = { \ .cra_name = (this_cra_name), \ .cra_driver_name = (this_cra_driver_name), \ @@ -880,7 +870,8 @@ static int linuxkm_test_ ## name(void) { \ \ struct wc_swallow_the_semicolon -#ifdef WOLFSSL_SMALL_STACK_CACHE +#if defined(WOLFSSL_SMALL_STACK_CACHE) && \ + (!defined(WC_HAVE_SHA2_NO_SMALL_STACK) || !defined(WC_SHA2_NO_SMALL_STACK)) /* The glue layer needs to take ownership of the .W working buffer to assure * it can't leak on abandoned descs, or double-free on export-import cycled * descs. It's small enough to fit comfortably on the stack, so there's @@ -904,18 +895,32 @@ struct wc_swallow_the_semicolon #define WC_LINUXKM_SHA2_POP_W(s) } WC_DO_NOTHING #endif -#define WC_LINUXKM_SHA2_IMPLEMENT(name, digest_size, block_size, W_size, \ +/* WC_SHA*_W_SIZE are only used when WC_LINUXKM_SHA2_FREE_W() and friends are + * substantively implemented. + */ +#ifndef WC_SHA256_W_SIZE + #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) +#endif +#ifndef WC_SHA512_W_SIZE + #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) +#endif + +#define WC_LINUXKM_SHA2_IMPLEMENT(name, s_name, digest_size, block_size, \ + W_size, \ this_cra_name, this_cra_driver_name, \ init_f, update_f, final_f, \ free_f, test_routine) \ \ \ +wc_static_assert(sizeof(struct s_name) <= HASH_MAX_DESCSIZE); \ +wc_static_assert(sizeof(struct s_name) <= HASH_MAX_STATESIZE); \ + \ static int km_ ## name ## _init(struct shash_desc *desc) { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ \ - int ret = init_f(&ctx-> name ## _state); \ + int ret = init_f(ctx); \ if (ret == 0) { \ - WC_LINUXKM_SHA2_FREE_W(&ctx-> name ## _state); \ + WC_LINUXKM_SHA2_FREE_W(ctx); \ return 0; \ } \ else \ @@ -925,32 +930,32 @@ static int km_ ## name ## _init(struct shash_desc *desc) { \ static int km_ ## name ## _update(struct shash_desc *desc, const u8 *data, \ unsigned int len) \ { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ int ret; \ - WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + WC_LINUXKM_SHA2_DECL_W(ctx, W_size); \ \ - WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ - ret = update_f(&ctx-> name ## _state, data, len); \ - WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + WC_LINUXKM_SHA2_PUSH_W(ctx); \ + ret = update_f(ctx, data, len); \ + WC_LINUXKM_SHA2_POP_W(ctx); \ \ if (ret == 0) \ return 0; \ else { \ - free_f(&ctx-> name ## _state); \ + free_f(ctx); \ return -EINVAL; \ } \ } \ \ static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ int ret; \ - WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + WC_LINUXKM_SHA2_DECL_W(ctx, W_size); \ \ - WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ - ret = final_f(&ctx-> name ## _state, out); \ - WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + WC_LINUXKM_SHA2_PUSH_W(ctx); \ + ret = final_f(ctx, out); \ + WC_LINUXKM_SHA2_POP_W(ctx); \ \ - free_f(&ctx-> name ## _state); \ + free_f(ctx); \ \ if (ret == 0) \ return 0; \ @@ -961,24 +966,24 @@ static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ static int km_ ## name ## _finup(struct shash_desc *desc, const u8 *data, \ unsigned int len, u8 *out) \ { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ int ret; \ - WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + WC_LINUXKM_SHA2_DECL_W(ctx, W_size); \ \ - WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ - ret = update_f(&ctx-> name ## _state, data, len); \ - WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + WC_LINUXKM_SHA2_PUSH_W(ctx); \ + ret = update_f(ctx, data, len); \ + WC_LINUXKM_SHA2_POP_W(ctx); \ \ if (ret != 0) { \ - free_f(&ctx-> name ## _state); \ + free_f(ctx); \ return -EINVAL; \ } \ \ - WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ - ret = final_f(&ctx-> name ## _state, out); \ - WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + WC_LINUXKM_SHA2_PUSH_W(ctx); \ + ret = final_f(ctx, out); \ + WC_LINUXKM_SHA2_POP_W(ctx); \ \ - free_f(&ctx-> name ## _state); \ + free_f(ctx); \ \ if (ret == 0) \ return 0; \ @@ -989,19 +994,19 @@ static int km_ ## name ## _finup(struct shash_desc *desc, const u8 *data, \ static int km_ ## name ## _digest(struct shash_desc *desc, const u8 *data, \ unsigned int len, u8 *out) \ { \ - struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + struct s_name *ctx = (struct s_name *)shash_desc_ctx(desc); \ int ret; \ \ - ret = init_f(&ctx-> name ## _state); \ + ret = init_f(ctx); \ if (ret != 0) \ return -EINVAL; \ \ - ret = update_f(&ctx-> name ## _state, data, len); \ + ret = update_f(ctx, data, len); \ \ if (ret == 0) \ - ret = final_f(&ctx-> name ## _state, out); \ + ret = final_f(ctx, out); \ \ - free_f(&ctx-> name ## _state); \ + free_f(ctx); \ \ if (ret == 0) \ return 0; \ @@ -1018,7 +1023,7 @@ static struct shash_alg name ## _alg = \ .final = km_ ## name ## _final, \ .finup = km_ ## name ## _finup, \ .digest = km_ ## name ## _digest, \ - .descsize = sizeof(struct km_sha_state), \ + .descsize = sizeof(struct s_name), \ .base = { \ .cra_name = (this_cra_name), \ .cra_driver_name = (this_cra_driver_name), \ @@ -1210,21 +1215,14 @@ static int linuxkm_test_ ## name(void) { \ struct wc_swallow_the_semicolon #ifdef LINUXKM_LKCAPI_REGISTER_SHA1 - WC_LINUXKM_SHA1_IMPLEMENT(sha1, WC_SHA_DIGEST_SIZE, WC_SHA_BLOCK_SIZE, + WC_LINUXKM_SHA1_IMPLEMENT(sha1, wc_Sha, WC_SHA_DIGEST_SIZE, WC_SHA_BLOCK_SIZE, WOLFKM_SHA1_NAME, WOLFKM_SHA1_DRIVER, wc_InitSha, wc_ShaUpdate, wc_ShaFinal, wc_ShaFree, sha_test); #endif -#ifndef WC_SHA256_W_SIZE - #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) -#endif -#ifndef WC_SHA512_W_SIZE - #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) -#endif - #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_224 - WC_LINUXKM_SHA2_IMPLEMENT(sha2_224, WC_SHA224_DIGEST_SIZE, WC_SHA224_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_224, wc_Sha256, WC_SHA224_DIGEST_SIZE, WC_SHA224_BLOCK_SIZE, WC_SHA256_W_SIZE, WOLFKM_SHA2_224_NAME, WOLFKM_SHA2_224_DRIVER, wc_InitSha224, wc_Sha224Update, wc_Sha224Final, @@ -1232,7 +1230,7 @@ struct wc_swallow_the_semicolon #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_256 - WC_LINUXKM_SHA2_IMPLEMENT(sha2_256, WC_SHA256_DIGEST_SIZE, WC_SHA256_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_256, wc_Sha256, WC_SHA256_DIGEST_SIZE, WC_SHA256_BLOCK_SIZE, WC_SHA256_W_SIZE, WOLFKM_SHA2_256_NAME, WOLFKM_SHA2_256_DRIVER, wc_InitSha256, wc_Sha256Update, wc_Sha256Final, @@ -1240,7 +1238,7 @@ struct wc_swallow_the_semicolon #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_384 - WC_LINUXKM_SHA2_IMPLEMENT(sha2_384, WC_SHA384_DIGEST_SIZE, WC_SHA384_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_384, wc_Sha512, WC_SHA384_DIGEST_SIZE, WC_SHA384_BLOCK_SIZE, WC_SHA512_W_SIZE, WOLFKM_SHA2_384_NAME, WOLFKM_SHA2_384_DRIVER, wc_InitSha384, wc_Sha384Update, wc_Sha384Final, @@ -1248,7 +1246,7 @@ struct wc_swallow_the_semicolon #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_512 - WC_LINUXKM_SHA2_IMPLEMENT(sha2_512, WC_SHA512_DIGEST_SIZE, WC_SHA512_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_512, wc_Sha512, WC_SHA512_DIGEST_SIZE, WC_SHA512_BLOCK_SIZE, WC_SHA512_W_SIZE, WOLFKM_SHA2_512_NAME, WOLFKM_SHA2_512_DRIVER, wc_InitSha512, wc_Sha512Update, wc_Sha512Final, @@ -1333,6 +1331,9 @@ struct km_sha_hmac_pstate { */ #ifndef WC_LINUXKM_HMAC_EXPORT_LIST_MAX #define WC_LINUXKM_HMAC_EXPORT_LIST_MAX (nr_cpu_ids * 2) +#else + wc_static_assert_if_const(WC_LINUXKM_HMAC_EXPORT_LIST_MAX > 0, + "WC_LINUXKM_HMAC_EXPORT_LIST_MAX must be positive."); #endif struct km_sha_hmac_export_state { @@ -1343,10 +1344,7 @@ struct km_sha_hmac_export_state { wc_static_assert(sizeof(struct km_sha_hmac_state) <= HASH_MAX_DESCSIZE); -/* HASH_MAX_STATESIZE added by 2b1a29ce33, kernel 6.16. */ -#ifdef HASH_MAX_STATESIZE wc_static_assert(sizeof(struct km_sha_hmac_export_state) <= HASH_MAX_STATESIZE); -#endif #ifdef WOLFSSL_LINUXKM_USE_MUTEXES #error LINUXKM_LKCAPI_REGISTER_HMAC requires spinlock-based mutexes. @@ -1560,6 +1558,7 @@ WC_MAYBE_UNUSED static int km_hmac_digest(struct shash_desc *desc, const u8 *dat ret = wc_HmacCopy(&p_ctx->wc_hmac, h); if (ret != 0) { + ForceZero(h, sizeof *h); free(h); return -EINVAL; } @@ -1739,8 +1738,8 @@ WC_MAYBE_UNUSED static int km_hmac_test_export_import( unsigned int split, i, dsz; byte key[32]; byte msg[300]; - byte ref[WC_SHA3_512_DIGEST_SIZE]; - byte tag[WC_SHA3_512_DIGEST_SIZE]; + byte ref[WC_MAX_DIGEST_SIZE]; + byte tag[WC_MAX_DIGEST_SIZE]; for (i = 0; i < (unsigned int)sizeof(key); i++) key[i] = (byte)(i + 1); diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index 16a56bc7575..4c3d4fd2ebb 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -747,7 +747,7 @@ static int InitSha256(wc_Sha256* sha256) sha256->devCtx = NULL; #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, + sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1329,7 +1329,7 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId) #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, + sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1482,7 +1482,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->devCtx = NULL; #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, + sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1570,12 +1570,12 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, return BAD_FUNC_ARG; #elif defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) word32* W; - W = (word32*)XMALLOC(WC_SHA256_W_SIZE, + W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); if (W == NULL) return MEMORY_E; #else - word32 W[WC_SHA256_W_SIZE / sizeof(word32)]; + word32 W[WC_SHA256_BLOCK_SIZE]; #endif /* Copy context->state[] to working vars */ @@ -1616,7 +1616,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #endif return 0; } -#else /* SHA256_MANY_REGISTERS */ +#else /* SHA256 version that keeps all data in registers */ #define SCHED1(j) (W[j] = *((word32*)&data[j*sizeof(word32)])) #define SCHED(j) ( \ @@ -1644,7 +1644,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef USE_SLOW_SHA256 int j; #endif - word32 W[WC_SHA256_W_SIZE / sizeof(word32)]; + word32 W[WC_SHA256_BLOCK_SIZE/sizeof(word32)]; /* Copy digest to working vars */ S[0] = sha256->digest[0]; @@ -1693,7 +1693,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, return 0; } #endif /* SHA256_MANY_REGISTERS */ -#endif /* NEED_SOFT_SHA256 */ +#endif /* End wc_ software implementation */ #ifdef XTRANSFORM @@ -2484,7 +2484,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef WOLFSSL_SMALL_STACK_CACHE if (sha224->W == NULL) { - sha224->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, + sha224->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, sha224->heap, DYNAMIC_TYPE_DIGEST); if (sha224->W == NULL) return MEMORY_E; @@ -3054,7 +3054,7 @@ int wc_Sha224_Grow(wc_Sha224* sha224, const byte* in, int inSz) XMEMCPY(dst, src, sizeof(wc_Sha224)); #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, + dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha224)); @@ -3208,7 +3208,7 @@ int wc_Sha256Copy(wc_Sha256* src, wc_Sha256* dst) #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, + dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha256)); diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 6a6bde5a03c..311ff37e436 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -1714,7 +1714,7 @@ static int InitSha512_Family(wc_Sha512* sha512, void* heap, int devId, * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - sha512->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, + sha512->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, sha512->heap, DYNAMIC_TYPE_DIGEST); if (sha512->W == NULL) return MEMORY_E; @@ -1890,11 +1890,11 @@ static int _Transform_Sha512(wc_Sha512* sha512) return BAD_FUNC_ARG; #elif defined(WOLFSSL_SMALL_STACK) word64* W; - W = (word64*)XMALLOC(WC_SHA512_W_SIZE, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); + W = (word64*) XMALLOC(sizeof(word64) * 16, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); if (W == NULL) return MEMORY_E; #else - word64 W[WC_SHA512_W_SIZE / sizeof(word64)]; + word64 W[16]; #endif /* Copy digest to working vars */ @@ -2759,7 +2759,7 @@ static int InitSha384(wc_Sha384* sha384) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - sha384->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, + sha384->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, sha384->heap, DYNAMIC_TYPE_DIGEST); if (sha384->W == NULL) return MEMORY_E; @@ -3117,7 +3117,7 @@ int wc_Sha512Copy(wc_Sha512* src, wc_Sha512* dst) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - dst->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, + dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha512)); @@ -3559,7 +3559,7 @@ int wc_Sha384Copy(wc_Sha384* src, wc_Sha384* dst) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - dst->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, + dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA384_BLOCK_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha384)); diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index e0d13ec0176..49ea49b0b84 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -4427,7 +4427,6 @@ defined(CONFIG_CRYPTO_ECDSA))) #define WC_MIN_DIGEST_SIZE_FOR_VERIFY 20 #endif - #endif /* WOLFSSL_LINUXKM */ /* FreeBSD Kernel Module */ @@ -4568,6 +4567,15 @@ #ifndef WOLFSSL_SMALL_STACK_STATIC #define WOLFSSL_SMALL_STACK_STATIC #endif + + /* The size of the W buffer used by SHA-2 is negligible, but with + * _SMALL_STACK it gets XMALLOC()d. This knob lets it be selectively moved + * back to the stack, avoiding overhead and awkwardness in + * linuxkm/lkcapi_sha_glue.c. + */ + #if !defined(WC_SHA2_NO_SMALL_STACK) && !defined(WC_SHA2_SMALL_STACK) + #define WC_SHA2_NO_SMALL_STACK + #endif #endif /* WOLFSSL_KERNEL_MODE || WOLFSSL_KERNEL_MODE_DEFAULTS */ #if defined(WC_SYM_RELOC_TABLES) && defined(HAVE_FIPS) && \ @@ -5830,6 +5838,12 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #define WC_NO_PTR_INT_CAST #endif +#if (defined(_WC_BUILDING_SHA256_C) || defined(_WC_BUILDING_SHA512_C)) && \ + defined(WC_SHA2_NO_SMALL_STACK) + #undef WOLFSSL_SMALL_STACK + #undef WOLFSSL_SMALL_STACK_CACHE +#endif + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index e78f0a80b9e..4305d3078ca 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -58,6 +58,10 @@ #include "cy_crypto_core.h" #endif +#ifndef WC_HAVE_SHA2_NO_SMALL_STACK + #define WC_HAVE_SHA2_NO_SMALL_STACK +#endif + #ifdef __cplusplus extern "C" { #endif @@ -199,7 +203,7 @@ struct wc_Sha256 { int sha_method; #endif -#endif /* !FREESCALE_LTC_SHA etc. */ +#endif void* heap; #ifdef WOLFSSL_PIC32MZ_HASH hashUpdCache cache; /* cache for updates */ @@ -207,14 +211,9 @@ struct wc_Sha256 { #ifdef WOLFSSL_ASYNC_CRYPT WC_ASYNC_DEV asyncDev; #endif /* WOLFSSL_ASYNC_CRYPT */ -#ifdef SHA256_MANY_REGISTERS - #define WC_SHA256_W_SIZE WC_SHA256_BLOCK_SIZE -#else /* !SHA256_MANY_REGISTERS */ - #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) -#endif /* !SHA256_MANY_REGISTERS */ -#ifdef WOLFSSL_SMALL_STACK_CACHE +#if defined(WOLFSSL_SMALL_STACK_CACHE) && !defined(WC_SHA2_NO_SMALL_STACK) word32* W; -#endif /* WOLFSSL_SMALL_STACK_CACHE */ +#endif /* !FREESCALE_LTC_SHA && !STM32_HASH_SHA2 */ #ifdef WOLFSSL_DEVCRYPTO_HASH WC_CRYPTODEV ctx; #endif diff --git a/wolfssl/wolfcrypt/sha512.h b/wolfssl/wolfcrypt/sha512.h index c8bc8ef0479..5ee250a32be 100644 --- a/wolfssl/wolfcrypt/sha512.h +++ b/wolfssl/wolfcrypt/sha512.h @@ -36,6 +36,10 @@ #include #endif /* HAVE_FIPS_VERSION >= 2 */ +#ifndef WC_HAVE_SHA2_NO_SMALL_STACK + #define WC_HAVE_SHA2_NO_SMALL_STACK +#endif + #ifdef __cplusplus extern "C" { #endif @@ -165,12 +169,10 @@ struct wc_Sha512 { #ifdef WOLFSSL_ASYNC_CRYPT WC_ASYNC_DEV asyncDev; #endif /* WOLFSSL_ASYNC_CRYPT */ -#ifdef WOLFSSL_SMALL_STACK_CACHE - #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) +#if defined(WOLFSSL_SMALL_STACK_CACHE) && !defined(WC_SHA2_NO_SMALL_STACK) word64* W; -#else /* !WOLFSSL_SMALL_STACK_CACHE */ - #define WC_SHA512_W_SIZE (sizeof(word64) * 16) -#endif /* !WOLFSSL_SMALL_STACK_CACHE */ +#endif + #if defined(WOLFSSL_ESP32_CRYPT) && \ !defined(NO_WOLFSSL_ESP32_CRYPT_HASH) && \ (!defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) || \ From 168893520a6dcf87ae0fe401a98f10deb7658a9a Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 17:46:30 -0500 Subject: [PATCH 13/19] configure.ac: enable SHA512 for fips=lean-aesgcm, to support SHA512 integrity check and DRBG. --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 662214246fe..c4734c32f38 100644 --- a/configure.ac +++ b/configure.ac @@ -7374,9 +7374,9 @@ AS_CASE([$FIPS_VERSION], (test "$FIPS_VERSION" != "lean-aesgcm-dev" || test "$enable_sha384" != "yes")], [enable_sha384="no"; ENABLED_SHA384="no"; AM_CFLAGS="$AM_CFLAGS -UWOLFSSL_SHA384 -UWOLFSSL_SHA384"]) - AS_IF([test "$ENABLED_SHA512" != "no" && - (test "$FIPS_VERSION" != "lean-aesgcm-dev" || test "$enable_sha512" != "yes")], - [enable_sha512="no"; ENABLED_SHA512="no"; ENABLED_SHA512_DRBG="no"; AM_CFLAGS="$AM_CFLAGS -UWOLFSSL_SHA512 -UWOLFSSL_SHA384"]) + AS_IF([test "$ENABLED_SHA512" = "no" && + (test "$FIPS_VERSION" != "lean-aesgcm-dev" || test "$enable_sha512" != "no")], + [enable_sha512="yes"; ENABLED_SHA512="yes"; ENABLED_SHA512_DRBG="yes"; AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SHA512 -DWOLFSSL_SHA384"]) # SHA512-224 and SHA512-256 are SHA-2 algorithms not in our FIPS algorithm list AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NOSHA512_224 -DWOLFSSL_NOSHA512_256" From e98b35996e2a7a4260ee3ef6a6ebdcf087f67fb8 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 17:51:51 -0500 Subject: [PATCH 14/19] tests/api/test_kdf.c: fix NO_SHA in test_wc_KdfFeatureCoverage(). --- tests/api/test_kdf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/api/test_kdf.c b/tests/api/test_kdf.c index 4eea6a3e1dc..0957b3ce492 100644 --- a/tests/api/test_kdf.c +++ b/tests/api/test_kdf.c @@ -849,14 +849,16 @@ int test_wc_KdfFeatureCoverage(void) #ifdef WOLFSSL_WOLFSSH { byte k[8] = {0x01}; +#ifndef NO_SHA byte kPadded[8] = {0}; /* top bit set: exercises kPad path */ +#endif byte h[8] = {0x02}; byte sessionId[8] = {0x03}; byte key[160] = {0}; +#ifndef NO_SHA kPadded[0] = 0x80; -#ifndef NO_SHA /* digestSz(SHA1)=20: keySz=10 -> blocks==0, remainder>0. */ ExpectIntEQ(wc_SSH_KDF(WC_SHA, 'A', key, 10, k, sizeof(k), h, sizeof(h), sessionId, sizeof(sessionId)), 0); From 2a0f06a24384e55196a68cfd2343477cc51e4217 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 18:24:19 -0500 Subject: [PATCH 15/19] wolfcrypt/src/cpuid.c: in cpuid_flag(), refactor out XMEMSET() to fix SEGV from gcc misoptimization. --- wolfcrypt/src/cpuid.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wolfcrypt/src/cpuid.c b/wolfcrypt/src/cpuid.c index 854a34ff535..35892b64aff 100644 --- a/wolfcrypt/src/cpuid.c +++ b/wolfcrypt/src/cpuid.c @@ -120,8 +120,7 @@ /* Feature leaves are only queried on known Intel and AMD CPUs. */ if (cpuid_is_intel() || cpuid_is_amd()) { unsigned int reg[5]; - - XMEMSET(reg, '\0', sizeof(reg)); + reg[num] = 0; cpuid(reg, leaf, sub); return ((reg[num] >> bit) & 0x1); } From 03d1c81608a91046a32f8f8e06c826622ca0d520 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 23 Jul 2026 19:02:37 -0500 Subject: [PATCH 16/19] wolfcrypt/src/aes.c: implement WC_C_DYNAMIC_FALLBACK for AES-NI in wc_AesGcmInit(). --- wolfcrypt/src/aes.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 46a89a6ad80..729ae65af7a 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -13992,9 +13992,19 @@ int wc_AesGcmInit(Aes* aes, const byte* key, word32 len, const byte* iv, #ifdef WOLFSSL_AESNI if (aes->use_aesni) { - SAVE_VECTOR_REGISTERS(return _svr_ret;); - ret = AesGcmInit_aesni(aes, iv, ivSz); - RESTORE_VECTOR_REGISTERS(); + ret = SAVE_VECTOR_REGISTERS2(); + if (ret == 0) { + ret = AesGcmInit_aesni(aes, iv, ivSz); + RESTORE_VECTOR_REGISTERS(); + } + else { +#ifdef WC_C_DYNAMIC_FALLBACK + aes->use_aesni = 0; + ret = AesGcmInit_C(aes, iv, ivSz); +#else + return ret; +#endif + } } else #elif defined(__aarch64__) && defined(WOLFSSL_ARMASM) && \ From 3c0746d89b8c95bfd478feae6902e7315d82190d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 24 Jul 2026 09:58:01 -0500 Subject: [PATCH 17/19] linuxkm/: rename enabled_fips to enabled_kernel_fips_enabled, and fix gating on its declaration (add dependency on defined(CONFIG_CRYPTO_FIPS)); use FIPS_NOT_ALLOWED_E rather than NOT_COMPILED_IN to signal FIPS-forbidden keysizes from linuxkm_test_ecdh_nist_driver() and linuxkm_test_ecdsa_nist_driver() to REGISTER_ALG_OPTIONAL(). --- linuxkm/lkcapi_ecdh_glue.c | 2 +- linuxkm/lkcapi_ecdsa_glue.c | 2 +- linuxkm/lkcapi_glue.c | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/linuxkm/lkcapi_ecdh_glue.c b/linuxkm/lkcapi_ecdh_glue.c index 1445bb17764..78f485221a9 100644 --- a/linuxkm/lkcapi_ecdh_glue.c +++ b/linuxkm/lkcapi_ecdh_glue.c @@ -913,7 +913,7 @@ static int linuxkm_test_ecdh_nist_driver(const char * driver, if ((PTR_ERR(tfm) == -ENOENT) && fips_enabled) { pr_info("info: skipping unsupported kpp algorithm %s: %d\n", driver, (int)PTR_ERR(tfm)); - test_rc = NOT_COMPILED_IN; + test_rc = FIPS_NOT_ALLOWED_E; } else #endif diff --git a/linuxkm/lkcapi_ecdsa_glue.c b/linuxkm/lkcapi_ecdsa_glue.c index 68639ac14bf..c32b5e8a54f 100644 --- a/linuxkm/lkcapi_ecdsa_glue.c +++ b/linuxkm/lkcapi_ecdsa_glue.c @@ -1186,7 +1186,7 @@ static int linuxkm_test_ecdsa_nist_driver(const char * driver, if ((PTR_ERR(tfm) == -ENOENT) && fips_enabled) { pr_info("info: skipping unsupported akcipher algorithm %s: %d\n", driver, (int)PTR_ERR(tfm)); - test_rc = NOT_COMPILED_IN; + test_rc = FIPS_NOT_ALLOWED_E; } else #endif diff --git a/linuxkm/lkcapi_glue.c b/linuxkm/lkcapi_glue.c index fc24bc4d44f..1fbaf973d1e 100644 --- a/linuxkm/lkcapi_glue.c +++ b/linuxkm/lkcapi_glue.c @@ -224,8 +224,8 @@ static wolfSSL_Atomic_Int linuxkm_lkcapi_registering_now = WOLFSSL_ATOMIC_INITIA static int linuxkm_lkcapi_register(void); static int linuxkm_lkcapi_unregister(void); -#if defined(HAVE_FIPS) && defined(WC_LINUX_CONFIG_SELFTESTS) -static int enabled_fips = 0; +#if defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && defined(WC_LINUX_CONFIG_SELFTESTS) +static int enabled_kernel_fips_enabled = 0; #endif static ssize_t install_algs_handler(struct kobject *kobj, struct kobj_attribute *attr, @@ -268,9 +268,9 @@ static ssize_t deinstall_algs_handler(struct kobject *kobj, struct kobj_attribut return ret; #if defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && defined(WC_LINUX_CONFIG_SELFTESTS) - if (enabled_fips) { + if (enabled_kernel_fips_enabled) { pr_info("wolfCrypt: restoring fips_enabled to off.\n"); - enabled_fips = fips_enabled = 0; + enabled_kernel_fips_enabled = fips_enabled = 0; } #endif @@ -355,7 +355,7 @@ static int linuxkm_lkcapi_register(void) * test vectors and fuzzing from the CRYPTO_MANAGER. */ pr_info("wolfCrypt: changing fips_enabled from 0 to 1 for FIPS module.\n"); - enabled_fips = fips_enabled = 1; + enabled_kernel_fips_enabled = fips_enabled = 1; } #endif @@ -401,7 +401,7 @@ static int linuxkm_lkcapi_register(void) if (! alg ## _loaded) { \ ret = (crypto_register_ ## alg_class)(&(alg)); \ if (ret) { \ - if (fips_enabled && (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN))) { \ + if (ret == WC_NO_ERR_TRACE(FIPS_NOT_ALLOWED_E)) { \ pr_info("wolfCrypt: skipping FIPS-incompatible alg %s.\n", \ (alg).base.cra_driver_name); \ } \ @@ -415,7 +415,7 @@ static int linuxkm_lkcapi_register(void) } else { \ ret = (tester()); \ if (ret) { \ - if (fips_enabled && (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN))) { \ + if (ret == WC_NO_ERR_TRACE(FIPS_NOT_ALLOWED_E)) { \ pr_info("wolfCrypt: skipping FIPS-incompatible alg %s.\n", \ (alg).base.cra_driver_name); \ } \ From 4fcf62f2fddc7e1eb701950a962f0d3a24671c1c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 24 Jul 2026 10:04:49 -0500 Subject: [PATCH 18/19] .github/workflows/codespell.yml: add *.bin to skip list. --- .github/workflows/codespell.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index d58736e6bcf..57e54f2dc21 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -31,4 +31,4 @@ jobs: # The exclude_file contains lines of code that should be ignored. This is useful for individual lines which have non-words that can safely be ignored. exclude_file: '.codespellexcludelines' # To skip files entirely from being processed, add it to the following list: - skip: '*.cproject,*.csr,*.der,*.mtpj,*.pem,*.vcxproj,.git,*.launch,*.scfg,*.revoked,./examples/asn1/dumpasn1.cfg,./examples/asn1/oid_names.h' + skip: '*.cproject,*.csr,*.der,*.mtpj,*.pem,*.vcxproj,.git,*.launch,*.scfg,*.revoked,./examples/asn1/dumpasn1.cfg,./examples/asn1/oid_names.h,*.bin' From 5d377ee676247f028db969dd67a9ba5981c5a088 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 24 Jul 2026 10:40:49 -0500 Subject: [PATCH 19/19] linuxkm/linuxkm_wc_port.h: fix raw_spinlock.magic poison strategy in wc_FreeMutex(). --- linuxkm/linuxkm_wc_port.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 43412887da9..77b395d8bd2 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -2004,7 +2004,10 @@ static __always_inline int wc_FreeMutex(wolfSSL_Mutex* m) { #ifdef CONFIG_DEBUG_SPINLOCK - m->lock.magic = 0; + /* clear raw_spinlock.magic, but without drilling through the + * abstraction barrier. + */ + memset(&m->lock, 0, sizeof m->lock); #endif #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG m->magic = 0;