mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-11 12:00:51 +02:00
Merge pull request #9668 from kaleb-himes/PQ-FS-2026-Part1
PQ FS 2026 part1
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user