From e642277402141b5ed9f3ae62a500be7d2fe1fb13 Mon Sep 17 00:00:00 2001 From: Kaleb Himes Date: Fri, 31 Jul 2026 15:14:44 -0600 Subject: [PATCH] Security review: WC_FIPS_NOT_APPROVED PQ seed gating, ECIES GCM DEM FIPS gate, SHA-3 BMI2-first dispatch, Keccak-256 test gate, arm32 FALL_THROUGH, FIPS 186-5 PSS salt cap, Windows in-core hash ASLR pin --- .wolfssl_known_macro_extras | 5 + IDE/WIN-PQ-FIPSv7/README.txt | 121 +++ IDE/WIN-PQ-FIPSv7/include.am | 11 + IDE/WIN-PQ-FIPSv7/resource.h | 14 + IDE/WIN-PQ-FIPSv7/test.vcxproj | 291 +++++++ IDE/WIN-PQ-FIPSv7/user_settings.h | 276 +++++++ IDE/WIN-PQ-FIPSv7/wolfssl-fips.rc | 100 +++ IDE/WIN-PQ-FIPSv7/wolfssl-fips.sln | 61 ++ IDE/WIN-PQ-FIPSv7/wolfssl-fips.vcxproj | 404 ++++++++++ IDE/WIN10/test.vcxproj | 16 +- IDE/include.am | 1 + configure.ac | 60 +- linuxkm/Kbuild | 21 +- linuxkm/Makefile | 13 +- linuxkm/linuxkm_memory.c | 12 + linuxkm/linuxkm_memory.h | 7 + linuxkm/linuxkm_wc_port.h | 88 +-- linuxkm/module_hooks.c | 13 +- linuxkm/pie_redirect_table.c | 65 +- linuxkm/x86_vector_register_glue.c | 25 +- src/include.am | 30 +- src/pk_rsa.c | 5 +- src/tls13.c | 5 +- tests/api.c | 14 + tests/api/api.h | 16 + tests/api/test_aes.c | 25 +- tests/api/test_ecc.c | 13 +- tests/api/test_ed25519.c | 9 + tests/api/test_mldsa.c | 52 +- tests/api/test_mlkem.c | 24 +- tests/api/test_slhdsa.c | 77 +- wolfcrypt/benchmark/fips_cast_bench.c | 340 +++++++++ wolfcrypt/benchmark/include.am | 9 + wolfcrypt/src/aes.c | 222 +++++- wolfcrypt/src/aes_xts_x86_asm.S | 835 +++++++++++++++++++++ wolfcrypt/src/cpuid.c | 6 + wolfcrypt/src/dh.c | 26 +- wolfcrypt/src/ecc.c | 84 ++- wolfcrypt/src/ed25519.c | 5 + wolfcrypt/src/ed448.c | 44 ++ wolfcrypt/src/ge_operations.c | 6 +- wolfcrypt/src/kdf.c | 6 + wolfcrypt/src/port/arm/armv8-32-sha3-asm.S | 6 + wolfcrypt/src/random.c | 315 +++++++- wolfcrypt/src/rng_bank.c | 28 +- wolfcrypt/src/rsa.c | 143 +++- wolfcrypt/src/sha.c | 4 + wolfcrypt/src/sha256.c | 27 +- wolfcrypt/src/sha3.c | 63 +- wolfcrypt/src/sha512.c | 14 + wolfcrypt/src/sp_arm32.c | 56 +- wolfcrypt/src/wc_lms.c | 7 + wolfcrypt/src/wc_lms_impl.c | 13 +- wolfcrypt/src/wc_mldsa.c | 163 +++- wolfcrypt/src/wc_mlkem.c | 169 +++-- wolfcrypt/src/wc_mlkem_poly.c | 14 +- wolfcrypt/src/wc_slhdsa.c | 181 ++++- wolfcrypt/src/wc_xmss.c | 7 + wolfcrypt/src/wc_xmss_impl.c | 9 + wolfcrypt/src/wolfentropy.c | 98 ++- wolfcrypt/test/test.c | 354 ++++++++- wolfssl/wolfcrypt/aes.h | 4 +- wolfssl/wolfcrypt/error-crypt.h | 10 +- wolfssl/wolfcrypt/fips_test.h | 14 +- wolfssl/wolfcrypt/random.h | 25 +- wolfssl/wolfcrypt/settings.h | 23 +- 66 files changed, 4805 insertions(+), 399 deletions(-) create mode 100644 IDE/WIN-PQ-FIPSv7/README.txt create mode 100644 IDE/WIN-PQ-FIPSv7/include.am create mode 100644 IDE/WIN-PQ-FIPSv7/resource.h create mode 100644 IDE/WIN-PQ-FIPSv7/test.vcxproj create mode 100644 IDE/WIN-PQ-FIPSv7/user_settings.h create mode 100644 IDE/WIN-PQ-FIPSv7/wolfssl-fips.rc create mode 100644 IDE/WIN-PQ-FIPSv7/wolfssl-fips.sln create mode 100644 IDE/WIN-PQ-FIPSv7/wolfssl-fips.vcxproj create mode 100644 wolfcrypt/benchmark/fips_cast_bench.c create mode 100644 wolfcrypt/src/aes_xts_x86_asm.S diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index f0557ab8a1d..57de68a890c 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 00000000000..b7a67487de9 --- /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 00000000000..e66535d5eba --- /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 00000000000..691fa76544c --- /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 00000000000..59f3310353c --- /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 00000000000..dec53d8c9f0 --- /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 00000000000..449cdb49efb --- /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 00000000000..1609e6d91e3 --- /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 00000000000..e042356e8d5 --- /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 36657348dbe..2de83ba3f68 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 e70c06fb5a3..11d6abb44ed 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 21a2a808b3b..ca042f6ed89 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 b9bf722c2b3..bbf0d2c60eb 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 00fc70df6a0..0b9d46f1041 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 2f1b75e1125..2d6940f435e 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 76e681da805..1c8ef3f6625 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 77b395d8bd2..9a6ee167a96 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 bb64ce959cd..7470047eded 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 03be2e04fa0..948694eaca1 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 e33c3d719e8..5bf163dff86 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 fa0e24a40be..be4718cfcdb 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 63ab70abd2e..39ffa09810d 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 7674cb7f24c..6d7f2eb8dde 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 95551a6c2b8..23d835ea276 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 69b1f433e51..dae18f8b1dc 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 ed4f762a610..be7fac8760b 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 b8ed0599fd4..81c005d43d7 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 b5859b9f9a9..3794d7dd910 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 eff7746520c..d4e7d5c9d11 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 759f25f6449..bff8cff5cf6 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 0c1dbb3f52b..070ef48a61a 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 00000000000..215b3db3116 --- /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 22cecbdaefe..0854a485c3f 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 87db0905363..5d4ae34d22e 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 00000000000..0cc929478c0 --- /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 35892b64aff..8f224c3ae6c 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 5a6daa98531..7976075a57f 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 34acd96ca87..469679961c0 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 4a0bc1257d2..99fbf81f700 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 08374ba446f..4f928a49c39 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 a59a8e48461..ee9c0b6c279 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 e9d10166c39..5f60fcbabc5 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 4f0a420398c..de0abc9135f 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 e65182dffc0..647cfed43cc 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 24d7cdccd38..297df0ad3f7 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 5f33baf7cdf..3c1c5131ecd 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 45b9948df42..ee358b05725 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 4c3d4fd2ebb..dcfa75b69d7 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 1ead204fb5f..48b7ce66a65 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 311ff37e436..cc9b6c65b69 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 44dfcc6a905..fd90db66e60 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 595b93622d8..f4051aa29b0 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 908fe975555..6fd9ca8bc74 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 f1e9d0496c4..9b4f3197ac4 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 52b9ef2cc80..faf774ddad2 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 ec61e67b6e8..11469306806 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 0fb574725e2..03f3242ed0f 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 b4f4c761850..9d6c02f9d99 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 369f0740803..784492e9856 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 88ddcb986d1..0f8765cc4a1 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 5f7047ac792..a95dc0f4fca 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 183aad072f8..f1329cd3986 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 e3f091ff736..7201d1cb1d7 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 dc247b272bd..34f04ccd65e 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 102f05d6b55..235576bacfc 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 696ece03230..5237f1006e0 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. */