diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras
index f0557ab8a1..57de68a890 100644
--- a/.wolfssl_known_macro_extras
+++ b/.wolfssl_known_macro_extras
@@ -9,6 +9,7 @@ AES_SR_CCF
AFX_RESOURCE_DLL
AFX_TARG_ENU
ALLOW_BINARY_MISMATCH_INTROSPECTION
+ALLOW_ENTROPY_FALLBACK
ALLOW_SELFSIGNED_INVALID_CERTSIGN
ALLOW_V1_EXTENSIONS
ANDROID
@@ -206,6 +207,7 @@ CONFIG_WOLFSSL_TLS_VERSION_1_3
CONFIG_WOLFTPM
CONFIG_WOLFTPM_EXAMPLE_NAME_ESPRESSIF
CONFIG_X86
+CONFIG_X86_32
CONV_WITH_DIV
CPA_CY_API_VERSION_NUM_MAJOR
CPA_CY_API_VERSION_NUM_MINOR
@@ -257,6 +259,7 @@ ETHERNET_H
EV_TRIGGER
EXTERNAL_LOADER_APP
FD_CLOEXEC
+FIPS_CODE_REVIEW
FIPS_OPTEST_FULL_RUN_AT_MODULE_INIT
FORCE_FAILURE_GETRANDOM
FP_ECC_CONTROL
@@ -724,6 +727,8 @@ WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS
WC_CACHE_RESISTANT_BASE64_TABLE
WC_DISABLE_RADIX_ZERO_PAD
WC_FIPS_AESGCM_ONE_SHOT_EXT_IV_ALLOWED
+WC_FIPS_ED25519CTX_NOT_APPROVED
+WC_FIPS_RSA_VERIFY_MIN_2048
WC_FLAG_DONT_USE_AESNI
WC_FORCE_LINUXKM_FORTIFY_SOURCE
WC_HASH_CUSTOM_MAX_BLOCK_SIZE
diff --git a/IDE/WIN-PQ-FIPSv7/README.txt b/IDE/WIN-PQ-FIPSv7/README.txt
new file mode 100644
index 0000000000..b7a67487de
--- /dev/null
+++ b/IDE/WIN-PQ-FIPSv7/README.txt
@@ -0,0 +1,121 @@
+# Notes on the wolfSSL FIPS 140-3 v7.0.0 (Post-Quantum) Windows project
+
+First, if you did not get the FIPS files with your archive, you must contact
+wolfSSL to obtain them.
+
+The IDE/WIN-PQ-FIPSv7/wolfssl-fips.sln solution is for the FIPS 140-3 v7.0.0
+module. In addition to the classical algorithms it includes the post-quantum
+algorithms ML-KEM (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205) and the
+stateful hash-based signatures LMS and XMSS (SP 800-208).
+
+# Building the wolfssl-fips project
+
+The wolfCrypt FIPS library for Windows is a part of the wolfSSL library. It
+must be built as a static library, for the moment.
+
+The library project is built with Whole Program Optimization disabled. This is
+required so that necessary components of the library are not optimized away.
+There are two functions added to the library that are used as markers in
+memory for the in-core memory check of the code. WPO consolidates them into a
+single function. WPO also optimizes away the automatic FIPS entry function.
+
+Each of the source files inside the FIPS boundary defines their own code and
+constant section. The code section names start with ".fipsA$" and the constant
+section names start with ".fipsB$". Each subsection has a letter to organize
+them in a specific order. This specific ordering puts marker functions and
+constants on either end of the boundary so it can be hashed.
+
+
+# In Core Memory Test
+
+The In Core Memory test calculates a checksum (HMAC-SHA512 for the v7.0.0
+module) of the wolfCrypt FIPS library code and constant data and compares it
+with a known value in the code.
+
+The following wolfCrypt FIPS project linker settings are required for the DLL Win32 configuration:
+1) The [Randomized Base Address setting (ASLR)](https://learn.microsoft.com/en-us/cpp/build/reference/dynamicbase-use-address-space-layout-randomization?view=msvc-170)
+needs to be disabled on all builds as the feature throws off the in-core memory calculation causing the test to fail.
+2) The [Incremental Link](https://learn.microsoft.com/en-us/cpp/build/reference/incremental-link-incrementally?view=msvc-170)
+option need turned off so function pointers go to actual code, not a jump instruction.
+3) The [FixedBaseAddress](https://learn.microsoft.com/en-us/cpp/build/reference/fixed-fixed-base-address?view=msvc-170)
+option to YES, which disables the support for ASLR.
+
+The "verifyCore" check value in the source fips_test.c needs to be updated when
+building the code. The POS performs this check and the default failure callback
+will print out the calculated checksum. When developing your code, copy this
+value and paste it back into your code in the verifyCore initializer then
+rebuild the code. When statically linking, you may have to recalculate your
+check value when changing your application.
+
+# Build Options
+
+The default build options should be the proper default set of options:
+
+ * HAVE_FIPS
+ * HAVE_FIPS_VERSION=7
+ * HAVE_FIPS_VERSION_MINOR=0
+ * HAVE_THREAD_LS
+ * WOLFSSL_KEY_GEN
+ * HAVE_AESGCM
+ * HAVE_HASHDRBG
+ * WOLFSSL_SHA384
+ * WOLFSSL_SHA512
+ * NO_PSK
+ * NO_RC4
+ * NO_DSA
+ * NO_MD4
+ * WOLFSSL_SHA224
+ * WOLFSSL_SHA3
+ * WC_RSA_PSS
+ * WC_RSA_NO_PADDING
+ * HAVE_ECC
+ * ECC_SHAMIR
+ * HAVE_ECC_CDH
+ * ECC_TIMING_RESISTANT
+ * TFM_TIMING_RESISTANT
+ * WOLFSSL_AES_COUNTER
+ * WOLFSSL_AES_DIRECT
+ * HAVE_AES_ECB
+ * HAVE_AESCCM
+ * WOLFSSL_CMAC
+ * HAVE_HKDF
+ * WOLFSSL_VALIDATE_ECC_IMPORT
+ * WOLFSSL_VALIDATE_FFC_IMPORT
+ * HAVE_FFDHE_Q
+ * NO_DES
+ * NO_DES3
+ * NO_MD5
+ * NO_OLD_TLS
+ * WOLFSSL_TLS13
+ * HAVE_TLS_EXTENSIONS
+ * HAVE_SUPPORTED_CURVES
+ * GCM_TABLE_4BIT
+ * WOLFSSL_NO_SHAKE256
+ * WOLFSSL_VALIDATE_ECC_KEYGEN
+ * WOLFSSL_ECDSA_SET_K
+ * WOLFSSL_WOLFSSH
+ * WOLFSSL_PUBLIC_MP
+ * WC_RNG_SEED_CB
+ * TFM_ECC256
+ * ECC_USER_CURVES
+ * HAVE_ECC192
+ * HAVE_ECC224
+ * HAVE_ECC256
+ * HAVE_ECC384
+ * HAVE_ECC521
+ * HAVE_FFDHE_2048
+ * HAVE_FFDHE_3072
+ * HAVE_FFDHE_4096
+ * HAVE_FFDHE_6144
+ * HAVE_FFDHE_8192
+ * FP_MAX_BITS 16384
+
+The "NO" options explicitly disable algorithms that are not allowed in
+FIPS mode.
+
+Additionally one may enable:
+
+ * WOLFSSL_AESNI
+ * OPENSSL_EXTRA
+
+These settings are defined in IDE/WIN-PQ-FIPSv7/user_settings.h.
diff --git a/IDE/WIN-PQ-FIPSv7/include.am b/IDE/WIN-PQ-FIPSv7/include.am
new file mode 100644
index 0000000000..e66535d5eb
--- /dev/null
+++ b/IDE/WIN-PQ-FIPSv7/include.am
@@ -0,0 +1,11 @@
+# vim:ft=automake
+# included from Top Level Makefile.am
+# All paths should be given relative to the root
+
+EXTRA_DIST+= IDE/WIN-PQ-FIPSv7/README.txt
+EXTRA_DIST+= IDE/WIN-PQ-FIPSv7/test.vcxproj
+EXTRA_DIST+= IDE/WIN-PQ-FIPSv7/wolfssl-fips.sln
+EXTRA_DIST+= IDE/WIN-PQ-FIPSv7/wolfssl-fips.vcxproj
+EXTRA_DIST+= IDE/WIN-PQ-FIPSv7/user_settings.h
+EXTRA_DIST+= IDE/WIN-PQ-FIPSv7/resource.h
+EXTRA_DIST+= IDE/WIN-PQ-FIPSv7/wolfssl-fips.rc
diff --git a/IDE/WIN-PQ-FIPSv7/resource.h b/IDE/WIN-PQ-FIPSv7/resource.h
new file mode 100644
index 0000000000..691fa76544
--- /dev/null
+++ b/IDE/WIN-PQ-FIPSv7/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by wolfssl-fips.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/IDE/WIN-PQ-FIPSv7/test.vcxproj b/IDE/WIN-PQ-FIPSv7/test.vcxproj
new file mode 100644
index 0000000000..59f3310353
--- /dev/null
+++ b/IDE/WIN-PQ-FIPSv7/test.vcxproj
@@ -0,0 +1,291 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ DLL Debug
+ Win32
+
+
+ DLL Debug
+ x64
+
+
+ DLL Release
+ Win32
+
+
+ DLL Release
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}
+ Win32Proj
+
+
+
+ Application
+ v142
+
+
+ Application
+ v142
+
+
+ Application
+ v142
+
+
+ Application
+ v142
+
+
+ Application
+ v142
+
+
+ Application
+ v142
+
+
+ Application
+ v142
+
+
+ Application
+ v142
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>11.0.61030.0
+
+
+ $(SolutionDir)$(Configuration)\$(Platform)\
+ $(Configuration)\$(Platform)\$(ProjectName)_obj\
+ false
+
+
+
+ Disabled
+ .\;..\..\;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;_CONSOLE;USE_CERT_BUFFERS_2048;USE_CERT_BUFFERS_256;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+ Level3
+ ProgramDatabase
+
+
+ true
+ Console
+ MachineX86
+ ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ false
+
+
+
+
+ Disabled
+ .\;..\..\;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;_CONSOLE;USE_CERT_BUFFERS_2048;USE_CERT_BUFFERS_256;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+ Level3
+ ProgramDatabase
+
+
+ false
+ true
+ true
+ Console
+ ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+ .\;..\..\;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;_CONSOLE;USE_CERT_BUFFERS_2048;USE_CERT_BUFFERS_256;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+ Level3
+ ProgramDatabase
+ true
+
+
+ true
+ Console
+ MachineX86
+ ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ true
+ true
+ UseLinkTimeCodeGeneration
+
+ false
+ true
+
+
+
+
+ .\;..\..\;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;_CONSOLE;USE_CERT_BUFFERS_2048;USE_CERT_BUFFERS_256;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+ Level3
+ ProgramDatabase
+ true
+
+
+ true
+
+ false
+ true
+ Console
+ ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ true
+ true
+ UseLinkTimeCodeGeneration
+
+
+
+
+ Disabled
+ .\;..\..\;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;_CONSOLE;USE_CERT_BUFFERS_2048;USE_CERT_BUFFERS_256;WOLFSSL_USER_SETTINGS;WOLFSSL_DLL;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+ Level3
+ ProgramDatabase
+ false
+
+
+ false
+ true
+ true
+ Console
+ MachineX86
+ ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+ Disabled
+ .\;..\..\;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;_CONSOLE;USE_CERT_BUFFERS_2048;USE_CERT_BUFFERS_256;WOLFSSL_USER_SETTINGS;CYASSL_DLL;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+ Level3
+ ProgramDatabase
+ false
+
+
+ true
+ Console
+ ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ false
+
+
+
+
+ .\;..\..\;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;_CONSOLE;USE_CERT_BUFFERS_2048;USE_CERT_BUFFERS_256;WOLFSSL_USER_SETTINGS;WOLFSSL_DLL;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+ Level3
+ ProgramDatabase
+
+
+ false
+ true
+ true
+ Console
+ ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ true
+ true
+ MachineX86
+ UseLinkTimeCodeGeneration
+
+
+
+
+ .\;..\..\;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;_CONSOLE;USE_CERT_BUFFERS_2048;USE_CERT_BUFFERS_256;WOLFSSL_USER_SETTINGS;WOLFSSL_DLL;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+ Level3
+ ProgramDatabase
+
+
+ false
+ true
+ true
+ Console
+ ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ true
+ true
+ UseLinkTimeCodeGeneration
+
+
+
+
+
+
+
+ {73973223-5ee8-41ca-8e88-1d60e89a237b}
+ false
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IDE/WIN-PQ-FIPSv7/user_settings.h b/IDE/WIN-PQ-FIPSv7/user_settings.h
new file mode 100644
index 0000000000..dec53d8c9f
--- /dev/null
+++ b/IDE/WIN-PQ-FIPSv7/user_settings.h
@@ -0,0 +1,276 @@
+#ifndef _WIN_USER_SETTINGS_H_
+#define _WIN_USER_SETTINGS_H_
+
+/* For FIPS 140-2 3389 build set to "#if 1" */
+#if 0
+#undef HAVE_FIPS
+#define HAVE_FIPS
+#undef HAVE_FIPS_VERSION
+#define HAVE_FIPS_VERSION 2
+#undef HAVE_FIPS_VERSION_MINOR
+#define HAVE_FIPS_VERSION_MINOR 0
+#endif
+
+/* Set the following to 1 for WCv5.0-RC12 build. */
+#if 1 /* wolfSSL FIPS 140-3 v7.0.0 PQ module (Windows MSVC) */
+#undef HAVE_FIPS
+#define HAVE_FIPS
+#undef HAVE_FIPS_VERSION
+#define HAVE_FIPS_VERSION 7
+#undef HAVE_FIPS_VERSION_MAJOR
+#define HAVE_FIPS_VERSION_MAJOR 7
+#undef HAVE_FIPS_VERSION_MINOR
+#define HAVE_FIPS_VERSION_MINOR 0
+#undef HAVE_FIPS_VERSION_PATCH
+#define HAVE_FIPS_VERSION_PATCH 0
+/* FIPS Ready, matching the Linux validated options.h: settings.h then forces
+ * HAVE_FIPS_VERSION 7 and selects FIPS 186-4, as the Linux module does. */
+#define WOLFSSL_FIPS_READY
+#endif
+
+/* ===== Operational test (optest) build toggle =====
+ * Define OPTEST_BUILD for the optest variant (MD5 + force-failure injection +
+ * verbose FIPS logging). Leave UNDEFINED for production. */
+/* #define OPTEST_BUILD */ /* OFF */
+#ifdef OPTEST_BUILD
+ #define HAVE_FORCE_FIPS_FAILURE
+ #define DEBUG_FIPS_VERBOSE
+ /* MSVC C has no C99 VLAs; the optest test.c sizes buffers with `const`
+ * variables -> route them to the heap (XMALLOC) via WOLFSSL_SMALL_STACK. */
+ #define WOLFSSL_SMALL_STACK
+ #define NO_MAIN_DRIVER
+ #define USE_CERT_BUFFERS_2048
+ #define USE_CERT_BUFFERS_256
+ #define OPTEST_LOGGING_ENABLED
+ #define OPTEST_INVALID_LOGGING_ENABLED
+ #define OPTEST_LOG_TE_MAPPING
+#endif
+
+/* ===== wolfACVP harness build toggle =====
+ * The harness needs heap-routed buffers and the embedded cert/key buffers, but
+ * not the optest force-failure/verbose logs. Exclusive with OPTEST_BUILD. */
+#define HARNESS_BUILD /* ON (OPTEST_BUILD must stay OFF) */
+#ifdef HARNESS_BUILD
+ #define WOLFSSL_SMALL_STACK
+ #define USE_CERT_BUFFERS_2048
+ #define USE_CERT_BUFFERS_256
+#endif
+
+/* x86_64 AES-NI PAA toggle. Default OFF = pure-C AES, matching the Linux
+ * validated options.h; x64 only, Win32 stays pure-C. */
+/* #define WOLFSSL_USE_AESNI_PAA */
+
+
+/* Verify this is Windows */
+#ifndef _WIN32
+#error This user_settings.h header is only designed for Windows
+#endif
+
+/* Configurations */
+#if defined(HAVE_FIPS)
+ /* FIPS */
+ #define OPENSSL_EXTRA
+ #define HAVE_THREAD_LS
+ #define WOLFSSL_KEY_GEN
+ #define HAVE_AESGCM
+ #define HAVE_HASHDRBG
+ #define WOLFSSL_SHA384
+ #define WOLFSSL_SHA512
+ #define NO_PSK
+ #define NO_RC4
+ #define NO_DSA
+ #define NO_MD4
+
+ #if defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)
+ #define WOLFSSL_SHA224
+ #define WOLFSSL_SHA3
+ #define WC_RSA_PSS
+ #define WC_RSA_NO_PADDING
+ #define HAVE_ECC
+ #define HAVE_ECC384
+ #define HAVE_ECC521
+ #define HAVE_SUPPORTED_CURVES
+ #define HAVE_TLS_EXTENSIONS
+ #define ECC_SHAMIR
+ #define HAVE_ECC_CDH
+ #define ECC_TIMING_RESISTANT
+ #define TFM_TIMING_RESISTANT
+ #define WOLFSSL_AES_COUNTER
+ #define WOLFSSL_AES_DIRECT
+ #define HAVE_AES_ECB
+ #define HAVE_AESCCM
+ #define WOLFSSL_CMAC
+ #define HAVE_HKDF
+ #define WOLFSSL_VALIDATE_ECC_IMPORT
+ #define WOLFSSL_VALIDATE_FFC_IMPORT
+ #define HAVE_FFDHE_Q
+ #ifdef _WIN64
+ #define WOLFSSL_AESNI
+ #endif
+ #endif /* FIPS v2 */
+ #if defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 5)
+ #define NO_DES
+ #define NO_DES3
+ /* MD5 stays disabled for production; the optest build enables it to
+ * prove module isolation. MD5 is outside the in-core boundary. */
+ #ifndef OPTEST_BUILD
+ #define NO_MD5
+ #endif
+ #define NO_OLD_TLS
+ #define WOLFSSL_TLS13
+ #define HAVE_TLS_EXTENSIONS
+ #define HAVE_SUPPORTED_CURVES
+ #define GCM_TABLE_4BIT
+ #define WOLFSSL_NO_SHAKE256
+ #define WOLFSSL_VALIDATE_ECC_KEYGEN
+ #define WOLFSSL_ECDSA_SET_K
+ /* Match Linux options.h: limits the all-zero digest rejection to
+ * deterministic-K signing, so random-k ECDSA over it still succeeds. */
+ #define WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT
+ #define WOLFSSL_WOLFSSH
+ #define WOLFSSL_PUBLIC_MP
+ #define WC_RNG_SEED_CB
+ /* v7 uses SP math instead of TFM/fast-math, matching the Linux
+ * validated options.h. settings.h auto-#undefs USE_FAST_MATH. */
+ #define WOLFSSL_SP_MATH_ALL
+ #define WOLFSSL_SP_INT_NEGATIVE
+ #define SP_INT_BITS 8192
+ #define ECC_USER_CURVES
+ /* Allow P-192/P-224 in FIPS mode (matches Linux options.h); without
+ * this the FIPS default min key size rejects P-224 -> ECC test -170. */
+ #define ECC_MIN_KEY_SZ 192
+ #define HAVE_ECC192
+ #define HAVE_ECC224
+ #define HAVE_ECC256
+ #define HAVE_ECC384
+ #define HAVE_ECC521
+ #define HAVE_FFDHE_2048
+ #define HAVE_FFDHE_3072
+ #define HAVE_FFDHE_4096
+ #define HAVE_FFDHE_6144
+ #define HAVE_FFDHE_8192
+ #define WOLFSSL_AES_OFB
+ #define FP_MAX_BITS 16384
+ #endif /* FIPS v5 */
+ #if defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 6)
+ #ifndef WOLFSSL_USE_AESNI_PAA
+ #undef WOLFSSL_AESNI /* default OFF (pure-C, match Linux) */
+ #endif
+ #define HAVE_ED25519
+ /* Curve25519/Curve448 (X25519/X448) are NOT in the v7 module: Linux
+ * options.h defines only Ed25519/Ed448. */
+ #define WOLFSSL_ED25519_STREAMING_VERIFY
+ #define HAVE_ED25519_KEY_IMPORT
+ #define HAVE_ED448
+ #define HAVE_ED448_KEY_IMPORT
+ #define WOLFSSL_ED448_STREAMING_VERIFY
+ #undef WOLFSSL_NO_SHAKE256
+ #define WOLFSSL_SHAKE256
+ #define WOLFSSL_SHAKE128
+ #define WOLFSSL_AES_CFB
+ #define WOLFSSL_AES_XTS
+ #define WOLFSSL_AESXTS_STREAM
+ #define WOLFSSL_AESGCM_STREAM
+ #define HAVE_AES_KEYWRAP
+ #define WC_SRTP_KDF
+ #define HAVE_PBKDF2
+ #define WOLFCRYPT_FIPS_CORE_HASH_VALUE \
+ AE8F969C072FB4A87B5C594F96162002F3CCEB6026BDB2553C8621AE197F7059 //woPAA
+ //E257E8C21764333E4710316D208A90D4ECA0682D6F40DC3F4A6E259D4752E306 //wPAA
+ /* SHA-512/224 and SHA-512/256 are approved v7 algorithms and the
+ * wolfACVP harness references them, so they must NOT be disabled. */
+ /* #define WOLFSSL_NOSHA512_224 */
+ /* #define WOLFSSL_NOSHA512_256 */
+
+ /* uncomment for FIPS debugging */
+ /* #define DEBUG_FIPS_VERBOSE */
+
+ /* uncomment for whole library debugging */
+ /* #define DEBUG_WOLFSSL */
+ #endif /* FIPS v6 */
+ #if defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 7)
+ /* v7.0.0 adds the post-quantum algorithms (FIPS 203/204/205,
+ * SP 800-208) + the SHA-512 Hash_DRBG (SP 800-90A). */
+ /* Classic finite-field DH is retired in the v7 module: the DH FIPS
+ * wrappers and the DH CAST are gone. */
+ #define NO_DH
+ #define WOLFSSL_HAVE_MLKEM /* ML-KEM (FIPS 203) */
+ #define WOLFSSL_TLS_NO_MLKEM_STANDALONE
+ #define WOLFSSL_PQC_HYBRIDS
+ #define WOLFSSL_HAVE_MLDSA /* ML-DSA (FIPS 204) */
+ #define WOLFSSL_HAVE_LMS /* LMS (SP 800-208) */
+ #define WOLFSSL_LMS_SHA256_192
+ #define WOLFSSL_LMS_SHAKE256
+ #define WOLFSSL_HAVE_XMSS /* XMSS (SP 800-208) */
+ #define WOLFSSL_HAVE_SLHDSA /* SLH-DSA (FIPS 205) */
+ #define WOLFSSL_WC_SLHDSA
+ #define WOLFSSL_SLHDSA_PARAM_128S
+ #define WOLFSSL_SLHDSA_PARAM_128F
+ #define WOLFSSL_SLHDSA_PARAM_192S
+ #define WOLFSSL_SLHDSA_PARAM_192F
+ #define WOLFSSL_SLHDSA_PARAM_256S
+ #define WOLFSSL_SLHDSA_PARAM_256F
+ #define WOLFSSL_SLHDSA_SHA2
+ #define WOLFSSL_SLHDSA_PARAM_SHA2_128S
+ #define WOLFSSL_SLHDSA_PARAM_SHA2_128F
+ #define WOLFSSL_SLHDSA_PARAM_SHA2_192S
+ #define WOLFSSL_SLHDSA_PARAM_SHA2_192F
+ #define WOLFSSL_SLHDSA_PARAM_SHA2_256S
+ #define WOLFSSL_SLHDSA_PARAM_SHA2_256F
+ #define WOLFSSL_DRBG_SHA512 /* SHA-512 Hash_DRBG (SP 800-90A) */
+
+ /* Leave WOLFCRYPT_FIPS_CORE_HASH_VALUE undefined for v7 (undo the v6
+ * block above) so fips_test.c uses its verifyCore[] placeholder. */
+ #undef WOLFCRYPT_FIPS_CORE_HASH_VALUE
+ #endif /* FIPS v7 */
+#else
+ /* Enables blinding mode, to prevent timing attacks */
+ #define WC_RSA_BLINDING
+
+ #if defined(WOLFSSL_LIB)
+ /* The lib */
+ #define OPENSSL_EXTRA
+ #define WOLFSSL_RIPEMD
+ #define NO_PSK
+ #define HAVE_EXTENDED_MASTER
+ #define WOLFSSL_SNIFFER
+ #define HAVE_SECURE_RENEGOTIATION
+
+ #define HAVE_AESGCM
+ #define WOLFSSL_SHA384
+ #define WOLFSSL_SHA512
+
+ #define HAVE_SUPPORTED_CURVES
+ #define HAVE_TLS_EXTENSIONS
+
+ #define HAVE_ECC
+ #define ECC_SHAMIR
+ #define ECC_TIMING_RESISTANT
+ #else
+ /* The servers and clients */
+ #define OPENSSL_EXTRA
+ #define NO_PSK
+ #endif
+#endif /* HAVE_FIPS */
+
+/* For optesting and code review and harness/vector processing */
+#if 0
+ #undef USE_CERT_BUFFERS_2048
+ #define USE_CERT_BUFFERS_2048
+
+ #undef USE_CERT_BUFFERS_256
+ #define USE_CERT_BUFFERS_256
+
+ #define NO_MAIN_DRIVER
+ #define HAVE_FORCE_FIPS_FAILURE
+ #define OPTEST_LOGGING_ENABLED
+ #define OPTEST_INVALID_LOGGING_ENABLED
+ #define DEBUG_FIPS_VERBOSE
+ #define OPTEST_RUNNING_ORGANIC
+ #define DEBUG_WOLFSSL
+ #define OPTEST_LOG_TE_MAPPING
+ #define DEEPLY_EMBEDDED
+ #define WORKING_WITH_AEGISOLVE
+#endif /* 1 || 0 */
+
+#endif /* _WIN_USER_SETTINGS_H_ */
diff --git a/IDE/WIN-PQ-FIPSv7/wolfssl-fips.rc b/IDE/WIN-PQ-FIPSv7/wolfssl-fips.rc
new file mode 100644
index 0000000000..449cdb49ef
--- /dev/null
+++ b/IDE/WIN-PQ-FIPSv7/wolfssl-fips.rc
@@ -0,0 +1,100 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,9,2,0
+ PRODUCTVERSION 5,9,2,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "wolfSSL Inc."
+ VALUE "FileDescription", "The wolfSSL FIPS embedded SSL library is a lightweight, portable, C-language-based SSL/TLS library targeted at IoT, embedded, and RTOS environments primarily because of its size, speed, and feature set."
+ VALUE "FileVersion", "5.9.2.0"
+ VALUE "InternalName", "wolfssl-fips"
+ VALUE "LegalCopyright", "Copyright (C) 2023"
+ VALUE "OriginalFilename", "wolfssl-fips.dll"
+ VALUE "ProductName", "wolfSSL FIPS"
+ VALUE "ProductVersion", "5.9.2.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/IDE/WIN-PQ-FIPSv7/wolfssl-fips.sln b/IDE/WIN-PQ-FIPSv7/wolfssl-fips.sln
new file mode 100644
index 0000000000..1609e6d91e
--- /dev/null
+++ b/IDE/WIN-PQ-FIPSv7/wolfssl-fips.sln
@@ -0,0 +1,61 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.32510.428
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfssl-fips", "wolfssl-fips.vcxproj", "{73973223-5EE8-41CA-8E88-1D60E89A237B}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{D04BDF66-664A-4D59-BEAC-8AB2D5809C21}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ DLL Debug|Win32 = DLL Debug|Win32
+ DLL Debug|x64 = DLL Debug|x64
+ DLL Release|Win32 = DLL Release|Win32
+ DLL Release|x64 = DLL Release|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug|Win32.Build.0 = Debug|Win32
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug|x64.ActiveCfg = Debug|x64
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug|x64.Build.0 = Debug|x64
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.DLL Debug|Win32.ActiveCfg = DLL Debug|Win32
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.DLL Debug|Win32.Build.0 = DLL Debug|Win32
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.DLL Debug|x64.ActiveCfg = DLL Debug|x64
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.DLL Debug|x64.Build.0 = DLL Debug|x64
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.DLL Release|Win32.ActiveCfg = DLL Release|Win32
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.DLL Release|Win32.Build.0 = DLL Release|Win32
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.DLL Release|x64.ActiveCfg = DLL Release|x64
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.DLL Release|x64.Build.0 = DLL Release|x64
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.Release|Win32.ActiveCfg = Release|Win32
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.Release|Win32.Build.0 = Release|Win32
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.Release|x64.ActiveCfg = Release|x64
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}.Release|x64.Build.0 = Release|x64
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.Debug|Win32.Build.0 = Debug|Win32
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.Debug|x64.ActiveCfg = Debug|x64
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.Debug|x64.Build.0 = Debug|x64
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.DLL Debug|Win32.ActiveCfg = DLL Debug|Win32
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.DLL Debug|Win32.Build.0 = DLL Debug|Win32
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.DLL Debug|x64.ActiveCfg = DLL Debug|x64
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.DLL Debug|x64.Build.0 = DLL Debug|x64
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.DLL Release|Win32.ActiveCfg = DLL Release|Win32
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.DLL Release|Win32.Build.0 = DLL Release|Win32
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.DLL Release|x64.ActiveCfg = DLL Release|x64
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.DLL Release|x64.Build.0 = DLL Release|x64
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.Release|Win32.ActiveCfg = Release|Win32
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.Release|Win32.Build.0 = Release|Win32
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.Release|x64.ActiveCfg = Release|x64
+ {D04BDF66-664A-4D59-BEAC-8AB2D5809C21}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0CFCC869-45D3-42AD-BA73-29938093A38F}
+ EndGlobalSection
+EndGlobal
diff --git a/IDE/WIN-PQ-FIPSv7/wolfssl-fips.vcxproj b/IDE/WIN-PQ-FIPSv7/wolfssl-fips.vcxproj
new file mode 100644
index 0000000000..e042356e8d
--- /dev/null
+++ b/IDE/WIN-PQ-FIPSv7/wolfssl-fips.vcxproj
@@ -0,0 +1,404 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ DLL Debug
+ Win32
+
+
+ DLL Debug
+ x64
+
+
+ DLL Release
+ Win32
+
+
+ DLL Release
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {73973223-5EE8-41CA-8E88-1D60E89A237B}
+ wolfssl-fips
+ Win32Proj
+
+
+
+ StaticLibrary
+ v143
+ Unicode
+ true
+
+
+ DynamicLibrary
+ v143
+ Unicode
+ true
+
+
+ StaticLibrary
+ v143
+ Unicode
+ true
+
+
+ DynamicLibrary
+ v143
+ Unicode
+ true
+
+
+ StaticLibrary
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ v143
+ Unicode
+
+
+ StaticLibrary
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ v143
+ Unicode
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(SolutionDir)$(Configuration)\$(Platform)\
+ $(Configuration)\$(Platform)\$(ProjectName)_obj\
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ Disabled
+ $(SolutionDir)XXX-fips-test\IDE\WIN-PQ-FIPSv7;$(SolutionDir)XXX-fips-test;%(AdditionalIncludeDirectories)
+ WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+ Level4
+ ProgramDatabase
+ 4206;4214;4706;%(DisableSpecificWarnings)
+
+
+
+
+ Disabled
+ $(SolutionDir)XXX-fips-test\IDE\WIN-PQ-FIPSv7;$(SolutionDir)XXX-fips-test;%(AdditionalIncludeDirectories)
+ BUILDING_WOLFSSL;WOLFSSL_DLL;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+ Level4
+ ProgramDatabase
+ 4206;4214;4706;%(DisableSpecificWarnings)
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ 0x5A000000
+ false
+ false
+ true
+
+
+
+
+ Disabled
+ $(SolutionDir)XXX-fips-test\IDE\WIN-PQ-FIPSv7;$(SolutionDir)XXX-fips-test;%(AdditionalIncludeDirectories)
+ WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+ Level4
+ ProgramDatabase
+ 4206;4214;4706;%(DisableSpecificWarnings)
+
+
+
+
+ Disabled
+ $(SolutionDir)XXX-fips-test\IDE\WIN-PQ-FIPSv7;$(SolutionDir)XXX-fips-test;%(AdditionalIncludeDirectories)
+ BUILDING_WOLFSSL;WOLFSSL_DLL;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+ Level4
+ ProgramDatabase
+ 4206;4214;4706;%(DisableSpecificWarnings)
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ 0x5A000000
+ false
+ false
+ true
+
+
+
+
+ MaxSpeed
+ true
+ $(SolutionDir)XXX-fips-test\IDE\WIN-PQ-FIPSv7;$(SolutionDir)XXX-fips-test;%(AdditionalIncludeDirectories)
+ WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ true
+ Level3
+ ProgramDatabase
+ false
+
+
+
+
+ MaxSpeed
+ true
+ $(SolutionDir)XXX-fips-test\IDE\WIN-PQ-FIPSv7;$(SolutionDir)XXX-fips-test;%(AdditionalIncludeDirectories)
+ BUILDING_WOLFSSL;WOLFSSL_DLL;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ true
+ Level3
+ ProgramDatabase
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ false
+ 0x5A000000
+ true
+
+
+
+
+ MaxSpeed
+ true
+ $(SolutionDir)XXX-fips-test\IDE\WIN-PQ-FIPSv7;$(SolutionDir)XXX-fips-test;%(AdditionalIncludeDirectories)
+ WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ true
+ Level3
+ ProgramDatabase
+ false
+
+
+
+
+ MaxSpeed
+ true
+ $(SolutionDir)XXX-fips-test\IDE\WIN-PQ-FIPSv7;$(SolutionDir)XXX-fips-test;%(AdditionalIncludeDirectories)
+ BUILDING_WOLFSSL;WOLFSSL_DLL;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ true
+ Level3
+ ProgramDatabase
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ false
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+ false
+ false
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+
+
+
+
+ false
+ false
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+ false
+ false
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+
+
+ false
+ false
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+ false
+ false
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+
+
+
+
+
+
+
+
+
+
+
+ false
+ false
+ false
+ false
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ ml64.exe /DHAVE_FIPS /DHAVE_FIPS_VERSION=7 /c /Zi /Fo"$(IntDir)%(Filename).obj" %(Identity)
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+ $(IntDir)%(Filename).obj
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IDE/WIN10/test.vcxproj b/IDE/WIN10/test.vcxproj
index 36657348db..2de83ba3f6 100644
--- a/IDE/WIN10/test.vcxproj
+++ b/IDE/WIN10/test.vcxproj
@@ -138,10 +138,11 @@
ProgramDatabase
+ false
+ true
true
Console
ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
- true
@@ -176,6 +177,10 @@
true
+
+ false
+ true
true
Console
ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
@@ -197,11 +202,12 @@
false
+ false
+ true
true
Console
MachineX86
ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
- true
@@ -233,6 +239,8 @@
ProgramDatabase
+ false
+ true
true
Console
ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
@@ -240,7 +248,6 @@
true
MachineX86
UseLinkTimeCodeGeneration
- true
@@ -253,13 +260,14 @@
ProgramDatabase
+ false
+ true
true
Console
ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
true
true
UseLinkTimeCodeGeneration
- true
diff --git a/IDE/include.am b/IDE/include.am
index e70c06fb5a..11d6abb44e 100644
--- a/IDE/include.am
+++ b/IDE/include.am
@@ -51,6 +51,7 @@ EXTRA_DIST+= IDE/VS-ARM
EXTRA_DIST+= IDE/VS-AZURE-SPHERE
EXTRA_DIST+= IDE/WICED-STUDIO
EXTRA_DIST+= IDE/WIN
+EXTRA_DIST+= IDE/WIN-PQ-FIPSv7
EXTRA_DIST+= IDE/WIN-SGX
EXTRA_DIST+= IDE/WIN-SRTP-KDF-140-3
EXTRA_DIST+= IDE/WIN10
diff --git a/configure.ac b/configure.ac
index 21a2a808b3..ca042f6ed8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1302,6 +1302,16 @@ AC_ARG_ENABLE([all],
[ ENABLED_ALL=$enableval ],
[ ENABLED_ALL=no ]
)
+
+# --enable-all turns on algorithms and options that sit outside the validated
+# FIPS module boundary (e.g. deterministic ECDSA per RFC 6979, which has no
+# CAST), so it must not be combined with a validation-targeted FIPS build.
+# Only the in-development 'dev' flavor is exempt.
+AS_IF([test "$ENABLED_ALL" = "yes"],
+ [AS_CASE([$FIPS_VERSION],
+ [""|none|disabled|dev], [],
+ [AC_MSG_ERROR([--enable-all cannot be combined with --enable-fips=$FIPS_VERSION: it enables algorithms outside the FIPS module boundary. Use --enable-fips=dev for development, or drop --enable-all.])])])
+
if test "$ENABLED_ALL" = "yes"
then
test "$enable_all_crypto" = "" && enable_all_crypto=yes
@@ -1564,6 +1574,11 @@ fi
# have dedicated streaming assembly (AES_GCM_*_RISCV64). (Previously disabled
# here.)
+# Capture explicit --enable-dh/--enable-dsa before any default sets them: the
+# v7 out-of-scope guard hard-errors only on an explicit request.
+explicit_enable_dsa="$enable_dsa"
+explicit_enable_dh="$enable_dh"
+
# All wolfCrypt features except quantum-resistant asymmetric:
AC_ARG_ENABLE([all-crypto],
[AS_HELP_STRING([--enable-all-crypto],[Enable all wolfcrypt algorithms except quantum-resistant asymmetric (default: disabled)])],
@@ -4205,13 +4220,15 @@ then
AC_MSG_NOTICE([32bit ARMv4 found])
;;
*)
- AM_CPPFLAGS="$AM_CPPFLAGS -mfpu=crypto-neon-fp-armv8 -marm"
+ # ARMv8-A crypto extensions are OPTIONAL, so the armv8-32-*-asm.S
+ # crypto instructions need an explicit -march=armv8-a+crypto.
+ AM_CPPFLAGS="$AM_CPPFLAGS -march=armv8-a+crypto -mfpu=crypto-neon-fp-armv8 -marm"
# Include options.h
AM_CCASFLAGS="$AM_CCASFLAGS -DEXTERNAL_OPTS_OPENVPN"
ENABLED_ARMASM_CRYPTO=yes
ENABLED_ARMASM_NEON=yes
ENABLED_ARM_32=yes
- AC_MSG_NOTICE([32bit ARMv8 found, setting mfpu to crypto-neon-fp-armv8])
+ AC_MSG_NOTICE([32bit ARMv8 found, setting -march=armv8-a+crypto + mfpu=crypto-neon-fp-armv8])
;;
esac
esac
@@ -6292,6 +6309,10 @@ if (test "x$ENABLED_WPAS" = "xyes" || test "x$ENABLED_NGINX" = "xyes" || \
then
ENABLED_ANON=yes
fi
+# Anonymous ciphers require classic DH, which is out of scope for the locked
+# FIPS 140-3 v7 module, so force anon off there however it was enabled.
+AS_IF([test "$FIPS_VERSION" = "v7"],
+ [ENABLED_ANON=no])
if test "x$ENABLED_ANON" = "xyes"
then
if test "$ENABLED_DH" = "no"
@@ -6850,6 +6871,23 @@ AS_CASE([$FIPS_VERSION],
-DHAVE_ECC256"])
DEFAULT_MAX_CLASSIC_ASYM_KEY_BITS=8192
+
+# Classic DH/DSA scope by FIPS mode: v7 puts them out of scope (hard-error on an
+# explicit --enable-dh/--enable-dsa); dev/ready default off but allow opt-in.
+ AS_IF([test "$FIPS_VERSION" = "v7"],
+ [AS_IF([test "$explicit_enable_dh" = "yes"],
+ [AC_MSG_ERROR([--enable-dh is not supported with --enable-fips=v7. Classic finite-field DH is out of scope for the FIPS 140-3 v7 PQ module. Use --enable-fips=v6 if you need DH support.])],
+ [test "$ENABLED_DH" != "no"],
+ [ENABLED_DH="no"; enable_dh="no"; AM_CFLAGS="$AM_CFLAGS -DNO_DH"])
+ AS_IF([test "$explicit_enable_dsa" = "yes"],
+ [AC_MSG_ERROR([--enable-dsa is not supported with --enable-fips=v7. DSA is retired by FIPS 186-5 and is out of scope for the FIPS 140-3 v7 PQ module. Use --enable-fips=v6 if you need DSA support.])],
+ [test "$ENABLED_DSA" != "no"],
+ [ENABLED_DSA="no"; enable_dsa="no"; AM_CFLAGS="$AM_CFLAGS -DNO_DSA"])],
+ [AS_IF([test "$explicit_enable_dh" != "yes" && test "x$enable_all" != "xyes"],
+ [ENABLED_DH="no"; enable_dh="no"; AM_CFLAGS="$AM_CFLAGS -DNO_DH"])
+ AS_IF([test "$explicit_enable_dsa" != "yes" && test "x$enable_all" != "xyes"],
+ [ENABLED_DSA="no"; enable_dsa="no"; AM_CFLAGS="$AM_CFLAGS -DNO_DSA"])])
+
# optimizations section
# protocol section
@@ -7029,6 +7067,13 @@ AS_CASE([$FIPS_VERSION],
# LMS: enable SHA-256/192 and SHAKE256 parameter sets for FIPS v7
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_LMS_SHA256_192 -DWOLFSSL_LMS_SHAKE256"
+ # SP 800-208 sec 8.1: the key generation and signature generation
+ # algorithms shall only be validated for use within hardware
+ # cryptographic modules, and the module shall not allow the export of
+ # private keying material. Build the stateful hash-based schemes for
+ # signature verification (SP 800-208 sec 8.2) only.
+ AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_XMSS_VERIFY_ONLY -DWOLFSSL_LMS_VERIFY_ONLY"
+
AS_IF([test "$ENABLED_SLHDSA" != "yes" &&
(test "$FIPS_VERSION" != "dev" || test "$enable_slhdsa" != "no")],
[ENABLED_SLHDSA="yes"
@@ -9484,8 +9529,15 @@ then
fi
if test "x$ENABLED_DH" = "xno"
then
- ENABLED_DH="yes"
- AM_CFLAGS="$AM_CFLAGS -DHAVE_DH"
+ # JNI normally auto-enables DH for legacy TLS suites; DH is out of
+ # scope for FIPS v7+, so report and skip rather than re-enabling it.
+ if test "$FIPS_VERSION" = "v7" || test "$FIPS_VERSION" = "ready" || test "$FIPS_VERSION" = "dev"
+ then
+ AC_MSG_NOTICE([JNI enabled but FIPS is $FIPS_VERSION, NOT turning on DH with this module])
+ else
+ ENABLED_DH="yes"
+ AM_CFLAGS="$AM_CFLAGS -DHAVE_DH"
+ fi
fi
if test "x$ENABLED_PSK" = "xno"
then
diff --git a/linuxkm/Kbuild b/linuxkm/Kbuild
index b9bf722c2b..bbf0d2c60e 100644
--- a/linuxkm/Kbuild
+++ b/linuxkm/Kbuild
@@ -111,6 +111,9 @@ $(LIBWOLFSSL_NAME)-y := $(WOLFSSL_OBJ_FILES) linuxkm/module_hooks.o linuxkm/modu
ifeq "$(FIPS_OPTEST)" "1"
$(LIBWOLFSSL_NAME)-y += linuxkm/optest-140-3/linuxkm_optest_wrapper.o
+ # The optest wrapper's AES invalid-input tests exceed the i386 2048-byte
+ # frame warning; relax it for the wrapper only (outside the FIPS boundary).
+ $(obj)/linuxkm/optest-140-3/linuxkm_optest_wrapper.o: ccflags-y += -Wframe-larger-than=4096
endif
WOLFSSL_CFLAGS_NO_VECTOR_INSNS := $(CFLAGS_SIMD_DISABLE) $(CFLAGS_FPU_DISABLE)
@@ -139,6 +142,10 @@ ifeq "$(ENABLED_LINUXKM_PIE)" "yes"
endif
endif
endif
+ ifeq ($(KERNEL_ARCH),i386)
+ NO_PIE_FLAG := 1
+ $(info Note: disabling -fPIE on 32-bit x86 -- i386 -fPIE routes every local symbol through the GOT (R_386_GOTOFF), which the wolfCrypt PIE containerization forbids.)
+ endif
endif
ifdef NO_PIE_FLAG
@@ -237,6 +244,18 @@ $(obj)/wolfcrypt/src/wc_mldsa_asm.o: OBJECT_FILES_NON_STANDARD := y
$(obj)/wolfcrypt/src/wc_falcon_fpr_x86_64_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE)
$(obj)/wolfcrypt/src/wc_falcon_fpr_x86_64_asm.o: OBJECT_FILES_NON_STANDARD := y
+# The kernel forces its own baseline -march, which rejects the port/arm/*.S
+# crypto+NEON instructions. Supply the right -march per kernel arch.
+ifeq ($(CONFIG_ARM64),y)
+ WOLFSSL_ARM_ASM_MARCH := -march=armv8-a+crypto
+else ifeq ($(CONFIG_ARM),y)
+ WOLFSSL_ARM_ASM_MARCH := -march=armv8-a -mfpu=crypto-neon-fp-armv8
+endif
+ifdef WOLFSSL_ARM_ASM_MARCH
+$(obj)/wolfcrypt/src/port/arm/%.o: asflags-y := $(WOLFSSL_ASFLAGS) $(WOLFSSL_ARM_ASM_MARCH)
+$(obj)/wolfcrypt/src/port/arm/%.o: OBJECT_FILES_NON_STANDARD := y
+endif
+
ifndef READELF
READELF := readelf
endif
@@ -345,7 +364,7 @@ RENAME_PIE_TEXT_AND_DATA_SECTIONS := \
next; \
} \
else if ($$4 == "OBJECT") { \
- if (! ($$7 in wolfcrypt_data_sections)) { \
+ if (! ($$7 in wolfcrypt_data_sections) && ! ($$7 in wolfcrypt_text_sections)) { \
if ((other_sections[$$7] == ".printk_index") || \
(($$8 ~ /^_entry\.[0-9]+$$|^kernel_read_file_str$$/) && \
(other_sections[$$7] == ".data.rel.ro.local"))) \
diff --git a/linuxkm/Makefile b/linuxkm/Makefile
index 00fc70df6a..0b9d46f104 100644
--- a/linuxkm/Makefile
+++ b/linuxkm/Makefile
@@ -52,7 +52,10 @@ ifndef SRC_TOP
SRC_TOP=$(shell dirname $(MODULE_TOP))
endif
-WOLFSSL_CFLAGS=-DHAVE_CONFIG_H -I$(SRC_TOP) -DBUILDING_WOLFSSL $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -Wno-declaration-after-statement -Wno-redundant-decls -DLIBWOLFSSL_GLOBAL_EXTRA_CFLAGS="\" $(KERNEL_EXTRA_CFLAGS)\""
+# -Wno-nested-externs: the kernel's _compiletime_assert emits an extern inside a
+# function body, which -Werror turns into a build failure. Diagnostic-only, so
+# object code (and the FIPS in-core hash) is unchanged.
+WOLFSSL_CFLAGS=-DHAVE_CONFIG_H -I$(SRC_TOP) -DBUILDING_WOLFSSL $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -Wno-declaration-after-statement -Wno-redundant-decls -Wno-nested-externs -DLIBWOLFSSL_GLOBAL_EXTRA_CFLAGS="\" $(KERNEL_EXTRA_CFLAGS)\""
ifdef KERNEL_EXTRA_CFLAGS
WOLFSSL_CFLAGS += $(KERNEL_EXTRA_CFLAGS)
endif
@@ -62,7 +65,9 @@ endif
WOLFSSL_ASFLAGS=-DHAVE_CONFIG_H -I$(SRC_TOP) -DBUILDING_WOLFSSL $(AM_CCASFLAGS) $(CCASFLAGS)
-WOLFSSL_OBJ_FILES=$(patsubst %.lo, %.o, $(patsubst src/src_libwolfssl_la-%, src/%, $(patsubst src/libwolfssl_la-%, src/%, $(patsubst wolfcrypt/src/src_libwolfssl_la-%, wolfcrypt/src/%, $(src_libwolfssl_la_OBJECTS)))))
+# Strip libtool's per-target object prefix so Kbuild sees the real object names.
+# The innermost patsubst covers the wolfcrypt/src/port/arm/ asm the others miss.
+WOLFSSL_OBJ_FILES=$(patsubst %.lo, %.o, $(patsubst src/src_libwolfssl_la-%, src/%, $(patsubst src/libwolfssl_la-%, src/%, $(patsubst wolfcrypt/src/src_libwolfssl_la-%, wolfcrypt/src/%, $(patsubst wolfcrypt/src/port/arm/src_libwolfssl_la-%, wolfcrypt/src/port/arm/%, $(src_libwolfssl_la_OBJECTS))))))
ifeq "$(ENABLED_CRYPT_TESTS)" "yes"
WOLFSSL_OBJ_FILES+=wolfcrypt/test/test.o
@@ -167,7 +172,7 @@ GENERATE_RELOC_TAB := $(AWK) ' \
function open_seg(seg) { \
seen_seg[seg] = 1; \
printf("%s\n ", \
- "WOLFSSL_LOCAL const struct wc_reloc_table_ent wc_linuxkm_pie_" seg "_reloc_tab[] = { "); \
+ "WOLFSSL_LOCAL const struct wc_reloc_table_ent wc_linuxkm_pie_" seg "_reloc_tab[] = {"); \
cur_seg = seg; \
} \
function close_cur_seg() { \
@@ -204,7 +209,7 @@ GENERATE_RELOC_TAB := $(AWK) ' \
next; \
} \
/^0/ { \
- if ($$3 !~ "^(R_X86_.*|R_AARCH64_.*|R_ARM.*)$$") { \
+ if ($$3 !~ "^(R_X86_.*|R_386_.*|R_AARCH64_.*|R_ARM.*)$$") { \
print "Unexpected relocation type in " cur_seg ":\n" $$0 >"/dev/stderr"; \
++bad_relocs; \
} \
diff --git a/linuxkm/linuxkm_memory.c b/linuxkm/linuxkm_memory.c
index 2f1b75e112..2d6940f435 100644
--- a/linuxkm/linuxkm_memory.c
+++ b/linuxkm/linuxkm_memory.c
@@ -52,6 +52,8 @@ static const struct reloc_layout_ent {
[WC_R_X86_64_64] = { "R_X86_64_64", ~0UL, 64, .is_signed = 0, .is_relative = 0 },
[WC_R_X86_64_PC32] = { "R_X86_64_PC32", ~0UL, 32, .is_signed = 1, .is_relative = 1 },
[WC_R_X86_64_PLT32] = { "R_X86_64_PLT32", ~0UL, 32, .is_signed = 1, .is_relative = 1 },
+ [WC_R_386_32] = { "R_386_32", ~0UL, 32, .is_signed = 0, .is_relative = 0 },
+ [WC_R_386_PC32] = { "R_386_PC32", ~0UL, 32, .is_signed = 1, .is_relative = 1 },
[WC_R_AARCH64_ABS32] = { "R_AARCH64_ABS32", ~0UL, 32, .is_signed = 1, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
[WC_R_AARCH64_ABS64] = { "R_AARCH64_ABS64", ~0UL, 64, .is_signed = 1, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
[WC_R_AARCH64_ADD_ABS_LO12_NC] = { "R_AARCH64_ADD_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 },
@@ -64,6 +66,10 @@ static const struct reloc_layout_ent {
[WC_R_AARCH64_LDST64_ABS_LO12_NC] = { "R_AARCH64_LDST64_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 },
[WC_R_AARCH64_PREL32] = { "R_AARCH64_PREL32", ~0UL, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
[WC_R_ARM_ABS32] = { "R_ARM_ABS32", ~0UL, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
+ /* ARM-mode BL/B: signed 24-bit word offset in bits [23:0]. Emitted by the
+ * arm32 ARM-mode (non-Thumb) kernel module build. */
+ [WC_R_ARM_CALL] = { "R_ARM_CALL", 0b00000000111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
+ [WC_R_ARM_JUMP24] = { "R_ARM_JUMP24", 0b00000000111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
[WC_R_ARM_PREL31] = { "R_ARM_PREL31", 0b01111111111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
[WC_R_ARM_REL32] = { "R_ARM_REL32", ~0UL, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
[WC_R_ARM_THM_CALL] = { "R_ARM_THM_CALL", 0b00000111111111110010111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 },
@@ -366,6 +372,10 @@ ssize_t wc_reloc_normalize_segment(
case WC_R_X86_64_32:
case WC_R_X86_64_32S:
case WC_R_X86_64_64:
+ /* i386 reuses the x86_64 path: the math is driven by
+ * layout->is_relative/is_signed and is width-correct via uintptr_t. */
+ case WC_R_386_32:
+ case WC_R_386_PC32:
if (dest_seg != WC_R_SEG_OTHER) {
#ifdef DEBUG_LINUXKM_PIE_SUPPORT
@@ -413,6 +423,8 @@ ssize_t wc_reloc_normalize_segment(
break;
case WC_R_ARM_ABS32:
+ case WC_R_ARM_CALL:
+ case WC_R_ARM_JUMP24:
case WC_R_ARM_PREL31:
case WC_R_ARM_REL32:
case WC_R_ARM_THM_CALL:
diff --git a/linuxkm/linuxkm_memory.h b/linuxkm/linuxkm_memory.h
index 76e681da80..1c8ef3f662 100644
--- a/linuxkm/linuxkm_memory.h
+++ b/linuxkm/linuxkm_memory.h
@@ -40,6 +40,11 @@ enum wc_reloc_type {
WC_R_X86_64_64,
WC_R_X86_64_PC32,
WC_R_X86_64_PLT32,
+ /* 32-bit x86 (i386): with NO_PIE_FLAG the container emits only R_386_32
+ * (absolute) and R_386_PC32 (PC-relative), equivalent to R_X86_64_32 /
+ * R_X86_64_PC32 and sharing their canonicalization case below. */
+ WC_R_386_32,
+ WC_R_386_PC32,
WC_R_AARCH64_ABS32,
WC_R_AARCH64_ABS64,
WC_R_AARCH64_ADD_ABS_LO12_NC,
@@ -52,6 +57,8 @@ enum wc_reloc_type {
WC_R_AARCH64_LDST64_ABS_LO12_NC,
WC_R_AARCH64_PREL32,
WC_R_ARM_ABS32,
+ WC_R_ARM_CALL,
+ WC_R_ARM_JUMP24,
WC_R_ARM_PREL31,
WC_R_ARM_REL32,
WC_R_ARM_THM_CALL,
diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h
index 77b395d8bd..9a6ee167a9 100644
--- a/linuxkm/linuxkm_wc_port.h
+++ b/linuxkm/linuxkm_wc_port.h
@@ -736,8 +736,10 @@
#define WOLFSSL_USE_SAVE_VECTOR_REGISTERS
#endif
+ /* x86 and ARM/ARM64 share the arch-neutral tracker in
+ * x86_vector_register_glue.c, which keeps wc_*_x86 names on all arches. */
#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && \
- defined(CONFIG_X86)
+ (defined(CONFIG_X86) || defined(CONFIG_ARM) || defined(CONFIG_ARM64))
extern __must_check int allocate_wolfcrypt_linuxkm_fpu_states(void);
extern void free_wolfcrypt_linuxkm_fpu_states(void);
@@ -745,18 +747,23 @@
WOLFSSL_API __must_check int wc_save_vector_registers_x86(enum wc_svr_flags flags);
WOLFSSL_API void wc_restore_vector_registers_x86(enum wc_svr_flags flags);
- #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
- #include
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
- /* added by a62b01cd6c */
- #include
- #endif
- #else
- #include
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
- /* added by 266d051601 */
- #include
+ #ifdef CONFIG_X86
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+ #include
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
+ /* added by a62b01cd6c */
+ #include
+ #endif
+ #else
+ #include
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
+ /* added by 266d051601 */
+ #include
+ #endif
#endif
+ #else /* CONFIG_ARM || CONFIG_ARM64 */
+ #include /* may_use_simd() */
+ #include /* kernel_neon_begin/end() */
#endif
#ifndef CAN_SAVE_VECTOR_REGISTERS
#define CAN_SAVE_VECTOR_REGISTERS() wc_can_save_vector_registers_x86()
@@ -787,42 +794,6 @@
#define REENABLE_VECTOR_REGISTERS() wc_restore_vector_registers_x86(WC_SVR_FLAG_INHIBIT)
#endif
- #elif defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
-
- #error kernel module ARM SIMD is not yet tested or usable.
-
- #include
-
- static WARN_UNUSED_RESULT inline int save_vector_registers_arm(void)
- {
- preempt_disable();
- if (! may_use_simd()) {
- preempt_enable();
- return BAD_STATE_E;
- } else {
- fpsimd_preserve_current_state();
- return 0;
- }
- }
- static inline void restore_vector_registers_arm(void)
- {
- fpsimd_restore_current_state();
- preempt_enable();
- }
-
- #ifndef SAVE_VECTOR_REGISTERS
- #define SAVE_VECTOR_REGISTERS(fail_clause) { int _svr_ret = save_vector_registers_arm(); if (_svr_ret != 0) { fail_clause } }
- #endif
- #ifndef SAVE_VECTOR_REGISTERS2
- #define SAVE_VECTOR_REGISTERS2() save_vector_registers_arm()
- #endif
- #ifndef CAN_SAVE_VECTOR_REGISTERS
- #define CAN_SAVE_VECTOR_REGISTERS() can_save_vector_registers_arm()
- #endif
- #ifndef RESTORE_VECTOR_REGISTERS
- #define RESTORE_VECTOR_REGISTERS() restore_vector_registers_arm()
- #endif
-
#elif defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS)
#error WOLFSSL_USE_SAVE_VECTOR_REGISTERS is set for an unimplemented architecture.
#endif /* WOLFSSL_USE_SAVE_VECTOR_REGISTERS */
@@ -1035,6 +1006,14 @@
extern int memcmp(const void *s1, const void *s2, size_t n);
#endif
+#ifdef CONFIG_X86_32
+ /* string_32.h #defines memcpy/memcmp/memset as __builtin_* macros, which
+ * would corrupt the PIE redirect-table members declared below. */
+ #undef memcpy
+ #undef memcmp
+ #undef memset
+#endif
+
struct wolfssl_linuxkm_pie_redirect_table {
#ifdef HAVE_FIPS
typeof(wc_linuxkm_normalize_relocations) *wc_linuxkm_normalize_relocations;
@@ -1171,13 +1150,13 @@
#ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS
- #ifdef CONFIG_X86
+ #if defined(CONFIG_X86) || defined(CONFIG_ARM) || defined(CONFIG_ARM64)
typeof(allocate_wolfcrypt_linuxkm_fpu_states) *allocate_wolfcrypt_linuxkm_fpu_states;
typeof(wc_can_save_vector_registers_x86) *wc_can_save_vector_registers_x86;
typeof(free_wolfcrypt_linuxkm_fpu_states) *free_wolfcrypt_linuxkm_fpu_states;
typeof(wc_restore_vector_registers_x86) *wc_restore_vector_registers_x86;
typeof(wc_save_vector_registers_x86) *wc_save_vector_registers_x86;
- #else /* !CONFIG_X86 */
+ #else
#error WOLFSSL_USE_SAVE_VECTOR_REGISTERS is set for an unimplemented architecture.
#endif /* arch */
@@ -1528,7 +1507,8 @@
#undef get_current
#define get_current WC_PIE_INDIRECT_SYM(get_current)
- #if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && defined(CONFIG_X86)
+ #if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && \
+ (defined(CONFIG_X86) || defined(CONFIG_ARM) || defined(CONFIG_ARM64))
#define allocate_wolfcrypt_linuxkm_fpu_states WC_PIE_INDIRECT_SYM(allocate_wolfcrypt_linuxkm_fpu_states)
#define wc_can_save_vector_registers_x86 WC_PIE_INDIRECT_SYM(wc_can_save_vector_registers_x86)
#define free_wolfcrypt_linuxkm_fpu_states WC_PIE_INDIRECT_SYM(free_wolfcrypt_linuxkm_fpu_states)
@@ -1873,7 +1853,7 @@
#if !defined(BUILDING_WOLFSSL)
/* some caller code needs these. */
#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS)
- #if defined(CONFIG_X86)
+ #if defined(CONFIG_X86) || defined(CONFIG_ARM) || defined(CONFIG_ARM64)
WOLFSSL_API __must_check int wc_can_save_vector_registers_x86(void);
WOLFSSL_API __must_check int wc_save_vector_registers_x86(enum wc_svr_flags flags);
WOLFSSL_API void wc_restore_vector_registers_x86(enum wc_svr_flags flags);
@@ -1883,9 +1863,9 @@
#ifndef REENABLE_VECTOR_REGISTERS
#define REENABLE_VECTOR_REGISTERS() wc_restore_vector_registers_x86(WC_SVR_FLAG_INHIBIT)
#endif
- #else /* !CONFIG_X86 */
+ #else
#error WOLFSSL_USE_SAVE_VECTOR_REGISTERS is set for an unimplemented architecture.
- #endif /* !CONFIG_X86 */
+ #endif
#endif /* WOLFSSL_USE_SAVE_VECTOR_REGISTERS */
#ifdef WC_LINUXKM_USE_HEAP_WRAPPERS
WOLFSSL_API extern void *wc_linuxkm_malloc(size_t size);
diff --git a/linuxkm/module_hooks.c b/linuxkm/module_hooks.c
index bb64ce959c..7470047ede 100644
--- a/linuxkm/module_hooks.c
+++ b/linuxkm/module_hooks.c
@@ -531,7 +531,9 @@ int wc_linuxkm_GenerateSeed_IntelRD(struct OS_Seed* os, byte* output, word32 sz)
#endif /* WC_LINUXKM_RDSEED_IN_GLUE_LAYER */
-#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && defined(CONFIG_X86)
+#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && \
+ (defined(CONFIG_X86) || defined(CONFIG_ARM) || defined(CONFIG_ARM64))
+ /* arch-generic vector save/restore (kernel_fpu_* x86, kernel_neon_* ARM) */
#include "linuxkm/x86_vector_register_glue.c"
#endif
@@ -1518,7 +1520,8 @@ static int set_up_wolfssl_linuxkm_pie_redirect_table(void) {
wolfssl_linuxkm_pie_redirect_table.get_current = my_get_current_thread;
-#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && defined(CONFIG_X86)
+#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && \
+ (defined(CONFIG_X86) || defined(CONFIG_ARM) || defined(CONFIG_ARM64))
wolfssl_linuxkm_pie_redirect_table.allocate_wolfcrypt_linuxkm_fpu_states = allocate_wolfcrypt_linuxkm_fpu_states;
wolfssl_linuxkm_pie_redirect_table.wc_can_save_vector_registers_x86 = wc_can_save_vector_registers_x86;
wolfssl_linuxkm_pie_redirect_table.free_wolfcrypt_linuxkm_fpu_states = free_wolfcrypt_linuxkm_fpu_states;
@@ -2047,7 +2050,9 @@ static ssize_t FIPS_optest_trig_handler(struct kobject *kobj, struct kobj_attrib
int ret;
int argc;
const char *argv[3];
- char code_buf[5];
+ /* Textual sysfs error code + NUL, plus headroom. Fits the v7.0.0 5-char
+ * codes (e.g. ML_KEM_PCT_E) that the old [5] rejected. */
+ char code_buf[8];
size_t corrected_count;
int i;
@@ -2063,7 +2068,7 @@ static ssize_t FIPS_optest_trig_handler(struct kobject *kobj, struct kobj_attrib
corrected_count = count - 1;
else
corrected_count = count;
- if ((corrected_count < 1) || (corrected_count > 4))
+ if ((corrected_count < 1) || (corrected_count > (sizeof(code_buf) - 1)))
return -EINVAL;
XMEMCPY(code_buf, buf, corrected_count);
code_buf[corrected_count] = 0;
diff --git a/linuxkm/pie_redirect_table.c b/linuxkm/pie_redirect_table.c
index 03be2e04fa..948694eaca 100644
--- a/linuxkm/pie_redirect_table.c
+++ b/linuxkm/pie_redirect_table.c
@@ -53,8 +53,10 @@ const struct wolfssl_linuxkm_pie_redirect_table
return &wolfssl_linuxkm_pie_redirect_table;
}
-/* placeholder implementations for missing functions. */
-#if defined(CONFIG_MIPS)
+/* placeholder implementations for missing functions.
+ * ARM/ARM64 need these like MIPS: gcc auto-emits memcpy/memset libcalls that
+ * the in-core integrity check forbids as undefined symbols. */
+#if defined(CONFIG_MIPS) || defined(CONFIG_ARM) || defined(CONFIG_ARM64)
#undef memcpy
void *memcpy(void *dest, const void *src, size_t n) {
char *dest_i = (char *)dest;
@@ -74,3 +76,62 @@ const struct wolfssl_linuxkm_pie_redirect_table
return dest;
}
#endif
+
+#if defined(CONFIG_ARM)
+ /* 32-bit ARM has no HW divide and the PIE FIPS container cannot reference
+ * the kernel's EABI helpers. *idivmod returns quot in r0, rem in r1. */
+ unsigned int __aeabi_uidiv(unsigned int n, unsigned int d);
+ unsigned int __aeabi_uidiv(unsigned int n, unsigned int d) {
+ unsigned int q = 0, r = 0;
+ int i;
+ if (d == 0)
+ return ~0u;
+ for (i = 31; i >= 0; i--) {
+ r = (r << 1) | ((n >> i) & 1u);
+ if (r >= d) {
+ r -= d;
+ q |= (1u << i);
+ }
+ }
+ return q;
+ }
+
+ unsigned long long __aeabi_uidivmod(unsigned int n, unsigned int d);
+ unsigned long long __aeabi_uidivmod(unsigned int n, unsigned int d) {
+ unsigned int q = 0, r = 0;
+ int i;
+ if (d == 0)
+ return (unsigned long long)n << 32; /* quot=0, rem=n */
+ for (i = 31; i >= 0; i--) {
+ r = (r << 1) | ((n >> i) & 1u);
+ if (r >= d) {
+ r -= d;
+ q |= (1u << i);
+ }
+ }
+ return ((unsigned long long)r << 32) | q;
+ }
+
+ int __aeabi_idiv(int n, int d);
+ int __aeabi_idiv(int n, int d) {
+ int neg = (n < 0) ^ (d < 0);
+ unsigned int un = (n < 0) ? (unsigned int)(-(long)n) : (unsigned int)n;
+ unsigned int ud = (d < 0) ? (unsigned int)(-(long)d) : (unsigned int)d;
+ unsigned int uq = __aeabi_uidiv(un, ud);
+ return neg ? -(int)uq : (int)uq;
+ }
+
+ unsigned long long __aeabi_idivmod(int n, int d);
+ unsigned long long __aeabi_idivmod(int n, int d) {
+ int nneg = (n < 0);
+ int qneg = (n < 0) ^ (d < 0);
+ unsigned int un = nneg ? (unsigned int)(-(long)n) : (unsigned int)n;
+ unsigned int ud = (d < 0) ? (unsigned int)(-(long)d) : (unsigned int)d;
+ unsigned long long um = __aeabi_uidivmod(un, ud);
+ unsigned int uq = (unsigned int)um;
+ unsigned int ur = (unsigned int)(um >> 32);
+ int q = qneg ? -(int)uq : (int)uq;
+ int r = nneg ? -(int)ur : (int)ur;
+ return ((unsigned long long)(unsigned int)r << 32) | (unsigned int)q;
+ }
+#endif /* CONFIG_ARM */
diff --git a/linuxkm/x86_vector_register_glue.c b/linuxkm/x86_vector_register_glue.c
index e33c3d719e..5bf163dff8 100644
--- a/linuxkm/x86_vector_register_glue.c
+++ b/linuxkm/x86_vector_register_glue.c
@@ -23,8 +23,20 @@
/* included by linuxkm/module_hooks.c */
#ifndef WC_SKIP_INCLUDED_C_FILES
-#if !defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) || !defined(CONFIG_X86)
- #error x86_vector_register_glue.c included in non-vectorized/non-x86 project.
+#if !defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) || \
+ !(defined(CONFIG_X86) || defined(CONFIG_ARM) || defined(CONFIG_ARM64))
+ #error vector register glue included in non-vectorized or unsupported-arch project.
+#endif
+
+/* Arch-neutral per-CPU tracker; only the SIMD claim/release differs: x86
+ * kernel_fpu_*, ARM/ARM64 kernel_neon_*. wc_*_x86 names kept on all arches. */
+#if defined(CONFIG_X86)
+ #define WC_LINUXKM_FPU_BEGIN() kernel_fpu_begin()
+ #define WC_LINUXKM_FPU_END() kernel_fpu_end()
+#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ #include
+ #define WC_LINUXKM_FPU_BEGIN() kernel_neon_begin()
+ #define WC_LINUXKM_FPU_END() kernel_neon_end()
#endif
#ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG
@@ -70,9 +82,10 @@ WARN_UNUSED_RESULT int allocate_wolfcrypt_linuxkm_fpu_states(void)
wc_linuxkm_fpu_states_n_tracked * sizeof(wc_linuxkm_fpu_states[0]));
if (! wc_linuxkm_fpu_states) {
+ /* cast to match %lu: the product's type is arch-dependent. */
pr_err("ERROR: allocation of %lu bytes for "
"wc_linuxkm_fpu_states failed.\n",
- nr_cpu_ids * sizeof(wc_linuxkm_fpu_states[0]));
+ (unsigned long)(nr_cpu_ids * sizeof(wc_linuxkm_fpu_states[0])));
return MEMORY_E;
}
@@ -454,10 +467,10 @@ WARN_UNUSED_RESULT int wc_save_vector_registers_x86(enum wc_svr_flags flags)
#if IS_ENABLED(CONFIG_PREEMPT_RT)
preempt_disable();
#endif
- kernel_fpu_begin();
+ WC_LINUXKM_FPU_BEGIN();
pstate = wc_linuxkm_fpu_state_assoc(1, 1);
if (pstate == NULL) {
- kernel_fpu_end();
+ WC_LINUXKM_FPU_END();
#if IS_ENABLED(CONFIG_PREEMPT_RT)
preempt_enable();
#endif
@@ -521,7 +534,7 @@ void wc_restore_vector_registers_x86(enum wc_svr_flags flags)
if (pstate->fpu_state == 0U) {
wc_linuxkm_fpu_state_release(pstate);
- kernel_fpu_end();
+ WC_LINUXKM_FPU_END();
#if IS_ENABLED(CONFIG_PREEMPT_RT)
preempt_enable();
#endif
diff --git a/src/include.am b/src/include.am
index fa0e24a40b..be4718cfcd 100644
--- a/src/include.am
+++ b/src/include.am
@@ -114,7 +114,11 @@ endif
if BUILD_AESNI
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_asm.S
if BUILD_X86_ASM
-src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_x86_asm.S
+# 32-bit GCM asm NOT built: non-PIC .text relocs break FIPS in-core integrity in
+# a .so; 32-bit uses C-GHASH + AES-NI blocks (see WC_AESNI_GCM gate in aes.c).
+if BUILD_AESXTS
+src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_xts_x86_asm.S
+endif
else
if BUILD_AESGCM
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_asm.S
@@ -276,7 +280,11 @@ endif BUILD_PPC32_ASM
if BUILD_AESNI
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_asm.S
if BUILD_X86_ASM
-src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_x86_asm.S
+# 32-bit GCM asm NOT built: non-PIC .text relocs break FIPS in-core integrity in
+# a .so; 32-bit uses C-GHASH + AES-NI blocks (see WC_AESNI_GCM gate in aes.c).
+if BUILD_AESXTS
+src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_xts_x86_asm.S
+endif
else
if BUILD_AESGCM
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_asm.S
@@ -612,7 +620,11 @@ endif BUILD_PPC32_ASM
if BUILD_AESNI
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_asm.S
if BUILD_X86_ASM
-src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_x86_asm.S
+# 32-bit GCM asm NOT built: non-PIC .text relocs break FIPS in-core integrity in
+# a .so; 32-bit uses C-GHASH + AES-NI blocks (see WC_AESNI_GCM gate in aes.c).
+if BUILD_AESXTS
+src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_xts_x86_asm.S
+endif
else
if BUILD_AESGCM
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_asm.S
@@ -999,7 +1011,11 @@ if BUILD_AESNI
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_asm.S
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_x86_64_asm.S
if BUILD_X86_ASM
-src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_x86_asm.S
+# 32-bit GCM asm NOT built: non-PIC .text relocs break FIPS in-core integrity in
+# a .so; 32-bit uses C-GHASH + AES-NI blocks (see WC_AESNI_GCM gate in aes.c).
+if BUILD_AESXTS
+src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_xts_x86_asm.S
+endif
else
if BUILD_AESGCM
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_asm.S
@@ -1992,7 +2008,11 @@ if BUILD_AESNI
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_asm.S
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_x86_64_asm.S
if BUILD_X86_ASM
-src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_x86_asm.S
+# 32-bit GCM asm NOT built: non-PIC .text relocs break FIPS in-core integrity in
+# a .so; 32-bit uses C-GHASH + AES-NI blocks (see WC_AESNI_GCM gate in aes.c).
+if BUILD_AESXTS
+src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_xts_x86_asm.S
+endif
else
if BUILD_AESGCM
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/aes_gcm_asm.S
diff --git a/src/pk_rsa.c b/src/pk_rsa.c
index 63ab70abd2..39ffa09810 100644
--- a/src/pk_rsa.c
+++ b/src/pk_rsa.c
@@ -2648,9 +2648,12 @@ static int rsa_pss_calc_salt(int saltLen, int hashLen, int emLen)
break;
case WC_RSA_PSS_SALTLEN_MAX_SIGN:
case WC_RSA_PSS_SALTLEN_MAX:
- #ifdef WOLFSSL_PSS_LONG_SALT
+ #if defined(WOLFSSL_PSS_LONG_SALT) && !FIPS_VERSION3_GE(7,0,0)
saltLen = emLen - hashLen - 2;
#else
+ /* FIPS 186-5 sec 5.4(g): 0 <= sLen <= hLen, so the largest salt an
+ * approved service may use is hLen -- cap "max" there rather than
+ * asking wolfCrypt for a length it will reject with PSS_SALTLEN_E. */
saltLen = hashLen;
(void)emLen;
#endif
diff --git a/src/tls13.c b/src/tls13.c
index 7674cb7f24..6d7f2eb8dd 100644
--- a/src/tls13.c
+++ b/src/tls13.c
@@ -4075,7 +4075,10 @@ static int EchCalcAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
}
}
- /* extract clientRandomInner with a key of all zeros */
+ /* extract clientRandomInner with a key of all zeros.
+ * TLS 1.3 MUST use raw wc_HKDF_Extract/wc_Tls13_HKDF_Expand_Label, never
+ * composite wc_HKDF(): wc_HKDF_fips is not gated on FIPS_CAST_KDF_TLS13,
+ * so a TLS 1.3 caller of it would bypass that CAST gate. */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
#if !defined(HAVE_FIPS) || \
diff --git a/tests/api.c b/tests/api.c
index 95551a6c2b..23d835ea27 100644
--- a/tests/api.c
+++ b/tests/api.c
@@ -27760,7 +27760,21 @@ static int test_wolfSSL_d2i_X509_REQ(void)
/*
* Verify the signature in the CSR
*/
+#ifdef WC_FIPS_RSA_VERIFY_MIN_2048
+ /* certs/csr.ext.der carries a 1024-bit RSA key. CMVP IG C.F
+ * ("Signature verification"): "For the FIPS 186-5 signature
+ * verification, the modulus size shall be at least 2048." A 1024-bit
+ * modulus is only verifiable under a FIPS 186-4 legacy SigVer claim,
+ * which this module does not make, so the approved service rejects it
+ * and verification fails by design.
+ * Gated on the module's capability macro (fips.h) rather than
+ * FIPS_VERSION3_GE(7,0,0): an earlier module reports a version that
+ * satisfies that predicate but enforces the floor on key generation
+ * only, so it still verifies the 1024-bit signature. */
+ ExpectIntEQ(X509_REQ_verify(req, pub_key), 0);
+#else
ExpectIntEQ(X509_REQ_verify(req, pub_key), 1);
+#endif
#ifdef OPENSSL_ALL
ExpectNotNull(exts = (STACK_OF(X509_EXTENSION)*)X509_REQ_get_extensions(
diff --git a/tests/api/api.h b/tests/api/api.h
index 69b1f433e5..dae18f8b1d 100644
--- a/tests/api/api.h
+++ b/tests/api/api.h
@@ -39,6 +39,22 @@
#include
+/* PQC known-answer tests drive ML-KEM/ML-DSA/SLH-DSA keygen and signing from
+ * fixed NIST seeds. FIPS 203 sec 3.3, 204 sec 5.4 and 205 sec 10.2 require the
+ * module to generate that randomness itself, so the public seed-input service
+ * returns WC_FIPS_NOT_APPROVED in a FIPS build; these KATs run in non-FIPS
+ * builds only (module CASTs / optest / CAVP cover them otherwise). */
+#if !defined(HAVE_FIPS)
+ #define WOLFSSL_TEST_PQC_SEED_KAT
+#endif
+
+/* Expected result for an argument-validation call to a *_with_seed /
+ * *_with_random seed-input wrapper: the public service is no longer gated, so
+ * it always reaches its normal argument validation and returns the given code
+ * (the WC_FIPS_NOT_APPROVED indicator applies only to a *successful* external
+ * call). */
+#define SEED_ARG_ERR(e) WC_NO_ERR_TRACE(e)
+
/* Old FIPS headers don't allow comparisons with WC_MIN_DIGEST_SIZE_FOR_SIGN by
* the preprocessor, so we catch those builds with one of the first two
* clauses.
diff --git a/tests/api/test_aes.c b/tests/api/test_aes.c
index ed4f762a61..be7fac8760 100644
--- a/tests/api/test_aes.c
+++ b/tests/api/test_aes.c
@@ -31,6 +31,16 @@
#include
#include
#include
+
+/* fips PR #405: wc_AesGcmInit/SetIV/SetExtIV with an IV shorter than 96 bits
+ * (GCM_NONCE_MID_SZ) returns WC_FIPS_NOT_APPROVED, including the key-only
+ * deferred-IV init; non-FIPS keeps the prior return. TEMPORARY shim pending
+ * the standalone test_aes.c update PR. */
+#ifdef HAVE_FIPS
+ #define WC_TEST_GCM_SHORT_IV_EXP WC_FIPS_NOT_APPROVED
+#else
+ #define WC_TEST_GCM_SHORT_IV_EXP 0
+#endif
#ifdef WOLFSSL_CMAC
/* Explicit include (rather than relying on aes.h's conditional
* transitive include via WOLFSSL_AES_EAX) so struct Cmac / wc_InitCmac()
@@ -722,7 +732,10 @@ static int test_wc_AesCbcEncryptDecrypt_WithKey(Aes* aes, byte* key,
ExpectIntEQ(wc_AesCbcEncrypt(aes, cipher, vector, vector_len),
0);
ExpectBufEQ(cipher, vector_enc, vector_len);
-#ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS
+ /* FIPSv2's wc_AesCbcEncrypt_fips predates the BAD_LENGTH_E check and
+ * returns 0 on unaligned input; only v5.x+ carry it. Skip for FIPSv2. */
+#if defined(WOLFSSL_AES_CBC_LENGTH_CHECKS) && \
+ (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5,0))
ExpectIntEQ(wc_AesCbcEncrypt(aes, cipher, vector, vector_len - 1),
WC_NO_ERR_TRACE(BAD_LENGTH_E));
#endif
@@ -732,7 +745,9 @@ static int test_wc_AesCbcEncryptDecrypt_WithKey(Aes* aes, byte* key,
ExpectIntEQ(wc_AesCbcDecrypt(aes, decrypted, cipher,
WC_AES_BLOCK_SIZE * 2), 0);
ExpectBufEQ(decrypted, vector, vector_len);
-#ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS
+#if defined(WOLFSSL_AES_CBC_LENGTH_CHECKS) && \
+ (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5,0))
+ /* Same FIPSv2 vs v5+ rationale as the encrypt assertion above. */
ExpectIntEQ(wc_AesCbcDecrypt(aes, decrypted, cipher,
WC_AES_BLOCK_SIZE * 2 - 1), WC_NO_ERR_TRACE(BAD_LENGTH_E));
#else
@@ -9241,12 +9256,8 @@ int test_wc_AesGcmArgMcdc(void)
* self-contained demonstration). */
ExpectIntEQ(wc_AesGcmSetIV(&aes, 10, NULL, 0, &rng),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
-#if defined(HAVE_FIPS) && FIPS_VERSION3_GE(7,0,0)
ExpectIntEQ(wc_AesGcmSetIV(&aes, GCM_NONCE_MIN_SZ, NULL, 0, &rng),
- WC_FIPS_NOT_APPROVED);
-#else
- ExpectIntEQ(wc_AesGcmSetIV(&aes, GCM_NONCE_MIN_SZ, NULL, 0, &rng), 0);
-#endif
+ WC_TEST_GCM_SHORT_IV_EXP);
ExpectIntEQ(wc_AesGcmSetIV(&aes, GCM_NONCE_MID_SZ, NULL, 0, &rng),
0);
ExpectIntEQ(wc_AesGcmSetIV(&aes, GCM_NONCE_MAX_SZ, NULL, 0, &rng),
diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c
index b8ed0599fd..81c005d43d 100644
--- a/tests/api/test_ecc.c
+++ b/tests/api/test_ecc.c
@@ -2029,11 +2029,14 @@ int test_wc_ecc_shared_secret_ssh(void)
WC_RNG rng;
int ret;
int keySz = KEY32;
-#if FIPS_VERSION3_GE(6,0,0)
- int key2Sz = KEY28;
-#else
- int key2Sz = KEY24;
-#endif
+ /* key2 must be on the SAME curve as key. ECDH (including this SSH variant,
+ * which takes a raw peer point) is only defined on a shared curve, and a
+ * FIPS v7 module validates the peer point against the private key's curve
+ * per SP 800-56A Rev 3 sec 5.6.2.2. The earlier KEY28/KEY24 (P-224/P-192)
+ * value made key2 a different curve than key (P-256): a cross-curve ECDH
+ * that only appeared to succeed because the raw-point path skipped the
+ * validation the ecc_key path already enforces (matching dp->id). */
+ int key2Sz = KEY32;
byte secret[KEY32];
word32 secretLen = (word32)keySz;
diff --git a/tests/api/test_ed25519.c b/tests/api/test_ed25519.c
index b5859b9f9a..3794d7dd91 100644
--- a/tests/api/test_ed25519.c
+++ b/tests/api/test_ed25519.c
@@ -958,12 +958,21 @@ int test_wc_ed25519_sign_verify_ctx_ph(void)
/* Ed25519ctx round trip: type==Ed25519ctx true side, real context. */
sigLen = sizeof(sig);
+#ifdef WC_FIPS_ED25519CTX_NOT_APPROVED
+ /* Ed25519ctx is not an Approved EdDSA instance in the FIPS module
+ * (FIPS 186-5 sec 7.6/7.8); the sign service must reject it. Gated on the
+ * module's capability macro (fips.h) rather than FIPS_VERSION3_GE(7,0,0):
+ * an earlier v7.0.0 module reports the same version but still signs. */
+ ExpectIntEQ(wc_ed25519ctx_sign_msg(msg, sizeof(msg), sig, &sigLen, &key,
+ ctx, sizeof(ctx)), WC_NO_ERR_TRACE(SIG_TYPE_E));
+#else
ExpectIntEQ(wc_ed25519ctx_sign_msg(msg, sizeof(msg), sig, &sigLen, &key,
ctx, sizeof(ctx)), 0);
verify_ok = 0;
ExpectIntEQ(wc_ed25519ctx_verify_msg(sig, sigLen, msg, sizeof(msg),
&verify_ok, &key, ctx, sizeof(ctx)), 0);
ExpectIntEQ(verify_ok, 1);
+#endif
/* Ed25519ph round trip via hash and via full message, type==Ed25519ph
* true side, WC_SHA512_DIGEST_SIZE length check false side (equal). */
diff --git a/tests/api/test_mldsa.c b/tests/api/test_mldsa.c
index eff7746520..d4e7d5c9d1 100644
--- a/tests/api/test_mldsa.c
+++ b/tests/api/test_mldsa.c
@@ -3293,7 +3293,10 @@ int test_mldsa_oneasymkey_version(void)
int test_mldsa_make_key_from_seed(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_MLDSA) && \
+ /* Uses caller-supplied keygen seeds (known-answer): in the FIPS module
+ * seed-input keygen is a CAVP/POST-only service (FIPS 204 sec 5.4), so this
+ * KAT runs only in non-FIPS or --enable-fipscavpseed builds. */
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_MLDSA) && \
!defined(WOLFSSL_MLDSA_NO_MAKE_KEY)
wc_MlDsaKey* key;
#ifndef WOLFSSL_NO_ML_DSA_44
@@ -12577,7 +12580,7 @@ int test_mldsa_sig_kats(void)
int test_mldsa_sign_ctx_kats(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_MLDSA) && \
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_MLDSA) && \
!defined(WOLFSSL_MLDSA_NO_SIGN)
wc_MlDsaKey* key;
word32 sigLen;
@@ -24602,7 +24605,7 @@ int test_mldsa_verify_kats(void)
int test_mldsa_sign_mu_kats(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \
(!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0))
wc_MlDsaKey* key = NULL;
word32 sigLen;
@@ -30881,9 +30884,9 @@ int test_wc_MldsaDecisionCoverage2(void)
XMEMSET(seed, 0, sizeof(seed));
ExpectIntEQ(wc_MlDsaKey_MakeKeyFromSeed(NULL, seed),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_MakeKeyFromSeed(&key, NULL),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
}
#endif
@@ -30938,60 +30941,63 @@ int test_wc_MldsaDecisionCoverage2(void)
/* wc_MlDsaKey_SignCtxWithSeed: four-way NULL OR + ctx/ctxLen. */
sigLen = (word32)sizeof(sig);
ExpectIntEQ(wc_MlDsaKey_SignCtxWithSeed(NULL, NULL, 0, sig, &sigLen,
- msg, (word32)sizeof(msg), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ msg, (word32)sizeof(msg), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxWithSeed(&key, NULL, 0, NULL, &sigLen,
- msg, (word32)sizeof(msg), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ msg, (word32)sizeof(msg), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxWithSeed(&key, NULL, 0, sig, NULL,
- msg, (word32)sizeof(msg), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ msg, (word32)sizeof(msg), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxWithSeed(&key, NULL, 0, sig, &sigLen,
- NULL, (word32)sizeof(msg), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ NULL, (word32)sizeof(msg), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxWithSeed(&key, NULL, 1, sig, &sigLen,
- msg, (word32)sizeof(msg), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ msg, (word32)sizeof(msg), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
/* wc_MlDsaKey_SignCtxHashWithSeed: five-way NULL OR (incl. seed)
* + ctx/ctxLen. */
sigLen = (word32)sizeof(sig);
ExpectIntEQ(wc_MlDsaKey_SignCtxHashWithSeed(NULL, NULL, 0, sig,
&sigLen, hash, sizeof(hash), WC_HASH_TYPE_SHA3_512, seed),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxHashWithSeed(&key, NULL, 0, NULL,
&sigLen, hash, sizeof(hash), WC_HASH_TYPE_SHA3_512, seed),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxHashWithSeed(&key, NULL, 0, sig,
NULL, hash, sizeof(hash), WC_HASH_TYPE_SHA3_512, seed),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxHashWithSeed(&key, NULL, 0, sig,
&sigLen, NULL, sizeof(hash), WC_HASH_TYPE_SHA3_512, seed),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxHashWithSeed(&key, NULL, 0, sig,
&sigLen, hash, sizeof(hash), WC_HASH_TYPE_SHA3_512, NULL),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignCtxHashWithSeed(&key, NULL, 1, sig,
&sigLen, hash, sizeof(hash), WC_HASH_TYPE_SHA3_512, seed),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
/* wc_MlDsaKey_SignMuWithSeed: five-way NULL OR + muLen!=MU_SZ. */
sigLen = (word32)sizeof(sig);
ExpectIntEQ(wc_MlDsaKey_SignMuWithSeed(NULL, sig, &sigLen, mu,
- sizeof(mu), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(mu), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignMuWithSeed(&key, NULL, &sigLen, mu,
- sizeof(mu), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(mu), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignMuWithSeed(&key, sig, NULL, mu,
- sizeof(mu), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(mu), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignMuWithSeed(&key, sig, &sigLen, NULL,
- sizeof(mu), seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(mu), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_SignMuWithSeed(&key, sig, &sigLen, mu,
- sizeof(mu), NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(mu), NULL), SEED_ARG_ERR(BAD_FUNC_ARG));
/* All five non-NULL, muLen != MLDSA_MU_SZ -> BAD_FUNC_ARG. */
ExpectIntEQ(wc_MlDsaKey_SignMuWithSeed(&key, sig, &sigLen, mu,
- sizeof(mu) - 1, seed), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(mu) - 1, seed), SEED_ARG_ERR(BAD_FUNC_ARG));
/* Sign path's too-small sigLen buffer guard (*sigLen <
* params->sigSz -> BUFFER_E), independent of the muLen guard
* above (all args otherwise valid). */
sigLen = 1;
+ /* v7 ML-DSA rejects an unset private key (prvKeySet) before the
+ * output-buffer-size check, so this returns BAD_FUNC_ARG rather than
+ * the BUFFER_E the un-hardened path returned. */
ExpectIntEQ(wc_MlDsaKey_SignMuWithSeed(&key, sig, &sigLen, mu,
- sizeof(mu), seed), WC_NO_ERR_TRACE(BUFFER_E));
+ sizeof(mu), seed), SEED_ARG_ERR(BAD_FUNC_ARG));
if (rngInited) {
DoExpectIntEQ(wc_FreeRng(&rng), 0);
diff --git a/tests/api/test_mlkem.c b/tests/api/test_mlkem.c
index 759f25f644..bff8cff5cf 100644
--- a/tests/api/test_mlkem.c
+++ b/tests/api/test_mlkem.c
@@ -38,7 +38,7 @@
int test_wc_mlkem_make_key_kats(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_MLKEM) && \
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_MLKEM) && \
!defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY)
MlKemKey* key;
#ifndef WOLFSSL_NO_ML_KEM_512
@@ -1497,7 +1497,7 @@ int test_wc_mlkem_make_key_kats(void)
int test_wc_mlkem_encapsulate_kats(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_MLKEM) && \
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_MLKEM) && \
!defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE)
MlKemKey* key;
#ifndef WOLFSSL_NO_ML_KEM_512
@@ -4300,11 +4300,11 @@ int test_wc_MlkemDecisionCoverage(void)
ExpectIntEQ(wc_MlKemKey_MakeKey(key, NULL),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlKemKey_MakeKeyWithRandom(NULL, rndMk, sizeof(rndMk)),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlKemKey_MakeKeyWithRandom(key, NULL, sizeof(rndMk)),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlKemKey_MakeKeyWithRandom(key, rndMk, sizeof(rndMk) - 1),
- WC_NO_ERR_TRACE(BUFFER_E));
+ SEED_ARG_ERR(BUFFER_E));
#endif
#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE
@@ -4319,15 +4319,15 @@ int test_wc_MlkemDecisionCoverage(void)
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* WithRandom: NULL operands + wrong length -> BUFFER_E. */
ExpectIntEQ(wc_MlKemKey_EncapsulateWithRandom(NULL, ct, ss, rndEnc,
- sizeof(rndEnc)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(rndEnc)), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlKemKey_EncapsulateWithRandom(key, NULL, ss, rndEnc,
- sizeof(rndEnc)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(rndEnc)), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlKemKey_EncapsulateWithRandom(key, ct, NULL, rndEnc,
- sizeof(rndEnc)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(rndEnc)), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlKemKey_EncapsulateWithRandom(key, ct, ss, NULL,
- sizeof(rndEnc)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(rndEnc)), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlKemKey_EncapsulateWithRandom(key, ct, ss, rndEnc,
- sizeof(rndEnc) - 1), WC_NO_ERR_TRACE(BUFFER_E));
+ sizeof(rndEnc) - 1), SEED_ARG_ERR(BUFFER_E));
#endif
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
@@ -4573,11 +4573,11 @@ int test_wc_mlkem_encapsulate_pubkey_unset_decision(void)
/* cond0 False: the len check at line 1513 fires first (BUFFER_E),
* short-circuiting the PUB_SET check away. */
ExpectIntEQ(wc_MlKemKey_EncapsulateWithRandom(key, ct, ss, randBuf,
- (int)sizeof(randBuf) - 1), WC_NO_ERR_TRACE(BUFFER_E));
+ (int)sizeof(randBuf) - 1), SEED_ARG_ERR(BUFFER_E));
/* cond0 True, cond1 True -> BAD_STATE_E. */
ExpectIntEQ(wc_MlKemKey_EncapsulateWithRandom(key, ct, ss, randBuf,
- (int)sizeof(randBuf)), WC_NO_ERR_TRACE(BAD_STATE_E));
+ (int)sizeof(randBuf)), SEED_ARG_ERR(BAD_STATE_E));
wc_MlKemKey_Free(key);
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
diff --git a/tests/api/test_slhdsa.c b/tests/api/test_slhdsa.c
index 0c1dbb3f52..070ef48a61 100644
--- a/tests/api/test_slhdsa.c
+++ b/tests/api/test_slhdsa.c
@@ -499,7 +499,7 @@ int test_wc_slhdsa_sizes(void)
int test_wc_slhdsa_make_key(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
SlhDsaKey key;
WC_RNG rng;
@@ -607,23 +607,23 @@ int test_wc_slhdsa_make_key(void)
/* Test NULL parameter handling. */
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(NULL, sk_seed,
sizeof(sk_seed), sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, NULL, sizeof(sk_seed),
sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed,
sizeof(sk_seed), NULL, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed,
sizeof(sk_seed), sk_prf, sizeof(sk_prf), NULL, sizeof(pk_seed)),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
/* Test wrong size. */
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed, 8,
sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed,
sizeof(sk_seed), sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)),
@@ -643,7 +643,7 @@ int test_wc_slhdsa_make_key(void)
int test_wc_slhdsa_sign(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
SlhDsaKey key;
WC_RNG rng;
byte msg[64];
@@ -725,10 +725,10 @@ int test_wc_slhdsa_sign(void)
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(NULL, ctx, sizeof(ctx),
msg, sizeof(msg), sig, &sigLen, addRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, ctx, sizeof(ctx),
msg, sizeof(msg), sig, &sigLen, NULL),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, ctx, sizeof(ctx),
msg, sizeof(msg), sig, &sigLen, addRnd), 0);
ExpectIntEQ(sigLen, expSigLen);
@@ -1019,7 +1019,7 @@ int test_wc_slhdsa_sign_vfy(void)
int test_wc_slhdsa_sign_hash(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
SlhDsaKey key;
WC_RNG rng;
byte hash[64];
@@ -1081,12 +1081,13 @@ int test_wc_slhdsa_sign_hash(void)
WC_HASH_TYPE_SHA256, sig, sigLen),
WC_NO_ERR_TRACE(BAD_LENGTH_E));
- /* Unsupported hashType (FIPS 205 doesn't list WC_HASH_TYPE_NONE) hits
- * the default branch of slhdsakey_validate_prehash. */
+ /* WC_HASH_TYPE_NONE (pure SLH-DSA sentinel) is never a valid pre-hash
+ * (FIPS 205 sec. 10.2.2 / Table 9), so HashSLH-DSA signing rejects it
+ * with an explicit BAD_FUNC_ARG. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_NONE, sig, &sigLen, &rng),
- WC_NO_ERR_TRACE(NOT_COMPILED_IN));
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* Test SignHash with SHA-256. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
@@ -1128,10 +1129,10 @@ int test_wc_slhdsa_sign_hash(void)
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(NULL, ctx, sizeof(ctx),
hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, addRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, ctx, sizeof(ctx),
hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, NULL),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, ctx, sizeof(ctx),
hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, addRnd), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
@@ -1200,7 +1201,7 @@ int test_wc_slhdsa_sign_hash(void)
int test_wc_slhdsa_sign_msg(void)
{
EXPECT_DECLS;
-#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
+#if defined(WOLFSSL_TEST_PQC_SEED_KAT) && defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
!defined(NO_SHA256)
SlhDsaKey key;
WC_RNG rng;
@@ -1248,14 +1249,14 @@ int test_wc_slhdsa_sign_msg(void)
/* SignMsgWithRandom NULL-arg checks. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(NULL, mprime, sizeof(mprime),
- sig, &sigLen, addRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sig, &sigLen, addRnd), SEED_ARG_ERR(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime),
- sig, &sigLen, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sig, &sigLen, NULL), SEED_ARG_ERR(BAD_FUNC_ARG));
/* SignMsgWithRandom must reject sigSz smaller than params->sigLen. */
sigLen = 1;
ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime),
- sig, &sigLen, addRnd), WC_NO_ERR_TRACE(BAD_LENGTH_E));
+ sig, &sigLen, addRnd), SEED_ARG_ERR(BAD_LENGTH_E));
/* Round-trip: WithRandom. Reset sigLen explicitly so the test doesn't
* silently rely on the previous call having set it to params->sigLen. */
@@ -2948,7 +2949,7 @@ int test_wc_SlhdsaDecisionCoverage(void)
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&zkey, dummyMsg,
sizeof(dummyMsg), dummyMsg, sizeof(dummyMsg), dummyMsg,
- sizeof(dummyMsg)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(dummyMsg)), SEED_ARG_ERR(BAD_FUNC_ARG));
{
byte sk_seed[TEST_SLHDSA_DEFAULT_SEED_LEN] = {0};
byte sk_prf[TEST_SLHDSA_DEFAULT_SEED_LEN] = {0};
@@ -2957,11 +2958,11 @@ int test_wc_SlhdsaDecisionCoverage(void)
/* sk_prf wrong length (sk_seed already right-length). */
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed,
sizeof(sk_seed), sk_prf, sizeof(sk_prf) - 1, pk_seed,
- sizeof(pk_seed)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(pk_seed)), SEED_ARG_ERR(BAD_FUNC_ARG));
/* pk_seed wrong length (sk_seed, sk_prf already right-length). */
ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed,
sizeof(sk_seed), sk_prf, sizeof(sk_prf), pk_seed,
- sizeof(pk_seed) - 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ sizeof(pk_seed) - 1), SEED_ARG_ERR(BAD_FUNC_ARG));
}
tinySigSz = 1;
@@ -2984,26 +2985,26 @@ int test_wc_SlhdsaDecisionCoverage(void)
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&zkey, NULL, 0, dummyMsg,
sizeof(dummyMsg), dummySig, &tinySigSz, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, dummyMsg, 0, NULL,
sizeof(dummyMsg), dummySig, &tinySigSz, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* msg==NULL */
+ SEED_ARG_ERR(BAD_FUNC_ARG)); /* msg==NULL */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, NULL, 0, dummyMsg,
sizeof(dummyMsg), NULL, &tinySigSz, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* sig==NULL */
+ SEED_ARG_ERR(BAD_FUNC_ARG)); /* sig==NULL */
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, NULL, 0, dummyMsg,
sizeof(dummyMsg), dummySig, NULL, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* sigSz==NULL */
+ SEED_ARG_ERR(BAD_FUNC_ARG)); /* sigSz==NULL */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, NULL, 5, dummyMsg,
sizeof(dummyMsg), dummySig, &tinySigSz, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* ctx==NULL && ctxSz>0 */
+ SEED_ARG_ERR(BAD_FUNC_ARG)); /* ctx==NULL && ctxSz>0 */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, NULL, 0, dummyMsg,
sizeof(dummyMsg), dummySig, &tinySigSz, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_LENGTH_E)); /* ctx==NULL, ctxSz==0 */
+ SEED_ARG_ERR(BAD_LENGTH_E)); /* ctx==NULL, ctxSz==0 */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&zkey, dummyMsg,
@@ -3013,18 +3014,18 @@ int test_wc_SlhdsaDecisionCoverage(void)
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&zkey, dummyMsg,
sizeof(dummyMsg), dummySig, &tinySigSz, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ SEED_ARG_ERR(BAD_FUNC_ARG));
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, NULL,
sizeof(dummyMsg), dummySig, &tinySigSz, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* mprime==NULL */
+ SEED_ARG_ERR(BAD_FUNC_ARG)); /* mprime==NULL */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, dummyMsg,
sizeof(dummyMsg), NULL, &tinySigSz, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* sig==NULL */
+ SEED_ARG_ERR(BAD_FUNC_ARG)); /* sig==NULL */
ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, dummyMsg,
sizeof(dummyMsg), dummySig, NULL, dummyAddRnd),
- WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* sigSz==NULL */
+ SEED_ARG_ERR(BAD_FUNC_ARG)); /* sigSz==NULL */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignHashDeterministic(&zkey, NULL, 0,
@@ -3033,15 +3034,15 @@ int test_wc_SlhdsaDecisionCoverage(void)
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&zkey, NULL, 0, dummyMsg,
sizeof(dummyMsg), WC_HASH_TYPE_SHA256, dummySig, &tinySigSz,
- dummyAddRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ dummyAddRnd), SEED_ARG_ERR(BAD_FUNC_ARG));
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, NULL, 5, dummyMsg,
sizeof(dummyMsg), WC_HASH_TYPE_SHA256, dummySig, &tinySigSz,
- dummyAddRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* ctx&&ctxSz>0 */
+ dummyAddRnd), SEED_ARG_ERR(BAD_FUNC_ARG)); /* ctx&&ctxSz>0 */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, NULL, 0, dummyMsg,
sizeof(dummyMsg), WC_HASH_TYPE_SHA256, dummySig, &tinySigSz,
- dummyAddRnd), WC_NO_ERR_TRACE(BAD_LENGTH_E)); /* ctxSz==0 */
+ dummyAddRnd), SEED_ARG_ERR(BAD_LENGTH_E)); /* ctxSz==0 */
/* hash/sig/sigSz==NULL: wc_SlhDsaKey_SignHashWithRandom bypasses the
* wrapper checks wc_SlhDsaKey_SignHash performs for these same
* operands (already covered in test_wc_slhdsa_sign_hash), so
@@ -3049,15 +3050,15 @@ int test_wc_SlhdsaDecisionCoverage(void)
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, dummyMsg, 0, NULL,
sizeof(dummyMsg), WC_HASH_TYPE_SHA256, dummySig, &tinySigSz,
- dummyAddRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* hash==NULL */
+ dummyAddRnd), SEED_ARG_ERR(BAD_FUNC_ARG)); /* hash==NULL */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, dummyMsg, 0,
dummyMsg, sizeof(dummyMsg), WC_HASH_TYPE_SHA256, NULL,
- &tinySigSz, dummyAddRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ &tinySigSz, dummyAddRnd), SEED_ARG_ERR(BAD_FUNC_ARG));
/* sig==NULL */
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, dummyMsg, 0,
dummyMsg, sizeof(dummyMsg), WC_HASH_TYPE_SHA256, dummySig, NULL,
- dummyAddRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* sigSz==NULL */
+ dummyAddRnd), SEED_ARG_ERR(BAD_FUNC_ARG)); /* sigSz==NULL */
tinySigSz = 1;
ExpectIntEQ(wc_SlhDsaKey_SignHash(&zkey, NULL, 0, dummyMsg,
diff --git a/wolfcrypt/benchmark/fips_cast_bench.c b/wolfcrypt/benchmark/fips_cast_bench.c
new file mode 100644
index 0000000000..215b3db311
--- /dev/null
+++ b/wolfcrypt/benchmark/fips_cast_bench.c
@@ -0,0 +1,340 @@
+/* fips_cast_bench.c
+ *
+ * Copyright (C) 2006-2026 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+/* FIPS CAST benchmark.
+ *
+ * Measures the wall-clock cost of each Conditional Algorithm Self-Test (CAST)
+ * in the wolfCrypt v7.0.0 FIPS module, so operators can budget power-on
+ * latency on constrained OEs.
+ *
+ * Citations:
+ * FIPS 140-3 sec 7.10 (Self-Tests) - CAST framework
+ * FIPS 140-3 IG 10.3.A - Algorithm-by-algorithm CAST coverage
+ * ISO/IEC 19790:2012 sec 7.10.2 - Conditional self-test execution
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#if !defined(WOLFSSL_USER_SETTINGS) && !defined(WOLFSSL_NO_OPTIONS_H)
+ #include
+#endif
+#include /* also picks up user_settings.h */
+
+/* wc_RunCast_fips() / wc_RunAllCast_fips() are v7.0.0-only; older module
+ * flavors would fail to link, so they use the empty-main stub below. */
+#if defined(HAVE_FIPS) && FIPS_VERSION3_GE(7,0,0)
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include
+#else
+ #include
+#endif
+
+
+#define BENCH_DEFAULT_ITERS 10
+
+/* Map FIPS_CAST_* enum value to a printable name. Kept in sync with
+ * wolfssl/wolfcrypt/fips_test.h FipsCastId enum. */
+static const char* cast_name(int id)
+{
+ switch (id) {
+ case FIPS_CAST_AES_CBC: return "AES-CBC";
+ case FIPS_CAST_AES_GCM: return "AES-GCM";
+ case FIPS_CAST_HMAC_SHA1: return "HMAC-SHA-1";
+ case FIPS_CAST_HMAC_SHA2_256: return "HMAC-SHA2-256";
+ case FIPS_CAST_HMAC_SHA2_512: return "HMAC-SHA2-512";
+ case FIPS_CAST_HMAC_SHA3_256: return "HMAC-SHA3-256";
+ case FIPS_CAST_DRBG: return "DRBG (SHA-256)";
+ case FIPS_CAST_RSA_SIGN_PKCS1v15: return "RSA-SIGN-PKCS1v15";
+ case FIPS_CAST_ECC_CDH: return "ECC-CDH";
+ case FIPS_CAST_ECC_PRIMITIVE_Z: return "ECC-Primitive-Z";
+ case FIPS_CAST_DH_PRIMITIVE_Z: return "DH-Primitive-Z";
+ case FIPS_CAST_ECDSA: return "ECDSA";
+ case FIPS_CAST_KDF_TLS12: return "KDF-TLS12";
+ case FIPS_CAST_KDF_TLS13: return "KDF-TLS13";
+ case FIPS_CAST_KDF_SSH: return "KDF-SSH";
+#if defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0)
+ case FIPS_CAST_KDF_SRTP: return "KDF-SRTP";
+ case FIPS_CAST_ED25519: return "Ed25519";
+ case FIPS_CAST_ED448: return "Ed448";
+ case FIPS_CAST_PBKDF2: return "PBKDF2";
+#endif
+#if defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(7,0)
+ case FIPS_CAST_AES_ECB: return "AES-ECB";
+ case FIPS_CAST_ML_KEM: return "ML-KEM";
+ case FIPS_CAST_ML_DSA: return "ML-DSA";
+ case FIPS_CAST_LMS: return "LMS";
+ case FIPS_CAST_XMSS: return "XMSS";
+ case FIPS_CAST_DRBG_SHA512: return "DRBG (SHA-512)";
+ case FIPS_CAST_SLH_DSA: return "SLH-DSA";
+ case FIPS_CAST_AES_CMAC: return "AES-CMAC";
+ case FIPS_CAST_SHAKE: return "SHAKE";
+ case FIPS_CAST_AES_KW: return "AES-KW";
+#endif
+ default: return "(unknown)";
+ }
+}
+
+
+/* Monotonic clock in nanoseconds. POSIX clock_gettime(CLOCK_MONOTONIC) on
+ * Unix-like systems; QueryPerformanceCounter on Windows. */
+static long long now_ns(void)
+{
+#ifdef _WIN32
+ static LARGE_INTEGER freq = { 0 };
+ LARGE_INTEGER count;
+ if (freq.QuadPart == 0)
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&count);
+ /* Multiply before divide to keep precision; freq is typically 10MHz. */
+ return (long long)((count.QuadPart * 1000000000LL) / freq.QuadPart);
+#else
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ return 0;
+ return (long long)ts.tv_sec * 1000000000LL + (long long)ts.tv_nsec;
+#endif
+}
+
+
+/* Run a single CAST iters times, populate stats (in milliseconds).
+ * Returns 0 on success, non-zero on first CAST failure. */
+static int run_one_cast(int id, int iters,
+ double* out_mean_ms, double* out_stddev_ms,
+ double* out_min_ms, double* out_max_ms)
+{
+ int i;
+ long long total = 0;
+ long long mn = LLONG_MAX;
+ long long mx = 0;
+ long long* samples;
+ double mean_ns;
+ double variance_acc = 0.0;
+
+ if (iters <= 0)
+ return BAD_FUNC_ARG;
+
+ samples = (long long*)XMALLOC((size_t)iters * sizeof(long long), NULL,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ if (samples == NULL)
+ return MEMORY_E;
+
+ for (i = 0; i < iters; i++) {
+ long long t0, t1, dt;
+ int rc;
+
+ t0 = now_ns();
+ rc = wc_RunCast_fips(id);
+ t1 = now_ns();
+ if (rc != 0) {
+ XFREE(samples, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return rc;
+ }
+ dt = t1 - t0;
+ if (dt < 0)
+ dt = 0;
+ samples[i] = dt;
+ total += dt;
+ if (dt < mn)
+ mn = dt;
+ if (dt > mx)
+ mx = dt;
+ }
+
+ mean_ns = (double)total / (double)iters;
+ for (i = 0; i < iters; i++) {
+ double d = (double)samples[i] - mean_ns;
+ variance_acc += d * d;
+ }
+ XFREE(samples, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+
+ *out_mean_ms = mean_ns / 1.0e6;
+ *out_stddev_ms = sqrt(variance_acc / (double)iters) / 1.0e6;
+ *out_min_ms = (double)mn / 1.0e6;
+ *out_max_ms = (double)mx / 1.0e6;
+ return 0;
+}
+
+
+static void usage(const char* prog)
+{
+ printf("usage: %s [-i ITERS] [-c CAST_ID] [-l]\n", prog);
+ printf(" -i ITERS iterations per CAST (default %d)\n",
+ BENCH_DEFAULT_ITERS);
+ printf(" -c CAST_ID benchmark only the named CAST id\n");
+ printf(" -l list CAST ids and names; do not run\n");
+ printf(" -h show this help\n");
+}
+
+
+int main(int argc, char** argv)
+{
+ int iters = BENCH_DEFAULT_ITERS;
+ int single = -1;
+ int list_only = 0;
+ int i;
+ int first, last;
+ int failures = 0;
+ int run_count = 0;
+ double total_mean_ms = 0.0;
+
+ for (i = 1; i < argc; i++) {
+ if (XSTRCMP(argv[i], "-i") == 0 && i + 1 < argc) {
+ iters = atoi(argv[++i]);
+ if (iters <= 0) {
+ fprintf(stderr, "-i requires a positive iteration count\n");
+ return 2;
+ }
+ } else if (XSTRCMP(argv[i], "-c") == 0 && i + 1 < argc) {
+ single = atoi(argv[++i]);
+ } else if (XSTRCMP(argv[i], "-l") == 0) {
+ list_only = 1;
+ } else if (XSTRCMP(argv[i], "-h") == 0
+ || XSTRCMP(argv[i], "--help") == 0) {
+ usage(argv[0]);
+ return 0;
+ } else {
+ fprintf(stderr, "unknown argument: %s\n", argv[i]);
+ usage(argv[0]);
+ return 2;
+ }
+ }
+
+ if (list_only) {
+ printf("FIPS CAST IDs (FIPS_CAST_COUNT = %d):\n", FIPS_CAST_COUNT);
+ for (i = 0; i < FIPS_CAST_COUNT; i++)
+ printf(" %2d %s\n", i, cast_name(i));
+ return 0;
+ }
+
+ if (single >= 0 && single >= FIPS_CAST_COUNT) {
+ fprintf(stderr, "CAST id %d out of range (0..%d)\n",
+ single, FIPS_CAST_COUNT - 1);
+ return 2;
+ }
+
+ printf("wolfCrypt FIPS CAST benchmark\n");
+ printf("Library version: %s\n", LIBWOLFSSL_VERSION_STRING);
+ printf("FIPS_CAST_COUNT: %d\n", FIPS_CAST_COUNT);
+ printf("Iterations per CAST: %d\n", iters);
+ printf("Clock: %s\n",
+#ifdef _WIN32
+ "QueryPerformanceCounter"
+#else
+ "clock_gettime(CLOCK_MONOTONIC)"
+#endif
+ );
+ printf("\n");
+
+ /* Under WC_RNG_SEED_CB the RNG needs a seed generator before _InitRng can
+ * build a working DRBG (mirrors benchmark.c and wolfcrypt/test/test.c). */
+#ifdef WC_RNG_SEED_CB
+ {
+ int seed_cb_rc = wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT);
+ if (seed_cb_rc != 0) {
+ fprintf(stderr,
+ "wc_SetSeed_Cb returned %d - DRBG-using CASTs will fail.\n",
+ seed_cb_rc);
+ }
+ }
+#endif
+
+ /* Prime every CAST once so each reaches FIPS_CAST_STATE_SUCCESS before
+ * measuring, isolating KAT runtime from the cold-CAST init chain. */
+ {
+ int prime_rc = wc_RunAllCast_fips();
+ if (prime_rc != 0) {
+ fprintf(stderr,
+ "wc_RunAllCast_fips() prime returned %d - some CASTs may have failed.\n"
+ "Per-CAST measurements continue but failed CASTs will report errors.\n\n",
+ prime_rc);
+ }
+ }
+
+ printf("ID | Name | Mean(ms) | StdDev(ms) | Min(ms) "
+ "| Max(ms)\n");
+ printf("---+---------------------+----------+------------+---------"
+ "+---------\n");
+
+ first = (single >= 0) ? single : 0;
+ last = (single >= 0) ? single + 1 : FIPS_CAST_COUNT;
+
+ for (i = first; i < last; i++) {
+ double mean_ms = 0, sd_ms = 0, mn_ms = 0, mx_ms = 0;
+ int rc = run_one_cast(i, iters, &mean_ms, &sd_ms, &mn_ms, &mx_ms);
+ if (rc != 0) {
+ printf("%2d | %-19s | FAILED rc=%d (%s)\n",
+ i, cast_name(i), rc, wc_GetErrorString(rc));
+ failures++;
+ continue;
+ }
+ printf("%2d | %-19s | %8.3f | %10.3f | %7.3f | %7.3f\n",
+ i, cast_name(i), mean_ms, sd_ms, mn_ms, mx_ms);
+ total_mean_ms += mean_ms;
+ run_count++;
+ }
+
+ printf("\n");
+ if (run_count > 0) {
+ printf("Sum of mean CAST times (one wc_RunAllCast_fips() pass): "
+ "%.3f ms\n", total_mean_ms);
+ }
+ if (failures > 0) {
+ printf("WARN: %d CAST(s) failed.\n", failures);
+ return 1;
+ }
+ return 0;
+}
+
+#else /* !(HAVE_FIPS && FIPS_VERSION3_GE(7,0,0)) */
+
+#include
+
+int main(void)
+{
+#ifndef HAVE_FIPS
+ fprintf(stderr,
+ "fips_cast_bench: built without HAVE_FIPS - nothing to measure\n");
+#else
+ fprintf(stderr,
+ "fips_cast_bench: requires v7.0.0+ FIPS module "
+ "(wc_RunCast_fips / wc_RunAllCast_fips were added in v7) - "
+ "nothing to measure on this older module flavor\n");
+#endif
+ return 0;
+}
+
+#endif /* HAVE_FIPS && FIPS_VERSION3_GE(7,0,0) */
diff --git a/wolfcrypt/benchmark/include.am b/wolfcrypt/benchmark/include.am
index 22cecbdaef..0854a485c3 100644
--- a/wolfcrypt/benchmark/include.am
+++ b/wolfcrypt/benchmark/include.am
@@ -10,6 +10,15 @@ wolfcrypt_benchmark_benchmark_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_
wolfcrypt_benchmark_benchmark_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
noinst_HEADERS += wolfcrypt/benchmark/benchmark.h
+# FIPS CAST benchmark - measures wc_RunCast_fips() execution time per CAST, so
+# operators can budget module power-on latency. Built only when FIPS is on.
+if BUILD_FIPS
+noinst_PROGRAMS += wolfcrypt/benchmark/fips_cast_bench
+wolfcrypt_benchmark_fips_cast_bench_SOURCES = wolfcrypt/benchmark/fips_cast_bench.c
+wolfcrypt_benchmark_fips_cast_bench_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD) -lm
+wolfcrypt_benchmark_fips_cast_bench_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
+endif
+
endif
endif
diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c
index 87db090536..5d4ae34d22 100644
--- a/wolfcrypt/src/aes.c
+++ b/wolfcrypt/src/aes.c
@@ -142,6 +142,12 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits
#include
+/* GCM asm is x86_64-only: the 32-bit aes_gcm_x86_asm.S is not PIC and its .text
+ * relocations break the in-core integrity check in a shared object. */
+#if defined(WOLFSSL_AESNI) && defined(WOLFSSL_X86_64_BUILD)
+ #define WC_AESNI_GCM
+#endif
+
#ifdef WOLF_CRYPTO_CB
#include
#endif
@@ -1100,6 +1106,116 @@ static void Check_CPU_support_HwCrypto(Aes* aes)
}
#endif /* __aarch64__ && !WOLFSSL_ARMASM_NO_HW_CRYPTO */
+/* In a Linux kernel module the 32-bit ARM AES asm must run between
+ * kernel_neon_begin()/end() or the first SIMD instruction faults. */
+#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && !defined(__aarch64__) && \
+ !defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)
+ static WC_INLINE void wc_svr_AES_set_key_AARCH32(const byte* userKey,
+ int keylen, byte* key, int dir) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_set_key_AARCH32(userKey, keylen, key, dir);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_encrypt_AARCH32(const byte* inBlock,
+ byte* outBlock, byte* key, int nr) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_encrypt_AARCH32(inBlock, outBlock, key, nr);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_decrypt_AARCH32(const byte* inBlock,
+ byte* outBlock, byte* key, int nr) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_decrypt_AARCH32(inBlock, outBlock, key, nr);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_encrypt_blocks_AARCH32(const byte* in,
+ byte* out, word32 sz, byte* key, int nr) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_encrypt_blocks_AARCH32(in, out, sz, key, nr);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_decrypt_blocks_AARCH32(const byte* in,
+ byte* out, word32 sz, byte* key, int nr) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_decrypt_blocks_AARCH32(in, out, sz, key, nr);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_CBC_encrypt_AARCH32(const byte* in,
+ byte* out, word32 sz, byte* reg, byte* key, int rounds) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_CBC_encrypt_AARCH32(in, out, sz, reg, key, rounds);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_CBC_decrypt_AARCH32(const byte* in,
+ byte* out, word32 sz, byte* reg, byte* key, int rounds) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_CBC_decrypt_AARCH32(in, out, sz, reg, key, rounds);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_CTR_encrypt_AARCH32(const byte* in,
+ byte* out, word32 sz, byte* reg, byte* key, byte* tmp, word32* left,
+ word32 rounds) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_CTR_encrypt_AARCH32(in, out, sz, reg, key, tmp, left, rounds);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_GCM_set_key_AARCH32(const byte* nonce,
+ const byte* key, byte* gcm_h, int nr) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_GCM_set_key_AARCH32(nonce, key, gcm_h, nr);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_GCM_encrypt_AARCH32(const byte* in,
+ byte* out, word32 sz, const byte* nonce, word32 nonceSz, byte* tag,
+ word32 tagSz, const byte* aad, word32 aadSz, byte* key, byte* gcm_h,
+ byte* tmp, byte* reg, int nr) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_GCM_encrypt_AARCH32(in, out, sz, nonce, nonceSz, tag, tagSz, aad,
+ aadSz, key, gcm_h, tmp, reg, nr);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE int wc_svr_AES_GCM_decrypt_AARCH32(const byte* in,
+ byte* out, word32 sz, const byte* nonce, word32 nonceSz, const byte* tag,
+ word32 tagSz, const byte* aad, word32 aadSz, byte* key, byte* gcm_h,
+ byte* tmp, byte* reg, int nr) {
+ int _ret, _svr = SAVE_VECTOR_REGISTERS2();
+ if (_svr != 0) return _svr;
+ _ret = AES_GCM_decrypt_AARCH32(in, out, sz, nonce, nonceSz, tag, tagSz,
+ aad, aadSz, key, gcm_h, tmp, reg, nr);
+ RESTORE_VECTOR_REGISTERS();
+ return _ret;
+ }
+ #define AES_set_key_AARCH32 wc_svr_AES_set_key_AARCH32
+ #define AES_encrypt_AARCH32 wc_svr_AES_encrypt_AARCH32
+ #define AES_decrypt_AARCH32 wc_svr_AES_decrypt_AARCH32
+ #define AES_encrypt_blocks_AARCH32 wc_svr_AES_encrypt_blocks_AARCH32
+ #define AES_decrypt_blocks_AARCH32 wc_svr_AES_decrypt_blocks_AARCH32
+ #define AES_CBC_encrypt_AARCH32 wc_svr_AES_CBC_encrypt_AARCH32
+ #define AES_CBC_decrypt_AARCH32 wc_svr_AES_CBC_decrypt_AARCH32
+ #define AES_CTR_encrypt_AARCH32 wc_svr_AES_CTR_encrypt_AARCH32
+ #define AES_GCM_set_key_AARCH32 wc_svr_AES_GCM_set_key_AARCH32
+ #define AES_GCM_encrypt_AARCH32 wc_svr_AES_GCM_encrypt_AARCH32
+ #define AES_GCM_decrypt_AARCH32 wc_svr_AES_GCM_decrypt_AARCH32
+ #ifdef WOLFSSL_AES_XTS
+ static WC_INLINE void wc_svr_AES_XTS_encrypt_AARCH32(const byte* in,
+ byte* out, word32 sz, const byte* i, byte* key, byte* key2, byte* tmp,
+ int nr) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_XTS_encrypt_AARCH32(in, out, sz, i, key, key2, tmp, nr);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ static WC_INLINE void wc_svr_AES_XTS_decrypt_AARCH32(const byte* in,
+ byte* out, word32 sz, const byte* i, byte* key, byte* key2, byte* tmp,
+ int nr) {
+ if (SAVE_VECTOR_REGISTERS2() != 0) return;
+ AES_XTS_decrypt_AARCH32(in, out, sz, i, key, key2, tmp, nr);
+ RESTORE_VECTOR_REGISTERS();
+ }
+ #define AES_XTS_encrypt_AARCH32 wc_svr_AES_XTS_encrypt_AARCH32
+ #define AES_XTS_decrypt_AARCH32 wc_svr_AES_XTS_decrypt_AARCH32
+ #endif /* WOLFSSL_AES_XTS */
+#endif /* WOLFSSL_USE_SAVE_VECTOR_REGISTERS && !__aarch64__ && !NO_HW_CRYPTO */
+
#if defined(WOLFSSL_AES_DIRECT) || defined(HAVE_AESCCM) || \
defined(WOLFSSL_AESGCM_STREAM) || defined(WOLFSSL_AESGCM_SIV)
static WARN_UNUSED_RESULT int wc_AesEncrypt(Aes* aes, const byte* inBlock,
@@ -4873,6 +4989,11 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock,
static int AesSetKey(Aes* aes, const byte* userKey, word32 keylen,
const byte* iv, int dir)
{
+ /* Reject invalid AES key lengths early (FIPS 197: 128/192/256 only);
+ * wc_AesSetKeyDirect only bounds-checks keylen. */
+ if (userKey == NULL || (keylen != 16 && keylen != 24 && keylen != 32)) {
+ return BAD_FUNC_ARG;
+ }
#if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \
defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \
defined(WOLFSSL_AES_CTS)
@@ -8154,8 +8275,10 @@ static WC_INLINE void IncrementGcmCounter(byte* inOutCtr)
#endif
#endif /* !FREESCALE_LTC_AES_GCM */
+/* arm32 armasm has no streaming GHASH asm: WOLFSSL_AESGCM_STREAM must compile
+ * this software GHASH, else GHASH_LEN_BLOCK's FlattenSzInBits is undefined. */
#if !defined(WOLFSSL_ARMASM) || defined(__aarch64__) || \
- defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)
+ defined(WOLFSSL_ARMASM_NO_HW_CRYPTO) || defined(WOLFSSL_AESGCM_STREAM)
#if defined(GCM_SMALL) || defined(GCM_TABLE) || defined(GCM_TABLE_4BIT)
static WC_INLINE void FlattenSzInBits(byte* buf, word32 sz)
@@ -8326,7 +8449,7 @@ void GenerateM0(Gcm* gcm)
#endif
#endif
-#if defined(WOLFSSL_AESNI) && defined(GCM_TABLE_4BIT) && \
+#if defined(WC_AESNI_GCM) && defined(GCM_TABLE_4BIT) && \
defined(WC_C_DYNAMIC_FALLBACK)
void GCM_generate_m0_aesni(const unsigned char *h, unsigned char *m)
XASM_LINK("GCM_generate_m0_aesni");
@@ -8420,6 +8543,23 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len)
#ifndef WOLFSSL_ARMASM_NO_HW_CRYPTO
#if !defined(__aarch64__)
AES_GCM_set_key_AARCH32(iv, (byte*)aes->key, aes->gcm.H, aes->rounds);
+ /* GenerateM0() is only compiled for arm32 armasm HW-crypto when
+ * AESGCM_STREAM is set; without this gate it is an undefined ref. */
+ #if defined(WOLFSSL_AESGCM_STREAM) && \
+ (defined(GCM_TABLE) || defined(GCM_TABLE_4BIT))
+ {
+ /* arm32 asm GCM keeps gcm->H bit-reflected for PMULL; M0 needs the
+ * standard H = E_K(0), else streaming GCM mis-authenticates. */
+ ALIGN16 byte gcmStdH[WC_AES_BLOCK_SIZE];
+ ALIGN16 byte gcmZero[WC_AES_BLOCK_SIZE];
+ XMEMSET(gcmZero, 0, WC_AES_BLOCK_SIZE);
+ XMEMCPY(gcmStdH, aes->gcm.H, WC_AES_BLOCK_SIZE);
+ AES_encrypt_AARCH32(gcmZero, aes->gcm.H, (byte*)aes->key,
+ (int)aes->rounds);
+ GenerateM0(&aes->gcm);
+ XMEMCPY(aes->gcm.H, gcmStdH, WC_AES_BLOCK_SIZE);
+ }
+ #endif
#else
if (aes->use_aes_hw_crypto && aes->use_pmull_hw_crypto) {
AES_GCM_set_key_AARCH64(iv, (byte*)aes->key, aes->gcm.H,
@@ -8470,7 +8610,7 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len)
if (ret == 0) {
#if defined(GCM_TABLE) || defined(GCM_TABLE_4BIT)
- #if defined(WOLFSSL_AESNI) && defined(GCM_TABLE_4BIT)
+ #if defined(WC_AESNI_GCM) && defined(GCM_TABLE_4BIT)
if (aes->use_aesni) {
#if defined(WC_C_DYNAMIC_FALLBACK)
#ifdef HAVE_INTEL_AVX2
@@ -8538,7 +8678,8 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len)
}
-#ifdef WOLFSSL_AESNI
+
+#ifdef WC_AESNI_GCM
void AES_GCM_encrypt_aesni(const unsigned char *in, unsigned char *out,
const unsigned char* addt, const unsigned char* ivec,
@@ -8626,8 +8767,10 @@ void AES_GCM_decrypt_vaes(const unsigned char *in, unsigned char *out,
#endif /* WOLFSSL_AESNI */
+/* As above: arm32 armasm has no streaming GHASH asm, so WOLFSSL_AESGCM_STREAM
+ * must compile these software GHASH symbols (no effect on x86 / arm64). */
#if !defined(WOLFSSL_ARMASM) || defined(__aarch64__) || \
- defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)
+ defined(WOLFSSL_ARMASM_NO_HW_CRYPTO) || defined(WOLFSSL_AESGCM_STREAM)
#if defined(WOLFSSL_RISCV_SCALAR_CRYPTO_ASM) && defined(HAVE_AESGCM) && \
!defined(WOLFSSL_RISCV_VECTOR_CRYPTO_ASM)
/* GHASH using the RISC-V scalar carryless-multiply (Zbc) helper. Vector crypto
@@ -11125,7 +11268,7 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz,
ret = AES_GCM_encrypt_ASM(aes, out, in, sz, iv, ivSz, authTag, authTagSz,
authIn, authInSz);
#else
-#ifdef WOLFSSL_AESNI
+#ifdef WC_AESNI_GCM
if (aes->use_aesni) {
#ifdef HAVE_INTEL_AVX512
if ((sz >= WC_AES_BLOCK_SIZE * WC_VAES_GCM_MIN_BLOCKS) &&
@@ -11771,7 +11914,7 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz,
const byte* authIn, word32 authInSz)
{
int ret;
-#ifdef WOLFSSL_AESNI
+#ifdef WC_AESNI_GCM
int res = WC_NO_ERR_TRACE(AES_GCM_AUTH_E);
#endif
@@ -11935,7 +12078,7 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz,
authTagSz, authIn, authInSz);
}
#else
-#ifdef WOLFSSL_AESNI
+#ifdef WC_AESNI_GCM
if (aes->use_aesni) {
#ifdef HAVE_INTEL_AVX512
if ((sz >= WC_AES_BLOCK_SIZE * WC_VAES_GCM_MIN_BLOCKS) &&
@@ -12002,6 +12145,13 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz,
VECTOR_REGISTERS_POP;
+ /* FIPS 140-3 / SP 800-38D: on authentication failure, decrypted-but-
+ * unauthenticated plaintext in `out` must not be released to the caller.
+ * All paths (AES-NI, AVX, ARM HW/NEON, C) funnel through `ret` here. */
+ if (ret == WC_NO_ERR_TRACE(AES_GCM_AUTH_E) && out != NULL && sz > 0) {
+ ForceZero(out, sz);
+ }
+
return ret;
}
#endif
@@ -12170,7 +12320,7 @@ static WARN_UNUSED_RESULT int AesGcmFinal_C(
return 0;
}
-#ifdef WOLFSSL_AESNI
+#ifdef WC_AESNI_GCM
#ifdef __cplusplus
extern "C" {
@@ -14014,7 +14164,7 @@ int wc_AesGcmInit(Aes* aes, const byte* key, word32 len, const byte* iv,
if (iv != NULL) {
/* Initialize with the IV. */
- #ifdef WOLFSSL_AESNI
+ #ifdef WC_AESNI_GCM
if (aes->use_aesni) {
ret = SAVE_VECTOR_REGISTERS2();
if (ret == 0) {
@@ -14154,7 +14304,7 @@ int wc_AesGcmEncryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz,
if (ret == 0) {
/* Encrypt with AAD and/or plaintext. */
- #ifdef WOLFSSL_AESNI
+ #ifdef WC_AESNI_GCM
if (aes->use_aesni) {
SAVE_VECTOR_REGISTERS(return _svr_ret;);
ret = AesGcmEncryptUpdate_aesni(aes, out, in, sz, authIn, authInSz);
@@ -14219,7 +14369,7 @@ int wc_AesGcmEncryptFinal(Aes* aes, byte* authTag, word32 authTagSz)
if (ret == 0) {
/* Calculate authentication tag. */
- #ifdef WOLFSSL_AESNI
+ #ifdef WC_AESNI_GCM
if (aes->use_aesni) {
SAVE_VECTOR_REGISTERS(return _svr_ret;);
ret = AesGcmEncryptFinal_aesni(aes, authTag, authTagSz);
@@ -14306,7 +14456,7 @@ int wc_AesGcmDecryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz,
if (ret == 0) {
/* Decrypt with AAD and/or cipher text. */
- #ifdef WOLFSSL_AESNI
+ #ifdef WC_AESNI_GCM
if (aes->use_aesni) {
SAVE_VECTOR_REGISTERS(return _svr_ret;);
ret = AesGcmDecryptUpdate_aesni(aes, out, in, sz, authIn, authInSz);
@@ -14369,7 +14519,7 @@ int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz)
if (ret == 0) {
/* Calculate authentication tag and compare with one passed in.. */
- #ifdef WOLFSSL_AESNI
+ #ifdef WC_AESNI_GCM
if (aes->use_aesni) {
SAVE_VECTOR_REGISTERS(return _svr_ret;);
ret = AesGcmDecryptFinal_aesni(aes, authTag, authTagSz);
@@ -14399,6 +14549,10 @@ int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz)
}
}
+ /* Final cannot zeroize prior Update output buffers (it does not see them).
+ * On AES_GCM_AUTH_E the caller must treat all Update-produced plaintext as
+ * invalid and wipe it. See PL-R34 Security Policy section 8 (Operational
+ * Rules). */
return ret;
}
#endif /* HAVE_AES_DECRYPT || HAVE_AESGCM_DECRYPT */
@@ -16926,8 +17080,14 @@ int wc_AesKeyUnWrap_ex(Aes *aes, const byte* in, word32 inSz, byte* out,
return ret;
/* verify IV */
- if (ConstantCompare(tmp, expIv, KEYWRAP_BLOCK_SIZE) != 0)
+ if (ConstantCompare(tmp, expIv, KEYWRAP_BLOCK_SIZE) != 0) {
+ /* ICV mismatch: out/tmp hold an unauthenticated candidate key; zeroize
+ * before returning so the keying material does not leak (ISO/IEC
+ * 19790:2012 7.9). */
+ ForceZero(out, inSz - KEYWRAP_BLOCK_SIZE);
+ ForceZero(tmp, sizeof(tmp));
return BAD_KEYWRAP_IV_E;
+ }
return (int)(inSz - KEYWRAP_BLOCK_SIZE);
}
@@ -18119,13 +18279,14 @@ int wc_AesXtsDecrypt(XtsAes* xaes, byte* out, const byte* in, word32 sz,
aes = &xaes->aes;
#endif
-/* FIPS TODO: SP800-38E - Restrict data unit to 2^20 blocks per key. A block is
- * WC_AES_BLOCK_SIZE or 16-bytes (128-bits). So each key may only be used to
- * protect up to 1,048,576 blocks of WC_AES_BLOCK_SIZE (16,777,216 bytes or
- * 134,217,728-bits) Add helpful printout and message along with BAD_FUNC_ARG
- * return whenever sz / WC_AES_BLOCK_SIZE > 1,048,576 or equal to that and sz is
- * not a sequence of complete blocks.
- */
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP800-38E - restrict the data unit to 2^20 blocks per key, as on the
+ * encrypt path above */
+ if (sz > FIPS_AES_XTS_MAX_BYTES_PER_TWEAK) {
+ WOLFSSL_MSG("Request exceeds allowed bytes per SP800-38E");
+ return BAD_FUNC_ARG;
+ }
+#endif
if (aes->keylen == 0) {
WOLFSSL_MSG("wc_AesXtsDecrypt called with unset decryption key.");
@@ -18381,6 +18542,15 @@ static int AesXtsDecryptUpdate(XtsAes* xaes, byte* out, const byte* in, word32 s
"in AesXtsDecryptUpdate().");
}
#endif
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP800-38E - 2^20 blocks per key, cumulative across Update calls */
+ if (stream->bytes_crypted_with_this_tweak >
+ FIPS_AES_XTS_MAX_BYTES_PER_TWEAK)
+ {
+ WOLFSSL_MSG("Request exceeds allowed bytes per SP800-38E");
+ return BAD_FUNC_ARG;
+ }
+#endif
{
#if defined(WOLFSSL_AESNI) && !defined(WOLFSSL_X86_BUILD)
@@ -18610,6 +18780,14 @@ int wc_local_CmacUpdateAes(struct Cmac *cmac, const byte* in, word32 inSz) {
#endif /* WOLFSSL_CMAC */
+/* AES-SIV (RFC 5297) and AES-EAX are not Approved and are not part of the FIPS
+ * module boundary; keep them out of a FIPS build so an unapproved mode cannot
+ * be pulled into the integrity-tested image. */
+#if FIPS_VERSION3_GE(7,0,0) && \
+ (defined(WOLFSSL_AES_SIV) || defined(WOLFSSL_AES_EAX))
+ #error "AES-SIV/AES-EAX are not part of the FIPS module; do not enable with --enable-fips"
+#endif
+
#ifdef WOLFSSL_AES_SIV
/*
diff --git a/wolfcrypt/src/aes_xts_x86_asm.S b/wolfcrypt/src/aes_xts_x86_asm.S
new file mode 100644
index 0000000000..0cc929478c
--- /dev/null
+++ b/wolfcrypt/src/aes_xts_x86_asm.S
@@ -0,0 +1,835 @@
+/* aes_xts_x86_asm
+ *
+ * Copyright (C) 2006-2026 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+#ifdef WOLFSSL_USER_SETTINGS
+#include "wolfssl/wolfcrypt/settings.h"
+#endif
+
+#ifndef HAVE_INTEL_AVX1
+#define HAVE_INTEL_AVX1
+#endif /* HAVE_INTEL_AVX1 */
+#ifndef NO_AVX2_SUPPORT
+#ifndef HAVE_INTEL_AVX2
+#define HAVE_INTEL_AVX2
+#endif /* HAVE_INTEL_AVX2 */
+#endif /* NO_AVX2_SUPPORT */
+
+#ifdef WOLFSSL_AES_XTS
+#ifdef WOLFSSL_X86_BUILD
+ # 32-bit (i386) AES-NI AES-XTS: single-block ports of the x86_64
+ # AES_XTS_*_aesni routines (xmm0-7, cdecl ABI); GF const on stack for PIC.
+ # void AES_XTS_init_aesni(unsigned char* i, const unsigned char* tweak_key,
+ # int tweak_nr);
+.text
+.globl AES_XTS_init_aesni
+.type AES_XTS_init_aesni,@function
+.align 16
+AES_XTS_init_aesni:
+ movl 4(%esp), %eax
+ movdqu (%eax), %xmm2
+ movl 8(%esp), %ecx
+ pxor (%ecx), %xmm2
+ movdqu 16(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 32(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 48(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 64(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 80(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 96(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 112(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 128(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 144(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ cmpl $11, 12(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_init_aesni_enclast_1
+ aesenc %xmm0, %xmm2
+ movdqu 176(%ecx), %xmm1
+ aesenc %xmm1, %xmm2
+ cmpl $13, 12(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_init_aesni_enclast_1
+ aesenc %xmm0, %xmm2
+ movdqu 208(%ecx), %xmm1
+ aesenc %xmm1, %xmm2
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_init_aesni_enclast_1:
+ aesenclast %xmm0, %xmm2
+ movdqu %xmm2, (%eax)
+ ret
+.size AES_XTS_init_aesni,.-AES_XTS_init_aesni
+ # void AES_XTS_encrypt_aesni(const unsigned char* in, unsigned char* out,
+ # word32 sz, const unsigned char* i, const unsigned char* key,
+ # const unsigned char* key2, int nr);
+.text
+.globl AES_XTS_encrypt_aesni
+.type AES_XTS_encrypt_aesni,@function
+.align 16
+AES_XTS_encrypt_aesni:
+ pushl %edi
+ pushl %ebx
+ subl $16, %esp
+ movl $0x87, (%esp)
+ movl $0x01, 4(%esp)
+ movl $0x01, 8(%esp)
+ movl $0x01, 12(%esp)
+ movdqu (%esp), %xmm6
+ movl 40(%esp), %eax
+ movdqu (%eax), %xmm2
+ movl 48(%esp), %ecx
+ pxor (%ecx), %xmm2
+ movdqu 16(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 32(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 48(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 64(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 80(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 96(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 112(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 128(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 144(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ cmpl $11, 52(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_aesni_enclast_2
+ aesenc %xmm0, %xmm2
+ movdqu 176(%ecx), %xmm1
+ aesenc %xmm1, %xmm2
+ cmpl $13, 52(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_aesni_enclast_2
+ aesenc %xmm0, %xmm2
+ movdqu 208(%ecx), %xmm1
+ aesenc %xmm1, %xmm2
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_encrypt_aesni_enclast_2:
+ aesenclast %xmm0, %xmm2
+ xorl %edi, %edi
+ movl 36(%esp), %edx
+ andl $0xfffffff0, %edx
+L_AES_XTS_encrypt_aesni_loop:
+ cmpl %edx, %edi
+ jge L_AES_XTS_encrypt_aesni_loop_done
+ movl 28(%esp), %eax
+ movdqu (%eax,%edi,1), %xmm3
+ pxor %xmm2, %xmm3
+ movl 44(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ cmpl $11, 52(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_aesni_enclast_3
+ aesenc %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesenc %xmm1, %xmm3
+ cmpl $13, 52(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_aesni_enclast_3
+ aesenc %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesenc %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_encrypt_aesni_enclast_3:
+ aesenclast %xmm0, %xmm3
+ pxor %xmm2, %xmm3
+ movl 32(%esp), %eax
+ movdqu %xmm3, (%eax,%edi,1)
+ movdqa %xmm2, %xmm4
+ psrad $31, %xmm4
+ pslld $0x01, %xmm2
+ pshufd $0x93, %xmm4, %xmm4
+ pand %xmm6, %xmm4
+ pxor %xmm4, %xmm2
+ addl $16, %edi
+ jmp L_AES_XTS_encrypt_aesni_loop
+L_AES_XTS_encrypt_aesni_loop_done:
+ movl 36(%esp), %eax
+ cmpl %eax, %edi
+ je L_AES_XTS_encrypt_aesni_done
+ subl $16, %edi
+ movl 32(%esp), %eax
+ movdqu (%eax,%edi,1), %xmm5
+ addl $16, %edi
+ movdqu %xmm5, (%esp)
+ xorl %edx, %edx
+L_AES_XTS_encrypt_aesni_cts:
+ movzbl (%esp,%edx,1), %ecx
+ movl 28(%esp), %eax
+ movzbl (%eax,%edi,1), %ebx
+ movl 32(%esp), %eax
+ movb %cl, (%eax,%edi,1)
+ movb %bl, (%esp,%edx,1)
+ incl %edi
+ incl %edx
+ movl 36(%esp), %eax
+ cmpl %eax, %edi
+ jl L_AES_XTS_encrypt_aesni_cts
+ subl %edx, %edi
+ movdqu (%esp), %xmm3
+ subl $16, %edi
+ pxor %xmm2, %xmm3
+ movl 44(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ cmpl $11, 52(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_aesni_enclast_4
+ aesenc %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesenc %xmm1, %xmm3
+ cmpl $13, 52(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_aesni_enclast_4
+ aesenc %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesenc %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_encrypt_aesni_enclast_4:
+ aesenclast %xmm0, %xmm3
+ pxor %xmm2, %xmm3
+ movl 32(%esp), %eax
+ movdqu %xmm3, (%eax,%edi,1)
+L_AES_XTS_encrypt_aesni_done:
+ addl $16, %esp
+ popl %ebx
+ popl %edi
+ ret
+.size AES_XTS_encrypt_aesni,.-AES_XTS_encrypt_aesni
+ # void AES_XTS_encrypt_update_aesni(const unsigned char* in,
+ # unsigned char* out, word32 sz, const unsigned char* key,
+ # unsigned char* i, int nr); Tweak is read (already encrypted) from *i
+ # and the advanced tweak written back to *i.
+.text
+.globl AES_XTS_encrypt_update_aesni
+.type AES_XTS_encrypt_update_aesni,@function
+.align 16
+AES_XTS_encrypt_update_aesni:
+ pushl %edi
+ pushl %ebx
+ subl $16, %esp
+ movl $0x87, (%esp)
+ movl $0x01, 4(%esp)
+ movl $0x01, 8(%esp)
+ movl $0x01, 12(%esp)
+ movdqu (%esp), %xmm6
+ movl 44(%esp), %eax
+ movdqu (%eax), %xmm2
+ xorl %edi, %edi
+ movl 36(%esp), %edx
+ andl $0xfffffff0, %edx
+L_AES_XTS_encrypt_update_aesni_loop:
+ cmpl %edx, %edi
+ jge L_AES_XTS_encrypt_update_aesni_loop_done
+ movl 28(%esp), %eax
+ movdqu (%eax,%edi,1), %xmm3
+ pxor %xmm2, %xmm3
+ movl 40(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ cmpl $11, 48(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_update_aesni_enclast_5
+ aesenc %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesenc %xmm1, %xmm3
+ cmpl $13, 48(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_update_aesni_enclast_5
+ aesenc %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesenc %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_encrypt_update_aesni_enclast_5:
+ aesenclast %xmm0, %xmm3
+ pxor %xmm2, %xmm3
+ movl 32(%esp), %eax
+ movdqu %xmm3, (%eax,%edi,1)
+ movdqa %xmm2, %xmm4
+ psrad $31, %xmm4
+ pslld $0x01, %xmm2
+ pshufd $0x93, %xmm4, %xmm4
+ pand %xmm6, %xmm4
+ pxor %xmm4, %xmm2
+ addl $16, %edi
+ jmp L_AES_XTS_encrypt_update_aesni_loop
+L_AES_XTS_encrypt_update_aesni_loop_done:
+ movl 36(%esp), %eax
+ cmpl %eax, %edi
+ je L_AES_XTS_encrypt_update_aesni_done
+ subl $16, %edi
+ movl 32(%esp), %eax
+ movdqu (%eax,%edi,1), %xmm5
+ addl $16, %edi
+ movdqu %xmm5, (%esp)
+ xorl %edx, %edx
+L_AES_XTS_encrypt_update_aesni_cts:
+ movzbl (%esp,%edx,1), %ecx
+ movl 28(%esp), %eax
+ movzbl (%eax,%edi,1), %ebx
+ movl 32(%esp), %eax
+ movb %cl, (%eax,%edi,1)
+ movb %bl, (%esp,%edx,1)
+ incl %edi
+ incl %edx
+ movl 36(%esp), %eax
+ cmpl %eax, %edi
+ jl L_AES_XTS_encrypt_update_aesni_cts
+ subl %edx, %edi
+ movdqu (%esp), %xmm3
+ subl $16, %edi
+ pxor %xmm2, %xmm3
+ movl 40(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesenc %xmm0, %xmm3
+ cmpl $11, 48(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_update_aesni_enclast_6
+ aesenc %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesenc %xmm1, %xmm3
+ cmpl $13, 48(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_encrypt_update_aesni_enclast_6
+ aesenc %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesenc %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_encrypt_update_aesni_enclast_6:
+ aesenclast %xmm0, %xmm3
+ pxor %xmm2, %xmm3
+ movl 32(%esp), %eax
+ movdqu %xmm3, (%eax,%edi,1)
+L_AES_XTS_encrypt_update_aesni_done:
+ movl 44(%esp), %eax
+ movdqu %xmm2, (%eax)
+ addl $16, %esp
+ popl %ebx
+ popl %edi
+ ret
+.size AES_XTS_encrypt_update_aesni,.-AES_XTS_encrypt_update_aesni
+ # void AES_XTS_decrypt_aesni(const unsigned char* in, unsigned char* out,
+ # word32 sz, const unsigned char* i, const unsigned char* key,
+ # const unsigned char* key2, int nr);
+.text
+.globl AES_XTS_decrypt_aesni
+.type AES_XTS_decrypt_aesni,@function
+.align 16
+AES_XTS_decrypt_aesni:
+ pushl %edi
+ pushl %ebx
+ subl $16, %esp
+ movl $0x87, (%esp)
+ movl $0x01, 4(%esp)
+ movl $0x01, 8(%esp)
+ movl $0x01, 12(%esp)
+ movdqu (%esp), %xmm6
+ movl 40(%esp), %eax
+ movdqu (%eax), %xmm2
+ movl 48(%esp), %ecx
+ pxor (%ecx), %xmm2
+ movdqu 16(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 32(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 48(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 64(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 80(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 96(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 112(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 128(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ movdqu 144(%ecx), %xmm0
+ aesenc %xmm0, %xmm2
+ cmpl $11, 52(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_aesni_enclast_7
+ aesenc %xmm0, %xmm2
+ movdqu 176(%ecx), %xmm1
+ aesenc %xmm1, %xmm2
+ cmpl $13, 52(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_aesni_enclast_7
+ aesenc %xmm0, %xmm2
+ movdqu 208(%ecx), %xmm1
+ aesenc %xmm1, %xmm2
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_decrypt_aesni_enclast_7:
+ aesenclast %xmm0, %xmm2
+ xorl %edi, %edi
+ movl 36(%esp), %eax
+ movl %eax, %edx
+ andl $0xfffffff0, %edx
+ cmpl %eax, %edx
+ je L_AES_XTS_decrypt_aesni_bound
+ subl $16, %edx
+L_AES_XTS_decrypt_aesni_bound:
+L_AES_XTS_decrypt_aesni_loop:
+ cmpl %edx, %edi
+ jge L_AES_XTS_decrypt_aesni_loop_done
+ movl 28(%esp), %eax
+ movdqu (%eax,%edi,1), %xmm3
+ pxor %xmm2, %xmm3
+ movl 44(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ cmpl $11, 52(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_aesni_declast_8
+ aesdec %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ cmpl $13, 52(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_aesni_declast_8
+ aesdec %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_decrypt_aesni_declast_8:
+ aesdeclast %xmm0, %xmm3
+ pxor %xmm2, %xmm3
+ movl 32(%esp), %eax
+ movdqu %xmm3, (%eax,%edi,1)
+ movdqa %xmm2, %xmm4
+ psrad $31, %xmm4
+ pslld $0x01, %xmm2
+ pshufd $0x93, %xmm4, %xmm4
+ pand %xmm6, %xmm4
+ pxor %xmm4, %xmm2
+ addl $16, %edi
+ jmp L_AES_XTS_decrypt_aesni_loop
+L_AES_XTS_decrypt_aesni_loop_done:
+ movl 36(%esp), %eax
+ cmpl %eax, %edi
+ je L_AES_XTS_decrypt_aesni_done
+ movdqa %xmm2, %xmm4
+ movdqa %xmm2, %xmm5
+ psrad $31, %xmm4
+ pslld $0x01, %xmm5
+ pshufd $0x93, %xmm4, %xmm4
+ pand %xmm6, %xmm4
+ pxor %xmm4, %xmm5
+ movl 28(%esp), %eax
+ movdqu (%eax,%edi,1), %xmm3
+ pxor %xmm5, %xmm3
+ movl 44(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ cmpl $11, 52(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_aesni_declast_9
+ aesdec %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ cmpl $13, 52(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_aesni_declast_9
+ aesdec %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_decrypt_aesni_declast_9:
+ aesdeclast %xmm0, %xmm3
+ pxor %xmm5, %xmm3
+ movdqu %xmm3, (%esp)
+ addl $16, %edi
+ xorl %edx, %edx
+L_AES_XTS_decrypt_aesni_cts:
+ movzbl (%esp,%edx,1), %ecx
+ movl 28(%esp), %eax
+ movzbl (%eax,%edi,1), %ebx
+ movl 32(%esp), %eax
+ movb %cl, (%eax,%edi,1)
+ movb %bl, (%esp,%edx,1)
+ incl %edi
+ incl %edx
+ movl 36(%esp), %eax
+ cmpl %eax, %edi
+ jl L_AES_XTS_decrypt_aesni_cts
+ subl %edx, %edi
+ movdqu (%esp), %xmm3
+ pxor %xmm2, %xmm3
+ movl 44(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ cmpl $11, 52(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_aesni_declast_10
+ aesdec %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ cmpl $13, 52(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_aesni_declast_10
+ aesdec %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_decrypt_aesni_declast_10:
+ aesdeclast %xmm0, %xmm3
+ pxor %xmm2, %xmm3
+ subl $16, %edi
+ movl 32(%esp), %eax
+ movdqu %xmm3, (%eax,%edi,1)
+L_AES_XTS_decrypt_aesni_done:
+ addl $16, %esp
+ popl %ebx
+ popl %edi
+ ret
+.size AES_XTS_decrypt_aesni,.-AES_XTS_decrypt_aesni
+ # void AES_XTS_decrypt_update_aesni(const unsigned char* in,
+ # unsigned char* out, word32 sz, const unsigned char* key,
+ # unsigned char* i, int nr); Tweak is read from *i and the advanced
+ # tweak written back to *i.
+.text
+.globl AES_XTS_decrypt_update_aesni
+.type AES_XTS_decrypt_update_aesni,@function
+.align 16
+AES_XTS_decrypt_update_aesni:
+ pushl %edi
+ pushl %ebx
+ subl $16, %esp
+ movl $0x87, (%esp)
+ movl $0x01, 4(%esp)
+ movl $0x01, 8(%esp)
+ movl $0x01, 12(%esp)
+ movdqu (%esp), %xmm6
+ movl 44(%esp), %eax
+ movdqu (%eax), %xmm2
+ xorl %edi, %edi
+ movl 36(%esp), %eax
+ movl %eax, %edx
+ andl $0xfffffff0, %edx
+ cmpl %eax, %edx
+ je L_AES_XTS_decrypt_update_aesni_bound
+ subl $16, %edx
+L_AES_XTS_decrypt_update_aesni_bound:
+L_AES_XTS_decrypt_update_aesni_loop:
+ cmpl %edx, %edi
+ jge L_AES_XTS_decrypt_update_aesni_loop_done
+ movl 28(%esp), %eax
+ movdqu (%eax,%edi,1), %xmm3
+ pxor %xmm2, %xmm3
+ movl 40(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ cmpl $11, 48(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_update_aesni_declast_11
+ aesdec %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ cmpl $13, 48(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_update_aesni_declast_11
+ aesdec %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_decrypt_update_aesni_declast_11:
+ aesdeclast %xmm0, %xmm3
+ pxor %xmm2, %xmm3
+ movl 32(%esp), %eax
+ movdqu %xmm3, (%eax,%edi,1)
+ movdqa %xmm2, %xmm4
+ psrad $31, %xmm4
+ pslld $0x01, %xmm2
+ pshufd $0x93, %xmm4, %xmm4
+ pand %xmm6, %xmm4
+ pxor %xmm4, %xmm2
+ addl $16, %edi
+ jmp L_AES_XTS_decrypt_update_aesni_loop
+L_AES_XTS_decrypt_update_aesni_loop_done:
+ movl 36(%esp), %eax
+ cmpl %eax, %edi
+ je L_AES_XTS_decrypt_update_aesni_done
+ movdqa %xmm2, %xmm4
+ movdqa %xmm2, %xmm5
+ psrad $31, %xmm4
+ pslld $0x01, %xmm5
+ pshufd $0x93, %xmm4, %xmm4
+ pand %xmm6, %xmm4
+ pxor %xmm4, %xmm5
+ movl 28(%esp), %eax
+ movdqu (%eax,%edi,1), %xmm3
+ pxor %xmm5, %xmm3
+ movl 40(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ cmpl $11, 48(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_update_aesni_declast_12
+ aesdec %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ cmpl $13, 48(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_update_aesni_declast_12
+ aesdec %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_decrypt_update_aesni_declast_12:
+ aesdeclast %xmm0, %xmm3
+ pxor %xmm5, %xmm3
+ movdqu %xmm3, (%esp)
+ addl $16, %edi
+ xorl %edx, %edx
+L_AES_XTS_decrypt_update_aesni_cts:
+ movzbl (%esp,%edx,1), %ecx
+ movl 28(%esp), %eax
+ movzbl (%eax,%edi,1), %ebx
+ movl 32(%esp), %eax
+ movb %cl, (%eax,%edi,1)
+ movb %bl, (%esp,%edx,1)
+ incl %edi
+ incl %edx
+ movl 36(%esp), %eax
+ cmpl %eax, %edi
+ jl L_AES_XTS_decrypt_update_aesni_cts
+ subl %edx, %edi
+ movdqu (%esp), %xmm3
+ pxor %xmm2, %xmm3
+ movl 40(%esp), %ecx
+ pxor (%ecx), %xmm3
+ movdqu 16(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 32(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 48(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 64(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 80(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 96(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 112(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 128(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ movdqu 144(%ecx), %xmm0
+ aesdec %xmm0, %xmm3
+ cmpl $11, 48(%esp)
+ movdqu 160(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_update_aesni_declast_13
+ aesdec %xmm0, %xmm3
+ movdqu 176(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ cmpl $13, 48(%esp)
+ movdqu 192(%ecx), %xmm0
+ jl L_AES_XTS_decrypt_update_aesni_declast_13
+ aesdec %xmm0, %xmm3
+ movdqu 208(%ecx), %xmm1
+ aesdec %xmm1, %xmm3
+ movdqu 224(%ecx), %xmm0
+L_AES_XTS_decrypt_update_aesni_declast_13:
+ aesdeclast %xmm0, %xmm3
+ pxor %xmm2, %xmm3
+ subl $16, %edi
+ movl 32(%esp), %eax
+ movdqu %xmm3, (%eax,%edi,1)
+L_AES_XTS_decrypt_update_aesni_done:
+ movl 44(%esp), %eax
+ movdqu %xmm2, (%eax)
+ addl $16, %esp
+ popl %ebx
+ popl %edi
+ ret
+.size AES_XTS_decrypt_update_aesni,.-AES_XTS_decrypt_update_aesni
+#endif /* WOLFSSL_X86_BUILD */
+#endif /* WOLFSSL_AES_XTS */
+
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/wolfcrypt/src/cpuid.c b/wolfcrypt/src/cpuid.c
index 35892b64af..8f224c3ae6 100644
--- a/wolfcrypt/src/cpuid.c
+++ b/wolfcrypt/src/cpuid.c
@@ -83,6 +83,12 @@
#define cpuid(a,b,c) __cpuidex((int*)a,b,c)
#endif /* _MSC_VER */
+ /* i386 kernel: #defines EAX/EBX/ECX/EDX as ptrace
+ * register indices, clashing with the cpuid array indices below. */
+ #undef EAX
+ #undef EBX
+ #undef ECX
+ #undef EDX
#define EAX 0
#define EBX 1
#define ECX 2
diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c
index 5a6daa9853..7976075a57 100644
--- a/wolfcrypt/src/dh.c
+++ b/wolfcrypt/src/dh.c
@@ -1443,8 +1443,17 @@ int wc_DhGeneratePublic(DhKey* key, byte* priv, word32 privSz,
#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_DH_KEYGEN)
if (ret == 0)
ret = _ffc_validate_public_key(key, pub, *pubSz, NULL, 0, 0);
- if (ret == 0)
- ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv, privSz);
+ if (ret == 0) {
+ /* FFC key-pair PCT per SP 800-56A r3 sec 5.6.2.1.4, required after
+ * KeyGen by FIPS 140-3 IG 10.3.B. Failure remaps to DH_PCT_E so
+ * DEGRADE_STATE moves FIPS_CAST_DH_PRIMITIVE_Z to the error state. */
+ ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv,
+ privSz);
+ #ifdef HAVE_FIPS
+ if (ret != 0)
+ ret = DH_PCT_E;
+ #endif
+ }
#endif /* FIPS V5 or later || WOLFSSL_VALIDATE_DH_KEYGEN */
return ret;
@@ -1467,8 +1476,17 @@ static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng,
#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_DH_KEYGEN)
if (ret == 0)
ret = _ffc_validate_public_key(key, pub, *pubSz, NULL, 0, 0);
- if (ret == 0)
- ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv, *privSz);
+ if (ret == 0) {
+ /* FFC key-pair PCT per SP 800-56A r3 sec 5.6.2.1.4, required after
+ * KeyGen by FIPS 140-3 IG 10.3.B. Failure remaps to DH_PCT_E so
+ * DEGRADE_STATE moves FIPS_CAST_DH_PRIMITIVE_Z to the error state. */
+ ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv,
+ *privSz);
+ #ifdef HAVE_FIPS
+ if (ret != 0)
+ ret = DH_PCT_E;
+ #endif
+ }
#endif /* FIPS V5 or later || WOLFSSL_VALIDATE_DH_KEYGEN */
return ret;
diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c
index 34acd96ca8..469679961c 100644
--- a/wolfcrypt/src/ecc.c
+++ b/wolfcrypt/src/ecc.c
@@ -5064,6 +5064,17 @@ int wc_ecc_shared_secret_gen_sync(ecc_key* private_key, ecc_point* point,
/* Use constant time map if compiled in */
err = ecc_map_ex(result, curve->prime, mp, 1);
}
+#if FIPS_VERSION3_GE(7,0,0)
+ if (err == MP_OKAY) {
+ /* SP 800-56A Rev 3 sec 5.7.1.2 step 2: the shared point shall not
+ * be the identity (point at infinity). ecc_map_ex maps the
+ * identity to (0,0) and returns success, so without this check an
+ * all-zero Z would be output with no error indicator */
+ if (wc_ecc_point_is_at_infinity(result)) {
+ err = ECC_INF_E;
+ }
+ }
+#endif
if (err == MP_OKAY) {
x = mp_unsigned_bin_size(curve->prime);
if (*outlen < (word32)x || x < mp_unsigned_bin_size(result->x)) {
@@ -5224,6 +5235,28 @@ int wc_ecc_shared_secret_ex(ecc_key* private_key, ecc_point* point,
return ECC_BAD_ARG_E;
}
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-131A Rev 2 sec 5 Table 5: EC key agreement with len(n) < 224
+ * provides < 112 bits of security strength and is Disallowed (there is no
+ * legacy-use provision for key agreement, unlike signature verification) */
+ if (private_key->dp->size < WC_ECC_FIPS_GEN_MIN) {
+ WOLFSSL_MSG("ECC curve too small for FIPS key agreement");
+ return ECC_CURVE_OID_E;
+ }
+ /* SP 800-56A Rev 3 sec 5.6.2.2.1 / 5.6.2.3.3, FIPS 140-3 IG D.F Scenario 2:
+ * assure the validity of the peer's public key before computing the shared
+ * secret. wc_ecc_point_is_on_curve enforces the [0, p-1] range and the
+ * curve equation; the identity is rejected in the gen_sync path and the
+ * approved curves have prime order (cofactor 1) so n*Q = O for every valid
+ * Q. Without this an off-curve peer point yields a real shared secret
+ * (invalid-curve attack). */
+ err = wc_ecc_point_is_on_curve(point, private_key->idx);
+ if (err != MP_OKAY) {
+ WOLFSSL_MSG("ECC peer public key failed validation");
+ return err;
+ }
+#endif
+
switch (private_key->state) {
case ECC_STATE_NONE:
case ECC_STATE_SHARED_SEC_GEN:
@@ -5397,7 +5430,7 @@ int wc_ecc_gen_k(WC_RNG* rng, int size, mp_int* k, mp_int* order)
}
/* generate 8 extra bytes to mitigate bias from the modulo operation below */
- /* see section A.1.2 in 'Suite B Implementor's Guide to FIPS 186-3 (ECDSA)' */
+ /* see section A.2.1 and A.4.1 in FIPS 186-5 (extra random bits) */
size += 8;
/* make up random string */
@@ -5776,6 +5809,16 @@ int wc_ecc_make_pub_ex(ecc_key* key, ecc_point* pubOut, WC_RNG* rng)
if (err == MP_OKAY) {
err = wc_ecc_curve_load(key->dp, &curve, ECC_CURVE_FIELD_ALL);
}
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-56A Rev3 sec 5.6.1.2.2: the private key d used to derive Q = dG
+ * shall satisfy 1 <= d <= n-1 */
+ if (err == MP_OKAY) {
+ if (mp_iszero(ecc_get_k(key)) || mp_isneg(ecc_get_k(key)) ||
+ mp_cmp(ecc_get_k(key), curve->order) != MP_LT) {
+ err = ECC_PRIV_KEY_E;
+ }
+ }
+#endif
if (err == MP_OKAY) {
err = ecc_make_pub_ex(key, curve, pubOut, rng);
}
@@ -5851,6 +5894,13 @@ static int _ecc_make_key_ex(WC_RNG* rng, int keysize, ecc_key* key,
#if FIPS_VERSION3_GE(6,0,0)
} /* end FIPS specific check */
#endif
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-131A: the FIPS curve-size floor set err but wc_ecc_set_curve was
+ * skipped, so key->dp is still unset -- return now, before the crypto-cb/HW
+ * backends below clobber err or dereference the unset key->dp. */
+ if (err != 0)
+ return err;
+#endif
key->flags = (byte)flags;
#if defined(WOLF_CRYPTO_CB) && defined(HAVE_ECC_DHE)
@@ -9265,6 +9315,15 @@ static int wc_ecc_check_r_s_range(ecc_key* key, mp_int* r, mp_int* s)
if (mp_iszero(r) || mp_iszero(s)) {
err = MP_ZERO_E;
}
+#if FIPS_VERSION3_GE(7,0,0)
+ /* FIPS 186-5 sec 6.4.2 step 1: r and s shall be in [1, n-1]. Under
+ * WOLFSSL_SP_INT_NEGATIVE a negative s passes the sign-aware upper-bound
+ * compare below, so (r, s-n) would verify as a valid signature; reject a
+ * negative value explicitly */
+ if ((err == 0) && (mp_isneg(r) || mp_isneg(s))) {
+ err = MP_VAL;
+ }
+#endif
if ((err == 0) && (mp_cmp(r, curve->order) != MP_LT)) {
err = MP_VAL;
}
@@ -10928,8 +10987,10 @@ static int ecc_check_privkey_gen(ecc_key* key, mp_int* a, mp_int* prime)
#endif /* FIPS_VERSION_GE(5,0) || WOLFSSL_VALIDATE_ECC_KEYGEN ||
* (!WOLFSSL_SP_MATH && WOLFSSL_VALIDATE_ECC_IMPORT) */
+/* The PCT (IG 10.3.A) uses base-point mul + ECDSA sign/verify, not ECDH, so this
+ * must match the decl/call guards (no HAVE_ECC_DHE) or -DNO_ECC_DHE fails to link. */
#if (FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN)) && \
- !defined(WOLFSSL_KCAPI_ECC) && defined(HAVE_ECC_DHE)
+ !defined(WOLFSSL_KCAPI_ECC)
/* check privkey generator helper, creates prime needed */
static int ecc_check_privkey_gen_helper(ecc_key* key)
@@ -11039,7 +11100,7 @@ static int _ecc_pairwise_consistency_test(ecc_key* key, WC_RNG* rng)
return err;
}
#endif /* (FIPS v5 or later || WOLFSSL_VALIDATE_ECC_KEYGEN) && \
- !WOLFSSL_KCAPI_ECC && HAVE_ECC_DHE */
+ !WOLFSSL_KCAPI_ECC */
#ifndef WOLFSSL_SP_MATH
/* validate order * pubkey = point at infinity, 0 on success */
@@ -12183,6 +12244,14 @@ static int _ecc_import_private_key_ex(const byte* priv, word32 privSz,
ret = mp_sub_d(order, 1, order);
}
#endif
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-56A Rev 3 sec 5.6.2.1.2 / FIPS 186-5 sec 6.4.1: an imported
+ * private key shall be an integer in [1, n-1]. The upper bound alone
+ * lets d = 0 through, and ECDSA would then sign with it */
+ if ((ret == 0) && (mp_iszero(key->k) || mp_isneg(key->k))) {
+ ret = ECC_PRIV_KEY_E;
+ }
+#endif
if ((ret == 0) && (mp_cmp(key->k, order) != MP_LT)) {
ret = ECC_PRIV_KEY_E;
}
@@ -15682,7 +15751,14 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
#endif
break;
}
- #if !defined(NO_AES) && defined(HAVE_AESGCM)
+ /* SP 800-38D 8.2 / IG C.H: the module must construct the GCM IV, but the
+ * ECIES DEM passes a KDF-derived one. Gate on upstream's own condition
+ * so an approved build simply does not offer the encrypt DEM
+ * (ecAES_*_GCM then falls to default: BAD_FUNC_ARG) rather than relaxing
+ * the control. LOCAL to Part3 pending upstream's own fix. */
+ #if !defined(NO_AES) && defined(HAVE_AESGCM) && \
+ (!defined(HAVE_FIPS) || \
+ defined(WC_FIPS_AESGCM_ONE_SHOT_EXT_IV_ALLOWED))
case ecAES_128_GCM:
case ecAES_256_GCM:
{
diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c
index 4a0bc1257d..99fbf81f70 100644
--- a/wolfcrypt/src/ed25519.c
+++ b/wolfcrypt/src/ed25519.c
@@ -390,6 +390,11 @@ int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey,
}
#endif /* WOLF_CRYPTO_CB_ONLY_ED25519 */
+#ifndef WOLF_CRYPTO_CB_ONLY_ED25519
+ /* az holds the clamped secret scalar; zeroize before return
+ * (ISO/IEC 19790:2012 7.9). */
+ ForceZero(az, sizeof(az));
+#endif
return ret;
}
diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c
index 08374ba446..4f928a49c3 100644
--- a/wolfcrypt/src/ed448.c
+++ b/wolfcrypt/src/ed448.c
@@ -353,6 +353,10 @@ int wc_ed448_make_public(ed448_key* key, unsigned char* pubKey, word32 pubKeySz)
key->pubKeySet = 1;
}
+ /* az holds the clamped secret scalar; zeroize before return
+ * (ISO/IEC 19790:2012 7.9). */
+ ForceZero(az, sizeof(az));
+
return ret;
}
@@ -832,6 +836,46 @@ static int ed448_verify_msg_final_with_sha(const byte* sig, word32 sigLen,
return BAD_FUNC_ARG;
}
+#if FIPS_VERSION3_GE(7,0,0)
+ /* Reject a non-canonical public-key encoding (y-ordinate >= p,
+ * p = 2^448-2^224-1 = 0xff..fe..ff). FIPS 186-5 sec 7.7 step 1 requires
+ * the decoding to fail for y >= p; the point decoder reduces mod p instead
+ * of rejecting, so enforce the range here (mirrors wc_ed448_check_key). */
+ {
+ int j;
+ int rangeRet = PUBLIC_KEY_E;
+ /* Check top part before 0xFE. */
+ for (j = ED448_PUB_KEY_SIZE - 1; j > ED448_PUB_KEY_SIZE/2; j--) {
+ if (key->p[j] < 0xff) {
+ rangeRet = 0;
+ break;
+ }
+ }
+ if (rangeRet == WC_NO_ERR_TRACE(PUBLIC_KEY_E)) {
+ /* Check against 0xFE. */
+ if (key->p[ED448_PUB_KEY_SIZE/2] < 0xfe) {
+ rangeRet = 0;
+ }
+ else if (key->p[ED448_PUB_KEY_SIZE/2] == 0xfe) {
+ /* Check bottom part before last byte. */
+ for (j = ED448_PUB_KEY_SIZE/2 - 1; j > 0; j--) {
+ if (key->p[j] != 0xff) {
+ rangeRet = 0;
+ break;
+ }
+ }
+ /* Check last byte. */
+ if ((rangeRet == WC_NO_ERR_TRACE(PUBLIC_KEY_E)) &&
+ (key->p[0] < 0xff)) {
+ rangeRet = 0;
+ }
+ }
+ }
+ if (rangeRet != 0)
+ return BAD_FUNC_ARG;
+ }
+#endif /* FIPS_VERSION3_GE(7,0,0) */
+
/* uncompress A (public key), test if valid, and negate it */
if (ge448_from_bytes_negate_vartime(&A, key->p) != 0)
return BAD_FUNC_ARG;
diff --git a/wolfcrypt/src/ge_operations.c b/wolfcrypt/src/ge_operations.c
index a59a8e4846..ee9c0b6c27 100644
--- a/wolfcrypt/src/ge_operations.c
+++ b/wolfcrypt/src/ge_operations.c
@@ -10200,9 +10200,11 @@ void ge_tobytes_nct(unsigned char *s,const ge_p2 *h)
/* if HAVE_ED25519 but not HAVE_CURVE25519, and an asm implementation is built,
* then curve25519() won't get its WOLFSSL_LOCAL attribute unless we dummy-call
* it here.
- */
+ * The 32-bit ARM asm port gates curve25519() on HAVE_CURVE25519, so the
+ * dummy-call would be an undefined symbol there -- exclude arm32 armasm. */
#if defined(CURVED25519_ASM) && defined(WOLFSSL_API_PREFIX_MAP) && \
- !defined(HAVE_CURVE25519) && !defined(FREESCALE_LTC_ECC)
+ !defined(HAVE_CURVE25519) && !defined(FREESCALE_LTC_ECC) && \
+ (!defined(WOLFSSL_ARMASM) || defined(__aarch64__))
WOLFSSL_LOCAL void _wc_curve25519_dummy(void);
WOLFSSL_LOCAL void _wc_curve25519_dummy(void) {
(void)curve25519((byte *)0, (byte *)0, (const byte *)0);
diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c
index e9d10166c3..5f60fcbabc 100644
--- a/wolfcrypt/src/kdf.c
+++ b/wolfcrypt/src/kdf.c
@@ -812,6 +812,8 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz,
ret = _HashFinal(enmhashId, &hash, lastBlock);
if (ret == 0)
XMEMCPY(key, lastBlock, remainder);
+ /* lastBlock held derived key material (ISO/IEC 19790 7.9). */
+ ForceZero(lastBlock, sizeof(lastBlock));
}
}
else {
@@ -857,6 +859,8 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz,
ret = _HashFinal(enmhashId, &hash, lastBlock);
if (ret == 0)
XMEMCPY(key + runningKeySz, lastBlock, remainder);
+ /* lastBlock held derived key material (ISO/IEC 19790 7.9). */
+ ForceZero(lastBlock, sizeof(lastBlock));
}
}
}
@@ -960,6 +964,8 @@ static int wc_srtp_kdf_derive_key(byte* block, int idxSz, byte label,
/* Copy into key required amount. */
XMEMCPY(key, enc, keySz);
}
+ /* enc held a derived SRTP key block (ISO/IEC 19790 7.9). */
+ ForceZero(enc, sizeof(enc));
}
/* XOR out label. */
block[WC_SRTP_MAX_SALT - idxSz - 1] ^= label;
diff --git a/wolfcrypt/src/port/arm/armv8-32-sha3-asm.S b/wolfcrypt/src/port/arm/armv8-32-sha3-asm.S
index 4f0a420398..de0abc9135 100644
--- a/wolfcrypt/src/port/arm/armv8-32-sha3-asm.S
+++ b/wolfcrypt/src/port/arm/armv8-32-sha3-asm.S
@@ -30,6 +30,12 @@
#include
+/* Honor WC_SHA3_NO_ASM as sha3.c does: suppress this NEON BlockSha3 so it
+ * doesn't multiply-define against sha3.c's C BlockSha3 on arm32. */
+#ifdef WC_SHA3_NO_ASM
+ #undef WOLFSSL_ARMASM
+#endif
+
#ifdef WOLFSSL_ARMASM
#if !defined(__aarch64__) && !defined(WOLFSSL_ARMASM_THUMB2)
#ifndef WOLFSSL_ARMASM_INLINE
diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c
index e65182dffc..647cfed43c 100644
--- a/wolfcrypt/src/random.c
+++ b/wolfcrypt/src/random.c
@@ -307,6 +307,9 @@ static wc_RngSeed_Cb seedCb = wc_GenerateSeed;
static wc_RngSeed_Cb seedCb = NULL;
#endif
+/* Install the global entropy-seed callback. Set ONCE at startup, before any
+ * RNG use or threads: seedCb is a shared global, so changing it concurrently
+ * with wc_InitRng()/reseed is a data race. Use one entropy source at a time. */
int wc_SetSeed_Cb(wc_RngSeed_Cb cb)
{
seedCb = cb;
@@ -337,6 +340,12 @@ int wc_SetSeed_Cb(wc_RngSeed_Cb cb)
#error RNG_MAX_BLOCK_LEN is larger than NIST DBRG max request length
#endif
+/* SP 800-90A Rev1: FIPS output must come from the validated Hash_DRBG; RDRAND
+ * as preferred source bypasses it (CUSTOM_RAND_GENERATE_BLOCK: see fips.h). */
+#if FIPS_VERSION3_GE(7,0,0) && defined(HAVE_INTEL_RDRAND)
+ #error "FIPS v7: HAVE_INTEL_RDRAND bypasses the validated Hash_DRBG (SP 800-90A)"
+#endif
+
enum {
drbgInitC = 0,
drbgReseed = 1,
@@ -425,6 +434,16 @@ int wc_DrbgState_MutexFree(void)
static int LockDrbgState(void)
{
#ifndef SINGLE_THREADED
+#ifndef WOLFSSL_MUTEX_INITIALIZER
+ /* No static mutex initializer (e.g. Windows CRITICAL_SECTION): the FIPS
+ * pre-operational self-test locks this from a load-time constructor that
+ * runs before wolfCrypt_Init(), so init on demand here. */
+ {
+ int initRet = wc_DrbgState_MutexInit();
+ if (initRet != 0)
+ return initRet;
+ }
+#endif
return wc_LockMutex(&drbgStateMutex);
#else
return 0;
@@ -642,6 +661,12 @@ static int Hash_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz
* remain available to SHA-512-only builds */
/* Returns: DRBG_SUCCESS and DRBG_FAILURE or BAD_FUNC_ARG on fail */
+/* Reseed the DRBG from a caller-supplied buffer. Unlike the internal reseed
+ * path (PollAndReSeed), which runs wc_RNG_TestSeed() over the entropy it
+ * gathers, the Module does NOT health-test this buffer: caller-supplied reseed
+ * entropy SHALL come from an SP 800-90B compliant source with its own health
+ * tests and a suitable ESV, per the seed-source obligation in the Security
+ * Policy. */
int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz)
{
if (rng == NULL || seed == NULL) {
@@ -791,6 +816,12 @@ static int Hash_gen(DRBG_internal* drbg, byte* out, word32 outSz, const byte* V)
defined(WOLFSSL_CHECK_MEM_ZERO)
wc_MemZero_Check(data, DRBG_SEED_LEN);
#endif
+#if FIPS_VERSION3_GE(7,0,0)
+ /* digest held the final Hashgen output block; zeroize it before free/return
+ * (ISO 19790 sec 7.9). Hash_df and Hash_DRBG_Generate already do this;
+ * Hash_gen was the outlier. v7.0.0+ only. */
+ ForceZero(digest, WC_SHA256_DIGEST_SIZE);
+#endif
#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
@@ -1366,6 +1397,10 @@ static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
defined(WOLFSSL_CHECK_MEM_ZERO)
wc_MemZero_Check(data, DRBG_SHA512_SEED_LEN);
#endif
+#if FIPS_VERSION3_GE(7,0,0)
+ /* As Hash_gen above: zeroize the final output block held in digest. */
+ ForceZero(digest, WC_SHA512_DIGEST_SIZE);
+#endif
#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
@@ -1615,6 +1650,37 @@ static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg)
#define WC_RNG_SEED_APT_CUTOFF 325
#endif
+#if FIPS_VERSION3_GE(7,0,0)
+/* The calibrated (window, cutoff) pair must satisfy FIPS 140-3 IG D.K
+ * Resolution 16 (C <= W) at the full window. */
+wc_static_assert(WC_RNG_SEED_APT_CUTOFF <= WC_RNG_SEED_APT_WINDOW);
+
+/* wc_RNG_TestSeed's byteCounts[] is indexed by seed byte value [0,255], so the
+ * array (sized MAX_ENTROPY_BITS) must hold at least 256 entries. */
+wc_static_assert(MAX_ENTROPY_BITS >= 256);
+
+/* Integer square root (floor). Used to recompute the APT cutoff for a window
+ * shorter than WC_RNG_SEED_APT_WINDOW (see wc_RNG_TestSeed). */
+static word32 wc_rng_apt_isqrt(word32 n)
+{
+ word32 root = 0;
+ word32 bit = (word32)1 << 30;
+ while (bit > n)
+ bit >>= 2;
+ while (bit != 0) {
+ if (n >= root + bit) {
+ n -= root + bit;
+ root = (root >> 1) + bit;
+ }
+ else {
+ root >>= 1;
+ }
+ bit >>= 2;
+ }
+ return root;
+}
+#endif
+
int wc_RNG_TestSeed(const byte* seed, word32 seedSz)
{
int ret = 0;
@@ -1670,6 +1736,22 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz)
word32 windowSize = min(seedSz, (word32)WC_RNG_SEED_APT_WINDOW);
word32 windowStart = 0;
word32 newIdx;
+#if FIPS_VERSION3_GE(7,0,0)
+ /* FIPS 140-3 IG D.K Resolution 16 (IG 2026-04-16): the APT cutoff C
+ * shall be no larger than the window W. A seed shorter than
+ * WC_RNG_SEED_APT_WINDOW clamps the window to seedSz, so the cutoff
+ * calibrated for W=512 would exceed W and the test could never fire
+ * (a 132-byte window can never reach a count of 325). Recompute C for
+ * the actual window on the same H=1, alpha=2^-30 basis used for the
+ * calibrated value: for Binomial(W,1/2) the (1-alpha) tail is about
+ * W/2 + 3*sqrt(W). The full-window case keeps the calibrated constant
+ * unchanged, so validated behavior is untouched */
+ word32 aptCutoff = (windowSize >= (word32)WC_RNG_SEED_APT_WINDOW)
+ ? (word32)WC_RNG_SEED_APT_CUTOFF
+ : (1 + windowSize / 2 + 3 * wc_rng_apt_isqrt(windowSize));
+#else
+ word32 aptCutoff = (word32)WC_RNG_SEED_APT_CUTOFF;
+#endif
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
byteCounts = (word16*)XMALLOC(MAX_ENTROPY_BITS * sizeof(word16), NULL,
@@ -1686,7 +1768,7 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz)
/* Check first window - scan all 256 counts */
for (i = 0; i < MAX_ENTROPY_BITS; i++) {
- aptFailed |= (byteCounts[i] >= WC_RNG_SEED_APT_CUTOFF);
+ aptFailed |= (byteCounts[i] >= aptCutoff);
}
/* Slide window through remaining seed data */
@@ -1700,7 +1782,7 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz)
byteCounts[seed[newIdx]]++;
/* Accumulate failure flag for new byte's count */
- aptFailed |= (byteCounts[seed[newIdx]] >= WC_RNG_SEED_APT_CUTOFF);
+ aptFailed |= (byteCounts[seed[newIdx]] >= aptCutoff);
}
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
@@ -1923,7 +2005,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
#endif
#ifdef HAVE_INTEL_RDRAND
- /* if CPU supports RDRAND, use it directly and by-pass DRBG init */
+ /* if CPU supports RDRAND, use it directly and bypass DRBG init */
if (IS_INTEL_RDRAND(intel_flags)) {
#ifdef HAVE_HASHDRBG
rng->status = DRBG_OK;
@@ -1975,6 +2057,16 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
rng->drbg = (struct DRBG*)&rng->drbg_data;
#endif /* WOLFSSL_NO_MALLOC or WOLFSSL_STATIC_MEMORY */
+ #if FIPS_VERSION3_GE(7,0,0)
+ /* Zero the freshly-allocated DRBG state so that a non-NULL rng->drbg is
+ * always safe to Hash_DRBG_Uninstantiate (which frees the SHA-256
+ * context) on the error-cleanup path below, even when instantiation is
+ * never reached */
+ if (rng->drbg != NULL) {
+ XMEMSET(rng->drbg, 0, sizeof(DRBG_internal));
+ }
+ #endif
+
#ifdef WOLFSSL_SMALL_STACK_CACHE
if (ret == 0) {
rng->drbg_scratch =
@@ -2028,6 +2120,14 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
rng->drbg512 = (struct DRBG_SHA512*)&rng->drbg512_data;
#endif
+ #if FIPS_VERSION3_GE(7,0,0)
+ /* As above for the SHA-512 DRBG: zero freshly-allocated state so the
+ * error-cleanup path can safely uninstantiate it. */
+ if (rng->drbg512 != NULL) {
+ XMEMSET(rng->drbg512, 0, sizeof(DRBG_SHA512_internal));
+ }
+ #endif
+
#ifdef WOLFSSL_SMALL_STACK_CACHE
if (ret == 0) {
rng->drbg512_scratch =
@@ -2185,6 +2285,15 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
if (ret != DRBG_SUCCESS) {
#ifndef NO_SHA256
if (rng->drbgType == WC_DRBG_SHA256) {
+ #if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-90A sec 9.4 / ISO 19790 sec 7.9: the DRBG internal state
+ * (V, C) is a CSP. Zeroize it (as wc_FreeRng does) before freeing
+ * the allocation on this error path; previously only the self-test
+ * scratch (7.9.7-exempt) was uninstantiated here. v7.0.0+ only. */
+ if (rng->drbg != NULL) {
+ (void)Hash_DRBG_Uninstantiate((DRBG_internal*)rng->drbg);
+ }
+ #endif
#if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY)
XFREE(rng->drbg, rng->heap, DYNAMIC_TYPE_RNG);
#endif
@@ -2203,6 +2312,13 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
#endif /* !NO_SHA256 */
#ifdef WOLFSSL_DRBG_SHA512
if (rng->drbgType == WC_DRBG_SHA512) {
+ #if FIPS_VERSION3_GE(7,0,0)
+ /* As above: zeroize the SHA-512 DRBG state (V, C) before free. */
+ if (rng->drbg512 != NULL) {
+ (void)Hash512_DRBG_Uninstantiate(
+ (DRBG_SHA512_internal*)rng->drbg512);
+ }
+ #endif
#if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY)
XFREE(rng->drbg512, rng->heap, DYNAMIC_TYPE_RNG);
#endif
@@ -2550,13 +2666,31 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz)
if (ret == DRBG_SUCCESS) {
ret = 0;
}
- else if (ret == DRBG_CONT_FAILURE) {
- ret = DRBG_CONT_FIPS_E;
- rng->status = DRBG_CONT_FAILED;
- }
else {
- ret = RNG_FAILURE_E;
- rng->status = DRBG_FAILED;
+ #if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-90A sec 9.3.1: "If any status other than SUCCESS is returned, a
+ * Null string shall be returned as the pseudorandom bits." A generate
+ * failure after Hashgen wrote to output would otherwise leave DRBG bits
+ * in the caller's buffer */
+ ForceZero(output, sz);
+ #endif
+ if (ret == DRBG_CONT_FAILURE) {
+ ret = DRBG_CONT_FIPS_E;
+ rng->status = DRBG_CONT_FAILED;
+ }
+ else {
+#if FIPS_VERSION3_GE(7,0,0)
+ /* Preserve the specific SP 800-90B code (ENTROPY_RT_E/ENTROPY_APT_E)
+ * from a failed reseed rather than flattening it to RNG_FAILURE_E,
+ * as the instantiate path does */
+ if (ret != WC_NO_ERR_TRACE(ENTROPY_RT_E) &&
+ ret != WC_NO_ERR_TRACE(ENTROPY_APT_E))
+ ret = RNG_FAILURE_E;
+#else
+ ret = RNG_FAILURE_E;
+#endif
+ rng->status = DRBG_FAILED;
+ }
}
#else
@@ -2845,6 +2979,10 @@ int wc_RNG_HealthTest_ex(int reseed, const byte* nonce, word32 nonceSz,
#endif /* !NO_SHA256 - wc_RNG_HealthTest{,_ex,_ex_internal} */
+/* Source: NIST CAVP Hash_DRBG.rsp, [SHA-256], PredictionResistance=False,
+ * EntropyInputLen=256, NonceLen=128, PersonalizationStringLen=0,
+ * AdditionalInputLen=0, ReturnedBitsLen=1024. seedA/reseedSeedA/outputA are the
+ * reseed-section vectors; seedB/outputB are the no-reseed set. */
const FLASH_QUALIFIER byte seedA_data[] = {
0x63, 0x36, 0x33, 0x77, 0xe4, 0x1e, 0x86, 0x46, 0x8d, 0xeb, 0x0a, 0xb4,
0xa8, 0xed, 0x68, 0x3f, 0x6a, 0x13, 0x4e, 0x47, 0xe0, 0x14, 0xc7, 0x00,
@@ -3003,7 +3141,14 @@ static const byte sha512_outputB_data[] = {
static int wc_RNG_HealthTestLocal(WC_RNG* rng, int reseed, void* heap,
int devId)
{
+#if FIPS_VERSION3_GE(7,0,0)
+ /* Fail closed: if no KAT path runs (e.g. an unexpected drbgType under an
+ * atypical build) the health test must report failure, not success.
+ * SP 800-90A sec 11.3 / ISO 19790 sec 7.9. */
+ int ret = WC_NO_ERR_TRACE(RNG_FAILURE_E);
+#else
int ret = 0;
+#endif
#ifdef WOLFSSL_DRBG_SHA512
/* SHA-512 DRBG health test path */
@@ -3736,23 +3881,60 @@ int wc_FreeNetRandom(void)
#if defined(HAVE_INTEL_RDRAND) || defined(HAVE_INTEL_RDSEED) || \
defined(HAVE_AMD_RDSEED)
-#ifdef WOLFSSL_ASYNC_CRYPT
- /* need more retries if multiple cores */
- #define INTELRD_RETRY (32 * 8)
-#else
- #define INTELRD_RETRY 32
+/* Bounds the RDRAND/RDSEED retry loop below: RDSEED sets CF=0 until on-chip
+ * entropy replenishes, so Intel DRNG guidance requires a retry. */
+#ifndef INTELRD_RETRY
+ #if defined(WOLFSSL_LINUXKM) || FIPS_VERSION3_GE(7,0,0)
+ /* Boot-time FIPS CASTs poll RDSEED while it is still warming up and
+ * contended, and the v7 module's SP 800-90A over-seeding
+ * (ENTROPY_SCALE_FACTOR, e.g. 512 for the RDSEED ESV) draws thousands of
+ * 64-bit samples per instantiate/reseed -- 32 retries give up long
+ * before the on-chip conditioner replenishes, spuriously failing the
+ * ESV/CAST with -199. Raise the ceiling in userspace FIPS v7 too (not
+ * just LINUXKM). A ceiling, not a fixed cost -- most samples return on
+ * the first RDSEED. */
+ #define INTELRD_RETRY 100000
+ #elif defined(WOLFSSL_ASYNC_CRYPT)
+ /* need more retries if multiple cores */
+ #define INTELRD_RETRY (32 * 8)
+ #else
+ #define INTELRD_RETRY 32
+ #endif
#endif
#if defined(HAVE_INTEL_RDSEED) || defined(HAVE_AMD_RDSEED)
+/* Vendor tag for the FIPS_CODE_REVIEW evidence prints below. */
+#if defined(HAVE_AMD_RDSEED)
+#define WC_RDSEED_VENDOR "AMD"
+#else
+#define WC_RDSEED_VENDOR "Intel"
+#endif
+
#ifndef USE_INTEL_INTRINSICS
- /* return 0 on success */
+ /* return 0 on success. Per the E27 PUD (CMVP entropy disclosure), each
+ * RDSEED is checked via the x86 Carry Flag: CF=1 -> *seed holds 64 bits
+ * of conditioned entropy; CF=0 -> unusable, caller must retry. */
static WC_INLINE int IntelRDseed64(word64* seed)
{
unsigned char ok;
__asm__ volatile("rdseed %0; setc %1":"=r"(*seed), "=qm"(ok));
+#ifdef FIPS_CODE_REVIEW
+ /* One-shot tracer: RDSEED fires per 64-bit chunk, so per-chunk
+ * prints would flood the sanity-log. */
+ {
+ static int printed_asm = 0;
+ if (!printed_asm) {
+ printed_asm = 1;
+ printf("FIPS_CODE_REVIEW IntelRDseed64 [asm path, %s] "
+ "(one-shot): delivered %u bits, CF=%u\n",
+ WC_RDSEED_VENDOR, (unsigned)(sizeof(word64) * 8u),
+ (unsigned)ok);
+ }
+ }
+#endif
return (ok) ? 0 : -1;
}
@@ -3760,7 +3942,9 @@ int wc_FreeNetRandom(void)
/* The compiler Visual Studio uses does not allow inline assembly.
* It does allow for Intel intrinsic functions. */
- /* return 0 on success */
+ /* return 0 on success. Intrinsic front-end for the same RDSEED as the
+ * asm path above: returns 1 when CF was set, else *seed MUST NOT be
+ * consumed and the caller must retry. */
# ifdef __GNUC__
__attribute__((target("rdseed")))
# endif
@@ -3769,6 +3953,19 @@ int wc_FreeNetRandom(void)
int ok;
ok = _rdseed64_step((unsigned long long*) seed);
+#ifdef FIPS_CODE_REVIEW
+ /* One-shot tracer; see asm-path comment above for rationale. */
+ {
+ static int printed_intrinsic = 0;
+ if (!printed_intrinsic) {
+ printed_intrinsic = 1;
+ printf("FIPS_CODE_REVIEW IntelRDseed64 [intrinsic path, %s] "
+ "(one-shot): delivered %u bits, "
+ "intrinsic_ret=%d (== CF)\n",
+ WC_RDSEED_VENDOR, (unsigned)(sizeof(word64) * 8u), ok);
+ }
+ }
+#endif
return (ok) ? 0 : -1;
}
@@ -3781,6 +3978,9 @@ static WC_INLINE int IntelRDseed64_r(word64* rnd)
for (i = 0; i < INTELRD_RETRY; i++) {
if (IntelRDseed64(rnd) == 0)
return 0;
+ /* Let the entropy source replenish between attempts; a no-op where
+ * blocking is unsafe. */
+ WC_RELAX_LONG_LOOP();
}
return -1;
}
@@ -3794,11 +3994,26 @@ static int wc_GenerateSeed_IntelRD(OS_Seed* os, byte* output, word32 sz)
(void)os;
+#ifdef FIPS_CODE_REVIEW
+ /* Evidence trace: sz bytes requested -> ceil(sz/8) 64-bit RDSEED
+ * samples expected. */
+ printf("FIPS_CODE_REVIEW wc_GenerateSeed_IntelRD [%s]: "
+ "requested %u bytes = %u bits "
+ "(expect %u RDSEED 64-bit samples)\n",
+ WC_RDSEED_VENDOR, (unsigned)sz, (unsigned)(sz * 8u),
+ (unsigned)((sz + sizeof(word64) - 1u) / sizeof(word64)));
+#endif
+
if (!IS_INTEL_RDSEED(intel_flags))
return -1;
/* Note, access to rdseed_sanity_status is benignly racey on multithreaded
* targets.
+ *
+ * This is a one-shot startup sanity check -- the status latches and is not
+ * re-evaluated. It is NOT the continuous health test: the SP 800-90B sec
+ * 4.4 RCT/APT in wc_RNG_TestSeed() run on every seed (instantiate and
+ * reseed) and are what would catch RDSEED degrading at run time.
*/
if (rdseed_sanity_status == 0) {
word64 sanity_word1 = 0, sanity_word2 = 0;
@@ -4105,6 +4320,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#elif defined(HAVE_RTP_SYS) || defined(EBSNET)
+/* SP 800-90B: rtp_rand() is not an approved FIPS entropy source. */
+#if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids the RTP/EBSNET rtp_rand() seed (SP 800-90B)"
+#endif
+
#include "rtprand.h" /* rtp_rand () */
@@ -4221,6 +4441,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
return 0;
}
#else /* WOLFSSL_PIC32MZ_RNG */
+ /* SP 800-90B: rand() fallback is not an approved FIPS entropy source. */
+ #if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids the PIC32 rand() seed fallback (SP 800-90B)"
+ #endif
+
/* uses the core timer, in nanoseconds to seed srand */
int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
{
@@ -4735,6 +4960,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#endif /* WOLFSSL_STM32_CUBEMX */
#elif defined(WOLFSSL_TIRTOS)
+/* SP 800-90B: rand() is not an approved FIPS entropy source. */
+#if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids the TI-RTOS rand() seed (SP 800-90B)"
+#endif
+
#warning "potential for not enough entropy, currently being used for testing"
#include
#include
@@ -4767,6 +4997,10 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
}
#elif defined(WOLFSSL_NUCLEUS)
+/* SP 800-90B: rand() is not an approved FIPS entropy source. */
+#if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids the Nucleus rand() seed (SP 800-90B)"
+#endif
#include "nucleus.h"
#include "kernel/plus_common.h"
@@ -5089,6 +5323,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
}
#elif defined(WOLFSSL_APACHE_MYNEWT)
+/* SP 800-90B: rand() is not an approved FIPS entropy source. */
+#if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids the Apache Mynewt rand() seed (SP 800-90B)"
+#endif
+
#include
#include "os/os_time.h"
@@ -5391,6 +5630,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
*/
#elif defined(__MICROBLAZE__)
+/* SP 800-90B: rand() is not an approved FIPS entropy source. */
+#if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids the MicroBlaze rand() seed (SP 800-90B)"
+#endif
+
#warning weak source of entropy
#define LPD_SCNTR_BASE_ADDRESS 0xFF250000
@@ -5449,6 +5693,10 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
}
#elif defined(WOLFSSL_TELIT_M2MB)
+/* SP 800-90B: rand() is not an approved FIPS entropy source. */
+#if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids the Telit M2MB rand() seed (SP 800-90B)"
+#endif
#include "stdlib.h"
static long get_timestamp(void) {
@@ -5658,6 +5906,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
}
#elif defined(DOLPHIN_EMULATOR) || defined (WOLFSSL_NDS)
+/* SP 800-90B: rand() is not an approved FIPS entropy source. */
+#if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids the Dolphin/NDS rand() seed (SP 800-90B)"
+#endif
+
int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
{
@@ -5797,6 +6050,14 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
return BAD_FUNC_ARG;
}
+ /* One-time acknowledgement gate: defining ALLOW_ENTROPY_FALLBACK under
+ * FIPS v7 re-enables the non-approved /dev/urandom fallback in every
+ * entropy-source branch below (SP 800-90B MemUse, RDSEED, getrandom),
+ * taking the module out of the approved mode of operation. */
+#if defined(ALLOW_ENTROPY_FALLBACK) && FIPS_VERSION3_GE(7,0,0)
+ #error "ALLOW_ENTROPY_FALLBACK enables the non-approved /dev/urandom entropy fallback; delete this line to acknowledge the module is no longer operating in the approved mode of operation."
+#endif
+
#ifdef WOLF_CRYPTO_CB
#ifndef WOLF_CRYPTO_CB_FIND
if (os->devId != INVALID_DEVID)
@@ -5819,6 +6080,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#ifdef ENTROPY_MEMUSE_FORCE_FAILURE
/* Don't fall back to /dev/urandom. */
return ret;
+ #elif FIPS_VERSION3_GE(7,0,0) && !defined(ALLOW_ENTROPY_FALLBACK)
+ /* FIPS 140-3 v7 fail-closed: the SP 800-90B MemUse source is validated;
+ * do not fall back to the non-approved /dev/urandom (see the
+ * ALLOW_ENTROPY_FALLBACK gate at the top of this function). */
+ return ret;
#else
/* Reset error and fall back to using /dev/urandom. */
ret = 0;
@@ -5837,6 +6103,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#ifdef FORCE_FAILURE_RDSEED
/* Don't fall back to /dev/urandom. */
return ret;
+ #elif FIPS_VERSION3_GE(7,0,0) && !defined(ALLOW_ENTROPY_FALLBACK)
+ /* FIPS 140-3 v7 fail-closed: RDSEED is the validated (ESV) entropy
+ * source; do not fall back to the non-approved /dev/urandom (see the
+ * ALLOW_ENTROPY_FALLBACK gate at the top of this function). */
+ return ret;
#else
/* Reset error and fall back to using /dev/urandom. */
ret = 0;
@@ -5882,6 +6153,13 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#ifdef FORCE_FAILURE_GETRANDOM
/* don't fall back to /dev/urandom */
return ret;
+ #elif FIPS_VERSION3_GE(7,0,0) && !defined(ALLOW_ENTROPY_FALLBACK)
+ /* FIPS 140-3 v7 fail-closed: getrandom() is the module's validated
+ * (ESV) entropy source. Do NOT silently seed from the non-approved
+ * /dev/urandom on a getrandom() failure -- return the error instead.
+ * Define ALLOW_ENTROPY_FALLBACK to permit the fallback (see the
+ * acknowledgement #error at the top of this function). */
+ return ret;
#elif !defined(NO_FILESYSTEM)
/* reset error and fall back to using /dev/urandom if filesystem
* support is compiled in */
@@ -5986,6 +6264,11 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#endif
#ifdef USE_TEST_GENSEED
+ /* SP 800-90B: USE_TEST_GENSEED emits a predictable counter, not entropy. */
+ #if FIPS_VERSION3_GE(7,0,0)
+ #error "FIPS v7 forbids USE_TEST_GENSEED, a predictable test seed (SP 800-90B)"
+ #endif
+
#if !defined(_MSC_VER) && !defined(__TASKING__)
#warning "write a real random seed!!!!, just for testing now"
#else
diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c
index 24d7cdccd3..297df0ad3f 100644
--- a/wolfcrypt/src/rng_bank.c
+++ b/wolfcrypt/src/rng_bank.c
@@ -71,6 +71,19 @@
((rng_ptr)->drbg == NULL)
#endif
+/* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to force
+ * a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when WORD64_AVAILABLE is
+ * undefined (random.h), so a reseed interval above 2^32 would truncate to 0 and
+ * silently defeat the forced reseed (SP 800-90A Rev1 sec 9.3). Fail the build
+ * rather than mis-reseed. This is a compile-time assert rather than a
+ * preprocessor #if because WC_RESEED_INTERVAL may be defined with a (word64)
+ * cast (settings.h kernel path) that the preprocessor cannot evaluate; the
+ * outer #if uses only defined() so the 64-bit path skips it without expanding
+ * that cast. */
+#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE)
+ wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL);
+#endif
+
/* To disable retry looping in wc_rng_bank_init(), pass timeout_secs=0, and to
* retry indefinitely, pass negative timeout_secs -- the flags arg here is only
* used to initialize the flags in the new bank.
@@ -124,9 +137,18 @@ WOLFSSL_API int wc_rng_bank_init(
if (flags & WC_RNG_BANK_FLAG_NO_VECTOR_OPS)
need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0);
- ret = wc_InitRngNonce_ex(
- WC_RNG_BANK_INST_TO_RNG(ctx->rngs + i),
- (byte *)&ctx->rngs[i], sizeof(byte *), heap, devId);
+ {
+ /* Per-instance nonce is the instance ADDRESS value, not the
+ * freshly zeroed instance memory; matches wc_rng_bank_reinit
+ * (SP 800-90A sec 8.6.7 -- a non-repeating per-instance
+ * value). The instantiation entropy_input over-pulled by
+ * _InitRng is the primary seed material. Held in a void* so
+ * the nonce length is an honest pointer size. */
+ void* nonceAddr = &ctx->rngs[i];
+ ret = wc_InitRngNonce_ex(
+ WC_RNG_BANK_INST_TO_RNG(ctx->rngs + i),
+ (byte *)&nonceAddr, sizeof(nonceAddr), heap, devId);
+ }
if (need_reenable_vec)
REENABLE_VECTOR_REGISTERS();
diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c
index 5f33baf7cd..3c1c5131ec 100644
--- a/wolfcrypt/src/rsa.c
+++ b/wolfcrypt/src/rsa.c
@@ -159,12 +159,28 @@ static void wc_RsaCleanup(RsaKey* key)
(!defined(WOLFSSL_RSA_VERIFY_ONLY) && !defined(WOLFSSL_RSA_VERIFY_INLINE)))
if (key != NULL) {
#ifndef WOLFSSL_RSA_PUBLIC_ONLY
+ #if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-56B Rev2 sec 7.2.2.4: destroy the recovered keying material on
+ * every exit. PRIVATE-key operations only: those are the ones that
+ * recover keying material (RSA key transport). The public-decrypt
+ * (signature verification) path recovers no keying material and returns
+ * a pointer INTO key->data to the caller (RsaPublicDecryptEx sets
+ * *outPtr = pad, where pad points inside key->data), so wiping it here
+ * would destroy the recovered signature before the caller reads it.
+ * Wipe only a buffer this operation allocated, never a caller-owned one. */
+ if (key->dataIsAlloc && key->data != NULL && key->dataLen > 0 &&
+ (key->type == RSA_PRIVATE_DECRYPT ||
+ key->type == RSA_PRIVATE_ENCRYPT)) {
+ ForceZero(key->data, key->dataLen);
+ }
+ #else
/* if private operation zero temp buffer */
if ((key->data != NULL && key->dataLen > 0) &&
(key->type == RSA_PRIVATE_DECRYPT ||
key->type == RSA_PRIVATE_ENCRYPT)) {
ForceZero(key->data, key->dataLen);
}
+ #endif
#endif
/* make sure any allocated memory is free'd */
if (key->dataIsAlloc) {
@@ -813,6 +829,16 @@ static int _ifc_pairwise_consistency_test(RsaKey* key, WC_RNG* rng)
}
+#if FIPS_VERSION3_GE(7,0,0) && defined(WOLFSSL_KEY_GEN) && \
+ !defined(WOLFSSL_RSA_PUBLIC_ONLY)
+/* Defined with the key generation code below; reused here so that key pair
+ * validation applies the same FIPS 186-5 App. A.1.1 prime tests as generation
+ * (range against the sqrt(2) lower bound, GCD(p-1,e) = 1, |p-q| separation and
+ * Miller-Rabin primality) rather than a weaker approximation. */
+static int _CheckProbablePrime(mp_int* p, mp_int* q, mp_int* e, int nlen,
+ int* isPrime, WC_RNG* rng);
+#endif
+
int wc_CheckRsaKey(RsaKey* key)
{
WC_RNG *rng = NULL;
@@ -895,6 +921,54 @@ int wc_CheckRsaKey(RsaKey* key)
}
}
+#if FIPS_VERSION3_GE(7,0,0) && defined(WOLFSSL_KEY_GEN) && \
+ !defined(WOLFSSL_RSA_PUBLIC_ONLY)
+ /* SP 800-56B Rev2 sec 6.4.1.4.3 (crt_pkv), inheriting sec 6.4.1.2.1 steps
+ * 1c, 3b/3c, 5 and 6. */
+ if (ret == 0) {
+ int nBits = mp_count_bits(&key->n);
+ int isPrime = 0;
+
+ /* step 3b/3c: nBits shall be an even integer >= 2048 */
+ if ((nBits < RSA_MIN_SIZE) || ((nBits & 1) != 0)) {
+ ret = WC_KEY_SIZE_E;
+ }
+
+ /* step 1c: e shall be odd with 65537 <= e < 2^256. e is held in an
+ * mp_int here, so the upper bound is checked explicitly. */
+ if (ret == 0) {
+ if (mp_iseven(&key->e) || (mp_cmp_d(&key->e, 65537) == MP_LT) ||
+ (mp_count_bits(&key->e) > 256)) {
+ ret = MP_EXPTMOD_E;
+ }
+ }
+
+ /* steps 5a/5b/5f/5g for p, then the same for q together with step 5c,
+ * |p - q| > 2^((nBits/2) - 100). _CheckProbablePrime() applies the
+ * sqrt(2) lower bound, GCD(prime-1, e) = 1 and Miller-Rabin. */
+ if (ret == 0) {
+ ret = _CheckProbablePrime(&key->p, NULL, &key->e, nBits,
+ &isPrime, rng);
+ }
+ if ((ret == 0) && (!isPrime)) {
+ ret = MP_EXPTMOD_E;
+ }
+ if (ret == 0) {
+ ret = _CheckProbablePrime(&key->p, &key->q, &key->e, nBits,
+ &isPrime, rng);
+ }
+ if ((ret == 0) && (!isPrime)) {
+ ret = MP_EXPTMOD_E;
+ }
+
+ /* step 6: 2^(nBits/2) < d. A d of at most nBits/2 bits is below
+ * 2^(nBits/2), so the bit count is a sufficient test here. */
+ if ((ret == 0) && (mp_count_bits(&key->d) <= (nBits / 2))) {
+ ret = MP_EXPTMOD_E;
+ }
+ }
+#endif
+
#ifndef WC_RSA_NO_FERMAT_CHECK
/* Fermat's Factorization works when difference between p and q
* is less than (conservatively):
@@ -913,7 +987,16 @@ int wc_CheckRsaKey(RsaKey* key)
/* Check dP, dQ and u if they exist */
if (ret == 0 && !mp_iszero(&key->dP)) {
- if (mp_sub_d(&key->p, 1, tmp) != MP_OKAY) {
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-56B Rev2 sec 6.4.1.2.1 steps 7a/7b/7c: 1 < dP, 1 < dQ and
+ * 1 < qInv. */
+ if ((mp_cmp_d(&key->dP, 1) != MP_GT) ||
+ (mp_cmp_d(&key->dQ, 1) != MP_GT) ||
+ (mp_cmp_d(&key->u, 1) != MP_GT)) {
+ ret = MP_EXPTMOD_E;
+ }
+#endif
+ if ((ret == 0) && (mp_sub_d(&key->p, 1, tmp) != MP_OKAY)) {
ret = MP_EXPTMOD_E;
}
/* Check dP <= p-1. */
@@ -1532,7 +1615,9 @@ static int RsaPad_PSS(const byte* input, word32 inputLen, byte* pkcsBlock,
}
#endif
}
-#ifndef WOLFSSL_PSS_LONG_SALT
+/* FIPS 186-5 sec 5.4(g): 0 <= sLen <= hLen. Enforced for v7.0.0+ even when
+ * WOLFSSL_PSS_LONG_SALT is defined. */
+#if !defined(WOLFSSL_PSS_LONG_SALT) || FIPS_VERSION3_GE(7,0,0)
else if (saltLen > hLen) {
return PSS_SALTLEN_E;
}
@@ -1940,7 +2025,9 @@ static int RsaUnPad_PSS(byte *pkcsBlock, unsigned int pkcsBlockLen,
saltLen = RSA_PSS_SALT_MAX_SZ;
#endif
}
-#ifndef WOLFSSL_PSS_LONG_SALT
+/* FIPS 186-5 sec 5.4(g): 0 <= sLen <= hLen, on verification as well as
+ * generation. */
+#if !defined(WOLFSSL_PSS_LONG_SALT) || FIPS_VERSION3_GE(7,0,0)
else if (saltLen > hLen)
return PSS_SALTLEN_E;
#endif
@@ -3602,6 +3689,19 @@ static int wc_RsaFunction_ex(const byte* in, word32 inLen, byte* out,
}
#endif
+#if !defined(NO_RSA_BOUNDS_CHECK) && FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-56B Rev2 sec 7.1.1 step 1 (RSAEP) and RFC 8017 sec 5.1.1/5.2.1:
+ * the representative shall satisfy 1 < m < n-1 before exponentiation. */
+ if ((type == RSA_PUBLIC_ENCRYPT || type == RSA_PRIVATE_ENCRYPT) &&
+ key->state == RSA_STATE_ENCRYPT_EXPTMOD) {
+
+ ret = RsaFunctionCheckIn(in, inLen, key, 1);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+#endif
+
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_RSA)
if (key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_RSA &&
key->n.raw.len > 0) {
@@ -4540,7 +4640,8 @@ int wc_RsaPSS_CheckPadding_ex2(const byte* in, word32 inSz, const byte* sig,
}
#endif
}
-#ifndef WOLFSSL_PSS_LONG_SALT
+/* FIPS 186-5 sec 5.4(g): 0 <= sLen <= hLen (inSz is the digest length). */
+#if !defined(WOLFSSL_PSS_LONG_SALT) || FIPS_VERSION3_GE(7,0,0)
else if (saltLen > (int)inSz) {
ret = PSS_SALTLEN_E;
}
@@ -5177,7 +5278,18 @@ static WC_INLINE int RsaSizeCheck(int size)
return 0;
}
-#ifdef HAVE_FIPS
+#if FIPS_VERSION3_GE(7,0,0)
+ /* Approved RSA key sizes per FIPS 186-5 sec 5.1 and SP 800-131Ar2 sec 4
+ * Table 2 - 2048, 3072, 4096 only (1024 disallowed since 2014-01-01) */
+ switch (size) {
+ case 2048:
+ case 3072:
+ case 4096:
+ return 1;
+ }
+
+ return 0;
+#elif defined(HAVE_FIPS)
/* Key size requirements for CAVP */
switch (size) {
case 1024:
@@ -5190,7 +5302,7 @@ static WC_INLINE int RsaSizeCheck(int size)
return 0;
#else
return 1; /* allow unusual key sizes in non FIPS mode */
-#endif /* HAVE_FIPS */
+#endif /* FIPS_VERSION3_GE(7,0,0) */
}
@@ -5478,6 +5590,16 @@ int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng)
goto out;
}
+#if FIPS_VERSION3_GE(7,0,0)
+ /* FIPS 186-5 sec 5.2: 2^16 + 1 <= e < 2^256, e odd. e is a long
+ * (<= 64 bits) so the upper bound holds structurally; enforce the 65537
+ * lower bound explicitly */
+ if (e < 65537L) {
+ err = BAD_FUNC_ARG;
+ goto out;
+ }
+#endif
+
#if defined(WOLFSSL_CRYPTOCELL)
err = cc310_RSA_GenerateKeyPair(key, size, e);
goto out;
@@ -5678,6 +5800,15 @@ int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng)
(void)i;
#endif
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-90A Rev1 sec 11.4.2: a DRBG failure shall report its own
+ * error indicator. Test err before WC_CHECK_FOR_INTR_SIGNALS()
+ * overwrites it, matching the p loop above; otherwise a
+ * DRBG_CONT_FIPS_E here is discarded and surfaces as PRIME_GEN_E */
+ if (err != MP_OKAY || isPrime || i >= failCount)
+ break;
+#endif
+
err = WC_CHECK_FOR_INTR_SIGNALS();
if (err != 0)
break;
diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c
index 45b9948df4..ee358b0572 100644
--- a/wolfcrypt/src/sha.c
+++ b/wolfcrypt/src/sha.c
@@ -1140,6 +1140,10 @@ void wc_ShaFree(wc_Sha* sha)
#if defined(PSOC6_HASH_SHA1)
wc_Psoc6_Sha_Free();
#endif
+
+ /* Zeroize the hash context on free; the state can hold key-derived
+ * HMAC inner/outer hash material (ISO/IEC 19790:2012 7.9). */
+ ForceZero(sha, sizeof(*sha));
}
#endif /* !MAX3266X_SHA */
diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c
index 4c3d4fd2eb..dcfa75b69d 100644
--- a/wolfcrypt/src/sha256.c
+++ b/wolfcrypt/src/sha256.c
@@ -1367,14 +1367,29 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId)
return ret;
}
+/* 32-bit ARM SHA-256 NEON/crypto transforms need SAVE/RESTORE_VECTOR_REGISTERS
+ * (kernel_neon_begin/end) in a kernel module, else SIMD faults. */
+#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && !defined(__aarch64__) && \
+ !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WOLFSSL_ARMASM_NO_NEON)
+ #define WC_SHA256_ARM_SVR_BEGIN(fail) SAVE_VECTOR_REGISTERS(fail)
+ #define WC_SHA256_ARM_SVR_END() RESTORE_VECTOR_REGISTERS()
+#else
+ #define WC_SHA256_ARM_SVR_BEGIN(fail) WC_DO_NOTHING
+ #define WC_SHA256_ARM_SVR_END() WC_DO_NOTHING
+#endif
+
static WC_INLINE int Transform_Sha256(wc_Sha256* sha256, const byte* data)
{
#if defined(WOLFSSL_ARMASM_THUMB2) || defined(WOLFSSL_ARMASM_NO_NEON)
Transform_Sha256_Len_base(sha256, data, WC_SHA256_BLOCK_SIZE);
-#elif defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)
- Transform_Sha256_Len_neon(sha256, data, WC_SHA256_BLOCK_SIZE);
#else
+ WC_SHA256_ARM_SVR_BEGIN(return _svr_ret;);
+ #if defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)
+ Transform_Sha256_Len_neon(sha256, data, WC_SHA256_BLOCK_SIZE);
+ #else
Transform_Sha256_Len_crypto(sha256, data, WC_SHA256_BLOCK_SIZE);
+ #endif
+ WC_SHA256_ARM_SVR_END();
#endif
return 0;
}
@@ -1384,10 +1399,14 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data,
{
#if defined(WOLFSSL_ARMASM_THUMB2) || defined(WOLFSSL_ARMASM_NO_NEON)
Transform_Sha256_Len_base(sha256, data, len);
-#elif defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)
- Transform_Sha256_Len_neon(sha256, data, len);
#else
+ WC_SHA256_ARM_SVR_BEGIN(return _svr_ret;);
+ #if defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)
+ Transform_Sha256_Len_neon(sha256, data, len);
+ #else
Transform_Sha256_Len_crypto(sha256, data, len);
+ #endif
+ WC_SHA256_ARM_SVR_END();
#endif
return 0;
}
diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c
index 1ead204fb5..48b7ce66a6 100644
--- a/wolfcrypt/src/sha3.c
+++ b/wolfcrypt/src/sha3.c
@@ -144,6 +144,15 @@
#define SHA3_USE_AVX2(f) (IS_INTEL_AVX2(f) && IS_CPU_INTEL(f))
#endif
+ /* True only when AVX2 was explicitly requested, in which case it is
+ * preferred over BMI2. Otherwise BMI2 is tried first -- see the selection
+ * order below. */
+#if !defined(WOLFSSL_SHA3_NO_AVX2) && defined(WOLFSSL_SHA3_AVX2)
+ #define SHA3_FORCE_AVX2(f) IS_INTEL_AVX2(f)
+#else
+ #define SHA3_FORCE_AVX2(f) 0
+#endif
+
/* True when the selected block function uses vector registers and so
* needs the caller to save/restore them. BMI2 and the C block use only
* general registers. */
@@ -766,8 +775,12 @@ static int InitSha3(wc_Sha3* sha3)
}
else
#endif
- /* See the selection comment above: AVX2 on Intel, otherwise BMI2. */
- if (SHA3_USE_AVX2(cpuid_flags)) {
+ /* BMI2 before AVX2. The AVX2-first order above was tuned on
+ * Haswell..Ice Lake; on Arrow Lake single-stream AVX2 is 0.87x vs the C
+ * block while BMI2 is 1.09x. BMI2 also uses only general registers, so
+ * it skips the SAVE/RESTORE_VECTOR_REGISTERS wrapper in-kernel.
+ * WOLFSSL_SHA3_AVX2 still forces AVX2 ahead of BMI2. */
+ if (SHA3_FORCE_AVX2(cpuid_flags)) {
SHA3_BLOCK = sha3_block_avx2;
SHA3_BLOCK_N = sha3_block_n_avx2;
}
@@ -775,6 +788,10 @@ static int InitSha3(wc_Sha3* sha3)
SHA3_BLOCK = sha3_block_bmi2;
SHA3_BLOCK_N = sha3_block_n_bmi2;
}
+ else if (SHA3_USE_AVX2(cpuid_flags)) {
+ SHA3_BLOCK = sha3_block_avx2;
+ SHA3_BLOCK_N = sha3_block_n_avx2;
+ }
else {
SHA3_BLOCK = BlockSha3;
SHA3_BLOCK_N = NULL;
@@ -813,6 +830,20 @@ void BlockSha3(word64* s)
}
#endif
+/* 32-bit ARM BlockSha3 is NEON asm; a Linux kernel module must enable NEON
+ * around it (SAVE/RESTORE_VECTOR_REGISTERS), else the vpush faults. */
+#if !defined(USE_INTEL_SPEEDUP) && defined(WOLFSSL_ARMASM) && \
+ !defined(__aarch64__) && !defined(WOLFSSL_ARMASM_THUMB2) && \
+ !defined(WOLFSSL_ARMASM_NO_NEON)
+ #define WC_SHA3_NEON_SVR_BEGIN() do { \
+ int _svr_ret = SAVE_VECTOR_REGISTERS2(); \
+ if (_svr_ret != 0) return _svr_ret; } while (0)
+ #define WC_SHA3_NEON_SVR_END() RESTORE_VECTOR_REGISTERS()
+#else
+ #define WC_SHA3_NEON_SVR_BEGIN() WC_DO_NOTHING
+ #define WC_SHA3_NEON_SVR_END() WC_DO_NOTHING
+#endif
+
/* Update the SHA-3 hash state with message data.
*
* sha3 wc_Sha3 object holding state.
@@ -907,7 +938,9 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p)
#ifdef SHA3_FUNC_PTR
(*sha3_block)(sha3->s);
#else
+ WC_SHA3_NEON_SVR_BEGIN();
BlockSha3(sha3->s);
+ WC_SHA3_NEON_SVR_END();
#endif
sha3->i = 0;
}
@@ -944,7 +977,9 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p)
#ifdef SHA3_FUNC_PTR
(*sha3_block)(sha3->s);
#else
+ WC_SHA3_NEON_SVR_BEGIN();
BlockSha3(sha3->s);
+ WC_SHA3_NEON_SVR_END();
#endif
len -= p * 8U;
data += p * 8U;
@@ -1003,7 +1038,9 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l
#if !defined(BIG_ENDIAN_ORDER) && !defined(WC_SHA3_FAULT_HARDEN)
xorbuf(sha3->s, sha3->t, sha3->i);
-#ifdef WOLFSSL_HASH_FLAGS
+ /* SHA3-256 emits the FIPS 202 0x06 pad; the non-approved legacy
+ * Keccak-256 0x01 pad is excluded from the FIPS module (FIPS 202 6.1). */
+#if defined(WOLFSSL_HASH_FLAGS) && !FIPS_VERSION3_GE(7,0,0)
if ((p == WC_SHA3_256_COUNT) && (sha3->flags & WC_HASH_SHA3_KECCAK256)) {
padChar = 0x01;
}
@@ -1012,7 +1049,9 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l
((byte*)sha3->s)[rate - 1] ^= 0x80;
#else
sha3->t[rate - 1] = 0x00;
-#ifdef WOLFSSL_HASH_FLAGS
+ /* SHA3-256 emits the FIPS 202 0x06 pad; the non-approved legacy
+ * Keccak-256 0x01 pad is excluded from the FIPS module (FIPS 202 6.1). */
+#if defined(WOLFSSL_HASH_FLAGS) && !FIPS_VERSION3_GE(7,0,0)
if ((p == WC_SHA3_256_COUNT) && (sha3->flags & WC_HASH_SHA3_KECCAK256)) {
padChar = 0x01;
}
@@ -1052,7 +1091,9 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l
#ifdef SHA3_FUNC_PTR
(*sha3_block)(sha3->s);
#else
+ WC_SHA3_NEON_SVR_BEGIN();
BlockSha3(sha3->s);
+ WC_SHA3_NEON_SVR_END();
#endif
#if defined(BIG_ENDIAN_ORDER)
ByteReverseWords64((word64*)(hash + j), sha3->s, rate);
@@ -1064,7 +1105,9 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l
#ifdef SHA3_FUNC_PTR
(*sha3_block)(sha3->s);
#else
+ WC_SHA3_NEON_SVR_BEGIN();
BlockSha3(sha3->s);
+ WC_SHA3_NEON_SVR_END();
#endif
#if defined(BIG_ENDIAN_ORDER)
ByteReverseWords64(sha3->s, sha3->s, rate);
@@ -2431,6 +2474,18 @@ int wc_Shake256_Copy(wc_Shake* src, wc_Shake* dst)
#if (defined(WOLFSSL_KMAC) || defined(WOLFSSL_CSHAKE)) && \
defined(WC_SHA3_SW_KECCAK)
+
+#if FIPS_VERSION3_GE(7,0,0) && \
+ !defined(WOLFSSL_FIPS_DEV) && !defined(WOLFSSL_FIPS_READY)
+ /* KMAC and cSHAKE (SP 800-185) are not approved services in the validated
+ * module: they have no CAST and no service-layer gate. Exclude them from
+ * the validated FIPS build so they cannot be linked into it. The dev and
+ * ready prep builds (WOLFSSL_FIPS_DEV / WOLFSSL_FIPS_READY) deliberately
+ * exercise not-yet-approved algorithms for a future module, so the guard is
+ * lifted there -- e.g. --enable-all with --enable-fips=dev must build. */
+ #error "KMAC/cSHAKE (SP 800-185) are not part of the FIPS module boundary"
+#endif
+
/* cSHAKE and KMAC - NIST SP 800-185.
*
* cSHAKE is a customizable SHAKE; KMAC is cSHAKE keyed with the function name
diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c
index 311ff37e43..cc9b6c65b6 100644
--- a/wolfcrypt/src/sha512.c
+++ b/wolfcrypt/src/sha512.c
@@ -1562,15 +1562,29 @@ static void (*Transform_Sha512_p)(wc_Sha512* sha512, const byte* data) = NULL;
static void (*Transform_Sha512_Len_p)(wc_Sha512* sha512, const byte* data,
word32 len) = NULL;
+/* 32-bit ARM SHA-512 NEON asm needs SAVE/RESTORE_VECTOR_REGISTERS
+ * (kernel_neon_begin/end) in a kernel module, else the NEON insn faults. */
+#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && !defined(__aarch64__) && \
+ !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WOLFSSL_ARMASM_NO_NEON)
+ #define WC_SHA512_ARM_SVR_BEGIN(fail) SAVE_VECTOR_REGISTERS(fail)
+ #define WC_SHA512_ARM_SVR_END() RESTORE_VECTOR_REGISTERS()
+#else
+ #define WC_SHA512_ARM_SVR_BEGIN(fail) WC_DO_NOTHING
+ #define WC_SHA512_ARM_SVR_END() WC_DO_NOTHING
+#endif
static WC_INLINE int Transform_Sha512(wc_Sha512 *sha512, const byte* data)
{
+ WC_SHA512_ARM_SVR_BEGIN(return _svr_ret;);
(*Transform_Sha512_p)(sha512, data);
+ WC_SHA512_ARM_SVR_END();
return 0;
}
static WC_INLINE int Transform_Sha512_Len(wc_Sha512 *sha512, const byte* data,
word32 len)
{
+ WC_SHA512_ARM_SVR_BEGIN(return _svr_ret;);
(*Transform_Sha512_Len_p)(sha512, data, len);
+ WC_SHA512_ARM_SVR_END();
return 0;
}
diff --git a/wolfcrypt/src/sp_arm32.c b/wolfcrypt/src/sp_arm32.c
index 44dfcc6a90..fd90db66e6 100644
--- a/wolfcrypt/src/sp_arm32.c
+++ b/wolfcrypt/src/sp_arm32.c
@@ -193,14 +193,14 @@ static void sp_2048_from_bin(sp_digit* r, int size, const byte* a, int n)
d = (byte*)(r + j);
#ifdef BIG_ENDIAN_ORDER
switch (i) {
- case 2: d[1] = *(a++); //fallthrough
- case 1: d[2] = *(a++); //fallthrough
+ case 2: d[1] = *(a++); FALL_THROUGH;
+ case 1: d[2] = *(a++); FALL_THROUGH;
case 0: d[3] = *a ; //fallthrough
}
#else
switch (i) {
- case 2: d[i-2] = a[2]; //fallthrough
- case 1: d[i-1] = a[1]; //fallthrough
+ case 2: d[i-2] = a[2]; FALL_THROUGH;
+ case 1: d[i-1] = a[1]; FALL_THROUGH;
case 0: d[i-0] = a[0]; //fallthrough
}
#endif
@@ -19176,14 +19176,14 @@ static void sp_3072_from_bin(sp_digit* r, int size, const byte* a, int n)
d = (byte*)(r + j);
#ifdef BIG_ENDIAN_ORDER
switch (i) {
- case 2: d[1] = *(a++); //fallthrough
- case 1: d[2] = *(a++); //fallthrough
+ case 2: d[1] = *(a++); FALL_THROUGH;
+ case 1: d[2] = *(a++); FALL_THROUGH;
case 0: d[3] = *a ; //fallthrough
}
#else
switch (i) {
- case 2: d[i-2] = a[2]; //fallthrough
- case 1: d[i-1] = a[1]; //fallthrough
+ case 2: d[i-2] = a[2]; FALL_THROUGH;
+ case 1: d[i-1] = a[1]; FALL_THROUGH;
case 0: d[i-0] = a[0]; //fallthrough
}
#endif
@@ -47426,14 +47426,14 @@ static void sp_4096_from_bin(sp_digit* r, int size, const byte* a, int n)
d = (byte*)(r + j);
#ifdef BIG_ENDIAN_ORDER
switch (i) {
- case 2: d[1] = *(a++); //fallthrough
- case 1: d[2] = *(a++); //fallthrough
+ case 2: d[1] = *(a++); FALL_THROUGH;
+ case 1: d[2] = *(a++); FALL_THROUGH;
case 0: d[3] = *a ; //fallthrough
}
#else
switch (i) {
- case 2: d[i-2] = a[2]; //fallthrough
- case 1: d[i-1] = a[1]; //fallthrough
+ case 2: d[i-2] = a[2]; FALL_THROUGH;
+ case 1: d[i-1] = a[1]; FALL_THROUGH;
case 0: d[i-0] = a[0]; //fallthrough
}
#endif
@@ -78806,14 +78806,14 @@ static void sp_256_from_bin(sp_digit* r, int size, const byte* a, int n)
d = (byte*)(r + j);
#ifdef BIG_ENDIAN_ORDER
switch (i) {
- case 2: d[1] = *(a++); //fallthrough
- case 1: d[2] = *(a++); //fallthrough
+ case 2: d[1] = *(a++); FALL_THROUGH;
+ case 1: d[2] = *(a++); FALL_THROUGH;
case 0: d[3] = *a ; //fallthrough
}
#else
switch (i) {
- case 2: d[i-2] = a[2]; //fallthrough
- case 1: d[i-1] = a[1]; //fallthrough
+ case 2: d[i-2] = a[2]; FALL_THROUGH;
+ case 1: d[i-1] = a[1]; FALL_THROUGH;
case 0: d[i-0] = a[0]; //fallthrough
}
#endif
@@ -97132,14 +97132,14 @@ static void sp_384_from_bin(sp_digit* r, int size, const byte* a, int n)
d = (byte*)(r + j);
#ifdef BIG_ENDIAN_ORDER
switch (i) {
- case 2: d[1] = *(a++); //fallthrough
- case 1: d[2] = *(a++); //fallthrough
+ case 2: d[1] = *(a++); FALL_THROUGH;
+ case 1: d[2] = *(a++); FALL_THROUGH;
case 0: d[3] = *a ; //fallthrough
}
#else
switch (i) {
- case 2: d[i-2] = a[2]; //fallthrough
- case 1: d[i-1] = a[1]; //fallthrough
+ case 2: d[i-2] = a[2]; FALL_THROUGH;
+ case 1: d[i-1] = a[1]; FALL_THROUGH;
case 0: d[i-0] = a[0]; //fallthrough
}
#endif
@@ -125131,14 +125131,14 @@ static void sp_521_from_bin(sp_digit* r, int size, const byte* a, int n)
d = (byte*)(r + j);
#ifdef BIG_ENDIAN_ORDER
switch (i) {
- case 2: d[1] = *(a++); //fallthrough
- case 1: d[2] = *(a++); //fallthrough
+ case 2: d[1] = *(a++); FALL_THROUGH;
+ case 1: d[2] = *(a++); FALL_THROUGH;
case 0: d[3] = *a ; //fallthrough
}
#else
switch (i) {
- case 2: d[i-2] = a[2]; //fallthrough
- case 1: d[i-1] = a[1]; //fallthrough
+ case 2: d[i-2] = a[2]; FALL_THROUGH;
+ case 1: d[i-1] = a[1]; FALL_THROUGH;
case 0: d[i-0] = a[0]; //fallthrough
}
#endif
@@ -160133,14 +160133,14 @@ static void sp_1024_from_bin(sp_digit* r, int size, const byte* a, int n)
d = (byte*)(r + j);
#ifdef BIG_ENDIAN_ORDER
switch (i) {
- case 2: d[1] = *(a++); //fallthrough
- case 1: d[2] = *(a++); //fallthrough
+ case 2: d[1] = *(a++); FALL_THROUGH;
+ case 1: d[2] = *(a++); FALL_THROUGH;
case 0: d[3] = *a ; //fallthrough
}
#else
switch (i) {
- case 2: d[i-2] = a[2]; //fallthrough
- case 1: d[i-1] = a[1]; //fallthrough
+ case 2: d[i-2] = a[2]; FALL_THROUGH;
+ case 1: d[i-1] = a[1]; FALL_THROUGH;
case 0: d[i-0] = a[0]; //fallthrough
}
#endif
diff --git a/wolfcrypt/src/wc_lms.c b/wolfcrypt/src/wc_lms.c
index 595b93622d..f4051aa29b 100644
--- a/wolfcrypt/src/wc_lms.c
+++ b/wolfcrypt/src/wc_lms.c
@@ -28,6 +28,13 @@
#if FIPS_VERSION3_GE(2,0,0)
/* set NO_WRAPPERS before headers, use direct internal f()s not wrappers */
#define FIPS_NO_WRAPPERS
+
+ /* Keep LMS inside the FIPS in-core integrity boundary; Windows sorts
+ * it by section name, between sha3 (.fipsA$n) and fips.c (.fipsA$o). */
+ #ifdef USE_WINDOWS_API
+ #pragma code_seg(".fipsA$nc")
+ #pragma const_seg(".fipsB$nc")
+ #endif
#endif
#include
#include
diff --git a/wolfcrypt/src/wc_lms_impl.c b/wolfcrypt/src/wc_lms_impl.c
index 908fe97555..6fd9ca8bc7 100644
--- a/wolfcrypt/src/wc_lms_impl.c
+++ b/wolfcrypt/src/wc_lms_impl.c
@@ -41,6 +41,15 @@
#include
+#if FIPS_VERSION3_GE(2,0,0)
+ /* Keep LMS inside the FIPS in-core integrity boundary; Windows sorts
+ * it by section name. */
+ #ifdef USE_WINDOWS_API
+ #pragma code_seg(".fipsA$ne")
+ #pragma const_seg(".fipsB$ne")
+ #endif
+#endif
+
#include
#ifdef NO_INLINE
@@ -2319,7 +2328,9 @@ static int wc_lms_treehash_update(LmsState* state, LmsPrivState* privState,
byte* left = dp + LMS_D_LEN;
byte* temp = left + params->hash_len;
WC_DECLARE_VAR(stack, byte, (LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN, 0);
- byte* sp;
+ /* Init to NULL: sp is set and used only on the ret==0 path; 32-bit ARM
+ * gcc reports a false-positive -Wmaybe-uninitialized without this. */
+ byte* sp = NULL;
word32 max_cb = (word32)1 << params->cacheBits;
word32 i;
diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c
index f1e9d0496c..9b4f3197ac 100644
--- a/wolfcrypt/src/wc_mldsa.c
+++ b/wolfcrypt/src/wc_mldsa.c
@@ -142,6 +142,13 @@
#if FIPS_VERSION3_GE(2,0,0)
/* set NO_WRAPPERS before headers, use direct internal f()s not wrappers */
#define FIPS_NO_WRAPPERS
+
+ /* Keep ML-DSA inside the FIPS in-core integrity boundary; Windows sorts
+ * it by section name, between sha3 (.fipsA$n) and fips.c (.fipsA$o). */
+ #ifdef USE_WINDOWS_API
+ #pragma code_seg(".fipsA$nc")
+ #pragma const_seg(".fipsB$nc")
+ #endif
#endif
#ifndef WOLFSSL_MLDSA_NO_ASN1
@@ -765,6 +772,90 @@ static int mldsa_hash256_ctx_msg(wc_Shake* shake256, const byte* tr,
return ret;
}
+/* HashML-DSA PH-vs-paramSet enforcement. FIPS 204 sec. 5.4 (Table 4) restricts
+ * the HashML-DSA pre-hash PH to algorithms whose collision-resistance strength
+ * meets or exceeds the paramSet's security level; enforced for both sigGen and
+ * sigVer. Returns 0 for an approved (hashAlg, level) pair, else BAD_FUNC_ARG. */
+static int mldsa_check_hash_for_level(int hashAlg, byte level)
+{
+ int strengthBits; /* collision-resistance strength of the chosen hash */
+ int requiredBits; /* security level required by the paramSet */
+
+ switch (hashAlg) {
+ #ifndef NO_SHA256
+ case WC_HASH_TYPE_SHA256:
+ strengthBits = 128;
+ break;
+ #endif
+ #ifdef WOLFSSL_SHA384
+ case WC_HASH_TYPE_SHA384:
+ strengthBits = 192;
+ break;
+ #endif
+ #ifdef WOLFSSL_SHA512
+ case WC_HASH_TYPE_SHA512:
+ strengthBits = 256;
+ break;
+ #ifndef WOLFSSL_NOSHA512_256
+ case WC_HASH_TYPE_SHA512_256:
+ /* SHA-512/256 has 128-bit collision resistance (truncated). */
+ strengthBits = 128;
+ break;
+ #endif
+ #endif
+ #ifdef WOLFSSL_SHA3
+ #ifndef WOLFSSL_NOSHA3_256
+ case WC_HASH_TYPE_SHA3_256:
+ strengthBits = 128;
+ break;
+ #endif
+ #ifndef WOLFSSL_NOSHA3_384
+ case WC_HASH_TYPE_SHA3_384:
+ strengthBits = 192;
+ break;
+ #endif
+ #ifndef WOLFSSL_NOSHA3_512
+ case WC_HASH_TYPE_SHA3_512:
+ strengthBits = 256;
+ break;
+ #endif
+ #endif
+ #ifdef WOLFSSL_SHAKE128
+ case WC_HASH_TYPE_SHAKE128:
+ strengthBits = 128;
+ break;
+ #endif
+ #ifdef WOLFSSL_SHAKE256
+ case WC_HASH_TYPE_SHAKE256:
+ strengthBits = 256;
+ break;
+ #endif
+ default:
+ /* Hash not on the FIPS 204 Table 4 approved list (e.g. SHA-224,
+ * SHA-512/224, SHA3-224, MD5). Reject regardless of level. */
+ return BAD_FUNC_ARG;
+ }
+
+ switch (level) {
+ case WC_ML_DSA_44:
+ requiredBits = 128;
+ break;
+ case WC_ML_DSA_65:
+ requiredBits = 192;
+ break;
+ case WC_ML_DSA_87:
+ requiredBits = 256;
+ break;
+ default:
+ return BAD_FUNC_ARG;
+ }
+
+ if (strengthBits < requiredBits) {
+ return BAD_FUNC_ARG;
+ }
+ return 0;
+}
+
/* Get the OID for the digest hash.
*
* @param [in] hash Hash algorithm.
@@ -8663,6 +8754,16 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key,
* Commit (c) and h already encoded into signature. */
mldsa_vec_encode_gamma1(z, params->l, params->gamma1_bits, ze);
}
+ else if (sig != NULL) {
+ /* FIPS 204 sec 6.2 footnote 10: when a valid signature is not
+ * produced, return an error and no other output, destroying the
+ * results of the unsuccessful signing attempts. Each rejected
+ * iteration wrote its commitment hash and hint into sig. */
+ ForceZero(sig, params->sigSz);
+ if (sigLen != NULL) {
+ *sigLen = 0;
+ }
+ }
ForceZero(priv_rand_seed, sizeof(priv_rand_seed));
#ifdef WOLFSSL_CHECK_MEM_ZERO
@@ -9578,6 +9679,11 @@ static int mldsa_sign_ctx_hash_with_seed(wc_MlDsaKey* key,
ret = BAD_LENGTH_E;
}
+ /* FIPS 204 sec. 5.4 Table 4: enforce hash <-> paramSet matching. */
+ if (ret == 0) {
+ ret = mldsa_check_hash_for_level(hashAlg, key->level);
+ }
+
if (ret == 0) {
XMEMCPY(seedMu, seed, MLDSA_RND_SZ);
@@ -10262,6 +10368,10 @@ static int mldsa_verify_ctx_hash(wc_MlDsaKey* key, const byte* ctx,
{
ret = BAD_LENGTH_E;
}
+ /* FIPS 204 sec. 5.4 Table 4: enforce hash <-> paramSet matching. */
+ if (ret == 0) {
+ ret = mldsa_check_hash_for_level(hashAlg, key->level);
+ }
if (ret == 0) {
/* Step 6: Hash public key. */
@@ -10324,7 +10434,7 @@ int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng)
}
}
-#ifdef HAVE_FIPS
+#if FIPS_VERSION3_GE(7,0,0)
/* Pairwise Consistency Test (PCT) per FIPS 140-3 / ISO 19790:2012
* Section 7.10.3.3 (TE10.35.02): sign with new sk, verify with pk.
* Runs on every key generation. */
@@ -10530,6 +10640,9 @@ int wc_MlDsaKey_SignCtxHash(wc_MlDsaKey* key, const byte* ctx, byte ctxLen,
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
ret = BAD_FUNC_ARG;
}
+ if ((ret == 0) && (!key->prvKeySet)) {
+ ret = BAD_FUNC_ARG;
+ }
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
@@ -10578,12 +10691,16 @@ int wc_MlDsaKey_SignCtxWithSeed(wc_MlDsaKey* key, const byte* ctx, byte ctxLen,
int ret = 0;
/* Validate parameters. */
- if ((msg == NULL) || (sig == NULL) || (sigLen == NULL) || (key == NULL)) {
+ if ((msg == NULL) || (sig == NULL) || (sigLen == NULL) || (key == NULL) ||
+ (seed == NULL)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
ret = BAD_FUNC_ARG;
}
+ if ((ret == 0) && (!key->prvKeySet)) {
+ ret = BAD_FUNC_ARG;
+ }
if (ret == 0) {
/* Sign message. */
@@ -10658,6 +10775,9 @@ int wc_MlDsaKey_SignCtxHashWithSeed(wc_MlDsaKey* key, const byte* ctx,
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
ret = BAD_FUNC_ARG;
}
+ if ((ret == 0) && (!key->prvKeySet)) {
+ ret = BAD_FUNC_ARG;
+ }
if (ret == 0) {
/* Sign message. */
@@ -10698,6 +10818,9 @@ int wc_MlDsaKey_SignMuWithSeed(wc_MlDsaKey* key, byte* sig, word32 *sigLen,
if ((ret == 0) && (muLen != MLDSA_MU_SZ)) {
ret = BAD_FUNC_ARG;
}
+ if ((ret == 0) && (!key->prvKeySet)) {
+ ret = BAD_FUNC_ARG;
+ }
if (ret == 0) {
/* Build [seed||mu] buffer and call internal sign function. */
@@ -10744,9 +10867,18 @@ int wc_MlDsaKey_VerifyCtx(wc_MlDsaKey* key, const byte* sig, word32 sigLen,
if ((key == NULL) || (sig == NULL) || (msg == NULL) || (res == NULL)) {
ret = BAD_FUNC_ARG;
}
+ else {
+ /* Set the result before any further validation so a caller that
+ * inspects only res cannot observe a stale value from a call that
+ * failed before the signature was checked. */
+ *res = 0;
+ }
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
ret = BAD_FUNC_ARG;
}
+ if ((ret == 0) && (!key->pubKeySet)) {
+ ret = BAD_FUNC_ARG;
+ }
/* Reject msgLen that would cause integer overflow in hash computations */
if ((ret == 0) && (msgLen > WOLFSSL_MAX_32BIT / 2)) {
ret = BAD_FUNC_ARG;
@@ -10801,6 +10933,15 @@ int wc_MlDsaKey_Verify(wc_MlDsaKey* key, const byte* sig, word32 sigLen,
if ((key == NULL) || (sig == NULL) || (msg == NULL) || (res == NULL)) {
ret = BAD_FUNC_ARG;
}
+ else {
+ /* Set the result before any further validation so a caller that
+ * inspects only res cannot observe a stale value from a call that
+ * failed before the signature was checked. */
+ *res = 0;
+ }
+ if ((ret == 0) && (!key->pubKeySet)) {
+ ret = BAD_FUNC_ARG;
+ }
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
@@ -10853,9 +10994,18 @@ int wc_MlDsaKey_VerifyCtxHash(wc_MlDsaKey* key, const byte* sig, word32 sigLen,
if ((key == NULL) || (sig == NULL) || (hash == NULL) || (res == NULL)) {
ret = BAD_FUNC_ARG;
}
+ else {
+ /* Set the result before any further validation so a caller that
+ * inspects only res cannot observe a stale value from a call that
+ * failed before the signature was checked. */
+ *res = 0;
+ }
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
ret = BAD_FUNC_ARG;
}
+ if ((ret == 0) && (!key->pubKeySet)) {
+ ret = BAD_FUNC_ARG;
+ }
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
@@ -10907,9 +11057,18 @@ int wc_MlDsaKey_VerifyMu(wc_MlDsaKey* key, const byte* sig, word32 sigLen,
(mu == NULL) || (res == NULL)) {
ret = BAD_FUNC_ARG;
}
+ else {
+ /* Set the result before any further validation so a caller that
+ * inspects only res cannot observe a stale value from a call that
+ * failed before the signature was checked. */
+ *res = 0;
+ }
if ((ret == 0) && (muLen != MLDSA_MU_SZ)) {
ret = BAD_FUNC_ARG;
}
+ if ((ret == 0) && (!key->pubKeySet)) {
+ ret = BAD_FUNC_ARG;
+ }
if (ret == 0) {
ret = mldsa_verify_with_mu(key, mu, sig, sigLen, res);
diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c
index 52b9ef2cc8..faf774ddad 100644
--- a/wolfcrypt/src/wc_mlkem.c
+++ b/wolfcrypt/src/wc_mlkem.c
@@ -83,6 +83,13 @@
#if FIPS_VERSION3_GE(2,0,0)
/* set NO_WRAPPERS before headers, use direct internal f()s not wrappers */
#define FIPS_NO_WRAPPERS
+
+ /* Keep ML-KEM inside the FIPS in-core integrity boundary; Windows sorts
+ * it by section name, between sha3 (.fipsA$n) and fips.c (.fipsA$o). */
+ #ifdef USE_WINDOWS_API
+ #pragma code_seg(".fipsA$na")
+ #pragma const_seg(".fipsB$na")
+ #endif
#endif
#include
@@ -701,61 +708,10 @@ int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng)
ret = wc_MlKemKey_MakeKeyWithRandom(key, rand, sizeof(rand));
}
-#ifdef HAVE_FIPS
- /* Pairwise Consistency Test (PCT) per FIPS 140-3 / ISO 19790:2012
- * Section 7.10.3.3: encapsulate with ek, decapsulate with dk,
- * verify shared secrets match. */
- if (ret == 0) {
- WC_DECLARE_VAR(pct_ct, byte, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE,
- key->heap);
- byte pct_ss1[WC_ML_KEM_SS_SZ];
- byte pct_ss2[WC_ML_KEM_SS_SZ];
- word32 ctSz = 0;
-
- WC_ALLOC_VAR_EX(pct_ct, byte, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE,
- key->heap, DYNAMIC_TYPE_TMP_BUFFER, ret = MEMORY_E);
-
- /* pct_ss1/pct_ss2 hold the PCT shared secrets; baseline-zero and
- * register up front (single-exit block). */
-#ifdef WOLFSSL_CHECK_MEM_ZERO
- XMEMSET(pct_ss1, 0, sizeof(pct_ss1));
- XMEMSET(pct_ss2, 0, sizeof(pct_ss2));
- wc_MemZero_Add("mlkem pct ss1", pct_ss1, sizeof(pct_ss1));
- wc_MemZero_Add("mlkem pct ss2", pct_ss2, sizeof(pct_ss2));
-#endif
- if (ret == 0)
- ret = wc_MlKemKey_CipherTextSize(key, &ctSz);
-
- if (ret == 0)
- ret = wc_MlKemKey_Encapsulate(key, pct_ct, pct_ss1, rng);
-
- if (ret == 0)
- ret = wc_MlKemKey_Decapsulate(key, pct_ss2, pct_ct, ctSz);
-
- if (ret == 0) {
- if (XMEMCMP(pct_ss1, pct_ss2, WC_ML_KEM_SS_SZ) != 0)
- ret = ML_KEM_PCT_E;
- }
-
- ForceZero(pct_ss1, sizeof(pct_ss1));
- ForceZero(pct_ss2, sizeof(pct_ss2));
-#ifdef WOLFSSL_CHECK_MEM_ZERO
- wc_MemZero_Check(pct_ss1, sizeof(pct_ss1));
- wc_MemZero_Check(pct_ss2, sizeof(pct_ss2));
-#endif
- if (WC_VAR_OK(pct_ct))
- ForceZero(pct_ct, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE);
-
- WC_FREE_VAR_EX(pct_ct, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
-
- /* FIPS 140-3 IG 10.3.A (TE10.35.02): a key pair that fails the PCT
- * must be rendered unusable. Zeroize the generated key material so
- * a caller that ignores the return value cannot use it. */
- if (ret != 0) {
- wc_MlKemKey_Free(key);
- }
- }
-#endif /* HAVE_FIPS */
+ /* PCT (FIPS 140-3 IG 10.3.A 1.B) runs in wc_MlKemKey_MakeKeyWithRandom(),
+ * called above, not here: EncapsulateWithRandom() with a fixed `m` needs no
+ * RNG, so the test sits in the deterministic path both entry points share.
+ * An inline PCT here would repeat it per keygen for no added coverage. */
/* Ensure seeds are zeroized. */
ForceZero((void*)rand, (word32)sizeof(rand));
@@ -1036,8 +992,77 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand,
#endif
#endif
- /* Note: PCT is performed in wc_MlKemKey_MakeKey() which calls this
- * function and has the RNG parameter needed for encapsulation. */
+#if FIPS_VERSION3_GE(7,0,0)
+ /* Pairwise Consistency Test (PCT) per FIPS 140-3 IG 10.3.A 1.B and
+ * ISO/IEC 19790:2012 Section 7.10.3.3: encapsulate with the generated
+ * encapsulation key (ek), decapsulate with the matching decapsulation
+ * key (dk), and verify the recovered shared secret matches. This is a
+ * deterministic key-gen path with no caller RNG, so the PCT uses
+ * wc_MlKemKey_EncapsulateWithRandom() with a fixed 32-byte `m` (FIPS 203
+ * Algorithm 17 input); `m` need not be unpredictable for a PCT roundtrip.
+ */
+ if (ret == 0) {
+ WC_DECLARE_VAR(pct_ct, byte, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE,
+ key->heap);
+ byte pct_ss1[WC_ML_KEM_SS_SZ];
+ byte pct_ss2[WC_ML_KEM_SS_SZ];
+ word32 pct_ctSz = 0;
+ /* Fixed test pattern for the FIPS 203 Alg 17 `m` input; the value is
+ * arbitrary - a PCT roundtrip does not require unpredictability. */
+ static const byte pct_m[WC_ML_KEM_ENC_RAND_SZ] = {
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB
+ };
+
+ WC_ALLOC_VAR_EX(pct_ct, byte, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE,
+ key->heap, DYNAMIC_TYPE_TMP_BUFFER, ret = MEMORY_E);
+
+ /* pct_ss1/pct_ss2 hold the PCT shared secrets; baseline-zero and
+ * register up front (single-exit block). Carried over from upstream's
+ * inline PCT when this test moved here -- see the note in
+ * wc_MlKemKey_MakeKey(). */
+#ifdef WOLFSSL_CHECK_MEM_ZERO
+ XMEMSET(pct_ss1, 0, sizeof(pct_ss1));
+ XMEMSET(pct_ss2, 0, sizeof(pct_ss2));
+ wc_MemZero_Add("mlkem pct ss1", pct_ss1, sizeof(pct_ss1));
+ wc_MemZero_Add("mlkem pct ss2", pct_ss2, sizeof(pct_ss2));
+#endif
+ if (ret == 0)
+ ret = wc_MlKemKey_CipherTextSize(key, &pct_ctSz);
+
+ if (ret == 0)
+ ret = wc_MlKemKey_EncapsulateWithRandom(key, pct_ct, pct_ss1,
+ pct_m, (int)sizeof(pct_m));
+
+ if (ret == 0)
+ ret = wc_MlKemKey_Decapsulate(key, pct_ss2, pct_ct, pct_ctSz);
+
+ if (ret == 0) {
+ if (XMEMCMP(pct_ss1, pct_ss2, WC_ML_KEM_SS_SZ) != 0)
+ ret = ML_KEM_PCT_E;
+ }
+
+ ForceZero(pct_ss1, sizeof(pct_ss1));
+ ForceZero(pct_ss2, sizeof(pct_ss2));
+#ifdef WOLFSSL_CHECK_MEM_ZERO
+ wc_MemZero_Check(pct_ss1, sizeof(pct_ss1));
+ wc_MemZero_Check(pct_ss2, sizeof(pct_ss2));
+#endif
+ if (WC_VAR_OK(pct_ct))
+ ForceZero(pct_ct, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE);
+
+ WC_FREE_VAR_EX(pct_ct, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
+
+ /* FIPS 140-3 IG 10.3.A (TE10.35.02): a key pair that fails the PCT
+ * must be rendered unusable. Zeroize the generated key material so
+ * a caller that ignores the return value cannot use it. */
+ if (ret != 0) {
+ wc_MlKemKey_Free(key);
+ }
+ }
+#endif /* HAVE_FIPS */
return ret;
}
@@ -2127,6 +2152,9 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss,
wc_MemZero_Check(msg, sizeof(msg));
wc_MemZero_Check(kr, sizeof(kr));
#endif
+ /* FIPS 203 sec 6.3: the implicit-reject flag is secret intermediate
+ * data and shall be destroyed before decapsulation terminates. */
+ ForceZero(&fail, sizeof(fail));
return ret;
}
@@ -2356,6 +2384,7 @@ int wc_MlKemKey_DecodePublicKey(MlKemKey* key, const unsigned char* in,
word32 pubLen = 0;
unsigned int k = 0;
const unsigned char* p = in;
+ byte newH[WC_ML_KEM_SYM_SZ];
if ((key == NULL) || (in == NULL)) {
ret = BAD_FUNC_ARG;
@@ -2426,10 +2455,28 @@ int wc_MlKemKey_DecodePublicKey(MlKemKey* key, const unsigned char* in,
ret = mlkem_check_reduced(key->pub, (int)k);
}
if (ret == 0) {
- /* Calculate public hash. */
- ret = MLKEM_HASH_H(&key->hash, in, len, key->h);
- }
- if (ret == 0) {
+ /* Calculate public hash into a temporary: key->h is still the OLD hash
+ * and is needed just below to tell whether this is the same key pair. */
+ ret = MLKEM_HASH_H(&key->hash, in, len, newH);
+ }
+ if (ret == 0) {
+ /* A private key blob embeds its own ek, so decoding one already set
+ * h = H(ek). If the public key being imported now hashes to that same
+ * value it is the matching half of the pair already held -- importing
+ * both halves of one key pair is normal -- and the private key stays
+ * bound. A DIFFERENT ek unbinds it and stales matrix A, which is keyed
+ * to the old public seed.
+ * Vendor-elected hardening, not a FIPS 203 sec 7.3 requirement: 7.3
+ * checks dk against the H(ek) carried inside dk (see the import path
+ * below) and says nothing about a separately imported ek. The basis is
+ * SP 800-227, which permits skipping a re-check only while the module
+ * "stores that input in a manner that prevents modification"; importing
+ * a different ek over a held dk is exactly such a modification. */
+ if (((key->flags & MLKEM_FLAG_H_SET) == 0) ||
+ (XMEMCMP(key->h, newH, WC_ML_KEM_SYM_SZ) != 0)) {
+ key->flags &= ~(MLKEM_FLAG_PRIV_SET | MLKEM_FLAG_A_SET);
+ }
+ XMEMCPY(key->h, newH, WC_ML_KEM_SYM_SZ);
/* Record public key and public hash set. */
key->flags |= MLKEM_FLAG_PUB_SET | MLKEM_FLAG_H_SET;
}
diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c
index ec61e67b6e..1146930680 100644
--- a/wolfcrypt/src/wc_mlkem_poly.c
+++ b/wolfcrypt/src/wc_mlkem_poly.c
@@ -71,6 +71,15 @@
#include
+#if FIPS_VERSION3_GE(2,0,0)
+ /* Keep ML-KEM inside the FIPS in-core integrity boundary; Windows sorts
+ * it by section name. */
+ #ifdef USE_WINDOWS_API
+ #pragma code_seg(".fipsA$nb")
+ #pragma const_seg(".fipsB$nb")
+ #endif
+#endif
+
#ifdef WC_MLKEM_NO_ASM
#undef USE_INTEL_SPEEDUP
#undef WOLFSSL_ARMASM
@@ -4892,7 +4901,10 @@ static int mlkem_cmp_c(const byte* a, const byte* b, int sz)
for (i = 0; i < sz; i++) {
r |= a[i] ^ b[i];
}
- return (int)(0 - ((-(word32)r) >> 31));
+ /* 0U - x rather than -x: identical modular negation for an unsigned
+ * operand, but avoids MSVC C4018/C4146 on the unary minus. Kept branch
+ * free so the comparison stays constant time. */
+ return (int)(0U - ((0U - (word32)r) >> 31));
}
#endif
diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c
index 0fb574725e..03f3242ed0 100644
--- a/wolfcrypt/src/wc_slhdsa.c
+++ b/wolfcrypt/src/wc_slhdsa.c
@@ -26,6 +26,13 @@
#if FIPS_VERSION3_GE(2,0,0)
/* set NO_WRAPPERS before headers, use direct internal f()s not wrappers */
#define FIPS_NO_WRAPPERS
+
+ /* Keep SLH-DSA inside the FIPS in-core integrity boundary; Windows sorts
+ * it by section name, between sha3 (.fipsA$n) and fips.c (.fipsA$o). */
+ #ifdef USE_WINDOWS_API
+ #pragma code_seg(".fipsA$nh")
+ #pragma const_seg(".fipsB$nh")
+ #endif
#endif
#include
@@ -6678,6 +6685,13 @@ int wc_SlhDsaKey_Init(SlhDsaKey* key, enum SlhDsaParam param, void* heap,
if (key == NULL) {
ret = BAD_FUNC_ARG;
}
+ if (ret == 0) {
+ /* Zeroize the key up front so a failed parameter lookup below leaves
+ * key->params == NULL and wc_SlhDsaKey_Free() stays a safe no-op; a
+ * garbage params->n must never drive the ForceZero of key->sk
+ * (memory safety / ISO/IEC 19790:2012 7.9). */
+ XMEMSET(key, 0, sizeof(SlhDsaKey));
+ }
if (ret == 0) {
int i;
@@ -6694,9 +6708,6 @@ int wc_SlhDsaKey_Init(SlhDsaKey* key, enum SlhDsaParam param, void* heap,
}
}
if (ret == 0) {
- /* Zeroize key. */
- XMEMSET(key, 0, sizeof(SlhDsaKey));
-
/* Set the parameters into key early so SLHDSA_IS_SHA2 works. */
key->params = &SlhDsaParams[idx];
/* Set heap hint to use with all allocations. */
@@ -7035,6 +7046,45 @@ int wc_SlhDsaKey_MakeKey(SlhDsaKey* key, WC_RNG* rng)
key->sk + 2 * n, n);
}
+#ifdef HAVE_FIPS
+ /* Pairwise Consistency Test (PCT) per FIPS 140-3 IG 10.3.A (TE10.35.02):
+ * sign with the new sk, verify with the matching pk. SLH-DSA (FIPS 205)
+ * is stateless, so the relaxed PCT rule for stateful HBS (LMS/XMSS) does
+ * not apply -- PCT runs on every KeyGen. SignDeterministic avoids
+ * consuming RNG state. */
+ if (ret == 0) {
+ static const byte pct_msg[] = "wolfSSL SLH-DSA PCT";
+ word32 pct_sigLen = key->params->sigLen;
+ byte* pct_sig = (byte*)XMALLOC(pct_sigLen, NULL,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ word32 pct_sigSz = pct_sigLen;
+
+ if (pct_sig == NULL) {
+ ret = MEMORY_E;
+ }
+ if (ret == 0) {
+ ret = wc_SlhDsaKey_SignDeterministic(key, NULL, 0,
+ pct_msg, sizeof(pct_msg), pct_sig, &pct_sigSz);
+ }
+ if (ret == 0) {
+ ret = wc_SlhDsaKey_Verify(key, NULL, 0,
+ pct_msg, sizeof(pct_msg), pct_sig, pct_sigSz);
+ if (ret != 0) {
+ ret = SLH_DSA_PCT_E;
+ }
+ }
+ if (pct_sig != NULL) {
+ ForceZero(pct_sig, pct_sigLen);
+ XFREE(pct_sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ }
+ /* IG 10.3.A (TE10.35.02): a key pair that fails the PCT must be
+ * rendered unusable. */
+ if (ret != 0) {
+ wc_SlhDsaKey_Free(key);
+ }
+ }
+#endif /* HAVE_FIPS */
+
return ret;
}
@@ -7989,6 +8039,88 @@ static const byte slhdsakey_oid_sha3_512[] = {
#endif
#endif
+/* HashSLH-DSA PH-vs-paramSet enforcement. FIPS 205 sec. 10.2.2 (Table 9): the
+ * pre-hash PH must have collision-resistance >= the paramSet security level
+ * (key->params->n in bytes: 16 = 128-bit, 24 = 192-bit, 32 = 256-bit).
+ * Returns 0 if approved, else BAD_FUNC_ARG. */
+static int slhdsa_check_hash_for_n(enum wc_HashType hashType, byte n)
+{
+ int strengthBits;
+ int requiredBits;
+
+ switch ((int)hashType) {
+ #ifndef NO_SHA256
+ case WC_HASH_TYPE_SHA256:
+ strengthBits = 128;
+ break;
+ #endif
+ #ifdef WOLFSSL_SHA384
+ case WC_HASH_TYPE_SHA384:
+ strengthBits = 192;
+ break;
+ #endif
+ #ifdef WOLFSSL_SHA512
+ case WC_HASH_TYPE_SHA512:
+ strengthBits = 256;
+ break;
+ #ifndef WOLFSSL_NOSHA512_256
+ case WC_HASH_TYPE_SHA512_256:
+ /* SHA-512/256 has 128-bit collision resistance (truncated). */
+ strengthBits = 128;
+ break;
+ #endif
+ #endif
+ #ifdef WOLFSSL_SHA3
+ #ifndef WOLFSSL_NOSHA3_256
+ case WC_HASH_TYPE_SHA3_256:
+ strengthBits = 128;
+ break;
+ #endif
+ #ifndef WOLFSSL_NOSHA3_384
+ case WC_HASH_TYPE_SHA3_384:
+ strengthBits = 192;
+ break;
+ #endif
+ #ifndef WOLFSSL_NOSHA3_512
+ case WC_HASH_TYPE_SHA3_512:
+ strengthBits = 256;
+ break;
+ #endif
+ #endif
+ #ifdef WOLFSSL_SHAKE128
+ case WC_HASH_TYPE_SHAKE128:
+ strengthBits = 128;
+ break;
+ #endif
+ #ifdef WOLFSSL_SHAKE256
+ case WC_HASH_TYPE_SHAKE256:
+ strengthBits = 256;
+ break;
+ #endif
+ default:
+ /* Hash not on the FIPS 205 Table 9 approved list. */
+ return BAD_FUNC_ARG;
+ }
+
+ if (n == WC_SLHDSA_N_128) {
+ requiredBits = 128;
+ }
+ else if (n == WC_SLHDSA_N_192) {
+ requiredBits = 192;
+ }
+ else if (n == WC_SLHDSA_N_256) {
+ requiredBits = 256;
+ }
+ else {
+ return BAD_FUNC_ARG;
+ }
+
+ if (strengthBits < requiredBits) {
+ return BAD_FUNC_ARG;
+ }
+ return 0;
+}
+
/* Validate the caller-supplied pre-hashed digest length and look up the
* corresponding OID for the chosen hash algorithm.
*
@@ -8206,6 +8338,12 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx,
(sigSz == NULL)) {
ret = BAD_FUNC_ARG;
}
+ /* HashSLH-DSA requires an explicit, approved pre-hash; the "pure
+ * SLH-DSA" sentinel WC_HASH_TYPE_NONE is never valid here
+ * (FIPS 205 Section 10.2.2 / Table 9). */
+ else if (hashType == WC_HASH_TYPE_NONE) {
+ ret = BAD_FUNC_ARG;
+ }
/* Check sig buffer is large enough to hold generated signature. */
else if (*sigSz < key->params->sigLen) {
ret = BAD_LENGTH_E;
@@ -8215,6 +8353,12 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx,
/* Alg 23, Step 6: Return error. */
ret = BAD_FUNC_ARG;
}
+ /* FIPS 205 sec. 10.2.2 Table 9: enforce PH <-> paramSet matching before
+ * pre-hashing the message. Rejects PHs whose collision-resistance
+ * strength is below the paramSet's security level (n). */
+ if (ret == 0) {
+ ret = slhdsa_check_hash_for_n(hashType, key->params->n);
+ }
if (ret == 0) {
/* Alg 23, Steps 8-23: Validate caller-supplied pre-hashed digest length
* and select OID for the chosen hash algorithm. */
@@ -8449,8 +8593,10 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
ret = MISSING_KEY;
}
/* First sanity check on hashType; the downstream prehash validator does
- * the detailed check for the actual type. */
- else if ((word32)hashType > (word32)WC_HASH_TYPE_MAX) {
+ * the detailed check. WC_HASH_TYPE_NONE is never a valid pre-hash
+ * (FIPS 205 Section 10.2.2 / Table 9). */
+ else if ((hashType == WC_HASH_TYPE_NONE) ||
+ ((word32)hashType > (word32)WC_HASH_TYPE_MAX)) {
ret = BAD_FUNC_ARG;
}
@@ -8586,6 +8732,14 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
ret = BAD_FUNC_ARG;
}
+ /* FIPS 205 sec. 10.2.2 Table 9: enforce PH <-> paramSet matching before
+ * verification on all build paths, matching wc_SlhDsaKey_SignHash.
+ * A compliant signer never emits a disallowed combo, so this rejects
+ * only out-of-policy signatures. */
+ if (ret == 0) {
+ ret = slhdsa_check_hash_for_n(hashType, key->params->n);
+ }
+
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
#ifndef WOLF_CRYPTO_CB_FIND
@@ -8772,6 +8926,7 @@ int wc_SlhDsaKey_CheckKey(SlhDsaKey* key)
if (ret == 0) {
byte root[SLHDSA_MAX_N];
byte n = key->params->n;
+ int savedFlags = key->flags;
/* Cache the public key root as making the key overwrites. */
XMEMCPY(root, key->sk + 3 * n, n);
@@ -8781,6 +8936,11 @@ int wc_SlhDsaKey_CheckKey(SlhDsaKey* key)
if ((ret == 0) && (XMEMCMP(root, key->sk + 3 * n, n) != 0)) {
ret = WC_KEY_MISMATCH_E;
}
+ /* CheckKey must be non-mutating: MakeKeyWithRandom recomputed the root
+ * in place and reset key->flags, so restore both on every path so a
+ * failed check leaves no seed-derived root or re-marked usable key. */
+ XMEMCPY(key->sk + 3 * n, root, n);
+ key->flags = savedFlags;
}
return ret;
@@ -8807,8 +8967,10 @@ int wc_SlhDsaKey_ExportPrivate(SlhDsaKey* key, byte* priv, word32* privLen)
(privLen == NULL)) {
ret = BAD_FUNC_ARG;
}
- /* Check private key buffer length. */
- else if (*privLen < key->params->n * 4) {
+ /* Check private key buffer length. params->n is a byte and promotes to
+ * int, so the comparison against a word32 is signed/unsigned (MSVC C4018);
+ * cast to match the word32 the else branch below already uses. */
+ else if (*privLen < (word32)key->params->n * 4U) {
ret = BAD_LENGTH_E;
}
else {
@@ -8842,8 +9004,9 @@ int wc_SlhDsaKey_ExportPublic(SlhDsaKey* key, byte* pub, word32* pubLen)
(pubLen == NULL)) {
ret = BAD_FUNC_ARG;
}
- /* Check public key buffer length. */
- else if (*pubLen < key->params->n * 2) {
+ /* Check public key buffer length. See the C4018 note in
+ * wc_SlhDsaKey_ExportPrivate() above. */
+ else if (*pubLen < (word32)key->params->n * 2U) {
ret = BAD_LENGTH_E;
}
else {
diff --git a/wolfcrypt/src/wc_xmss.c b/wolfcrypt/src/wc_xmss.c
index b4f4c76185..9d6c02f9d9 100644
--- a/wolfcrypt/src/wc_xmss.c
+++ b/wolfcrypt/src/wc_xmss.c
@@ -28,6 +28,13 @@
#if FIPS_VERSION3_GE(2,0,0)
/* set NO_WRAPPERS before headers, use direct internal f()s not wrappers */
#define FIPS_NO_WRAPPERS
+
+ /* Keep XMSS inside the FIPS in-core integrity boundary; Windows sorts
+ * it by section name, between sha3 (.fipsA$n) and fips.c (.fipsA$o). */
+ #ifdef USE_WINDOWS_API
+ #pragma code_seg(".fipsA$nf")
+ #pragma const_seg(".fipsB$nf")
+ #endif
#endif
#include
#include
diff --git a/wolfcrypt/src/wc_xmss_impl.c b/wolfcrypt/src/wc_xmss_impl.c
index 369f074080..784492e985 100644
--- a/wolfcrypt/src/wc_xmss_impl.c
+++ b/wolfcrypt/src/wc_xmss_impl.c
@@ -33,6 +33,15 @@
#include
+#if FIPS_VERSION3_GE(2,0,0)
+ /* Keep XMSS inside the FIPS in-core integrity boundary; Windows sorts
+ * it by section name. */
+ #ifdef USE_WINDOWS_API
+ #pragma code_seg(".fipsA$ng")
+ #pragma const_seg(".fipsB$ng")
+ #endif
+#endif
+
#include
#include
diff --git a/wolfcrypt/src/wolfentropy.c b/wolfcrypt/src/wolfentropy.c
index 88ddcb986d..0f8765cc4a 100644
--- a/wolfcrypt/src/wolfentropy.c
+++ b/wolfcrypt/src/wolfentropy.c
@@ -433,6 +433,10 @@ static int Entropy_MemUse(void)
}
}
+ /* d held SHA3-256 digests of the entropy pool; clear it (ISO/IEC
+ * 19790:2012 7.9). */
+ wc_ForceZero(d, sizeof(d));
+
return 0;
}
@@ -518,6 +522,49 @@ static int Entropy_GetNoise(unsigned char* noise, int samples)
*/
static wolfSSL_Mutex entropy_mutex WOLFSSL_MUTEX_INITIALIZER_CLAUSE(entropy_mutex);
+#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_MUTEX_INITIALIZER)
+/* One-time initialization state for entropy_mutex on platforms that lack a
+ * static mutex initializer. A mutex cannot be lazily initialized safely
+ * without a static initializer, so an atomic compare-exchange elects exactly
+ * one thread to run wc_InitMutex(); other callers wait for it to finish.
+ * States: 0 = uninitialized, 1 = initializing, 2 = ready. When
+ * WOLFSSL_NO_ATOMICS is defined the compare-exchange is not truly atomic (see
+ * wolfssl/wolfcrypt/wc_port.h), so such a platform must complete the first
+ * entropy initialization single-threaded, e.g. via wolfCrypt_Init(). */
+#ifdef WOLFSSL_NO_ATOMICS
+ static int entropy_mutex_state = 0;
+#else
+ static wolfSSL_Atomic_Int entropy_mutex_state = WOLFSSL_ATOMIC_INITIALIZER(0);
+#endif
+
+static int Entropy_InitMutexOnce(void)
+{
+ int ret = 0;
+ int expected = 0;
+
+ if (wolfSSL_Atomic_Int_CompareExchange(&entropy_mutex_state, &expected, 1)) {
+ /* This thread was elected to initialize the mutex. */
+ ret = wc_InitMutex(&entropy_mutex);
+ /* Publish ready (2) on success, or reset to 0 so a later call retries. */
+ WOLFSSL_ATOMIC_STORE(entropy_mutex_state, (ret == 0) ? 2 : 0);
+ }
+ else {
+ /* Another thread is initializing (1) or has finished (2); wait for it
+ * to leave the initializing state before the mutex is used. */
+ int st;
+ while ((st = (int)WOLFSSL_ATOMIC_LOAD(entropy_mutex_state)) == 1) {
+ WC_RELAX_LONG_LOOP();
+ }
+ if (st != 2) {
+ /* The electing thread's wc_InitMutex() failed. */
+ ret = BAD_MUTEX_E;
+ }
+ }
+
+ return ret;
+}
+#endif /* !SINGLE_THREADED && !WOLFSSL_MUTEX_INITIALIZER */
+
/* Generate raw entropy for performing assessment.
*
* @param [out] raw Buffer to hold raw entropy data.
@@ -720,7 +767,7 @@ static int Entropy_HealthTest_Proportion(byte noise)
/* Check whether first value has too many repetitions in queue. */
if (prop_cnt[noise] >= PROP_CUTOFF) {
#ifdef WOLFSSL_DEBUG_ENTROPY_MEMUSE
- fprintf(stderr, "PROPORTION FAILED: %d %d\n", val, prop_cnt[noise]);
+ fprintf(stderr, "PROPORTION FAILED: %d %d\n", noise, prop_cnt[noise]);
#endif
Entropy_HealthTest_Proportion_Reset();
/* Error code returned. */
@@ -879,6 +926,12 @@ int wc_Entropy_Get(int bits, unsigned char* entropy, word32 len)
if ((ret == 0) && (wc_LockMutex(&entropy_mutex) != 0)) {
ret = BAD_MUTEX_E;
}
+ /* Bail if the source was finalized between the pre-lock initialization
+ * check and acquiring the mutex, so a freed entropyHash is never used. */
+ if ((ret == 0) && (!entropy_memuse_initialized)) {
+ wc_UnLockMutex(&entropy_mutex);
+ ret = BAD_MUTEX_E;
+ }
#ifdef ENTROPY_MEMUSE_THREADED
if (ret == 0) {
@@ -930,6 +983,10 @@ int wc_Entropy_Get(int bits, unsigned char* entropy, word32 len)
}
}
+ /* Raw pre-conditioning noise is sensitive; do not leave it in the static
+ * buffer after use (ISO/IEC 19790:2012 7.9). */
+ wc_ForceZero(noise, sizeof(noise));
+
#ifdef ENTROPY_MEMUSE_THREADED
/* Stop the counter thread to avoid thrashing the system. */
Entropy_StopThread();
@@ -959,6 +1016,12 @@ int wc_Entropy_OnDemandTest(void)
if (wc_LockMutex(&entropy_mutex) != 0) {
ret = BAD_MUTEX_E;
}
+ /* Bail if the source was finalized concurrently, so the freed entropyHash
+ * and health-test state are not used. */
+ if ((ret == 0) && (!entropy_memuse_initialized)) {
+ wc_UnLockMutex(&entropy_mutex);
+ ret = BAD_MUTEX_E;
+ }
if (ret == 0) {
/* Perform startup tests. */
@@ -984,7 +1047,8 @@ int Entropy_Init(void)
/* Check whether initialization has succeeded before. */
if (!entropy_memuse_initialized) {
#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_MUTEX_INITIALIZER)
- ret = wc_InitMutex(&entropy_mutex);
+ /* Initialize the mutex exactly once, even under concurrent callers. */
+ ret = Entropy_InitMutexOnce();
#endif
if (ret == 0)
ret = wc_LockMutex(&entropy_mutex);
@@ -1035,15 +1099,35 @@ void Entropy_Final(void)
{
/* Only finalize when initialized. */
if (entropy_memuse_initialized) {
- /* Dispose of the SHA3-356 hash object. */
+ /* Serialize teardown with Entropy_GetEntropy() and
+ * wc_Entropy_OnDemandTest(): the shared SHA3 conditioning object
+ * (entropyHash) and the health-test state must not be freed while a
+ * collector holds the mutex and is still using them, which would be a
+ * use-after-free of the SP 800-90B entropy source state. */
+ int locked = (wc_LockMutex(&entropy_mutex) == 0);
+ /* Clear the initialized flag first, under the lock, so any collector
+ * that acquires the mutex after this point observes finalization and
+ * bails instead of using the freed hash object. */
+ entropy_memuse_initialized = 0;
+ /* Dispose of the SHA3-256 hash object. */
wc_Sha3_256_Free(&entropyHash);
+ /* Clear health test data. */
+ Entropy_HealthTest_Reset();
+ /* Zeroize the accumulated entropy pool on teardown (ISO/IEC
+ * 19790:2012 7.9); it holds pre-conditioning entropy-source state. */
+ wc_ForceZero(entropy_state, sizeof(entropy_state));
+ if (locked) {
+ wc_UnLockMutex(&entropy_mutex);
+ }
#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_MUTEX_INITIALIZER)
+ /* Free the mutex last, after releasing it. Entropy_Final() is a
+ * single-threaded shutdown operation; a thread blocked in
+ * wc_LockMutex() during finalization is a caller-contract violation. */
wc_FreeMutex(&entropy_mutex);
+ /* Reset the one-time-init state so a later Entropy_Init() re-creates
+ * the mutex. */
+ WOLFSSL_ATOMIC_STORE(entropy_mutex_state, 0);
#endif
- /* Clear health test data. */
- Entropy_HealthTest_Reset();
- /* No longer initialized. */
- entropy_memuse_initialized = 0;
}
}
diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c
index 5f7047ac79..a95dc0f4fc 100644
--- a/wolfcrypt/test/test.c
+++ b/wolfcrypt/test/test.c
@@ -139,7 +139,7 @@ static const byte const_byte_array[] = "A+Gd\0\0\0";
esp_start_heap = esp_this_heap; \
} \
ESP_LOGI(ESPIDF_TAG, "%s #%d; Heap free: %d", \
- ((b) ? (b) : ""), /* breadcumb string */ \
+ ((b) ? (b) : ""), /* breadcrumb string */ \
((i) ? (i) : 0), /* index */ \
esp_this_heap);
@@ -7355,7 +7355,14 @@ static wc_test_ret_t sha3_256_kat_test(wc_Sha3* sha, wc_Sha3* shaCopy)
/* this is a software only variant of SHA3 not supported by external
* hardware devices */
-#if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_ASYNC_CRYPT)
+ /* Legacy Keccak-256 (0x01 pad) is NOT an approved algorithm and is excluded
+ * from the v7 module -- Sha3Final() only applies the Keccak pad when
+ * !FIPS_VERSION3_GE(7,0,0) (see wolfcrypt/src/sha3.c, FIPS 202 6.1). An
+ * approved build therefore emits the FIPS 202 0x06 pad and this vector will
+ * not match, so gate the test on the SAME condition as the module rather
+ * than relaxing the module to satisfy the test. */
+#if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_ASYNC_CRYPT) && \
+ !FIPS_VERSION3_GE(7,0,0)
{
/* test vector with hash of empty string */
static const char* Keccak256EmptyOut =
@@ -28324,7 +28331,9 @@ static wc_test_ret_t rsa_pss_test(WC_RNG* rng, RsaKey* key)
enum wc_HashType hash[] = { WC_HASH_TYPE_SHA256 };
#else
int mgf[] = {
-#ifndef NO_SHA
+/* FIPS 186-5 sec 5.4 disallows SHA-1 for RSA signature generation; the module
+ * rejects it at the RSA-PSS signing service. */
+#if !defined(NO_SHA) && !FIPS_VERSION3_GE(7,0,0)
WC_MGF1SHA1,
#endif
#ifdef WOLFSSL_SHA224
@@ -28341,7 +28350,7 @@ static wc_test_ret_t rsa_pss_test(WC_RNG* rng, RsaKey* key)
#endif
};
enum wc_HashType hash[] = {
-#ifndef NO_SHA
+#if !defined(NO_SHA) && !FIPS_VERSION3_GE(7,0,0)
WC_HASH_TYPE_SHA,
#endif
#ifdef WOLFSSL_SHA224
@@ -47394,6 +47403,29 @@ static wc_test_ret_t ed25519ctx_test(void)
ret = wc_ed25519_import_private_key(sKeyCtx, ED25519_KEY_SIZE, pKeyCtx,
sizeof(pKeyCtx), &key);
+#ifdef WC_FIPS_ED25519CTX_NOT_APPROVED
+ /* Ed25519ctx (dom2(0,context)) is not an Approved EdDSA instance in the
+ * FIPS module (FIPS 186-5 sec 7.6/7.8); the service must reject it with
+ * SIG_TYPE_E rather than produce a signature.
+ * Gated on the module's own capability macro (fips.h), NOT on
+ * FIPS_VERSION3_GE(7,0,0): an earlier v7.0.0 module reports the same
+ * version but still signs, so the version predicate would assert a
+ * behavior that module does not have. When the macro is absent the #else
+ * branch below runs the ordinary RFC 8032 Ed25519ctx test instead. */
+ if (ret == 0) {
+ ret = wc_ed25519ctx_sign_msg(msgCtx, sizeof(msgCtx), out, &outlen, &key,
+ contextCtx, sizeof(contextCtx));
+ if (ret == WC_NO_ERR_TRACE(SIG_TYPE_E))
+ ret = 0;
+ else if (ret == 0)
+ ret = WC_TEST_RET_ENC_NC;
+ }
+ (void)sigCtx1;
+ (void)sigCtx2;
+#ifdef HAVE_ED25519_VERIFY
+ (void)verify;
+#endif
+#else
if (ret == 0)
ret = wc_ed25519ctx_sign_msg(msgCtx, sizeof(msgCtx), out, &outlen, &key,
contextCtx, sizeof(contextCtx));
@@ -47424,6 +47456,7 @@ static wc_test_ret_t ed25519ctx_test(void)
if (ret == 0 && verify != 1)
ret = WC_TEST_RET_ENC_NC;
#endif
+#endif /* FIPS_VERSION3_GE(7,0,0) */
wc_ed25519_free(&key);
@@ -50313,8 +50346,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed448_test(void)
}
#endif /* HAVE_ED448 */
+/* Seed-injecting PQC KATs drive ML-KEM and SLH-DSA keygen/sign from fixed NIST
+ * seeds. FIPS 203 sec 3.3, 204 sec 5.4 and 205 sec 10.2 require the module to
+ * generate that randomness itself, so the public *_with_seed/_with_random
+ * service returns WC_FIPS_NOT_APPROVED in a FIPS build; run in non-FIPS only. */
+#if !defined(HAVE_FIPS)
+ #define WOLFSSL_TEST_PQC_SEED_KAT
+#endif
+
#ifdef WOLFSSL_HAVE_MLKEM
-#if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512)
+#if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
static wc_test_ret_t mlkem512_kat(void)
{
wc_test_ret_t ret;
@@ -51408,7 +51449,7 @@ static wc_test_ret_t mlkem512_kat(void)
}
#endif /* !WOLFSSL_NO_KYBER512 && !WOLFSSL_NO_ML_KEM_512 */
-#if !defined(WOLFSSL_NO_KYBER768) && !defined(WOLFSSL_NO_ML_KEM_768)
+#if !defined(WOLFSSL_NO_KYBER768) && !defined(WOLFSSL_NO_ML_KEM_768) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
static wc_test_ret_t mlkem768_kat(void)
{
wc_test_ret_t ret;
@@ -52848,7 +52889,7 @@ static wc_test_ret_t mlkem768_kat(void)
}
#endif /* !WOLFSSL_NO_KYBER768 && !WOLFSSL_NO_ML_KEM_768 */
-#if !defined(WOLFSSL_NO_KYBER1024) && !defined(WOLFSSL_NO_ML_KEM_1024)
+#if !defined(WOLFSSL_NO_KYBER1024) && !defined(WOLFSSL_NO_ML_KEM_1024) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
static wc_test_ret_t mlkem1024_kat(void)
{
wc_test_ret_t ret;
@@ -54948,17 +54989,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mlkem_test(void)
#endif
}
-#if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512)
+#if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
ret = mlkem512_kat();
if (ret != 0)
goto out;
#endif
-#if !defined(WOLFSSL_NO_KYBER768) && !defined(WOLFSSL_NO_ML_KEM_768)
+#if !defined(WOLFSSL_NO_KYBER768) && !defined(WOLFSSL_NO_ML_KEM_768) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
ret = mlkem768_kat();
if (ret != 0)
goto out;
#endif
-#if !defined(WOLFSSL_NO_KYBER1024) && !defined(WOLFSSL_NO_ML_KEM_1024)
+#if !defined(WOLFSSL_NO_KYBER1024) && !defined(WOLFSSL_NO_ML_KEM_1024) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
ret = mlkem1024_kat();
if (ret != 0)
goto out;
@@ -58483,6 +58524,126 @@ static wc_test_ret_t mldsa_param_test(int param, WC_RNG* rng)
#endif
return ret;
}
+
+#if !defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
+ !defined(WOLFSSL_DILITHIUM_NO_VERIFY)
+/* Negative test: HashML-DSA must reject a pre-hash whose collision resistance
+ * is below the parameter set's claimed security strength (FIPS 204 sec. 5.4,
+ * Table 4: approved PH per level). Asserts sigGen and sigVer both reject. */
+static wc_test_ret_t mldsa_hash_paramset_rejection_test(WC_RNG* rng)
+{
+ wc_test_ret_t ret = 0;
+ int i;
+#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
+ dilithium_key* key = NULL;
+ byte* sig = NULL;
+#else
+ dilithium_key key[1];
+ byte sig[DILITHIUM_MAX_SIG_SIZE];
+#endif
+ word32 sigLen;
+ int verified;
+
+ /* Fixed-content digests; sizes match each digest length so the length
+ * check does not short-circuit before the hash-vs-paramSet gate. */
+ static const byte hash32[32] = { /* SHA-256 digest size */
+ 0xBA,0x78,0x16,0xBF,0x8F,0x01,0xCF,0xEA,
+ 0x41,0x41,0x40,0xDE,0x5D,0xAE,0x22,0x23,
+ 0xB0,0x03,0x61,0xA3,0x96,0x17,0x7A,0x9C,
+ 0xB4,0x10,0xFF,0x61,0xF2,0x00,0x15,0xAD
+ };
+ static const byte hash48[48] = { /* SHA-384 digest size */
+ 0xCB,0x00,0x75,0x3F,0x45,0xA3,0x5E,0x8B,
+ 0xB5,0xA0,0x3D,0x69,0x9A,0xC6,0x50,0x07,
+ 0x27,0x2C,0x32,0xAB,0x0E,0xDE,0xD1,0x63,
+ 0x1A,0x8B,0x60,0x5A,0x43,0xFF,0x5B,0xED,
+ 0x80,0x86,0x07,0x2B,0xA1,0xE7,0xCC,0x23,
+ 0x58,0xBA,0xEC,0xA1,0x34,0xC8,0x25,0xA7
+ };
+
+ struct {
+ int level;
+ int hashAlg;
+ const byte* hash;
+ word32 hashLen;
+ } forbidden[] = {
+ /* ML-DSA-65 needs >=192-bit collision strength; SHA-256 = 128-bit. */
+ { WC_ML_DSA_65, WC_HASH_TYPE_SHA256, hash32, 32 },
+ /* ML-DSA-87 needs >=256-bit collision strength; SHA-384 = 192-bit. */
+ { WC_ML_DSA_87, WC_HASH_TYPE_SHA384, hash48, 48 }
+ };
+
+#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
+ key = (dilithium_key*)XMALLOC(sizeof(*key), HEAP_HINT,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ sig = (byte*)XMALLOC(DILITHIUM_MAX_SIG_SIZE, HEAP_HINT,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ if ((key == NULL) || (sig == NULL)) {
+ ERROR_OUT(WC_TEST_RET_ENC_ERRNO, neg_out);
+ }
+#endif
+ XMEMSET(sig, 0, DILITHIUM_MAX_SIG_SIZE);
+
+ for (i = 0; i < (int)(sizeof(forbidden) / sizeof(forbidden[0])); i++) {
+ #ifdef WOLFSSL_NO_ML_DSA_65
+ if (forbidden[i].level == WC_ML_DSA_65) continue;
+ #endif
+ #ifdef WOLFSSL_NO_ML_DSA_87
+ if (forbidden[i].level == WC_ML_DSA_87) continue;
+ #endif
+
+ ret = wc_dilithium_init_ex(key, NULL, devId);
+ if (ret != 0) {
+ ERROR_OUT(WC_TEST_RET_ENC_EC(ret), neg_out);
+ }
+ ret = wc_dilithium_set_level(key, (byte)forbidden[i].level);
+ if (ret != 0) {
+ wc_dilithium_free(key);
+ ERROR_OUT(WC_TEST_RET_ENC_EC(ret), neg_out);
+ }
+ ret = wc_dilithium_make_key(key, rng);
+ if (ret != 0) {
+ wc_dilithium_free(key);
+ ERROR_OUT(WC_TEST_RET_ENC_EC(ret), neg_out);
+ }
+
+ sigLen = (word32)wc_dilithium_sig_size(key);
+
+ /* sigGen with disallowed PH must be REJECTED. */
+ PRIVATE_KEY_UNLOCK();
+ ret = wc_dilithium_sign_ctx_hash(NULL, 0, forbidden[i].hashAlg,
+ forbidden[i].hash, forbidden[i].hashLen, sig, &sigLen, key, rng);
+ PRIVATE_KEY_LOCK();
+ if (ret == 0) {
+ /* Module accepted a disallowed pre-hash. */
+ wc_dilithium_free(key);
+ ERROR_OUT(WC_TEST_RET_ENC_NC, neg_out);
+ }
+
+ /* sigVer with disallowed PH must ALSO be REJECTED. */
+ verified = -1;
+ sigLen = (word32)wc_dilithium_sig_size(key);
+ ret = wc_dilithium_verify_ctx_hash(sig, sigLen, NULL, 0,
+ forbidden[i].hashAlg, forbidden[i].hash, forbidden[i].hashLen,
+ &verified, key);
+ if (ret == 0) {
+ wc_dilithium_free(key);
+ ERROR_OUT(WC_TEST_RET_ENC_NC, neg_out);
+ }
+
+ wc_dilithium_free(key);
+ ret = 0;
+ }
+
+neg_out:
+#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
+ if (sig != NULL) XFREE(sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ if (key != NULL) XFREE(key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+ return ret;
+}
+#endif /* !WOLFSSL_DILITHIUM_NO_SIGN && !WOLFSSL_DILITHIUM_NO_VERIFY */
+
#endif
#if defined(WC_MLDSA_CACHE_MATRIX_A) && \
@@ -59501,6 +59662,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mldsa_test(void)
#endif /* (WOLFSSL_MLDSA_PUBLIC_KEY && !WOLFSSL_MLDSA_NO_VERIFY) ||
* (WOLFSSL_MLDSA_PRIVATE_KEY && !WOLFSSL_MLDSA_NO_SIGN) */
+#if !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \
+ !defined(WOLFSSL_MLDSA_NO_SIGN) && \
+ !defined(WOLFSSL_MLDSA_NO_VERIFY) && \
+ (!defined(WOLFSSL_NO_ML_DSA_65) || !defined(WOLFSSL_NO_ML_DSA_87))
+ /* FIPS 204 sec. 5.4 -- HashML-DSA must reject pre-hashes weaker than
+ * the parameter set's security level. */
+ ret = mldsa_hash_paramset_rejection_test(&rng);
+ if (ret != 0) {
+ ERROR_OUT(ret, out);
+ }
+#endif
+
#if !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) || \
!defined(WOLFSSL_MLDSA_NO_VERIFY) || \
defined(WOLFSSL_MLDSA_PRIVATE_KEY) || \
@@ -60802,7 +60975,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test_verify_only(void)
#define WOLFSSL_SLHDSA_VERIFY_ONLY
#endif
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
/* KeyGen KAT: deterministic key generation cross-validated against NIST CAVP
* vectors. Verifies that MakeKeyWithRandom produces the expected sk and pk
* for a given parameter set. */
@@ -60946,29 +61119,16 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
- /* HashSLH-DSA takes the caller's pre-hashed digest as input. */
+ /* HashSLH-DSA takes the caller's pre-hashed digest as input. SHAKE-256 is
+ * approved for all SLH-DSA-{128,192,256} variants (FIPS 205 sec. 10.2.2
+ * Table 9), so use it unconditionally for the positive round-trip. */
{
-#ifdef WOLFSSL_SLHDSA_SHA2
- enum wc_HashType phType = SLHDSA_IS_SHA2(param) ?
- WC_HASH_TYPE_SHA256 : WC_HASH_TYPE_SHAKE256;
-#else
enum wc_HashType phType = WC_HASH_TYPE_SHAKE256;
-#endif
byte digest[WC_SHA3_512_DIGEST_SIZE];
- word32 digestLen;
+ word32 digestLen = WC_SHA3_512_DIGEST_SIZE;
-#ifdef WOLFSSL_SLHDSA_SHA2
- if (phType == WC_HASH_TYPE_SHA256) {
- ret = wc_Sha256Hash(msg, (word32)sizeof(msg), digest);
- digestLen = WC_SHA256_DIGEST_SIZE;
- }
- else
-#endif
- {
- ret = wc_Shake256Hash(msg, (word32)sizeof(msg), digest,
- WC_SHA3_512_DIGEST_SIZE);
- digestLen = WC_SHA3_512_DIGEST_SIZE;
- }
+ ret = wc_Shake256Hash(msg, (word32)sizeof(msg), digest,
+ WC_SHA3_512_DIGEST_SIZE);
if (ret != 0) {
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
@@ -60987,9 +61147,11 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
- /* Additional pre-hash test: SHA-384 exercises a different OID path */
+ /* Additional pre-hash test: SHA-384 exercises a different OID path. Skip
+ * for SLH-DSA-256 -- SHA-384 (192-bit collision) is below its 256-bit
+ * security level (FIPS 205 sec. 10.2.2 Table 9). */
#ifdef WOLFSSL_SHA384
- {
+ if (key->params->n != WC_SLHDSA_N_256) {
byte digest384[WC_SHA384_DIGEST_SIZE];
ret = wc_Sha384Hash(msg, (word32)sizeof(msg), digest384);
@@ -61049,6 +61211,85 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
return ret;
}
+
+/* Negative test: HashSLH-DSA must reject a pre-hash whose collision resistance
+ * is below the parameter set's claimed security strength (FIPS 205 sec.
+ * 10.2.2, Table 9: approved PH per level). Asserts sigGen and sigVer both
+ * reject. */
+static wc_test_ret_t slhdsa_hash_paramset_rejection_test(enum SlhDsaParam param)
+{
+ int ret = 0;
+ WC_RNG rng;
+ SlhDsaKey key[1];
+ byte sig[WC_SLHDSA_MAX_SIG_LEN];
+ word32 sigLen;
+ static const byte msg[] = {
+ 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,
+ 0x72,0x6c,0x64,0x21
+ };
+ byte ctx[1];
+ /* SHA-256 (128-bit collision) is approved only for 128-bit paramSets, so
+ * any 192/256-bit paramSet must reject it. */
+ enum wc_HashType badHash = WC_HASH_TYPE_SHA256;
+
+ XMEMSET(&key, 0, sizeof(key));
+
+#ifndef HAVE_FIPS
+ ret = wc_InitRng_ex(&rng, HEAP_HINT, devId);
+#else
+ ret = wc_InitRng(&rng);
+#endif
+ if (ret != 0) return WC_TEST_RET_ENC_EC(ret);
+
+ ret = wc_SlhDsaKey_Init(key, param, NULL, INVALID_DEVID);
+ if (ret != 0) {
+ wc_FreeRng(&rng);
+ return WC_TEST_RET_ENC_EC(ret);
+ }
+
+ ret = wc_SlhDsaKey_MakeKey(key, &rng);
+ if (ret != 0) {
+ wc_SlhDsaKey_Free(key);
+ wc_FreeRng(&rng);
+ return WC_TEST_RET_ENC_EC(ret);
+ }
+
+ /* Only enforce on paramSets above 128-bit security; SHA-256 is approved
+ * for 128-bit so wouldn't be a rejection target there. */
+ if (key->params->n == WC_SLHDSA_N_128) {
+ wc_SlhDsaKey_Free(key);
+ wc_FreeRng(&rng);
+ return 0;
+ }
+
+ /* sigGen with too-weak PH must be REJECTED. */
+ sigLen = WC_SLHDSA_MAX_SIG_LEN;
+ PRIVATE_KEY_UNLOCK();
+ ret = wc_SlhDsaKey_SignHash(key, ctx, 0, msg, (word32)sizeof(msg),
+ badHash, sig, &sigLen, &rng);
+ PRIVATE_KEY_LOCK();
+ if (ret == 0) {
+ /* Module accepted a disallowed pre-hash. */
+ wc_SlhDsaKey_Free(key);
+ wc_FreeRng(&rng);
+ return WC_TEST_RET_ENC_NC;
+ }
+
+ /* sigVer with too-weak PH must ALSO be REJECTED. */
+ sigLen = WC_SLHDSA_MAX_SIG_LEN;
+ XMEMSET(sig, 0, sigLen);
+ ret = wc_SlhDsaKey_VerifyHash(key, ctx, 0, msg, (word32)sizeof(msg),
+ badHash, sig, sigLen);
+ if (ret == 0) {
+ wc_SlhDsaKey_Free(key);
+ wc_FreeRng(&rng);
+ return WC_TEST_RET_ENC_NC;
+ }
+
+ wc_SlhDsaKey_Free(key);
+ wc_FreeRng(&rng);
+ return 0;
+}
#endif
/* True iff slhdsa_test() actually emits at least one `goto out;` /
@@ -61235,7 +61476,7 @@ wc_test_ret_t slhdsa_test(void)
int ret = 0;
#ifdef WOLFSSL_SLHDSA_PARAM_128S
WC_DECLARE_VAR(key_vfy, SlhDsaKey, 1, HEAP_HINT);
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
WC_DECLARE_VAR(key, SlhDsaKey, 1, HEAP_HINT);
static const byte sk_seed_shake128s[] = {
0x17, 0x3D, 0x04, 0xC9, 0x38, 0xC1, 0xC3, 0x6B,
@@ -61266,7 +61507,7 @@ wc_test_ret_t slhdsa_test(void)
0x82, 0x6e, 0x97, 0xbc, 0xb0, 0x1b, 0x78, 0x7b,
0xc6, 0xb5, 0xa7, 0xbb, 0xe3, 0x7e, 0xb4, 0xa8
};
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
WC_DECLARE_VAR(sk, byte, WC_SLHDSA_MAX_PRIV_LEN, HEAP_HINT);
WC_DECLARE_VAR(pk, byte, WC_SLHDSA_MAX_PUB_LEN, HEAP_HINT);
word32 outLen;
@@ -62260,14 +62501,14 @@ wc_test_ret_t slhdsa_test(void)
0xc0, 0x48, 0xd0, 0x63, 0x82, 0x20, 0x2b, 0x06,
0xe8, 0x21, 0xf8, 0xcd, 0x56, 0xc5, 0xc7, 0x31,
};
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
WC_DECLARE_VAR(sig, byte, sizeof(sig_shake128s), HEAP_HINT);
#endif
WC_ALLOC_VAR_EX(key_vfy, SlhDsaKey, 1, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER, ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out));
XMEMSET(key_vfy, 0, sizeof(*key_vfy));
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
WC_ALLOC_VAR_EX(key, SlhDsaKey, 1, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER, ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out));
XMEMSET(key, 0, sizeof(*key));
@@ -62281,7 +62522,7 @@ wc_test_ret_t slhdsa_test(void)
/* // NOLINTEND(bugprone-sizeof-expression) */
#endif
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
ret = wc_SlhDsaKey_Init(key, SLHDSA_SHAKE128S, NULL, devId);
if (ret != 0) {
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
@@ -62341,7 +62582,7 @@ wc_test_ret_t slhdsa_test(void)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
outLen = (word32)sizeof(sig_shake128s);
PRIVATE_KEY_UNLOCK();
ret = wc_SlhDsaKey_SignWithRandom(key, ctx, 0, msg, (word32)sizeof(msg),
@@ -62367,7 +62608,7 @@ wc_test_ret_t slhdsa_test(void)
* These verify that deterministic key generation produces the exact pk/sk
* that NIST expects. Covers both SHA-256 (cat 1, n=16) and SHA-512
* (cat 3 n=24, cat 5 n=32) code paths. */
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
{
/* NIST CAVP SLH-DSA-SHA2-128s keyGen vector (tgId=1, tcId=1).
@@ -63042,6 +63283,39 @@ wc_test_ret_t slhdsa_test(void)
}
#endif
+ /* FIPS 205 sec. 10.2.2 -- HashSLH-DSA must reject pre-hashes below the
+ * paramSet's security level. Use any available 192/256-bit paramSet;
+ * 128-bit paramSets allow SHA-256 so are not useful targets here. */
+#ifdef WOLFSSL_SLHDSA_PARAM_192S
+ ret = slhdsa_hash_paramset_rejection_test(SLHDSA_SHAKE192S);
+ if (ret != 0) {
+ wc_test_render_error_message("SLHDSA_SHAKE192S (hash-paramset reject)",
+ 0);
+ goto out;
+ }
+#elif defined(WOLFSSL_SLHDSA_PARAM_256S)
+ ret = slhdsa_hash_paramset_rejection_test(SLHDSA_SHAKE256S);
+ if (ret != 0) {
+ wc_test_render_error_message("SLHDSA_SHAKE256S (hash-paramset reject)",
+ 0);
+ goto out;
+ }
+#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
+ ret = slhdsa_hash_paramset_rejection_test(SLHDSA_SHA2_192S);
+ if (ret != 0) {
+ wc_test_render_error_message("SLHDSA_SHA2_192S (hash-paramset reject)",
+ 0);
+ goto out;
+ }
+#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
+ ret = slhdsa_hash_paramset_rejection_test(SLHDSA_SHA2_256S);
+ if (ret != 0) {
+ wc_test_render_error_message("SLHDSA_SHA2_256S (hash-paramset reject)",
+ 0);
+ goto out;
+ }
+#endif
+
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */
#if defined(WOLF_PRIVATE_KEY_ID) && \
@@ -63075,7 +63349,7 @@ wc_test_ret_t slhdsa_test(void)
/* key, sig, sk, pk are declared inside #ifdef WOLFSSL_SLHDSA_PARAM_128S
* (alongside the SHAKE-128s test data) so they only exist when 128S is
* built. Their cleanup must match. */
-#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
#ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC
if (key)
#endif
diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h
index 183aad072f..f1329cd398 100644
--- a/wolfssl/wolfcrypt/aes.h
+++ b/wolfssl/wolfcrypt/aes.h
@@ -67,8 +67,10 @@ typedef struct Gcm {
#endif
WOLFSSL_LOCAL void GenerateM0(Gcm* gcm);
+/* GCM_SMALL form of GMULT; scoped so it does not clash with the static
+ * table-mode (GCM_TABLE/GCM_TABLE_4BIT) GMULT. */
#if !defined(__aarch64__) && defined(WOLFSSL_ARMASM) && \
- !defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)
+ !defined(WOLFSSL_ARMASM_NO_HW_CRYPTO) && defined(GCM_SMALL)
WOLFSSL_LOCAL void GMULT(byte* X, byte* Y);
#endif
WOLFSSL_LOCAL void WC_ARG_NOT_NULL(1) GHASH(Gcm* gcm, const byte* a,
diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h
index e3f091ff73..7201d1cb1d 100644
--- a/wolfssl/wolfcrypt/error-crypt.h
+++ b/wolfssl/wolfcrypt/error-crypt.h
@@ -335,14 +335,16 @@ enum wolfCrypt_ErrorCodes {
* match request */
SLH_DSA_PCT_E = -1020, /* SLH-DSA Pairwise Consistency Test failure */
- CMAC_KAT_FIPS_E = -1021, /* AES-CMAC KAT failure */
- SHAKE_KAT_FIPS_E = -1022, /* SHAKE KAT failure */
- DH_PCT_E = -1023, /* DH Pairwise Consistency Test failure.
+ CMAC_KAT_FIPS_E = -1021, /* AES-CMAC KAT failure (vendor-elected) */
+ SHAKE_KAT_FIPS_E = -1022, /* SHAKE KAT failure (vendor-elected) */
+ DH_PCT_E = -1023, /* DH Pairwise Consistency Test failure
+ * (SP 800-56A r3 sec 5.6.2.1.4, IG 10.3.B).
* Retired in FIPS v7+ (classic DH left the
* module boundary); the code stays allocated
* so fips.c can report it as retired rather
* than unknown. */
- AES_KW_KAT_FIPS_E = -1024, /* AES Key Wrap KAT failure */
+ AES_KW_KAT_FIPS_E = -1024, /* AES Key Wrap KAT failure (vendor-elected,
+ * SP 800-38F sec 6.2 / RFC 3394) */
FIPS_WRONG_API_E = -1025, /* Requested API is not allowed in FIPS mode */
KMAC_MIN_KEYLEN_E = -1026, /* FIPS Mode KMAC Minimum Key Length error */
diff --git a/wolfssl/wolfcrypt/fips_test.h b/wolfssl/wolfcrypt/fips_test.h
index dc247b272b..34f04ccd65 100644
--- a/wolfssl/wolfcrypt/fips_test.h
+++ b/wolfssl/wolfcrypt/fips_test.h
@@ -75,7 +75,9 @@ enum FipsCastId {
FIPS_CAST_RSA_SIGN_PKCS1v15 = 7,
FIPS_CAST_ECC_CDH = 8,
FIPS_CAST_ECC_PRIMITIVE_Z = 9,
- FIPS_CAST_DH_PRIMITIVE_Z = 10,
+ FIPS_CAST_DH_PRIMITIVE_Z = 10, /* RETIRED (v7+): classic DH left the
+ * module boundary. Kept for ABI; do
+ * not reuse this id. */
FIPS_CAST_ECDSA = 11,
FIPS_CAST_KDF_TLS12 = 12,
FIPS_CAST_KDF_TLS13 = 13,
@@ -93,8 +95,14 @@ enum FipsCastId {
FIPS_CAST_XMSS = 23,
FIPS_CAST_DRBG_SHA512 = 24,
FIPS_CAST_SLH_DSA = 25,
- /* Vendor-elected enhanced self-tests, appended so the ids above keep
- * their v7.0.0 values. */
+ /* Retired vendor-elected CASTs (v7 lab-prep). The dedicated AES-CMAC,
+ * SHAKE and AES-KW CASTs were removed because FIPS 140-3 IG 10.3.A covers
+ * them via the more-complex tier: AES-KW (1.d item (ii)) and AES-CMAC by
+ * the AES-GCM CAST (item (i)), and SHAKE by the HMAC-SHA3-256 CAST (item 3
+ * + Note 2, shared Keccak-p). The ids are KEPT (do not reuse) so code that
+ * still references them compiles; the *services* re-gate onto the covering
+ * CAST (see fips.c), and DoCAST/RunAllCast no longer run these -- the slots
+ * stay at INIT, exactly like the retired FIPS_CAST_DH_PRIMITIVE_Z (= 10). */
FIPS_CAST_AES_CMAC = 26,
FIPS_CAST_SHAKE = 27,
FIPS_CAST_AES_KW = 28,
diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h
index 102f05d6b5..235576bacf 100644
--- a/wolfssl/wolfcrypt/random.h
+++ b/wolfssl/wolfcrypt/random.h
@@ -57,8 +57,12 @@
#define DRBG_SEED_LEN (440/8)
#endif
+/* Size of the DRBG seed (SHA-512) */
#ifdef WOLFSSL_DRBG_SHA512
- #define DRBG_SHA512_SEED_LEN (888/8) /* 111 bytes per SP 800-90A Table 2 */
+ #ifndef DRBG_SHA512_SEED_LEN
+ #define DRBG_SHA512_SEED_LEN (888/8) /* 111 bytes per SP 800-90A
+ * Table 2 */
+ #endif
#endif
@@ -212,12 +216,12 @@ struct OS_Seed {
*/
#define ENTROPY_SCALE_FACTOR (512)
#elif defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND)
- /* The value of 2 applies to Intel's RDSEED which provides about
- * 0.5 bits minimum of entropy per bit. The value of 4 gives a
- * conservative margin for FIPS. */
+ /* Intel RDSEED provides ~0.5 bits min entropy per bit (cert3389
+ * PUD). FIPS uses the AMD worst case above so one seeding budget
+ * covers any x86 OE. */
#if defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \
(HAVE_FIPS_VERSION >= 2)
- #define ENTROPY_SCALE_FACTOR (2*4)
+ #define ENTROPY_SCALE_FACTOR (512)
#else
/* Not FIPS, but Intel RDSEED, only double. */
#define ENTROPY_SCALE_FACTOR (2)
@@ -234,6 +238,12 @@ struct OS_Seed {
#endif
#endif /* !ENTROPY_SCALE_FACTOR */
+/* SP 800-90A Rev1: FIPS over-seeds to cover low-entropy NDRNGs; a scale
+ * factor below 4 (256 bits) defeats that margin. */
+#if FIPS_VERSION3_GE(7,0,0) && (ENTROPY_SCALE_FACTOR < 4)
+ #error "FIPS v7 requires ENTROPY_SCALE_FACTOR >= 4 (SP 800-90A over-seeding)"
+#endif
+
/* SEED_BLOCK_SZ is unprefixed for backward compat. */
#ifndef SEED_BLOCK_SZ
/* The seed block size, is the size of the output of the underlying NDRNG.
@@ -480,10 +490,15 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng);
#endif
#ifdef WC_RNG_SEED_CB
+ /* Set the entropy-seed callback ONCE at startup, before any RNG use or
+ * threads; it writes a shared global and must not change per-thread or
+ * concurrently with RNG operations. Use one entropy source at a time. */
WOLFSSL_API int wc_SetSeed_Cb(wc_RngSeed_Cb cb);
#endif
#ifdef HAVE_HASHDRBG
+ /* Caller-supplied reseed entropy is NOT health-tested by the Module; it
+ * SHALL come from an SP 800-90B compliant source (see random.c). */
WOLFSSL_API int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed,
word32 seedSz);
WOLFSSL_API int wc_RNG_TestSeed(const byte* seed, word32 seedSz);
diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h
index 696ece0323..5237f1006e 100644
--- a/wolfssl/wolfcrypt/settings.h
+++ b/wolfssl/wolfcrypt/settings.h
@@ -537,7 +537,11 @@
!defined(WC_FIPS_186_5) && !defined(WC_FIPS_186_4)
#if defined(HAVE_SELFTEST)
#define WC_FIPS_186_4
- #elif FIPS_VERSION3_GE(7,0,0) && !defined(WOLFSSL_FIPS_READY)
+ #elif FIPS_VERSION3_GE(7,0,0)
+ /* FIPS 186-5 governs the v7+ module, including fips-ready/fips-dev
+ * builds that track the in-development v7 source. Its sec 6.1.1
+ * signature-digest floor (SHA-224 and larger for ECDSA/DSA signing)
+ * must apply to all of them, so do not exclude WOLFSSL_FIPS_READY. */
#define WC_FIPS_186_5
#else
#define WC_FIPS_186_4
@@ -554,6 +558,15 @@
#define WC_FIPS_186_5_PLUS
#endif
+#if FIPS_VERSION3_GE(7,0,0)
+ /* SP 800-56A Rev3 sec 5.6.2.2: an ECC public key used for key agreement
+ * shall be validated on import. configure enables this for FIPS builds;
+ * force it for v7+ so user_settings.h OEs (kernel/Windows) validate too. */
+ #ifndef WOLFSSL_VALIDATE_ECC_IMPORT
+ #define WOLFSSL_VALIDATE_ECC_IMPORT
+ #endif
+#endif
+
/*------------------------------------------------------------*/
@@ -572,6 +585,14 @@
#endif
/* blinding adds API not available yet in FIPS mode */
#undef WC_RSA_BLINDING
+
+ /* NIST SP 800-38A sec 6.2: CBC plaintext must be a multiple of the block
+ * size, and the cipher does not pad. Force the block-alignment check so
+ * an unaligned length returns BAD_LENGTH_E rather than silently
+ * truncating to the largest aligned prefix. */
+ #ifndef WOLFSSL_AES_CBC_LENGTH_CHECKS
+ #define WOLFSSL_AES_CBC_LENGTH_CHECKS
+ #endif
#endif
/* old FIPS has only AES_BLOCK_SIZE. */