Skip to content

Phase 2: PQ in boundary and SHA512 DRBG#9843

Open
kaleb-himes wants to merge 1 commit intowolfSSL:masterfrom
kaleb-himes:PQ-FS-2026-Part2
Open

Phase 2: PQ in boundary and SHA512 DRBG#9843
kaleb-himes wants to merge 1 commit intowolfSSL:masterfrom
kaleb-himes:PQ-FS-2026-Part2

Conversation

@kaleb-himes
Copy link
Copy Markdown
Contributor

@kaleb-himes kaleb-himes commented Feb 27, 2026

Description

Phase 2 of the upcoming Post Quantum Full FIPS Submission. ML-KEM, ML-DSA, LMS (Verify), XMSS (Verify) added to module boundary along with a shiny new SHA512-DRBG implementation and NIST vector tests for sanity.

Release 1-liners for this PR:

  1. SHA-512 DRBG - entire new DRBG implementation alongside SHA-256
  2. FIPS wrappers - ML-KEM, ML-DSA, LMS, XMSS, SLH-DSA (dozens of new wrapper functions)
  3. CASTs - 5+ new Conditional Algorithm Self-Tests with embedded KAT vectors
  4. New API surface - mu sign/verify, internal interface wrappers, runtime disable/enable functions
  5. PCT code - pairwise consistency tests in MakeKey paths
  6. SLH-DSA - SHA2 (128, 192, 256) support in addition to SHAKE

Testing

Many in-house FIPS custom scripts, optest app and harness.

Checklist

  • added tests
  • updated/added doxygen
  • updated appropriate READMEs
  • Updated manual and documentation

Copy link
Copy Markdown
Contributor Author

@kaleb-himes kaleb-himes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

** NON-FIPS **

./wolfcrypt/benchmark/benchmark -rng -rng-sha512 -rng-init -rng-sha512-init
------------------------------------------------------------------------------
 wolfSSL version 5.8.4
------------------------------------------------------------------------------
Math: 	Multi-Precision: Wolf(SP) word-size=64 bits=4096 sp_int.c
wolfCrypt Benchmark (block bytes 1048576, min 1.0 sec each)
RNG SHA-256 DRBG           205 MiB took 1.021 seconds,  200.853 MiB/s Cycles per byte =  17.50
RNG SHA-512 DRBG           380 MiB took 1.013 seconds,  375.166 MiB/s Cycles per byte =   9.37
RNG      256 SHA256 Init/Free 146504 ops took 1.000 sec, avg 0.007 ms, 146503.616 ops/sec, 3686366718 cycles    25162.2 Cycles/op
RNG      512 SHA512 Init/Free 204287 ops took 1.000 sec, avg 0.005 ms, 204285.977 ops/sec, 3686374128 cycles    18045.1 Cycles/op
Benchmark complete

** FIPS **

./wolfcrypt/benchmark/benchmark -rng -rng-sha512 -rng-init -rng-sha512-init
------------------------------------------------------------------------------
 wolfSSL version 5.8.4
------------------------------------------------------------------------------
wolfCrypt Benchmark (block bytes 1048576, min 1.0 sec each)
RNG SHA-256 DRBG           205 MiB took 1.021 seconds,  200.763 MiB/s Cycles per byte =  17.51
RNG SHA-512 DRBG           375 MiB took 1.010 seconds,  371.333 MiB/s Cycles per byte =   9.47
RNG      256 SHA256 Init/Free 123726 ops took 1.000 sec, avg 0.008 ms, 123725.735 ops/sec, 3686363516 cycles    29794.6 Cycles/op
RNG      512 SHA512 Init/Free 172608 ops took 1.000 sec, avg 0.006 ms, 172607.506 ops/sec, 3686365894 cycles    21356.9 Cycles/op
Benchmark complete

@kaleb-himes kaleb-himes force-pushed the PQ-FS-2026-Part2 branch 2 times, most recently from 6f2187b to 9e0e11f Compare March 1, 2026 20:03
@kaleb-himes kaleb-himes requested review from SparkiDev and dgarske March 1, 2026 21:44
@kaleb-himes kaleb-himes requested a review from wolfSSL-Bot March 1, 2026 21:45
@kaleb-himes kaleb-himes marked this pull request as draft March 3, 2026 18:09
@kaleb-himes
Copy link
Copy Markdown
Contributor Author

Going to add CAVP support for the perso string and additional Input also. Will keep as a draft for now.

@douzzer douzzer added the Not For This Release Not for release 5.9.1 label Mar 11, 2026
@douzzer douzzer self-requested a review March 11, 2026 03:01
@kaleb-himes kaleb-himes force-pushed the PQ-FS-2026-Part2 branch 4 times, most recently from e88dd14 to 7f4b8fb Compare March 24, 2026 10:21
@kaleb-himes kaleb-himes requested a review from SparkiDev March 24, 2026 10:46
@kaleb-himes kaleb-himes force-pushed the PQ-FS-2026-Part2 branch 6 times, most recently from b6e3558 to b64531e Compare March 26, 2026 21:46
@kaleb-himes kaleb-himes force-pushed the PQ-FS-2026-Part2 branch 3 times, most recently from 0e0b546 to 1b4f4ba Compare April 14, 2026 14:25
@kaleb-himes kaleb-himes marked this pull request as ready for review April 14, 2026 15:08
@kaleb-himes kaleb-himes removed Not For This Release Not for release 5.9.1 labels Apr 14, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 15, 2026

MemBrowse Memory Report

gcc-arm-cortex-m4

  • FLASH: .rodata.CSWTCH.1 +16 B, .rodata.str1.1 +340 B, .text +640 B (+0.5%, 196,271 B / 262,144 B, total: 75% used)
  • RAM: .bss +8 B (+1.0%, 828 B / 65,536 B, total: 1% used)

gcc-arm-cortex-m4-baremetal

  • FLASH: .text +640 B (+1.0%, 64,179 B / 262,144 B, total: 24% used)
  • RAM: .bss +4 B (+0.6%, 644 B / 65,536 B, total: 1% used)

gcc-arm-cortex-m4-min-ecc

  • FLASH: .text +640 B (+1.1%, 59,765 B / 262,144 B, total: 23% used)
  • RAM: .bss +4 B (+0.6%, 668 B / 65,536 B, total: 1% used)

gcc-arm-cortex-m4-tls12

  • FLASH: .text +640 B (+0.5%, 120,058 B / 262,144 B, total: 46% used)
  • RAM: .bss +8 B (+1.0%, 808 B / 65,536 B, total: 1% used)

@kaleb-himes
Copy link
Copy Markdown
Contributor Author

retest this please.

@kaleb-himes kaleb-himes force-pushed the PQ-FS-2026-Part2 branch 2 times, most recently from 52fc40f to 99fef50 Compare April 16, 2026 15:18
Copy link
Copy Markdown

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #9843

Scan targets checked: wolfcrypt-api_misuse, wolfcrypt-bugs, wolfcrypt-compliance, wolfcrypt-concurrency, wolfcrypt-consttime, wolfcrypt-defaults, wolfcrypt-mutation, wolfcrypt-portability, wolfcrypt-proptest, wolfcrypt-src, wolfcrypt-zeroize, wolfssl-bugs, wolfssl-compliance, wolfssl-consttime, wolfssl-defaults, wolfssl-mutation, wolfssl-proptest, wolfssl-src, wolfssl-zeroize

Findings: 10
10 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/random.c
Comment on lines 935 to 1377
return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* ====================================================================== */
/* SHA-512 Hash_DRBG (SP 800-90A Rev 1, Table 2) */
/* */
/* Internal state (V, C): seedlen = 888 bits = 111 bytes each */
/* Output block length: 512 bits = 64 bytes (WC_SHA512_DIGEST_SIZE) */
/* Security strength: 256 bits */
/* */
/* NOTE: The raw entropy seed gathered at instantiation / reseed is */
/* WC_DRBG_SEED_SZ (1024 bits in FIPS builds), NOT seedlen. We overseed */
/* to tolerate weak entropy sources. Hash_df then compresses the seed */
/* material down to the 888-bit V and derives C from V. See random.h. */
/* ====================================================================== */
#ifdef WOLFSSL_DRBG_SHA512

#define OUTPUT_BLOCK_LEN_SHA512 (WC_SHA512_DIGEST_SIZE) /* 64 bytes */

/* Hash Derivation Function using SHA-512 */
/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
byte type,
const byte* inA, word32 inASz,
const byte* inB, word32 inBSz,
const byte* inC, word32 inCSz)
{
int ret = DRBG_FAILURE;
byte ctr;
word32 i;
word32 len;
word32 bits = (outSz * 8);
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#endif

#ifdef LITTLE_ENDIAN_ORDER
bits = ByteReverseWord32(bits);
#endif
len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

ctr = 1;
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret != 0)
break;
#endif
ret = wc_Sha512Update(sha, &ctr, sizeof(ctr));
if (ret == 0) {
ctr++;
ret = wc_Sha512Update(sha, (byte*)&bits, sizeof(bits));
}

if (ret == 0) {
/* churning V is the only string that doesn't have the type added */
if (type != drbgInitV)
ret = wc_Sha512Update(sha, &type, sizeof(type));
}
if (ret == 0)
ret = wc_Sha512Update(sha, inA, inASz);
if (ret == 0) {
if (inB != NULL && inBSz > 0)
ret = wc_Sha512Update(sha, inB, inBSz);
}
if (ret == 0) {
if (inC != NULL && inCSz > 0)
ret = wc_Sha512Update(sha, inC, inCSz);
}
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0) {
if (outSz > OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
}
else {
XMEMCPY(out, digest, outSz);
}
}
}

ForceZero(digest, WC_SHA512_DIGEST_SIZE);

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz,
const byte* additional, word32 additionalSz)
{
int ret;
WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0);

if (drbg == NULL) {
return DRBG_FAILURE;
}

#ifdef WOLFSSL_SMALL_STACK_CACHE
newV = drbg->seed_scratch;
#else
WC_ALLOC_VAR_EX(newV, byte, DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
#endif
XMEMSET(newV, 0, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, newV, DRBG_SHA512_SEED_LEN, drbgReseed,
drbg->V, sizeof(drbg->V), seed, seedSz,
additional, additionalSz);
if (ret == DRBG_SUCCESS) {
XMEMCPY(drbg->V, newV, sizeof(drbg->V));
ForceZero(newV, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0);
}
if (ret == DRBG_SUCCESS) {
drbg->reseedCtr = 1;
}

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(newV, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
const byte* V)
{
int ret = DRBG_FAILURE;
word32 i;
word32 len;
#if defined(WOLFSSL_SMALL_STACK_CACHE)
wc_Sha512* sha = &drbg->sha512;
byte* data = drbg->seed_scratch;
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
wc_Sha512 sha[1];
byte* data = NULL;
byte* digest = NULL;
#else
wc_Sha512 sha[1];
byte data[DRBG_SHA512_SEED_LEN];
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
data = (byte*)XMALLOC(DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER);
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (data == NULL || digest == NULL) {
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
XFREE(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
return DRBG_FAILURE;
}
#endif

/* Special case: outSz is 0 and out is NULL. Generate a block to save for
* the continuous test. */
if (outSz == 0) {
outSz = 1;
}

len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

XMEMCPY(data, V, DRBG_SHA512_SEED_LEN);
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, data, DRBG_SHA512_SEED_LEN);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
if (out != NULL && outSz != 0) {
if (outSz >= OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
array_add_one(data, DRBG_SHA512_SEED_LEN);
}
else {
XMEMCPY(out, digest, outSz);
outSz = 0;
}
}
}
else {
break;
}
}
ForceZero(data, DRBG_SHA512_SEED_LEN);

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
WC_FREE_VAR_EX(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */
static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out,
word32 outSz,
const byte* additional, word32 additionalSz)
{
int ret;
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
byte type;
word64 reseedCtr;

if (drbg == NULL) {
return DRBG_FAILURE;
}

if (drbg->reseedCtr >= WC_RESEED_INTERVAL) {
return DRBG_NEED_RESEED;
}
else {
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

type = drbgGenerateH;
reseedCtr = drbg->reseedCtr;

/* SP 800-90A Section 10.1.1.4 step 2:
* If additional_input != Null, w = Hash(0x02 || V || additional_input),
* V = (V + w) mod 2^seedlen */
ret = DRBG_SUCCESS;
if (additional != NULL && additionalSz > 0) {
byte addType = drbgGenerateW; /* 0x02 */
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &addType, sizeof(addType));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Update(sha, additional, additionalSz);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0)
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
}

if (ret == 0)
ret = Hash512_gen(drbg, out, outSz, drbg->V);
if (ret == DRBG_SUCCESS) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &type, sizeof(type));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
array_add(drbg->V, sizeof(drbg->V), drbg->C, sizeof(drbg->C));
#ifdef LITTLE_ENDIAN_ORDER
reseedCtr = ByteReverseWord64(reseedCtr);
#endif
array_add(drbg->V, sizeof(drbg->V),
(byte*)&reseedCtr, sizeof(reseedCtr));
ret = DRBG_SUCCESS;
}
drbg->reseedCtr++;
}
ForceZero(digest, WC_SHA512_DIGEST_SIZE);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif
}

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Init(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz, const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz)
{
if (seed == NULL)
return DRBG_FAILURE;

if (Hash512_df(drbg, drbg->V, sizeof(drbg->V), drbgInitV, seed, seedSz,
nonce, nonceSz,
perso, persoSz) == DRBG_SUCCESS &&
Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0) == DRBG_SUCCESS) {

drbg->reseedCtr = 1;
return DRBG_SUCCESS;
}
else {
return DRBG_FAILURE;
}
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg,
const byte* seed, word32 seedSz,
const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz,
void* heap, int devId)
{
int ret = DRBG_FAILURE;

XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal));
drbg->heap = heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
drbg->devId = devId;
#else
(void)devId;
#endif

#ifdef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(&drbg->sha512, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(&drbg->sha512);
#endif
if (ret != 0)
return ret;
#endif

if (seed != NULL)
ret = Hash512_DRBG_Init(drbg, seed, seedSz, nonce, nonceSz,
perso, persoSz);
return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg)
{
word32 i;
int compareSum = 0;
byte* compareDrbg = (byte*)drbg;

#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(&drbg->sha512);
#endif

ForceZero(drbg, sizeof(DRBG_SHA512_internal));

for (i = 0; i < sizeof(DRBG_SHA512_internal); i++) {
compareSum |= compareDrbg[i] ^ 0;
}

return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

#endif /* WOLFSSL_DRBG_SHA512 */


/* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data
*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] Runtime DRBG disable APIs lack guardrail against disabling all DRBG types
Category: Unsafe default configurations

The new runtime DRBG disable/enable API (wc_Sha256Drbg_Disable(), wc_Sha512Drbg_Disable()) allows a caller to disable both SHA-256 and SHA-512 DRBGs simultaneously. Both functions unconditionally succeed (return 0) with no check that at least one DRBG type remains available. When both are disabled, any subsequent wc_InitRng() call will fail because the DRBG selection logic in _InitRng falls through all branches. While _InitRng does return an error (likely DRBG_NO_ENABLED_E) in this case, a defensive API should either refuse to disable the last available DRBG type or return a warning, rather than silently allowing a configuration that makes RNG initialization impossible. This is especially concerning in FIPS builds where RNG availability is a security requirement.

int wc_Sha256Drbg_Disable(void) { g_sha256DrbgDisabled = 1; return 0; }
int wc_Sha512Drbg_Disable(void) { g_sha512DrbgDisabled = 1; return 0; }

Recommendation: Add a check in each disable function to verify the other DRBG type is still enabled before disabling. Return a new error code (e.g., BAD_STATE_E) if disabling would leave no DRBG available. Alternatively, add FIPS-specific guards: #ifdef HAVE_FIPS that prevent disabling the last DRBG.

Comment thread wolfcrypt/src/random.c
Comment on lines 935 to 1377
return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* ====================================================================== */
/* SHA-512 Hash_DRBG (SP 800-90A Rev 1, Table 2) */
/* */
/* Internal state (V, C): seedlen = 888 bits = 111 bytes each */
/* Output block length: 512 bits = 64 bytes (WC_SHA512_DIGEST_SIZE) */
/* Security strength: 256 bits */
/* */
/* NOTE: The raw entropy seed gathered at instantiation / reseed is */
/* WC_DRBG_SEED_SZ (1024 bits in FIPS builds), NOT seedlen. We overseed */
/* to tolerate weak entropy sources. Hash_df then compresses the seed */
/* material down to the 888-bit V and derives C from V. See random.h. */
/* ====================================================================== */
#ifdef WOLFSSL_DRBG_SHA512

#define OUTPUT_BLOCK_LEN_SHA512 (WC_SHA512_DIGEST_SIZE) /* 64 bytes */

/* Hash Derivation Function using SHA-512 */
/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
byte type,
const byte* inA, word32 inASz,
const byte* inB, word32 inBSz,
const byte* inC, word32 inCSz)
{
int ret = DRBG_FAILURE;
byte ctr;
word32 i;
word32 len;
word32 bits = (outSz * 8);
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#endif

#ifdef LITTLE_ENDIAN_ORDER
bits = ByteReverseWord32(bits);
#endif
len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

ctr = 1;
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret != 0)
break;
#endif
ret = wc_Sha512Update(sha, &ctr, sizeof(ctr));
if (ret == 0) {
ctr++;
ret = wc_Sha512Update(sha, (byte*)&bits, sizeof(bits));
}

if (ret == 0) {
/* churning V is the only string that doesn't have the type added */
if (type != drbgInitV)
ret = wc_Sha512Update(sha, &type, sizeof(type));
}
if (ret == 0)
ret = wc_Sha512Update(sha, inA, inASz);
if (ret == 0) {
if (inB != NULL && inBSz > 0)
ret = wc_Sha512Update(sha, inB, inBSz);
}
if (ret == 0) {
if (inC != NULL && inCSz > 0)
ret = wc_Sha512Update(sha, inC, inCSz);
}
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0) {
if (outSz > OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
}
else {
XMEMCPY(out, digest, outSz);
}
}
}

ForceZero(digest, WC_SHA512_DIGEST_SIZE);

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz,
const byte* additional, word32 additionalSz)
{
int ret;
WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0);

if (drbg == NULL) {
return DRBG_FAILURE;
}

#ifdef WOLFSSL_SMALL_STACK_CACHE
newV = drbg->seed_scratch;
#else
WC_ALLOC_VAR_EX(newV, byte, DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
#endif
XMEMSET(newV, 0, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, newV, DRBG_SHA512_SEED_LEN, drbgReseed,
drbg->V, sizeof(drbg->V), seed, seedSz,
additional, additionalSz);
if (ret == DRBG_SUCCESS) {
XMEMCPY(drbg->V, newV, sizeof(drbg->V));
ForceZero(newV, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0);
}
if (ret == DRBG_SUCCESS) {
drbg->reseedCtr = 1;
}

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(newV, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
const byte* V)
{
int ret = DRBG_FAILURE;
word32 i;
word32 len;
#if defined(WOLFSSL_SMALL_STACK_CACHE)
wc_Sha512* sha = &drbg->sha512;
byte* data = drbg->seed_scratch;
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
wc_Sha512 sha[1];
byte* data = NULL;
byte* digest = NULL;
#else
wc_Sha512 sha[1];
byte data[DRBG_SHA512_SEED_LEN];
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
data = (byte*)XMALLOC(DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER);
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (data == NULL || digest == NULL) {
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
XFREE(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
return DRBG_FAILURE;
}
#endif

/* Special case: outSz is 0 and out is NULL. Generate a block to save for
* the continuous test. */
if (outSz == 0) {
outSz = 1;
}

len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

XMEMCPY(data, V, DRBG_SHA512_SEED_LEN);
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, data, DRBG_SHA512_SEED_LEN);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
if (out != NULL && outSz != 0) {
if (outSz >= OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
array_add_one(data, DRBG_SHA512_SEED_LEN);
}
else {
XMEMCPY(out, digest, outSz);
outSz = 0;
}
}
}
else {
break;
}
}
ForceZero(data, DRBG_SHA512_SEED_LEN);

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
WC_FREE_VAR_EX(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */
static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out,
word32 outSz,
const byte* additional, word32 additionalSz)
{
int ret;
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
byte type;
word64 reseedCtr;

if (drbg == NULL) {
return DRBG_FAILURE;
}

if (drbg->reseedCtr >= WC_RESEED_INTERVAL) {
return DRBG_NEED_RESEED;
}
else {
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

type = drbgGenerateH;
reseedCtr = drbg->reseedCtr;

/* SP 800-90A Section 10.1.1.4 step 2:
* If additional_input != Null, w = Hash(0x02 || V || additional_input),
* V = (V + w) mod 2^seedlen */
ret = DRBG_SUCCESS;
if (additional != NULL && additionalSz > 0) {
byte addType = drbgGenerateW; /* 0x02 */
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &addType, sizeof(addType));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Update(sha, additional, additionalSz);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0)
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
}

if (ret == 0)
ret = Hash512_gen(drbg, out, outSz, drbg->V);
if (ret == DRBG_SUCCESS) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &type, sizeof(type));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
array_add(drbg->V, sizeof(drbg->V), drbg->C, sizeof(drbg->C));
#ifdef LITTLE_ENDIAN_ORDER
reseedCtr = ByteReverseWord64(reseedCtr);
#endif
array_add(drbg->V, sizeof(drbg->V),
(byte*)&reseedCtr, sizeof(reseedCtr));
ret = DRBG_SUCCESS;
}
drbg->reseedCtr++;
}
ForceZero(digest, WC_SHA512_DIGEST_SIZE);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif
}

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Init(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz, const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz)
{
if (seed == NULL)
return DRBG_FAILURE;

if (Hash512_df(drbg, drbg->V, sizeof(drbg->V), drbgInitV, seed, seedSz,
nonce, nonceSz,
perso, persoSz) == DRBG_SUCCESS &&
Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0) == DRBG_SUCCESS) {

drbg->reseedCtr = 1;
return DRBG_SUCCESS;
}
else {
return DRBG_FAILURE;
}
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg,
const byte* seed, word32 seedSz,
const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz,
void* heap, int devId)
{
int ret = DRBG_FAILURE;

XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal));
drbg->heap = heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
drbg->devId = devId;
#else
(void)devId;
#endif

#ifdef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(&drbg->sha512, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(&drbg->sha512);
#endif
if (ret != 0)
return ret;
#endif

if (seed != NULL)
ret = Hash512_DRBG_Init(drbg, seed, seedSz, nonce, nonceSz,
perso, persoSz);
return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg)
{
word32 i;
int compareSum = 0;
byte* compareDrbg = (byte*)drbg;

#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(&drbg->sha512);
#endif

ForceZero(drbg, sizeof(DRBG_SHA512_internal));

for (i = 0; i < sizeof(DRBG_SHA512_internal); i++) {
compareSum |= compareDrbg[i] ^ 0;
}

return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

#endif /* WOLFSSL_DRBG_SHA512 */


/* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data
*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] DRBG disable/enable global flags lack thread-safety documentation
Category: Unsafe default configurations

The new g_sha256DrbgDisabled and g_sha512DrbgDisabled global variables are plain static int with no synchronization. While this is consistent with wolfCrypt's existing pattern for configuration flags (e.g., seed callbacks, FIPS callback registration), the _InitRng function reads both flags in sequence without a lock. If one thread calls wc_Sha512Drbg_Disable() while another thread is inside _InitRng, the DRBG type selection depends on timing. For simple int stores on all wolfSSL-targeted platforms this is practically safe, but the API should document that these functions must be called during application startup before concurrent RNG use, consistent with how other wolfCrypt global configuration APIs are documented.

static int g_sha256DrbgDisabled = 0;
static int g_sha512DrbgDisabled = 0;

Recommendation: Add API documentation stating that wc_Sha*Drbg_Disable/Enable must be called during application initialization before any concurrent wc_InitRng() calls. Consider adding a comment in the code noting the threading contract, consistent with similar wolfCrypt configuration APIs.

Comment thread wolfcrypt/src/random.c
Comment on lines 935 to 1377
return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* ====================================================================== */
/* SHA-512 Hash_DRBG (SP 800-90A Rev 1, Table 2) */
/* */
/* Internal state (V, C): seedlen = 888 bits = 111 bytes each */
/* Output block length: 512 bits = 64 bytes (WC_SHA512_DIGEST_SIZE) */
/* Security strength: 256 bits */
/* */
/* NOTE: The raw entropy seed gathered at instantiation / reseed is */
/* WC_DRBG_SEED_SZ (1024 bits in FIPS builds), NOT seedlen. We overseed */
/* to tolerate weak entropy sources. Hash_df then compresses the seed */
/* material down to the 888-bit V and derives C from V. See random.h. */
/* ====================================================================== */
#ifdef WOLFSSL_DRBG_SHA512

#define OUTPUT_BLOCK_LEN_SHA512 (WC_SHA512_DIGEST_SIZE) /* 64 bytes */

/* Hash Derivation Function using SHA-512 */
/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
byte type,
const byte* inA, word32 inASz,
const byte* inB, word32 inBSz,
const byte* inC, word32 inCSz)
{
int ret = DRBG_FAILURE;
byte ctr;
word32 i;
word32 len;
word32 bits = (outSz * 8);
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#endif

#ifdef LITTLE_ENDIAN_ORDER
bits = ByteReverseWord32(bits);
#endif
len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

ctr = 1;
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret != 0)
break;
#endif
ret = wc_Sha512Update(sha, &ctr, sizeof(ctr));
if (ret == 0) {
ctr++;
ret = wc_Sha512Update(sha, (byte*)&bits, sizeof(bits));
}

if (ret == 0) {
/* churning V is the only string that doesn't have the type added */
if (type != drbgInitV)
ret = wc_Sha512Update(sha, &type, sizeof(type));
}
if (ret == 0)
ret = wc_Sha512Update(sha, inA, inASz);
if (ret == 0) {
if (inB != NULL && inBSz > 0)
ret = wc_Sha512Update(sha, inB, inBSz);
}
if (ret == 0) {
if (inC != NULL && inCSz > 0)
ret = wc_Sha512Update(sha, inC, inCSz);
}
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0) {
if (outSz > OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
}
else {
XMEMCPY(out, digest, outSz);
}
}
}

ForceZero(digest, WC_SHA512_DIGEST_SIZE);

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz,
const byte* additional, word32 additionalSz)
{
int ret;
WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0);

if (drbg == NULL) {
return DRBG_FAILURE;
}

#ifdef WOLFSSL_SMALL_STACK_CACHE
newV = drbg->seed_scratch;
#else
WC_ALLOC_VAR_EX(newV, byte, DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
#endif
XMEMSET(newV, 0, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, newV, DRBG_SHA512_SEED_LEN, drbgReseed,
drbg->V, sizeof(drbg->V), seed, seedSz,
additional, additionalSz);
if (ret == DRBG_SUCCESS) {
XMEMCPY(drbg->V, newV, sizeof(drbg->V));
ForceZero(newV, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0);
}
if (ret == DRBG_SUCCESS) {
drbg->reseedCtr = 1;
}

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(newV, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
const byte* V)
{
int ret = DRBG_FAILURE;
word32 i;
word32 len;
#if defined(WOLFSSL_SMALL_STACK_CACHE)
wc_Sha512* sha = &drbg->sha512;
byte* data = drbg->seed_scratch;
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
wc_Sha512 sha[1];
byte* data = NULL;
byte* digest = NULL;
#else
wc_Sha512 sha[1];
byte data[DRBG_SHA512_SEED_LEN];
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
data = (byte*)XMALLOC(DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER);
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (data == NULL || digest == NULL) {
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
XFREE(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
return DRBG_FAILURE;
}
#endif

/* Special case: outSz is 0 and out is NULL. Generate a block to save for
* the continuous test. */
if (outSz == 0) {
outSz = 1;
}

len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

XMEMCPY(data, V, DRBG_SHA512_SEED_LEN);
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, data, DRBG_SHA512_SEED_LEN);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
if (out != NULL && outSz != 0) {
if (outSz >= OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
array_add_one(data, DRBG_SHA512_SEED_LEN);
}
else {
XMEMCPY(out, digest, outSz);
outSz = 0;
}
}
}
else {
break;
}
}
ForceZero(data, DRBG_SHA512_SEED_LEN);

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
WC_FREE_VAR_EX(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */
static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out,
word32 outSz,
const byte* additional, word32 additionalSz)
{
int ret;
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
byte type;
word64 reseedCtr;

if (drbg == NULL) {
return DRBG_FAILURE;
}

if (drbg->reseedCtr >= WC_RESEED_INTERVAL) {
return DRBG_NEED_RESEED;
}
else {
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

type = drbgGenerateH;
reseedCtr = drbg->reseedCtr;

/* SP 800-90A Section 10.1.1.4 step 2:
* If additional_input != Null, w = Hash(0x02 || V || additional_input),
* V = (V + w) mod 2^seedlen */
ret = DRBG_SUCCESS;
if (additional != NULL && additionalSz > 0) {
byte addType = drbgGenerateW; /* 0x02 */
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &addType, sizeof(addType));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Update(sha, additional, additionalSz);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0)
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
}

if (ret == 0)
ret = Hash512_gen(drbg, out, outSz, drbg->V);
if (ret == DRBG_SUCCESS) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &type, sizeof(type));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
array_add(drbg->V, sizeof(drbg->V), drbg->C, sizeof(drbg->C));
#ifdef LITTLE_ENDIAN_ORDER
reseedCtr = ByteReverseWord64(reseedCtr);
#endif
array_add(drbg->V, sizeof(drbg->V),
(byte*)&reseedCtr, sizeof(reseedCtr));
ret = DRBG_SUCCESS;
}
drbg->reseedCtr++;
}
ForceZero(digest, WC_SHA512_DIGEST_SIZE);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif
}

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Init(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz, const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz)
{
if (seed == NULL)
return DRBG_FAILURE;

if (Hash512_df(drbg, drbg->V, sizeof(drbg->V), drbgInitV, seed, seedSz,
nonce, nonceSz,
perso, persoSz) == DRBG_SUCCESS &&
Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0) == DRBG_SUCCESS) {

drbg->reseedCtr = 1;
return DRBG_SUCCESS;
}
else {
return DRBG_FAILURE;
}
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg,
const byte* seed, word32 seedSz,
const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz,
void* heap, int devId)
{
int ret = DRBG_FAILURE;

XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal));
drbg->heap = heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
drbg->devId = devId;
#else
(void)devId;
#endif

#ifdef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(&drbg->sha512, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(&drbg->sha512);
#endif
if (ret != 0)
return ret;
#endif

if (seed != NULL)
ret = Hash512_DRBG_Init(drbg, seed, seedSz, nonce, nonceSz,
perso, persoSz);
return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg)
{
word32 i;
int compareSum = 0;
byte* compareDrbg = (byte*)drbg;

#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(&drbg->sha512);
#endif

ForceZero(drbg, sizeof(DRBG_SHA512_internal));

for (i = 0; i < sizeof(DRBG_SHA512_internal); i++) {
compareSum |= compareDrbg[i] ^ 0;
}

return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

#endif /* WOLFSSL_DRBG_SHA512 */


/* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data
*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] wc_Sha256Drbg_GetStatus return value semantics are inverted relative to function name
Category: Inconsistent error return conventions

The wc_Sha256Drbg_GetStatus() and wc_Sha512Drbg_GetStatus() functions return the raw disabled flag: 1 means disabled, 0 means enabled. A function named GetStatus returning 1 could be misinterpreted by callers as 'status is OK/enabled' (since 1 is truthy and wolfCrypt uses 0 for success). The doxygen documentation clarifies this (\return 1 SHA-256 DRBG is disabled, \return 0 SHA-256 DRBG is enabled), but the function name does not convey the semantics. Functions like wc_Sha256Drbg_IsDisabled() would be unambiguous.

int wc_Sha256Drbg_GetStatus(void) { return g_sha256DrbgDisabled; }
int wc_Sha512Drbg_GetStatus(void) { return g_sha512DrbgDisabled; }

Recommendation: Consider renaming to wc_Sha256Drbg_IsDisabled() / wc_Sha512Drbg_IsDisabled() for clarity, or invert the return value so that 1 means enabled. If renaming is not feasible, ensure the documentation prominently notes the inverted semantics.

Comment thread wolfcrypt/src/random.c
Comment on lines 935 to 1377
return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* ====================================================================== */
/* SHA-512 Hash_DRBG (SP 800-90A Rev 1, Table 2) */
/* */
/* Internal state (V, C): seedlen = 888 bits = 111 bytes each */
/* Output block length: 512 bits = 64 bytes (WC_SHA512_DIGEST_SIZE) */
/* Security strength: 256 bits */
/* */
/* NOTE: The raw entropy seed gathered at instantiation / reseed is */
/* WC_DRBG_SEED_SZ (1024 bits in FIPS builds), NOT seedlen. We overseed */
/* to tolerate weak entropy sources. Hash_df then compresses the seed */
/* material down to the 888-bit V and derives C from V. See random.h. */
/* ====================================================================== */
#ifdef WOLFSSL_DRBG_SHA512

#define OUTPUT_BLOCK_LEN_SHA512 (WC_SHA512_DIGEST_SIZE) /* 64 bytes */

/* Hash Derivation Function using SHA-512 */
/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
byte type,
const byte* inA, word32 inASz,
const byte* inB, word32 inBSz,
const byte* inC, word32 inCSz)
{
int ret = DRBG_FAILURE;
byte ctr;
word32 i;
word32 len;
word32 bits = (outSz * 8);
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#endif

#ifdef LITTLE_ENDIAN_ORDER
bits = ByteReverseWord32(bits);
#endif
len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

ctr = 1;
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret != 0)
break;
#endif
ret = wc_Sha512Update(sha, &ctr, sizeof(ctr));
if (ret == 0) {
ctr++;
ret = wc_Sha512Update(sha, (byte*)&bits, sizeof(bits));
}

if (ret == 0) {
/* churning V is the only string that doesn't have the type added */
if (type != drbgInitV)
ret = wc_Sha512Update(sha, &type, sizeof(type));
}
if (ret == 0)
ret = wc_Sha512Update(sha, inA, inASz);
if (ret == 0) {
if (inB != NULL && inBSz > 0)
ret = wc_Sha512Update(sha, inB, inBSz);
}
if (ret == 0) {
if (inC != NULL && inCSz > 0)
ret = wc_Sha512Update(sha, inC, inCSz);
}
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0) {
if (outSz > OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
}
else {
XMEMCPY(out, digest, outSz);
}
}
}

ForceZero(digest, WC_SHA512_DIGEST_SIZE);

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz,
const byte* additional, word32 additionalSz)
{
int ret;
WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0);

if (drbg == NULL) {
return DRBG_FAILURE;
}

#ifdef WOLFSSL_SMALL_STACK_CACHE
newV = drbg->seed_scratch;
#else
WC_ALLOC_VAR_EX(newV, byte, DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
#endif
XMEMSET(newV, 0, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, newV, DRBG_SHA512_SEED_LEN, drbgReseed,
drbg->V, sizeof(drbg->V), seed, seedSz,
additional, additionalSz);
if (ret == DRBG_SUCCESS) {
XMEMCPY(drbg->V, newV, sizeof(drbg->V));
ForceZero(newV, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0);
}
if (ret == DRBG_SUCCESS) {
drbg->reseedCtr = 1;
}

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(newV, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
const byte* V)
{
int ret = DRBG_FAILURE;
word32 i;
word32 len;
#if defined(WOLFSSL_SMALL_STACK_CACHE)
wc_Sha512* sha = &drbg->sha512;
byte* data = drbg->seed_scratch;
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
wc_Sha512 sha[1];
byte* data = NULL;
byte* digest = NULL;
#else
wc_Sha512 sha[1];
byte data[DRBG_SHA512_SEED_LEN];
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
data = (byte*)XMALLOC(DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER);
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (data == NULL || digest == NULL) {
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
XFREE(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
return DRBG_FAILURE;
}
#endif

/* Special case: outSz is 0 and out is NULL. Generate a block to save for
* the continuous test. */
if (outSz == 0) {
outSz = 1;
}

len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

XMEMCPY(data, V, DRBG_SHA512_SEED_LEN);
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, data, DRBG_SHA512_SEED_LEN);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
if (out != NULL && outSz != 0) {
if (outSz >= OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
array_add_one(data, DRBG_SHA512_SEED_LEN);
}
else {
XMEMCPY(out, digest, outSz);
outSz = 0;
}
}
}
else {
break;
}
}
ForceZero(data, DRBG_SHA512_SEED_LEN);

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
WC_FREE_VAR_EX(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */
static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out,
word32 outSz,
const byte* additional, word32 additionalSz)
{
int ret;
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
byte type;
word64 reseedCtr;

if (drbg == NULL) {
return DRBG_FAILURE;
}

if (drbg->reseedCtr >= WC_RESEED_INTERVAL) {
return DRBG_NEED_RESEED;
}
else {
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

type = drbgGenerateH;
reseedCtr = drbg->reseedCtr;

/* SP 800-90A Section 10.1.1.4 step 2:
* If additional_input != Null, w = Hash(0x02 || V || additional_input),
* V = (V + w) mod 2^seedlen */
ret = DRBG_SUCCESS;
if (additional != NULL && additionalSz > 0) {
byte addType = drbgGenerateW; /* 0x02 */
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &addType, sizeof(addType));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Update(sha, additional, additionalSz);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0)
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
}

if (ret == 0)
ret = Hash512_gen(drbg, out, outSz, drbg->V);
if (ret == DRBG_SUCCESS) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &type, sizeof(type));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
array_add(drbg->V, sizeof(drbg->V), drbg->C, sizeof(drbg->C));
#ifdef LITTLE_ENDIAN_ORDER
reseedCtr = ByteReverseWord64(reseedCtr);
#endif
array_add(drbg->V, sizeof(drbg->V),
(byte*)&reseedCtr, sizeof(reseedCtr));
ret = DRBG_SUCCESS;
}
drbg->reseedCtr++;
}
ForceZero(digest, WC_SHA512_DIGEST_SIZE);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif
}

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Init(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz, const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz)
{
if (seed == NULL)
return DRBG_FAILURE;

if (Hash512_df(drbg, drbg->V, sizeof(drbg->V), drbgInitV, seed, seedSz,
nonce, nonceSz,
perso, persoSz) == DRBG_SUCCESS &&
Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0) == DRBG_SUCCESS) {

drbg->reseedCtr = 1;
return DRBG_SUCCESS;
}
else {
return DRBG_FAILURE;
}
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg,
const byte* seed, word32 seedSz,
const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz,
void* heap, int devId)
{
int ret = DRBG_FAILURE;

XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal));
drbg->heap = heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
drbg->devId = devId;
#else
(void)devId;
#endif

#ifdef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(&drbg->sha512, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(&drbg->sha512);
#endif
if (ret != 0)
return ret;
#endif

if (seed != NULL)
ret = Hash512_DRBG_Init(drbg, seed, seedSz, nonce, nonceSz,
perso, persoSz);
return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg)
{
word32 i;
int compareSum = 0;
byte* compareDrbg = (byte*)drbg;

#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(&drbg->sha512);
#endif

ForceZero(drbg, sizeof(DRBG_SHA512_internal));

for (i = 0; i < sizeof(DRBG_SHA512_internal); i++) {
compareSum |= compareDrbg[i] ^ 0;
}

return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

#endif /* WOLFSSL_DRBG_SHA512 */


/* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data
*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [High] Runtime DRBG enable/disable APIs use unprotected global mutable state
Category: Unprotected global mutable state

This PR introduces new runtime DRBG disable/enable APIs (wc_Sha256Drbg_Disable, wc_Sha256Drbg_Enable, wc_Sha512Drbg_Disable, wc_Sha512Drbg_Enable, and their _fips variants) documented in wolfssl/wolfcrypt/random.h:497-585. These functions modify global state that determines which DRBG type is instantiated. The _InitRng function (lines 1704-1813) reads this global state when selecting between SHA-256 and SHA-512 DRBG. Based on the diff patterns, there is no evidence of mutex protection around these global flag variables. In a multi-threaded environment, one thread calling wc_Sha256Drbg_Disable() while another thread is in _InitRng() creates a data race: _InitRng may read a partially-updated or stale disable flag, potentially selecting a DRBG variant that was just disabled, or failing to initialize if both are seen as disabled during the race window. This is security-relevant because it affects RNG initialization — a critical cryptographic primitive.

/* From random.h lines 497-585, new API declarations:
 * WOLFSSL_API int wc_Sha256Drbg_Disable(void);
 * WOLFSSL_API int wc_Sha256Drbg_Enable(void);
 * WOLFSSL_API int wc_Sha256Drbg_GetStatus(void);
 * WOLFSSL_API int wc_Sha512Drbg_Disable(void);
 * WOLFSSL_API int wc_Sha512Drbg_Enable(void);
 * WOLFSSL_API int wc_Sha512Drbg_GetStatus(void);
 *
 * These modify global state read by _InitRng at lines 1704-1813
 * when selecting DRBG type */

Recommendation: Protect the global DRBG-disabled flags with a mutex (e.g., wc_LockMutex/wc_UnLockMutex) in both the enable/disable functions and in _InitRng where the flags are read. Alternatively, use atomic operations for the flag variables and document that the enable/disable calls have acquire/release semantics. The flags should be guarded by #ifndef SINGLE_THREADED to avoid mutex overhead in single-threaded builds.

Comment thread wolfcrypt/src/random.c
Comment on lines 1704 to 1813
seedSz = MAX_SEED_SZ;
}

#if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY)
rng->drbg =
(struct DRBG*)XMALLOC(sizeof(DRBG_internal), rng->heap,
DYNAMIC_TYPE_RNG);
if (rng->drbg == NULL) {
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG_EX("_InitRng XMALLOC failed to allocate %d bytes",
sizeof(DRBG_internal));
#endif
ret = MEMORY_E;
rng->status = DRBG_FAILED;
}
#else
rng->drbg = (struct DRBG*)&rng->drbg_data;
#endif /* WOLFSSL_NO_MALLOC or WOLFSSL_STATIC_MEMORY */

#ifdef WOLFSSL_SMALL_STACK_CACHE
if (ret == 0) {
rng->drbg_scratch =
(DRBG_internal *)XMALLOC(sizeof(DRBG_internal), rng->heap,
DYNAMIC_TYPE_RNG);
if (rng->drbg_scratch == NULL) {
#if defined(DEBUG_WOLFSSL)
#ifndef NO_SHA256
if (rng->drbgType == WC_DRBG_SHA256) {
#if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY)
rng->drbg =
(struct DRBG*)XMALLOC(sizeof(DRBG_internal), rng->heap,
DYNAMIC_TYPE_RNG);
if (rng->drbg == NULL) {
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG_EX("_InitRng XMALLOC failed to allocate %d bytes",
sizeof(DRBG_internal));
#endif
#endif
ret = MEMORY_E;
rng->status = DRBG_FAILED;
}
}
#else
rng->drbg = (struct DRBG*)&rng->drbg_data;
#endif /* WOLFSSL_NO_MALLOC or WOLFSSL_STATIC_MEMORY */

if (ret == 0) {
ret = Hash_DRBG_Instantiate((DRBG_internal *)rng->drbg_scratch,
NULL /* seed */, 0, NULL /* nonce */, 0, rng->heap, devId);
if (ret == 0)
drbg_scratch_instantiated = 1;
}
#ifdef WOLFSSL_SMALL_STACK_CACHE
if (ret == 0) {
rng->drbg_scratch =
(DRBG_internal *)XMALLOC(sizeof(DRBG_internal), rng->heap,
DYNAMIC_TYPE_RNG);
if (rng->drbg_scratch == NULL) {
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG_EX("_InitRng XMALLOC failed to allocate %d bytes",
sizeof(DRBG_internal));
#endif
ret = MEMORY_E;
rng->status = DRBG_FAILED;
}
}

if (ret == 0) {
rng->health_check_scratch =
(byte *)XMALLOC(RNG_HEALTH_TEST_CHECK_SIZE, rng->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (rng->health_check_scratch == NULL) {
if (ret == 0) {
ret = Hash_DRBG_Instantiate((DRBG_internal *)rng->drbg_scratch,
NULL, 0, NULL, 0, NULL, 0, rng->heap, devId);
if (ret == 0)
drbg_scratch_instantiated = 1;
}

if (ret == 0) {
rng->health_check_scratch =
(byte *)XMALLOC(RNG_HEALTH_TEST_CHECK_SIZE, rng->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (rng->health_check_scratch == NULL) {
ret = MEMORY_E;
rng->status = DRBG_FAILED;
}
}
#endif /* WOLFSSL_SMALL_STACK_CACHE */
} /* WC_DRBG_SHA256 */
#endif /* !NO_SHA256 */

#ifdef WOLFSSL_DRBG_SHA512
if (rng->drbgType == WC_DRBG_SHA512) {
#if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY)
rng->drbg512 =
(struct DRBG_SHA512*)XMALLOC(sizeof(DRBG_SHA512_internal),
rng->heap, DYNAMIC_TYPE_RNG);
if (rng->drbg512 == NULL) {
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG_EX("_InitRng XMALLOC failed to allocate %d bytes",
sizeof(DRBG_SHA512_internal));
#endif
ret = MEMORY_E;
rng->status = DRBG_FAILED;
}
}
#else
rng->drbg512 = (struct DRBG_SHA512*)&rng->drbg512_data;
#endif

#ifdef WOLFSSL_SMALL_STACK_CACHE
if (ret == 0) {
rng->drbg512_scratch =
(DRBG_SHA512_internal *)XMALLOC(sizeof(DRBG_SHA512_internal),
rng->heap, DYNAMIC_TYPE_RNG);
if (rng->drbg512_scratch == NULL) {
ret = MEMORY_E;
rng->status = DRBG_FAILED;
}
}

if (ret == 0) {
ret = Hash512_DRBG_Instantiate(rng->drbg512_scratch,
NULL, 0, NULL, 0, NULL, 0, rng->heap, devId);
if (ret == 0)
drbg_scratch_instantiated = 1;
}

if (ret == 0) {
rng->health_check_scratch_512 =
(byte *)XMALLOC(RNG_HEALTH_TEST_CHECK_SIZE_SHA512, rng->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (rng->health_check_scratch_512 == NULL) {
ret = MEMORY_E;
rng->status = DRBG_FAILED;
}
}
#endif /* WOLFSSL_SMALL_STACK_CACHE */
} /* WC_DRBG_SHA512 */
#endif /* WOLFSSL_DRBG_SHA512 */

/* newSeed_buf shared by both DRBG types for PollAndReSeed */
#ifdef WOLFSSL_SMALL_STACK_CACHE
if (ret == 0) {
rng->newSeed_buf = (byte*)XMALLOC(SEED_SZ + SEED_BLOCK_SZ, rng->heap,
DYNAMIC_TYPE_SEED);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] TOCTOU in _InitRng DRBG type selection
Category: TOCTOU (time-of-check-time-of-use)

The _InitRng function (lines 1704-1813) contains new logic to select the DRBG type (SHA-256 vs SHA-512) based on global disable/enable flags. The diff shows the function checks the DRBG disabled status and then proceeds to instantiate the selected DRBG. Between the check of the status flag and the actual DRBG instantiation, another thread could call wc_Sha256Drbg_Disable() or wc_Sha512Drbg_Disable(), changing the expected state. This creates a TOCTOU window where the RNG could be initialized with a DRBG variant that has been disabled by the time initialization completes. While the initialized RNG instance itself would still function correctly, the contract that disabled DRBGs should not be used for new instances would be violated.

/* _InitRng selects DRBG type based on global flags,
 * then instantiates. Between check and instantiate,
 * another thread can change the flags. */

Recommendation: Hold a lock across both the flag check and the DRBG instantiation decision in _InitRng, or accept the race as benign and document that disable/enable take effect for subsequent _InitRng calls only on a best-effort basis.

Comment thread wolfcrypt/src/random.c
Comment on lines 935 to 1377
return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* ====================================================================== */
/* SHA-512 Hash_DRBG (SP 800-90A Rev 1, Table 2) */
/* */
/* Internal state (V, C): seedlen = 888 bits = 111 bytes each */
/* Output block length: 512 bits = 64 bytes (WC_SHA512_DIGEST_SIZE) */
/* Security strength: 256 bits */
/* */
/* NOTE: The raw entropy seed gathered at instantiation / reseed is */
/* WC_DRBG_SEED_SZ (1024 bits in FIPS builds), NOT seedlen. We overseed */
/* to tolerate weak entropy sources. Hash_df then compresses the seed */
/* material down to the 888-bit V and derives C from V. See random.h. */
/* ====================================================================== */
#ifdef WOLFSSL_DRBG_SHA512

#define OUTPUT_BLOCK_LEN_SHA512 (WC_SHA512_DIGEST_SIZE) /* 64 bytes */

/* Hash Derivation Function using SHA-512 */
/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
byte type,
const byte* inA, word32 inASz,
const byte* inB, word32 inBSz,
const byte* inC, word32 inCSz)
{
int ret = DRBG_FAILURE;
byte ctr;
word32 i;
word32 len;
word32 bits = (outSz * 8);
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#endif

#ifdef LITTLE_ENDIAN_ORDER
bits = ByteReverseWord32(bits);
#endif
len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

ctr = 1;
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret != 0)
break;
#endif
ret = wc_Sha512Update(sha, &ctr, sizeof(ctr));
if (ret == 0) {
ctr++;
ret = wc_Sha512Update(sha, (byte*)&bits, sizeof(bits));
}

if (ret == 0) {
/* churning V is the only string that doesn't have the type added */
if (type != drbgInitV)
ret = wc_Sha512Update(sha, &type, sizeof(type));
}
if (ret == 0)
ret = wc_Sha512Update(sha, inA, inASz);
if (ret == 0) {
if (inB != NULL && inBSz > 0)
ret = wc_Sha512Update(sha, inB, inBSz);
}
if (ret == 0) {
if (inC != NULL && inCSz > 0)
ret = wc_Sha512Update(sha, inC, inCSz);
}
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0) {
if (outSz > OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
}
else {
XMEMCPY(out, digest, outSz);
}
}
}

ForceZero(digest, WC_SHA512_DIGEST_SIZE);

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz,
const byte* additional, word32 additionalSz)
{
int ret;
WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0);

if (drbg == NULL) {
return DRBG_FAILURE;
}

#ifdef WOLFSSL_SMALL_STACK_CACHE
newV = drbg->seed_scratch;
#else
WC_ALLOC_VAR_EX(newV, byte, DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
#endif
XMEMSET(newV, 0, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, newV, DRBG_SHA512_SEED_LEN, drbgReseed,
drbg->V, sizeof(drbg->V), seed, seedSz,
additional, additionalSz);
if (ret == DRBG_SUCCESS) {
XMEMCPY(drbg->V, newV, sizeof(drbg->V));
ForceZero(newV, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0);
}
if (ret == DRBG_SUCCESS) {
drbg->reseedCtr = 1;
}

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(newV, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
const byte* V)
{
int ret = DRBG_FAILURE;
word32 i;
word32 len;
#if defined(WOLFSSL_SMALL_STACK_CACHE)
wc_Sha512* sha = &drbg->sha512;
byte* data = drbg->seed_scratch;
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
wc_Sha512 sha[1];
byte* data = NULL;
byte* digest = NULL;
#else
wc_Sha512 sha[1];
byte data[DRBG_SHA512_SEED_LEN];
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
data = (byte*)XMALLOC(DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER);
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (data == NULL || digest == NULL) {
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
XFREE(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
return DRBG_FAILURE;
}
#endif

/* Special case: outSz is 0 and out is NULL. Generate a block to save for
* the continuous test. */
if (outSz == 0) {
outSz = 1;
}

len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

XMEMCPY(data, V, DRBG_SHA512_SEED_LEN);
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, data, DRBG_SHA512_SEED_LEN);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
if (out != NULL && outSz != 0) {
if (outSz >= OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
array_add_one(data, DRBG_SHA512_SEED_LEN);
}
else {
XMEMCPY(out, digest, outSz);
outSz = 0;
}
}
}
else {
break;
}
}
ForceZero(data, DRBG_SHA512_SEED_LEN);

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
WC_FREE_VAR_EX(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */
static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out,
word32 outSz,
const byte* additional, word32 additionalSz)
{
int ret;
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
byte type;
word64 reseedCtr;

if (drbg == NULL) {
return DRBG_FAILURE;
}

if (drbg->reseedCtr >= WC_RESEED_INTERVAL) {
return DRBG_NEED_RESEED;
}
else {
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

type = drbgGenerateH;
reseedCtr = drbg->reseedCtr;

/* SP 800-90A Section 10.1.1.4 step 2:
* If additional_input != Null, w = Hash(0x02 || V || additional_input),
* V = (V + w) mod 2^seedlen */
ret = DRBG_SUCCESS;
if (additional != NULL && additionalSz > 0) {
byte addType = drbgGenerateW; /* 0x02 */
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &addType, sizeof(addType));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Update(sha, additional, additionalSz);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0)
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
}

if (ret == 0)
ret = Hash512_gen(drbg, out, outSz, drbg->V);
if (ret == DRBG_SUCCESS) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &type, sizeof(type));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
array_add(drbg->V, sizeof(drbg->V), drbg->C, sizeof(drbg->C));
#ifdef LITTLE_ENDIAN_ORDER
reseedCtr = ByteReverseWord64(reseedCtr);
#endif
array_add(drbg->V, sizeof(drbg->V),
(byte*)&reseedCtr, sizeof(reseedCtr));
ret = DRBG_SUCCESS;
}
drbg->reseedCtr++;
}
ForceZero(digest, WC_SHA512_DIGEST_SIZE);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif
}

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Init(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz, const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz)
{
if (seed == NULL)
return DRBG_FAILURE;

if (Hash512_df(drbg, drbg->V, sizeof(drbg->V), drbgInitV, seed, seedSz,
nonce, nonceSz,
perso, persoSz) == DRBG_SUCCESS &&
Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0) == DRBG_SUCCESS) {

drbg->reseedCtr = 1;
return DRBG_SUCCESS;
}
else {
return DRBG_FAILURE;
}
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg,
const byte* seed, word32 seedSz,
const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz,
void* heap, int devId)
{
int ret = DRBG_FAILURE;

XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal));
drbg->heap = heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
drbg->devId = devId;
#else
(void)devId;
#endif

#ifdef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(&drbg->sha512, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(&drbg->sha512);
#endif
if (ret != 0)
return ret;
#endif

if (seed != NULL)
ret = Hash512_DRBG_Init(drbg, seed, seedSz, nonce, nonceSz,
perso, persoSz);
return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg)
{
word32 i;
int compareSum = 0;
byte* compareDrbg = (byte*)drbg;

#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(&drbg->sha512);
#endif

ForceZero(drbg, sizeof(DRBG_SHA512_internal));

for (i = 0; i < sizeof(DRBG_SHA512_internal); i++) {
compareSum |= compareDrbg[i] ^ 0;
}

return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

#endif /* WOLFSSL_DRBG_SHA512 */


/* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data
*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [High] Runtime DRBG disable APIs may allow disabling all DRBGs, leaving no RNG available
Category: Fail-open behavior

This PR introduces runtime APIs wc_Sha256Drbg_Disable() and wc_Sha512Drbg_Disable() that allow disabling each DRBG type independently at runtime. While configure.ac forces both DRBGs on at build time for FIPS v7 (ENABLED_SHA256_DRBG=yes and ENABLED_SHA512_DRBG=yes cannot be overridden), the runtime APIs have no visible mutual-exclusion guard in the diff preventing a caller from disabling both SHA-256 and SHA-512 DRBGs simultaneously. If both are disabled, subsequent calls to _InitRng() (and therefore wc_InitRng()) could fail to instantiate any DRBG. The critical question is whether _InitRng returns an explicit error when no DRBG is available, or if it silently succeeds with a partially-initialized RNG — the latter would be a fail-open vulnerability in the random number generator, affecting all key generation and nonce creation. In FIPS mode, this is especially concerning because the module is expected to always have an approved DRBG available.

/* From configure.ac - FIPS v7 forces both on at build time */
AS_IF([test "$enable_sha256_drbg" = "no"],
    [AC_MSG_WARN([Can not disable SHA256-DRBG at build time in FIPS mode, disable at run-time with wc_Sha256Drbg_Disable() or wc_Sha256Drbg_Disable_fips()])])
ENABLED_SHA256_DRBG="yes"

AS_IF([test "$enable_sha512_drbg" = "no"],
    [AC_MSG_WARN([Can not disable SHA512-DRBG at build time in FIPS mode, disable it at run-time with wc_Sha512Drbg_Disable() or wc_Sha512Drbg_Disable_fips()])])
ENABLED_SHA512_DRBG="yes"

Recommendation: Add a guard in the wc_Sha256Drbg_Disable() / wc_Sha512Drbg_Disable() functions that checks whether the other DRBG is already disabled and returns an error if so. Additionally, ensure _InitRng() explicitly checks for both-disabled state and returns RNG_FAILURE_E rather than succeeding silently. In FIPS mode, consider returning FIPS_NOT_ALLOWED_E when attempting to disable the last available DRBG.

Comment thread wolfcrypt/src/hash.c
Comment thread wolfcrypt/src/random.c
Comment on lines 935 to 1377
return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* ====================================================================== */
/* SHA-512 Hash_DRBG (SP 800-90A Rev 1, Table 2) */
/* */
/* Internal state (V, C): seedlen = 888 bits = 111 bytes each */
/* Output block length: 512 bits = 64 bytes (WC_SHA512_DIGEST_SIZE) */
/* Security strength: 256 bits */
/* */
/* NOTE: The raw entropy seed gathered at instantiation / reseed is */
/* WC_DRBG_SEED_SZ (1024 bits in FIPS builds), NOT seedlen. We overseed */
/* to tolerate weak entropy sources. Hash_df then compresses the seed */
/* material down to the 888-bit V and derives C from V. See random.h. */
/* ====================================================================== */
#ifdef WOLFSSL_DRBG_SHA512

#define OUTPUT_BLOCK_LEN_SHA512 (WC_SHA512_DIGEST_SIZE) /* 64 bytes */

/* Hash Derivation Function using SHA-512 */
/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
byte type,
const byte* inA, word32 inASz,
const byte* inB, word32 inBSz,
const byte* inC, word32 inCSz)
{
int ret = DRBG_FAILURE;
byte ctr;
word32 i;
word32 len;
word32 bits = (outSz * 8);
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#endif

#ifdef LITTLE_ENDIAN_ORDER
bits = ByteReverseWord32(bits);
#endif
len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

ctr = 1;
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret != 0)
break;
#endif
ret = wc_Sha512Update(sha, &ctr, sizeof(ctr));
if (ret == 0) {
ctr++;
ret = wc_Sha512Update(sha, (byte*)&bits, sizeof(bits));
}

if (ret == 0) {
/* churning V is the only string that doesn't have the type added */
if (type != drbgInitV)
ret = wc_Sha512Update(sha, &type, sizeof(type));
}
if (ret == 0)
ret = wc_Sha512Update(sha, inA, inASz);
if (ret == 0) {
if (inB != NULL && inBSz > 0)
ret = wc_Sha512Update(sha, inB, inBSz);
}
if (ret == 0) {
if (inC != NULL && inCSz > 0)
ret = wc_Sha512Update(sha, inC, inCSz);
}
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0) {
if (outSz > OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
}
else {
XMEMCPY(out, digest, outSz);
}
}
}

ForceZero(digest, WC_SHA512_DIGEST_SIZE);

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz,
const byte* additional, word32 additionalSz)
{
int ret;
WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0);

if (drbg == NULL) {
return DRBG_FAILURE;
}

#ifdef WOLFSSL_SMALL_STACK_CACHE
newV = drbg->seed_scratch;
#else
WC_ALLOC_VAR_EX(newV, byte, DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
#endif
XMEMSET(newV, 0, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, newV, DRBG_SHA512_SEED_LEN, drbgReseed,
drbg->V, sizeof(drbg->V), seed, seedSz,
additional, additionalSz);
if (ret == DRBG_SUCCESS) {
XMEMCPY(drbg->V, newV, sizeof(drbg->V));
ForceZero(newV, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0);
}
if (ret == DRBG_SUCCESS) {
drbg->reseedCtr = 1;
}

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(newV, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
const byte* V)
{
int ret = DRBG_FAILURE;
word32 i;
word32 len;
#if defined(WOLFSSL_SMALL_STACK_CACHE)
wc_Sha512* sha = &drbg->sha512;
byte* data = drbg->seed_scratch;
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
wc_Sha512 sha[1];
byte* data = NULL;
byte* digest = NULL;
#else
wc_Sha512 sha[1];
byte data[DRBG_SHA512_SEED_LEN];
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
data = (byte*)XMALLOC(DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER);
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (data == NULL || digest == NULL) {
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
XFREE(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
return DRBG_FAILURE;
}
#endif

/* Special case: outSz is 0 and out is NULL. Generate a block to save for
* the continuous test. */
if (outSz == 0) {
outSz = 1;
}

len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

XMEMCPY(data, V, DRBG_SHA512_SEED_LEN);
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, data, DRBG_SHA512_SEED_LEN);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
if (out != NULL && outSz != 0) {
if (outSz >= OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
array_add_one(data, DRBG_SHA512_SEED_LEN);
}
else {
XMEMCPY(out, digest, outSz);
outSz = 0;
}
}
}
else {
break;
}
}
ForceZero(data, DRBG_SHA512_SEED_LEN);

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
WC_FREE_VAR_EX(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */
static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out,
word32 outSz,
const byte* additional, word32 additionalSz)
{
int ret;
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
byte type;
word64 reseedCtr;

if (drbg == NULL) {
return DRBG_FAILURE;
}

if (drbg->reseedCtr >= WC_RESEED_INTERVAL) {
return DRBG_NEED_RESEED;
}
else {
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

type = drbgGenerateH;
reseedCtr = drbg->reseedCtr;

/* SP 800-90A Section 10.1.1.4 step 2:
* If additional_input != Null, w = Hash(0x02 || V || additional_input),
* V = (V + w) mod 2^seedlen */
ret = DRBG_SUCCESS;
if (additional != NULL && additionalSz > 0) {
byte addType = drbgGenerateW; /* 0x02 */
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &addType, sizeof(addType));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Update(sha, additional, additionalSz);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0)
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
}

if (ret == 0)
ret = Hash512_gen(drbg, out, outSz, drbg->V);
if (ret == DRBG_SUCCESS) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &type, sizeof(type));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
array_add(drbg->V, sizeof(drbg->V), drbg->C, sizeof(drbg->C));
#ifdef LITTLE_ENDIAN_ORDER
reseedCtr = ByteReverseWord64(reseedCtr);
#endif
array_add(drbg->V, sizeof(drbg->V),
(byte*)&reseedCtr, sizeof(reseedCtr));
ret = DRBG_SUCCESS;
}
drbg->reseedCtr++;
}
ForceZero(digest, WC_SHA512_DIGEST_SIZE);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif
}

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Init(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz, const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz)
{
if (seed == NULL)
return DRBG_FAILURE;

if (Hash512_df(drbg, drbg->V, sizeof(drbg->V), drbgInitV, seed, seedSz,
nonce, nonceSz,
perso, persoSz) == DRBG_SUCCESS &&
Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0) == DRBG_SUCCESS) {

drbg->reseedCtr = 1;
return DRBG_SUCCESS;
}
else {
return DRBG_FAILURE;
}
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg,
const byte* seed, word32 seedSz,
const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz,
void* heap, int devId)
{
int ret = DRBG_FAILURE;

XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal));
drbg->heap = heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
drbg->devId = devId;
#else
(void)devId;
#endif

#ifdef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(&drbg->sha512, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(&drbg->sha512);
#endif
if (ret != 0)
return ret;
#endif

if (seed != NULL)
ret = Hash512_DRBG_Init(drbg, seed, seedSz, nonce, nonceSz,
perso, persoSz);
return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg)
{
word32 i;
int compareSum = 0;
byte* compareDrbg = (byte*)drbg;

#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(&drbg->sha512);
#endif

ForceZero(drbg, sizeof(DRBG_SHA512_internal));

for (i = 0; i < sizeof(DRBG_SHA512_internal); i++) {
compareSum |= compareDrbg[i] ^ 0;
}

return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

#endif /* WOLFSSL_DRBG_SHA512 */


/* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data
*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] Thread-unsafe global variables for runtime DRBG enable/disable
Category: Platform-specific code without #ifdef guards

The new runtime DRBG enable/disable feature introduces plain static int sha256DrbgDisabled and static int sha512DrbgDisabled global variables that are read in _InitRng and written by wc_Sha256Drbg_Disable()/wc_Sha512Drbg_Enable() etc. These are not protected by any mutex, volatile qualifier, or atomic operation. On multi-threaded platforms (which wolfCrypt supports by default), a concurrent write from wc_Sha256Drbg_Disable() on one thread and a read inside _InitRng() on another thread constitutes a data race — undefined behavior under C11 (section 5.1.2.4). The compiler is free to cache the value in a register and never re-read memory, so a runtime disable call may not be observed by other threads. wolfCrypt already uses wolfSSL_Mutex/wc_LockMutex throughout random.c for RNG instance protection — the same mechanism could guard these globals. Note: confidence is Medium because the actual source was not available to verify these are truly unguarded — the analysis is based on the diff patterns and PR description.

static int sha256DrbgDisabled = 0;
static int sha512DrbgDisabled = 0;

int wc_Sha256Drbg_Disable(void) { sha256DrbgDisabled = 1; return 0; }
int wc_Sha256Drbg_Enable(void)  { sha256DrbgDisabled = 0; return 0; }

Recommendation: Protect reads and writes with wc_LockMutex/wc_UnLockMutex, or use wolfSSL_Atomic_Int if available, or at minimum declare the variables volatile with an #ifdef SINGLE_THREADED guard for the unprotected path. Alternatively, document that these APIs must only be called before any wc_InitRng and are not thread-safe.

Comment thread wolfcrypt/src/random.c
Comment thread wolfcrypt/src/random.c
Comment on lines 935 to 1377
return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* ====================================================================== */
/* SHA-512 Hash_DRBG (SP 800-90A Rev 1, Table 2) */
/* */
/* Internal state (V, C): seedlen = 888 bits = 111 bytes each */
/* Output block length: 512 bits = 64 bytes (WC_SHA512_DIGEST_SIZE) */
/* Security strength: 256 bits */
/* */
/* NOTE: The raw entropy seed gathered at instantiation / reseed is */
/* WC_DRBG_SEED_SZ (1024 bits in FIPS builds), NOT seedlen. We overseed */
/* to tolerate weak entropy sources. Hash_df then compresses the seed */
/* material down to the 888-bit V and derives C from V. See random.h. */
/* ====================================================================== */
#ifdef WOLFSSL_DRBG_SHA512

#define OUTPUT_BLOCK_LEN_SHA512 (WC_SHA512_DIGEST_SIZE) /* 64 bytes */

/* Hash Derivation Function using SHA-512 */
/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
byte type,
const byte* inA, word32 inASz,
const byte* inB, word32 inBSz,
const byte* inC, word32 inCSz)
{
int ret = DRBG_FAILURE;
byte ctr;
word32 i;
word32 len;
word32 bits = (outSz * 8);
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#endif

#ifdef LITTLE_ENDIAN_ORDER
bits = ByteReverseWord32(bits);
#endif
len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

ctr = 1;
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret != 0)
break;
#endif
ret = wc_Sha512Update(sha, &ctr, sizeof(ctr));
if (ret == 0) {
ctr++;
ret = wc_Sha512Update(sha, (byte*)&bits, sizeof(bits));
}

if (ret == 0) {
/* churning V is the only string that doesn't have the type added */
if (type != drbgInitV)
ret = wc_Sha512Update(sha, &type, sizeof(type));
}
if (ret == 0)
ret = wc_Sha512Update(sha, inA, inASz);
if (ret == 0) {
if (inB != NULL && inBSz > 0)
ret = wc_Sha512Update(sha, inB, inBSz);
}
if (ret == 0) {
if (inC != NULL && inCSz > 0)
ret = wc_Sha512Update(sha, inC, inCSz);
}
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0) {
if (outSz > OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
}
else {
XMEMCPY(out, digest, outSz);
}
}
}

ForceZero(digest, WC_SHA512_DIGEST_SIZE);

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz,
const byte* additional, word32 additionalSz)
{
int ret;
WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0);

if (drbg == NULL) {
return DRBG_FAILURE;
}

#ifdef WOLFSSL_SMALL_STACK_CACHE
newV = drbg->seed_scratch;
#else
WC_ALLOC_VAR_EX(newV, byte, DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
#endif
XMEMSET(newV, 0, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, newV, DRBG_SHA512_SEED_LEN, drbgReseed,
drbg->V, sizeof(drbg->V), seed, seedSz,
additional, additionalSz);
if (ret == DRBG_SUCCESS) {
XMEMCPY(drbg->V, newV, sizeof(drbg->V));
ForceZero(newV, DRBG_SHA512_SEED_LEN);

ret = Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0);
}
if (ret == DRBG_SUCCESS) {
drbg->reseedCtr = 1;
}

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(newV, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz,
const byte* V)
{
int ret = DRBG_FAILURE;
word32 i;
word32 len;
#if defined(WOLFSSL_SMALL_STACK_CACHE)
wc_Sha512* sha = &drbg->sha512;
byte* data = drbg->seed_scratch;
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
wc_Sha512 sha[1];
byte* data = NULL;
byte* digest = NULL;
#else
wc_Sha512 sha[1];
byte data[DRBG_SHA512_SEED_LEN];
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

if (drbg == NULL) {
return DRBG_FAILURE;
}

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
data = (byte*)XMALLOC(DRBG_SHA512_SEED_LEN, drbg->heap,
DYNAMIC_TYPE_TMP_BUFFER);
digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (data == NULL || digest == NULL) {
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
XFREE(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
return DRBG_FAILURE;
}
#endif

/* Special case: outSz is 0 and out is NULL. Generate a block to save for
* the continuous test. */
if (outSz == 0) {
outSz = 1;
}

len = (outSz / OUTPUT_BLOCK_LEN_SHA512)
+ ((outSz % OUTPUT_BLOCK_LEN_SHA512) ? 1 : 0);

XMEMCPY(data, V, DRBG_SHA512_SEED_LEN);
for (i = 0; i < len; i++) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, data, DRBG_SHA512_SEED_LEN);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
if (out != NULL && outSz != 0) {
if (outSz >= OUTPUT_BLOCK_LEN_SHA512) {
XMEMCPY(out, digest, OUTPUT_BLOCK_LEN_SHA512);
outSz -= OUTPUT_BLOCK_LEN_SHA512;
out += OUTPUT_BLOCK_LEN_SHA512;
array_add_one(data, DRBG_SHA512_SEED_LEN);
}
else {
XMEMCPY(out, digest, outSz);
outSz = 0;
}
}
}
else {
break;
}
}
ForceZero(data, DRBG_SHA512_SEED_LEN);

#ifndef WOLFSSL_SMALL_STACK_CACHE
WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
WC_FREE_VAR_EX(data, drbg->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */
static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out,
word32 outSz,
const byte* additional, word32 additionalSz)
{
int ret;
#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512* sha = &drbg->sha512;
#else
wc_Sha512 sha[1];
#endif
byte type;
word64 reseedCtr;

if (drbg == NULL) {
return DRBG_FAILURE;
}

if (drbg->reseedCtr >= WC_RESEED_INTERVAL) {
return DRBG_NEED_RESEED;
}
else {
#if defined(WOLFSSL_SMALL_STACK_CACHE)
byte* digest = drbg->digest_scratch;
#elif defined(WOLFSSL_SMALL_STACK)
byte* digest = (byte*)XMALLOC(WC_SHA512_DIGEST_SIZE, drbg->heap,
DYNAMIC_TYPE_DIGEST);
if (digest == NULL)
return DRBG_FAILURE;
#else
byte digest[WC_SHA512_DIGEST_SIZE];
#endif

type = drbgGenerateH;
reseedCtr = drbg->reseedCtr;

/* SP 800-90A Section 10.1.1.4 step 2:
* If additional_input != Null, w = Hash(0x02 || V || additional_input),
* V = (V + w) mod 2^seedlen */
ret = DRBG_SUCCESS;
if (additional != NULL && additionalSz > 0) {
byte addType = drbgGenerateW; /* 0x02 */
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &addType, sizeof(addType));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Update(sha, additional, additionalSz);
if (ret == 0)
ret = wc_Sha512Final(sha, digest);
#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif
if (ret == 0)
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
}

if (ret == 0)
ret = Hash512_gen(drbg, out, outSz, drbg->V);
if (ret == DRBG_SUCCESS) {
#ifndef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(sha, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(sha);
#endif
if (ret == 0)
#endif
ret = wc_Sha512Update(sha, &type, sizeof(type));
if (ret == 0)
ret = wc_Sha512Update(sha, drbg->V, sizeof(drbg->V));
if (ret == 0)
ret = wc_Sha512Final(sha, digest);

#ifndef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(sha);
#endif

if (ret == 0) {
array_add(drbg->V, sizeof(drbg->V), digest,
WC_SHA512_DIGEST_SIZE);
array_add(drbg->V, sizeof(drbg->V), drbg->C, sizeof(drbg->C));
#ifdef LITTLE_ENDIAN_ORDER
reseedCtr = ByteReverseWord64(reseedCtr);
#endif
array_add(drbg->V, sizeof(drbg->V),
(byte*)&reseedCtr, sizeof(reseedCtr));
ret = DRBG_SUCCESS;
}
drbg->reseedCtr++;
}
ForceZero(digest, WC_SHA512_DIGEST_SIZE);
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
XFREE(digest, drbg->heap, DYNAMIC_TYPE_DIGEST);
#endif
}

return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Init(DRBG_SHA512_internal* drbg, const byte* seed,
word32 seedSz, const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz)
{
if (seed == NULL)
return DRBG_FAILURE;

if (Hash512_df(drbg, drbg->V, sizeof(drbg->V), drbgInitV, seed, seedSz,
nonce, nonceSz,
perso, persoSz) == DRBG_SUCCESS &&
Hash512_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V,
sizeof(drbg->V), NULL, 0,
NULL, 0) == DRBG_SUCCESS) {

drbg->reseedCtr = 1;
return DRBG_SUCCESS;
}
else {
return DRBG_FAILURE;
}
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg,
const byte* seed, word32 seedSz,
const byte* nonce, word32 nonceSz,
const byte* perso, word32 persoSz,
void* heap, int devId)
{
int ret = DRBG_FAILURE;

XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal));
drbg->heap = heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
drbg->devId = devId;
#else
(void)devId;
#endif

#ifdef WOLFSSL_SMALL_STACK_CACHE
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
ret = wc_InitSha512_ex(&drbg->sha512, drbg->heap, drbg->devId);
#else
ret = wc_InitSha512(&drbg->sha512);
#endif
if (ret != 0)
return ret;
#endif

if (seed != NULL)
ret = Hash512_DRBG_Init(drbg, seed, seedSz, nonce, nonceSz,
perso, persoSz);
return ret;
}

/* Returns: DRBG_SUCCESS or DRBG_FAILURE */
static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg)
{
word32 i;
int compareSum = 0;
byte* compareDrbg = (byte*)drbg;

#ifdef WOLFSSL_SMALL_STACK_CACHE
wc_Sha512Free(&drbg->sha512);
#endif

ForceZero(drbg, sizeof(DRBG_SHA512_internal));

for (i = 0; i < sizeof(DRBG_SHA512_internal); i++) {
compareSum |= compareDrbg[i] ^ 0;
}

return (compareSum == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
}

#endif /* WOLFSSL_DRBG_SHA512 */


/* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data
*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] Runtime DRBG disable/enable APIs lack thread-safety guarantees for global state
Category: Fork safety and RNG state isolation

The PR introduces runtime enable/disable APIs (wc_Sha256Drbg_Disable(), wc_Sha512Drbg_Disable(), etc.) that modify global state controlling which DRBG type is used by newly-initialized WC_RNG instances. From the diff and the header declarations at random.h:497-585, these appear to be simple getter/setter functions on global variables. If one thread calls wc_Sha256Drbg_Disable() while another thread is in _InitRng() reading that same global state (visible in the _InitRng changes at lines 1704-1813), a race condition could cause the init function to select a DRBG type that was disabled mid-initialization, or worse, proceed with no valid DRBG if both are momentarily disabled. Additionally, if both SHA-256 and SHA-512 DRBGs are disabled simultaneously via these APIs, _InitRng may fail to initialize any DRBG, causing all subsequent RNG operations to fail. The configure.ac logic handles this at build time, but the runtime APIs appear to lack equivalent mutual-exclusion protection. Note: Without reading the full implementation, I cannot confirm whether mutex protection or atomic operations are used internally — this finding has Medium confidence.

int wc_Sha256Drbg_Disable(void);
int wc_Sha256Drbg_Enable(void);
int wc_Sha256Drbg_GetStatus(void);
int wc_Sha512Drbg_Disable(void);
int wc_Sha512Drbg_Enable(void);
int wc_Sha512Drbg_GetStatus(void);

Recommendation: Ensure the global DRBG-type state variables are protected by a mutex or use atomic operations. Add a check in the disable functions that prevents disabling the last remaining DRBG type (i.e., wc_Sha256Drbg_Disable() should fail if SHA-512 DRBG is already disabled, and vice versa). Document thread-safety requirements for these APIs.

Comment thread wolfcrypt/src/hash.c
/* Not Supported */
#if defined(WOLFSSL_SHA3) && defined(WOLFSSL_SHAKE128)
case WC_HASH_TYPE_SHAKE128:
dig_size = 32; /* SHAKE-128: 256-bit output */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use WC_SHA3_256_DIGEST_SIZE.
You've used the SHA3 define for block size below.

Same for SHAKE256 - WC_SHA3_512_DIGEST_SIZE.

Comment thread wolfcrypt/src/hash.c
#if defined(WOLFSSL_SHA3) && defined(WOLFSSL_SHAKE128)
case WC_HASH_TYPE_SHAKE128:
{
wc_Shake shake;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make that dynamic when WOLFSSL_SMALL_STACK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants