From 3ef3e980f8dc618c962ecdaa299d85d17b460458 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 10 Jul 2026 11:14:48 -0500 Subject: [PATCH 1/4] linuxkm/Makefile: move --no-silent kludge into $(NO_SILENT), off by default (apparently unneeded with make 4.4.1 and current Makefile/Kbuild). --- linuxkm/Makefile | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/linuxkm/Makefile b/linuxkm/Makefile index 24a867b9356..5fdbd4f2629 100644 --- a/linuxkm/Makefile +++ b/linuxkm/Makefile @@ -29,6 +29,13 @@ else QFLAG := --quiet endif +# Some versions and invocations of Gnu make have glitched on --quiet builds, +# due to a bug around $(MAKEFLAGS), like so: +# "No rule to make target 's'. Stop." +# If you encounter this, try uncommenting the below line, which is supported on +# Gnu make 4.3+: +# NO_SILENT := --no-silent + ifndef LIBWOLFSSL_NAME LIBWOLFSSL_NAME := libwolfssl endif @@ -331,8 +338,7 @@ ifeq "$(ENABLED_LINUXKM_PIE)" "yes" @RELOC_TMP=$$(mktemp "$(MAKE_TMPDIR)/wc_linuxkm_pie_reloc_tab.c.XXXXXX") @trap 'rm "$$RELOC_TMP"' EXIT @if [[ -f "$@" ]]; then touch -r "$@" "$$RELOC_TMP"; fi - # --no-silent works around make bug that otherwise leads to "No rule to make target 's'. Stop." (due to a bug around $(MAKEFLAGS)) in --quiet builds. - +$(MAKE) $(QFLAG) --no-print-directory --no-silent ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= + +$(MAKE) $(QFLAG) $(NO_SILENT) --no-print-directory ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= # if the above make didn't build a fresh libwolfssl.ko, then the module is already up to date and we leave it untouched, assuring stability for purposes of module-update-fips-hash. @if [[ ! "$@" -nt "$$RELOC_TMP" ]]; then echo ' Module already up-to-date.'; exit 0; fi @SECTION_MAP=$$(mktemp) @@ -340,12 +346,11 @@ ifeq "$(ENABLED_LINUXKM_PIE)" "yes" @export SECTION_MAP @$(READELF) --wide --sections --symbols "$@" | $(GENERATE_SECTION_MAP) @$(READELF) --wide --relocs "$@" | $(GENERATE_RELOC_TAB) >| '$(MODULE_TOP)/linuxkm/wc_linuxkm_pie_reloc_tab.c' - +$(MAKE) $(QFLAG) --no-print-directory --no-silent ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= + +$(MAKE) $(QFLAG) --no-print-directory $(NO_SILENT) ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= @$(READELF) --wide --relocs "$@" | $(GENERATE_RELOC_TAB) >| "$$RELOC_TMP" @if diff '$(MODULE_TOP)/linuxkm/wc_linuxkm_pie_reloc_tab.c' "$$RELOC_TMP"; then echo " Relocation table is stable."; else echo "PIE failed: relocation table is unstable." 1>&2; exit 1; fi else - # --no-silent works around make bug that otherwise leads to "No rule to make target 's'. Stop." (due to a bug around $(MAKEFLAGS)) in --quiet builds. - +$(MAKE) $(QFLAG) --no-print-directory --no-silent ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) + +$(MAKE) $(QFLAG) --no-print-directory $(NO_SILENT) ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) endif $(MODULE_TOP)/$(LIBWOLFSSL_NAME).ko: $(LIBWOLFSSL_NAME).ko @@ -368,7 +373,7 @@ module-update-fips-hash: $(LIBWOLFSSL_NAME).ko if [[ '$(FIPS_HASH)' == "$$current_verifyCore" ]]; then echo ' Supplied FIPS_HASH matches existing verifyCore -- no update needed.'; exit 0; fi; \ echo -n '$(FIPS_HASH)' | dd bs=1 conv=notrunc of="$<" seek=$$verifyCore_offset count=64 status=none && \ echo " FIPS verifyCore updated successfully." && \ - if [[ -f '$(LIBWOLFSSL_NAME).ko.signed' ]]; then $(MAKE) $(QFLAG) --no-print-directory --no-silent -C . '$(LIBWOLFSSL_NAME).ko.signed'; fi + if [[ -f '$(LIBWOLFSSL_NAME).ko.signed' ]]; then $(MAKE) $(QFLAG) --no-print-directory $(NO_SILENT) -C . '$(LIBWOLFSSL_NAME).ko.signed'; fi # linuxkm-fips-hash implements offline (no-load) FIPS hash calculation and graft-in. @@ -431,7 +436,7 @@ linuxkm-fips-hash: $(MODULE_TOP)/libwolfssl-user-build/src/.libs/libwolfssl.so l module-with-matching-fips-hash: $(LIBWOLFSSL_NAME).ko linuxkm-fips-hash @set -e @./linuxkm-fips-hash-wrapper.sh "$<" $(QFLAG) $(VFLAG) - +$(MAKE) $(QFLAG) --no-print-directory --no-silent -C . '$(LIBWOLFSSL_NAME).ko.signed' + +$(MAKE) $(QFLAG) --no-print-directory $(NO_SILENT) -C . '$(LIBWOLFSSL_NAME).ko.signed' .PHONY: module-with-matching-fips-hash-no-sign module-with-matching-fips-hash-no-sign: $(LIBWOLFSSL_NAME).ko linuxkm-fips-hash @@ -482,13 +487,13 @@ endif .PHONY: install modules_install install modules_install: - +$(MAKE) $(QFLAG) --no-silent -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP) INSTALL_MOD_DIR=wolfssl modules_install + +$(MAKE) $(QFLAG) $(NO_SILENT) -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP) INSTALL_MOD_DIR=wolfssl modules_install .PHONY: clean # note, must supply $(MODULE_TOP) as the src value for clean so that Kbuild is included, else # the top Makefile (which is not for the kernel build) would be included here. clean: - +$(MAKE) $(QFLAG) --no-silent -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(MODULE_TOP) clean + +$(MAKE) $(QFLAG) $(NO_SILENT) -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(MODULE_TOP) clean $(RM) -rf '$(MODULE_TOP)/linuxkm' $(RM) -rf '$(MODULE_TOP)/wolfcrypt' $(RM) -rf '$(MODULE_TOP)/src' From 972fa21b279545a9eb553b40ef22a8096d178988 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 10 Jul 2026 17:27:36 -0500 Subject: [PATCH 2/4] linuxkm/lkcapi_aes_glue.c: in non-streaming AesGcmCrypt_1(), use wc_AesGcmSetExtIV() and wc_AesGcmEncrypt_ex(), not wc_AesGcmEncrypt(), to stay inside FIPS boundary. --- linuxkm/lkcapi_aes_glue.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/linuxkm/lkcapi_aes_glue.c b/linuxkm/lkcapi_aes_glue.c index d1336864295..b3f19d3a08b 100644 --- a/linuxkm/lkcapi_aes_glue.c +++ b/linuxkm/lkcapi_aes_glue.c @@ -1489,13 +1489,23 @@ static int AesGcmCrypt_1(struct aead_request *req, int decrypt_p, int rfc4106_p) } } else { - err = wc_AesGcmEncrypt(aes_copy, out_text, in_text, req->cryptlen, + err = wc_AesGcmSetExtIV(aes_copy, #ifdef LINUXKM_LKCAPI_REGISTER_AESGCM_RFC4106 - rfc4106_p ? rfc4106_iv : + rfc4106_p ? rfc4106_iv : #endif - sk_walk.iv, GCM_NONCE_MID_SZ, - authTag, tfm->authsize, - assoc, assoclen); + sk_walk.iv, GCM_NONCE_MID_SZ); + if (unlikely(err)) { + pr_err("%s: wc_AesGcmSetExtIV() failed: %d\n", + crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm)), err); + err = -EINVAL; + goto out; + } + + { + byte ivOut[GCM_NONCE_MID_SZ]; + err = wc_AesGcmEncrypt_ex(aes_copy, out_text, in_text, req->cryptlen, ivOut, GCM_NONCE_MID_SZ, authTag, + tfm->authsize, assoc, assoclen); + } if (unlikely(err)) { pr_err("%s: wc_AesGcmEncrypt failed: %d\n", @@ -1503,7 +1513,6 @@ static int AesGcmCrypt_1(struct aead_request *req, int decrypt_p, int rfc4106_p) err = -EINVAL; goto out; } - } if (sg_buf) { From dcb4d0edea8edef27282c7accb1e4d1cee3c5724 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 10 Jul 2026 17:29:11 -0500 Subject: [PATCH 3/4] tests/api.c: fix test_wc_CryptoCb_registry() for new ALREADY_E from attempted re-registration of callback. --- tests/api.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/api.c b/tests/api.c index 2f4dce871e6..6381da923d5 100644 --- a/tests/api.c +++ b/tests/api.c @@ -30477,10 +30477,10 @@ static int test_wc_CryptoCb_registry(void) NULL), 0); ExpectIntEQ(wc_CryptoCb_GetDevIdAtIndex(0), devId); - /* Re-registering the same device id updates the entry instead of growing - * the device table. */ - ExpectIntEQ(wc_CryptoCb_RegisterDevice(devId, NULL, (void*)1), 0); - ExpectIntEQ(wc_CryptoCb_GetDevIdAtIndex(0), devId); + /* Re-registering the same device id used to (5.9.2-) update the entry + * instead of growing the device table. Now it just returns ALREADY_E -- + * the way to update it is to unregister and freshly register. */ + ExpectIntEQ(wc_CryptoCb_RegisterDevice(devId, NULL, (void*)1), ALREADY_E); wc_CryptoCb_UnRegisterDevice(INVALID_DEVID); ExpectIntEQ(wc_CryptoCb_GetDevIdAtIndex(0), devId); From 436bce8f4152875c5fd8d2d289c98ed355d423eb Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 10 Jul 2026 17:29:44 -0500 Subject: [PATCH 4/4] wolfcrypt/src/port/af_alg/afalg_aes.c, wolfcrypt/src/port/kcapi/kcapi_aes.c, tests/api/test_aes.c: fixes for AF_ALG and KCAPI ports. fixes longstanding bug in afalg_aes.c that made no-AAD handles non-interchangeable with AAD handles. also adds missing arg validation and KEYUSAGE_E checks throughout AF_ALG. --- tests/api/test_aes.c | 5 +- wolfcrypt/src/port/af_alg/afalg_aes.c | 96 +++++++++++++++++++-------- wolfcrypt/src/port/kcapi/kcapi_aes.c | 2 +- 3 files changed, 74 insertions(+), 29 deletions(-) diff --git a/tests/api/test_aes.c b/tests/api/test_aes.c index 505935a8aa4..eaae6380a3a 100644 --- a/tests/api/test_aes.c +++ b/tests/api/test_aes.c @@ -59,7 +59,8 @@ * (e.g. WOLFSSL_ARMASM on ARMv8 with crypto extensions). * In all these cases the corrupted struct is ignored and the op returns 0. */ #if defined(WOLF_CRYPTO_CB_FIND) || defined(WOLF_CRYPTO_CB_ONLY_AES) || \ - defined(WOLFSSL_ARMASM) || defined(HAVE_FIPS) || defined(HAVE_SELFTEST) + defined(WOLFSSL_ARMASM) || defined(HAVE_FIPS) || defined(HAVE_SELFTEST) || \ + defined(WOLFSSL_AFALG) /* The aes->rounds=0 corruption trick relies on the pure-C AesEncryptBlocks_C * guard (if r==0 return KEYUSAGE_E). When AES is offloaded (crypto-cb / asm) * or provided by the FIPS/self-test module, that guard is absent: rounds=0 @@ -9910,7 +9911,7 @@ int test_wc_AesKeyExportArgMcdc(void) byte id[4] = { 1, 2, 3, 4 }; const char* label = "test-label"; -#ifndef WC_NO_CONSTRUCTORS +#if !defined(WC_NO_CONSTRUCTORS) && !defined(WOLFSSL_KCAPI) /* * _AesNew_common() (aes.c ~14766, ~14774, ~14783) validates its * id/idLen/label arguments differently per construction path. Each diff --git a/wolfcrypt/src/port/af_alg/afalg_aes.c b/wolfcrypt/src/port/af_alg/afalg_aes.c index 060ed8000b9..efbf6358594 100644 --- a/wolfcrypt/src/port/af_alg/afalg_aes.c +++ b/wolfcrypt/src/port/af_alg/afalg_aes.c @@ -42,6 +42,16 @@ static const char WC_TYPE_SYMKEY[] = "skcipher"; +#ifdef HAVE_AESGCM +#ifdef WOLFSSL_AFALG_XILINX_AES + static const char WC_NAME_AESGCM[] = "xilinx-zynqmp-aes"; + static const char* WC_TYPE_AEAD = WC_TYPE_SYMKEY; +#else + static const char WC_NAME_AESGCM[] = "gcm(aes)"; + static const char WC_TYPE_AEAD[] = "aead"; +#endif +#endif + static int wc_AesSetup(Aes* aes, const char* type, const char* name, int ivSz, int aadSz) { #ifdef WOLFSSL_AFALG_XILINX_AES @@ -88,12 +98,20 @@ static int wc_AesSetup(Aes* aes, const char* type, const char* name, int ivSz, i (void)aadSz; #else aes->msg.msg_controllen = CMSG_SPACE(4); - if (aadSz > 0) { - aes->msg.msg_controllen += CMSG_SPACE(4); - } if (ivSz > 0) { aes->msg.msg_controllen += CMSG_SPACE((sizeof(struct af_alg_iv) + ivSz)); } + /* Always reserve ASSOCLEN slot for AEAD (gcm(aes)). Value may be 0 on any + * given call. skcipher paths (CBC etc.) simply won't use the 3rd cmsg. */ + if ((aadSz > 0) +#ifdef HAVE_AESGCM + || (strcmp(type, WC_TYPE_AEAD) == 0) +#endif + ) + { + aes->msg.msg_controllen += CMSG_SPACE(4); + } + #endif if (wc_Afalg_SetOp(CMSG_FIRSTHDR(&(aes->msg)), aes->dir) < 0) { @@ -174,6 +192,10 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, } #endif + if (aes->dir != AES_ENCRYPTION) { + return KEYUSAGE_E; + } + if (aes->rdFd == WC_SOCK_NOTSET) { if ((ret = wc_AesSetup(aes, WC_TYPE_SYMKEY, WC_NAME_AESCBC, AES_IV_SIZE, 0)) != 0) { @@ -239,6 +261,10 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, #endif } + if (aes->dir != AES_DECRYPTION) { + return KEYUSAGE_E; + } + if (aes->rdFd == WC_SOCK_NOTSET) { if ((ret = wc_AesSetup(aes, WC_TYPE_SYMKEY, WC_NAME_AESCBC, AES_IV_SIZE, 0)) != 0) { @@ -336,12 +362,20 @@ static int wc_Afalg_AesDirect(Aes* aes, byte* out, const byte* in, word32 sz) #if defined(WOLFSSL_AES_DIRECT) && defined(WOLFSSL_AFALG) int wc_AesEncryptDirect(Aes* aes, byte* out, const byte* in) { + if (aes && (aes->dir != AES_ENCRYPTION)) { + return KEYUSAGE_E; + } + return wc_Afalg_AesDirect(aes, out, in, WC_AES_BLOCK_SIZE); } int wc_AesDecryptDirect(Aes* aes, byte* out, const byte* in) { + if (aes && (aes->dir != AES_DECRYPTION)) { + return KEYUSAGE_E; + } + return wc_Afalg_AesDirect(aes, out, in, WC_AES_BLOCK_SIZE); } @@ -380,6 +414,10 @@ int wc_AesSetKeyDirect(Aes* aes, const byte* userKey, word32 keylen, return BAD_FUNC_ARG; } + if (aes->dir != AES_ENCRYPTION) { + return KEYUSAGE_E; + } + /* consume any unused bytes left in aes->tmp */ tmp = (byte*)aes->tmp + WC_AES_BLOCK_SIZE - aes->left; while (aes->left && sz) { @@ -478,15 +516,6 @@ int wc_AesSetKeyDirect(Aes* aes, const byte* userKey, word32 keylen, #ifdef HAVE_AESGCM - -#ifdef WOLFSSL_AFALG_XILINX_AES - static const char WC_NAME_AESGCM[] = "xilinx-zynqmp-aes"; - static const char* WC_TYPE_AEAD = WC_TYPE_SYMKEY; -#else - static const char WC_NAME_AESGCM[] = "gcm(aes)"; - static const char WC_TYPE_AEAD[] = "aead"; -#endif - #ifndef WC_SYSTEM_AESGCM_IV /* size of IV allowed on system for AES-GCM */ #define WC_SYSTEM_AESGCM_IV 12 @@ -526,6 +555,7 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len) #endif aes->keylen = len; aes->rounds = len/4 + 6; + aes->dir = AES_ENCRYPTION; if (aes->rdFd > WC_SOCK_NOTSET) { (void)close(aes->rdFd); @@ -573,7 +603,10 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, byte scratch[WC_AES_BLOCK_SIZE]; /* argument checks */ - if (aes == NULL || authTagSz > WC_AES_BLOCK_SIZE) { + if (aes == NULL || authTagSz > WC_AES_BLOCK_SIZE || + (sz > 0 && (in == NULL || out == NULL)) || + authTag == NULL || (authInSz > 0 && authIn == NULL)) + { return BAD_FUNC_ARG; } @@ -696,13 +729,14 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, xorbuf(authTag, scratch, authTagSz); } #else - if (authInSz > 0) { - cmsg = CMSG_NXTHDR(msg, cmsg); - ret = wc_Afalg_SetAad(cmsg, authInSz); - if (ret < 0) { - WOLFSSL_MSG("Unable to set AAD size"); - return ret; - } + /* Always set AAD length (even 0). This is required by the AF_ALG AEAD interface + * and prevents kernel state mismatch when switching between AAD and no-AAD + * operations on the same rdFd. */ + cmsg = CMSG_NXTHDR(msg, cmsg); + ret = wc_Afalg_SetAad(cmsg, authInSz); + if (ret < 0) { + WOLFSSL_MSG("Unable to set AAD size"); + return ret; } /* set data to be encrypted*/ @@ -901,12 +935,14 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, } #else - if (authInSz > 0) { - cmsg = CMSG_NXTHDR(msg, cmsg); - ret = wc_Afalg_SetAad(cmsg, authInSz); - if (ret < 0) { - return ret; - } + /* Always set AAD length (even 0). This is required by the AF_ALG AEAD interface + * and prevents kernel state mismatch when switching between AAD and no-AAD + * operations on the same rdFd. */ + + cmsg = CMSG_NXTHDR(msg, cmsg); + ret = wc_Afalg_SetAad(cmsg, authInSz); + if (ret < 0) { + return ret; } /* set data to be decrypted*/ @@ -955,12 +991,20 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, #ifdef HAVE_AES_ECB int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if (aes && (aes->dir != AES_ENCRYPTION)) { + return KEYUSAGE_E; + } + return wc_Afalg_AesDirect(aes, out, in, sz); } int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if (aes && (aes->dir != AES_DECRYPTION)) { + return KEYUSAGE_E; + } + return wc_Afalg_AesDirect(aes, out, in, sz); } #endif /* HAVE_AES_ECB */ diff --git a/wolfcrypt/src/port/kcapi/kcapi_aes.c b/wolfcrypt/src/port/kcapi/kcapi_aes.c index 71d86ec5346..038706bdf30 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_aes.c +++ b/wolfcrypt/src/port/kcapi/kcapi_aes.c @@ -428,7 +428,7 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, if (ret == -EBADMSG) ret = AES_GCM_AUTH_E; } - else if (ret != outbuflen) { + else if (ret > outbuflen) { WOLFSSL_MSG("GcmDecrypt produced wrong output length"); ret = BAD_FUNC_ARG; }