Address failure rates from FIPS CRNGT test by implementing alternate RCT/ADP tests

Update ret code to match docs and update docs

Replace magic numbers with appropriate define

Define MAX_ENTROPY_BITS when MEMUSE not enabled

Fix type cast windows detection

Older FIPS modules still need the old check

CodeSpell you're wrong, that is what I want to name my variable

Turn the hostap into a manual dispatch until it gets fixed

Upon closer review we can not skip the test when memuse enabled

Fix whitespace stuff found by multitest

More syntax things

Correct comments based on latest findings
This commit is contained in:
kaleb-himes
2026-01-14 16:13:03 -07:00
parent 62ca34497c
commit 20b2fd200f
8 changed files with 826 additions and 19 deletions

View File

@@ -23,7 +23,7 @@ jobs:
check_filenames: true
check_hidden: true
# Add comma separated list of words that occur multiple times that should be ignored (sorted alphabetically, case sensitive)
ignore_words_list: adin,aNULL,brunch,carryIn,chainG,ciph,cLen,cliKs,dout,haveA,inCreated,inOut,inout,larg,LEAPYEAR,Merget,optionA,parm,parms,repid,rIn,userA,ser,siz,te,Te,HSI,
ignore_words_list: adin,aNULL,brunch,carryIn,chainG,ciph,cLen,cliKs,dout,haveA,inCreated,inOut,inout,larg,LEAPYEAR,Merget,optionA,parm,parms,repid,rIn,userA,ser,siz,te,Te,HSI,failT,
# The exclude_file contains lines of code that should be ignored. This is useful for individual lines which have non-words that can safely be ignored.
exclude_file: '.codespellexcludelines'
# To skip files entirely from being processed, add it to the following list:

View File

@@ -2,10 +2,14 @@ name: hostap and wpa-supplicant Tests
# START OF COMMON SECTION
on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]
workflow_dispatch: # Allows people to run it manually if they want but
# disables it from running automatically when broken
# To restore this to an auto test delete the above workflow_dispatch line and
# comments and uncomment the below lines for push and pull_request
# push:
# branches: [ 'master', 'main', 'release/**' ]
# pull_request:
# branches: [ '*' ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@@ -476,7 +476,7 @@ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz);
\return 0 If valid
\return BAD_FUNC_ARG If seed is NULL
\return RNG_FAILURE_E Validation failed
\return ENTROPY_RT_E || ENTROPY_APT_E Validation failed
\param seed Seed to test
\param seedSz Seed size

View File

@@ -345,7 +345,12 @@ int test_wc_RNG_TestSeed(void)
/* Bad seed as it repeats. */
XMEMSET(seed, 0xa5, sizeof(seed));
/* Return value is DRBG_CONT_FAILURE which is not public. */
/* Moving forward with the RCT test check LT instead of GT */
#if !defined(HAVE_FIPS) || ( defined(HAVE_FIPS) && FIPS_VERSION3_GE(7,0,0) )
ExpectIntLT(wc_RNG_TestSeed(seed, sizeof(seed)), 0);
#else
ExpectIntGT(wc_RNG_TestSeed(seed, sizeof(seed)), 0);
#endif
/* Good seed. */
for (i = 0; i < (byte)sizeof(seed); i++)

View File

@@ -746,28 +746,131 @@ static int Hash_DRBG_Uninstantiate(DRBG_internal* drbg)
}
/* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data
*
* These tests replace the older FIPS 140-2 Continuous Random Number Generator
* Test (CRNGT) with more mathematically robust statistical tests per
* ISO 19790 / SP800-90B requirements.
*
* When HAVE_ENTROPY_MEMUSE is defined, the wolfentropy.c jitter-based TRNG
* performs another set of these health tests, but those are on the noise not
* the conditioned output so we still need to retest here even in that case
* to evaluate the conditioned output for the same behavior. These tests ensure
* the seed data meets basic entropy requirements regardless of the source.
*/
/* SP800-90B 4.4.1 - Repetition Count Test
* Detects if the noise source becomes "stuck" producing repeated output.
*
* C = 1 + ceil(-log2(alpha) / H)
* For alpha = 2^-30 (false positive probability) and H = 1 (min entropy):
* C = 1 + ceil(30 / 1) = 31
*/
#ifndef WC_RNG_SEED_RCT_CUTOFF
#define WC_RNG_SEED_RCT_CUTOFF 31
#endif
/* SP800-90B 4.4.2 - Adaptive Proportion Test
* Monitors if a particular sample value appears too frequently within a
* window of samples, indicating loss of entropy.
*
* Window size W = 512 for non-binary alphabet (byte values 0-255)
* C = 1 + CRITBINOM(W, 2^(-H), 1-alpha)
* For alpha = 2^-30 and H = 1, W = 512:
* C = 1 + CRITBINOM(512, 0.5, 1-2^-30) = 325
*/
#ifndef WC_RNG_SEED_APT_WINDOW
#define WC_RNG_SEED_APT_WINDOW 512
#endif
#ifndef WC_RNG_SEED_APT_CUTOFF
#define WC_RNG_SEED_APT_CUTOFF 325
#endif
int wc_RNG_TestSeed(const byte* seed, word32 seedSz)
{
int ret = 0;
/* Check the seed for duplicate words. */
word32 seedIdx = 0;
word32 scratchSz = 0;
word32 i;
int rctFailed = 0;
int aptFailed = 0;
if (seed == NULL || seedSz < SEED_BLOCK_SZ)
if (seed == NULL || seedSz == 0) {
return BAD_FUNC_ARG;
}
scratchSz = min(SEED_BLOCK_SZ, seedSz - SEED_BLOCK_SZ);
/* SP800-90B 4.4.1 - Repetition Count Test (RCT)
* Check for consecutive identical bytes that would indicate a stuck
* entropy source. Fail if we see WC_RNG_SEED_RCT_CUTOFF or more
* consecutive identical values.
*
* Constant-time implementation: always process full seed, accumulate
* failure status without early exit to prevent timing side-channels.
*/
{
int repCount = 1;
byte prevByte = seed[0];
while (seedIdx < seedSz - SEED_BLOCK_SZ) {
if (ConstantCompare(seed + seedIdx,
seed + seedIdx + scratchSz,
(int)scratchSz) == 0) {
ret = DRBG_CONT_FAILURE;
for (i = 1; i < seedSz; i++) {
/* Constant-time: always evaluate both branches effects */
int match = (seed[i] == prevByte);
/* If match, increment count, if not, reset to 1 */
repCount = (match * (repCount + 1)) + (!match * 1);
/* Update prevByte only when not matching (new value) */
prevByte = (byte) ((match * prevByte) + (!match * seed[i]));
/* Accumulate failure flag - once set, stays set */
rctFailed |= (repCount >= WC_RNG_SEED_RCT_CUTOFF);
}
seedIdx += SEED_BLOCK_SZ;
scratchSz = min(SEED_BLOCK_SZ, (seedSz - seedIdx));
}
/* SP800-90B 4.4.2 - Adaptive Proportion Test (APT)
* Check that no single byte value appears too frequently within
* a sliding window. This detects bias in the entropy source.
*
* For seeds smaller than the window size, we test the entire seed.
* For larger seeds, we use a sliding window approach.
*
* Constant-time implementation: always process full seed and check
* all counts to prevent timing side-channels.
*/
{
word16 byteCounts[MAX_ENTROPY_BITS];
word32 windowSize = min(seedSz, (word32)WC_RNG_SEED_APT_WINDOW);
word32 windowStart = 0;
word32 newIdx;
XMEMSET(byteCounts, 0, sizeof(byteCounts));
/* Initialize counts for first window */
for (i = 0; i < windowSize; i++) {
byteCounts[seed[i]]++;
}
/* Check first window - scan all 256 counts */
for (i = 0; i < MAX_ENTROPY_BITS; i++) {
aptFailed |= (byteCounts[i] >= WC_RNG_SEED_APT_CUTOFF);
}
/* Slide window through remaining seed data */
while ((windowStart + windowSize) < seedSz) {
/* Remove byte leaving the window */
byteCounts[seed[windowStart]]--;
windowStart++;
/* Add byte entering the window */
newIdx = windowStart + windowSize - 1;
byteCounts[seed[newIdx]]++;
/* Accumulate failure flag for new byte's count */
aptFailed |= (byteCounts[seed[newIdx]] >= WC_RNG_SEED_APT_CUTOFF);
}
}
/* Set return code based on accumulated failure flags */
if (rctFailed) {
ret = ENTROPY_RT_E;
}
else if (aptFailed) {
ret = ENTROPY_APT_E;
}
return ret;

View File

@@ -58,3 +58,185 @@ For building wolfCrypt test project in Visual Studio open the `test.sln`. For ne
If you see an error about `rc.exe` then you'll need to update the "Target Platform Version". You can do this by right-clicking on the test project -> General -> "Target Platform Version" and changing to 8.1 (needs to match the wolfssl library project).
This solution includes the wolfSSL library project at `<wolfssl-root>wolfssl.vcxproj` and will compile the library, then the test project.
--------
Jan 2026 - Reviewing the older FIPS compliant CRNGT test specified in FIPS 140-2
ss 4.9.2 vs the newer replacement tests RCT/ADP that are allowed to replace the
CRNGT under the new FIPS 140-3 / ISO 19790 standard.
================================================================================
DRBG Continuous Health Test Statistical Analysis & Diagnostic Report
================================================================================
OVERVIEW
--------
This document describes the statistical false positive behavior of the DRBG
continuous health test in wc_RNG_TestSeed() and provides diagnostic tools to
distinguish between:
1. Statistical false positives (expected behavior)
2. Entropy source depletion (under heavy concurrent load)
3. Actual stuck entropy source (hardware failure)
BACKGROUND: THE ISSUE
---------------------
The DRBG was experiencing high volumes of (DRBG_CONT_FIPS_E) on wc_InitRng()
calls.
Example error:
ERROR: wc_InitRng failed at iteration 330788 with code -209
This raises the question: Is this a bug in wc_RNG_TestSeed() or expected
statistical behavior?
STATISTICAL ANALYSIS
--------------------
The wc_RNG_TestSeed() Function Behavior:
- Compares ALL consecutive SEED_BLOCK_SZ chunks in the seed buffer
- With FIPS mode (typical configuration):
SEED_SZ = 256 * 4 / 8 = 128 bytes (1024-bits)
SEED_BLOCK_SZ = 4 bytes (default) (32-bits)
seedSz passed to test = 132 bytes (SEED_SZ + SEED_BLOCK_SZ)
Number of comparisons = ~32 consecutive block pairs
False Positive Probability Calculation:
- Probability one 4-byte block equals another random 4-byte block: 1/2^32
- With 32 comparisons per seed: 32/2^32 ≈ 1 in 134 million per wc_InitRng()
Test Configuration (Default):
- 40 threads × 100M iterations = 4 BILLION total wc_InitRng() calls
- Expected false positives: 4,000,000,000 × (32/2^32) ≈ 30 failures
Conclusion:
Seeing failures around 1 in 30-140 million is EXPECTED STATISTICAL BEHAVIOR.
Under heavy concurrent load (40 threads), entropy source
depletion can also cause legitimate failures.
TESTING IT
--------------------
Non-FIPS:
./configure CFLAGS="-DWC_RNG_SEED_DEBUG -DREALLY_LONG_DRBG_CONTINUOUS_TEST"
make
./wolfcrypt/test/testwolfcrypt
FIPS:
./configure --enable-fips=<flavor> CFLAGS="-DWC_RNG_SEED_DEBUG -DREALLY_LONG_DRBG_CONTINUOUS_TEST"
make
./fips-hash.sh
make
./wolfcrypt/test/testwolfcrypt
OUTPUTS EXPECTED
--------------------
Non-FIPS:
Math: Multi-Precision: Wolf(SP) word-size=64 bits=4096 sp_int.c
------------------------------------------------------------------------------
wolfSSL version 5.8.4
------------------------------------------------------------------------------
macro test passed!
error test passed!
MEMORY test passed!
base64 test passed!
asn test passed!
MD5 test passed!
SHA test passed!
SHA-224 test passed!
SHA-256 test passed!
SHA-384 test passed!
SHA-512 test passed!
SHA-512/224 test passed!
SHA-512/256 test passed!
SHA-3 test passed!
RNG Entropy Source: getrandom() syscall
===============================================
DRBG Continuous Test Validation Suite
===============================================
FIPS Build: NO
--- Test 1: Basic RNG Functionality ---
Generated 32 random bytes successfully
[PASS] Basic RNG Functionality
--- Test 2: Multiple RNG Instances ---
Successfully operated 100 RNG instances concurrently
[PASS] Multiple RNG Instances
--- Test 3: FIPS Status Check ---
SKIPPED: FIPS not enabled
[PASS] FIPS Status Check
--- Test 4: RNG ReInit Test (multi-threaded) ---
Configuration: 40 threads × 100000000 iterations = 4000000000 total
Test Profile: Default (Aggressive multi-threaded)
Expected statistical false positive rate: ~29.80 failures
Duplicate block at offset 4:
Block 1: E6 E9 D1 7B
Block 2: E6 E9 D1 7B
Full seed buffer (52 bytes):
DA 93 B7 88 E6 E9 D1 7B E6 E9 D1 7B A5 4C C9 E9
13 EE D8 4C B3 C1 71 DE 32 37 17 F2 E7 A4 29 7D
9B 02 B0 0C EC 8D AC F5 DA B1 71 05 84 C0 61 75
59 6D 87 B5
ERROR: wc_InitRng failed at iteration 778551 with code -209
ERROR: wc_RNG_GenerateBlock failed at iteration 778551 with code -199
...
(18 other failures truncated here for brevity)
...
Duplicate block at offset 16:
Block 1: C1 19 37 B1
Block 2: C1 19 37 B1
Full seed buffer (52 bytes):
62 66 5B D2 F5 54 47 9B 59 DD 0A 55 4B 52 8C 39
C1 19 37 B1 C1 19 37 B1 3F 62 CB 2E FE 56 65 4D
4F 0C A7 7D 1C 09 48 51 30 1B CA 00 56 9F 29 A7
E3 93 EF 8E
ERROR: wc_InitRng failed at iteration 90467867 with code -209
ERROR: wc_RNG_GenerateBlock failed at iteration 90467867 with code -199
Thread 0 Succeeded
...
38 other thread results truncated here for brevity (all threads succeeded
even though they experienced 1 or 2 failures in several of the threads)
...
Thread 39 Succeeded
Reinitialized RNG 4000000000 times across 40 threads
Experienced 0 thread failures and 40 thread successes
20/4000000000 API calls failed <--- This is the bread and the butter of the
test, we unfortunately expect to see
~29.80 failures, prior to the newer FIPS
140-3 RCT and ADP tests the CRNGT was
required. Now the CRNGT is replaceable
by the more mathematically robust
RCT/ADP.
[PASS] RNG Reinitialization
TESTING RESULTS with the CRNGT test:
--------------------
Old implementation non-FIPS:
Run 1 - 6 failures in 4 billion runs (100M per thread, 40 threads)
Run 2 - 11 failures in 4 billion (100M per thread, 40 threads)
Run 3 - 13 failures in 4 billion (100M per thread, 40 threads)
Old implementation with FIPS:
(keeping in mind just a single failure means catastrophic
failure for the entire module until power cycled):
Run 1 - 3990118689 failures in 4 billion API calls (yikes)
TESTING RESULTS with the RCT/ADP tests in place of the CRNGT test:
New implementation non-FIPS: 4 billion successes
New implementation FIPS: 4 billion successes

View File

@@ -865,6 +865,21 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void);
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_eax_test(void);
#endif /* WOLFSSL_AES_EAX */
#ifdef REALLY_LONG_DRBG_CONTINUOUS_TEST
#ifdef WOLFSSL_PTHREADS
#include <wolfssl/wolfcrypt/wolfevent.h>
#include <wolfssl/wolfcrypt/types.h>
#endif
/* Prototypes */
void print_result(const char* test_name, int passed, int *all_passed);
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_basic_rng(void);
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_multiple_rngs(void);
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_stress_rng(unsigned long iterations);
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_reinit_rng(void);
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_fips_status(void);
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t drbg_continuous_main(void);
#endif
/* General big buffer size for many tests. */
#define FOURK_BUF 4096
@@ -1008,9 +1023,15 @@ typedef struct func_args {
/* Kernel modules implement and install their own FIPS callback with similar
* functionality.
*/
#ifdef REALLY_LONG_DRBG_CONTINUOUS_TEST
int only_run_cb_once = 1;
#endif
#if defined(HAVE_FIPS) && !defined(WOLFSSL_KERNEL_MODE)
static void myFipsCb(int ok, int err, const char* hash)
{
#ifdef REALLY_LONG_DRBG_CONTINUOUS_TEST
if (only_run_cb_once == 1) {
#endif
printf("in my Fips callback, ok = %d, err = %d\n", ok, err);
printf("message = %s\n", wc_GetErrorString(err));
printf("hash = %s\n", hash);
@@ -1019,6 +1040,14 @@ static void myFipsCb(int ok, int err, const char* hash)
printf("In core integrity hash check failure, copy above hash\n");
printf("into verifyCore[] in fips_test.c and rebuild\n");
}
#ifdef REALLY_LONG_DRBG_CONTINUOUS_TEST
only_run_cb_once = 0;
} else {
(void) ok;
(void) err;
(void) hash;
}
#endif
}
#endif /* HAVE_FIPS && !WOLFSSL_KERNEL_MODE */
@@ -20042,6 +20071,10 @@ static wc_test_ret_t random_rng_test(void)
}
#endif
#ifdef REALLY_LONG_DRBG_CONTINUOUS_TEST
ret = drbg_continuous_main();
#endif
return ret;
}
@@ -63872,6 +63905,483 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void)
}
#endif
#ifdef REALLY_LONG_DRBG_CONTINUOUS_TEST
/* Test configuration */
#define STRESS_TEST_ITERATIONS 4000000000UL /* 100 million iterations */
#define PROGRESS_INTERVAL 10000000UL /* Report every 10M */
#define BUFFER_SIZE 32
/* Color codes for output */
#define COLOR_GREEN "\033[0;32m"
#define COLOR_RED "\033[0;31m"
#define COLOR_YELLOW "\033[0;33m"
#define COLOR_RESET "\033[0m"
void print_result(const char* test_name, int passed, int *all_passed)
{
printf("[%s] %s\n",
(passed == 0) ? COLOR_GREEN "PASS" COLOR_RESET :
COLOR_RED "FAIL" COLOR_RESET,
test_name);
if (passed != 0) {
printf("Test result was %d\n", passed);
*all_passed = 0;
}
}
/* Test 1: Basic RNG functionality */
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_basic_rng(void)
{
WC_RNG rng;
byte buffer[BUFFER_SIZE];
int ret = 0;
printf("--- Test 1: Basic RNG Functionality ---\n");
ret = wc_InitRng(&rng);
if (ret != 0) {
printf("ERROR: wc_InitRng failed with code %d: %s\n",
ret, wc_GetErrorString(ret));
return ret;
}
/* Generate some random bytes */
ret = wc_RNG_GenerateBlock(&rng, buffer, BUFFER_SIZE);
if (ret != 0) {
printf("ERROR: wc_RNG_GenerateBlock failed with code %d: %s\n",
ret, wc_GetErrorString(ret));
wc_FreeRng(&rng);
return ret;
}
/* Check that buffer is not all zeros */
int all_zeros = 1;
for (int i = 0; i < BUFFER_SIZE; i++) {
if (buffer[i] != 0) {
all_zeros = 0;
break;
}
}
if (all_zeros) {
printf("ERROR: RNG generated all zeros\n");
wc_FreeRng(&rng);
return DRBG_CONT_FIPS_E;
}
printf("Generated %d random bytes successfully\n", BUFFER_SIZE);
ret = wc_FreeRng(&rng);
return ret;
}
/* Test 2: Multiple RNG instances */
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_multiple_rngs(void)
{
#define NUM_RNGS 100
WC_RNG rngs[NUM_RNGS];
byte buffers[NUM_RNGS][BUFFER_SIZE];
int ret = 0;
int i, j;
int all_same = 1;
printf("\n--- Test 2: Multiple RNG Instances ---\n");
/* Initialize all RNGs */
for (i = 0; i < NUM_RNGS; i++) {
ret = wc_InitRng(&rngs[i]);
if (ret != 0) {
printf("ERROR: wc_InitRng[%d] failed with code %d\n", i, ret);
/* Clean up any initialized RNGs */
for (j = 0; j < i; j++) {
wc_FreeRng(&rngs[j]);
}
return ret;
}
}
/* Generate random data from all RNGs */
for (i = 0; i < NUM_RNGS; i++) {
ret = wc_RNG_GenerateBlock(&rngs[i], buffers[i], BUFFER_SIZE);
if (ret != 0) {
printf("ERROR: wc_RNG_GenerateBlock[%d] failed with code %d\n",
i, ret);
for (j = 0; j < NUM_RNGS; j++) {
wc_FreeRng(&rngs[j]);
}
return ret;
}
}
/* Verify outputs are different (not all the same) */
for (i = 1; i < NUM_RNGS; i++) {
if (memcmp(buffers[0], buffers[i], BUFFER_SIZE) != 0) {
all_same = 0;
break;
}
}
if (all_same) {
printf("WARNING: All RNG outputs are identical (unexpected)\n");
}
/* Clean up */
for (i = 0; i < NUM_RNGS; i++) {
wc_FreeRng(&rngs[i]);
}
if (ret == 0) {
printf("Successfully operated %d RNG instances concurrently\n",
NUM_RNGS);
} else {
printf("Experienced failure %d\n", ret);
}
return ret;
}
/* Test 3: Stress test - run many iterations to detect false positives */
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_stress_rng(
unsigned long iterations)
{
WC_RNG rng;
byte buffer[BUFFER_SIZE];
int ret = 0;
unsigned long i;
unsigned long errors = 0;
printf("\n--- Test 5: Stress Test (%lu iterations) ---\n", iterations);
printf("Verifies no false positive continuous test failures occur.\n");
ret = wc_InitRng(&rng);
if (ret != 0) {
printf("ERROR: wc_InitRng failed with code %d\n", ret);
return ret;
}
for (i = 0; i < iterations; i++) {
ret = wc_RNG_GenerateBlock(&rng, buffer, BUFFER_SIZE);
if (ret != 0 ) {
printf("\n" COLOR_RED "ERROR: error at iteration %lu"
COLOR_RESET "\n", i);
errors++;
}
/* Progress reporting */
if ((i + 1) % PROGRESS_INTERVAL == 0) {
printf(" Progress: %lu iterations (%.1f%%)...\r",
i + 1, (double)(i + 1) / iterations * 100.0);
fflush(stdout);
}
}
printf("\n");
wc_FreeRng(&rng);
if (errors == 0) {
printf(COLOR_GREEN "Completed %lu iterations, no false positives!"
COLOR_RESET "\n", iterations);
return 0;
} else {
printf(COLOR_RED "Test failed with %lu errors" COLOR_RESET "\n",
errors);
return ret;
}
}
/* Test 4: Reinitialize RNG multiple times */
#ifndef WOLFSSL_PTHREADS
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_reinit_rng(void)
{
WC_RNG rng;
byte buffer[BUFFER_SIZE];
int ret = 0;
unsigned long i;
unsigned long REINIT_COUNT = STRESS_TEST_ITERATIONS;
printf("\n--- Test 4: RNG Reinitialization Test ---\n");
for (i = 0; i < REINIT_COUNT; i++) {
ret = wc_InitRng(&rng);
if (ret != 0) {
#if defined(HAVE_FIPS) && defined(VERBOSE_STRESS_TEST)
/* SUPER noisy default on when not FIPS and off when FIPS */
printf("ERROR: wc_InitRng failed at iteration %lu with code "
"%d\n", i, ret);
#endif
return ret;
}
ret = wc_RNG_GenerateBlock(&rng, buffer, BUFFER_SIZE);
if (ret != 0) {
#if defined(HAVE_FIPS) && defined(VERBOSE_STRESS_TEST)
/* SUPER noisy default on when not FIPS and off when FIPS */
printf("ERROR: wc_RNG_GenerateBlock failed at iteration %lu "
"with code %d\n", i, ret);
#endif
wc_FreeRng(&rng);
return ret;
}
wc_FreeRng(&rng);
}
printf("Successfully reinitialized RNG %lu times\n", REINIT_COUNT);
return 0;
}
#else
/* RNG ReInit Test Configuration
*
* You can override these at compile time with -DNUM_THREADS=X
* or use one of the predefined test profiles below:
*
* Profile A (Default - Aggressive): 40 threads x 100M = 4B iterations
* - Expected ~30 statistical false positives
* - High entropy source stress
* - Total entropy demand: ~528 GB
*
* Profile B (Moderate): 4 threads x 1M = 4M iterations
* - Expected ~0.03 statistical false positives (unlikely to see any)
* - Moderate entropy source stress
* - Compile with: -DRNG_TEST_PROFILE=2
*
* Profile C (Single-threaded): 1 thread x 100M = 100M iterations
* - Expected ~0.75 statistical false positives
* - Tests for false positives without threading stress
* - Compile with: -DRNG_TEST_PROFILE=3
*/
#if defined(RNG_TEST_PROFILE) && (RNG_TEST_PROFILE == 2)
/* Profile B: Moderate test - reduced entropy stress */
#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif
#ifndef ITERATIONS_PER_THREAD
/* Only testing 400 million, expect 1 or 0 failures */
#define ITERATIONS_PER_THREAD 1000000
#endif
#elif defined(RNG_TEST_PROFILE) && (RNG_TEST_PROFILE == 3)
/* Profile C: Single-threaded stress - no thread contention */
#ifndef NUM_THREADS
#define NUM_THREADS 1
#endif
#ifndef ITERATIONS_PER_THREAD
/* Test 4 billion with no thread contention and less likely to
* deplete entropy pool, expect 0 failures */
#define ITERATIONS_PER_THREAD 4000000000
#endif
#else
/* Profile A (Default): Aggressive multi-threaded test */
#ifndef NUM_THREADS
#define NUM_THREADS 40
#endif
#ifndef ITERATIONS_PER_THREAD
/* Test 4 billion with high probability of entropy depletion.
* expect many failures (30+ threads failing) */
#define ITERATIONS_PER_THREAD 100000000
#endif
#endif
struct worker_args {
int id;
unsigned long iterations;
int result;
unsigned long succCnt;
};
static THREAD_RETURN WOLFSSL_THREAD reinit_worker(void *args)
{
struct worker_args* wa = (struct worker_args*)args;
WC_RNG rng;
byte buffer[BUFFER_SIZE];
int ret = 0;
unsigned long i;
for (i = 0; i < wa->iterations; i++) {
ret = wc_InitRng(&rng);
if (ret != 0) {
#if defined(HAVE_FIPS) && defined(VERBOSE_STRESS_TEST)
/* SUPER noisy default on when not FIPS and off when FIPS */
printf("ERROR: wc_InitRng failed at iteration %lu with code "
"%d\n", i, ret);
#endif
wa->result = ret;
wa->succCnt -= 1;
}
ret = wc_RNG_GenerateBlock(&rng, buffer, BUFFER_SIZE);
if (ret != 0) {
#if defined(HAVE_FIPS) && defined(VERBOSE_STRESS_TEST)
/* SUPER noisy default on when not FIPS and off when FIPS */
printf("ERROR: wc_RNG_GenerateBlock failed at iteration %lu "
"with code %d\n", i, ret);
#endif
wc_FreeRng(&rng);
wa->result = ret;
}
(void) wc_FreeRng(&rng);
}
wa->result = 0;
WOLFSSL_RETURN_FROM_THREAD(0);
}
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_reinit_rng(void)
{
THREAD_TYPE threads[NUM_THREADS];
struct worker_args args[NUM_THREADS];
int i, ret = 0;
int succT = NUM_THREADS, failT = 0;
unsigned long totalCnt, totalSuccCnt = 0;
unsigned long total_iterations = (unsigned long)NUM_THREADS *
ITERATIONS_PER_THREAD;
totalCnt = (unsigned long) ITERATIONS_PER_THREAD *
(unsigned long) NUM_THREADS;
printf("\n--- Test 4: RNG ReInit Test (multi-threaded) ---\n");
printf("Configuration: %d threads x %lu iterations = %lu total\n",
NUM_THREADS, (unsigned long)ITERATIONS_PER_THREAD,
total_iterations);
#if defined(RNG_TEST_PROFILE)
printf("Test Profile: %d ", RNG_TEST_PROFILE);
#if RNG_TEST_PROFILE == 2
printf("(Moderate - reduced entropy stress)\n");
#elif RNG_TEST_PROFILE == 3
printf("(Single-threaded stress test)\n");
#else
printf("(Custom)\n");
#endif
#else
printf("Test Profile: Default (Aggressive multi-threaded)\n");
#endif
printf("Expected statistical false positive rate: ~%.2f failures\n",
(double)total_iterations * 32.0 / 4294967296.0);
for (i = 0; i < NUM_THREADS; i++) {
args[i].id = i;
args[i].iterations = ITERATIONS_PER_THREAD;
args[i].succCnt = ITERATIONS_PER_THREAD;
ret = wolfSSL_NewThread(&threads[i], &reinit_worker, &args[i]);
if (ret != 0) {
printf("ERROR: Failed to create thread %d\n", i);
goto drbg_cont_end;
}
}
for (i = 0; i < NUM_THREADS; i++) {
wolfSSL_JoinThread(threads[i]);
}
for (i = 0; i < NUM_THREADS; i++) {
if (args[i].result == 0) {
printf("Thread %d Succeeded\n", i);
} else {
succT -= 1;
failT += 1;
printf("Thread %d failed\n", i);
}
totalSuccCnt += args[i].succCnt;
}
drbg_cont_end:
printf("Reinitialized RNG %lu times across %d threads\n",
total_iterations, NUM_THREADS);
printf("Experienced %d thread failures and %d thread successes\n",
failT, succT);
if (totalCnt == totalSuccCnt) {
printf("All %lu API calls succeeded\n", totalSuccCnt);
} else {
printf("%lu/%lu API calls failed\n", totalCnt - totalSuccCnt,
totalCnt);
}
return failT;
}
#endif /* !WOLFSSL_PTHREADS */
/* Test 5: FIPS status check */
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t test_fips_status(void)
{
#ifdef HAVE_FIPS
int status;
printf("\n--- Test 3: FIPS Status Check ---\n");
status = wolfCrypt_GetStatus_fips();
printf("FIPS Module Status: %d\n", status);
if (status != 0) {
printf(COLOR_YELLOW "WARNING: FIPS module not OK state (status=%d)"
COLOR_RESET "\n", status);
return -1;
} else {
printf(COLOR_GREEN "FIPS module in OK state" COLOR_RESET "\n");
return 0;
}
#else
printf("\n--- Test 3: FIPS Status Check ---\n");
printf(COLOR_YELLOW "SKIPPED: FIPS not enabled" COLOR_RESET "\n");
return 0; /* Not a failure, just skipped */
#endif
}
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t drbg_continuous_main(void)
{
int all_passed = 1;
unsigned long stress_iterations = STRESS_TEST_ITERATIONS;
printf("===============================================\n");
printf("DRBG Continuous Test Validation Suite\n");
printf("===============================================\n");
#ifdef HAVE_FIPS
printf("FIPS Build: YES\n");
printf("FIPS Version: %s\n", wolfCrypt_GetVersion_fips());
#else
printf("FIPS Build: NO\n");
#endif
printf("\n");
/* Run tests */
/* Test 1 */
print_result("Basic RNG Functionality", test_basic_rng(), &all_passed);
/* Test 2 */
print_result("Multiple RNG Instances", test_multiple_rngs(),
&all_passed);
/* Test 3 */
print_result("FIPS Status Check", test_fips_status(), &all_passed);
/* Test 4 */
print_result("RNG Reinitialization", test_reinit_rng(), &all_passed);
/* Stress test (takes longest) */
/* Test 5 */
print_result("Stress Test (No False Positives)",
test_stress_rng(stress_iterations), &all_passed);
/* Test 3 - ReRun after the heavy stress tests */
print_result("FIPS Status Check", test_fips_status(), &all_passed);
/* Summary */
printf("\n===============================================\n");
if (all_passed) {
printf(COLOR_GREEN "ALL TESTS PASSED" COLOR_RESET "\n");
printf("The DRBG continuous test fix is working correctly.\n");
} else {
printf(COLOR_RED "SOME TESTS FAILED" COLOR_RESET "\n");
printf("Please review the errors above.\n");
}
printf("===============================================\n");
return all_passed ? 0 : 1;
}
#endif /* REALLY_LONG_DRBG_CONTINUOUS_TEST */
#undef ERROR_OUT
static WC_MAYBE_UNUSED const int fiducial4 = WC_TEST_RET_LN;

View File

@@ -179,6 +179,9 @@ struct OS_Seed {
/* wolfentropy.h will define for HAVE_ENTROPY_MEMUSE */
#ifdef HAVE_ENTROPY_MEMUSE
#include <wolfssl/wolfcrypt/wolfentropy.h>
#else
/* Maximum entropy bits that can be produced. */
#define MAX_ENTROPY_BITS 256
#endif
/* ENTROPY_SCALE_FACTOR is unprefixed for backward compat. */