Merge pull request #9668 from kaleb-himes/PQ-FS-2026-Part1

PQ FS 2026 part1
This commit is contained in:
Sean Parkinson
2026-01-23 10:30:47 +10:00
committed by GitHub
8 changed files with 826 additions and 19 deletions
+1 -1
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:
+8 -4
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 }}
+1 -1
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
+5
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++)
+116 -13
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 < SEED_BLOCK_SZ) {
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;
+182
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
+510
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;
+3
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. */