Phase 2: PQ in boundary and SHA512 DRBG#9843
Phase 2: PQ in boundary and SHA512 DRBG#9843kaleb-himes wants to merge 1 commit intowolfSSL:masterfrom
Conversation
82c3c5c to
f8d12a1
Compare
There was a problem hiding this comment.
** 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
6f2187b to
9e0e11f
Compare
|
Going to add CAVP support for the perso string and additional Input also. Will keep as a draft for now. |
6043ac5 to
6b8c222
Compare
e88dd14 to
7f4b8fb
Compare
b6e3558 to
b64531e
Compare
b64531e to
0db89b3
Compare
0e0b546 to
1b4f4ba
Compare
13ef400 to
0c62c85
Compare
|
|
retest this please. |
0c62c85 to
b232b66
Compare
52fc40f to
99fef50
Compare
99fef50 to
1f27050
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
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.
| 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 | ||
| * |
There was a problem hiding this comment.
🟠 [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.
| 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 | ||
| * |
There was a problem hiding this comment.
🔵 [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.
| 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 | ||
| * |
There was a problem hiding this comment.
🔵 [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.
| 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 | ||
| * |
There was a problem hiding this comment.
🔴 [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.
| 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); |
There was a problem hiding this comment.
🟠 [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.
| 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 | ||
| * |
There was a problem hiding this comment.
🔴 [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.
| 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 | ||
| * |
There was a problem hiding this comment.
🟠 [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.
| 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 | ||
| * |
There was a problem hiding this comment.
🟠 [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.
| /* Not Supported */ | ||
| #if defined(WOLFSSL_SHA3) && defined(WOLFSSL_SHAKE128) | ||
| case WC_HASH_TYPE_SHAKE128: | ||
| dig_size = 32; /* SHAKE-128: 256-bit output */ |
There was a problem hiding this comment.
Use WC_SHA3_256_DIGEST_SIZE.
You've used the SHA3 define for block size below.
Same for SHAKE256 - WC_SHA3_512_DIGEST_SIZE.
| #if defined(WOLFSSL_SHA3) && defined(WOLFSSL_SHAKE128) | ||
| case WC_HASH_TYPE_SHAKE128: | ||
| { | ||
| wc_Shake shake; |
There was a problem hiding this comment.
Make that dynamic when WOLFSSL_SMALL_STACK.
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:
Testing
Many in-house FIPS custom scripts, optest app and harness.
Checklist