From 957ec5d2e58c396af571f8aca8b797fcb75f3cff Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Wed, 30 Sep 2026 14:52:01 +0100 Subject: [PATCH 1/3] Fix AES key wrap pad build with FIPS v5.2 modules FIPS modules before v5.3 declare wc_AesEncryptDirect() and wc_AesDecryptDirect() as returning void. The RFC 5649 key wrap with padding code assigns their return value, so builds against a FIPS v5.2.x wolfSSL with key wrap enabled fail with "void value not ignored as it ought to be". Add small wrappers that return the wolfSSL result where one exists and 0 otherwise, using the same FIPS_VERSION_GE(5, 3) guard as the existing wc_CmacFree() calls. --- src/internal.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/internal.c b/src/internal.c index 7ea63651..3b0c9c4d 100644 --- a/src/internal.c +++ b/src/internal.c @@ -17250,6 +17250,29 @@ int WP11_AesKeyWrap_Decrypt(unsigned char* enc, word32 encSz, return 0; } +/* Single-block AES encrypt/decrypt. FIPS modules before v5.3 can declare + * wc_AesEncryptDirect/wc_AesDecryptDirect as returning void, so no result is + * available there. */ +static int wp11_AesEncryptDirect(Aes* aes, byte* out, const byte* in) +{ +#if (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5, 3)) + return wc_AesEncryptDirect(aes, out, in); +#else + wc_AesEncryptDirect(aes, out, in); + return 0; +#endif +} + +static int wp11_AesDecryptDirect(Aes* aes, byte* out, const byte* in) +{ +#if (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5, 3)) + return wc_AesDecryptDirect(aes, out, in); +#else + wc_AesDecryptDirect(aes, out, in); + return 0; +#endif +} + /** * RFC 3394 key unwrap core that returns the recovered integrity register A * instead of verifying it, so RFC 5649 can inspect the AIV (which encodes the @@ -17293,7 +17316,7 @@ static int wp11_AesKeyUnwrapRaw(Aes* aes, const unsigned char* in, word32 inSz, r = out + (i - 1) * KEYWRAP_BLOCK_SIZE; XMEMCPY(tmp, a, KEYWRAP_BLOCK_SIZE); XMEMCPY(tmp + KEYWRAP_BLOCK_SIZE, r, KEYWRAP_BLOCK_SIZE); - ret = wc_AesDecryptDirect(aes, tmp, tmp); + ret = wp11_AesDecryptDirect(aes, tmp, tmp); if (ret != 0) break; XMEMCPY(a, tmp, KEYWRAP_BLOCK_SIZE); @@ -17352,7 +17375,7 @@ int WP11_AesKeyWrapPad_Encrypt(unsigned char* plain, word32 plainSz, unsigned char block[2 * KEYWRAP_BLOCK_SIZE]; XMEMCPY(block, aiv, KEYWRAP_BLOCK_SIZE); XMEMCPY(block + KEYWRAP_BLOCK_SIZE, buf, KEYWRAP_BLOCK_SIZE); - ret = wc_AesEncryptDirect(&wrap->aes, enc, block); + ret = wp11_AesEncryptDirect(&wrap->aes, enc, block); wc_ForceZero(block, sizeof(block)); } else { @@ -17401,7 +17424,7 @@ int WP11_AesKeyWrapPad_Decrypt(unsigned char* enc, word32 encSz, if (encSz == 2 * KEYWRAP_BLOCK_SIZE) { /* Single semiblock: ECB-decrypt to AIV || padded plaintext. */ unsigned char block[2 * KEYWRAP_BLOCK_SIZE]; - ret = wc_AesDecryptDirect(&wrap->aes, block, enc); + ret = wp11_AesDecryptDirect(&wrap->aes, block, enc); if (ret == 0) { XMEMCPY(aiv, block, KEYWRAP_BLOCK_SIZE); XMEMCPY(padBuf, block + KEYWRAP_BLOCK_SIZE, KEYWRAP_BLOCK_SIZE); From 98db55ec4a21116f243c6ce93ba2299ce6756e02 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Wed, 30 Sep 2026 14:52:02 +0100 Subject: [PATCH 2/3] Skip copyobject Test6 when token storage stays writable Test6 makes the token storage directory read-only to force a persistence failure. Privileged users such as root bypass directory permissions, so the copy succeeds and the test fails, for example when building the Debian package as root inside a container. Probe whether a file can still be created in the directory after making it read-only and skip the check if so. --- tests/copyobject_token_test.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/copyobject_token_test.c b/tests/copyobject_token_test.c index faa18307..861584e9 100644 --- a/tests/copyobject_token_test.c +++ b/tests/copyobject_token_test.c @@ -463,6 +463,21 @@ static int test_copy_token_findable(CK_SESSION_HANDLE session) /* A failed persistence attempt must roll the new token object out of the * in-memory list before C_CopyObject frees it. */ #ifndef WOLFPKCS11_NO_STORE +#define COPY_TOKEN_PROBE_FILE COPY_TOKEN_TEST_DIR "/.write_probe" + +/* Returns 1 if a new file can still be created in the token storage + * directory. Privileged users (e.g. root) bypass directory permissions. */ +static int test_store_dir_writable(void) +{ + FILE* f = fopen(COPY_TOKEN_PROBE_FILE, "wb"); + + if (f == NULL) + return 0; + fclose(f); + (void)remove(COPY_TOKEN_PROBE_FILE); + return 1; +} + static int test_copy_token_store_failure(CK_SESSION_HANDLE session) { CK_RV ret; @@ -486,6 +501,11 @@ static int test_copy_token_store_failure(CK_SESSION_HANDLE session) CHECK_COND(ret == 0, "Test6: make token storage read-only"); storeReadOnly = 1; + if (test_store_dir_writable()) { + printf("SKIP: Test6: token storage still writable (running as root?)\n"); + goto cleanup; + } + ret = funcList->C_CopyObject(session, src, copyTmpl, 1, ©); (void)TEST_SET_WRITABLE(COPY_TOKEN_TEST_DIR); storeReadOnly = 0; From be2dd9f77c4d3f23edf347bf3e909a9f52dbb1d8 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Wed, 30 Sep 2026 15:40:20 +0100 Subject: [PATCH 3/3] Keep AES direct errors on FIPS v5.2.3+ ARM builds The FIPS v5.2.3 and v5.2.4 modules declare wc_AesEncryptDirect() and wc_AesDecryptDirect() as returning int when WOLFSSL_ARMASM is defined, unlike v5.2.1 which returns void there too. The wrappers treated all FIPS modules before v5.3 as void and discarded the result, so an AES failure in key wrap with padding could be reported as success. Return the result for ARM assembly builds of FIPS v5.2.3 and later. FIPS_VERSION3_GE() is checked for first as older wolfSSL releases do not define it. --- src/internal.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/internal.c b/src/internal.c index 3b0c9c4d..c844a26b 100644 --- a/src/internal.c +++ b/src/internal.c @@ -17250,12 +17250,21 @@ int WP11_AesKeyWrap_Decrypt(unsigned char* enc, word32 encSz, return 0; } -/* Single-block AES encrypt/decrypt. FIPS modules before v5.3 can declare +/* Single-block AES encrypt/decrypt. FIPS modules before v5.3 declare * wc_AesEncryptDirect/wc_AesDecryptDirect as returning void, so no result is - * available there. */ + * available there. The v5.2.3 and v5.2.4 modules return int for ARM assembly + * builds. */ +#if (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5, 3)) + #define WP11_AES_DIRECT_RETURNS_INT +#elif defined(WOLFSSL_ARMASM) && defined(FIPS_VERSION3_GE) + #if FIPS_VERSION3_GE(5, 2, 3) + #define WP11_AES_DIRECT_RETURNS_INT + #endif +#endif + static int wp11_AesEncryptDirect(Aes* aes, byte* out, const byte* in) { -#if (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5, 3)) +#ifdef WP11_AES_DIRECT_RETURNS_INT return wc_AesEncryptDirect(aes, out, in); #else wc_AesEncryptDirect(aes, out, in); @@ -17265,7 +17274,7 @@ static int wp11_AesEncryptDirect(Aes* aes, byte* out, const byte* in) static int wp11_AesDecryptDirect(Aes* aes, byte* out, const byte* in) { -#if (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5, 3)) +#ifdef WP11_AES_DIRECT_RETURNS_INT return wc_AesDecryptDirect(aes, out, in); #else wc_AesDecryptDirect(aes, out, in);