From 8ee768326916822bc3793544684ae4f75e0d3b58 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 14 Aug 2026 16:33:59 -0500 Subject: [PATCH 001/102] wolfcrypt/src/error.c, wolfssl/wolfcrypt/error-crypt.h: * add NO_DEFAULT_FOUND_E "No default object registered for request type". * add missing #include in WOLFSSL_DEBUG_TRACE_ERROR_CODES path. * add __func__ to __GNUC__ WC_ERR_TRACE(). wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/test/test.c: * add wc_rng_bank_inst.bank for parent access from the children. * add wc_rng_bank_inst_checkin(). * improve thread safety, error checking, and default bank support in wc_rng_bank_checkout(), wc_local_rng_bank_checkout_for_bankref(), wc_rng_bank_inst_reinit(), wc_rng_bank_seed(), wc_rng_bank_reseed(), and wc_InitRng_BankRef_local(). * orthogonalize common code in wc_InitRng_BankRef() and wc_rng_new_bankref() into wc_InitRng_BankRef_local(). wolfcrypt/test/test.c: * fix typo in PRINT_HEAP_CHECKPOINT(). * add wc_rng_bank_inst_checkin() tests to random_bank_test(), update expected failure codes, and remove obsolete test clauses. * add missing !HAVE_FIPS gates in cryptocb_test(). wolfcrypt/src/random.c: * in WC_RNG_BANK_SUPPORT variant of wc_RNG_GenerateBlock(), use the new wc_rng_bank_inst_checkin(), not wc_rng_bank_checkin(). * tweaks to WOLFSSL_DEBUG_TRACE_ERROR_CODES code wrappers to mollify clang-tidy and -Wconversion. * fix a spelling error in _InitRng(). linuxkm/lkcapi_sha_glue.c: * in wc_linuxkm_drbg_generate(), opportunistically reseed once half way to WC_RESEED_INTERVAL and wc_linuxkm_can_block(). Also properly inhibit the recovery call to wc_rng_bank_inst_reinit() if ! wc_linuxkm_can_block(). * refactor wc_mix_pool_bytes() to use wc_RNG_DRBG_Reseed(), and only on the CPU-local RNG. wolfcrypt/src/asn.c, wolfcrypt/src/curve25519.c, wolfcrypt/src/evp.c, wolfcrypt/src/pkcs7.c, wolfcrypt/src/pkcs12.c, wolfcrypt/src/srp.c: at each existing wc_InitRng(), attempt wc_InitRng_BankRef() if WC_RNG_BANK_DEFAULT_SUPPORT && WC_HAVE_RNG_BANKREF. --- linuxkm/lkcapi_sha_glue.c | 150 ++++++++++++------ wolfcrypt/src/asn.c | 16 +- wolfcrypt/src/curve25519.c | 16 +- wolfcrypt/src/evp.c | 30 +++- wolfcrypt/src/pkcs12.c | 9 +- wolfcrypt/src/pkcs7.c | 104 +++++++++++-- wolfcrypt/src/random.c | 21 +-- wolfcrypt/src/rng_bank.c | 289 ++++++++++++++++++++++------------- wolfcrypt/src/srp.c | 8 +- wolfcrypt/test/test.c | 56 +++++-- wolfssl/wolfcrypt/rng_bank.h | 6 + 11 files changed, 499 insertions(+), 206 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 928eabcd123..9126dc81e05 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -26,7 +26,7 @@ #error lkcapi_sha_glue.c included in non-LINUXKM_LKCAPI_REGISTER project. #endif -#if defined(WC_LINUXKM_C_FALLBACK_IN_SHIMS) && defined(USE_INTEL_SPEEDUP) +#if defined(WC_LINUXKM_C_FALLBACK_IN_SHIMS) && defined(USE_INTEL_SPEEDUP) && !defined(WC_DEBUG_FORCE_KERNEL_SETTINGS) #error SHA* WC_LINUXKM_C_FALLBACK_IN_SHIMS is not currently supported. #endif @@ -1927,7 +1927,7 @@ WC_MAYBE_UNUSED static int km_hmac_test_export_import( return ret; } -PRAGMA_DIAG_POP +PRAGMA_DIAG_POP /* -Wno-pointer-arith -Wno-nested-externs, for linux/list.h */ WC_MAYBE_UNUSED static int hmac_sha3_test_once(void) { static int once = 0; @@ -2238,10 +2238,10 @@ static struct wc_rng_bank_inst *linuxkm_get_drbg(struct wc_rng_bank *ctx) { return ret; } -static void linuxkm_put_drbg(struct wc_rng_bank *ctx, struct wc_rng_bank_inst **drbg) { - int ret = wc_rng_bank_checkin(ctx, drbg); +static void linuxkm_put_drbg(struct wc_rng_bank_inst **drbg) { + int ret = wc_rng_bank_inst_checkin(drbg); if (ret != 0) { - pr_err("ERROR: wc_rng_bank_checkin() in linuxkm_put_drbg() returned err %d.\n", ret); + pr_err("ERROR: wc_rng_bank_inst_checkin() in linuxkm_put_drbg() returned err %d.\n", ret); WC_DUMP_BACKTRACE_NONDEBUG; } } @@ -2284,6 +2284,46 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRef(WC_RNG* rng) { #endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT && HAVE_HASHDRBG */ +#if defined(WOLFSSL_DRBG_SHA512) && !defined(NO_SHA256) + /* Both DRBGs compiled in: dispatch on the runtime drbgType. */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((rng_ptr)->drbgType == WC_DRBG_SHA512) \ + ? ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + : ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + if ((rng_ptr)->drbgType == WC_DRBG_SHA512) \ + ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + = (val); \ + else \ + ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg == NULL && (rng_ptr)->drbg512 == NULL) +#elif defined(WOLFSSL_DRBG_SHA512) + /* SHA-512 DRBG only (NO_SHA256 defined); the SHA-256 struct and + * rng->drbg field do not exist in this build. */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg512 == NULL) +#else + /* SHA-256 DRBG only (the historical default). */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg == NULL) +#endif + static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, const u8 *src, unsigned int slen, u8 *dst, unsigned int dlen) @@ -2304,6 +2344,20 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, goto out; } } + else if ((! WC_RNG_BANK_DRBG_NULL(WC_RNG_BANK_INST_TO_RNG(drbg))) && + (WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)) > WC_RESEED_INTERVAL / 2) && + wc_linuxkm_can_block()) + { + byte scratch[4]; + word64 cur_counter = WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)); + WC_RNG_BANK_SET_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg), WC_RESEED_INTERVAL); + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(drbg), scratch, + (word32)sizeof scratch); + if ((ret != 0) && (WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)) >= WC_RESEED_INTERVAL)) { + WC_RNG_BANK_SET_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg), cur_counter + 1); + } + ForceZero(scratch, sizeof scratch); + } for (;;) { #define RNG_MAX_BLOCK_LEN_ROUNDED (RNG_MAX_BLOCK_LEN & ~0xfU) @@ -2335,8 +2389,10 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, break; retried = 1; - ret = wc_rng_bank_inst_reinit(ctx, - drbg, + if (! wc_linuxkm_can_block()) + break; + + ret = wc_rng_bank_inst_reinit(NULL, drbg, WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT); @@ -2360,7 +2416,7 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, out: - linuxkm_put_drbg(ctx, &drbg); + linuxkm_put_drbg(&drbg); return ret; } @@ -2596,15 +2652,22 @@ static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) { __builtin_unreachable(); } +/* Note, wc_mix_pool_bytes() only injects the supplied entropy into one RNG, + * CPU-local when uncontended. This routine can be pegged by unprivileged + * users, so its impact needs to stay as CPU-local as possible. */ static int wc_mix_pool_bytes(const void *buf, size_t len) { int ret; - struct wc_rng_bank *ctx; - size_t i; - int n; - int can_sleep = wc_linuxkm_can_block(); + struct wc_rng_bank *ctx = NULL; + word32 flags = + WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST; + struct wc_rng_bank_inst *drbg = NULL; + word64 cur_counter; - if (len == 0) - return 0; + if (len > WC_MAX_UINT_OF(word32)) + return -EFBIG; + + /* Continue even if len == 0 -- churning the DRBG is still meaningful. */ ret = wc_rng_bank_default_checkout(&ctx); if (ret) { @@ -2614,45 +2677,38 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { return -EFAULT; } - ret = 0; + if (wc_linuxkm_can_block()) + flags |= WC_RNG_BANK_FLAG_AFFINITY_LOCK; + else + flags |= WC_RNG_BANK_FLAG_NO_VECTOR_OPS; - for (n = ctx->n_rngs - 1; n >= 0; --n) { - struct wc_rng_bank_inst *drbg; + ret = wc_rng_bank_checkout(ctx, &drbg, 0, 0, flags); + if (ret != 0) { + ret = -EINVAL; + goto out; + } - int V_offset; + if (WC_RNG_BANK_DRBG_NULL(WC_RNG_BANK_INST_TO_RNG(drbg))) { + ret = 0; /* consistent with wc_RNG_DRBG_Reseed() behavior in RDRAND configs. */ + goto out; + } - if (wc_rng_bank_checkout(ctx, &drbg, n, 0, WC_RNG_BANK_FLAG_NONE) != 0) - continue; + cur_counter = WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)); -#ifdef WOLFSSL_DRBG_SHA512 - if (WC_RNG_BANK_INST_TO_RNG(drbg)->drbgType == WC_DRBG_SHA512) { - for (i = 0, V_offset = 0; i < len; ++i) { - ((struct DRBG_SHA512_internal *)WC_RNG_BANK_INST_TO_RNG(drbg)->drbg512)->V[V_offset++] += ((byte *)buf)[i]; - if (V_offset == (int)sizeof ((struct DRBG_SHA512_internal *)WC_RNG_BANK_INST_TO_RNG(drbg)->drbg512)->V) - V_offset = 0; - } - } - else -#endif /* WOLFSSL_DRBG_SHA512 */ - { - for (i = 0, V_offset = 0; i < len; ++i) { - ((struct DRBG_internal *)WC_RNG_BANK_INST_TO_RNG(drbg)->drbg)->V[V_offset++] += ((byte *)buf)[i]; - if (V_offset == (int)sizeof ((struct DRBG_internal *)WC_RNG_BANK_INST_TO_RNG(drbg)->drbg)->V) - V_offset = 0; - } - } + ret = wc_RNG_DRBG_Reseed(WC_RNG_BANK_INST_TO_RNG(drbg), buf, (word32)len); + if (ret != 0) + ret = -EINVAL; - wc_rng_bank_checkin(ctx, &drbg); - if (can_sleep) { - if (signal_pending(current)) { - ret = -EINTR; - break; - } - cond_resched(); - } - } + /* Unconditionally restore the reseed counter -- don't credit the + * contributed entropy. */ + WC_RNG_BANK_SET_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg), cur_counter); - (void)wc_rng_bank_default_checkin(&ctx); +out: + + if (drbg) + (void)wc_rng_bank_inst_checkin(&drbg); + if (ctx) + (void)wc_rng_bank_default_checkin(&ctx); return ret; } diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 2df6050a862..94f3405cb14 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -37829,11 +37829,17 @@ int InitOcspRequest(OcspRequest* req, DecodedCert* cert, byte useNonce, if (useNonce) { WC_RNG rng; - #ifndef HAVE_FIPS - ret = wc_InitRng_ex(&rng, req->heap, INVALID_DEVID); - #else - ret = wc_InitRng(&rng); - #endif +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + ret = wc_InitRng_ex(&rng, req->heap, INVALID_DEVID); + #else + ret = wc_InitRng(&rng); + #endif + } if (ret != 0) { WOLFSSL_MSG("\tCannot initialize RNG. Skipping the OCSP Nonce."); } else { diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 9cf5cb08d72..31d2d687342 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -237,7 +237,13 @@ static int curve25519_make_pub_ex(int public_size, byte* pub, int private_size, { WC_RNG rng; - ret = wc_InitRng(&rng); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng(&rng); + } if (ret == 0) { ret = curve25519_make_pub_blind_sw(public_size, pub, priv, &rng); @@ -557,7 +563,13 @@ int wc_curve25519_generic(int public_size, byte* pub, { WC_RNG rng; - ret = wc_InitRng(&rng); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng(&rng); + } if (ret == 0) { ret = curve25519_generic_blind_sw(pub, priv, basepoint, &rng); diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index ca0ccdd7432..41d359493cd 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -2853,7 +2853,16 @@ int wolfSSL_EVP_PKEY_derive(WOLFSSL_EVP_PKEY_CTX *ctx, unsigned char *key, size_ (defined(HAVE_FIPS_VERSION) && HAVE_FIPS_VERSION > 2)) WC_RNG rng; - if (wc_InitRng(&rng) != MP_OKAY) { + int ret; + +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng(&rng); + } + if (ret != 0) { WOLFSSL_MSG("Init RNG failed"); return WOLFSSL_FAILURE; } @@ -6755,7 +6764,14 @@ void wolfSSL_EVP_init(void) } /* arg is 4...(ctx->ivSz - 8) */ XMEMCPY(ctx->iv, ptr, (size_t)arg); - if (wc_InitRng(&rng) != 0) { +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng(&rng); + } + if (ret != 0) { WOLFSSL_MSG("wc_InitRng failed"); break; } @@ -12499,11 +12515,17 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_ex(void* heap) pkey->heap = heap; pkey->type = WOLFSSL_EVP_PKEY_DEFAULT; +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &pkey->rng); + if (ret != 0) +#endif + { #ifndef HAVE_FIPS - ret = wc_InitRng_ex(&pkey->rng, heap, INVALID_DEVID); + ret = wc_InitRng_ex(&pkey->rng, heap, INVALID_DEVID); #else - ret = wc_InitRng(&pkey->rng); + ret = wc_InitRng(&pkey->rng); #endif + } if (ret != 0){ /* Free directly since mutex for ref count not set yet */ XFREE(pkey, heap, DYNAMIC_TYPE_PUBLIC_KEY); diff --git a/wolfcrypt/src/pkcs12.c b/wolfcrypt/src/pkcs12.c index 67d55117882..f0eb94b2e47 100644 --- a/wolfcrypt/src/pkcs12.c +++ b/wolfcrypt/src/pkcs12.c @@ -2807,7 +2807,14 @@ WC_PKCS12* wc_PKCS12_create(char* pass, word32 passSz, char* name, WOLFSSL_ENTER("wc_PKCS12_create"); - if (wc_InitRng_ex(&rng, heap, INVALID_DEVID) != 0) { +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, heap, INVALID_DEVID); + } + if (ret != 0) { return NULL; } diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 6f63ba28354..2e8f8d223b4 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -4565,7 +4565,13 @@ int wc_PKCS7_EncodeSignedFPD(wc_PKCS7* pkcs7, byte* privateKey, content == NULL || contentSz == 0 || output == NULL || outputSz == 0) return BAD_FUNC_ARG; - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) return ret; @@ -4673,7 +4679,13 @@ int wc_PKCS7_EncodeSignedEncryptedFPD(wc_PKCS7* pkcs7, byte* encryptKey, XMEMCPY(encrypted, output, (word32)encryptedSz); ForceZero(output, outputSz); - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { ForceZero(encrypted, (word32)encryptedSz); XFREE(encrypted, pkcs7->heap, DYNAMIC_TYPE_PKCS7); @@ -4771,7 +4783,13 @@ int wc_PKCS7_EncodeSignedCompressedFPD(wc_PKCS7* pkcs7, byte* privateKey, XMEMCPY(compressed, output, compressedSz); ForceZero(output, outputSz); - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { ForceZero(compressed, compressedSz); XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); @@ -4907,7 +4925,13 @@ int wc_PKCS7_EncodeSignedEncryptedCompressedFPD(wc_PKCS7* pkcs7, byte* encryptK XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); ForceZero(output, outputSz); - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { ForceZero(encrypted, encryptedSz); XFREE(encrypted, pkcs7->heap, DYNAMIC_TYPE_PKCS7); @@ -8439,7 +8463,13 @@ static int PKCS7_GenerateContentEncryptionKey(wc_PKCS7* pkcs7, word32 len) XMEMSET(tmpKey, 0, len); - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { XFREE(tmpKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ret; @@ -8782,7 +8812,13 @@ static int wc_PKCS7_KariGenerateEphemeralKey(WC_PKCS7_KARI* kari) kari->senderKeyInit = 1; - ret = wc_InitRng_ex(&rng, kari->heap, kari->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, kari->heap, kari->devId); + } if (ret != 0) { XFREE(kari->senderKeyExport, kari->heap, DYNAMIC_TYPE_PKCS7); kari->senderKeyExportSz = 0; @@ -9658,7 +9694,13 @@ int wc_PKCS7_AddRecipient_KTRI(wc_PKCS7* pkcs7, const byte* cert, word32 certSz, return PUBLIC_KEY_E; } - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { wc_FreeRsaKey(pubKey); FreeDecodedCert(decoded); @@ -10451,7 +10493,13 @@ static int wc_PKCS7_GenerateBlock(wc_PKCS7* pkcs7, WC_RNG* rng, byte* out, if (rnd == NULL) return MEMORY_E; - ret = wc_InitRng_ex(rnd, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, rnd); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(rnd, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { XFREE(rnd, pkcs7->heap, DYNAMIC_TYPE_RNG); return ret; @@ -10731,7 +10779,13 @@ static int wc_PKCS7_PwriKek_KeyWrap(wc_PKCS7* pkcs7, const byte* kek, XMEMCPY(out + 4, cek, cekSz); /* random padding of size padSz */ - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) return ret; @@ -11538,7 +11592,13 @@ int wc_PKCS7_EncodeEnvelopedData(wc_PKCS7* pkcs7, byte* output, word32 outputSz) verSz = SetMyVersion((word32)kariVersion, ver, 0); - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { wc_PKCS7_FreeEncodedRecipientSet(pkcs7); return ret; @@ -11866,7 +11926,13 @@ static int wc_PKCS7_KtriFakeCEK(wc_PKCS7* pkcs7, const byte* encryptedKey, WC_ALLOC_VAR_EX(localRng, WC_RNG, 1, pkcs7->heap, DYNAMIC_TYPE_RNG, WC_FREE_VAR_EX(hmac, pkcs7->heap, DYNAMIC_TYPE_HMAC); return MEMORY_E); - ret = wc_InitRng_ex(localRng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, localRng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(localRng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { WC_FREE_VAR_EX(localRng, pkcs7->heap, DYNAMIC_TYPE_RNG); WC_FREE_VAR_EX(hmac, pkcs7->heap, DYNAMIC_TYPE_HMAC); @@ -12220,7 +12286,13 @@ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz, /* decrypt encryptedKey */ #ifdef WC_RSA_BLINDING - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + #if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) + #endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret == 0) { ret = wc_RsaSetRNG(privKey, &rng); } @@ -15376,7 +15448,13 @@ int wc_PKCS7_EncodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* output, #endif /* HAVE_AESCCM */ } - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + ret = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (ret != 0) +#endif + { + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + } if (ret != 0) { wc_PKCS7_FreeEncodedRecipientSet(pkcs7); return ret; diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index d7ae5ff465f..935af4b40f1 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -336,12 +336,15 @@ enum { CONST_NUM_ERR_DRBG_CONT_FAILURE = DRBG_CONT_FAILURE, CONST_NUM_ERR_DRBG_NO_SEED_CB = DRBG_NO_SEED_CB }; - #define DRBG_FAILURE WC_ERR_TRACE(DRBG_FAILURE) - #define DRBG_NEED_RESEED WC_ERR_TRACE(DRBG_NEED_RESEED) - #define DRBG_CONT_FAILURE WC_ERR_TRACE(DRBG_CONT_FAILURE) - #define DRBG_NO_SEED_CB WC_ERR_TRACE(DRBG_NO_SEED_CB) - #define WC_DRBG_FAILED WC_ERR_TRACE(WC_DRBG_FAILED) - #define WC_DRBG_CONT_FAILED WC_ERR_TRACE(WC_DRBG_CONT_FAILED) + /* DRBG_SUCCESS needs to be macroized to avoid "enumerated and + * non-enumerated type in conditional expression" in C++. */ + #define DRBG_SUCCESS (byte)DRBG_SUCCESS + #define DRBG_FAILURE (byte)WC_ERR_TRACE(DRBG_FAILURE) + #define DRBG_NEED_RESEED (byte)WC_ERR_TRACE(DRBG_NEED_RESEED) + #define DRBG_CONT_FAILURE (byte)WC_ERR_TRACE(DRBG_CONT_FAILURE) + #define DRBG_NO_SEED_CB (byte)WC_ERR_TRACE(DRBG_NO_SEED_CB) + #define WC_DRBG_FAILED (byte)WC_ERR_TRACE(WC_DRBG_FAILED) + #define WC_DRBG_CONT_FAILED (byte)WC_ERR_TRACE(WC_DRBG_CONT_FAILED) #endif /* RNG health states */ @@ -2015,7 +2018,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, #endif #ifdef HAVE_INTEL_RDRAND - /* if CPU supports RDRAND, use it directly and by-pass DRBG init */ + /* if CPU supports RDRAND, use it directly and bypass DRBG init */ if (IS_INTEL_RDRAND(intel_flags)) { #ifdef HAVE_HASHDRBG rng->status = DRBG_OK; @@ -2680,11 +2683,11 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) ret = wc_local_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(bank_inst), output, sz); { - int checkin_ret = wc_rng_bank_checkin(rng->bankref, &bank_inst); + int checkin_ret = wc_rng_bank_inst_checkin(&bank_inst); if (checkin_ret != 0) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( - "ERROR: wc_RNG_GenerateBlock() wc_rng_bank_checkin() " + "ERROR: wc_RNG_GenerateBlock() wc_rng_bank_inst_checkin() " "failed with err %d.", checkin_ret); #endif if (ret == 0) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index c17e30eda11..96dfe63a124 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -122,6 +122,7 @@ WOLFSSL_API int wc_rng_bank_init( int nretries = 0; #endif time_t ts1 = XTIME(0); + rng_inst->bank = ctx; for (;;) { time_t ts2; @@ -354,7 +355,7 @@ static struct wc_rng_bank * volatile default_rng_bank; WOLFSSL_API int wc_rng_bank_default_set(struct wc_rng_bank *bank) { int ret; struct wc_rng_bank *cur_default_rng_bank = NULL; - int new_refcount; + WC_ATOMIC_INT_ARG new_refcount; if (bank == NULL) return BAD_FUNC_ARG; @@ -399,6 +400,8 @@ WOLFSSL_API int wc_rng_bank_default_checkout(struct wc_rng_bank **bank) { if (bank == NULL) return BAD_FUNC_ARG; if (cur_default_rng_bank == NULL) + return NO_DEFAULT_FOUND_E; + else if (! (cur_default_rng_bank->flags & WC_RNG_BANK_FLAG_INITED)) return BAD_STATE_E; wolfSSL_RefInc_IfAtLeast(&cur_default_rng_bank->refcount, 2, &new_refcount, &ret); @@ -412,7 +415,7 @@ WOLFSSL_API int wc_rng_bank_default_checkout(struct wc_rng_bank **bank) { WOLFSSL_API int wc_rng_bank_default_checkin(struct wc_rng_bank **bank) { int ret; - int new_refcount; + WC_ATOMIC_INT_ARG new_refcount; if ((bank == NULL) || (*bank == NULL)) return BAD_FUNC_ARG; wolfSSL_RefDec2(&(*bank)->refcount, &new_refcount, &ret); @@ -437,7 +440,7 @@ WOLFSSL_API int wc_rng_bank_default_clear(struct wc_rng_bank *bank) { return BAD_FUNC_ARG; if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, (void **)&bank, NULL)) { int ret; - int new_refcount; + WC_ATOMIC_INT_ARG new_refcount; wolfSSL_RefDec2(&bank->refcount, &new_refcount, &ret); #ifdef WC_VERBOSE_RNG /* wc_rng_bank_fini() is the sole responsibility of the context that @@ -482,21 +485,39 @@ WOLFSSL_API int wc_rng_bank_checkout( int n_rngs_tried = 0; WC_ATOMIC_INT_ARG new_refcount; -#ifdef WC_RNG_BANK_DEFAULT_SUPPORT - if (bank == NULL) - bank = default_rng_bank; -#endif + if (rng_inst == NULL) + return BAD_FUNC_ARG; - if ((bank == NULL) || - (rng_inst == NULL)) - { + if (bank == NULL) { +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + ret = wc_rng_bank_default_checkout(&bank); + if (ret != 0) + return ret; + /* wc_rng_bank_default_checkout() increments bank->refcount, which we + * carry through below (no matching wc_rng_bank_default_checkin()). + */ +#else return BAD_FUNC_ARG; +#endif } + else { + if ((! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || + (wolfSSL_RefCur(bank->refcount) < 1)) + { + return BAD_STATE_E; + } - if ((! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || - (wolfSSL_RefCur(bank->refcount) < 1)) - { - return BAD_STATE_E; + /* Increment bank->refcount here speculatively to mitigate races with + * bank deallocation. + */ + wolfSSL_RefInc_IfAtLeast(&bank->refcount, 1, &new_refcount, &ret); + if (ret != 0) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "wc_rng_bank_checkout() called with refcount %d.\n", new_refcount); +#endif + return ret; + } } if ((flags & WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST) && @@ -507,19 +528,8 @@ WOLFSSL_API int wc_rng_bank_checkout( "BUG: wc_rng_bank_checkout() called with _PREFER_AFFINITY_INST but " "no _get_id_cb.\n"); #endif - return BAD_FUNC_ARG; - } - - /* Increment bank->refcount here speculatively, and assert on the resulting - * refcount, to mitigate races with bank deallocation. - */ - wolfSSL_RefInc_IfAtLeast(&bank->refcount, 1, &new_refcount, &ret); - if (ret != 0) { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "wc_rng_bank_checkout() called with refcount %d.\n", new_refcount); -#endif - return ret; + ret = BAD_FUNC_ARG; + goto out; } if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) @@ -545,11 +555,11 @@ WOLFSSL_API int wc_rng_bank_checkout( ret = bank->affinity_lock_cb(bank->cb_arg); if (ret == 0) new_lock_value |= WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; - else if ((ret == WC_NO_ERR_TRACE(ALREADY_E)) || - (ret == WC_NO_ERR_TRACE(WC_ACCEL_INHIBIT_E))) + else { + /* need to, and can, continue regardless of the error code from + * bank->affinity_lock_cb. */ ret = 0; - else - break; + } } if (flags & WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST) { @@ -680,13 +690,18 @@ WOLFSSL_API int wc_rng_bank_checkout( } } +out: + if (ret == 0) ret = RNG_FAILURE_E; if (new_lock_value & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) (void)bank->affinity_unlock_cb(bank->cb_arg); - /* Decrement the speculative refcount increment. */ + /* Decrement the speculative refcount increment. This also covers the + * refcount increment in wc_rng_bank_default_checkout() if that's how it was + * incremented. + */ { int refdec_err; wolfSSL_RefDec2(&bank->refcount, &new_refcount, &refdec_err); @@ -711,6 +726,8 @@ WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref( struct wc_rng_bank *bank, struct wc_rng_bank_inst **rng_inst) { + if (bank == NULL) + return BAD_FUNC_ARG; return wc_rng_bank_checkout( bank, rng_inst, 0, 0, WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | @@ -726,6 +743,10 @@ static WC_INLINE int rng_inst_matches_bank( { if ((bank == NULL) || (rng_inst == NULL)) return BAD_FUNC_ARG; + if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) + return BAD_STATE_E; + if (wolfSSL_RefCur(bank->refcount) < 2) + return BAD_STATE_E; #ifdef WC_RNG_BANK_STATIC if ((rng_inst >= &bank->rngs[0]) && (rng_inst <= &bank->rngs[WC_RNG_BANK_STATIC_SIZE - 1])) @@ -748,12 +769,12 @@ WOLFSSL_API int wc_rng_bank_checkin( int lockval; int ret; - if (rng_inst == NULL) + if ((rng_inst == NULL) || (*rng_inst == NULL)) return BAD_FUNC_ARG; #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (bank == NULL) - bank = default_rng_bank; + bank = (*rng_inst)->bank; #endif ret = rng_inst_matches_bank(bank, *rng_inst); @@ -794,6 +815,14 @@ WOLFSSL_API int wc_rng_bank_checkin( return ret; } +WOLFSSL_API int wc_rng_bank_inst_checkin( + struct wc_rng_bank_inst **rng_inst) +{ + if ((rng_inst == NULL) || (*rng_inst == NULL)) + return BAD_FUNC_ARG; + return wc_rng_bank_checkin((*rng_inst)->bank, rng_inst); +} + /* note the rng_inst passed to wc_rng_bank_inst_reinit() must have been obtained * via wc_rng_bank_checkout() to assure that the caller holds the proper locks. */ @@ -807,15 +836,15 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( time_t ts1 = 0; int devId; -#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (rng_inst == NULL) + return BAD_FUNC_ARG; + if (bank == NULL) - bank = default_rng_bank; -#endif + bank = rng_inst->bank; - /* rng_inst NULL check handled by rng_inst_matches_bank() */ ret = rng_inst_matches_bank(bank, rng_inst); if (ret < 0) - return BAD_FUNC_ARG; + return ret; if (WC_RNG_BANK_DRBG_NULL(&rng_inst->rng)) { @@ -870,20 +899,32 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, { int ret = 0; int n; - #ifdef WC_RNG_BANK_DEFAULT_SUPPORT - if (bank == NULL) - bank = default_rng_bank; + int bank_is_default = 0; #endif - if ((bank == NULL) || - (! (bank->flags & WC_RNG_BANK_FLAG_INITED))) - { + if (bank == NULL) { +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (seedSz == 0) { + if (default_rng_bank == NULL) + return NO_DEFAULT_FOUND_E; + else + return 0; + } + ret = wc_rng_bank_default_checkout(&bank); + if (ret != 0) + return ret; + bank_is_default = 1; +#else return BAD_FUNC_ARG; +#endif + } + else { + if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) + return BAD_STATE_E; + if (seedSz == 0) + return 0; } - - if (seedSz == 0) - return 0; /* this iteration counts down, whereas the iteration in get_drbg() counts * up, to assure they can't possibly phase-lock to each other. @@ -922,6 +963,11 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, break; } +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (bank_is_default) + (void)wc_rng_bank_default_checkin(&bank); +#endif + return ret; } @@ -932,22 +978,29 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, int n; int ret; time_t ts1 = 0; - #ifdef WC_RNG_BANK_DEFAULT_SUPPORT - if (bank == NULL) - bank = default_rng_bank; + int bank_is_default = 0; #endif - if ((bank == NULL) || - (! (bank->flags & WC_RNG_BANK_FLAG_INITED))) - { - return BAD_FUNC_ARG; - } - if (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST)) return BAD_FUNC_ARG; + if (bank == NULL) { +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + ret = wc_rng_bank_default_checkout(&bank); + if (ret != 0) + return ret; + bank_is_default = 1; +#else + return BAD_FUNC_ARG; +#endif + } + else { + if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) + return BAD_STATE_E; + } + if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) ts1 = XTIME(0); @@ -956,7 +1009,7 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, flags); if (ret != 0) - return ret; + goto out; WC_RNG_BANK_SET_RESEED_CTR(&drbg->rng, WC_RESEED_INTERVAL); @@ -1000,11 +1053,11 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, if ((ret == WC_NO_ERR_TRACE(WC_TIMEOUT_E)) || (ret == WC_NO_ERR_TRACE(INTERRUPTED_E))) { - return ret; + goto out; } ret = WC_CHECK_FOR_INTR_SIGNALS(); if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) - return ret; + goto out; WC_RELAX_LONG_LOOP(); } else { @@ -1012,41 +1065,84 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, } } - return 0; + ret = 0; + +out: + +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (bank_is_default) + (void)wc_rng_bank_default_checkin(&bank); +#endif + + return ret; } #ifdef WC_HAVE_RNG_BANKREF -WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng) -{ +static int wc_InitRng_BankRef_local(struct wc_rng_bank *bank, WC_RNG **rng) { int ret; WC_ATOMIC_INT_ARG new_refcount; + if (rng == NULL) + return BAD_FUNC_ARG; + + if (bank == NULL) { #ifdef WC_RNG_BANK_DEFAULT_SUPPORT - if (bank == NULL) - bank = default_rng_bank; + ret = wc_rng_bank_default_checkout(&bank); + if (ret != 0) + return ret; + /* wc_rng_bank_default_checkout() increments bank->refcount, which we + * carry through below (no matching wc_rng_bank_default_checkin()). + */ +#else + return BAD_FUNC_ARG; #endif + } + else { + if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) + return BAD_STATE_E; + wolfSSL_RefInc_IfAtLeast(&bank->refcount, 1, &new_refcount, &ret); + (void)new_refcount; + if (ret != 0) + return ret; + } - if ((bank == NULL) || - (rng == NULL)) - { - return BAD_FUNC_ARG; +#if !defined(WC_RNG_BANK_STATIC) && !defined(WC_NO_CONSTRUCTORS) + if (*rng == NULL) { + *rng = (WC_RNG*)XMALLOC(sizeof(WC_RNG), bank->heap, DYNAMIC_TYPE_RNG); + if (*rng == NULL) { + ret = MEMORY_E; + goto out; + } } +#endif - if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) - return BAD_STATE_E; + XMEMSET(*rng, 0, sizeof(**rng)); + (*rng)->heap = bank->heap; + (*rng)->status = WC_DRBG_BANKREF; + (*rng)->bankref = bank; - wolfSSL_RefInc_IfAtLeast(&bank->refcount, 1, &new_refcount, &ret); - (void)new_refcount; - if (ret != 0) - return ret; + ret = 0; - XMEMSET(rng, 0, sizeof(*rng)); - rng->heap = bank->heap; - rng->status = WC_DRBG_BANKREF; - rng->bankref = bank; +#if !defined(WC_RNG_BANK_STATIC) && !defined(WC_NO_CONSTRUCTORS) +out: +#endif - return 0; + if (ret != 0) { + int refdec_err; + wolfSSL_RefDec2(&bank->refcount, &new_refcount, &refdec_err); + (void)new_refcount; + (void)refdec_err; + } + + return ret; +} + +WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + return wc_InitRng_BankRef_local(bank, &rng); } WOLFSSL_API int wc_BankRef_Release(WC_RNG *rng) @@ -1073,37 +1169,10 @@ WOLFSSL_API int wc_BankRef_Release(WC_RNG *rng) #if !defined(WC_RNG_BANK_STATIC) && !defined(WC_NO_CONSTRUCTORS) WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng) { - int ret; - -#ifdef WC_RNG_BANK_DEFAULT_SUPPORT - if (bank == NULL) - bank = default_rng_bank; -#endif - - if ((bank == NULL) || - (rng == NULL)) - { + if (rng == NULL) return BAD_FUNC_ARG; - } - - if ((! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || - (wolfSSL_RefCur(bank->refcount) < 1)) - { - return BAD_STATE_E; - } - - *rng = (WC_RNG*)XMALLOC(sizeof(WC_RNG), bank->heap, DYNAMIC_TYPE_RNG); - if (*rng == NULL) { - return MEMORY_E; - } - - ret = wc_InitRng_BankRef(bank, *rng); - if (ret != 0) { - XFREE(*rng, bank->heap, DYNAMIC_TYPE_RNG); - *rng = NULL; - } - - return ret; + *rng = NULL; + return wc_InitRng_BankRef_local(bank, rng); } #endif /* !WC_RNG_BANK_STATIC && !WC_NO_CONSTRUCTORS */ diff --git a/wolfcrypt/src/srp.c b/wolfcrypt/src/srp.c index 44c48d45ef0..20a504f0f43 100644 --- a/wolfcrypt/src/srp.c +++ b/wolfcrypt/src/srp.c @@ -557,7 +557,13 @@ static int wc_SrpGenPrivate(Srp* srp, byte* priv, word32 size) WC_RNG rng; int r; - r = wc_InitRng_ex(&rng, srp->heap, INVALID_DEVID); +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) + r = wc_InitRng_BankRef(NULL /* bank */, &rng); + if (r != 0) +#endif + { + r = wc_InitRng_ex(&rng, srp->heap, INVALID_DEVID); + } if (r == 0) { r = wc_RNG_GenerateBlock(&rng, priv, size); if (r == 0) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 61201f426e7..430daa6dfa5 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -138,7 +138,7 @@ static const byte const_byte_array[] = "A+Gd\0\0\0"; esp_start_heap = esp_this_heap; \ } \ ESP_LOGI(ESPIDF_TAG, "%s #%d; Heap free: %d", \ - ((b) ? (b) : ""), /* breadcrumb string */ \ + ((b) ? (b) : ""), /* breadcrumb string */ \ ((i) ? (i) : 0), /* index */ \ esp_this_heap); @@ -27996,7 +27996,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (rng_inst != bank->rngs + 3) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - ret = wc_rng_bank_checkin(bank, &rng_inst); + ret = wc_rng_bank_inst_checkin(&rng_inst); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28011,7 +28011,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_checkin(bank, &rng_inst); + ret = wc_rng_bank_inst_checkin(&rng_inst); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (rng_inst != NULL) @@ -28022,8 +28022,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef WC_HAVE_RNG_BANKREF ret = wc_InitRng_BankRef(NULL, rng); +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#else if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif ret = wc_InitRng_BankRef(bank, NULL); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) @@ -28043,8 +28048,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif ret = wc_rng_bank_reseed(NULL, 10, WC_RNG_BANK_FLAG_NONE); +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#else if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif ret = wc_rng_bank_reseed(bank, 10, WC_RNG_BANK_FLAG_NONE); if (ret != 0) @@ -28091,7 +28101,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(rng_inst), outbuf2, sizeof(outbuf2)); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_checkin(bank, &rng_inst); + ret = wc_rng_bank_inst_checkin(&rng_inst); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28117,13 +28127,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) /* can't wc_rng_bank_seed() while holding an inst (deadlock/timeout) -- * check in then check back out. */ - ret = wc_rng_bank_checkin(bank, &rng_inst); + ret = wc_rng_bank_inst_checkin(&rng_inst); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_rng_bank_seed(NULL, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#else if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif ret = wc_rng_bank_seed(bank, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) @@ -28144,8 +28159,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* NULL bank to wc_rng_bank_inst_reinit() tells it to use the bank with + * which rng_inst is associated. */ ret = wc_rng_bank_inst_reinit(NULL, rng_inst, 10, WC_RNG_BANK_FLAG_CAN_WAIT); - if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); /* bogus pointer test */ @@ -28164,17 +28181,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - ret = wc_rng_bank_checkin(NULL, &rng_inst); - if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_checkin(bank, &rng_inst); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_rng_bank_checkout(NULL, &rng_inst, -1, 10, WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | WC_RNG_BANK_FLAG_AFFINITY_LOCK); +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#else if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif #ifdef WC_HAVE_RNG_BANKREF if (wolfSSL_RefCur(bank->refcount) != 2) @@ -28346,8 +28364,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef WC_HAVE_RNG_BANKREF ret = wc_rng_new_bankref(NULL, &rng2); +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#else if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif ret = wc_rng_new_bankref(bank2, NULL); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) @@ -86676,9 +86699,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) #endif /* Driver coverage for the new CryptoCb hooks: confirm each op is routed - * through myCryptoDevCb (counter bumped) and the round-trip is correct. */ + * through myCryptoDevCb (counter bumped) and the round-trip is correct. + * + * The FIPS wrappers force the devId to FIPS_INVALID_DEVID, so we skip + * the check for FIPS. */ #if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) && \ - defined(HAVE_ED448_VERIFY) && !defined(WC_NO_RNG) + defined(HAVE_ED448_VERIFY) && !defined(WC_NO_RNG) && \ + !defined(HAVE_FIPS) if (ret == 0) { WC_RNG ed448Rng; int ed448RngInit = 0; @@ -86742,7 +86769,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) #endif /* HAVE_ED448 */ #if defined(WOLFSSL_CMAC) && defined(WOLF_CRYPTO_CB_FREE) && \ - !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) + !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) && \ + !defined(HAVE_FIPS) if (ret == 0) { byte cmacKey[WC_AES_BLOCK_SIZE] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, @@ -86802,7 +86830,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ !defined(NO_RSA) && !defined(WC_NO_RNG) && defined(WOLFSSL_KEY_GEN) && \ - !defined(NO_SHA256) + !defined(NO_SHA256) && !defined(HAVE_FIPS) if (ret == 0) { WC_RNG rsaRng; int rsaRngInit = 0; diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index f1e102ef0fa..4045d86e475 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -57,12 +57,15 @@ typedef int (*wc_affinity_lock_fn_t)(void *arg); typedef int (*wc_affinity_get_id_fn_t)(void *arg, int *id); typedef int (*wc_affinity_unlock_fn_t)(void *arg); +struct wc_rng_bank; + struct wc_rng_bank_inst { #ifdef WOLFSSL_NO_ATOMICS int lock; #else wolfSSL_Atomic_Int lock; #endif + struct wc_rng_bank *bank; WC_RNG rng; }; @@ -149,6 +152,9 @@ WOLFSSL_API int wc_rng_bank_checkin( struct wc_rng_bank *bank, struct wc_rng_bank_inst **rng_inst); +WOLFSSL_API int wc_rng_bank_inst_checkin( + struct wc_rng_bank_inst **rng_inst); + WOLFSSL_API int wc_rng_bank_inst_reinit( struct wc_rng_bank *bank, struct wc_rng_bank_inst *rng_inst, From d64cd611d44bad37bfeef3dd514b3689fc20c5c9 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 18 Aug 2026 11:56:15 -0500 Subject: [PATCH 002/102] wolfcrypt/src/rng_bank.c: additional arg validating, hardening, and improved dynamics. --- wolfcrypt/src/rng_bank.c | 303 +++++++++++++++++++++++++++++++++++---- 1 file changed, 274 insertions(+), 29 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 96dfe63a124..4cbbe66fd08 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -71,6 +71,19 @@ ((rng_ptr)->drbg == NULL) #endif +/* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to + * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when + * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 + * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 + * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time + * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be + * defined with a (word64) cast (settings.h kernel path) that the preprocessor + * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips + * it without expanding that cast. */ +#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) + wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); +#endif + /* To disable retry looping in wc_rng_bank_init(), pass timeout_secs=0, and to * retry indefinitely, pass negative timeout_secs -- the flags arg here is only * used to initialize the flags in the new bank. @@ -158,6 +171,12 @@ WOLFSSL_API int wc_rng_bank_init( case WC_NO_ERR_TRACE(DRBG_CONT_FIPS_E): goto out; } + + if (timeout_secs == 0) { + break; /* Retry disabled -- return the real error, not + * WC_TIMEOUT_E. */ + } + /* Allow interrupt only if we're stuck spinning retries -- i.e., * don't allow an untimely user signal to derail an * initialization that is proceeding expeditiously. @@ -166,7 +185,7 @@ WOLFSSL_API int wc_rng_bank_init( if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) break; ts2 = XTIME(0); - if ((timeout_secs >= 0) && (ts2 - ts1 > timeout_secs)) { + if ((timeout_secs > 0) && (ts2 - ts1 > timeout_secs)) { ret = WC_TIMEOUT_E; break; } @@ -574,13 +593,25 @@ WOLFSSL_API int wc_rng_bank_checkout( } } - if ((preferred_inst_offset < 0) || - (preferred_inst_offset >= bank->n_rngs)) - { + if (preferred_inst_offset < 0) { ret = BAD_INDEX_E; break; } + if (preferred_inst_offset >= bank->n_rngs) { + /* An affinity id can legitimately exceed n_rngs, there may be + * more CPUs than instances. Wrap into range when the caller + * allows failover; otherwise the index is unusable. + */ + if (flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) { + preferred_inst_offset %= bank->n_rngs; + } + else { + ret = BAD_INDEX_E; + break; + } + } + if (wolfSSL_Atomic_Int_CompareExchange( &bank->rngs[preferred_inst_offset].lock, &expected, @@ -588,11 +619,29 @@ WOLFSSL_API int wc_rng_bank_checkout( { *rng_inst = &bank->rngs[preferred_inst_offset]; - if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && - (WC_RNG_BANK_RESEED_CTR(&(*rng_inst)->rng) >= - WC_RESEED_INTERVAL) && - (flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) && - (n_rngs_tried < bank->n_rngs)) + /* Two scenarios where we put an instance back and move on, both of + * them only when the caller allows failover and instances remain: + * + * (1) It's not in service (an earlier wc_rng_bank_inst_reinit() + * failed and set status to WC_DRBG_FAILED), or + * + * (2) It's due for reseed and the caller can't wait. + * + * rng.status, not a NULL DRBG, is the out-of-service test. With + * HAVE_INTEL_RDRAND on an RDRAND-capable CPU, _InitRng() bypasses + * DRBG instantiation entirely and returns a usable instance whose + * drbg is NULL and whose status is WC_DRBG_OK; treating that as + * out of service would divert away from every instance in the + * bank. WC_RNG_BANK_DRBG_NULL() below is only guarding the + * reseedCtr read, which dereferences the DRBG pointer. + */ + if ((flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) && + (n_rngs_tried < bank->n_rngs) && + (((*rng_inst)->rng.status != WC_DRBG_OK) || + ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && + (! WC_RNG_BANK_DRBG_NULL(&(*rng_inst)->rng)) && + (WC_RNG_BANK_RESEED_CTR(&(*rng_inst)->rng) >= + WC_RESEED_INTERVAL)))) { WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); *rng_inst = NULL; @@ -600,6 +649,7 @@ WOLFSSL_API int wc_rng_bank_checkout( else { #ifdef WC_VERBOSE_RNG if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && + (! WC_RNG_BANK_DRBG_NULL(&(*rng_inst)->rng)) && (WC_RNG_BANK_RESEED_CTR(&(*rng_inst)->rng) >= WC_RESEED_INTERVAL)) { @@ -722,21 +772,62 @@ WOLFSSL_API int wc_rng_bank_checkout( } #ifdef WC_HAVE_RNG_BANKREF +/* wc_local_rng_bank_checkout_for_bankref() is the shim to the real WC_RNG when + * wc_RNG_GenerateBlock() is called on a bankref WC_RNG. It's called from + * kernel atomic contexts, where waiting for a busy instance is the hazard, not + * the fix. Thus we pass timeout_secs = 0. + * + * _CAN_WAIT is not in contradiction with that. _CAN_WAIT allows selection of + * instances that would otherwise be skipped because due for reseed, so the + * generate absorbs the reseed inline instead of skipping instances, while + * timeout_secs = 0 inhibits waiting when no instances are available. + * + * _CAN_FAIL_OVER_INST tells wc_rng_bank_checkout() to sweep every instance -- + * BUSY_E is reachable only when all of them are held at once, which is + * impossible by construction when the bank has at least as many instances as + * there can be concurrent callers. That sizing is the caller's contract: the + * linuxkm module allocates nr_cpu_ids + 4. An undersized bank does not fail + * unsafely, but it does make this return BUSY_E to callers of the public API + * that have no reason to expect it, so when WC_VERBOSE_RNG, we print a warning + * if it occurs. + */ WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref( struct wc_rng_bank *bank, struct wc_rng_bank_inst **rng_inst) { + int ret; + if (bank == NULL) return BAD_FUNC_ARG; - return wc_rng_bank_checkout( + + ret = wc_rng_bank_checkout( bank, rng_inst, 0, 0, WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_CAN_WAIT | ((bank->affinity_get_id_cb != NULL) ? WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST : 0) | ((bank->affinity_lock_cb != NULL) ? WC_RNG_BANK_FLAG_AFFINITY_LOCK : 0)); + +#ifdef WC_VERBOSE_RNG + if (ret == WC_NO_ERR_TRACE(BUSY_E)) { + WOLFSSL_DEBUG_PRINTF( + "WARNING: all %d rng_bank instances busy; size the bank to at " + "least the peak number of concurrent callers.\n", bank->n_rngs); + } +#endif + + return ret; } #endif /* WC_HAVE_RNG_BANKREF */ +/* rng_inst_matches_bank() returns 1 if rng_inst is one of this bank's live + * instances, else an error. The INITED and refcount gates catch calls on a + * torn-down bank (wc_rng_bank_fini() clears the flags and zeroes n_rngs); the + * n_rngs and NULL checks are additional checks for the same case, to + * catch data corruption opportunistically. The range check's upper bound is + * n_rngs - 1, not WC_RNG_BANK_STATIC_SIZE - 1: on a live bank sized below + * WC_RNG_BANK_STATIC_SIZE, that bound is the only thing rejecting a pointer + * to a trailing slot that was never instantiated. + */ static WC_INLINE int rng_inst_matches_bank( struct wc_rng_bank *bank, struct wc_rng_bank_inst *rng_inst) @@ -747,19 +838,30 @@ static WC_INLINE int rng_inst_matches_bank( return BAD_STATE_E; if (wolfSSL_RefCur(bank->refcount) < 2) return BAD_STATE_E; -#ifdef WC_RNG_BANK_STATIC - if ((rng_inst >= &bank->rngs[0]) && - (rng_inst <= &bank->rngs[WC_RNG_BANK_STATIC_SIZE - 1])) - return 1; - else + + if (bank->n_rngs <= 0) return BAD_FUNC_ARG; -#else - if ((rng_inst >= bank->rngs) && - (rng_inst <= bank->rngs + bank->n_rngs - 1)) - return 1; - else + +#ifndef WC_RNG_BANK_STATIC + /* Not testable in the static build, rngs is an array, never NULL. */ + if (bank->rngs == NULL) return BAD_FUNC_ARG; #endif + + if ((rng_inst < &bank->rngs[0]) || + (rng_inst > &bank->rngs[bank->n_rngs - 1])) + { + return BAD_FUNC_ARG; + } + + /* Reject a pointer into the middle of an instance. */ + if ((((size_t)((const char *)rng_inst - (const char *)&bank->rngs[0])) % + sizeof(*rng_inst)) != 0) + { + return BAD_FUNC_ARG; + } + + return 1; } WOLFSSL_API int wc_rng_bank_checkin( @@ -778,12 +880,57 @@ WOLFSSL_API int wc_rng_bank_checkin( #endif ret = rng_inst_matches_bank(bank, *rng_inst); - if (ret < 0) + if (ret < 0) { + /* Nothing can be released here: the instance the caller actually holds + * can't be identified from a pointer that isn't in this bank, so its + * lock and the bank refcount stay held and wc_rng_bank_fini() will + * report BUSY_E/BAD_STATE_E until the caller checks in correctly. + * + * We can't warn for this misuse because random_bank_test() exercises + * the functionality. + */ return ret; + } lockval = (int)WOLFSSL_ATOMIC_LOAD((*rng_inst)->lock); + /* Opportunistically check for lock misuse/corruption. + * + * An instance must be checked in exactly once, by the caller that checked + * it out. A duplicate or cross-thread checkin double-releases the affinity + * lock (double migrate_enable() in linuxkm) and double-decrements the bank + * refcount. In normal builds we detect sequential misuse -- duplicate or + * stale checkins ordered after the release -- with a cheap check that the + * lock has WC_RNG_BANK_INST_LOCK_HELD. In WC_RNG_BANK_LOCK_DEBUG builds, + * the release is the more expensive compare-and-exchange, which catches + * both sequential misuses and concurrent duplicates (short of ABA reuse of + * the slot within the race window). + */ + if (! (lockval & WC_RNG_BANK_INST_LOCK_HELD)) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "BUG: wc_rng_bank_checkin() on an instance that is not checked " + "out (lock %d).\n", lockval); +#endif + return BAD_STATE_E; + } + +#ifdef WC_RNG_BANK_LOCK_DEBUG + { + int expected = lockval; + if (! wolfSSL_Atomic_Int_CompareExchange( + &(*rng_inst)->lock, &expected, + WC_RNG_BANK_INST_LOCK_FREE)) + { + WOLFSSL_DEBUG_PRINTF( + "BUG: wc_rng_bank_checkin() lock changed under it " + "(%d -> %d).\n", lockval, expected); + return BAD_STATE_E; + } + } +#else /* !WC_RNG_BANK_LOCK_DEBUG */ WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); +#endif /* !WC_RNG_BANK_LOCK_DEBUG */ *rng_inst = NULL; @@ -846,11 +993,12 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( if (ret < 0) return ret; - if (WC_RNG_BANK_DRBG_NULL(&rng_inst->rng)) - { - return BAD_FUNC_ARG; - } - + /* No DRBG-NULL rejection here. wc_FreeRng() below nulls the DRBG, so an + * instance left that way by an earlier failed reinit needs another attempt. + * Note that with HAVE_INTEL_RDRAND on an RDRAND-capable CPU, a NULL DRBG is + * the normal in-service state. wc_FreeRng() null-checks each member, so + * it is a safe no-op when called on an already-freed instance. + */ if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) ts1 = XTIME(0); @@ -866,8 +1014,40 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( ret = wc_InitRngNonce_ex(WC_RNG_BANK_INST_TO_RNG(rng_inst), (byte *)&rng_inst, sizeof(byte *), bank->heap, devId); + + /* Relax between iterations exactly as wc_rng_bank_init() does. The + * caller may hold the affinity lock taken by wc_rng_bank_checkout(), + * so this must not sleep in atomic context; WC_RELAX_LONG_LOOP() + * degrades to a cpu_relax() there. + */ + WC_RELAX_LONG_LOOP(); + if (ret == 0) break; + + /* Several plausible error codes are non-retryable -- fail early for + * these rather than reattempting until the timeout. Same list as + * wc_rng_bank_init(). + */ + switch (ret) { + case WC_NO_ERR_TRACE(BAD_MUTEX_E): + case WC_NO_ERR_TRACE(BAD_FUNC_ARG): + case WC_NO_ERR_TRACE(MEMORY_E): + case WC_NO_ERR_TRACE(NOT_COMPILED_IN): + case WC_NO_ERR_TRACE(MISSING_RNG_E): + case WC_NO_ERR_TRACE(BUFFER_E): + case WC_NO_ERR_TRACE(OPEN_RAN_E): + case WC_NO_ERR_TRACE(FIPS_NOT_ALLOWED_E): + case WC_NO_ERR_TRACE(DRBG_KAT_FIPS_E): + case WC_NO_ERR_TRACE(DRBG_CONT_FIPS_E): +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_inst_reinit() non-retryable err %d.\n", + ret); +#endif + goto out; + } + if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) || (timeout_secs == 0)) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( @@ -876,6 +1056,18 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( break; } + /* Allow interrupt only once we are stuck spinning retries. Without + * this, a negative timeout_secs (retry indefinitely) has no break at + * all other than success. + */ + { + int intr = WC_CHECK_FOR_INTR_SIGNALS(); + if (intr == WC_NO_ERR_TRACE(INTERRUPTED_E)) { + ret = intr; + break; + } + } + if (timeout_secs > 0) { time_t ts2 = XTIME(0); if (ts2 - ts1 >= timeout_secs) { @@ -889,6 +1081,17 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( } } +out: + + /* Leave a failed instance explicitly out of service rather than relying + * on whichever status _InitRng() happened to leave behind. The DRBG is + * NULL at this point; wc_rng_bank_checkout() diverts away from such an + * instance when the caller allows failover, and the seed/reseed walks + * refuse it. + */ + if (ret != 0) + rng_inst->rng.status = WC_DRBG_FAILED; + return ret; } @@ -903,6 +1106,14 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, int bank_is_default = 0; #endif + /* wc_rng_bank_seed() must walk every instance by explicit index -- forbid + * flags that would let wc_rng_bank_checkout() pick a different instance + * than requested. Same restriction applies in wc_rng_bank_reseed(). + */ + if (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST)) + return BAD_FUNC_ARG; + if (bank == NULL) { #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (seedSz == 0) { @@ -926,7 +1137,7 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, return 0; } - /* this iteration counts down, whereas the iteration in get_drbg() counts + /* This iteration counts down, whereas the iteration in get_drbg() counts * up, to assure they can't possibly phase-lock to each other. */ for (n = bank->n_rngs - 1; n >= 0; --n) { @@ -940,10 +1151,18 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, #endif break; } - else if (WC_RNG_BANK_DRBG_NULL(&drbg->rng)) { + /* Note that a NULL DRBG doesn't necessarily indicate failure: + * _InitRng() bypasses DRBG instantiation when the CPU has RDRAND + * (HAVE_INTEL_RDRAND), leaving a usable instance with drbg NULL and + * status WC_DRBG_OK. wc_RNG_DRBG_Reseed() gracefully handles that case + * itself (random.c returns success for a NULL DRBG under RDRAND), so + * let it through rather than calling it an error. + */ + else if (drbg->rng.status != WC_DRBG_OK) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_seed(): inst#%d has null .drbg.\n", n); + "WARNING: wc_rng_bank_seed(): inst#%d is out of service " + "(status %d).\n", n, (int)drbg->rng.status); #endif ret = BAD_STATE_E; } @@ -982,6 +1201,10 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, int bank_is_default = 0; #endif + /* wc_rng_bank_reseed() must walk every instance by explicit index -- forbid + * flags that would let wc_rng_bank_checkout() pick a different instance + * than requested. Same restriction applies in wc_rng_bank_seed(). + */ if (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST)) return BAD_FUNC_ARG; @@ -1011,6 +1234,28 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, if (ret != 0) goto out; + if (drbg->rng.status != WC_DRBG_OK) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_reseed(): inst#%d is out of service " + "(status %d).\n", n, (int)drbg->rng.status); +#endif + (void)wc_rng_bank_checkin(bank, &drbg); + ret = BAD_STATE_E; + goto out; + } + + /* The store below writes through the DRBG pointer. An in-service + * instance can still have none: _InitRng() bypasses DRBG + * instantiation when the CPU has RDRAND (HAVE_INTEL_RDRAND). There + * is no reseed counter to force in that case, and nothing to reseed; + * skip, do not fail. + */ + if (WC_RNG_BANK_DRBG_NULL(&drbg->rng)) { + (void)wc_rng_bank_checkin(bank, &drbg); + continue; + } + WC_RNG_BANK_SET_RESEED_CTR(&drbg->rng, WC_RESEED_INTERVAL); if (flags & WC_RNG_BANK_FLAG_CAN_WAIT) { From bc3b2935e7e6705b5d06809ee3e5a179b373f67f Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 18 Aug 2026 19:26:24 -0500 Subject: [PATCH 003/102] wolfcrypt/src/asn.c, wolfcrypt/src/curve25519.c, wolfcrypt/src/evp.c, wolfcrypt/src/pkcs7.c, wolfcrypt/src/pkcs12.c, wolfcrypt/src/srp.c: revert wc_InitRng_BankRef() airdrops. --- wolfcrypt/src/asn.c | 16 ++---- wolfcrypt/src/curve25519.c | 16 +----- wolfcrypt/src/evp.c | 30 ++--------- wolfcrypt/src/pkcs12.c | 9 +--- wolfcrypt/src/pkcs7.c | 104 +++++-------------------------------- wolfcrypt/src/srp.c | 8 +-- 6 files changed, 26 insertions(+), 157 deletions(-) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 94f3405cb14..2df6050a862 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -37829,17 +37829,11 @@ int InitOcspRequest(OcspRequest* req, DecodedCert* cert, byte useNonce, if (useNonce) { WC_RNG rng; -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) - ret = wc_InitRng_ex(&rng, req->heap, INVALID_DEVID); - #else - ret = wc_InitRng(&rng); - #endif - } + #ifndef HAVE_FIPS + ret = wc_InitRng_ex(&rng, req->heap, INVALID_DEVID); + #else + ret = wc_InitRng(&rng); + #endif if (ret != 0) { WOLFSSL_MSG("\tCannot initialize RNG. Skipping the OCSP Nonce."); } else { diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 31d2d687342..9cf5cb08d72 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -237,13 +237,7 @@ static int curve25519_make_pub_ex(int public_size, byte* pub, int private_size, { WC_RNG rng; -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng(&rng); - } + ret = wc_InitRng(&rng); if (ret == 0) { ret = curve25519_make_pub_blind_sw(public_size, pub, priv, &rng); @@ -563,13 +557,7 @@ int wc_curve25519_generic(int public_size, byte* pub, { WC_RNG rng; -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng(&rng); - } + ret = wc_InitRng(&rng); if (ret == 0) { ret = curve25519_generic_blind_sw(pub, priv, basepoint, &rng); diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 41d359493cd..ca0ccdd7432 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -2853,16 +2853,7 @@ int wolfSSL_EVP_PKEY_derive(WOLFSSL_EVP_PKEY_CTX *ctx, unsigned char *key, size_ (defined(HAVE_FIPS_VERSION) && HAVE_FIPS_VERSION > 2)) WC_RNG rng; - int ret; - -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng(&rng); - } - if (ret != 0) { + if (wc_InitRng(&rng) != MP_OKAY) { WOLFSSL_MSG("Init RNG failed"); return WOLFSSL_FAILURE; } @@ -6764,14 +6755,7 @@ void wolfSSL_EVP_init(void) } /* arg is 4...(ctx->ivSz - 8) */ XMEMCPY(ctx->iv, ptr, (size_t)arg); -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng(&rng); - } - if (ret != 0) { + if (wc_InitRng(&rng) != 0) { WOLFSSL_MSG("wc_InitRng failed"); break; } @@ -12515,17 +12499,11 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_ex(void* heap) pkey->heap = heap; pkey->type = WOLFSSL_EVP_PKEY_DEFAULT; -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &pkey->rng); - if (ret != 0) -#endif - { #ifndef HAVE_FIPS - ret = wc_InitRng_ex(&pkey->rng, heap, INVALID_DEVID); + ret = wc_InitRng_ex(&pkey->rng, heap, INVALID_DEVID); #else - ret = wc_InitRng(&pkey->rng); + ret = wc_InitRng(&pkey->rng); #endif - } if (ret != 0){ /* Free directly since mutex for ref count not set yet */ XFREE(pkey, heap, DYNAMIC_TYPE_PUBLIC_KEY); diff --git a/wolfcrypt/src/pkcs12.c b/wolfcrypt/src/pkcs12.c index f0eb94b2e47..67d55117882 100644 --- a/wolfcrypt/src/pkcs12.c +++ b/wolfcrypt/src/pkcs12.c @@ -2807,14 +2807,7 @@ WC_PKCS12* wc_PKCS12_create(char* pass, word32 passSz, char* name, WOLFSSL_ENTER("wc_PKCS12_create"); -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, heap, INVALID_DEVID); - } - if (ret != 0) { + if (wc_InitRng_ex(&rng, heap, INVALID_DEVID) != 0) { return NULL; } diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 2e8f8d223b4..6f63ba28354 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -4565,13 +4565,7 @@ int wc_PKCS7_EncodeSignedFPD(wc_PKCS7* pkcs7, byte* privateKey, content == NULL || contentSz == 0 || output == NULL || outputSz == 0) return BAD_FUNC_ARG; -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) return ret; @@ -4679,13 +4673,7 @@ int wc_PKCS7_EncodeSignedEncryptedFPD(wc_PKCS7* pkcs7, byte* encryptKey, XMEMCPY(encrypted, output, (word32)encryptedSz); ForceZero(output, outputSz); -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) { ForceZero(encrypted, (word32)encryptedSz); XFREE(encrypted, pkcs7->heap, DYNAMIC_TYPE_PKCS7); @@ -4783,13 +4771,7 @@ int wc_PKCS7_EncodeSignedCompressedFPD(wc_PKCS7* pkcs7, byte* privateKey, XMEMCPY(compressed, output, compressedSz); ForceZero(output, outputSz); -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) { ForceZero(compressed, compressedSz); XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); @@ -4925,13 +4907,7 @@ int wc_PKCS7_EncodeSignedEncryptedCompressedFPD(wc_PKCS7* pkcs7, byte* encryptK XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); ForceZero(output, outputSz); -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) { ForceZero(encrypted, encryptedSz); XFREE(encrypted, pkcs7->heap, DYNAMIC_TYPE_PKCS7); @@ -8463,13 +8439,7 @@ static int PKCS7_GenerateContentEncryptionKey(wc_PKCS7* pkcs7, word32 len) XMEMSET(tmpKey, 0, len); -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) { XFREE(tmpKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ret; @@ -8812,13 +8782,7 @@ static int wc_PKCS7_KariGenerateEphemeralKey(WC_PKCS7_KARI* kari) kari->senderKeyInit = 1; -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, kari->heap, kari->devId); - } + ret = wc_InitRng_ex(&rng, kari->heap, kari->devId); if (ret != 0) { XFREE(kari->senderKeyExport, kari->heap, DYNAMIC_TYPE_PKCS7); kari->senderKeyExportSz = 0; @@ -9694,13 +9658,7 @@ int wc_PKCS7_AddRecipient_KTRI(wc_PKCS7* pkcs7, const byte* cert, word32 certSz, return PUBLIC_KEY_E; } -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) { wc_FreeRsaKey(pubKey); FreeDecodedCert(decoded); @@ -10493,13 +10451,7 @@ static int wc_PKCS7_GenerateBlock(wc_PKCS7* pkcs7, WC_RNG* rng, byte* out, if (rnd == NULL) return MEMORY_E; -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, rnd); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(rnd, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(rnd, pkcs7->heap, pkcs7->devId); if (ret != 0) { XFREE(rnd, pkcs7->heap, DYNAMIC_TYPE_RNG); return ret; @@ -10779,13 +10731,7 @@ static int wc_PKCS7_PwriKek_KeyWrap(wc_PKCS7* pkcs7, const byte* kek, XMEMCPY(out + 4, cek, cekSz); /* random padding of size padSz */ -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) return ret; @@ -11592,13 +11538,7 @@ int wc_PKCS7_EncodeEnvelopedData(wc_PKCS7* pkcs7, byte* output, word32 outputSz) verSz = SetMyVersion((word32)kariVersion, ver, 0); -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) { wc_PKCS7_FreeEncodedRecipientSet(pkcs7); return ret; @@ -11926,13 +11866,7 @@ static int wc_PKCS7_KtriFakeCEK(wc_PKCS7* pkcs7, const byte* encryptedKey, WC_ALLOC_VAR_EX(localRng, WC_RNG, 1, pkcs7->heap, DYNAMIC_TYPE_RNG, WC_FREE_VAR_EX(hmac, pkcs7->heap, DYNAMIC_TYPE_HMAC); return MEMORY_E); -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, localRng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(localRng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(localRng, pkcs7->heap, pkcs7->devId); if (ret != 0) { WC_FREE_VAR_EX(localRng, pkcs7->heap, DYNAMIC_TYPE_RNG); WC_FREE_VAR_EX(hmac, pkcs7->heap, DYNAMIC_TYPE_HMAC); @@ -12286,13 +12220,7 @@ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz, /* decrypt encryptedKey */ #ifdef WC_RSA_BLINDING - #if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) - #endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret == 0) { ret = wc_RsaSetRNG(privKey, &rng); } @@ -15448,13 +15376,7 @@ int wc_PKCS7_EncodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* output, #endif /* HAVE_AESCCM */ } -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - ret = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (ret != 0) -#endif - { - ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); - } + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); if (ret != 0) { wc_PKCS7_FreeEncodedRecipientSet(pkcs7); return ret; diff --git a/wolfcrypt/src/srp.c b/wolfcrypt/src/srp.c index 20a504f0f43..44c48d45ef0 100644 --- a/wolfcrypt/src/srp.c +++ b/wolfcrypt/src/srp.c @@ -557,13 +557,7 @@ static int wc_SrpGenPrivate(Srp* srp, byte* priv, word32 size) WC_RNG rng; int r; -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_HAVE_RNG_BANKREF) - r = wc_InitRng_BankRef(NULL /* bank */, &rng); - if (r != 0) -#endif - { - r = wc_InitRng_ex(&rng, srp->heap, INVALID_DEVID); - } + r = wc_InitRng_ex(&rng, srp->heap, INVALID_DEVID); if (r == 0) { r = wc_RNG_GenerateBlock(&rng, priv, size); if (r == 0) { From a65f7da03929e1340755ec71e1ba3af22d67e300 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 20 Aug 2026 14:19:21 -0500 Subject: [PATCH 004/102] wolfcrypt/src/rng_bank.c: * supply WC_DRBG_OK for HAVE_FIPS with FIPS_VERSION3 < 5.2.4 or == 6.0.0, for the DRBG health tests in wc_rng_bank_checkout(), wc_rng_bank_seed(), and wc_rng_bank_reseed(); * in rng_inst_matches_bank(), compare integer addresses (wc_ptr_t), and compute the mid-instance alignment check on the integer difference -- the negative tests deliberately supply fabricated pointers, for which pointer relationals and subtraction are undefined (C11 6.5.8p5 / 6.5.6p9), while integer comparisons are defined for any value. --- wolfcrypt/src/rng_bank.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 4cbbe66fd08..37cfb4468ef 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -71,6 +71,12 @@ ((rng_ptr)->drbg == NULL) #endif +/* WC_DRBG_OK is used to test DRBG health in wc_rng_bank_checkout(), + * wc_rng_bank_seed(), and wc_rng_bank_reseed(). */ +#if defined(HAVE_FIPS) && (FIPS_VERSION3_LT(5,2,4) || FIPS_VERSION3_EQ(6,0,0)) + #define WC_DRBG_OK 1 +#endif + /* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 @@ -848,14 +854,18 @@ static WC_INLINE int rng_inst_matches_bank( return BAD_FUNC_ARG; #endif - if ((rng_inst < &bank->rngs[0]) || - (rng_inst > &bank->rngs[bank->n_rngs - 1])) + /* Compare integer addresses: the negative tests deliberately supply + * fabricated pointers, for which pointer relationals and subtraction + * are undefined (C11 6.5.8p5 / 6.5.6p9). Integer comparisons are + * defined for any value. */ + if (((wc_ptr_t)rng_inst < (wc_ptr_t)&bank->rngs[0]) || + ((wc_ptr_t)rng_inst > (wc_ptr_t)&bank->rngs[bank->n_rngs - 1])) { return BAD_FUNC_ARG; } /* Reject a pointer into the middle of an instance. */ - if ((((size_t)((const char *)rng_inst - (const char *)&bank->rngs[0])) % + if ((((wc_ptr_t)rng_inst - (wc_ptr_t)&bank->rngs[0]) % sizeof(*rng_inst)) != 0) { return BAD_FUNC_ARG; From 0fb8d25dfd328ceb4fd29c32c0cb59f69cdd4fc8 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 20 Aug 2026 14:19:32 -0500 Subject: [PATCH 005/102] wolfcrypt/test/test.c: in random_bank_test(), add negative coverage for wc_rng_bank checkin and seed edge cases from the 20260820 review batch: duplicate (stale-copy) checkin through both wc_rng_bank_checkin() and wc_rng_bank_inst_checkin(), asserting BAD_STATE_E without bank mutation; seedSz == 0 no-op success for an explicit inited bank and for the default form while a default is set; and seedSz == 0 with no default bank set, asserting NO_DEFAULT_FOUND_E. --- wolfcrypt/test/test.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 430daa6dfa5..798d20a8f30 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28000,6 +28000,25 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + /* A duplicate (stale-copy) check-in must be rejected with BAD_STATE_E -- + * the instance's HELD flag is already clear -- without mutating the + * bank, through both entry points. */ + ret = wc_rng_bank_checkout(bank, &rng_inst, 3, 10, WC_RNG_BANK_FLAG_NONE); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + { + struct wc_rng_bank_inst *stale_inst = rng_inst; + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_checkin(bank, &stale_inst); + if (ret != WC_NO_ERR_TRACE(BAD_STATE_E)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_inst_checkin(&stale_inst); + if (ret != WC_NO_ERR_TRACE(BAD_STATE_E)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + rng_bank_affinity_get_id_id = 3; ret = wc_rng_bank_checkout(bank, &rng_inst, -1, 10, WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | WC_RNG_BANK_FLAG_AFFINITY_LOCK); if (ret != 0) @@ -28144,6 +28163,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + /* seedSz == 0 short-circuits: no-op success for an explicit inited + * bank, and for the default form while a default is set. (The + * seed pointer is never read on these paths.) */ + ret = wc_rng_bank_seed(bank, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_checkout(bank, &rng_inst, -1, 10, WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | WC_RNG_BANK_FLAG_AFFINITY_LOCK); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28266,10 +28292,21 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + ret = wc_rng_bank_seed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif + ret = wc_rng_bank_default_clear(bank); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + /* seedSz == 0 probe with no default bank set: NO_DEFAULT_FOUND_E. */ + ret = wc_rng_bank_seed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + #endif /* WC_RNG_BANK_DEFAULT_SUPPORT */ #ifdef WC_RNG_BANK_STATIC From b2f907fcb5814a357828b9b7d9f1b505915d5238 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 20 Aug 2026 14:20:50 -0500 Subject: [PATCH 006/102] linuxkm/lkcapi_sha_glue.c: in wc_linuxkm_drbg_generate(), sample blockability into can_wait before the DRBG checkout, and gate the opportunistic reseed and the RNG_FAILURE_E reinit recovery on it -- wc_linuxkm_can_block() is false whenever the affinity lock is held (the lock callback is SAVE_VECTOR_REGISTERS_MAYBE_INHIBIT()), so the previous live-sampled guards were unsatisfiable in every reachable state and both features were dead. For each of the two blocking operations, bracket it to restore preemptibility while keeping the instance checked out: migrate_disable() (CONFIG_SMP && >= 5.11.0 -- on earlier kernels migrate_disable() is absent or maps to preempt_disable(), so migration during the operation is simply tolerated there), then RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED() keyed on the lock's _AFFINITY_LOCKED/_VEC_OPS_INH bits, then the blocking operation preemptibly (wc_rng_bank_inst_reinit()'s WC_RELAX_LONG_LOOP now sees can_block() true and actually yields), then re-establish the vector state flavor-matched (SAVE_VECTOR_REGISTERS2() for the affinity hold, DISABLE_VECTOR_REGISTERS() for the inhibit hold), then migrate_enable(). If re-establishment fails, clear the corresponding lock bits so checkin doesn't double-restore; the cleared bits also make a failed bracket a valid entry state for the subsequent one. Plain (non-atomic) accesses to drbg->lock are sound across the span: WC_RNG_BANK_INST_LOCK_HELD is invariantly held, and it is the only bit considered by contending threads. (root defect identified in 20260820 review batch; see PR discussion for why the review's proposed fix -- pre-captured blockability alone, without releasing the holds -- would have converted the dead code into sleep-in-atomic / bh-off stalls) --- linuxkm/lkcapi_sha_glue.c | 68 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 9126dc81e05..c9e20696a6c 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2329,6 +2329,9 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, u8 *dst, unsigned int dlen) { int ret, retried = 0; + /* can_block() is false whenever the affinity lock is held -- blockability + * must be sampled before checkout. */ + int can_wait = wc_linuxkm_can_block(); struct wc_rng_bank_inst *drbg = linuxkm_get_drbg(ctx); if (! drbg) { @@ -2346,13 +2349,46 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, } else if ((! WC_RNG_BANK_DRBG_NULL(WC_RNG_BANK_INST_TO_RNG(drbg))) && (WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)) > WC_RESEED_INTERVAL / 2) && - wc_linuxkm_can_block()) + can_wait) { byte scratch[4]; word64 cur_counter = WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)); WC_RNG_BANK_SET_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg), WC_RESEED_INTERVAL); + +#ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS + /* carefully restore preemptibility for the reseed operation. */ + + #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) + migrate_disable(); + #endif + + /* note, no need to use formal atomic accessors on drbg->lock -- + * WC_RNG_BANK_INST_LOCK_HELD is held invariantly across the span, and + * is the only bit considered by contending threads. */ + if (drbg->lock & (WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED | WC_RNG_BANK_INST_LOCK_VEC_OPS_INH)) + RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); +#endif + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(drbg), scratch, (word32)sizeof scratch); + +#ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS + if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { + int ret2 = DISABLE_VECTOR_REGISTERS(); + if (ret2 != 0) + drbg->lock &= ~(WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED | WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); + } + else if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { + int ret2 = SAVE_VECTOR_REGISTERS2(); + if (ret2 != 0) + drbg->lock &= ~WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; + } + + #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) + migrate_enable(); + #endif +#endif + if ((ret != 0) && (WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)) >= WC_RESEED_INTERVAL)) { WC_RNG_BANK_SET_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg), cur_counter + 1); } @@ -2389,13 +2425,41 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, break; retried = 1; - if (! wc_linuxkm_can_block()) + if (! can_wait) break; +#ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS + /* carefully restore preemptibility for the reinit operation. */ + + #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) + migrate_disable(); + #endif + + if (drbg->lock & (WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED | WC_RNG_BANK_INST_LOCK_VEC_OPS_INH)) + RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); +#endif + ret = wc_rng_bank_inst_reinit(NULL, drbg, WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT); +#ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS + if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { + int ret2 = DISABLE_VECTOR_REGISTERS(); + if (ret2 != 0) + drbg->lock &= ~(WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED | WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); + } + else if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { + int ret2 = SAVE_VECTOR_REGISTERS2(); + if (ret2 != 0) + drbg->lock &= ~WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; + } + + #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) + migrate_enable(); + #endif +#endif + if (ret == 0) { pr_warn_ratelimited("WARNING: reinitialized DRBG #%d after RNG_FAILURE_E from wc_RNG_GenerateBlock().\n", raw_smp_processor_id()); continue; From 1691abd0531faf00fd8aa91396127964573391e7 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 20 Aug 2026 16:14:13 -0500 Subject: [PATCH 007/102] wolfcrypt/test/test.c: remove redundant gate in random_bank_test(). --- wolfcrypt/test/test.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 798d20a8f30..5ff04334c66 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28292,11 +28292,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#ifdef WC_RNG_BANK_DEFAULT_SUPPORT ret = wc_rng_bank_seed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#endif ret = wc_rng_bank_default_clear(bank); if (ret != 0) From 343c76a44f523cc5ff9cf84e6509859901bfac72 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 20 Aug 2026 16:15:44 -0500 Subject: [PATCH 008/102] linuxkm/lkcapi_sha_glue.c: in wc__get_random_bytes(), ungate the error message on failed wc_rng_bank_default_checkout(), and rework the error message when wc_linuxkm_drbg_generate() fails. --- linuxkm/lkcapi_sha_glue.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index c9e20696a6c..7e37e1c9c7d 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2576,9 +2576,7 @@ static int wc__get_random_bytes(void *buf, size_t len) ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { -#ifdef WC_VERBOSE_RNG pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc__get_random_bytes() returned %d.\n", ret); -#endif return -EFAULT; } else { @@ -2586,7 +2584,7 @@ static int wc__get_random_bytes(void *buf, size_t len) NULL, 0, buf, (unsigned int)len); (void)wc_rng_bank_default_checkin(¤t_default_wc_rng_bank); if (ret) { - pr_warn("BUG: wc__get_random_bytes falling through to native get_random_bytes with wc_linuxkm_drbg_default_instance_registered, ret=%d.\n", ret); + pr_err_ratelimited("ERROR: wc__get_random_bytes(): wc_linuxkm_drbg_generate() failed with code %d.\n", ret); } return ret; } From cd7a7d8a827ae99f0b0a56d9a6df6d16e951dbc7 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 21 Aug 2026 13:24:52 -0500 Subject: [PATCH 009/102] wolfcrypt/test/test.c: in random_bank_test(), hold a second instance checked out across the stale check-in probes. With the bank refcount at 1, rng_inst_matches_bank() rejected the probes with BAD_STATE_E before the WC_RNG_BANK_INST_LOCK_HELD guard in wc_rng_bank_checkin() -- the guard under test -- was reached, leaving it uncovered. With refcount >= 2 the HELD guard is the rejecting path. --- wolfcrypt/test/test.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 5ff04334c66..cdecf63cfff 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28002,12 +28002,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) /* A duplicate (stale-copy) check-in must be rejected with BAD_STATE_E -- * the instance's HELD flag is already clear -- without mutating the - * bank, through both entry points. */ + * bank, through both entry points. Hold a second instance across the + * stale check-ins: with the bank refcount at 1, rng_inst_matches_bank() + * rejects with BAD_STATE_E before the HELD-flag guard in + * wc_rng_bank_checkin() -- the guard under test here -- is reached. */ ret = wc_rng_bank_checkout(bank, &rng_inst, 3, 10, WC_RNG_BANK_FLAG_NONE); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); { struct wc_rng_bank_inst *stale_inst = rng_inst; + struct wc_rng_bank_inst *held_inst = NULL; + ret = wc_rng_bank_checkout(bank, &held_inst, 2, 10, + WC_RNG_BANK_FLAG_NONE); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_rng_bank_inst_checkin(&rng_inst); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28017,6 +28025,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ret = wc_rng_bank_inst_checkin(&stale_inst); if (ret != WC_NO_ERR_TRACE(BAD_STATE_E)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_inst_checkin(&held_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } rng_bank_affinity_get_id_id = 3; From 1a018ce08d64f930262894bf4c28ea3baf557c8f Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 24 Aug 2026 17:13:01 -0500 Subject: [PATCH 010/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h: add DRBG state accessor and reseed scheduling services Give outside-the-boundary consumers defined service interfaces for previously missing semantics, avoiding any need for reaching into WC_RNG/DRBG internals for. * wc_RNG_GetStatus(): read-only health-status accessor. * wc_RNG_DRBG_Present(): discriminates the HAVE_INTEL_RDRAND no-DRBG instantiation shape. * wc_RNG_DRBG_GetReseedCtr(): read-only reseed counter accessor; no DRBG reports 0 (never due), so callers need no special-casing. Adds sentinel-guarded wc_drbg_reseed_ctr_t (word64, word32 when !WORD64_AVAILABLE) tracking struct DRBG_internal. * wc_RNG_DRBG_ScheduleReseed(): drive reseedCtr to WC_RESEED_INTERVAL, forcing reseed on the next generate. Deliberately set-to-threshold only -- a general setter would be a mandatory-reseed-bypass primitive. Compile-time assert that WC_RESEED_INTERVAL fits a 32-bit counter when !WORD64_AVAILABLE. * wc_RNG_DRBG_Reseed_Uncredited(): wc_RNG_DRBG_Reseed() semantics with reseedCtr preserved (save/restore in-boundary) -- the SP 800-90A additional-input concept via the reseed derivation, for mixing caller material without entropy credit. * wc_RNG_DRBG_Reseed_Now() (!CUSTOM_RAND_GENERATE_BLOCK): immediate reseed from the module's own seed source, identical gather/health-test/apply/counter/status semantics to the WC_RESEED_INTERVAL backstop reseed; optional nonce rides the same 10.1.1.3 transition as uncredited additional input, via new (additional, additionalSz) pass-through in PollAndReSeed(). --- wolfcrypt/src/random.c | 214 +++++++++++++++++++++++++++++++++++-- wolfssl/wolfcrypt/random.h | 33 ++++++ 2 files changed, 241 insertions(+), 6 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 935af4b40f1..0c5fa9675de 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -748,6 +748,160 @@ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz) return BAD_FUNC_ARG; } +/* Read-only accessor for the RNG health status (enum wc_RngHealthState). + * Returns the status, or BAD_FUNC_ARG for a NULL rng. */ +int wc_RNG_GetStatus(const WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + return (int)rng->status; +} + +/* Returns 1 if rng has an instantiated DRBG, else 0. An in-service WC_RNG + * can lack one: _InitRng() bypasses DRBG instantiation when the CPU has + * RDRAND (HAVE_INTEL_RDRAND). */ +int wc_RNG_DRBG_Present(const WC_RNG* rng) +{ + if (rng == NULL) + return 0; +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) + return 1; +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) + return 1; +#endif + return 0; +} + +/* Read-only accessor for the DRBG reseed counter. When no DRBG is + * instantiated (see wc_RNG_DRBG_Present()) there is no counter; *reseedCtr + * is set to 0 -- never due for reseed -- and 0 is returned. */ +int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, + wc_drbg_reseed_ctr_t* reseedCtr) +{ + if ((rng == NULL) || (reseedCtr == NULL)) + return BAD_FUNC_ARG; + *reseedCtr = 0; +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) + *reseedCtr = ((const DRBG_internal *)rng->drbg)->reseedCtr; +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) + *reseedCtr = (wc_drbg_reseed_ctr_t) + ((const DRBG_SHA512_internal *)rng->drbg512)->reseedCtr; +#endif + return 0; +} + +/* wc_RNG_DRBG_ScheduleReseed() drives reseedCtr up to WC_RESEED_INTERVAL to + * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when + * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 + * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 + * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time + * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be + * defined with a (word64) cast (settings.h kernel path) that the preprocessor + * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips + * it without expanding that cast. */ +#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) + wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); +#endif + +/* Mark rng due for reseed: the next generate operation reseeds from the + * module's built-in or registered seed source before producing output, and + * wc_RNG_DRBG_Reseed_Now() performs the same reseed immediately. This can + * only shorten the current seed's remaining lifetime, never extend it. + * When no DRBG is instantiated (RDRAND et al.) there is nothing to reseed; + * the call is a successful no-op. */ +int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { + ((DRBG_internal *)rng->drbg)->reseedCtr = + (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL; + return 0; + } +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { + ((DRBG_SHA512_internal *)rng->drbg512)->reseedCtr = + (word64)WC_RESEED_INTERVAL; + return 0; + } +#endif + return 0; +} + +/* Identical to wc_RNG_DRBG_Reseed(), except that the reseed counter is + * preserved: the caller-supplied material is mixed into the DRBG state via + * the reseed derivation function without being credited as entropy, so only + * the module's own seed source (wc_RNG_DRBG_Reseed_Now() or the + * WC_RESEED_INTERVAL backstop) resets the reseed schedule. This is the + * SP 800-90A additional-input concept, applied via the reseed derivation. */ +int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz) +{ + if (rng == NULL || seed == NULL) { + return BAD_FUNC_ARG; + } + +#ifndef NO_SHA256 + if (rng->drbgType == WC_DRBG_SHA256) { + if (rng->drbg == NULL) { + #if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) + if (IS_INTEL_RDRAND(intel_flags)) { + /* using RDRAND not DRBG, so return success */ + return 0; + } + #endif + return BAD_FUNC_ARG; + } + { + DRBG_internal* drbg = (DRBG_internal *)rng->drbg; + wc_drbg_reseed_ctr_t saved_ctr = drbg->reseedCtr; + int ret = Hash_DRBG_Reseed(drbg, seed, seedSz, NULL, 0); + /* Hash_DRBG_Reseed() only writes reseedCtr on success, so the + * unconditional restore is exact either way. */ + drbg->reseedCtr = saved_ctr; + return ret; + } + } +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if (rng->drbgType == WC_DRBG_SHA512) { + if (rng->drbg512 == NULL) { + #if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) + if (IS_INTEL_RDRAND(intel_flags)) { + /* using RDRAND not DRBG, so return success */ + return 0; + } + #endif + return BAD_FUNC_ARG; + } + { + DRBG_SHA512_internal* drbg512 = + (DRBG_SHA512_internal *)rng->drbg512; + word64 saved_ctr = drbg512->reseedCtr; + int ret = Hash512_DRBG_Reseed(drbg512, seed, seedSz, NULL, 0); + drbg512->reseedCtr = saved_ctr; + return ret; + } + } +#endif + + /* No DRBG type matched; if using RDRAND, that's OK */ +#if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) + if (IS_INTEL_RDRAND(intel_flags)) { + return 0; + } +#endif + + return BAD_FUNC_ARG; +} + /* Generic byte-array helper -- shared by both SHA-256 and SHA-512 DRBG * cores. Lives outside the NO_SHA256 guard so SHA-512-only builds * still link. */ @@ -2436,7 +2590,8 @@ int wc_InitRngNonce_ex(WC_RNG* rng, byte* nonce, word32 nonceSz, } #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) -static int PollAndReSeed(WC_RNG* rng) +static int PollAndReSeed(WC_RNG* rng, const byte* additional, + word32 additionalSz) { int ret = WC_NO_ERR_TRACE(DRBG_NEED_RESEED); int devId = INVALID_DEVID; @@ -2497,13 +2652,14 @@ static int PollAndReSeed(WC_RNG* rng) if (rng->drbgType == WC_DRBG_SHA256) ret = Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, newSeed + SEED_BLOCK_SZ, SEED_SZ, - NULL, 0); + additional, additionalSz); #endif #ifdef WOLFSSL_DRBG_SHA512 if (rng->drbgType == WC_DRBG_SHA512) ret = Hash512_DRBG_Reseed( (DRBG_SHA512_internal *)rng->drbg512, - newSeed + SEED_BLOCK_SZ, SEED_SZ, NULL, 0); + newSeed + SEED_BLOCK_SZ, SEED_SZ, + additional, additionalSz); #endif } #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE) @@ -2531,6 +2687,52 @@ static int PollAndReSeed(WC_RNG* rng) return ret; } + +/* Immediately reseed rng from the module's built-in or previously registered + * seed source, exactly as the WC_RESEED_INTERVAL backstop does during a + * generate operation: the gathered seed is health-tested and applied by the + * module's own reseed function, and the reseed counter is reset iff the + * reseed succeeds. If nonceSz > 0, nonce is incorporated into the same + * reseed derivation as (uncredited) additional input, with the semantics of + * wc_RNG_DRBG_Reseed_Uncredited(), in a single state transition. On failure + * the reseed counter is not reset and rng->status reflects the failure + * exactly as a generate-time reseed failure would. The caller must hold + * exclusive access to rng, as for all WC_RNG operations. */ +int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) +{ + int ret; + + if (rng == NULL) + return BAD_FUNC_ARG; + if ((nonce == NULL) && (nonceSz > 0)) + return BAD_FUNC_ARG; + + /* Mirror wc_RNG_GenerateBlock(): only an in-service DRBG may reseed. */ + if (rng->status != DRBG_OK) + return RNG_FAILURE_E; + + if (! wc_RNG_DRBG_Present(rng)) { + /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ + return 0; + } + + ret = PollAndReSeed(rng, nonce, nonceSz); + + /* Identical outcome mapping to the generate-path reseed. */ + if (ret == DRBG_SUCCESS) { + ret = 0; + } + else if (ret == WC_NO_ERR_TRACE(DRBG_CONT_FAILURE)) { + ret = DRBG_CONT_FIPS_E; + rng->status = DRBG_CONT_FAILED; + } + else { + ret = RNG_FAILURE_E; + rng->status = DRBG_FAILED; + } + + return ret; +} #endif /* place a generated block in output */ @@ -2603,7 +2805,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) if (rng->pid != getpid()) { rng->pid = getpid(); - ret = PollAndReSeed(rng); + ret = PollAndReSeed(rng, NULL, 0); if (ret != DRBG_SUCCESS) { rng->status = DRBG_FAILED; return RNG_FAILURE_E; @@ -2616,7 +2818,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, NULL, 0); if (ret == WC_NO_ERR_TRACE(DRBG_NEED_RESEED)) { - ret = PollAndReSeed(rng); + ret = PollAndReSeed(rng, NULL, 0); if (ret == DRBG_SUCCESS) ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, NULL, 0); @@ -2629,7 +2831,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) ret = Hash512_DRBG_Generate((DRBG_SHA512_internal *)rng->drbg512, output, sz, NULL, 0); if (ret == WC_NO_ERR_TRACE(DRBG_NEED_RESEED)) { - ret = PollAndReSeed(rng); + ret = PollAndReSeed(rng, NULL, 0); if (ret == DRBG_SUCCESS) ret = Hash512_DRBG_Generate( (DRBG_SHA512_internal *)rng->drbg512, output, sz, diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 0b416da560d..c727e07dfe4 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -589,6 +589,39 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); WOLFSSL_API int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz); WOLFSSL_API int wc_RNG_TestSeed(const byte* seed, word32 seedSz); + + /* Reseed-counter width tracks struct DRBG_internal above. The sentinel + * lets wolfssl/wolfcrypt/rng_bank.h supply the same typedef when building + * against a legacy FIPS random.h that predates it. */ + #ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED + #define WC_DRBG_RESEED_CTR_TYPE_DEFINED + #ifdef WORD64_AVAILABLE + typedef word64 wc_drbg_reseed_ctr_t; + #else + typedef word32 wc_drbg_reseed_ctr_t; + #endif + #endif + + /* DRBG state accessor and reseed scheduling services. These let + * applications outside the module boundary (e.g. the wc_rng_bank + * facility) observe DRBG status and reseed scheduling, mix in uncredited + * material, and request reseeds, all through defined service interfaces + * rather than by direct access to module-internal state. Pre-v7 FIPS + * boundaries lack these services; rng_bank.h supplies source-compatible + * fallbacks for those builds. */ + WOLFSSL_API int wc_RNG_GetStatus(const WC_RNG* rng); + WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); + WOLFSSL_API int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, + wc_drbg_reseed_ctr_t* reseedCtr); + WOLFSSL_API int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); + WOLFSSL_API int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, + const byte* seed, + word32 seedSz); +#ifndef CUSTOM_RAND_GENERATE_BLOCK + WOLFSSL_API int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, + word32 nonceSz); +#endif + #ifndef NO_SHA256 /* SHA-256 Hash_DRBG health test entry points. SHA-512-only builds * (NO_SHA256 + WOLFSSL_DRBG_SHA512) use wc_RNG_HealthTest_SHA512_ex From 9dd3cec1d3ff5aecaa65da76d16f6075bf401d28 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 24 Aug 2026 17:14:47 -0500 Subject: [PATCH 011/102] wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h: access DRBG state exclusively through the new random.c services; relocate the legacy direct-access mechanism to a pre-v7-FIPS-only compat block. wc_rng_bank_checkout() reseed-due divert and out-of-service gates, wc_rng_bank_seed(), and wc_rng_bank_reseed() now use wc_RNG_GetStatus() / wc_RNG_DRBG_Present() / wc_RNG_DRBG_GetReseedCtr(); the reseed walk uses wc_RNG_DRBG_Reseed_Now() when waiting is allowed (no more scratch-generate counter forcing) and wc_RNG_DRBG_ScheduleReseed() for the non-waiting arm and to preserve the pending-reseed contract after a failed forced reseed. wc_rng_bank_inst_reinit() failure now marks the instance out of service via wc_FreeRng() (deterministic WC_DRBG_NOT_INIT) instead of writing rng->status directly; _InitRng() has platform failure arms (async devctx, Versal TRNG) that return nonzero with status WC_DRBG_OK, so the explicit marking must stay, but it can be service-mediated. No consumer distinguishes WC_DRBG_FAILED from other non-OK statuses. New WC_RNG_BANK_FLAG_SEED_UNCREDITED (1<<6): wc_rng_bank_seed() mixes via wc_RNG_DRBG_Reseed_Uncredited() per instance (and masks the flag from its checkout calls); wc_rng_bank_reseed() rejects it (bank reseeds are always from the module's own source, always credited). rng_bank.h gains a HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) compat block: the historic three-combination reseedCtr/DRBG-null macros relocated verbatim, plus same-named static WC_INLINE fallbacks for the six services, so call sites are uniform across boundary versions and direct state access is confined to one labeled block that exists only where frozen boundaries cannot supply services. Legacy Reseed_Now is schedule + 4-byte generate (counter lands 2 on success; nonce is a separate follow-on uncredited transition; still-at-threshold-guarded counter restore on failure). --- wolfcrypt/src/rng_bank.c | 178 ++++++++++++++----------------- wolfssl/wolfcrypt/rng_bank.h | 196 +++++++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+), 103 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 37cfb4468ef..c23eaf14e42 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -26,69 +26,11 @@ #include #include -/* Helpers to access reseedCtr / null-check the active DRBG. The shape of - * struct WC_RNG and the DRBG_*_internal types varies by which DRBGs are - * compiled in; random.h gates the SHA-256 side on !NO_SHA256 and the SHA-512 - * side on WOLFSSL_DRBG_SHA512, so all three live combinations are handled - * separately here. */ -#if defined(WOLFSSL_DRBG_SHA512) && !defined(NO_SHA256) - /* Both DRBGs compiled in: dispatch on the runtime drbgType. */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((rng_ptr)->drbgType == WC_DRBG_SHA512) \ - ? ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - : ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - if ((rng_ptr)->drbgType == WC_DRBG_SHA512) \ - ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - = (val); \ - else \ - ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg == NULL && (rng_ptr)->drbg512 == NULL) -#elif defined(WOLFSSL_DRBG_SHA512) - /* SHA-512 DRBG only (NO_SHA256 defined); the SHA-256 struct and - * rng->drbg field do not exist in this build. */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg512 == NULL) -#else - /* SHA-256 DRBG only (the historical default). */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg == NULL) -#endif - -/* WC_DRBG_OK is used to test DRBG health in wc_rng_bank_checkout(), - * wc_rng_bank_seed(), and wc_rng_bank_reseed(). */ -#if defined(HAVE_FIPS) && (FIPS_VERSION3_LT(5,2,4) || FIPS_VERSION3_EQ(6,0,0)) - #define WC_DRBG_OK 1 -#endif - -/* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to - * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when - * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 - * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 - * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time - * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be - * defined with a (word64) cast (settings.h kernel path) that the preprocessor - * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips - * it without expanding that cast. */ -#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) - wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); -#endif +/* DRBG status and reseed-counter access, and reseed forcing, are via the + * wc_RNG_GetStatus() / wc_RNG_DRBG_*() services in wolfcrypt/src/random.c + * (FIPS v7+ and non-FIPS builds). For pre-v7 FIPS boundaries, which lack + * those services, rng_bank.h supplies source-compatible static fallbacks. + */ /* To disable retry looping in wc_rng_bank_init(), pass timeout_secs=0, and to * retry indefinitely, pass negative timeout_secs -- the flags arg here is only @@ -623,31 +565,37 @@ WOLFSSL_API int wc_rng_bank_checkout( &expected, new_lock_value)) { + wc_drbg_reseed_ctr_t cur_reseed_ctr = 0; + *rng_inst = &bank->rngs[preferred_inst_offset]; /* Two scenarios where we put an instance back and move on, both of * them only when the caller allows failover and instances remain: * - * (1) It's not in service (an earlier wc_rng_bank_inst_reinit() - * failed and set status to WC_DRBG_FAILED), or + * (1) It's not in service (a module-side failure marked it + * WC_DRBG_FAILED, or an earlier wc_rng_bank_inst_reinit() failed + * and left it WC_DRBG_NOT_INIT), or * * (2) It's due for reseed and the caller can't wait. * - * rng.status, not a NULL DRBG, is the out-of-service test. With - * HAVE_INTEL_RDRAND on an RDRAND-capable CPU, _InitRng() bypasses - * DRBG instantiation entirely and returns a usable instance whose - * drbg is NULL and whose status is WC_DRBG_OK; treating that as + * rng.status, not a missing DRBG, is the out-of-service test. + * With HAVE_INTEL_RDRAND on an RDRAND-capable CPU, _InitRng() + * bypasses DRBG instantiation entirely and returns a usable + * instance with no DRBG and status WC_DRBG_OK; treating that as * out of service would divert away from every instance in the - * bank. WC_RNG_BANK_DRBG_NULL() below is only guarding the - * reseedCtr read, which dereferences the DRBG pointer. + * bank. wc_RNG_DRBG_GetReseedCtr() reports a counter of 0 for + * such instances -- never due for reseed -- so no separate + * DRBG-presence test is needed here. */ if ((flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) && (n_rngs_tried < bank->n_rngs) && - (((*rng_inst)->rng.status != WC_DRBG_OK) || + ((wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(*rng_inst)) != + WC_DRBG_OK) || ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && - (! WC_RNG_BANK_DRBG_NULL(&(*rng_inst)->rng)) && - (WC_RNG_BANK_RESEED_CTR(&(*rng_inst)->rng) >= - WC_RESEED_INTERVAL)))) + (wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(*rng_inst), + &cur_reseed_ctr) == 0) && + (cur_reseed_ctr >= WC_RESEED_INTERVAL)))) { WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); *rng_inst = NULL; @@ -655,9 +603,10 @@ WOLFSSL_API int wc_rng_bank_checkout( else { #ifdef WC_VERBOSE_RNG if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && - (! WC_RNG_BANK_DRBG_NULL(&(*rng_inst)->rng)) && - (WC_RNG_BANK_RESEED_CTR(&(*rng_inst)->rng) >= - WC_RESEED_INTERVAL)) + (wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(*rng_inst), + &cur_reseed_ctr) == 0) && + (cur_reseed_ctr >= WC_RESEED_INTERVAL)) { WOLFSSL_DEBUG_PRINTF( "WARNING: wc_rng_bank_checkout() returning RNG ID %d, " @@ -1094,13 +1043,17 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( out: /* Leave a failed instance explicitly out of service rather than relying - * on whichever status _InitRng() happened to leave behind. The DRBG is - * NULL at this point; wc_rng_bank_checkout() diverts away from such an - * instance when the caller allows failover, and the seed/reseed walks - * refuse it. + * on whichever status _InitRng() happened to leave behind (some of its + * platform-specific failure paths return with status WC_DRBG_OK). + * wc_FreeRng() deterministically leaves status WC_DRBG_NOT_INIT -- out of + * service under every status-gate in this facility -- and is idempotent + * on the failed-init carcass, so the marking happens entirely through + * the module's own service interface. wc_rng_bank_checkout() diverts + * away from such an instance when the caller allows failover, and the + * seed/reseed walks refuse it. */ if (ret != 0) - rng_inst->rng.status = WC_DRBG_FAILED; + (void)wc_FreeRng(WC_RNG_BANK_INST_TO_RNG(rng_inst)); return ret; } @@ -1152,7 +1105,9 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, */ for (n = bank->n_rngs - 1; n >= 0; --n) { struct wc_rng_bank_inst *drbg; - ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, flags); + ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, + flags & ~(word32) + WC_RNG_BANK_FLAG_SEED_UNCREDITED); if (ret != 0) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( @@ -1168,16 +1123,23 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, * itself (random.c returns success for a NULL DRBG under RDRAND), so * let it through rather than calling it an error. */ - else if (drbg->rng.status != WC_DRBG_OK) { + else if (wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(drbg)) != + WC_DRBG_OK) + { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( "WARNING: wc_rng_bank_seed(): inst#%d is out of service " - "(status %d).\n", n, (int)drbg->rng.status); + "(status %d).\n", n, + wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(drbg))); #endif ret = BAD_STATE_E; } - else if ((ret = wc_RNG_DRBG_Reseed(WC_RNG_BANK_INST_TO_RNG(drbg), seed, - seedSz)) != 0) + else if ((ret = ((flags & WC_RNG_BANK_FLAG_SEED_UNCREDITED) + ? wc_RNG_DRBG_Reseed_Uncredited( + WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz) + : wc_RNG_DRBG_Reseed( + WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz))) + != 0) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( @@ -1214,9 +1176,13 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, /* wc_rng_bank_reseed() must walk every instance by explicit index -- forbid * flags that would let wc_rng_bank_checkout() pick a different instance * than requested. Same restriction applies in wc_rng_bank_seed(). + * WC_RNG_BANK_FLAG_SEED_UNCREDITED applies only to wc_rng_bank_seed() -- + * a bank reseed is always from the module's own seed source, and always + * credited. */ if (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | - WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST)) + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | + WC_RNG_BANK_FLAG_SEED_UNCREDITED)) return BAD_FUNC_ARG; if (bank == NULL) { @@ -1244,36 +1210,32 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, if (ret != 0) goto out; - if (drbg->rng.status != WC_DRBG_OK) { + if (wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(drbg)) != WC_DRBG_OK) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( "WARNING: wc_rng_bank_reseed(): inst#%d is out of service " - "(status %d).\n", n, (int)drbg->rng.status); + "(status %d).\n", n, + wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(drbg))); #endif (void)wc_rng_bank_checkin(bank, &drbg); ret = BAD_STATE_E; goto out; } - /* The store below writes through the DRBG pointer. An in-service - * instance can still have none: _InitRng() bypasses DRBG - * instantiation when the CPU has RDRAND (HAVE_INTEL_RDRAND). There - * is no reseed counter to force in that case, and nothing to reseed; - * skip, do not fail. + /* An in-service instance can still have no DRBG: _InitRng() bypasses + * DRBG instantiation when the CPU has RDRAND (HAVE_INTEL_RDRAND). + * There is nothing to reseed in that case; skip, do not fail. */ - if (WC_RNG_BANK_DRBG_NULL(&drbg->rng)) { + if (! wc_RNG_DRBG_Present(WC_RNG_BANK_INST_TO_RNG(drbg))) { (void)wc_rng_bank_checkin(bank, &drbg); continue; } - WC_RNG_BANK_SET_RESEED_CTR(&drbg->rng, WC_RESEED_INTERVAL); - if (flags & WC_RNG_BANK_FLAG_CAN_WAIT) { - byte scratch[4]; for (;;) { time_t ts2; - ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(drbg), scratch, - (word32)sizeof(scratch)); + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), + NULL, 0); if (ret == 0) break; if ((timeout_secs == 0) || @@ -1298,10 +1260,16 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, break; WC_RELAX_LONG_LOOP(); } + if (ret != 0) { + /* Preserve the pre-existing contract: a failed forced reseed + * leaves the instance due for reseed, so the next generate + * operation retries it in-boundary. */ + (void)wc_RNG_DRBG_ScheduleReseed(WC_RNG_BANK_INST_TO_RNG(drbg)); + } #ifdef WC_VERBOSE_RNG if ((ret != 0) && (ret != WC_NO_ERR_TRACE(WC_TIMEOUT_E))) WOLFSSL_DEBUG_PRINTF( - "ERROR: wc_crng_reseed() wc_RNG_GenerateBlock() " + "ERROR: wc_rng_bank_reseed() wc_RNG_DRBG_Reseed_Now() " "for DRBG #%d returned %d.", n, ret); #endif (void)wc_rng_bank_checkin(bank, &drbg); @@ -1316,6 +1284,10 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, WC_RELAX_LONG_LOOP(); } else { + /* Cannot gather entropy without waiting -- mark the instance due + * for reseed and let the next entropy-capable generate operation + * perform it in-boundary. */ + (void)wc_RNG_DRBG_ScheduleReseed(WC_RNG_BANK_INST_TO_RNG(drbg)); (void)wc_rng_bank_checkin(bank, &drbg); } } diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 4045d86e475..147d84009e6 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -47,12 +47,208 @@ #define WC_RNG_BANK_FLAG_NO_VECTOR_OPS (1<<3) #define WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST (1<<4) #define WC_RNG_BANK_FLAG_AFFINITY_LOCK (1<<5) +/* WC_RNG_BANK_FLAG_SEED_UNCREDITED applies only to wc_rng_bank_seed(): the + * supplied seed material is mixed into each instance without entropy credit + * (wc_RNG_DRBG_Reseed_Uncredited()), leaving the reseed schedule governed + * solely by the module's own seed source. */ +#define WC_RNG_BANK_FLAG_SEED_UNCREDITED (1<<6) #define WC_RNG_BANK_INST_LOCK_FREE 0 #define WC_RNG_BANK_INST_LOCK_HELD (1<<0) #define WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED (1<<1) #define WC_RNG_BANK_INST_LOCK_VEC_OPS_INH (1<<2) +/* ---- Legacy FIPS boundary compatibility -------------------------------- + * + * Pre-v7 FIPS boundaries do not export the DRBG accessor and reseed + * scheduling services that wolfcrypt/src/random.c supplies as of FIPS v7 + * (wc_RNG_GetStatus(), wc_RNG_DRBG_Present(), wc_RNG_DRBG_GetReseedCtr(), + * wc_RNG_DRBG_ScheduleReseed(), wc_RNG_DRBG_Reseed_Uncredited(), and + * wc_RNG_DRBG_Reseed_Now()). Supply source-compatible static fallbacks + * here, implemented via the public DRBG struct definitions in the legacy + * random.h. These fallbacks are the historic direct-access mechanism, now + * confined to frozen pre-v7 boundaries, which cannot gain new services; + * wherever the in-boundary services exist, they are used instead. + */ +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + +#include + +#ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED +#define WC_DRBG_RESEED_CTR_TYPE_DEFINED + #ifdef WORD64_AVAILABLE + typedef word64 wc_drbg_reseed_ctr_t; + #else + typedef word32 wc_drbg_reseed_ctr_t; + #endif +#endif + +#if FIPS_VERSION3_LT(5,2,4) || FIPS_VERSION3_EQ(6,0,0) + /* WC_DRBG_OK predates these FIPS random.h editions. */ + #define WC_DRBG_OK 1 +#endif + +/* Helpers to access reseedCtr / null-check the active DRBG. The shape of + * struct WC_RNG and the DRBG_*_internal types varies by which DRBGs are + * compiled in; random.h gates the SHA-256 side on !NO_SHA256 and the SHA-512 + * side on WOLFSSL_DRBG_SHA512, so all three live combinations are handled + * separately here. */ +#if defined(WOLFSSL_DRBG_SHA512) && !defined(NO_SHA256) + /* Both DRBGs compiled in: dispatch on the runtime drbgType. */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((rng_ptr)->drbgType == WC_DRBG_SHA512) \ + ? ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + : ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + if ((rng_ptr)->drbgType == WC_DRBG_SHA512) \ + ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + = (val); \ + else \ + ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg == NULL && (rng_ptr)->drbg512 == NULL) +#elif defined(WOLFSSL_DRBG_SHA512) + /* SHA-512 DRBG only (NO_SHA256 defined); the SHA-256 struct and + * rng->drbg field do not exist in this build. */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg512 == NULL) +#else + /* SHA-256 DRBG only (the historical default). */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg == NULL) +#endif + +/* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to + * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when + * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 + * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 + * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time + * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be + * defined with a (word64) cast (settings.h kernel path) that the preprocessor + * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips + * it without expanding that cast. */ +#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) + wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); +#endif + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_GetStatus(const WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + return (int)rng->status; +} + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Present(const WC_RNG* rng) +{ + return (rng != NULL) && (! WC_RNG_BANK_DRBG_NULL(rng)); +} + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_GetReseedCtr( + const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr) +{ + if ((rng == NULL) || (reseedCtr == NULL)) + return BAD_FUNC_ARG; + if (WC_RNG_BANK_DRBG_NULL(rng)) + *reseedCtr = 0; + else + *reseedCtr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + return 0; +} + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + if (! WC_RNG_BANK_DRBG_NULL(rng)) + WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); + return 0; +} + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Uncredited( + WC_RNG* rng, const byte* seed, word32 seedSz) +{ + wc_drbg_reseed_ctr_t saved_ctr; + int ret; + + if ((rng == NULL) || (seed == NULL)) + return BAD_FUNC_ARG; + if (WC_RNG_BANK_DRBG_NULL(rng)) { + /* defer to wc_RNG_DRBG_Reseed()'s RDRAND-config handling. */ + return wc_RNG_DRBG_Reseed(rng, seed, seedSz); + } + saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + ret = wc_RNG_DRBG_Reseed(rng, seed, seedSz); + /* wc_RNG_DRBG_Reseed() only resets the counter on success, so the + * unconditional restore is exact either way. */ + WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); + return ret; +} + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Now( + WC_RNG* rng, const byte* nonce, word32 nonceSz) +{ + wc_drbg_reseed_ctr_t saved_ctr; + int ret; + byte scratch[4]; + + if (rng == NULL) + return BAD_FUNC_ARG; + if ((nonce == NULL) && (nonceSz > 0)) + return BAD_FUNC_ARG; + if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) + return RNG_FAILURE_E; + if (WC_RNG_BANK_DRBG_NULL(rng)) { + /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ + return 0; + } + + saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); + + /* The legacy boundary has no direct reseed-from-source service; a + * minimal generate at the forced counter performs the module's own + * PollAndReSeed() in-boundary. This consumes 4 bytes of output, so on + * success the fresh reseed counter is 2 rather than 1. scratch holds + * only discarded output bytes; XMEMSET suffices for it here. */ + ret = wc_RNG_GenerateBlock(rng, scratch, (word32)sizeof(scratch)); + XMEMSET(scratch, 0, sizeof(scratch)); + + if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { + /* On the legacy boundary, nonce incorporation is a separate + * (uncredited) transition following the reseed, rather than part of + * the same reseed derivation. */ + ret = wc_RNG_DRBG_Reseed_Uncredited(rng, nonce, nonceSz); + } + + if ((ret != 0) && + ((wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng) >= + (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL)) + { + /* The reseed did not occur -- restore the counter, leaving it + * unmodified as the contract requires. */ + WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); + } + + return ret; +} + +#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ + typedef int (*wc_affinity_lock_fn_t)(void *arg); typedef int (*wc_affinity_get_id_fn_t)(void *arg, int *id); typedef int (*wc_affinity_unlock_fn_t)(void *arg); From a2cc195533c4d69d12a952a4fafef6d5e1b06b70 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 24 Aug 2026 17:20:49 -0500 Subject: [PATCH 012/102] linuxkm/lkcapi_sha_glue.c: use the FIPS DRBG services; map kernel-supplied seed material as uncredited, per mainline crypto/drbg.c Delete the local copy of the reseedCtr/DRBG-null macros (now in rng_bank.h's pre-v7 compat block); zero direct DRBG state access remains on the v7 path. wc_linuxkm_drbg_generate(): generate-op src now mixes via wc_RNG_DRBG_Reseed_Uncredited() (crypto/drbg.c passes the same input as SP 800-90A additional input, never crediting it); the opportunistic early reseed observes wc_RNG_DRBG_GetReseedCtr() and calls wc_RNG_DRBG_Reseed_Now() inside the existing SVR release/reestablish bracket -- counter save/poke/restore and the scratch generate are gone, since Reseed_Now resets the counter iff the reseed succeeds. wc_linuxkm_drbg_seed(): pass WC_RNG_BANK_FLAG_SEED_UNCREDITED, so crypto_rng_reset() with caller data no longer resets reseed schedules. wc_mix_pool_bytes(): wc_RNG_DRBG_Present() + wc_RNG_DRBG_Reseed_Uncredited(), dropping the counter save/restore. --- linuxkm/lkcapi_sha_glue.c | 102 ++++++++++++++------------------------ 1 file changed, 36 insertions(+), 66 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 7e37e1c9c7d..e30f724d3e6 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2284,46 +2284,6 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRef(WC_RNG* rng) { #endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT && HAVE_HASHDRBG */ -#if defined(WOLFSSL_DRBG_SHA512) && !defined(NO_SHA256) - /* Both DRBGs compiled in: dispatch on the runtime drbgType. */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((rng_ptr)->drbgType == WC_DRBG_SHA512) \ - ? ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - : ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - if ((rng_ptr)->drbgType == WC_DRBG_SHA512) \ - ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - = (val); \ - else \ - ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg == NULL && (rng_ptr)->drbg512 == NULL) -#elif defined(WOLFSSL_DRBG_SHA512) - /* SHA-512 DRBG only (NO_SHA256 defined); the SHA-256 struct and - * rng->drbg field do not exist in this build. */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg512 == NULL) -#else - /* SHA-256 DRBG only (the historical default). */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg == NULL) -#endif - static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, const u8 *src, unsigned int slen, u8 *dst, unsigned int dlen) @@ -2332,6 +2292,7 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, /* can_block() is false whenever the affinity lock is held -- blockability * must be sampled before checkout. */ int can_wait = wc_linuxkm_can_block(); + wc_drbg_reseed_ctr_t cur_counter = 0; struct wc_rng_bank_inst *drbg = linuxkm_get_drbg(ctx); if (! drbg) { @@ -2340,21 +2301,24 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, } if (slen > 0) { - ret = wc_RNG_DRBG_Reseed(WC_RNG_BANK_INST_TO_RNG(drbg), src, slen); + /* The kernel crypto API's generate-op src is additional data (cf. + * crypto/drbg.c, which passes it as SP 800-90A additional input). + * Mix it in without entropy credit -- the reseed counter is + * unmodified, so only the module's own seed source resets the + * reseed schedule. */ + ret = wc_RNG_DRBG_Reseed_Uncredited(WC_RNG_BANK_INST_TO_RNG(drbg), + src, slen); if (ret != 0) { - pr_warn_once("WARNING: wc_RNG_DRBG_Reseed returned %d\n",ret); + pr_warn_once("WARNING: wc_RNG_DRBG_Reseed_Uncredited returned %d\n",ret); ret = -EINVAL; goto out; } } - else if ((! WC_RNG_BANK_DRBG_NULL(WC_RNG_BANK_INST_TO_RNG(drbg))) && - (WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)) > WC_RESEED_INTERVAL / 2) && - can_wait) + else if (can_wait && + (wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(drbg), + &cur_counter) == 0) && + (cur_counter > WC_RESEED_INTERVAL / 2)) { - byte scratch[4]; - word64 cur_counter = WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)); - WC_RNG_BANK_SET_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg), WC_RESEED_INTERVAL); - #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS /* carefully restore preemptibility for the reseed operation. */ @@ -2369,8 +2333,14 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); #endif - ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(drbg), scratch, - (word32)sizeof scratch); + /* Reseed immediately from the module's own seed source. + * wc_RNG_DRBG_Reseed_Now() resets the reseed counter iff the reseed + * succeeds; on failure it leaves the counter unmodified (the + * WC_RESEED_INTERVAL backstop still governs) and marks the instance + * out of service, exactly as an interval-forced reseed failure + * would. A persistent failure is surfaced by the generate loop + * below. */ + (void)wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { @@ -2389,10 +2359,6 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, #endif #endif - if ((ret != 0) && (WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)) >= WC_RESEED_INTERVAL)) { - WC_RNG_BANK_SET_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg), cur_counter + 1); - } - ForceZero(scratch, sizeof scratch); } for (;;) { @@ -2475,7 +2441,7 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, if (ret != 0) { pr_err_ratelimited("ERROR: wc_linuxkm_drbg_generate() failing on wolfCrypt code %d.\n",ret); - ret = -EINVAL; + ret = -EIO; } out: @@ -2507,7 +2473,14 @@ static int wc_linuxkm_drbg_seed(struct wc_rng_bank *ctx, if (slen == 0) return 0; - ret = wc_rng_bank_seed(ctx, seed, slen, WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT); + /* The kernel crypto API's seed op carries caller-supplied material (cf. + * crypto/drbg.c, which maps it to an SP 800-90A personalization string / + * additional input, never crediting it as entropy). Mix it into every + * instance without credit; the reseed schedule stays governed solely by + * the module's own seed source. */ + ret = wc_rng_bank_seed(ctx, seed, slen, WC_LINUXKM_INITRNG_TIMEOUT_SEC, + WC_RNG_BANK_FLAG_CAN_WAIT | + WC_RNG_BANK_FLAG_SEED_UNCREDITED); if (ret != 0) { pr_err("wc_rng_bank_seed() in wc_linuxkm_drbg_seed() returned err %d.\n", ret); ret = -EINVAL; @@ -2724,7 +2697,6 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST; struct wc_rng_bank_inst *drbg = NULL; - word64 cur_counter; if (len > WC_MAX_UINT_OF(word32)) return -EFBIG; @@ -2750,21 +2722,19 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { goto out; } - if (WC_RNG_BANK_DRBG_NULL(WC_RNG_BANK_INST_TO_RNG(drbg))) { + if (! wc_RNG_DRBG_Present(WC_RNG_BANK_INST_TO_RNG(drbg))) { ret = 0; /* consistent with wc_RNG_DRBG_Reseed() behavior in RDRAND configs. */ goto out; } - cur_counter = WC_RNG_BANK_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg)); - - ret = wc_RNG_DRBG_Reseed(WC_RNG_BANK_INST_TO_RNG(drbg), buf, (word32)len); + /* Mix without crediting the contributed entropy -- + * wc_RNG_DRBG_Reseed_Uncredited() leaves the reseed counter unmodified, + * so only the module's own seed source resets the reseed schedule. */ + ret = wc_RNG_DRBG_Reseed_Uncredited(WC_RNG_BANK_INST_TO_RNG(drbg), buf, + (word32)len); if (ret != 0) ret = -EINVAL; - /* Unconditionally restore the reseed counter -- don't credit the - * contributed entropy. */ - WC_RNG_BANK_SET_RESEED_CTR(WC_RNG_BANK_INST_TO_RNG(drbg), cur_counter); - out: if (drbg) From 2f7bb1de9cab275c81b7dc9ac1ee7714cd61d90b Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 24 Aug 2026 17:21:14 -0500 Subject: [PATCH 013/102] linuxkm/lkcapi_sha_glue.c: fail closed on userspace randomness, and log panic-adjacent, when the FIPS RNG cannot serve (WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH) HAVE_FIPS now implies WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH (no opt-out). Under it, wc_get_random_bytes_user() and wc_extract_crng_user() return -EIO -- final, no fallthrough to native randomness -- on checkout or generate failure, with pr_emerg_ratelimited records. In a FIPS setting an RNG failure in a must-succeed RNG call is a system-unusable condition; emerg-over- panic() is deliberate, so the operator can restart in a controlled fashion (glibc getrandom()/arc4random consumers go fatal on -EIO, which is the intended blast radius). wc__get_random_bytes() remains must-succeed in every flavor -- the kernel patch's dispatch point serves the request from the native DRBG on any nonzero return, honoring the get_random_bytes() unconditional-success contract -- but the event is now recorded loudly (emerg under the knob, err otherwise, never silent). Checkout-failure logging on the user paths is un-gated from WC_VERBOSE_RNG; in-loop generate failures become ratelimited (the paths are unprivileged-hammerable). wc_linuxkm_drbg_generate()'s tail conversion becomes -EIO (RNG- failure class) instead of -EINVAL, keeping the slen>0 arm's argument-class -EINVAL distinct; no wolfCrypt-domain code reaches the kernel interface. wc_extract_crng_user()'s tail is reworked to convert live generate failures (previously the raw wolfCrypt-mapped code leaked out, and only an unreachable ret==0 arm was handled), excluding -EFAULT so first-block copy_to_user() faults keep their honest code; partial-copy-then-failure returns final -EIO in both flavors, required because the -ECANCELED fallthrough contract promises an untouched iterator. --- linuxkm/lkcapi_sha_glue.c | 71 ++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index e30f724d3e6..3428f2e2fe7 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2539,6 +2539,10 @@ static int wc_linuxkm_drbg_loaded = 0; #ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS +#if defined(HAVE_FIPS) && !defined(WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH) + #define WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH +#endif + static int wc__get_random_bytes(void *buf, size_t len) { struct wc_rng_bank *current_default_wc_rng_bank; @@ -2549,15 +2553,26 @@ static int wc__get_random_bytes(void *buf, size_t len) ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { - pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc__get_random_bytes() returned %d.\n", ret); - return -EFAULT; +#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH + pr_emerg_ratelimited("ERROR: FIPS RNG source failed in wc__get_random_bytes(): wc_rng_bank_default_checkout() returned %d.\n", ret); +#else + pr_err_ratelimited("ERROR: FIPS RNG source failed in wc__get_random_bytes(): wc_rng_bank_default_checkout() returned %d.\n", ret); +#endif + /* kernel must-succeed call used from hard IRQ contexts etc. -- the + * callback dispatch point will always fall through to native DRBG, but + * we log the condition loudly from here. */ + return -ECANCELED; } else { ret = wc_linuxkm_drbg_generate(current_default_wc_rng_bank, NULL, 0, buf, (unsigned int)len); (void)wc_rng_bank_default_checkin(¤t_default_wc_rng_bank); if (ret) { - pr_err_ratelimited("ERROR: wc__get_random_bytes(): wc_linuxkm_drbg_generate() failed with code %d.\n", ret); +#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH + pr_emerg_ratelimited("ERROR: FIPS RNG source failed: wc__get_random_bytes(): wc_linuxkm_drbg_generate() failed with code %d.\n", ret); +#else + pr_err_ratelimited("ERROR: FIPS RNG source failed: wc__get_random_bytes(): wc_linuxkm_drbg_generate() failed with code %d.\n", ret); +#endif } return ret; } @@ -2573,10 +2588,13 @@ static ssize_t wc_get_random_bytes_user(struct iov_iter *iter) { ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { -#ifdef WC_VERBOSE_RNG +#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH + pr_emerg_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_get_random_bytes_user() returned %ld.\n", ret); + return -EIO; /* no fallthrough to native randomness */ +#else pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_get_random_bytes_user() returned %ld.\n", ret); + return -ECANCELED; /* fallthrough to native randomness */ #endif - return -ECANCELED; } else { size_t this_copied, total_copied = 0; @@ -2586,7 +2604,11 @@ static ssize_t wc_get_random_bytes_user(struct iov_iter *iter) { ret = wc_linuxkm_drbg_generate(current_default_wc_rng_bank, NULL, 0, block, sizeof block); if (unlikely(ret != 0)) { - pr_err("ERROR: wc_get_random_bytes_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); +#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH + pr_emerg_ratelimited("ERROR: wc_get_random_bytes_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); +#else + pr_err_ratelimited("ERROR: wc_get_random_bytes_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); +#endif break; } @@ -2614,8 +2636,13 @@ static ssize_t wc_get_random_bytes_user(struct iov_iter *iter) { if (total_copied == 0) { if (ret == 0) ret = -EFAULT; - else + else { +#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH + ret = -EIO; +#else ret = -ECANCELED; +#endif + } } if (ret == 0) @@ -2635,10 +2662,13 @@ static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) { ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { -#ifdef WC_VERBOSE_RNG +#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH + pr_emerg_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_extract_crng_user() returned %ld.\n", ret); + return -EIO; /* no fallthrough to native randomness */ +#else pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_extract_crng_user() returned %ld.\n", ret); + return -ECANCELED; /* fallthrough to native randomness */ #endif - return -ECANCELED; } else { size_t this_copied, total_copied = 0; @@ -2648,7 +2678,11 @@ static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) { ret = wc_linuxkm_drbg_generate(current_default_wc_rng_bank, NULL, 0, block, sizeof block); if (unlikely(ret != 0)) { - pr_err("ERROR: wc_extract_crng_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); +#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH + pr_emerg_ratelimited("ERROR: wc_extract_crng_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); +#else + pr_err_ratelimited("ERROR: wc_extract_crng_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); +#endif break; } @@ -2675,8 +2709,21 @@ static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) { ForceZero(block, sizeof(block)); - if ((total_copied == 0) && (ret == 0)) { - ret = -ECANCELED; + if (total_copied == 0) { + if (ret == 0) { + /* Not reachable -- the loop always copies at least one + * block before any zero-status exit -- but keep the belt. + */ + ret = -EFAULT; + } + else if (ret != -EFAULT) { + /* Generate failure with nothing delivered. */ +#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH + ret = -EIO; /* no fallthrough to native randomness */ +#else + ret = -ECANCELED; /* fallthrough to native randomness */ +#endif + } } if (ret == 0) From 542697d49143c067e1fbc62935af562c1dc7fac3 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 24 Aug 2026 17:22:46 -0500 Subject: [PATCH 014/102] wolfcrypt/src/{rsa,ecc,curve25519}.c, wolfssl/wolfcrypt/{rsa,ecc,curve25519}.h: add wc_RsaClearRNG(), wc_ecc_clear_rng(), wc_curve25519_clear_rng(). Companions to the three existing RNG setters (the complete set in wolfcrypt), so a caller can detach a key's RNG association through a dedicated API; the setters are unchanged, in particular wc_RsaSetRNG() retains BAD_FUNC_ARG on a NULL rng. Each companion is declared beside its setter under the same gate and mirrors its shape (wc_ecc_clear_rng() keeps the success-no-op stub arm when !ECC_TIMING_RESISTANT). A cleared key fails RNG-requiring operations safely: MISSING_RNG_E on RSA blinding and ECC timing-resistant paths, BAD_FUNC_ARG from the RNG service on curve25519 blinding; the RSA pairwise consistency test falls back to a locally instantiated RNG. For each key type the member written is the sole storage site -- no subordinate object holds a copy -- so clearing the top level detaches everything. Enables checkout / set / operate / clear / checkin usage of wc_rng_bank instances against per-object RNG consumers. FIPS wrapper plumbing for the three new symbols to follow with the DRBG service wrappers. wolfcrypt/src/ecc.c: in wc_ecc_encrypt_ex() and wc_ecc_decrypt(), restore privKey->rng after lending ctx->rng for the operation Both functions implanted ctx->rng into privKey->rng when the ctx had an RNG and the key did not (ECC_TIMING_RESISTANT needs it in the shared-secret point multiply), and never removed it -- the caller's key left the call holding a borrowed pointer into the ecEncCtx's RNG, dangling once the ctx or its RNG is freed, and silently re-attaching an RNG behind the back of any caller practicing set/operate/clear hygiene. Track the lend (lentRng) and restore privKey->rng = NULL at every post-implant exit, including both WOLFSSL_SMALL_STACK allocation-failure arms and both tails. Callers that pre-set the key's RNG are unaffected. --- wolfcrypt/src/curve25519.c | 11 +++++ wolfcrypt/src/ecc.c | 87 ++++++++++++++++++++++++++++++++-- wolfcrypt/src/rsa.c | 14 ++++++ wolfssl/wolfcrypt/curve25519.h | 2 + wolfssl/wolfcrypt/ecc.h | 2 + wolfssl/wolfcrypt/rsa.h | 1 + 6 files changed, 112 insertions(+), 5 deletions(-) diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 9cf5cb08d72..13a7a98f234 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -1436,6 +1436,17 @@ int wc_curve25519_set_rng(curve25519_key* key, WC_RNG* rng) key->rng = rng; return 0; } + +/* Companion to wc_curve25519_set_rng(): detach the key's RNG association. + * Subsequent blinded operations then fail in the RNG service (BAD_FUNC_ARG + * on the NULL WC_RNG) until a new one is set. */ +int wc_curve25519_clear_rng(curve25519_key* key) +{ + if (key == NULL) + return BAD_FUNC_ARG; + key->rng = NULL; + return 0; +} #endif /* get key size */ diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 4609def586c..078b288e167 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -15101,6 +15101,28 @@ int wc_ecc_set_rng(ecc_key* key, WC_RNG* rng) return err; } +/* Companion to wc_ecc_set_rng(): detach the key's RNG association. + * Subsequent operations that require the key's RNG then fail with + * MISSING_RNG_E until a new one is set. */ +int wc_ecc_clear_rng(ecc_key* key) +{ + int err = 0; + +#ifdef ECC_TIMING_RESISTANT + if (key == NULL) { + err = BAD_FUNC_ARG; + } + else { + key->rng = NULL; + } +#else + (void)key; + /* report success, not an error if ECC_TIMING_RESISTANT is not defined */ +#endif + + return err; +} + #ifdef HAVE_ECC_ENCRYPT @@ -15760,6 +15782,9 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, /* Device for the ECIES callback and the KDF/AES/HMAC steps. It comes * only from the context; unset means software, or the CB_FIND finder. */ int eciesDevId = INVALID_DEVID; +#ifdef ECC_TIMING_RESISTANT + int lentRng = 0; /* ctx->rng lent to privKey for this op */ +#endif if (privKey == NULL || pubKey == NULL || msg == NULL || out == NULL || outSz == NULL) @@ -15848,8 +15873,14 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, return BUFFER_E; #ifdef ECC_TIMING_RESISTANT - if (ctx->rng != NULL && privKey->rng == NULL) + if (ctx->rng != NULL && privKey->rng == NULL) { + /* Lend the ctx's RNG to the key for the duration of this operation + * only. Restored to NULL before every subsequent return, so no + * borrowed pointer survives on the caller's key object after the + * call. */ privKey->rng = ctx->rng; + lentRng = 1; + } #endif #ifndef WOLFSSL_ECIES_OLD @@ -15859,23 +15890,42 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, #else ret = wc_ecc_make_pub_ex(privKey, NULL, NULL); #endif - if (ret != 0) + if (ret != 0) { + #ifdef ECC_TIMING_RESISTANT + if (lentRng) + privKey->rng = NULL; + #endif return ret; + } } ret = wc_ecc_export_x963_ex(privKey, out, &pubKeySz, compressed); - if (ret != 0) + if (ret != 0) { + #ifdef ECC_TIMING_RESISTANT + if (lentRng) + privKey->rng = NULL; + #endif return ret; + } out += pubKeySz; #endif #ifdef WOLFSSL_SMALL_STACK sharedSecret = (byte*)XMALLOC(sharedSz, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); - if (sharedSecret == NULL) + if (sharedSecret == NULL) { + #ifdef ECC_TIMING_RESISTANT + if (lentRng) + privKey->rng = NULL; + #endif return MEMORY_E; + } keys = (byte*)XMALLOC(ECC_BUFSIZE, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); if (keys == NULL) { XFREE(sharedSecret, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); + #ifdef ECC_TIMING_RESISTANT + if (lentRng) + privKey->rng = NULL; + #endif return MEMORY_E; } #endif @@ -16160,6 +16210,11 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, WC_FREE_VAR_EX(sharedSecret, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); WC_FREE_VAR_EX(keys, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); +#ifdef ECC_TIMING_RESISTANT + if (lentRng) + privKey->rng = NULL; +#endif + return ret; } @@ -16218,6 +16273,9 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, /* Device for the ECIES callback and the KDF/AES/HMAC steps. It comes * only from the context; unset means software, or the CB_FIND finder. */ int eciesDevId = INVALID_DEVID; +#ifdef ECC_TIMING_RESISTANT + int lentRng = 0; /* ctx->rng lent to privKey for this op */ +#endif if (privKey == NULL || msg == NULL || out == NULL || outSz == NULL) @@ -16347,8 +16405,14 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, } #ifdef ECC_TIMING_RESISTANT - if (ctx->rng != NULL && privKey->rng == NULL) + if (ctx->rng != NULL && privKey->rng == NULL) { + /* Lend the ctx's RNG to the key for the duration of this operation + * only. Restored to NULL before every subsequent return, so no + * borrowed pointer survives on the caller's key object after the + * call. */ privKey->rng = ctx->rng; + lentRng = 1; + } #endif #ifdef WOLFSSL_SMALL_STACK @@ -16357,6 +16421,10 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, #ifndef WOLFSSL_ECIES_OLD if (pubKey == peerKey) wc_ecc_free(peerKey); + #endif + #ifdef ECC_TIMING_RESISTANT + if (lentRng) + privKey->rng = NULL; #endif return MEMORY_E; } @@ -16367,6 +16435,10 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, #ifndef WOLFSSL_ECIES_OLD if (pubKey == peerKey) wc_ecc_free(peerKey); + #endif + #ifdef ECC_TIMING_RESISTANT + if (lentRng) + privKey->rng = NULL; #endif return MEMORY_E; } @@ -16675,6 +16747,11 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, XFREE(keys, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); #endif +#ifdef ECC_TIMING_RESISTANT + if (lentRng) + privKey->rng = NULL; +#endif + return ret; } diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 69d292aa2a8..f498bc7914b 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -6177,6 +6177,20 @@ int wc_RsaSetRNG(RsaKey* key, WC_RNG* rng) return 0; } + +/* Companion to wc_RsaSetRNG(): detach the key's RNG association. + * Subsequent operations that require the key's RNG (blinding, pairwise + * consistency) then either fail with MISSING_RNG_E or fall back to a + * locally instantiated RNG, per operation, until a new RNG is set. */ +int wc_RsaClearRNG(RsaKey* key) +{ + if (key == NULL) + return BAD_FUNC_ARG; + + key->rng = NULL; + + return 0; +} #endif /* !WC_NO_RNG */ #ifdef WC_RSA_NONBLOCK diff --git a/wolfssl/wolfcrypt/curve25519.h b/wolfssl/wolfcrypt/curve25519.h index 64978403411..ec1db0b69e8 100644 --- a/wolfssl/wolfcrypt/curve25519.h +++ b/wolfssl/wolfcrypt/curve25519.h @@ -222,6 +222,8 @@ void wc_curve25519_free(curve25519_key* key); #ifdef WOLFSSL_CURVE25519_BLINDING WOLFSSL_API int wc_curve25519_set_rng(curve25519_key* key, WC_RNG* rng); +WOLFSSL_API +int wc_curve25519_clear_rng(curve25519_key* key); #endif #ifndef WC_NO_CONSTRUCTORS diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index efa57e4290f..072910a574c 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -898,6 +898,8 @@ WOLFSSL_API void wc_ecc_fp_init(void); WOLFSSL_API int wc_ecc_set_rng(ecc_key* key, WC_RNG* rng); +WOLFSSL_API +int wc_ecc_clear_rng(ecc_key* key); WOLFSSL_API int wc_ecc_set_curve(ecc_key* key, int keysize, int curve_id); diff --git a/wolfssl/wolfcrypt/rsa.h b/wolfssl/wolfcrypt/rsa.h index 0d0b0cecb88..3ec1e3f2d19 100644 --- a/wolfssl/wolfcrypt/rsa.h +++ b/wolfssl/wolfcrypt/rsa.h @@ -447,6 +447,7 @@ WOLFSSL_API int wc_RsaPublicKeyDecodeRaw(const byte* n, word32 nSz, #ifndef WC_NO_RNG WOLFSSL_API int wc_RsaSetRNG(RsaKey* key, WC_RNG* rng); + WOLFSSL_API int wc_RsaClearRNG(RsaKey* key); #endif #ifdef WC_RSA_NONBLOCK WOLFSSL_API int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb); From cba2551278af1cadc7322d46167a516e222c0c5c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 24 Aug 2026 17:39:36 -0500 Subject: [PATCH 015/102] linuxkm/lkcapi_sha_glue.c: in wc_linuxkm_drbg_generate()'s reseed and reinit brackets, release and re-establish the scheduling locks per-level -- an affinity-locked checkout with WC_RNG_BANK_FLAG_NO_VECTOR_OPS (from the caller's flags or bank-wide bank->flags) holds BOTH the affinity save and the vector-ops inhibit, and the single flavor-sniffing release only popped the innermost level, while the re-establishment failure arm cleared both lock bits including the never-released affinity hold, leaving checkin unable to unwind it (leaked bh-off). Release now mirrors wc_rng_bank_inst_checkin() (innermost first), re-establishment mirrors wc_rng_bank_checkout() (acquisition order), and each level's failure clears only its own bit. (identified in 20260824 review batch) --- linuxkm/lkcapi_sha_glue.c | 50 ++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 3428f2e2fe7..9c9cb3de347 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2329,7 +2329,14 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, /* note, no need to use formal atomic accessors on drbg->lock -- * WC_RNG_BANK_INST_LOCK_HELD is held invariantly across the span, and * is the only bit considered by contending threads. */ - if (drbg->lock & (WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED | WC_RNG_BANK_INST_LOCK_VEC_OPS_INH)) + /* both levels can be held (an affinity-locked checkout with + * WC_RNG_BANK_FLAG_NO_VECTOR_OPS, whether from the caller's flags or + * bank-wide bank->flags, also takes the vector-ops inhibit) -- release + * each held level separately, innermost first, mirroring + * wc_rng_bank_inst_checkin(). */ + if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) + REENABLE_VECTOR_REGISTERS(); + if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); #endif @@ -2343,16 +2350,20 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, (void)wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS - if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { - int ret2 = DISABLE_VECTOR_REGISTERS(); - if (ret2 != 0) - drbg->lock &= ~(WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED | WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); - } - else if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { + /* re-establish each level separately, in acquisition order (the + * affinity save first, then the vector-ops inhibit), mirroring + * wc_rng_bank_checkout(); a failed re-acquisition clears only its own + * lock bit, so check-in unwinds exactly the levels actually held. */ + if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { int ret2 = SAVE_VECTOR_REGISTERS2(); if (ret2 != 0) drbg->lock &= ~WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; } + if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { + int ret2 = DISABLE_VECTOR_REGISTERS(); + if (ret2 != 0) + drbg->lock &= ~WC_RNG_BANK_INST_LOCK_VEC_OPS_INH; + } #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) migrate_enable(); @@ -2401,7 +2412,14 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, migrate_disable(); #endif - if (drbg->lock & (WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED | WC_RNG_BANK_INST_LOCK_VEC_OPS_INH)) + /* both levels can be held (an affinity-locked checkout with + * WC_RNG_BANK_FLAG_NO_VECTOR_OPS, whether from the caller's flags or + * bank-wide bank->flags, also takes the vector-ops inhibit) -- release + * each held level separately, innermost first, mirroring + * wc_rng_bank_inst_checkin(). */ + if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) + REENABLE_VECTOR_REGISTERS(); + if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); #endif @@ -2410,16 +2428,20 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, WC_RNG_BANK_FLAG_CAN_WAIT); #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS - if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { - int ret2 = DISABLE_VECTOR_REGISTERS(); - if (ret2 != 0) - drbg->lock &= ~(WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED | WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); - } - else if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { + /* re-establish each level separately, in acquisition order (the + * affinity save first, then the vector-ops inhibit), mirroring + * wc_rng_bank_checkout(); a failed re-acquisition clears only its own + * lock bit, so check-in unwinds exactly the levels actually held. */ + if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { int ret2 = SAVE_VECTOR_REGISTERS2(); if (ret2 != 0) drbg->lock &= ~WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; } + if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { + int ret2 = DISABLE_VECTOR_REGISTERS(); + if (ret2 != 0) + drbg->lock &= ~WC_RNG_BANK_INST_LOCK_VEC_OPS_INH; + } #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) migrate_enable(); From 87af605c2f245be7ceec93a660c19a3ec0e3d65a Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 25 Aug 2026 10:05:55 -0500 Subject: [PATCH 016/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/test/test.c, wolfcrypt/test/test.h: add SP 800-90C RBG chain (RBGC) APIs: wc_InitRngRBGC(), wc_InitRngNonceRBGC(), wc_InitRngRBGC_New(), wc_InitRngNonceRBGC_New(), wc_RNG_DRBG_ReseedRBGC(), and read accessor wc_RNG_DRBG_IsRBGCLeaf(). implement by adding a seedRng arg to _InitRng() and PollAndReSeed(): when non-null, seed material is drawn from the parent DRBG's generate function in place of the module's seed source, inheriting all other instantiate/reseed mechanics verbatim (seed byte accounting, wc_RNG_TestSeed(), instantiate/reseed dispatch, failure disposition, RDRAND bypass, SMALL_STACK arms). unified spawn mechanics in static SpawnRngRBGC() with exactly-one-destination arg checking; the four spawn APIs and wc_RNG_DRBG_ReseedRBGC() are minimal stubs. add WC_RNG.isRbgcLeaf: sticky tag set by spawn and by successful wc_RNG_DRBG_ReseedRBGC() (chain-reseeding a source-born instance demotes it, one-way); every RBGC API rejects a tagged root, enforcing depth-one chains programmatically. the tag survives source reseeds by policy. annotate the deliberate PollAndReSeed() <-> wc_RNG_GenerateBlock() recursion for clang-tidy misc-no-recursion on each cycle member: the cycle is bounded at one trip lexically -- wc_RNG_GenerateBlock()'s backstop reseed always passes a null seedRng -- independent of chain topology and the depth-one policy. gate RBGC APIs on !WC_NO_CONSTRUCTORS (the _New variants allocate from the root's heap and release with wc_rng_free()). also neutralize the seed-acquisition failure message attribution in _InitRng() (no longer necessarily wc_GenerateSeed()). add rng_drbg_svc_test() ("RNGSVC") and rng_drbg_rbgc_test() ("RNGRBGC"). RNGSVC covers the DRBG accessor/reseed services and per-key RNG clear APIs: accessor null contracts, generate advances the reseed counter, wc_RNG_DRBG_Reseed_Uncredited() preserves it, credited wc_RNG_DRBG_Reseed() resets it, wc_RNG_DRBG_ScheduleReseed() lands it exactly at WC_RESEED_INTERVAL and the next generate performs a source reseed, wc_RNG_DRBG_Reseed_Now() with and without nonce, and wc_RsaClearRNG()/wc_ecc_clear_rng()/wc_curve25519_clear_rng() including the preserved wc_RsaSetRNG(key, NULL) rejection. RNGRBGC covers the RBGC APIs: spawn arg contracts, root reseed-counter debit, leaf tagging and wc_RNG_DRBG_IsRBGCLeaf(), depth-one rejections (spawn-from-leaf in stack and _New forms, ReseedRBGC-with-leaf-as-root), wc_RNG_DRBG_ReseedRBGC() with and without nonce, tag stickiness across a forced source reseed, one-way demotion, and the _New/wc_rng_free() lifecycle. DRBG-internal observations are gated at runtime on wc_RNG_DRBG_Present() so both tests pass on RDRAND-shaped instantiations, exercising the degenerate arms. key and WC_RNG objects use the WC_*_VAR() macros to respect kernel frame limits. --- wolfcrypt/src/random.c | 211 +++++++++++++++++- wolfcrypt/test/test.c | 442 +++++++++++++++++++++++++++++++++++++ wolfcrypt/test/test.h | 5 + wolfssl/wolfcrypt/random.h | 20 ++ 4 files changed, 666 insertions(+), 12 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 0c5fa9675de..08ffb68fc35 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -775,6 +775,15 @@ int wc_RNG_DRBG_Present(const WC_RNG* rng) return 0; } +/* Returns 1 if rng was seeded from another DRBG's output (an SP 800-90C + * chain leaf, via wc_InitRng*RBGC() or wc_RNG_DRBG_ReseedRBGC()), else 0. + * The tag is sticky for the instance's lifetime; a leaf is never usable as + * a chain root. */ +int wc_RNG_DRBG_IsRBGCLeaf(const WC_RNG* rng) +{ + return (rng != NULL) && rng->isRbgcLeaf; +} + /* Read-only accessor for the DRBG reseed counter. When no DRBG is * instantiated (see wc_RNG_DRBG_Present()) there is no counter; *reseedCtr * is set to 0 -- never due for reseed -- and 0 is returned. */ @@ -2061,7 +2070,7 @@ int wc_Sha512Drbg_IsDisabled(void) static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, - void* heap, int devId) + void* heap, int devId, WC_RNG* seedRng) { int ret = 0; #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) @@ -2078,6 +2087,9 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, (void)nonce; (void)nonceSz; + /* seedRng is consumed only in the seed-acquisition arm; cast for + * configurations that compile that arm out. */ + (void)seedRng; if (rng == NULL) return BAD_FUNC_ARG; @@ -2085,6 +2097,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, return BAD_FUNC_ARG; XMEMSET(rng, 0, sizeof(*rng)); + rng->isRbgcLeaf = (seedRng != NULL); #ifdef WOLFSSL_HEAP_TEST rng->heap = (void*)WOLFSSL_HEAP_TEST; @@ -2346,6 +2359,16 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, #endif } else { + if (seedRng != NULL) { + /* RBGC spawn (SpawnRngRBGC()): draw the seed material from + * the parent DRBG's generate function in place of the + * module's seed source -- the SP 800-90C RBG chain + * construction. All subsequent handling (health test, seed + * byte accounting, instantiate, failure disposition) is + * identical to the seed-source path. */ + ret = wc_RNG_GenerateBlock(seedRng, seed, seedSz); + } + else { #ifdef WC_RNG_SEED_CB if (seedCb == NULL) { ret = DRBG_NO_SEED_CB; @@ -2364,6 +2387,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, #else ret = wc_GenerateSeed(&rng->seed, seed, seedSz); #endif /* WC_RNG_SEED_CB */ + } #ifdef WOLFSSL_CHECK_MEM_ZERO /* seed now holds entropy; register across DRBG instantiation */ wc_MemZero_Add("_InitRng seed", seed, seedSz); @@ -2373,7 +2397,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, WOLFSSL_MSG_EX("Seed generation failed... %d", ret); #elif defined(WC_VERBOSE_RNG) WOLFSSL_DEBUG_PRINTF( - "ERROR: wc_GenerateSeed() in _InitRng() failed with err %d", + "ERROR: seed acquisition in _InitRng() failed with err %d", ret); #endif ret = DRBG_FAILURE; @@ -2541,7 +2565,7 @@ int wc_rng_new_ex(WC_RNG **rng, byte* nonce, word32 nonceSz, return MEMORY_E; } - ret = _InitRng(*rng, nonce, nonceSz, heap, devId); + ret = _InitRng(*rng, nonce, nonceSz, heap, devId, NULL); if (ret != 0) { XFREE(*rng, heap, DYNAMIC_TYPE_RNG); *rng = NULL; @@ -2567,31 +2591,126 @@ void wc_rng_free(WC_RNG* rng) WOLFSSL_ABI int wc_InitRng(WC_RNG* rng) { - return _InitRng(rng, NULL, 0, NULL, INVALID_DEVID); + return _InitRng(rng, NULL, 0, NULL, INVALID_DEVID, NULL); } int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId) { - return _InitRng(rng, NULL, 0, heap, devId); + return _InitRng(rng, NULL, 0, heap, devId, NULL); } int wc_InitRngNonce(WC_RNG* rng, byte* nonce, word32 nonceSz) { - return _InitRng(rng, nonce, nonceSz, NULL, INVALID_DEVID); + return _InitRng(rng, nonce, nonceSz, NULL, INVALID_DEVID, NULL); } int wc_InitRngNonce_ex(WC_RNG* rng, byte* nonce, word32 nonceSz, void* heap, int devId) { - return _InitRng(rng, nonce, nonceSz, heap, devId); + return _InitRng(rng, nonce, nonceSz, heap, devId, NULL); } #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) + +/* Unified mechanics for the four wc_InitRng*RBGC() APIs: instantiate a leaf + * DRBG subordinate to root in an SP 800-90C RBG chain, drawing its seed + * material from root's generate function in place of the module's seed + * source; every other aspect of instantiation -- seed byte accounting, + * health testing, nonce handling, failure disposition -- is _InitRng()'s, + * identically to wc_InitRngNonce_ex(). + * + * Exactly one of new_leaf_stack (caller-provided WC_RNG, uninitialized) and + * new_leaf_heap (callee-allocated from root's heap, to be released with + * wc_rng_free()) must be non-NULL. The caller must hold exclusive access + * to root for the duration of the call, as for all WC_RNG operations; the + * spawn debits root's reseed counter by one generate. + * + * SP 800-90C accounting: the leaf's claimable security strength is capped + * by root's, and the leaf has no prediction resistance. The leaf's own + * reseeds default to the module's seed source (the reseed-interval + * backstop, wc_RNG_DRBG_Reseed_Now()); wc_RNG_DRBG_ReseedRBGC() reseeds it + * from root instead. Chains are depth-one BY POLICY, with programmatic + * enforcement: a leaf is tagged (WC_RNG.isRbgcLeaf, sticky for the + * instance's lifetime even across source reseeds) and is rejected as a + * root by every RBGC API. In configurations with no DRBG (RDRAND et al.), + * the leaf comes up as _InitRng() dictates for such configurations and + * root is not consulted. */ +static int SpawnRngRBGC(WC_RNG* new_leaf_stack, WC_RNG** new_leaf_heap, + WC_RNG* root, byte* nonce, word32 nonceSz) +{ + WC_RNG* leaf = new_leaf_stack; + int ret; +#ifdef WC_USE_DEVID + int devId = WC_USE_DEVID; +#else + int devId = INVALID_DEVID; +#endif + + if ((root == NULL) || + ((new_leaf_stack == NULL) == (new_leaf_heap == NULL)) || + (new_leaf_stack == root)) + { + return BAD_FUNC_ARG; + } + + /* Depth-one chains only, by policy: a leaf is never a root. */ + if (root->isRbgcLeaf) + return BAD_FUNC_ARG; + + if (new_leaf_heap != NULL) { + leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); + if (leaf == NULL) + return MEMORY_E; + } + + ret = _InitRng(leaf, nonce, nonceSz, root->heap, devId, root); + + if (new_leaf_heap != NULL) { + if (ret != 0) { + XFREE(leaf, root->heap, DYNAMIC_TYPE_RNG); + leaf = NULL; + } + *new_leaf_heap = leaf; + } + + return ret; +} + +int wc_InitRngRBGC(WC_RNG* leaf, WC_RNG* root) +{ + return SpawnRngRBGC(leaf, NULL, root, NULL, 0); +} + +int wc_InitRngNonceRBGC(WC_RNG* leaf, WC_RNG* root, byte* nonce, + word32 nonceSz) +{ + return SpawnRngRBGC(leaf, NULL, root, nonce, nonceSz); +} + +#ifndef WC_NO_CONSTRUCTORS +int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root) +{ + return SpawnRngRBGC(NULL, leaf, root, NULL, 0); +} + +int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, byte* nonce, + word32 nonceSz) +{ + return SpawnRngRBGC(NULL, leaf, root, nonce, nonceSz); +} +#endif /* !WC_NO_CONSTRUCTORS */ + +/* PollAndReSeed() and wc_RNG_GenerateBlock() form a single-cycle recursion when + * a seedRng is passed to PollAndReSeed() by the RBGC chain APIs. + * wc_RNG_GenerateBlock() itself never passes a seedRng, ending the cycle + * immediately. + */ +/* NOLINTNEXTLINE(misc-no-recursion) */ static int PollAndReSeed(WC_RNG* rng, const byte* additional, - word32 additionalSz) + word32 additionalSz, WC_RNG* seedRng) { int ret = WC_NO_ERR_TRACE(DRBG_NEED_RESEED); int devId = INVALID_DEVID; @@ -2611,6 +2730,17 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, ret = DRBG_SUCCESS; #endif if (ret == DRBG_SUCCESS) { + if (seedRng != NULL) { + /* RBGC reseed (wc_RNG_DRBG_ReseedRBGC()): draw the seed + * material from the parent DRBG's generate function in place + * of the module's seed source; all subsequent handling is + * identical to the seed-source path. */ + ret = wc_RNG_GenerateBlock(seedRng, newSeed, + SEED_SZ + SEED_BLOCK_SZ); + if (ret != 0) + ret = DRBG_FAILURE; + } + else { #ifdef WC_RNG_SEED_CB if (seedCb == NULL) { ret = DRBG_NO_SEED_CB; @@ -2637,6 +2767,7 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, ret = DRBG_FAILURE; } #endif + } } if (ret == DRBG_SUCCESS) { ret = wc_RNG_TestSeed(newSeed, SEED_SZ + SEED_BLOCK_SZ); @@ -2716,7 +2847,7 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) return 0; } - ret = PollAndReSeed(rng, nonce, nonceSz); + ret = PollAndReSeed(rng, nonce, nonceSz, NULL); /* Identical outcome mapping to the generate-path reseed. */ if (ret == DRBG_SUCCESS) { @@ -2733,12 +2864,67 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) return ret; } + +/* Immediately reseed leaf from root's generate output -- the reseed + * counterpart of the wc_InitRng*RBGC() spawn, with identical semantics to + * wc_RNG_DRBG_Reseed_Now() except for the seed source: the drawn material is + * health-tested and applied by the module's own reseed function, the reseed + * counter is reset iff the reseed succeeds, and a nonce rides the same + * reseed derivation as (uncredited) additional input. The caller must hold + * exclusive access to BOTH leaf and root. On success leaf is (or remains) a + * chain leaf: its current seed period is chain-backed, so isRbgcLeaf is set + * and it is not usable as a root. Depth-one policy applies: root must not + * itself be a leaf. */ +int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, const byte* nonce, + word32 nonceSz) +{ + int ret; + + if ((leaf == NULL) || (root == NULL) || (leaf == root) || + ((nonce == NULL) && (nonceSz > 0))) + { + return BAD_FUNC_ARG; + } + + /* Depth-one chains only, by policy: a leaf is never a root. */ + if (root->isRbgcLeaf) + return BAD_FUNC_ARG; + + /* Mirror wc_RNG_GenerateBlock(): only an in-service DRBG may reseed. */ + if (leaf->status != DRBG_OK) + return RNG_FAILURE_E; + + if (! wc_RNG_DRBG_Present(leaf)) { + /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ + return 0; + } + + ret = PollAndReSeed(leaf, nonce, nonceSz, root); + + /* Identical outcome mapping to the generate-path reseed. */ + if (ret == DRBG_SUCCESS) { + leaf->isRbgcLeaf = 1; + ret = 0; + } + else if (ret == WC_NO_ERR_TRACE(DRBG_CONT_FAILURE)) { + ret = DRBG_CONT_FIPS_E; + leaf->status = DRBG_CONT_FAILED; + } + else { + ret = RNG_FAILURE_E; + leaf->status = DRBG_FAILED; + } + + return ret; +} #endif /* place a generated block in output */ #ifdef WC_RNG_BANK_SUPPORT +/* NOLINTNEXTLINE(misc-no-recursion) */ static int wc_local_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #else +/* NOLINTNEXTLINE(misc-no-recursion) */ WOLFSSL_ABI int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #endif @@ -2805,7 +2991,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) if (rng->pid != getpid()) { rng->pid = getpid(); - ret = PollAndReSeed(rng, NULL, 0); + ret = PollAndReSeed(rng, NULL, 0, NULL); if (ret != DRBG_SUCCESS) { rng->status = DRBG_FAILED; return RNG_FAILURE_E; @@ -2818,7 +3004,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, NULL, 0); if (ret == WC_NO_ERR_TRACE(DRBG_NEED_RESEED)) { - ret = PollAndReSeed(rng, NULL, 0); + ret = PollAndReSeed(rng, NULL, 0, NULL); if (ret == DRBG_SUCCESS) ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, NULL, 0); @@ -2831,7 +3017,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) ret = Hash512_DRBG_Generate((DRBG_SHA512_internal *)rng->drbg512, output, sz, NULL, 0); if (ret == WC_NO_ERR_TRACE(DRBG_NEED_RESEED)) { - ret = PollAndReSeed(rng, NULL, 0); + ret = PollAndReSeed(rng, NULL, 0, NULL); if (ret == DRBG_SUCCESS) ret = Hash512_DRBG_Generate( (DRBG_SHA512_internal *)rng->drbg512, output, sz, @@ -2868,6 +3054,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #ifdef WC_RNG_BANK_SUPPORT WOLFSSL_ABI +/* NOLINTNEXTLINE(misc-no-recursion) */ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) { if (rng == NULL) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index cdecf63cfff..56d34d31d3f 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -941,6 +941,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); #ifdef WOLFSSL_NOISE_SRC WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void); #endif +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); +#endif #endif /* WC_NO_RNG */ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); #if defined(USE_CERT_BUFFERS_2048) && \ @@ -2604,6 +2609,17 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ else TEST_PASS("NOISESRC test passed!\n"); #endif +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) + if ((ret = rng_drbg_svc_test()) != 0) + TEST_FAIL("RNGSVC test failed!\n", ret); + else + TEST_PASS("RNGSVC test passed!\n"); + if ((ret = rng_drbg_rbgc_test()) != 0) + TEST_FAIL("RNGRBGC test failed!\n", ret); + else + TEST_PASS("RNGRBGC test passed!\n"); +#endif #endif /* WC_NO_RNG */ #ifdef WOLFSSL_SHAKE128 @@ -28507,6 +28523,432 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif /* WC_RNG_BANK_SUPPORT */ +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +/* Coverage for the DRBG state accessor / reseed scheduling services and the + * per-key RNG clear APIs. Probes that observe DRBG internals via the + * accessors are gated at runtime on wc_RNG_DRBG_Present(), so the test also + * passes on RDRAND-shaped instantiations. (The clear-API probes ride this + * test's gate for economy; a CUSTOM_RAND_GENERATE_BLOCK config loses only + * that sliver of coverage.) The SP 800-90C RBGC APIs are covered + * separately, in rng_drbg_rbgc_test(). */ +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) +{ + wc_test_ret_t ret = 0; + int api_ret; + int present; + int root_inited = 0; + WC_DECLARE_VAR(root, WC_RNG, 1, HEAP_HINT); + wc_drbg_reseed_ctr_t c1 = 0; + wc_drbg_reseed_ctr_t c2 = 0; + byte buf[32]; + byte matter[32]; + + WOLFSSL_ENTER("rng_drbg_svc_test"); + + WC_ALLOC_VAR_EX(root, WC_RNG, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); + + XMEMSET(matter, 0x5a, sizeof(matter)); + + /* accessor argument contracts, pre-init */ + if (wc_RNG_GetStatus(NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_Present(NULL) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_IsRBGCLeaf(NULL) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_GetReseedCtr(NULL, &c1) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + api_ret = wc_InitRng(root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + root_inited = 1; + + if (wc_RNG_GetStatus(root) != WC_DRBG_OK) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_GetReseedCtr(root, NULL) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_IsRBGCLeaf(root) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + present = wc_RNG_DRBG_Present(root); + + /* generate advances the reseed counter */ + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); + if ((api_ret != 0) || (c1 < 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_GenerateBlock(root, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c2); + if ((api_ret != 0) || (c2 <= c1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* uncredited mixing preserves the counter */ + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + api_ret = wc_RNG_DRBG_Reseed_Uncredited(root, matter, sizeof(matter)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c2); + if ((api_ret != 0) || (c2 != c1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* credited reseed resets the counter */ + api_ret = wc_RNG_DRBG_Reseed(root, matter, sizeof(matter)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); + if ((api_ret != 0) || (c1 != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* schedule-then-generate performs a source reseed */ + api_ret = wc_RNG_DRBG_ScheduleReseed(root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); + if ((api_ret != 0) || + (c1 != (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + api_ret = wc_RNG_GenerateBlock(root, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); + if ((api_ret != 0) || (c1 > 2)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* immediate source reseed, without and with a nonce */ + api_ret = wc_RNG_DRBG_Reseed_Now(root, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_Reseed_Now(root, matter, 16); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); + if ((api_ret != 0) || (c1 != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + if (wc_RNG_DRBG_Reseed_Now(NULL, NULL, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_Reseed_Now(root, NULL, 5) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* per-key RNG clear APIs */ +#if !defined(NO_RSA) + { + WC_DECLARE_VAR(rsaKey, RsaKey, 1, HEAP_HINT); + int rsaKey_inited = 0; + WC_ALLOC_VAR_EX(rsaKey, RsaKey, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); + api_ret = wc_InitRsaKey(rsaKey, HEAP_HINT); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + else { + rsaKey_inited = 1; + api_ret = wc_RsaSetRNG(rsaKey, root); + if (api_ret != 0) { + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + } + if (api_ret == 0) { + /* the setter's NULL rejection is contractual and preserved */ + if ((wc_RsaSetRNG(rsaKey, NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) || + (wc_RsaClearRNG(rsaKey) != 0) || + (wc_RsaClearRNG(NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG))) + { + api_ret = WC_TEST_RET_ENC_NC; + } + } + if (rsaKey_inited) + (void)wc_FreeRsaKey(rsaKey); + WC_FREE_VAR(rsaKey, HEAP_HINT); + if (api_ret != 0) + ERROR_OUT(api_ret, out); + } +#endif /* !NO_RSA */ +#ifdef HAVE_ECC + { + WC_DECLARE_VAR(eccKey, ecc_key, 1, HEAP_HINT); + int eccKey_inited = 0; + WC_ALLOC_VAR_EX(eccKey, ecc_key, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); + api_ret = wc_ecc_init(eccKey); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + else { + eccKey_inited = 1; + if ((wc_ecc_set_rng(eccKey, root) != 0) || + (wc_ecc_clear_rng(eccKey) != 0) +#ifdef ECC_TIMING_RESISTANT + || (wc_ecc_clear_rng(NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) +#endif + ) + { + api_ret = WC_TEST_RET_ENC_NC; + } + } + if (eccKey_inited) + (void)wc_ecc_free(eccKey); + WC_FREE_VAR(eccKey, HEAP_HINT); + if (api_ret != 0) + ERROR_OUT(api_ret, out); + } +#endif /* HAVE_ECC */ +#if defined(HAVE_CURVE25519) && defined(WOLFSSL_CURVE25519_BLINDING) + { + WC_DECLARE_VAR(cvKey, curve25519_key, 1, HEAP_HINT); + int cvKey_inited = 0; + WC_ALLOC_VAR_EX(cvKey, curve25519_key, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); + api_ret = wc_curve25519_init(cvKey); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + else { + cvKey_inited = 1; + if ((wc_curve25519_set_rng(cvKey, root) != 0) || + (wc_curve25519_clear_rng(cvKey) != 0) || + (wc_curve25519_clear_rng(NULL) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG))) + { + api_ret = WC_TEST_RET_ENC_NC; + } + } + if (cvKey_inited) + wc_curve25519_free(cvKey); + WC_FREE_VAR(cvKey, HEAP_HINT); + if (api_ret != 0) + ERROR_OUT(api_ret, out); + } +#endif /* HAVE_CURVE25519 && WOLFSSL_CURVE25519_BLINDING */ + +out: + + if (root_inited) { + int cleanup_ret = wc_FreeRng(root); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + + WC_FREE_VAR(root, HEAP_HINT); + + return ret; +} + +/* Coverage for the SP 800-90C RBGC (RBG chain) APIs: spawn, reseed-from- + * root, the leaf tag and accessor, and the sticky depth-one enforcement. + * DRBG-internal observations are gated at runtime on wc_RNG_DRBG_Present(), + * so the test also passes on RDRAND-shaped instantiations, where the RBGC + * APIs are exercised in their degenerate arms. */ +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) +{ + wc_test_ret_t ret = 0; + int api_ret; + int present; + int root_inited = 0; + int leaf_inited = 0; + int extra_inited = 0; + WC_RNG root; + WC_RNG leaf; + WC_RNG extra; + WC_RNG* pleaf = NULL; + wc_drbg_reseed_ctr_t c1 = 0; + wc_drbg_reseed_ctr_t c2 = 0; + byte buf[32]; + byte matter[32]; + + WOLFSSL_ENTER("rng_drbg_rbgc_test"); + + XMEMSET(matter, 0xa5, sizeof(matter)); + + api_ret = wc_InitRng(&root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + root_inited = 1; + + present = wc_RNG_DRBG_Present(&root); + + /* spawn argument contracts */ + if (wc_InitRngRBGC(NULL, &root) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_InitRngRBGC(&leaf, NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_InitRngRBGC(&root, &root) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifndef WC_NO_CONSTRUCTORS + if (wc_InitRngRBGC_New(NULL, &root) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif + + /* spawn a leaf; the spawn debits root's counter; the leaf is tagged */ + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(&root, &c1); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + api_ret = wc_InitRngRBGC(&leaf, &root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + leaf_inited = 1; + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(&root, &c2); + if ((api_ret != 0) || (c2 <= c1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + if (wc_RNG_DRBG_IsRBGCLeaf(&leaf) != 1) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_IsRBGCLeaf(&root) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* depth-one enforcement: a leaf is never a root */ + if (wc_InitRngRBGC(&extra, &leaf) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifndef WC_NO_CONSTRUCTORS + if (wc_InitRngRBGC_New(&pleaf, &leaf) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (pleaf != NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif + if (wc_RNG_DRBG_ReseedRBGC(&root, &leaf, NULL, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* reseed-from-root, without and with a nonce; counter resets */ + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, matter, 16); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(&leaf, &c1); + if ((api_ret != 0) || (c1 != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + if (wc_RNG_DRBG_ReseedRBGC(&leaf, &leaf, NULL, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_ReseedRBGC(NULL, &root, NULL, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_ReseedRBGC(&leaf, &root, NULL, 7) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* the leaf tag is sticky across a source reseed */ + api_ret = wc_RNG_DRBG_ScheduleReseed(&leaf); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (wc_RNG_DRBG_IsRBGCLeaf(&leaf) != 1) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + +#if !defined(WC_NO_CONSTRUCTORS) + /* chain-reseeding a source-born instance demotes it, one-way */ + api_ret = wc_InitRng(&extra); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + extra_inited = 1; + if (wc_RNG_DRBG_IsRBGCLeaf(&extra) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (present) { + api_ret = wc_RNG_DRBG_ReseedRBGC(&extra, &root, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (wc_RNG_DRBG_IsRBGCLeaf(&extra) != 1) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_InitRngRBGC_New(&pleaf, &extra) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* heap-allocated leaves, without and with a nonce */ + api_ret = wc_InitRngRBGC_New(&pleaf, &root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if ((pleaf == NULL) || (wc_RNG_DRBG_IsRBGCLeaf(pleaf) != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_GenerateBlock(pleaf, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + wc_rng_free(pleaf); + pleaf = NULL; + api_ret = wc_InitRngNonceRBGC_New(&pleaf, &root, matter, 16); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if ((pleaf == NULL) || (wc_RNG_DRBG_IsRBGCLeaf(pleaf) != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + wc_rng_free(pleaf); + pleaf = NULL; +#endif /* !WC_NO_CONSTRUCTORS */ + + /* nonce-bearing stack spawn */ + api_ret = wc_FreeRng(&leaf); + leaf_inited = 0; + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_InitRngNonceRBGC(&leaf, &root, matter, 16); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + leaf_inited = 1; + if (wc_RNG_DRBG_IsRBGCLeaf(&leaf) != 1) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + +out: + + { + int cleanup_ret; + if (pleaf != NULL) + wc_rng_free(pleaf); + if (leaf_inited) { + cleanup_ret = wc_FreeRng(&leaf); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (extra_inited) { + cleanup_ret = wc_FreeRng(&extra); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (root_inited) { + cleanup_ret = wc_FreeRng(&root); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + } + + return ret; +} +#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && + * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + #endif /* !WC_NO_RNG */ #ifndef MEM_TEST_SZ diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index 0f83600be61..d881a3fd695 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -255,6 +255,11 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void); #ifdef WC_RNG_BANK_SUPPORT extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); #endif +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); +extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); +#endif #endif /* WC_NO_RNG */ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); #if defined(USE_CERT_BUFFERS_2048) && \ diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index c727e07dfe4..7d9cc03e51b 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -362,6 +362,11 @@ struct WC_RNG { struct OS_Seed seed; void* heap; byte status; + /* Set when this instance was seeded from another DRBG's output + * (wc_InitRng*RBGC(), wc_RNG_DRBG_ReseedRBGC()) -- an SP 800-90C chain + * leaf. Sticky by policy: a leaf is never usable as a chain root, even + * after a subsequent reseed from the module's seed source. */ + byte isRbgcLeaf; #if defined(WC_RNG_BANK_SUPPORT) || defined(HAVE_HASHDRBG) @@ -611,6 +616,7 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); * fallbacks for those builds. */ WOLFSSL_API int wc_RNG_GetStatus(const WC_RNG* rng); WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); + WOLFSSL_API int wc_RNG_DRBG_IsRBGCLeaf(const WC_RNG* rng); WOLFSSL_API int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr); WOLFSSL_API int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); @@ -620,6 +626,20 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); #ifndef CUSTOM_RAND_GENERATE_BLOCK WOLFSSL_API int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz); + + /* SP 800-90C RBG-chain spawn: instantiate leaf as a subordinate DRBG + * seeded from root's generate output. The _New variants allocate the + * leaf from root's heap; release those with wc_rng_free(). */ + WOLFSSL_API int wc_InitRngRBGC(WC_RNG* leaf, WC_RNG* root); + WOLFSSL_API int wc_InitRngNonceRBGC(WC_RNG* leaf, WC_RNG* root, + byte* nonce, word32 nonceSz); +#ifndef WC_NO_CONSTRUCTORS + WOLFSSL_API int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root); + WOLFSSL_API int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, + byte* nonce, word32 nonceSz); +#endif /* !WC_NO_CONSTRUCTORS */ + WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, + const byte* nonce, word32 nonceSz); #endif #ifndef NO_SHA256 From 65084f56dad97e20555b73f95aa08a64b5897c7f Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 26 Aug 2026 14:24:30 -0500 Subject: [PATCH 017/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/test/test.c, wolfcrypt/test/test.h: add seed banking facility: wolfcrypt/src/random.c and wolfssl/wolfcrypt/random.h: add the banked-next-seed facility: wc_RNG_DRBG_NextSeedGenerate(), wc_RNG_DRBG_NextSeedCurrent(), wc_RNG_DRBG_NextSeedNow(), and wc_RNG_DRBG_NextSeedNow_Nonce(), with unit test coverage in new rng_drbg_nextseed_test() ("RNGNXTS"). seed material is banked incrementally, in-boundary, from the module's seed source, and consumed in an immediate credited reseed that touches no seed source -- pure computation, safe in atomic context (the one credited reseed shape with that property). banked reseeds provide no SP 800-90 prediction resistance (the material predates the request by construction); wc_RNG_DRBG_Reseed_Now() remains the live-gather shape. DRBG_internal and DRBG_SHA512_internal gain nextSeed[] (identical byte accounting to every other source-fed (re)seed) and nextSeedLen, a wolfSSL_Atomic_Int hand-off aperture: non-negative values count banked bytes (filling); negative values are sentinels (WC_DRBG_NEXT_SEED_READY, _CONSUMING). the single-writer daemon fills with AddFetch and publishes _READY with a CAS after health-testing the completed bank (wc_RNG_TestSeed()); a consumer claims with a CAS _READY -> _CONSUMING, reseeds, zeroizes, and release-stores _EMPTY. use-once throughout: delivered, failed, and health-test-rejected material is all zeroized before the aperture reopens. distinct protocol results, deliberately loud: ALREADY_E (bank ready or consuming), NOT_READY_E (nothing consumable), RETRY_E (health test could not run -- non-dispositive MEMORY_E; leave the bank complete and re-call), MISSING_RNG_E (no DRBG instantiated, e.g. RDRAND bypass -- a direct caller must know what instance it holds). gathering calls wc_GenerateSeed() with a local zero-initialized throwaway OS_Seed, fully independent of rng->seed, so the daemon cannot race an owner's own source reseed; any working seed source suffices (wolfEntropy, RDSEED, et al.). gate: WC_RNG_HAVE_NEXT_SEED = HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && WOLFSSL_ATOMIC_OPS, opt-out WC_RNG_NO_NEXT_SEED (registered in .wolfssl_known_macro_extras). RNGNXTS covers the aperture protocol as observed through _NextSeedCurrent() (empty, partial-preserved-across-consume-attempt, monotone fill, ready sentinel, ALREADY_E idempotence, consume -> reseed counter 1 -> empty, refill and _Nonce consume) and the argument contracts, runtime-gated on wc_RNG_DRBG_Present(). wolfcrypt/test/test.c: in random_bank_test(), gate the wc_rng_new_bankref() probes (and the rng2 local and its cleanup) on !WC_NO_CONSTRUCTORS, matching the API's declaration; the wc_InitRng_BankRef() probes are constructor-free and keep the bare WC_HAVE_RNG_BANKREF gate. (fixes an implicit function declaration under WC_NO_CONSTRUCTORS + WC_RNG_BANK_SUPPORT, a configuration with no prior harness coverage. predates the NextSeed work.) --- .wolfssl_known_macro_extras | 1 + wolfcrypt/src/random.c | 259 +++++++++++++++++++++++++ wolfcrypt/test/test.c | 374 +++++++++++++++++++++++++++++++++++- wolfcrypt/test/test.h | 5 + wolfssl/wolfcrypt/random.h | 58 ++++++ 5 files changed, 692 insertions(+), 5 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 25e01d94eac..be8bdc16151 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -803,6 +803,7 @@ WC_PUF_HELPER_COMPACT WC_PUF_SHA3 WC_RNG_BANK_NO_DEFAULT_SUPPORT WC_RNG_BLOCKING +WC_RNG_NO_NEXT_SEED WC_RSA_NONBLOCK_TIME WC_RSA_NO_FERMAT_CHECK WC_RTL8735B_NO_DERIVE_CACHE diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 08ffb68fc35..cfab8165ec8 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1275,6 +1275,9 @@ static int Hash_DRBG_Instantiate(DRBG_internal* drbg, const byte* seed, int ret = WC_NO_ERR_TRACE(DRBG_FAILURE); XMEMSET(drbg, 0, sizeof(DRBG_internal)); +#ifdef WC_RNG_HAVE_NEXT_SEED + wolfSSL_Atomic_Int_Init(&drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); +#endif drbg->heap = heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) drbg->devId = devId; @@ -1784,6 +1787,9 @@ static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg, int ret = WC_NO_ERR_TRACE(DRBG_FAILURE); XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal)); +#ifdef WC_RNG_HAVE_NEXT_SEED + wolfSSL_Atomic_Int_Init(&drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); +#endif drbg->heap = heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) drbg->devId = devId; @@ -2917,6 +2923,259 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, const byte* nonce, return ret; } + +#ifdef WC_RNG_HAVE_NEXT_SEED + +/* Banked-next-seed ("aperture") protocol. + * + * Entropy is gathered incrementally, in-boundary, from the + * module's seed source by wc_RNG_DRBG_NextSeedGenerate(), and consumed + * (source-free, atomic-context-safe) by wc_RNG_DRBG_NextSeedNow(). + * nextSeedLen is the hand-off aperture: values in [0, bank length) + * count banked bytes (filling); WC_DRBG_NEXT_SEED_READY marks a complete, + * health-tested bank; WC_DRBG_NEXT_SEED_CONSUMING marks exclusive ownership by a + * consumer. + * + * DRBG_internal.nextSeedLen (and the DRBG_SHA512_internal analog) is a + * wolfSSL_Atomic_Int with C-native atomic semantics (release stores, acquire + * loads, sequentially consistent RMWs): + * + * The single scheduling daemon (one writer per instance, by contract) advances + * the fill with the AddFetch in wc_RNG_DRBG_NextSeedGenerate() and publishes by + * storing _READY; a consumer claims with a CAS from _READY to _CONSUMING, + * consumes, zeroizes, and releases to _EMPTY. Ownership-taking transitions are + * atomic RMWs and releases are atomic stores with release semantics, so the + * hand-off is ordered on all supported targets, and the daemon never touches + * any other DRBG state. Gathering draws from the configured / installed + * entropy source directly, never from rng->seed, so the daemon also does not + * race an owner's own source reseed. */ + +/* Locate the aperture members for rng's live DRBG. Returns nonzero when no + * DRBG is instantiated (RDRAND et al.). */ +static int NextSeedPtrs(WC_RNG* rng, byte** seed, wolfSSL_Atomic_Int** len) +{ +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { + *seed = ((DRBG_internal*)rng->drbg)->nextSeed; + *len = &((DRBG_internal*)rng->drbg)->nextSeedLen; + return 0; + } +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { + *seed = ((DRBG_SHA512_internal*)rng->drbg512)->nextSeed; + *len = &((DRBG_SHA512_internal*)rng->drbg512)->nextSeedLen; + return 0; + } +#endif + return MISSING_RNG_E; +} + +/* Bank up to n more bytes of seed material from the module's seed source into + * rng's next-seed bank. Callable without owning the instance (the scheduling + * daemon's entry point); deliberately independent of rng->status so that + * banking can proceed for any instantiated DRBG. n is clamped to the space + * remaining; a ready or consuming bank is signaled with ALREADY_E. On + * completing the bank, the material is health-tested (wc_RNG_TestSeed()) and + * published; a failed test consumes the material (use-once) and returns the + * test's error, leaving an empty bank for the next cycle. A gather failure + * leaves the partial bank intact for retry. */ +int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) +{ + byte* seed; + wolfSSL_Atomic_Int* lenp; + WC_ATOMIC_INT_ARG cur; + int ret; + + if ((rng == NULL) || (n == 0)) + return BAD_FUNC_ARG; + + if (NextSeedPtrs(rng, &seed, &lenp) != 0) { + /* No DRBG instantiated -- nothing to bank (RDRAND et al.). */ + return BAD_FUNC_ARG; + } + + cur = *lenp; + if ((cur < 0) || (cur >= (WC_ATOMIC_INT_ARG)WC_DRBG_NEXT_SEED_LEN)) { + if (cur != (WC_ATOMIC_INT_ARG)WC_DRBG_NEXT_SEED_LEN) { + /* Ready, consuming, or other sentinel -- nothing to do. */ + return ALREADY_E; + } + /* Complete but unpublished (interrupted between fill completion and + * publication): retry the health test and publication below. */ + n = 0; + } + else if (n > WC_DRBG_NEXT_SEED_LEN - (word32)cur) { + n = WC_DRBG_NEXT_SEED_LEN - (word32)cur; + } + + if (n > 0) { + /* wc_GenerateSeed() must be called completely independent of rng, aside + * from the memory aperture itself. For safety, we pass a dummy + * OS_Seed, which will be ignored by the wc_GenerateSeed() typically + * used in conjunction with WC_RNG_HAVE_NEXT_SEED. + */ + struct OS_Seed os; + + /* Named-member init: layout-proof against OS_Seed growing or + * reordering members under its several config axes. */ + XMEMSET(&os, 0, sizeof(os)); +#ifndef USE_WINDOWS_API + os.fd = -1; +#endif +#ifdef WOLF_CRYPTO_CB + os.devId = INVALID_DEVID; +#endif + + ret = wc_GenerateSeed(&os, seed + cur, n); + if (ret != 0) { + /* Partial bank preserved -- retry on a later cycle. */ + return ret; + } + cur = wolfSSL_Atomic_Int_AddFetch(lenp, (WC_ATOMIC_INT_ARG)n); + } + + if (cur == (WC_ATOMIC_INT_ARG)WC_DRBG_NEXT_SEED_LEN) { + /* Bank complete: health-test now, in advance of consumption, so + * that wc_RNG_DRBG_NextSeedNow() is pure computation. */ + ret = wc_RNG_TestSeed(seed, WC_DRBG_NEXT_SEED_LEN); + if (ret == 0) { + WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + } + else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) { + /* wc_RNG_TestSeed() did nothing with the data -- not + * dispositive. */ + return RETRY_E; + } + else if ((ret == WC_NO_ERR_TRACE(ENTROPY_RT_E)) || + (ret == WC_NO_ERR_TRACE(ENTROPY_APT_E))) + { + /* Use-once: a failed test consumes the material. Release + * store: the ForceZero() must be visible before the empty + * aperture is. */ + ForceZero(seed, WC_DRBG_NEXT_SEED_LEN); + WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); + return ret; + } + else { + /* Buggy or brokey */ + return ret; + } + } + + return 0; +} + +/* Report the raw aperture value: a racy snapshot by design. Values in [0, bank + * length) count banked bytes; WC_DRBG_NEXT_SEED_READY and + * WC_DRBG_NEXT_SEED_CONSUMING indicate a ready or in-consumption bank, + * respectively. With no DRBG instantiated, reports WC_DRBG_NEXT_SEED_EMPTY. */ +int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng, WC_ATOMIC_INT_ARG* n) +{ + byte* seed; + wolfSSL_Atomic_Int* lenp; + + if ((rng == NULL) || (n == NULL)) + return BAD_FUNC_ARG; + + if (NextSeedPtrs(rng, &seed, &lenp) != 0) { + *n = WC_DRBG_NEXT_SEED_EMPTY; + return 0; + } + + *n = *lenp; + return 0; +} + +/* Consume a ready next-seed bank in an immediate credited reseed. The + * caller must own the instance. Source-free by construction -- the + * material was gathered from the module's seed source and health-tested at + * bank time -- so consumption is pure computation and safe in atomic + * context: the one credited reseed shape with that property. Distinct + * protocol results: NOT_READY_E when no bank is ready (nothing consumed), + * MISSING_RNG_E when the instance has no DRBG (RDRAND et al.) -- both + * deliberately loud, so a direct caller must demonstrate it understands + * the instance it holds. (The WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED + * checkout arm is return-agnostic by construction and needs neither.) + * Use-once: the bank is consumed by the attempt, success or failure. Note + * that a banked reseed can never provide SP 800-90 prediction resistance + * (the material predates the request by construction); + * wc_RNG_DRBG_Reseed_Now() remains the live-gather shape. */ +int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, + word32 nonceSz) +{ + byte* seed; + wolfSSL_Atomic_Int* lenp; + WC_ATOMIC_INT_ARG expected = WC_DRBG_NEXT_SEED_READY; + int ret; + + if (rng == NULL) + return BAD_FUNC_ARG; + + if ((nonce == NULL) && (nonceSz != 0)) + return BAD_FUNC_ARG; + + /* Mirror wc_RNG_GenerateBlock(): only an in-service DRBG may reseed. */ + if (rng->status != DRBG_OK) + return RNG_FAILURE_E; + + ret = NextSeedPtrs(rng, &seed, &lenp); + if (ret != 0) { + /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ + return ret; + } + + if (! wolfSSL_Atomic_Int_CompareExchange(lenp, &expected, + WC_DRBG_NEXT_SEED_CONSUMING)) + { + /* No ready bank -- nothing consumed; reported distinctly. */ + return NOT_READY_E; + } + + /* Identical byte accounting to PollAndReSeed(): the SEED_BLOCK_SZ + * prefix was consumed by the bank-time health testing. */ +#ifndef NO_SHA256 + if (rng->drbgType == WC_DRBG_SHA256) + ret = Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, + seed + SEED_BLOCK_SZ, SEED_SZ, + nonce, nonceSz); + else +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if (rng->drbgType == WC_DRBG_SHA512) + ret = Hash512_DRBG_Reseed((DRBG_SHA512_internal *)rng->drbg512, + seed + SEED_BLOCK_SZ, SEED_SZ, + nonce, nonceSz); + else +#endif + ret = WC_NO_ERR_TRACE(DRBG_FAILURE); + + /* Use-once: consumed by the attempt, success or not. Release store: + * the ForceZero() must be visible before the empty aperture is. */ + ForceZero(seed, WC_DRBG_NEXT_SEED_LEN); + WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); + + /* Identical outcome mapping to the generate-path reseed. */ + if (ret == DRBG_SUCCESS) { + ret = 0; + } + else if (ret == WC_NO_ERR_TRACE(DRBG_CONT_FAILURE)) { + ret = DRBG_CONT_FIPS_E; + rng->status = DRBG_CONT_FAILED; + } + else { + ret = RNG_FAILURE_E; + rng->status = DRBG_FAILED; + } + + return ret; +} + +int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng) { + return wc_RNG_DRBG_NextSeedNow_Nonce(rng, NULL, 0); +} + +#endif /* WC_RNG_HAVE_NEXT_SEED */ #endif /* place a generated block in output */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 56d34d31d3f..af43d7d0372 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -166,7 +166,6 @@ static const byte const_byte_array[] = "A+Gd\0\0\0"; #endif #endif /* WOLFSSL_ESPIDF */ - #ifdef USE_FLAT_TEST_H #ifdef HAVE_CONFIG_H #include "test_paths.h" @@ -946,6 +945,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); #endif +#ifdef WC_RNG_HAVE_NEXT_SEED +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); +#endif #endif /* WC_NO_RNG */ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); #if defined(USE_CERT_BUFFERS_2048) && \ @@ -2620,6 +2622,12 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ else TEST_PASS("RNGRBGC test passed!\n"); #endif +#ifdef WC_RNG_HAVE_NEXT_SEED + if ((ret = rng_drbg_nextseed_test()) != 0) + TEST_FAIL("RNGNXTS test failed!\n", ret); + else + TEST_PASS("RNGNXTS test passed!\n"); +#endif #endif /* WC_NO_RNG */ #ifdef WOLFSSL_SHAKE128 @@ -27901,13 +27909,25 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifndef WC_RNG_BANK_STATIC struct wc_rng_bank *bank2 = NULL; struct wc_rng_bank_inst *rng_inst2 = NULL; -#ifdef WC_HAVE_RNG_BANKREF +#if defined(WC_HAVE_RNG_BANKREF) && !defined(WC_NO_CONSTRUCTORS) WC_RNG *rng2 = NULL; #endif #endif /* !WC_RNG_BANK_STATIC */ static const char bank_arg[] = "hi"; byte outbuf1[16], outbuf2[16]; int i; + int svc_present = 0; +#ifdef WC_RNG_HAVE_NEXT_SEED + wc_drbg_reseed_ctr_t ns_ctr = 0; +#endif +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +#ifndef WC_NO_CONSTRUCTORS + WC_RNG *spawned_rng = NULL; +#endif + int leaf_rng_inited = 0; + WC_DECLARE_VAR(leaf_rng, WC_RNG, 1, HEAP_HINT); +#endif WC_CALLOC_VAR_EX(bank, struct wc_rng_bank, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, @@ -27919,6 +27939,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); XMEMSET(rng, 0, sizeof(*rng)); #endif +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) + WC_ALLOC_VAR_EX(leaf_rng, WC_RNG, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); +#endif ret = wc_rng_bank_init(NULL, WC_RNG_BANK_STATIC_SIZE, WC_RNG_BANK_FLAG_CAN_WAIT, 10, HEAP_HINT, INVALID_DEVID); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) @@ -28296,7 +28322,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); wc_FreeRng(rng); -#ifndef WC_RNG_BANK_STATIC +#if !defined(WC_RNG_BANK_STATIC) && !defined(WC_NO_CONSTRUCTORS) ret = wc_rng_new_bankref(NULL, &rng2); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28424,7 +28450,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#ifdef WC_HAVE_RNG_BANKREF +#if defined(WC_HAVE_RNG_BANKREF) && !defined(WC_NO_CONSTRUCTORS) ret = wc_rng_new_bankref(NULL, &rng2); #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) @@ -28475,6 +28501,173 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif /* !WC_RNG_BANK_STATIC */ + /* ---- rng_bank service extensions: recovery-patrol and in-service- + * guarantee checkout flags, wc_rng_bank_inst_checkin(), the daemon + * banking entry point with consume-at-checkout, and the RBGC spawn + * APIs. DRBG-internal probes are runtime-gated on + * wc_RNG_DRBG_Present(). ---- */ + + (void)svc_present; + + /* plain check-out roundtrip via the one-arg check-in */ + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 0, 0); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + svc_present = wc_RNG_DRBG_Present(WC_RNG_BANK_INST_TO_RNG(rng_inst)); + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (rng_inst != NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* the in-service guarantee on a healthy instance is transparent */ + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 0, + WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* contradictory flag combinations */ + if (wc_rng_bank_checkout(bank, &rng_inst, 0, 0, + WC_RNG_BANK_FLAG_FOR_RECOVERY | + WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_checkout(bank, &rng_inst, 0, 0, + WC_RNG_BANK_FLAG_FOR_RECOVERY | + WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* recovery patrol: argument contracts; a healthy instance is a + * success no-op */ + if (wc_rng_bank_recover_inst(NULL, 0, 0, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_recover_inst(bank, 0, 0, + WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_recover_inst(bank, WC_RNG_BANK_STATIC_SIZE, 0, 0) == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_recover_inst(bank, 0, 0, 0); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + +#ifdef WC_RNG_HAVE_NEXT_SEED + /* daemon banking entry point: argument contracts */ + if (wc_rng_bank_next_seed_generate(NULL, 0, 32) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_next_seed_generate(bank, -1, 32) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_next_seed_generate(bank, WC_RNG_BANK_STATIC_SIZE, 32) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_next_seed_generate(bank, 0, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + if (svc_present) { + /* bank instance 0's next seed to publication, then consume it at + * checkout: an atomic-context-safe credited reseed (counter + * lands at 1) */ + for (i = 0; i < 64; i++) { + ret = wc_rng_bank_next_seed_generate(bank, 0, 32); + if (ret == WC_NO_ERR_TRACE(ALREADY_E)) + break; + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } + if (i >= 64) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 0, + WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(rng_inst), &ns_ctr); + if ((ret != 0) || (ns_ctr != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(rng_inst), + outbuf1, sizeof(outbuf1)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* consume with no bank ready is transparent: counter undisturbed */ + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 0, + WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(rng_inst), &ns_ctr); + if ((ret != 0) || (ns_ctr != 2)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } +#endif /* WC_RNG_HAVE_NEXT_SEED */ + +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) + /* RBGC spawn: argument and flag contracts */ + if (wc_rng_bank_spawn(bank, NULL, NULL, 0, 0, 0, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifndef WC_NO_CONSTRUCTORS + if (wc_rng_bank_spawn_new(bank, NULL, NULL, 0, 0, 0, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif + if (wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, + WC_RNG_BANK_FLAG_SEED_UNCREDITED) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, + WC_RNG_BANK_FLAG_FOR_RECOVERY) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* nonce-bearing stack spawn: the leaf is a tagged chain leaf, + * generates, and is torn down independently of the bank */ + ret = wc_rng_bank_spawn(bank, leaf_rng, outbuf2, sizeof(outbuf2), 0, 0, + 0); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + leaf_rng_inited = 1; + if (wc_RNG_DRBG_IsRBGCLeaf(leaf_rng) != 1) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_RNG_GenerateBlock(leaf_rng, outbuf1, sizeof(outbuf1)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_FreeRng(leaf_rng); + leaf_rng_inited = 0; + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + +#ifndef WC_NO_CONSTRUCTORS + /* heap spawn from the second instance */ + ret = wc_rng_bank_spawn_new(bank, &spawned_rng, NULL, 0, 1, 0, 0); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if ((spawned_rng == NULL) || (wc_RNG_DRBG_IsRBGCLeaf(spawned_rng) != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_RNG_GenerateBlock(spawned_rng, outbuf1, sizeof(outbuf1)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + wc_rng_free(spawned_rng); + spawned_rng = NULL; +#endif /* !WC_NO_CONSTRUCTORS */ +#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && + * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + out: { @@ -28499,7 +28692,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) WC_FREE_VAR_EX(bank, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); #ifndef WC_RNG_BANK_STATIC -#ifdef WC_HAVE_RNG_BANKREF +#if defined(WC_HAVE_RNG_BANKREF) && !defined(WC_NO_CONSTRUCTORS) if (rng2) wc_rng_free(rng2); #endif @@ -28516,6 +28709,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if ((bank2 != NULL) && (ret == 0)) ret = WC_TEST_RET_ENC_NC; #endif /* !WC_RNG_BANK_STATIC */ + +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +#ifndef WC_NO_CONSTRUCTORS + if (spawned_rng != NULL) + wc_rng_free(spawned_rng); +#endif + if (leaf_rng_inited) { + cleanup_ret = wc_FreeRng(leaf_rng); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + WC_FREE_VAR_EX(leaf_rng, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); +#endif } return ret; @@ -28949,6 +29156,163 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) #endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ +#ifdef WC_RNG_HAVE_NEXT_SEED +/* Coverage for the banked-next-seed facility: the aperture protocol + * (fill / publish / claim / release) as observed through + * wc_RNG_DRBG_NextSeedCurrent(), the distinct protocol results + * (NOT_READY_E, ALREADY_E), advance health testing at publish, + * source-free consumption resetting the reseed counter, use-once + * emptying, and the argument contracts. DRBG-internal probes are gated + * at runtime on wc_RNG_DRBG_Present(). */ +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) +{ + wc_test_ret_t ret = 0; + int api_ret; + int present; + int root_inited = 0; + int i; + WC_DECLARE_VAR(root, WC_RNG, 1, HEAP_HINT); + WC_ATOMIC_INT_ARG cur = 0; + WC_ATOMIC_INT_ARG prev = 0; + wc_drbg_reseed_ctr_t c1 = 0; + byte buf[32]; + byte matter[16]; + + WOLFSSL_ENTER("rng_drbg_nextseed_test"); + + WC_ALLOC_VAR_EX(root, WC_RNG, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); + + XMEMSET(matter, 0x3c, sizeof(matter)); + + /* argument contracts, pre-init */ + if (wc_RNG_DRBG_NextSeedGenerate(NULL, 1) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_NextSeedCurrent(NULL, &cur) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_NextSeedNow(NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_NextSeedNow_Nonce(NULL, NULL, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + api_ret = wc_InitRng(root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + root_inited = 1; + + present = wc_RNG_DRBG_Present(root); + + if (wc_RNG_DRBG_NextSeedGenerate(root, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_NextSeedCurrent(root, NULL) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_NextSeedNow_Nonce(root, NULL, 5) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + if (present) { + /* empty bank: nothing consumable */ + if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || + (cur != WC_DRBG_NEXT_SEED_EMPTY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_NextSeedNow(root) != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* partial bank: counted, still not consumable, preserved across + * the consume attempt */ + api_ret = wc_RNG_DRBG_NextSeedGenerate(root, 7); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || (cur != 7)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_NextSeedNow(root) != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || (cur != 7)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* fill in granules to publication: the count grows monotonically, + * then the ready sentinel appears */ + prev = cur; + for (i = 0; i < 64; i++) { + api_ret = wc_RNG_DRBG_NextSeedGenerate(root, 32); + if ((api_ret != 0) && + (api_ret != WC_NO_ERR_TRACE(ALREADY_E))) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (cur == WC_DRBG_NEXT_SEED_READY) + break; + if (cur <= prev) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + prev = cur; + } + if (cur != WC_DRBG_NEXT_SEED_READY) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* ready bank: further banking is ALREADY_E and changes nothing */ + if (wc_RNG_DRBG_NextSeedGenerate(root, 32) != + WC_NO_ERR_TRACE(ALREADY_E)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || + (cur != WC_DRBG_NEXT_SEED_READY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* consume: source-free credited reseed; counter resets to 1; + * bank empties (use-once) */ + api_ret = wc_RNG_DRBG_NextSeedNow(root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); + if ((api_ret != 0) || (c1 != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || + (cur != WC_DRBG_NEXT_SEED_EMPTY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* advance the counter, refill, and consume with a nonce */ + api_ret = wc_RNG_GenerateBlock(root, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + for (i = 0; i < 64; i++) { + api_ret = wc_RNG_DRBG_NextSeedGenerate(root, 32); + if (api_ret == WC_NO_ERR_TRACE(ALREADY_E)) + break; + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } + if (i >= 64) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_DRBG_NextSeedNow_Nonce(root, matter, + sizeof(matter)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); + if ((api_ret != 0) || (c1 != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || + (cur != WC_DRBG_NEXT_SEED_EMPTY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + +out: + + if (root_inited) { + int cleanup_ret = wc_FreeRng(root); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + WC_FREE_VAR(root, HEAP_HINT); + + return ret; +} +#endif /* WC_RNG_HAVE_NEXT_SEED */ + #endif /* !WC_NO_RNG */ #ifndef MEM_TEST_SZ diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index d881a3fd695..4bdeea7ac96 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -114,6 +114,10 @@ wc_static_assert(-(long)MIN_CODE_E < 0x7ffL); #endif #endif +/* Note, all macro gates used below must be available with just + * wolfcrypt/types.h included, i.e. no macros in alg-specific headers can be + * used here. + */ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t macro_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t error_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t octets_test(void); @@ -259,6 +263,7 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); +extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); #endif #endif /* WC_NO_RNG */ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 7d9cc03e51b..e8fcd15045f 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -249,6 +249,17 @@ struct OS_Seed { /* Setting the default to 4. */ #define SEED_BLOCK_SZ 4 #endif + +/* In-boundary banked-next-seed support: the wc_RNG_DRBG_NextSeed*() APIs and + * the aperture members in the DRBG state structs. Requires native atomics for + * the hand-off protocol. */ +#ifdef WC_RNG_NO_NEXT_SEED + #undef WC_RNG_HAVE_NEXT_SEED +#elif defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + defined(WOLFSSL_ATOMIC_OPS) + #define WC_RNG_HAVE_NEXT_SEED +#endif + #endif #define WC_DRBG_SEED_BLOCK_SZ SEED_BLOCK_SZ @@ -296,6 +307,15 @@ struct OS_Seed { #endif #ifndef NO_SHA256 + +#ifdef WC_RNG_HAVE_NEXT_SEED +/* Length of the banked next seed: identical byte accounting to every other + * source-fed (re)seed in the module (gather SEED_SZ + SEED_BLOCK_SZ, apply + * the block-offset remainder). */ +#define WC_DRBG_NEXT_SEED_LEN ((word32)(WC_DRBG_SEED_SZ + \ + WC_DRBG_SEED_BLOCK_SZ)) +#endif + struct DRBG_internal { #ifdef WORD64_AVAILABLE word64 reseedCtr; @@ -304,6 +324,10 @@ struct DRBG_internal { #endif byte V[DRBG_SEED_LEN]; byte C[DRBG_SEED_LEN]; +#ifdef WC_RNG_HAVE_NEXT_SEED + byte nextSeed[WC_DRBG_NEXT_SEED_LEN]; + wolfSSL_Atomic_Int nextSeedLen; +#endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) int devId; @@ -321,6 +345,10 @@ struct DRBG_SHA512_internal { word64 reseedCtr; byte V[DRBG_SHA512_SEED_LEN]; byte C[DRBG_SHA512_SEED_LEN]; +#ifdef WC_RNG_HAVE_NEXT_SEED + byte nextSeed[WC_DRBG_NEXT_SEED_LEN]; + wolfSSL_Atomic_Int nextSeedLen; +#endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) int devId; @@ -640,6 +668,36 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); #endif /* !WC_NO_CONSTRUCTORS */ WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, const byte* nonce, word32 nonceSz); + +#ifdef WC_RNG_HAVE_NEXT_SEED + /* Banked-next-seed services. _NextSeedGenerate() banks up to n more + * bytes from the module's seed source (clamped to the space remaining; + * ALREADY_E when the bank is ready or being consumed), health-testing + * and publishing the bank when it completes (RETRY_E when the health + * test could not run and the call should simply be retried); a + * scheduling daemon may call it without owning the instance. + * _NextSeedCurrent() reports the raw aperture value (racy snapshot). + * _NextSeedNow() claims a ready bank and performs a source-free + * credited reseed with it -- safe in atomic context -- or returns + * NOT_READY_E when no bank is ready; _NextSeedNow_Nonce() is the same + * with a nonce as uncredited additional input. All report + * MISSING_RNG_E for an instance with no DRBG (RDRAND et al.). The + * caller must own the instance for _NextSeedNow[_Nonce](). */ + + #define WC_DRBG_NEXT_SEED_EMPTY 0 + /* All sentinel states are negative; non-negative values are banked byte + * counts. */ + #define WC_DRBG_NEXT_SEED_READY ((WC_ATOMIC_INT_ARG)(-2)) + #define WC_DRBG_NEXT_SEED_CONSUMING ((WC_ATOMIC_INT_ARG)(-1)) + + WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n); + WOLFSSL_API int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng, + WC_ATOMIC_INT_ARG* n); + WOLFSSL_API int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, + const byte* nonce, + word32 nonceSz); + WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); +#endif /* WC_RNG_HAVE_NEXT_SEED */ #endif #ifndef NO_SHA256 From 8054f3e5d59015b221b4d6c5a02f63f1ee5d8073 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 26 Aug 2026 14:40:03 -0500 Subject: [PATCH 018/102] wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/test/test.c: * integrate the banked-next-seed facility, and add WC_RNG_BANK_FLAG_QUIET. * add recovery-patrol and leased-spawn support: WC_RNG_BANK_FLAG_FOR_RECOVERY, WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED, wc_rng_bank_recover_inst(), wc_rng_bank_spawn(), and wc_rng_bank_spawn_new(). struct wc_rng_bank gains inst_op_gate, a bank-global wolfSSL_Atomic_Int serializing whole-instance operations against the daemon's lockless banking: wc_rng_bank_next_seed_generate() (the daemon entry point) claims it around wc_RNG_DRBG_NextSeedGenerate(), and wc_rng_bank_inst_reinit() claims it around its free/reinstantiate cycle -- non-blocking on both sides (BUSY_E = skip this turn / this attempt). lease-holders never consult the gate; instance-lock exclusion already covers every lease-holder interaction, so the hot path is untouched. the entry point's return taxonomy is daemon policy: ALREADY_E = sleep until consumed; MISSING_RNG_E = retire the instance from the banking rotation; others transient. WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED: wc_rng_bank_checkout() consumes a ready banked next seed on the locked instance BEFORE the usability evaluation, with the return value deliberately ignored -- every outcome is fully represented in instance state and handled uniformly by the incumbent divert/error/handout machinery: a consumed bank cures a reseed-due instance in place (no divert, no warning); a hard reseed failure marks the instance out of service and flows through the standard paths. WC_RNG_BANK_FLAG_QUIET (bank-level only, set at wc_rng_bank_init(); no per-call meaning): suppresses the facility's WC_VERBOSE_RNG expected-condition warnings -- reseed-due handout, reinit retry/timeout reports, all-instances-busy, seed-walker out-of-service reports -- so deliberate exercising doesn't spam the log; never suppresses refcount/consistency diagnostics. random_bank_test() sets it at both bank constructions and gains coverage for the daemon entry point's contracts and the consume-at-checkout flow (fill to ALREADY_E, consume lands the reseed counter at 1, empty-bank consume is transparent). _FOR_RECOVERY declares a recovery-intent checkout of an explicit instance: out-of-service status is expected, the consume-next-seed arm is suppressed, and selection-altering flags are rejected. wc_rng_bank_recover_inst() packages checkout -> reinit-iff-out-of-service -> checkin; healthy instances are success no-ops, so patrols can act on lockless status observations. _ERROR_ON_RNG_FAILED guarantees checkout returns either a lease on an in-service instance or an error with no lease, closing the two paths that could lease a dead one (targeted checkout; failover after the anti-livelock lap disarm). under _CAN_WAIT a dead instance is retried within the timeout budget -- the window in which a recovery patrol restores it -- and a lap or wait that found only out-of-service instances reports the distinguished BAD_STATE_E. wc_rng_bank_spawn()/_spawn_new() check out an instance (implying _ERROR_ON_RNG_FAILED), wc_InitRngNonceRBGC[_New]() an SP 800-90C chain leaf from it, and check it back in via wc_rng_bank_inst_checkin(); the leaf's lifetime is thereafter decoupled from the bank and lock-free for its owner. _CONSUME_NEXT_SEED composes for a banked reseed of the root before the spawn draw. the heap form and its supporting arms are gated !WC_NO_CONSTRUCTORS, mirroring wc_InitRngNonceRBGC_New()'s declaration; the stack form remains available under WC_NO_CONSTRUCTORS. random_bank_test() gains coverage for the one-arg checkin roundtrip, the flag contracts and rejected combinations, recovery-patrol no-op semantics, and both spawn forms. --- wolfcrypt/src/rng_bank.c | 359 ++++++++++++++++++++++++++++++++--- wolfcrypt/test/test.c | 3 +- wolfssl/wolfcrypt/rng_bank.h | 123 ++++++++++++ 3 files changed, 455 insertions(+), 30 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index c23eaf14e42..248dd4066be 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -57,6 +57,9 @@ WOLFSSL_API int wc_rng_bank_init( if (ret != 0) return ret; +#ifdef WC_RNG_HAVE_NEXT_SEED + wolfSSL_Atomic_Int_Init(&ctx->inst_op_gate, 0); +#endif ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; ctx->heap = heap; @@ -450,6 +453,7 @@ WOLFSSL_API int wc_rng_bank_checkout( int ret = 0; time_t ts1, ts2; int n_rngs_tried = 0; + int diverted_unusable = 0; WC_ATOMIC_INT_ARG new_refcount; if (rng_inst == NULL) @@ -487,6 +491,17 @@ WOLFSSL_API int wc_rng_bank_checkout( } } + if ((flags & WC_RNG_BANK_FLAG_FOR_RECOVERY) && + (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | + WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED))) + { + /* Recovery targets one explicit instance -- selection-altering flags + * contradict it. */ + ret = BAD_FUNC_ARG; + goto out; + } + if ((flags & WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST) && (bank->affinity_get_id_cb == NULL)) { @@ -565,10 +580,40 @@ WOLFSSL_API int wc_rng_bank_checkout( &expected, new_lock_value)) { + int inst_unusable; wc_drbg_reseed_ctr_t cur_reseed_ctr = 0; *rng_inst = &bank->rngs[preferred_inst_offset]; +#ifdef WC_RNG_HAVE_NEXT_SEED + if ((flags & WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) && + (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY))) + { + /* Consume a ready banked next seed, if any, BEFORE the + * usability evaluation below, so that evaluation judges the + * post-consume state: a reseed-due instance with a ready + * bank is cured here rather than diverted from or warned + * about. The return value is deliberately ignored -- + * every outcome is fully represented in instance state and + * is handled uniformly below: + * + * consumed: reseed counter reset; no longer due; + * not ready / no DRBG: nothing changed; + * status-gated (instance already out of service): the + * bank is left intact and the instance diverts or + * errors below per flags; + * hard reseed failure: the instance is now out of + * service, and diverts (failover finds another + * instance), errors (_ERROR_ON_RNG_FAILED -> + * BAD_STATE_E via the out: mapping), or is handed + * out under incumbent bare-targeted semantics -- + * identically to any other out-of-service instance. + */ + (void)wc_RNG_DRBG_NextSeedNow( + WC_RNG_BANK_INST_TO_RNG(*rng_inst)); + } +#endif /* WC_RNG_HAVE_NEXT_SEED */ + /* Two scenarios where we put an instance back and move on, both of * them only when the caller allows failover and instances remain: * @@ -587,27 +632,53 @@ WOLFSSL_API int wc_rng_bank_checkout( * such instances -- never due for reseed -- so no separate * DRBG-presence test is needed here. */ - if ((flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) && - (n_rngs_tried < bank->n_rngs) && - ((wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(*rng_inst)) != - WC_DRBG_OK) || - ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && - (wc_RNG_DRBG_GetReseedCtr( - WC_RNG_BANK_INST_TO_RNG(*rng_inst), - &cur_reseed_ctr) == 0) && - (cur_reseed_ctr >= WC_RESEED_INTERVAL)))) + inst_unusable = + (wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(*rng_inst)) != + WC_DRBG_OK); + + /* Divert (release and move on / retry) when: + * + * (a) the instance is out of service and the caller demanded + * the WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED guarantee -- + * unconditionally, in both targeted and failover modes, + * with the wait/timeout machinery below bounding the + * retries and the out: mapping converting the resulting + * BUSY_E/WC_TIMEOUT_E to BAD_STATE_E; or + * + * (b) the incumbent best-effort failover divert: instances + * remain untried this lap, and the instance is out of + * service or is due for reseed for a caller that can't + * wait. (The lap disarm is the anti-livelock provision; + * with (a) in force, the guarantee supersedes it.) + */ + if ((inst_unusable && + (flags & WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED)) || + ((flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) && + (n_rngs_tried < bank->n_rngs) && + (inst_unusable || + ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && + (wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(*rng_inst), + &cur_reseed_ctr) == 0) && + (cur_reseed_ctr >= WC_RESEED_INTERVAL))))) { + if (inst_unusable) + diverted_unusable = 1; WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); *rng_inst = NULL; } else { #ifdef WC_VERBOSE_RNG - if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && + if ((! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) && + (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && (wc_RNG_DRBG_GetReseedCtr( WC_RNG_BANK_INST_TO_RNG(*rng_inst), &cur_reseed_ctr) == 0) && (cur_reseed_ctr >= WC_RESEED_INTERVAL)) { + /* With WC_RNG_HAVE_NEXT_SEED, this reports only a + * genuinely-due instance: a consumable banked seed + * would already have cured it above. */ WOLFSSL_DEBUG_PRINTF( "WARNING: wc_rng_bank_checkout() returning RNG ID %d, " "currently marked for reseed, to !_CAN_WAIT caller.\n", @@ -700,6 +771,18 @@ WOLFSSL_API int wc_rng_bank_checkout( if (ret == 0) ret = RNG_FAILURE_E; + /* Under the WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED guarantee, a lap or + * wait that diverted from an out-of-service instance reports + * BAD_STATE_E -- distinguishing bank degradation from mere contention + * (BUSY_E) or slow contention (WC_TIMEOUT_E). */ + if (diverted_unusable && + (flags & WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED) && + ((ret == WC_NO_ERR_TRACE(BUSY_E)) || + (ret == WC_NO_ERR_TRACE(WC_TIMEOUT_E)))) + { + ret = BAD_STATE_E; + } + if (new_lock_value & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) (void)bank->affinity_unlock_cb(bank->cb_arg); @@ -763,7 +846,9 @@ WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref( ((bank->affinity_lock_cb != NULL) ? WC_RNG_BANK_FLAG_AFFINITY_LOCK : 0)); #ifdef WC_VERBOSE_RNG - if (ret == WC_NO_ERR_TRACE(BUSY_E)) { + if ((ret == WC_NO_ERR_TRACE(BUSY_E)) && + (! (bank->flags & WC_RNG_BANK_FLAG_QUIET))) + { WOLFSSL_DEBUG_PRINTF( "WARNING: all %d rng_bank instances busy; size the bank to at " "least the peak number of concurrent callers.\n", bank->n_rngs); @@ -929,6 +1014,43 @@ WOLFSSL_API int wc_rng_bank_inst_checkin( return wc_rng_bank_checkin((*rng_inst)->bank, rng_inst); } +#ifdef WC_RNG_HAVE_NEXT_SEED + +#define WC_RNG_BANK_INST_OP_DAEMON ((WC_ATOMIC_INT_ARG)1) +#define WC_RNG_BANK_INST_OP_REINIT ((WC_ATOMIC_INT_ARG)2) + +WOLFSSL_API int wc_rng_bank_next_seed_generate( + struct wc_rng_bank *bank, + int inst_offset, + word32 n) +{ + int ret; + WC_ATOMIC_INT_ARG expected = 0; + + if ((bank == NULL) || (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || + (inst_offset < 0) || (inst_offset >= bank->n_rngs)) + { + return BAD_FUNC_ARG; + } + + if (! wolfSSL_Atomic_Int_CompareExchange(&bank->inst_op_gate, &expected, + WC_RNG_BANK_INST_OP_DAEMON)) + { + /* A whole-instance operation (reinit) is in progress somewhere in + * the bank -- skip this turn. */ + return BUSY_E; + } + + ret = wc_RNG_DRBG_NextSeedGenerate( + WC_RNG_BANK_INST_TO_RNG(&bank->rngs[inst_offset]), n); + + WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, 0); + + return ret; +} + +#endif /* WC_RNG_HAVE_NEXT_SEED */ + /* note the rng_inst passed to wc_rng_bank_inst_reinit() must have been obtained * via wc_rng_bank_checkout() to assure that the caller holds the proper locks. */ @@ -967,6 +1089,23 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( devId = INVALID_DEVID; #endif +#ifdef WC_RNG_HAVE_NEXT_SEED + /* Exclude the entropy daemon's lockless banking for the duration of the + * free/reinstantiate cycle. Non-blocking on both sides: if the daemon + * holds the gate, skip this reinit attempt (the instance stays out of + * service and a later checkout retries); if reinit holds it, the daemon + * skips its turn. */ + { + WC_ATOMIC_INT_ARG expected = 0; + if (! wolfSSL_Atomic_Int_CompareExchange(&bank->inst_op_gate, + &expected, + WC_RNG_BANK_INST_OP_REINIT)) + { + return BUSY_E; + } + } +#endif + wc_FreeRng(&rng_inst->rng); for (;;) { @@ -1000,17 +1139,20 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( case WC_NO_ERR_TRACE(DRBG_KAT_FIPS_E): case WC_NO_ERR_TRACE(DRBG_CONT_FIPS_E): #ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_inst_reinit() non-retryable err %d.\n", - ret); + if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_inst_reinit() non-retryable err " + "%d.\n", ret); #endif goto out; } if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) || (timeout_secs == 0)) { #ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_inst_reinit() returning err %d.\n", ret); + if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_inst_reinit() returning err %d.\n", + ret); #endif break; } @@ -1031,9 +1173,10 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( time_t ts2 = XTIME(0); if (ts2 - ts1 >= timeout_secs) { #ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_inst_reinit() timed out, err %d.\n", - ret); + if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_inst_reinit() timed out, " + "err %d.\n", ret); #endif break; } @@ -1055,9 +1198,160 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( if (ret != 0) (void)wc_FreeRng(WC_RNG_BANK_INST_TO_RNG(rng_inst)); +#ifdef WC_RNG_HAVE_NEXT_SEED + WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, 0); +#endif + return ret; } +WOLFSSL_API int wc_rng_bank_recover_inst( + struct wc_rng_bank *bank, + int inst_offset, + int timeout_secs, + word32 flags) +{ + struct wc_rng_bank_inst *rng_inst = NULL; + int ret; + int checkin_ret; + + if ((bank == NULL) || + (flags & ~(word32)(WC_RNG_BANK_FLAG_CAN_WAIT | + WC_RNG_BANK_FLAG_AFFINITY_LOCK))) + { + return BAD_FUNC_ARG; + } + + ret = wc_rng_bank_checkout(bank, &rng_inst, inst_offset, timeout_secs, + flags | WC_RNG_BANK_FLAG_FOR_RECOVERY); + if (ret != 0) + return ret; + + if (wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(rng_inst)) != WC_DRBG_OK) { + /* Out of service -- recover it. A BUSY_E from the whole-instance- + * operation gate is retryable on a later patrol turn. */ + ret = wc_rng_bank_inst_reinit(bank, rng_inst, timeout_secs, flags); + } + /* else: healthy -- a stale lockless status observation; no-op. */ + + checkin_ret = wc_rng_bank_checkin(bank, &rng_inst); + if ((checkin_ret != 0) && (ret == 0)) + ret = checkin_ret; + + return ret; +} + +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +/* Unified mechanics for wc_rng_bank_spawn() and wc_rng_bank_spawn_new(): + * check out -> wc_InitRngNonceRBGC[_New]() -> check in, following the + * exactly-one-destination convention of random.c's SpawnRngRBGC(). All + * RBGC semantics (depth-one enforcement, leaf tagging, strength + * accounting, root reseed-counter debit) are the spawn APIs' own; the + * bank contributes instance selection, the lease, and (optionally, via + * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) a banked reseed of the root before + * the spawn draw. */ +static int rng_bank_spawn( + struct wc_rng_bank *bank, + WC_RNG *leaf_stack, + WC_RNG **leaf_heap, + byte *nonce, + word32 nonceSz, + int preferred_inst_offset, + int timeout_secs, + word32 flags) +{ + struct wc_rng_bank_inst *rng_inst = NULL; + int ret; + int checkin_ret; + + if ((leaf_stack == NULL) == (leaf_heap == NULL)) + return BAD_FUNC_ARG; + + if (flags & (WC_RNG_BANK_FLAG_SEED_UNCREDITED | + WC_RNG_BANK_FLAG_FOR_RECOVERY)) + return BAD_FUNC_ARG; + + /* bank == NULL resolves to the default bank inside + * wc_rng_bank_checkout(), which carries the default-bank refcount + * through the lease; the one-arg wc_rng_bank_inst_checkin() releases + * the whole arrangement without requiring a bank pointer here. + * WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED makes the checkout itself + * guarantee an in-service instance (or an error with no lease), so no + * status gate is needed here. */ + ret = wc_rng_bank_checkout(bank, &rng_inst, preferred_inst_offset, + timeout_secs, + flags | WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED); + if (ret != 0) + return ret; + + if (leaf_stack != NULL) { + ret = wc_InitRngNonceRBGC(leaf_stack, + WC_RNG_BANK_INST_TO_RNG(rng_inst), + nonce, nonceSz); + } + else { +#ifndef WC_NO_CONSTRUCTORS + ret = wc_InitRngNonceRBGC_New(leaf_heap, + WC_RNG_BANK_INST_TO_RNG(rng_inst), + nonce, nonceSz); +#else + /* Unreachable: wc_rng_bank_spawn_new() is absent under + * WC_NO_CONSTRUCTORS, so leaf_heap is always null here. */ + ret = BAD_FUNC_ARG; +#endif + } + + checkin_ret = wc_rng_bank_inst_checkin(&rng_inst); + if ((checkin_ret != 0) && (ret == 0)) { + /* The leaf came up but the lease release failed: surface the + * check-in error and don't hand back a leaf the caller would + * reasonably pair with a healthy bank. */ + if (leaf_stack != NULL) { + (void)wc_FreeRng(leaf_stack); + } +#ifndef WC_NO_CONSTRUCTORS + else { + wc_rng_free(*leaf_heap); + *leaf_heap = NULL; + } +#endif + ret = checkin_ret; + } + + return ret; +} + +WOLFSSL_API int wc_rng_bank_spawn( + struct wc_rng_bank *bank, + WC_RNG *leaf_rng, + byte *nonce, + word32 nonceSz, + int preferred_inst_offset, + int timeout_secs, + word32 flags) +{ + return rng_bank_spawn(bank, leaf_rng, NULL, nonce, nonceSz, + preferred_inst_offset, timeout_secs, flags); +} + +#ifndef WC_NO_CONSTRUCTORS +WOLFSSL_API int wc_rng_bank_spawn_new( + struct wc_rng_bank *bank, + WC_RNG **leaf_rng, + byte *nonce, + word32 nonceSz, + int preferred_inst_offset, + int timeout_secs, + word32 flags) +{ + return rng_bank_spawn(bank, NULL, leaf_rng, nonce, nonceSz, + preferred_inst_offset, timeout_secs, flags); +} +#endif /* !WC_NO_CONSTRUCTORS */ +#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && + * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, word32 seedSz, int timeout_secs, @@ -1074,7 +1368,9 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, * than requested. Same restriction applies in wc_rng_bank_reseed(). */ if (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | - WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST)) + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | + WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED | + WC_RNG_BANK_FLAG_FOR_RECOVERY)) return BAD_FUNC_ARG; if (bank == NULL) { @@ -1107,12 +1403,14 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, struct wc_rng_bank_inst *drbg; ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, flags & ~(word32) - WC_RNG_BANK_FLAG_SEED_UNCREDITED); + (WC_RNG_BANK_FLAG_SEED_UNCREDITED | + WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED)); if (ret != 0) { #ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_seed(): wc_rng_bank_checkout() for " - "inst#%d returned err %d.\n", n, ret); + if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_seed(): wc_rng_bank_checkout() for " + "inst#%d returned err %d.\n", n, ret); #endif break; } @@ -1127,10 +1425,11 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, WC_DRBG_OK) { #ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_seed(): inst#%d is out of service " - "(status %d).\n", n, - wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(drbg))); + if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_seed(): inst#%d is out of service " + "(status %d).\n", n, + wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(drbg))); #endif ret = BAD_STATE_E; } @@ -1182,7 +1481,9 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, */ if (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | - WC_RNG_BANK_FLAG_SEED_UNCREDITED)) + WC_RNG_BANK_FLAG_SEED_UNCREDITED | + WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED | + WC_RNG_BANK_FLAG_FOR_RECOVERY)) return BAD_FUNC_ARG; if (bank == NULL) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index af43d7d0372..0b56a10b18b 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -27960,6 +27960,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifndef DEBUG_VECTOR_REGISTER_ACCESS_ALWAYS_ON WC_RNG_BANK_FLAG_NO_VECTOR_OPS | #endif + WC_RNG_BANK_FLAG_QUIET | WC_RNG_BANK_FLAG_CAN_WAIT, 10, HEAP_HINT, INVALID_DEVID); if (ret != 0) @@ -28366,7 +28367,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #else /* !WC_RNG_BANK_STATIC */ - ret = wc_rng_bank_new(&bank2, WC_RNG_BANK_STATIC_SIZE + 1, WC_RNG_BANK_FLAG_NONE, 10, HEAP_HINT, INVALID_DEVID); + ret = wc_rng_bank_new(&bank2, WC_RNG_BANK_STATIC_SIZE + 1, WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, INVALID_DEVID); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 147d84009e6..c51674e8574 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -52,6 +52,47 @@ * (wc_RNG_DRBG_Reseed_Uncredited()), leaving the reseed schedule governed * solely by the module's own seed source. */ #define WC_RNG_BANK_FLAG_SEED_UNCREDITED (1<<6) +/* WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED applies only to wc_rng_bank_checkout(): + * if the checked-out instance has a ready banked next seed (see + * wc_RNG_DRBG_NextSeedGenerate() et al.), consume it in an immediate, + * source-free credited reseed before returning the instance; a no-op when + * no bank is ready or the build/instance has no next-seed support. Safe in + * atomic context. */ +#define WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED (1<<7) +/* WC_RNG_BANK_FLAG_FOR_RECOVERY declares a recovery-intent checkout of a + * specific instance (e.g. by a reseed-and-recovery daemon's patrol): + * out-of-service status is expected and accepted, and the + * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED arm is suppressed (a consume would + * fail on exactly the instances recovery targets). Requires an explicit + * instance: rejected in combination with _CAN_FAIL_OVER_INST or + * _PREFER_AFFINITY_INST, and by the seed/reseed walkers. Note that a + * targeted (non-failover) checkout admits out-of-service instances with or + * without this flag; the flag makes the intent explicit and + * interaction-safe. */ +#define WC_RNG_BANK_FLAG_FOR_RECOVERY (1<<8) +/* WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED guarantees that + * wc_rng_bank_checkout() (and APIs built on it, e.g. wc_rng_bank_spawn()) + * either returns a lease on an in-service instance (status WC_DRBG_OK) or + * returns an error with NO lease held -- never a lease on an out-of-service + * instance. This closes the two paths that can otherwise lease one: a + * targeted (non-failover) checkout, and a failover checkout after a full + * unsuccessful lap (the anti-livelock disarm). Under _CAN_WAIT, an + * out-of-service instance is retried within the timeout budget (allowing a + * recovery patrol to restore it) before the error is returned; the + * distinguished error for a lap or wait that found only out-of-service + * instances is BAD_STATE_E. Contradicts, and is rejected with, + * _FOR_RECOVERY. Applies to instance status only; reseed-due diversion + * semantics are unchanged. */ +#define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1<<9) +/* WC_RNG_BANK_FLAG_QUIET suppresses the facility's WC_VERBOSE_RNG + * operational warnings -- expected-condition notices such as the + * reseed-due-instance handout, reinit retry/timeout reports, the + * all-instances-busy notice, and the seed-walker's out-of-service reports + * -- so that deliberate exercising (e.g. unit tests) doesn't spam the + * log. A bank-level flag only, set at wc_rng_bank_init(); it has no + * per-call meaning and never suppresses refcount/consistency + * diagnostics. */ +#define WC_RNG_BANK_FLAG_QUIET (1<<10) #define WC_RNG_BANK_INST_LOCK_FREE 0 #define WC_RNG_BANK_INST_LOCK_HELD (1<<0) @@ -283,6 +324,16 @@ struct wc_rng_bank { wc_affinity_unlock_fn_t affinity_unlock_cb; void *cb_arg; /* if mutable, caller is responsible for thread safety. */ int n_rngs; +#ifdef WC_RNG_HAVE_NEXT_SEED + /* Serializes whole-instance operations (wc_rng_bank_inst_reinit()'s + * free/reinstantiate cycle) against the entropy daemon's lockless + * banking calls (wc_rng_bank_next_seed_generate()). 0 = free, + * WC_RNG_BANK_INST_OP_DAEMON = daemon banking in progress, + * WC_RNG_BANK_INST_OP_REINIT = reinit in progress. Lease-holders + * never consult it: instance-lock exclusion already covers every + * lease-holder <-> reinit and lease-holder <-> consume interaction. */ + wolfSSL_Atomic_Int inst_op_gate; +#endif #ifdef WC_RNG_BANK_STATIC struct wc_rng_bank_inst rngs[WC_RNG_BANK_STATIC_SIZE]; #else @@ -351,12 +402,84 @@ WOLFSSL_API int wc_rng_bank_checkin( WOLFSSL_API int wc_rng_bank_inst_checkin( struct wc_rng_bank_inst **rng_inst); +#ifdef WC_RNG_HAVE_NEXT_SEED +/* Daemon entry point for banking next-seed material: resolves the instance + * at inst_offset and calls wc_RNG_DRBG_NextSeedGenerate(rng, n) under the + * bank's whole-instance-operation gate, so a concurrent + * wc_rng_bank_inst_reinit() can never free the DRBG out from under the + * gather. Returns BUSY_E (skip this turn) when the gate is held by a + * reinit; ALREADY_E when the instance's bank is already complete (sleep + * until consumed); MISSING_RNG_E when the instance has no DRBG (RDRAND + * et al.) and can be retired from the banking rotation permanently. + * Other errors are transient gather/health-test failures: skip the turn + * and alarm if persistent. The caller must hold a bank reference (e.g. + * per the daemon association) for the duration of the call. + */ +WOLFSSL_API int wc_rng_bank_next_seed_generate( + struct wc_rng_bank *bank, + int inst_offset, + word32 n); +#endif + WOLFSSL_API int wc_rng_bank_inst_reinit( struct wc_rng_bank *bank, struct wc_rng_bank_inst *rng_inst, int timeout_secs, word32 flags); +/* Patrol helper: check out the instance at inst_offset with + * WC_RNG_BANK_FLAG_FOR_RECOVERY, reinitialize it iff it is out of service, + * and check it back in. A healthy instance is a success no-op, so callers + * can invoke this unconditionally on a status observed locklessly (a stale + * observation costs one harmless round trip). Returns BUSY_E when the + * instance lock or the whole-instance-operation gate is contended -- retry + * on a later patrol turn. flags may include WC_RNG_BANK_FLAG_CAN_WAIT and + * WC_RNG_BANK_FLAG_AFFINITY_LOCK, which are passed through; bank must be + * non-NULL. */ +WOLFSSL_API int wc_rng_bank_recover_inst( + struct wc_rng_bank *bank, + int inst_offset, + int timeout_secs, + word32 flags); + +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +/* Spawn an SP 800-90C chain leaf from a bank instance: check out an + * instance (honoring the usual selection flags), wc_InitRngNonceRBGC() / + * wc_InitRngNonceRBGC_New() the leaf from it, and check the instance back + * in. The leaf's lifetime is thereafter decoupled from the bank: it is + * lock-free for its owner and is released with wc_FreeRng() (stack form) + * or wc_rng_free() (heap form). nonce/nonceSz may be NULL/0 for a plain + * spawn; per the bank's distinctness convention, passing the address of + * the leaf's owning object or request is recommended. bank == NULL uses + * the default bank where support is compiled in. + * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED composes (banked reseed before the + * spawn draw); WC_RNG_BANK_FLAG_SEED_UNCREDITED and + * WC_RNG_BANK_FLAG_FOR_RECOVERY are rejected. + * WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED is implied: the root is guaranteed + * in-service, or an error is returned with no lease and no leaf. */ +WOLFSSL_API int wc_rng_bank_spawn( + struct wc_rng_bank *bank, + WC_RNG *leaf_rng, + byte *nonce, + word32 nonceSz, + int preferred_inst_offset, + int timeout_secs, + word32 flags); + +#ifndef WC_NO_CONSTRUCTORS +WOLFSSL_API int wc_rng_bank_spawn_new( + struct wc_rng_bank *bank, + WC_RNG **leaf_rng, + byte *nonce, + word32 nonceSz, + int preferred_inst_offset, + int timeout_secs, + word32 flags); +#endif /* !WC_NO_CONSTRUCTORS */ +#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && + * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, word32 seedSz, int timeout_secs, From 8e5ef6aeaf39acdaae9bd314faa93cc656543620 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 04:50:58 +0000 Subject: [PATCH 019/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfssl/wolfcrypt/settings.h, wolfssl/wolfcrypt/wc_port.h, .wolfssl_known_macro_extras: portability and correctness fixes for the RNG: * wc_GenerateSeed() Intel RDSEED/RDRAND paths: return WC_HW_E/NOT_READY_E rather than raw -1; * wc_RNG_DRBG_NextSeedGenerate(): return NOT_READY_E, not RETRY_E, for the non-dispositive MEMORY_E arm from wc_RNG_TestSeed() -- RETRY_E is defined nowhere (latent build break in WC_RNG_HAVE_NEXT_SEED configs); update the random.h comment to match; * _InitRng(): add missing braces around a conditional arm; * random.h: move the WC_RNG_NO_NEXT_SEED/WC_RNG_HAVE_NEXT_SEED knob derivation ahead of first use, and add WC_DRBG_nextSeedLen_t with an atomics-free arm (sword32) for WOLFSSL_NO_ATOMICS configs; * random.h: add a 32-bit reseedCtr arm to struct DRBG_SHA512_internal for !WORD64_AVAILABLE targets, and retire the WC_RESEED_INTERVAL compile-time assert it obsoletes; settings.h: tier the kernel-mode WC_RESEED_INTERVAL default (UINT_MAX on 16/32-bit targets, 1<<48 on 64-bit targets) with untyped literals so the preprocessor can evaluate it; * settings.h: drop the stale NO_ASN_TIME guard un-defining WC_RNG_BANK_SUPPORT -- XTIME() availability does not depend on ASN time support; * wc_port.h: de-volatile the WOLFSSL_NO_ATOMICS wolfSSL_Atomic_{Int,Uint} typedefs (volatile implies protection these types cannot provide there), and add #error guards and clarified comments for the TIME_OVERRIDES contract; * random.c: refresh the banked-next-seed doc comments (services summary and hand-off protocol); --- wolfcrypt/src/random.c | 41 +++++++++++++++--------------- wolfssl/wolfcrypt/random.h | 48 ++++++++++++++++++++++-------------- wolfssl/wolfcrypt/settings.h | 15 +++++------ wolfssl/wolfcrypt/wc_port.h | 24 ++++++++++++------ 4 files changed, 75 insertions(+), 53 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index cfab8165ec8..7d38c98c269 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -805,19 +805,6 @@ int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, return 0; } -/* wc_RNG_DRBG_ScheduleReseed() drives reseedCtr up to WC_RESEED_INTERVAL to - * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when - * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 - * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 - * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time - * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be - * defined with a (word64) cast (settings.h kernel path) that the preprocessor - * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips - * it without expanding that cast. */ -#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) - wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); -#endif - /* Mark rng due for reseed: the next generate operation reseeds from the * module's built-in or registered seed source before producing output, and * wc_RNG_DRBG_Reseed_Now() performs the same reseed immediately. This can @@ -2393,7 +2380,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, #else ret = wc_GenerateSeed(&rng->seed, seed, seedSz); #endif /* WC_RNG_SEED_CB */ - } + } #ifdef WOLFSSL_CHECK_MEM_ZERO /* seed now holds entropy; register across DRBG instantiation */ wc_MemZero_Add("_InitRng seed", seed, seedSz); @@ -2926,7 +2913,21 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, const byte* nonce, #ifdef WC_RNG_HAVE_NEXT_SEED -/* Banked-next-seed ("aperture") protocol. + /* Banked-next-seed services. _NextSeedGenerate() banks up to n more + * bytes from the module's seed source (clamped to the space remaining; + * ALREADY_E when the bank is ready or being consumed), health-testing + * and publishing the bank when it completes (NOT_READY_E when the health + * test could not run and the call should simply be retried); a + * scheduling daemon may call it without owning the instance. + * _NextSeedCurrent() reports the raw aperture value (racy snapshot). + * _NextSeedNow() claims a ready bank and performs a source-free + * credited reseed with it -- safe in atomic context -- or returns + * NOT_READY_E when no bank is ready; _NextSeedNow_Nonce() is the same + * with a nonce as uncredited additional input. All report + * MISSING_RNG_E for an instance with no DRBG (RDRAND et al.). The + * caller must own the instance for _NextSeedNow[_Nonce](). */ + +/* Banked-next-seed protocol. * * Entropy is gathered incrementally, in-boundary, from the * module's seed source by wc_RNG_DRBG_NextSeedGenerate(), and consumed @@ -3045,7 +3046,7 @@ int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) { /* wc_RNG_TestSeed() did nothing with the data -- not * dispositive. */ - return RETRY_E; + return NOT_READY_E; } else if ((ret == WC_NO_ERR_TRACE(ENTROPY_RT_E)) || (ret == WC_NO_ERR_TRACE(ENTROPY_APT_E))) @@ -4519,7 +4520,7 @@ static WC_INLINE int IntelRDseed64_r(word64* rnd) if (IntelRDseed64(rnd) == 0) return 0; } - return -1; + return NOT_READY_E; } /* return 0 on success */ @@ -4533,7 +4534,7 @@ static int wc_GenerateSeed_IntelRD(OS_Seed* os, byte* output, word32 sz) (void)os; if (!IS_INTEL_RDSEED(intel_flags)) - return -1; + return WC_HW_E; /* Note, access to rdseed_sanity_status is benignly racey on multithreaded * targets. @@ -4561,14 +4562,14 @@ static int wc_GenerateSeed_IntelRD(OS_Seed* os, byte* output, word32 sz) "check CPU microcode version.", sanity_word2); #endif rdseed_sanity_status = -1; - return -1; + return WC_HW_E; } } rdseed_sanity_status = 1; } else if (rdseed_sanity_status < 0) { - return -1; + return WC_HW_E; } for (; (sz / sizeof(word64)) > 0; sz -= sizeof(word64), diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index e8fcd15045f..55b9c5dacc3 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -43,6 +43,23 @@ WOLFSSL_LOCAL int wolfCrypt_FIPS_DRBG_sanity(void); #endif +#if !defined(HAVE_HASHDRBG) || defined(CUSTOM_RAND_GENERATE_BLOCK) && \ + !defined(WC_RNG_NO_NEXT_SEED) + #define WC_RNG_NO_NEXT_SEED +#endif +#ifndef WC_RNG_NO_NEXT_SEED + #ifndef WC_RNG_HAVE_NEXT_SEED + #define WC_RNG_HAVE_NEXT_SEED + #endif + #ifdef WOLFSSL_NO_ATOMICS + typedef sword32 WC_DRBG_nextSeedLen_t; + #else + typedef wolfSSL_Atomic_Int WC_DRBG_nextSeedLen_t; + #endif +#else + #undef WC_RNG_HAVE_NEXT_SEED +#endif + /* Maximum generate block length */ #ifndef RNG_MAX_BLOCK_LEN #ifdef HAVE_INTEL_QA @@ -73,7 +90,7 @@ #undef HAVE_HASHDRBG #define HAVE_HASHDRBG #ifndef WC_RESEED_INTERVAL - #define WC_RESEED_INTERVAL (1000000) + #define WC_RESEED_INTERVAL 1000000 #endif #endif @@ -250,16 +267,6 @@ struct OS_Seed { #define SEED_BLOCK_SZ 4 #endif -/* In-boundary banked-next-seed support: the wc_RNG_DRBG_NextSeed*() APIs and - * the aperture members in the DRBG state structs. Requires native atomics for - * the hand-off protocol. */ -#ifdef WC_RNG_NO_NEXT_SEED - #undef WC_RNG_HAVE_NEXT_SEED -#elif defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - defined(WOLFSSL_ATOMIC_OPS) - #define WC_RNG_HAVE_NEXT_SEED -#endif - #endif #define WC_DRBG_SEED_BLOCK_SZ SEED_BLOCK_SZ @@ -309,11 +316,10 @@ struct OS_Seed { #ifndef NO_SHA256 #ifdef WC_RNG_HAVE_NEXT_SEED -/* Length of the banked next seed: identical byte accounting to every other - * source-fed (re)seed in the module (gather SEED_SZ + SEED_BLOCK_SZ, apply - * the block-offset remainder). */ -#define WC_DRBG_NEXT_SEED_LEN ((word32)(WC_DRBG_SEED_SZ + \ - WC_DRBG_SEED_BLOCK_SZ)) + /* Length of the banked next seed: identical byte accounting to other + * source-fed (re)seeds in the module (gather SEED_SZ + SEED_BLOCK_SZ, apply + * the block-offset remainder). */ + #define WC_DRBG_NEXT_SEED_LEN (WC_DRBG_SEED_SZ + WC_DRBG_SEED_BLOCK_SZ) #endif struct DRBG_internal { @@ -326,7 +332,7 @@ struct DRBG_internal { byte C[DRBG_SEED_LEN]; #ifdef WC_RNG_HAVE_NEXT_SEED byte nextSeed[WC_DRBG_NEXT_SEED_LEN]; - wolfSSL_Atomic_Int nextSeedLen; + WC_DRBG_nextSeedLen_t nextSeedLen; #endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -342,12 +348,16 @@ struct DRBG_internal { #ifdef WOLFSSL_DRBG_SHA512 struct DRBG_SHA512_internal { + #ifdef WORD64_AVAILABLE word64 reseedCtr; + #else + word32 reseedCtr; + #endif byte V[DRBG_SHA512_SEED_LEN]; byte C[DRBG_SHA512_SEED_LEN]; #ifdef WC_RNG_HAVE_NEXT_SEED byte nextSeed[WC_DRBG_NEXT_SEED_LEN]; - wolfSSL_Atomic_Int nextSeedLen; + WC_DRBG_nextSeedLen_t nextSeedLen; #endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -673,7 +683,7 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); /* Banked-next-seed services. _NextSeedGenerate() banks up to n more * bytes from the module's seed source (clamped to the space remaining; * ALREADY_E when the bank is ready or being consumed), health-testing - * and publishing the bank when it completes (RETRY_E when the health + * and publishing the bank when it completes (NOT_READY_E when the health * test could not run and the call should simply be retried); a * scheduling daemon may call it without owning the instance. * _NextSeedCurrent() reports the raw aperture value (racy snapshot). diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 870c2d856e5..516dd8805ca 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -1527,7 +1527,7 @@ /* user needs to define XTIME to function that provides * seconds since Unix epoch */ #ifndef XTIME - #error XTIME must be defined in wolfSSL settings.h + #error XTIME must be defined in wolfSSL user_settings.h /* #define XTIME fnSecondsSinceEpoch */ #endif @@ -4640,15 +4640,20 @@ #endif #ifndef WC_RESEED_INTERVAL - /* In kernel mode, use the maximum reseed interval allowed by + /* In kernel mode, use the maximum mandatory reseed threshold allowed by * NIST SP 800-90A Rev. 1, to avoid unnecessary delays in DRBG * generation. */ #if defined(HAVE_FIPS) && \ FIPS_VERSION_LT(6,0) && FIPS_VERSION3_NE(5,2,4) #define WC_RESEED_INTERVAL UINT_MAX + #elif defined(WC_16BIT_CPU) || defined(WC_32BIT_CPU) || defined(NO_64BIT) + #define WC_RESEED_INTERVAL UINT_MAX + #elif defined(__x86_64__) || defined(__ia64__) || \ + defined(__aarch64__) || defined(__mips64) + #define WC_RESEED_INTERVAL (1UL << 48UL) #else - #define WC_RESEED_INTERVAL (((word64)1UL)<<48UL) + #define WC_RESEED_INTERVAL UINT_MAX #endif #endif @@ -5994,10 +5999,6 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #error "If TLS is enabled please make sure either client or server is enabled." #endif -#if defined(WC_RNG_BANK_SUPPORT) && defined(NO_ASN_TIME) - #undef WC_RNG_BANK_SUPPORT -#endif - /* The OCSP responder time-stamps every response it generates (producedAt, * thisUpdate and, for revoked certs, revocationDate), so it needs ASN time * support. */ diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index ca9e7823e67..6cc33cac23e 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -620,8 +620,11 @@ #endif /* !WOLFSSL_NO_ATOMICS */ #ifdef WOLFSSL_NO_ATOMICS - typedef volatile int wolfSSL_Atomic_Int; - typedef volatile unsigned int wolfSSL_Atomic_Uint; + /* Note, not volatile. _NO_ATOMICS configs promise no concurrent mutation + * (single-threaded, or externally serialized); volatile would imply + * protection these types do not and cannot provide here. */ + typedef int wolfSSL_Atomic_Int; + typedef unsigned int wolfSSL_Atomic_Uint; #define WOLFSSL_ATOMIC_INITIALIZER(x) (x) #define WOLFSSL_ATOMIC_LOAD(x) (x) #define WOLFSSL_ATOMIC_STORE(x, val) (x) = (val) @@ -1582,11 +1585,18 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); #endif #elif defined(TIME_OVERRIDES) - /* Override XTIME() and XGMTIME() functionality. - Requires user to provide these functions: - time_t XTIME(time_t * timer) {} - struct tm* XGMTIME(const time_t* timer, struct tm* tmp) {} - */ + /* User-supplied override XTIME() and XGMTIME() functionality. + * + * Requires user-supplied macro definitions for XTIME() and XGMTIME(), + * mapping to function with signatures time_t time_f(time_t * timer) and + * struct tm* gmtime_f(const time_t* timer, struct tm* tmp) respectively. + */ + #ifndef XTIME + #error TIME_OVERRIDES requires a user-supplied XTIME definition. + #endif + #ifndef XGMTIME + #error TIME_OVERRIDES requires a user-supplied XGMTIME definition. + #endif #ifndef HAVE_TIME_T_TYPE #define USE_WOLF_TIME_T #endif From 6a04d7f857c38e3b97c9f0dd996bde599f06b165 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 04:54:50 +0000 Subject: [PATCH 020/102] wolfcrypt/src/random.c and wolfssl/wolfcrypt/random.h: unify DRBG reseed plumbing and make entropy crediting explicit: * funnel all reseeds through a dual-DRBG-aware rng-level Hash_DRBG_Reseed() (the SHA-256-internal routine becomes Hash256_DRBG_Reseed()), with an explicit credited flag: credited reseeds reset the reseed counter, uncredited ones mix in material without resetting it; * add wc_RNG_DRBG_Reseed_Nonce() and wc_RNG_DRBG_Reseed_Nonce_Uncredited(), accepting a nonce as additional input; wc_RNG_DRBG_Reseed() and wc_RNG_DRBG_Reseed_Uncredited() become thin wrappers around them; * wc_RNG_DRBG_ScheduleReseed() and the commanded-reseed paths now return WRONG_TYPE_OBJECT_E, not success, for instances with no DRBG (RDRAND et al.) -- a commanded reseed that cannot happen is not a success; * hoist wc_RNG_GetStatus() and wc_RNG_DRBG_Present() above the HASHDRBG region with hardened gates, and promote them to public API (WOLFSSL_API prototypes in random.h); * random.h: regate the service prototypes per-facility (accessors ungated, RBGC spawn under HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK, banked- next-seed under WC_RNG_HAVE_NEXT_SEED), migrating the explanatory comments into random.c beside the implementations. --- wolfcrypt/src/random.c | 322 ++++++++++++++++++------------------- wolfcrypt/test/test.c | 156 +++++++++++------- wolfssl/wolfcrypt/random.h | 113 ++++++------- 3 files changed, 310 insertions(+), 281 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 7d38c98c269..b79bf0c57cb 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -297,6 +297,33 @@ This library contains implementation for the random number generator. #endif /* USE_WINDOWS_API */ #endif +/* Read-only accessor for the RNG health status (enum wc_RngHealthState). + * Returns the status, or BAD_FUNC_ARG for a NULL rng. */ +int wc_RNG_GetStatus(const WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + return (int)rng->status; +} + +/* Returns 1 if rng has an instantiated DRBG, else 0. An in-service WC_RNG + * can lack one: _InitRng() bypasses DRBG instantiation when the CPU has + * RDRAND (HAVE_INTEL_RDRAND). */ +int wc_RNG_DRBG_Present(const WC_RNG* rng) +{ + if (rng == NULL) + return 0; +#if defined(HAVE_HASHDRBG) && !defined(NO_SHA256) + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) + return 1; +#endif +#if defined(HAVE_HASHDRBG) && defined(WOLFSSL_DRBG_SHA512) + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) + return 1; +#endif + return 0; +} + /* Start NIST DRBG code */ #ifdef HAVE_HASHDRBG @@ -383,7 +410,8 @@ typedef struct DRBG_SHA512_internal DRBG_SHA512_internal; static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed, word32 seedSz, - const byte* additional, word32 additionalSz); + const byte* additional, word32 additionalSz, + int credited); static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, const byte* additional, word32 additionalSz); @@ -643,8 +671,8 @@ static int Hash_df(DRBG_internal* drbg, byte* out, word32 outSz, byte type, } /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz, - const byte* additional, word32 additionalSz) +static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz, + const byte* additional, word32 additionalSz, int credited) { int ret; WC_DECLARE_VAR(newV, byte, DRBG_SEED_LEN, 0); @@ -673,7 +701,7 @@ static int Hash_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz ret = Hash_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V, sizeof(drbg->V), NULL, 0, NULL, 0); } - if (ret == DRBG_SUCCESS) { + if ((ret == DRBG_SUCCESS) && credited) { drbg->reseedCtr = 1; } @@ -696,85 +724,98 @@ static int Hash_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz return ret; } -#endif /* !NO_SHA256 - close before wc_RNG_DRBG_Reseed (dual-DRBG-aware) - * and array_add_one (shared utility) which both must - * remain available to SHA-512-only builds */ +#endif /* !NO_SHA256 */ -/* Returns: DRBG_SUCCESS and DRBG_FAILURE or BAD_FUNC_ARG on fail */ -int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz) +static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, + const byte* additional, word32 additionalSz, + int credited) { - if (rng == NULL || seed == NULL) { + int ret; + + if (rng == NULL) return BAD_FUNC_ARG; - } + #ifndef NO_SHA256 if (rng->drbgType == WC_DRBG_SHA256) { - if (rng->drbg == NULL) { + DRBG_internal* drbg = (DRBG_internal *)rng->drbg; + + if (drbg == NULL) { #if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) if (IS_INTEL_RDRAND(intel_flags)) { /* using RDRAND not DRBG, so return success */ - return 0; + ret = 0; + goto out; } #endif - return BAD_FUNC_ARG; + ret = BAD_FUNC_ARG; + goto out; } - return Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, seed, seedSz, - NULL, 0); + + ret = Hash256_DRBG_Reseed(drbg, seed, seedSz, + additional, additionalSz, credited); + goto out; } #endif + #ifdef WOLFSSL_DRBG_SHA512 if (rng->drbgType == WC_DRBG_SHA512) { - if (rng->drbg512 == NULL) { + DRBG_SHA512_internal* drbg512 = + (DRBG_SHA512_internal *)rng->drbg512; + + if (drbg512 == NULL) { #if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) if (IS_INTEL_RDRAND(intel_flags)) { /* using RDRAND not DRBG, so return success */ - return 0; + ret = 0; + goto out; } #endif - return BAD_FUNC_ARG; + ret = BAD_FUNC_ARG; + goto out; } - return Hash512_DRBG_Reseed((DRBG_SHA512_internal *)rng->drbg512, - seed, seedSz, NULL, 0); + + ret = Hash512_DRBG_Reseed(drbg512, seed, seedSz, + additional, additionalSz, credited); + goto out; } #endif /* No DRBG type matched; if using RDRAND, that's OK */ -#if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) + #if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) if (IS_INTEL_RDRAND(intel_flags)) { - return 0; + /* using RDRAND not DRBG, so return success */ + ret = 0; + goto out; } -#endif + #endif - return BAD_FUNC_ARG; + ret = WRONG_TYPE_OBJECT_E; + + out: + + return ret; } -/* Read-only accessor for the RNG health status (enum wc_RngHealthState). - * Returns the status, or BAD_FUNC_ARG for a NULL rng. */ -int wc_RNG_GetStatus(const WC_RNG* rng) +int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz) { - if (rng == NULL) + int ret; + + if (rng == NULL || seed == NULL) { return BAD_FUNC_ARG; - return (int)rng->status; + } + + ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, 1 /* credited */); + + return ret; } -/* Returns 1 if rng has an instantiated DRBG, else 0. An in-service WC_RNG - * can lack one: _InitRng() bypasses DRBG instantiation when the CPU has - * RDRAND (HAVE_INTEL_RDRAND). */ -int wc_RNG_DRBG_Present(const WC_RNG* rng) -{ - if (rng == NULL) - return 0; -#ifndef NO_SHA256 - if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) - return 1; -#endif -#ifdef WOLFSSL_DRBG_SHA512 - if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) - return 1; -#endif - return 0; +int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz) { + return wc_RNG_DRBG_Reseed_Nonce(rng, seed, seedSz, NULL, 0); } + /* Returns 1 if rng was seeded from another DRBG's output (an SP 800-90C * chain leaf, via wc_InitRng*RBGC() or wc_RNG_DRBG_ReseedRBGC()), else 0. * The tag is sticky for the instance's lifetime; a leaf is never usable as @@ -805,12 +846,16 @@ int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, return 0; } +#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) + wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); +#endif + /* Mark rng due for reseed: the next generate operation reseeds from the - * module's built-in or registered seed source before producing output, and - * wc_RNG_DRBG_Reseed_Now() performs the same reseed immediately. This can - * only shorten the current seed's remaining lifetime, never extend it. - * When no DRBG is instantiated (RDRAND et al.) there is nothing to reseed; - * the call is a successful no-op. */ + * module's built-in or registered seed source before producing output (see also + * wc_RNG_DRBG_Reseed_Now()). This can only shorten the current seed's + * remaining lifetime, never extend it. When no DRBG is instantiated (RDRAND et + * al.) commanded reseed is not supported and the call returns + * WRONG_TYPE_OBJECT_E. */ int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) { if (rng == NULL) @@ -829,78 +874,33 @@ int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) return 0; } #endif - return 0; + return WRONG_TYPE_OBJECT_E; } -/* Identical to wc_RNG_DRBG_Reseed(), except that the reseed counter is +/* Similar to wc_RNG_DRBG_Reseed_Nonce(), except that the reseed counter is * preserved: the caller-supplied material is mixed into the DRBG state via - * the reseed derivation function without being credited as entropy, so only + * the reseed derivation function without being credited as entropy -- * the module's own seed source (wc_RNG_DRBG_Reseed_Now() or the * WC_RESEED_INTERVAL backstop) resets the reseed schedule. This is the * SP 800-90A additional-input concept, applied via the reseed derivation. */ -int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz) +int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz) { - if (rng == NULL || seed == NULL) { + if (rng == NULL || seed == NULL) return BAD_FUNC_ARG; - } - -#ifndef NO_SHA256 - if (rng->drbgType == WC_DRBG_SHA256) { - if (rng->drbg == NULL) { - #if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) - if (IS_INTEL_RDRAND(intel_flags)) { - /* using RDRAND not DRBG, so return success */ - return 0; - } - #endif - return BAD_FUNC_ARG; - } - { - DRBG_internal* drbg = (DRBG_internal *)rng->drbg; - wc_drbg_reseed_ctr_t saved_ctr = drbg->reseedCtr; - int ret = Hash_DRBG_Reseed(drbg, seed, seedSz, NULL, 0); - /* Hash_DRBG_Reseed() only writes reseedCtr on success, so the - * unconditional restore is exact either way. */ - drbg->reseedCtr = saved_ctr; - return ret; - } - } -#endif -#ifdef WOLFSSL_DRBG_SHA512 - if (rng->drbgType == WC_DRBG_SHA512) { - if (rng->drbg512 == NULL) { - #if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) - if (IS_INTEL_RDRAND(intel_flags)) { - /* using RDRAND not DRBG, so return success */ - return 0; - } - #endif - return BAD_FUNC_ARG; - } - { - DRBG_SHA512_internal* drbg512 = - (DRBG_SHA512_internal *)rng->drbg512; - word64 saved_ctr = drbg512->reseedCtr; - int ret = Hash512_DRBG_Reseed(drbg512, seed, seedSz, NULL, 0); - drbg512->reseedCtr = saved_ctr; - return ret; - } - } -#endif - /* No DRBG type matched; if using RDRAND, that's OK */ -#if defined(HAVE_INTEL_RDSEED) || defined(HAVE_INTEL_RDRAND) - if (IS_INTEL_RDRAND(intel_flags)) { - return 0; - } -#endif + return Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, 0 /* credited */); +} - return BAD_FUNC_ARG; +int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz) +{ + return wc_RNG_DRBG_Reseed_Nonce_Uncredited(rng, seed, seedSz, NULL, + 0); } /* Generic byte-array helper -- shared by both SHA-256 and SHA-512 DRBG * cores. Lives outside the NO_SHA256 guard so SHA-512-only builds - * still link. */ + * still build. */ static WC_INLINE void array_add_one(byte* data, word32 dataSz) { int i; @@ -1466,7 +1466,8 @@ static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, /* 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) + const byte* additional, word32 additionalSz, + int credited) { int ret; WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0); @@ -1496,7 +1497,7 @@ static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed, sizeof(drbg->V), NULL, 0, NULL, 0); } - if (ret == DRBG_SUCCESS) { + if ((ret == DRBG_SUCCESS) && credited) { drbg->reseedCtr = 1; } @@ -2061,7 +2062,6 @@ int wc_Sha512Drbg_IsDisabled(void) #endif /* HAVE_HASHDRBG */ /* End NIST DRBG Code */ - static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, void* heap, int devId, WC_RNG* seedRng) { @@ -2092,6 +2092,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, XMEMSET(rng, 0, sizeof(*rng)); rng->isRbgcLeaf = (seedRng != NULL); + #ifdef WOLFSSL_HEAP_TEST rng->heap = (void*)WOLFSSL_HEAP_TEST; (void)heap; @@ -2352,16 +2353,16 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, #endif } else { - if (seedRng != NULL) { - /* RBGC spawn (SpawnRngRBGC()): draw the seed material from - * the parent DRBG's generate function in place of the - * module's seed source -- the SP 800-90C RBG chain - * construction. All subsequent handling (health test, seed - * byte accounting, instantiate, failure disposition) is - * identical to the seed-source path. */ - ret = wc_RNG_GenerateBlock(seedRng, seed, seedSz); - } - else { + if (seedRng != NULL) { + /* RBGC spawn (SpawnRngRBGC()): draw the seed material from + * the parent DRBG's generate function in place of the + * module's seed source -- the SP 800-90C RBG chain + * construction. All subsequent handling (health test, seed + * byte accounting, instantiate, failure disposition) is + * identical to the seed-source path. */ + ret = wc_RNG_GenerateBlock(seedRng, seed, seedSz); + } + else { #ifdef WC_RNG_SEED_CB if (seedCb == NULL) { ret = DRBG_NO_SEED_CB; @@ -2772,19 +2773,8 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, #endif } if (ret == DRBG_SUCCESS) { -#ifndef NO_SHA256 - if (rng->drbgType == WC_DRBG_SHA256) - ret = Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, - newSeed + SEED_BLOCK_SZ, SEED_SZ, - additional, additionalSz); -#endif -#ifdef WOLFSSL_DRBG_SHA512 - if (rng->drbgType == WC_DRBG_SHA512) - ret = Hash512_DRBG_Reseed( - (DRBG_SHA512_internal *)rng->drbg512, - newSeed + SEED_BLOCK_SZ, SEED_SZ, - additional, additionalSz); -#endif + ret = Hash_DRBG_Reseed(rng, newSeed + SEED_BLOCK_SZ, SEED_SZ, + additional, additionalSz, 1 /* credited */); } #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE) if (newSeed != NULL) { @@ -2830,7 +2820,6 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) return BAD_FUNC_ARG; if ((nonce == NULL) && (nonceSz > 0)) return BAD_FUNC_ARG; - /* Mirror wc_RNG_GenerateBlock(): only an in-service DRBG may reseed. */ if (rng->status != DRBG_OK) return RNG_FAILURE_E; @@ -2878,7 +2867,6 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, const byte* nonce, { return BAD_FUNC_ARG; } - /* Depth-one chains only, by policy: a leaf is never a root. */ if (root->isRbgcLeaf) return BAD_FUNC_ARG; @@ -3135,21 +3123,8 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, /* Identical byte accounting to PollAndReSeed(): the SEED_BLOCK_SZ * prefix was consumed by the bank-time health testing. */ -#ifndef NO_SHA256 - if (rng->drbgType == WC_DRBG_SHA256) - ret = Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, - seed + SEED_BLOCK_SZ, SEED_SZ, - nonce, nonceSz); - else -#endif -#ifdef WOLFSSL_DRBG_SHA512 - if (rng->drbgType == WC_DRBG_SHA512) - ret = Hash512_DRBG_Reseed((DRBG_SHA512_internal *)rng->drbg512, - seed + SEED_BLOCK_SZ, SEED_SZ, - nonce, nonceSz); - else -#endif - ret = WC_NO_ERR_TRACE(DRBG_FAILURE); + ret = Hash_DRBG_Reseed(rng, seed + SEED_BLOCK_SZ, SEED_SZ, + nonce, nonceSz, 1 /* credited */); /* Use-once: consumed by the attempt, success or not. Release store: * the ForceZero() must be visible before the empty aperture is. */ @@ -3177,7 +3152,8 @@ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng) { } #endif /* WC_RNG_HAVE_NEXT_SEED */ -#endif + +#endif /* HAVE_HASHDRBG */ /* place a generated block in output */ #ifdef WC_RNG_BANK_SUPPORT @@ -3248,6 +3224,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) if (rng->status != DRBG_OK) return RNG_FAILURE_E; + #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) if (rng->pid != getpid()) { rng->pid = getpid(); @@ -3360,6 +3337,7 @@ int wc_FreeRng(WC_RNG* rng) { int ret = 0; + if (rng == NULL) return BAD_FUNC_ARG; @@ -3368,6 +3346,7 @@ int wc_FreeRng(WC_RNG* rng) return wc_BankRef_Release(rng); #endif /* WC_RNG_BANK_SUPPORT */ + #if defined(WOLFSSL_ASYNC_CRYPT) wolfAsync_DevCtxFree(&rng->asyncDev, WOLFSSL_ASYNC_MARKER_RNG); #endif @@ -3513,7 +3492,9 @@ static int wc_RNG_HealthTest_ex_internal(DRBG_internal* drbg, #endif if (reseed) { - if (Hash_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0) != 0) { + if (Hash256_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0, + 1 /* credited */) != 0) + { goto exit_rng_ht; } } @@ -4002,7 +3983,9 @@ static int wc_RNG_HealthTest_SHA512_ex_internal(DRBG_SHA512_internal* drbg, #endif if (reseed) { - if (Hash512_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0) != 0) { + if (Hash512_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0, + 1 /* credited */) != 0) + { goto exit_rng_ht512; } } @@ -4079,7 +4062,8 @@ int wc_RNG_HealthTest_SHA512_ex(int reseed, if (reseed) { if (seedB != NULL && seedBSz > 0) { - ret = Hash512_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0); + ret = Hash512_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0, + 1 /* credited */); if (ret != 0) goto exit_sha512_ex; } } @@ -4206,8 +4190,9 @@ int wc_RNG_HealthTest_SHA256_ex( /* Reseed 1 with additionalA, then Generate 1 with NULL (discard) */ if (entropyB != NULL && entropyBSz > 0) { - ret = Hash_DRBG_Reseed(drbg, entropyB, entropyBSz, - additionalA, additionalASz); + ret = Hash256_DRBG_Reseed(drbg, entropyB, entropyBSz, + additionalA, additionalASz, + 1 /* credited */); if (ret != 0) goto exit_sha256_ex; } ret = Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0); @@ -4215,8 +4200,9 @@ int wc_RNG_HealthTest_SHA256_ex( /* Reseed 2 with additionalB, then Generate 2 with NULL (keep) */ if (entropyC != NULL && entropyCsz > 0) { - ret = Hash_DRBG_Reseed(drbg, entropyC, entropyCsz, - additionalB, additionalBSz); + ret = Hash256_DRBG_Reseed(drbg, entropyC, entropyCsz, + additionalB, additionalBSz, + 1 /* credited */); if (ret != 0) goto exit_sha256_ex; } ret = Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0); @@ -4224,8 +4210,9 @@ int wc_RNG_HealthTest_SHA256_ex( else { /* Standard mode: explicit reseed, then two generates */ if (entropyB != NULL && entropyBSz > 0) { - ret = Hash_DRBG_Reseed(drbg, entropyB, entropyBSz, - additionalReseed, additionalReseedSz); + ret = Hash256_DRBG_Reseed(drbg, entropyB, entropyBSz, + additionalReseed, additionalReseedSz, + 1 /* credited */); if (ret != 0) goto exit_sha256_ex; } @@ -4311,7 +4298,8 @@ int wc_RNG_HealthTest_SHA512_ex2( /* Reseed 1 with additionalA, then Generate 1 with NULL (discard) */ if (entropyB != NULL && entropyBSz > 0) { ret = Hash512_DRBG_Reseed(drbg, entropyB, entropyBSz, - additionalA, additionalASz); + additionalA, additionalASz, + 1 /* credited */); if (ret != 0) goto exit_sha512_ex2; } ret = Hash512_DRBG_Generate(drbg, output, outputSz, NULL, 0); @@ -4320,7 +4308,8 @@ int wc_RNG_HealthTest_SHA512_ex2( /* Reseed 2 with additionalB, then Generate 2 with NULL (keep) */ if (entropyC != NULL && entropyCsz > 0) { ret = Hash512_DRBG_Reseed(drbg, entropyC, entropyCsz, - additionalB, additionalBSz); + additionalB, additionalBSz, + 1 /* credited */); if (ret != 0) goto exit_sha512_ex2; } ret = Hash512_DRBG_Generate(drbg, output, outputSz, NULL, 0); @@ -4329,7 +4318,8 @@ int wc_RNG_HealthTest_SHA512_ex2( /* Standard mode: explicit reseed, then two generates */ if (entropyB != NULL && entropyBSz > 0) { ret = Hash512_DRBG_Reseed(drbg, entropyB, entropyBSz, - additionalReseed, additionalReseedSz); + additionalReseed, additionalReseedSz, + 1 /* credited */); if (ret != 0) goto exit_sha512_ex2; } @@ -7385,4 +7375,6 @@ int wc_hwrng_generate_block(byte *output, word32 sz) } #endif + + #endif /* WC_NO_RNG */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 0b56a10b18b..cc15c5faede 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -23859,7 +23859,8 @@ typedef struct keywrapVector { word32 verifyLen; } keywrapVector; -#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) +#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \ + !defined(WOLFSSL_NO_MALLOC) /* struct Aes cannot be a local here: with --enable-aesgcm=table its GCM tables * alone are 4096 bytes, past the frame limit CI enforces. It also asks for 16 * byte alignment through its ALIGN16 members, which XMALLOC does not guarantee @@ -24128,6 +24129,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aeskeywrap_test(void) } #if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) + +#ifndef WOLFSSL_NO_MALLOC /* Drive wc_AesKeyWrap_ex/wc_AesKeyUnWrap_ex directly with a caller Aes; the * KAT loop above already covers every vector via the key-based wrappers. */ { @@ -24137,6 +24140,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aeskeywrap_test(void) if (exRet != 0) return exRet; } +#endif /* WOLFSSL_NO_MALLOC */ /* In-place round-trip (in == out): wrap then unwrap a single buffer. * Exercises the XMEMMOVE staging in wc_AesKeyWrap_ex / AesKeyUnWrapRaw. */ @@ -26897,9 +26901,13 @@ static wc_test_ret_t _rng_test(WC_RNG* rng) } #endif + { ret = wc_RNG_GenerateBlock(rng, block, sizeof(block)); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); + /* the forced interval reseed is credited, and the request is + * fully served */ + } #if defined(WOLFSSL_DRBG_SHA512) && !defined(HAVE_SELFTEST) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) @@ -28840,6 +28848,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if ((api_ret != 0) || (c1 > 2)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* the scheduled reseed rides the generate, credited */ } /* immediate source reseed, without and with a nonce */ @@ -28853,6 +28862,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if ((api_ret != 0) || (c1 != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* both credited; the nonce is additional input, not an + * uncredited reseed */ } if (wc_RNG_DRBG_Reseed_Now(NULL, NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) @@ -29188,17 +29199,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) XMEMSET(matter, 0x3c, sizeof(matter)); /* argument contracts, pre-init */ - if (wc_RNG_DRBG_NextSeedGenerate(NULL, 1) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_NextSeedCurrent(NULL, &cur) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_NextSeedNow(NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_NextSeedNow_Nonce(NULL, NULL, 0) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_DRBG_NextSeedGenerate(NULL, 1); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedCurrent(NULL, &cur); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedNow(NULL); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedNow_Nonce(NULL, NULL, 0); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_InitRng(root); if (api_ret != 0) @@ -29207,62 +29219,84 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) present = wc_RNG_DRBG_Present(root); - if (wc_RNG_DRBG_NextSeedGenerate(root, 0) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_NextSeedCurrent(root, NULL) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_NextSeedNow_Nonce(root, NULL, 5) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_DRBG_NextSeedGenerate(root, 0); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedCurrent(root, NULL); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedNow_Nonce(root, NULL, 5); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if (present) { /* empty bank: nothing consumable */ - if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || - (cur != WC_DRBG_NEXT_SEED_EMPTY)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_NextSeedNow(root) != WC_NO_ERR_TRACE(NOT_READY_E)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (cur != WC_DRBG_NEXT_SEED_EMPTY) + ERROR_OUT(WC_TEST_RET_ENC_I((int)cur), out); + api_ret = wc_RNG_DRBG_NextSeedNow(root); + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* partial bank: counted, still not consumable, preserved across * the consume attempt */ api_ret = wc_RNG_DRBG_NextSeedGenerate(root, 7); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || (cur != 7)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_NextSeedNow(root) != WC_NO_ERR_TRACE(NOT_READY_E)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || (cur != 7)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (cur != 7) + ERROR_OUT(WC_TEST_RET_ENC_I((int)cur), out); + api_ret = wc_RNG_DRBG_NextSeedNow(root); + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (cur != 7) + ERROR_OUT(WC_TEST_RET_ENC_I((int)cur), out); /* fill in granules to publication: the count grows monotonically, * then the ready sentinel appears */ prev = cur; for (i = 0; i < 64; i++) { - api_ret = wc_RNG_DRBG_NextSeedGenerate(root, 32); - if ((api_ret != 0) && - (api_ret != WC_NO_ERR_TRACE(ALREADY_E))) + api_ret = wc_RNG_DRBG_NextSeedGenerate( + root, (word32)(WC_DRBG_NEXT_SEED_LEN / 7)); + api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); + if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if (wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (cur == WC_DRBG_NEXT_SEED_READY) + if ((api_ret == WC_NO_ERR_TRACE(ALREADY_E)) || + (cur == WC_DRBG_NEXT_SEED_READY)) + { break; - if (cur <= prev) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - prev = cur; + } + if ((api_ret != 0) && (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) && + (api_ret != WC_NO_ERR_TRACE(ENTROPY_RT_E)) && + (api_ret != WC_NO_ERR_TRACE(ENTROPY_APT_E))) + { + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } + if (api_ret == 0) { + if (cur <= prev) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + prev = cur; + } } if (cur != WC_DRBG_NEXT_SEED_READY) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* ready bank: further banking is ALREADY_E and changes nothing */ - if (wc_RNG_DRBG_NextSeedGenerate(root, 32) != - WC_NO_ERR_TRACE(ALREADY_E)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || - (cur != WC_DRBG_NEXT_SEED_READY)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_DRBG_NextSeedGenerate(root, 32); + if (api_ret != WC_NO_ERR_TRACE(ALREADY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (cur != WC_DRBG_NEXT_SEED_READY) + ERROR_OUT(WC_TEST_RET_ENC_I((int)cur), out); /* consume: source-free credited reseed; counter resets to 1; * bank empties (use-once) */ @@ -29272,20 +29306,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if ((api_ret != 0) || (c1 != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || - (cur != WC_DRBG_NEXT_SEED_EMPTY)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (cur != WC_DRBG_NEXT_SEED_EMPTY) + ERROR_OUT(WC_TEST_RET_ENC_I((int)cur), out); /* advance the counter, refill, and consume with a nonce */ api_ret = wc_RNG_GenerateBlock(root, buf, sizeof(buf)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); for (i = 0; i < 64; i++) { - api_ret = wc_RNG_DRBG_NextSeedGenerate(root, 32); + api_ret = wc_RNG_DRBG_NextSeedGenerate( + root, (word32)(WC_DRBG_NEXT_SEED_LEN / 7)); if (api_ret == WC_NO_ERR_TRACE(ALREADY_E)) break; - if (api_ret != 0) + if ((api_ret != 0) && (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) && + (api_ret != WC_NO_ERR_TRACE(ENTROPY_RT_E)) && + (api_ret != WC_NO_ERR_TRACE(ENTROPY_APT_E))) + { ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } } if (i >= 64) ERROR_OUT(WC_TEST_RET_ENC_NC, out); @@ -29296,11 +29337,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if ((api_ret != 0) || (c1 != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if ((wc_RNG_DRBG_NextSeedCurrent(root, &cur) != 0) || - (cur != WC_DRBG_NEXT_SEED_EMPTY)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* the nonce rides as additional input: the redemption is still one + * credited, primary-provenance reseed */ + api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (cur != WC_DRBG_NEXT_SEED_EMPTY) + ERROR_OUT(WC_TEST_RET_ENC_I((int)cur), out); } + out: if (root_inited) { diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 55b9c5dacc3..6c0fa10939e 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -266,7 +266,6 @@ struct OS_Seed { /* Setting the default to 4. */ #define SEED_BLOCK_SZ 4 #endif - #endif #define WC_DRBG_SEED_BLOCK_SZ SEED_BLOCK_SZ @@ -406,6 +405,7 @@ struct WC_RNG { * after a subsequent reseed from the module's seed source. */ byte isRbgcLeaf; + #if defined(WC_RNG_BANK_SUPPORT) || defined(HAVE_HASHDRBG) #ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES @@ -598,6 +598,7 @@ WOLFSSL_ABI WOLFSSL_API void wc_rng_free(WC_RNG* rng); WOLFSSL_ABI WOLFSSL_API int wc_InitRng(WC_RNG* rng); WOLFSSL_API int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId); WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, byte* nonce, word32 nonceSz); + WOLFSSL_API int wc_InitRngNonce_ex(WC_RNG* rng, byte* nonce, word32 nonceSz, void* heap, int devId); WOLFSSL_ABI WOLFSSL_API int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz); @@ -628,9 +629,23 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); WOLFSSL_API int wc_SetSeed_Cb(wc_RngSeed_Cb cb); #endif +WOLFSSL_API int wc_RNG_GetStatus(const WC_RNG* rng); +WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); + #ifdef HAVE_HASHDRBG WOLFSSL_API int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz); + WOLFSSL_API int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, + word32 seedSz, const byte *nonce, + word32 nonceSz); + WOLFSSL_API int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, + const byte* seed, + word32 seedSz); + WOLFSSL_API int wc_RNG_DRBG_Reseed_Nonce_Uncredited( + WC_RNG* rng, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz); + WOLFSSL_API int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, + word32 nonceSz); WOLFSSL_API int wc_RNG_TestSeed(const byte* seed, word32 seedSz); /* Reseed-counter width tracks struct DRBG_internal above. The sentinel @@ -645,70 +660,10 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); #endif #endif - /* DRBG state accessor and reseed scheduling services. These let - * applications outside the module boundary (e.g. the wc_rng_bank - * facility) observe DRBG status and reseed scheduling, mix in uncredited - * material, and request reseeds, all through defined service interfaces - * rather than by direct access to module-internal state. Pre-v7 FIPS - * boundaries lack these services; rng_bank.h supplies source-compatible - * fallbacks for those builds. */ - WOLFSSL_API int wc_RNG_GetStatus(const WC_RNG* rng); - WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); WOLFSSL_API int wc_RNG_DRBG_IsRBGCLeaf(const WC_RNG* rng); WOLFSSL_API int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr); WOLFSSL_API int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); - WOLFSSL_API int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, - const byte* seed, - word32 seedSz); -#ifndef CUSTOM_RAND_GENERATE_BLOCK - WOLFSSL_API int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, - word32 nonceSz); - - /* SP 800-90C RBG-chain spawn: instantiate leaf as a subordinate DRBG - * seeded from root's generate output. The _New variants allocate the - * leaf from root's heap; release those with wc_rng_free(). */ - WOLFSSL_API int wc_InitRngRBGC(WC_RNG* leaf, WC_RNG* root); - WOLFSSL_API int wc_InitRngNonceRBGC(WC_RNG* leaf, WC_RNG* root, - byte* nonce, word32 nonceSz); -#ifndef WC_NO_CONSTRUCTORS - WOLFSSL_API int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root); - WOLFSSL_API int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, - byte* nonce, word32 nonceSz); -#endif /* !WC_NO_CONSTRUCTORS */ - WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, - const byte* nonce, word32 nonceSz); - -#ifdef WC_RNG_HAVE_NEXT_SEED - /* Banked-next-seed services. _NextSeedGenerate() banks up to n more - * bytes from the module's seed source (clamped to the space remaining; - * ALREADY_E when the bank is ready or being consumed), health-testing - * and publishing the bank when it completes (NOT_READY_E when the health - * test could not run and the call should simply be retried); a - * scheduling daemon may call it without owning the instance. - * _NextSeedCurrent() reports the raw aperture value (racy snapshot). - * _NextSeedNow() claims a ready bank and performs a source-free - * credited reseed with it -- safe in atomic context -- or returns - * NOT_READY_E when no bank is ready; _NextSeedNow_Nonce() is the same - * with a nonce as uncredited additional input. All report - * MISSING_RNG_E for an instance with no DRBG (RDRAND et al.). The - * caller must own the instance for _NextSeedNow[_Nonce](). */ - - #define WC_DRBG_NEXT_SEED_EMPTY 0 - /* All sentinel states are negative; non-negative values are banked byte - * counts. */ - #define WC_DRBG_NEXT_SEED_READY ((WC_ATOMIC_INT_ARG)(-2)) - #define WC_DRBG_NEXT_SEED_CONSUMING ((WC_ATOMIC_INT_ARG)(-1)) - - WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n); - WOLFSSL_API int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng, - WC_ATOMIC_INT_ARG* n); - WOLFSSL_API int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, - const byte* nonce, - word32 nonceSz); - WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); -#endif /* WC_RNG_HAVE_NEXT_SEED */ -#endif #ifndef NO_SHA256 /* SHA-256 Hash_DRBG health test entry points. SHA-512-only builds @@ -818,6 +773,42 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); #endif /* HAVE_HASHDRBG */ +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) + /* SP 800-90C RBG-chain spawn: instantiate leaf as a subordinate DRBG + * seeded from root's generate output. The _New variants allocate the + * leaf from root's heap; release those with wc_rng_free(). */ + WOLFSSL_API int wc_InitRngRBGC(WC_RNG* leaf, WC_RNG* root); + WOLFSSL_API int wc_InitRngNonceRBGC(WC_RNG* leaf, WC_RNG* root, + byte* nonce, word32 nonceSz); +#ifndef WC_NO_CONSTRUCTORS + WOLFSSL_API int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root); + WOLFSSL_API int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, + byte* nonce, word32 nonceSz); +#endif /* !WC_NO_CONSTRUCTORS */ + WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, + const byte* nonce, word32 nonceSz); +#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK */ + +#ifdef WC_RNG_HAVE_NEXT_SEED + #define WC_DRBG_NEXT_SEED_EMPTY 0 + /* All sentinel states are negative; non-negative values are banked byte + * counts. */ + #define WC_DRBG_NEXT_SEED_READY ((WC_ATOMIC_INT_ARG)(-2)) + #define WC_DRBG_NEXT_SEED_CONSUMING ((WC_ATOMIC_INT_ARG)(-1)) + + WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n); + WOLFSSL_API int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng, + WC_ATOMIC_INT_ARG* n); + WOLFSSL_API int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, + const byte* nonce, + word32 nonceSz); + WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); + +#endif /* WC_RNG_HAVE_NEXT_SEED */ + + + + #ifdef __cplusplus } /* extern "C" */ #endif From 39f508b89a93a1a6bb3ceaf12b0efa60c6024506 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 04:58:08 +0000 Subject: [PATCH 021/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/src/error.c, wolfssl/wolfcrypt/error-crypt.h: add the WC_RNG instance lock machine: * new wc_RNG_lock_get()/wc_RNG_lock_put() and relatives: a CAS latch arbitrating exclusive instance ownership, with a sticky WC_RNG_LOCK_REQUIRED policy bit enforced on entry to every instance-consuming public API (rng_lock_required_check()), and an optional blocking wolfSSL_Mutex outermost layer for user-mode sharing (WC_RNG_HAVE_LOCK_FULL_MUTEX, opt-in); * knobs: WC_RNG_NO_LOCK / WC_RNG_NO_LOCK_FULL_MUTEX; WC_RNG_lock_t is atomic (wolfSSL_Atomic_Uint) with a plain-word arm for WOLFSSL_NO_ATOMICS configs; * add a flags word to struct WC_RNG (WC_RNG_FLAG_*); the bankref marking migrates from the WC_DRBG_BANKREF status value to WC_RNG_FLAG_BANKREF, freeing status to always carry health state; * add wc_InitRng_ex2()/wc_InitRngNonce_ex2() constructors taking WC_RNG_INIT_FLAGS_* -- security attributes are fixed at instantiation (_LOCK_REQUIRED, _LOCK_INITIALLY, _USE_FULL_MUTEX) -- and const-ify the wc_InitRngNonce*() nonce arguments; * error-crypt.h/error.c: add NEEDS_RECOVERY_E (-1033) and UNEXPECTED_STATE_E (-1034). Test coverage for the lock machine arrives with the rng_bank migration and entropy-invalidation commits, whose tests exercise it end-to-end. --- .wolfssl_known_macro_extras | 2 + wolfcrypt/src/error.c | 6 + wolfcrypt/src/random.c | 477 +++++++++++++++++++++++++++++--- wolfssl/wolfcrypt/error-crypt.h | 6 +- wolfssl/wolfcrypt/random.h | 92 +++++- 5 files changed, 536 insertions(+), 47 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index be8bdc16151..b6be54aa049 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -803,6 +803,8 @@ WC_PUF_HELPER_COMPACT WC_PUF_SHA3 WC_RNG_BANK_NO_DEFAULT_SUPPORT WC_RNG_BLOCKING +WC_RNG_NO_LOCK +WC_RNG_NO_LOCK_FULL_MUTEX WC_RNG_NO_NEXT_SEED WC_RSA_NONBLOCK_TIME WC_RSA_NO_FERMAT_CHECK diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c index 7a6e39e27d6..82261393a4a 100644 --- a/wolfcrypt/src/error.c +++ b/wolfcrypt/src/error.c @@ -760,6 +760,12 @@ const char* wc_GetErrorString(int error) case WRONG_TYPE_OBJECT_E: return "Object is wrong type for requested operation"; + case NEEDS_RECOVERY_E: + return "Object needs recovery before use"; + + case UNEXPECTED_STATE_E: + return "Object has unexpected state"; + case MAX_CODE_E: case WC_SPAN1_MIN_CODE_E: case MIN_CODE_E: diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index b79bf0c57cb..d1e3446a1e6 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -35,12 +35,12 @@ This library contains implementation for the random number generator. * WC_RNG_BLOCKING: Make RNG operations blocking default: off * WC_VERBOSE_RNG: Enable verbose RNG debug output default: off * WC_RNG_SEED_CB: Use custom seed callback function default: off - * WC_RNG_BANK_SUPPORT: Enable RNG bank (pre-generated) default: off - * random data support - * WOLFSSL_RNG_USE_FULL_SEED: Use full-length seed for DRBG default: off + * WC_HAVE_RNG_BANKREF: Enable RNG bank indirect RNG default: off + * support + * WOLFSSL_RNG_USE_FULL_SEED: Use full-length seed for DRBG default: off * WOLFSSL_GENSEED_FORTEST: Use deterministic seed for testing default: off * WARNING: not for production use - * WOLFSSL_KEEP_RNG_SEED_FD_OPEN: Keep /dev/random fd open default: off + * WOLFSSL_KEEP_RNG_SEED_FD_OPEN: Keep /dev/random fd open default: off * between seed operations * * Custom RNG Sources: @@ -135,7 +135,10 @@ This library contains implementation for the random number generator. #include -#ifdef WC_RNG_BANK_SUPPORT +#ifdef WC_HAVE_RNG_BANKREF + #if defined(HAVE_FIPS) && !defined(WOLFSSL_EXPERIMENTAL_SETTINGS) + #error WC_HAVE_RNG_BANKREF is unsupported in FIPS configurations. + #endif #include #endif #include @@ -297,6 +300,37 @@ This library contains implementation for the random number generator. #endif /* USE_WINDOWS_API */ #endif +/* RNG health states */ +#define DRBG_NOT_INIT WC_DRBG_NOT_INIT +#define DRBG_OK WC_DRBG_OK +#define DRBG_FAILED WC_DRBG_FAILED +#define DRBG_CONT_FAILED WC_DRBG_CONT_FAILED + +/* enforcement helper for WC_RNG_LOCK_REQUIRED: instance-consuming public APIs + * call this on entry. */ +static WC_MAYBE_UNUSED WC_INLINE int rng_lock_required_check(WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; +#ifndef WC_RNG_HAVE_LOCK + return 0; +#else /* WC_RNG_HAVE_LOCK */ + else { + #ifdef WOLFSSL_NO_ATOMICS + WC_RNG_lock_t lock_state = rng->lock; + #else + WC_RNG_lock_arg_t lock_state = WOLFSSL_ATOMIC_LOAD(rng->lock); + #endif + if ((lock_state & WC_RNG_LOCK_REQUIRED) && + (! (lock_state & WC_RNG_LOCK_HELD))) + { + return OBJECT_NOT_LOCKED_E; + } + return 0; + } +#endif /* WC_RNG_HAVE_LOCK */ +} + /* Read-only accessor for the RNG health status (enum wc_RngHealthState). * Returns the status, or BAD_FUNC_ARG for a NULL rng. */ int wc_RNG_GetStatus(const WC_RNG* rng) @@ -374,12 +408,6 @@ enum { #define WC_DRBG_CONT_FAILED (byte)WC_ERR_TRACE(WC_DRBG_CONT_FAILED) #endif -/* RNG health states */ -#define DRBG_NOT_INIT WC_DRBG_NOT_INIT -#define DRBG_OK WC_DRBG_OK -#define DRBG_FAILED WC_DRBG_FAILED -#define DRBG_CONT_FAILED WC_DRBG_CONT_FAILED - #define SEED_SZ WC_DRBG_SEED_SZ #define MAX_SEED_SZ WC_DRBG_MAX_SEED_SZ @@ -806,6 +834,10 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, return BAD_FUNC_ARG; } + ret = rng_lock_required_check(rng); + if (ret != 0) + return ret; + ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, 1 /* credited */); return ret; @@ -889,6 +921,12 @@ int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, const byte* seed, word32 se if (rng == NULL || seed == NULL) return BAD_FUNC_ARG; + { + int lock_ret = rng_lock_required_check(rng); + if (lock_ret != 0) + return lock_ret; + } + return Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, 0 /* credited */); } @@ -2062,8 +2100,26 @@ int wc_Sha512Drbg_IsDisabled(void) #endif /* HAVE_HASHDRBG */ /* End NIST DRBG Code */ -static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, - void* heap, int devId, WC_RNG* seedRng) +/* Semantics of "flags": + * + * Security attributes are fixed at instantiation and caller-declared -- the + * constructor never reads the target object. _LOCK_REQUIRED sets the sticky + * WC_RNG_LOCK_REQUIRED latch bit at birth, so there is no reachable state in + * which the instance serves without its lock policy. _LOCK_INITIALLY sets + * WC_RNG_LOCK_HELD at birth: the caller is the lease holder from the first + * instruction -- the flag for constructing into a held lease (a lease-holding + * reinitialization keeps the instance invariantly locked across the reinit), to + * be released by wc_RNG_lock_put() as usual. _USE_FULL_MUTEX layers a blocking + * wolfSSL_Mutex outermost around wc_RNG_lock_get() and wc_RNG_lock_put*() -- + * for user-mode sharing of one instance among threads, where spinning on the + * CAS would be wrong; contending getters sleep in wc_LockMutex(). The inner + * latch (and every other wc_RNG_lock_*() operation) is unchanged. + * NOT_COMPILED_IN unless WC_RNG_HAVE_LOCK_FULL_MUTEX; composes with + * _LOCK_INITIALLY (born held at both layers). wc_FreeRng() releases (if the + * latch is held) and frees the mutex. + */ +static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, + void* heap, int devId, WC_RNG* seedRng, word32 flags) { int ret = 0; #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) @@ -2089,8 +2145,30 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, if (nonce == NULL && nonceSz != 0) return BAD_FUNC_ARG; - XMEMSET(rng, 0, sizeof(*rng)); +#ifdef WC_RNG_HAVE_LOCK + if (flags & (WC_RNG_INIT_FLAGS_LOCK_REQUIRED | + WC_RNG_INIT_FLAGS_LOCK_INITIALLY)) + { + word32 initial_flags = + ((flags & WC_RNG_INIT_FLAGS_LOCK_REQUIRED) ? + WC_RNG_LOCK_REQUIRED : 0) | + ((flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) ? + WC_RNG_LOCK_HELD : 0); + XMEMSET(rng, 0, WC_OFFSETOF(WC_RNG, lock)); + XMEMSET((byte *)rng + WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock), 0, sizeof(*rng) - + (WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock))); +#ifdef WOLFSSL_NO_ATOMICS + rng->lock = initial_flags; +#else + wolfSSL_Atomic_Uint_Init(&rng->lock, initial_flags); +#endif + } + else +#endif /* WC_RNG_HAVE_LOCK */ + { + XMEMSET(rng, 0, sizeof(*rng)); rng->isRbgcLeaf = (seedRng != NULL); + } #ifdef WOLFSSL_HEAP_TEST @@ -2520,6 +2598,23 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, #endif /* HAVE_HASHDRBG */ #endif /* CUSTOM_RAND_GENERATE_BLOCK */ + if ((ret == 0) && (flags & WC_RNG_INIT_FLAGS_USE_FULL_MUTEX)) { +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + /* deliberately the last init step: no failure path can strand an + * initialized mutex. */ + if (wc_InitMutex(&rng->mutex) != 0) + return BAD_MUTEX_E; + rng->flags |= WC_RNG_FLAG_FULL_MUTEX; + if (flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) { + /* born held at both layers: the constructor's caller holds + * the whole latch, mutex included. */ + (void)wc_LockMutex(&rng->mutex); + } +#else + return NOT_COMPILED_IN; +#endif + } + return ret; } @@ -2559,7 +2654,8 @@ int wc_rng_new_ex(WC_RNG **rng, byte* nonce, word32 nonceSz, return MEMORY_E; } - ret = _InitRng(*rng, nonce, nonceSz, heap, devId, NULL); + ret = _InitRng(*rng, nonce, nonceSz, heap, devId, NULL, + WC_RNG_INIT_FLAGS_NONE); if (ret != 0) { XFREE(*rng, heap, DYNAMIC_TYPE_RNG); *rng = NULL; @@ -2585,28 +2681,306 @@ void wc_rng_free(WC_RNG* rng) WOLFSSL_ABI int wc_InitRng(WC_RNG* rng) { - return _InitRng(rng, NULL, 0, NULL, INVALID_DEVID, NULL); + return _InitRng(rng, NULL, 0, NULL, INVALID_DEVID, NULL, + WC_RNG_INIT_FLAGS_NONE); } int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId) { - return _InitRng(rng, NULL, 0, heap, devId, NULL); + return _InitRng(rng, NULL, 0, heap, devId, NULL, + WC_RNG_INIT_FLAGS_NONE); } -int wc_InitRngNonce(WC_RNG* rng, byte* nonce, word32 nonceSz) +int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz) { - return _InitRng(rng, nonce, nonceSz, NULL, INVALID_DEVID, NULL); + return _InitRng(rng, nonce, nonceSz, NULL, INVALID_DEVID, NULL, + WC_RNG_INIT_FLAGS_NONE); } -int wc_InitRngNonce_ex(WC_RNG* rng, byte* nonce, word32 nonceSz, +int wc_InitRngNonce_ex(WC_RNG* rng, const byte* nonce, word32 nonceSz, void* heap, int devId) { - return _InitRng(rng, nonce, nonceSz, heap, devId, NULL); + return _InitRng(rng, nonce, nonceSz, heap, devId, NULL, + WC_RNG_INIT_FLAGS_NONE); +} + +int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags) +{ + return _InitRng(rng, NULL, 0, heap, devId, NULL, flags); +} + +int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz, + void* heap, int devId, word32 flags) +{ + return _InitRng(rng, nonce, nonceSz, heap, devId, NULL, flags); +} + +#ifdef WC_RNG_HAVE_LOCK + +/* Note, in CAS updates here, the stored value derives only from expected and + * the caller's arguments, never from a prior load. This assures no race with + * unlocked changes to any bits. + */ + +int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + + if (rng == NULL) + return BAD_FUNC_ARG; + +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + /* outermost blocking layer, when constructed with _USE_FULL_MUTEX: + * contending getters sleep here rather than seeing BUSY_E. */ + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) { + if (wc_LockMutex(&rng->mutex) != 0) { + return BAD_MUTEX_E; + } + } +#endif + + /* extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the + * reserved section. */ + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && + (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_HELD | extra_bits))) + { + return 0; + } + + +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + /* CAS failure with the mutex held means a non-mutex claimant holds + * the latch (mixed-discipline use); back out the mutex. */ + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) + (void)wc_UnLockMutex(&rng->mutex); +#endif + + if (cur_lock & WC_RNG_LOCK_HELD) + return BUSY_E; + else /* not reachable */ + return UNEXPECTED_STATE_E; +} + +int wc_RNG_lock_get_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bits, WC_RNG_lock_arg_t want_extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, expected; + + if (rng == NULL) + return BAD_FUNC_ARG; + +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + /* outermost blocking layer, when constructed with _USE_FULL_MUTEX: + * contending getters sleep here rather than seeing BUSY_E. */ + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) { + if (wc_LockMutex(&rng->mutex) != 0) { + return BAD_MUTEX_E; + } + } +#endif + + /* *_extra_bits are allowed to assert WC_RNG_LOCK_REQUIRED, which is in the + * reserved section. */ + expected_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + want_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + + expected = (cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & + ~WC_RNG_LOCK_HELD)) | + expected_extra_bits; + + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && + (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &expected, + expected | WC_RNG_LOCK_HELD | want_extra_bits))) + { + return 0; + } + + +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + /* CAS failure with the mutex held means a non-mutex claimant holds + * the latch (mixed-discipline use); back out the mutex. */ + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) + (void)wc_UnLockMutex(&rng->mutex); +#endif + + if (expected & WC_RNG_LOCK_HELD) + return BUSY_E; + else + return UNEXPECTED_STATE_E; } +int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, new_lock; + if (rng == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + if (! (cur_lock & WC_RNG_LOCK_HELD)) + return OBJECT_NOT_LOCKED_E; + + + for (;;) { + new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD); + /* extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the + * reserved section. */ + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + new_lock |= extra_bits; + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, new_lock)) + break; + } + +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) + (void)wc_UnLockMutex(&rng->mutex); +#endif + + return 0; +} + +int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bits, WC_RNG_lock_arg_t want_extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, expected, new_lock; + + if (rng == NULL) + return BAD_FUNC_ARG; + + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + if (! (cur_lock & WC_RNG_LOCK_HELD)) + return OBJECT_NOT_LOCKED_E; + + + for (;;) { + new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD); + /* want_extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the + * reserved section. */ + want_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + new_lock |= want_extra_bits; + + expected = WC_RNG_LOCK_HELD | expected_extra_bits; + + new_lock |= (expected_extra_bits & WC_RNG_LOCK_REQUIRED); + + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &expected, + new_lock)) + { +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) + (void)wc_UnLockMutex(&rng->mutex); +#endif + return 0; + } + if ((expected & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U)) != + (expected_extra_bits & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U))) + { + break; + } + + cur_lock = expected; + } + + /* conditional release failed: the caller is still the holder, at both + * layers -- the mutex stays held. */ + + + return UNEXPECTED_STATE_E; +} + +int wc_RNG_lock_read(WC_RNG* rng, WC_RNG_lock_arg_t* state) +{ + if ((rng == NULL) || (state == NULL)) + return BAD_FUNC_ARG; + *state = WOLFSSL_ATOMIC_LOAD(rng->lock); + return 0; +} + +int wc_RNG_lock_set_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, new_lock; + if (rng == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + + for (;;) { + new_lock = cur_lock & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); + /* extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the + * reserved section. */ + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + new_lock |= extra_bits; + + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, + new_lock)) + break; + } + return 0; +} + +int wc_RNG_lock_add_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + if (rng == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + + /* extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the + * reserved section. */ + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, + cur_lock | extra_bits)) + break; + } + return 0; +} + +int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + if (rng == NULL) + return BAD_FUNC_ARG; + if (extra_bits & WC_RNG_LOCK_REQUIRED) { + /* WC_RNG_LOCK_REQUIRED is sticky by contract */ + return BAD_FUNC_ARG; + } + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); + + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, + cur_lock & ~extra_bits)) + break; + } + return 0; +} + +#endif /* WC_RNG_HAVE_LOCK */ + + + #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) /* Unified mechanics for the four wc_InitRng*RBGC() APIs: instantiate a leaf @@ -2660,7 +3034,8 @@ static int SpawnRngRBGC(WC_RNG* new_leaf_stack, WC_RNG** new_leaf_heap, return MEMORY_E; } - ret = _InitRng(leaf, nonce, nonceSz, root->heap, devId, root); + ret = _InitRng(leaf, nonce, nonceSz, root->heap, devId, root, + WC_RNG_INIT_FLAGS_NONE); if (new_leaf_heap != NULL) { if (ret != 0) { @@ -2820,6 +3195,10 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) return BAD_FUNC_ARG; if ((nonce == NULL) && (nonceSz > 0)) return BAD_FUNC_ARG; + ret = rng_lock_required_check(rng); + if (ret != 0) + return ret; + /* Mirror wc_RNG_GenerateBlock(): only an in-service DRBG may reseed. */ if (rng->status != DRBG_OK) return RNG_FAILURE_E; @@ -2867,6 +3246,10 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, const byte* nonce, { return BAD_FUNC_ARG; } + ret = rng_lock_required_check(leaf); + if (ret != 0) + return ret; + /* Depth-one chains only, by policy: a leaf is never a root. */ if (root->isRbgcLeaf) return BAD_FUNC_ARG; @@ -3104,6 +3487,10 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, if ((nonce == NULL) && (nonceSz != 0)) return BAD_FUNC_ARG; + ret = rng_lock_required_check(rng); + if (ret != 0) + return ret; + /* Mirror wc_RNG_GenerateBlock(): only an in-service DRBG may reseed. */ if (rng->status != DRBG_OK) return RNG_FAILURE_E; @@ -3156,11 +3543,9 @@ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng) { #endif /* HAVE_HASHDRBG */ /* place a generated block in output */ -#ifdef WC_RNG_BANK_SUPPORT -/* NOLINTNEXTLINE(misc-no-recursion) */ +#ifdef WC_HAVE_RNG_BANKREF static int wc_local_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #else -/* NOLINTNEXTLINE(misc-no-recursion) */ WOLFSSL_ABI int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #endif @@ -3170,6 +3555,10 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) if (rng == NULL || output == NULL) return BAD_FUNC_ARG; + ret = rng_lock_required_check(rng); + if (ret != 0) + return ret; + if (sz == 0) return 0; @@ -3289,15 +3678,20 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) return ret; } -#ifdef WC_RNG_BANK_SUPPORT +#ifdef WC_HAVE_RNG_BANKREF WOLFSSL_ABI -/* NOLINTNEXTLINE(misc-no-recursion) */ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) { if (rng == NULL) return BAD_FUNC_ARG; - if (rng->status == WC_DRBG_BANKREF) { + { + int lock_ret = rng_lock_required_check(rng); + if (lock_ret != 0) + return lock_ret; + } + + if (rng->flags & WC_RNG_FLAG_BANKREF) { int ret; struct wc_rng_bank_inst *bank_inst = NULL; @@ -3337,14 +3731,19 @@ int wc_FreeRng(WC_RNG* rng) { int ret = 0; + if (rng != NULL) { + ret = rng_lock_required_check(rng); + if (ret != 0) + return ret; + } if (rng == NULL) return BAD_FUNC_ARG; -#ifdef WC_RNG_BANK_SUPPORT - if (rng->status == WC_DRBG_BANKREF) +#ifdef WC_HAVE_RNG_BANKREF + if (rng->flags & WC_RNG_FLAG_BANKREF) return wc_BankRef_Release(rng); -#endif /* WC_RNG_BANK_SUPPORT */ +#endif /* WC_HAVE_RNG_BANKREF */ #if defined(WOLFSSL_ASYNC_CRYPT) @@ -3436,6 +3835,22 @@ int wc_FreeRng(WC_RNG* rng) } #endif +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) { + /* If the latch is held, the caller is the holder (enforced when + * _LOCK_REQUIRED) and owns the mutex: release it before + * destruction. A free latch means the mutex is unowned. */ + if (WOLFSSL_ATOMIC_LOAD(rng->lock) & WC_RNG_LOCK_HELD) + (void)wc_UnLockMutex(&rng->mutex); + (void)wc_FreeMutex(&rng->mutex); + rng->flags &= ~WC_RNG_FLAG_FULL_MUTEX; + } +#endif + + /* Note, rng->lock must *not* be cleared -- it may still be arbitrating + * access even after wc_FreeRng(). + */ + return ret; } diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index fad6f926c7c..f0f57c492ff 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -354,9 +354,11 @@ enum wolfCrypt_ErrorCodes { OBJECT_NOT_LOCKED_E = -1031, /* Required lock on object is not held */ WRONG_TYPE_OBJECT_E = -1032, /* Object is wrong type for requested */ /* operation */ + NEEDS_RECOVERY_E = -1033, /* Object needs recovery before use */ + UNEXPECTED_STATE_E = -1034, /* Object has unexpected state */ - WC_SPAN2_LAST_E = -1032, /* Update to indicate last used error code */ - WC_LAST_E = -1032, /* the last code used either here or in + WC_SPAN2_LAST_E = -1034, /* Update to indicate last used error code */ + WC_LAST_E = -1034, /* the last code used either here or in * error-ssl.h */ WC_SPAN2_MIN_CODE_E = -1999, /* Last usable code in span 2 */ diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 6c0fa10939e..d83c3882852 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -43,6 +43,30 @@ WOLFSSL_LOCAL int wolfCrypt_FIPS_DRBG_sanity(void); #endif +/* _FULL_MUTEX is opt-in, and depends on WC_RNG_HAVE_LOCK. */ +#ifdef WC_RNG_NO_LOCK_FULL_MUTEX + #undef WC_RNG_HAVE_LOCK_FULL_MUTEX +#elif defined(WC_RNG_HAVE_LOCK_FULL_MUTEX) + #ifdef WC_RNG_NO_LOCK + #error FULL_MUTEX depends on WC_RNG_HAVE_LOCK. + #endif +#endif + +#ifndef WC_RNG_NO_LOCK + #ifndef WC_RNG_HAVE_LOCK + #define WC_RNG_HAVE_LOCK + #endif + #ifdef WOLFSSL_NO_ATOMICS + typedef word32 WC_RNG_lock_t; + typedef word32 WC_RNG_lock_arg_t; + #else + typedef wolfSSL_Atomic_Uint WC_RNG_lock_t; + typedef WC_ATOMIC_UINT_ARG WC_RNG_lock_arg_t; + #endif +#else + #undef WC_RNG_HAVE_LOCK +#endif + #if !defined(HAVE_HASHDRBG) || defined(CUSTOM_RAND_GENERATE_BLOCK) && \ !defined(WC_RNG_NO_NEXT_SEED) #define WC_RNG_NO_NEXT_SEED @@ -384,35 +408,40 @@ enum wc_RngHealthState { WC_DRBG_NOT_INIT = 0, WC_DRBG_OK = 1, WC_DRBG_FAILED = 2, - WC_DRBG_CONT_FAILED = 3, -#ifdef WC_RNG_BANK_SUPPORT - WC_DRBG_BANKREF = 4, /* Marks the WC_RNG as a ref to a wc_rng_bank, - * with no usable DRBG of its own. - */ - #define WC_HAVE_RNG_BANKREF -#endif - WOLF_ENUM_DUMMY_LAST_ELEMENT(wc_RngHealthState) + WC_DRBG_CONT_FAILED = 3 }; +#define WC_RNG_FLAG_NONE 0 +#define WC_RNG_FLAG_FULL_MUTEX (1U << 1) +#define WC_RNG_FLAG_BANKREF (1U << 2) + + /* RNG context */ struct WC_RNG { struct OS_Seed seed; void* heap; byte status; + word32 flags; /* Set when this instance was seeded from another DRBG's output * (wc_InitRng*RBGC(), wc_RNG_DRBG_ReseedRBGC()) -- an SP 800-90C chain * leaf. Sticky by policy: a leaf is never usable as a chain root, even * after a subsequent reseed from the module's seed source. */ byte isRbgcLeaf; +#ifdef WC_RNG_HAVE_LOCK + WC_RNG_lock_t lock; + #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + wolfSSL_Mutex mutex; + #endif +#endif -#if defined(WC_RNG_BANK_SUPPORT) || defined(HAVE_HASHDRBG) +#if defined(HAVE_HASHDRBG) || defined(WC_HAVE_RNG_BANKREF) #ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES union { #endif - #ifdef WC_RNG_BANK_SUPPORT + #ifdef WC_HAVE_RNG_BANKREF struct wc_rng_bank *bankref; #endif @@ -458,7 +487,7 @@ struct WC_RNG { }; #endif -#endif /* WC_RNG_BANK_SUPPORT || HAVE_HASHDRBG */ +#endif /* HAVE_HASHDRBG || WC_HAVE_RNG_BANKREF */ #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) pid_t pid; @@ -597,9 +626,18 @@ WOLFSSL_ABI WOLFSSL_API void wc_rng_free(WC_RNG* rng); #ifndef WC_NO_RNG WOLFSSL_ABI WOLFSSL_API int wc_InitRng(WC_RNG* rng); WOLFSSL_API int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId); -WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, byte* nonce, word32 nonceSz); - -WOLFSSL_API int wc_InitRngNonce_ex(WC_RNG* rng, byte* nonce, word32 nonceSz, +WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz); + +#define WC_RNG_INIT_FLAGS_NONE 0 +#define WC_RNG_INIT_FLAGS_LOCK_REQUIRED (1U << 0) +#define WC_RNG_INIT_FLAGS_LOCK_INITIALLY (1U << 1) +#define WC_RNG_INIT_FLAGS_USE_FULL_MUTEX (1U << 2) + +WOLFSSL_API int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, + word32 flags); +WOLFSSL_API int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz, + void* heap, int devId, word32 flags); +WOLFSSL_API int wc_InitRngNonce_ex(WC_RNG* rng, const byte* nonce, word32 nonceSz, void* heap, int devId); WOLFSSL_ABI WOLFSSL_API int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz); WOLFSSL_API int wc_RNG_GenerateByte(WC_RNG* rng, byte* b); @@ -610,6 +648,8 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); #define wc_InitRng_ex(rng, h, d) NOT_COMPILED_IN #define wc_InitRngNonce(rng, n, s) NOT_COMPILED_IN #define wc_InitRngNonce_ex(rng, n, s, h, d) NOT_COMPILED_IN +#define wc_InitRng_ex2(rng, h, d, f) NOT_COMPILED_IN +#define wc_InitRngNonce_ex2(rng, n, s, h, d, f) NOT_COMPILED_IN #if defined(__ghs__) || defined(WC_NO_RNG_SIMPLE) /* some older compilers do not like macro function in expression */ #define wc_RNG_GenerateBlock(rng, b, s) NOT_COMPILED_IN @@ -806,6 +846,30 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); #endif /* WC_RNG_HAVE_NEXT_SEED */ +#ifdef WC_RNG_HAVE_LOCK + #define WC_RNG_LOCK_FREE 0 + #define WC_RNG_LOCK_HELD (1U<<0) + #define WC_RNG_LOCK_REQUIRED (1U<<1) + #define WC_RNG_LOCK_ENTROPY_INVALIDATED (1U<<2) + /* consumers' annotation bits start here (see e.g. rng_bank.h) */ + #define WC_RNG_LOCK_EXTRA_SHIFT 3U + + WOLFSSL_API int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_API int wc_RNG_lock_get_conditional(WC_RNG* rng, + WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits); + WOLFSSL_API int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_API int wc_RNG_lock_put_conditional(WC_RNG* rng, + WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits); + WOLFSSL_API int wc_RNG_lock_read(WC_RNG* rng, WC_RNG_lock_arg_t* state); + WOLFSSL_API int wc_RNG_lock_set_extra(WC_RNG* rng, + WC_RNG_lock_arg_t extra_bits); + WOLFSSL_API int wc_RNG_lock_add_extra(WC_RNG* rng, + WC_RNG_lock_arg_t extra_bits); + WOLFSSL_API int wc_RNG_lock_clear_extra(WC_RNG* rng, + WC_RNG_lock_arg_t extra_bits); +#endif /* WC_RNG_HAVE_LOCK */ From 2237e5e1c16e36ce88447775761592738b138b9c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:00:30 +0000 Subject: [PATCH 022/102] wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/test/test.c: migrate bank instance locking onto the WC_RNG lock machine, and add prediction-resistance checkout: * wc_rng_bank_inst_lock_{get,put,read}() are reimplemented over wc_RNG_lock_*(): the per-instance lock word is now the instance WC_RNG's own lock, with the bank-specific bits (affinity-locked, vec-ops-inherit) carried above WC_RNG_LOCK_EXTRA_SHIFT; instances are instantiated WC_RNG_INIT_FLAGS_LOCK_REQUIRED on FIPS v7+ boundaries; stale check-ins now surface OBJECT_NOT_LOCKED_E; optional WC_RNG_BANK_LOCK_DEBUG instrumentation; * WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE, per-call or bank-wide: every sleepable lease is freshly credited-reseeded before the caller's first draw; demands _CAN_WAIT and contradicts uncredited and recovery seeding (BAD_FUNC_ARG); atomic callers under a bank-wide posture are refused; recovery is exempt, and remains the sole restoration path for a failed instance; PR supersedes _CONSUME_NEXT_SEED, leaving the banked seed intact for a later consumer; * new wc_rng_bank_init_nonce(), wc_rng_bank_first_failover_inst_set(), wc_rng_bank_get_inst_id(); WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING; bank flag literals become unsigned (1U << n); * test.c random_bank_test(): argument/alignment contracts for the check-in entry points, PR contract and posture coverage, and probe modernization -- capture retvals and report them with WC_TEST_RET_ENC_EC()/_ENC_I() instead of bare _ENC_NC. --- wolfcrypt/src/rng_bank.c | 317 +++++++++++---- wolfcrypt/test/test.c | 269 ++++++++++--- wolfssl/wolfcrypt/rng_bank.h | 747 +++++++++++++++++++++++++---------- 3 files changed, 981 insertions(+), 352 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 248dd4066be..b6f5465e11f 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -36,13 +36,15 @@ * retry indefinitely, pass negative timeout_secs -- the flags arg here is only * used to initialize the flags in the new bank. */ -WOLFSSL_API int wc_rng_bank_init( +WOLFSSL_API int wc_rng_bank_init_nonce( struct wc_rng_bank *ctx, int n_rngs, word32 flags, int timeout_secs, void *heap, - int devId) + int devId, + const byte *nonce, + word32 nonceSz) { int i; int ret; @@ -62,6 +64,7 @@ WOLFSSL_API int wc_rng_bank_init( #endif ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; ctx->heap = heap; + ctx->first_failover_inst = -1; #ifdef WC_RNG_BANK_STATIC if (n_rngs > WC_RNG_BANK_STATIC_SIZE) @@ -74,6 +77,9 @@ WOLFSSL_API int wc_rng_bank_init( ret = MEMORY_E; #endif + (void)nonce; + (void)nonceSz; + if (ret == 0) { XMEMSET(ctx->rngs, 0, sizeof(*ctx->rngs) * (size_t)n_rngs); ctx->n_rngs = n_rngs; @@ -89,13 +95,21 @@ WOLFSSL_API int wc_rng_bank_init( rng_inst->bank = ctx; for (;;) { time_t ts2; - if (flags & WC_RNG_BANK_FLAG_NO_VECTOR_OPS) need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0); - ret = wc_InitRngNonce_ex( + + { +#ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED + ret = wc_InitRngNonce_ex2( + WC_RNG_BANK_INST_TO_RNG(rng_inst), + (byte *)&rng_inst, sizeof(byte *), heap, devId, + WC_RNG_INIT_FLAGS_LOCK_REQUIRED); +#else + ret = wc_InitRngNonce_ex( WC_RNG_BANK_INST_TO_RNG(rng_inst), (byte *)&rng_inst, sizeof(byte *), heap, devId); - +#endif + } if (need_reenable_vec) REENABLE_VECTOR_REGISTERS(); /* if we're allowed to sleep, relax the loop between each inner @@ -160,9 +174,36 @@ WOLFSSL_API int wc_rng_bank_init( if (ret != 0) (void)wc_rng_bank_fini(ctx); + return ret; } +WOLFSSL_API int wc_rng_bank_init( + struct wc_rng_bank *ctx, + int n_rngs, + word32 flags, + int timeout_secs, + void *heap, + int devId) +{ + + return wc_rng_bank_init_nonce(ctx, n_rngs, flags, timeout_secs, heap, devId, NULL, 0); +} + +WOLFSSL_API int wc_rng_bank_first_failover_inst_set( + struct wc_rng_bank *ctx, + int first_failover_inst) +{ + if ((ctx == NULL) || + (first_failover_inst < 0) || + (first_failover_inst >= ctx->n_rngs)) + { + return BAD_FUNC_ARG; + } + ctx->first_failover_inst = first_failover_inst; + return 0; +} + #ifndef WC_RNG_BANK_STATIC WOLFSSL_API int wc_rng_bank_new( struct wc_rng_bank **ctx, @@ -251,8 +292,15 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { #endif { for (i = 0; i < ctx->n_rngs; ++i) { - if (ctx->rngs[i].lock != 0) { - /* better to leak than to crash. */ + WC_RNG_lock_arg_t fini_lock_state = 0; + (void)wc_rng_bank_inst_lock_read(&ctx->rngs[i], + &fini_lock_state); + if (fini_lock_state & WC_RNG_LOCK_HELD) { + /* Held is the disqualifier; a bare sticky + * WC_RNG_LOCK_REQUIRED is the at-rest state of a marked + * free instance and is expected here. + * + * better to leak than to crash. */ #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( "BUG: wc_rng_bank_fini() called with RNG #%d still " @@ -269,8 +317,25 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { } for (i = 0; i < ctx->n_rngs; ++i) { + /* Lease-taking teardown: wc_FreeRng() on a _LOCK_REQUIRED + * instance is (correctly) refused without the lease, so take + * it -- structurally uncontended at refcount zero with the + * held-check above passed. The latch dies held in dying + * memory, per the uncleared-on-free contract. */ + if (wc_rng_bank_inst_lock_get(&ctx->rngs[i], 0) != 0) { + /* can't happen absent corruption; leak, don't crash. */ +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "BUG: wc_rng_bank_fini() couldn't take the teardown " + "lease on RNG #%d.\n", i); +#endif + ret = BAD_STATE_E; + continue; + } wc_FreeRng(&ctx->rngs[i].rng); } + if (ret == WC_NO_ERR_TRACE(BAD_STATE_E)) + return ret; #ifndef WC_RNG_BANK_STATIC XFREE(ctx->rngs, ctx->heap, DYNAMIC_TYPE_RNG); @@ -343,8 +408,9 @@ WOLFSSL_API int wc_rng_bank_default_set(struct wc_rng_bank *bank) { #endif return ret; } - if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, (void **)&cur_default_rng_bank, bank)) + if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, (void **)&cur_default_rng_bank, bank)) { return 0; + } else { wolfSSL_RefDec2(&bank->refcount, &new_refcount, &ret); #ifdef WC_VERBOSE_RNG @@ -374,9 +440,19 @@ WOLFSSL_API int wc_rng_bank_default_checkout(struct wc_rng_bank **bank) { else if (! (cur_default_rng_bank->flags & WC_RNG_BANK_FLAG_INITED)) return BAD_STATE_E; - wolfSSL_RefInc_IfAtLeast(&cur_default_rng_bank->refcount, 2, &new_refcount, &ret); - if (ret != 0) - return ret; + if (cur_default_rng_bank->flags & WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING) { + /* read-only validity test: >= 2 means inited and still registered + * as the default (wc_rng_bank_default_set()'s standing ref). */ + if (wolfSSL_RefCur(cur_default_rng_bank->refcount) < 2) + return BAD_STATE_E; + ret = 0; + } + else { + wolfSSL_RefInc_IfAtLeast(&cur_default_rng_bank->refcount, 2, + &new_refcount, &ret); + if (ret != 0) + return ret; + } *bank = cur_default_rng_bank; @@ -388,6 +464,10 @@ WOLFSSL_API int wc_rng_bank_default_checkin(struct wc_rng_bank **bank) { WC_ATOMIC_INT_ARG new_refcount; if ((bank == NULL) || (*bank == NULL)) return BAD_FUNC_ARG; + if ((*bank)->flags & WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING) { + *bank = NULL; + return 0; + } wolfSSL_RefDec2(&(*bank)->refcount, &new_refcount, &ret); #ifdef WC_VERBOSE_RNG if (new_refcount <= 0) @@ -406,7 +486,9 @@ WOLFSSL_API int wc_rng_bank_default_checkin(struct wc_rng_bank **bank) { * wc_rng_bank_default_set(). */ WOLFSSL_API int wc_rng_bank_default_clear(struct wc_rng_bank *bank) { - if ((bank != default_rng_bank) || (bank == NULL)) + if (bank == NULL) + return BAD_FUNC_ARG; + if (bank != default_rng_bank) return BAD_FUNC_ARG; if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, (void **)&bank, NULL)) { int ret; @@ -449,13 +531,14 @@ WOLFSSL_API int wc_rng_bank_checkout( int timeout_secs, word32 flags) { - int new_lock_value = WC_RNG_BANK_INST_LOCK_HELD; + WC_RNG_lock_arg_t lock_extra_bits = 0; int ret = 0; time_t ts1, ts2; int n_rngs_tried = 0; int diverted_unusable = 0; WC_ATOMIC_INT_ARG new_refcount; + if (rng_inst == NULL) return BAD_FUNC_ARG; @@ -479,15 +562,35 @@ WOLFSSL_API int wc_rng_bank_checkout( } /* Increment bank->refcount here speculatively to mitigate races with - * bank deallocation. + * bank deallocation. With _NO_CHECKOUT_REFCOUNTING the container + * guarantees liveness and the RefCur test above suffices. */ - wolfSSL_RefInc_IfAtLeast(&bank->refcount, 1, &new_refcount, &ret); - if (ret != 0) { + if (! (bank->flags & WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING)) { + wolfSSL_RefInc_IfAtLeast(&bank->refcount, 1, &new_refcount, &ret); + if (ret != 0) { #ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "wc_rng_bank_checkout() called with refcount %d.\n", new_refcount); + WOLFSSL_DEBUG_PRINTF( + "wc_rng_bank_checkout() called with refcount %d.\n", + new_refcount); #endif - return ret; + return ret; + } + } + } + + if (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY) { + if (flags & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE) { + ret = BAD_FUNC_ARG; + goto out; + } + } + else { + if (((flags | bank->flags) & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE) && + (((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT))) || + (flags & WC_RNG_BANK_FLAG_SEED_UNCREDITED))) + { + ret = BAD_FUNC_ARG; + goto out; } } @@ -502,6 +605,9 @@ WOLFSSL_API int wc_rng_bank_checkout( goto out; } + if (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY)) + flags |= bank->flags & (WC_RNG_BANK_FLAG_AFFINITY_LOCK | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST); + if ((flags & WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST) && (bank->affinity_get_id_cb == NULL)) { @@ -520,7 +626,6 @@ WOLFSSL_API int wc_rng_bank_checkout( ts1 = 0; /* mollify -Wmaybe-uninitialized... */ for (; ret == 0;) { - int expected = 0; if (flags & WC_RNG_BANK_FLAG_AFFINITY_LOCK) { if ((bank->affinity_lock_cb == NULL) || @@ -536,7 +641,9 @@ WOLFSSL_API int wc_rng_bank_checkout( } ret = bank->affinity_lock_cb(bank->cb_arg); if (ret == 0) - new_lock_value |= WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; + lock_extra_bits |= WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; + else if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) + break; else { /* need to, and can, continue regardless of the error code from * bank->affinity_lock_cb. */ @@ -575,10 +682,8 @@ WOLFSSL_API int wc_rng_bank_checkout( } } - if (wolfSSL_Atomic_Int_CompareExchange( - &bank->rngs[preferred_inst_offset].lock, - &expected, - new_lock_value)) + if (wc_rng_bank_inst_lock_get(&bank->rngs[preferred_inst_offset], + lock_extra_bits) == 0) { int inst_unusable; wc_drbg_reseed_ctr_t cur_reseed_ctr = 0; @@ -586,8 +691,9 @@ WOLFSSL_API int wc_rng_bank_checkout( *rng_inst = &bank->rngs[preferred_inst_offset]; #ifdef WC_RNG_HAVE_NEXT_SEED - if ((flags & WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) && - (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY))) + if (((flags | bank->flags) & WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) && + (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY)) && + (! ((flags | bank->flags) & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE))) { /* Consume a ready banked next seed, if any, BEFORE the * usability evaluation below, so that evaluation judges the @@ -664,13 +770,14 @@ WOLFSSL_API int wc_rng_bank_checkout( { if (inst_unusable) diverted_unusable = 1; - WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); + (void)wc_rng_bank_inst_lock_put(*rng_inst); *rng_inst = NULL; } else { #ifdef WC_VERBOSE_RNG if ((! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) && - (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && + (! (flags & (WC_RNG_BANK_FLAG_CAN_WAIT | + WC_RNG_BANK_FLAG_FOR_RECOVERY))) && (wc_RNG_DRBG_GetReseedCtr( WC_RNG_BANK_INST_TO_RNG(*rng_inst), &cur_reseed_ctr) == 0) && @@ -693,16 +800,28 @@ WOLFSSL_API int wc_rng_bank_checkout( */ #endif + if (((flags | bank->flags) & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE) && + (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY))) + { + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(*rng_inst), + NULL, 0); + if (ret != 0) { + (void)wc_rng_bank_inst_lock_put(*rng_inst); + *rng_inst = NULL; + goto out; + } + } + #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS if ((flags | bank->flags) & WC_RNG_BANK_FLAG_NO_VECTOR_OPS) { ret = DISABLE_VECTOR_REGISTERS(); if (ret == 0) - WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, new_lock_value | - WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); + ret = wc_rng_bank_inst_lock_add_extra(*rng_inst, + WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); else if (ret == WC_NO_ERR_TRACE(WC_ACCEL_INHIBIT_E)) ret = 0; else { - WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); + (void)wc_rng_bank_inst_lock_put(*rng_inst); *rng_inst = NULL; break; } @@ -730,9 +849,13 @@ WOLFSSL_API int wc_rng_bank_checkout( flags &= ~(word32)WC_RNG_BANK_FLAG_AFFINITY_LOCK; flags &= ~(word32)WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST; - ++preferred_inst_offset; - if (preferred_inst_offset >= bank->n_rngs) - preferred_inst_offset = 0; + if ((n_rngs_tried == 0) && (bank->first_failover_inst >= 0)) + preferred_inst_offset = bank->first_failover_inst; + else { + ++preferred_inst_offset; + if (preferred_inst_offset >= bank->n_rngs) + preferred_inst_offset = 0; + } ++n_rngs_tried; } else { @@ -744,9 +867,9 @@ WOLFSSL_API int wc_rng_bank_checkout( } } - if (new_lock_value & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { + if (lock_extra_bits & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { (void)bank->affinity_unlock_cb(bank->cb_arg); - new_lock_value &= ~WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; + lock_extra_bits &= ~WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; } if ((flags & WC_RNG_BANK_FLAG_CAN_WAIT) && (timeout_secs != 0)) { @@ -783,14 +906,15 @@ WOLFSSL_API int wc_rng_bank_checkout( ret = BAD_STATE_E; } - if (new_lock_value & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) + if (lock_extra_bits & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) (void)bank->affinity_unlock_cb(bank->cb_arg); /* Decrement the speculative refcount increment. This also covers the * refcount increment in wc_rng_bank_default_checkout() if that's how it was - * incremented. + * incremented. With _NO_CHECKOUT_REFCOUNTING neither increment + * happened. */ - { + if (! (bank->flags & WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING)) { int refdec_err; wolfSSL_RefDec2(&bank->refcount, &new_refcount, &refdec_err); #ifdef WC_VERBOSE_RNG @@ -809,6 +933,7 @@ WOLFSSL_API int wc_rng_bank_checkout( return ret; } + #ifdef WC_HAVE_RNG_BANKREF /* wc_local_rng_bank_checkout_for_bankref() is the shim to the real WC_RNG when * wc_RNG_GenerateBlock() is called on a bankref WC_RNG. It's called from @@ -876,7 +1001,10 @@ static WC_INLINE int rng_inst_matches_bank( return BAD_FUNC_ARG; if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) return BAD_STATE_E; - if (wolfSSL_RefCur(bank->refcount) < 2) + /* a live lease implies a per-checkout ref -- unless the bank runs + * _NO_CHECKOUT_REFCOUNTING, where only the standing baseline holds. */ + if (wolfSSL_RefCur(bank->refcount) < + ((bank->flags & WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING) ? 1 : 2)) return BAD_STATE_E; if (bank->n_rngs <= 0) @@ -908,11 +1036,22 @@ static WC_INLINE int rng_inst_matches_bank( return 1; } +WOLFSSL_API int wc_rng_bank_get_inst_id(struct wc_rng_bank_inst *rng_inst) { + int ret; + if (rng_inst == NULL) + return BAD_FUNC_ARG; + ret = rng_inst_matches_bank(rng_inst->bank, rng_inst); + if (ret < 0) + return ret; + return (int)(((wc_ptr_t)rng_inst - (wc_ptr_t)&rng_inst->bank->rngs[0]) / + sizeof(*rng_inst)); +} + WOLFSSL_API int wc_rng_bank_checkin( struct wc_rng_bank *bank, struct wc_rng_bank_inst **rng_inst) { - int lockval; + WC_RNG_lock_arg_t lockval; int ret; if ((rng_inst == NULL) || (*rng_inst == NULL)) @@ -933,48 +1072,28 @@ WOLFSSL_API int wc_rng_bank_checkin( * We can't warn for this misuse because random_bank_test() exercises * the functionality. */ - return ret; - } - - lockval = (int)WOLFSSL_ATOMIC_LOAD((*rng_inst)->lock); - - /* Opportunistically check for lock misuse/corruption. - * - * An instance must be checked in exactly once, by the caller that checked - * it out. A duplicate or cross-thread checkin double-releases the affinity - * lock (double migrate_enable() in linuxkm) and double-decrements the bank - * refcount. In normal builds we detect sequential misuse -- duplicate or - * stale checkins ordered after the release -- with a cheap check that the - * lock has WC_RNG_BANK_INST_LOCK_HELD. In WC_RNG_BANK_LOCK_DEBUG builds, - * the release is the more expensive compare-and-exchange, which catches - * both sequential misuses and concurrent duplicates (short of ABA reuse of - * the slot within the race window). - */ - if (! (lockval & WC_RNG_BANK_INST_LOCK_HELD)) { -#ifdef WC_VERBOSE_RNG +#ifdef WC_RNG_BANK_LOCK_DEBUG WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_checkin() on an instance that is not checked " - "out (lock %d).\n", lockval); + "BUG: wc_rng_bank_checkin() with an instance that is not in this " + "bank; caller's lock and bank refcount (if any) remain held.\n"); #endif - return BAD_STATE_E; + return ret; } + ret = wc_rng_bank_inst_lock_read(*rng_inst, &lockval); + if (ret < 0) + return ret; + + ret = wc_rng_bank_inst_lock_put(*rng_inst); + if (ret != 0) { #ifdef WC_RNG_BANK_LOCK_DEBUG - { - int expected = lockval; - if (! wolfSSL_Atomic_Int_CompareExchange( - &(*rng_inst)->lock, &expected, - WC_RNG_BANK_INST_LOCK_FREE)) - { - WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_checkin() lock changed under it " - "(%d -> %d).\n", lockval, expected); - return BAD_STATE_E; - } + WOLFSSL_DEBUG_PRINTF( + "wc_rng_bank_checkin(): wc_rng_bank_inst_lock_put() returned code %d " + "(lock state 0x%x).\n", ret, lockval); +#endif + if (ret == WC_NO_ERR_TRACE(OBJECT_NOT_LOCKED_E)) + return ret; } -#else /* !WC_RNG_BANK_LOCK_DEBUG */ - WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); -#endif /* !WC_RNG_BANK_LOCK_DEBUG */ *rng_inst = NULL; @@ -982,11 +1101,9 @@ WOLFSSL_API int wc_rng_bank_checkin( REENABLE_VECTOR_REGISTERS(); if (lockval & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) - ret = bank->affinity_unlock_cb(bank->cb_arg); - else - ret = 0; + (void)bank->affinity_unlock_cb(bank->cb_arg); - { + if (! (bank->flags & WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING)) { WC_ATOMIC_INT_ARG new_refcount; int refdec_err; wolfSSL_RefDec2(&bank->refcount, &new_refcount, &refdec_err); @@ -1063,6 +1180,7 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( int ret; time_t ts1 = 0; int devId; + WC_RNG_lock_arg_t cur_lock = 0; if (rng_inst == NULL) return BAD_FUNC_ARG; @@ -1106,12 +1224,38 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( } #endif + /* Pre-read the held latch's annotation bits -- an owner-context read + * of a live object (the constructor itself never reads its target). + * The reinit is declared _LOCK_INITIALLY, so the instance is + * invariantly locked across the free/reinstantiate cycle, and the + * annotations (including a sticky WC_RNG_LOCK_REQUIRED, when the + * instance carries one) are re-asserted below on success. */ + ret = wc_rng_bank_inst_lock_read(rng_inst, &cur_lock); + if (ret < 0) + return ret; + + wc_FreeRng(&rng_inst->rng); for (;;) { +#ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED + ret = wc_InitRngNonce_ex2(WC_RNG_BANK_INST_TO_RNG(rng_inst), + (byte *)&rng_inst, sizeof(byte *), + bank->heap, devId, + WC_RNG_INIT_FLAGS_LOCK_REQUIRED | + WC_RNG_INIT_FLAGS_LOCK_INITIALLY); +#else ret = wc_InitRngNonce_ex(WC_RNG_BANK_INST_TO_RNG(rng_inst), - (byte *)&rng_inst, sizeof(byte *), - bank->heap, devId); + (byte *)&rng_inst, sizeof(byte *), + bank->heap, devId); +#endif + + if (ret == 0) { + if (cur_lock != 0) { + ret = wc_rng_bank_inst_lock_set_extra(rng_inst, cur_lock); + } + break; + } /* Relax between iterations exactly as wc_rng_bank_init() does. The * caller may hold the affinity lock taken by wc_rng_bank_checkout(), @@ -1120,9 +1264,6 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( */ WC_RELAX_LONG_LOOP(); - if (ret == 0) - break; - /* Several plausible error codes are non-retryable -- fail early for * these rather than reattempting until the timeout. Same list as * wc_rng_bank_init(). @@ -1241,6 +1382,7 @@ WOLFSSL_API int wc_rng_bank_recover_inst( return ret; } + #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) /* Unified mechanics for wc_rng_bank_spawn() and wc_rng_bank_spawn_new(): @@ -1706,4 +1848,5 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng) { #endif /* WC_HAVE_RNG_BANKREF */ + #endif /* WC_RNG_BANK_SUPPORT */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index cc15c5faede..1ed0d5ab3f2 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -27924,9 +27924,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) static const char bank_arg[] = "hi"; byte outbuf1[16], outbuf2[16]; int i; +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) int svc_present = 0; -#ifdef WC_RNG_HAVE_NEXT_SEED - wc_drbg_reseed_ctr_t ns_ctr = 0; #endif #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) @@ -28034,6 +28033,30 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } + { + struct wc_rng_bank_inst *neg_inst = NULL; + + ret = wc_rng_bank_inst_checkin(NULL); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + ret = wc_rng_bank_inst_checkin(&neg_inst); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* A misaligned pointer within the instance array is memory-safe to + * probe through wc_rng_bank_checkin() -- the caller-supplied bank is + * validated before any instance dereference -- and exercises + * rng_inst_matches_bank()'s mid-instance alignment rejection. + * (The same probe through wc_rng_bank_inst_checkin() would be + * undefined behavior: that API must read (*rng_inst)->bank before + * any validation can run.) */ + neg_inst = (struct wc_rng_bank_inst *)((wc_ptr_t)bank->rngs + 1); + ret = wc_rng_bank_checkin(bank, &neg_inst); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } + ret = wc_rng_bank_checkin(bank, &rng_inst); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28051,12 +28074,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - /* A duplicate (stale-copy) check-in must be rejected with BAD_STATE_E -- - * the instance's HELD flag is already clear -- without mutating the - * bank, through both entry points. Hold a second instance across the - * stale check-ins: with the bank refcount at 1, rng_inst_matches_bank() - * rejects with BAD_STATE_E before the HELD-flag guard in - * wc_rng_bank_checkin() -- the guard under test here -- is reached. */ + /* A duplicate (stale-copy) check-in must be rejected with BAD_STATE_E or + * OBJECT_NOT_LOCKED_E -- the instance's HELD flag is already clear -- + * without mutating the bank, through both entry points. Hold a second + * instance across the stale check-ins: with the bank refcount at 1, + * rng_inst_matches_bank() rejects with BAD_STATE_E/OBJECT_NOT_LOCKED_E + * before the HELD-flag guard in wc_rng_bank_checkin() -- the guard under + * test here -- is reached. */ ret = wc_rng_bank_checkout(bank, &rng_inst, 3, 10, WC_RNG_BANK_FLAG_NONE); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28071,11 +28095,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_rng_bank_checkin(bank, &stale_inst); - if (ret != WC_NO_ERR_TRACE(BAD_STATE_E)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (ret != WC_NO_ERR_TRACE(OBJECT_NOT_LOCKED_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_rng_bank_inst_checkin(&stale_inst); - if (ret != WC_NO_ERR_TRACE(BAD_STATE_E)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (ret != WC_NO_ERR_TRACE(OBJECT_NOT_LOCKED_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_rng_bank_inst_checkin(&held_inst); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28283,8 +28307,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif #ifdef WC_HAVE_RNG_BANKREF - if (wolfSSL_RefCur(bank->refcount) != 2) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wolfSSL_RefCur(bank->refcount); + if (ret != 2) + ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); ret = wc_rng_bank_fini(bank); if (ret != WC_NO_ERR_TRACE(BUSY_E)) @@ -28292,8 +28317,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) wc_FreeRng(rng); - if (wolfSSL_RefCur(bank->refcount) != 1) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wolfSSL_RefCur(bank->refcount); + if (ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); #endif #ifdef WC_RNG_BANK_DEFAULT_SUPPORT @@ -28487,8 +28513,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wolfSSL_RefCur(bank2->refcount) != 2) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wolfSSL_RefCur(bank2->refcount); + if (ret != 2) + ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); ret = wc_rng_bank_free(&bank2); if (ret != WC_NO_ERR_TRACE(BUSY_E)) @@ -28497,8 +28524,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) wc_rng_free(rng2); rng2 = NULL; - if (wolfSSL_RefCur(bank2->refcount) != 1) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wolfSSL_RefCur(bank2->refcount); + if (ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); #endif /* WC_HAVE_RNG_BANKREF */ @@ -28510,6 +28538,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif /* !WC_RNG_BANK_STATIC */ +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) /* ---- rng_bank service extensions: recovery-patrol and in-service- * guarantee checkout flags, wc_rng_bank_inst_checkin(), the daemon * banking entry point with consume-at-checkout, and the RBGC spawn @@ -28529,6 +28558,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (rng_inst != NULL) ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7,0,0) */ + /* the in-service guarantee on a healthy instance is transparent */ ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 0, WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED); @@ -28539,26 +28570,21 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); /* contradictory flag combinations */ - if (wc_rng_bank_checkout(bank, &rng_inst, 0, 0, - WC_RNG_BANK_FLAG_FOR_RECOVERY | - WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_rng_bank_checkout(bank, &rng_inst, 0, 0, - WC_RNG_BANK_FLAG_FOR_RECOVERY | - WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 0, WC_RNG_BANK_FLAG_FOR_RECOVERY | WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 0, WC_RNG_BANK_FLAG_FOR_RECOVERY | WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); /* recovery patrol: argument contracts; a healthy instance is a * success no-op */ - if (wc_rng_bank_recover_inst(NULL, 0, 0, 0) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_rng_bank_recover_inst(bank, 0, 0, - WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_recover_inst(NULL, 0, 0, 0); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_recover_inst(bank, 0, 0, WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (wc_rng_bank_recover_inst(bank, WC_RNG_BANK_STATIC_SIZE, 0, 0) == 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); ret = wc_rng_bank_recover_inst(bank, 0, 0, 0); @@ -28567,29 +28593,34 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef WC_RNG_HAVE_NEXT_SEED /* daemon banking entry point: argument contracts */ - if (wc_rng_bank_next_seed_generate(NULL, 0, 32) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_rng_bank_next_seed_generate(bank, -1, 32) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_rng_bank_next_seed_generate(bank, WC_RNG_BANK_STATIC_SIZE, 32) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_rng_bank_next_seed_generate(bank, 0, 0) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_next_seed_generate(NULL, 0, 32); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_next_seed_generate(bank, -1, 32); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_next_seed_generate(bank, WC_RNG_BANK_STATIC_SIZE, 32); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_next_seed_generate(bank, 0, 0); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (svc_present) { + wc_drbg_reseed_ctr_t ns_ctr; /* bank instance 0's next seed to publication, then consume it at * checkout: an atomic-context-safe credited reseed (counter * lands at 1) */ for (i = 0; i < 64; i++) { - ret = wc_rng_bank_next_seed_generate(bank, 0, 32); + ret = wc_rng_bank_next_seed_generate(bank, 0, (word32)(WC_DRBG_NEXT_SEED_LEN / 7)); if (ret == WC_NO_ERR_TRACE(ALREADY_E)) break; - if (ret != 0) + if ((ret != 0) && (ret != WC_NO_ERR_TRACE(NOT_READY_E)) && + (ret != WC_NO_ERR_TRACE(ENTROPY_RT_E)) && + (ret != WC_NO_ERR_TRACE(ENTROPY_APT_E))) + { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } } if (i >= 64) ERROR_OUT(WC_TEST_RET_ENC_NC, out); @@ -28677,6 +28708,139 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + /* WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE checkout contracts. + * Per-call PR demands CAN_WAIT (the fresh gather may block) and + * contradicts uncredited and recovery seeding. */ + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_SEED_UNCREDITED); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_FOR_RECOVERY); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + if (svc_present) { + wc_drbg_reseed_ctr_t ns_ctr; + /* effective PR: the leased instance is freshly credited-reseeded + * (counter exactly 1) before any caller draw */ + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, + WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | + WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(rng_inst), &ns_ctr); + if ((ret != 0) || (ns_ctr != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(rng_inst), + outbuf1, sizeof(outbuf1)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* bank-wide PR posture: same freshness on every sleepable lease; + * atomic callers are refused outright; recovery is exempt. */ + bank->flags |= WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE; + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, + WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(rng_inst), &ns_ctr); + if ((ret != 0) || (ns_ctr != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* take the instance out of service while holding it */ + WC_RNG_BANK_INST_TO_RNG(rng_inst)->status = WC_DRBG_FAILED; + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + /* a bare-targeted PR checkout refuses to serve -- or heal -- a + * failed instance (recovery is the sole restoration path), and + * unwinds completely */ + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != WC_NO_ERR_TRACE(RNG_FAILURE_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (rng_inst != NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* recovery is exempt from the bank-wide posture */ + ret = wc_rng_bank_recover_inst(bank, 0, 10, + WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + /* restored: PR-served again, proving no leaked lock or refcount */ + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, + WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(rng_inst), &ns_ctr); + if ((ret != 0) || (ns_ctr != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + bank->flags &= ~(word32)WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE; + +#ifdef WC_RNG_HAVE_NEXT_SEED + /* PR supersedes CONSUME_NEXT_SEED: the fresh reseed is performed, + * and the banked seed is left intact for a later consumer */ + for (i = 0; i < 64; i++) { + ret = wc_rng_bank_next_seed_generate(bank, 0, + (word32)(WC_DRBG_NEXT_SEED_LEN / 7)); + if (ret == WC_NO_ERR_TRACE(ALREADY_E)) + break; + if ((ret != 0) && (ret != WC_NO_ERR_TRACE(NOT_READY_E)) && + (ret != WC_NO_ERR_TRACE(ENTROPY_RT_E)) && + (ret != WC_NO_ERR_TRACE(ENTROPY_APT_E))) + { + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } + } + if (i >= 64) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, + WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | + WC_RNG_BANK_FLAG_CAN_WAIT | + WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + { + WC_ATOMIC_INT_ARG pr_ns_cur = 0; + ret = wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(rng_inst), &ns_ctr); + if ((ret != 0) || (ns_ctr != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_RNG_DRBG_NextSeedCurrent( WC_RNG_BANK_INST_TO_RNG(rng_inst), &pr_ns_cur); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (pr_ns_cur != WC_DRBG_NEXT_SEED_READY) + ERROR_OUT(WC_TEST_RET_ENC_I((int)pr_ns_cur), out); + /* the surviving banked seed remains redeemable */ + ret = wc_RNG_DRBG_NextSeedNow( + WC_RNG_BANK_INST_TO_RNG(rng_inst)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_RNG_DRBG_NextSeedCurrent( WC_RNG_BANK_INST_TO_RNG(rng_inst), &pr_ns_cur); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (pr_ns_cur != WC_DRBG_NEXT_SEED_EMPTY) + ERROR_OUT(WC_TEST_RET_ENC_I((int)pr_ns_cur), out); + } + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif /* WC_RNG_HAVE_NEXT_SEED */ + } +#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7,0,0) */ + out: { @@ -28731,7 +28895,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ret = WC_TEST_RET_ENC_EC(cleanup_ret); } WC_FREE_VAR_EX(leaf_rng, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); -#endif +#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && + * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ } return ret; diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index c51674e8574..c33990aa712 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -41,24 +41,24 @@ #endif #define WC_RNG_BANK_FLAG_NONE 0 -#define WC_RNG_BANK_FLAG_INITED (1<<0) -#define WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST (1<<1) -#define WC_RNG_BANK_FLAG_CAN_WAIT (1<<2) -#define WC_RNG_BANK_FLAG_NO_VECTOR_OPS (1<<3) -#define WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST (1<<4) -#define WC_RNG_BANK_FLAG_AFFINITY_LOCK (1<<5) +#define WC_RNG_BANK_FLAG_INITED (1U << 0) +#define WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST (1U << 1) +#define WC_RNG_BANK_FLAG_CAN_WAIT (1U << 2) +#define WC_RNG_BANK_FLAG_NO_VECTOR_OPS (1U << 3) +#define WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST (1U << 4) +#define WC_RNG_BANK_FLAG_AFFINITY_LOCK (1U << 5) /* WC_RNG_BANK_FLAG_SEED_UNCREDITED applies only to wc_rng_bank_seed(): the * supplied seed material is mixed into each instance without entropy credit * (wc_RNG_DRBG_Reseed_Uncredited()), leaving the reseed schedule governed * solely by the module's own seed source. */ -#define WC_RNG_BANK_FLAG_SEED_UNCREDITED (1<<6) +#define WC_RNG_BANK_FLAG_SEED_UNCREDITED (1U << 6) /* WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED applies only to wc_rng_bank_checkout(): * if the checked-out instance has a ready banked next seed (see * wc_RNG_DRBG_NextSeedGenerate() et al.), consume it in an immediate, * source-free credited reseed before returning the instance; a no-op when * no bank is ready or the build/instance has no next-seed support. Safe in * atomic context. */ -#define WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED (1<<7) +#define WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED (1U << 7) /* WC_RNG_BANK_FLAG_FOR_RECOVERY declares a recovery-intent checkout of a * specific instance (e.g. by a reseed-and-recovery daemon's patrol): * out-of-service status is expected and accepted, and the @@ -69,7 +69,7 @@ * targeted (non-failover) checkout admits out-of-service instances with or * without this flag; the flag makes the intent explicit and * interaction-safe. */ -#define WC_RNG_BANK_FLAG_FOR_RECOVERY (1<<8) +#define WC_RNG_BANK_FLAG_FOR_RECOVERY (1U << 8) /* WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED guarantees that * wc_rng_bank_checkout() (and APIs built on it, e.g. wc_rng_bank_spawn()) * either returns a lease on an in-service instance (status WC_DRBG_OK) or @@ -83,7 +83,7 @@ * instances is BAD_STATE_E. Contradicts, and is rejected with, * _FOR_RECOVERY. Applies to instance status only; reseed-due diversion * semantics are unchanged. */ -#define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1<<9) +#define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1U << 9) /* WC_RNG_BANK_FLAG_QUIET suppresses the facility's WC_VERBOSE_RNG * operational warnings -- expected-condition notices such as the * reseed-due-instance handout, reinit retry/timeout reports, the @@ -92,203 +92,43 @@ * log. A bank-level flag only, set at wc_rng_bank_init(); it has no * per-call meaning and never suppresses refcount/consistency * diagnostics. */ -#define WC_RNG_BANK_FLAG_QUIET (1<<10) - -#define WC_RNG_BANK_INST_LOCK_FREE 0 -#define WC_RNG_BANK_INST_LOCK_HELD (1<<0) -#define WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED (1<<1) -#define WC_RNG_BANK_INST_LOCK_VEC_OPS_INH (1<<2) - -/* ---- Legacy FIPS boundary compatibility -------------------------------- - * - * Pre-v7 FIPS boundaries do not export the DRBG accessor and reseed - * scheduling services that wolfcrypt/src/random.c supplies as of FIPS v7 - * (wc_RNG_GetStatus(), wc_RNG_DRBG_Present(), wc_RNG_DRBG_GetReseedCtr(), - * wc_RNG_DRBG_ScheduleReseed(), wc_RNG_DRBG_Reseed_Uncredited(), and - * wc_RNG_DRBG_Reseed_Now()). Supply source-compatible static fallbacks - * here, implemented via the public DRBG struct definitions in the legacy - * random.h. These fallbacks are the historic direct-access mechanism, now - * confined to frozen pre-v7 boundaries, which cannot gain new services; - * wherever the in-boundary services exist, they are used instead. - */ -#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - -#include - -#ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED -#define WC_DRBG_RESEED_CTR_TYPE_DEFINED - #ifdef WORD64_AVAILABLE - typedef word64 wc_drbg_reseed_ctr_t; +#define WC_RNG_BANK_FLAG_QUIET (1U << 10) +/* WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (bank-level, set at + * wc_rng_bank_init()) declares that the bank's lifetime is guaranteed by + * its container to enclose all checkouts (e.g. a bank embedded in a + * kernel crypto tfm context, torn down only after the API has quiesced + * callers). Per-checkout refcount traffic -- the one bank-global RMW + * pair on the readout hot path -- is suppressed; refcount checks degrade + * to read-only validity tests. The refcount itself remains, serving its + * standing roles: the INITED baseline, default-bank registration + * (wc_rng_bank_default_set()), and per-bankref lifetime references. + * Contract: with this flag, a wc_rng_bank_fini() racing live checkouts is + * a use-after-free instead of BUSY_E -- only containers whose teardown + * provably quiesces consumers first may set it. */ +#define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 11) +#define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 14) + +/* base lock states are WC_RNG_LOCK_FREE / WC_RNG_LOCK_HELD in random.h; + * these annotation bits ride above WC_RNG_LOCK_HELD via + * wc_RNG_lock_get()/_set_extra()/_clear_extra(). */ + +#ifndef WC_RNG_HAVE_LOCK + #define WC_RNG_LOCK_FREE 0 + #define WC_RNG_LOCK_HELD (1U<<0) + #define WC_RNG_LOCK_REQUIRED (1U<<1) + #define WC_RNG_LOCK_ENTROPY_INVALIDATED (1U<<2) + #define WC_RNG_LOCK_EXTRA_SHIFT 3U + #ifdef WOLFSSL_NO_ATOMICS + typedef word32 WC_RNG_lock_t; + typedef word32 WC_RNG_lock_arg_t; #else - typedef word32 wc_drbg_reseed_ctr_t; + typedef wolfSSL_Atomic_Uint WC_RNG_lock_t; + typedef WC_ATOMIC_UINT_ARG WC_RNG_lock_arg_t; #endif #endif -#if FIPS_VERSION3_LT(5,2,4) || FIPS_VERSION3_EQ(6,0,0) - /* WC_DRBG_OK predates these FIPS random.h editions. */ - #define WC_DRBG_OK 1 -#endif - -/* Helpers to access reseedCtr / null-check the active DRBG. The shape of - * struct WC_RNG and the DRBG_*_internal types varies by which DRBGs are - * compiled in; random.h gates the SHA-256 side on !NO_SHA256 and the SHA-512 - * side on WOLFSSL_DRBG_SHA512, so all three live combinations are handled - * separately here. */ -#if defined(WOLFSSL_DRBG_SHA512) && !defined(NO_SHA256) - /* Both DRBGs compiled in: dispatch on the runtime drbgType. */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((rng_ptr)->drbgType == WC_DRBG_SHA512) \ - ? ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - : ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - if ((rng_ptr)->drbgType == WC_DRBG_SHA512) \ - ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - = (val); \ - else \ - ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg == NULL && (rng_ptr)->drbg512 == NULL) -#elif defined(WOLFSSL_DRBG_SHA512) - /* SHA-512 DRBG only (NO_SHA256 defined); the SHA-256 struct and - * rng->drbg field do not exist in this build. */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg512 == NULL) -#else - /* SHA-256 DRBG only (the historical default). */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg == NULL) -#endif - -/* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to - * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when - * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 - * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 - * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time - * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be - * defined with a (word64) cast (settings.h kernel path) that the preprocessor - * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips - * it without expanding that cast. */ -#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) - wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); -#endif - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_GetStatus(const WC_RNG* rng) -{ - if (rng == NULL) - return BAD_FUNC_ARG; - return (int)rng->status; -} - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Present(const WC_RNG* rng) -{ - return (rng != NULL) && (! WC_RNG_BANK_DRBG_NULL(rng)); -} - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_GetReseedCtr( - const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr) -{ - if ((rng == NULL) || (reseedCtr == NULL)) - return BAD_FUNC_ARG; - if (WC_RNG_BANK_DRBG_NULL(rng)) - *reseedCtr = 0; - else - *reseedCtr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); - return 0; -} - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) -{ - if (rng == NULL) - return BAD_FUNC_ARG; - if (! WC_RNG_BANK_DRBG_NULL(rng)) - WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); - return 0; -} - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Uncredited( - WC_RNG* rng, const byte* seed, word32 seedSz) -{ - wc_drbg_reseed_ctr_t saved_ctr; - int ret; - - if ((rng == NULL) || (seed == NULL)) - return BAD_FUNC_ARG; - if (WC_RNG_BANK_DRBG_NULL(rng)) { - /* defer to wc_RNG_DRBG_Reseed()'s RDRAND-config handling. */ - return wc_RNG_DRBG_Reseed(rng, seed, seedSz); - } - saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); - ret = wc_RNG_DRBG_Reseed(rng, seed, seedSz); - /* wc_RNG_DRBG_Reseed() only resets the counter on success, so the - * unconditional restore is exact either way. */ - WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); - return ret; -} - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Now( - WC_RNG* rng, const byte* nonce, word32 nonceSz) -{ - wc_drbg_reseed_ctr_t saved_ctr; - int ret; - byte scratch[4]; - - if (rng == NULL) - return BAD_FUNC_ARG; - if ((nonce == NULL) && (nonceSz > 0)) - return BAD_FUNC_ARG; - if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) - return RNG_FAILURE_E; - if (WC_RNG_BANK_DRBG_NULL(rng)) { - /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ - return 0; - } - - saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); - WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); - - /* The legacy boundary has no direct reseed-from-source service; a - * minimal generate at the forced counter performs the module's own - * PollAndReSeed() in-boundary. This consumes 4 bytes of output, so on - * success the fresh reseed counter is 2 rather than 1. scratch holds - * only discarded output bytes; XMEMSET suffices for it here. */ - ret = wc_RNG_GenerateBlock(rng, scratch, (word32)sizeof(scratch)); - XMEMSET(scratch, 0, sizeof(scratch)); - - if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { - /* On the legacy boundary, nonce incorporation is a separate - * (uncredited) transition following the reseed, rather than part of - * the same reseed derivation. */ - ret = wc_RNG_DRBG_Reseed_Uncredited(rng, nonce, nonceSz); - } - - if ((ret != 0) && - ((wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng) >= - (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL)) - { - /* The reseed did not occur -- restore the counter, leaving it - * unmodified as the contract requires. */ - WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); - } - - return ret; -} - -#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ +#define WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED (1U<<(WC_RNG_LOCK_EXTRA_SHIFT+0)) +#define WC_RNG_BANK_INST_LOCK_VEC_OPS_INH (1U<<(WC_RNG_LOCK_EXTRA_SHIFT+1)) typedef int (*wc_affinity_lock_fn_t)(void *arg); typedef int (*wc_affinity_get_id_fn_t)(void *arg, int *id); @@ -297,11 +137,17 @@ typedef int (*wc_affinity_unlock_fn_t)(void *arg); struct wc_rng_bank; struct wc_rng_bank_inst { -#ifdef WOLFSSL_NO_ATOMICS - int lock; -#else - wolfSSL_Atomic_Int lock; -#endif + #ifdef WC_RNG_HAVE_LOCK + /* the exclusivity latch lives in rng.lock (wc_RNG_lock_*()) -- + * in-FIPS-boundary, module-enforced. This struct persists for the + * parent pointer and future bank-side slots. */ + #else + #ifdef WOLFSSL_NO_ATOMICS + word32 lock; + #else + wolfSSL_Atomic_Uint lock; + #endif + #endif struct wc_rng_bank *bank; WC_RNG rng; }; @@ -324,6 +170,7 @@ struct wc_rng_bank { wc_affinity_unlock_fn_t affinity_unlock_cb; void *cb_arg; /* if mutable, caller is responsible for thread safety. */ int n_rngs; + int first_failover_inst; #ifdef WC_RNG_HAVE_NEXT_SEED /* Serializes whole-instance operations (wc_rng_bank_inst_reinit()'s * free/reinstantiate cycle) against the entropy daemon's lockless @@ -359,6 +206,20 @@ WOLFSSL_API int wc_rng_bank_init( void *heap, int devId); +WOLFSSL_API int wc_rng_bank_init_nonce( + struct wc_rng_bank *ctx, + int n_rngs, + word32 flags, + int timeout_secs, + void *heap, + int devId, + const byte *nonce, + word32 nonceSz); + +WOLFSSL_API int wc_rng_bank_first_failover_inst_set( + struct wc_rng_bank *ctx, + int first_failover_inst); + WOLFSSL_API int wc_rng_bank_set_affinity_handlers( struct wc_rng_bank *ctx, wc_affinity_lock_fn_t affinity_lock_cb, @@ -391,9 +252,19 @@ WOLFSSL_API int wc_rng_bank_checkout( int timeout_secs, word32 flags); + +#if defined(WC_DRBG_BANKREF) && !defined(WC_HAVE_RNG_BANKREF) + /* forward compat for FIPS v5.2.4 random.h */ + #define WC_HAVE_RNG_BANKREF +#endif + +#ifdef WC_HAVE_RNG_BANKREF WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref( struct wc_rng_bank *bank, struct wc_rng_bank_inst **rng_inst); +#endif + +WOLFSSL_API int wc_rng_bank_get_inst_id(struct wc_rng_bank_inst *rng_inst); WOLFSSL_API int wc_rng_bank_checkin( struct wc_rng_bank *bank, @@ -442,6 +313,7 @@ WOLFSSL_API int wc_rng_bank_recover_inst( int timeout_secs, word32 flags); + #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) /* Spawn an SP 800-90C chain leaf from a bank instance: check out an @@ -489,11 +361,6 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, int timeout_secs, word32 flags); -#if defined(WC_DRBG_BANKREF) && !defined(WC_HAVE_RNG_BANKREF) - /* forward compat for FIPS v5.2.4 random.h */ - #define WC_HAVE_RNG_BANKREF -#endif - #ifdef WC_HAVE_RNG_BANKREF WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng); @@ -507,6 +374,460 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); #define WC_RNG_BANK_INST_TO_RNG(rng_inst) (&(rng_inst)->rng) +#ifdef WC_RNG_HAVE_LOCK + + static WC_INLINE int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { + return wc_RNG_lock_get(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); + } + static WC_INLINE int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) { + return wc_RNG_lock_put(WC_RNG_BANK_INST_TO_RNG(inst), 0); + } + static WC_INLINE int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expect_extra_bits) { + return wc_RNG_lock_put_conditional(WC_RNG_BANK_INST_TO_RNG(inst), expect_extra_bits, 0); + } + static WC_INLINE int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t *state) { + return wc_RNG_lock_read(WC_RNG_BANK_INST_TO_RNG(inst), state); + } + static WC_INLINE int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { + return wc_RNG_lock_set_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); + } + static WC_INLINE int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { + return wc_RNG_lock_add_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); + } + static WC_INLINE int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { + return wc_RNG_lock_clear_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); + } + static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_get_conditional( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits) + { + return wc_RNG_lock_get_conditional(WC_RNG_BANK_INST_TO_RNG(inst), + expected_extra_bits, want_extra_bits); + } + + +#else /* !WC_RNG_HAVE_LOCK */ + +/* Backward compat: with a pre-v7 FIPS boundary (or WC_RNG_NO_LOCK), the + * latch lives in the bank instance rather than in the (frozen) WC_RNG. + * These are ports of the wc_RNG_lock_*() state machine. + * In every CAS below, the stored value derives only from the CAS-verified + * value and the caller's arguments -- never from a prior load. */ + +static WC_INLINE int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + + if (inst == NULL) + return BAD_FUNC_ARG; + + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && + (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_HELD | extra_bits))) + { + return 0; + } + + if (cur_lock & WC_RNG_LOCK_HELD) + return BUSY_E; + else + return UNEXPECTED_STATE_E; +} + +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_get_conditional( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, expected; + + if (inst == NULL) + return BAD_FUNC_ARG; + + expected_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + want_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + expected = (cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & + ~WC_RNG_LOCK_HELD)) | + expected_extra_bits; + + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && + (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &expected, + expected | WC_RNG_LOCK_HELD | want_extra_bits))) + { + return 0; + } + + if (expected & WC_RNG_LOCK_HELD) + return BUSY_E; + else + return UNEXPECTED_STATE_E; +} + +static WC_INLINE int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) +{ + WC_RNG_lock_arg_t cur_lock, new_lock; + if (inst == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + if (! (cur_lock & WC_RNG_LOCK_HELD)) + return OBJECT_NOT_LOCKED_E; + + for (;;) { + new_lock = cur_lock & + ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD)); + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, new_lock)) + break; + } + + return 0; +} + +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, expected, new_lock; + + if (inst == NULL) + return BAD_FUNC_ARG; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + if (! (cur_lock & WC_RNG_LOCK_HELD)) + return OBJECT_NOT_LOCKED_E; + + for (;;) { + new_lock = (cur_lock & + ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD))) | + (extra_bits & WC_RNG_LOCK_REQUIRED); + + expected = WC_RNG_LOCK_HELD | extra_bits; + + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &expected, new_lock)) + { + return 0; + } + if ((expected & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U)) != + (extra_bits & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U))) + { + break; + } + cur_lock = expected; + } + /* conditional release failed: the caller is still the holder. */ + return UNEXPECTED_STATE_E; +} + +static WC_INLINE int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state) +{ + if ((inst == NULL) || (state == NULL)) + return BAD_FUNC_ARG; + *state = WOLFSSL_ATOMIC_LOAD(inst->lock); + return 0; +} + +static WC_INLINE int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, new_lock; + if (inst == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + for (;;) { + new_lock = cur_lock & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + new_lock |= extra_bits; + + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, new_lock)) + break; + } + return 0; +} + +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + if (inst == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock | extra_bits)) + break; + } + return 0; +} + +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + if (inst == NULL) + return BAD_FUNC_ARG; + if (extra_bits & WC_RNG_LOCK_REQUIRED) { + /* WC_RNG_LOCK_REQUIRED is sticky by contract */ + return BAD_FUNC_ARG; + } + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); + + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock & ~extra_bits)) + break; + } + return 0; +} + + +#endif /* !WC_RNG_HAVE_LOCK */ + + +/* ---- Legacy FIPS boundary compatibility -------------------------------- + * + * Pre-v7 FIPS boundaries do not export the DRBG accessor and reseed + * scheduling services that wolfcrypt/src/random.c supplies as of FIPS v7 + * (wc_RNG_GetStatus(), wc_RNG_DRBG_Present(), wc_RNG_DRBG_GetReseedCtr(), + * wc_RNG_DRBG_ScheduleReseed(), wc_RNG_DRBG_Reseed_Uncredited(), and + * wc_RNG_DRBG_Reseed_Now()). Supply source-compatible static fallbacks + * here, implemented via the public DRBG struct definitions in the legacy + * random.h. These fallbacks are the historic direct-access mechanism, now + * confined to frozen pre-v7 boundaries, which cannot gain new services; + * wherever the in-boundary services exist, they are used instead. + */ +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + +#include + +#ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED + #define WC_DRBG_RESEED_CTR_TYPE_DEFINED + #if defined(WORD64_AVAILABLE) && FIPS_VERSION3_GE(5,2,4) + typedef word64 wc_drbg_reseed_ctr_t; + #else + typedef word32 wc_drbg_reseed_ctr_t; + #endif +#endif + +/* WC_DRBG_OK predates some old FIPS editions, but is 1 in all of them -- force + * consistency. */ +#undef WC_DRBG_OK +#define WC_DRBG_OK 1 + +/* Helpers to access reseedCtr / null-check the active DRBG. The shape of + * struct WC_RNG and the DRBG_*_internal types varies by which DRBGs are + * compiled in; random.h gates the SHA-256 side on !NO_SHA256 and the SHA-512 + * side on WOLFSSL_DRBG_SHA512, so all three live combinations are handled + * separately here. */ +#if defined(WOLFSSL_DRBG_SHA512) && !defined(NO_SHA256) + /* Both DRBGs compiled in: dispatch on the runtime drbgType. */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((rng_ptr)->drbgType == WC_DRBG_SHA512) \ + ? ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + : ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + if ((rng_ptr)->drbgType == WC_DRBG_SHA512) \ + ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + = (val); \ + else \ + ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg == NULL && (rng_ptr)->drbg512 == NULL) +#elif defined(WOLFSSL_DRBG_SHA512) + /* SHA-512 DRBG only (NO_SHA256 defined); the SHA-256 struct and + * rng->drbg field do not exist in this build. */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg512 == NULL) +#else + /* SHA-256 DRBG only (the historical default). */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg == NULL) +#endif + +/* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to + * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when + * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 + * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 + * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time + * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be + * defined with a (word64) cast (settings.h kernel path) that the preprocessor + * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips + * it without expanding that cast. */ +#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) + wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); +#endif + + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_GetStatus(const WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + return (int)rng->status; +} + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Present(const WC_RNG* rng) +{ + return (rng != NULL) && (! WC_RNG_BANK_DRBG_NULL(rng)); +} + +#if FIPS_VERSION3_NE(5,2,4) +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_GetReseedCtr( + const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr) +{ + if ((rng == NULL) || (reseedCtr == NULL)) + return BAD_FUNC_ARG; + if (WC_RNG_BANK_DRBG_NULL(rng)) + *reseedCtr = 0; + else + *reseedCtr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + return 0; +} +#endif + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + if (! WC_RNG_BANK_DRBG_NULL(rng)) + WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); + return 0; +} + +#if FIPS_VERSION3_NE(5,2,4) +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Uncredited( + WC_RNG* rng, const byte* seed, word32 seedSz) +{ + wc_drbg_reseed_ctr_t saved_ctr; + int ret; + + if ((rng == NULL) || (seed == NULL)) + return BAD_FUNC_ARG; + if (WC_RNG_BANK_DRBG_NULL(rng)) { + /* defer to wc_RNG_DRBG_Reseed()'s RDRAND-config handling. */ + return wc_RNG_DRBG_Reseed(rng, seed, seedSz); + } + saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + ret = wc_RNG_DRBG_Reseed(rng, seed, seedSz); + /* wc_RNG_DRBG_Reseed() only resets the counter on success, so the + * unconditional restore is exact either way. */ + WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); + return ret; +} + +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Now( + WC_RNG* rng, const byte* nonce, word32 nonceSz) +{ + wc_drbg_reseed_ctr_t saved_ctr; + int ret; + byte scratch[4]; + + if (rng == NULL) + return BAD_FUNC_ARG; + if ((nonce == NULL) && (nonceSz > 0)) + return BAD_FUNC_ARG; + if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) + return RNG_FAILURE_E; + if (WC_RNG_BANK_DRBG_NULL(rng)) { + /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ + return 0; + } + + saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); + + /* The legacy boundary has no direct reseed-from-source service; a + * minimal generate at the forced counter performs the module's own + * PollAndReSeed() in-boundary. This consumes 4 bytes of output, so on + * success the fresh reseed counter is 2 rather than 1. scratch holds + * only discarded output bytes; XMEMSET suffices for it here. */ + ret = wc_RNG_GenerateBlock(rng, scratch, (word32)sizeof(scratch)); + XMEMSET(scratch, 0, sizeof(scratch)); + + if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { + /* On the legacy boundary, nonce incorporation is a separate + * (uncredited) transition following the reseed, rather than part of + * the same reseed derivation. */ + ret = wc_RNG_DRBG_Reseed_Uncredited(rng, nonce, nonceSz); + } + + if ((ret != 0) && + ((wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng) >= + (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL)) + { + /* The reseed did not occur -- restore the counter, leaving it + * unmodified as the contract requires. */ + WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); + } + + return ret; +} +#endif /* FIPS_VERSION3_NE(5,2,4) */ + +#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ + +/* Portable reseed helpers: with the in-boundary latch (WC_RNG_HAVE_LOCK) + * these merely forward to the random.c services; with the bank-side latch + * they forward through the compat shims. */ + +#ifdef WC_RNG_HAVE_LOCK + +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( + struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz) +{ + if (inst == NULL) + return BAD_FUNC_ARG; + return wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), + nonce, nonceSz); +} + +#else /* !WC_RNG_HAVE_LOCK */ + +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( + struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz) +{ + int ret; + if (inst == NULL) + return BAD_FUNC_ARG; + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), + nonce, nonceSz); + return ret; +} + + +#endif /* !WC_RNG_HAVE_LOCK */ + #endif /* WC_RNG_BANK_SUPPORT */ #endif /* WOLF_CRYPT_RNG_BANK_H */ From 7bca2d66f6572970f2c4bc2d6570794b4af8f73c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:03:21 +0000 Subject: [PATCH 023/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/test/test.c: generalize SP 800-90C RBG chains from a leaf tag to strata: * struct WC_RNG.isRbgcLeaf becomes int RBGCStratum: 0 for a root, n for a chain member seeded from a stratum-(n-1) parent, sticky for the instance's lifetime, with SEQ_OVERFLOW_E on stratum overflow; wc_RNG_DRBG_IsRBGCLeaf() becomes wc_RNG_DRBG_GetRBGCStratum(); SpawnRngRBGC() reworks to parent/child terms with a flags argument, omitting the seed health test (superfluous when a healthy DRBG generates the seed data); * the wc_InitRng*RBGC() constructors gain a flags argument (WC_RNG_INIT_FLAGS_*); new wc_RNG_DRBG_ReseedRBGC_Uncredited(); * banked next seeds carry provenance: nextSeedRBGCStratum in the DRBG aperture, WC_RNG_FLAG_RBGC_NEXT_SEED marking a chain-filled bank, wc_RNG_DRBG_NextSeedGenerate_RBGC() to fill from a parent, and wc_RNG_DRBG_GetNextSeedRBGCStratum() (race-free via the aperture protocol) to interrogate it; * carve the whole facility out under WC_RNG_HAVE_RBGC (opt-out: WC_RNG_NO_RBGC), derived from HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK; * rng_bank: WC_RNG_BANK_FLAG_INIT_RBGC instantiates bank instances as chain children of an internal root, on FIPS v7+ boundaries with _LOCK_REQUIRED; * test.c: rng_drbg_rbgc_test() reworked for strata (including a pre-v7 compat arm via the rng_bank.h shims), spawn contracts updated for the flags argument, and stratum probes in the svc and bank tests. --- .wolfssl_known_macro_extras | 1 + wolfcrypt/src/random.c | 540 +++++++++++++++++++++++------------ wolfcrypt/src/rng_bank.c | 101 ++++++- wolfcrypt/test/test.c | 414 ++++++++++++++++++++++----- wolfcrypt/test/test.h | 2 +- wolfssl/wolfcrypt/random.h | 77 +++-- wolfssl/wolfcrypt/rng_bank.h | 105 +++++-- 7 files changed, 921 insertions(+), 319 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index b6be54aa049..e39705308f6 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -806,6 +806,7 @@ WC_RNG_BLOCKING WC_RNG_NO_LOCK WC_RNG_NO_LOCK_FULL_MUTEX WC_RNG_NO_NEXT_SEED +WC_RNG_NO_RBGC WC_RSA_NONBLOCK_TIME WC_RSA_NO_FERMAT_CHECK WC_RTL8735B_NO_DERIVE_CACHE diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index d1e3446a1e6..efaf0621275 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -839,6 +839,10 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, return ret; ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, 1 /* credited */); +#ifdef WC_RNG_HAVE_RBGC + if (ret == 0) + rng->RBGCStratum = 0; +#endif return ret; } @@ -847,16 +851,53 @@ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz) { return wc_RNG_DRBG_Reseed_Nonce(rng, seed, seedSz, NULL, 0); } +#ifdef WC_RNG_HAVE_RBGC -/* Returns 1 if rng was seeded from another DRBG's output (an SP 800-90C - * chain leaf, via wc_InitRng*RBGC() or wc_RNG_DRBG_ReseedRBGC()), else 0. - * The tag is sticky for the instance's lifetime; a leaf is never usable as - * a chain root. */ -int wc_RNG_DRBG_IsRBGCLeaf(const WC_RNG* rng) +int wc_RNG_DRBG_GetRBGCStratum(const WC_RNG* rng) { - return (rng != NULL) && rng->isRbgcLeaf; + if (rng) + return rng->RBGCStratum; + else + return BAD_FUNC_ARG; } +#ifdef WC_RNG_HAVE_NEXT_SEED +int wc_RNG_DRBG_GetNextSeedRBGCStratum(const WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + + /* Race-free via the NextSeed aperture protocol. If called with rng locked, + * and ->nextSeedLen == WC_DRBG_NEXT_SEED_READY, then competing producers + * and consumers are all excluded, unambiguously marking ->nextSeedRBGCStratum + * as strictly reliable and stable. nextSeedLen functions as the + * synchronizer -- the producer writes ->nextSeedRBGCStratum before publishing + * WC_DRBG_NEXT_SEED_READY to nextSeedLen with release semantics. + */ + +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { + if (WOLFSSL_ATOMIC_LOAD(((const DRBG_internal*)rng->drbg)->nextSeedLen) != WC_DRBG_NEXT_SEED_READY) + return NOT_READY_E; + else + return ((const DRBG_internal *)rng->drbg)->nextSeedRBGCStratum; + } +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { + if (WOLFSSL_ATOMIC_LOAD(((const DRBG_SHA512_internal*)rng->drbg512)->nextSeedLen) != WC_DRBG_NEXT_SEED_READY) + return NOT_READY_E; + else + return ((const DRBG_SHA512_internal *)rng->drbg512)->nextSeedRBGCStratum; + } +#endif + + return BAD_FUNC_ARG; +} +#endif /* WC_RNG_HAVE_NEXT_SEED */ + +#endif /* WC_RNG_HAVE_RBGC */ + /* Read-only accessor for the DRBG reseed counter. When no DRBG is * instantiated (see wc_RNG_DRBG_Present()) there is no counter; *reseedCtr * is set to 0 -- never due for reseed -- and 0 is returned. */ @@ -2167,9 +2208,17 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #endif /* WC_RNG_HAVE_LOCK */ { XMEMSET(rng, 0, sizeof(*rng)); - rng->isRbgcLeaf = (seedRng != NULL); } +#ifdef WC_RNG_HAVE_RBGC + if (seedRng == NULL) + rng->RBGCStratum = 0; + else { + if (seedRng->RBGCStratum >= WC_MAX_SINT_OF(int)) + return SEQ_OVERFLOW_E; + rng->RBGCStratum = seedRng->RBGCStratum + 1; + } +#endif #ifdef WOLFSSL_HEAP_TEST rng->heap = (void*)WOLFSSL_HEAP_TEST; @@ -2980,106 +3029,175 @@ int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) #endif /* WC_RNG_HAVE_LOCK */ +#ifdef WC_RNG_HAVE_RBGC -#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) - -/* Unified mechanics for the four wc_InitRng*RBGC() APIs: instantiate a leaf - * DRBG subordinate to root in an SP 800-90C RBG chain, drawing its seed - * material from root's generate function in place of the module's seed - * source; every other aspect of instantiation -- seed byte accounting, - * health testing, nonce handling, failure disposition -- is _InitRng()'s, - * identically to wc_InitRngNonce_ex(). +/* Unified mechanics for the four wc_InitRng*RBGC() APIs: instantiate a child + * DRBG subordinate to parent in an SP 800-90C RBG chain, drawing its seed + * material from parent's generate function in place of the module's seed + * source; every other aspect of instantiation -- seed byte accounting, nonce + * handling, failure disposition -- is _InitRng()'s, identically to + * wc_InitRngNonce_ex() with the omission of health testing, which is + * superfluous when a healthy DRBG generates the seed data. * - * Exactly one of new_leaf_stack (caller-provided WC_RNG, uninitialized) and - * new_leaf_heap (callee-allocated from root's heap, to be released with - * wc_rng_free()) must be non-NULL. The caller must hold exclusive access - * to root for the duration of the call, as for all WC_RNG operations; the - * spawn debits root's reseed counter by one generate. + * SP 800-90C accounting: the child's claimable security strength is capped by + * the parent's, and the child has no formal prediction resistance. The child's + * own reseeds default to the module's seed source (the reseed-interval + * backstop, wc_RNG_DRBG_Reseed_Now()); wc_RNG_DRBG_ReseedRBGC() reseeds it from + * a supplied root instead. In configurations with no DRBG (RDRAND et al.), the + * child comes up as _InitRng() dictates for such configurations and the parent + * is not consulted. * - * SP 800-90C accounting: the leaf's claimable security strength is capped - * by root's, and the leaf has no prediction resistance. The leaf's own - * reseeds default to the module's seed source (the reseed-interval - * backstop, wc_RNG_DRBG_Reseed_Now()); wc_RNG_DRBG_ReseedRBGC() reseeds it - * from root instead. Chains are depth-one BY POLICY, with programmatic - * enforcement: a leaf is tagged (WC_RNG.isRbgcLeaf, sticky for the - * instance's lifetime even across source reseeds) and is rejected as a - * root by every RBGC API. In configurations with no DRBG (RDRAND et al.), - * the leaf comes up as _InitRng() dictates for such configurations and - * root is not consulted. */ -static int SpawnRngRBGC(WC_RNG* new_leaf_stack, WC_RNG** new_leaf_heap, - WC_RNG* root, byte* nonce, word32 nonceSz) + * Exactly one of new_child_stack (caller-provided WC_RNG, uninitialized) and + * new_child_heap (callee-allocated from parent's heap, to be released with + * wc_rng_free()) must be non-NULL. The caller must hold exclusive access + * to parent for the duration of the call, as for all WC_RNG operations; the + * spawn debits parent's reseed counter by one generate. + */ +static int SpawnRngRBGC(WC_RNG* new_child_stack, WC_RNG** new_child_heap, + WC_RNG* parent, const byte* nonce, word32 nonceSz, + word32 flags) { - WC_RNG* leaf = new_leaf_stack; + WC_RNG* child = new_child_stack; int ret; -#ifdef WC_USE_DEVID - int devId = WC_USE_DEVID; -#else - int devId = INVALID_DEVID; -#endif - if ((root == NULL) || - ((new_leaf_stack == NULL) == (new_leaf_heap == NULL)) || - (new_leaf_stack == root)) - { + if (parent == NULL) + return BAD_FUNC_ARG; + + if ((new_child_stack == NULL) == (new_child_heap == NULL)) + return BAD_FUNC_ARG; + + if (new_child_stack == parent) return BAD_FUNC_ARG; - } - /* Depth-one chains only, by policy: a leaf is never a root. */ - if (root->isRbgcLeaf) + if ((nonce == NULL) && (nonceSz > 0)) return BAD_FUNC_ARG; - if (new_leaf_heap != NULL) { - leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); - if (leaf == NULL) + if (new_child_heap != NULL) { + *new_child_heap = (WC_RNG*)XMALLOC(sizeof(WC_RNG), parent->heap, DYNAMIC_TYPE_RNG); + if (*new_child_heap == NULL) return MEMORY_E; + child = *new_child_heap; } - ret = _InitRng(leaf, nonce, nonceSz, root->heap, devId, root, - WC_RNG_INIT_FLAGS_NONE); + ret = _InitRng(child, nonce, nonceSz, parent->heap, + #if defined(WOLF_CRYPTO_CB) + parent->devId, + #else + INVALID_DEVID, + #endif + parent, flags); - if (new_leaf_heap != NULL) { + if (new_child_heap != NULL) { if (ret != 0) { - XFREE(leaf, root->heap, DYNAMIC_TYPE_RNG); - leaf = NULL; + XFREE(child, parent->heap, DYNAMIC_TYPE_RNG); + *new_child_heap = child = NULL; } - *new_leaf_heap = leaf; } return ret; } -int wc_InitRngRBGC(WC_RNG* leaf, WC_RNG* root) +int wc_InitRngRBGC(WC_RNG* child, WC_RNG* parent, word32 flags) { - return SpawnRngRBGC(leaf, NULL, root, NULL, 0); + return SpawnRngRBGC(child, NULL, parent, NULL, 0, flags); } -int wc_InitRngNonceRBGC(WC_RNG* leaf, WC_RNG* root, byte* nonce, - word32 nonceSz) +int wc_InitRngNonceRBGC(WC_RNG* child, WC_RNG* parent, const byte* nonce, + word32 nonceSz, word32 flags) { - return SpawnRngRBGC(leaf, NULL, root, nonce, nonceSz); + return SpawnRngRBGC(child, NULL, parent, nonce, nonceSz, flags); } #ifndef WC_NO_CONSTRUCTORS -int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root) +int wc_InitRngRBGC_New(WC_RNG** child, WC_RNG* parent, word32 flags) { - return SpawnRngRBGC(NULL, leaf, root, NULL, 0); + return SpawnRngRBGC(NULL, child, parent, NULL, 0, flags); } -int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, byte* nonce, - word32 nonceSz) +int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, + word32 nonceSz, word32 flags) { - return SpawnRngRBGC(NULL, leaf, root, nonce, nonceSz); + return SpawnRngRBGC(NULL, child, parent, nonce, nonceSz, flags); } #endif /* !WC_NO_CONSTRUCTORS */ -/* PollAndReSeed() and wc_RNG_GenerateBlock() form a single-cycle recursion when - * a seedRng is passed to PollAndReSeed() by the RBGC chain APIs. - * wc_RNG_GenerateBlock() itself never passes a seedRng, ending the cycle - * immediately. +/* Immediately reseed rng from root's generate output -- the reseed counterpart + * of the wc_InitRng*RBGC() spawn. The reseed counter is reset iff the reseed + * succeeds and credited. The nonce, if any, rides the same reseed derivation + * as (uncredited) additional input. The caller must hold exclusive access to + * BOTH rng and root. On credited success, rng is (or remains) a chain RNG: + * its current seed period is chain-backed, so RBGCStratum is set, and it is not + * usable as a reseed root. */ -/* NOLINTNEXTLINE(misc-no-recursion) */ +static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, const byte* nonce, + word32 nonceSz, int credited) +{ +#ifdef WOLFSSL_SMALL_STACK_CACHE + byte *seed; +#else + byte seed[SEED_SZ]; +#endif + int ret; + + if ((rng == NULL) || (root == NULL) || (rng == root) || + ((nonce == NULL) && (nonceSz > 0))) + { + return BAD_FUNC_ARG; + } + ret = rng_lock_required_check(rng); + if (ret != 0) + return ret; + +#ifdef WOLFSSL_SMALL_STACK_CACHE + seed = rng->newSeed_buf; +#endif + + /* Reseed from root only, by policy. */ + if (root->RBGCStratum > 0) + return BAD_FUNC_ARG; + + if (rng->status != DRBG_OK) + return RNG_FAILURE_E; + + if (! wc_RNG_DRBG_Present(rng)) { + return 0; + } + + ret = wc_RNG_GenerateBlock(root, seed, SEED_SZ); + if (ret == 0) { + if (credited) { + ret = wc_RNG_DRBG_Reseed_Nonce(rng, seed, SEED_SZ, nonce, nonceSz); + if (ret == 0) { + rng->RBGCStratum = root->RBGCStratum + 1; + } + } + else { + ret = wc_RNG_DRBG_Reseed_Nonce_Uncredited(rng, seed, SEED_SZ, nonce, nonceSz); + } + } + ForceZero(seed, SEED_SZ); + + return ret; +} + +int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, + word32 nonceSz) +{ + return wc_RNG_DRBG_ReseedRBGC_local(rng, root, nonce, nonceSz, 1); +} + +int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, WC_RNG* root, const byte* nonce, + word32 nonceSz) +{ + return wc_RNG_DRBG_ReseedRBGC_local(rng, root, nonce, nonceSz, 0); +} + +#endif /* WC_RNG_HAVE_RBGC */ + +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) + static int PollAndReSeed(WC_RNG* rng, const byte* additional, - word32 additionalSz, WC_RNG* seedRng) + word32 additionalSz) { int ret = WC_NO_ERR_TRACE(DRBG_NEED_RESEED); int devId = INVALID_DEVID; @@ -3099,17 +3217,6 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, ret = DRBG_SUCCESS; #endif if (ret == DRBG_SUCCESS) { - if (seedRng != NULL) { - /* RBGC reseed (wc_RNG_DRBG_ReseedRBGC()): draw the seed - * material from the parent DRBG's generate function in place - * of the module's seed source; all subsequent handling is - * identical to the seed-source path. */ - ret = wc_RNG_GenerateBlock(seedRng, newSeed, - SEED_SZ + SEED_BLOCK_SZ); - if (ret != 0) - ret = DRBG_FAILURE; - } - else { #ifdef WC_RNG_SEED_CB if (seedCb == NULL) { ret = DRBG_NO_SEED_CB; @@ -3136,7 +3243,6 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, ret = DRBG_FAILURE; } #endif - } } if (ret == DRBG_SUCCESS) { ret = wc_RNG_TestSeed(newSeed, SEED_SZ + SEED_BLOCK_SZ); @@ -3150,6 +3256,11 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, if (ret == DRBG_SUCCESS) { ret = Hash_DRBG_Reseed(rng, newSeed + SEED_BLOCK_SZ, SEED_SZ, additional, additionalSz, 1 /* credited */); + + #ifdef WC_RNG_HAVE_RBGC + if (ret == 0) + rng->RBGCStratum = 0; + #endif } #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE) if (newSeed != NULL) { @@ -3208,7 +3319,7 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) return 0; } - ret = PollAndReSeed(rng, nonce, nonceSz, NULL); + ret = PollAndReSeed(rng, nonce, nonceSz); /* Identical outcome mapping to the generate-path reseed. */ if (ret == DRBG_SUCCESS) { @@ -3226,62 +3337,6 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) return ret; } -/* Immediately reseed leaf from root's generate output -- the reseed - * counterpart of the wc_InitRng*RBGC() spawn, with identical semantics to - * wc_RNG_DRBG_Reseed_Now() except for the seed source: the drawn material is - * health-tested and applied by the module's own reseed function, the reseed - * counter is reset iff the reseed succeeds, and a nonce rides the same - * reseed derivation as (uncredited) additional input. The caller must hold - * exclusive access to BOTH leaf and root. On success leaf is (or remains) a - * chain leaf: its current seed period is chain-backed, so isRbgcLeaf is set - * and it is not usable as a root. Depth-one policy applies: root must not - * itself be a leaf. */ -int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, const byte* nonce, - word32 nonceSz) -{ - int ret; - - if ((leaf == NULL) || (root == NULL) || (leaf == root) || - ((nonce == NULL) && (nonceSz > 0))) - { - return BAD_FUNC_ARG; - } - ret = rng_lock_required_check(leaf); - if (ret != 0) - return ret; - - /* Depth-one chains only, by policy: a leaf is never a root. */ - if (root->isRbgcLeaf) - return BAD_FUNC_ARG; - - /* Mirror wc_RNG_GenerateBlock(): only an in-service DRBG may reseed. */ - if (leaf->status != DRBG_OK) - return RNG_FAILURE_E; - - if (! wc_RNG_DRBG_Present(leaf)) { - /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ - return 0; - } - - ret = PollAndReSeed(leaf, nonce, nonceSz, root); - - /* Identical outcome mapping to the generate-path reseed. */ - if (ret == DRBG_SUCCESS) { - leaf->isRbgcLeaf = 1; - ret = 0; - } - else if (ret == WC_NO_ERR_TRACE(DRBG_CONT_FAILURE)) { - ret = DRBG_CONT_FIPS_E; - leaf->status = DRBG_CONT_FAILED; - } - else { - ret = RNG_FAILURE_E; - leaf->status = DRBG_FAILED; - } - - return ret; -} - #ifdef WC_RNG_HAVE_NEXT_SEED /* Banked-next-seed services. _NextSeedGenerate() banks up to n more @@ -3324,26 +3379,42 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, const byte* nonce, /* Locate the aperture members for rng's live DRBG. Returns nonzero when no * DRBG is instantiated (RDRAND et al.). */ -static int NextSeedPtrs(WC_RNG* rng, byte** seed, wolfSSL_Atomic_Int** len) +static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, wolfSSL_Atomic_Int** len, int **nextSeedRBGCStratum) { #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { *seed = ((DRBG_internal*)rng->drbg)->nextSeed; + *nextSeedSz = (word32)sizeof(((DRBG_internal*)rng->drbg)->nextSeed); *len = &((DRBG_internal*)rng->drbg)->nextSeedLen; + if (nextSeedRBGCStratum) { + #ifdef WC_RNG_HAVE_RBGC + *nextSeedRBGCStratum = &((DRBG_internal*)rng->drbg)->nextSeedRBGCStratum; + #else + *nextSeedRBGCStratum = NULL; + #endif + } return 0; } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { *seed = ((DRBG_SHA512_internal*)rng->drbg512)->nextSeed; + *nextSeedSz = (word32)sizeof(((DRBG_SHA512_internal*)rng->drbg512)->nextSeed); *len = &((DRBG_SHA512_internal*)rng->drbg512)->nextSeedLen; + if (nextSeedRBGCStratum) { + #ifdef WC_RNG_HAVE_RBGC + *nextSeedRBGCStratum = &((DRBG_SHA512_internal*)rng->drbg512)->nextSeedRBGCStratum; + #else + *nextSeedRBGCStratum = NULL; + #endif + } return 0; } #endif return MISSING_RNG_E; } -/* Bank up to n more bytes of seed material from the module's seed source into +/* Bank up to n more bytes of seed material from a supplied root RNG into * rng's next-seed bank. Callable without owning the instance (the scheduling * daemon's entry point); deliberately independent of rng->status so that * banking can proceed for any instantiated DRBG. n is clamped to the space @@ -3352,67 +3423,138 @@ static int NextSeedPtrs(WC_RNG* rng, byte** seed, wolfSSL_Atomic_Int** len) * published; a failed test consumes the material (use-once) and returns the * test's error, leaving an empty bank for the next cycle. A gather failure * leaves the partial bank intact for retry. */ -int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) +static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 n) { byte* seed; wolfSSL_Atomic_Int* lenp; + int *nextSeedRBGCStratum_p = NULL; WC_ATOMIC_INT_ARG cur; + word32 nextSeedSz; int ret; if ((rng == NULL) || (n == 0)) return BAD_FUNC_ARG; - if (NextSeedPtrs(rng, &seed, &lenp) != 0) { + /* Note, rng need not be locked -- that's the whole point of the + * banked-next-seed aperture protocol. + */ + + if (root) { +#ifdef WC_RNG_HAVE_RBGC + if (root->RBGCStratum > 0) + return BAD_FUNC_ARG; + ret = rng_lock_required_check(root); + if (ret != 0) + return ret; +#else + return NOT_COMPILED_IN; +#endif + } + + ret = NextSeedPtrs(rng, &seed, &nextSeedSz, &lenp, &nextSeedRBGCStratum_p); + if (ret != 0) { /* No DRBG instantiated -- nothing to bank (RDRAND et al.). */ - return BAD_FUNC_ARG; + return ret; } cur = *lenp; - if ((cur < 0) || (cur >= (WC_ATOMIC_INT_ARG)WC_DRBG_NEXT_SEED_LEN)) { - if (cur != (WC_ATOMIC_INT_ARG)WC_DRBG_NEXT_SEED_LEN) { + if ((cur < 0) || (cur >= (WC_ATOMIC_INT_ARG)nextSeedSz)) { + + if (cur != (WC_ATOMIC_INT_ARG)nextSeedSz) { /* Ready, consuming, or other sentinel -- nothing to do. */ - return ALREADY_E; + return WC_NO_ERR_TRACE(ALREADY_E); /* not an error */ } /* Complete but unpublished (interrupted between fill completion and * publication): retry the health test and publication below. */ n = 0; } - else if (n > WC_DRBG_NEXT_SEED_LEN - (word32)cur) { - n = WC_DRBG_NEXT_SEED_LEN - (word32)cur; - } if (n > 0) { - /* wc_GenerateSeed() must be called completely independent of rng, aside - * from the memory aperture itself. For safety, we pass a dummy - * OS_Seed, which will be ignored by the wc_GenerateSeed() typically - * used in conjunction with WC_RNG_HAVE_NEXT_SEED. - */ - struct OS_Seed os; +#ifdef WC_RNG_HAVE_RBGC + if (root) { + /* If primary seed bytes were carried forward, reset now to avoid + * wc_RNG_TestSeed() at completion. */ + if ((cur > 0) && (*nextSeedRBGCStratum_p == 0)) { + cur = 0; + WOLFSSL_ATOMIC_STORE(*lenp, cur); + } + + if (n > nextSeedSz - (word32)cur) + n = nextSeedSz - (word32)cur; + + ret = wc_RNG_GenerateBlock(root, seed + cur, n); + if (ret != 0) { + /* Partial bank preserved -- retry on a later cycle. */ + return ret; + } - /* Named-member init: layout-proof against OS_Seed growing or - * reordering members under its several config axes. */ - XMEMSET(&os, 0, sizeof(os)); + /* If RBGC seed bytes were carried forward, make sure we're + * pessimistic about the RBGC stratum. */ + if ((cur == 0) || (*nextSeedRBGCStratum_p < root->RBGCStratum + 1)) + *nextSeedRBGCStratum_p = root->RBGCStratum + 1; + } + else +#endif /* WC_RNG_HAVE_RBGC */ + { + /* wc_GenerateSeed() must be called completely independent of rng, aside + * from the memory aperture itself. For safety, we pass a dummy + * OS_Seed, which will be ignored by the wc_GenerateSeed() typically + * used in conjunction with WC_RNG_HAVE_NEXT_SEED. + */ + struct OS_Seed os; + + /* Named-member init: layout-proof against OS_Seed growing or + * reordering members under its several config axes. */ + XMEMSET(&os, 0, sizeof(os)); #ifndef USE_WINDOWS_API - os.fd = -1; + os.fd = -1; #endif #ifdef WOLF_CRYPTO_CB - os.devId = INVALID_DEVID; + os.devId = INVALID_DEVID; #endif - ret = wc_GenerateSeed(&os, seed + cur, n); - if (ret != 0) { - /* Partial bank preserved -- retry on a later cycle. */ - return ret; +#ifdef WC_RNG_HAVE_RBGC + /* If RBGC seed bytes were carried forward, reset now to avoid + * intermixture and force wc_RNG_TestSeed() at completion. */ + if ((cur > 0) && (*nextSeedRBGCStratum_p > 0)) { + cur = 0; + WOLFSSL_ATOMIC_STORE(*lenp, cur); + } +#endif + + if (n > nextSeedSz - (word32)cur) + n = nextSeedSz - (word32)cur; + + ret = wc_GenerateSeed(&os, seed + cur, n); + if (ret != 0) { + /* Partial bank preserved -- retry on a later cycle. */ + return ret; + } + +#ifdef WC_RNG_HAVE_RBGC + *nextSeedRBGCStratum_p = 0; +#endif } - cur = wolfSSL_Atomic_Int_AddFetch(lenp, (WC_ATOMIC_INT_ARG)n); + + cur += (int)n; + WOLFSSL_ATOMIC_STORE(*lenp, cur); } - if (cur == (WC_ATOMIC_INT_ARG)WC_DRBG_NEXT_SEED_LEN) { + if (cur == (WC_ATOMIC_INT_ARG)nextSeedSz) { +#ifdef WC_RNG_HAVE_RBGC + /* If RBGC bytes were used for the reseed, then we can skip + * wc_RNG_TestSeed(). */ + if (*nextSeedRBGCStratum_p > 0) { + WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + return 0; + } +#endif /* Bank complete: health-test now, in advance of consumption, so * that wc_RNG_DRBG_NextSeedNow() is pure computation. */ - ret = wc_RNG_TestSeed(seed, WC_DRBG_NEXT_SEED_LEN); + ret = wc_RNG_TestSeed(seed, nextSeedSz); if (ret == 0) { WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + return 0; } else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) { /* wc_RNG_TestSeed() did nothing with the data -- not @@ -3425,7 +3567,7 @@ int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) /* Use-once: a failed test consumes the material. Release * store: the ForceZero() must be visible before the empty * aperture is. */ - ForceZero(seed, WC_DRBG_NEXT_SEED_LEN); + ForceZero(seed, nextSeedSz); WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); return ret; } @@ -3438,6 +3580,18 @@ int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) return 0; } +#ifdef WC_RNG_HAVE_RBGC +int wc_RNG_DRBG_NextSeedGenerate_RBGC(WC_RNG* rng, WC_RNG *root, word32 n) { + if (root == NULL) + return BAD_FUNC_ARG; + return wc_RNG_DRBG_NextSeedGenerate_local(rng, root, n); +} +#endif + +int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) { + return wc_RNG_DRBG_NextSeedGenerate_local(rng, NULL, n); +} + /* Report the raw aperture value: a racy snapshot by design. Values in [0, bank * length) count banked bytes; WC_DRBG_NEXT_SEED_READY and * WC_DRBG_NEXT_SEED_CONSUMING indicate a ready or in-consumption bank, @@ -3446,11 +3600,12 @@ int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng, WC_ATOMIC_INT_ARG* n) { byte* seed; wolfSSL_Atomic_Int* lenp; + word32 nextSeedSz; if ((rng == NULL) || (n == NULL)) return BAD_FUNC_ARG; - if (NextSeedPtrs(rng, &seed, &lenp) != 0) { + if (NextSeedPtrs(rng, &seed, &nextSeedSz, &lenp, NULL) != 0) { *n = WC_DRBG_NEXT_SEED_EMPTY; return 0; } @@ -3459,25 +3614,26 @@ int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng, WC_ATOMIC_INT_ARG* n) return 0; } -/* Consume a ready next-seed bank in an immediate credited reseed. The - * caller must own the instance. Source-free by construction -- the - * material was gathered from the module's seed source and health-tested at - * bank time -- so consumption is pure computation and safe in atomic - * context: the one credited reseed shape with that property. Distinct - * protocol results: NOT_READY_E when no bank is ready (nothing consumed), - * MISSING_RNG_E when the instance has no DRBG (RDRAND et al.) -- both - * deliberately loud, so a direct caller must demonstrate it understands - * the instance it holds. (The WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED - * checkout arm is return-agnostic by construction and needs neither.) - * Use-once: the bank is consumed by the attempt, success or failure. Note - * that a banked reseed can never provide SP 800-90 prediction resistance - * (the material predates the request by construction); - * wc_RNG_DRBG_Reseed_Now() remains the live-gather shape. */ +/* Consume a ready next-seed bank in an immediate credited reseed. The caller + * must own the instance. Source-free by construction -- the material was + * gathered from the module's seed source and health-tested at bank time -- so + * consumption is pure computation and safe in atomic context: the one credited + * primary reseed shape with that property. Distinct protocol results: + * NOT_READY_E when no bank is ready (nothing consumed), MISSING_RNG_E when the + * instance has no DRBG (RDRAND et al.) -- both deliberately loud, so a direct + * caller must demonstrate it understands the instance it holds. (The + * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED checkout arm is return-agnostic by + * construction and needs neither.) Use-once: the bank is consumed by the + * attempt, success or failure. Note that a banked reseed can never provide SP + * 800-90 prediction resistance (the material predates the request by + * construction); wc_RNG_DRBG_Reseed_Now() remains the live-gather shape. */ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, word32 nonceSz) { byte* seed; wolfSSL_Atomic_Int* lenp; + word32 nextSeedSz; + int *nextSeedRBGCStratum_p; WC_ATOMIC_INT_ARG expected = WC_DRBG_NEXT_SEED_READY; int ret; @@ -3495,7 +3651,7 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, if (rng->status != DRBG_OK) return RNG_FAILURE_E; - ret = NextSeedPtrs(rng, &seed, &lenp); + ret = NextSeedPtrs(rng, &seed, &nextSeedSz, &lenp, &nextSeedRBGCStratum_p); if (ret != 0) { /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ return ret; @@ -3513,6 +3669,14 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, ret = Hash_DRBG_Reseed(rng, seed + SEED_BLOCK_SZ, SEED_SZ, nonce, nonceSz, 1 /* credited */); + + #ifdef WC_RNG_HAVE_RBGC + if (ret == 0) { + rng->RBGCStratum = *nextSeedRBGCStratum_p; + *nextSeedRBGCStratum_p = 0; + } + #endif + /* Use-once: consumed by the attempt, success or not. Release store: * the ForceZero() must be visible before the empty aperture is. */ ForceZero(seed, WC_DRBG_NEXT_SEED_LEN); @@ -3617,7 +3781,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) if (rng->pid != getpid()) { rng->pid = getpid(); - ret = PollAndReSeed(rng, NULL, 0, NULL); + ret = PollAndReSeed(rng, NULL, 0); if (ret != DRBG_SUCCESS) { rng->status = DRBG_FAILED; return RNG_FAILURE_E; @@ -3630,7 +3794,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, NULL, 0); if (ret == WC_NO_ERR_TRACE(DRBG_NEED_RESEED)) { - ret = PollAndReSeed(rng, NULL, 0, NULL); + ret = PollAndReSeed(rng, NULL, 0); if (ret == DRBG_SUCCESS) ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, NULL, 0); @@ -3643,7 +3807,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) ret = Hash512_DRBG_Generate((DRBG_SHA512_internal *)rng->drbg512, output, sz, NULL, 0); if (ret == WC_NO_ERR_TRACE(DRBG_NEED_RESEED)) { - ret = PollAndReSeed(rng, NULL, 0, NULL); + ret = PollAndReSeed(rng, NULL, 0); if (ret == DRBG_SUCCESS) ret = Hash512_DRBG_Generate( (DRBG_SHA512_internal *)rng->drbg512, output, sz, diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index b6f5465e11f..90bc1ff796e 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -49,6 +49,10 @@ WOLFSSL_API int wc_rng_bank_init_nonce( int i; int ret; int need_reenable_vec = 0; +#ifdef WC_RNG_HAVE_RBGC + WC_RNG root; + int root_inited = 0; +#endif if ((ctx == NULL) || (n_rngs <= 0)) return BAD_FUNC_ARG; @@ -77,8 +81,16 @@ WOLFSSL_API int wc_rng_bank_init_nonce( ret = MEMORY_E; #endif +#ifdef WC_RNG_HAVE_RBGC + if ((ret == 0) && (flags & WC_RNG_BANK_FLAG_INIT_RBGC)) { + ret = wc_InitRngNonce_ex(&root, nonce, nonceSz, heap, devId); + if (ret == 0) + root_inited = 1; + } +#else (void)nonce; (void)nonceSz; +#endif if (ret == 0) { XMEMSET(ctx->rngs, 0, sizeof(*ctx->rngs) * (size_t)n_rngs); @@ -98,6 +110,21 @@ WOLFSSL_API int wc_rng_bank_init_nonce( if (flags & WC_RNG_BANK_FLAG_NO_VECTOR_OPS) need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0); +#ifdef WC_RNG_HAVE_RBGC + if (flags & WC_RNG_BANK_FLAG_INIT_RBGC) { + ret = wc_InitRngNonceRBGC( + WC_RNG_BANK_INST_TO_RNG(rng_inst), + &root, + (byte *)&rng_inst, sizeof(byte *) +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + , WC_RNG_INIT_FLAGS_LOCK_REQUIRED +#else + , WC_RNG_INIT_FLAGS_NONE +#endif + ); + } + else +#endif { #ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED ret = wc_InitRngNonce_ex2( @@ -174,6 +201,10 @@ WOLFSSL_API int wc_rng_bank_init_nonce( if (ret != 0) (void)wc_rng_bank_fini(ctx); +#ifdef WC_RNG_HAVE_RBGC + if (root_inited) + wc_FreeRng(&root); +#endif return ret; } @@ -1136,19 +1167,28 @@ WOLFSSL_API int wc_rng_bank_inst_checkin( #define WC_RNG_BANK_INST_OP_DAEMON ((WC_ATOMIC_INT_ARG)1) #define WC_RNG_BANK_INST_OP_REINIT ((WC_ATOMIC_INT_ARG)2) -WOLFSSL_API int wc_rng_bank_next_seed_generate( +static int wc_rng_bank_next_seed_generate_local( struct wc_rng_bank *bank, int inst_offset, - word32 n) + word32 n, + WC_RNG *root) { int ret; WC_ATOMIC_INT_ARG expected = 0; - if ((bank == NULL) || (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || - (inst_offset < 0) || (inst_offset >= bank->n_rngs)) - { + if (bank == NULL) return BAD_FUNC_ARG; - } + if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) + return BAD_FUNC_ARG; + if (inst_offset < 0) + return BAD_FUNC_ARG; + if (inst_offset >= bank->n_rngs) + return BAD_FUNC_ARG; + +#ifndef WC_RNG_HAVE_RBGC + if (root != NULL) + return NOT_COMPILED_IN; +#endif if (! wolfSSL_Atomic_Int_CompareExchange(&bank->inst_op_gate, &expected, WC_RNG_BANK_INST_OP_DAEMON)) @@ -1158,14 +1198,42 @@ WOLFSSL_API int wc_rng_bank_next_seed_generate( return BUSY_E; } - ret = wc_RNG_DRBG_NextSeedGenerate( - WC_RNG_BANK_INST_TO_RNG(&bank->rngs[inst_offset]), n); +#ifdef WC_RNG_HAVE_RBGC + if (root != NULL) { + ret = wc_RNG_DRBG_NextSeedGenerate_RBGC( + WC_RNG_BANK_INST_TO_RNG(&bank->rngs[inst_offset]), root, n); + } + else +#endif + { + ret = wc_RNG_DRBG_NextSeedGenerate( + WC_RNG_BANK_INST_TO_RNG(&bank->rngs[inst_offset]), n); + } WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, 0); return ret; } +WOLFSSL_API int wc_rng_bank_next_seed_generate_rbgc( + struct wc_rng_bank *bank, + int inst_offset, + word32 n, + WC_RNG *root) +{ + if (root == NULL) + return BAD_FUNC_ARG; + return wc_rng_bank_next_seed_generate_local(bank, inst_offset, n, root); +} + +WOLFSSL_API int wc_rng_bank_next_seed_generate( + struct wc_rng_bank *bank, + int inst_offset, + word32 n) +{ + return wc_rng_bank_next_seed_generate_local(bank, inst_offset, n, NULL); +} + #endif /* WC_RNG_HAVE_NEXT_SEED */ /* note the rng_inst passed to wc_rng_bank_inst_reinit() must have been obtained @@ -1382,9 +1450,7 @@ WOLFSSL_API int wc_rng_bank_recover_inst( return ret; } - -#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +#ifdef WC_RNG_HAVE_RBGC /* Unified mechanics for wc_rng_bank_spawn() and wc_rng_bank_spawn_new(): * check out -> wc_InitRngNonceRBGC[_New]() -> check in, following the * exactly-one-destination convention of random.c's SpawnRngRBGC(). All @@ -1427,22 +1493,28 @@ static int rng_bank_spawn( if (ret != 0) return ret; + { + word32 child_init_flags = WC_RNG_INIT_FLAGS_NONE; if (leaf_stack != NULL) { ret = wc_InitRngNonceRBGC(leaf_stack, WC_RNG_BANK_INST_TO_RNG(rng_inst), - nonce, nonceSz); + nonce, nonceSz, + child_init_flags + ); } else { #ifndef WC_NO_CONSTRUCTORS ret = wc_InitRngNonceRBGC_New(leaf_heap, WC_RNG_BANK_INST_TO_RNG(rng_inst), - nonce, nonceSz); + nonce, nonceSz, + child_init_flags); #else /* Unreachable: wc_rng_bank_spawn_new() is absent under * WC_NO_CONSTRUCTORS, so leaf_heap is always null here. */ ret = BAD_FUNC_ARG; #endif } + } checkin_ret = wc_rng_bank_inst_checkin(&rng_inst); if ((checkin_ret != 0) && (ret == 0)) { @@ -1491,8 +1563,7 @@ WOLFSSL_API int wc_rng_bank_spawn_new( preferred_inst_offset, timeout_secs, flags); } #endif /* !WC_NO_CONSTRUCTORS */ -#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && - * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ +#endif /* WC_RNG_HAVE_RBGC */ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, word32 seedSz, diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 1ed0d5ab3f2..fc8508c0a3a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -943,6 +943,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void); #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); +#endif +#ifdef WC_RNG_HAVE_RBGC WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); #endif #ifdef WC_RNG_HAVE_NEXT_SEED @@ -2617,6 +2619,8 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_FAIL("RNGSVC test failed!\n", ret); else TEST_PASS("RNGSVC test passed!\n"); +#endif +#ifdef WC_RNG_HAVE_RBGC if ((ret = rng_drbg_rbgc_test()) != 0) TEST_FAIL("RNGRBGC test failed!\n", ret); else @@ -27927,8 +27931,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) int svc_present = 0; #endif -#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +#ifdef WC_RNG_HAVE_RBGC #ifndef WC_NO_CONSTRUCTORS WC_RNG *spawned_rng = NULL; #endif @@ -27946,8 +27949,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); XMEMSET(rng, 0, sizeof(*rng)); #endif -#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +#ifdef WC_RNG_HAVE_RBGC WC_ALLOC_VAR_EX(leaf_rng, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); @@ -28655,25 +28657,22 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) } #endif /* WC_RNG_HAVE_NEXT_SEED */ -#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +#ifdef WC_RNG_HAVE_RBGC /* RBGC spawn: argument and flag contracts */ - if (wc_rng_bank_spawn(bank, NULL, NULL, 0, 0, 0, 0) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_spawn(bank, NULL, NULL, 0, 0, 0, 0); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #ifndef WC_NO_CONSTRUCTORS - if (wc_rng_bank_spawn_new(bank, NULL, NULL, 0, 0, 0, 0) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_spawn_new(bank, NULL, NULL, 0, 0, 0, 0); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #endif - if (wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, - WC_RNG_BANK_FLAG_SEED_UNCREDITED) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, - WC_RNG_BANK_FLAG_FOR_RECOVERY) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_SEED_UNCREDITED); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_FOR_RECOVERY); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); /* nonce-bearing stack spawn: the leaf is a tagged chain leaf, * generates, and is torn down independently of the bank */ @@ -28682,8 +28681,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); leaf_rng_inited = 1; - if (wc_RNG_DRBG_IsRBGCLeaf(leaf_rng) != 1) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + ret = wc_RNG_DRBG_GetRBGCStratum(leaf_rng); + if (ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); +#endif ret = wc_RNG_GenerateBlock(leaf_rng, outbuf1, sizeof(outbuf1)); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28697,16 +28699,59 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ret = wc_rng_bank_spawn_new(bank, &spawned_rng, NULL, 0, 1, 0, 0); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - if ((spawned_rng == NULL) || (wc_RNG_DRBG_IsRBGCLeaf(spawned_rng) != 1)) + if (spawned_rng == NULL) ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + ret = wc_RNG_DRBG_GetRBGCStratum(spawned_rng); + if (ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); +#endif ret = wc_RNG_GenerateBlock(spawned_rng, outbuf1, sizeof(outbuf1)); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); wc_rng_free(spawned_rng); spawned_rng = NULL; #endif /* !WC_NO_CONSTRUCTORS */ -#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && - * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + + /* PR spawn: a fresh credited primary reseed of the parent instance + * immediately before the child's seed draw (the SP 800-90C Sec. 4.1.1 + * pattern). The child is stratum 1; the parent instance's counter + * shows reseed-then-one-draw. */ + ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 10, + WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | + WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + leaf_rng_inited = 1; +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + ret = wc_RNG_DRBG_GetRBGCStratum(leaf_rng); + if (ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); +#endif + ret = wc_RNG_GenerateBlock(leaf_rng, outbuf1, sizeof(outbuf1)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_FreeRng(leaf_rng); + leaf_rng_inited = 0; + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + if (svc_present) { + wc_drbg_reseed_ctr_t ns_ctr; + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, + WC_RNG_BANK_FLAG_NONE); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(rng_inst), &ns_ctr); + if ((ret != 0) || (ns_ctr != 2)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_inst_checkin(&rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } +#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7,0,0) */ +#endif /* WC_RNG_HAVE_RBGC */ /* WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE checkout contracts. * Per-call PR demands CAN_WAIT (the fresh gather may block) and @@ -28883,8 +28928,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ret = WC_TEST_RET_ENC_NC; #endif /* !WC_RNG_BANK_STATIC */ -#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +#ifdef WC_RNG_HAVE_RBGC #ifndef WC_NO_CONSTRUCTORS if (spawned_rng != NULL) wc_rng_free(spawned_rng); @@ -28895,8 +28939,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ret = WC_TEST_RET_ENC_EC(cleanup_ret); } WC_FREE_VAR_EX(leaf_rng, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); -#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && - * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ +#endif /* WC_RNG_HAVE_RBGC */ } return ret; @@ -28938,8 +28981,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); if (wc_RNG_DRBG_Present(NULL) != 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_IsRBGCLeaf(NULL) != 0) +#ifdef WC_RNG_HAVE_RBGC + if (wc_RNG_DRBG_GetRBGCStratum(NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif if (wc_RNG_DRBG_GetReseedCtr(NULL, &c1) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); @@ -28953,8 +28998,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) if (wc_RNG_DRBG_GetReseedCtr(root, NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_IsRBGCLeaf(root) != 0) +#ifdef WC_RNG_HAVE_RBGC + if (wc_RNG_DRBG_GetRBGCStratum(root) != 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif present = wc_RNG_DRBG_Present(root); @@ -29141,8 +29188,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) return ret; } +#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && */ + /* (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + +#ifdef WC_RNG_HAVE_RBGC + +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + /* Coverage for the SP 800-90C RBGC (RBG chain) APIs: spawn, reseed-from- - * root, the leaf tag and accessor, and the sticky depth-one enforcement. + * root, the leaf tag and accessor, and the sticky stratum-one enforcement. * DRBG-internal observations are gated at runtime on wc_RNG_DRBG_Present(), * so the test also passes on RDRAND-shaped instantiations, where the RBGC * APIs are exercised in their degenerate arms. */ @@ -29175,15 +29229,19 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) present = wc_RNG_DRBG_Present(&root); /* spawn argument contracts */ - if (wc_InitRngRBGC(NULL, &root) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_InitRngRBGC(&leaf, NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_InitRngRBGC(&root, &root) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_InitRngRBGC(NULL, &root, WC_RNG_INIT_FLAGS_NONE); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_InitRngRBGC(&leaf, NULL, WC_RNG_INIT_FLAGS_NONE); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_InitRngRBGC(&root, &root, WC_RNG_INIT_FLAGS_NONE); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); #ifndef WC_NO_CONSTRUCTORS - if (wc_InitRngRBGC_New(NULL, &root) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_InitRngRBGC_New(NULL, &root, WC_RNG_INIT_FLAGS_NONE); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); #endif /* spawn a leaf; the spawn debits root's counter; the leaf is tagged */ @@ -29192,7 +29250,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - api_ret = wc_InitRngRBGC(&leaf, &root); + api_ret = wc_InitRngRBGC(&leaf, &root, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; @@ -29200,33 +29258,235 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) api_ret = wc_RNG_DRBG_GetReseedCtr(&root, &c2); if ((api_ret != 0) || (c2 <= c1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* the spawn draw is one fully-served generate on the parent */ + } + api_ret = wc_RNG_DRBG_GetRBGCStratum(&leaf); + if (api_ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (present) { + /* chain-provenance accounting: bytes generated at stratum 1 count + * in both the total and the RBGC ledgers */ } - if (wc_RNG_DRBG_IsRBGCLeaf(&leaf) != 1) + + api_ret = wc_RNG_DRBG_ReseedRBGC(&root, &leaf, NULL, 0); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* reseed-from-root, without and with a nonce; counter resets */ + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, matter, 16); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(&leaf, &c1); + if ((api_ret != 0) || (c1 != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* target: two credited chain reseeds; source: two fully-served + * seed draws */ + } + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &leaf, NULL, 0); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_ReseedRBGC(NULL, &root, NULL, 0); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, NULL, 7); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* The RBGC stratum is reset to zero by a primary source reseed. */ + api_ret = wc_RNG_DRBG_ScheduleReseed(&leaf); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + ret = wc_RNG_DRBG_GetRBGCStratum(&leaf); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); + if (present) { + /* the primary reseed precedes the byte production, so the served + * bytes are not chain-provenance */ + } + +#if !defined(WC_NO_CONSTRUCTORS) + /* chain-reseeding a source-born instance demotes it, one-way */ + api_ret = wc_InitRng(&extra); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + extra_inited = 1; + api_ret = wc_RNG_DRBG_GetRBGCStratum(&extra); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + if (present) { + api_ret = wc_RNG_DRBG_ReseedRBGC(&extra, &root, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&extra); + if (api_ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + /* long-chained init is allowed, only chained reseed is forbidden. */ + ret = wc_InitRngRBGC_New(&pleaf, &extra, WC_RNG_INIT_FLAGS_NONE); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if ((pleaf == NULL) || (wc_RNG_DRBG_GetRBGCStratum(pleaf) != 2)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + wc_rng_free(pleaf); + pleaf = NULL; + } + + /* heap-allocated leaves, without and with a nonce */ + api_ret = wc_InitRngRBGC_New(&pleaf, &root, WC_RNG_INIT_FLAGS_NONE); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if ((pleaf == NULL) || (wc_RNG_DRBG_GetRBGCStratum(pleaf) != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_IsRBGCLeaf(&root) != 0) + api_ret = wc_RNG_GenerateBlock(pleaf, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + wc_rng_free(pleaf); + pleaf = NULL; + api_ret = wc_InitRngNonceRBGC_New(&pleaf, &root, matter, 16, + WC_RNG_INIT_FLAGS_NONE); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if ((pleaf == NULL) || (wc_RNG_DRBG_GetRBGCStratum(pleaf) != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); + wc_rng_free(pleaf); + pleaf = NULL; +#endif /* !WC_NO_CONSTRUCTORS */ + + /* nonce-bearing stack spawn */ + api_ret = wc_FreeRng(&leaf); + leaf_inited = 0; + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_InitRngNonceRBGC(&leaf, &root, matter, 16, + WC_RNG_INIT_FLAGS_NONE); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + leaf_inited = 1; + api_ret = wc_RNG_DRBG_GetRBGCStratum(&leaf); + if (api_ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + +out: + + { + int cleanup_ret; + if (pleaf != NULL) + wc_rng_free(pleaf); + if (leaf_inited) { + cleanup_ret = wc_FreeRng(&leaf); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (extra_inited) { + cleanup_ret = wc_FreeRng(&extra); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (root_inited) { + cleanup_ret = wc_FreeRng(&root); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + } + + return ret; +} + +#else /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ + +#ifndef WC_RNG_BANK_SUPPORT + /* needed for compat setup */ + #define WC_RNG_BANK_SUPPORT + #include + #undef WC_RNG_BANK_SUPPORT +#endif + +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) +{ + wc_test_ret_t ret = 0; + int api_ret; + int present; + int root_inited = 0; + int leaf_inited = 0; + int extra_inited = 0; + WC_RNG root; + WC_RNG leaf; + WC_RNG extra; + WC_RNG* pleaf = NULL; + wc_drbg_reseed_ctr_t c1 = 0; + wc_drbg_reseed_ctr_t c2 = 0; + byte buf[32]; + byte matter[32]; + + WOLFSSL_ENTER("rng_drbg_rbgc_test"); + + XMEMSET(matter, 0xa5, sizeof(matter)); + + api_ret = wc_InitRng(&root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + root_inited = 1; + + present = wc_RNG_DRBG_Present(&root); - /* depth-one enforcement: a leaf is never a root */ - if (wc_InitRngRBGC(&extra, &leaf) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + /* spawn argument contracts */ + if (wc_InitRngRBGC(NULL, &root, WC_RNG_INIT_FLAGS_NONE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); -#ifndef WC_NO_CONSTRUCTORS - if (wc_InitRngRBGC_New(&pleaf, &leaf) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + if (wc_InitRngRBGC(&leaf, NULL, WC_RNG_INIT_FLAGS_NONE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_InitRngRBGC(&root, &root, WC_RNG_INIT_FLAGS_NONE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (pleaf != NULL) +#ifndef WC_NO_CONSTRUCTORS + if (wc_InitRngRBGC_New(NULL, &root, WC_RNG_INIT_FLAGS_NONE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif - if (wc_RNG_DRBG_ReseedRBGC(&root, &leaf, NULL, 0) != + + /* spawn a leaf; the spawn debits root's counter; the leaf is tagged */ + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(&root, &c1); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + api_ret = wc_InitRngRBGC(&leaf, &root, WC_RNG_INIT_FLAGS_NONE); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + leaf_inited = 1; + if (present) { + api_ret = wc_RNG_DRBG_GetReseedCtr(&root, &c2); + if ((api_ret != 0) || (c2 <= c1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + if (wc_RNG_DRBG_ReseedRBGC(&root, &leaf) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + #endif /* reseed-from-root, without and with a nonce; counter resets */ - api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, NULL, 0); + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, matter, 16); + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if (present) { @@ -29234,25 +29494,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if ((api_ret != 0) || (c1 != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - if (wc_RNG_DRBG_ReseedRBGC(&leaf, &leaf, NULL, 0) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_ReseedRBGC(NULL, &root, NULL, 0) != + if (wc_RNG_DRBG_ReseedRBGC(&leaf, &leaf) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_RNG_DRBG_ReseedRBGC(&leaf, &root, NULL, 7) != + if (wc_RNG_DRBG_ReseedRBGC(NULL, &root) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - /* the leaf tag is sticky across a source reseed */ + /* The RGBC stratum is reset to zero by a primary source reseed. */ api_ret = wc_RNG_DRBG_ScheduleReseed(&leaf); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if (wc_RNG_DRBG_IsRBGCLeaf(&leaf) != 1) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); #if !defined(WC_NO_CONSTRUCTORS) /* chain-reseeding a source-born instance demotes it, one-way */ @@ -29260,34 +29515,36 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); extra_inited = 1; - if (wc_RNG_DRBG_IsRBGCLeaf(&extra) != 0) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); if (present) { - api_ret = wc_RNG_DRBG_ReseedRBGC(&extra, &root, NULL, 0); + api_ret = wc_RNG_DRBG_ReseedRBGC(&extra, &root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if (wc_RNG_DRBG_IsRBGCLeaf(&extra) != 1) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_InitRngRBGC_New(&pleaf, &extra) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + /* long-chained init is allowed, only chained reseed is forbidden. */ + ret = wc_InitRngRBGC_New(&pleaf, &extra, WC_RNG_INIT_FLAGS_NONE); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (pleaf == NULL) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + wc_rng_free(pleaf); + pleaf = NULL; } /* heap-allocated leaves, without and with a nonce */ - api_ret = wc_InitRngRBGC_New(&pleaf, &root); + api_ret = wc_InitRngRBGC_New(&pleaf, &root, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if ((pleaf == NULL) || (wc_RNG_DRBG_IsRBGCLeaf(pleaf) != 1)) + if (pleaf == NULL) ERROR_OUT(WC_TEST_RET_ENC_NC, out); api_ret = wc_RNG_GenerateBlock(pleaf, buf, sizeof(buf)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); wc_rng_free(pleaf); pleaf = NULL; - api_ret = wc_InitRngNonceRBGC_New(&pleaf, &root, matter, 16); + api_ret = wc_InitRngNonceRBGC_New(&pleaf, &root, matter, 16, + WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if ((pleaf == NULL) || (wc_RNG_DRBG_IsRBGCLeaf(pleaf) != 1)) + if (pleaf == NULL) ERROR_OUT(WC_TEST_RET_ENC_NC, out); wc_rng_free(pleaf); pleaf = NULL; @@ -29298,12 +29555,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) leaf_inited = 0; if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_InitRngNonceRBGC(&leaf, &root, matter, 16); + api_ret = wc_InitRngNonceRBGC(&leaf, &root, matter, 16, + WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; - if (wc_RNG_DRBG_IsRBGCLeaf(&leaf) != 1) - ERROR_OUT(WC_TEST_RET_ENC_NC, out); out: @@ -29330,8 +29586,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) return ret; } -#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && - * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + +#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ + +#endif /* WC_RNG_HAVE_RBGC */ #ifdef WC_RNG_HAVE_NEXT_SEED /* Coverage for the banked-next-seed facility: the aperture protocol @@ -29471,6 +29729,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if ((api_ret != 0) || (c1 != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* redemption of a primary-provenance bank: credited, counted as a + * primary redemption */ +#ifdef WC_RNG_HAVE_RBGC +#endif api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index 4bdeea7ac96..e755feaf646 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -262,9 +262,9 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); +#endif extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); -#endif #endif /* WC_NO_RNG */ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); #if defined(USE_CERT_BUFFERS_2048) && \ diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index d83c3882852..e5d43cf0107 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -43,6 +43,16 @@ WOLFSSL_LOCAL int wolfCrypt_FIPS_DRBG_sanity(void); #endif +#ifndef WC_RNG_NO_RBGC + #if !defined(WC_RNG_HAVE_RBGC) && \ + defined(HAVE_HASHDRBG) && \ + !defined(CUSTOM_RAND_GENERATE_BLOCK) + #define WC_RNG_HAVE_RBGC + #endif +#else + #undef WC_RNG_HAVE_RBGC +#endif + /* _FULL_MUTEX is opt-in, and depends on WC_RNG_HAVE_LOCK. */ #ifdef WC_RNG_NO_LOCK_FULL_MUTEX #undef WC_RNG_HAVE_LOCK_FULL_MUTEX @@ -356,6 +366,9 @@ struct DRBG_internal { #ifdef WC_RNG_HAVE_NEXT_SEED byte nextSeed[WC_DRBG_NEXT_SEED_LEN]; WC_DRBG_nextSeedLen_t nextSeedLen; + #ifdef WC_RNG_HAVE_RBGC + int nextSeedRBGCStratum; + #endif #endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -381,6 +394,9 @@ struct DRBG_SHA512_internal { #ifdef WC_RNG_HAVE_NEXT_SEED byte nextSeed[WC_DRBG_NEXT_SEED_LEN]; WC_DRBG_nextSeedLen_t nextSeedLen; + #ifdef WC_RNG_HAVE_RBGC + int nextSeedRBGCStratum; + #endif #endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -412,6 +428,7 @@ enum wc_RngHealthState { }; #define WC_RNG_FLAG_NONE 0 +#define WC_RNG_FLAG_RBGC_NEXT_SEED (1U << 0) #define WC_RNG_FLAG_FULL_MUTEX (1U << 1) #define WC_RNG_FLAG_BANKREF (1U << 2) @@ -422,11 +439,9 @@ struct WC_RNG { void* heap; byte status; word32 flags; - /* Set when this instance was seeded from another DRBG's output - * (wc_InitRng*RBGC(), wc_RNG_DRBG_ReseedRBGC()) -- an SP 800-90C chain - * leaf. Sticky by policy: a leaf is never usable as a chain root, even - * after a subsequent reseed from the module's seed source. */ - byte isRbgcLeaf; +#ifdef WC_RNG_HAVE_RBGC + int RBGCStratum; +#endif #ifdef WC_RNG_HAVE_LOCK WC_RNG_lock_t lock; #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX @@ -700,7 +715,12 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); #endif #endif - WOLFSSL_API int wc_RNG_DRBG_IsRBGCLeaf(const WC_RNG* rng); +#ifdef WC_RNG_HAVE_RBGC + WOLFSSL_API int wc_RNG_DRBG_GetRBGCStratum(const WC_RNG* rng); + #ifdef WC_RNG_HAVE_NEXT_SEED + WOLFSSL_API int wc_RNG_DRBG_GetNextSeedRBGCStratum(const WC_RNG* rng); + #endif +#endif /* WC_RNG_HAVE_RBGC */ WOLFSSL_API int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr); WOLFSSL_API int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); @@ -813,21 +833,33 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); #endif /* HAVE_HASHDRBG */ -#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) - /* SP 800-90C RBG-chain spawn: instantiate leaf as a subordinate DRBG - * seeded from root's generate output. The _New variants allocate the - * leaf from root's heap; release those with wc_rng_free(). */ - WOLFSSL_API int wc_InitRngRBGC(WC_RNG* leaf, WC_RNG* root); - WOLFSSL_API int wc_InitRngNonceRBGC(WC_RNG* leaf, WC_RNG* root, - byte* nonce, word32 nonceSz); -#ifndef WC_NO_CONSTRUCTORS - WOLFSSL_API int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root); - WOLFSSL_API int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, - byte* nonce, word32 nonceSz); -#endif /* !WC_NO_CONSTRUCTORS */ - WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* leaf, WC_RNG* root, +#ifdef WC_RNG_HAVE_RBGC + /* SP 800-90C RBG-chain spawn: instantiate child as a subordinate DRBG + * seeded from parent's generate output. The _New variants allocate the + * child from parent's heap; release them with ordinary wc_rng_free(). */ + WOLFSSL_API int wc_InitRngRBGC(WC_RNG* child, WC_RNG* parent, word32 flags); + WOLFSSL_API int wc_InitRngNonceRBGC(WC_RNG* child, WC_RNG* parent, + const byte* nonce, word32 nonceSz, + word32 flags); + #ifndef WC_NO_CONSTRUCTORS + /* flags are per-object (WC_RNG_INIT_FLAGS_*), deliberately NOT + * inherited from the parent: a child's lock policy is its own. */ + WOLFSSL_API int wc_InitRngRBGC_New(WC_RNG** child, WC_RNG* parent, + word32 flags); + WOLFSSL_API int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, + const byte* nonce, word32 nonceSz, + word32 flags); + #endif /* !WC_NO_CONSTRUCTORS */ + /* Note, only a root RNG -- stratum 0, i.e. primary-seeded -- is permitted + * to generate reseed bytes (wolfCrypt policy; stricter than SP 800-90C + * 7.1.2.2, which also permits parent reseed). */ + WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz); -#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK */ + WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, + WC_RNG* root, + const byte* nonce, + word32 nonceSz); +#endif /* WC_RNG_HAVE_RBGC */ #ifdef WC_RNG_HAVE_NEXT_SEED #define WC_DRBG_NEXT_SEED_EMPTY 0 @@ -837,6 +869,11 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); #define WC_DRBG_NEXT_SEED_CONSUMING ((WC_ATOMIC_INT_ARG)(-1)) WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n); +#ifdef WC_RNG_HAVE_RBGC + WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate_RBGC(WC_RNG* rng, + WC_RNG *root, + word32 n); +#endif WOLFSSL_API int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng, WC_ATOMIC_INT_ARG* n); WOLFSSL_API int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index c33990aa712..13f00392181 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -106,6 +106,7 @@ * a use-after-free instead of BUSY_E -- only containers whose teardown * provably quiesces consumers first may set it. */ #define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 11) +#define WC_RNG_BANK_FLAG_INIT_RBGC (1U << 12) #define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 14) /* base lock states are WC_RNG_LOCK_FREE / WC_RNG_LOCK_HELD in random.h; @@ -290,6 +291,11 @@ WOLFSSL_API int wc_rng_bank_next_seed_generate( struct wc_rng_bank *bank, int inst_offset, word32 n); +WOLFSSL_API int wc_rng_bank_next_seed_generate_rbgc( + struct wc_rng_bank *bank, + int inst_offset, + word32 n, + WC_RNG *root); #endif WOLFSSL_API int wc_rng_bank_inst_reinit( @@ -313,26 +319,25 @@ WOLFSSL_API int wc_rng_bank_recover_inst( int timeout_secs, word32 flags); +#ifdef WC_RNG_HAVE_RBGC -#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) -/* Spawn an SP 800-90C chain leaf from a bank instance: check out an +/* Spawn an SP 800-90C chain RNG from a bank instance: check out a parent * instance (honoring the usual selection flags), wc_InitRngNonceRBGC() / - * wc_InitRngNonceRBGC_New() the leaf from it, and check the instance back - * in. The leaf's lifetime is thereafter decoupled from the bank: it is - * lock-free for its owner and is released with wc_FreeRng() (stack form) - * or wc_rng_free() (heap form). nonce/nonceSz may be NULL/0 for a plain - * spawn; per the bank's distinctness convention, passing the address of - * the leaf's owning object or request is recommended. bank == NULL uses - * the default bank where support is compiled in. - * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED composes (banked reseed before the - * spawn draw); WC_RNG_BANK_FLAG_SEED_UNCREDITED and - * WC_RNG_BANK_FLAG_FOR_RECOVERY are rejected. - * WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED is implied: the root is guaranteed - * in-service, or an error is returned with no lease and no leaf. */ + * wc_InitRngNonceRBGC_New() the child from it, and check the parent instance + * back in. The child's lifetime is thereafter decoupled from the parent and + * its bank: it is lock-free for its owner and is released with wc_FreeRng() + * (stack form) or wc_rng_free() (heap form). The child's RBGC stratum is one + * plus the parent's stratum at time of instantiation. nonce/nonceSz may be + * NULL/0 for a plain spawn; an example of a recommended nonce is Linux kernel + * random_get_entropy() (which is typically a racy read of a high-resolution + * timer). bank == NULL uses the default bank where support is compiled in. + * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED composes (banked reseed before the spawn + * draw); WC_RNG_BANK_FLAG_SEED_UNCREDITED and WC_RNG_BANK_FLAG_FOR_RECOVERY are + * rejected. WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED is implied: the parent is + * guaranteed in-service, or an error is returned with no lease and no child. */ WOLFSSL_API int wc_rng_bank_spawn( struct wc_rng_bank *bank, - WC_RNG *leaf_rng, + WC_RNG *child_rng, byte *nonce, word32 nonceSz, int preferred_inst_offset, @@ -342,15 +347,15 @@ WOLFSSL_API int wc_rng_bank_spawn( #ifndef WC_NO_CONSTRUCTORS WOLFSSL_API int wc_rng_bank_spawn_new( struct wc_rng_bank *bank, - WC_RNG **leaf_rng, + WC_RNG **child_rng, byte *nonce, word32 nonceSz, int preferred_inst_offset, int timeout_secs, word32 flags); #endif /* !WC_NO_CONSTRUCTORS */ -#endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && - * (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ + +#endif /* WC_RNG_HAVE_RBGC */ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, word32 seedSz, @@ -689,6 +694,35 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_clear_extra(struct wc wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); #endif +#ifdef WC_RNG_HAVE_RBGC + +#define wc_InitRngRBGC(leaf, root, flags) \ + wc_InitRngNonceRBGC(leaf, root, NULL, 0, flags) + +WC_MAYBE_UNUSED static WC_INLINE int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root, word32 flags) { + if ((leaf == NULL) || (root == NULL)) + return BAD_FUNC_ARG; + *leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); + if (*leaf == NULL) + return MEMORY_E; + else + return wc_InitRngNonceRBGC(*leaf, root, NULL, 0, flags); +} + +WC_MAYBE_UNUSED static WC_INLINE int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, + const byte* nonce, word32 nonceSz, + word32 flags) +{ + if ((leaf == NULL) || (root == NULL)) + return BAD_FUNC_ARG; + *leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); + if (*leaf == NULL) + return MEMORY_E; + else + return wc_InitRngNonceRBGC(*leaf, root, nonce, nonceSz, flags); +} + +#endif /* WC_RNG_HAVE_RBGC */ WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_GetStatus(const WC_RNG* rng) { @@ -811,6 +845,17 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( return wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), nonce, nonceSz); } +#ifdef WC_RNG_HAVE_RBGC +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_rbgc( + struct wc_rng_bank_inst *inst, WC_RNG* root, const byte* nonce, + word32 nonceSz) +{ + if (inst == NULL) + return BAD_FUNC_ARG; + return wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root, + nonce, nonceSz); +} +#endif /* WC_RNG_HAVE_RBGC */ #else /* !WC_RNG_HAVE_LOCK */ @@ -825,6 +870,28 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( return ret; } +#ifdef WC_RNG_HAVE_RBGC +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_rbgc( + struct wc_rng_bank_inst *inst, WC_RNG* root, const byte* nonce, + word32 nonceSz) +{ + int ret; + if (inst == NULL) + return BAD_FUNC_ARG; +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + /* the pre-v7 boundary's wc_RNG_DRBG_ReseedRBGC() predates the nonce + * parameters; honest rejection, as with the boundary's Reseed_Now(). */ + if (nonceSz > 0) + return NOT_COMPILED_IN; + (void)nonce; + ret = wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root); +#else + ret = wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root, + nonce, nonceSz); +#endif + return ret; +} +#endif /* WC_RNG_HAVE_RBGC */ #endif /* !WC_RNG_HAVE_LOCK */ From e5e1484540678811ba9b8e5e77739ae337639efd Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:05:57 +0000 Subject: [PATCH 024/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/test/test.c: add the per-instance random pool: * a caller-sized buffer of pre-generated output attached to a WC_RNG (wc_RNG_Pool_Alloc()), filled incrementally by wc_RNG_Pool_Collect() -- or wc_RNG_Pool_Collect2(), drawing from a second instance -- and drained atomic-context-safely by wc_RNG_Pool_Extract(); wc_RNG_Pool_Current() reports the fill; the collect/extract hand-off is arbitrated by an aperture word (WC_RNG_pool_state_t, atomic with a plain-word arm for WOLFSSL_NO_ATOMICS), on the same protocol shape as the banked next seed; * gated by WC_RNG_HAVE_POOL (opt-out: WC_RNG_NO_POOL); * test.c: new rng_drbg_pool_test() (RNGPOOL), covering the collect / extract protocol, the two-instance collect, exhaustion, and the argument contracts. --- .wolfssl_known_macro_extras | 2 + wolfcrypt/src/random.c | 230 ++++++++++++++++++++++++++++++++++++ wolfcrypt/test/test.c | 179 ++++++++++++++++++++++++++++ wolfcrypt/test/test.h | 1 + wolfssl/wolfcrypt/random.h | 26 ++++ 5 files changed, 438 insertions(+) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index e39705308f6..11c0edb4dcf 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -803,9 +803,11 @@ WC_PUF_HELPER_COMPACT WC_PUF_SHA3 WC_RNG_BANK_NO_DEFAULT_SUPPORT WC_RNG_BLOCKING +WC_RNG_NO_FREE_HOOK WC_RNG_NO_LOCK WC_RNG_NO_LOCK_FULL_MUTEX WC_RNG_NO_NEXT_SEED +WC_RNG_NO_POOL WC_RNG_NO_RBGC WC_RSA_NONBLOCK_TIME WC_RSA_NO_FERMAT_CHECK diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index efaf0621275..6efb14c1342 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -763,6 +763,17 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, if (rng == NULL) return BAD_FUNC_ARG; +#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_POOL) + /* Purge the pool on credited reseeds, but not on uncredited ones. A + * credited reseed is an epoch boundary -- the pool must not serve output of + * a retired state. An uncredited + * reseed merely stirs the state; the pooled bytes' own credited provenance + * is unaffected. Purging at stirs would also empty the pool exactly when + * harvest-driven mixing is heaviest, i.e. when atomic-context consumers + * most need it. */ + if (credited) + WOLFSSL_ATOMIC_STORE(rng->poolState, 0); +#endif /* WC_RNG_HAVE_LOCK && WC_RNG_HAVE_POOL */ #ifndef NO_SHA256 if (rng->drbgType == WC_DRBG_SHA256) { @@ -3028,6 +3039,215 @@ int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) #endif /* WC_RNG_HAVE_LOCK */ +#ifdef WC_RNG_HAVE_POOL + /* In-boundary asynchronous DRBG output pool. _Alloc() sizes the ring + * (2..65535 bytes; a second allocation is ALREADY_E). _Collect() tops it + * up from rng's own DRBG; _Collect2() tops dest's ring up from an + * independent src instance, generating directly into the free span + * and publishing with a single CAS -- callable WITHOUT any lease on + * dest (contending writers regenerate on CAS failure; the final write + * of every published byte is certified DRBG output). _Extract() + * (lease-holder only) delivers up to *n bytes destructively, burning + * each byte on the way out, and fails closed (burning the ring) on an + * out-of-service DRBG; *n = 0 on empty, for fall-through to a direct + * generate. _Current() reports the published count (racy snapshot). + * No free API: the ring lives until wc_FreeRng(), eliminating + * deallocation races by construction. */ + + /* In-boundary DRBG output pool (wc_RNG_Pool_*()): a circular buffer of + * pre-generated output, held and zeroized under the module's CSP + * discipline and consumed destructively (each delivered or discarded + * byte is burned). No internal synchronization: all pool operations + * require exclusive ownership of the instance (an rng_bank lease, or + * an intrinsically uncontended object). */ + + /* Asynchronous pool aperture: the low half of poolState is the + * published byte count, the high half the read offset, packed in one + * atomic word so reader updates are single release stores and writer + * publications are single CASes -- no torn {current,offset} snapshot + * is observable. Sole reader = the instance lease holder; writers + * (e.g. the entropy daemon via wc_RNG_Pool_Collect2()) never hold the + * instance. */ + +/* Asynchronous in-boundary DRBG output pool. See random.h for the + * aperture encoding. Protocol: the sole reader (instance lease holder) + * loads a snapshot, copies out, ForceZero()s the consumed span, then + * release-stores {current - m, offset + m}; writers load a snapshot, + * generate certified output directly into the unpublished span, and + * publish with one CAS of the whole word -- on CAS failure the written + * material is burned and regenerated fresh (never reused: no DRBG output + * may be deliverable twice). A reader store may overwrite a concurrent + * writer's publication; the loss is unidirectionally conservative (the + * count only ever drops), so no reader can claim unpublished bytes, and + * the clobbered bytes are benign unaccounted content awaiting + * overwrite. */ + +typedef union { + WC_ATOMIC_UINT_ARG state; + struct { + word16 current; + word16 offset; + } pool; +} wc_rng_pool_state_u; + +int wc_RNG_Pool_Alloc(WC_RNG* rng, word32 size) +{ + if ((rng == NULL) || (size < 2) || (size > 65535U)) + return BAD_FUNC_ARG; /* halves are word16; current in [0, size] */ + if (rng->pool != NULL) { + /* the requested condition already holds -- distinct from the + * BAD_STATE_E that pool operations report for a MISSING pool */ + return ALREADY_E; + } + + rng->pool = (byte*)XMALLOC(size, rng->heap, DYNAMIC_TYPE_RNG); + if (rng->pool == NULL) + return MEMORY_E; + rng->poolSize = (word16)size; + wolfSSL_Atomic_Uint_Init(&rng->poolState, 0); + + return 0; +} + +int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, word32 n) +{ + int retries; + + if ((rng_dest == NULL) || (rng_src == NULL)) + return BAD_FUNC_ARG; + if (rng_dest->pool == NULL) + return BAD_STATE_E; + if (n == 0) + return 0; + + for (retries = 0; retries < 8; retries++) { + wc_rng_pool_state_u snap, next; + word32 m, done = 0; + int ret; + + snap.state = WOLFSSL_ATOMIC_LOAD(rng_dest->poolState); + m = (word32)rng_dest->poolSize - (word32)snap.pool.current; + if (m == 0) + return 0; /* full: success no-op */ + if (m > n) + m = n; + + /* generate directly into the unpublished span (up to two + * contiguous segments), then publish the whole of it with one + * CAS */ + while (done < m) { + word32 at = ((word32)snap.pool.offset + (word32)snap.pool.current + + done) % (word32)rng_dest->poolSize; + word32 chunk = (word32)rng_dest->poolSize - at; + if (chunk > m - done) + chunk = m - done; + ret = wc_RNG_GenerateBlock(rng_src, rng_dest->pool + at, + (word32)chunk); + if (ret != 0) { + /* Abandon in place. The written bytes MUST NOT be burned: + * a competing writer may have published a span overlapping + * them (final-write-wins), and zeroing published content + * would deliver zeros as randomness. Abandoned bytes are + * benign in-boundary content awaiting overwrite. */ + return ret; + } + done += chunk; + } + + next = snap; + next.pool.current = (word16)((word32)snap.pool.current + m); + if (wolfSSL_Atomic_Uint_CompareExchange(&rng_dest->poolState, + &snap.state, next.state)) + { + return 0; + } + + /* Lost the publication race: abandon in place (see above -- never + * burn a span we may no longer own) and regenerate fresh against a + * new snapshot (never republish the same output: no DRBG output + * may be deliverable twice). */ + } + + return NOT_READY_E; /* persistent contention: retry on a later cycle */ +} + +int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n) +{ + return wc_RNG_Pool_Collect2(rng, rng, n); +} + +int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) +{ + wc_rng_pool_state_u snap, next; + word32 m, done = 0; + + if ((rng == NULL) || (out == NULL) || (n == NULL)) + return BAD_FUNC_ARG; + { + int lock_ret = rng_lock_required_check(rng); + if (lock_ret != 0) + return lock_ret; + } + if (rng->pool == NULL) + return BAD_STATE_E; + + /* Fail closed: no serving output on behalf of an out-of-service DRBG, + * and its pooled output is unusable material at rest -- burn it. A + * concurrent writer's CAS fails against the store and abandons. */ + if (wc_RNG_DRBG_Present(rng) && (rng->status != DRBG_OK)) { + ForceZero(rng->pool, rng->poolSize); + WOLFSSL_ATOMIC_STORE(rng->poolState, 0); + return RNG_FAILURE_E; + } + + snap.state = WOLFSSL_ATOMIC_LOAD(rng->poolState); + if (snap.pool.current == 0) { + return NOT_READY_E; + } + m = *n; + if (m > (word32)snap.pool.current) + m = (word32)snap.pool.current; + + while (done < m) { + word32 at = ((word32)snap.pool.offset + done) % + (word32)rng->poolSize; + word32 chunk = (word32)rng->poolSize - at; + if (chunk > m - done) + chunk = m - done; + XMEMCPY(out + done, rng->pool + at, chunk); + /* burn on the way out the door, before the span is republished */ + ForceZero(rng->pool + at, chunk); + done += chunk; + } + + next.pool.current = (word16)((word32)snap.pool.current - m); + next.pool.offset = (word16)(((word32)snap.pool.offset + m) % + (word32)rng->poolSize); + /* single release store: the burn above is visible before the space + * is. May clobber a concurrent writer's publication -- benign and + * conservative (see the protocol comment). */ + WOLFSSL_ATOMIC_STORE(rng->poolState, next.state); + + *n = m; + + return 0; +} + +int wc_RNG_Pool_Current(WC_RNG* rng, word32* n) +{ + if ((rng == NULL) || (n == NULL)) + return BAD_FUNC_ARG; + if (rng->pool != NULL) { + wc_rng_pool_state_u snap; + snap.state = WOLFSSL_ATOMIC_LOAD(rng->poolState); + *n = (word32)snap.pool.current; + } + else { + *n = 0; + } + return 0; +} +#endif /* WC_RNG_HAVE_POOL */ #ifdef WC_RNG_HAVE_RBGC @@ -3909,6 +4129,16 @@ int wc_FreeRng(WC_RNG* rng) return wc_BankRef_Release(rng); #endif /* WC_HAVE_RNG_BANKREF */ +#ifdef WC_RNG_HAVE_POOL + /* single-owner teardown; the only pool deallocation site */ + if (rng->pool != NULL) { + ForceZero(rng->pool, rng->poolSize); + XFREE(rng->pool, rng->heap, DYNAMIC_TYPE_RNG); + rng->pool = NULL; + rng->poolSize = 0; + WOLFSSL_ATOMIC_STORE(rng->poolState, 0); + } +#endif #if defined(WOLFSSL_ASYNC_CRYPT) wolfAsync_DevCtxFree(&rng->asyncDev, WOLFSSL_ASYNC_MARKER_RNG); diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index fc8508c0a3a..e4316573b5f 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -950,6 +950,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); #ifdef WC_RNG_HAVE_NEXT_SEED WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); #endif +#ifdef WC_RNG_HAVE_POOL +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); +#endif #endif /* WC_NO_RNG */ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); #if defined(USE_CERT_BUFFERS_2048) && \ @@ -2632,6 +2635,12 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ else TEST_PASS("RNGNXTS test passed!\n"); #endif +#ifdef WC_RNG_HAVE_POOL + if ((ret = rng_pool_test()) != 0) + TEST_FAIL("RNGPOOL test failed!\n", ret); + else + TEST_PASS("RNGPOOL test passed!\n"); +#endif #endif /* WC_NO_RNG */ #ifdef WOLFSSL_SHAKE128 @@ -29787,6 +29796,176 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) } #endif /* WC_RNG_HAVE_NEXT_SEED */ +#ifdef WC_RNG_HAVE_POOL +/* Coverage for the asynchronous DRBG output pool: allocation contracts + * (incl. the word16 size bound and double-alloc rejection), self- and + * cross-instance collection, published-count tracking, destructive + * extraction with partial delivery and the empty-pool NOT_READY_E, and ring + * wraparound on both the collect and extract sides. Single-threaded, so + * reader/writer interleavings are exercised elsewhere; this pins the + * sequential contracts. */ +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void) +{ + wc_test_ret_t ret = 0; + int api_ret; + int rng_inited = 0; + int src_inited = 0; + WC_DECLARE_VAR(rng, WC_RNG, 1, HEAP_HINT); + WC_DECLARE_VAR(src, WC_RNG, 1, HEAP_HINT); + word32 n = 0; + byte out[48]; + + WOLFSSL_ENTER("rng_pool_test"); + + WC_ALLOC_VAR_EX(rng, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out_l)); + WC_ALLOC_VAR_EX(src, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out_l)); + + /* argument contracts, pre-init */ + api_ret = wc_RNG_Pool_Alloc(NULL, 48); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Collect(NULL, 1); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Extract(NULL, out, &n); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Current(NULL, &n); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + + api_ret = wc_InitRng(rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + rng_inited = 1; + api_ret = wc_InitRng(src); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + src_inited = 1; + + /* size bounds; operations on a pool-less instance */ + api_ret = wc_RNG_Pool_Alloc(rng, 1); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Alloc(rng, 65536); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Collect(rng, 8); + if (api_ret != WC_NO_ERR_TRACE(BAD_STATE_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + n = sizeof(out); + api_ret = wc_RNG_Pool_Extract(rng, out, &n); + if (api_ret != WC_NO_ERR_TRACE(BAD_STATE_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Current(rng, &n); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (n != 0) + ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); + +#ifndef WOLFSSL_NO_MALLOC + api_ret = wc_RNG_Pool_Alloc(rng, 48); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Alloc(rng, 48); + if (api_ret != WC_NO_ERR_TRACE(ALREADY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + + /* empty pool: a distinct protocol code, nothing delivered */ + n = sizeof(out); + api_ret = wc_RNG_Pool_Extract(rng, out, &n); + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + /* the whole request is missed bytes; nothing served */ + + /* self-collect to full (clamped), verify count, over-collect no-op */ + api_ret = wc_RNG_Pool_Collect(rng, 100); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Current(rng, &n); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (n != 48) + ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); + api_ret = wc_RNG_Pool_Collect(rng, 1); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + + /* partial extract, then cross-instance top-off wrapping the ring, + * then full drain crossing the wrap on the read side */ + n = 32; + api_ret = wc_RNG_Pool_Extract(rng, out, &n); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (n != 32) + ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); + api_ret = wc_RNG_Pool_Current(rng, &n); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (n != 16) + ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); + /* a fully-fulfillable partial drain is all produced, no shortfall */ + api_ret = wc_RNG_Pool_Collect2(rng, src, 32); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (wc_RNG_DRBG_Present(src)) { + /* the top-off span starts exactly at the ring origin + * ((offset + current) % size == 0): one contiguous generate */ + } + api_ret = wc_RNG_Pool_Current(rng, &n); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (n != 48) + ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); + n = sizeof(out); + api_ret = wc_RNG_Pool_Extract(rng, out, &n); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (n != 48) + ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); + api_ret = wc_RNG_Pool_Current(rng, &n); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (n != 0) + ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); + /* full serve across the ring wrap: all produced, no shortfall */ + + /* Collect2 contracts */ + api_ret = wc_RNG_Pool_Collect2(rng, NULL, 8); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Collect2(NULL, src, 8); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_Pool_Collect2(rng, src, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); +#endif /* WOLFSSL_NO_MALLOC */ + +out_l: + + { + int cleanup_ret; + if (rng_inited) { + cleanup_ret = wc_FreeRng(rng); /* sole pool teardown site */ + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (src_inited) { + cleanup_ret = wc_FreeRng(src); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + } + WC_FREE_VAR(rng, HEAP_HINT); + WC_FREE_VAR(src, HEAP_HINT); + + return ret; +} +#endif /* WC_RNG_HAVE_POOL */ + #endif /* !WC_NO_RNG */ #ifndef MEM_TEST_SZ diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index e755feaf646..8b24689654f 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -265,6 +265,7 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); #endif extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); +extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); #endif /* WC_NO_RNG */ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); #if defined(USE_CERT_BUFFERS_2048) && \ diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index e5d43cf0107..f5560fbda32 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -43,6 +43,19 @@ WOLFSSL_LOCAL int wolfCrypt_FIPS_DRBG_sanity(void); #endif +#ifndef WC_RNG_NO_POOL + #ifndef WC_RNG_HAVE_POOL + #define WC_RNG_HAVE_POOL + #endif + #ifdef WOLFSSL_NO_ATOMICS + typedef word32 WC_RNG_pool_state_t; + #else + typedef wolfSSL_Atomic_Uint WC_RNG_pool_state_t; + #endif +#else + #undef WC_RNG_HAVE_POOL +#endif + #ifndef WC_RNG_NO_RBGC #if !defined(WC_RNG_HAVE_RBGC) && \ defined(HAVE_HASHDRBG) && \ @@ -448,6 +461,11 @@ struct WC_RNG { wolfSSL_Mutex mutex; #endif #endif +#ifdef WC_RNG_HAVE_POOL + byte* pool; + word16 poolSize; + WC_RNG_pool_state_t poolState; +#endif #if defined(HAVE_HASHDRBG) || defined(WC_HAVE_RNG_BANKREF) @@ -908,6 +926,14 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); WC_RNG_lock_arg_t extra_bits); #endif /* WC_RNG_HAVE_LOCK */ +#ifdef WC_RNG_HAVE_POOL + WOLFSSL_API int wc_RNG_Pool_Alloc(WC_RNG* rng, word32 size); + WOLFSSL_API int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n); + WOLFSSL_API int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, + word32 n); + WOLFSSL_API int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n); + WOLFSSL_API int wc_RNG_Pool_Current(WC_RNG* rng, word32* n); +#endif /* WC_RNG_HAVE_POOL */ #ifdef __cplusplus From 6341d921cf266e988006b0e69ea418eda5ca6627 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:08:35 +0000 Subject: [PATCH 025/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/test/test.c: add entropy invalidation and recovery admission: * wc_RNG_invalidate_entropy() / wc_rng_bank_invalidate_entropy(): mark an instance's (or a whole bank's) seed material untrusted -- for VM fork/resume and similar duplication events -- by latching WC_RNG_LOCK_ENTROPY_INVALIDATED in the lock word; an invalidated instance refuses service until recovery-reseeded, and wc_rng_bank_inst_lock_get_conditional() lets checkout skip invalidated instances rather than block on them; * recovery admission: WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY tolerates a not-actually-invalidated instance, WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (and the bank spawn analog _SPAWN_RECOVER_AND_PROMOTE) admits a banked next seed as the recovery source; wc_rng_bank_{seed,reseed}_range() (re)seed instance subsets; * free hooks: wc_RNG_register_free_hook() / wc_rng_bank_register_free_hook() -- teardown notification for external registries (e.g. a kernel-module RNG object registry that must invalidate on VM resume), gated WC_RNG_HAVE_FREE_HOOK; * test.c: new rng_drbg_invalidate_test() (RNGINVAL) covering invalidation latching, refused service, conditional checkout, recovery admission (including next-seed-sourced), free-hook firing, and the argument contracts; lock-word read probes in the svc test. --- wolfcrypt/src/random.c | 199 ++++++++++++- wolfcrypt/src/rng_bank.c | 279 +++++++++++++++-- wolfcrypt/test/test.c | 561 +++++++++++++++++++++++++++++++++++ wolfcrypt/test/test.h | 1 + wolfssl/wolfcrypt/random.h | 33 +++ wolfssl/wolfcrypt/rng_bank.h | 148 ++++++++- 6 files changed, 1176 insertions(+), 45 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 6efb14c1342..1d4fc0e1823 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -306,8 +306,9 @@ This library contains implementation for the random number generator. #define DRBG_FAILED WC_DRBG_FAILED #define DRBG_CONT_FAILED WC_DRBG_CONT_FAILED -/* enforcement helper for WC_RNG_LOCK_REQUIRED: instance-consuming public APIs - * call this on entry. */ +/* enforcement helper for WC_RNG_LOCK_REQUIRED and + * WC_RNG_LOCK_ENTROPY_INVALIDATED: instance-consuming public APIs call this on + * entry. */ static WC_MAYBE_UNUSED WC_INLINE int rng_lock_required_check(WC_RNG* rng) { if (rng == NULL) @@ -326,6 +327,12 @@ static WC_MAYBE_UNUSED WC_INLINE int rng_lock_required_check(WC_RNG* rng) { return OBJECT_NOT_LOCKED_E; } + /* WC_RNG_LOCK_ENTROPY_INVALIDATED deliberately does not gate entry + * here: on lock-required instances the lock API refuses new leases, + * and on unlocked instances the saturated reseedCtr (see + * wc_RNG_invalidate_entropy()) forces a credited reseed -- which + * clears the flag -- before the next generate. Refusing here would + * brick unlocked instances, with no path to recovery. */ return 0; } #endif /* WC_RNG_HAVE_LOCK */ @@ -759,14 +766,21 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, int credited) { int ret; +#ifdef WC_RNG_HAVE_LOCK + WC_RNG_lock_arg_t cur_lock; +#endif if (rng == NULL) return BAD_FUNC_ARG; +#ifdef WC_RNG_HAVE_LOCK + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); +#endif + #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_POOL) /* Purge the pool on credited reseeds, but not on uncredited ones. A * credited reseed is an epoch boundary -- the pool must not serve output of - * a retired state. An uncredited + * a retired state (particularly pre-invalidation state). An uncredited * reseed merely stirs the state; the pooled bytes' own credited provenance * is unaffected. Purging at stirs would also empty the pool exactly when * harvest-driven mixing is heaviest, i.e. when atomic-context consumers @@ -791,6 +805,12 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, goto out; } +#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_NEXT_SEED) + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { + WOLFSSL_ATOMIC_STORE(drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + } +#endif + ret = Hash256_DRBG_Reseed(drbg, seed, seedSz, additional, additionalSz, credited); goto out; @@ -814,6 +834,12 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, goto out; } +#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_NEXT_SEED) + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { + WOLFSSL_ATOMIC_STORE(drbg512->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + } +#endif + ret = Hash512_DRBG_Reseed(drbg512, seed, seedSz, additional, additionalSz, credited); goto out; @@ -833,6 +859,19 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, out: +#ifdef WC_RNG_HAVE_LOCK + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && (ret == 0) && credited) { + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, + cur_lock & ~WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + break; + } + } + } +#endif /* WC_RNG_HAVE_LOCK */ + return ret; } @@ -2658,6 +2697,16 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #endif /* HAVE_HASHDRBG */ #endif /* CUSTOM_RAND_GENERATE_BLOCK */ +#ifndef WC_RNG_HAVE_NEXT_SEED + if (flags & WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED) + return NOT_COMPILED_IN; +#else + if ((ret == 0) && + (flags & WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED)) + { + rng->flags |= WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; + } +#endif if ((ret == 0) && (flags & WC_RNG_INIT_FLAGS_USE_FULL_MUTEX)) { #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX /* deliberately the last init step: no failure path can strand an @@ -2809,6 +2858,14 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) + (void)wc_UnLockMutex(&rng->mutex); +#endif + return NEEDS_RECOVERY_E; + } + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && (wolfSSL_Atomic_Uint_CompareExchange( &rng->lock, &cur_lock, @@ -2825,7 +2882,9 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) (void)wc_UnLockMutex(&rng->mutex); #endif - if (cur_lock & WC_RNG_LOCK_HELD) + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else if (cur_lock & WC_RNG_LOCK_HELD) return BUSY_E; else /* not reachable */ return UNEXPECTED_STATE_E; @@ -2849,16 +2908,27 @@ int wc_RNG_lock_get_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi #endif /* *_extra_bits are allowed to assert WC_RNG_LOCK_REQUIRED, which is in the - * reserved section. */ + * reserved section. Additionally, expected_extra_bits is allowed to + * include WC_RNG_LOCK_ENTROPY_INVALIDATED, for purposes of recovery. */ expected_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | - WC_RNG_LOCK_REQUIRED; + WC_RNG_LOCK_REQUIRED | WC_RNG_LOCK_ENTROPY_INVALIDATED; want_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | WC_RNG_LOCK_REQUIRED; cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && + (! (expected_extra_bits & WC_RNG_LOCK_ENTROPY_INVALIDATED))) + { +#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) + (void)wc_UnLockMutex(&rng->mutex); +#endif + return NEEDS_RECOVERY_E; + } + expected = (cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & - ~WC_RNG_LOCK_HELD)) | + ~(WC_RNG_LOCK_HELD | WC_RNG_LOCK_ENTROPY_INVALIDATED))) | expected_extra_bits; if ((! (cur_lock & WC_RNG_LOCK_HELD)) && @@ -2877,7 +2947,12 @@ int wc_RNG_lock_get_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi (void)wc_UnLockMutex(&rng->mutex); #endif - if (expected & WC_RNG_LOCK_HELD) + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) != + (expected & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + return NEEDS_RECOVERY_E; + } + else if (expected & WC_RNG_LOCK_HELD) return BUSY_E; else return UNEXPECTED_STATE_E; @@ -2910,7 +2985,10 @@ int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) (void)wc_UnLockMutex(&rng->mutex); #endif - return 0; + if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else + return 0; } int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bits, WC_RNG_lock_arg_t want_extra_bits) @@ -2933,10 +3011,11 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi WC_RNG_LOCK_REQUIRED; new_lock |= want_extra_bits; - expected = WC_RNG_LOCK_HELD | expected_extra_bits; + expected = WC_RNG_LOCK_HELD | expected_extra_bits | (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); - new_lock |= (expected_extra_bits & WC_RNG_LOCK_REQUIRED); + new_lock |= (expected_extra_bits & WC_RNG_LOCK_REQUIRED) | (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); + /* release preserves the sticky bit if the caller reports it held */ if (wolfSSL_Atomic_Uint_CompareExchange( &rng->lock, &expected, new_lock)) @@ -2945,7 +3024,10 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) (void)wc_UnLockMutex(&rng->mutex); #endif - return 0; + if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else + return 0; } if ((expected & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U)) != (expected_extra_bits & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U))) @@ -3037,8 +3119,68 @@ int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) return 0; } +WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { + WC_RNG_lock_arg_t cur_lock; + + if (rng == NULL) + return BAD_FUNC_ARG; + + cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + break; + } + } + + /* If no lock is held, either the RNG is in use without a lock, in which + * case the reseedCtr is the only way to force invalidation semantics on the + * user, or it is not in use at all and scheduling a reseed is harmless. + * + * If a lock is held, the holder will learn of the invalidation at unlock + * time, and will implement its own mitigation strategy. We do not force it + * into a synchronous reseed. + */ + if (! (cur_lock & WC_RNG_LOCK_HELD)) + (void)wc_RNG_DRBG_ScheduleReseed(rng); +#ifdef WC_RNG_HAVE_POOL + WOLFSSL_ATOMIC_STORE(rng->poolState, 0); +#endif +#ifdef WC_RNG_HAVE_NEXT_SEED +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { + WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + } +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { + WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + } +#endif +#endif /* WC_RNG_HAVE_NEXT_SEED */ + + return 0; +} + #endif /* WC_RNG_HAVE_LOCK */ +#ifdef WC_RNG_HAVE_FREE_HOOK +/* This routine is used for mitigation of RNG cloning events, particularly by + * hypervisors. */ +WOLFSSL_API int wc_RNG_register_free_hook(WC_RNG* rng, + wc_RNG_free_hook_cb_t free_hook, + void *arg) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + rng->free_hook = free_hook; + rng->free_hook_arg = arg; + return 0; +} +#endif /* WC_RNG_HAVE_FREE_HOOK */ + #ifdef WC_RNG_HAVE_POOL /* In-boundary asynchronous DRBG output pool. _Alloc() sizes the ring * (2..65535 bytes; a second allocation is ALREADY_E). _Collect() tops it @@ -4009,6 +4151,28 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) } #endif +#if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_LOCK) && \ + defined(WC_RNG_HAVE_RBGC) + if (rng->flags & WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED) { + /* externally-refreshed instance: consume a READY banked seed to + * recover from entropy invalidation (any provenance -- the purge + * in wc_RNG_invalidate_entropy() guarantees a READY seed is + * post-event), or to promote a chain-backed instance to primary. + * Consumption is a credited reseed, clearing the flag and + * resetting the schedule; failure falls through to the ordinary + * forced-reseed machinery. */ + int banked_stratum = wc_RNG_DRBG_GetNextSeedRBGCStratum(rng); + if (banked_stratum >= 0) { + if ((WOLFSSL_ATOMIC_LOAD(rng->lock) & + WC_RNG_LOCK_ENTROPY_INVALIDATED) || + ((rng->RBGCStratum > 0) && (banked_stratum == 0))) + { + (void)wc_RNG_DRBG_NextSeedNow(rng); + } + } + } +#endif + #ifndef NO_SHA256 if (rng->drbgType == WC_DRBG_SHA256) { ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, @@ -4129,6 +4293,17 @@ int wc_FreeRng(WC_RNG* rng) return wc_BankRef_Release(rng); #endif /* WC_HAVE_RNG_BANKREF */ +#ifdef WC_RNG_HAVE_FREE_HOOK + if (rng->free_hook != NULL) { + /* one-shot, cleared before firing: re-entrant frees from the hook + * (not that they would be a good idea) can't loop. */ + wc_RNG_free_hook_cb_t free_hook = rng->free_hook; + rng->free_hook = NULL; + (void)free_hook(rng, rng->free_hook_arg); + rng->free_hook_arg = NULL; + } +#endif + #ifdef WC_RNG_HAVE_POOL /* single-owner teardown; the only pool deallocation site */ if (rng->pool != NULL) { diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 90bc1ff796e..c5c5b9cf919 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -347,13 +347,24 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { } } + if (ctx->free_hook != NULL) { + /* teardown is committed: fire the free hook (one-shot). */ + wc_rng_bank_free_hook_cb_t free_hook = ctx->free_hook; + ctx->free_hook = NULL; + (void)free_hook(ctx, ctx->free_hook_arg); + ctx->free_hook_arg = NULL; + } + for (i = 0; i < ctx->n_rngs; ++i) { /* Lease-taking teardown: wc_FreeRng() on a _LOCK_REQUIRED * instance is (correctly) refused without the lease, so take * it -- structurally uncontended at refcount zero with the * held-check above passed. The latch dies held in dying * memory, per the uncleared-on-free contract. */ - if (wc_rng_bank_inst_lock_get(&ctx->rngs[i], 0) != 0) { + if ((wc_rng_bank_inst_lock_get(&ctx->rngs[i], 0) != 0) && + (wc_rng_bank_inst_lock_get_conditional(&ctx->rngs[i], + WC_RNG_LOCK_ENTROPY_INVALIDATED, 0) != 0)) + { /* can't happen absent corruption; leak, don't crash. */ #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( @@ -568,6 +579,11 @@ WOLFSSL_API int wc_rng_bank_checkout( int n_rngs_tried = 0; int diverted_unusable = 0; WC_ATOMIC_INT_ARG new_refcount; +#ifdef WC_RNG_HAVE_NEXT_SEED + WC_ATOMIC_INT_ARG NextSeedCurrent = 0; + int recovered_claim = 0; +#endif + int maybe_recovery_claim = 0; if (rng_inst == NULL) @@ -713,8 +729,56 @@ WOLFSSL_API int wc_rng_bank_checkout( } } - if (wc_rng_bank_inst_lock_get(&bank->rngs[preferred_inst_offset], +#ifdef WC_RNG_HAVE_NEXT_SEED + recovered_claim = 0; +#endif + maybe_recovery_claim = 0; + if ((wc_rng_bank_inst_lock_get(&bank->rngs[preferred_inst_offset], lock_extra_bits) == 0) + || + /* recovery-intent checkouts claim quarantined + * (_ENTROPY_INVALIDATED) instances too -- the claimant is about + * to recover them; ordinary consumers stay refused... */ + ((flags & WC_RNG_BANK_FLAG_FOR_RECOVERY) && + (wc_rng_bank_inst_lock_get_conditional( + &bank->rngs[preferred_inst_offset], + WC_RNG_LOCK_ENTROPY_INVALIDATED, + lock_extra_bits) == 0)) +#ifdef WC_RNG_HAVE_NEXT_SEED + || + /* ...UNLESS recovery is pure computation: when the quarantined + * instance's banked next seed reads READY, any claimant + * completes the recovery -- the invalidation purge guarantees + * a READY bank is post-event, and a recovered_claim forces the + * consume-at-checkout leg below (independent of bank consume + * policy), performing the credited reseed from banked material + * before the instance is handed out. Without this admission, + * the quarantine stands in front of the only machinery that + * can lift it, and no bank instance ever recovers (observed as + * system-wide checkout timeouts after a live + * state-invalidation event). */ + ((wc_RNG_DRBG_NextSeedCurrent( + WC_RNG_BANK_INST_TO_RNG(&bank->rngs[preferred_inst_offset]), + &NextSeedCurrent) == 0) && + (NextSeedCurrent == WC_DRBG_NEXT_SEED_READY) && + (wc_rng_bank_inst_lock_get_conditional( + &bank->rngs[preferred_inst_offset], + WC_RNG_LOCK_ENTROPY_INVALIDATED, + lock_extra_bits) == 0) && + ((recovered_claim = 1) != 0)) +#endif /* WC_RNG_HAVE_NEXT_SEED */ + || + /* last resort, by caller declaration: admit to the quarantined + * instance anyway, transferring the recovery obligation to the + * caller (checkout will return NEEDS_RECOVERY_E with the lease + * held -- see WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY). */ + ((flags & WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY) && + (wc_rng_bank_inst_lock_get_conditional( + &bank->rngs[preferred_inst_offset], + WC_RNG_LOCK_ENTROPY_INVALIDATED, + lock_extra_bits) == 0) && + ((maybe_recovery_claim = 1) != 0)) + ) { int inst_unusable; wc_drbg_reseed_ctr_t cur_reseed_ctr = 0; @@ -722,9 +786,10 @@ WOLFSSL_API int wc_rng_bank_checkout( *rng_inst = &bank->rngs[preferred_inst_offset]; #ifdef WC_RNG_HAVE_NEXT_SEED - if (((flags | bank->flags) & WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) && - (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY)) && - (! ((flags | bank->flags) & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE))) + if ((recovered_claim != 0) || + (((flags | bank->flags) & WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) && + (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY)) && + (! ((flags | bank->flags) & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE)))) { /* Consume a ready banked next seed, if any, BEFORE the * usability evaluation below, so that evaluation judges the @@ -746,8 +811,15 @@ WOLFSSL_API int wc_rng_bank_checkout( * out under incumbent bare-targeted semantics -- * identically to any other out-of-service instance. */ - (void)wc_RNG_DRBG_NextSeedNow( - WC_RNG_BANK_INST_TO_RNG(*rng_inst)); + if (wc_RNG_DRBG_NextSeedNow( + WC_RNG_BANK_INST_TO_RNG(*rng_inst)) == 0) + { +#ifndef WC_RNG_HAVE_LOCK + /* consumption is a credited reseed; mirror the + * in-boundary clear (see wc_rng_bank_reseed_range()). */ + (void)wc_rng_bank_inst_lock_clear_invalidated(*rng_inst); +#endif + } } #endif /* WC_RNG_HAVE_NEXT_SEED */ @@ -797,7 +869,12 @@ WOLFSSL_API int wc_rng_bank_checkout( (wc_RNG_DRBG_GetReseedCtr( WC_RNG_BANK_INST_TO_RNG(*rng_inst), &cur_reseed_ctr) == 0) && - (cur_reseed_ctr >= WC_RESEED_INTERVAL))))) + (cur_reseed_ctr >= WC_RESEED_INTERVAL) + #ifdef WC_RNG_HAVE_NEXT_SEED + && (wc_RNG_DRBG_NextSeedCurrent(WC_RNG_BANK_INST_TO_RNG(*rng_inst), &NextSeedCurrent) == 0) + && (NextSeedCurrent != WC_DRBG_NEXT_SEED_READY) + #endif + )))) { if (inst_unusable) diverted_unusable = 1; @@ -808,7 +885,8 @@ WOLFSSL_API int wc_rng_bank_checkout( #ifdef WC_VERBOSE_RNG if ((! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) && (! (flags & (WC_RNG_BANK_FLAG_CAN_WAIT | - WC_RNG_BANK_FLAG_FOR_RECOVERY))) && + WC_RNG_BANK_FLAG_FOR_RECOVERY | + WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY))) && (wc_RNG_DRBG_GetReseedCtr( WC_RNG_BANK_INST_TO_RNG(*rng_inst), &cur_reseed_ctr) == 0) && @@ -859,6 +937,44 @@ WOLFSSL_API int wc_rng_bank_checkout( } #endif /* WOLFSSL_USE_SAVE_VECTOR_REGISTERS */ + if (maybe_recovery_claim) { + WC_RNG_lock_arg_t claimed_lock_state = 0; + if ((wc_rng_bank_inst_lock_read(*rng_inst, + &claimed_lock_state) == 0) + && (claimed_lock_state & + WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { +#ifdef WC_RNG_HAVE_NEXT_SEED + /* material may have raced in since the admission + * scan: a successful banked consume cures the + * instance and downgrades this to an ordinary + * checkout. */ + if (wc_RNG_DRBG_NextSeedNow( + WC_RNG_BANK_INST_TO_RNG(*rng_inst)) != 0) +#endif + { + /* The robust-mutex (EOWNERDEAD) pattern: an + * error return WITH the acquisition complete + * and persistent. As with pthread robust + * mutexes, "this resource needs consistency + * recovery" is only safely reportable to a + * caller that already holds it -- reporting + * without the lease races the diagnosis + * against concurrent state changes, and + * leasing without the report invites blind + * use of unrecovered state. The caller owns + * the lease: recover (credited reseed clears + * the quarantine) or check in. + * + * NOT via out: -- that's the failure path, + * which unwinds refcount and affinity state. + * This return holds everything the success + * return below holds. */ + return NEEDS_RECOVERY_E; + } + } + } + return 0; /* Short-circuit return, holding onto bank refcount, * RNG lock, affinity locks, and (if applicable) * vector register inhibition. @@ -964,6 +1080,16 @@ WOLFSSL_API int wc_rng_bank_checkout( return ret; } +WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, + wc_rng_bank_free_hook_cb_t free_hook, void *arg) +{ + if (bank == NULL) + return BAD_FUNC_ARG; + bank->free_hook = free_hook; + bank->free_hook_arg = arg; + return 0; +} + #ifdef WC_HAVE_RNG_BANKREF /* wc_local_rng_bank_checkout_for_bankref() is the shim to the real WC_RNG when @@ -1124,6 +1250,7 @@ WOLFSSL_API int wc_rng_bank_checkin( #endif if (ret == WC_NO_ERR_TRACE(OBJECT_NOT_LOCKED_E)) return ret; + /* else NEEDS_RECOVERY_E -- proceed with check-in. */ } *rng_inst = NULL; @@ -1495,6 +1622,8 @@ static int rng_bank_spawn( { word32 child_init_flags = WC_RNG_INIT_FLAGS_NONE; + if (flags & WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE) + child_init_flags |= WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; if (leaf_stack != NULL) { ret = wc_InitRngNonceRBGC(leaf_stack, WC_RNG_BANK_INST_TO_RNG(rng_inst), @@ -1565,10 +1694,11 @@ WOLFSSL_API int wc_rng_bank_spawn_new( #endif /* !WC_NO_CONSTRUCTORS */ #endif /* WC_RNG_HAVE_RBGC */ -WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, - const byte* seed, word32 seedSz, - int timeout_secs, - word32 flags) +WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, + int first_inst, int last_inst, + const byte* seed, word32 seedSz, + int timeout_secs, + word32 flags) { int ret = 0; int n; @@ -1586,6 +1716,9 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, WC_RNG_BANK_FLAG_FOR_RECOVERY)) return BAD_FUNC_ARG; + if (first_inst < 0) + return BAD_INDEX_E; + if (bank == NULL) { #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (seedSz == 0) { @@ -1609,10 +1742,25 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, return 0; } + if (first_inst >= bank->n_rngs) { + ret = BAD_INDEX_E; + goto out; + } + if (last_inst < 0) + last_inst = bank->n_rngs - 1; + else if (last_inst >= bank->n_rngs) { + ret = BAD_INDEX_E; + goto out; + } + else if (last_inst < first_inst) { + ret = BAD_INDEX_E; + goto out; + } + /* This iteration counts down, whereas the iteration in get_drbg() counts * up, to assure they can't possibly phase-lock to each other. */ - for (n = bank->n_rngs - 1; n >= 0; --n) { + for (n = last_inst; n >= first_inst; --n) { struct wc_rng_bank_inst *drbg; ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, flags & ~(word32) @@ -1666,6 +1814,8 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, break; } +out: + #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (bank_is_default) (void)wc_rng_bank_default_checkin(&bank); @@ -1674,9 +1824,19 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, return ret; } -WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, - int timeout_secs, - word32 flags) +WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, + const byte* seed, word32 seedSz, + int timeout_secs, + word32 flags) +{ + return wc_rng_bank_seed_range(bank, 0, -1, seed, seedSz, timeout_secs, + flags); +} + +WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, + int first_inst, int last_inst, + int timeout_secs, + word32 flags) { int n; int ret; @@ -1699,6 +1859,9 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, WC_RNG_BANK_FLAG_FOR_RECOVERY)) return BAD_FUNC_ARG; + if (first_inst < 0) + return BAD_INDEX_E; + if (bank == NULL) { #ifdef WC_RNG_BANK_DEFAULT_SUPPORT ret = wc_rng_bank_default_checkout(&bank); @@ -1714,13 +1877,29 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, return BAD_STATE_E; } + if (first_inst >= bank->n_rngs) { + ret = BAD_INDEX_E; + goto out; + } + if (last_inst < 0) + last_inst = bank->n_rngs - 1; + else if (last_inst >= bank->n_rngs) { + ret = BAD_INDEX_E; + goto out; + } + else if (last_inst < first_inst) { + ret = BAD_INDEX_E; + goto out; + } + if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) ts1 = XTIME(0); - for (n = bank->n_rngs - 1; n >= 0; --n) { + for (n = last_inst; n >= first_inst; --n) { struct wc_rng_bank_inst *drbg; - ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, flags); + ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, + flags | WC_RNG_BANK_FLAG_FOR_RECOVERY); if (ret != 0) goto out; @@ -1750,8 +1929,14 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, time_t ts2; ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); - if (ret == 0) + if (ret == 0) { +#ifndef WC_RNG_HAVE_LOCK + /* the pre-lock boundary can't see the inst-side latch: + * mirror the in-boundary clear-on-credited-reseed. */ + (void)wc_rng_bank_inst_lock_clear_invalidated(drbg); +#endif break; + } if ((timeout_secs == 0) || (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT))) { @@ -1762,8 +1947,8 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, if (ts2 - ts1 > timeout_secs) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( - "ERROR: timeout after attempted reseed by " - "wc_RNG_GenerateBlock() for DRBG #%d, err %d.", n, ret); + "ERROR: timeout trying wc_RNG_DRBG_Reseed_Now() " + "for DRBG #%d, err %d.", n, ret); #endif ret = WC_TIMEOUT_E; break; @@ -1818,6 +2003,56 @@ WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, return ret; } +WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, + int timeout_secs, + word32 flags) +{ + return wc_rng_bank_reseed_range(bank, 0, -1, timeout_secs, flags); +} + +WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, + word32 flags) +{ + int n; + int ret = 0; +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + int bank_is_default = 0; +#endif + + if (flags != 0) + return BAD_FUNC_ARG; + + if (bank == NULL) { +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + ret = wc_rng_bank_default_checkout(&bank); + if (ret != 0) + return ret; + bank_is_default = 1; +#else + return BAD_FUNC_ARG; +#endif + } + else { + if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) + return BAD_STATE_E; + } + + /* Best-effort-complete: an error on one instance must not leave the + * rest un-flagged. First error wins the return. */ + for (n = 0; n < bank->n_rngs; n++) { + int this_ret = wc_rng_bank_inst_invalidate_entropy(&bank->rngs[n]); + if ((this_ret != 0) && (ret == 0)) + ret = this_ret; + } + +#ifdef WC_RNG_BANK_DEFAULT_SUPPORT + if (bank_is_default) + (void)wc_rng_bank_default_checkin(&bank); +#endif + + return ret; +} + #ifdef WC_HAVE_RNG_BANKREF static int wc_InitRng_BankRef_local(struct wc_rng_bank *bank, WC_RNG **rng) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index e4316573b5f..31d974a4870 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -947,6 +947,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); #ifdef WC_RNG_HAVE_RBGC WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); #endif +#ifdef WC_RNG_BANK_SUPPORT +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); +#endif #ifdef WC_RNG_HAVE_NEXT_SEED WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); #endif @@ -2629,6 +2632,12 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ else TEST_PASS("RNGRBGC test passed!\n"); #endif +#ifdef WC_RNG_BANK_SUPPORT + if ((ret = rng_entropy_invalidate_test()) != 0) + TEST_FAIL("RNGINVAL test failed!\n", ret); + else + TEST_PASS("RNGINVAL test passed!\n"); +#endif #ifdef WC_RNG_HAVE_NEXT_SEED if ((ret = rng_drbg_nextseed_test()) != 0) TEST_FAIL("RNGNXTS test failed!\n", ret); @@ -29200,6 +29209,558 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) #endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && */ /* (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ +/* Unit coverage for WC_RNG_LOCK_ENTROPY_INVALIDATED and the + * invalidation-recovery protocol (VM fork / resume), exercised through the + * regime-portable bank-instance latch interface so it runs identically + * against the in-boundary (v7+) lock and the pre-v7 rng_bank.h shims. + * Covers: flag set/read, lock refusal and conditional-claim recovery, + * credited-vs-uncredited clearing, put-side reporting with the flag riding + * through release, put_conditional convergence under a mid-hold + * invalidation, clear_extra immunity, and (v7+) unlocked recovery through + * the forced credited reseed, banked-next-seed purge, and RBGC chain + * recovery. */ +#ifdef WC_RNG_BANK_SUPPORT +#ifdef WC_RNG_HAVE_FREE_HOOK +static int rng_inval_test_bank_hook_fired = 0; +static int rng_inval_test_bank_hook_cb(const struct wc_rng_bank *bank, + void *arg) +{ + (void)bank; + if (arg == (void *)&rng_inval_test_bank_hook_fired) + rng_inval_test_bank_hook_fired++; + return 0; +} +static int rng_inval_test_hook_fired = 0; +static int rng_inval_test_hook_cb(const WC_RNG *rng, void *arg) +{ + (void)rng; + if (arg == (void *)&rng_inval_test_hook_fired) + rng_inval_test_hook_fired++; + return 0; +} +#endif + +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) +{ + wc_test_ret_t ret = 0; + int api_ret; + WC_DECLARE_VAR(bank, struct wc_rng_bank, 1, HEAP_HINT); + struct wc_rng_bank_inst *inst = NULL; + struct wc_rng_bank_inst *held = NULL; + WC_RNG_lock_arg_t lock_state; + byte block[32]; + int bank_inited = 0; +#ifdef WC_RNG_HAVE_RBGC + WC_RNG root; + int root_inited = 0; +#endif + const WC_RNG_lock_arg_t annot_a = (1U << WC_RNG_LOCK_EXTRA_SHIFT); + + WOLFSSL_ENTER("rng_entropy_invalidate_test"); + + WC_CALLOC_VAR_EX(bank, struct wc_rng_bank, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, + return WC_TEST_RET_ENC_EC(MEMORY_E)); + + api_ret = wc_rng_bank_init(bank, WC_RNG_BANK_STATIC_SIZE, + WC_RNG_BANK_FLAG_CAN_WAIT, 10, HEAP_HINT, + INVALID_DEVID); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + bank_inited = 1; +#ifdef WC_RNG_HAVE_FREE_HOOK + rng_inval_test_bank_hook_fired = 0; + api_ret = wc_rng_bank_register_free_hook(bank, rng_inval_test_bank_hook_cb, + (void *)&rng_inval_test_bank_hook_fired); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#endif + + if (wc_rng_bank_inst_invalidate_entropy(NULL) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* take a working instance the front way; keep the pointer past the + * check-in (the storage is the bank's). */ + api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + held = inst; + api_ret = wc_rng_bank_checkin(bank, &inst); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* fresh instance: flag clear. */ + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* invalidate while unheld: flag set, reseed scheduled. */ + api_ret = wc_rng_bank_inst_invalidate_entropy(held); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (! (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (lock_state & WC_RNG_LOCK_HELD) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#if (!defined(HAVE_INTEL_RDSEED) && !defined(HAVE_INTEL_RDRAND)) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) + { + wc_drbg_reseed_ctr_t reseed_ctr = 0; + api_ret = wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(held), + &reseed_ctr); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (reseed_ctr < (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } +#endif + + /* lock refusal on both get flavors; conditional claim carries the + * flag through acquisition. */ + api_ret = wc_rng_bank_inst_lock_get(held, 0); + if (api_ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_get_conditional(held, 0, 0); + if (api_ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_get_conditional( + held, WC_RNG_LOCK_ENTROPY_INVALIDATED, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if ((! (lock_state & WC_RNG_LOCK_HELD)) || + (! (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED))) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* an uncredited reseed must not clear the flag; a credited one must. */ + XMEMSET(block, 0x5a, sizeof(block)); + api_ret = wc_RNG_DRBG_Reseed_Uncredited(WC_RNG_BANK_INST_TO_RNG(held), + block, sizeof(block)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (! (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_rng_bank_inst_reseed_now(held, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_rng_bank_inst_lock_put(held); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* mid-hold invalidation: put reports NEEDS_RECOVERY_E, the flag rides + * through the release. */ + api_ret = wc_rng_bank_inst_lock_get(held, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_invalidate_entropy(held); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_put(held); + if (api_ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if ((lock_state & WC_RNG_LOCK_HELD) || + (! (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED))) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* the flag is not clearable through the annotation interface. */ + api_ret = wc_rng_bank_inst_lock_get_conditional( + held, WC_RNG_LOCK_ENTROPY_INVALIDATED, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_clear_extra( + held, WC_RNG_LOCK_ENTROPY_INVALIDATED); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (! (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_inst_lock_clear_extra(held, WC_RNG_LOCK_REQUIRED) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* put_conditional under the flag: converges promptly to + * NEEDS_RECOVERY_E (regression probe for the expected-reconstruction + * refresh), releases, and preserves the flag. */ + api_ret = wc_rng_bank_inst_lock_set_extra(held, annot_a); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + /* bank instances are born REQUIRED; the conditional release must + * report the sticky bit among the expected extras, as check-in does. */ + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_put_conditional(held, + annot_a | (lock_state & WC_RNG_LOCK_REQUIRED)); + if (api_ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if ((lock_state & WC_RNG_LOCK_HELD) || + (lock_state & annot_a) || + (! (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED))) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + +#ifdef WC_RNG_HAVE_RBGC + /* credited-chain recovery: an RBGC reseed from a healthy root clears + * the flag, exactly as a primary reseed does. */ + api_ret = wc_InitRng(&root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + root_inited = 1; + api_ret = wc_rng_bank_inst_lock_get_conditional( + held, WC_RNG_LOCK_ENTROPY_INVALIDATED, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_reseed_rbgc(held, &root, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_rng_bank_inst_lock_put(held); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#else + /* recover for a clean teardown. */ + api_ret = wc_rng_bank_inst_lock_get_conditional( + held, WC_RNG_LOCK_ENTROPY_INVALIDATED, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_reseed_now(held, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_put(held); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#endif /* WC_RNG_HAVE_RBGC */ + + /* bank-wide invalidation, and recovery via credited bank reseed. */ + api_ret = wc_rng_bank_invalidate_entropy(bank, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (! (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_rng_bank_invalidate_entropy(bank, WC_RNG_BANK_FLAG_QUIET) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + api_ret = wc_rng_bank_reseed_range(bank, 0, -1, 10, + WC_RNG_BANK_FLAG_CAN_WAIT); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + +#if defined(WC_RNG_HAVE_LOCK) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) + /* v7+ in-boundary extras: unlocked recovery through the generate + * path's forced credited reseed, and the banked-next-seed purge. */ + { + WC_RNG* held_rng = WC_RNG_BANK_INST_TO_RNG(held); + + api_ret = wc_RNG_invalidate_entropy(held_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_get_conditional( + held, WC_RNG_LOCK_ENTROPY_INVALIDATED, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_GenerateBlock(held_rng, block, sizeof(block)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef WC_RNG_HAVE_NEXT_SEED + api_ret = wc_RNG_DRBG_NextSeedGenerate(held_rng, WC_DRBG_NEXT_SEED_LEN); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#ifdef WC_RNG_HAVE_RBGC + api_ret = wc_RNG_DRBG_GetNextSeedRBGCStratum(held_rng); + if (api_ret < 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#endif + api_ret = wc_RNG_invalidate_entropy(held_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#ifdef WC_RNG_HAVE_RBGC + api_ret = wc_RNG_DRBG_GetNextSeedRBGCStratum(held_rng); + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#endif + api_ret = wc_rng_bank_inst_reseed_now(held, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#endif /* WC_RNG_HAVE_NEXT_SEED */ + api_ret = wc_rng_bank_inst_lock_put(held); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } + +#ifdef WC_RNG_HAVE_FREE_HOOK + /* free-hook lifecycle: register, fire-on-free (one-shot, arg intact), + * NULL-unregister. */ + { + WC_RNG hook_rng; + api_ret = wc_InitRng(&hook_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (wc_RNG_register_free_hook(NULL, rng_inval_test_hook_cb, NULL) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + api_ret = wc_RNG_register_free_hook(&hook_rng, rng_inval_test_hook_cb, + (void *)&rng_inval_test_hook_fired); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + rng_inval_test_hook_fired = 0; + api_ret = wc_FreeRng(&hook_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (rng_inval_test_hook_fired != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(rng_inval_test_hook_fired), out); + /* NULL-unregister: no fire on free. */ + api_ret = wc_InitRng(&hook_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_register_free_hook(&hook_rng, rng_inval_test_hook_cb, + (void *)&rng_inval_test_hook_fired); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_register_free_hook(&hook_rng, NULL, NULL); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_FreeRng(&hook_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (rng_inval_test_hook_fired != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(rng_inval_test_hook_fired), out); + } +#endif /* WC_RNG_HAVE_FREE_HOOK */ + +#if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) && \ + !defined(HAVE_INTEL_RDSEED) && !defined(HAVE_INTEL_RDRAND) + /* WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED: + * recovery-consumption and chain-promotion at generate. */ + { + WC_RNG flag_rng; + api_ret = wc_InitRng_ex2(&flag_rng, HEAP_HINT, INVALID_DEVID, + WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + /* recovery: invalidate (purges the aperture), bank a fresh primary + * seed post-event, and generate -- consumption clears the flag. */ + api_ret = wc_RNG_invalidate_entropy(&flag_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedGenerate(&flag_rng, + WC_DRBG_NEXT_SEED_LEN); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_GenerateBlock(&flag_rng, block, sizeof(block)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_lock_read(&flag_rng, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&flag_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_FreeRng(&flag_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#ifdef WC_RNG_HAVE_RBGC + /* promotion: chain-backed leaf with a banked primary seed + * upgrades to stratum 0 at generate; without the flag it must + * not. */ + { + WC_RNG proot; + api_ret = wc_InitRng(&proot); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_InitRngNonceRBGC(&flag_rng, &proot, NULL, 0, + WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&flag_rng); + if (api_ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedGenerate(&flag_rng, + WC_DRBG_NEXT_SEED_LEN); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_GenerateBlock(&flag_rng, block, sizeof(block)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&flag_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_FreeRng(&flag_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + /* negative: unflagged leaf keeps its stratum. */ + api_ret = wc_InitRngNonceRBGC(&flag_rng, &proot, NULL, 0, + WC_RNG_INIT_FLAGS_NONE); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedGenerate(&flag_rng, + WC_DRBG_NEXT_SEED_LEN); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_GenerateBlock(&flag_rng, block, sizeof(block)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&flag_rng); + if (api_ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_FreeRng(&flag_rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_FreeRng(&proot); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } +#endif /* WC_RNG_HAVE_RBGC */ + } +#endif /* NEXT_SEED && RBGC && !RDSEED && !RDRAND */ +#endif /* WC_RNG_HAVE_LOCK && (!HAVE_FIPS || >= 7.0.0) */ + +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_RNG_HAVE_LOCK) + /* WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY contract: checkout of a + * quarantined instance returns NEEDS_RECOVERY_E with the lease held; + * a credited reseed by the lease-holder recovers it. */ + { + struct wc_rng_bank *mb = NULL; + struct wc_rng_bank_inst *minst = NULL; + WC_RNG_lock_arg_t mlock = 0; + + api_ret = wc_rng_bank_new(&mb, 2, WC_RNG_BANK_FLAG_NONE, 0, HEAP_HINT, + INVALID_DEVID); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_invalidate_entropy(mb, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + /* without the flag: refused (retry disabled: timeout_secs 0). */ + api_ret = wc_rng_bank_checkout(mb, &minst, 0, 0, + WC_RNG_BANK_FLAG_NONE); + if (api_ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (minst != NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* with the flag: leased-but-quarantined. */ + api_ret = wc_rng_bank_checkout(mb, &minst, 0, 0, + WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY); + if (api_ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (minst == NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_rng_bank_inst_lock_read(minst, &mlock); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (! (mlock & WC_RNG_LOCK_HELD)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (! (mlock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* the lease-holder recovers: credited reseed clears quarantine. */ + api_ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(minst), + NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_lock_read(minst, &mlock); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (mlock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(minst), block, + sizeof(block)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_checkin(&minst); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_free(&mb); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } +#endif /* WC_RNG_BANK_DEFAULT_SUPPORT && WC_RNG_HAVE_LOCK */ + + out: + + if (held != NULL) + (void)wc_rng_bank_inst_lock_put(held); + +#ifdef WC_RNG_HAVE_RBGC + if (root_inited) { + api_ret = wc_FreeRng(&root); + if ((ret == 0) && (api_ret != 0)) + ret = WC_TEST_RET_ENC_EC(api_ret); + } +#endif + if (bank_inited) { + api_ret = wc_rng_bank_fini(bank); + if ((ret == 0) && (api_ret != 0)) + ret = WC_TEST_RET_ENC_EC(api_ret); +#ifdef WC_RNG_HAVE_FREE_HOOK + if ((ret == 0) && (api_ret == 0) && + (rng_inval_test_bank_hook_fired != 1)) + { + ret = WC_TEST_RET_ENC_I(rng_inval_test_bank_hook_fired); + } +#endif + } + WC_FREE_VAR_EX(bank, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} +#endif /* WC_RNG_BANK_SUPPORT */ + #ifdef WC_RNG_HAVE_RBGC #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index 8b24689654f..73682bb5c89 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -264,6 +264,7 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); #endif extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); +extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); #endif /* WC_NO_RNG */ diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index f5560fbda32..511c1bdb73f 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -75,6 +75,14 @@ #endif #endif +#ifndef WC_RNG_NO_FREE_HOOK + #define WC_RNG_HAVE_FREE_HOOK +#endif +#ifdef WC_RNG_HAVE_FREE_HOOK + struct WC_RNG; /* tag forward-declaration for the callback signature */ + typedef int (*wc_RNG_free_hook_cb_t)(const struct WC_RNG *rng, void *arg); +#endif + #ifndef WC_RNG_NO_LOCK #ifndef WC_RNG_HAVE_LOCK #define WC_RNG_HAVE_LOCK @@ -444,6 +452,7 @@ enum wc_RngHealthState { #define WC_RNG_FLAG_RBGC_NEXT_SEED (1U << 0) #define WC_RNG_FLAG_FULL_MUTEX (1U << 1) #define WC_RNG_FLAG_BANKREF (1U << 2) +#define WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3) /* RNG context */ @@ -461,6 +470,12 @@ struct WC_RNG { wolfSSL_Mutex mutex; #endif #endif +#ifdef WC_RNG_HAVE_FREE_HOOK + /* fired by wc_FreeRng() before state destruction (one-shot); + * see wc_RNG_register_free_hook(). */ + wc_RNG_free_hook_cb_t free_hook; + void *free_hook_arg; +#endif #ifdef WC_RNG_HAVE_POOL byte* pool; word16 poolSize; @@ -665,6 +680,12 @@ WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz) #define WC_RNG_INIT_FLAGS_LOCK_REQUIRED (1U << 0) #define WC_RNG_INIT_FLAGS_LOCK_INITIALLY (1U << 1) #define WC_RNG_INIT_FLAGS_USE_FULL_MUTEX (1U << 2) +/* At each generate, if a banked next seed is READY, consume it when the + * instance is flagged _ENTROPY_INVALIDATED (recovery; any provenance), or + * when the instance is chain-backed and the banked seed is primary + * (promotion). For externally-refreshed long-lived RNGs, e.g. the kernel + * module's registered RBGC leaves. */ +#define WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3) WOLFSSL_API int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags); @@ -924,8 +945,20 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); WC_RNG_lock_arg_t extra_bits); WOLFSSL_API int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng); #endif /* WC_RNG_HAVE_LOCK */ +#ifdef WC_RNG_HAVE_FREE_HOOK +/* Register a callback fired by wc_FreeRng() immediately before state + * destruction, e.g. to unlink the object from an external registry. + * One-shot: cleared before firing. A NULL free_hook unregisters. + * Reinitialization (wc_InitRng*() on a live object) clears any + * registered hook without firing it: hooks are per-lifetime. */ +WOLFSSL_API int wc_RNG_register_free_hook(WC_RNG* rng, + wc_RNG_free_hook_cb_t free_hook, + void *arg); +#endif + #ifdef WC_RNG_HAVE_POOL WOLFSSL_API int wc_RNG_Pool_Alloc(WC_RNG* rng, word32 size); WOLFSSL_API int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n); diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 13f00392181..26fcdf7cc93 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -107,7 +107,20 @@ * provably quiesces consumers first may set it. */ #define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 11) #define WC_RNG_BANK_FLAG_INIT_RBGC (1U << 12) +/* WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY admits the caller to a quarantined + * (WC_RNG_LOCK_ENTROPY_INVALIDATED) instance when no cheaper admission + * applies, accepting the recovery obligation: wc_rng_bank_checkout() may + * then return NEEDS_RECOVERY_E with the checkout otherwise complete -- + * *rng_inst set, instance lock (and any affinity/vector-inhibit state) + * HELD. The caller owns the lease and must either recover the instance + * (a credited reseed, e.g. wc_RNG_DRBG_Reseed_Now(), clears the + * quarantine) or check it back in. Ordinary consumers that cannot + * complete a recovery must not pass this flag. */ +#define WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY (1U << 13) #define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 14) +/* wc_rng_bank_spawn[_new]() only: the child is born with + * WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED. */ +#define WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE (1U << 15) /* base lock states are WC_RNG_LOCK_FREE / WC_RNG_LOCK_HELD in random.h; * these annotation bits ride above WC_RNG_LOCK_HELD via @@ -137,6 +150,9 @@ typedef int (*wc_affinity_unlock_fn_t)(void *arg); struct wc_rng_bank; +typedef int (*wc_rng_bank_free_hook_cb_t)(const struct wc_rng_bank *bank, + void *arg); + struct wc_rng_bank_inst { #ifdef WC_RNG_HAVE_LOCK /* the exclusivity latch lives in rng.lock (wc_RNG_lock_*()) -- @@ -166,6 +182,10 @@ struct wc_rng_bank { wolfSSL_Ref refcount; void *heap; word32 flags; + /* fired by wc_rng_bank_fini() after its gates pass, before teardown + * (one-shot); see wc_rng_bank_register_free_hook(). */ + wc_rng_bank_free_hook_cb_t free_hook; + void *free_hook_arg; wc_affinity_lock_fn_t affinity_lock_cb; wc_affinity_get_id_fn_t affinity_get_id_cb; wc_affinity_unlock_fn_t affinity_unlock_cb; @@ -362,10 +382,38 @@ WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, int timeout_secs, word32 flags); +WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, + int first_inst, int last_inst, + const byte* seed, word32 seedSz, + int timeout_secs, + word32 flags); + WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, int timeout_secs, word32 flags); +WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, + int first_inst, int last_inst, + int timeout_secs, + word32 flags); + +/* Set WC_RNG_LOCK_ENTROPY_INVALIDATED on every instance (see + * wc_RNG_invalidate_entropy()): cached entropy products are discarded, and + * each instance is forced through a credited reseed before its next + * generate serves output. Lock-free and constant-time per instance; safe + * from the state-invalidation event context. Walks every instance even on + * error, returning the first error. flags must be 0. */ +WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, + word32 flags); + + +/* Register a callback fired by wc_rng_bank_fini() once its refcount and + * leak gates pass -- i.e. once teardown is committed -- e.g. to unlink the + * bank from an external registry. One-shot: cleared before firing. A + * NULL free_hook unregisters. */ +WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, + wc_rng_bank_free_hook_cb_t free_hook, void *arg); + #ifdef WC_HAVE_RNG_BANKREF WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng); @@ -415,7 +463,8 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); /* Backward compat: with a pre-v7 FIPS boundary (or WC_RNG_NO_LOCK), the * latch lives in the bank instance rather than in the (frozen) WC_RNG. - * These are ports of the wc_RNG_lock_*() state machine. + * These are ports of the wc_RNG_lock_*() state machine, including + * WC_RNG_LOCK_ENTROPY_INVALIDATED quarantine/claim/report semantics. * In every CAS below, the stored value derives only from the CAS-verified * value and the caller's arguments -- never from a prior load. */ @@ -431,6 +480,9 @@ static WC_INLINE int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && (wolfSSL_Atomic_Uint_CompareExchange( &inst->lock, &cur_lock, @@ -439,7 +491,9 @@ static WC_INLINE int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC return 0; } - if (cur_lock & WC_RNG_LOCK_HELD) + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else if (cur_lock & WC_RNG_LOCK_HELD) return BUSY_E; else return UNEXPECTED_STATE_E; @@ -455,14 +509,20 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_get_conditional( return BAD_FUNC_ARG; expected_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | - WC_RNG_LOCK_REQUIRED; + WC_RNG_LOCK_REQUIRED | WC_RNG_LOCK_ENTROPY_INVALIDATED; want_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | WC_RNG_LOCK_REQUIRED; cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && + (! (expected_extra_bits & WC_RNG_LOCK_ENTROPY_INVALIDATED))) + { + return NEEDS_RECOVERY_E; + } + expected = (cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & - ~WC_RNG_LOCK_HELD)) | + ~(WC_RNG_LOCK_HELD | WC_RNG_LOCK_ENTROPY_INVALIDATED))) | expected_extra_bits; if ((! (cur_lock & WC_RNG_LOCK_HELD)) && @@ -473,7 +533,12 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_get_conditional( return 0; } - if (expected & WC_RNG_LOCK_HELD) + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) != + (expected & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + return NEEDS_RECOVERY_E; + } + else if (expected & WC_RNG_LOCK_HELD) return BUSY_E; else return UNEXPECTED_STATE_E; @@ -496,7 +561,10 @@ static WC_INLINE int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) break; } - return 0; + if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else + return 0; } static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) @@ -515,18 +583,25 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_put_conditional(struc ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD))) | (extra_bits & WC_RNG_LOCK_REQUIRED); - expected = WC_RNG_LOCK_HELD | extra_bits; + expected = WC_RNG_LOCK_HELD | extra_bits | + (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); if (wolfSSL_Atomic_Uint_CompareExchange( &inst->lock, &expected, new_lock)) { - return 0; + if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else + return 0; } if ((expected & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U)) != (extra_bits & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U))) { break; } + /* the CAS's failure feedback flows through expected; reseed the + * next reconstruction from it, else a concurrent invalidation + * loops forever. */ cur_lock = expected; } /* conditional release failed: the caller is still the holder. */ @@ -831,12 +906,21 @@ WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Now( #endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ -/* Portable reseed helpers: with the in-boundary latch (WC_RNG_HAVE_LOCK) - * these merely forward to the random.c services; with the bank-side latch - * they forward through the compat shims. */ +/* Portable invalidation-recovery helpers. With the in-boundary latch + * (WC_RNG_HAVE_LOCK), invalidation and clear-on-credited-reseed are + * module-enforced and these merely forward; with the bank-side latch, + * the bit is set and cleared out here, clearing only on credited + * reseeds, under the lease, mirroring the in-boundary semantics. */ #ifdef WC_RNG_HAVE_LOCK +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_invalidate_entropy( + struct wc_rng_bank_inst *inst) +{ + if (inst == NULL) + return BAD_FUNC_ARG; + return wc_RNG_invalidate_entropy(WC_RNG_BANK_INST_TO_RNG(inst)); +} static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz) { @@ -859,6 +943,44 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_rbgc( #else /* !WC_RNG_HAVE_LOCK */ +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_clear_invalidated( + struct wc_rng_bank_inst *inst) +{ + WC_RNG_lock_arg_t cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock & ~WC_RNG_LOCK_ENTROPY_INVALIDATED)) + break; + } + return 0; +} + +static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_invalidate_entropy( + struct wc_rng_bank_inst *inst) +{ + WC_RNG_lock_arg_t cur_lock; + + if (inst == NULL) + return BAD_FUNC_ARG; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_ENTROPY_INVALIDATED)) + break; + } + + /* If no lock is held, the saturated reseedCtr is the only way to force + * invalidation semantics on a lock-free consumer; if a lock is held, + * the holder learns at unlock time. */ + if (! (cur_lock & WC_RNG_LOCK_HELD)) + (void)wc_RNG_DRBG_ScheduleReseed(WC_RNG_BANK_INST_TO_RNG(inst)); + + return 0; +} + static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz) { @@ -867,6 +989,8 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( return BAD_FUNC_ARG; ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), nonce, nonceSz); + if (ret == 0) + (void)wc_rng_bank_inst_lock_clear_invalidated(inst); return ret; } @@ -889,6 +1013,8 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_rbgc( ret = wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root, nonce, nonceSz); #endif + if (ret == 0) + (void)wc_rng_bank_inst_lock_clear_invalidated(inst); return ret; } #endif /* WC_RNG_HAVE_RBGC */ From 7ba78036d682fe98952697cb1ef5b5fd071a9225 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:11:05 +0000 Subject: [PATCH 026/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/test/test.c: add the uncredited-nextseed stir facility: * a second, fixed-size aperture beside the banked next seed: wc_RNG_DRBG_NextUncreditedSeedStore() banks caller-supplied material (up to WC_DRBG_NEXT_UNCREDITED_SEED_LEN bytes) and wc_RNG_DRBG_NextUncreditedSeedNow() stirs it into the DRBG as an uncredited, source-free, atomic-context-safe reseed -- harvested entropy improves the instance without claiming credit or resetting the reseed counter; the hand-off reuses the nextSeedLen aperture protocol (WC_DRBG_nextSeedLen_t); * test.c: uncredited-aperture coverage in rng_drbg_nextseed_test(): store/stir protocol, non-resetting counter, coexistence with the credited bank, and the argument contracts. --- wolfcrypt/src/random.c | 168 ++++++++++++++++++++++++++++++++++++- wolfcrypt/test/test.c | 136 ++++++++++++++++++++++++++++++ wolfssl/wolfcrypt/random.h | 9 ++ 3 files changed, 309 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 1d4fc0e1823..bae2d3afa20 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -808,6 +808,14 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_NEXT_SEED) if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { WOLFSSL_ATOMIC_STORE(drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + /* the uncredited stir aperture is purged too, for provenance + * uniformity; best-effort (an in-flight depositor may + * resurrect a partial fill -- benign, stirs carry no + * divergence burden), and never zeroized (racy, and + * interleaved entropy of compatible provenance is harmless). + */ + WOLFSSL_ATOMIC_STORE(drbg->nextUncreditedSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); } #endif @@ -837,6 +845,9 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_NEXT_SEED) if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { WOLFSSL_ATOMIC_STORE(drbg512->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + /* see the SHA-256 arm re best-effort and no-zeroize. */ + WOLFSSL_ATOMIC_STORE(drbg512->nextUncreditedSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); } #endif @@ -1393,6 +1404,8 @@ static int Hash_DRBG_Instantiate(DRBG_internal* drbg, const byte* seed, XMEMSET(drbg, 0, sizeof(DRBG_internal)); #ifdef WC_RNG_HAVE_NEXT_SEED wolfSSL_Atomic_Int_Init(&drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + wolfSSL_Atomic_Int_Init(&drbg->nextUncreditedSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); #endif drbg->heap = heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -1906,6 +1919,8 @@ static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg, XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal)); #ifdef WC_RNG_HAVE_NEXT_SEED wolfSSL_Atomic_Int_Init(&drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + wolfSSL_Atomic_Int_Init(&drbg->nextUncreditedSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); #endif drbg->heap = heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -3152,11 +3167,13 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif #endif /* WC_RNG_HAVE_NEXT_SEED */ @@ -3776,6 +3793,27 @@ static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, return MISSING_RNG_E; } +static WC_INLINE int NextUncreditedSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, wolfSSL_Atomic_Int** len) +{ +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { + *seed = ((DRBG_internal*)rng->drbg)->nextUncreditedSeed; + *nextSeedSz = (word32)sizeof(((DRBG_internal*)rng->drbg)->nextUncreditedSeed); + *len = &((DRBG_internal*)rng->drbg)->nextUncreditedSeedLen; + return 0; + } +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { + *seed = ((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeed; + *nextSeedSz = (word32)sizeof(((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeed); + *len = &((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeedLen; + return 0; + } +#endif + return MISSING_RNG_E; +} + /* Bank up to n more bytes of seed material from a supplied root RNG into * rng's next-seed bank. Callable without owning the instance (the scheduling * daemon's entry point); deliberately independent of rng->status so that @@ -3785,7 +3823,7 @@ static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, * published; a failed test consumes the material (use-once) and returns the * test's error, leaving an empty bank for the next cycle. A gather failure * leaves the partial bank intact for retry. */ -static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 n) +static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, byte *nonce, word32 n) { byte* seed; wolfSSL_Atomic_Int* lenp; @@ -3797,6 +3835,9 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 if ((rng == NULL) || (n == 0)) return BAD_FUNC_ARG; + if ((root != NULL) && (nonce != NULL)) + return BAD_FUNC_ARG; + /* Note, rng need not be locked -- that's the whole point of the * banked-next-seed aperture protocol. */ @@ -3813,7 +3854,10 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 #endif } - ret = NextSeedPtrs(rng, &seed, &nextSeedSz, &lenp, &nextSeedRBGCStratum_p); + if (nonce) + ret = NextUncreditedSeedPtrs(rng, &seed, &nextSeedSz, &lenp); + else + ret = NextSeedPtrs(rng, &seed, &nextSeedSz, &lenp, &nextSeedRBGCStratum_p); if (ret != 0) { /* No DRBG instantiated -- nothing to bank (RDRAND et al.). */ return ret; @@ -3822,6 +3866,19 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 cur = *lenp; if ((cur < 0) || (cur >= (WC_ATOMIC_INT_ARG)nextSeedSz)) { + if (nonce) { + /* The accumulator is full (READY) or being consumed: fold the + * arriving entropy in rather than discarding it. The sentinel + * check is deliberately advisory in this lane -- a torn read by a + * racing consumer, or an XOR lost to a racing depositor, yields + * interleaved stir material of compatible provenance, which is + * always harmless. */ + if (n > nextSeedSz) + n = nextSeedSz; + xorbuf(seed, nonce, n); + return 0; + } + if (cur != (WC_ATOMIC_INT_ARG)nextSeedSz) { /* Ready, consuming, or other sentinel -- nothing to do. */ return WC_NO_ERR_TRACE(ALREADY_E); /* not an error */ @@ -3832,6 +3889,12 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 } if (n > 0) { + if (nonce != NULL) { + if (n > nextSeedSz - (word32)cur) + n = nextSeedSz - (word32)cur; + XMEMCPY(seed + cur, nonce, n); + } + else #ifdef WC_RNG_HAVE_RBGC if (root) { /* If primary seed bytes were carried forward, reset now to avoid @@ -3903,6 +3966,10 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 } if (cur == (WC_ATOMIC_INT_ARG)nextSeedSz) { + if (nonce != NULL) { + WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + return 0; + } #ifdef WC_RNG_HAVE_RBGC /* If RBGC bytes were used for the reseed, then we can skip * wc_RNG_TestSeed(). */ @@ -3946,12 +4013,12 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 int wc_RNG_DRBG_NextSeedGenerate_RBGC(WC_RNG* rng, WC_RNG *root, word32 n) { if (root == NULL) return BAD_FUNC_ARG; - return wc_RNG_DRBG_NextSeedGenerate_local(rng, root, n); + return wc_RNG_DRBG_NextSeedGenerate_local(rng, root, NULL, n); } #endif int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) { - return wc_RNG_DRBG_NextSeedGenerate_local(rng, NULL, n); + return wc_RNG_DRBG_NextSeedGenerate_local(rng, NULL, NULL, n); } /* Report the raw aperture value: a racy snapshot by design. Values in [0, bank @@ -4064,6 +4131,72 @@ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng) { return wc_RNG_DRBG_NextSeedNow_Nonce(rng, NULL, 0); } +/* Deposit raw uncredited stir material into rng's accumulator. Callable + * from any context and without owning the instance: the deposit protocol + * (read-copy-store) is multi-writer-tolerant -- every published span was + * written by its publisher, lost updates merely drop entropy, and + * interleaved fragments of compatible provenance are harmless. A full + * accumulator publishes WC_DRBG_NEXT_SEED_READY (no health test -- no + * claim is being made) and blocks further deposits until consumed. */ +int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, const byte *nonce, + word32 nonceSz) +{ + if ((nonce == NULL) || (nonceSz == 0)) + return BAD_FUNC_ARG; + /* _local's nonce arm only reads the buffer; the parameter is non-const + * for the benefit of the other arms. */ + return wc_RNG_DRBG_NextSeedGenerate_local(rng, NULL, (byte *)nonce, + nonceSz); +} + +/* Consume a ready uncredited accumulator in an immediate uncredited + * (stirring) reseed. The caller must own the instance. The credited=0 + * path holds the three no-ops by construction: the reseed counter is not + * reset, WC_RNG_LOCK_ENTROPY_INVALIDATED is not cleared, and RBGCStratum + * is unchanged -- a stir must never masquerade as recovery or promotion. + * Use-once: the material is consumed (accumulation reopens) whether or not + * the reseed succeeds. The buffer is never zeroized (racy against + * depositors, and always a net entropy loss). */ +int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng) +{ + byte* seed; + wolfSSL_Atomic_Int* lenp; + word32 nextSeedSz; + WC_ATOMIC_INT_ARG expected = WC_DRBG_NEXT_SEED_READY; + int ret; + + if (rng == NULL) + return BAD_FUNC_ARG; + + ret = rng_lock_required_check(rng); + if (ret != 0) + return ret; + + /* Mirror wc_RNG_GenerateBlock(): only an in-service DRBG may reseed. */ + if (rng->status != DRBG_OK) + return RNG_FAILURE_E; + + ret = NextUncreditedSeedPtrs(rng, &seed, &nextSeedSz, &lenp); + if (ret != 0) { + /* No DRBG instantiated -- nothing to stir (RDRAND et al.). */ + return ret; + } + + if (! wolfSSL_Atomic_Int_CompareExchange(lenp, &expected, + WC_DRBG_NEXT_SEED_CONSUMING)) + { + /* No ready accumulator -- nothing consumed; reported distinctly. */ + return NOT_READY_E; + } + + ret = wc_RNG_DRBG_Reseed_Uncredited(rng, seed, nextSeedSz); + + + WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); + + return ret; +} + #endif /* WC_RNG_HAVE_NEXT_SEED */ #endif /* HAVE_HASHDRBG */ @@ -4173,6 +4306,33 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) } #endif +#ifdef WC_RNG_HAVE_NEXT_SEED + /* Universal opportunistic stir: a READY uncredited accumulator is + * consumed by any generate, unconditionally -- stirs are always + * harmless, and are invisible to the credited legs above (no counter + * reset, no flag clear, no stratum change). One relaxed load when + * empty. */ + { + int stir_ready = 0; +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL) && + (WOLFSSL_ATOMIC_LOAD(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen) == WC_DRBG_NEXT_SEED_READY)) + { + stir_ready = 1; + } +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL) && + (WOLFSSL_ATOMIC_LOAD(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen) == WC_DRBG_NEXT_SEED_READY)) + { + stir_ready = 1; + } +#endif + if (stir_ready) + (void)wc_RNG_DRBG_NextUncreditedSeedNow(rng); + } +#endif /* WC_RNG_HAVE_NEXT_SEED */ + #ifndef NO_SHA256 if (rng->drbgType == WC_DRBG_SHA256) { ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 31d974a4870..536526dab65 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30343,6 +30343,142 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) ERROR_OUT(WC_TEST_RET_ENC_I((int)cur), out); } + /* --- uncredited stir aperture (NextUncreditedSeed) lifecycle --- */ + if (present) { + byte frag[16]; + XMEMSET(frag, 0x71, sizeof(frag)); + + if (wc_RNG_DRBG_NextUncreditedSeedStore(NULL, frag, sizeof(frag)) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + if (wc_RNG_DRBG_NextUncreditedSeedStore(root, NULL, 1) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + if (wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, 0) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + + /* partial accumulation is not consumable. */ + api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, + sizeof(frag)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* fill to the top: READY; excess deposits absorbed by xorbuf(); + * oversize deposits clamp. */ + for (i = 0; i < (int)(WC_DRBG_NEXT_UNCREDITED_SEED_LEN / + sizeof(frag)); i++) + { + api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, + sizeof(frag)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } + /* deposits on a READY accumulator fold in place using xorbuf() + * (advisory sentinel): absorbed, never refused. */ + api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, sizeof(frag)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + +#if !defined(HAVE_INTEL_RDSEED) && !defined(HAVE_INTEL_RDRAND) + /* consumption is a stir, not an epoch: the reseed counter is not + * reset. */ + { + wc_drbg_reseed_ctr_t ctr_before = 0, ctr_after = 0; + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &ctr_before); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &ctr_after); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (ctr_after < ctr_before) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } +#else + api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#endif + + /* use-once: accumulation reopened. */ + api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, + sizeof(frag)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* top up and verify the universal opportunistic consume at + * generate: post-generate, the accumulator is spent. */ + for (i = 0; i < (int)(WC_DRBG_NEXT_UNCREDITED_SEED_LEN / + sizeof(frag)); i++) + { + api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, + sizeof(frag)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } + api_ret = wc_RNG_GenerateBlock(root, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } + +#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_RBGC) + /* a stir must never masquerade as recovery or promotion: consumption + * preserves WC_RNG_LOCK_ENTROPY_INVALIDATED and RBGCStratum. */ + if (present) { + WC_RNG leaf; + WC_RNG_lock_arg_t lock_state; + byte frag64[WC_DRBG_NEXT_UNCREDITED_SEED_LEN]; + XMEMSET(frag64, 0x5e, sizeof(frag64)); + + api_ret = wc_InitRngNonceRBGC(&leaf, root, NULL, 0, + WC_RNG_INIT_FLAGS_NONE); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_invalidate_entropy(&leaf); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + /* deposit post-event (the event purge emptied the accumulator). */ + api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(&leaf, frag64, + sizeof(frag64)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(&leaf); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_lock_read(&leaf, &lock_state); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + if (! (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_RNG_DRBG_GetRBGCStratum(&leaf) != 1) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + /* recover for a clean teardown. */ + api_ret = wc_RNG_DRBG_Reseed_Now(&leaf, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_FreeRng(&leaf); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + } +#endif /* WC_RNG_HAVE_LOCK && WC_RNG_HAVE_RBGC */ out: diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 511c1bdb73f..a0c599a7146 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -374,6 +374,7 @@ struct OS_Seed { * source-fed (re)seeds in the module (gather SEED_SZ + SEED_BLOCK_SZ, apply * the block-offset remainder). */ #define WC_DRBG_NEXT_SEED_LEN (WC_DRBG_SEED_SZ + WC_DRBG_SEED_BLOCK_SZ) + #define WC_DRBG_NEXT_UNCREDITED_SEED_LEN 64 #endif struct DRBG_internal { @@ -390,6 +391,8 @@ struct DRBG_internal { #ifdef WC_RNG_HAVE_RBGC int nextSeedRBGCStratum; #endif + byte nextUncreditedSeed[WC_DRBG_NEXT_UNCREDITED_SEED_LEN]; + WC_DRBG_nextSeedLen_t nextUncreditedSeedLen; #endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -418,6 +421,8 @@ struct DRBG_SHA512_internal { #ifdef WC_RNG_HAVE_RBGC int nextSeedRBGCStratum; #endif + byte nextUncreditedSeed[WC_DRBG_NEXT_UNCREDITED_SEED_LEN]; + WC_DRBG_nextSeedLen_t nextUncreditedSeedLen; #endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -919,6 +924,10 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); const byte* nonce, word32 nonceSz); WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); + WOLFSSL_API int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, + const byte *nonce, + word32 nonceSz); + WOLFSSL_API int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng); #endif /* WC_RNG_HAVE_NEXT_SEED */ From bd15bfd1e2a89f19118d08ff4123b9d58c556d06 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:13:35 +0000 Subject: [PATCH 027/102] wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/test/test.c: add bank daemon registration and the default bank: * daemon protocol: wc_rng_bank_daemon_reserve() /_register() /_unregister() /_release(), a magic-word claim (WC_RNG_BANK_DAEMON_MAGIC_FREE when unclaimed) admitting exactly one scheduling daemon per bank, and wc_rng_bank_daemon_root_{set,get}() binding the daemon's RBGC root; gated WC_RNG_BANK_HAVE_DAEMON_SUPPORT; * WC_RNG_BANK_FLAG_DEFAULT_BANK marks the process-default bank; wc_rng_bank_fini() for symmetric teardown; per-instance flags word (WC_RNG_BANK_INST_FLAG_*, with _ALREADY_WARNED de-duplicating degradation warnings); * test.c random_bank_test(): daemon claim/release contracts, double claim rejection, root binding, and teardown coverage. --- wolfcrypt/src/rng_bank.c | 116 +++++++++++++++++++++++++++++++++++ wolfcrypt/test/test.c | 115 ++++++++++++++++++++++++++++++++++ wolfssl/wolfcrypt/rng_bank.h | 64 +++++++++++++++++++ 3 files changed, 295 insertions(+) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index c5c5b9cf919..653e053e06a 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -451,6 +451,7 @@ WOLFSSL_API int wc_rng_bank_default_set(struct wc_rng_bank *bank) { return ret; } if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, (void **)&cur_default_rng_bank, bank)) { + bank->flags |= WC_RNG_BANK_FLAG_DEFAULT_BANK; return 0; } else { @@ -535,6 +536,7 @@ WOLFSSL_API int wc_rng_bank_default_clear(struct wc_rng_bank *bank) { if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, (void **)&bank, NULL)) { int ret; WC_ATOMIC_INT_ARG new_refcount; + bank->flags &= ~WC_RNG_BANK_FLAG_DEFAULT_BANK; wolfSSL_RefDec2(&bank->refcount, &new_refcount, &ret); #ifdef WC_VERBOSE_RNG /* wc_rng_bank_fini() is the sole responsibility of the context that @@ -1090,6 +1092,120 @@ WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, return 0; } +#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT + +WOLFSSL_API int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank, WC_ATOMIC_UINT_ARG magic) { + int ret; + WC_ATOMIC_INT_ARG new_refcount; + + if ((bank == NULL) || (magic == WC_RNG_BANK_DAEMON_MAGIC_FREE)) + return BAD_FUNC_ARG; + + if (bank->daemon != NULL) + return BUSY_E; + + { + WC_ATOMIC_UINT_ARG expected = WC_RNG_BANK_DAEMON_MAGIC_FREE; + if (! wolfSSL_Atomic_Uint_CompareExchange(&bank->daemon_magic, &expected, + magic)) + return BUSY_E; + } + + wolfSSL_RefInc_IfAtLeast(&bank->refcount, 1, &new_refcount, &ret); + if (ret != 0) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "wc_rng_bank_daemon_reserve() called with refcount %d.\n", + new_refcount); +#endif + WOLFSSL_ATOMIC_STORE(bank->daemon_magic, WC_RNG_BANK_DAEMON_MAGIC_FREE); + return ret; + } + + return 0; +} + +WOLFSSL_API int wc_rng_bank_daemon_register(struct wc_rng_bank *bank, void *daemon, WC_ATOMIC_UINT_ARG magic) { + if ((bank == NULL) || (daemon == NULL) || (magic == WC_RNG_BANK_DAEMON_MAGIC_FREE)) + return BAD_FUNC_ARG; + + if (bank->daemon != NULL) + return ALREADY_E; + + if (WOLFSSL_ATOMIC_LOAD(bank->daemon_magic) != magic) + return WRONG_TYPE_OBJECT_E; + + bank->daemon = daemon; + + return 0; +} + +WOLFSSL_API int wc_rng_bank_daemon_unregister(struct wc_rng_bank *bank, void **daemon, WC_ATOMIC_UINT_ARG magic) { + if ((bank == NULL) || (daemon == NULL) || (magic == WC_RNG_BANK_DAEMON_MAGIC_FREE)) + return BAD_FUNC_ARG; + + if (WOLFSSL_ATOMIC_LOAD(bank->daemon_magic) != magic) + return WRONG_TYPE_OBJECT_E; + + if (bank->daemon == NULL) + return ALREADY_E; + + *daemon = bank->daemon; + bank->daemon = NULL; + + return 0; +} + +WOLFSSL_API int wc_rng_bank_daemon_release(struct wc_rng_bank *bank, WC_ATOMIC_UINT_ARG magic) { + int ret; + WC_ATOMIC_INT_ARG new_refcount; + + if ((bank == NULL) || (magic == WC_RNG_BANK_DAEMON_MAGIC_FREE)) + return BAD_FUNC_ARG; + + if (WOLFSSL_ATOMIC_LOAD(bank->daemon_magic) != magic) + return WRONG_TYPE_OBJECT_E; + + if (bank->daemon != NULL) + return BUSY_E; + + wolfSSL_RefDec2(&bank->refcount, &new_refcount, &ret); +#ifdef WC_VERBOSE_RNG + /* wc_rng_bank_fini() is the sole responsibility of the context that + * called wc_rng_bank_daemon_reserve() for this wc_rng_bank. + */ + if (new_refcount < 1) + WOLFSSL_DEBUG_PRINTF( + "wc_rng_bank_daemon_release() popped refcount to %d.\n", new_refcount); + if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) + WOLFSSL_DEBUG_PRINTF( + "BUG: wc_rng_bank_daemon_release() bank is already uninited.\n"); +#else + (void)new_refcount; +#endif + + WOLFSSL_ATOMIC_STORE(bank->daemon_magic, WC_RNG_BANK_DAEMON_MAGIC_FREE); + + return 0; +} + +WOLFSSL_API int wc_rng_bank_daemon_root_set(struct wc_rng_bank *bank, + WC_RNG *daemon_root) +{ + if (bank == NULL) + return BAD_FUNC_ARG; + bank->daemon_root = daemon_root; + return 0; +} + +WOLFSSL_API WC_RNG *wc_rng_bank_daemon_root_get(struct wc_rng_bank *bank) +{ + if (bank == NULL) + return NULL; + return bank->daemon_root; +} + +#endif /* WC_RNG_BANK_HAVE_DAEMON_SUPPORT */ #ifdef WC_HAVE_RNG_BANKREF /* wc_local_rng_bank_checkout_for_bankref() is the shim to the real WC_RNG when diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 536526dab65..9563a8b8706 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -27956,6 +27956,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) int leaf_rng_inited = 0; WC_DECLARE_VAR(leaf_rng, WC_RNG, 1, HEAP_HINT); #endif +#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT + void *daemon_out = NULL; +#endif WC_CALLOC_VAR_EX(bank, struct wc_rng_bank, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, @@ -28904,6 +28907,118 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) } #endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7,0,0) */ +#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT + #define RBT_MAGIC ((WC_ATOMIC_UINT_ARG)0x746e6164) /* arbitrary nonzero */ + #define RBT_MAGIC_2 ((WC_ATOMIC_UINT_ARG)0x746e6145) + + /* arg validation: NULL bank, FREE magic */ + ret = wc_rng_bank_daemon_reserve(NULL, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_reserve(bank, WC_RNG_BANK_DAEMON_MAGIC_FREE); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* register/unregister/release before any reserve: slot magic is FREE, + * so the caller's magic can never match. */ + ret = wc_rng_bank_daemon_register(bank, (void *)&ret, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_unregister(bank, &daemon_out, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_release(bank, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* reserve claims the slot */ + ret = wc_rng_bank_daemon_reserve(bank, RBT_MAGIC); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* the reservation's bank ref makes fini refuse: the documented + * leak-to-BUSY_E demotion, probed directly. */ + ret = wc_rng_bank_fini(bank); + if (ret != WC_NO_ERR_TRACE(BUSY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* double-reserve, same and different magic: slot occupied. */ + ret = wc_rng_bank_daemon_reserve(bank, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(BUSY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_reserve(bank, RBT_MAGIC_2); + if (ret != WC_NO_ERR_TRACE(BUSY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* register: NULL daemon rejected; wrong magic rejected; then accepted. */ + ret = wc_rng_bank_daemon_register(bank, NULL, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_register(bank, (void *)&ret, RBT_MAGIC_2); + if (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_register(bank, (void *)&ret, RBT_MAGIC); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* double-register: occupied. */ + ret = wc_rng_bank_daemon_register(bank, (void *)&outbuf1, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(ALREADY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* release while registered: refused, registration intact. */ + ret = wc_rng_bank_daemon_release(bank, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(BUSY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* unregister: NULL out and wrong magic rejected; then hands back the + * registered pointer, exactly once. */ + ret = wc_rng_bank_daemon_unregister(bank, NULL, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_unregister(bank, &daemon_out, RBT_MAGIC_2); + if (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_unregister(bank, &daemon_out, RBT_MAGIC); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (daemon_out != (void *)&ret) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* at-most-once: second unregister finds the slot empty. */ + daemon_out = NULL; + ret = wc_rng_bank_daemon_unregister(bank, &daemon_out, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(ALREADY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (daemon_out != NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* release frees the slot and drops the reservation ref. */ + ret = wc_rng_bank_daemon_release(bank, RBT_MAGIC); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* released slot: stale-magic ops can't match FREE. */ + ret = wc_rng_bank_daemon_release(bank, RBT_MAGIC); + if (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* slot is reusable, under a different magic, for the + * reserve -> spawn-failed -> release unwind shape (no register). */ + ret = wc_rng_bank_daemon_reserve(bank, RBT_MAGIC_2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_daemon_release(bank, RBT_MAGIC_2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + /* refcount balance is implicitly audited by the closing + * wc_rng_bank_fini(bank) succeeding below. */ + + #undef RBT_MAGIC + #undef RBT_MAGIC_2 +#endif /* WC_RNG_BANK_HAVE_DAEMON_SUPPORT */ + out: { diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 26fcdf7cc93..205b4a2e2f5 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -40,6 +40,11 @@ #error WC_RNG_BANK_SUPPORT requires RNG support. #endif +#ifndef WOLFSSL_NO_ATOMICS + #define WC_RNG_BANK_HAVE_DAEMON_SUPPORT + #define WC_RNG_BANK_DAEMON_MAGIC_FREE 0U +#endif + #define WC_RNG_BANK_FLAG_NONE 0 #define WC_RNG_BANK_FLAG_INITED (1U << 0) #define WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST (1U << 1) @@ -117,6 +122,7 @@ * quarantine) or check it back in. Ordinary consumers that cannot * complete a recovery must not pass this flag. */ #define WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY (1U << 13) +#define WC_RNG_BANK_FLAG_DEFAULT_BANK (1U << 13) #define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 14) /* wc_rng_bank_spawn[_new]() only: the child is born with * WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED. */ @@ -153,6 +159,9 @@ struct wc_rng_bank; typedef int (*wc_rng_bank_free_hook_cb_t)(const struct wc_rng_bank *bank, void *arg); +#define WC_RNG_BANK_INST_FLAG_NONE 0 +#define WC_RNG_BANK_INST_FLAG_ALREADY_WARNED (1U << 0) + struct wc_rng_bank_inst { #ifdef WC_RNG_HAVE_LOCK /* the exclusivity latch lives in rng.lock (wc_RNG_lock_*()) -- @@ -167,6 +176,7 @@ struct wc_rng_bank_inst { #endif struct wc_rng_bank *bank; WC_RNG rng; + volatile word32 flags; }; #if defined(WOLFSSL_NO_MALLOC) && defined(NO_WOLFSSL_MEMORY) && \ @@ -207,6 +217,14 @@ struct wc_rng_bank { #else struct wc_rng_bank_inst *rngs; /* typically one per CPU ID, plus a few */ #endif +#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT + wolfSSL_Atomic_Uint daemon_magic; + void *daemon; /* e.g. a task_struct* for a wc_linuxkm_entropy_daemon() */ + /* the daemon's private root DRBG, published for the state-invalidation + * handler (see wc_rng_bank_daemon_root_set()); the daemon owns its + * lifecycle and clears it before teardown. */ + WC_RNG *daemon_root; +#endif }; #ifndef WC_RNG_BANK_STATIC @@ -273,6 +291,22 @@ WOLFSSL_API int wc_rng_bank_checkout( int timeout_secs, word32 flags); +#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT +/* Note, these APIs must be called in order, _reserve -> _register -> + * _unregister -> _release, for lifecycle hygiene. A registered daemon must be + * _unregister()ed, and the bank _release()d, before wc_rng_bank_fini(), + * otherwise _fini() will return BUSY_E. */ +WOLFSSL_API int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank, + WC_ATOMIC_UINT_ARG magic); +WOLFSSL_API int wc_rng_bank_daemon_register(struct wc_rng_bank *bank, + void *daemon, + WC_ATOMIC_UINT_ARG magic); +WOLFSSL_API int wc_rng_bank_daemon_unregister(struct wc_rng_bank *bank, + void **daemon, + WC_ATOMIC_UINT_ARG magic); +WOLFSSL_API int wc_rng_bank_daemon_release(struct wc_rng_bank *bank, + WC_ATOMIC_UINT_ARG magic); +#endif /* WC_RNG_BANK_HAVE_DAEMON_SUPPORT */ #if defined(WC_DRBG_BANKREF) && !defined(WC_HAVE_RNG_BANKREF) /* forward compat for FIPS v5.2.4 random.h */ @@ -287,6 +321,28 @@ WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref( WOLFSSL_API int wc_rng_bank_get_inst_id(struct wc_rng_bank_inst *rng_inst); +static WC_INLINE int WC_ARG_NOT_NULL(1) wc_rng_bank_inst_flags_up( + struct wc_rng_bank_inst *rng_inst, word32 flags) +{ + if (! (rng_inst->flags & flags)) { + rng_inst->flags = rng_inst->flags | flags; + return 1; + } + else + return 0; +} + +static WC_INLINE int WC_ARG_NOT_NULL(1) wc_rng_bank_inst_flags_down( + struct wc_rng_bank_inst *rng_inst, word32 flags) +{ + if (rng_inst->flags & flags) { + rng_inst->flags = rng_inst->flags & ~flags; + return 1; + } + else + return 0; +} + WOLFSSL_API int wc_rng_bank_checkin( struct wc_rng_bank *bank, struct wc_rng_bank_inst **rng_inst); @@ -406,6 +462,14 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, word32 flags); +#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT +/* Publish (or, with NULL, retract) the daemon's private root DRBG for the + * state-invalidation handler. Caller (the daemon) owns the ordering: + * publish after successful init, retract before teardown. */ +WOLFSSL_API int wc_rng_bank_daemon_root_set(struct wc_rng_bank *bank, + WC_RNG *daemon_root); +WOLFSSL_API WC_RNG *wc_rng_bank_daemon_root_get(struct wc_rng_bank *bank); +#endif /* Register a callback fired by wc_rng_bank_fini() once its refcount and * leak gates pass -- i.e. once teardown is committed -- e.g. to unlink the From 553822dfbe62b49986823af8f63cc57e2d7fd507 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:17:12 +0000 Subject: [PATCH 028/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfssl/wolfcrypt/settings.h, wolfcrypt/test/test.c: add opt-in RNG debug statistics: * WC_RNG_DEBUG_STATS: global atomic counters across the RNG facilities -- seeds and reseeds by provenance (source, RBGC, banked next seed, uncredited stir), generates, pool collects/extracts, bank checkouts/recoveries -- with wc_rng_debug_stats_snap() /_restore() /_sum() for snapshot-delta accounting in tests and daemons; wc_rng_debug_counter_t is word64, word32 where 64-bit is unavailable; * settings.h: default the knob on for verbose-debug kernel builds (WC_VERBOSE_RNG && WOLFSSL_KERNEL_VERBOSE_DEBUG), opt-out WC_RNG_NO_DEBUG_STATS; * test.c: snapshot-delta assertions verifying the counters advance with the operations that claim them. --- wolfcrypt/src/random.c | 241 +++++++++++++++++++++++++++++++++++ wolfcrypt/src/rng_bank.c | 37 ++++++ wolfcrypt/test/test.c | 160 +++++++++++++++++++++++ wolfssl/wolfcrypt/random.h | 76 +++++++++++ wolfssl/wolfcrypt/rng_bank.h | 4 + wolfssl/wolfcrypt/settings.h | 5 + 6 files changed, 523 insertions(+) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index bae2d3afa20..1be260ce5f8 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -761,6 +761,33 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 see #endif /* !NO_SHA256 */ +/* WC_RNG_DEBUG_STATS collection points. + * + * Placement doctrine: each counter is maintained at the single funnel that + * owns the distinction it records -- + * - reseed counts in Hash_DRBG_Reseed() (every reseed flavor routes + * through it: interval backstop, Reseed_Now, RBGC, banked redemption); + * - request/byte counts in the DRBG arm of wc_RNG_GenerateBlock() + * (hardware-offload arms -- RDRAND, Silabs, async, cryptocb, custom -- + * are deliberately uncounted: these are DRBG-facility statistics); + * - banked-seed redemption provenance in wc_RNG_DRBG_NextSeedNow_Nonce(); + * - seed health failures at the two sites that observe them per-instance + * (PollAndReSeed(), NextSeedGenerate); + * - chain-provenance bytes (RBGC_bytes_produced: output generated while + * the instance's own RBGCStratum > 0) in the same generate funnel; + * - pool byte accounting in wc_RNG_Pool_Extract(), under the consumer's + * instance lock: bytes produced by reading from the pool, and bytes + * requested but not fulfilled (empty-pool and partial-serve shortfall), + * so requested == produced + missed on the capacity paths. The + * failed-DRBG burn path deliberately counts nothing: it is a failure + * event (visible via rng->status), not a capacity signal. + * + * Counters are plain (non-atomic) adds/increments, and update under the owner's + * exclusive access (the lock contract shared by all WC_RNG operations) except + * where labeled racy: those are unreliable under concurrency, by design. Every + * site carries its own #ifdef WC_RNG_DEBUG_STATS gate so the facility is + * removable outright with unifdef. */ + static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, const byte* additional, word32 additionalSz, int credited) @@ -821,6 +848,14 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, ret = Hash256_DRBG_Reseed(drbg, seed, seedSz, additional, additionalSz, credited); +#ifdef WC_RNG_DEBUG_STATS + if (ret == 0) { + if (credited) + ++rng->_stats_credited_reseeds; + else + ++rng->_stats_uncredited_reseeds; + } +#endif goto out; } #endif @@ -853,6 +888,14 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, ret = Hash512_DRBG_Reseed(drbg512, seed, seedSz, additional, additionalSz, credited); +#ifdef WC_RNG_DEBUG_STATS + if (ret == 0) { + if (credited) + ++rng->_stats_credited_reseeds; + else + ++rng->_stats_uncredited_reseeds; + } +#endif goto out; } #endif @@ -2263,6 +2306,10 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, XMEMSET(rng, 0, WC_OFFSETOF(WC_RNG, lock)); XMEMSET((byte *)rng + WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock), 0, sizeof(*rng) - (WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock))); + #ifdef WC_RNG_DEBUG_STATS + if (flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) + rng->_stats_locks_taken = 1; + #endif #ifdef WOLFSSL_NO_ATOMICS rng->lock = initial_flags; #else @@ -2861,6 +2908,9 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) * contending getters sleep here rather than seeing BUSY_E. */ if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) { if (wc_LockMutex(&rng->mutex) != 0) { + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_refused; /* racy */ + #endif return BAD_MUTEX_E; } } @@ -2874,6 +2924,9 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_refused; /* racy */ + #endif #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) (void)wc_UnLockMutex(&rng->mutex); @@ -2886,9 +2939,15 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) &rng->lock, &cur_lock, cur_lock | WC_RNG_LOCK_HELD | extra_bits))) { + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_taken; + #endif return 0; } + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_refused; + #endif #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX /* CAS failure with the mutex held means a non-mutex claimant holds @@ -2917,6 +2976,9 @@ int wc_RNG_lock_get_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi * contending getters sleep here rather than seeing BUSY_E. */ if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) { if (wc_LockMutex(&rng->mutex) != 0) { + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_refused; /* racy */ + #endif return BAD_MUTEX_E; } } @@ -2935,6 +2997,9 @@ int wc_RNG_lock_get_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && (! (expected_extra_bits & WC_RNG_LOCK_ENTROPY_INVALIDATED))) { + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_refused; /* racy */ + #endif #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) (void)wc_UnLockMutex(&rng->mutex); @@ -2951,9 +3016,15 @@ int wc_RNG_lock_get_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi &rng->lock, &expected, expected | WC_RNG_LOCK_HELD | want_extra_bits))) { + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_taken; + #endif return 0; } + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_refused; /* racy */ + #endif #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX /* CAS failure with the mutex held means a non-mutex claimant holds @@ -2982,6 +3053,9 @@ int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) if (! (cur_lock & WC_RNG_LOCK_HELD)) return OBJECT_NOT_LOCKED_E; + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_released; + #endif for (;;) { new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD); @@ -3017,6 +3091,9 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi if (! (cur_lock & WC_RNG_LOCK_HELD)) return OBJECT_NOT_LOCKED_E; + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_released; + #endif for (;;) { new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD); @@ -3056,6 +3133,9 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi /* conditional release failed: the caller is still the holder, at both * layers -- the mutex stays held. */ + #ifdef WC_RNG_DEBUG_STATS + --rng->_stats_locks_released; + #endif return UNEXPECTED_STATE_E; } @@ -3361,6 +3441,9 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) snap.state = WOLFSSL_ATOMIC_LOAD(rng->poolState); if (snap.pool.current == 0) { +#ifdef WC_RNG_DEBUG_STATS + rng->_stats_pool_bytes_missed += *n; +#endif return NOT_READY_E; } m = *n; @@ -3387,6 +3470,10 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) * conservative (see the protocol comment). */ WOLFSSL_ATOMIC_STORE(rng->poolState, next.state); +#ifdef WC_RNG_DEBUG_STATS + rng->_stats_pool_bytes_produced += m; + rng->_stats_pool_bytes_missed += *n - m; /* shortfall on partial serve */ +#endif *n = m; return 0; @@ -3548,6 +3635,9 @@ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, const byte* n ret = wc_RNG_DRBG_Reseed_Nonce(rng, seed, SEED_SZ, nonce, nonceSz); if (ret == 0) { rng->RBGCStratum = root->RBGCStratum + 1; + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_RBGC_reseeds; + #endif } } else { @@ -3614,6 +3704,9 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, ret = wc_GenerateSeed(&rng->seed, newSeed, SEED_SZ + SEED_BLOCK_SZ); if (ret != 0) { + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_seed_failures; + #endif #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( "ERROR: wc_GenerateSeed() in PollAndReSeed() failed with " @@ -3625,6 +3718,10 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, } if (ret == DRBG_SUCCESS) { ret = wc_RNG_TestSeed(newSeed, SEED_SZ + SEED_BLOCK_SZ); + #ifdef WC_RNG_DEBUG_STATS + if (ret != DRBG_SUCCESS) + ++rng->_stats_seed_failures; + #endif #ifdef WC_VERBOSE_RNG if (ret != DRBG_SUCCESS) WOLFSSL_DEBUG_PRINTF( @@ -3968,6 +4065,9 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, byte *n if (cur == (WC_ATOMIC_INT_ARG)nextSeedSz) { if (nonce != NULL) { WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_n_nextuncreditedseed_banked; + #endif return 0; } #ifdef WC_RNG_HAVE_RBGC @@ -3975,6 +4075,9 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, byte *n * wc_RNG_TestSeed(). */ if (*nextSeedRBGCStratum_p > 0) { WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_n_nextseed_banked; + #endif return 0; } #endif @@ -3983,6 +4086,9 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, byte *n ret = wc_RNG_TestSeed(seed, nextSeedSz); if (ret == 0) { WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_n_nextseed_banked; + #endif return 0; } else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) { @@ -3996,6 +4102,9 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, byte *n /* Use-once: a failed test consumes the material. Release * store: the ForceZero() must be visible before the empty * aperture is. */ + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_seed_failures; + #endif ForceZero(seed, nextSeedSz); WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); return ret; @@ -4098,6 +4207,16 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, ret = Hash_DRBG_Reseed(rng, seed + SEED_BLOCK_SZ, SEED_SZ, nonce, nonceSz, 1 /* credited */); + #ifdef WC_RNG_DEBUG_STATS + if (ret == 0) { + #ifdef WC_RNG_HAVE_RBGC + if (*nextSeedRBGCStratum_p > 0) + ++rng->_stats_n_nextseed_RBGC_redeemed; + else + #endif + ++rng->_stats_n_nextseed_primary_redeemed; + } + #endif #ifdef WC_RNG_HAVE_RBGC if (ret == 0) { @@ -4191,6 +4310,10 @@ int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng) ret = wc_RNG_DRBG_Reseed_Uncredited(rng, seed, nextSeedSz); +#ifdef WC_RNG_DEBUG_STATS + if (ret == 0) + ++rng->_stats_n_nextuncreditedseed_redeemed; +#endif WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); @@ -4272,6 +4395,10 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) if (rng->status != DRBG_OK) return RNG_FAILURE_E; +#ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_total_requests; + rng->_stats_total_bytes_requested += sz; +#endif #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) if (rng->pid != getpid()) { @@ -4366,6 +4493,14 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) if (ret == DRBG_SUCCESS) { ret = 0; +#ifdef WC_RNG_DEBUG_STATS + rng->_stats_total_bytes_produced += sz; + #ifdef WC_RNG_HAVE_RBGC + /* chain-provenance output: generated while chain-backed */ + if (rng->RBGCStratum > 0) + rng->_stats_RBGC_bytes_produced += sz; + #endif +#endif } else if (ret == WC_NO_ERR_TRACE(DRBG_CONT_FAILURE)) { ret = DRBG_CONT_FIPS_E; @@ -8519,6 +8654,112 @@ int wc_hwrng_generate_block(byte *output, word32 sz) } #endif +#ifdef WC_RNG_DEBUG_STATS + +WOLFSSL_API int wc_rng_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, + const WC_RNG *rng) +{ + if ((s == NULL) || (rng == NULL)) + return BAD_FUNC_ARG; + + s->_stats_total_bytes_requested = rng->_stats_total_bytes_requested; + s->_stats_total_bytes_produced = rng->_stats_total_bytes_produced; + s->_stats_total_requests = rng->_stats_total_requests; + s->_stats_credited_reseeds = rng->_stats_credited_reseeds; + s->_stats_uncredited_reseeds = rng->_stats_uncredited_reseeds; + s->_stats_seed_failures = rng->_stats_seed_failures; + s->_stats_locks_taken = rng->_stats_locks_taken; + s->_stats_locks_released = rng->_stats_locks_released; + s->_stats_locks_refused = rng->_stats_locks_refused; +#ifdef WC_RNG_HAVE_RBGC + s->_stats_RBGC_bytes_produced = rng->_stats_RBGC_bytes_produced; + s->_stats_RBGC_reseeds = rng->_stats_RBGC_reseeds; +#endif +#ifdef WC_RNG_HAVE_POOL + s->_stats_pool_bytes_produced = rng->_stats_pool_bytes_produced; + s->_stats_pool_bytes_missed = rng->_stats_pool_bytes_missed; +#endif +#ifdef WC_RNG_HAVE_NEXT_SEED + s->_stats_n_nextseed_primary_redeemed = rng->_stats_n_nextseed_primary_redeemed; + s->_stats_n_nextseed_RBGC_redeemed = rng->_stats_n_nextseed_RBGC_redeemed; + s->_stats_n_nextuncreditedseed_redeemed = rng->_stats_n_nextuncreditedseed_redeemed; + s->_stats_n_nextseed_banked = rng->_stats_n_nextseed_banked; + s->_stats_n_nextuncreditedseed_banked = rng->_stats_n_nextuncreditedseed_banked; +#endif + + return 0; +} + +WOLFSSL_API int wc_rng_debug_stats_restore( + const struct wc_rng_debug_stats_snapshot *s, + WC_RNG *rng) +{ + if ((s == NULL) || (rng == NULL)) + return BAD_FUNC_ARG; + + rng->_stats_total_bytes_requested = s->_stats_total_bytes_requested; + rng->_stats_total_bytes_produced = s->_stats_total_bytes_produced; + rng->_stats_total_requests = s->_stats_total_requests; + rng->_stats_credited_reseeds = s->_stats_credited_reseeds; + rng->_stats_uncredited_reseeds = s->_stats_uncredited_reseeds; + rng->_stats_seed_failures = s->_stats_seed_failures; + rng->_stats_locks_taken = s->_stats_locks_taken; + rng->_stats_locks_released = s->_stats_locks_released; + rng->_stats_locks_refused = s->_stats_locks_refused; +#ifdef WC_RNG_HAVE_RBGC + rng->_stats_RBGC_bytes_produced = s->_stats_RBGC_bytes_produced; + rng->_stats_RBGC_reseeds = s->_stats_RBGC_reseeds; +#endif +#ifdef WC_RNG_HAVE_POOL + rng->_stats_pool_bytes_produced = s->_stats_pool_bytes_produced; + rng->_stats_pool_bytes_missed = s->_stats_pool_bytes_missed; +#endif +#ifdef WC_RNG_HAVE_NEXT_SEED + rng->_stats_n_nextseed_primary_redeemed = s->_stats_n_nextseed_primary_redeemed; + rng->_stats_n_nextseed_RBGC_redeemed = s->_stats_n_nextseed_RBGC_redeemed; + rng->_stats_n_nextuncreditedseed_redeemed = s->_stats_n_nextuncreditedseed_redeemed; + rng->_stats_n_nextseed_banked = s->_stats_n_nextseed_banked; + rng->_stats_n_nextuncreditedseed_banked = s->_stats_n_nextuncreditedseed_banked; +#endif + + return 0; +} + +WOLFSSL_API int wc_rng_debug_stats_sum(struct wc_rng_debug_stats_snapshot *s, + const WC_RNG *rng) +{ + if ((s == NULL) || (rng == NULL)) + return BAD_FUNC_ARG; + + s->_stats_total_bytes_requested += rng->_stats_total_bytes_requested; + s->_stats_total_bytes_produced += rng->_stats_total_bytes_produced; + s->_stats_total_requests += rng->_stats_total_requests; + s->_stats_credited_reseeds += rng->_stats_credited_reseeds; + s->_stats_uncredited_reseeds += rng->_stats_uncredited_reseeds; + s->_stats_seed_failures += rng->_stats_seed_failures; + s->_stats_locks_taken += rng->_stats_locks_taken; + s->_stats_locks_released += rng->_stats_locks_released; + s->_stats_locks_refused += rng->_stats_locks_refused; +#ifdef WC_RNG_HAVE_RBGC + s->_stats_RBGC_bytes_produced += rng->_stats_RBGC_bytes_produced; + s->_stats_RBGC_reseeds += rng->_stats_RBGC_reseeds; +#endif +#ifdef WC_RNG_HAVE_POOL + s->_stats_pool_bytes_produced += rng->_stats_pool_bytes_produced; + s->_stats_pool_bytes_missed += rng->_stats_pool_bytes_missed; +#endif +#ifdef WC_RNG_HAVE_NEXT_SEED + s->_stats_n_nextseed_primary_redeemed += rng->_stats_n_nextseed_primary_redeemed; + s->_stats_n_nextseed_RBGC_redeemed += rng->_stats_n_nextseed_RBGC_redeemed; + s->_stats_n_nextuncreditedseed_redeemed += rng->_stats_n_nextuncreditedseed_redeemed; + s->_stats_n_nextseed_banked += rng->_stats_n_nextseed_banked; + s->_stats_n_nextuncreditedseed_banked += rng->_stats_n_nextuncreditedseed_banked; +#endif + + return 0; +} + +#endif /* WC_RNG_DEBUG_STATS */ #endif /* WC_NO_RNG */ diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 653e053e06a..1d4632cddbd 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -1492,6 +1492,10 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( time_t ts1 = 0; int devId; WC_RNG_lock_arg_t cur_lock = 0; +#ifdef WC_RNG_DEBUG_STATS + struct wc_rng_debug_stats_snapshot s; + int stats_snap_ret; +#endif if (rng_inst == NULL) return BAD_FUNC_ARG; @@ -1545,6 +1549,10 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( if (ret < 0) return ret; +#ifdef WC_RNG_DEBUG_STATS + stats_snap_ret = + wc_rng_debug_stats_snap(&s, WC_RNG_BANK_INST_TO_RNG(rng_inst)); +#endif wc_FreeRng(&rng_inst->rng); @@ -1565,6 +1573,11 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( if (cur_lock != 0) { ret = wc_rng_bank_inst_lock_set_extra(rng_inst, cur_lock); } +#ifdef WC_RNG_DEBUG_STATS + if (stats_snap_ret == 0) + wc_rng_debug_stats_restore( + &s, WC_RNG_BANK_INST_TO_RNG(rng_inst)); +#endif break; } @@ -2270,5 +2283,29 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng) { #endif /* WC_HAVE_RNG_BANKREF */ +#ifdef WC_RNG_DEBUG_STATS + +WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, + struct wc_rng_bank *bank) +{ + int i; + int ret; + + if ((s == NULL) || (bank == NULL)) + return BAD_FUNC_ARG; + + XMEMSET(s, 0, sizeof(*s)); + + for (i = 0; i < bank->n_rngs; ++i) { + WC_RNG *rng = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); + ret = wc_rng_debug_stats_sum(s, rng); + if (ret != 0) + break; + } + + return ret; +} + +#endif /* WC_RNG_DEBUG_STATS */ #endif /* WC_RNG_BANK_SUPPORT */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 9563a8b8706..675a2a31383 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -26858,6 +26858,68 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t XChaCha20Poly1305_test(void) #endif /* defined(HAVE_XCHACHA) && defined(HAVE_POLY1305) */ #ifndef WC_NO_RNG +#ifdef WC_RNG_DEBUG_STATS +/* Snapshot-and-delta assertion kit for the WC_RNG_DEBUG_STATS counters. + * + * Usage: RNG_STATS_DECLS; as the LAST declaration in the block (it + * declares two snapshot cells, so two instances can be tracked across one + * API call, e.g. a chain reseed's source and target). RNG_STATS_SNAP[2]() + * snapshots an instance; RNG_STATS_EXPECT[2]() asserts an exact + * counter delta since the matching snapshot, RNG_STATS_EXPECT_GE[2]() a + * minimum delta, and RNG_STATS_EXPECT_SAME_DELTA() that two counters of + * one instance moved together. fail_action is executed on mismatch + * (e.g. ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)), so the + * encoded line number pinpoints the failing assertion; the observed + * delta (for _SAME_DELTA, the difference of the two deltas) is available + * to fail_action as rng_stats_d_ for encoding via WC_TEST_RET_ENC_I(). Without + * WC_RNG_DEBUG_STATS everything expands to nothing. + */ + +#define RNG_STATS_DECLS \ + struct wc_rng_debug_stats_snapshot rng_stats_s WC_MAYBE_UNUSED, \ + rng_stats_s2 WC_MAYBE_UNUSED +#define RNG_STATS_SNAP(rng) wc_rng_debug_stats_snap(&rng_stats_s, (rng)) +#define RNG_STATS_SNAP2(rng) wc_rng_debug_stats_snap(&rng_stats_s2, (rng)) +#define RNG_STATS_D_(snap, rng, field) ((rng)->field - (snap).field) +#define RNG_STATS_EXPECT_(snap, rng, field, delta, fail_action) \ + do { \ + wc_rng_debug_counter_t rng_stats_d_ = \ + RNG_STATS_D_(snap, rng, field); \ + if (rng_stats_d_ != (wc_rng_debug_counter_t)(delta)) { \ + fail_action; \ + } \ + } while (0) +#define RNG_STATS_EXPECT(rng, field, delta, fail_action) \ + RNG_STATS_EXPECT_(rng_stats_s, rng, field, delta, fail_action) +#define RNG_STATS_EXPECT2(rng, field, delta, fail_action) \ + RNG_STATS_EXPECT_(rng_stats_s2, rng, field, delta, fail_action) +#define RNG_STATS_EXPECT_GE(rng, field, delta, fail_action) \ + do { \ + wc_rng_debug_counter_t rng_stats_d_ = \ + RNG_STATS_D_(rng_stats_s, rng, field); \ + if (rng_stats_d_ < (wc_rng_debug_counter_t)(delta)) { \ + fail_action; \ + } \ + } while (0) +#define RNG_STATS_EXPECT_SAME_DELTA(rng, f1, f2, fail_action) \ + do { \ + wc_rng_debug_counter_t rng_stats_d_ = \ + RNG_STATS_D_(rng_stats_s, rng, f1) - \ + RNG_STATS_D_(rng_stats_s, rng, f2); \ + if (rng_stats_d_ != 0) { \ + fail_action; \ + } \ + } while (0) +#else /* !WC_RNG_DEBUG_STATS */ +#define RNG_STATS_DECLS +#define RNG_STATS_SNAP(rng) WC_DO_NOTHING +#define RNG_STATS_SNAP2(rng) WC_DO_NOTHING +#define RNG_STATS_EXPECT(rng, field, delta, fail_action) WC_DO_NOTHING +#define RNG_STATS_EXPECT2(rng, field, delta, fail_action) WC_DO_NOTHING +#define RNG_STATS_EXPECT_GE(rng, field, delta, fail_action) WC_DO_NOTHING +#define RNG_STATS_EXPECT_SAME_DELTA(rng, f1, f2, fail_action) WC_DO_NOTHING +#endif /* WC_RNG_DEBUG_STATS */ + static wc_test_ret_t _rng_test(WC_RNG* rng) { byte block[32]; @@ -26924,11 +26986,23 @@ static wc_test_ret_t _rng_test(WC_RNG* rng) #endif { + RNG_STATS_DECLS; + RNG_STATS_SNAP(rng); ret = wc_RNG_GenerateBlock(rng, block, sizeof(block)); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); /* the forced interval reseed is credited, and the request is * fully served */ + RNG_STATS_EXPECT(rng, _stats_credited_reseeds, 1, + return WC_TEST_RET_ENC_I((int)rng_stats_d_)); + RNG_STATS_EXPECT(rng, _stats_uncredited_reseeds, 0, + return WC_TEST_RET_ENC_I((int)rng_stats_d_)); + RNG_STATS_EXPECT(rng, _stats_total_requests, 1, + return WC_TEST_RET_ENC_I((int)rng_stats_d_)); + RNG_STATS_EXPECT(rng, _stats_total_bytes_requested, sizeof(block), + return WC_TEST_RET_ENC_I((int)rng_stats_d_)); + RNG_STATS_EXPECT(rng, _stats_total_bytes_produced, sizeof(block), + return WC_TEST_RET_ENC_I((int)rng_stats_d_)); } #if defined(WOLFSSL_DRBG_SHA512) && !defined(HAVE_SELFTEST) && \ @@ -29100,6 +29174,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) wc_drbg_reseed_ctr_t c2 = 0; byte buf[32]; byte matter[32]; + RNG_STATS_DECLS; WOLFSSL_ENTER("rng_drbg_svc_test"); @@ -29186,6 +29261,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) (c1 != (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } + RNG_STATS_SNAP(root); api_ret = wc_RNG_GenerateBlock(root, buf, sizeof(buf)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -29194,9 +29270,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) if ((api_ret != 0) || (c1 > 2)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* the scheduled reseed rides the generate, credited */ + RNG_STATS_EXPECT(root, _stats_credited_reseeds, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT(root, _stats_total_requests, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT(root, _stats_total_bytes_produced, sizeof(buf), + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } /* immediate source reseed, without and with a nonce */ + RNG_STATS_SNAP(root); api_ret = wc_RNG_DRBG_Reseed_Now(root, NULL, 0); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -29209,6 +29292,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* both credited; the nonce is additional input, not an * uncredited reseed */ + RNG_STATS_EXPECT(root, _stats_credited_reseeds, 2, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT(root, _stats_uncredited_reseeds, 0, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } if (wc_RNG_DRBG_Reseed_Now(NULL, NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) @@ -29902,6 +29989,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) byte buf[32]; byte matter[32]; + RNG_STATS_DECLS; + WOLFSSL_ENTER("rng_drbg_rbgc_test"); XMEMSET(matter, 0xa5, sizeof(matter)); @@ -29935,6 +30024,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } + RNG_STATS_SNAP(&root); api_ret = wc_InitRngRBGC(&leaf, &root, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -29944,6 +30034,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if ((api_ret != 0) || (c2 <= c1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* the spawn draw is one fully-served generate on the parent */ + RNG_STATS_EXPECT(&root, _stats_total_requests, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT_GE(&root, _stats_total_bytes_produced, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT_SAME_DELTA(&root, _stats_total_bytes_requested, + _stats_total_bytes_produced, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } api_ret = wc_RNG_DRBG_GetRBGCStratum(&leaf); if (api_ret != 1) @@ -29951,12 +30048,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) api_ret = wc_RNG_DRBG_GetRBGCStratum(&root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + RNG_STATS_SNAP2(&leaf); api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if (present) { /* chain-provenance accounting: bytes generated at stratum 1 count * in both the total and the RBGC ledgers */ + RNG_STATS_EXPECT2(&leaf, _stats_total_bytes_produced, sizeof(buf), + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT2(&leaf, _stats_RBGC_bytes_produced, sizeof(buf), + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } api_ret = wc_RNG_DRBG_ReseedRBGC(&root, &leaf, NULL, 0); @@ -29964,6 +30066,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* reseed-from-root, without and with a nonce; counter resets */ + RNG_STATS_SNAP(&root); + RNG_STATS_SNAP2(&leaf); api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, NULL, 0); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -29976,6 +30080,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* target: two credited chain reseeds; source: two fully-served * seed draws */ + RNG_STATS_EXPECT2(&leaf, _stats_credited_reseeds, 2, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT2(&leaf, _stats_RBGC_reseeds, 2, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT2(&leaf, _stats_uncredited_reseeds, 0, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT(&root, _stats_total_requests, 2, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT_SAME_DELTA(&root, _stats_total_bytes_requested, + _stats_total_bytes_produced, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &leaf, NULL, 0); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) @@ -29991,6 +30106,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) api_ret = wc_RNG_DRBG_ScheduleReseed(&leaf); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + RNG_STATS_SNAP2(&leaf); api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30000,6 +30116,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (present) { /* the primary reseed precedes the byte production, so the served * bytes are not chain-provenance */ + RNG_STATS_EXPECT2(&leaf, _stats_credited_reseeds, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT2(&leaf, _stats_total_bytes_produced, sizeof(buf), + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT2(&leaf, _stats_RBGC_bytes_produced, 0, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } #if !defined(WC_NO_CONSTRUCTORS) @@ -30298,6 +30420,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) byte buf[32]; byte matter[16]; + RNG_STATS_DECLS; + WOLFSSL_ENTER("rng_drbg_nextseed_test"); WC_ALLOC_VAR_EX(root, WC_RNG, 1, HEAP_HINT, @@ -30408,6 +30532,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) /* consume: source-free credited reseed; counter resets to 1; * bank empties (use-once) */ + RNG_STATS_SNAP(root); api_ret = wc_RNG_DRBG_NextSeedNow(root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30416,7 +30541,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* redemption of a primary-provenance bank: credited, counted as a * primary redemption */ + RNG_STATS_EXPECT(root, _stats_credited_reseeds, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT(root, _stats_n_nextseed_primary_redeemed, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); #ifdef WC_RNG_HAVE_RBGC + RNG_STATS_EXPECT(root, _stats_n_nextseed_RBGC_redeemed, 0, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); #endif api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); if (api_ret != 0) @@ -30442,6 +30573,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) } if (i >= 64) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + RNG_STATS_SNAP(root); api_ret = wc_RNG_DRBG_NextSeedNow_Nonce(root, matter, sizeof(matter)); if (api_ret != 0) @@ -30451,6 +30583,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* the nonce rides as additional input: the redemption is still one * credited, primary-provenance reseed */ + RNG_STATS_EXPECT(root, _stats_credited_reseeds, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT(root, _stats_uncredited_reseeds, 0, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); + RNG_STATS_EXPECT(root, _stats_n_nextseed_primary_redeemed, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30627,6 +30765,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void) word32 n = 0; byte out[48]; + RNG_STATS_DECLS; + WOLFSSL_ENTER("rng_pool_test"); WC_ALLOC_VAR_EX(rng, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, @@ -30687,10 +30827,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void) /* empty pool: a distinct protocol code, nothing delivered */ n = sizeof(out); + RNG_STATS_SNAP(rng); api_ret = wc_RNG_Pool_Extract(rng, out, &n); if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); /* the whole request is missed bytes; nothing served */ + RNG_STATS_EXPECT(rng, _stats_pool_bytes_missed, sizeof(out), + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out_l)); + RNG_STATS_EXPECT(rng, _stats_pool_bytes_produced, 0, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out_l)); /* self-collect to full (clamped), verify count, over-collect no-op */ api_ret = wc_RNG_Pool_Collect(rng, 100); @@ -30708,6 +30853,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void) /* partial extract, then cross-instance top-off wrapping the ring, * then full drain crossing the wrap on the read side */ n = 32; + RNG_STATS_SNAP(rng); api_ret = wc_RNG_Pool_Extract(rng, out, &n); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); @@ -30719,12 +30865,21 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void) if (n != 16) ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); /* a fully-fulfillable partial drain is all produced, no shortfall */ + RNG_STATS_EXPECT(rng, _stats_pool_bytes_produced, 32, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out_l)); + RNG_STATS_EXPECT(rng, _stats_pool_bytes_missed, 0, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out_l)); + RNG_STATS_SNAP2(src); api_ret = wc_RNG_Pool_Collect2(rng, src, 32); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); if (wc_RNG_DRBG_Present(src)) { /* the top-off span starts exactly at the ring origin * ((offset + current) % size == 0): one contiguous generate */ + RNG_STATS_EXPECT2(src, _stats_total_requests, 1, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out_l)); + RNG_STATS_EXPECT2(src, _stats_total_bytes_produced, 32, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out_l)); } api_ret = wc_RNG_Pool_Current(rng, &n); if (api_ret != 0) @@ -30732,6 +30887,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void) if (n != 48) ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); n = sizeof(out); + RNG_STATS_SNAP(rng); api_ret = wc_RNG_Pool_Extract(rng, out, &n); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); @@ -30743,6 +30899,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void) if (n != 0) ERROR_OUT(WC_TEST_RET_ENC_I((int)n), out_l); /* full serve across the ring wrap: all produced, no shortfall */ + RNG_STATS_EXPECT(rng, _stats_pool_bytes_produced, 48, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out_l)); + RNG_STATS_EXPECT(rng, _stats_pool_bytes_missed, 0, + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out_l)); /* Collect2 contracts */ api_ret = wc_RNG_Pool_Collect2(rng, NULL, 8); diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index a0c599a7146..f0eaaefa38c 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -459,6 +459,13 @@ enum wc_RngHealthState { #define WC_RNG_FLAG_BANKREF (1U << 2) #define WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3) +#ifdef WC_RNG_DEBUG_STATS + #ifdef WORD64_AVAILABLE + typedef word64 wc_rng_debug_counter_t; + #else + typedef word32 wc_rng_debug_counter_t; + #endif +#endif /* RNG context */ struct WC_RNG { @@ -466,14 +473,31 @@ struct WC_RNG { void* heap; byte status; word32 flags; + #ifdef WC_RNG_DEBUG_STATS + wc_rng_debug_counter_t _stats_total_bytes_requested; + wc_rng_debug_counter_t _stats_total_bytes_produced; + wc_rng_debug_counter_t _stats_total_requests; + wc_rng_debug_counter_t _stats_credited_reseeds; + wc_rng_debug_counter_t _stats_uncredited_reseeds; + wc_rng_debug_counter_t _stats_seed_failures; + #endif #ifdef WC_RNG_HAVE_RBGC int RBGCStratum; + #ifdef WC_RNG_DEBUG_STATS + wc_rng_debug_counter_t _stats_RBGC_bytes_produced; + wc_rng_debug_counter_t _stats_RBGC_reseeds; + #endif #endif #ifdef WC_RNG_HAVE_LOCK WC_RNG_lock_t lock; #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX wolfSSL_Mutex mutex; #endif + #ifdef WC_RNG_DEBUG_STATS + wc_rng_debug_counter_t _stats_locks_taken; + wc_rng_debug_counter_t _stats_locks_released; + wc_rng_debug_counter_t _stats_locks_refused; /* racy */ + #endif #endif #ifdef WC_RNG_HAVE_FREE_HOOK /* fired by wc_FreeRng() before state destruction (one-shot); @@ -485,8 +509,24 @@ struct WC_RNG { byte* pool; word16 poolSize; WC_RNG_pool_state_t poolState; + #ifdef WC_RNG_DEBUG_STATS + wc_rng_debug_counter_t _stats_pool_bytes_produced; + wc_rng_debug_counter_t _stats_pool_bytes_missed; + #endif #endif +#ifdef WC_RNG_DEBUG_STATS + #ifdef WC_RNG_HAVE_NEXT_SEED + wc_rng_debug_counter_t _stats_n_nextseed_primary_redeemed; + wc_rng_debug_counter_t _stats_n_nextseed_RBGC_redeemed; + wc_rng_debug_counter_t _stats_n_nextuncreditedseed_redeemed; + /* production-side twins of the consumption counters above; plain + * increments, racy if there are competing seed bankers (usually + * there aren't). */ + wc_rng_debug_counter_t _stats_n_nextseed_banked; + wc_rng_debug_counter_t _stats_n_nextuncreditedseed_banked; + #endif +#endif #if defined(HAVE_HASHDRBG) || defined(WC_HAVE_RNG_BANKREF) @@ -977,6 +1017,42 @@ WOLFSSL_API int wc_RNG_register_free_hook(WC_RNG* rng, WOLFSSL_API int wc_RNG_Pool_Current(WC_RNG* rng, word32* n); #endif /* WC_RNG_HAVE_POOL */ +#ifdef WC_RNG_DEBUG_STATS +struct wc_rng_debug_stats_snapshot { + wc_rng_debug_counter_t _stats_total_bytes_requested; + wc_rng_debug_counter_t _stats_total_bytes_produced; + wc_rng_debug_counter_t _stats_total_requests; + wc_rng_debug_counter_t _stats_credited_reseeds; + wc_rng_debug_counter_t _stats_uncredited_reseeds; + wc_rng_debug_counter_t _stats_seed_failures; + wc_rng_debug_counter_t _stats_locks_taken; + wc_rng_debug_counter_t _stats_locks_released; + wc_rng_debug_counter_t _stats_locks_refused; +#ifdef WC_RNG_HAVE_RBGC + wc_rng_debug_counter_t _stats_RBGC_bytes_produced; + wc_rng_debug_counter_t _stats_RBGC_reseeds; +#endif +#ifdef WC_RNG_HAVE_POOL + wc_rng_debug_counter_t _stats_pool_bytes_produced; + wc_rng_debug_counter_t _stats_pool_bytes_missed; +#endif +#ifdef WC_RNG_HAVE_NEXT_SEED + wc_rng_debug_counter_t _stats_n_nextseed_primary_redeemed; + wc_rng_debug_counter_t _stats_n_nextseed_RBGC_redeemed; + wc_rng_debug_counter_t _stats_n_nextuncreditedseed_redeemed; + wc_rng_debug_counter_t _stats_n_nextseed_banked; + wc_rng_debug_counter_t _stats_n_nextuncreditedseed_banked; +#endif +}; + +WOLFSSL_API int wc_rng_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, + const WC_RNG *rng); +WOLFSSL_API int wc_rng_debug_stats_restore( + const struct wc_rng_debug_stats_snapshot *s, + WC_RNG *rng); +WOLFSSL_API int wc_rng_debug_stats_sum(struct wc_rng_debug_stats_snapshot *s, + const WC_RNG *rng); +#endif /* WC_RNG_DEBUG_STATS */ #ifdef __cplusplus } /* extern "C" */ diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 205b4a2e2f5..ae9ec1a1111 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -744,6 +744,10 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_clear_extra(struct wc #endif /* !WC_RNG_HAVE_LOCK */ +#ifdef WC_RNG_DEBUG_STATS +WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, + struct wc_rng_bank *bank); +#endif /* ---- Legacy FIPS boundary compatibility -------------------------------- * diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 516dd8805ca..a7a1e087a8a 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -4661,6 +4661,11 @@ #define WC_VERBOSE_RNG #endif + #if defined(WC_VERBOSE_RNG) && defined(WOLFSSL_KERNEL_VERBOSE_DEBUG) && \ + !defined(WC_RNG_NO_DEBUG_STATS) && !defined(WC_RNG_DEBUG_STATS) + #define WC_RNG_DEBUG_STATS + #endif + #if WOLFSSL_GENERAL_ALIGNMENT < SIZEOF_LONG #undef WOLFSSL_GENERAL_ALIGNMENT #define WOLFSSL_GENERAL_ALIGNMENT SIZEOF_LONG From 580c71e8c067a549ccc336f32a842049911d38ec Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:20:22 +0000 Subject: [PATCH 029/102] linuxkm/lkcapi_sha_glue.c, .wolfssl_known_macro_extras: rework the DRBG glue onto the rng_bank facility, with an in-module entropy daemon: * the LKCAPI stdrng backend now draws from a wc_rng_bank (wc_linuxkm_rng_bank_init()/_fini()), instances RBGC-chained to a single-owner daemon root, with a per-instance random pool (WC_LINUXKM_RNG_POOL_SIZE) for atomic-context service; * in-module entropy daemon (kthread, claim arbitrated by WC_LINUXKM_ENTROPY_DAEMON_MAGIC through the bank daemon protocol): banks next seeds and tops up pools on a nap cadence (WC_LINUXKM_ENTROPY_DAEMON_NAP_MS), with a bonus-reseed backstop (WC_LINUXKM_BONUS_RESEED_INTERVAL); opt-out WC_LINUXKM_NO_ENTROPY_DAEMON (registered in .wolfssl_known_macro_extras); * WC_LINUXKM_DRBG_SMALL_LIMIT request-size handling and WC_LINUXKM_INITRNG_TIMEOUT_SEC init-time seeding timeout. --- .wolfssl_known_macro_extras | 1 + linuxkm/lkcapi_sha_glue.c | 1036 +++++++++++++++++++++++++++++------ 2 files changed, 862 insertions(+), 175 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 11c0edb4dcf..9c6952c8ddb 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -787,6 +787,7 @@ WC_HASH_CUSTOM_MAX_BLOCK_SIZE WC_HASH_CUSTOM_MAX_DIGEST_SIZE WC_HASH_CUSTOM_MIN_DIGEST_SIZE WC_INIT_ERROR_WHEN_CONTENDED +WC_LINUXKM_NO_ENTROPY_DAEMON WC_LINUXKM_NO_USE_HEAP_WRAPPERS WC_LINUXKM_SVR_NO_BATCHING WC_MLDSA_NO_ASM diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 9c9cb3de347..4b95b2c4740 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -123,14 +123,20 @@ #define WOLFKM_STDRNG_RDSEED "" #endif +#ifdef WOLFSSL_DRBG_SHA512 + #define WOLFKM_STDRNG_DRIVER_BASE "sha2-512-drbg-nopr" +#else + #define WOLFKM_STDRNG_DRIVER_BASE "sha2-256-drbg-nopr" +#endif + #ifdef LINUXKM_DRBG_GET_RANDOM_BYTES - #define WOLFKM_STDRNG_DRIVER ("sha2-256-drbg-nopr" \ + #define WOLFKM_STDRNG_DRIVER (WOLFKM_STDRNG_DRIVER_BASE \ WOLFKM_STDRNG_WOLFENTROPY \ WOLFKM_STDRNG_RDSEED \ WOLFKM_DRIVER_SUFFIX_BASE \ "-with-global-replace") #else - #define WOLFKM_STDRNG_DRIVER ("sha2-256-drbg-nopr" \ + #define WOLFKM_STDRNG_DRIVER (WOLFKM_STDRNG_DRIVER_BASE \ WOLFKM_STDRNG_WOLFENTROPY \ WOLFKM_STDRNG_RDSEED \ WOLFKM_DRIVER_SUFFIX_BASE) @@ -2093,9 +2099,32 @@ static int linuxkm_affinity_lock(void *arg) { #endif /* !WOLFSSL_USE_SAVE_VECTOR_REGISTERS */ } +/* one per CPU for each of task, softirq, hardirq, and NMI, plus 4 slop */ +#define LINUXKM_RNG_BANK_SIZE (nr_cpu_ids * 4 + 4) +#define LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE (nr_cpu_ids * 2 - 1) +#define LINUXKM_RNG_BANK_FIRST_FAILOVER (nr_cpu_ids * 4) + static int linuxkm_affinity_get_id(void *arg, int *id) { (void)arg; *id = raw_smp_processor_id(); + /* Stratify by execution context class -- one band of nr_cpu_ids + * instances each for task, softirq, hardirq, and NMI -- so that + * same-CPU context nesting never contends for an instance. Note + * in_serving_softirq(), NOT in_softirq(): the latter is also true + * whenever softirqs are merely disabled (local_bh_disable(), + * spin_lock_bh(), including our own affinity-lock callback), which + * would misroute task-context callers into the softirq band. + * Order matters: NMI context also carries hardirq state. + * Misclassification is never unsafe -- the per-instance CAS lease + * is the enforcement -- it only costs the structural-noncontention + * property. + */ + if (in_nmi()) + *id += nr_cpu_ids * 3; + else if (hardirq_count()) + *id += nr_cpu_ids * 2; + else if (in_serving_softirq()) + *id += nr_cpu_ids * 1; return 0; } @@ -2118,10 +2147,372 @@ static int linuxkm_affinity_unlock(void *arg) { #endif /* !WOLFSSL_USE_SAVE_VECTOR_REGISTERS */ } +#define WC_LINUXKM_ENTROPY_DAEMON_MAGIC 0x6f77666c + +#ifndef WC_LINUXKM_NO_ENTROPY_DAEMON + +/* Entropy-banking daemon for the default rng bank: cycles the bank's + * instances, keeping each DRBG's nextSeed aperture full so that + * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED checkouts can perform credited + * reseeds as pure computation, patrolling for out-of-service + * instances (taking their lease and reinitializing them, per + * wc_rng_bank_recover_inst()), and topping off each instance's output + * pool (wc_RNG_Pool_Collect2()) from a daemon-local source DRBG -- + * serviced by the module's normal inline reseed machinery, in task + * context -- so that lease-holding extractors in atomic contexts find + * pre-generated output. The daemon is a control plane only for seed + * material -- entropy moves from the module's seed source to the + * in-boundary aperture without ever crossing into daemon-visible + * storage -- and the pool top-off path likewise never holds a lease on + * the destination: publication is by CAS against the pool aperture, + * with lost races abandoned in place per the pool protocol. + * + * Policy: the daemon sleeps only when it runs out of work, or makes no + * progress on any instance -- it must keep pace with a consumer + * draining nextSeeds as fast as it can. Per-turn classification of + * wc_rng_bank_next_seed_generate() returns: + * 0 gathered/published -- progress; + * NOT_READY_E transient (incl. a burned bank, which is refill-eligible + * now, and an environmental TestSeed miss with the aperture + * preserved) -- progress, so a forced burn can never induce + * a nap; + * ALREADY_E ready or consuming -- no work on this instance; + * BUSY_E instance-op gate held by a reinit -- no progress here, + * but the gate holder is making it; + * MISSING_RNG_E no DRBG behind the instance -- nothing to bank; + * ENTROPY_RT_E / ENTROPY_APT_E and anything else: entropy source + * suspect or code defect; shout (unconditionally -- the + * library layer deliberately doesn't), and treat as + * no-progress so retry is nap-paced, not spin-paced. + * + * Lifecycle: spawned when the default bank is installed, reaped + * (kthread_stop(), which joins) in wc_linuxkm_drbg_exit_tfm() before + * wc_rng_bank_default_clear()/wc_rng_bank_fini() -- the join is what + * makes the daemon's use of the bank safe without a separate refcount + * hold. + */ + +#ifndef WC_LINUXKM_ENTROPY_DAEMON_NAP_MS + #define WC_LINUXKM_ENTROPY_DAEMON_NAP_MS 100 +#endif +#ifndef WC_LINUXKM_ENTROPY_DAEMON_GRANULE + /* wc_Entropy_Get() gathers and hashes in 32 byte blocks -- smaller + * requests cost the same. */ + #define WC_LINUXKM_ENTROPY_DAEMON_GRANULE 32 +#endif +#if !defined(WC_LINUXKM_BONUS_RESEED_INTERVAL) + /* Opportunistic supplementary reseed interval, for daemon seeding and + * wc_RNG_DRBG_NextSeedNow(). Pass rate is load-variable (e.g. nap-paced + * when idle, cond_resched()-paced when busy), so a busy RNG reseeds more + * often -- the conservative direction. wolfcrypt's internal + * WC_RESEED_INTERVAL auto-reseed remains the backstop if this schedule is + * somehow starved. */ + #define WC_LINUXKM_BONUS_RESEED_INTERVAL 1000 + #if WC_LINUXKM_BONUS_RESEED_INTERVAL >= WC_RESEED_INTERVAL / 2 + #undef WC_LINUXKM_BONUS_RESEED_INTERVAL + #define WC_LINUXKM_BONUS_RESEED_INTERVAL (WC_RESEED_INTERVAL / 2) + #endif +#endif + +wc_static_assert(WC_LINUXKM_BONUS_RESEED_INTERVAL >= 0 && + WC_LINUXKM_BONUS_RESEED_INTERVAL < WC_RESEED_INTERVAL / 2); + +#if defined(WC_RNG_HAVE_POOL) && !defined(WC_LINUXKM_RNG_POOL_SIZE) + /* per-instance output pool ring size (2..65535). 256 = 8 native + * get_random_u32-batch-sized draws between top-offs. */ + #define WC_LINUXKM_RNG_POOL_SIZE 256 +#endif + +static int wc_linuxkm_entropy_daemon(void *arg) +{ + struct wc_rng_bank *bank = (struct wc_rng_bank *)arg; + int i; + int ret; + + if (WOLFSSL_ATOMIC_LOAD(bank->daemon_magic) != WC_LINUXKM_ENTROPY_DAEMON_MAGIC) + return -EINVAL; + +#if defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED) + /* Daemon-local source DRBG for pool top-offs: a full peer of the bank's + * instances, inline-reseeded at WC_LINUXKM_BONUS_RESEED_INTERVAL cadence in + * the daemon's task context, torn down through wc_FreeRng() at shutdown. + * wc_RNG_Pool_Collect2() is called to generate bytes into the destination + * pools with flow that stays confined within random.c, hence inside the + * FIPS boundary. */ + WC_RNG *local_root = (WC_RNG *)XMALLOC(sizeof(*local_root), NULL, + DYNAMIC_TYPE_RNG); + int local_root_reseed_countdown = WC_LINUXKM_BONUS_RESEED_INTERVAL; + + if (local_root != NULL) { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_InitRngNonce(local_root, (byte *)&uncredited_nonce, sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret != 0) { + pr_err("wc_entropyd: pool source DRBG init failed: %d -- " + "pool top-off disabled\n", ret); + XFREE(local_root, NULL, DYNAMIC_TYPE_RNG); + local_root = NULL; + } + else { + /* published in the bank's daemon-root slot; retracted before + * teardown. safe: the random_bytes handlers are unregistered + * (and drained) before the daemon is stopped. */ + (void)wc_rng_bank_daemon_root_set(bank, local_root); + } + } + +#ifdef WC_RNG_HAVE_POOL + if (local_root != NULL) { + /* One-time pool allocation for every instance, before any + * extractor can hold a lease against a nonempty ring. A + * failure leaves that instance poolless: extract-side callers + * fall through to direct generates, and Collect2() skips it + * (BAD_STATE_E) each turn. */ + for (i = 0; i < bank->n_rngs; i++) { + ret = wc_RNG_Pool_Alloc(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), + WC_LINUXKM_RNG_POOL_SIZE); + if ((ret != 0) && (ret != WC_NO_ERR_TRACE(ALREADY_E))) { + /* ALREADY_E: already allocated (daemon restart) */ + pr_err("wc_entropyd: pool alloc for DRBG inst %d " + "failed: %d\n", i, ret); + } + } + } +#endif /* WC_RNG_HAVE_POOL */ +#endif /* WC_RNG_HAVE_POOL || WC_RNG_HAVE_NEXT_SEED */ + + for (;;) { + int progress = 0; + int congested_progress = 0; + + if (kthread_should_stop()) + break; + +#if defined(WC_RNG_HAVE_LOCK) && \ + (defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED)) + /* deterministic local_root recovery after a state-invalidation + * event: the saturated reseedCtr from wc_RNG_invalidate_entropy() + * also forces this, but that write races our own generates (the + * root is unleased by design), so the flag is the authoritative + * signal and this the authoritative response. */ + if (local_root != NULL) { + WC_RNG_lock_arg_t root_lock_state; + if ((wc_RNG_lock_read(local_root, &root_lock_state) == 0) && + (root_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + int inv_ret = wc_RNG_DRBG_Reseed_Now(local_root, NULL, 0); + if (inv_ret != 0) + pr_err_ratelimited("wc_entropyd: post-invalidation " + "local_root reseed failed: %d\n", inv_ret); + } + } +#endif + +#ifdef HAVE_HASHDRBG + /* recovery pass: fix out-of-service instances. The status + * peek is lockless and possibly stale -- worst case it sends a + * recover_inst() at a healthy instance (no-op) or misses one + * cycle; the instance-op gate arbitrates any race with an + * inline recovery (BUSY_E). */ + for (i = 0; i < bank->n_rngs; i++) { + if (wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i])) + == WC_DRBG_OK) + { + continue; + } + ret = wc_rng_bank_recover_inst(bank, i, 0 /* timeout_secs */, + 0 /* flags */); + if (ret == 0) { + (void)wc_rng_bank_inst_flags_down(&bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED); + progress = 1; + } + else if (ret != WC_NO_ERR_TRACE(BUSY_E)) { + if (wc_rng_bank_inst_flags_up(&bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED)) { + pr_err_ratelimited( + "ERROR: wc_entropyd: recovery of DRBG inst %d failed: %d\n", + i, ret); + } + } + } +#endif /* HAVE_HASHDRBG */ + +#ifdef WC_RNG_HAVE_NEXT_SEED + if (local_root != NULL) { + /* congestion-triggered RBGC seed pass. */ + for (i = 0; i < bank->n_rngs; i++) { + wc_drbg_reseed_ctr_t this_reseedCtr; + ret = wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), &this_reseedCtr); + if ((ret == 0) && (this_reseedCtr > WC_RESEED_INTERVAL / 2)) { + WC_ATOMIC_INT_ARG this_NextSeedCurrent; + ret = wc_RNG_DRBG_NextSeedCurrent(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), &this_NextSeedCurrent); + if ((ret == 0) && (this_NextSeedCurrent != WC_DRBG_NEXT_SEED_READY) && (this_NextSeedCurrent != WC_DRBG_NEXT_SEED_CONSUMING)) { + ret = wc_rng_bank_next_seed_generate_rbgc(bank, i, WC_DRBG_NEXT_SEED_LEN, local_root); + congested_progress = 1; + if (ret == 0) + progress = 1; + } + } + } + } +#endif /* WC_RNG_HAVE_NEXT_SEED */ + +#ifdef WC_RNG_HAVE_POOL + /* pooling pass -- run this pass even if there was high-load seed + * generation, as it is good defense against reseedCtr exhaustion. + */ + for (i = 0; i < bank->n_rngs; i++) { + /* pool top-off: fill whatever free span the ring reports. + * The fullness peek is a lockless aperture load; Collect2() + * re-clamps against a fresh snapshot and publishes by CAS, + * so staleness costs at most a wasted attempt. Progress + * accounting keys on the peek, not the call: a full ring is + * not work, and NOT_READY_E means a racing consumer is making + * the progress. */ + if (local_root != NULL) { + word32 pool_n = 0; + WC_RNG *inst_rng = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); + + if ((wc_RNG_Pool_Current(inst_rng, &pool_n) == 0) && + (inst_rng->pool != NULL) && + (pool_n < (word32)inst_rng->poolSize)) + { + unsigned long uncredited_nonce = random_get_entropy(); + (void)wc_RNG_DRBG_Reseed_Uncredited(local_root, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + ret = wc_RNG_Pool_Collect2(inst_rng, local_root, + (word32)inst_rng->poolSize + - pool_n); + if (ret == 0) { + progress = 1; + } + else if ((ret == WC_NO_ERR_TRACE(NOT_READY_E)) || + (ret == WC_NO_ERR_TRACE(BAD_STATE_E))) + { + /* contention (consumer active) or no pool -- + * nothing to do here this turn. */ + } + else { + pr_err_ratelimited( + "wc_entropyd: pool top-off on DRBG inst %d " + "returned %d\n", i, ret); + } + } + } + } +#endif /* WC_RNG_HAVE_POOL */ + + /* if we're coping with congestion hits, continue here, don't bog down + * in primary seed ops. */ + if (congested_progress) + goto next_pass; + +#if defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED) + + /* Periodic explicit reseed of the daemon-local pool source, with + * a fresh cycle-counter nonce -- scheduled fresh entropy in task + * context, rather than waiting for the counter-forced internal + * reseed. */ + if ((local_root != NULL) && (--local_root_reseed_countdown < 0)) { +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + ret = wc_RNG_DRBG_Reseed_Now(local_root, NULL, 0); +#else + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now(local_root, + (byte *)&uncredited_nonce, + sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, sizeof uncredited_nonce); +#endif + if (ret == 0) { + local_root_reseed_countdown = + WC_LINUXKM_BONUS_RESEED_INTERVAL; + } + else { + pr_err_ratelimited( + "wc_entropyd: pool source reseed failed: %d\n", ret); + } + } +#endif /* WC_RNG_HAVE_POOL || WC_RNG_HAVE_NEXT_SEED */ + +#ifdef WC_RNG_HAVE_NEXT_SEED + /* seed banking pass: one gather granule per instance per turn. */ + for (i = 0; i < bank->n_rngs; i++) { + + ret = wc_rng_bank_next_seed_generate( + bank, i, WC_LINUXKM_ENTROPY_DAEMON_GRANULE); + if ((ret == 0) || (ret == WC_NO_ERR_TRACE(NOT_READY_E))) { + progress = 1; + } + else if ((ret == WC_NO_ERR_TRACE(ALREADY_E)) || + (ret == WC_NO_ERR_TRACE(BUSY_E)) || + (ret == WC_NO_ERR_TRACE(MISSING_RNG_E))) + { + /* nothing to do here this turn. */ + } + else if ((ret == WC_NO_ERR_TRACE(ENTROPY_RT_E)) || + (ret == WC_NO_ERR_TRACE(ENTROPY_APT_E))) + { +#ifdef WC_VERBOSE_RNG + pr_err_ratelimited( + "WARNING: wc_entropyd: seed health test failed on DRBG inst " + "%d: %d -- entropy source suspect\n", i, ret); +#endif + } + else { + pr_err_ratelimited( + "ERROR: wc_entropyd: next_seed_generate on DRBG inst %d " + "returned %d\n", i, ret); + } + } +#endif /* WC_RNG_HAVE_NEXT_SEED */ + + next_pass: + + if (progress) { + cond_resched(); + } + else { + if (! kthread_should_stop()) + schedule_timeout_interruptible(msecs_to_jiffies(WC_LINUXKM_ENTROPY_DAEMON_NAP_MS)); + } + } + +#if defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED) + if (local_root != NULL) { +#ifdef WC_RNG_DEBUG_STATS + struct wc_rng_debug_stats_snapshot s; + if (wc_rng_debug_stats_snap(&s, local_root) == 0) { + pr_info("RNG INFO: wc_entropyd root total_bytes_requested=%lu\n" + " total_bytes_produced=%lu total_requests=%lu\n" + " credited_reseeds=%lu uncredited_reseeds=%lu seed_failures=%lu\n" + " n_nextuncreditedseed_banked=%lu n_nextuncreditedseed_redeemed=%lu\n", + s._stats_total_bytes_requested, + s._stats_total_bytes_produced, + s._stats_total_requests, + s._stats_credited_reseeds, + s._stats_uncredited_reseeds, + s._stats_seed_failures, + s._stats_n_nextuncreditedseed_banked, + s._stats_n_nextuncreditedseed_redeemed); + } +#endif /* WC_RNG_DEBUG_STATS */ + (void)wc_rng_bank_daemon_root_set(bank, NULL); + (void)wc_FreeRng(local_root); + XFREE(local_root, NULL, DYNAMIC_TYPE_RNG); + } +#endif + + return 0; +} + +#endif /* !WC_LINUXKM_NO_ENTROPY_DAEMON */ + static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) { int ret; word32 flags = WC_RNG_BANK_FLAG_CAN_WAIT; + unsigned long uncredited_nonce = random_get_entropy(); #if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && \ defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) @@ -2143,11 +2534,19 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) } #endif - ret = wc_rng_bank_init( - ctx, nr_cpu_ids + 4, flags, WC_LINUXKM_INITRNG_TIMEOUT_SEC, - NULL /* heap */, INVALID_DEVID); + /* The bank is embedded in the tfm context: its lifetime encloses all + * checkouts by kernel crypto API teardown ordering, so per-checkout + * refcounting buys nothing here and is the one bank-global RMW pair + * on the readout hot path. */ + ret = wc_rng_bank_init_nonce( + ctx, LINUXKM_RNG_BANK_SIZE, + flags | WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING | WC_RNG_BANK_FLAG_INIT_RBGC, + WC_LINUXKM_INITRNG_TIMEOUT_SEC, + NULL /* heap */, INVALID_DEVID, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); if (ret == 0) { + (void)wc_rng_bank_first_failover_inst_set(ctx, LINUXKM_RNG_BANK_FIRST_FAILOVER); ret = wc_rng_bank_set_affinity_handlers( ctx, linuxkm_affinity_lock, @@ -2162,6 +2561,36 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) pr_err("ERROR: wc_rng_bank_default_set() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); WC_DUMP_BACKTRACE_NONDEBUG; } +#ifndef WC_LINUXKM_NO_ENTROPY_DAEMON + else { + /* Try to launch the entropy daemon. Failure is nonfatal: + * the inline reseed and recovery paths serve daemonless + * operation. */ + ret = wc_rng_bank_daemon_reserve(ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + if (ret != 0) { + pr_err("ERROR: wc_rng_bank_daemon_reserve() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); + ret = 0; + } + else { + struct task_struct *t = kthread_run( + wc_linuxkm_entropy_daemon, ctx, "wc_entropyd"); + if (IS_ERR(t)) { + (void)wc_rng_bank_daemon_release(ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + pr_err("WARNING: wc_entropyd spawn failed: %d (falling back to synchronous entropy strategy)\n", + (int)PTR_ERR(t)); + } + else { + ret = wc_rng_bank_daemon_register(ctx, t, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + if (ret != 0) { + pr_err("ERROR: wc_rng_bank_daemon_register() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); + (void)kthread_stop(t); + (void)wc_rng_bank_daemon_release(ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + ret = 0; + } + } + } + } +#endif /* !WC_LINUXKM_NO_ENTROPY_DAEMON */ } } else { @@ -2185,26 +2614,128 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) return ret; } -static int wc_linuxkm_drbg_init_tfm(struct crypto_tfm *tfm) +#ifdef WC_RNG_DEBUG_STATS +/* Dump the default bank's aggregate stats, and (when reachable via the + * daemon-root slot) the entropy daemon's root stats, to the kernel log. + * Snapshots are racy by design (debug stats doctrine); callable any time + * from task context. */ +static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) { - return wc_linuxkm_rng_bank_init((struct wc_rng_bank *)crypto_tfm_ctx(tfm)); + struct wc_rng_debug_stats_snapshot s; + + { + WC_RNG *daemon_root = wc_rng_bank_daemon_root_get(ctx); + if ((daemon_root != NULL) && + (wc_rng_debug_stats_snap(&s, daemon_root) == 0)) + { + pr_info("RNG INFO: wc_entropyd root total_bytes_requested=%lu\n" + " total_bytes_produced=%lu total_requests=%lu\n" + " credited_reseeds=%lu uncredited_reseeds=%lu seed_failures=%lu\n" + " n_nextuncreditedseed_banked=%lu n_nextuncreditedseed_redeemed=%lu\n", + s._stats_total_bytes_requested, + s._stats_total_bytes_produced, + s._stats_total_requests, + s._stats_credited_reseeds, + s._stats_uncredited_reseeds, + s._stats_seed_failures, + s._stats_n_nextuncreditedseed_banked, + s._stats_n_nextuncreditedseed_redeemed); + } + } + + if (wc_rng_bank_debug_stats_snap(&s, ctx) == 0) { + pr_info("RNG INFO: default bank size=%d total_bytes_requested=%lu\n" + " total_bytes_produced=%lu total_requests=%lu\n" + " credited_reseeds=%lu uncredited_reseeds=%lu seed_failures=%lu\n" + " locks_taken=%lu locks_released=%lu locks_refused=%lu\n" +#ifdef WC_RNG_HAVE_RBGC + " RBGC_bytes_produced=%lu RBGC_reseeds=%lu\n" +#endif +#ifdef WC_RNG_HAVE_POOL + " pool_bytes_produced=%lu pool_bytes_missed=%lu\n" +#endif +#ifdef WC_RNG_HAVE_NEXT_SEED + " n_nextseed_primary_redeemed=%lu n_nextseed_RBGC_redeemed=%lu\n" + " n_nextseed_banked=%lu n_nextuncreditedseed_banked=%lu n_nextuncreditedseed_redeemed=%lu\n" +#endif + , + ctx->n_rngs, + s._stats_total_bytes_requested, + s._stats_total_bytes_produced, + s._stats_total_requests, + s._stats_credited_reseeds, + s._stats_uncredited_reseeds, + s._stats_seed_failures, + s._stats_locks_taken, + s._stats_locks_released, + s._stats_locks_refused +#ifdef WC_RNG_HAVE_RBGC + ,s._stats_RBGC_bytes_produced + ,s._stats_RBGC_reseeds +#endif +#ifdef WC_RNG_HAVE_POOL + ,s._stats_pool_bytes_produced + ,s._stats_pool_bytes_missed +#endif +#ifdef WC_RNG_HAVE_NEXT_SEED + ,s._stats_n_nextseed_primary_redeemed + ,s._stats_n_nextseed_RBGC_redeemed + ,s._stats_n_nextseed_banked + ,s._stats_n_nextuncreditedseed_banked + ,s._stats_n_nextuncreditedseed_redeemed +#endif + ); + } } +#endif /* WC_RNG_DEBUG_STATS */ -static void wc_linuxkm_drbg_exit_tfm(struct crypto_tfm *tfm) -{ - struct wc_rng_bank *ctx = (struct wc_rng_bank *)crypto_tfm_ctx(tfm); +static int wc_linuxkm_rng_bank_fini(struct wc_rng_bank *ctx) { int ret; - ret = wc_rng_bank_default_clear(ctx); - if (ret && (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))) - pr_err("ERROR: wc_rng_bank_default_clear() in wc_linuxkm_drbg_exit_tfm() returned unexpected code %d\n", ret); +#ifndef WC_LINUXKM_NO_ENTROPY_DAEMON + if (WOLFSSL_ATOMIC_LOAD(ctx->daemon_magic) == WC_LINUXKM_ENTROPY_DAEMON_MAGIC) { + struct task_struct *t; + ret = wc_rng_bank_daemon_unregister(ctx, (void **)&t, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + if ((ret == 0) || (ret == WC_NO_ERR_TRACE(ALREADY_E))) { + if (ret == 0) + (void)kthread_stop(t); + ret = wc_rng_bank_daemon_release(ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + if (ret != 0) + pr_err("ERROR: wc_rng_bank_daemon_release() in wc_linuxkm_rng_bank_fini() returned code %d\n", ret); + } + else + pr_err("ERROR: wc_rng_bank_daemon_unregister() in wc_linuxkm_rng_bank_fini() returned code %d\n", ret); + } +#endif /* !WC_LINUXKM_NO_ENTROPY_DAEMON */ + + if (ctx->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK) { + ret = wc_rng_bank_default_clear(ctx); + if (ret != 0) + pr_err("ERROR: wc_rng_bank_default_clear() in wc_linuxkm_rng_bank_fini() returned code %d\n", ret); + +#ifdef WC_RNG_DEBUG_STATS + wc_linuxkm_rng_dump_stats(ctx); +#endif + } ret = wc_rng_bank_fini(ctx); if (ret != 0) - pr_err("ERROR: wc_rng_bank_fini() in wc_linuxkm_drbg_exit_tfm() returned err %d\n", ret); + pr_err("ERROR: wc_rng_bank_fini() in wc_linuxkm_rng_bank_fini() returned err %d\n", ret); - return; + return ret; +} + +static int wc_linuxkm_drbg_init_tfm(struct crypto_tfm *tfm) +{ + return wc_linuxkm_rng_bank_init((struct wc_rng_bank *)crypto_tfm_ctx(tfm)); +} + +static void wc_linuxkm_drbg_exit_tfm(struct crypto_tfm *tfm) +{ + struct wc_rng_bank *ctx = (struct wc_rng_bank *)crypto_tfm_ctx(tfm); + + (void)wc_linuxkm_rng_bank_fini(ctx); } static int wc_linuxkm_drbg_default_instance_registered = 0; @@ -2224,11 +2755,32 @@ static struct wc_rng_bank_inst *linuxkm_get_drbg(struct wc_rng_bank *ctx) { #endif if (wc_linuxkm_can_block()) flags |= WC_RNG_BANK_FLAG_AFFINITY_LOCK; +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) else flags |= WC_RNG_BANK_FLAG_NO_VECTOR_OPS; +#endif + + if (! wc_linuxkm_can_block()) { + /* atomic-context callers can't wait out a quarantine: accept + * admission to an invalidated instance and recover it inline + * (below) with a synchronous credited primary reseed. The + * entropy gather rides wc_LockMutex()'s atomic-context arm. */ + flags |= WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY; + } err = wc_rng_bank_checkout(ctx, &ret, 0, WC_LINUXKM_INITRNG_TIMEOUT_SEC, flags); + if ((err == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) && (ret != NULL)) { + /* leased-but-quarantined per WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY: + * we own the recovery obligation. */ + err = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(ret), NULL, 0); + if (err == 0) + return ret; + pr_err_ratelimited("ERROR: inline recovery reseed in linuxkm_get_drbg() returned err %d.\n", err); + (void)wc_rng_bank_inst_checkin(&ret); + return NULL; + } + if (err != 0) { pr_err("ERROR: wc_rng_bank_checkout() in linuxkm_get_drbg() returned err %d.\n", err); WC_DUMP_BACKTRACE_NONDEBUG; @@ -2260,9 +2812,33 @@ int wc_linux_kernel_rng_is_wolfcrypt(struct crypto_rng *rng) { } } -#ifndef WC_HAVE_RNG_BANKREF - #error LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT requires WC_HAVE_RNG_BANKREF. -#endif +#ifdef WC_RNG_HAVE_RBGC + +WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { + unsigned long uncredited_nonce = random_get_entropy(); + int can_sleep = wc_linuxkm_can_block(); + int ret = wc_rng_bank_spawn(NULL /* bank */, rng, (byte *)&uncredited_nonce, + sizeof uncredited_nonce, + 0 /* preferred_inst_offset */, + 0 /* timeout_secs */, + WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | + (can_sleep ? + WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE : + 0)); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret != 0) { + pr_warn_ratelimited("WARNING: linuxkm_InitRng_DefaultRBGC() failed " + "with code %d; falling through to wc_InitRng().\n", + ret); + ret = wc_InitRng(rng); + } + return ret; +} + +#define LKCAPI_INITRNG(rng) linuxkm_InitRng_DefaultRBGC(rng) + +#elif defined(WC_HAVE_RNG_BANKREF) WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRef(WC_RNG* rng) { struct wc_rng_bank *ctx; @@ -2274,7 +2850,8 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRef(WC_RNG* rng) { return ret; } else { - pr_warn_once("WARNING: linuxkm_InitRng_DefaultRef() called with null default_wc_rng_bank; falling through to wc_InitRng().\n"); + pr_warn_once("WARNING: linuxkm_InitRng_DefaultRef() called with null " + "default_wc_rng_bank; falling through to wc_InitRng().\n"); return wc_InitRng(rng); } @@ -2282,24 +2859,66 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRef(WC_RNG* rng) { } #define LKCAPI_INITRNG(rng) linuxkm_InitRng_DefaultRef(rng) +#else /* !WC_RNG_HAVE_RBGC && !WC_HAVE_RNG_BANKREF */ + + #error LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT requires WC_RNG_HAVE_RBGC or WC_HAVE_RNG_BANKREF. + +#endif /* !WC_RNG_HAVE_RBGC && !WC_HAVE_RNG_BANKREF */ + #endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT && HAVE_HASHDRBG */ +#ifndef WC_LINUXKM_DRBG_SMALL_LIMIT + #define WC_LINUXKM_DRBG_SMALL_LIMIT 8 +#endif + +wc_static_assert(WC_LINUXKM_DRBG_SMALL_LIMIT <= WC_LINUXKM_RNG_POOL_SIZE); + static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, const u8 *src, unsigned int slen, - u8 *dst, unsigned int dlen) + u8 *dst, unsigned int dlen, int pr) { int ret, retried = 0; /* can_block() is false whenever the affinity lock is held -- blockability * must be sampled before checkout. */ int can_wait = wc_linuxkm_can_block(); wc_drbg_reseed_ctr_t cur_counter = 0; - struct wc_rng_bank_inst *drbg = linuxkm_get_drbg(ctx); + struct wc_rng_bank_inst *drbg; + + if (pr && !can_wait) + return -EAGAIN; + + drbg = linuxkm_get_drbg(ctx); if (! drbg) { pr_err_once("BUG: linuxkm_get_drbg() failed.\n"); return -EFAULT; } +#ifdef WC_RNG_HAVE_POOL + if ((! pr) && (slen == 0) && (dlen <= WC_LINUXKM_DRBG_SMALL_LIMIT)) { + for (retried = 0; retried < 2; ++retried) { + word32 dlen_before_pool_extraction = dlen; + if (wc_RNG_Pool_Extract(WC_RNG_BANK_INST_TO_RNG(drbg), dst, &dlen) == 0) { + dst += dlen; + dlen = dlen_before_pool_extraction - dlen; + if (dlen == 0) { + ret = 0; + goto out; + } + } + else { + if (wc_RNG_Pool_Collect(WC_RNG_BANK_INST_TO_RNG(drbg), + can_wait ? WC_LINUXKM_RNG_POOL_SIZE : WC_SHA256_BLOCK_SIZE) + != 0) + { + break; + } + } + } + } + retried = 0; +#endif + if (slen > 0) { /* The kernel crypto API's generate-op src is additional data (cf. * crypto/drbg.c, which passes it as SP 800-90A additional input). @@ -2314,62 +2933,117 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, goto out; } } - else if (can_wait && - (wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(drbg), - &cur_counter) == 0) && - (cur_counter > WC_RESEED_INTERVAL / 2)) - { + + if (pr || (wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(drbg), &cur_counter) == 0)) { + +#ifdef WC_RNG_HAVE_NEXT_SEED + WC_ATOMIC_INT_ARG NextSeedCurrent; + ret = wc_RNG_DRBG_NextSeedCurrent(WC_RNG_BANK_INST_TO_RNG(drbg), &NextSeedCurrent); + if ((! pr) && + (ret == 0) && + (NextSeedCurrent == WC_DRBG_NEXT_SEED_READY) && + ((cur_counter >= WC_LINUXKM_BONUS_RESEED_INTERVAL) +#ifdef WC_RNG_HAVE_RBGC + || + ((wc_RNG_DRBG_GetRBGCStratum(WC_RNG_BANK_INST_TO_RNG(drbg)) > 0) && + (wc_RNG_DRBG_GetNextSeedRBGCStratum(WC_RNG_BANK_INST_TO_RNG(drbg)) == 0))) +#endif + ) + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG_BANK_INST_TO_RNG(drbg), + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret == 0) + { + /* Consumed a daemon-banked seed: full reseed, counter reset, no + * entropy gathering, safe in any context -- nothing more to do. + * On any nonzero return (typically nothing banked), fall through + * to the direct-reseed leg below. + */ + cur_counter = 0; + } + } +#endif + if (pr || (can_wait && (cur_counter > WC_RESEED_INTERVAL / 2))) { #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS - /* carefully restore preemptibility for the reseed operation. */ + /* carefully restore preemptibility for the reseed operation. */ - #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) - migrate_disable(); - #endif + #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) + migrate_disable(); + #endif - /* note, no need to use formal atomic accessors on drbg->lock -- - * WC_RNG_BANK_INST_LOCK_HELD is held invariantly across the span, and - * is the only bit considered by contending threads. */ - /* both levels can be held (an affinity-locked checkout with - * WC_RNG_BANK_FLAG_NO_VECTOR_OPS, whether from the caller's flags or - * bank-wide bank->flags, also takes the vector-ops inhibit) -- release - * each held level separately, innermost first, mirroring - * wc_rng_bank_inst_checkin(). */ - if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) - REENABLE_VECTOR_REGISTERS(); - if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) - RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); -#endif - - /* Reseed immediately from the module's own seed source. - * wc_RNG_DRBG_Reseed_Now() resets the reseed counter iff the reseed - * succeeds; on failure it leaves the counter unmodified (the - * WC_RESEED_INTERVAL backstop still governs) and marks the instance - * out of service, exactly as an interval-forced reseed failure - * would. A persistent failure is surfaced by the generate loop - * below. */ - (void)wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); + /* wc_RNG_lock_read()/wc_RNG_lock_clear_extra() suffice here without + * stronger synchronization: WC_RNG_LOCK_HELD is held invariantly + * across the span, so this holder is the latch's sole writer -- the + * exact owner-only contract those accessors encode. */ -#ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS - /* re-establish each level separately, in acquisition order (the - * affinity save first, then the vector-ops inhibit), mirroring - * wc_rng_bank_checkout(); a failed re-acquisition clears only its own - * lock bit, so check-in unwinds exactly the levels actually held. */ - if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { - int ret2 = SAVE_VECTOR_REGISTERS2(); - if (ret2 != 0) - drbg->lock &= ~WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; - } - if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { - int ret2 = DISABLE_VECTOR_REGISTERS(); - if (ret2 != 0) - drbg->lock &= ~WC_RNG_BANK_INST_LOCK_VEC_OPS_INH; - } + /* both levels can be held (an affinity-locked check-out with + * WC_RNG_BANK_FLAG_NO_VECTOR_OPS, whether from the caller's flags or + * bank-wide bank->flags, also takes the vector-ops inhibit) -- release + * each held level separately, innermost first, mirroring + * wc_rng_bank_inst_checkin(). */ + { + WC_RNG_lock_arg_t lock_state = 0; + (void)wc_rng_bank_inst_lock_read(drbg, &lock_state); + if (lock_state & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) + REENABLE_VECTOR_REGISTERS(); + if (lock_state & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) + RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); + } +#endif - #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) - migrate_enable(); - #endif + /* Reseed synchronously. wc_RNG_DRBG_Reseed_Now() resets the reseed + * counter iff the reseed succeeds; on failure it leaves the counter + * unmodified (the WC_RESEED_INTERVAL backstop still governs) and marks + * the instance out of service, exactly as an interval-forced reseed + * failure would. */ +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); +#else + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + } #endif + if (ret != 0) { + pr_warn_ratelimited("WARNING: wc_RNG_DRBG_Reseed_Now() failed " + "for RNG #%d: %d\n", + wc_rng_bank_get_inst_id(drbg), ret); + } +#ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS + /* re-establish each level separately, in acquisition order (the + * affinity save first, then the vector-ops inhibit), mirroring + * wc_rng_bank_checkout(); a failed re-acquisition clears only its own + * lock bit, so check-in unwinds exactly the levels actually held. */ + { + WC_RNG_lock_arg_t lock_state = 0; + (void)wc_rng_bank_inst_lock_read(drbg, &lock_state); + if (lock_state & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { + int ret2 = SAVE_VECTOR_REGISTERS2(); + if (ret2 != 0) + (void)wc_rng_bank_inst_lock_clear_extra(drbg, + WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED); + } + if (lock_state & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { + int ret2 = DISABLE_VECTOR_REGISTERS(); + if (ret2 != 0) + (void)wc_rng_bank_inst_lock_clear_extra(drbg, + WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); + } + } + + #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) + migrate_enable(); + #endif +#endif + } } for (;;) { @@ -2417,10 +3091,14 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, * bank-wide bank->flags, also takes the vector-ops inhibit) -- release * each held level separately, innermost first, mirroring * wc_rng_bank_inst_checkin(). */ - if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) - REENABLE_VECTOR_REGISTERS(); - if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) - RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); + { + WC_RNG_lock_arg_t lock_state = 0; + (void)wc_rng_bank_inst_lock_read(drbg, &lock_state); + if (lock_state & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) + REENABLE_VECTOR_REGISTERS(); + if (lock_state & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) + RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); + } #endif ret = wc_rng_bank_inst_reinit(NULL, drbg, @@ -2432,15 +3110,24 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, * affinity save first, then the vector-ops inhibit), mirroring * wc_rng_bank_checkout(); a failed re-acquisition clears only its own * lock bit, so check-in unwinds exactly the levels actually held. */ - if (drbg->lock & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { - int ret2 = SAVE_VECTOR_REGISTERS2(); - if (ret2 != 0) - drbg->lock &= ~WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED; - } - if (drbg->lock & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { - int ret2 = DISABLE_VECTOR_REGISTERS(); - if (ret2 != 0) - drbg->lock &= ~WC_RNG_BANK_INST_LOCK_VEC_OPS_INH; + { + /* the latch (annotation bits included) is preserved + * across wc_rng_bank_inst_reinit()'s _InitRng() by the + * module, so this post-reinit read is authoritative. */ + WC_RNG_lock_arg_t lock_state = 0; + (void)wc_rng_bank_inst_lock_read(drbg, &lock_state); + if (lock_state & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) { + int ret2 = SAVE_VECTOR_REGISTERS2(); + if (ret2 != 0) + (void)wc_rng_bank_inst_lock_clear_extra(drbg, + WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED); + } + if (lock_state & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) { + int ret2 = DISABLE_VECTOR_REGISTERS(); + if (ret2 != 0) + (void)wc_rng_bank_inst_lock_clear_extra(drbg, + WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); + } } #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) @@ -2449,11 +3136,11 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, #endif if (ret == 0) { - pr_warn_ratelimited("WARNING: reinitialized DRBG #%d after RNG_FAILURE_E from wc_RNG_GenerateBlock().\n", raw_smp_processor_id()); + pr_warn_ratelimited("WARNING: reinitialized DRBG #%d after RNG_FAILURE_E from wc_RNG_GenerateBlock().\n", wc_rng_bank_get_inst_id(drbg)); continue; } else { - pr_err_ratelimited("ERROR: reinitialization of DRBG #%d after RNG_FAILURE_E failed with ret %d.\n", raw_smp_processor_id(), ret); + pr_err_ratelimited("ERROR: reinitialization of DRBG #%d after RNG_FAILURE_E failed with ret %d.\n", wc_rng_bank_get_inst_id(drbg), ret); break; } } @@ -2484,7 +3171,7 @@ static int wc_linuxkm_drbg_generate_tfm(struct crypto_rng *tfm, } return wc_linuxkm_drbg_generate((struct wc_rng_bank *)crypto_rng_ctx(tfm), - src, slen, dst, dlen); + src, slen, dst, dlen, 0 /* pr */); } static int wc_linuxkm_drbg_seed(struct wc_rng_bank *ctx, @@ -2500,9 +3187,10 @@ static int wc_linuxkm_drbg_seed(struct wc_rng_bank *ctx, * additional input, never crediting it as entropy). Mix it into every * instance without credit; the reseed schedule stays governed solely by * the module's own seed source. */ - ret = wc_rng_bank_seed(ctx, seed, slen, WC_LINUXKM_INITRNG_TIMEOUT_SEC, - WC_RNG_BANK_FLAG_CAN_WAIT | - WC_RNG_BANK_FLAG_SEED_UNCREDITED); + ret = wc_rng_bank_seed_range(ctx, 0, LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE, + seed, slen, WC_LINUXKM_INITRNG_TIMEOUT_SEC, + WC_RNG_BANK_FLAG_CAN_WAIT | + WC_RNG_BANK_FLAG_SEED_UNCREDITED); if (ret != 0) { pr_err("wc_rng_bank_seed() in wc_linuxkm_drbg_seed() returned err %d.\n", ret); ret = -EINVAL; @@ -2587,7 +3275,7 @@ static int wc__get_random_bytes(void *buf, size_t len) } else { ret = wc_linuxkm_drbg_generate(current_default_wc_rng_bank, - NULL, 0, buf, (unsigned int)len); + NULL, 0, buf, (unsigned int)len, 0 /* pr */); (void)wc_rng_bank_default_checkin(¤t_default_wc_rng_bank); if (ret) { #ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH @@ -2605,32 +3293,38 @@ static int wc__get_random_bytes(void *buf, size_t len) static ssize_t wc_get_random_bytes_user(struct iov_iter *iter) { struct wc_rng_bank *current_default_wc_rng_bank; ssize_t ret; + if (unlikely(!iov_iter_count(iter))) return 0; ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { -#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH pr_emerg_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_get_random_bytes_user() returned %ld.\n", ret); return -EIO; /* no fallthrough to native randomness */ -#else - pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_get_random_bytes_user() returned %ld.\n", ret); - return -ECANCELED; /* fallthrough to native randomness */ -#endif } else { size_t this_copied, total_copied = 0; - byte block[WC_SHA256_BLOCK_SIZE]; + byte *block; + byte block_small[WC_SHA256_BLOCK_SIZE]; + size_t block_size; + + if (iov_iter_count(iter) <= sizeof block_small) + block = NULL; + else + block = (byte *)malloc(PAGE_SIZE); + if (block == NULL) { + block = block_small; + block_size = sizeof block_small; + } + else + block_size = PAGE_SIZE; for (;;) { + size_t n = min_t(size_t, iov_iter_count(iter), block_size); ret = wc_linuxkm_drbg_generate(current_default_wc_rng_bank, - NULL, 0, block, sizeof block); + NULL, 0, block, n, 0 /* pr */); if (unlikely(ret != 0)) { -#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH pr_emerg_ratelimited("ERROR: wc_get_random_bytes_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); -#else - pr_err_ratelimited("ERROR: wc_get_random_bytes_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); -#endif break; } @@ -2638,37 +3332,33 @@ static ssize_t wc_get_random_bytes_user(struct iov_iter *iter) { * DISABLE_VECTOR_REGISTERS() or kprobes status, i.e. * irq_count() must be zero here. */ - this_copied = copy_to_iter(block, sizeof(block), iter); + this_copied = copy_to_iter(block, n, iter); total_copied += this_copied; - if (!iov_iter_count(iter) || this_copied != sizeof(block)) + if (!iov_iter_count(iter) || this_copied != n) break; - wc_static_assert(PAGE_SIZE % sizeof(block) == 0); - if (total_copied % PAGE_SIZE == 0) { - if (signal_pending(current)) - break; - cond_resched(); - } + if (signal_pending(current)) + break; + cond_resched(); } (void)wc_rng_bank_default_checkin(¤t_default_wc_rng_bank); - ForceZero(block, sizeof(block)); + ForceZero(block, block_size); + + if (block != block_small) + free(block); if (total_copied == 0) { if (ret == 0) ret = -EFAULT; else { -#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH ret = -EIO; -#else - ret = -ECANCELED; -#endif } } - if (ret == 0) - ret = (ssize_t)total_copied; + if (total_copied != 0) + ret = (ssize_t)total_copied; /* partial success wins */ return ret; } @@ -2679,77 +3369,72 @@ static ssize_t wc_get_random_bytes_user(struct iov_iter *iter) { static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) { ssize_t ret; struct wc_rng_bank *current_default_wc_rng_bank; + if (unlikely(!nbytes)) return 0; ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { -#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH pr_emerg_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_extract_crng_user() returned %ld.\n", ret); return -EIO; /* no fallthrough to native randomness */ -#else - pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_extract_crng_user() returned %ld.\n", ret); - return -ECANCELED; /* fallthrough to native randomness */ -#endif } else { size_t this_copied, total_copied = 0; - byte block[WC_SHA256_BLOCK_SIZE]; + byte *block; + byte block_small[WC_SHA256_BLOCK_SIZE]; + size_t block_size; + + if (nbytes <= sizeof block_small) + block = NULL; + else + block = (byte *)malloc(PAGE_SIZE); + if (block == NULL) { + block = block_small; + block_size = sizeof block_small; + } + else + block_size = PAGE_SIZE; for (;;) { + size_t n = min_t(size_t, nbytes - total_copied, block_size); ret = wc_linuxkm_drbg_generate(current_default_wc_rng_bank, - NULL, 0, block, sizeof block); + NULL, 0, block, n, 0 /* pr */); if (unlikely(ret != 0)) { -#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH pr_emerg_ratelimited("ERROR: wc_extract_crng_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); -#else - pr_err_ratelimited("ERROR: wc_extract_crng_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); -#endif break; } - this_copied = nbytes - total_copied; - if (this_copied > sizeof(block)) - this_copied = sizeof(block); - if (copy_to_user((byte *)buf + total_copied, block, this_copied)) { - ret = -EFAULT; - break; - } + /* note copy_to_user() cannot be safely executed with + * DISABLE_VECTOR_REGISTERS() or kprobes status, i.e. + * irq_count() must be zero here. + */ + this_copied = n - copy_to_user((byte *)buf + total_copied, + block, n); total_copied += this_copied; - if (this_copied != sizeof(block)) + if ((total_copied == nbytes) || (this_copied != n)) break; - wc_static_assert(PAGE_SIZE % sizeof(block) == 0); - if (total_copied % PAGE_SIZE == 0) { - if (signal_pending(current)) - break; - cond_resched(); - } + if (signal_pending(current)) + break; + cond_resched(); } (void)wc_rng_bank_default_checkin(¤t_default_wc_rng_bank); - ForceZero(block, sizeof(block)); + ForceZero(block, block_size); + + if (block != block_small) + free(block); if (total_copied == 0) { - if (ret == 0) { - /* Not reachable -- the loop always copies at least one - * block before any zero-status exit -- but keep the belt. - */ + if (ret == 0) ret = -EFAULT; - } - else if (ret != -EFAULT) { - /* Generate failure with nothing delivered. */ -#ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH - ret = -EIO; /* no fallthrough to native randomness */ -#else - ret = -ECANCELED; /* fallthrough to native randomness */ -#endif - } + else + ret = -EIO; } - if (ret == 0) - ret = (ssize_t)total_copied; + if (total_copied != 0) + ret = (ssize_t)total_copied; /* partial success wins */ return ret; } @@ -2782,8 +3467,10 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { if (wc_linuxkm_can_block()) flags |= WC_RNG_BANK_FLAG_AFFINITY_LOCK; +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) else flags |= WC_RNG_BANK_FLAG_NO_VECTOR_OPS; +#endif ret = wc_rng_bank_checkout(ctx, &drbg, 0, 0, flags); if (ret != 0) { @@ -2826,17 +3513,18 @@ static int wc_crng_reseed(void) { return -EFAULT; } - ret = wc_rng_bank_reseed(ctx, WC_LINUXKM_INITRNG_TIMEOUT_SEC, - can_sleep - ? - WC_RNG_BANK_FLAG_CAN_WAIT - : - WC_RNG_BANK_FLAG_NONE); + ret = wc_rng_bank_reseed_range(ctx, 0, LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE, + WC_LINUXKM_INITRNG_TIMEOUT_SEC, + can_sleep + ? + WC_RNG_BANK_FLAG_CAN_WAIT + : + WC_RNG_BANK_FLAG_NONE); (void)wc_rng_bank_default_checkin(&ctx); if (ret != 0) { - pr_err("ERROR: wc_rng_bank_reseed() returned err %d.\n", ret); + pr_err("ERROR: wc_rng_bank_reseed_range() returned err %d.\n", ret); return -EINVAL; } else { @@ -2855,7 +3543,7 @@ struct wolfssl_linuxkm_random_bytes_handlers random_bytes_handlers = { .mix_pool_bytes = wc_mix_pool_bytes, /* .credit_init_bits not implemented */ - .crng_reseed = wc_crng_reseed + .crng_reseed = wc_crng_reseed, }; static int wc_get_random_bytes_callbacks_installed = 0; @@ -2885,7 +3573,11 @@ static int wc_get_random_bytes_by_kprobe(struct kprobe *p, struct pt_regs *regs) regs->ip = (unsigned long)p->addr + p->ainsn.size; return 1; /* Handled. */ } - pr_warn("BUG: wc_get_random_bytes_by_kprobe falling through to native get_random_bytes with wc_linuxkm_drbg_default_instance_registered, ret=%d.\n", ret); +#ifdef HAVE_FIPS + pr_emerg_ratelimited("ERROR: wc_get_random_bytes_by_kprobe falling through to native get_random_bytes with wc_linuxkm_drbg_default_instance_registered, ret=%d.\n", ret); +#else + pr_warn_ratelimited("ERROR: wc_get_random_bytes_by_kprobe falling through to native get_random_bytes with wc_linuxkm_drbg_default_instance_registered, ret=%d.\n", ret); +#endif } else pr_warn("BUG: wc_get_random_bytes_by_kprobe called without wc_linuxkm_drbg_default_instance_registered.\n"); @@ -3251,8 +3943,7 @@ static int wc_linuxkm_drbg_startup(void) #if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ (LINUX_VERSION_CODE >= KERNEL_VERSION(7, 1, 0)) if (default_bank_inited) { - (void)wc_rng_bank_default_clear(&default_bank); - (void)wc_rng_bank_fini(&default_bank); + (void)wc_linuxkm_rng_bank_fini(&default_bank); default_bank_inited = 0; } #endif @@ -3418,14 +4109,9 @@ static int wc_linuxkm_drbg_cleanup(void) { else #endif /* CONFIG_CRYPTO_FIPS */ if (default_bank_inited) { - ret = wc_rng_bank_default_clear(&default_bank); + ret = wc_linuxkm_rng_bank_fini(&default_bank); if (ret) - pr_err("ERROR: wc_rng_bank_default_clear in wc_linuxkm_drbg_cleanup failed: %d\n", ret); - else { - ret = wc_rng_bank_fini(&default_bank); - if (ret) - pr_err("ERROR: wc_rng_bank_fini in wc_linuxkm_drbg_cleanup failed: %d\n", ret); - } + pr_err("ERROR: wc_linuxkm_rng_bank_fini in wc_linuxkm_drbg_cleanup failed: %d\n", ret); default_bank_inited = 0; } #endif /* >= 7.1.0 */ From b40a76a2ea75dddf1178d3c21adcd07e57ec07a9 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:20:34 +0000 Subject: [PATCH 030/102] linuxkm/patches/7.3/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch, linuxkm/lkcapi_sha_glue.c: add the get_random callback kernel patch and wire the harvest to the daemon root: * out-of-tree kernel patch adding a callback facility to drivers/char/random.c: registered callbacks observe material entering the kernel's input pool, letting an out-of-kernel-tree RNG harvest the same entropy stream without draining it; * lkcapi_sha_glue.c: a harvest fragment stirs the leased instance directly; the daemon root -- the one node the harvest wire otherwise never reaches -- receives the fragment through its uncredited accumulator (wc_RNG_DRBG_NextUncreditedSeedStore(), writer-safe without a lease), for consumption at the root's own next generate. --- linuxkm/lkcapi_sha_glue.c | 16 + ...INUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch | 548 ++++++++++++++++++ 2 files changed, 564 insertions(+) create mode 100644 linuxkm/patches/7.3/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 4b95b2c4740..1478cf653fc 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3488,6 +3488,22 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { * so only the module's own seed source resets the reseed schedule. */ ret = wc_RNG_DRBG_Reseed_Uncredited(WC_RNG_BANK_INST_TO_RNG(drbg), buf, (word32)len); +#ifdef WC_RNG_HAVE_NEXT_SEED + /* The leased instance was just stirred directly, above. The daemon root -- + * the one node the harvest wire otherwise never reaches -- is single-owner + * and can't be stirred from here; deposit the fragment into its uncredited + * accumulator instead (writer-safe without a lease: read-copy-store, see + * wc_RNG_DRBG_NextUncreditedSeedStore()), for consumption at the root's own + * next generate. The supplied entropy is unconditionally absorbed by + * wc_RNG_DRBG_NextUncreditedSeedStore() -- if nextUncreditedSeedLen is + * already full, the absorption is by xorbuf(). */ + if (len > 0) { + WC_RNG *stir_root = wc_rng_bank_daemon_root_get(ctx); + if (stir_root != NULL) + (void)wc_RNG_DRBG_NextUncreditedSeedStore(stir_root, (const byte *)buf, + (word32)len); + } +#endif /* WC_RNG_HAVE_NEXT_SEED */ if (ret != 0) ret = -EINVAL; diff --git a/linuxkm/patches/7.3/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch b/linuxkm/patches/7.3/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch new file mode 100644 index 00000000000..be16a5ab725 --- /dev/null +++ b/linuxkm/patches/7.3/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch @@ -0,0 +1,548 @@ +--- 7.3/drivers/char/random.c.dist 2026-09-07 15:16:52.000000000 -0500 ++++ 7.3/drivers/char/random.c 2026-09-08 14:52:24.223216472 -0500 +@@ -84,6 +84,310 @@ static enum { + } crng_init __read_mostly = CRNG_EMPTY; + static DEFINE_STATIC_KEY_FALSE(crng_is_ready); + #define crng_ready() (static_branch_likely(&crng_is_ready) || crng_init >= CRNG_READY) ++ ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ ++#include ++ ++static atomic_long_t random_bytes_cb_owner = ++ ATOMIC_LONG_INIT((long)NULL); ++static struct percpu_ref random_bytes_cb_ref; ++static bool random_bytes_cb_live; /* READ_ONCE/WRITE_ONCE only */ ++static DECLARE_COMPLETION(random_bytes_cb_drained); ++static _get_random_bytes_cb_t _get_random_bytes_cb; ++static get_random_bytes_user_cb_t get_random_bytes_user_cb; ++static crng_ready_cb_t crng_ready_cb; ++static mix_pool_bytes_cb_t mix_pool_bytes_cb; ++static credit_init_bits_cb_t credit_init_bits_cb; ++static crng_reseed_cb_t crng_reseed_cb; ++ ++static void random_bytes_cb_ref_release(struct percpu_ref *ref) ++{ ++ complete(&random_bytes_cb_drained); ++} ++ ++/* distinct non-module value marking teardown in progress; guarantees ++ * non-collision with any real module pointer. */ ++static char random_bytes_cb_teardown_sentinel; ++#define RANDOM_BYTES_CB_TEARDOWN ((long)&random_bytes_cb_teardown_sentinel) ++ ++int wolfssl_linuxkm_register_random_bytes_handlers( ++ struct module *new_random_bytes_cb_owner, ++ const struct wolfssl_linuxkm_random_bytes_handlers *handlers) ++{ ++ int ret; ++ ++ if ((new_random_bytes_cb_owner == NULL) || ++ (handlers == NULL) || ++ (handlers->_get_random_bytes == NULL) || ++ (handlers->get_random_bytes_user == NULL)) ++ { ++ return -EINVAL; ++ } ++ ++ /* random_bytes_cb_owner is used to enforce serialization of ++ * wolfssl_linuxkm_register_random_bytes_handlers() and ++ * wolfssl_linuxkm_unregister_random_bytes_handlers(): NULL means ++ * unowned, a module pointer means registered (or registration in ++ * flight, while random_bytes_cb_live is still false), and ++ * RANDOM_BYTES_CB_TEARDOWN means unregistration in flight. ++ */ ++ if (atomic_long_cmpxchg(&random_bytes_cb_owner, ++ (long)NULL, ++ (long)new_random_bytes_cb_owner) ++ != (long)NULL) ++ { ++ return -EBUSY; ++ } ++ ++ if (!try_module_get(new_random_bytes_cb_owner)) { ++ atomic_long_set(&random_bytes_cb_owner, (long)NULL); ++ return -ENODEV; ++ } ++ ++ /* handlers must be published before the ref goes live. */ ++ _get_random_bytes_cb = handlers->_get_random_bytes; ++ get_random_bytes_user_cb = handlers->get_random_bytes_user; ++ crng_ready_cb = handlers->crng_ready; ++ mix_pool_bytes_cb = handlers->mix_pool_bytes; ++ credit_init_bits_cb = handlers->credit_init_bits; ++ crng_reseed_cb = handlers->crng_reseed; ++ ++ reinit_completion(&random_bytes_cb_drained); ++ ret = percpu_ref_init(&random_bytes_cb_ref, ++ random_bytes_cb_ref_release, 0, GFP_KERNEL); ++ if (ret) { ++ _get_random_bytes_cb = NULL; ++ get_random_bytes_user_cb = NULL; ++ crng_ready_cb = NULL; ++ mix_pool_bytes_cb = NULL; ++ credit_init_bits_cb = NULL; ++ crng_reseed_cb = NULL; ++ module_put(new_random_bytes_cb_owner); ++ /* an unregister call racing this in-flight registration may ++ * hold the owner word at RANDOM_BYTES_CB_TEARDOWN for the ++ * brief interval before it observes !random_bytes_cb_live ++ * and restores our pointer; wait it out rather than ++ * clobbering its claim. */ ++ while (atomic_long_cmpxchg(&random_bytes_cb_owner, ++ (long)new_random_bytes_cb_owner, ++ (long)NULL) ++ != (long)new_random_bytes_cb_owner) ++ { ++ cpu_relax(); ++ } ++ return ret; ++ } ++ ++ if (IS_ENABLED(CONFIG_VDSO_GETRANDOM)) { ++ /* route vDSO getrandom() users to the syscall, hence to the ++ * callbacks. */ ++ WRITE_ONCE(vdso_k_rng_data->is_ready, false); ++ } ++ ++ WRITE_ONCE(random_bytes_cb_live, true); ++ ++ return 0; ++} ++EXPORT_SYMBOL(wolfssl_linuxkm_register_random_bytes_handlers); ++ ++int wolfssl_linuxkm_unregister_random_bytes_handlers(void) ++{ ++ long prev = atomic_long_read(&random_bytes_cb_owner); ++ struct module *owner; ++ ++ /* claim teardown ownership: exactly one caller transitions the owner ++ * word from a module pointer to RANDOM_BYTES_CB_TEARDOWN; any ++ * concurrent or repeated caller is refused. ++ */ ++ for (;;) { ++ if ((prev == (long)NULL) || (prev == RANDOM_BYTES_CB_TEARDOWN)) ++ return -ENODEV; ++ if (atomic_long_try_cmpxchg(&random_bytes_cb_owner, &prev, ++ RANDOM_BYTES_CB_TEARDOWN)) ++ break; ++ /* prev was refreshed by the failed cmpxchg; loop. */ ++ } ++ owner = (struct module *)prev; ++ ++ if (!READ_ONCE(random_bytes_cb_live)) { ++ /* registration still in flight (or failing): hand the owner ++ * word back and refuse. */ ++ atomic_long_set(&random_bytes_cb_owner, prev); ++ return -EBUSY; ++ } ++ ++ WRITE_ONCE(random_bytes_cb_live, false); ++ /* after this, no resolver can be between its liveness check and its ++ * tryget. */ ++ synchronize_rcu(); ++ percpu_ref_kill(&random_bytes_cb_ref); ++ wait_for_completion(&random_bytes_cb_drained); ++ percpu_ref_exit(&random_bytes_cb_ref); ++ ++ _get_random_bytes_cb = NULL; ++ get_random_bytes_user_cb = NULL; ++ crng_ready_cb = NULL; ++ mix_pool_bytes_cb = NULL; ++ credit_init_bits_cb = NULL; ++ crng_reseed_cb = NULL; ++ ++ if (IS_ENABLED(CONFIG_VDSO_GETRANDOM)) ++ WRITE_ONCE(vdso_k_rng_data->is_ready, crng_ready()); ++ ++ module_put(owner); ++ atomic_long_set(&random_bytes_cb_owner, (long)NULL); ++ ++ return 0; ++} ++EXPORT_SYMBOL(wolfssl_linuxkm_unregister_random_bytes_handlers); ++ ++/* Output emitters covered while a generate callback is live: ++ * _get_random_bytes (and the batched get_random_uXX fills, which route ++ * through it), get_random_bytes_user, and vDSO getrandom (readiness ++ * withheld, forcing the syscall path). New crng_make_state callers or ++ * vDSO datapage exports need corresponding treatment. */ ++static __always_inline int reserve_random_bytes_cb(void) ++{ ++ int ret = -ENODEV; ++ ++ rcu_read_lock(); ++ if (READ_ONCE(random_bytes_cb_live) && ++ percpu_ref_tryget_live(&random_bytes_cb_ref)) ++ { ++ ret = 0; ++ } ++ rcu_read_unlock(); ++ ++ return ret; ++} ++ ++static __always_inline void release_random_bytes_cb(void) ++{ ++ percpu_ref_put(&random_bytes_cb_ref); ++} ++ ++static inline int call__get_random_bytes_cb(void *buf, size_t len) ++{ ++ int ret; ++ ++ if (_get_random_bytes_cb == NULL) ++ return -ENODEV; ++ ++ ret = reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = _get_random_bytes_cb(buf, len); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline ssize_t call_get_random_bytes_user_cb(struct iov_iter *iter) ++{ ++ ssize_t ret; ++ ++ if (get_random_bytes_user_cb == NULL) ++ return -ECANCELED; ++ ++ ret = (ssize_t)reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = get_random_bytes_user_cb(iter); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline bool call_crng_ready_cb(void) ++{ ++ bool ret; ++ ++ /* Null crng_ready_cb signifies that the DRBG is always ready, i.e. that if ++ * called, it will always have or obtain sufficient entropy to fulfill the ++ * call. ++ */ ++ if (crng_ready_cb == NULL) ++ return 1; ++ ++ if (reserve_random_bytes_cb() != 0) ++ return 0; ++ ++ ret = crng_ready_cb(); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline int call_mix_pool_bytes_cb(const void *buf, size_t len) ++{ ++ int ret; ++ ++ if (mix_pool_bytes_cb == NULL) ++ return -ENODEV; ++ ++ ret = reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = mix_pool_bytes_cb(buf, len); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline int call_credit_init_bits_cb(size_t bits) ++{ ++ int ret; ++ ++ if (credit_init_bits_cb == NULL) ++ return -ENODEV; ++ ++ ret = reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = credit_init_bits_cb(bits); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline int call_crng_reseed_cb(void) ++{ ++ int ret; ++ ++ if (crng_reseed_cb == NULL) ++ return -ENODEV; ++ ++ ret = reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = crng_reseed_cb(); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++#endif /* WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS */ ++ ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ #define crng_ready_by_cb() (READ_ONCE(random_bytes_cb_live) && call_crng_ready_cb()) ++ #define crng_ready_maybe_cb() (READ_ONCE(random_bytes_cb_live) ? \ ++ (call_crng_ready_cb() || crng_ready()) : crng_ready()) ++#else ++ #define crng_ready_maybe_cb() crng_ready() ++#endif ++ + /* Various types of waiters for crng_init->CRNG_READY transition. */ + static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait); + static struct fasync_struct *fasync; +@@ -107,7 +411,7 @@ MODULE_PARM_DESC(ratelimit_disable, "Dis + */ + bool rng_is_initialized(void) + { +- return crng_ready(); ++ return crng_ready_maybe_cb(); + } + EXPORT_SYMBOL(rng_is_initialized); + +@@ -131,11 +435,11 @@ static void try_to_generate_entropy(void + */ + int wait_for_random_bytes(void) + { +- while (!crng_ready()) { ++ while (!crng_ready_maybe_cb()) { + int ret; + + try_to_generate_entropy(); +- ret = wait_event_interruptible_timeout(crng_init_wait, crng_ready(), HZ); ++ ret = wait_event_interruptible_timeout(crng_init_wait, crng_ready_maybe_cb(), HZ); + if (ret) + return ret > 0 ? 0 : ret; + } +@@ -155,7 +459,7 @@ int __cold execute_with_initialized_rng( + int ret = 0; + + spin_lock_irqsave(&random_ready_notifier.lock, flags); +- if (crng_ready()) ++ if (crng_ready_maybe_cb()) + nb->notifier_call(nb, 0, NULL); + else + ret = raw_notifier_chain_register((struct raw_notifier_head *)&random_ready_notifier.head, nb); +@@ -392,6 +696,24 @@ static void _get_random_bytes(void *buf, + if (!len) + return; + ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ { ++ int cb_ret = call__get_random_bytes_cb(buf, len); ++ if (cb_ret == 0) ++ return; ++ /* Nonzero cb_ret: no handlers installed (-ENODEV), a lost ++ * race with deinstallation (also -ENODEV), or a genuine ++ * callback failure. Check the sentinel at warn time to ++ * distinguish: only a failure with the sentinel still live is ++ * a contract violation. Continue regardless; native ++ * fallthrough is the only available mechanism to preserve ++ * the void contract. */ ++ WARN_ONCE(READ_ONCE(random_bytes_cb_live), ++ "_get_random_bytes callback failed with code %d; " ++ "native fallthrough\n", cb_ret); ++ } ++#endif ++ + first_block_len = min_t(size_t, 32, len); + crng_make_state(&chacha_state, buf, first_block_len); + len -= first_block_len; +@@ -437,6 +759,20 @@ static ssize_t get_random_bytes_user(str + if (unlikely(!iov_iter_count(iter))) + return 0; + ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ if (READ_ONCE(random_bytes_cb_live)) { ++ ssize_t cb_ret = call_get_random_bytes_user_cb(iter); ++ /* -ECANCELED: no iter callback registered; iter is intact. ++ * While a generate callback is live, no output bytes come ++ * from the native crng: a live handler set must include the ++ * iter callback. */ ++ if (cb_ret != -ECANCELED) ++ return cb_ret; ++ WARN_ONCE(1, "live random_bytes handler set lacks get_random_bytes_user callback"); ++ return -EIO; ++ } ++#endif ++ + /* + * Immediately overwrite the ChaCha key at index 4 with random + * bytes, in case userspace causes copy_to_iter() below to sleep +@@ -512,7 +848,7 @@ type get_random_ ##type(void) \ + struct batch_ ##type *batch; \ + unsigned long next_gen; \ + \ +- if (!crng_ready()) { \ ++ if (READ_ONCE(random_bytes_cb_live) || !crng_ready()) { \ + _get_random_bytes(&ret, sizeof(ret)); \ + return ret; \ + } \ +@@ -648,6 +984,11 @@ static void mix_pool_bytes(const void *b + { + unsigned long flags; + ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ (void)call_mix_pool_bytes_cb(buf, len); ++ /* continue to mix into native pool too. */ ++#endif ++ + spin_lock_irqsave(&input_pool.lock, flags); + _mix_pool_bytes(buf, len); + spin_unlock_irqrestore(&input_pool.lock, flags); +@@ -707,7 +1048,13 @@ static void extract_entropy(void *buf, s + memzero_explicit(&block, sizeof(block)); + } + +-#define credit_init_bits(bits) if (!crng_ready()) _credit_init_bits(bits) ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ #define credit_init_bits(bits) do { (void)call_credit_init_bits_cb(bits); \ ++ if (!crng_ready()) \ ++ _credit_init_bits(bits); } while (0) ++#else ++ #define credit_init_bits(bits) do { if (!crng_ready()) _credit_init_bits(bits); } while (0) ++#endif + + static void __cold _credit_init_bits(size_t bits) + { +@@ -731,7 +1078,11 @@ static void __cold _credit_init_bits(siz + if (system_dfl_wq) + queue_work(system_dfl_wq, &set_ready); + atomic_notifier_call_chain(&random_ready_notifier, 0, NULL); +- if (IS_ENABLED(CONFIG_VDSO_GETRANDOM)) ++ if (IS_ENABLED(CONFIG_VDSO_GETRANDOM) ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ && !READ_ONCE(random_bytes_cb_live) ++#endif ++ ) + WRITE_ONCE(vdso_k_rng_data->is_ready, true); + wake_up_interruptible(&crng_init_wait); + kill_fasync(&fasync, SIGIO, POLL_IN); +@@ -934,6 +1285,10 @@ void add_device_randomness(const void *b + _mix_pool_bytes(&entropy, sizeof(entropy)); + _mix_pool_bytes(buf, len); + spin_unlock_irqrestore(&input_pool.lock, flags); ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ if (READ_ONCE(random_bytes_cb_live)) ++ (void)call_mix_pool_bytes_cb(buf, len); ++#endif + } + EXPORT_SYMBOL(add_device_randomness); + +@@ -1392,7 +1747,7 @@ SYSCALL_DEFINE3(getrandom, char __user * + if ((flags & (GRND_INSECURE | GRND_RANDOM)) == (GRND_INSECURE | GRND_RANDOM)) + return -EINVAL; + +- if (!crng_ready() && !(flags & GRND_INSECURE)) { ++ if (!crng_ready_maybe_cb() && !(flags & GRND_INSECURE)) { + if (flags & GRND_NONBLOCK) + return -EAGAIN; + ret = wait_for_random_bytes(); +@@ -1408,6 +1763,10 @@ SYSCALL_DEFINE3(getrandom, char __user * + + static __poll_t random_poll(struct file *file, poll_table *wait) + { ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ if (crng_ready_by_cb()) ++ return EPOLLIN | EPOLLRDNORM; ++#endif + poll_wait(file, &crng_init_wait, wait); + return crng_ready() ? EPOLLIN | EPOLLRDNORM : EPOLLOUT | EPOLLWRNORM; + } +@@ -1453,10 +1812,10 @@ static ssize_t urandom_read_iter(struct + * Opportunistically attempt to initialize the RNG on platforms that + * have fast cycle counters, but don't (for now) require it to succeed. + */ +- if (!crng_ready()) ++ if (!crng_ready_maybe_cb()) + try_to_generate_entropy(); + +- if (!crng_ready()) { ++ if (!crng_ready_maybe_cb()) { + if (!ratelimit_disable && maxwarn <= 0) + ratelimit_state_inc_miss(&urandom_warning); + else if (ratelimit_disable || __ratelimit(&urandom_warning)) { +@@ -1473,7 +1832,7 @@ static ssize_t random_read_iter(struct k + { + int ret; + +- if (!crng_ready() && ++ if (!crng_ready_by_cb() && + ((kiocb->ki_flags & (IOCB_NOWAIT | IOCB_NOIO)) || + (kiocb->ki_filp->f_flags & O_NONBLOCK))) + return -EAGAIN; +@@ -1538,6 +1897,14 @@ static long random_ioctl(struct file *f, + case RNDRESEEDCRNG: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ /* continue to reseed native crng too. */ ++ if (call_crng_reseed_cb() == 0) { ++ if (crng_ready()) ++ crng_reseed(NULL); ++ return 0; ++ } ++#endif + if (!crng_ready()) + return -ENODATA; + crng_reseed(NULL); +--- 7.3/include/linux/random.h.dist 2026-09-07 15:16:52.000000000 -0500 ++++ 7.3/include/linux/random.h 2026-09-08 14:52:45.063663933 -0500 +@@ -139,4 +139,37 @@ int random_online_cpu(unsigned int cpu); + extern const struct file_operations random_fops, urandom_fops; + #endif + ++#ifndef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ #define WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS 1 ++#endif ++ ++typedef int (*_get_random_bytes_cb_t)(void *buf, size_t len); ++struct iov_iter; ++/* kernels >= 5.17.0 use get_random_bytes_user() */ ++typedef ssize_t (*get_random_bytes_user_cb_t)(struct iov_iter *iter); ++/* kernels < 5.17.0 use extract_crng_user(), though some LTS kernels, ++ * e.g. 5.10.236, have the 5.17+ architecture backported. ++ */ ++typedef ssize_t (*extract_crng_user_cb_t)(void __user *buf, size_t nbytes); ++typedef bool (*crng_ready_cb_t)(void); ++typedef int (*mix_pool_bytes_cb_t)(const void *buf, size_t len); ++typedef int (*credit_init_bits_cb_t)(size_t bits); ++typedef int (*crng_reseed_cb_t)(void); ++ ++struct wolfssl_linuxkm_random_bytes_handlers { ++ _get_random_bytes_cb_t _get_random_bytes; ++ get_random_bytes_user_cb_t get_random_bytes_user; ++ extract_crng_user_cb_t extract_crng_user; ++ crng_ready_cb_t crng_ready; ++ mix_pool_bytes_cb_t mix_pool_bytes; ++ credit_init_bits_cb_t credit_init_bits; ++ crng_reseed_cb_t crng_reseed; ++}; ++ ++int wolfssl_linuxkm_register_random_bytes_handlers( ++ struct module *new_random_bytes_cb_owner, ++ const struct wolfssl_linuxkm_random_bytes_handlers *handlers); ++ ++int wolfssl_linuxkm_unregister_random_bytes_handlers(void); ++ + #endif /* _LINUX_RANDOM_H */ From 7badfed886c96e64de4ba32d4187fbcf5dc82ae0 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 05:20:34 +0000 Subject: [PATCH 031/102] linuxkm/lkcapi_sha_glue.c, linuxkm/lkcapi_glue.c, linuxkm/linuxkm_wc_port.h: invalidate RNG state on VM fork/resume: * an RNG object registry (wc_linuxkm_rng_registry_*, mutex-guarded, populated via the wolfCrypt free hooks) records every live in-module RNG so duplication events can reach all of them; * wc_linuxkm_rng_state_invalidate() marks every registered object's entropy untrusted (wc_RNG_invalidate_entropy() / wc_rng_bank_invalidate_entropy()); recovery then proceeds through the bank's recovery admission on next checkout; * triggers: a vmfork notifier, a PM sleep notifier (CONFIG_PM_SLEEP), and a WC_LINUXKM_VMGENID_POLL ACPI VMGenID poller for hypervisors that expose only the generation counter; plus a manual sysfs trigger node (wc_linuxkm_rng_state_invalidate_attr) and, with WC_RNG_DEBUG_STATS, a stats readout node (lkcapi_glue.c); * linuxkm_wc_port.h: pull in notifier.h, suspend.h, acpi.h, io.h for the above. --- linuxkm/linuxkm_wc_port.h | 6 + linuxkm/lkcapi_glue.c | 45 +++- linuxkm/lkcapi_sha_glue.c | 508 +++++++++++++++++++++++++++++++++++++- 3 files changed, 557 insertions(+), 2 deletions(-) diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 051c37ed012..661e77c1a40 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -795,6 +795,12 @@ } #endif #define WC_LKM_REFCOUNT_TO_INT(refcount) wc_lkm_refcount_to_int(&(refcount)) + #include + #ifdef CONFIG_PM_SLEEP + #include + #endif + #include + #include #endif /* !WC_CONTAINERIZE_THIS */ #endif /* LINUXKM_LKCAPI_REGISTER */ diff --git a/linuxkm/lkcapi_glue.c b/linuxkm/lkcapi_glue.c index 685e4736010..beb22911a59 100644 --- a/linuxkm/lkcapi_glue.c +++ b/linuxkm/lkcapi_glue.c @@ -295,6 +295,32 @@ static int linuxkm_lkcapi_sysfs_install(void) { (void)linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, NULL); return ret; } + +#ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG + ret = linuxkm_lkcapi_sysfs_install_node(&wc_linuxkm_rng_state_invalidate_attr, + NULL); + if (ret) { + (void)linuxkm_lkcapi_sysfs_deinstall_node(&deinstall_algs_attr, + NULL); + (void)linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, + NULL); + return ret; + } +#if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ + defined(WC_RNG_DEBUG_STATS) + ret = linuxkm_lkcapi_sysfs_install_node(&wc_linuxkm_rng_stats_attr, + NULL); + if (ret) { + (void)linuxkm_lkcapi_sysfs_deinstall_node(&wc_linuxkm_rng_state_invalidate_attr, + NULL); + (void)linuxkm_lkcapi_sysfs_deinstall_node(&deinstall_algs_attr, + NULL); + (void)linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, + NULL); + return ret; + } +#endif +#endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG */ installed_sysfs_LKCAPI_files = 1; } return 0; @@ -302,7 +328,24 @@ static int linuxkm_lkcapi_sysfs_install(void) { static int linuxkm_lkcapi_sysfs_deinstall(void) { if (installed_sysfs_LKCAPI_files) { - int ret = linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, NULL); + int ret; +#ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG +#if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ + defined(WC_RNG_DEBUG_STATS) + ret = linuxkm_lkcapi_sysfs_deinstall_node(&wc_linuxkm_rng_stats_attr, + NULL); + if (ret) + return ret; +#endif + /* removed first (LIFO), and in any case before RNG teardown can + * begin: the store handler walks the registry and reaches the + * daemon root. */ + ret = linuxkm_lkcapi_sysfs_deinstall_node(&wc_linuxkm_rng_state_invalidate_attr, + NULL); + if (ret) + return ret; +#endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG */ + ret = linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, NULL); if (ret) return ret; ret = linuxkm_lkcapi_sysfs_deinstall_node(&deinstall_algs_attr, NULL); diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 1478cf653fc..dad85fb92ff 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2147,10 +2147,424 @@ static int linuxkm_affinity_unlock(void *arg) { #endif /* !WOLFSSL_USE_SAVE_VECTOR_REGISTERS */ } +/* Registry of every kernel-module RNG object needing state-invalidation + * coverage: banks (default and tfm-private) and long-lived process-context + * RBGC leaves from LKCAPI_INITRNG(). Atomic-born leaves are deliberately + * excluded (see linuxkm_InitRng_DefaultRBGC()): the mutex is thereby never + * taken from atomic context, so it can sleep, and the daemon's leaf pass + * may gather entropy under it. */ +struct linuxkm_rng_object { + struct linuxkm_rng_object *prev, *next; + int is_bank; + union { + WC_RNG *rng; + struct wc_rng_bank *bank; + }; +}; +static DEFINE_MUTEX(wc_linuxkm_rng_registry_mutex); +static struct linuxkm_rng_object *wc_linuxkm_rng_registry_head; +/* Generation counter gating the daemon's registered-leaf recovery sweep: + * incremented by wc_linuxkm_rng_state_invalidate() before it releases the registry + * mutex, snapshotted by the daemon at sweep start, CAS'd from the snapshot + * to 0 at completion. A CAS failure means an invalidation landed since + * the snapshot -- the counter stays hot and the next pass re-sweeps. The + * daemon thereby pays one atomic load per iteration instead of a mutexed + * list walk. */ +static wolfSSL_Atomic_Int wc_linuxkm_rng_registry_needs_recovery = 0; + +static void wc_linuxkm_rng_registry_link(struct linuxkm_rng_object *obj) +{ + mutex_lock(&wc_linuxkm_rng_registry_mutex); + obj->prev = NULL; + obj->next = wc_linuxkm_rng_registry_head; + if (obj->next) + obj->next->prev = obj; + wc_linuxkm_rng_registry_head = obj; + mutex_unlock(&wc_linuxkm_rng_registry_mutex); +} + +static void wc_linuxkm_rng_registry_unlink(struct linuxkm_rng_object *obj) +{ + mutex_lock(&wc_linuxkm_rng_registry_mutex); + if (obj->prev) + obj->prev->next = obj->next; + else + wc_linuxkm_rng_registry_head = obj->next; + if (obj->next) + obj->next->prev = obj->prev; + mutex_unlock(&wc_linuxkm_rng_registry_mutex); +} + +/* wc_FreeRng() free hook for registered leaves: O(1) unlink (arg is the + * registry entry), then free the entry. Process context by the atomic-born + * exclusion rule. */ +static int wc_linuxkm_rng_registry_free_hook(const WC_RNG *rng, void *arg) +{ + struct linuxkm_rng_object *obj = (struct linuxkm_rng_object *)arg; + (void)rng; + wc_linuxkm_rng_registry_unlink(obj); + kfree(obj); + return 0; +} + +static void wc_linuxkm_rng_registry_add_rng(WC_RNG *rng) +{ + struct linuxkm_rng_object *obj = kmalloc(sizeof(*obj), GFP_KERNEL); + if (obj == NULL) + return; /* best-effort: an unregistered leaf is merely unprotected */ + obj->is_bank = 0; + obj->rng = rng; + if (wc_RNG_register_free_hook(rng, wc_linuxkm_rng_registry_free_hook, + obj) != 0) + { + kfree(obj); + return; + } + wc_linuxkm_rng_registry_link(obj); +} + +static int wc_linuxkm_rng_registry_bank_free_hook( + const struct wc_rng_bank *bank, void *arg) +{ + struct linuxkm_rng_object *obj = (struct linuxkm_rng_object *)arg; + (void)bank; + wc_linuxkm_rng_registry_unlink(obj); + kfree(obj); + return 0; +} + +static void wc_linuxkm_rng_registry_add_bank(struct wc_rng_bank *bank) +{ + struct linuxkm_rng_object *obj = kmalloc(sizeof(*obj), GFP_KERNEL); + if (obj == NULL) + return; /* best-effort: an unregistered bank is merely unprotected */ + obj->is_bank = 1; + obj->bank = bank; + if (wc_rng_bank_register_free_hook(bank, + wc_linuxkm_rng_registry_bank_free_hook, obj) != 0) + { + kfree(obj); + return; + } + wc_linuxkm_rng_registry_link(obj); +} + #define WC_LINUXKM_ENTROPY_DAEMON_MAGIC 0x6f77666c +/* platform announcement (VM fork/clone, resume from hibernation) that RNG + * state assumptions no longer hold: invalidate the daemon's local root + * directly (it is unleased by design), invalidate every bank instance, + * and wake the daemon -- its loop-head check recovers the root first, and + * consumers recover per-instance through the NEEDS_RECOVERY_E protocol + * and the daemon's recovery pass. */ +static int wc_linuxkm_rng_state_invalidate(void) { + struct linuxkm_rng_object *obj; + int ret = 0; + + /* Process context (vmfork notifier / pm notifier); the registry mutex + * is sleepable and never taken from atomic context. */ + mutex_lock(&wc_linuxkm_rng_registry_mutex); + for (obj = wc_linuxkm_rng_registry_head; obj != NULL; obj = obj->next) { + if (obj->is_bank) { + WC_RNG *daemon_root; + int this_ret = wc_rng_bank_invalidate_entropy(obj->bank, 0); + if ((this_ret != 0) && (ret == 0)) + ret = this_ret; +#ifndef WC_LINUXKM_NO_ENTROPY_DAEMON + daemon_root = wc_rng_bank_daemon_root_get(obj->bank); + if (daemon_root != NULL) + (void)wc_RNG_invalidate_entropy(daemon_root); + if (WOLFSSL_ATOMIC_LOAD(obj->bank->daemon_magic) == + WC_LINUXKM_ENTROPY_DAEMON_MAGIC) + { + struct task_struct *t = (struct task_struct *)obj->bank->daemon; + if (t != NULL) + wake_up_process(t); + } + else +#endif + { + /* daemon-less bank: recover synchronously -- the + * FOR_RECOVERY claim path in wc_rng_bank_reseed_range()'s + * checkouts claims the quarantined instances. */ + this_ret = wc_rng_bank_reseed_range(obj->bank, 0, -1, + WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT); + if ((this_ret != 0) && (ret == 0)) + ret = this_ret; + } + } + else { + (void)wc_RNG_invalidate_entropy(obj->rng); + } + } + (void)wolfSSL_Atomic_Int_FetchAdd(&wc_linuxkm_rng_registry_needs_recovery, + 1); + mutex_unlock(&wc_linuxkm_rng_registry_mutex); + + if (ret != 0) { + pr_err("ERROR: wc_linuxkm_rng_state_invalidate() walk returned err %d.\n", ret); + return -EINVAL; + } + pr_notice("wolfssl: RNG state invalidated; all instances will recover by credited reseed\n"); + return 0; +} + +/* Stock-kernel event coverage for the invalidation machinery: the kernel + * already broadcasts the two state-duplication events publicly -- VM fork + * (vmgenid, via the random_vmfork notifier chain, kernels >= 5.18) and + * resume from hibernation (pm notifier) -- so no kernel patch is needed to + * receive them. Both chains are blocking (process context), so the + * handler's registry mutex is legal, and both unregister calls return only + * after in-flight callbacks complete, so uninstall-before-teardown is + * race-free. */ + +#if IS_ENABLED(CONFIG_VMGENID) +static int wc_linuxkm_rng_vmfork_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + int ret; + (void)nb; + (void)action; + (void)data; /* the vmfork chain carries no payload; on kernels with the + * callback patch, the fork id itself reaches the module as + * harvest via the mix_pool_bytes hook. */ + ret = wc_linuxkm_rng_state_invalidate(); + if (ret != 0) + pr_err("libwolfssl: wc_linuxkm_rng_vmfork_notify: " + "wc_linuxkm_rng_state_invalidate failed with code %d.\n", ret); + return NOTIFY_OK; +} +static struct notifier_block wc_linuxkm_rng_vmfork_nb = { + .notifier_call = wc_linuxkm_rng_vmfork_notify +}; +#endif /* CONFIG_VMGENID */ + +#ifdef CONFIG_PM_SLEEP +static int wc_linuxkm_rng_pm_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + (void)nb; + (void)data; + /* mirror the native crng's policy: hibernation writes RNG state to + * disk (duplication-class); suspend-to-RAM does not. */ + if ((action == PM_POST_HIBERNATION) || (action == PM_POST_RESTORE)) { + int ret = wc_linuxkm_rng_state_invalidate(); + if (ret != 0) + pr_err("libwolfssl: wc_linuxkm_rng_pm_notify for action 0x%lx: " + "wc_linuxkm_rng_state_invalidate failed with code %d.\n", action, ret); + } + return NOTIFY_OK; +} +static struct notifier_block wc_linuxkm_rng_pm_nb = { + .notifier_call = wc_linuxkm_rng_pm_notify +}; +#endif /* CONFIG_PM_SLEEP */ + +static int wc_linuxkm_rng_notifiers_installed = 0; + +static void wc_linuxkm_rng_notifiers_install(void) +{ + if (wc_linuxkm_rng_notifiers_installed) + return; +#if IS_ENABLED(CONFIG_VMGENID) + if (register_random_vmfork_notifier(&wc_linuxkm_rng_vmfork_nb) != 0) + pr_warn("libwolfssl: register_random_vmfork_notifier failed -- " + "no VM-fork RNG invalidation coverage.\n"); +#endif +#ifdef CONFIG_PM_SLEEP + if (register_pm_notifier(&wc_linuxkm_rng_pm_nb) != 0) + pr_warn("libwolfssl: register_pm_notifier failed -- " + "no hibernation RNG invalidation coverage.\n"); +#endif + wc_linuxkm_rng_notifiers_installed = 1; +} + +static void wc_linuxkm_rng_notifiers_uninstall(void) +{ + if (! wc_linuxkm_rng_notifiers_installed) + return; +#ifdef CONFIG_PM_SLEEP + (void)unregister_pm_notifier(&wc_linuxkm_rng_pm_nb); +#endif +#if IS_ENABLED(CONFIG_VMGENID) + (void)unregister_random_vmfork_notifier(&wc_linuxkm_rng_vmfork_nb); +#endif + wc_linuxkm_rng_notifiers_installed = 0; +} + +static ssize_t wc_linuxkm_rng_state_invalidate_handler(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int mode = 0; + int ret; + + (void)kobj; + (void)attr; + + if (kstrtoint(buf, 10, &mode) < 0) + return -EINVAL; + if (mode == 1) { + /* direct local exercise */ + ret = wc_linuxkm_rng_state_invalidate(); +#ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + pr_info("wc_linuxkm_rng_state_invalidate_handler: called wc_linuxkm_rng_state_invalidate, retval %d.\n", ret); +#endif + return ret ? -EIO : (ssize_t)count; + } +#if IS_ENABLED(CONFIG_VMGENID) + if (mode == 2) { + u8 fake_id[16]; + get_random_bytes(fake_id, sizeof fake_id); /* any unique blob */ + add_vmfork_randomness(fake_id, sizeof fake_id); /* full wire */ +#ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + pr_info("wc_linuxkm_rng_state_invalidate_handler: called add_vmfork_randomness.\n"); +#endif + return (ssize_t)count; + } +#endif /* CONFIG_VMGENID */ +#if IS_ENABLED(CONFIG_PM_SLEEP) + if (mode == 3) { + /* synthetic PM_POST_HIBERNATION delivered directly to our own pm + * callback: exercises the wake-from-hibernation leg from the + * notifier boundary inward. (Injecting into the kernel's pm chain + * itself would deliver a fake hibernation event to every + * registered subsystem -- not a test, an incident.) */ + ret = wc_linuxkm_rng_pm_notify(&wc_linuxkm_rng_pm_nb, + PM_POST_HIBERNATION, NULL); +#ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG + pr_info("wc_linuxkm_rng_state_invalidate_handler: called wc_linuxkm_rng_pm_notify(PM_POST_HIBERNATION), retval %d.\n", ret); +#endif + return (ret == NOTIFY_OK) ? (ssize_t)count : -EIO; + } +#endif /* CONFIG_PM_SLEEP */ + + return -EINVAL; +} + +static struct kobj_attribute wc_linuxkm_rng_state_invalidate_attr = + __ATTR(rng_state_invalidate, 0220, NULL, wc_linuxkm_rng_state_invalidate_handler); + #ifndef WC_LINUXKM_NO_ENTROPY_DAEMON +#if defined(WC_LINUXKM_VMGENID_POLL) || \ + (defined(CONFIG_ACPI) && !IS_ENABLED(CONFIG_VMGENID)) +/* Without CONFIG_VMGENID, we can only detect VM fork events by polling. + * Mainline gained vmgenid and the random_vmfork notifier chain together in + * kernel 5.18, so on older kernels and kernels with CONFIG_VMGENID configured + * off, there is no event to subscribe to -- but the ACPI VM Generation ID + * device (Microsoft spec; exposed by QEMU, Hyper-V, VMware) is still present, + * and its 16-byte counter changes exactly when the hypervisor + * forks/clones/restores the VM. The daemon polls it each iteration (a 16-byte + * compare of a memremap'd page -- effectively free) and, on change, invalidates + * all module RNG state and recovers its own root immediately, folding the new + * generation id into the credited recovery reseed as nonce. Detection latency + * is bounded by the daemon nap. + * + * All state is per-daemon, on the daemon's stack: wc_linuxkm_entropy_daemon() + * is threadsafe, and concurrent daemons discover, map, and poll + * independently. Redundant detections by multiple daemons are benign: + * wc_linuxkm_rng_state_invalidate() is idempotent, and the sweep generation + * counter dedups the recovery work. + */ + +#ifndef WC_LINUXKM_VMGENID_POLL + #define WC_LINUXKM_VMGENID_POLL +#endif + +struct wc_linuxkm_vmgenid_poll_state { + void *map; + int state; /* 0 untried, 1 mapped, -1 absent */ + u8 last[16]; +}; + +static acpi_status wc_linuxkm_vmgenid_acpi_cb(acpi_handle handle, u32 depth, + void *context, void **ret) +{ + struct wc_linuxkm_vmgenid_poll_state *st = + (struct wc_linuxkm_vmgenid_poll_state *)context; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + u64 gpa; + + (void)depth; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, (acpi_string)"ADDR", NULL, &buf))) + return AE_OK; /* not it -- keep walking */ + obj = (union acpi_object *)buf.pointer; + if ((obj != NULL) && (obj->type == ACPI_TYPE_PACKAGE) && + (obj->package.count == 2) && + (obj->package.elements[0].type == ACPI_TYPE_INTEGER) && + (obj->package.elements[1].type == ACPI_TYPE_INTEGER)) + { + gpa = (obj->package.elements[0].integer.value & 0xffffffffULL) | + (obj->package.elements[1].integer.value << 32); + if (gpa != 0) { + st->map = memremap(gpa, 16, MEMREMAP_WB); + if (st->map != NULL) { + kfree(buf.pointer); + *ret = st->map; + return AE_CTRL_TERMINATE; + } + } + } + kfree(buf.pointer); + return AE_OK; +} + +static void wc_linuxkm_vmgenid_poll(struct wc_linuxkm_vmgenid_poll_state *st, + WC_RNG *local_root) +{ + if (st->state == 0) { + /* one-time discovery, in daemon task context. The device's _CID + * is "VM_Gen_Counter" per the Microsoft spec (QEMU adds _HID + * "QEMUVGID"); acpi_get_devices() matches against both HID and + * CID lists. */ + void *found = NULL; + (void)acpi_get_devices("VM_Gen_Counter", wc_linuxkm_vmgenid_acpi_cb, + st, &found); + if (found == NULL) + (void)acpi_get_devices("QEMUVGID", wc_linuxkm_vmgenid_acpi_cb, + st, &found); + if (found != NULL) { + memcpy(st->last, st->map, 16); + st->state = 1; + pr_info("libwolfssl: vmgenid ACPI poller active (VM-fork " + "RNG invalidation coverage).\n"); + } + else { + st->state = -1; /* bare metal or no device */ + } + return; + } + if (st->state != 1) + return; + + if (memcmp(st->map, st->last, 16) != 0) { + memcpy(st->last, st->map, 16); + pr_notice("libwolfssl: VM generation change detected by poller.\n"); + (void)wc_linuxkm_rng_state_invalidate(); + /* recover our root immediately, folding the new generation id in + * as the credited reseed's nonce; the loop-head recovery check + * then finds the flag already clear. (Invalidate-then-reseed + * ordering keeps the recovery-entry scrub ahead of the fold.) */ + if (local_root != NULL) + (void)wc_RNG_DRBG_Reseed_Now(local_root, (const byte *)st->last, + 16); + } +} + +static void wc_linuxkm_vmgenid_poll_teardown( + struct wc_linuxkm_vmgenid_poll_state *st) +{ + if (st->map != NULL) { + memunmap(st->map); + st->map = NULL; + } + st->state = 0; +} +#endif /* CONFIG_ACPI && !CONFIG_VMGENID */ + /* Entropy-banking daemon for the default rng bank: cycles the bank's * instances, keeping each DRBG's nextSeed aperture full so that * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED checkouts can perform credited @@ -2228,6 +2642,9 @@ static int wc_linuxkm_entropy_daemon(void *arg) struct wc_rng_bank *bank = (struct wc_rng_bank *)arg; int i; int ret; +#ifdef WC_LINUXKM_VMGENID_POLL + struct wc_linuxkm_vmgenid_poll_state vmgenid_poll_state = {}; +#endif if (WOLFSSL_ATOMIC_LOAD(bank->daemon_magic) != WC_LINUXKM_ENTROPY_DAEMON_MAGIC) return -EINVAL; @@ -2254,7 +2671,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) local_root = NULL; } else { - /* published in the bank's daemon-root slot; retracted before + /* published for wc_linuxkm_rng_state_invalidate(); retracted before * teardown. safe: the random_bytes handlers are unregistered * (and drained) before the daemon is stopped. */ (void)wc_rng_bank_daemon_root_set(bank, local_root); @@ -2288,6 +2705,10 @@ static int wc_linuxkm_entropy_daemon(void *arg) if (kthread_should_stop()) break; +#ifdef WC_LINUXKM_VMGENID_POLL + wc_linuxkm_vmgenid_poll(&vmgenid_poll_state, local_root); +#endif + #if defined(WC_RNG_HAVE_LOCK) && \ (defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED)) /* deterministic local_root recovery after a state-invalidation @@ -2405,6 +2826,45 @@ static int wc_linuxkm_entropy_daemon(void *arg) /* if we're coping with congestion hits, continue here, don't bog down * in primary seed ops. */ +#if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) + /* registered-leaf pass: bank RBGC seeds from local_root into + * long-lived leaves that are invalidated or chain-backed, so their + * next generate recovers/promotes in place + * (WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED). + * Sleepable-mutex context; entropy gathers are legal under it by + * the atomic-born exclusion rule. */ + if (local_root != NULL) { + WC_ATOMIC_INT_ARG needs_recovery_snapshot = + WOLFSSL_ATOMIC_LOAD(wc_linuxkm_rng_registry_needs_recovery); + if (needs_recovery_snapshot != 0) { + struct linuxkm_rng_object *obj; + mutex_lock(&wc_linuxkm_rng_registry_mutex); + for (obj = wc_linuxkm_rng_registry_head; obj != NULL; + obj = obj->next) + { + WC_RNG_lock_arg_t leaf_lock_state; + if (obj->is_bank) + continue; + if (wc_RNG_lock_read(obj->rng, &leaf_lock_state) != 0) + continue; + if ((leaf_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) || + (wc_RNG_DRBG_GetRBGCStratum(obj->rng) > 0)) + { + if (wc_RNG_DRBG_NextSeedGenerate_RBGC(obj->rng, + local_root, WC_DRBG_NEXT_SEED_LEN) == 0) + progress = 1; + } + } + mutex_unlock(&wc_linuxkm_rng_registry_mutex); + /* on failure, an invalidation landed since the snapshot: + * leave the counter hot and re-sweep next pass. */ + (void)wolfSSL_Atomic_Int_CompareExchange( + &wc_linuxkm_rng_registry_needs_recovery, + &needs_recovery_snapshot, 0); + } + } +#endif /* WC_RNG_HAVE_NEXT_SEED && WC_RNG_HAVE_RBGC */ + if (congested_progress) goto next_pass; @@ -2497,6 +2957,9 @@ static int wc_linuxkm_entropy_daemon(void *arg) s._stats_n_nextuncreditedseed_redeemed); } #endif /* WC_RNG_DEBUG_STATS */ +#ifdef WC_LINUXKM_VMGENID_POLL + wc_linuxkm_vmgenid_poll_teardown(&vmgenid_poll_state); +#endif (void)wc_rng_bank_daemon_root_set(bank, NULL); (void)wc_FreeRng(local_root); XFREE(local_root, NULL, DYNAMIC_TYPE_RNG); @@ -2611,6 +3074,9 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) ret = -EINVAL; } + if (ret == 0) + wc_linuxkm_rng_registry_add_bank(ctx); + return ret; } @@ -2833,6 +3299,13 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { ret); ret = wc_InitRng(rng); } + if (ret == 0) { + /* Long-lived process-context leaves join the invalidation registry; + * atomic-born leaves are excluded by rule (and are transient by + * nature). Registration is best-effort. */ + if (can_sleep) + wc_linuxkm_rng_registry_add_rng(rng); + } return ret; } @@ -3755,6 +4228,30 @@ static struct wc_rng_bank default_bank; static int default_bank_inited; #endif +#ifdef WC_RNG_DEBUG_STATS +/* control channel at /sys/module/libwolfssl/rng_stats: echo 1 to dump the + * current RNG stats to the kernel log on demand (they otherwise appear + * only at teardown). */ +static ssize_t wc_linuxkm_rng_stats_handler(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int arg; + + (void)kobj; + (void)attr; + + if (kstrtoint(buf, 10, &arg) || (arg != 1)) + return -EINVAL; + if (! default_bank_inited) + return -ENODEV; + wc_linuxkm_rng_dump_stats(&default_bank); + return (ssize_t)count; +} +static struct kobj_attribute wc_linuxkm_rng_stats_attr = + __ATTR(rng_stats, 0220, NULL, wc_linuxkm_rng_stats_handler); +#endif /* WC_RNG_DEBUG_STATS */ + static int wc_linuxkm_drbg_startup(void) { int ret; @@ -3971,6 +4468,10 @@ static int wc_linuxkm_drbg_startup(void) pr_info("%s registered as systemwide default stdrng.\n", wc_linuxkm_drbg.base.cra_driver_name); pr_info("libwolfssl: to unload module, first echo 1 > /sys/module/libwolfssl/deinstall_algs\n"); + /* stock-notifier invalidation coverage rides with the registered + * DRBGs, patched and unpatched kernels alike. */ + wc_linuxkm_rng_notifiers_install(); + #ifdef LINUXKM_DRBG_GET_RANDOM_BYTES #ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS @@ -4064,6 +4565,11 @@ static int wc_linuxkm_drbg_cleanup(void) { */ int ret; + /* the notifier callbacks walk the RNG registry: uninstall them + * before any of what they reference is dismantled. unregister + * returns only after in-flight callbacks complete. */ + wc_linuxkm_rng_notifiers_uninstall(); + #ifdef LINUXKM_DRBG_GET_RANDOM_BYTES /* we need to unregister the get_random_bytes handlers first to remove From f7e9a22bdbab33c69f002cd1db6c46b7d2d0a047 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 8 Sep 2026 22:12:00 -0500 Subject: [PATCH 032/102] wolfssl/wolfcrypt/rng_bank.h: fix collision in WC_RNG_BANK_FLAG_* allocations. --- wolfssl/wolfcrypt/rng_bank.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index ae9ec1a1111..0e1805c9998 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -75,6 +75,16 @@ * without this flag; the flag makes the intent explicit and * interaction-safe. */ #define WC_RNG_BANK_FLAG_FOR_RECOVERY (1U << 8) +/* WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY admits the caller to a quarantined + * (WC_RNG_LOCK_ENTROPY_INVALIDATED) instance when no cheaper admission + * applies, accepting the recovery obligation: wc_rng_bank_checkout() may + * then return NEEDS_RECOVERY_E with the checkout otherwise complete -- + * *rng_inst set, instance lock (and any affinity/vector-inhibit state) + * HELD. The caller owns the lease and must either recover the instance + * (a credited reseed, e.g. wc_RNG_DRBG_Reseed_Now(), clears the + * quarantine) or check it back in. Ordinary consumers that cannot + * complete a recovery must not pass this flag. */ +#define WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY (1U << 9) /* WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED guarantees that * wc_rng_bank_checkout() (and APIs built on it, e.g. wc_rng_bank_spawn()) * either returns a lease on an in-service instance (status WC_DRBG_OK) or @@ -88,7 +98,7 @@ * instances is BAD_STATE_E. Contradicts, and is rejected with, * _FOR_RECOVERY. Applies to instance status only; reseed-due diversion * semantics are unchanged. */ -#define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1U << 9) +#define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1U << 10) /* WC_RNG_BANK_FLAG_QUIET suppresses the facility's WC_VERBOSE_RNG * operational warnings -- expected-condition notices such as the * reseed-due-instance handout, reinit retry/timeout reports, the @@ -97,7 +107,7 @@ * log. A bank-level flag only, set at wc_rng_bank_init(); it has no * per-call meaning and never suppresses refcount/consistency * diagnostics. */ -#define WC_RNG_BANK_FLAG_QUIET (1U << 10) +#define WC_RNG_BANK_FLAG_QUIET (1U << 11) /* WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (bank-level, set at * wc_rng_bank_init()) declares that the bank's lifetime is guaranteed by * its container to enclose all checkouts (e.g. a bank embedded in a @@ -110,23 +120,13 @@ * Contract: with this flag, a wc_rng_bank_fini() racing live checkouts is * a use-after-free instead of BUSY_E -- only containers whose teardown * provably quiesces consumers first may set it. */ -#define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 11) -#define WC_RNG_BANK_FLAG_INIT_RBGC (1U << 12) -/* WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY admits the caller to a quarantined - * (WC_RNG_LOCK_ENTROPY_INVALIDATED) instance when no cheaper admission - * applies, accepting the recovery obligation: wc_rng_bank_checkout() may - * then return NEEDS_RECOVERY_E with the checkout otherwise complete -- - * *rng_inst set, instance lock (and any affinity/vector-inhibit state) - * HELD. The caller owns the lease and must either recover the instance - * (a credited reseed, e.g. wc_RNG_DRBG_Reseed_Now(), clears the - * quarantine) or check it back in. Ordinary consumers that cannot - * complete a recovery must not pass this flag. */ -#define WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY (1U << 13) -#define WC_RNG_BANK_FLAG_DEFAULT_BANK (1U << 13) -#define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 14) +#define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 12) +#define WC_RNG_BANK_FLAG_INIT_RBGC (1U << 13) +#define WC_RNG_BANK_FLAG_DEFAULT_BANK (1U << 14) +#define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 15) /* wc_rng_bank_spawn[_new]() only: the child is born with * WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED. */ -#define WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE (1U << 15) +#define WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE (1U << 16) /* base lock states are WC_RNG_LOCK_FREE / WC_RNG_LOCK_HELD in random.h; * these annotation bits ride above WC_RNG_LOCK_HELD via From adc551c14e264b5b89850ea1189da5533424b65f Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 8 Sep 2026 22:27:30 -0500 Subject: [PATCH 033/102] .wolfssl_known_macro_extras: add CONFIG_ACPI and CONFIG_PM_SLEEP. --- .wolfssl_known_macro_extras | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 9c6952c8ddb..171bcceff34 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -63,6 +63,7 @@ CIOCGSESSINFO CLOCK_MONOTONIC CMSIS_OS2_H_ COMPONENT_WOLFSSL +CONFIG_ACPI CONFIG_ARCH_CHIP_STM32F746ZG CONFIG_ARCH_CHIP_STM32H743ZI CONFIG_ARCH_CHIP_STM32L552ZE @@ -155,6 +156,7 @@ CONFIG_NET_SOCKETS_SOCKOPT_TLS CONFIG_NEWLIB_LIBC CONFIG_NEWLIB_NANO_FORMAT CONFIG_PICOLIBC +CONFIG_PM_SLEEP CONFIG_POSIX_API CONFIG_POSIX_THREADS CONFIG_PREEMPT_COUNT From a8911b10d9b3b686929107a41341eaff6026660e Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 8 Sep 2026 23:28:16 -0500 Subject: [PATCH 034/102] wolfcrypt/test/test.c: add FIPS gate for rng_entropy_invalidate_test(); wolfcrypt/src/rng_bank.c: in rng_bank_spawn(), add missing feature-sensing gate for WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED. --- wolfcrypt/src/rng_bank.c | 2 ++ wolfcrypt/test/test.c | 27 ++++++++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 1d4632cddbd..5a3bbdd181c 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -1751,8 +1751,10 @@ static int rng_bank_spawn( { word32 child_init_flags = WC_RNG_INIT_FLAGS_NONE; +#ifdef WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE) child_init_flags |= WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; +#endif if (leaf_stack != NULL) { ret = wc_InitRngNonceRBGC(leaf_stack, WC_RNG_BANK_INST_TO_RNG(rng_inst), diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 675a2a31383..aeab4688506 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -944,12 +944,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void); (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); #endif +#if defined(WC_RNG_BANK_SUPPORT) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); +#endif #ifdef WC_RNG_HAVE_RBGC WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); #endif -#ifdef WC_RNG_BANK_SUPPORT -WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); -#endif #ifdef WC_RNG_HAVE_NEXT_SEED WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); #endif @@ -2626,18 +2627,19 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ else TEST_PASS("RNGSVC test passed!\n"); #endif +#if defined(WC_RNG_BANK_SUPPORT) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) + if ((ret = rng_entropy_invalidate_test()) != 0) + TEST_FAIL("RNGINVAL test failed!\n", ret); + else + TEST_PASS("RNGINVAL test passed!\n"); +#endif #ifdef WC_RNG_HAVE_RBGC if ((ret = rng_drbg_rbgc_test()) != 0) TEST_FAIL("RNGRBGC test failed!\n", ret); else TEST_PASS("RNGRBGC test passed!\n"); #endif -#ifdef WC_RNG_BANK_SUPPORT - if ((ret = rng_entropy_invalidate_test()) != 0) - TEST_FAIL("RNGINVAL test failed!\n", ret); - else - TEST_PASS("RNGINVAL test passed!\n"); -#endif #ifdef WC_RNG_HAVE_NEXT_SEED if ((ret = rng_drbg_nextseed_test()) != 0) TEST_FAIL("RNGNXTS test failed!\n", ret); @@ -29411,6 +29413,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) #endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && */ /* (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ +#if defined(WC_RNG_BANK_SUPPORT) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) + /* Unit coverage for WC_RNG_LOCK_ENTROPY_INVALIDATED and the * invalidation-recovery protocol (VM fork / resume), exercised through the * regime-portable bank-instance latch interface so it runs identically @@ -29421,7 +29426,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) * invalidation, clear_extra immunity, and (v7+) unlocked recovery through * the forced credited reseed, banked-next-seed purge, and RBGC chain * recovery. */ -#ifdef WC_RNG_BANK_SUPPORT + #ifdef WC_RNG_HAVE_FREE_HOOK static int rng_inval_test_bank_hook_fired = 0; static int rng_inval_test_bank_hook_cb(const struct wc_rng_bank *bank, @@ -29961,7 +29966,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) return ret; } -#endif /* WC_RNG_BANK_SUPPORT */ +#endif /* WC_RNG_BANK_SUPPORT && (!HAVE_FIPS || FIPS_VERSION3_GE(5,2,4)) */ #ifdef WC_RNG_HAVE_RBGC From a975de00b51421184c62a29926dea52ec9b9ed9b Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 00:01:15 -0500 Subject: [PATCH 035/102] wolfcrypt/src/random.c: in wc_RNG_DRBG_NextSeedGenerate_local(), add missing const to nonce arg; wolfcrypt/test/test.c: gate out argon2_test() if WOLFSSL_NO_MALLOC, and in rng_entropy_invalidate_test(), add missing !WC_NO_CONSTRUCTORS gates. --- wolfcrypt/src/random.c | 4 ++-- wolfcrypt/test/test.c | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 1be260ce5f8..05131059cfb 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3920,7 +3920,7 @@ static WC_INLINE int NextUncreditedSeedPtrs(WC_RNG* rng, byte** seed, word32 *ne * published; a failed test consumes the material (use-once) and returns the * test's error, leaving an empty bank for the next cycle. A gather failure * leaves the partial bank intact for retry. */ -static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, byte *nonce, word32 n) +static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, const byte *nonce, word32 n) { byte* seed; wolfSSL_Atomic_Int* lenp; @@ -4264,7 +4264,7 @@ int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, const byte *nonce, return BAD_FUNC_ARG; /* _local's nonce arm only reads the buffer; the parameter is non-const * for the benefit of the other arms. */ - return wc_RNG_DRBG_NextSeedGenerate_local(rng, NULL, (byte *)nonce, + return wc_RNG_DRBG_NextSeedGenerate_local(rng, NULL, nonce, nonceSz); } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index aeab4688506..98dfa67e81e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -987,7 +987,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf2_test(void); #if !defined(NO_PWDBASED) && defined(HAVE_SCRYPT) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t scrypt_test(void); #endif -#ifdef HAVE_ARGON2 +#if defined(HAVE_ARGON2) && !defined(WOLFSSL_NO_MALLOC) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t argon2_test(void); #endif #ifdef HAVE_ECC @@ -3172,7 +3172,7 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("PWDBASED test passed!\n"); #endif -#ifdef HAVE_ARGON2 +#if defined(HAVE_ARGON2) && !defined(WOLFSSL_NO_MALLOC) if ( (ret = argon2_test()) != 0) TEST_FAIL("ARGON2 test failed!\n", ret); else @@ -29878,7 +29878,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) #endif /* NEXT_SEED && RBGC && !RDSEED && !RDRAND */ #endif /* WC_RNG_HAVE_LOCK && (!HAVE_FIPS || >= 7.0.0) */ -#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_RNG_HAVE_LOCK) +#if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_RNG_HAVE_LOCK) && \ + !defined(WC_NO_CONSTRUCTORS) /* WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY contract: checkout of a * quarantined instance returns NEEDS_RECOVERY_E with the lease held; * a credited reseed by the lease-holder recovers it. */ @@ -29936,7 +29937,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); } -#endif /* WC_RNG_BANK_DEFAULT_SUPPORT && WC_RNG_HAVE_LOCK */ +#endif /* WC_RNG_BANK_DEFAULT_SUPPORT && WC_RNG_HAVE_LOCK && */ + /* !WC_NO_CONSTRUCTORS */ out: @@ -41223,7 +41225,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t openssl_evpSig_test(void) #endif /* OPENSSL_EXTRA */ -#ifdef HAVE_ARGON2 +#if defined(HAVE_ARGON2) && !defined(WOLFSSL_NO_MALLOC) /* Test vectors from RFC 9106 section 5, which uses the same inputs for all * three variants: p=4, T=32, m=32, t=3, v=0x13, with a secret and associated * data supplied. */ @@ -41357,7 +41359,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t argon2_test(void) return 0; } -#endif /* HAVE_ARGON2 */ +#endif /* HAVE_ARGON2 && !WOLFSSL_NO_MALLOC */ #ifndef NO_PWDBASED #ifdef HAVE_SCRYPT From 7f9f4dd8727eb89d208e5a685b569c392429512d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 13:50:58 -0500 Subject: [PATCH 036/102] linuxkm/lkcapi_sha_glue.c: * add static struct wc_rng_bank *default_bank alongside wc_linuxkm_rng_initing_default_bank_flag, and use it consistently in all configurations; * add static struct wc_rng_bank local_default_bank in wc_linuxkm_drbg_startup() local to the direct wc_linuxkm_rng_bank_init(); move inclusion of linux/acpi.h and linux/io.h from linuxkm/linuxkm_wc_port.h to linuxkm/lkcapi_sha_glue.c to avoid collision with "equal" in wolfcrypt/src/ge_operations.c. --- linuxkm/linuxkm_wc_port.h | 2 -- linuxkm/lkcapi_sha_glue.c | 49 +++++++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 661e77c1a40..6902c62cdf6 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -799,8 +799,6 @@ #ifdef CONFIG_PM_SLEEP #include #endif - #include - #include #endif /* !WC_CONTAINERIZE_THIS */ #endif /* LINUXKM_LKCAPI_REGISTER */ diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index dad85fb92ff..d145b6396af 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -49,6 +49,15 @@ #include #include +#ifdef LINUXKM_LKCAPI_REGISTER + _Pragma("GCC diagnostic push"); + _Pragma("GCC diagnostic ignored \"-Wpointer-arith\""); + _Pragma("GCC diagnostic ignored \"-Wbad-function-cast\""); + #include + #include + _Pragma("GCC diagnostic pop"); +#endif + #define WOLFKM_SHA1_NAME "sha1" #define WOLFKM_SHA2_224_NAME "sha224" #define WOLFKM_SHA2_256_NAME "sha256" @@ -2066,6 +2075,7 @@ struct wc_swallow_the_semicolon #endif static volatile int wc_linuxkm_rng_initing_default_bank_flag = 0; +static struct wc_rng_bank *default_bank; #ifndef WC_LINUXKM_INITRNG_TIMEOUT_SEC #define WC_LINUXKM_INITRNG_TIMEOUT_SEC 30 @@ -2977,6 +2987,11 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) word32 flags = WC_RNG_BANK_FLAG_CAN_WAIT; unsigned long uncredited_nonce = random_get_entropy(); + if (wc_linuxkm_rng_initing_default_bank_flag && (default_bank != NULL)) { + pr_err("BUG: wc_linuxkm_rng_bank_init() called with wc_linuxkm_rng_initing_default_bank_flag asserted and default_bank != NULL.\n"); + return -EINVAL; + } + #if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && \ defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) /* before v7, the SHA-2 implementations couldn't dynamically switch between @@ -3024,8 +3039,9 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) pr_err("ERROR: wc_rng_bank_default_set() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); WC_DUMP_BACKTRACE_NONDEBUG; } -#ifndef WC_LINUXKM_NO_ENTROPY_DAEMON else { + default_bank = ctx; +#ifndef WC_LINUXKM_NO_ENTROPY_DAEMON /* Try to launch the entropy daemon. Failure is nonfatal: * the inline reseed and recovery paths serve daemonless * operation. */ @@ -3052,8 +3068,8 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) } } } - } #endif /* !WC_LINUXKM_NO_ENTROPY_DAEMON */ + } } } else { @@ -3175,6 +3191,11 @@ static int wc_linuxkm_rng_bank_fini(struct wc_rng_bank *ctx) { #endif /* !WC_LINUXKM_NO_ENTROPY_DAEMON */ if (ctx->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK) { + /* clear the _inited flag unconditionally -- if either + * wc_rng_bank_default_clear() or wc_rng_bank_fini() fails, then the ctx + * is in an indeterminate state and should not be accessed. */ + default_bank = NULL; + ret = wc_rng_bank_default_clear(ctx); if (ret != 0) pr_err("ERROR: wc_rng_bank_default_clear() in wc_linuxkm_rng_bank_fini() returned code %d\n", ret); @@ -4222,12 +4243,6 @@ static int wc_get_random_bytes_user_kretprobe_installed = 0; #endif /* LINUXKM_DRBG_GET_RANDOM_BYTES */ -#if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ - (LINUX_VERSION_CODE >= KERNEL_VERSION(7, 1, 0)) -static struct wc_rng_bank default_bank; -static int default_bank_inited; -#endif - #ifdef WC_RNG_DEBUG_STATS /* control channel at /sys/module/libwolfssl/rng_stats: echo 1 to dump the * current RNG stats to the kernel log on demand (they otherwise appear @@ -4243,9 +4258,9 @@ static ssize_t wc_linuxkm_rng_stats_handler(struct kobject *kobj, if (kstrtoint(buf, 10, &arg) || (arg != 1)) return -EINVAL; - if (! default_bank_inited) + if (! default_bank) return -ENODEV; - wc_linuxkm_rng_dump_stats(&default_bank); + wc_linuxkm_rng_dump_stats(default_bank); return (ssize_t)count; } static struct kobj_attribute wc_linuxkm_rng_stats_attr = @@ -4431,13 +4446,13 @@ static int wc_linuxkm_drbg_startup(void) else #endif /* CONFIG_CRYPTO_FIPS */ { - ret = wc_linuxkm_rng_bank_init(&default_bank); + static struct wc_rng_bank local_default_bank; + ret = wc_linuxkm_rng_bank_init(&local_default_bank); wc_linuxkm_rng_initing_default_bank_flag = 0; if (ret) { pr_err("ERROR: wc_linuxkm_rng_bank_init returned %d\n", ret); return ret; } - default_bank_inited = 1; } #endif /* >= 7.1.0 */ @@ -4455,9 +4470,8 @@ static int wc_linuxkm_drbg_startup(void) if (ret != 0) { #if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ (LINUX_VERSION_CODE >= KERNEL_VERSION(7, 1, 0)) - if (default_bank_inited) { - (void)wc_linuxkm_rng_bank_fini(&default_bank); - default_bank_inited = 0; + if (default_bank != NULL) { + (void)wc_linuxkm_rng_bank_fini(default_bank); } #endif return -ECANCELED; @@ -4630,11 +4644,10 @@ static int wc_linuxkm_drbg_cleanup(void) { } else #endif /* CONFIG_CRYPTO_FIPS */ - if (default_bank_inited) { - ret = wc_linuxkm_rng_bank_fini(&default_bank); + if (default_bank) { + ret = wc_linuxkm_rng_bank_fini(default_bank); if (ret) pr_err("ERROR: wc_linuxkm_rng_bank_fini in wc_linuxkm_drbg_cleanup failed: %d\n", ret); - default_bank_inited = 0; } #endif /* >= 7.1.0 */ From 3fa61e215142c2c4cb389643e86e859565f05b6d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 15:04:29 -0500 Subject: [PATCH 037/102] wolfcrypt/src/random.c, wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/random.h, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/test/test.c: fixes for !HAVE_HASHDRBG. --- wolfcrypt/src/random.c | 2 ++ wolfcrypt/src/rng_bank.c | 25 +++++++++++++++++++++++-- wolfcrypt/test/test.c | 31 +++++++++++++++++++++++++------ wolfssl/wolfcrypt/random.h | 2 ++ wolfssl/wolfcrypt/rng_bank.h | 8 ++++++++ 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 05131059cfb..87b3a2da33d 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3214,6 +3214,7 @@ int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) return 0; } +#ifdef HAVE_HASHDRBG WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { WC_RNG_lock_arg_t cur_lock; @@ -3260,6 +3261,7 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { return 0; } +#endif /* HAVE_HASHDRBG */ #endif /* WC_RNG_HAVE_LOCK */ diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 5a3bbdd181c..b165e35788c 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -783,7 +783,9 @@ WOLFSSL_API int wc_rng_bank_checkout( ) { int inst_unusable; +#ifdef HAVE_HASHDRBG wc_drbg_reseed_ctr_t cur_reseed_ctr = 0; +#endif *rng_inst = &bank->rngs[preferred_inst_offset]; @@ -843,9 +845,13 @@ WOLFSSL_API int wc_rng_bank_checkout( * such instances -- never due for reseed -- so no separate * DRBG-presence test is needed here. */ +#ifdef HAVE_HASHDRBG inst_unusable = (wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(*rng_inst)) != WC_DRBG_OK); +#else + inst_unusable = 0; +#endif /* Divert (release and move on / retry) when: * @@ -861,9 +867,16 @@ WOLFSSL_API int wc_rng_bank_checkout( * service or is due for reseed for a caller that can't * wait. (The lap disarm is the anti-livelock provision; * with (a) in force, the guarantee supersedes it.) + * + * Without HAVE_HASHDRBG neither divert cause can exist -- + * inst_unusable is constant 0 and there is no reseed + * schedule -- so the failover disjunct is compiled out + * entirely: nothing to divert for, never divert. */ if ((inst_unusable && - (flags & WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED)) || + (flags & WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED)) +#ifdef HAVE_HASHDRBG + || ((flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) && (n_rngs_tried < bank->n_rngs) && (inst_unusable || @@ -876,7 +889,9 @@ WOLFSSL_API int wc_rng_bank_checkout( && (wc_RNG_DRBG_NextSeedCurrent(WC_RNG_BANK_INST_TO_RNG(*rng_inst), &NextSeedCurrent) == 0) && (NextSeedCurrent != WC_DRBG_NEXT_SEED_READY) #endif - )))) + ))) +#endif /* HAVE_HASHDRBG */ + ) { if (inst_unusable) diverted_unusable = 1; @@ -884,6 +899,7 @@ WOLFSSL_API int wc_rng_bank_checkout( *rng_inst = NULL; } else { +#ifdef HAVE_HASHDRBG #ifdef WC_VERBOSE_RNG if ((! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) && (! (flags & (WC_RNG_BANK_FLAG_CAN_WAIT | @@ -922,6 +938,7 @@ WOLFSSL_API int wc_rng_bank_checkout( goto out; } } +#endif /* HAVE_HASHDRBG */ #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS if ((flags | bank->flags) & WC_RNG_BANK_FLAG_NO_VECTOR_OPS) { @@ -1825,6 +1842,8 @@ WOLFSSL_API int wc_rng_bank_spawn_new( #endif /* !WC_NO_CONSTRUCTORS */ #endif /* WC_RNG_HAVE_RBGC */ +#ifdef HAVE_HASHDRBG + WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int first_inst, int last_inst, const byte* seed, word32 seedSz, @@ -2184,6 +2203,8 @@ WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, return ret; } +#endif /* HAVE_HASHDRBG */ + #ifdef WC_HAVE_RNG_BANKREF static int wc_InitRng_BankRef_local(struct wc_rng_bank *bank, WC_RNG **rng) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 98dfa67e81e..1d107340d13 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -944,7 +944,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void); (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); #endif -#if defined(WC_RNG_BANK_SUPPORT) && \ +#if defined(WC_RNG_BANK_SUPPORT) && defined(HAVE_HASHDRBG) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); #endif @@ -2627,7 +2627,7 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ else TEST_PASS("RNGSVC test passed!\n"); #endif -#if defined(WC_RNG_BANK_SUPPORT) && \ +#if defined(WC_RNG_BANK_SUPPORT) && defined(HAVE_HASHDRBG) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) if ((ret = rng_entropy_invalidate_test()) != 0) TEST_FAIL("RNGINVAL test failed!\n", ret); @@ -28021,7 +28021,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif /* !WC_RNG_BANK_STATIC */ static const char bank_arg[] = "hi"; byte outbuf1[16], outbuf2[16]; +#ifdef HAVE_HASHDRBG int i; +#endif #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) int svc_present = 0; #endif @@ -28251,6 +28253,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif +#ifdef HAVE_HASHDRBG ret = wc_rng_bank_reseed(NULL, 10, WC_RNG_BANK_FLAG_NONE); #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) @@ -28283,6 +28286,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_I(i), out); } } +#endif /* HAVE_HASHDRBG */ rng_bank_affinity_get_id_id = 0; /* WC_RNG_BANK_FLAG_CAN_WAIT needed to avoiding warning message that the @@ -28312,6 +28316,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_HASHDRBG ret = wc_rng_bank_seed(bank, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28396,6 +28401,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif /* HAVE_HASHDRBG */ + ret = wc_rng_bank_checkout(NULL, &rng_inst, -1, 10, WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | WC_RNG_BANK_FLAG_AFFINITY_LOCK); #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) @@ -28471,6 +28478,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#ifdef HAVE_HASHDRBG + ret = wc_rng_bank_seed(NULL, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28483,15 +28492,21 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif /* HAVE_HASHDRBG */ + ret = wc_rng_bank_default_clear(bank); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#ifdef HAVE_HASHDRBG + /* seedSz == 0 probe with no default bank set: NO_DEFAULT_FOUND_E. */ ret = wc_rng_bank_seed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif /* HAVE_HASHDRBG */ + #endif /* WC_RNG_BANK_DEFAULT_SUPPORT */ #ifdef WC_RNG_BANK_STATIC @@ -28531,6 +28546,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#ifdef HAVE_HASHDRBG + ret = wc_rng_bank_seed(bank2, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28584,6 +28601,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#endif /* HAVE_HASHDRBG */ + #if defined(WC_HAVE_RNG_BANKREF) && !defined(WC_NO_CONSTRUCTORS) ret = wc_rng_new_bankref(NULL, &rng2); #ifdef WC_RNG_BANK_DEFAULT_SUPPORT @@ -28863,7 +28882,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && defined(HAVE_HASHDRBG) if (svc_present) { wc_drbg_reseed_ctr_t ns_ctr; /* effective PR: the leased instance is freshly credited-reseeded @@ -28981,7 +29000,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #endif /* WC_RNG_HAVE_NEXT_SEED */ } -#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7,0,0) */ +#endif /* (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) && HAVE_HASHDRBG */ #ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT #define RBT_MAGIC ((WC_ATOMIC_UINT_ARG)0x746e6164) /* arbitrary nonzero */ @@ -29413,7 +29432,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) #endif /* HAVE_HASHDRBG && !CUSTOM_RAND_GENERATE_BLOCK && */ /* (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ -#if defined(WC_RNG_BANK_SUPPORT) && \ +#if defined(WC_RNG_BANK_SUPPORT) && defined(HAVE_HASHDRBG) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) /* Unit coverage for WC_RNG_LOCK_ENTROPY_INVALIDATED and the @@ -29876,7 +29895,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) #endif /* WC_RNG_HAVE_RBGC */ } #endif /* NEXT_SEED && RBGC && !RDSEED && !RDRAND */ -#endif /* WC_RNG_HAVE_LOCK && (!HAVE_FIPS || >= 7.0.0) */ +#endif /* WC_RNG_HAVE_LOCK && HAVE_HASHDRBG && (!HAVE_FIPS || >= 7.0.0) */ #if defined(WC_RNG_BANK_DEFAULT_SUPPORT) && defined(WC_RNG_HAVE_LOCK) && \ !defined(WC_NO_CONSTRUCTORS) diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index f0eaaefa38c..c44fff0513d 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -994,7 +994,9 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); WC_RNG_lock_arg_t extra_bits); WOLFSSL_API int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + #ifdef HAVE_HASHDRBG WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng); + #endif #endif /* WC_RNG_HAVE_LOCK */ #ifdef WC_RNG_HAVE_FREE_HOOK diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 0e1805c9998..dc10de5a3c2 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -433,6 +433,8 @@ WOLFSSL_API int wc_rng_bank_spawn_new( #endif /* WC_RNG_HAVE_RBGC */ +#ifdef HAVE_HASHDRBG + WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, word32 seedSz, int timeout_secs, @@ -462,6 +464,8 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, word32 flags); +#endif /* HAVE_HASHDRBG */ + #ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT /* Publish (or, with NULL, retract) the daemon's private root DRBG for the * state-invalidation handler. Caller (the daemon) owns the ordering: @@ -974,6 +978,8 @@ WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Now( #endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ +#ifdef HAVE_HASHDRBG + /* Portable invalidation-recovery helpers. With the in-boundary latch * (WC_RNG_HAVE_LOCK), invalidation and clear-on-credited-reseed are * module-enforced and these merely forward; with the bank-side latch, @@ -1089,6 +1095,8 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_rbgc( #endif /* !WC_RNG_HAVE_LOCK */ +#endif /* HAVE_HASHDRBG */ + #endif /* WC_RNG_BANK_SUPPORT */ #endif /* WOLF_CRYPT_RNG_BANK_H */ From 6c067bb867e54b201d453f180315b8a9cf06cd8a Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 16:59:23 -0500 Subject: [PATCH 038/102] linuxkm/include.am: add linuxkm/patches/7.3/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch; linuxkm/lkcapi_glue.c and linuxkm/lkcapi_sha_glue.c: disable state invalidation mechanism on old FIPS (depends on wc_RNG_register_free_hook()); add missing WC_RNG_HAVE_POOL gate; add missing warning suppressions for linux/acpi.h (needed on old kernels); wolfssl/wolfcrypt/random.h and wolfssl/wolfcrypt/settings.h: use WC_RNG_WANT_DEBUG_STATS as the feature toggle, so that WC_RNG_DEBUG_STATS is the availability sensor. --- linuxkm/include.am | 3 ++- linuxkm/lkcapi_glue.c | 10 ++++---- linuxkm/lkcapi_sha_glue.c | 44 +++++++++++++++++++++++++++++++++--- wolfssl/wolfcrypt/random.h | 4 ++++ wolfssl/wolfcrypt/rng_bank.h | 1 - wolfssl/wolfcrypt/settings.h | 4 ++-- 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/linuxkm/include.am b/linuxkm/include.am index e99954af615..39b28a5ce6a 100644 --- a/linuxkm/include.am +++ b/linuxkm/include.am @@ -35,4 +35,5 @@ EXTRA_DIST += m4/ax_linuxkm.m4 \ linuxkm/patches/6.1.73/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v1v73.patch \ linuxkm/patches/6.12/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v12.patch \ linuxkm/patches/6.15/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v15.patch \ - linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch + linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch \ + linuxkm/patches/7.3/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch diff --git a/linuxkm/lkcapi_glue.c b/linuxkm/lkcapi_glue.c index beb22911a59..490a335d07b 100644 --- a/linuxkm/lkcapi_glue.c +++ b/linuxkm/lkcapi_glue.c @@ -296,7 +296,7 @@ static int linuxkm_lkcapi_sysfs_install(void) { return ret; } -#ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG +#ifdef WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER ret = linuxkm_lkcapi_sysfs_install_node(&wc_linuxkm_rng_state_invalidate_attr, NULL); if (ret) { @@ -306,13 +306,16 @@ static int linuxkm_lkcapi_sysfs_install(void) { NULL); return ret; } +#endif #if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ defined(WC_RNG_DEBUG_STATS) ret = linuxkm_lkcapi_sysfs_install_node(&wc_linuxkm_rng_stats_attr, NULL); if (ret) { +#ifdef WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER (void)linuxkm_lkcapi_sysfs_deinstall_node(&wc_linuxkm_rng_state_invalidate_attr, NULL); +#endif (void)linuxkm_lkcapi_sysfs_deinstall_node(&deinstall_algs_attr, NULL); (void)linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, @@ -320,7 +323,6 @@ static int linuxkm_lkcapi_sysfs_install(void) { return ret; } #endif -#endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG */ installed_sysfs_LKCAPI_files = 1; } return 0; @@ -329,7 +331,6 @@ static int linuxkm_lkcapi_sysfs_install(void) { static int linuxkm_lkcapi_sysfs_deinstall(void) { if (installed_sysfs_LKCAPI_files) { int ret; -#ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG #if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ defined(WC_RNG_DEBUG_STATS) ret = linuxkm_lkcapi_sysfs_deinstall_node(&wc_linuxkm_rng_stats_attr, @@ -337,6 +338,7 @@ static int linuxkm_lkcapi_sysfs_deinstall(void) { if (ret) return ret; #endif +#ifdef WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER /* removed first (LIFO), and in any case before RNG teardown can * begin: the store handler walks the registry and reaches the * daemon root. */ @@ -344,7 +346,7 @@ static int linuxkm_lkcapi_sysfs_deinstall(void) { NULL); if (ret) return ret; -#endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG */ +#endif ret = linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, NULL); if (ret) return ret; diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index d145b6396af..8fc83af053e 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -51,8 +51,20 @@ #ifdef LINUXKM_LKCAPI_REGISTER _Pragma("GCC diagnostic push"); + _Pragma("GCC diagnostic ignored \"-Wunused-parameter\""); _Pragma("GCC diagnostic ignored \"-Wpointer-arith\""); + _Pragma("GCC diagnostic ignored \"-Wshadow\""); + _Pragma("GCC diagnostic ignored \"-Wnested-externs\""); + _Pragma("GCC diagnostic ignored \"-Wredundant-decls\""); + _Pragma("GCC diagnostic ignored \"-Wsign-compare\""); + _Pragma("GCC diagnostic ignored \"-Wpointer-sign\""); _Pragma("GCC diagnostic ignored \"-Wbad-function-cast\""); +#ifndef __clang__ + _Pragma("GCC diagnostic ignored \"-Wdiscarded-qualifiers\""); +#endif +#if defined(__GNUC__) && (__GNUC__ >= 17) + _Pragma("GCC diagnostic ignored \"-Wconstant-logical-operand\""); +#endif #include #include _Pragma("GCC diagnostic pop"); @@ -2157,12 +2169,22 @@ static int linuxkm_affinity_unlock(void *arg) { #endif /* !WOLFSSL_USE_SAVE_VECTOR_REGISTERS */ } +#define WC_LINUXKM_ENTROPY_DAEMON_MAGIC 0x6f77666c + +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + +#define WC_LINUXKM_HAVE_RNG_REGISTRY + /* Registry of every kernel-module RNG object needing state-invalidation * coverage: banks (default and tfm-private) and long-lived process-context * RBGC leaves from LKCAPI_INITRNG(). Atomic-born leaves are deliberately * excluded (see linuxkm_InitRng_DefaultRBGC()): the mutex is thereby never * taken from atomic context, so it can sleep, and the daemon's leaf pass - * may gather entropy under it. */ + * may gather entropy under it. + * + * Not usable on old FIPS because the mechanism fundamentally depends on + * wc_RNG_register_free_hook(). + */ struct linuxkm_rng_object { struct linuxkm_rng_object *prev, *next; int is_bank; @@ -2259,8 +2281,6 @@ static void wc_linuxkm_rng_registry_add_bank(struct wc_rng_bank *bank) wc_linuxkm_rng_registry_link(obj); } -#define WC_LINUXKM_ENTROPY_DAEMON_MAGIC 0x6f77666c - /* platform announcement (VM fork/clone, resume from hibernation) that RNG * state assumptions no longer hold: invalidate the daemon's local root * directly (it is unleased by design), invalidate every bank instance, @@ -2455,8 +2475,14 @@ static ssize_t wc_linuxkm_rng_state_invalidate_handler(struct kobject *kobj, static struct kobj_attribute wc_linuxkm_rng_state_invalidate_attr = __ATTR(rng_state_invalidate, 0220, NULL, wc_linuxkm_rng_state_invalidate_handler); +#define WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER + +#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7,0,0) */ + #ifndef WC_LINUXKM_NO_ENTROPY_DAEMON +#ifdef WC_LINUXKM_HAVE_RNG_REGISTRY + #if defined(WC_LINUXKM_VMGENID_POLL) || \ (defined(CONFIG_ACPI) && !IS_ENABLED(CONFIG_VMGENID)) /* Without CONFIG_VMGENID, we can only detect VM fork events by polling. @@ -2575,6 +2601,8 @@ static void wc_linuxkm_vmgenid_poll_teardown( } #endif /* CONFIG_ACPI && !CONFIG_VMGENID */ +#endif /* WC_LINUXKM_HAVE_RNG_REGISTRY */ + /* Entropy-banking daemon for the default rng bank: cycles the bank's * instances, keeping each DRBG's nextSeed aperture full so that * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED checkouts can perform credited @@ -3090,8 +3118,10 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) ret = -EINVAL; } +#ifdef WC_LINUXKM_HAVE_RNG_REGISTRY if (ret == 0) wc_linuxkm_rng_registry_add_bank(ctx); +#endif return ret; } @@ -3320,6 +3350,7 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { ret); ret = wc_InitRng(rng); } +#ifdef WC_LINUXKM_HAVE_RNG_REGISTRY if (ret == 0) { /* Long-lived process-context leaves join the invalidation registry; * atomic-born leaves are excluded by rule (and are transient by @@ -3327,6 +3358,7 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { if (can_sleep) wc_linuxkm_rng_registry_add_rng(rng); } +#endif return ret; } @@ -3365,7 +3397,9 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRef(WC_RNG* rng) { #define WC_LINUXKM_DRBG_SMALL_LIMIT 8 #endif +#ifdef WC_RNG_HAVE_POOL wc_static_assert(WC_LINUXKM_DRBG_SMALL_LIMIT <= WC_LINUXKM_RNG_POOL_SIZE); +#endif static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, const u8 *src, unsigned int slen, @@ -4482,9 +4516,11 @@ static int wc_linuxkm_drbg_startup(void) pr_info("%s registered as systemwide default stdrng.\n", wc_linuxkm_drbg.base.cra_driver_name); pr_info("libwolfssl: to unload module, first echo 1 > /sys/module/libwolfssl/deinstall_algs\n"); +#ifdef WC_LINUXKM_HAVE_RNG_REGISTRY /* stock-notifier invalidation coverage rides with the registered * DRBGs, patched and unpatched kernels alike. */ wc_linuxkm_rng_notifiers_install(); +#endif #ifdef LINUXKM_DRBG_GET_RANDOM_BYTES @@ -4579,10 +4615,12 @@ static int wc_linuxkm_drbg_cleanup(void) { */ int ret; +#ifdef WC_LINUXKM_HAVE_RNG_REGISTRY /* the notifier callbacks walk the RNG registry: uninstall them * before any of what they reference is dismantled. unregister * returns only after in-flight callbacks complete. */ wc_linuxkm_rng_notifiers_uninstall(); +#endif #ifdef LINUXKM_DRBG_GET_RANDOM_BYTES diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index c44fff0513d..5d7a16b1dc2 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -459,6 +459,10 @@ enum wc_RngHealthState { #define WC_RNG_FLAG_BANKREF (1U << 2) #define WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3) +#ifdef WC_RNG_WANT_DEBUG_STATS + #define WC_RNG_DEBUG_STATS +#endif + #ifdef WC_RNG_DEBUG_STATS #ifdef WORD64_AVAILABLE typedef word64 wc_rng_debug_counter_t; diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index dc10de5a3c2..a960b3dfebc 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -745,7 +745,6 @@ static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_clear_extra(struct wc return 0; } - #endif /* !WC_RNG_HAVE_LOCK */ #ifdef WC_RNG_DEBUG_STATS diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index a7a1e087a8a..2352f7bcbba 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -4662,8 +4662,8 @@ #endif #if defined(WC_VERBOSE_RNG) && defined(WOLFSSL_KERNEL_VERBOSE_DEBUG) && \ - !defined(WC_RNG_NO_DEBUG_STATS) && !defined(WC_RNG_DEBUG_STATS) - #define WC_RNG_DEBUG_STATS + !defined(WC_RNG_NO_DEBUG_STATS) && !defined(WC_RNG_WANT_DEBUG_STATS) + #define WC_RNG_WANT_DEBUG_STATS #endif #if WOLFSSL_GENERAL_ALIGNMENT < SIZEOF_LONG From 4f8c8cc412a7558e9033ead7c0fe7b84f373e5d6 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 20:13:44 -0500 Subject: [PATCH 039/102] linuxkm/lkcapi_sha_glue.c: add WC_RNG_STAT_FMT to accommodate long long wc_rng_debug_counter_t (e.g. arm32); wolfcrypt/src/rng_bank.c: in wc_rng_bank_debug_stats_snap(), initialize ret to 0 to avoid -Wmaybe-uninitialized; in wc_linuxkm_rng_state_invalidate_handler(), use my_kallsyms_lookup_name() to gain access to add_vmfork_randomness() on target kernels with IS_ENABLED(CONFIG_VMGENID) but not IS_MODULE(CONFIG_VMGENID) (weird export in linux/random.h). --- linuxkm/lkcapi_sha_glue.c | 58 ++++++++++++++++++++++++++++----------- wolfcrypt/src/rng_bank.c | 2 +- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 8fc83af053e..f7129cb0297 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2086,6 +2086,14 @@ struct wc_swallow_the_semicolon #error LINUXKM_LKCAPI_REGISTER_HASH_DRBG requires WC_RNG_BANK_DEFAULT_SUPPORT. #endif +#ifdef WC_RNG_DEBUG_STATS + #if defined(SIZEOF_LONG) && (SIZEOF_LONG == 8) + #define WC_RNG_STAT_FMT "%ld" + #else + #define WC_RNG_STAT_FMT "%lld" + #endif +#endif + static volatile int wc_linuxkm_rng_initing_default_bank_flag = 0; static struct wc_rng_bank *default_bank; @@ -2445,8 +2453,26 @@ static ssize_t wc_linuxkm_rng_state_invalidate_handler(struct kobject *kobj, #if IS_ENABLED(CONFIG_VMGENID) if (mode == 2) { u8 fake_id[16]; +#if !IS_MODULE(CONFIG_VMGENID) && defined(WC_LINUXKM_HAVE_MY_KALLSYMS_LOOKUP_NAME) + static typeof(add_vmfork_randomness) *my_add_vmfork_randomness = NULL; +#endif + get_random_bytes(fake_id, sizeof fake_id); /* any unique blob */ + +#if IS_MODULE(CONFIG_VMGENID) add_vmfork_randomness(fake_id, sizeof fake_id); /* full wire */ +#elif defined(WC_LINUXKM_HAVE_MY_KALLSYMS_LOOKUP_NAME) + /* add_vmfork_randomness() is exported only if vmgenid is a module -- + * work around it. */ + if (my_add_vmfork_randomness == NULL) + my_add_vmfork_randomness = my_kallsyms_lookup_name("add_vmfork_randomness"); + if (my_add_vmfork_randomness == NULL) + return -ENOSYS; + my_add_vmfork_randomness(fake_id, sizeof fake_id); /* full wire */ +#else + return -ENOSYS; +#endif + #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG pr_info("wc_linuxkm_rng_state_invalidate_handler: called add_vmfork_randomness.\n"); #endif @@ -2981,10 +3007,10 @@ static int wc_linuxkm_entropy_daemon(void *arg) #ifdef WC_RNG_DEBUG_STATS struct wc_rng_debug_stats_snapshot s; if (wc_rng_debug_stats_snap(&s, local_root) == 0) { - pr_info("RNG INFO: wc_entropyd root total_bytes_requested=%lu\n" - " total_bytes_produced=%lu total_requests=%lu\n" - " credited_reseeds=%lu uncredited_reseeds=%lu seed_failures=%lu\n" - " n_nextuncreditedseed_banked=%lu n_nextuncreditedseed_redeemed=%lu\n", + pr_info("RNG INFO: wc_entropyd root total_bytes_requested=" WC_RNG_STAT_FMT "\n" + " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" + " credited_reseeds=" WC_RNG_STAT_FMT " uncredited_reseeds=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" + " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n", s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, @@ -3140,10 +3166,10 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) if ((daemon_root != NULL) && (wc_rng_debug_stats_snap(&s, daemon_root) == 0)) { - pr_info("RNG INFO: wc_entropyd root total_bytes_requested=%lu\n" - " total_bytes_produced=%lu total_requests=%lu\n" - " credited_reseeds=%lu uncredited_reseeds=%lu seed_failures=%lu\n" - " n_nextuncreditedseed_banked=%lu n_nextuncreditedseed_redeemed=%lu\n", + pr_info("RNG INFO: wc_entropyd root total_bytes_requested=" WC_RNG_STAT_FMT "\n" + " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" + " credited_reseeds=" WC_RNG_STAT_FMT " uncredited_reseeds=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" + " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n", s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, @@ -3156,19 +3182,19 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) } if (wc_rng_bank_debug_stats_snap(&s, ctx) == 0) { - pr_info("RNG INFO: default bank size=%d total_bytes_requested=%lu\n" - " total_bytes_produced=%lu total_requests=%lu\n" - " credited_reseeds=%lu uncredited_reseeds=%lu seed_failures=%lu\n" - " locks_taken=%lu locks_released=%lu locks_refused=%lu\n" + pr_info("RNG INFO: default bank size=%d total_bytes_requested=" WC_RNG_STAT_FMT "\n" + " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" + " credited_reseeds=" WC_RNG_STAT_FMT " uncredited_reseeds=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" + " locks_taken=" WC_RNG_STAT_FMT " locks_released=" WC_RNG_STAT_FMT " locks_refused=" WC_RNG_STAT_FMT "\n" #ifdef WC_RNG_HAVE_RBGC - " RBGC_bytes_produced=%lu RBGC_reseeds=%lu\n" + " RBGC_bytes_produced=" WC_RNG_STAT_FMT " RBGC_reseeds=" WC_RNG_STAT_FMT "\n" #endif #ifdef WC_RNG_HAVE_POOL - " pool_bytes_produced=%lu pool_bytes_missed=%lu\n" + " pool_bytes_produced=" WC_RNG_STAT_FMT " pool_bytes_missed=" WC_RNG_STAT_FMT "\n" #endif #ifdef WC_RNG_HAVE_NEXT_SEED - " n_nextseed_primary_redeemed=%lu n_nextseed_RBGC_redeemed=%lu\n" - " n_nextseed_banked=%lu n_nextuncreditedseed_banked=%lu n_nextuncreditedseed_redeemed=%lu\n" + " n_nextseed_primary_redeemed=" WC_RNG_STAT_FMT " n_nextseed_RBGC_redeemed=" WC_RNG_STAT_FMT "\n" + " n_nextseed_banked=" WC_RNG_STAT_FMT " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n" #endif , ctx->n_rngs, diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index b165e35788c..d1f9d1f3274 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -2312,7 +2312,7 @@ WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot struct wc_rng_bank *bank) { int i; - int ret; + int ret = 0; if ((s == NULL) || (bank == NULL)) return BAD_FUNC_ARG; From 3183c734729ffc7a8f281e4ab6924405a4662876 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 9 Sep 2026 23:25:34 -0500 Subject: [PATCH 040/102] linuxkm/lkcapi_glue.c, linuxkm/lkcapi_sha_glue.c: convert sysfs nodes from struct kobj_attribute to struct module_attribute, matching the module_sysfs_ops dispatch on THIS_MODULE->mkobj.kobj, for compatibility with CONFIG_CFI_CLANG (kCFI) kernels. --- linuxkm/lkcapi_glue.c | 44 +++++++++++++++++++-------------------- linuxkm/lkcapi_sha_glue.c | 20 +++++++++--------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/linuxkm/lkcapi_glue.c b/linuxkm/lkcapi_glue.c index 490a335d07b..42d26a251ad 100644 --- a/linuxkm/lkcapi_glue.c +++ b/linuxkm/lkcapi_glue.c @@ -228,14 +228,14 @@ static int linuxkm_lkcapi_unregister(void); static int enabled_kernel_fips_enabled = 0; #endif -static ssize_t install_algs_handler(struct kobject *kobj, struct kobj_attribute *attr, +static ssize_t install_algs_handler(WC_MODULE_ATTR_CONST struct module_attribute *mattr, struct module_kobject *mk, const char *buf, size_t count) { int arg; int ret; - (void)kobj; - (void)attr; + (void)mattr; + (void)mk; if (kstrtoint(buf, 10, &arg) || arg != 1) return -EINVAL; @@ -249,14 +249,14 @@ static ssize_t install_algs_handler(struct kobject *kobj, struct kobj_attribute return count; } -static ssize_t deinstall_algs_handler(struct kobject *kobj, struct kobj_attribute *attr, +static ssize_t deinstall_algs_handler(WC_MODULE_ATTR_CONST struct module_attribute *mattr, struct module_kobject *mk, const char *buf, size_t count) { int arg; int ret; - (void)kobj; - (void)attr; + (void)mattr; + (void)mk; if (kstrtoint(buf, 10, &arg) || arg != 1) return -EINVAL; @@ -279,46 +279,46 @@ static ssize_t deinstall_algs_handler(struct kobject *kobj, struct kobj_attribut /* create control channels at /sys/module/libwolfssl/{install_algs,deinstall_algs} */ -static struct kobj_attribute install_algs_attr = __ATTR(install_algs, 0220, NULL, install_algs_handler); -static struct kobj_attribute deinstall_algs_attr = __ATTR(deinstall_algs, 0220, NULL, deinstall_algs_handler); +static struct module_attribute install_algs_attr = __ATTR(install_algs, 0220, NULL, install_algs_handler); +static struct module_attribute deinstall_algs_attr = __ATTR(deinstall_algs, 0220, NULL, deinstall_algs_handler); static int installed_sysfs_LKCAPI_files = 0; static int linuxkm_lkcapi_sysfs_install(void) { int ret; if (! installed_sysfs_LKCAPI_files) { - ret = linuxkm_lkcapi_sysfs_install_node(&install_algs_attr, NULL); + ret = linuxkm_sysfs_install_attr(&install_algs_attr.attr, NULL); if (ret) return ret; - ret = linuxkm_lkcapi_sysfs_install_node(&deinstall_algs_attr, NULL); + ret = linuxkm_sysfs_install_attr(&deinstall_algs_attr.attr, NULL); if (ret) { - (void)linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, NULL); + (void)linuxkm_sysfs_deinstall_attr(&install_algs_attr.attr, NULL); return ret; } #ifdef WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER - ret = linuxkm_lkcapi_sysfs_install_node(&wc_linuxkm_rng_state_invalidate_attr, + ret = linuxkm_sysfs_install_attr(&wc_linuxkm_rng_state_invalidate_attr.attr, NULL); if (ret) { - (void)linuxkm_lkcapi_sysfs_deinstall_node(&deinstall_algs_attr, + (void)linuxkm_sysfs_deinstall_attr(&deinstall_algs_attr.attr, NULL); - (void)linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, + (void)linuxkm_sysfs_deinstall_attr(&install_algs_attr.attr, NULL); return ret; } #endif #if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ defined(WC_RNG_DEBUG_STATS) - ret = linuxkm_lkcapi_sysfs_install_node(&wc_linuxkm_rng_stats_attr, + ret = linuxkm_sysfs_install_attr(&wc_linuxkm_rng_stats_attr.attr, NULL); if (ret) { #ifdef WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER - (void)linuxkm_lkcapi_sysfs_deinstall_node(&wc_linuxkm_rng_state_invalidate_attr, + (void)linuxkm_sysfs_deinstall_attr(&wc_linuxkm_rng_state_invalidate_attr.attr, NULL); #endif - (void)linuxkm_lkcapi_sysfs_deinstall_node(&deinstall_algs_attr, + (void)linuxkm_sysfs_deinstall_attr(&deinstall_algs_attr.attr, NULL); - (void)linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, + (void)linuxkm_sysfs_deinstall_attr(&install_algs_attr.attr, NULL); return ret; } @@ -333,7 +333,7 @@ static int linuxkm_lkcapi_sysfs_deinstall(void) { int ret; #if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \ defined(WC_RNG_DEBUG_STATS) - ret = linuxkm_lkcapi_sysfs_deinstall_node(&wc_linuxkm_rng_stats_attr, + ret = linuxkm_sysfs_deinstall_attr(&wc_linuxkm_rng_stats_attr.attr, NULL); if (ret) return ret; @@ -342,15 +342,15 @@ static int linuxkm_lkcapi_sysfs_deinstall(void) { /* removed first (LIFO), and in any case before RNG teardown can * begin: the store handler walks the registry and reaches the * daemon root. */ - ret = linuxkm_lkcapi_sysfs_deinstall_node(&wc_linuxkm_rng_state_invalidate_attr, + ret = linuxkm_sysfs_deinstall_attr(&wc_linuxkm_rng_state_invalidate_attr.attr, NULL); if (ret) return ret; #endif - ret = linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, NULL); + ret = linuxkm_sysfs_deinstall_attr(&install_algs_attr.attr, NULL); if (ret) return ret; - ret = linuxkm_lkcapi_sysfs_deinstall_node(&deinstall_algs_attr, NULL); + ret = linuxkm_sysfs_deinstall_attr(&deinstall_algs_attr.attr, NULL); if (ret) return ret; installed_sysfs_LKCAPI_files = 0; diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index f7129cb0297..314adcc32c7 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2430,15 +2430,15 @@ static void wc_linuxkm_rng_notifiers_uninstall(void) wc_linuxkm_rng_notifiers_installed = 0; } -static ssize_t wc_linuxkm_rng_state_invalidate_handler(struct kobject *kobj, - struct kobj_attribute *attr, +static ssize_t wc_linuxkm_rng_state_invalidate_handler(WC_MODULE_ATTR_CONST struct module_attribute *mattr, + struct module_kobject *mk, const char *buf, size_t count) { int mode = 0; int ret; - (void)kobj; - (void)attr; + (void)mattr; + (void)mk; if (kstrtoint(buf, 10, &mode) < 0) return -EINVAL; @@ -2498,7 +2498,7 @@ static ssize_t wc_linuxkm_rng_state_invalidate_handler(struct kobject *kobj, return -EINVAL; } -static struct kobj_attribute wc_linuxkm_rng_state_invalidate_attr = +static struct module_attribute wc_linuxkm_rng_state_invalidate_attr = __ATTR(rng_state_invalidate, 0220, NULL, wc_linuxkm_rng_state_invalidate_handler); #define WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER @@ -4307,14 +4307,14 @@ static int wc_get_random_bytes_user_kretprobe_installed = 0; /* control channel at /sys/module/libwolfssl/rng_stats: echo 1 to dump the * current RNG stats to the kernel log on demand (they otherwise appear * only at teardown). */ -static ssize_t wc_linuxkm_rng_stats_handler(struct kobject *kobj, - struct kobj_attribute *attr, +static ssize_t wc_linuxkm_rng_stats_handler(WC_MODULE_ATTR_CONST struct module_attribute *mattr, + struct module_kobject *mk, const char *buf, size_t count) { int arg; - (void)kobj; - (void)attr; + (void)mattr; + (void)mk; if (kstrtoint(buf, 10, &arg) || (arg != 1)) return -EINVAL; @@ -4323,7 +4323,7 @@ static ssize_t wc_linuxkm_rng_stats_handler(struct kobject *kobj, wc_linuxkm_rng_dump_stats(default_bank); return (ssize_t)count; } -static struct kobj_attribute wc_linuxkm_rng_stats_attr = +static struct module_attribute wc_linuxkm_rng_stats_attr = __ATTR(rng_stats, 0220, NULL, wc_linuxkm_rng_stats_handler); #endif /* WC_RNG_DEBUG_STATS */ From 4f1cc1e0173a7ee3980b01ce4446e5d6faeb333c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 14:09:54 -0500 Subject: [PATCH 041/102] wolfcrypt/src/random.c, wolfcrypt/test/test.c: in wc_RNG_DRBG_ReseedRBGC_local() and wc_RNG_DRBG_NextSeedGenerate_local(), tolerate reseed by an RBGC root provided its stratum is less than the child's stratum (no stratum downgrade allowed), unless defined(WC_RNG_NO_RBGC_RESEED). Uncredited reseeds by wc_RNG_DRBG_ReseedRBGC_local() are now permitted unconditionally. wolfssl/wolfcrypt/random.h: move wc_drbg_reseed_ctr_t definition up, and use it in DRBG*_internal. --- .wolfssl_known_macro_extras | 1 + wolfcrypt/src/random.c | 29 +++++++++++-- wolfcrypt/test/test.c | 87 ++++++++++++++++++++++++++++++++++++- wolfssl/wolfcrypt/random.h | 45 +++++-------------- 4 files changed, 125 insertions(+), 37 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 171bcceff34..997c5b93bc0 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -812,6 +812,7 @@ WC_RNG_NO_LOCK_FULL_MUTEX WC_RNG_NO_NEXT_SEED WC_RNG_NO_POOL WC_RNG_NO_RBGC +WC_RNG_NO_RBGC_RESEED WC_RSA_NONBLOCK_TIME WC_RSA_NO_FERMAT_CHECK WC_RTL8735B_NO_DERIVE_CACHE diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 87b3a2da33d..5ecf43ea7ad 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3596,6 +3596,14 @@ int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, * BOTH rng and root. On credited success, rng is (or remains) a chain RNG: * its current seed period is chain-backed, so RBGCStratum is set, and it is not * usable as a reseed root. + * + * By default, consistent with SP 800-90C 7.1.2.2, reseed by an RBGC root is + * allowed, provided its stratum is less than the child's stratum (no stratum + * downgrade allowed, no cycles possible); build-time option + * WC_RNG_NO_RBGC_RESEED restricts credited reseeds to primary-seeded roots. + * + * Uncredited reseeds by wc_RNG_DRBG_ReseedRBGC_local() are unconditionally + * permitted (these are just stirs). */ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz, int credited) @@ -3620,9 +3628,16 @@ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, const byte* n seed = rng->newSeed_buf; #endif - /* Reseed from root only, by policy. */ - if (root->RBGCStratum > 0) + if (credited && (root->RBGCStratum > 0) +#ifndef WC_RNG_NO_RBGC_RESEED + && (root->RBGCStratum >= rng->RBGCStratum) +#else + /* Credited reseed from root only, by policy. */ +#endif + ) + { return BAD_FUNC_ARG; + } if (rng->status != DRBG_OK) return RNG_FAILURE_E; @@ -3943,8 +3958,16 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, const b if (root) { #ifdef WC_RNG_HAVE_RBGC - if (root->RBGCStratum > 0) + if ((root->RBGCStratum > 0) + #ifndef WC_RNG_NO_RBGC_RESEED + && (root->RBGCStratum >= rng->RBGCStratum) + #else + /* Credited reseed from root only, by policy. */ + #endif + ) + { return BAD_FUNC_ARG; + } ret = rng_lock_required_check(root); if (ret != 0) return ret; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 1d107340d13..47c5396f63a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30087,6 +30087,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } + /* root(0) from leaf(1): refused -- no stratum downgrade. */ api_ret = wc_RNG_DRBG_ReseedRBGC(&root, &leaf, NULL, 0); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30166,12 +30167,96 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) api_ret = wc_RNG_DRBG_GetRBGCStratum(&extra); if (api_ret != 1) ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); - /* long-chained init is allowed, only chained reseed is forbidden. */ + /* long-chained init is allowed; chained reseeds are governed by the + * no-downgrade rule probed below. */ ret = wc_InitRngRBGC_New(&pleaf, &extra, WC_RNG_INIT_FLAGS_NONE); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if ((pleaf == NULL) || (wc_RNG_DRBG_GetRBGCStratum(pleaf) != 2)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* force leaf back to primary class (stratum 0) for the source-class + * probes below. */ + api_ret = wc_RNG_DRBG_ScheduleReseed(&leaf); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&leaf); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + + /* chained credited reseeds: permitted iff the source's stratum + * strictly improves on (is less than) the target's. */ + api_ret = wc_RNG_DRBG_ReseedRBGC(pleaf, &extra, NULL, 0); + if (api_ret != 0) /* 1 < 2: allowed */ + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(pleaf); + if (api_ret != 2) /* acquires extra+1 */ + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_RNG_DRBG_ReseedRBGC(&extra, pleaf, NULL, 0); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) /* 2 >= 1: refused */ + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &extra, NULL, 0); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) /* 1 >= 0: refused -- + * primary-born instances never downgrade + * by chained reseed. */ + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* primary-class (stratum-0) sources are always welcome, root or + * not. */ + api_ret = wc_RNG_DRBG_ReseedRBGC(&extra, &leaf, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&extra); + if (api_ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + + /* lateral (equal-stratum) chained reseeds are refused: the strict + * inequality is what makes cycles impossible. */ + api_ret = wc_RNG_DRBG_ReseedRBGC(&leaf, &root, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&leaf); + if (api_ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_RNG_DRBG_ReseedRBGC(&extra, &leaf, NULL, 0); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) /* 1 >= 1: refused */ + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + + /* uncredited chained reseeds are unrestricted (stirs claim + * nothing): any source stratum, target stratum untouched. */ + api_ret = wc_RNG_DRBG_ReseedRBGC_Uncredited(&extra, pleaf, NULL, 0); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(&extra); + if (api_ret != 1) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + +#ifdef WC_RNG_HAVE_NEXT_SEED + /* the banked twin obeys the same rule: refuse banking whose + * redemption would violate no-downgrade... */ + api_ret = wc_RNG_DRBG_NextSeedGenerate_RBGC(&extra, pleaf, 1); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) /* 2 >= 1: refused */ + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + /* ...and permit improving banked material, whose redemption + * carries the recorded stratum. */ + api_ret = wc_RNG_DRBG_NextSeedGenerate_RBGC(pleaf, &extra, + 0xffffffffU); + if (api_ret != 0) /* 1 < 2: allowed; oversize fill clamps */ + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetNextSeedRBGCStratum(pleaf); + if (api_ret != 2) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); + api_ret = wc_RNG_DRBG_NextSeedNow(pleaf); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetRBGCStratum(pleaf); + if (api_ret != 2) + ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); +#endif /* WC_RNG_HAVE_NEXT_SEED */ + wc_rng_free(pleaf); pleaf = NULL; } diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 5d7a16b1dc2..b9914b22290 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -66,7 +66,6 @@ #undef WC_RNG_HAVE_RBGC #endif -/* _FULL_MUTEX is opt-in, and depends on WC_RNG_HAVE_LOCK. */ #ifdef WC_RNG_NO_LOCK_FULL_MUTEX #undef WC_RNG_HAVE_LOCK_FULL_MUTEX #elif defined(WC_RNG_HAVE_LOCK_FULL_MUTEX) @@ -79,7 +78,7 @@ #define WC_RNG_HAVE_FREE_HOOK #endif #ifdef WC_RNG_HAVE_FREE_HOOK - struct WC_RNG; /* tag forward-declaration for the callback signature */ + struct WC_RNG; typedef int (*wc_RNG_free_hook_cb_t)(const struct WC_RNG *rng, void *arg); #endif @@ -367,8 +366,6 @@ struct OS_Seed { #define RNG_HEALTH_TEST_CHECK_SIZE_SHA512 (WC_SHA512_DIGEST_SIZE * 4) #endif -#ifndef NO_SHA256 - #ifdef WC_RNG_HAVE_NEXT_SEED /* Length of the banked next seed: identical byte accounting to other * source-fed (re)seeds in the module (gather SEED_SZ + SEED_BLOCK_SZ, apply @@ -377,12 +374,18 @@ struct OS_Seed { #define WC_DRBG_NEXT_UNCREDITED_SEED_LEN 64 #endif -struct DRBG_internal { +#ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED +#define WC_DRBG_RESEED_CTR_TYPE_DEFINED #ifdef WORD64_AVAILABLE - word64 reseedCtr; + typedef word64 wc_drbg_reseed_ctr_t; #else - word32 reseedCtr; + typedef word32 wc_drbg_reseed_ctr_t; #endif +#endif + +#ifndef NO_SHA256 +struct DRBG_internal { + wc_drbg_reseed_ctr_t reseedCtr; byte V[DRBG_SEED_LEN]; byte C[DRBG_SEED_LEN]; #ifdef WC_RNG_HAVE_NEXT_SEED @@ -408,11 +411,7 @@ struct DRBG_internal { #ifdef WOLFSSL_DRBG_SHA512 struct DRBG_SHA512_internal { - #ifdef WORD64_AVAILABLE - word64 reseedCtr; - #else - word32 reseedCtr; - #endif + wc_drbg_reseed_ctr_t reseedCtr; byte V[DRBG_SHA512_SEED_LEN]; byte C[DRBG_SHA512_SEED_LEN]; #ifdef WC_RNG_HAVE_NEXT_SEED @@ -504,8 +503,6 @@ struct WC_RNG { #endif #endif #ifdef WC_RNG_HAVE_FREE_HOOK - /* fired by wc_FreeRng() before state destruction (one-shot); - * see wc_RNG_register_free_hook(). */ wc_RNG_free_hook_cb_t free_hook; void *free_hook_arg; #endif @@ -524,9 +521,6 @@ struct WC_RNG { wc_rng_debug_counter_t _stats_n_nextseed_primary_redeemed; wc_rng_debug_counter_t _stats_n_nextseed_RBGC_redeemed; wc_rng_debug_counter_t _stats_n_nextuncreditedseed_redeemed; - /* production-side twins of the consumption counters above; plain - * increments, racy if there are competing seed bankers (usually - * there aren't). */ wc_rng_debug_counter_t _stats_n_nextseed_banked; wc_rng_debug_counter_t _stats_n_nextuncreditedseed_banked; #endif @@ -791,18 +785,6 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); word32 nonceSz); WOLFSSL_API int wc_RNG_TestSeed(const byte* seed, word32 seedSz); - /* Reseed-counter width tracks struct DRBG_internal above. The sentinel - * lets wolfssl/wolfcrypt/rng_bank.h supply the same typedef when building - * against a legacy FIPS random.h that predates it. */ - #ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED - #define WC_DRBG_RESEED_CTR_TYPE_DEFINED - #ifdef WORD64_AVAILABLE - typedef word64 wc_drbg_reseed_ctr_t; - #else - typedef word32 wc_drbg_reseed_ctr_t; - #endif - #endif - #ifdef WC_RNG_HAVE_RBGC WOLFSSL_API int wc_RNG_DRBG_GetRBGCStratum(const WC_RNG* rng); #ifdef WC_RNG_HAVE_NEXT_SEED @@ -930,7 +912,7 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); const byte* nonce, word32 nonceSz, word32 flags); #ifndef WC_NO_CONSTRUCTORS - /* flags are per-object (WC_RNG_INIT_FLAGS_*), deliberately NOT + /* Flags are per-object (WC_RNG_INIT_FLAGS_*), deliberately not * inherited from the parent: a child's lock policy is its own. */ WOLFSSL_API int wc_InitRngRBGC_New(WC_RNG** child, WC_RNG* parent, word32 flags); @@ -938,9 +920,6 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); const byte* nonce, word32 nonceSz, word32 flags); #endif /* !WC_NO_CONSTRUCTORS */ - /* Note, only a root RNG -- stratum 0, i.e. primary-seeded -- is permitted - * to generate reseed bytes (wolfCrypt policy; stricter than SP 800-90C - * 7.1.2.2, which also permits parent reseed). */ WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz); WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, From 6f0e2b17d0ac3e95d67509b92e6ad44a306f7451 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 14:36:50 -0500 Subject: [PATCH 042/102] wolfcrypt/src/rng_bank.c: fix overlong lines. --- wolfcrypt/src/rng_bank.c | 117 ++++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 37 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index d1f9d1f3274..aa6c3702685 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -218,7 +218,8 @@ WOLFSSL_API int wc_rng_bank_init( int devId) { - return wc_rng_bank_init_nonce(ctx, n_rngs, flags, timeout_secs, heap, devId, NULL, 0); + return wc_rng_bank_init_nonce(ctx, n_rngs, flags, timeout_secs, heap, devId, + NULL, 0); } WOLFSSL_API int wc_rng_bank_first_failover_inst_set( @@ -249,7 +250,8 @@ WOLFSSL_API int wc_rng_bank_new( if ((ctx == NULL) || (n_rngs <= 0)) return BAD_FUNC_ARG; - *ctx = (struct wc_rng_bank *)XMALLOC(sizeof(struct wc_rng_bank), heap, DYNAMIC_TYPE_RNG); + *ctx = (struct wc_rng_bank *)XMALLOC(sizeof(struct wc_rng_bank), heap, + DYNAMIC_TYPE_RNG); if (*ctx == NULL) return MEMORY_E; @@ -310,7 +312,8 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { if (ret != 0) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_fini() called with refcount %d.", new_refcount); + "WARNING: wc_rng_bank_fini() called with refcount %d.", + new_refcount); #endif if (new_refcount > 1) return BUSY_E; @@ -450,7 +453,10 @@ WOLFSSL_API int wc_rng_bank_default_set(struct wc_rng_bank *bank) { #endif return ret; } - if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, (void **)&cur_default_rng_bank, bank)) { + if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, + (void **)&cur_default_rng_bank, + bank)) + { bank->flags |= WC_RNG_BANK_FLAG_DEFAULT_BANK; return 0; } @@ -459,7 +465,8 @@ WOLFSSL_API int wc_rng_bank_default_set(struct wc_rng_bank *bank) { #ifdef WC_VERBOSE_RNG if (new_refcount <= 0) WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_default_set() cleanup popped refcount to %d.\n", new_refcount); + "BUG: wc_rng_bank_default_set() cleanup popped refcount to %d.\n", + new_refcount); #else (void)new_refcount; #endif @@ -483,7 +490,8 @@ WOLFSSL_API int wc_rng_bank_default_checkout(struct wc_rng_bank **bank) { else if (! (cur_default_rng_bank->flags & WC_RNG_BANK_FLAG_INITED)) return BAD_STATE_E; - if (cur_default_rng_bank->flags & WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING) { + if (cur_default_rng_bank->flags & WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING) + { /* read-only validity test: >= 2 means inited and still registered * as the default (wc_rng_bank_default_set()'s standing ref). */ if (wolfSSL_RefCur(cur_default_rng_bank->refcount) < 2) @@ -515,7 +523,8 @@ WOLFSSL_API int wc_rng_bank_default_checkin(struct wc_rng_bank **bank) { #ifdef WC_VERBOSE_RNG if (new_refcount <= 0) WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_default_checkin() popped refcount to %d.\n", new_refcount); + "BUG: wc_rng_bank_default_checkin() popped refcount to %d.\n", + new_refcount); #else (void)new_refcount; #endif @@ -533,7 +542,9 @@ WOLFSSL_API int wc_rng_bank_default_clear(struct wc_rng_bank *bank) { return BAD_FUNC_ARG; if (bank != default_rng_bank) return BAD_FUNC_ARG; - if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, (void **)&bank, NULL)) { + if (wolfSSL_Atomic_Ptr_CompareExchange((void * volatile *)&default_rng_bank, + (void **)&bank, NULL)) + { int ret; WC_ATOMIC_INT_ARG new_refcount; bank->flags &= ~WC_RNG_BANK_FLAG_DEFAULT_BANK; @@ -544,7 +555,8 @@ WOLFSSL_API int wc_rng_bank_default_clear(struct wc_rng_bank *bank) { */ if (new_refcount < 1) WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_default_clear() popped refcount to %d.\n", new_refcount); + "BUG: wc_rng_bank_default_clear() popped refcount to %d.\n", + new_refcount); if (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) WOLFSSL_DEBUG_PRINTF( "BUG: wc_rng_bank_default_clear() bank is already uninited.\n"); @@ -655,7 +667,8 @@ WOLFSSL_API int wc_rng_bank_checkout( } if (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY)) - flags |= bank->flags & (WC_RNG_BANK_FLAG_AFFINITY_LOCK | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST); + flags |= bank->flags & (WC_RNG_BANK_FLAG_AFFINITY_LOCK | + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST); if ((flags & WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST) && (bank->affinity_get_id_cb == NULL)) @@ -682,8 +695,8 @@ WOLFSSL_API int wc_rng_bank_checkout( { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_checkout() called with _AFFINITY_LOCK but " - "missing _lock_cb.\n"); + "BUG: wc_rng_bank_checkout() called with _AFFINITY_LOCK but" + " missing _lock_cb.\n"); #endif ret = BAD_FUNC_ARG; break; @@ -702,7 +715,8 @@ WOLFSSL_API int wc_rng_bank_checkout( if (flags & WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST) { preferred_inst_offset = -1; - ret = bank->affinity_get_id_cb(bank->cb_arg, &preferred_inst_offset); + ret = bank->affinity_get_id_cb(bank->cb_arg, + &preferred_inst_offset); if (ret != 0) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( @@ -793,7 +807,8 @@ WOLFSSL_API int wc_rng_bank_checkout( if ((recovered_claim != 0) || (((flags | bank->flags) & WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED) && (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY)) && - (! ((flags | bank->flags) & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE)))) + (! ((flags | bank->flags) & + WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE)))) { /* Consume a ready banked next seed, if any, BEFORE the * usability evaluation below, so that evaluation judges the @@ -886,7 +901,9 @@ WOLFSSL_API int wc_rng_bank_checkout( &cur_reseed_ctr) == 0) && (cur_reseed_ctr >= WC_RESEED_INTERVAL) #ifdef WC_RNG_HAVE_NEXT_SEED - && (wc_RNG_DRBG_NextSeedCurrent(WC_RNG_BANK_INST_TO_RNG(*rng_inst), &NextSeedCurrent) == 0) + && (wc_RNG_DRBG_NextSeedCurrent( + WC_RNG_BANK_INST_TO_RNG(*rng_inst), + &NextSeedCurrent) == 0) && (NextSeedCurrent != WC_DRBG_NEXT_SEED_READY) #endif ))) @@ -927,11 +944,12 @@ WOLFSSL_API int wc_rng_bank_checkout( */ #endif - if (((flags | bank->flags) & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE) && + if (((flags | bank->flags) & + WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE) && (! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY))) { - ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(*rng_inst), - NULL, 0); + ret = wc_RNG_DRBG_Reseed_Now( + WC_RNG_BANK_INST_TO_RNG(*rng_inst), NULL, 0); if (ret != 0) { (void)wc_rng_bank_inst_lock_put(*rng_inst); *rng_inst = NULL; @@ -1086,10 +1104,12 @@ WOLFSSL_API int wc_rng_bank_checkout( #ifdef WC_VERBOSE_RNG if (refdec_err != 0) WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_checkout() cleanup wolfSSL_RefDec2 returned %d.", refdec_err); + "WARNING: wc_rng_bank_checkout() cleanup wolfSSL_RefDec2 " + "returned %d.", refdec_err); else if (new_refcount <= 0) WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_checkout() bank refcount after wolfSSL_RefDec2() is %d.", new_refcount); + "WARNING: wc_rng_bank_checkout() bank refcount after " + "wolfSSL_RefDec2() is %d.", new_refcount); #else (void)new_refcount; (void)refdec_err; @@ -1111,7 +1131,9 @@ WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, #ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT -WOLFSSL_API int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank, WC_ATOMIC_UINT_ARG magic) { +WOLFSSL_API int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank, + WC_ATOMIC_UINT_ARG magic) +{ int ret; WC_ATOMIC_INT_ARG new_refcount; @@ -1123,8 +1145,9 @@ WOLFSSL_API int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank, WC_ATOMIC_U { WC_ATOMIC_UINT_ARG expected = WC_RNG_BANK_DAEMON_MAGIC_FREE; - if (! wolfSSL_Atomic_Uint_CompareExchange(&bank->daemon_magic, &expected, - magic)) + if (! wolfSSL_Atomic_Uint_CompareExchange(&bank->daemon_magic, + &expected, + magic)) return BUSY_E; } @@ -1142,9 +1165,15 @@ WOLFSSL_API int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank, WC_ATOMIC_U return 0; } -WOLFSSL_API int wc_rng_bank_daemon_register(struct wc_rng_bank *bank, void *daemon, WC_ATOMIC_UINT_ARG magic) { - if ((bank == NULL) || (daemon == NULL) || (magic == WC_RNG_BANK_DAEMON_MAGIC_FREE)) +WOLFSSL_API int wc_rng_bank_daemon_register(struct wc_rng_bank *bank, + void *daemon, + WC_ATOMIC_UINT_ARG magic) +{ + if ((bank == NULL) || (daemon == NULL) || + (magic == WC_RNG_BANK_DAEMON_MAGIC_FREE)) + { return BAD_FUNC_ARG; + } if (bank->daemon != NULL) return ALREADY_E; @@ -1157,9 +1186,15 @@ WOLFSSL_API int wc_rng_bank_daemon_register(struct wc_rng_bank *bank, void *daem return 0; } -WOLFSSL_API int wc_rng_bank_daemon_unregister(struct wc_rng_bank *bank, void **daemon, WC_ATOMIC_UINT_ARG magic) { - if ((bank == NULL) || (daemon == NULL) || (magic == WC_RNG_BANK_DAEMON_MAGIC_FREE)) +WOLFSSL_API int wc_rng_bank_daemon_unregister(struct wc_rng_bank *bank, + void **daemon, + WC_ATOMIC_UINT_ARG magic) +{ + if ((bank == NULL) || (daemon == NULL) || + (magic == WC_RNG_BANK_DAEMON_MAGIC_FREE)) + { return BAD_FUNC_ARG; + } if (WOLFSSL_ATOMIC_LOAD(bank->daemon_magic) != magic) return WRONG_TYPE_OBJECT_E; @@ -1173,7 +1208,9 @@ WOLFSSL_API int wc_rng_bank_daemon_unregister(struct wc_rng_bank *bank, void **d return 0; } -WOLFSSL_API int wc_rng_bank_daemon_release(struct wc_rng_bank *bank, WC_ATOMIC_UINT_ARG magic) { +WOLFSSL_API int wc_rng_bank_daemon_release(struct wc_rng_bank *bank, + WC_ATOMIC_UINT_ARG magic) +{ int ret; WC_ATOMIC_INT_ARG new_refcount; @@ -1257,8 +1294,10 @@ WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref( bank, rng_inst, 0, 0, WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_CAN_WAIT | - ((bank->affinity_get_id_cb != NULL) ? WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST : 0) | - ((bank->affinity_lock_cb != NULL) ? WC_RNG_BANK_FLAG_AFFINITY_LOCK : 0)); + ((bank->affinity_get_id_cb != NULL) ? + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST : 0) | + ((bank->affinity_lock_cb != NULL) ? + WC_RNG_BANK_FLAG_AFFINITY_LOCK : 0)); #ifdef WC_VERBOSE_RNG if ((ret == WC_NO_ERR_TRACE(BUSY_E)) && @@ -1378,8 +1417,8 @@ WOLFSSL_API int wc_rng_bank_checkin( if (ret != 0) { #ifdef WC_RNG_BANK_LOCK_DEBUG WOLFSSL_DEBUG_PRINTF( - "wc_rng_bank_checkin(): wc_rng_bank_inst_lock_put() returned code %d " - "(lock state 0x%x).\n", ret, lockval); + "wc_rng_bank_checkin(): wc_rng_bank_inst_lock_put() returned " + "code %d (lock state 0x%x).\n", ret, lockval); #endif if (ret == WC_NO_ERR_TRACE(OBJECT_NOT_LOCKED_E)) return ret; @@ -1401,10 +1440,12 @@ WOLFSSL_API int wc_rng_bank_checkin( #ifdef WC_VERBOSE_RNG if (refdec_err != 0) WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_checkin() wolfSSL_RefDec2 returned %d.", refdec_err); + "WARNING: wc_rng_bank_checkin() wolfSSL_RefDec2 returned %d.", + refdec_err); else if (new_refcount <= 0) WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_checkin() bank refcount after wolfSSL_RefDec2() is %d.", new_refcount); + "WARNING: wc_rng_bank_checkin() bank refcount after " + "wolfSSL_RefDec2() is %d.", new_refcount); #else (void)new_refcount; (void)refdec_err; @@ -1770,7 +1811,8 @@ static int rng_bank_spawn( word32 child_init_flags = WC_RNG_INIT_FLAGS_NONE; #ifdef WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE) - child_init_flags |= WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; + child_init_flags |= + WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; #endif if (leaf_stack != NULL) { ret = wc_InitRngNonceRBGC(leaf_stack, @@ -2308,8 +2350,9 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng) { #ifdef WC_RNG_DEBUG_STATS -WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, - struct wc_rng_bank *bank) +WOLFSSL_API int wc_rng_bank_debug_stats_snap( + struct wc_rng_debug_stats_snapshot *s, + struct wc_rng_bank *bank) { int i; int ret = 0; From f085128602ff63c5d613f506dbc8ead424b92a70 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 15:36:05 -0500 Subject: [PATCH 043/102] linuxkm/lkcapi_sha_glue.c: fix overlong lines. --- linuxkm/lkcapi_sha_glue.c | 252 +++++++++++++++++++++++++------------- 1 file changed, 169 insertions(+), 83 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 314adcc32c7..78c90ba2c96 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -26,7 +26,8 @@ #error lkcapi_sha_glue.c included in non-LINUXKM_LKCAPI_REGISTER project. #endif -#if defined(WC_LINUXKM_C_FALLBACK_IN_SHIMS) && defined(USE_INTEL_SPEEDUP) && !defined(WC_DEBUG_FORCE_KERNEL_SETTINGS) +#if defined(WC_LINUXKM_C_FALLBACK_IN_SHIMS) && defined(USE_INTEL_SPEEDUP) && \ + !defined(WC_DEBUG_FORCE_KERNEL_SETTINGS) #error SHA* WC_LINUXKM_C_FALLBACK_IN_SHIMS is not currently supported. #endif @@ -1391,7 +1392,8 @@ PRAGMA("GCC diagnostic ignored \"-Wnested-externs\""); #include -WC_MAYBE_UNUSED static int linuxkm_hmac_setkey_common(struct crypto_shash *tfm, int type, const byte* key, word32 length) +WC_MAYBE_UNUSED static int linuxkm_hmac_setkey_common(struct crypto_shash *tfm, + int type, const byte* key, word32 length) { struct km_sha_hmac_pstate *p_ctx = (struct km_sha_hmac_pstate *)crypto_shash_ctx(tfm); int ret; @@ -2430,9 +2432,10 @@ static void wc_linuxkm_rng_notifiers_uninstall(void) wc_linuxkm_rng_notifiers_installed = 0; } -static ssize_t wc_linuxkm_rng_state_invalidate_handler(WC_MODULE_ATTR_CONST struct module_attribute *mattr, - struct module_kobject *mk, - const char *buf, size_t count) +static ssize_t wc_linuxkm_rng_state_invalidate_handler( + WC_MODULE_ATTR_CONST struct module_attribute *mattr, + struct module_kobject *mk, + const char *buf, size_t count) { int mode = 0; int ret; @@ -2446,7 +2449,8 @@ static ssize_t wc_linuxkm_rng_state_invalidate_handler(WC_MODULE_ATTR_CONST stru /* direct local exercise */ ret = wc_linuxkm_rng_state_invalidate(); #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG - pr_info("wc_linuxkm_rng_state_invalidate_handler: called wc_linuxkm_rng_state_invalidate, retval %d.\n", ret); + pr_info("wc_linuxkm_rng_state_invalidate_handler: called " + "wc_linuxkm_rng_state_invalidate, retval %d.\n", ret); #endif return ret ? -EIO : (ssize_t)count; } @@ -2489,7 +2493,8 @@ static ssize_t wc_linuxkm_rng_state_invalidate_handler(WC_MODULE_ATTR_CONST stru ret = wc_linuxkm_rng_pm_notify(&wc_linuxkm_rng_pm_nb, PM_POST_HIBERNATION, NULL); #ifdef WOLFSSL_LINUXKM_VERBOSE_DEBUG - pr_info("wc_linuxkm_rng_state_invalidate_handler: called wc_linuxkm_rng_pm_notify(PM_POST_HIBERNATION), retval %d.\n", ret); + pr_info("wc_linuxkm_rng_state_invalidate_handler: called " + "wc_linuxkm_rng_pm_notify(PM_POST_HIBERNATION), retval %d.\n", ret); #endif return (ret == NOTIFY_OK) ? (ssize_t)count : -EIO; } @@ -2808,11 +2813,14 @@ static int wc_linuxkm_entropy_daemon(void *arg) ret = wc_rng_bank_recover_inst(bank, i, 0 /* timeout_secs */, 0 /* flags */); if (ret == 0) { - (void)wc_rng_bank_inst_flags_down(&bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED); + (void)wc_rng_bank_inst_flags_down( + &bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED); progress = 1; } else if (ret != WC_NO_ERR_TRACE(BUSY_E)) { - if (wc_rng_bank_inst_flags_up(&bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED)) { + if (wc_rng_bank_inst_flags_up( + &bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED)) + { pr_err_ratelimited( "ERROR: wc_entropyd: recovery of DRBG inst %d failed: %d\n", i, ret); @@ -2826,12 +2834,18 @@ static int wc_linuxkm_entropy_daemon(void *arg) /* congestion-triggered RBGC seed pass. */ for (i = 0; i < bank->n_rngs; i++) { wc_drbg_reseed_ctr_t this_reseedCtr; - ret = wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), &this_reseedCtr); + ret = wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), + &this_reseedCtr); if ((ret == 0) && (this_reseedCtr > WC_RESEED_INTERVAL / 2)) { WC_ATOMIC_INT_ARG this_NextSeedCurrent; - ret = wc_RNG_DRBG_NextSeedCurrent(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), &this_NextSeedCurrent); - if ((ret == 0) && (this_NextSeedCurrent != WC_DRBG_NEXT_SEED_READY) && (this_NextSeedCurrent != WC_DRBG_NEXT_SEED_CONSUMING)) { - ret = wc_rng_bank_next_seed_generate_rbgc(bank, i, WC_DRBG_NEXT_SEED_LEN, local_root); + ret = wc_RNG_DRBG_NextSeedCurrent( + WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), &this_NextSeedCurrent); + if ((ret == 0) && + (this_NextSeedCurrent != WC_DRBG_NEXT_SEED_READY) && + (this_NextSeedCurrent != WC_DRBG_NEXT_SEED_CONSUMING)) + { + ret = wc_rng_bank_next_seed_generate_rbgc( + bank, i, WC_DRBG_NEXT_SEED_LEN, local_root); congested_progress = 1; if (ret == 0) progress = 1; @@ -3008,9 +3022,13 @@ static int wc_linuxkm_entropy_daemon(void *arg) struct wc_rng_debug_stats_snapshot s; if (wc_rng_debug_stats_snap(&s, local_root) == 0) { pr_info("RNG INFO: wc_entropyd root total_bytes_requested=" WC_RNG_STAT_FMT "\n" - " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" - " credited_reseeds=" WC_RNG_STAT_FMT " uncredited_reseeds=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" - " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n", + " total_bytes_produced=" WC_RNG_STAT_FMT + " total_requests=" WC_RNG_STAT_FMT "\n" + " credited_reseeds=" WC_RNG_STAT_FMT + " uncredited_reseeds=" WC_RNG_STAT_FMT + " seed_failures=" WC_RNG_STAT_FMT "\n" + " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT + " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n", s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, @@ -3042,7 +3060,8 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) unsigned long uncredited_nonce = random_get_entropy(); if (wc_linuxkm_rng_initing_default_bank_flag && (default_bank != NULL)) { - pr_err("BUG: wc_linuxkm_rng_bank_init() called with wc_linuxkm_rng_initing_default_bank_flag asserted and default_bank != NULL.\n"); + pr_err("BUG: wc_linuxkm_rng_bank_init() called with " + "wc_linuxkm_rng_initing_default_bank_flag asserted and default_bank != NULL.\n"); return -EINVAL; } @@ -3090,7 +3109,8 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) ret = wc_rng_bank_default_set(ctx); if (ret != 0) { (void)wc_rng_bank_fini(ctx); - pr_err("ERROR: wc_rng_bank_default_set() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); + pr_err("ERROR: wc_rng_bank_default_set() in " + "wc_linuxkm_rng_bank_init() returned err %d\n", ret); WC_DUMP_BACKTRACE_NONDEBUG; } else { @@ -3099,25 +3119,34 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) /* Try to launch the entropy daemon. Failure is nonfatal: * the inline reseed and recovery paths serve daemonless * operation. */ - ret = wc_rng_bank_daemon_reserve(ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + ret = wc_rng_bank_daemon_reserve( + ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); if (ret != 0) { - pr_err("ERROR: wc_rng_bank_daemon_reserve() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); + pr_err("ERROR: wc_rng_bank_daemon_reserve() in " + "wc_linuxkm_rng_bank_init() returned err %d\n", + ret); ret = 0; } else { struct task_struct *t = kthread_run( wc_linuxkm_entropy_daemon, ctx, "wc_entropyd"); if (IS_ERR(t)) { - (void)wc_rng_bank_daemon_release(ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); - pr_err("WARNING: wc_entropyd spawn failed: %d (falling back to synchronous entropy strategy)\n", + (void)wc_rng_bank_daemon_release( + ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + pr_err("WARNING: wc_entropyd spawn failed: %d " + "(falling back to synchronous entropy strategy)\n", (int)PTR_ERR(t)); } else { - ret = wc_rng_bank_daemon_register(ctx, t, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + ret = wc_rng_bank_daemon_register( + ctx, t, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); if (ret != 0) { - pr_err("ERROR: wc_rng_bank_daemon_register() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); + pr_err("ERROR: wc_rng_bank_daemon_register() " + "in wc_linuxkm_rng_bank_init() returned err %d\n", + ret); (void)kthread_stop(t); - (void)wc_rng_bank_daemon_release(ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + (void)wc_rng_bank_daemon_release( + ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); ret = 0; } } @@ -3128,12 +3157,14 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) } else { (void)wc_rng_bank_fini(ctx); - pr_err("ERROR: wc_rng_bank_set_affinity_handlers() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); + pr_err("ERROR: wc_rng_bank_set_affinity_handlers() in " + "wc_linuxkm_rng_bank_init() returned err %d\n", ret); WC_DUMP_BACKTRACE_NONDEBUG; } } else { - pr_err("ERROR: wc_rng_bank_init() in wc_linuxkm_rng_bank_init() returned err %d\n", ret); + pr_err("ERROR: wc_rng_bank_init() in wc_linuxkm_rng_bank_init() " + "returned err %d\n", ret); if (ret == WC_NO_ERR_TRACE(MEMORY_E)) ret = -ENOMEM; else if (ret == WC_NO_ERR_TRACE(WC_TIMEOUT_E)) @@ -3167,9 +3198,13 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) (wc_rng_debug_stats_snap(&s, daemon_root) == 0)) { pr_info("RNG INFO: wc_entropyd root total_bytes_requested=" WC_RNG_STAT_FMT "\n" - " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" - " credited_reseeds=" WC_RNG_STAT_FMT " uncredited_reseeds=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" - " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n", + " total_bytes_produced=" WC_RNG_STAT_FMT + " total_requests=" WC_RNG_STAT_FMT "\n" + " credited_reseeds=" WC_RNG_STAT_FMT + " uncredited_reseeds=" WC_RNG_STAT_FMT + " seed_failures=" WC_RNG_STAT_FMT "\n" + " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT + " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n", s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, @@ -3183,18 +3218,28 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) if (wc_rng_bank_debug_stats_snap(&s, ctx) == 0) { pr_info("RNG INFO: default bank size=%d total_bytes_requested=" WC_RNG_STAT_FMT "\n" - " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" - " credited_reseeds=" WC_RNG_STAT_FMT " uncredited_reseeds=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" - " locks_taken=" WC_RNG_STAT_FMT " locks_released=" WC_RNG_STAT_FMT " locks_refused=" WC_RNG_STAT_FMT "\n" + " total_bytes_produced=" WC_RNG_STAT_FMT + " total_requests=" WC_RNG_STAT_FMT "\n" + " credited_reseeds=" WC_RNG_STAT_FMT + " uncredited_reseeds=" WC_RNG_STAT_FMT + " seed_failures=" WC_RNG_STAT_FMT "\n" + " locks_taken=" WC_RNG_STAT_FMT + " locks_released=" WC_RNG_STAT_FMT + " locks_refused=" WC_RNG_STAT_FMT "\n" #ifdef WC_RNG_HAVE_RBGC - " RBGC_bytes_produced=" WC_RNG_STAT_FMT " RBGC_reseeds=" WC_RNG_STAT_FMT "\n" + " RBGC_bytes_produced=" WC_RNG_STAT_FMT + " RBGC_reseeds=" WC_RNG_STAT_FMT "\n" #endif #ifdef WC_RNG_HAVE_POOL - " pool_bytes_produced=" WC_RNG_STAT_FMT " pool_bytes_missed=" WC_RNG_STAT_FMT "\n" + " pool_bytes_produced=" WC_RNG_STAT_FMT + " pool_bytes_missed=" WC_RNG_STAT_FMT "\n" #endif #ifdef WC_RNG_HAVE_NEXT_SEED - " n_nextseed_primary_redeemed=" WC_RNG_STAT_FMT " n_nextseed_RBGC_redeemed=" WC_RNG_STAT_FMT "\n" - " n_nextseed_banked=" WC_RNG_STAT_FMT " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n" + " n_nextseed_primary_redeemed=" WC_RNG_STAT_FMT + " n_nextseed_RBGC_redeemed=" WC_RNG_STAT_FMT "\n" + " n_nextseed_banked=" WC_RNG_STAT_FMT + " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT + " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n" #endif , ctx->n_rngs, @@ -3233,16 +3278,20 @@ static int wc_linuxkm_rng_bank_fini(struct wc_rng_bank *ctx) { #ifndef WC_LINUXKM_NO_ENTROPY_DAEMON if (WOLFSSL_ATOMIC_LOAD(ctx->daemon_magic) == WC_LINUXKM_ENTROPY_DAEMON_MAGIC) { struct task_struct *t; - ret = wc_rng_bank_daemon_unregister(ctx, (void **)&t, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + ret = wc_rng_bank_daemon_unregister( + ctx, (void **)&t, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); if ((ret == 0) || (ret == WC_NO_ERR_TRACE(ALREADY_E))) { if (ret == 0) (void)kthread_stop(t); - ret = wc_rng_bank_daemon_release(ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); + ret = wc_rng_bank_daemon_release( + ctx, WC_LINUXKM_ENTROPY_DAEMON_MAGIC); if (ret != 0) - pr_err("ERROR: wc_rng_bank_daemon_release() in wc_linuxkm_rng_bank_fini() returned code %d\n", ret); + pr_err("ERROR: wc_rng_bank_daemon_release() in " + "wc_linuxkm_rng_bank_fini() returned code %d\n", ret); } else - pr_err("ERROR: wc_rng_bank_daemon_unregister() in wc_linuxkm_rng_bank_fini() returned code %d\n", ret); + pr_err("ERROR: wc_rng_bank_daemon_unregister() in " + "wc_linuxkm_rng_bank_fini() returned code %d\n", ret); } #endif /* !WC_LINUXKM_NO_ENTROPY_DAEMON */ @@ -3254,7 +3303,8 @@ static int wc_linuxkm_rng_bank_fini(struct wc_rng_bank *ctx) { ret = wc_rng_bank_default_clear(ctx); if (ret != 0) - pr_err("ERROR: wc_rng_bank_default_clear() in wc_linuxkm_rng_bank_fini() returned code %d\n", ret); + pr_err("ERROR: wc_rng_bank_default_clear() in " + "wc_linuxkm_rng_bank_fini() returned code %d\n", ret); #ifdef WC_RNG_DEBUG_STATS wc_linuxkm_rng_dump_stats(ctx); @@ -3264,7 +3314,8 @@ static int wc_linuxkm_rng_bank_fini(struct wc_rng_bank *ctx) { ret = wc_rng_bank_fini(ctx); if (ret != 0) - pr_err("ERROR: wc_rng_bank_fini() in wc_linuxkm_rng_bank_fini() returned err %d\n", ret); + pr_err("ERROR: wc_rng_bank_fini() in wc_linuxkm_rng_bank_fini() " + "returned err %d\n", ret); return ret; } @@ -3319,13 +3370,15 @@ static struct wc_rng_bank_inst *linuxkm_get_drbg(struct wc_rng_bank *ctx) { err = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(ret), NULL, 0); if (err == 0) return ret; - pr_err_ratelimited("ERROR: inline recovery reseed in linuxkm_get_drbg() returned err %d.\n", err); + pr_err_ratelimited("ERROR: inline recovery reseed in " + "linuxkm_get_drbg() returned err %d.\n", err); (void)wc_rng_bank_inst_checkin(&ret); return NULL; } if (err != 0) { - pr_err("ERROR: wc_rng_bank_checkout() in linuxkm_get_drbg() returned err %d.\n", err); + pr_err("ERROR: wc_rng_bank_checkout() in linuxkm_get_drbg() returned " + "err %d.\n", err); WC_DUMP_BACKTRACE_NONDEBUG; return NULL; } @@ -3336,7 +3389,8 @@ static struct wc_rng_bank_inst *linuxkm_get_drbg(struct wc_rng_bank *ctx) { static void linuxkm_put_drbg(struct wc_rng_bank_inst **drbg) { int ret = wc_rng_bank_inst_checkin(drbg); if (ret != 0) { - pr_err("ERROR: wc_rng_bank_inst_checkin() in linuxkm_put_drbg() returned err %d.\n", ret); + pr_err("ERROR: wc_rng_bank_inst_checkin() in linuxkm_put_drbg() " + "returned err %d.\n", ret); WC_DUMP_BACKTRACE_NONDEBUG; } } @@ -3462,7 +3516,8 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, } else { if (wc_RNG_Pool_Collect(WC_RNG_BANK_INST_TO_RNG(drbg), - can_wait ? WC_LINUXKM_RNG_POOL_SIZE : WC_SHA256_BLOCK_SIZE) + can_wait ? WC_LINUXKM_RNG_POOL_SIZE : + WC_SHA256_BLOCK_SIZE) != 0) { break; @@ -3488,11 +3543,13 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, } } - if (pr || (wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(drbg), &cur_counter) == 0)) { - + if (pr || (wc_RNG_DRBG_GetReseedCtr( + WC_RNG_BANK_INST_TO_RNG(drbg), &cur_counter) == 0)) + { #ifdef WC_RNG_HAVE_NEXT_SEED WC_ATOMIC_INT_ARG NextSeedCurrent; - ret = wc_RNG_DRBG_NextSeedCurrent(WC_RNG_BANK_INST_TO_RNG(drbg), &NextSeedCurrent); + ret = wc_RNG_DRBG_NextSeedCurrent( + WC_RNG_BANK_INST_TO_RNG(drbg), &NextSeedCurrent); if ((! pr) && (ret == 0) && (NextSeedCurrent == WC_DRBG_NEXT_SEED_READY) && @@ -3534,9 +3591,9 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, * exact owner-only contract those accessors encode. */ /* both levels can be held (an affinity-locked check-out with - * WC_RNG_BANK_FLAG_NO_VECTOR_OPS, whether from the caller's flags or - * bank-wide bank->flags, also takes the vector-ops inhibit) -- release - * each held level separately, innermost first, mirroring + * WC_RNG_BANK_FLAG_NO_VECTOR_OPS, whether from the caller's flags + * or bank-wide bank->flags, also takes the vector-ops inhibit) -- + * release each held level separately, innermost first, mirroring * wc_rng_bank_inst_checkin(). */ { WC_RNG_lock_arg_t lock_state = 0; @@ -3550,9 +3607,9 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, /* Reseed synchronously. wc_RNG_DRBG_Reseed_Now() resets the reseed * counter iff the reseed succeeds; on failure it leaves the counter - * unmodified (the WC_RESEED_INTERVAL backstop still governs) and marks - * the instance out of service, exactly as an interval-forced reseed - * failure would. */ + * unmodified (the WC_RESEED_INTERVAL backstop still governs) and + * marks the instance out of service, exactly as an interval-forced + * reseed failure would. */ #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); #else @@ -3574,8 +3631,9 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS /* re-establish each level separately, in acquisition order (the * affinity save first, then the vector-ops inhibit), mirroring - * wc_rng_bank_checkout(); a failed re-acquisition clears only its own - * lock bit, so check-in unwinds exactly the levels actually held. */ + * wc_rng_bank_checkout(); a failed re-acquisition clears only its + * own lock bit, so check-in unwinds exactly the levels actually + * held. */ { WC_RNG_lock_arg_t lock_state = 0; (void)wc_rng_bank_inst_lock_read(drbg, &lock_state); @@ -3603,7 +3661,8 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, for (;;) { #define RNG_MAX_BLOCK_LEN_ROUNDED (RNG_MAX_BLOCK_LEN & ~0xfU) if (dlen > RNG_MAX_BLOCK_LEN_ROUNDED) { - ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(drbg), dst, RNG_MAX_BLOCK_LEN_ROUNDED); + ret = wc_RNG_GenerateBlock( + WC_RNG_BANK_INST_TO_RNG(drbg), dst, RNG_MAX_BLOCK_LEN_ROUNDED); if (ret == 0) { dlen -= RNG_MAX_BLOCK_LEN_ROUNDED; dst += RNG_MAX_BLOCK_LEN_ROUNDED; @@ -3641,9 +3700,9 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, #endif /* both levels can be held (an affinity-locked checkout with - * WC_RNG_BANK_FLAG_NO_VECTOR_OPS, whether from the caller's flags or - * bank-wide bank->flags, also takes the vector-ops inhibit) -- release - * each held level separately, innermost first, mirroring + * WC_RNG_BANK_FLAG_NO_VECTOR_OPS, whether from the caller's flags + * or bank-wide bank->flags, also takes the vector-ops inhibit) -- + * release each held level separately, innermost first, mirroring * wc_rng_bank_inst_checkin(). */ { WC_RNG_lock_arg_t lock_state = 0; @@ -3662,8 +3721,9 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, #ifdef WOLFSSL_USE_SAVE_VECTOR_REGISTERS /* re-establish each level separately, in acquisition order (the * affinity save first, then the vector-ops inhibit), mirroring - * wc_rng_bank_checkout(); a failed re-acquisition clears only its own - * lock bit, so check-in unwinds exactly the levels actually held. */ + * wc_rng_bank_checkout(); a failed re-acquisition clears only its + * own lock bit, so check-in unwinds exactly the levels actually + * held. */ { /* the latch (annotation bits included) is preserved * across wc_rng_bank_inst_reinit()'s _InitRng() by the @@ -3690,11 +3750,15 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, #endif if (ret == 0) { - pr_warn_ratelimited("WARNING: reinitialized DRBG #%d after RNG_FAILURE_E from wc_RNG_GenerateBlock().\n", wc_rng_bank_get_inst_id(drbg)); + pr_warn_ratelimited("WARNING: reinitialized DRBG #%d after " + "RNG_FAILURE_E from wc_RNG_GenerateBlock().\n", + wc_rng_bank_get_inst_id(drbg)); continue; } else { - pr_err_ratelimited("ERROR: reinitialization of DRBG #%d after RNG_FAILURE_E failed with ret %d.\n", wc_rng_bank_get_inst_id(drbg), ret); + pr_err_ratelimited("ERROR: reinitialization of DRBG #%d after " + "RNG_FAILURE_E failed with ret %d.\n", + wc_rng_bank_get_inst_id(drbg), ret); break; } } @@ -3703,7 +3767,8 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, } if (ret != 0) { - pr_err_ratelimited("ERROR: wc_linuxkm_drbg_generate() failing on wolfCrypt code %d.\n",ret); + pr_err_ratelimited("ERROR: wc_linuxkm_drbg_generate() failing on " + "wolfCrypt code %d.\n",ret); ret = -EIO; } @@ -3818,9 +3883,12 @@ static int wc__get_random_bytes(void *buf, size_t len) ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { #ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH - pr_emerg_ratelimited("ERROR: FIPS RNG source failed in wc__get_random_bytes(): wc_rng_bank_default_checkout() returned %d.\n", ret); + pr_emerg_ratelimited("ERROR: FIPS RNG source failed in " + "wc__get_random_bytes(): wc_rng_bank_default_checkout() " + "returned %d.\n", ret); #else - pr_err_ratelimited("ERROR: FIPS RNG source failed in wc__get_random_bytes(): wc_rng_bank_default_checkout() returned %d.\n", ret); + pr_err_ratelimited("ERROR: FIPS RNG source failed in wc__get_random_bytes(): " + "wc_rng_bank_default_checkout() returned %d.\n", ret); #endif /* kernel must-succeed call used from hard IRQ contexts etc. -- the * callback dispatch point will always fall through to native DRBG, but @@ -3833,9 +3901,13 @@ static int wc__get_random_bytes(void *buf, size_t len) (void)wc_rng_bank_default_checkin(¤t_default_wc_rng_bank); if (ret) { #ifdef WOLFSSL_LINUXKM_GET_RANDOM_NO_FALLTHROUGH - pr_emerg_ratelimited("ERROR: FIPS RNG source failed: wc__get_random_bytes(): wc_linuxkm_drbg_generate() failed with code %d.\n", ret); + pr_emerg_ratelimited("ERROR: FIPS RNG source failed: " + "wc__get_random_bytes(): wc_linuxkm_drbg_generate() " + "failed with code %d.\n", ret); #else - pr_err_ratelimited("ERROR: FIPS RNG source failed: wc__get_random_bytes(): wc_linuxkm_drbg_generate() failed with code %d.\n", ret); + pr_err_ratelimited("ERROR: FIPS RNG source failed: " + "wc__get_random_bytes(): wc_linuxkm_drbg_generate() " + "failed with code %d.\n", ret); #endif } return ret; @@ -3853,7 +3925,8 @@ static ssize_t wc_get_random_bytes_user(struct iov_iter *iter) { ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { - pr_emerg_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_get_random_bytes_user() returned %ld.\n", ret); + pr_emerg_ratelimited("ERROR: wc_rng_bank_default_checkout() in " + "wc_get_random_bytes_user() returned %ld.\n", ret); return -EIO; /* no fallthrough to native randomness */ } else { @@ -3878,7 +3951,8 @@ static ssize_t wc_get_random_bytes_user(struct iov_iter *iter) { ret = wc_linuxkm_drbg_generate(current_default_wc_rng_bank, NULL, 0, block, n, 0 /* pr */); if (unlikely(ret != 0)) { - pr_emerg_ratelimited("ERROR: wc_get_random_bytes_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); + pr_emerg_ratelimited("ERROR: wc_get_random_bytes_user() " + "wc_linuxkm_drbg_generate() returned %ld.\n", ret); break; } @@ -3929,7 +4003,8 @@ static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) { ret = wc_rng_bank_default_checkout(¤t_default_wc_rng_bank); if (ret) { - pr_emerg_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_extract_crng_user() returned %ld.\n", ret); + pr_emerg_ratelimited("ERROR: wc_rng_bank_default_checkout() in " + "wc_extract_crng_user() returned %ld.\n", ret); return -EIO; /* no fallthrough to native randomness */ } else { @@ -3954,7 +4029,8 @@ static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) { ret = wc_linuxkm_drbg_generate(current_default_wc_rng_bank, NULL, 0, block, n, 0 /* pr */); if (unlikely(ret != 0)) { - pr_emerg_ratelimited("ERROR: wc_extract_crng_user() wc_linuxkm_drbg_generate() returned %ld.\n", ret); + pr_emerg_ratelimited("ERROR: wc_extract_crng_user() " + "wc_linuxkm_drbg_generate() returned %ld.\n", ret); break; } @@ -4014,7 +4090,8 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { ret = wc_rng_bank_default_checkout(&ctx); if (ret) { #ifdef WC_VERBOSE_RNG - pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_mix_pool_bytes() returned %d.\n", ret); + pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in " + "wc_mix_pool_bytes() returned %d.\n", ret); #endif return -EFAULT; } @@ -4078,12 +4155,14 @@ static int wc_crng_reseed(void) { if (ret) { #ifdef WC_VERBOSE_RNG - pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in wc_crng_reseed() returned %d.\n", ret); + pr_err_ratelimited("ERROR: wc_rng_bank_default_checkout() in " + "wc_crng_reseed() returned %d.\n", ret); #endif return -EFAULT; } - ret = wc_rng_bank_reseed_range(ctx, 0, LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE, + ret = wc_rng_bank_reseed_range(ctx, 0, + LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE, WC_LINUXKM_INITRNG_TIMEOUT_SEC, can_sleep ? @@ -4144,13 +4223,19 @@ static int wc_get_random_bytes_by_kprobe(struct kprobe *p, struct pt_regs *regs) return 1; /* Handled. */ } #ifdef HAVE_FIPS - pr_emerg_ratelimited("ERROR: wc_get_random_bytes_by_kprobe falling through to native get_random_bytes with wc_linuxkm_drbg_default_instance_registered, ret=%d.\n", ret); + pr_emerg_ratelimited("ERROR: wc_get_random_bytes_by_kprobe falling " + "through to native get_random_bytes with " + "wc_linuxkm_drbg_default_instance_registered, ret=%d.\n", ret); #else - pr_warn_ratelimited("ERROR: wc_get_random_bytes_by_kprobe falling through to native get_random_bytes with wc_linuxkm_drbg_default_instance_registered, ret=%d.\n", ret); + pr_warn_ratelimited("ERROR: wc_get_random_bytes_by_kprobe falling " + "through to native get_random_bytes with " + "wc_linuxkm_drbg_default_instance_registered, ret=%d.\n", ret); #endif } - else - pr_warn("BUG: wc_get_random_bytes_by_kprobe called without wc_linuxkm_drbg_default_instance_registered.\n"); + else { + pr_warn("BUG: wc_get_random_bytes_by_kprobe called without " + "wc_linuxkm_drbg_default_instance_registered.\n"); + } /* Not handled. Fall through to native implementation, given * that the alternative is an immediate kernel panic. @@ -4474,7 +4559,8 @@ static int wc_linuxkm_drbg_startup(void) } if (crypto_default_rng->base.__crt_alg->cra_init != wc_linuxkm_drbg_init_tfm) { - pr_err("ERROR: %s NOT registered as systemwide default stdrng -- found \"%s\".\n", wc_linuxkm_drbg.base.cra_driver_name, crypto_tfm_alg_driver_name(&crypto_default_rng->base)); + pr_err("ERROR: %s NOT registered as systemwide default stdrng -- found \"%s\".\n", + wc_linuxkm_drbg.base.cra_driver_name, crypto_tfm_alg_driver_name(&crypto_default_rng->base)); crypto_put_default_rng(); return -EINVAL; } From 026d71eb6eb0302137abc5226b335b447f0639f9 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 15:36:43 -0500 Subject: [PATCH 044/102] wolfcrypt/src/random.c: fix overlong lines. --- wolfcrypt/src/random.c | 141 ++++++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 51 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 5ecf43ea7ad..620a3c1731d 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -706,8 +706,9 @@ static int Hash_df(DRBG_internal* drbg, byte* out, word32 outSz, byte type, } /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz, - const byte* additional, word32 additionalSz, int credited) +static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, + word32 seedSz, const byte* additional, + word32 additionalSz, int credited) { int ret; WC_DECLARE_VAR(newV, byte, DRBG_SEED_LEN, 0); @@ -973,24 +974,31 @@ int wc_RNG_DRBG_GetNextSeedRBGCStratum(const WC_RNG* rng) /* Race-free via the NextSeed aperture protocol. If called with rng locked, * and ->nextSeedLen == WC_DRBG_NEXT_SEED_READY, then competing producers - * and consumers are all excluded, unambiguously marking ->nextSeedRBGCStratum - * as strictly reliable and stable. nextSeedLen functions as the - * synchronizer -- the producer writes ->nextSeedRBGCStratum before publishing - * WC_DRBG_NEXT_SEED_READY to nextSeedLen with release semantics. + * and consumers are all excluded, unambiguously marking + * ->nextSeedRBGCStratum as strictly reliable and stable. nextSeedLen + * functions as the synchronizer -- the producer writes + * ->nextSeedRBGCStratum before publishing WC_DRBG_NEXT_SEED_READY to + * nextSeedLen with release semantics. */ #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - if (WOLFSSL_ATOMIC_LOAD(((const DRBG_internal*)rng->drbg)->nextSeedLen) != WC_DRBG_NEXT_SEED_READY) + if (WOLFSSL_ATOMIC_LOAD(((const DRBG_internal*)rng->drbg)->nextSeedLen) + != WC_DRBG_NEXT_SEED_READY) + { return NOT_READY_E; + } else return ((const DRBG_internal *)rng->drbg)->nextSeedRBGCStratum; } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { - if (WOLFSSL_ATOMIC_LOAD(((const DRBG_SHA512_internal*)rng->drbg512)->nextSeedLen) != WC_DRBG_NEXT_SEED_READY) + if (WOLFSSL_ATOMIC_LOAD(((const DRBG_SHA512_internal*)rng->drbg512)->nextSeedLen) + != WC_DRBG_NEXT_SEED_READY) + { return NOT_READY_E; + } else return ((const DRBG_SHA512_internal *)rng->drbg512)->nextSeedRBGCStratum; } @@ -1060,7 +1068,8 @@ int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) * the module's own seed source (wc_RNG_DRBG_Reseed_Now() or the * WC_RESEED_INTERVAL backstop) resets the reseed schedule. This is the * SP 800-90A additional-input concept, applied via the reseed derivation. */ -int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz, +int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, + const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz) { if (rng == NULL || seed == NULL) @@ -1497,14 +1506,14 @@ static int Hash_DRBG_Uninstantiate(DRBG_internal* drbg) /* ====================================================================== */ /* 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 */ +/* */ +/* 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 */ +/* 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 @@ -2304,7 +2313,8 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, ((flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) ? WC_RNG_LOCK_HELD : 0); XMEMSET(rng, 0, WC_OFFSETOF(WC_RNG, lock)); - XMEMSET((byte *)rng + WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock), 0, sizeof(*rng) - + XMEMSET((byte *)rng + WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock), 0, + sizeof(*rng) - (WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock))); #ifdef WC_RNG_DEBUG_STATS if (flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) @@ -2964,7 +2974,9 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) return UNEXPECTED_STATE_E; } -int wc_RNG_lock_get_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bits, WC_RNG_lock_arg_t want_extra_bits) +int wc_RNG_lock_get_conditional(WC_RNG* rng, + WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits) { WC_RNG_lock_arg_t cur_lock, expected; @@ -3008,7 +3020,8 @@ int wc_RNG_lock_get_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi } expected = (cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & - ~(WC_RNG_LOCK_HELD | WC_RNG_LOCK_ENTROPY_INVALIDATED))) | + ~(WC_RNG_LOCK_HELD | + WC_RNG_LOCK_ENTROPY_INVALIDATED))) | expected_extra_bits; if ((! (cur_lock & WC_RNG_LOCK_HELD)) && @@ -3058,7 +3071,8 @@ int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) #endif for (;;) { - new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD); + new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & + ~WC_RNG_LOCK_HELD); /* extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the * reserved section. */ extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | @@ -3080,7 +3094,9 @@ int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) return 0; } -int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bits, WC_RNG_lock_arg_t want_extra_bits) +int wc_RNG_lock_put_conditional(WC_RNG* rng, + WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits) { WC_RNG_lock_arg_t cur_lock, expected, new_lock; @@ -3096,16 +3112,19 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bi #endif for (;;) { - new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD); - /* want_extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the - * reserved section. */ + new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & + ~WC_RNG_LOCK_HELD); + /* want_extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is + * in the reserved section. */ want_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | WC_RNG_LOCK_REQUIRED; new_lock |= want_extra_bits; - expected = WC_RNG_LOCK_HELD | expected_extra_bits | (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); + expected = WC_RNG_LOCK_HELD | expected_extra_bits | + (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); - new_lock |= (expected_extra_bits & WC_RNG_LOCK_REQUIRED) | (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); + new_lock |= (expected_extra_bits & WC_RNG_LOCK_REQUIRED) | + (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); /* release preserves the sticky bit if the caller reports it held */ if (wolfSSL_Atomic_Uint_CompareExchange( @@ -3247,14 +3266,18 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { #ifdef WC_RNG_HAVE_NEXT_SEED #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); - WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); + WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { - WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); - WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); + WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); } #endif #endif /* WC_RNG_HAVE_NEXT_SEED */ @@ -3541,7 +3564,8 @@ static int SpawnRngRBGC(WC_RNG* new_child_stack, WC_RNG** new_child_heap, return BAD_FUNC_ARG; if (new_child_heap != NULL) { - *new_child_heap = (WC_RNG*)XMALLOC(sizeof(WC_RNG), parent->heap, DYNAMIC_TYPE_RNG); + *new_child_heap = (WC_RNG*)XMALLOC(sizeof(WC_RNG), parent->heap, + DYNAMIC_TYPE_RNG); if (*new_child_heap == NULL) return MEMORY_E; child = *new_child_heap; @@ -3605,8 +3629,9 @@ int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, * Uncredited reseeds by wc_RNG_DRBG_ReseedRBGC_local() are unconditionally * permitted (these are just stirs). */ -static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, const byte* nonce, - word32 nonceSz, int credited) +static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, + const byte* nonce, word32 nonceSz, + int credited) { #ifdef WOLFSSL_SMALL_STACK_CACHE byte *seed; @@ -3658,7 +3683,8 @@ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, const byte* n } } else { - ret = wc_RNG_DRBG_Reseed_Nonce_Uncredited(rng, seed, SEED_SZ, nonce, nonceSz); + ret = wc_RNG_DRBG_Reseed_Nonce_Uncredited(rng, seed, SEED_SZ, nonce, + nonceSz); } } ForceZero(seed, SEED_SZ); @@ -3672,8 +3698,8 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, return wc_RNG_DRBG_ReseedRBGC_local(rng, root, nonce, nonceSz, 1); } -int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, WC_RNG* root, const byte* nonce, - word32 nonceSz) +int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, WC_RNG* root, + const byte* nonce, word32 nonceSz) { return wc_RNG_DRBG_ReseedRBGC_local(rng, root, nonce, nonceSz, 0); } @@ -3872,7 +3898,9 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) /* Locate the aperture members for rng's live DRBG. Returns nonzero when no * DRBG is instantiated (RDRAND et al.). */ -static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, wolfSSL_Atomic_Int** len, int **nextSeedRBGCStratum) +static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, + wolfSSL_Atomic_Int** len, + int **nextSeedRBGCStratum) { #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { @@ -3881,7 +3909,8 @@ static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, *len = &((DRBG_internal*)rng->drbg)->nextSeedLen; if (nextSeedRBGCStratum) { #ifdef WC_RNG_HAVE_RBGC - *nextSeedRBGCStratum = &((DRBG_internal*)rng->drbg)->nextSeedRBGCStratum; + *nextSeedRBGCStratum = + &((DRBG_internal*)rng->drbg)->nextSeedRBGCStratum; #else *nextSeedRBGCStratum = NULL; #endif @@ -3892,11 +3921,13 @@ static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { *seed = ((DRBG_SHA512_internal*)rng->drbg512)->nextSeed; - *nextSeedSz = (word32)sizeof(((DRBG_SHA512_internal*)rng->drbg512)->nextSeed); + *nextSeedSz = + (word32)sizeof(((DRBG_SHA512_internal*)rng->drbg512)->nextSeed); *len = &((DRBG_SHA512_internal*)rng->drbg512)->nextSeedLen; if (nextSeedRBGCStratum) { #ifdef WC_RNG_HAVE_RBGC - *nextSeedRBGCStratum = &((DRBG_SHA512_internal*)rng->drbg512)->nextSeedRBGCStratum; + *nextSeedRBGCStratum = + &((DRBG_SHA512_internal*)rng->drbg512)->nextSeedRBGCStratum; #else *nextSeedRBGCStratum = NULL; #endif @@ -3907,12 +3938,15 @@ static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, return MISSING_RNG_E; } -static WC_INLINE int NextUncreditedSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, wolfSSL_Atomic_Int** len) +static WC_INLINE int NextUncreditedSeedPtrs(WC_RNG* rng, byte** seed, + word32 *nextSeedSz, + wolfSSL_Atomic_Int** len) { #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { *seed = ((DRBG_internal*)rng->drbg)->nextUncreditedSeed; - *nextSeedSz = (word32)sizeof(((DRBG_internal*)rng->drbg)->nextUncreditedSeed); + *nextSeedSz = + (word32)sizeof(((DRBG_internal*)rng->drbg)->nextUncreditedSeed); *len = &((DRBG_internal*)rng->drbg)->nextUncreditedSeedLen; return 0; } @@ -3920,7 +3954,8 @@ static WC_INLINE int NextUncreditedSeedPtrs(WC_RNG* rng, byte** seed, word32 *ne #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { *seed = ((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeed; - *nextSeedSz = (word32)sizeof(((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeed); + *nextSeedSz = + (word32)sizeof(((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeed); *len = &((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeedLen; return 0; } @@ -3937,7 +3972,8 @@ static WC_INLINE int NextUncreditedSeedPtrs(WC_RNG* rng, byte** seed, word32 *ne * published; a failed test consumes the material (use-once) and returns the * test's error, leaving an empty bank for the next cycle. A gather failure * leaves the partial bank intact for retry. */ -static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, const byte *nonce, word32 n) +static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, + const byte *nonce, word32 n) { byte* seed; wolfSSL_Atomic_Int* lenp; @@ -3979,7 +4015,8 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, const b if (nonce) ret = NextUncreditedSeedPtrs(rng, &seed, &nextSeedSz, &lenp); else - ret = NextSeedPtrs(rng, &seed, &nextSeedSz, &lenp, &nextSeedRBGCStratum_p); + ret = NextSeedPtrs(rng, &seed, &nextSeedSz, &lenp, + &nextSeedRBGCStratum_p); if (ret != 0) { /* No DRBG instantiated -- nothing to bank (RDRAND et al.). */ return ret; @@ -4043,10 +4080,10 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, const b else #endif /* WC_RNG_HAVE_RBGC */ { - /* wc_GenerateSeed() must be called completely independent of rng, aside - * from the memory aperture itself. For safety, we pass a dummy - * OS_Seed, which will be ignored by the wc_GenerateSeed() typically - * used in conjunction with WC_RNG_HAVE_NEXT_SEED. + /* wc_GenerateSeed() must be called completely independent of rng, + * aside from the memory aperture itself. For safety, we pass a + * dummy OS_Seed, which will be ignored by the wc_GenerateSeed() + * typically used in conjunction with WC_RNG_HAVE_NEXT_SEED. */ struct OS_Seed os; @@ -4468,14 +4505,16 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) int stir_ready = 0; #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL) && - (WOLFSSL_ATOMIC_LOAD(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen) == WC_DRBG_NEXT_SEED_READY)) + (WOLFSSL_ATOMIC_LOAD(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen) + == WC_DRBG_NEXT_SEED_READY)) { stir_ready = 1; } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL) && - (WOLFSSL_ATOMIC_LOAD(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen) == WC_DRBG_NEXT_SEED_READY)) + (WOLFSSL_ATOMIC_LOAD(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen) + == WC_DRBG_NEXT_SEED_READY)) { stir_ready = 1; } From ba5f45ffb377653c97abe8d8cb25e96f4ac8ef3d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 15:46:25 -0500 Subject: [PATCH 045/102] linuxkm/lkcapi_sha_glue.c, wolfcrypt/src/rng_bank.c, wolfcrypt/test/test.c: in linuxkm_put_drbg((), properly ignore NEEDS_RECOVERY_E from wc_rng_bank_inst_checkin(); in wc_rng_bank_recover_inst(), properly handle WC_RNG_LOCK_ENTROPY_INVALIDATED; in rng_entropy_invalidate_test(), test recovery after invalidation. --- linuxkm/lkcapi_sha_glue.c | 8 +++++++- wolfcrypt/src/rng_bank.c | 16 ++++++++++++++++ wolfcrypt/test/test.c | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 78c90ba2c96..0b540a84d53 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3388,7 +3388,13 @@ static struct wc_rng_bank_inst *linuxkm_get_drbg(struct wc_rng_bank *ctx) { static void linuxkm_put_drbg(struct wc_rng_bank_inst **drbg) { int ret = wc_rng_bank_inst_checkin(drbg); - if (ret != 0) { + if (ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { + /* informational: checked in successfully; the instance is + * entropy-invalidated (e.g. a state-invalidation event landed + * mid-lease) and recovers via the checkout admissions or the + * patrol. */ + } + else if (ret != 0) { pr_err("ERROR: wc_rng_bank_inst_checkin() in linuxkm_put_drbg() " "returned err %d.\n", ret); WC_DUMP_BACKTRACE_NONDEBUG; diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index aa6c3702685..7fcc198a374 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -1755,7 +1755,23 @@ WOLFSSL_API int wc_rng_bank_recover_inst( * operation gate is retryable on a later patrol turn. */ ret = wc_rng_bank_inst_reinit(bank, rng_inst, timeout_secs, flags); } +#if defined(WC_RNG_HAVE_LOCK) && defined(HAVE_HASHDRBG) + else { + WC_RNG_lock_arg_t lock_state = 0; + if ((wc_rng_bank_inst_lock_read(rng_inst, &lock_state) == 0) && + (lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + /* In service but quarantined: one credited reseed clears the + * quarantine -- lighter than reinit, preserving instance + * identity. */ + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(rng_inst), + NULL, 0); + } + /* else: healthy -- a stale lockless status observation; no-op. */ + } +#else /* else: healthy -- a stale lockless status observation; no-op. */ +#endif checkin_ret = wc_rng_bank_checkin(bank, &rng_inst); if ((checkin_ret != 0) && (ret == 0)) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 47c5396f63a..6f9c1027cfb 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -29952,6 +29952,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) api_ret = wc_rng_bank_inst_checkin(&minst); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + /* patrol recovery: instance 1 is still quarantined; a single + * wc_rng_bank_recover_inst() clears it, after which an ordinary + * checkout succeeds. */ + api_ret = wc_rng_bank_recover_inst(mb, 1, 0, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_checkout(mb, &minst, 1, 0, + WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_inst_checkin(&minst); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_rng_bank_free(&mb); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); From 4700ddbbf26cf82a6e526a2715024565ec1a9d37 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 15:46:52 -0500 Subject: [PATCH 046/102] add documentation for RNG expansion --- doc/dox_comments/header_files/random.h | 1014 ++++++++++++++++++++++ doc/dox_comments/header_files/rng_bank.h | 938 ++++++++++++++++++++ wolfssl/wolfcrypt/rng_bank.h | 170 +--- 3 files changed, 1976 insertions(+), 146 deletions(-) create mode 100644 doc/dox_comments/header_files/rng_bank.h diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index b5cac51e509..eb665fb7046 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -477,6 +477,8 @@ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz); \return 0 If valid \return BAD_FUNC_ARG If seed is NULL \return ENTROPY_RT_E || ENTROPY_APT_E Validation failed + \return ENTROPY_APT_E The adaptive proportion test failed. + \return MEMORY_E Allocation failed. \param seed Seed to test \param seedSz Seed size @@ -761,3 +763,1015 @@ int wc_Sha512Drbg_Enable(void); \sa wc_Sha512Drbg_Enable */ int wc_Sha512Drbg_IsDisabled(void); + +/*! + \ingroup Random + + \brief Initialize a WC_RNG with instantiation-time security attributes. + Identical to wc_InitRng_ex(), with a flags argument fixing attributes at + birth: WC_RNG_INIT_FLAGS_LOCK_REQUIRED latches the sticky lock-required + policy bit, so there is no reachable state in which the instance serves + without its lock policy; WC_RNG_INIT_FLAGS_LOCK_INITIALLY constructs into + a held lease, to be released with wc_RNG_lock_put(); + WC_RNG_INIT_FLAGS_USE_FULL_MUTEX layers a blocking wolfSSL_Mutex + outermost around the lock latch, for user-mode sharing of one instance + among threads (requires WC_RNG_HAVE_LOCK_FULL_MUTEX). + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + \return NOT_COMPILED_IN A requested flag is not compiled in. + + \param rng The RNG object to initialize. + \param heap Heap hint for dynamic allocation. + \param devId Device id, or INVALID_DEVID. + \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes. + + _Example_ + \code + WC_RNG rng; + if (wc_InitRng_ex2(&rng, NULL, INVALID_DEVID, + WC_RNG_INIT_FLAGS_LOCK_REQUIRED | + WC_RNG_INIT_FLAGS_LOCK_INITIALLY) != 0) { + // error handling + } + // caller holds the lease from birth + \endcode + + \sa wc_InitRng_ex + \sa wc_InitRngNonce_ex2 + \sa wc_RNG_lock_get + \sa wc_RNG_lock_put +*/ +int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags); + +/*! + \ingroup Random + + \brief Initialize a WC_RNG with a caller-supplied nonce and + instantiation-time security attributes. The nonce semantics are those of + wc_InitRngNonce_ex(); the flags semantics are those of wc_InitRng_ex2(). + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + \return NOT_COMPILED_IN A requested flag is not compiled in. + + \param rng The RNG object to initialize. + \param nonce Optional nonce used as additional instantiation input. + \param nonceSz Length of nonce in bytes. + \param heap Heap hint for dynamic allocation. + \param devId Device id, or INVALID_DEVID. + \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes. + + \sa wc_InitRng_ex2 + \sa wc_InitRngNonce_ex +*/ +int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz, + void* heap, int devId, word32 flags); + +/*! + \ingroup Random + + \brief Read-only accessor for the RNG health status. Returns the + instance's enum wc_RngHealthState value (WC_DRBG_NOT_INIT, WC_DRBG_OK, + WC_DRBG_FAILED, WC_DRBG_CONT_FAILED). + + \return WC_DRBG_OK The instance is in service. + \return BAD_FUNC_ARG rng is null. + + \param rng The RNG object to interrogate. + + _Example_ + \code + if (wc_RNG_GetStatus(&rng) != WC_DRBG_OK) { + // instance is not serviceable + } + \endcode + + \sa wc_RNG_DRBG_Present + \sa wc_RNG_DRBG_GetReseedCtr +*/ +int wc_RNG_GetStatus(const WC_RNG* rng); + +/*! + \ingroup Random + + \brief Returns 1 if rng has an instantiated DRBG, else 0. An in-service + WC_RNG can lack one: instantiation bypasses the DRBG when the CPU has + RDRAND (HAVE_INTEL_RDRAND). DRBG-specific services (commanded reseed, + banked next seeds, RBG chains) are unavailable on such instances. + + \return 1 rng has a live DRBG. + \return 0 rng is null or has no DRBG. + + \param rng The RNG object to interrogate. + + \sa wc_RNG_GetStatus + \sa wc_RNG_DRBG_ScheduleReseed +*/ +int wc_RNG_DRBG_Present(const WC_RNG* rng); + +/*! + \ingroup Random + + \brief Report the DRBG's current reseed counter -- the number of generate + operations since the last credited (re)seed, starting at 1. + + \return 0 Success + \return BAD_FUNC_ARG rng or reseedCtr is null. + \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.). + + \param rng The RNG object to interrogate. + \param reseedCtr Receives the counter. + + \sa wc_RNG_DRBG_Present + \sa wc_RNG_DRBG_ScheduleReseed +*/ +int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr); + +/*! + \ingroup Random + + \brief Mark rng due for reseed: the next generate operation reseeds from + the module's built-in or registered seed source before producing output. + This can only shorten the current seed's remaining lifetime, never extend + it. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.) -- a + commanded reseed that cannot happen is not a success. + + \param rng The RNG object to schedule. + + \sa wc_RNG_DRBG_Reseed_Now + \sa wc_RNG_DRBG_GetReseedCtr +*/ +int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); + +/*! + \ingroup Random + + \brief Immediately reseed rng from the module's built-in or registered + seed source, with an optional nonce as additional input. The credited + reseed resets the reseed counter. + + \return 0 Success + \return BAD_FUNC_ARG rng is null, or nonce is null with nonceSz nonzero. + \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.). + \return DRBG_CONT_FIPS_E The continuous test failed; the DRBG is out of + service. + \return RNG_FAILURE_E The DRBG is out of service or reseeding failed. + + \param rng The RNG object to reseed. + \param nonce Optional additional input. + \param nonceSz Length of nonce in bytes. + + \sa wc_RNG_DRBG_ScheduleReseed + \sa wc_RNG_DRBG_Reseed_Nonce +*/ +int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz); + +/*! + \ingroup Random + + \brief Reseed rng's DRBG with caller-supplied seed material and an + optional nonce as additional input. The material is credited as entropy: + the reseed counter resets. + + \return 0 Success + \return BAD_FUNC_ARG rng or seed is null. + \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.). + + \param rng The RNG object to reseed. + \param seed Seed material. + \param seedSz Length of seed in bytes. + \param nonce Optional additional input. + \param nonceSz Length of nonce in bytes. + + \sa wc_RNG_DRBG_Reseed + \sa wc_RNG_DRBG_Reseed_Nonce_Uncredited +*/ +int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz); + +/*! + \ingroup Random + + \brief Similar to wc_RNG_DRBG_Reseed(), except the caller-supplied + material is mixed through the reseed derivation function without being + credited as entropy: the reseed counter is not reset, so only the + module's own seed source ever extends the instance's seed lifetime. + + \return 0 Success + \return BAD_FUNC_ARG rng or seed is null. + \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.). + + \param rng The RNG object to stir. + \param seed Material to mix in. + \param seedSz Length of seed in bytes. + + \sa wc_RNG_DRBG_Reseed + \sa wc_RNG_DRBG_Reseed_Nonce_Uncredited +*/ +int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz); + +/*! + \ingroup Random + + \brief The nonce-bearing form of wc_RNG_DRBG_Reseed_Uncredited(). + + \return 0 Success + \return BAD_FUNC_ARG rng or seed is null. + \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.). + + \param rng The RNG object to stir. + \param seed Material to mix in. + \param seedSz Length of seed in bytes. + \param nonce Optional additional input. + \param nonceSz Length of nonce in bytes. + + \sa wc_RNG_DRBG_Reseed_Uncredited + \sa wc_RNG_DRBG_Reseed_Nonce +*/ +int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, const byte* seed, + word32 seedSz, const byte *nonce, + word32 nonceSz); + +/*! + \ingroup Random + + \brief Instantiate child as an SP 800-90C RBG-chain member subordinate to + parent, drawing its seed material from parent's generate function in + place of the module's seed source. Every other aspect of instantiation + is wc_InitRng_ex2()'s. The child is tagged with stratum + (parent's stratum + 1), sticky for the instance's lifetime even across + subsequent source reseeds; its claimable security strength is capped by + parent's, and it has no prediction resistance. The caller must hold + exclusive access to parent for the duration of the call; the spawn debits + parent's reseed counter by one generate. + + \return 0 Success + \return BAD_FUNC_ARG child or parent is null, or child equals parent. + \return SEQ_OVERFLOW_E parent's stratum is at the representable maximum. + + \param child The caller-provided WC_RNG to instantiate (uninitialized). + \param parent The chain parent to draw seed material from. + \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes for the child. + + _Example_ + \code + WC_RNG root, child; + wc_InitRng(&root); + if (wc_InitRngRBGC(&child, &root, WC_RNG_INIT_FLAGS_NONE) == 0) { + // child serves independently; release with wc_FreeRng(&child) + } + \endcode + + \sa wc_InitRngNonceRBGC + \sa wc_InitRngRBGC_New + \sa wc_RNG_DRBG_ReseedRBGC + \sa wc_RNG_DRBG_GetRBGCStratum +*/ +int wc_InitRngRBGC(WC_RNG* child, WC_RNG* parent, word32 flags); + +/*! + \ingroup Random + + \brief The nonce-bearing form of wc_InitRngRBGC(): the nonce is used as + additional instantiation input, as in wc_InitRngNonce_ex2(). + + \return 0 Success + \return BAD_FUNC_ARG child or parent is null, child equals parent, or + nonce is null with nonceSz nonzero. + \return SEQ_OVERFLOW_E parent's stratum is at the representable maximum. + + \param child The caller-provided WC_RNG to instantiate (uninitialized). + \param parent The chain parent to draw seed material from. + \param nonce Additional instantiation input. + \param nonceSz Length of nonce in bytes. + \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes for the child. + + \sa wc_InitRngRBGC + \sa wc_InitRngNonceRBGC_New +*/ +int wc_InitRngNonceRBGC(WC_RNG* child, WC_RNG* parent, const byte* nonce, + word32 nonceSz, word32 flags); + +/*! + \ingroup Random + + \brief The allocating form of wc_InitRngRBGC(): the child is allocated + from parent's heap and returned through child. Release with + wc_rng_free(). + + \return 0 Success + \return BAD_FUNC_ARG child or parent is null. + \return MEMORY_E Allocation failed. + \return SEQ_OVERFLOW_E parent's stratum is at the representable maximum. + + \param child Receives the allocated, instantiated WC_RNG. + \param parent The chain parent to draw seed material from. + \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes for the child. + + \sa wc_InitRngRBGC + \sa wc_InitRngNonceRBGC_New +*/ +int wc_InitRngRBGC_New(WC_RNG** child, WC_RNG* parent, word32 flags); + +/*! + \ingroup Random + + \brief The allocating, nonce-bearing form of wc_InitRngRBGC(). + + \return 0 Success + \return BAD_FUNC_ARG child or parent is null, or nonce is null with + nonceSz nonzero. + \return MEMORY_E Allocation failed. + \return SEQ_OVERFLOW_E parent's stratum is at the representable maximum. + + \param child Receives the allocated, instantiated WC_RNG. + \param parent The chain parent to draw seed material from. + \param nonce Additional instantiation input. + \param nonceSz Length of nonce in bytes. + \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes for the child. + + \sa wc_InitRngRBGC_New + \sa wc_InitRngNonceRBGC +*/ +int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, + word32 nonceSz, word32 flags); + +/*! + \ingroup Random + + \brief Reseed rng from root's generate output -- the SP 800-90C chain + reseed -- with an optional nonce as additional input. The reseed is + credited (the reseed counter resets) and rng acquires root's stratum + plus one. The caller must hold exclusive access to both instances. + + \details Credited chain reseeds obey a no-downgrade rule: a + primary-seeded (stratum-0) root is always accepted, and a chained + (stratum > 0) root is accepted only when its stratum is strictly less + than rng's -- the acquired stratum never increases, so reseed cycles + are impossible by construction, consistent with SP 800-90C 7.1.2.2. + Lateral (equal-stratum) and downgrading reseeds are refused with + BAD_FUNC_ARG. Building WC_RNG_NO_RBGC_RESEED restricts credited chain + reseeds to primary-seeded roots. Uncredited chain reseeds + (wc_RNG_DRBG_ReseedRBGC_Uncredited()) are exempt from all of this: they + are stirs, claim nothing, and leave rng's stratum untouched. + + \return 0 Success + \return BAD_FUNC_ARG rng or root is null, rng equals root, or the + no-downgrade rule refuses root as a chain parent (see \details). + \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.). + \return SEQ_OVERFLOW_E root's stratum is at the representable maximum. + + \param rng The chain member to reseed. + \param root The chain parent to draw seed material from. + \param nonce Optional additional input. + \param nonceSz Length of nonce in bytes. + + \sa wc_InitRngRBGC + \sa wc_RNG_DRBG_ReseedRBGC_Uncredited + \sa wc_RNG_DRBG_Reseed_Now +*/ +int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, + word32 nonceSz); + +/*! + \ingroup Random + + \brief The uncredited form of wc_RNG_DRBG_ReseedRBGC(): material from + root is mixed in without resetting rng's reseed counter. + + \return 0 Success + \return BAD_FUNC_ARG rng or root is null, or rng equals root. + \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.). + + \param rng The chain member to stir. + \param root The chain parent to draw material from. + \param nonce Optional additional input. + \param nonceSz Length of nonce in bytes. + + \sa wc_RNG_DRBG_ReseedRBGC + \sa wc_RNG_DRBG_Reseed_Uncredited + \details Unrestricted by the credited no-downgrade rule: any source + stratum is accepted, and rng's reseed counter, stratum, and + entropy-invalidated state are all left untouched -- an uncredited + chain reseed is a stir, and a stir must never masquerade as recovery + or promotion. + +*/ +int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, WC_RNG* root, + const byte* nonce, word32 nonceSz); + +/*! + \ingroup Random + + \brief Report rng's RBG-chain stratum: 0 for a root (never chain-seeded), + n for a member seeded from a stratum-(n-1) parent. The stratum is sticky + for the instance's lifetime, even across subsequent source reseeds. + + \return 0 rng is a chain root. + \return n The stratum, positive for a chain member. + \return BAD_FUNC_ARG rng is null. + + \param rng The RNG object to interrogate. + + \sa wc_InitRngRBGC + \sa wc_RNG_DRBG_GetNextSeedRBGCStratum +*/ +int wc_RNG_DRBG_GetRBGCStratum(const WC_RNG* rng); + +/*! + \ingroup Random + + \brief Report the RBG-chain stratum of rng's banked next seed -- + race-free via the aperture protocol -- for provenance-aware consumers. + + \return 0 The banked seed has root (source) provenance. + \return n The banked seed's stratum, positive for chain provenance. + \return BAD_FUNC_ARG rng is null or has no DRBG. + \return NOT_READY_E No banked seed is ready. + + \param rng The RNG object to interrogate. + + \sa wc_RNG_DRBG_GetRBGCStratum + \sa wc_RNG_DRBG_NextSeedGenerate_RBGC +*/ +int wc_RNG_DRBG_GetNextSeedRBGCStratum(const WC_RNG* rng); + +/*! + \ingroup Random + + \brief Bank up to n more bytes of next-seed material from the module's + seed source, health-testing and publishing the bank when it completes. + The fill is incremental and in-boundary; a scheduling daemon may call + this without owning the instance -- the single-writer fill and the + atomic aperture hand-off make it safe alongside a concurrent consumer. + + \return 0 Bytes were banked (bank may or may not yet be complete). + \return ALREADY_E The bank is ready or being consumed. + \return NOT_READY_E The health test could not run; simply retry. + \return BAD_FUNC_ARG rng is null or n is 0. + \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). + + \param rng The RNG object whose bank to fill. + \param n Maximum bytes to bank this call (clamped to space remaining). + + _Example_ + \code + // scheduling daemon: fill incrementally until published + int ret = wc_RNG_DRBG_NextSeedGenerate(rng, 16); + if (ret == ALREADY_E) { + // bank is ready; nothing to do until a consumer claims it + } + \endcode + + \sa wc_RNG_DRBG_NextSeedNow + \sa wc_RNG_DRBG_NextSeedCurrent + \sa wc_RNG_DRBG_NextSeedGenerate_RBGC +*/ +int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n); + +/*! + \ingroup Random + + \brief The chain-sourced form of wc_RNG_DRBG_NextSeedGenerate(): the + banked material is drawn from root's generate function, and the bank is + tagged with root's stratum plus one for provenance-aware consumption. + + \return 0 Bytes were banked. + \return ALREADY_E The bank is ready or being consumed. + \return NOT_READY_E The health test could not run; simply retry. + \return BAD_FUNC_ARG rng or root is null, or n is 0. + \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). + \return SEQ_OVERFLOW_E root's stratum is at the representable maximum. + + \param rng The RNG object whose bank to fill. + \param root The chain parent to draw material from. + \param n Maximum bytes to bank this call. + + \sa wc_RNG_DRBG_NextSeedGenerate + \sa wc_RNG_DRBG_GetNextSeedRBGCStratum + \details Banking is bound for credited redemption, so the credited + no-downgrade rule applies at bank time: a primary-seeded (stratum-0) + root is always accepted, and a chained root only when its stratum is + strictly less than rng's -- banking whose redemption would raise rng's + stratum is refused with BAD_FUNC_ARG. The banked material records + root's stratum plus one, observable via + wc_RNG_DRBG_GetNextSeedRBGCStratum(), and redemption + (wc_RNG_DRBG_NextSeedNow()) carries it onto rng. + +*/ +int wc_RNG_DRBG_NextSeedGenerate_RBGC(WC_RNG* rng, WC_RNG *root, word32 n); + +/*! + \ingroup Random + + \brief Report the raw next-seed aperture value: a non-negative banked + byte count (filling), WC_DRBG_NEXT_SEED_READY, or + WC_DRBG_NEXT_SEED_CONSUMING. The snapshot is racy by design; use it for + scheduling and diagnostics, not for hand-off decisions. + + \return 0 Success + \return BAD_FUNC_ARG rng or n is null. + \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). + + \param rng The RNG object to interrogate. + \param n Receives the aperture value. + + \sa wc_RNG_DRBG_NextSeedGenerate + \sa wc_RNG_DRBG_NextSeedNow +*/ +int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng, WC_ATOMIC_INT_ARG* n); + +/*! + \ingroup Random + + \brief Claim a ready next-seed bank and perform a source-free credited + reseed with it -- safe in atomic context. The bank empties (use-once) + and the reseed counter resets. The caller must own the instance. + + \return 0 Success + \return NOT_READY_E No bank is ready. + \return BAD_FUNC_ARG rng is null. + \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). + + \param rng The RNG object to reseed. + + _Example_ + \code + // atomic-context consumer + if (wc_RNG_DRBG_NextSeedNow(rng) == 0) { + // freshly reseeded without touching the seed source + } + \endcode + + \sa wc_RNG_DRBG_NextSeedGenerate + \sa wc_RNG_DRBG_NextSeedNow_Nonce +*/ +int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); + +/*! + \ingroup Random + + \brief The nonce-bearing form of wc_RNG_DRBG_NextSeedNow(): the nonce is + mixed in as uncredited additional input alongside the banked seed. + + \return 0 Success + \return NOT_READY_E No bank is ready. + \return BAD_FUNC_ARG rng is null, or nonce is null with nonceSz nonzero. + \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). + \return DRBG_CONT_FIPS_E The continuous test failed; the DRBG is out of + service. + \return RNG_FAILURE_E The DRBG is out of service. + + \param rng The RNG object to reseed. + \param nonce Additional input. + \param nonceSz Length of nonce in bytes. + + \sa wc_RNG_DRBG_NextSeedNow +*/ +int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, + word32 nonceSz); + +/*! + \ingroup Random + + \brief Bank caller-supplied material (up to + WC_DRBG_NEXT_UNCREDITED_SEED_LEN bytes) in the uncredited accumulator + beside the banked next seed. Writer-safe without a lease + (read-copy-store); if the accumulator is already full, the material is + absorbed by xor. Harvested entropy deposited here improves the instance + without claiming credit. + + \return 0 Success + \return BAD_FUNC_ARG rng or nonce is null, or nonceSz is 0. + \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). + + \param rng The RNG object whose accumulator to feed. + \param nonce Material to bank. + \param nonceSz Length of nonce in bytes. + + \sa wc_RNG_DRBG_NextUncreditedSeedNow + \sa wc_RNG_DRBG_Reseed_Uncredited +*/ +int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, const byte *nonce, + word32 nonceSz); + +/*! + \ingroup Random + + \brief Stir the banked uncredited accumulator into the DRBG as an + uncredited, source-free reseed -- safe in atomic context; the reseed + counter is not reset. The caller must own the instance. + + \return 0 Success + \return NOT_READY_E The accumulator is empty. + \return BAD_FUNC_ARG rng is null. + \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). + \return RNG_FAILURE_E The DRBG is out of service. + + \param rng The RNG object to stir. + + \sa wc_RNG_DRBG_NextUncreditedSeedStore +*/ +int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng); + +/*! + \ingroup Random + + \brief Acquire rng's exclusive-ownership lock latch, spinning on the CAS + until acquired, and or the caller's extra bits into the lock word. On an + instance without the lock-required policy the call is a successful no-op + unless extra bits are supplied. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + \return NEEDS_RECOVERY_E The instance's entropy is invalidated; recover + before use. + \return BUSY_E The lock is held. + \return NEEDS_RECOVERY_E The instance is entropy-invalidated (see + wc_RNG_invalidate_entropy()); recover by credited reseed. + \return BAD_MUTEX_E (WC_RNG_HAVE_LOCK_FULL_MUTEX) The outer mutex failed. + \return UNEXPECTED_STATE_E Spurious acquisition failure; retry. + \return BUSY_E The lock is held. + \return NEEDS_RECOVERY_E The instance is entropy-invalidated (see + wc_RNG_invalidate_entropy()); recover by credited reseed. + \return BAD_MUTEX_E (WC_RNG_HAVE_LOCK_FULL_MUTEX) The outer mutex failed. + \return UNEXPECTED_STATE_E Spurious acquisition failure; retry. + + \param rng The RNG object to lock. + \param extra_bits Caller-defined bits (above WC_RNG_LOCK_EXTRA_SHIFT) to + set atomically with the acquisition, or 0. + + _Example_ + \code + if (wc_RNG_lock_get(rng, 0) == 0) { + ret = wc_RNG_GenerateBlock(rng, out, sizeof(out)); + wc_RNG_lock_put(rng, 0); + } + \endcode + + \sa wc_RNG_lock_put + \sa wc_RNG_lock_get_conditional + \sa wc_RNG_lock_read +*/ +int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + +/*! + \ingroup Random + + \brief The conditional form of wc_RNG_lock_get(): acquire only if the + current extra bits equal expected_extra_bits, atomically replacing them + with want_extra_bits on success. Non-blocking with respect to the + condition: a mismatch fails immediately rather than spinning. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + \return BUSY_E The lock is held, or the extra bits do not match + expected_extra_bits. + \return NEEDS_RECOVERY_E Entropy-invalidated and + WC_RNG_LOCK_ENTROPY_INVALIDATED is not in expected_extra_bits. + \return BAD_MUTEX_E (WC_RNG_HAVE_LOCK_FULL_MUTEX) The outer mutex + failed. + \return UNEXPECTED_STATE_E Spurious acquisition failure; retry. + + \param rng The RNG object to lock. + \param expected_extra_bits The extra bits required for acquisition. + \param want_extra_bits The extra bits to install on acquisition. + + \sa wc_RNG_lock_get + \sa wc_RNG_lock_put_conditional +*/ +int wc_RNG_lock_get_conditional(WC_RNG* rng, + WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits); + +/*! + \ingroup Random + + \brief Release rng's lock latch, clearing the supplied extra bits + atomically with the release. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + \return OBJECT_NOT_LOCKED_E The latch is not held. + \return NEEDS_RECOVERY_E Released successfully; informational notice that the + instance is entropy-invalidated. + + \param rng The RNG object to unlock. + \param extra_bits Caller-defined bits to clear with the release, or 0. + + \sa wc_RNG_lock_get + \sa wc_RNG_lock_put_conditional +*/ +int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + +/*! + \ingroup Random + + \brief The conditional form of wc_RNG_lock_put(): release only if the + current extra bits equal expected_extra_bits, atomically replacing them + with want_extra_bits on success. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + \return BUSY_E The extra bits did not match expected_extra_bits. + \return OBJECT_NOT_LOCKED_E The latch is not held. + \return NEEDS_RECOVERY_E Released successfully; informational notice + that the instance is entropy-invalidated. + \return UNEXPECTED_STATE_E Spurious release failure. + + \param rng The RNG object to unlock. + \param expected_extra_bits The extra bits required for release. + \param want_extra_bits The extra bits to install on release. + + \sa wc_RNG_lock_put + \sa wc_RNG_lock_get_conditional +*/ +int wc_RNG_lock_put_conditional(WC_RNG* rng, + WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits); + +/*! + \ingroup Random + + \brief Read rng's lock word: the held/required latch bits, the + entropy-invalidated bit, and any caller extra bits. The snapshot is + racy by design. + + \return 0 Success + \return BAD_FUNC_ARG rng or state is null. + + \param rng The RNG object to interrogate. + \param state Receives the lock word. + + \sa wc_RNG_lock_get + \sa wc_RNG_invalidate_entropy +*/ +int wc_RNG_lock_read(WC_RNG* rng, WC_RNG_lock_arg_t* state); + +/*! + \ingroup Random + + \brief Atomically set (or) the supplied caller extra bits in rng's lock + word. The caller should hold the latch. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + + \param rng The RNG object to modify. + \param extra_bits The bits to set. + + \sa wc_RNG_lock_add_extra + \sa wc_RNG_lock_clear_extra +*/ +int wc_RNG_lock_set_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + +/*! + \ingroup Random + + \brief Atomically add the supplied value to the caller extra-bits field + of rng's lock word -- for counters carried above + WC_RNG_LOCK_EXTRA_SHIFT. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + + \param rng The RNG object to modify. + \param extra_bits The value to add. + + \sa wc_RNG_lock_set_extra + \sa wc_RNG_lock_clear_extra +*/ +int wc_RNG_lock_add_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + +/*! + \ingroup Random + + \brief Atomically clear the supplied caller extra bits in rng's lock + word. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + + \param rng The RNG object to modify. + \param extra_bits The bits to clear. + + \sa wc_RNG_lock_set_extra + \sa wc_RNG_lock_add_extra +*/ +int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); + +/*! + \ingroup Random + + \brief Mark rng's seed material untrusted -- for VM fork/resume and + similar duplication events -- by latching the entropy-invalidated bit in + the lock word. An invalidated instance refuses service + (NEEDS_RECOVERY_E) until recovery-reseeded. + + \return 0 Success + \return BAD_FUNC_ARG rng is null. + + \param rng The RNG object to invalidate. + + _Example_ + \code + // VM-resume handler + (void)wc_RNG_invalidate_entropy(rng); + // subsequent wc_RNG_lock_get() returns NEEDS_RECOVERY_E until recovery + \endcode + + \sa wc_RNG_lock_get + \sa wc_RNG_register_free_hook +*/ +int wc_RNG_invalidate_entropy(WC_RNG* rng); + +/*! + \ingroup Random + + \brief Register a callback fired by wc_FreeRng() at teardown -- for + external registries (e.g. a kernel-module registry that must reach every + live RNG on a VM duplication event) that need to drop their reference + when the object dies. + + \return 0 Success + \return BAD_FUNC_ARG rng or free_hook is null. + + \param rng The RNG object to hook. + \param free_hook The callback. + \param arg Opaque argument passed to the callback. + + \sa wc_RNG_invalidate_entropy + \sa wc_FreeRng +*/ +int wc_RNG_register_free_hook(WC_RNG* rng, wc_RNG_free_hook_cb_t free_hook, + void *arg); + +/*! + \ingroup Random + + \brief Attach a random pool to rng: a buffer of size bytes of + pre-generated output, filled by wc_RNG_Pool_Collect() and drained + atomic-context-safely by wc_RNG_Pool_Extract(). The pool is released + with the instance. + + \return 0 Success + \return BAD_FUNC_ARG rng is null, or size is 0 or out of range. + \return MEMORY_E Allocation failed. + \return ALREADY_E The pool is already allocated. + + \param rng The RNG object to equip. + \param size Pool capacity in bytes. + + _Example_ + \code + wc_RNG_Pool_Alloc(rng, 256); + wc_RNG_Pool_Collect(rng, 256); // sleepable context + word32 n = 16; + if (wc_RNG_Pool_Extract(rng, out, &n) == 0) { + // n bytes delivered, atomic-context-safe + } + \endcode + + \sa wc_RNG_Pool_Collect + \sa wc_RNG_Pool_Extract +*/ +int wc_RNG_Pool_Alloc(WC_RNG* rng, word32 size); + +/*! + \ingroup Random + + \brief Generate up to n bytes into rng's pool from rng itself. The + collect/extract hand-off is arbitrated by an atomic aperture word, so a + single collector is safe alongside concurrent extractors. + + \return 0 Success + \return ALREADY_E The pool is full or being drained. + \return BAD_FUNC_ARG rng is null, has no pool, or n is 0. + + \param rng The RNG object whose pool to fill. + \param n Maximum bytes to collect this call. + + \sa wc_RNG_Pool_Alloc + \sa wc_RNG_Pool_Collect2 + \sa wc_RNG_Pool_Extract +*/ +int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n); + +/*! + \ingroup Random + + \brief The two-instance form of wc_RNG_Pool_Collect(): fill rng_dest's + pool with output drawn from rng_src -- so a service instance's pool can + be topped up by a daemon-owned generator. + + \return 0 Success + \return ALREADY_E The pool is full or being drained. + \return BAD_FUNC_ARG rng_dest or rng_src is null, or rng_dest has no + pool, or n is 0. + \return BAD_STATE_E The pool is not allocated. + \return NOT_READY_E The source could not serve; retry later. + + \param rng_dest The RNG object whose pool to fill. + \param rng_src The RNG object to draw output from. + \param n Maximum bytes to collect this call. + + \sa wc_RNG_Pool_Collect +*/ +int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, word32 n); + +/*! + \ingroup Random + + \brief Drain up to *n bytes from rng's pool into out -- + atomic-context-safe. On success *n reports the bytes actually + delivered. + + \return 0 Success + \return NOT_READY_E The pool is empty or being filled. + \return BAD_FUNC_ARG rng, out, or n is null, or rng has no pool. + \return BAD_STATE_E The pool is not allocated. + \return RNG_FAILURE_E The instance is out of service. + + \param rng The RNG object whose pool to drain. + \param out Receives the output. + \param n In: bytes requested; out: bytes delivered. + + \sa wc_RNG_Pool_Collect + \sa wc_RNG_Pool_Current +*/ +int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n); + +/*! + \ingroup Random + + \brief Report the pool's current fill in bytes. The snapshot is racy by + design. + + \return 0 Success + \return BAD_FUNC_ARG rng or n is null, or rng has no pool. + + \param rng The RNG object to interrogate. + \param n Receives the fill. + + \sa wc_RNG_Pool_Extract +*/ +int wc_RNG_Pool_Current(WC_RNG* rng, word32* n); + +/*! + \ingroup Random + + \brief Snapshot the global RNG debug counters (WC_RNG_DEBUG_STATS) -- + seeds and reseeds by provenance, generates, pool and bank traffic -- + into s, for later delta accounting with wc_rng_debug_stats_sum(). + + \return 0 Success + \return BAD_FUNC_ARG s is null. + + \param s Receives the snapshot. + \param rng Optional instance for per-instance context, or null. + + \sa wc_rng_debug_stats_sum + \sa wc_rng_debug_stats_restore +*/ +int wc_rng_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, + const WC_RNG *rng); + +/*! + \ingroup Random + + \brief Restore the global RNG debug counters from a snapshot -- so a + test can unwind its own accounting. + + \return 0 Success + \return BAD_FUNC_ARG s is null. + + \param s The snapshot to restore from. + \param rng Optional instance for per-instance context, or null. + + \sa wc_rng_debug_stats_snap +*/ +int wc_rng_debug_stats_restore(const struct wc_rng_debug_stats_snapshot *s, + WC_RNG *rng); + +/*! + \ingroup Random + + \brief Accumulate the current global RNG debug counters into s -- + combined with a prior wc_rng_debug_stats_snap(), a delta accounting of + the interval's RNG activity. + + \return 0 Success + \return BAD_FUNC_ARG s is null. + + \param s The snapshot to accumulate into. + \param rng Optional instance for per-instance context, or null. + + \sa wc_rng_debug_stats_snap +*/ +int wc_rng_debug_stats_sum(struct wc_rng_debug_stats_snapshot *s, + const WC_RNG *rng); diff --git a/doc/dox_comments/header_files/rng_bank.h b/doc/dox_comments/header_files/rng_bank.h new file mode 100644 index 00000000000..0eb863acf12 --- /dev/null +++ b/doc/dox_comments/header_files/rng_bank.h @@ -0,0 +1,938 @@ +/*! + \ingroup Random + + \brief Allocate and initialize a bank of n_rngs pre-instantiated WC_RNG + instances, checked out and back in by consumers (wc_rng_bank_checkout() + et al.). The bank is allocated from heap; release with + wc_rng_bank_free(). Bank-level flags (WC_RNG_BANK_FLAG_*) fix the + bank's posture at initialization: e.g. _CAN_WAIT admits sleeping, + _QUIET suppresses seeding-degradation warnings, + _NO_CHECKOUT_REFCOUNTING suppresses per-checkout refcount traffic for + container-guaranteed lifetimes, _PREDICTION_RESISTANCE imposes a + bank-wide fresh-reseed posture on every sleepable lease. + + \return 0 Success + \return BAD_FUNC_ARG ctx is null or n_rngs is out of range. + \return MEMORY_E Allocation failed. + \return RNG_FAILURE_E No instance could be seeded within timeout_secs. + + \param ctx Receives the allocated bank. + \param n_rngs Number of instances. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* bank-posture flags. + \param timeout_secs Seeding timeout budget. + \param heap Heap hint for dynamic allocation. + \param devId Device id, or INVALID_DEVID. + + _Example_ + \code + struct wc_rng_bank *bank = NULL; + if (wc_rng_bank_new(&bank, 4, WC_RNG_BANK_FLAG_CAN_WAIT, 10, + NULL, INVALID_DEVID) == 0) { + // ... checkout/checkin traffic ... + wc_rng_bank_free(&bank); + } + \endcode + + \sa wc_rng_bank_init + \sa wc_rng_bank_checkout + \sa wc_rng_bank_free +*/ +int wc_rng_bank_new(struct wc_rng_bank **ctx, int n_rngs, word32 flags, + int timeout_secs, void *heap, int devId); + +/*! + \ingroup Random + + \brief Initialize a caller-provided bank object. Semantics of + wc_rng_bank_new(), without the allocation; release with + wc_rng_bank_fini(). + + \return 0 Success + \return BAD_FUNC_ARG ctx is null or n_rngs is out of range. + \return BAD_LENGTH_E n_rngs exceeds the static capacity + (WC_RNG_BANK_STATIC builds). + \return RNG_FAILURE_E No instance could be seeded within timeout_secs. + + \param ctx The bank object to initialize. + \param n_rngs Number of instances. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* bank-posture flags. + \param timeout_secs Seeding timeout budget. + \param heap Heap hint for dynamic allocation. + \param devId Device id, or INVALID_DEVID. + + \sa wc_rng_bank_new + \sa wc_rng_bank_init_nonce + \sa wc_rng_bank_fini +*/ +int wc_rng_bank_init(struct wc_rng_bank *ctx, int n_rngs, word32 flags, + int timeout_secs, void *heap, int devId); + +/*! + \ingroup Random + + \brief The nonce-bearing form of wc_rng_bank_init(): the nonce is used + as additional instantiation input for each instance. + + \return 0 Success + \return BAD_FUNC_ARG ctx is null, n_rngs is out of range, or nonce is + null with nonceSz nonzero. + \return RNG_FAILURE_E No instance could be seeded within timeout_secs. + \return BAD_LENGTH_E nonceSz exceeds the supported maximum. + \return MEMORY_E Allocation failed. + \return WC_TIMEOUT_E Instance seeding exceeded timeout_secs. + + \param ctx The bank object to initialize. + \param n_rngs Number of instances. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* bank-posture flags. + \param timeout_secs Seeding timeout budget. + \param heap Heap hint for dynamic allocation. + \param devId Device id, or INVALID_DEVID. + \param nonce Additional instantiation input. + \param nonceSz Length of nonce in bytes. + + \sa wc_rng_bank_init +*/ +int wc_rng_bank_init_nonce(struct wc_rng_bank *ctx, int n_rngs, word32 flags, + int timeout_secs, void *heap, int devId, + const byte *nonce, word32 nonceSz); + +/*! + \ingroup Random + + \brief Designate the first instance of the failover pool: checkouts + without a targeted preference rotate through instances at and above + first_failover_inst, reserving the lower offsets for targeted + (affinity or daemon) use. + + \return 0 Success + \return BAD_FUNC_ARG ctx is null or first_failover_inst is out of range. + + \param ctx The bank to configure. + \param first_failover_inst The first failover-eligible instance offset. + + \sa wc_rng_bank_checkout +*/ +int wc_rng_bank_first_failover_inst_set(struct wc_rng_bank *ctx, + int first_failover_inst); + +/*! + \ingroup Random + + \brief Install affinity handlers: callbacks that pin the caller to an + execution context (e.g. disable preemption or migration), report its id + for instance affinity, and unpin. With handlers installed, + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST checkouts prefer the instance + matching the caller's affinity id, and WC_RNG_BANK_FLAG_AFFINITY_LOCK + holds the pin across the lease. + + \return 0 Success + \return BAD_FUNC_ARG ctx is null. + \return BUSY_E The bank is in service; handlers must be set before use. + + \param ctx The bank to configure. + \param affinity_lock_cb Pin the caller; may be null. + \param affinity_get_id_cb Report the caller's affinity id. + \param affinity_unlock_cb Unpin the caller; may be null. + \param cb_arg Opaque argument passed to the callbacks. + + \sa wc_rng_bank_checkout +*/ +int wc_rng_bank_set_affinity_handlers(struct wc_rng_bank *ctx, + wc_affinity_lock_fn_t affinity_lock_cb, + wc_affinity_get_id_fn_t affinity_get_id_cb, + wc_affinity_unlock_fn_t affinity_unlock_cb, + void *cb_arg); + +/*! + \ingroup Random + + \brief Tear down a bank initialized with wc_rng_bank_init(): once the + refcount and per-instance lease gates pass, fires any registered free + hook and frees every instance. Never waits: a referenced or leased + bank is refused with BUSY_E, and the caller quiesces its consumers and + retries. + + \return 0 Success + \return BAD_FUNC_ARG ctx is null. + \return BUSY_E The bank is still referenced, or an instance lease is + outstanding. + \return BAD_STATE_E The refcount is below its initialization baseline + (teardown of an uninitialized or corrupted bank). + + \param ctx The bank to tear down. + + \sa wc_rng_bank_init + \sa wc_rng_bank_free + \sa wc_rng_bank_register_free_hook +*/ +int wc_rng_bank_fini(struct wc_rng_bank *ctx); + +/*! + \ingroup Random + + \brief Tear down and release a bank allocated with wc_rng_bank_new(). + + \return 0 Success + \return BAD_FUNC_ARG ctx or *ctx is null. + \return BAD_STATE_E The bank is still referenced. + + \param ctx The bank to release; nulled on success. + + \sa wc_rng_bank_new + \sa wc_rng_bank_fini +*/ +int wc_rng_bank_free(struct wc_rng_bank **ctx); + +/*! + \ingroup Random + + \brief Register bank as the process-default bank, retrievable with + wc_rng_bank_default_checkout(). + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return BAD_STATE_E A default bank is already registered. + \return BUSY_E Registration is contended; retry. + + \param bank The bank to register. + + \sa wc_rng_bank_default_checkout + \sa wc_rng_bank_default_clear +*/ +int wc_rng_bank_default_set(struct wc_rng_bank *bank); + +/*! + \ingroup Random + + \brief Take a reference on the process-default bank. + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return BAD_STATE_E No default bank is registered. + \return NO_DEFAULT_FOUND_E No default bank is registered. + + \param bank Receives the default bank. + + \sa wc_rng_bank_default_set + \sa wc_rng_bank_default_checkin +*/ +int wc_rng_bank_default_checkout(struct wc_rng_bank **bank); + +/*! + \ingroup Random + + \brief Release a reference taken with wc_rng_bank_default_checkout(). + + \return 0 Success + \return BAD_FUNC_ARG bank or *bank is null. + + \param bank The reference to release; nulled on success. + + \sa wc_rng_bank_default_checkout +*/ +int wc_rng_bank_default_checkin(struct wc_rng_bank **bank); + +/*! + \ingroup Random + + \brief Unregister the process-default bank. + + \return 0 Success + \return BAD_FUNC_ARG bank is null or is not the registered default. + \return BUSY_E Unregistration is contended; retry. + + \param bank The bank to unregister. + + \sa wc_rng_bank_default_set +*/ +int wc_rng_bank_default_clear(struct wc_rng_bank *bank); + +/*! + \ingroup Random + + \brief Lease an in-service instance from the bank: either the preferred + (or affinity-matched) instance, or -- with + WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST -- the first available failover + instance. On success the caller owns the instance's lock; access the + WC_RNG with WC_RNG_BANK_INST_TO_RNG() and return the lease with + wc_rng_bank_inst_checkin(). WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED consumes + a ready banked next seed in an immediate source-free credited reseed + before returning; WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (per-call, + requires _CAN_WAIT) freshly credited-reseeds the lease before the + caller's first draw. + + \details Out-of-service instances: a targeted (non-failover) checkout + admits them; failover checkouts divert around them. + WC_RNG_BANK_FLAG_FOR_RECOVERY makes the targeted admission explicit and + interaction-safe (recovery machinery and patrols): out-of-service + status is expected, the _CONSUME_NEXT_SEED arm is suppressed (a consume + would fail on exactly the instances recovery targets), and failover and + affinity selection are rejected in combination. + WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED gives the opposite guarantee: + either a lease on an in-service instance, or an error with no lease -- + never a lease on an out-of-service instance; under _CAN_WAIT an + out-of-service instance is retried within the timeout budget (allowing + a patrol to restore it), and the distinguished error for a lap or wait + that found only out-of-service instances is BAD_STATE_E. + + Entropy-invalidated (quarantined) instances refuse ordinary leases with + NEEDS_RECOVERY_E, with two admissions. First, when the quarantined + instance holds a READY banked next seed, any claimant is admitted and + the consume-at-checkout reseed runs unconditionally: the invalidation + purge guarantees READY banked material post-dates the invalidation + event, so admitting the claimant is completing the recovery. Second, + WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY admits the caller to a quarantined + instance with no banked material, transferring the recovery obligation: + checkout then returns NEEDS_RECOVERY_E with the checkout otherwise + complete -- *rng_inst set; instance lock, affinity locks, and any + vector-inhibit state held. This is the robust-mutex (EOWNERDEAD) + pattern: an error return with the acquisition complete and persistent, + because "this resource needs consistency recovery" is only safely + reportable to a caller that already holds it. The caller must either + recover the instance (a credited reseed, e.g. wc_RNG_DRBG_Reseed_Now(), + clears the quarantine) or check it back in. Ordinary consumers that + cannot complete a recovery must not pass this flag. + + \return 0 Success; *rng_inst holds the lease. + \return BAD_FUNC_ARG bank or rng_inst is null, or the flags are + contradictory. + \return NEEDS_RECOVERY_E Quarantined: refused without a lease + (ordinary checkout), or lease held with recovery owed + (_MAYBE_FOR_RECOVERY; see \details). + \return BAD_STATE_E (_ERROR_ON_RNG_FAILED) only out-of-service + instances were found. + \return WC_TIMEOUT_E The wait budget expired. + \return RNG_FAILURE_E No serviceable instance. + \return BAD_INDEX_E preferred_inst_offset is out of range. + \return BUSY_E The selected instance is contended (without _CAN_WAIT). + + \param bank The bank to lease from. + \param rng_inst Receives the leased instance. + \param preferred_inst_offset The preferred instance, or 0. + \param timeout_secs Wait budget (with _CAN_WAIT). + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* per-call flags. + + _Example_ + \code + struct wc_rng_bank_inst *inst = NULL; + if (wc_rng_bank_checkout(bank, &inst, 0, 10, + WC_RNG_BANK_FLAG_CAN_WAIT | + WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) == 0) { + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), + out, sizeof(out)); + wc_rng_bank_inst_checkin(&inst); + } + \endcode + + \sa wc_rng_bank_inst_checkin + \sa wc_rng_bank_recover_inst + \sa wc_rng_bank_spawn +*/ +int wc_rng_bank_checkout(struct wc_rng_bank *bank, + struct wc_rng_bank_inst **rng_inst, + int preferred_inst_offset, int timeout_secs, + word32 flags); + +/*! + \ingroup Random + + \brief Return a lease through the bank object, validating that rng_inst + belongs to bank. Prefer wc_rng_bank_inst_checkin() when only the + instance pointer is at hand. + + \return 0 Success + \return BAD_FUNC_ARG bank or rng_inst is null, or the instance does not + belong to bank. + \return OBJECT_NOT_LOCKED_E The instance's lease is not held (e.g. a + stale duplicate check-in). + \return NEEDS_RECOVERY_E Checked in successfully; informational + notice that the instance is entropy-invalidated. + + \param bank The bank the instance belongs to. + \param rng_inst The lease to return; nulled on success. + + \sa wc_rng_bank_inst_checkin + \sa wc_rng_bank_checkout +*/ +int wc_rng_bank_checkin(struct wc_rng_bank *bank, + struct wc_rng_bank_inst **rng_inst); + +/*! + \ingroup Random + + \brief Return a lease by instance pointer alone. + + \return 0 Success + \return BAD_FUNC_ARG rng_inst or *rng_inst is null. + \return OBJECT_NOT_LOCKED_E The instance's lease is not held. + \return NEEDS_RECOVERY_E Checked in successfully; informational + notice that the instance is entropy-invalidated. + + \param rng_inst The lease to return; nulled on success. + + \sa wc_rng_bank_checkout + \sa wc_rng_bank_checkin +*/ +int wc_rng_bank_inst_checkin(struct wc_rng_bank_inst **rng_inst); + +/*! + \ingroup Random + + \brief Report the instance's offset within its bank. + + \return n The instance offset, non-negative. + \return BAD_FUNC_ARG rng_inst is null. + + \param rng_inst The instance to interrogate. + + \sa wc_rng_bank_checkout +*/ +int wc_rng_bank_get_inst_id(struct wc_rng_bank_inst *rng_inst); + +/*! + \ingroup Random + + \brief Bank next-seed material for the instance at inst_offset -- + wc_RNG_DRBG_NextSeedGenerate() through the bank, without taking the + instance lock; the daemon-side serialization word arbitrates against + concurrent whole-instance reinitialization. + + \return 0 Bytes were banked. + \return ALREADY_E The instance's bank is ready or being consumed. + \return NOT_READY_E The health test could not run; simply retry. + \return BAD_FUNC_ARG bank is null, inst_offset is out of range, or n + is 0. + + \param bank The bank. + \param inst_offset The instance to bank for. + \param n Maximum bytes to bank this call. + + \sa wc_RNG_DRBG_NextSeedGenerate + \sa wc_rng_bank_next_seed_generate_rbgc + \details The daemon-side serialization word (not the instance lock) + excludes a concurrent wc_rng_bank_inst_reinit() from freeing the DRBG + out from under the gather; lease-holders never consult it, since + instance-lock exclusion already covers every lease-holder interaction. + The caller must hold a bank reference (e.g. per the daemon association) + for the duration of the call. Return taxonomy for a banking rotation: + BUSY_E, the gate is held by a reinit -- skip this turn; ALREADY_E, + sleep until the bank is consumed; MISSING_RNG_E, the instance has no + DRBG (RDRAND et al.) and can be retired from the rotation permanently; + other errors are transient gather or health-test failures -- skip the + turn, and alarm if persistent. + +*/ +int wc_rng_bank_next_seed_generate(struct wc_rng_bank *bank, int inst_offset, + word32 n); + +/*! + \ingroup Random + + \brief The chain-sourced form of wc_rng_bank_next_seed_generate(): the + banked material is drawn from root's generate function and tagged with + its provenance. + + \return 0 Bytes were banked. + \return ALREADY_E The instance's bank is ready or being consumed. + \return NOT_READY_E The health test could not run; simply retry. + \return BAD_FUNC_ARG bank or root is null, inst_offset is out of range, + or n is 0. + + \param bank The bank. + \param inst_offset The instance to bank for. + \param n Maximum bytes to bank this call. + \param root The chain parent to draw material from. + + \sa wc_rng_bank_next_seed_generate + \sa wc_RNG_DRBG_NextSeedGenerate_RBGC +*/ +int wc_rng_bank_next_seed_generate_rbgc(struct wc_rng_bank *bank, + int inst_offset, word32 n, + WC_RNG *root); + +/*! + \ingroup Random + + \brief Free and reinstantiate a leased instance in place. The caller + must hold the lease; the daemon-side serialization word excludes + concurrent lockless banking during the cycle. + + \return 0 Success + \return BAD_FUNC_ARG bank or rng_inst is null. + \return RNG_FAILURE_E Reinstantiation could not be seeded within + timeout_secs. + \return BUSY_E The whole-instance-operation gate is held (daemon banking in + progress); retry. + + \param bank The bank the instance belongs to. + \param rng_inst The leased instance to reinitialize. + \param timeout_secs Seeding timeout budget. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. + + \sa wc_rng_bank_recover_inst +*/ +int wc_rng_bank_inst_reinit(struct wc_rng_bank *bank, + struct wc_rng_bank_inst *rng_inst, + int timeout_secs, word32 flags); + +/*! + \ingroup Random + + \brief Patrol helper: check out the instance at inst_offset with + WC_RNG_BANK_FLAG_FOR_RECOVERY, recover it iff it needs recovery, and + check it back in. Two recovery arms: an out-of-service instance + (wc_RNG_GetStatus() != WC_DRBG_OK) is reinitialized in place; an + in-service but entropy-invalidated (quarantined) instance takes one + credited reseed, which clears the quarantine while preserving instance + identity. A healthy instance is a success no-op, so callers can + invoke this unconditionally on state observed locklessly. + + \return 0 Success (recovered, or nothing to recover). + \return BAD_FUNC_ARG bank is null, inst_offset is out of range, or the + flags are contradictory. + \return RNG_FAILURE_E Recovery could not be seeded within timeout_secs. + \return BUSY_E The instance lock or whole-instance-operation gate is + contended; retry on a later patrol turn. + + \param bank The bank. + \param inst_offset The instance to patrol. + \param timeout_secs Seeding timeout budget. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. + + _Example_ + \code + // after a VM duplication event has invalidated the bank: + for (i = 0; i < n_rngs; i++) + (void)wc_rng_bank_recover_inst(bank, i, 10, + WC_RNG_BANK_FLAG_CAN_WAIT); + \endcode + + \sa wc_rng_bank_invalidate_entropy + \sa wc_rng_bank_checkout + \details A stale lockless status observation costs one harmless round + trip. BUSY_E reports contention on the instance lock or the + whole-instance-operation gate: retry on a later patrol turn. flags may + include WC_RNG_BANK_FLAG_CAN_WAIT and WC_RNG_BANK_FLAG_AFFINITY_LOCK, + which are passed through. + +*/ +int wc_rng_bank_recover_inst(struct wc_rng_bank *bank, int inst_offset, + int timeout_secs, word32 flags); + +/*! + \ingroup Random + + \brief Spawn an SP 800-90C chain RNG from a bank instance: check out a + parent instance (honoring the usual selection flags), instantiate + child_rng as its chain child (wc_InitRngNonceRBGC()), and check the + parent back in. The child's lifetime is thereafter decoupled from the + parent and its bank; release it with wc_FreeRng() (or wc_rng_free() for + the heap form, wc_rng_bank_spawn_new()). The child's RBGC stratum is + one plus the parent's stratum at instantiation. + + \details A recommended nonce, when available, is a racy read of a + high-resolution timer (e.g. Linux kernel random_get_entropy()). + WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED composes: a ready banked seed is + redeemed on the parent before the spawn draw. + WC_RNG_BANK_FLAG_SEED_UNCREDITED and WC_RNG_BANK_FLAG_FOR_RECOVERY are + rejected. WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED is implied: the parent + is guaranteed in-service, or an error is returned with no lease and no + child. + + \return 0 Success + \return BAD_FUNC_ARG bank or child_rng is null, or the flags are + contradictory (uncredited or recovery seeding contradict a spawn). + \return RNG_FAILURE_E No serviceable parent within the timeout budget. + + \param bank The bank to spawn from. + \param child_rng The caller-provided WC_RNG to instantiate. + \param nonce Optional additional instantiation input. + \param nonceSz Length of nonce in bytes. + \param preferred_inst_offset The preferred parent instance, or 0. + \param timeout_secs Wait budget (with _CAN_WAIT). + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* per-call flags. + + \sa wc_rng_bank_spawn_new + \sa wc_InitRngNonceRBGC +*/ +int wc_rng_bank_spawn(struct wc_rng_bank *bank, WC_RNG *child_rng, + byte *nonce, word32 nonceSz, int preferred_inst_offset, + int timeout_secs, word32 flags); + +/*! + \ingroup Random + + \brief The allocating form of wc_rng_bank_spawn(): the child is + allocated from the bank's heap and returned through child_rng; release + with wc_rng_free(). + + \return 0 Success + \return BAD_FUNC_ARG bank or child_rng is null. + \return MEMORY_E Allocation failed. + \return RNG_FAILURE_E No serviceable parent within the timeout budget. + + \param bank The bank to spawn from. + \param child_rng Receives the allocated, instantiated WC_RNG. + \param nonce Optional additional instantiation input. + \param nonceSz Length of nonce in bytes. + \param preferred_inst_offset The preferred parent instance, or 0. + \param timeout_secs Wait budget (with _CAN_WAIT). + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* per-call flags. + + \sa wc_rng_bank_spawn +*/ +int wc_rng_bank_spawn_new(struct wc_rng_bank *bank, WC_RNG **child_rng, + byte *nonce, word32 nonceSz, + int preferred_inst_offset, int timeout_secs, + word32 flags); + +/*! + \ingroup Random + + \brief Reseed every instance with caller-supplied seed material. + WC_RNG_BANK_FLAG_SEED_UNCREDITED mixes the material in without + crediting it. + + \return 0 Success + \return BAD_FUNC_ARG bank or seed is null. + \return RNG_FAILURE_E An instance could not be reseeded within + timeout_secs. + + \param bank The bank to seed. + \param seed Seed material. + \param seedSz Length of seed in bytes. + \param timeout_secs Wait budget per instance. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. + + \sa wc_rng_bank_seed_range + \sa wc_rng_bank_reseed +*/ +int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, + word32 seedSz, int timeout_secs, word32 flags); + +/*! + \ingroup Random + + \brief The range form of wc_rng_bank_seed(): seed instances first_inst + through last_inst inclusive. + + \return 0 Success + \return BAD_FUNC_ARG bank or seed is null, or the range is out of + bounds. + \return RNG_FAILURE_E An instance could not be reseeded within + timeout_secs. + \return BAD_INDEX_E The instance range is invalid. + \return BAD_STATE_E The bank is not initialized. + \return NO_DEFAULT_FOUND_E bank is null and no default bank is registered. + + \param bank The bank to seed. + \param first_inst The first instance offset. + \param last_inst The last instance offset. + \param seed Seed material. + \param seedSz Length of seed in bytes. + \param timeout_secs Wait budget per instance. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. + + \sa wc_rng_bank_seed +*/ +int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int first_inst, + int last_inst, const byte* seed, word32 seedSz, + int timeout_secs, word32 flags); + +/*! + \ingroup Random + + \brief Reseed every instance from the module's seed source. + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return RNG_FAILURE_E An instance could not be reseeded within + timeout_secs. + + \param bank The bank to reseed. + \param timeout_secs Wait budget per instance. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. + + \sa wc_rng_bank_reseed_range + \sa wc_rng_bank_seed +*/ +int wc_rng_bank_reseed(struct wc_rng_bank *bank, int timeout_secs, + word32 flags); + +/*! + \ingroup Random + + \brief The range form of wc_rng_bank_reseed(). + + \return 0 Success + \return BAD_FUNC_ARG bank is null, or the range is out of bounds. + \return RNG_FAILURE_E An instance could not be reseeded within + timeout_secs. + \return BAD_INDEX_E The instance range is invalid. + \return BAD_STATE_E The bank is not initialized. + \return WC_TIMEOUT_E The walk exceeded timeout_secs. + + \param bank The bank to reseed. + \param first_inst The first instance offset. + \param last_inst The last instance offset. + \param timeout_secs Wait budget per instance. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. + + \sa wc_rng_bank_reseed +*/ +int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, int first_inst, + int last_inst, int timeout_secs, word32 flags); + +/*! + \ingroup Random + + \brief Set the entropy-invalidated latch on every instance (see + wc_RNG_invalidate_entropy()): cached entropy products are discarded, + and each instance is forced through a credited reseed before its next + generate serves output. Lock-free and constant-time per instance; safe + from a state-invalidation event context (VM fork/resume). + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return BAD_STATE_E The bank is not initialized. + + \param bank The bank to invalidate. + \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. + + _Example_ + \code + // VM-resume notifier + (void)wc_rng_bank_invalidate_entropy(bank, WC_RNG_BANK_FLAG_NONE); + // instances recover on next checkout, or by patrol: + // wc_rng_bank_recover_inst() + \endcode + + \sa wc_RNG_invalidate_entropy + \sa wc_rng_bank_recover_inst + \details Also purges each instance's banked next-seed apertures: the + purge is the provenance guarantee that a READY bank observed after the + event holds post-event material (see wc_rng_bank_checkout()'s recovery + admissions). Walks every instance even on error, returning the first + error. flags must be 0. + +*/ +int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, word32 flags); + +/*! + \ingroup Random + + \brief Reserve the bank's daemon slot with a caller-chosen nonzero + magic word, admitting exactly one scheduling daemon per bank. The + lifecycle is strictly ordered: _reserve, then _register, then + _unregister, then _release. + + \return 0 Success + \return BAD_FUNC_ARG bank is null or magic is the free sentinel. + \return BUSY_E The slot is claimed, or claiming is contended. + + \param bank The bank to claim. + \param magic The daemon's magic word. + + \sa wc_rng_bank_daemon_register + \sa wc_rng_bank_daemon_release +*/ +int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank, + WC_ATOMIC_UINT_ARG magic); + +/*! + \ingroup Random + + \brief Register the daemon object in a slot reserved with the same + magic word. + + \return 0 Success + \return BAD_FUNC_ARG bank or daemon is null. + \return ALREADY_E The slot is already in the requested state. + \return WRONG_TYPE_OBJECT_E magic does not match the claim. + \return BUSY_E The slot transition is contended; retry. + + \param bank The bank. + \param daemon The daemon object to register. + \param magic The daemon's magic word. + + \sa wc_rng_bank_daemon_reserve + \sa wc_rng_bank_daemon_unregister +*/ +int wc_rng_bank_daemon_register(struct wc_rng_bank *bank, void *daemon, + WC_ATOMIC_UINT_ARG magic); + +/*! + \ingroup Random + + \brief Unregister the daemon object, returning it through daemon. + + \return 0 Success + \return BAD_FUNC_ARG bank or daemon is null. + \return ALREADY_E The slot is already in the requested state. + \return WRONG_TYPE_OBJECT_E magic does not match the claim. + \return BUSY_E The slot transition is contended; retry. + + \param bank The bank. + \param daemon Receives the registered daemon object. + \param magic The daemon's magic word. + + \sa wc_rng_bank_daemon_register + \sa wc_rng_bank_daemon_release +*/ +int wc_rng_bank_daemon_unregister(struct wc_rng_bank *bank, void **daemon, + WC_ATOMIC_UINT_ARG magic); + +/*! + \ingroup Random + + \brief Release the daemon slot claimed with magic, returning it to the + free sentinel. + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return ALREADY_E The slot is already in the requested state. + \return WRONG_TYPE_OBJECT_E magic does not match the claim. + \return BUSY_E The slot transition is contended; retry. + + \param bank The bank. + \param magic The daemon's magic word. + + \sa wc_rng_bank_daemon_reserve +*/ +int wc_rng_bank_daemon_release(struct wc_rng_bank *bank, + WC_ATOMIC_UINT_ARG magic); + +/*! + \ingroup Random + + \brief Bind the daemon's RBG-chain root to the bank, for chain-sourced + banking (wc_rng_bank_next_seed_generate_rbgc()) and harvest deposit + (wc_RNG_DRBG_NextUncreditedSeedStore() on the root). + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + + \param bank The bank. + \param daemon_root The daemon's root WC_RNG, or null to unbind. + + \sa wc_rng_bank_daemon_root_get + \sa wc_rng_bank_daemon_reserve + \details The caller (the daemon) owns the ordering: bind after + successful root initialization, unbind before root teardown. + +*/ +int wc_rng_bank_daemon_root_set(struct wc_rng_bank *bank, + WC_RNG *daemon_root); + +/*! + \ingroup Random + + \brief Report the bank's bound daemon root. + + \return The daemon root, or null when none is bound or bank is null. + + \param bank The bank to interrogate. + + \sa wc_rng_bank_daemon_root_set +*/ +WC_RNG *wc_rng_bank_daemon_root_get(struct wc_rng_bank *bank); + +/*! + \ingroup Random + + \brief Register a callback fired by wc_rng_bank_fini() once its + refcount and leak gates pass -- i.e. once teardown is committed -- for + external registries that must drop their reference when the bank dies. + One-shot: the hook is cleared before firing. A null free_hook + unregisters. + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + + \param bank The bank to hook. + \param free_hook The callback. + \param arg Opaque argument passed to the callback. + + \sa wc_rng_bank_fini + \sa wc_RNG_register_free_hook +*/ +int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, + wc_rng_bank_free_hook_cb_t free_hook, + void *arg); + +/*! + \ingroup Random + + \brief Initialize rng as a bank reference: a WC_RNG with no DRBG of its + own, whose wc_RNG_GenerateBlock() transparently checks an instance out + of bank, generates, and checks it back in. Release with wc_FreeRng(). + + \return 0 Success + \return BAD_FUNC_ARG bank or rng is null. + + \param bank The bank to reference. + \param rng The WC_RNG to initialize as a reference. + + _Example_ + \code + WC_RNG rng; + if (wc_InitRng_BankRef(bank, &rng) == 0) { + // rng now serves through the bank + ret = wc_RNG_GenerateBlock(&rng, out, sizeof(out)); + wc_FreeRng(&rng); + } + \endcode + + \sa wc_BankRef_Release + \sa wc_rng_new_bankref +*/ +int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng); + +/*! + \ingroup Random + + \brief Release a bank reference. wc_FreeRng() calls this + automatically for bank references; direct use is rarely needed. + + \return 0 Success + \return BAD_FUNC_ARG rng is null or is not a bank reference. + + \param rng The bank reference to release. + + \sa wc_InitRng_BankRef +*/ +int wc_BankRef_Release(WC_RNG *rng); + +/*! + \ingroup Random + + \brief The allocating form of wc_InitRng_BankRef(): the reference is + allocated from the bank's heap; release with wc_rng_free(). + + \return 0 Success + \return BAD_FUNC_ARG bank or rng is null. + \return MEMORY_E Allocation failed. + + \param bank The bank to reference. + \param rng Receives the allocated bank reference. + + \sa wc_InitRng_BankRef +*/ +int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); + +/*! + \ingroup Random + + \brief Snapshot the RNG debug counters (WC_RNG_DEBUG_STATS) with + bank-level context. + + \return 0 Success + \return BAD_FUNC_ARG s is null. + + \param s Receives the snapshot. + \param bank Optional bank for context, or null. + + \sa wc_rng_debug_stats_snap +*/ +int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, + struct wc_rng_bank *bank); diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index a960b3dfebc..659ee9d5938 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -45,94 +45,27 @@ #define WC_RNG_BANK_DAEMON_MAGIC_FREE 0U #endif -#define WC_RNG_BANK_FLAG_NONE 0 -#define WC_RNG_BANK_FLAG_INITED (1U << 0) -#define WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST (1U << 1) -#define WC_RNG_BANK_FLAG_CAN_WAIT (1U << 2) -#define WC_RNG_BANK_FLAG_NO_VECTOR_OPS (1U << 3) -#define WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST (1U << 4) -#define WC_RNG_BANK_FLAG_AFFINITY_LOCK (1U << 5) -/* WC_RNG_BANK_FLAG_SEED_UNCREDITED applies only to wc_rng_bank_seed(): the - * supplied seed material is mixed into each instance without entropy credit - * (wc_RNG_DRBG_Reseed_Uncredited()), leaving the reseed schedule governed - * solely by the module's own seed source. */ -#define WC_RNG_BANK_FLAG_SEED_UNCREDITED (1U << 6) -/* WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED applies only to wc_rng_bank_checkout(): - * if the checked-out instance has a ready banked next seed (see - * wc_RNG_DRBG_NextSeedGenerate() et al.), consume it in an immediate, - * source-free credited reseed before returning the instance; a no-op when - * no bank is ready or the build/instance has no next-seed support. Safe in - * atomic context. */ -#define WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED (1U << 7) -/* WC_RNG_BANK_FLAG_FOR_RECOVERY declares a recovery-intent checkout of a - * specific instance (e.g. by a reseed-and-recovery daemon's patrol): - * out-of-service status is expected and accepted, and the - * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED arm is suppressed (a consume would - * fail on exactly the instances recovery targets). Requires an explicit - * instance: rejected in combination with _CAN_FAIL_OVER_INST or - * _PREFER_AFFINITY_INST, and by the seed/reseed walkers. Note that a - * targeted (non-failover) checkout admits out-of-service instances with or - * without this flag; the flag makes the intent explicit and - * interaction-safe. */ -#define WC_RNG_BANK_FLAG_FOR_RECOVERY (1U << 8) -/* WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY admits the caller to a quarantined - * (WC_RNG_LOCK_ENTROPY_INVALIDATED) instance when no cheaper admission - * applies, accepting the recovery obligation: wc_rng_bank_checkout() may - * then return NEEDS_RECOVERY_E with the checkout otherwise complete -- - * *rng_inst set, instance lock (and any affinity/vector-inhibit state) - * HELD. The caller owns the lease and must either recover the instance - * (a credited reseed, e.g. wc_RNG_DRBG_Reseed_Now(), clears the - * quarantine) or check it back in. Ordinary consumers that cannot - * complete a recovery must not pass this flag. */ -#define WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY (1U << 9) -/* WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED guarantees that - * wc_rng_bank_checkout() (and APIs built on it, e.g. wc_rng_bank_spawn()) - * either returns a lease on an in-service instance (status WC_DRBG_OK) or - * returns an error with NO lease held -- never a lease on an out-of-service - * instance. This closes the two paths that can otherwise lease one: a - * targeted (non-failover) checkout, and a failover checkout after a full - * unsuccessful lap (the anti-livelock disarm). Under _CAN_WAIT, an - * out-of-service instance is retried within the timeout budget (allowing a - * recovery patrol to restore it) before the error is returned; the - * distinguished error for a lap or wait that found only out-of-service - * instances is BAD_STATE_E. Contradicts, and is rejected with, - * _FOR_RECOVERY. Applies to instance status only; reseed-due diversion - * semantics are unchanged. */ -#define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1U << 10) -/* WC_RNG_BANK_FLAG_QUIET suppresses the facility's WC_VERBOSE_RNG - * operational warnings -- expected-condition notices such as the - * reseed-due-instance handout, reinit retry/timeout reports, the - * all-instances-busy notice, and the seed-walker's out-of-service reports - * -- so that deliberate exercising (e.g. unit tests) doesn't spam the - * log. A bank-level flag only, set at wc_rng_bank_init(); it has no - * per-call meaning and never suppresses refcount/consistency - * diagnostics. */ -#define WC_RNG_BANK_FLAG_QUIET (1U << 11) -/* WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (bank-level, set at - * wc_rng_bank_init()) declares that the bank's lifetime is guaranteed by - * its container to enclose all checkouts (e.g. a bank embedded in a - * kernel crypto tfm context, torn down only after the API has quiesced - * callers). Per-checkout refcount traffic -- the one bank-global RMW - * pair on the readout hot path -- is suppressed; refcount checks degrade - * to read-only validity tests. The refcount itself remains, serving its - * standing roles: the INITED baseline, default-bank registration - * (wc_rng_bank_default_set()), and per-bankref lifetime references. - * Contract: with this flag, a wc_rng_bank_fini() racing live checkouts is - * a use-after-free instead of BUSY_E -- only containers whose teardown - * provably quiesces consumers first may set it. */ -#define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 12) -#define WC_RNG_BANK_FLAG_INIT_RBGC (1U << 13) -#define WC_RNG_BANK_FLAG_DEFAULT_BANK (1U << 14) -#define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 15) -/* wc_rng_bank_spawn[_new]() only: the child is born with - * WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED. */ +#define WC_RNG_BANK_FLAG_NONE 0 +#define WC_RNG_BANK_FLAG_INITED (1U << 0) +#define WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST (1U << 1) +#define WC_RNG_BANK_FLAG_CAN_WAIT (1U << 2) +#define WC_RNG_BANK_FLAG_NO_VECTOR_OPS (1U << 3) +#define WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST (1U << 4) +#define WC_RNG_BANK_FLAG_AFFINITY_LOCK (1U << 5) +#define WC_RNG_BANK_FLAG_SEED_UNCREDITED (1U << 6) +#define WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED (1U << 7) +#define WC_RNG_BANK_FLAG_FOR_RECOVERY (1U << 8) +#define WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY (1U << 9) +#define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1U << 10) +#define WC_RNG_BANK_FLAG_QUIET (1U << 11) +#define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 12) +#define WC_RNG_BANK_FLAG_INIT_RBGC (1U << 13) +#define WC_RNG_BANK_FLAG_DEFAULT_BANK (1U << 14) +#define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 15) #define WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE (1U << 16) -/* base lock states are WC_RNG_LOCK_FREE / WC_RNG_LOCK_HELD in random.h; - * these annotation bits ride above WC_RNG_LOCK_HELD via - * wc_RNG_lock_get()/_set_extra()/_clear_extra(). */ - #ifndef WC_RNG_HAVE_LOCK + /* Definitions for backward-compat / WC_RNG_NO_LOCK */ #define WC_RNG_LOCK_FREE 0 #define WC_RNG_LOCK_HELD (1U<<0) #define WC_RNG_LOCK_REQUIRED (1U<<1) @@ -147,6 +80,9 @@ #endif #endif +/* When WC_RNG_HAVE_LOCK, base lock states are in random.h and the lock word + * itself is in WC_RNG.lock; these annotation bits ride above the base bits via + * wc_RNG_lock_get() / _set_extra() / _clear_extra(). */ #define WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED (1U<<(WC_RNG_LOCK_EXTRA_SHIFT+0)) #define WC_RNG_BANK_INST_LOCK_VEC_OPS_INH (1U<<(WC_RNG_LOCK_EXTRA_SHIFT+1)) @@ -164,9 +100,8 @@ typedef int (*wc_rng_bank_free_hook_cb_t)(const struct wc_rng_bank *bank, struct wc_rng_bank_inst { #ifdef WC_RNG_HAVE_LOCK - /* the exclusivity latch lives in rng.lock (wc_RNG_lock_*()) -- - * in-FIPS-boundary, module-enforced. This struct persists for the - * parent pointer and future bank-side slots. */ + /* the exclusivity latch lives in WC_RNG.lock (wc_RNG_lock_*()) -- + * in-FIPS-boundary, module-enforced. */ #else #ifdef WOLFSSL_NO_ATOMICS word32 lock; @@ -192,8 +127,6 @@ struct wc_rng_bank { wolfSSL_Ref refcount; void *heap; word32 flags; - /* fired by wc_rng_bank_fini() after its gates pass, before teardown - * (one-shot); see wc_rng_bank_register_free_hook(). */ wc_rng_bank_free_hook_cb_t free_hook; void *free_hook_arg; wc_affinity_lock_fn_t affinity_lock_cb; @@ -203,19 +136,12 @@ struct wc_rng_bank { int n_rngs; int first_failover_inst; #ifdef WC_RNG_HAVE_NEXT_SEED - /* Serializes whole-instance operations (wc_rng_bank_inst_reinit()'s - * free/reinstantiate cycle) against the entropy daemon's lockless - * banking calls (wc_rng_bank_next_seed_generate()). 0 = free, - * WC_RNG_BANK_INST_OP_DAEMON = daemon banking in progress, - * WC_RNG_BANK_INST_OP_REINIT = reinit in progress. Lease-holders - * never consult it: instance-lock exclusion already covers every - * lease-holder <-> reinit and lease-holder <-> consume interaction. */ wolfSSL_Atomic_Int inst_op_gate; #endif #ifdef WC_RNG_BANK_STATIC struct wc_rng_bank_inst rngs[WC_RNG_BANK_STATIC_SIZE]; #else - struct wc_rng_bank_inst *rngs; /* typically one per CPU ID, plus a few */ + struct wc_rng_bank_inst *rngs; #endif #ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT wolfSSL_Atomic_Uint daemon_magic; @@ -351,18 +277,6 @@ WOLFSSL_API int wc_rng_bank_inst_checkin( struct wc_rng_bank_inst **rng_inst); #ifdef WC_RNG_HAVE_NEXT_SEED -/* Daemon entry point for banking next-seed material: resolves the instance - * at inst_offset and calls wc_RNG_DRBG_NextSeedGenerate(rng, n) under the - * bank's whole-instance-operation gate, so a concurrent - * wc_rng_bank_inst_reinit() can never free the DRBG out from under the - * gather. Returns BUSY_E (skip this turn) when the gate is held by a - * reinit; ALREADY_E when the instance's bank is already complete (sleep - * until consumed); MISSING_RNG_E when the instance has no DRBG (RDRAND - * et al.) and can be retired from the banking rotation permanently. - * Other errors are transient gather/health-test failures: skip the turn - * and alarm if persistent. The caller must hold a bank reference (e.g. - * per the daemon association) for the duration of the call. - */ WOLFSSL_API int wc_rng_bank_next_seed_generate( struct wc_rng_bank *bank, int inst_offset, @@ -380,15 +294,6 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( int timeout_secs, word32 flags); -/* Patrol helper: check out the instance at inst_offset with - * WC_RNG_BANK_FLAG_FOR_RECOVERY, reinitialize it iff it is out of service, - * and check it back in. A healthy instance is a success no-op, so callers - * can invoke this unconditionally on a status observed locklessly (a stale - * observation costs one harmless round trip). Returns BUSY_E when the - * instance lock or the whole-instance-operation gate is contended -- retry - * on a later patrol turn. flags may include WC_RNG_BANK_FLAG_CAN_WAIT and - * WC_RNG_BANK_FLAG_AFFINITY_LOCK, which are passed through; bank must be - * non-NULL. */ WOLFSSL_API int wc_rng_bank_recover_inst( struct wc_rng_bank *bank, int inst_offset, @@ -397,20 +302,6 @@ WOLFSSL_API int wc_rng_bank_recover_inst( #ifdef WC_RNG_HAVE_RBGC -/* Spawn an SP 800-90C chain RNG from a bank instance: check out a parent - * instance (honoring the usual selection flags), wc_InitRngNonceRBGC() / - * wc_InitRngNonceRBGC_New() the child from it, and check the parent instance - * back in. The child's lifetime is thereafter decoupled from the parent and - * its bank: it is lock-free for its owner and is released with wc_FreeRng() - * (stack form) or wc_rng_free() (heap form). The child's RBGC stratum is one - * plus the parent's stratum at time of instantiation. nonce/nonceSz may be - * NULL/0 for a plain spawn; an example of a recommended nonce is Linux kernel - * random_get_entropy() (which is typically a racy read of a high-resolution - * timer). bank == NULL uses the default bank where support is compiled in. - * WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED composes (banked reseed before the spawn - * draw); WC_RNG_BANK_FLAG_SEED_UNCREDITED and WC_RNG_BANK_FLAG_FOR_RECOVERY are - * rejected. WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED is implied: the parent is - * guaranteed in-service, or an error is returned with no lease and no child. */ WOLFSSL_API int wc_rng_bank_spawn( struct wc_rng_bank *bank, WC_RNG *child_rng, @@ -455,30 +346,17 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, int timeout_secs, word32 flags); -/* Set WC_RNG_LOCK_ENTROPY_INVALIDATED on every instance (see - * wc_RNG_invalidate_entropy()): cached entropy products are discarded, and - * each instance is forced through a credited reseed before its next - * generate serves output. Lock-free and constant-time per instance; safe - * from the state-invalidation event context. Walks every instance even on - * error, returning the first error. flags must be 0. */ WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, word32 flags); #endif /* HAVE_HASHDRBG */ #ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT -/* Publish (or, with NULL, retract) the daemon's private root DRBG for the - * state-invalidation handler. Caller (the daemon) owns the ordering: - * publish after successful init, retract before teardown. */ WOLFSSL_API int wc_rng_bank_daemon_root_set(struct wc_rng_bank *bank, WC_RNG *daemon_root); WOLFSSL_API WC_RNG *wc_rng_bank_daemon_root_get(struct wc_rng_bank *bank); #endif -/* Register a callback fired by wc_rng_bank_fini() once its refcount and - * leak gates pass -- i.e. once teardown is committed -- e.g. to unlink the - * bank from an external registry. One-shot: cleared before firing. A - * NULL free_hook unregisters. */ WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, wc_rng_bank_free_hook_cb_t free_hook, void *arg); From 2266e7d1a6dad99d27a727b1986f86b145d12f43 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 17:55:06 -0500 Subject: [PATCH 047/102] .github/workflows/codespell.yml: add "checkin" to ignore_words_list. --- .github/workflows/codespell.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 7ff31f705ce..82e80658d40 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -27,7 +27,7 @@ jobs: check_filenames: true check_hidden: true # Add comma separated list of words that occur multiple times that should be ignored (sorted alphabetically, case sensitive) - ignore_words_list: adin,ameba,aNULL,brunch,carryIn,chainG,ciph,cLen,cliKs,cna,dout,FPR,fpr,haveA,inCreated,inOut,inout,larg,LEAPYEAR,Merget,optionA,parm,parms,repid,rIn,userA,ser,siz,te,Te,HSI,failT,toLen,vor, + ignore_words_list: adin,ameba,aNULL,brunch,carryIn,chainG,ciph,cLen,cliKs,cna,dout,FPR,fpr,haveA,inCreated,inOut,inout,larg,LEAPYEAR,Merget,optionA,parm,parms,repid,rIn,userA,ser,siz,te,Te,HSI,failT,toLen,vor,checkin # The exclude_file contains lines of code that should be ignored. This is useful for individual lines which have non-words that can safely be ignored. exclude_file: '.codespellexcludelines' # To skip files entirely from being processed, add it to the following list: From a61e91f261baac370a1480ffab43238edcf390db Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 17:57:18 -0500 Subject: [PATCH 048/102] doc/dox_comments/header_files/random.h: clean up duplicated clauses in wc_RNG_lock_get() docs. --- doc/dox_comments/header_files/random.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index eb665fb7046..8e00f0ab595 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -1389,16 +1389,10 @@ int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng); \return 0 Success \return BAD_FUNC_ARG rng is null. - \return NEEDS_RECOVERY_E The instance's entropy is invalidated; recover - before use. \return BUSY_E The lock is held. - \return NEEDS_RECOVERY_E The instance is entropy-invalidated (see - wc_RNG_invalidate_entropy()); recover by credited reseed. - \return BAD_MUTEX_E (WC_RNG_HAVE_LOCK_FULL_MUTEX) The outer mutex failed. - \return UNEXPECTED_STATE_E Spurious acquisition failure; retry. - \return BUSY_E The lock is held. - \return NEEDS_RECOVERY_E The instance is entropy-invalidated (see - wc_RNG_invalidate_entropy()); recover by credited reseed. + \return NEEDS_RECOVERY_E The instance's entropy is invalidated (see + wc_RNG_invalidate_entropy()); recover with a credited reseed before + use. \return BAD_MUTEX_E (WC_RNG_HAVE_LOCK_FULL_MUTEX) The outer mutex failed. \return UNEXPECTED_STATE_E Spurious acquisition failure; retry. From 8a1d890f1da232fdbaa2089ad6e168fad5c1a8ec Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 18:06:48 -0500 Subject: [PATCH 049/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h: implement producer mutex on the credited nextSeed aperture, with assiduous observation of invalidation, making provenance guarantees firm. Producers claim by CAS (PRODUCING); invalidation repaints in-flight claims PURGED, so pre-event material never publishes. Contention and purge-discard return traced BUSY_E; seed buffers are zeroized only at consumption, under CONSUMING. --- wolfcrypt/src/random.c | 197 ++++++++++++++++++++++++++++++------- wolfssl/wolfcrypt/random.h | 6 +- 2 files changed, 164 insertions(+), 39 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 620a3c1731d..3b8de5538cd 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -789,6 +789,60 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, * site carries its own #ifdef WC_RNG_DEBUG_STATS gate so the facility is * removable outright with unifdef. */ +#ifdef WC_RNG_HAVE_NEXT_SEED +/* Purge the credited next-seed aperture. A plain store would race an + * in-flight producer: the producer's publish must lose against a purge, + * never the reverse, or material generated before a state-invalidation + * event could surface READY after it -- exactly the resurrection the + * provenance guarantee forbids. A producer mid-fill (PRODUCING) owns + * the buffer, so the purge only repaints the sentinel (PRODUCING -> + * PURGED); the producer's failed publish-CAS observes the repaint and + * reopens the aperture EMPTY (see NextSeedProducerRelease()) -- the + * sentinel alone suppresses the pre-event material; seed-aperture + * buffers are never zeroized outside consumption. All other states + * purge directly to EMPTY. (The uncredited stir aperture keeps its + * plain-store purge: + * stirs carry no claims, so resurrection there is benign by the + * three-no-ops doctrine.) */ +static void NextSeedPurge(wolfSSL_Atomic_Int *lenp) +{ + WC_ATOMIC_INT_ARG cur = WOLFSSL_ATOMIC_LOAD(*lenp); + for (;;) { + WC_ATOMIC_INT_ARG want; + if (cur == WC_DRBG_NEXT_SEED_PURGED) + return; /* already handed off to a producer's unwind. */ + want = (cur == WC_DRBG_NEXT_SEED_PRODUCING) ? + WC_DRBG_NEXT_SEED_PURGED : WC_DRBG_NEXT_SEED_EMPTY; + if (wolfSSL_Atomic_Int_CompareExchange(lenp, &cur, want)) + return; + /* cur was reloaded by the failed exchange; re-evaluate. */ + } +} + +/* Release a producer claim (PRODUCING) on the credited aperture, + * installing val (a fill offset, the full length, READY, or EMPTY). + * Returns 0 on release, else BUSY_E: the release CAS can fail for + * exactly one reason -- a concurrent NextSeedPurge() repainted the + * claim PURGED (producers cannot claim a PRODUCING word, consumers + * only exchange from READY, and the purge is the sole writer against + * a claim). The producer's material then predates the invalidation + * event and must not surface: the aperture reopens EMPTY, which is + * the whole suppression -- an EMPTY aperture is never consumed, and + * the next fill overwrites from offset zero. The buffer is never + * zeroized (house rule for the seed apertures): zeroization buys + * nothing here -- suppression is the sentinel's job, and a + * fork-sibling clone holds the same bytes regardless. */ +static int NextSeedProducerRelease(wolfSSL_Atomic_Int *lenp, + WC_ATOMIC_INT_ARG val) +{ + WC_ATOMIC_INT_ARG expected = WC_DRBG_NEXT_SEED_PRODUCING; + if (wolfSSL_Atomic_Int_CompareExchange(lenp, &expected, val)) + return 0; + WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); + return BUSY_E; +} +#endif /* WC_RNG_HAVE_NEXT_SEED */ + static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, const byte* additional, word32 additionalSz, int credited) @@ -835,7 +889,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_NEXT_SEED) if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { - WOLFSSL_ATOMIC_STORE(drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + NextSeedPurge(&drbg->nextSeedLen); /* the uncredited stir aperture is purged too, for provenance * uniformity; best-effort (an in-flight depositor may * resurrect a partial fill -- benign, stirs carry no @@ -880,7 +934,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_NEXT_SEED) if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { - WOLFSSL_ATOMIC_STORE(drbg512->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); + NextSeedPurge(&drbg512->nextSeedLen); /* see the SHA-256 arm re best-effort and no-zeroize. */ WOLFSSL_ATOMIC_STORE(drbg512->nextUncreditedSeedLen, WC_DRBG_NEXT_SEED_EMPTY); @@ -3266,16 +3320,14 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { #ifdef WC_RNG_HAVE_NEXT_SEED #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextSeedLen, - WC_DRBG_NEXT_SEED_EMPTY); + NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { - WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen, - WC_DRBG_NEXT_SEED_EMPTY); + NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen, WC_DRBG_NEXT_SEED_EMPTY); } @@ -4022,10 +4074,9 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, return ret; } - cur = *lenp; - if ((cur < 0) || (cur >= (WC_ATOMIC_INT_ARG)nextSeedSz)) { - - if (nonce) { + cur = WOLFSSL_ATOMIC_LOAD(*lenp); + if (nonce) { + if ((cur < 0) || (cur >= (WC_ATOMIC_INT_ARG)nextSeedSz)) { /* The accumulator is full (READY) or being consumed: fold the * arriving entropy in rather than discarding it. The sentinel * check is deliberately advisory in this lane -- a torn read by a @@ -4037,14 +4088,44 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, xorbuf(seed, nonce, n); return 0; } - - if (cur != (WC_ATOMIC_INT_ARG)nextSeedSz) { - /* Ready, consuming, or other sentinel -- nothing to do. */ - return WC_NO_ERR_TRACE(ALREADY_E); /* not an error */ + } + else { + /* Producer claim: exactly one banker may fill or publish at a + * time. Concurrent fills would be tolerable as BYTES (a torn + * mix is still entropy) but poisonous as CLAIMS: interleaved + * primary/RBGC production could publish material under the + * wrong stratum or health-test disposition. The claim makes + * produce-side exclusivity a CAS, matching the consume side's + * READY -> CONSUMING claim. This lane only: the uncredited + * accumulator above stays multi-writer by design. */ + for (;;) { + if ((cur == WC_DRBG_NEXT_SEED_PRODUCING) || + (cur == WC_DRBG_NEXT_SEED_PURGED)) + { + /* A producer is in flight (or unwinding a purge): + * retryable on a later banking cycle. Traced: the + * competing producer -- typically the entropy daemon + * -- is the diagnosis a surprised caller needs. */ + return BUSY_E; + } + if (cur < 0) { + /* Ready or consuming -- nothing to do. */ + return WC_NO_ERR_TRACE(ALREADY_E); /* not an error */ + } + if (wolfSSL_Atomic_Int_CompareExchange(lenp, &cur, + WC_DRBG_NEXT_SEED_PRODUCING)) + break; + /* cur was reloaded by the failed exchange; re-evaluate. */ + } + if (cur == (WC_ATOMIC_INT_ARG)nextSeedSz) { + /* Complete but unpublished (interrupted between fill + * completion and publication): retry the health test and + * publication below. */ + n = 0; } - /* Complete but unpublished (interrupted between fill completion and - * publication): retry the health test and publication below. */ - n = 0; + /* From here to release/publication the aperture word is + * PRODUCING; fill progress lives only in the local cur, and + * every exit passes through NextSeedProducerRelease(). */ } if (n > 0) { @@ -4057,18 +4138,21 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, #ifdef WC_RNG_HAVE_RBGC if (root) { /* If primary seed bytes were carried forward, reset now to avoid - * wc_RNG_TestSeed() at completion. */ - if ((cur > 0) && (*nextSeedRBGCStratum_p == 0)) { + * wc_RNG_TestSeed() at completion. Local only: the word is + * held at PRODUCING. */ + if ((cur > 0) && (*nextSeedRBGCStratum_p == 0)) cur = 0; - WOLFSSL_ATOMIC_STORE(*lenp, cur); - } if (n > nextSeedSz - (word32)cur) n = nextSeedSz - (word32)cur; ret = wc_RNG_GenerateBlock(root, seed + cur, n); if (ret != 0) { - /* Partial bank preserved -- retry on a later cycle. */ + /* Partial bank preserved -- retry on a later cycle. + * (If a purge landed meanwhile, the release discards + * instead; the draw failure is the more informative + * code and wins over the release's BUSY_E.) */ + (void)NextSeedProducerRelease(lenp, cur); return ret; } @@ -4099,11 +4183,10 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, #ifdef WC_RNG_HAVE_RBGC /* If RBGC seed bytes were carried forward, reset now to avoid - * intermixture and force wc_RNG_TestSeed() at completion. */ - if ((cur > 0) && (*nextSeedRBGCStratum_p > 0)) { + * intermixture and force wc_RNG_TestSeed() at completion. + * Local only: the word is held at PRODUCING. */ + if ((cur > 0) && (*nextSeedRBGCStratum_p > 0)) cur = 0; - WOLFSSL_ATOMIC_STORE(*lenp, cur); - } #endif if (n > nextSeedSz - (word32)cur) @@ -4111,7 +4194,12 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, ret = wc_GenerateSeed(&os, seed + cur, n); if (ret != 0) { - /* Partial bank preserved -- retry on a later cycle. */ + /* Partial bank preserved -- retry on a later cycle. + * (If a purge landed meanwhile, the release discards + * instead; the seed-gather failure is the more + * informative code and wins over the release's + * BUSY_E.) */ + (void)NextSeedProducerRelease(lenp, cur); return ret; } @@ -4121,7 +4209,17 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, } cur += (int)n; - WOLFSSL_ATOMIC_STORE(*lenp, cur); + if (nonce != NULL) + WOLFSSL_ATOMIC_STORE(*lenp, cur); + } + + if (nonce == NULL && cur < (WC_ATOMIC_INT_ARG)nextSeedSz) { + /* Partial credited fill this call: release the claim back to + * the fill offset for a later cycle to resume. A purge + * meanwhile discards instead, and the release's BUSY_E + * percolates -- returning 0 would claim banked progress the + * purge just evaporated. */ + return NextSeedProducerRelease(lenp, cur); } if (cur == (WC_ATOMIC_INT_ARG)nextSeedSz) { @@ -4136,7 +4234,12 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, /* If RBGC bytes were used for the reseed, then we can skip * wc_RNG_TestSeed(). */ if (*nextSeedRBGCStratum_p > 0) { - WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + ret = NextSeedProducerRelease(lenp, WC_DRBG_NEXT_SEED_READY); + if (ret != 0) { + /* Purged while producing (BUSY_E): nothing banked; + * post-event material wanted. Retryable. */ + return ret; + } #ifdef WC_RNG_DEBUG_STATS ++rng->_stats_n_nextseed_banked; #endif @@ -4147,7 +4250,12 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, * that wc_RNG_DRBG_NextSeedNow() is pure computation. */ ret = wc_RNG_TestSeed(seed, nextSeedSz); if (ret == 0) { - WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); + ret = NextSeedProducerRelease(lenp, WC_DRBG_NEXT_SEED_READY); + if (ret != 0) { + /* Purged while producing (BUSY_E): nothing banked; + * post-event material wanted. Retryable. */ + return ret; + } #ifdef WC_RNG_DEBUG_STATS ++rng->_stats_n_nextseed_banked; #endif @@ -4155,24 +4263,39 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, } else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) { /* wc_RNG_TestSeed() did nothing with the data -- not - * dispositive. */ + * dispositive. Release complete-but-unpublished for a + * later retry; a purge-discard's BUSY_E percolates (the + * retry cause is then the purge, not the test). */ + ret = NextSeedProducerRelease(lenp, + (WC_ATOMIC_INT_ARG)nextSeedSz); + if (ret != 0) + return ret; return NOT_READY_E; } else if ((ret == WC_NO_ERR_TRACE(ENTROPY_RT_E)) || (ret == WC_NO_ERR_TRACE(ENTROPY_APT_E))) { - /* Use-once: a failed test consumes the material. Release - * store: the ForceZero() must be visible before the empty - * aperture is. */ + /* Use-once: a failed test consumes the material. */ #ifdef WC_RNG_DEBUG_STATS ++rng->_stats_seed_failures; #endif - ForceZero(seed, nextSeedSz); - WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); + /* Use-once on a failed test is enforced by the sentinel + * alone: an EMPTY aperture is never consumed, and the next + * fill overwrites from offset zero. The buffer is never + * zeroized (house rule for the seed apertures). Burn and + * purge-discard converge on EMPTY; the release handles + * both, and the health-test failure is the more + * informative code and wins over the release's BUSY_E. */ + (void)NextSeedProducerRelease(lenp, WC_DRBG_NEXT_SEED_EMPTY); return ret; } else { - /* Buggy or brokey */ + /* Buggy or brokey. Release complete-but-unpublished; the + * material is untested but intact, and a later cycle + * re-adjudicates. The primary error is the more + * informative code and wins over a purge-discard's + * BUSY_E. */ + (void)NextSeedProducerRelease(lenp, (WC_ATOMIC_INT_ARG)nextSeedSz); return ret; } } diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index b9914b22290..7a137680edc 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -932,8 +932,10 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); #define WC_DRBG_NEXT_SEED_EMPTY 0 /* All sentinel states are negative; non-negative values are banked byte * counts. */ - #define WC_DRBG_NEXT_SEED_READY ((WC_ATOMIC_INT_ARG)(-2)) - #define WC_DRBG_NEXT_SEED_CONSUMING ((WC_ATOMIC_INT_ARG)(-1)) + #define WC_DRBG_NEXT_SEED_PRODUCING (-1) + #define WC_DRBG_NEXT_SEED_READY (-2) + #define WC_DRBG_NEXT_SEED_CONSUMING (-3) + #define WC_DRBG_NEXT_SEED_PURGED (-4) WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n); #ifdef WC_RNG_HAVE_RBGC From 2e4bf5f3f0ee622b6d380335c378270ee3118413 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 10 Sep 2026 22:50:21 -0500 Subject: [PATCH 050/102] RNG-extras fixes from CI results: configure.ac: add --enable-rng-extras aka -DWC_RNG_EXTRAS, default off unless KERNEL_MODE_DEFAULTS, and add it to enable-all-crypto and FIPS v7 setup. wolfssl/wolfcrypt/random.h: refactor setup for RNG extras to default off unless defined(WC_RNG_EXTRAS) or the specific WC_RNG_WANT_foo is defined. wolfcrypt/src/random.c: add missing WC_RNG_HAVE_LOCK gate around NextSeedPurge(). .wolfssl_known_macro_extras: add WC_RNG_WANT_* wolfcrypt/test/test.c: add !HAVE_SELFTEST gate around rng_drbg_svc_test(). wolfssl/wolfcrypt/types.h: if WOLFSSL_NO_MALLOC and !WOLFSSL_STATIC_MEMORY, make sure WC_NO_CONSTRUCTORS is defined. --- .wolfssl_known_macro_extras | 6 +++ configure.ac | 20 ++++++++- wolfcrypt/src/random.c | 2 + wolfcrypt/test/test.c | 6 +-- wolfssl/wolfcrypt/random.h | 90 ++++++++++++++++++------------------- wolfssl/wolfcrypt/types.h | 5 +++ 6 files changed, 79 insertions(+), 50 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 997c5b93bc0..1416883e373 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -813,6 +813,12 @@ WC_RNG_NO_NEXT_SEED WC_RNG_NO_POOL WC_RNG_NO_RBGC WC_RNG_NO_RBGC_RESEED +WC_RNG_WANT_FREE_HOOK +WC_RNG_WANT_LOCK +WC_RNG_WANT_LOCK_FULL_MUTEX +WC_RNG_WANT_NEXT_SEED +WC_RNG_WANT_POOL +WC_RNG_WANT_RBGC WC_RSA_NONBLOCK_TIME WC_RSA_NO_FERMAT_CHECK WC_RTL8735B_NO_DERIVE_CACHE diff --git a/configure.ac b/configure.ac index 0028f10ba0b..45a7cf82342 100644 --- a/configure.ac +++ b/configure.ac @@ -1700,6 +1700,7 @@ then test "$enable_base16" = "" && enable_base16=yes test "$enable_ssh" = "" && test "$enable_hmac" != "no" && enable_ssh=yes test "$enable_rng_bank" = "" && enable_rng_bank=yes + test "$enable_rng_extras" = "" && enable_rng_extras=yes test "$enable_dh" = "" && enable_dh=yes test "$enable_defaultdhparams" = "" && enable_defaultdhparams=yes @@ -1917,6 +1918,8 @@ then test "$enable_supportedcurves" = "" && enable_supportedcurves=yes fi test "$enable_rng" != "no" && test "$enable_rng_bank" = "" && enable_rng_bank=yes + test "$enable_rng" != "no" && test "$enable_rng_extras" = "" && enable_rng_extras=yes + if test "$ENABLED_FIPS" = "no" || test "$HAVE_FIPS_VERSION" -ge 6 then test "$enable_aes" != "no" && test "$enable_aescfb" = "" && enable_aescfb=yes @@ -2788,6 +2791,20 @@ then AM_CFLAGS="$AM_CFLAGS -DWC_NO_RNG" fi + +# RNG extras +AC_ARG_ENABLE([rng-extras], + [AS_HELP_STRING([--enable-rng-extras],[Enable RNG locks, pools, RBGC, and daemon support (default: disabled)])], + [ ENABLED_RNG_EXTRAS=$enableval ], + [ ENABLED_RNG_EXTRAS=$KERNEL_MODE_DEFAULTS ] + ) + +if test "$ENABLED_RNG_EXTRAS" = "yes" +then + AM_CFLAGS="$AM_CFLAGS -DWC_RNG_EXTRAS" +fi + + AC_ARG_ENABLE([rng-bank], [AS_HELP_STRING([--enable-rng-bank],[Enable compiling and using RNG banks (default: disabled)])], [ ENABLED_RNG_BANK=$enableval ], @@ -7167,7 +7184,8 @@ AS_CASE([$FIPS_VERSION], -DECC_USER_CURVES \ -DHAVE_ECC384 \ -DHAVE_ECC521 \ - -DWOLFSSL_VALIDATE_FFC_IMPORT" + -DWOLFSSL_VALIDATE_FFC_IMPORT \ + -DWC_RNG_EXTRAS" # KCAPI API does not support custom k for sign, don't force enable ECC key sizes and don't use seed callback AS_IF([test "x$ENABLED_KCAPI_ECC" = "xno"], diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 3b8de5538cd..24213feb9a7 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -804,6 +804,7 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, * plain-store purge: * stirs carry no claims, so resurrection there is benign by the * three-no-ops doctrine.) */ +#ifdef WC_RNG_HAVE_LOCK static void NextSeedPurge(wolfSSL_Atomic_Int *lenp) { WC_ATOMIC_INT_ARG cur = WOLFSSL_ATOMIC_LOAD(*lenp); @@ -818,6 +819,7 @@ static void NextSeedPurge(wolfSSL_Atomic_Int *lenp) /* cur was reloaded by the failed exchange; re-evaluate. */ } } +#endif /* WC_RNG_HAVE_LOCK */ /* Release a producer claim (PRODUCING) on the credited aperture, * installing val (a fill offset, the full length, READY, or EMPTY). diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 6f9c1027cfb..164549724f1 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -941,7 +941,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void); #endif #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && !defined(HAVE_SELFTEST) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); #endif #if defined(WC_RNG_BANK_SUPPORT) && defined(HAVE_HASHDRBG) && \ @@ -2621,7 +2621,7 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("NOISESRC test passed!\n"); #endif #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && !defined(HAVE_SELFTEST) if ((ret = rng_drbg_svc_test()) != 0) TEST_FAIL("RNGSVC test failed!\n", ret); else @@ -29176,7 +29176,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif /* WC_RNG_BANK_SUPPORT */ #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && !defined(HAVE_SELFTEST) /* Coverage for the DRBG state accessor / reseed scheduling services and the * per-key RNG clear APIs. Probes that observe DRBG internals via the * accessors are gated at runtime on wc_RNG_DRBG_Present(), so the test also diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 7a137680edc..15c3733f698 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -43,77 +43,75 @@ WOLFSSL_LOCAL int wolfCrypt_FIPS_DRBG_sanity(void); #endif -#ifndef WC_RNG_NO_POOL - #ifndef WC_RNG_HAVE_POOL - #define WC_RNG_HAVE_POOL - #endif +/***** Setup for RNG extra features *****/ + +#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_LOCK)) && \ + !defined(WC_RNG_NO_LOCK) + #define WC_RNG_HAVE_LOCK #ifdef WOLFSSL_NO_ATOMICS - typedef word32 WC_RNG_pool_state_t; + typedef word32 WC_RNG_lock_t; + typedef word32 WC_RNG_lock_arg_t; #else - typedef wolfSSL_Atomic_Uint WC_RNG_pool_state_t; + typedef wolfSSL_Atomic_Uint WC_RNG_lock_t; + typedef WC_ATOMIC_UINT_ARG WC_RNG_lock_arg_t; #endif #else - #undef WC_RNG_HAVE_POOL + #undef WC_RNG_HAVE_LOCK #endif -#ifndef WC_RNG_NO_RBGC - #if !defined(WC_RNG_HAVE_RBGC) && \ - defined(HAVE_HASHDRBG) && \ - !defined(CUSTOM_RAND_GENERATE_BLOCK) - #define WC_RNG_HAVE_RBGC +#ifdef WC_RNG_WANT_LOCK_FULL_MUTEX + #ifndef WC_RNG_HAVE_LOCK + #error FULL_MUTEX depends on WC_RNG_HAVE_LOCK. #endif + #define WC_RNG_HAVE_LOCK_FULL_MUTEX #else - #undef WC_RNG_HAVE_RBGC -#endif - -#ifdef WC_RNG_NO_LOCK_FULL_MUTEX #undef WC_RNG_HAVE_LOCK_FULL_MUTEX -#elif defined(WC_RNG_HAVE_LOCK_FULL_MUTEX) - #ifdef WC_RNG_NO_LOCK - #error FULL_MUTEX depends on WC_RNG_HAVE_LOCK. - #endif #endif -#ifndef WC_RNG_NO_FREE_HOOK - #define WC_RNG_HAVE_FREE_HOOK -#endif -#ifdef WC_RNG_HAVE_FREE_HOOK - struct WC_RNG; - typedef int (*wc_RNG_free_hook_cb_t)(const struct WC_RNG *rng, void *arg); +#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_RBGC)) && \ + !defined(WC_RNG_NO_RBGC) && defined(HAVE_HASHDRBG) && \ + !defined(CUSTOM_RAND_GENERATE_BLOCK) + #define WC_RNG_HAVE_RBGC +#else + #undef WC_RNG_HAVE_RBGC #endif -#ifndef WC_RNG_NO_LOCK - #ifndef WC_RNG_HAVE_LOCK - #define WC_RNG_HAVE_LOCK - #endif +#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_NEXT_SEED)) && \ + !defined(WC_RNG_NO_NEXT_SEED) && defined(HAVE_HASHDRBG) && \ + !defined(CUSTOM_RAND_GENERATE_BLOCK) + #define WC_RNG_HAVE_NEXT_SEED #ifdef WOLFSSL_NO_ATOMICS - typedef word32 WC_RNG_lock_t; - typedef word32 WC_RNG_lock_arg_t; + typedef sword32 WC_DRBG_nextSeedLen_t; #else - typedef wolfSSL_Atomic_Uint WC_RNG_lock_t; - typedef WC_ATOMIC_UINT_ARG WC_RNG_lock_arg_t; + typedef wolfSSL_Atomic_Int WC_DRBG_nextSeedLen_t; #endif #else - #undef WC_RNG_HAVE_LOCK + #undef WC_RNG_HAVE_NEXT_SEED #endif -#if !defined(HAVE_HASHDRBG) || defined(CUSTOM_RAND_GENERATE_BLOCK) && \ - !defined(WC_RNG_NO_NEXT_SEED) - #define WC_RNG_NO_NEXT_SEED -#endif -#ifndef WC_RNG_NO_NEXT_SEED - #ifndef WC_RNG_HAVE_NEXT_SEED - #define WC_RNG_HAVE_NEXT_SEED - #endif +#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_POOL)) && \ + !defined(WC_RNG_NO_POOL) + #define WC_RNG_HAVE_POOL #ifdef WOLFSSL_NO_ATOMICS - typedef sword32 WC_DRBG_nextSeedLen_t; + typedef word32 WC_RNG_pool_state_t; #else - typedef wolfSSL_Atomic_Int WC_DRBG_nextSeedLen_t; + typedef wolfSSL_Atomic_Uint WC_RNG_pool_state_t; #endif #else - #undef WC_RNG_HAVE_NEXT_SEED + #undef WC_RNG_HAVE_POOL +#endif + +#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_FREE_HOOK)) && \ + !defined(WC_RNG_NO_FREE_HOOK) + #define WC_RNG_HAVE_FREE_HOOK + struct WC_RNG; + typedef int (*wc_RNG_free_hook_cb_t)(const struct WC_RNG *rng, void *arg); +#else + #undef WC_RNG_HAVE_FREE_HOOK #endif +/***** End setup for RNG extra features *****/ + /* Maximum generate block length */ #ifndef RNG_MAX_BLOCK_LEN #ifdef HAVE_INTEL_QA diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 2ccaa95b98a..7b74e271c75 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -926,6 +926,11 @@ enum { #endif /* WOLFSSL_STATIC_MEMORY */ #endif +#if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY) && \ + !defined(WC_NO_CONSTRUCTORS) + #define WC_NO_CONSTRUCTORS +#endif + #if defined(WOLFSSL_SMALL_STACK) && defined(WC_NO_CONSTRUCTORS) #error WOLFSSL_SMALL_STACK requires constructors. #endif From a3f9a129030b53a901f45138e05e44268675826d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 11 Sep 2026 12:42:52 -0500 Subject: [PATCH 051/102] wolfcrypt/src/random.c, wolfcrypt/test/test.c: * implement Hash_DRBG_StirGenerate(), refactor wc_RNG_DRBG_Reseed_Nonce_Uncredited() and wc_RNG_DRBG_Reseed_Uncredited() atop it, and revert addition of "credited" arg to Hash256_DRBG_Reseed(), Hash512_DRBG_Reseed(), and Hash_DRBG_Reseed(); * in _InitRng(), error early on invalid flags, and implement proper catch-all error path cleanup; * in wc_RNG_GenerateBlock(), when rng->pid != getpid(), alongside PollAndReSeeD(), empty the pool and cached seeds, if any; * also check for WC_RNG_LOCK_ENTROPY_INVALIDATED immediately before calling Hash*_DRBG_Generate() and if found, recover inline with must-succeed PollAndReSeed(). --- wolfcrypt/src/random.c | 402 +++++++++++++++++++++++++---------------- wolfcrypt/test/test.c | 19 +- 2 files changed, 258 insertions(+), 163 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 24213feb9a7..95054fad773 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -445,8 +445,7 @@ typedef struct DRBG_SHA512_internal DRBG_SHA512_internal; static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed, word32 seedSz, - const byte* additional, word32 additionalSz, - int credited); + const byte* additional, word32 additionalSz); static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, const byte* additional, word32 additionalSz); @@ -708,7 +707,7 @@ static int Hash_df(DRBG_internal* drbg, byte* out, word32 outSz, byte type, /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz, const byte* additional, - word32 additionalSz, int credited) + word32 additionalSz) { int ret; WC_DECLARE_VAR(newV, byte, DRBG_SEED_LEN, 0); @@ -737,7 +736,7 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, ret = Hash_df(drbg, drbg->C, sizeof(drbg->C), drbgInitC, drbg->V, sizeof(drbg->V), NULL, 0, NULL, 0); } - if ((ret == DRBG_SUCCESS) && credited) { + if (ret == DRBG_SUCCESS) { drbg->reseedCtr = 1; } @@ -846,8 +845,7 @@ static int NextSeedProducerRelease(wolfSSL_Atomic_Int *lenp, #endif /* WC_RNG_HAVE_NEXT_SEED */ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, - const byte* additional, word32 additionalSz, - int credited) + const byte* additional, word32 additionalSz) { int ret; #ifdef WC_RNG_HAVE_LOCK @@ -862,15 +860,10 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, #endif #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_POOL) - /* Purge the pool on credited reseeds, but not on uncredited ones. A - * credited reseed is an epoch boundary -- the pool must not serve output of - * a retired state (particularly pre-invalidation state). An uncredited - * reseed merely stirs the state; the pooled bytes' own credited provenance - * is unaffected. Purging at stirs would also empty the pool exactly when - * harvest-driven mixing is heaviest, i.e. when atomic-context consumers - * most need it. */ - if (credited) - WOLFSSL_ATOMIC_STORE(rng->poolState, 0); + /* Purge the pool on credited reseeds. A credited reseed is an epoch + * boundary -- the pool must not serve output of a retired state + * (particularly pre-invalidation state). */ + WOLFSSL_ATOMIC_STORE(rng->poolState, 0); #endif /* WC_RNG_HAVE_LOCK && WC_RNG_HAVE_POOL */ #ifndef NO_SHA256 @@ -903,8 +896,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, } #endif - ret = Hash256_DRBG_Reseed(drbg, seed, seedSz, - additional, additionalSz, credited); + ret = Hash256_DRBG_Reseed(drbg, seed, seedSz, additional, additionalSz); #ifdef WC_RNG_DEBUG_STATS if (ret == 0) { if (credited) @@ -944,7 +936,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, #endif ret = Hash512_DRBG_Reseed(drbg512, seed, seedSz, - additional, additionalSz, credited); + additional, additionalSz); #ifdef WC_RNG_DEBUG_STATS if (ret == 0) { if (credited) @@ -971,7 +963,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, out: #ifdef WC_RNG_HAVE_LOCK - if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && (ret == 0) && credited) { + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && (ret == 0)) { for (;;) { if (wolfSSL_Atomic_Uint_CompareExchange( &rng->lock, &cur_lock, @@ -999,7 +991,7 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, if (ret != 0) return ret; - ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, 1 /* credited */); + ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz); #ifdef WC_RNG_HAVE_RBGC if (ret == 0) rng->RBGCStratum = 0; @@ -1118,34 +1110,6 @@ int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) return WRONG_TYPE_OBJECT_E; } -/* Similar to wc_RNG_DRBG_Reseed_Nonce(), except that the reseed counter is - * preserved: the caller-supplied material is mixed into the DRBG state via - * the reseed derivation function without being credited as entropy -- - * the module's own seed source (wc_RNG_DRBG_Reseed_Now() or the - * WC_RESEED_INTERVAL backstop) resets the reseed schedule. This is the - * SP 800-90A additional-input concept, applied via the reseed derivation. */ -int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, - const byte* seed, word32 seedSz, - const byte *nonce, word32 nonceSz) -{ - if (rng == NULL || seed == NULL) - return BAD_FUNC_ARG; - - { - int lock_ret = rng_lock_required_check(rng); - if (lock_ret != 0) - return lock_ret; - } - - return Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, 0 /* credited */); -} - -int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz) -{ - return wc_RNG_DRBG_Reseed_Nonce_Uncredited(rng, seed, seedSz, NULL, - 0); -} - /* Generic byte-array helper -- shared by both SHA-256 and SHA-512 DRBG * cores. Lives outside the NO_SHA256 guard so SHA-512-only builds * still build. */ @@ -1716,8 +1680,7 @@ static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, /* 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 credited) + const byte* additional, word32 additionalSz) { int ret; WC_DECLARE_VAR(newV, byte, DRBG_SHA512_SEED_LEN, 0); @@ -1747,7 +1710,7 @@ static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed, sizeof(drbg->V), NULL, 0, NULL, 0); } - if ((ret == DRBG_SUCCESS) && credited) { + if (ret == DRBG_SUCCESS) { drbg->reseedCtr = 1; } @@ -2075,6 +2038,88 @@ static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg) #endif /* WOLFSSL_DRBG_SHA512 */ +/* Uncredited stirring, per SP 800-90A 10.1.1.4 generate with additional_input + * (step 2: V += Hash(0x02 || V || additional_input)). The generate is + * zero-length: Hash_gen()'s (and Hash512_gen()'s) outSz==0 mode banks the + * generated block for the continuous test, so the stir also primes CRNGT, and + * out is never dereferenced. The reseed counter is incremented as for any + * generate, and quarantine/stratum are untouched, so the no-claims doctrine + * holds as a theorem of the standard rather than a property of a custom + * transition. Refused with NOT_READY_E when the instance is quarantined or due + * for a credited reseed: a generate must not run past the reseed interval. */ + +static int Hash_DRBG_StirGenerate(WC_RNG* rng, const byte* add, word32 addSz) +{ + wc_drbg_reseed_ctr_t ctr = 0; + int ret; + +#ifdef WC_RNG_HAVE_LOCK + /* The lock word is the atomic source of truth for quarantine: the + * invalidator's counter saturation can be lost to a racing + * lease-holder's plain reseedCtr++, but the latch cannot. Checked + * before the counter for exactly that reason. */ + if (WOLFSSL_ATOMIC_LOAD(rng->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NOT_READY_E; +#endif + if (wc_RNG_DRBG_GetReseedCtr(rng, &ctr) == 0) { + if (ctr >= WC_RESEED_INTERVAL) + return NOT_READY_E; + } + + ret = RNG_FAILURE_E; +#ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { + ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, NULL, 0, + add, addSz); + } +#endif +#ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { + ret = Hash512_DRBG_Generate((DRBG_SHA512_internal *)rng->drbg512, + NULL, 0, add, addSz); + } +#endif +#ifdef WC_RNG_DEBUG_STATS + if (ret == 0) + ++rng->_stats_uncredited_reseeds; /* counts uncredited + * stir-generates. */ +#endif + return ret; +} + +int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, + const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz) +{ + if (rng == NULL || seed == NULL) + return BAD_FUNC_ARG; + + if (rng->status != WC_DRBG_OK) + return RNG_FAILURE_E; + + { + int lock_ret = rng_lock_required_check(rng); + if (lock_ret != 0) + return lock_ret; + } + + { + int ret = Hash_DRBG_StirGenerate(rng, seed, seedSz); + if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { + /* Second chunk as its own specified generate: additional + * input is per-call, and chunking beats concatenation + * scratch. */ + ret = Hash_DRBG_StirGenerate(rng, nonce, nonceSz); + } + return ret; + } +} + +int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz) +{ + return wc_RNG_DRBG_Reseed_Nonce_Uncredited(rng, seed, seedSz, NULL, + 0); +} /* FIPS 140-3 IG 10.3.A / SP800-90B Health Tests for Seed Data * @@ -2343,6 +2388,7 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, word32 seedSz = SEED_SZ + SEED_BLOCK_SZ; #endif WC_DECLARE_VAR(seed, byte, MAX_SEED_SZ, rng->heap); + int drbg_instantiated = 0; #ifdef WOLFSSL_SMALL_STACK_CACHE int drbg_scratch_instantiated = 0; #endif @@ -2359,6 +2405,15 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, if (nonce == NULL && nonceSz != 0) return BAD_FUNC_ARG; +#ifndef WC_RNG_HAVE_NEXT_SEED + if (flags & WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED) + return NOT_COMPILED_IN; +#endif +#ifndef WC_RNG_HAVE_LOCK_FULL_MUTEX + if (flags & WC_RNG_INIT_FLAGS_USE_FULL_MUTEX) + return NOT_COMPILED_IN; +#endif + #ifdef WC_RNG_HAVE_LOCK if (flags & (WC_RNG_INIT_FLAGS_LOCK_REQUIRED | WC_RNG_INIT_FLAGS_LOCK_INITIALLY)) @@ -2659,12 +2714,12 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, } else { if (seedRng != NULL) { - /* RBGC spawn (SpawnRngRBGC()): draw the seed material from - * the parent DRBG's generate function in place of the - * module's seed source -- the SP 800-90C RBG chain - * construction. All subsequent handling (health test, seed - * byte accounting, instantiate, failure disposition) is - * identical to the seed-source path. */ + /* RBGC spawn: draw the seed material from the parent DRBG's + * generate function in place of the module's seed source -- the SP + * 800-90C RBG chain construction. The root DRBG is implicitly + * healthy, so the seed Health test is omitted; all subsequent + * handling (seed byte accounting, instantiate, failure disposition) + * is then identical to the primary seed path. */ ret = wc_RNG_GenerateBlock(seedRng, seed, seedSz); } else { @@ -2687,61 +2742,72 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, ret = wc_GenerateSeed(&rng->seed, seed, seedSz); #endif /* WC_RNG_SEED_CB */ } + #ifdef WOLFSSL_CHECK_MEM_ZERO - /* seed now holds entropy; register across DRBG instantiation */ - wc_MemZero_Add("_InitRng seed", seed, seedSz); + /* seed now holds entropy; register across DRBG instantiation */ + wc_MemZero_Add("_InitRng seed", seed, seedSz); #endif - if (ret != 0) { + + if (ret != 0) { #if defined(DEBUG_WOLFSSL) - WOLFSSL_MSG_EX("Seed generation failed... %d", ret); + WOLFSSL_MSG_EX("Seed generation failed... %d", ret); #elif defined(WC_VERBOSE_RNG) - WOLFSSL_DEBUG_PRINTF( - "ERROR: seed acquisition in _InitRng() failed with err %d", - ret); + WOLFSSL_DEBUG_PRINTF( + "ERROR: seed acquisition in _InitRng() failed with err %d", + ret); #endif - ret = DRBG_FAILURE; - rng->status = DRBG_FAILED; - } + ret = DRBG_FAILURE; + rng->status = DRBG_FAILED; + } - if (ret == 0) - ret = wc_RNG_TestSeed(seed, seedSz); - #if defined(DEBUG_WOLFSSL) + /* Health-check the primary seed -- RBGC seed is implicitly healthy. */ + + if ((ret == 0) && (seedRng == NULL)) { + ret = wc_RNG_TestSeed(seed, seedSz); + #if defined(DEBUG_WOLFSSL) if (ret != 0) { WOLFSSL_MSG_EX("wc_RNG_TestSeed failed... %d", ret); } - #elif defined(WC_VERBOSE_RNG) + #elif defined(WC_VERBOSE_RNG) if (ret != DRBG_SUCCESS) { WOLFSSL_DEBUG_PRINTF( "ERROR: wc_RNG_TestSeed() in _InitRng() returned err %d.", ret); } - #endif + #endif + } + + /* Instantiate the DRBG */ - if (ret == DRBG_SUCCESS) { + if (ret == DRBG_SUCCESS) { #ifndef NO_SHA256 - if (rng->drbgType == WC_DRBG_SHA256) - ret = Hash_DRBG_Instantiate((DRBG_internal *)rng->drbg, + if (rng->drbgType == WC_DRBG_SHA256) + ret = Hash_DRBG_Instantiate((DRBG_internal *)rng->drbg, #if defined(HAVE_FIPS) || !defined(WOLFSSL_RNG_USE_FULL_SEED) - seed + SEED_BLOCK_SZ, seedSz - SEED_BLOCK_SZ, + seed + SEED_BLOCK_SZ, seedSz - SEED_BLOCK_SZ, #else - seed, seedSz, + seed, seedSz, #endif - nonce, nonceSz, NULL, 0, rng->heap, devId); + nonce, nonceSz, NULL, 0, rng->heap, devId); #endif #ifdef WOLFSSL_DRBG_SHA512 - if (rng->drbgType == WC_DRBG_SHA512) - ret = Hash512_DRBG_Instantiate( - (DRBG_SHA512_internal *)rng->drbg512, + if (rng->drbgType == WC_DRBG_SHA512) + ret = Hash512_DRBG_Instantiate( + (DRBG_SHA512_internal *)rng->drbg512, #if defined(HAVE_FIPS) || !defined(WOLFSSL_RNG_USE_FULL_SEED) - seed + SEED_BLOCK_SZ, seedSz - SEED_BLOCK_SZ, + seed + SEED_BLOCK_SZ, seedSz - SEED_BLOCK_SZ, #else - seed, seedSz, + seed, seedSz, #endif - nonce, nonceSz, NULL, 0, rng->heap, devId); + nonce, nonceSz, NULL, 0, rng->heap, devId); #endif - } + if (ret == 0) + drbg_instantiated = 1; + } } /* ret == 0 */ + /* Unconditionally burn the seed data. */ + #ifdef WOLFSSL_SMALL_STACK if (seed) #endif @@ -2754,49 +2820,6 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, } WC_FREE_VAR_EX(seed, rng->heap, DYNAMIC_TYPE_SEED); - if (ret != DRBG_SUCCESS) { - #ifndef NO_SHA256 - if (rng->drbgType == WC_DRBG_SHA256) { - #if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY) - XFREE(rng->drbg, rng->heap, DYNAMIC_TYPE_RNG); - #endif - rng->drbg = NULL; - #ifdef WOLFSSL_SMALL_STACK_CACHE - XFREE(rng->health_check_scratch, rng->heap, - DYNAMIC_TYPE_TMP_BUFFER); - rng->health_check_scratch = NULL; - if (drbg_scratch_instantiated) - (void)Hash_DRBG_Uninstantiate( - (DRBG_internal *)rng->drbg_scratch); - XFREE(rng->drbg_scratch, rng->heap, DYNAMIC_TYPE_RNG); - rng->drbg_scratch = NULL; - #endif - } - #endif /* !NO_SHA256 */ - #ifdef WOLFSSL_DRBG_SHA512 - if (rng->drbgType == WC_DRBG_SHA512) { - #if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY) - XFREE(rng->drbg512, rng->heap, DYNAMIC_TYPE_RNG); - #endif - rng->drbg512 = NULL; - #ifdef WOLFSSL_SMALL_STACK_CACHE - XFREE(rng->health_check_scratch_512, rng->heap, - DYNAMIC_TYPE_TMP_BUFFER); - rng->health_check_scratch_512 = NULL; - if (drbg_scratch_instantiated) - (void)Hash512_DRBG_Uninstantiate(rng->drbg512_scratch); - XFREE(rng->drbg512_scratch, rng->heap, DYNAMIC_TYPE_RNG); - rng->drbg512_scratch = NULL; - #endif - } - #endif - #ifdef WOLFSSL_SMALL_STACK_CACHE - XFREE(rng->newSeed_buf, rng->heap, DYNAMIC_TYPE_SEED); - rng->newSeed_buf = NULL; - #endif - } - /* else wc_RNG_HealthTestLocal was successful */ - if (ret == DRBG_SUCCESS) { #ifdef WOLFSSL_CHECK_MEM_ZERO #ifndef NO_SHA256 @@ -2825,10 +2848,7 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #endif /* HAVE_HASHDRBG */ #endif /* CUSTOM_RAND_GENERATE_BLOCK */ -#ifndef WC_RNG_HAVE_NEXT_SEED - if (flags & WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED) - return NOT_COMPILED_IN; -#else +#ifdef WC_RNG_HAVE_NEXT_SEED if ((ret == 0) && (flags & WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED)) { @@ -2847,11 +2867,59 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, * the whole latch, mutex included. */ (void)wc_LockMutex(&rng->mutex); } -#else - return NOT_COMPILED_IN; #endif } + if (ret != 0) { + #ifndef NO_SHA256 + if (rng->drbgType == WC_DRBG_SHA256) { + if (drbg_instantiated) { + (void)Hash_DRBG_Uninstantiate( + (DRBG_internal *)rng->drbg); + } + #if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY) + XFREE(rng->drbg, rng->heap, DYNAMIC_TYPE_RNG); + #endif + rng->drbg = NULL; + #ifdef WOLFSSL_SMALL_STACK_CACHE + XFREE(rng->health_check_scratch, rng->heap, + DYNAMIC_TYPE_TMP_BUFFER); + rng->health_check_scratch = NULL; + if (drbg_scratch_instantiated) + (void)Hash_DRBG_Uninstantiate( + (DRBG_internal *)rng->drbg_scratch); + XFREE(rng->drbg_scratch, rng->heap, DYNAMIC_TYPE_RNG); + rng->drbg_scratch = NULL; + #endif /* WOLFSSL_SMALL_STACK_CACHE */ + } + #endif /* !NO_SHA256 */ + #ifdef WOLFSSL_DRBG_SHA512 + if (rng->drbgType == WC_DRBG_SHA512) { + if (drbg_instantiated) { + (void)Hash512_DRBG_Uninstantiate( + (DRBG_SHA512_internal *)rng->drbg512); + } + #if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY) + XFREE(rng->drbg512, rng->heap, DYNAMIC_TYPE_RNG); + #endif + rng->drbg512 = NULL; + #ifdef WOLFSSL_SMALL_STACK_CACHE + XFREE(rng->health_check_scratch_512, rng->heap, + DYNAMIC_TYPE_TMP_BUFFER); + rng->health_check_scratch_512 = NULL; + if (drbg_scratch_instantiated) + (void)Hash512_DRBG_Uninstantiate(rng->drbg512_scratch); + XFREE(rng->drbg512_scratch, rng->heap, DYNAMIC_TYPE_RNG); + rng->drbg512_scratch = NULL; + #endif /* WOLFSSL_SMALL_STACK_CACHE */ + } + #endif + #ifdef WOLFSSL_SMALL_STACK_CACHE + XFREE(rng->newSeed_buf, rng->heap, DYNAMIC_TYPE_SEED); + rng->newSeed_buf = NULL; + #endif + } + return ret; } @@ -3828,7 +3896,7 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, } if (ret == DRBG_SUCCESS) { ret = Hash_DRBG_Reseed(rng, newSeed + SEED_BLOCK_SZ, SEED_SZ, - additional, additionalSz, 1 /* credited */); + additional, additionalSz); #ifdef WC_RNG_HAVE_RBGC if (ret == 0) @@ -4036,7 +4104,7 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, word32 nextSeedSz; int ret; - if ((rng == NULL) || (n == 0)) + if ((rng == NULL) || (n == 0) || (rng == root)) return BAD_FUNC_ARG; if ((root != NULL) && (nonce != NULL)) @@ -4391,8 +4459,7 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, /* Identical byte accounting to PollAndReSeed(): the SEED_BLOCK_SZ * prefix was consumed by the bank-time health testing. */ - ret = Hash_DRBG_Reseed(rng, seed + SEED_BLOCK_SZ, SEED_SZ, - nonce, nonceSz, 1 /* credited */); + ret = Hash_DRBG_Reseed(rng, seed + SEED_BLOCK_SZ, SEED_SZ, nonce, nonceSz); #ifdef WC_RNG_DEBUG_STATS if (ret == 0) { @@ -4495,7 +4562,7 @@ int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng) return NOT_READY_E; } - ret = wc_RNG_DRBG_Reseed_Uncredited(rng, seed, nextSeedSz); + ret = Hash_DRBG_StirGenerate(rng, seed, nextSeedSz); #ifdef WC_RNG_DEBUG_STATS if (ret == 0) @@ -4595,6 +4662,26 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) rng->status = DRBG_FAILED; return RNG_FAILURE_E; } + + #ifdef WC_RNG_HAVE_POOL + WOLFSSL_ATOMIC_STORE(rng->poolState, 0); + #endif + #ifdef WC_RNG_HAVE_NEXT_SEED + #ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { + NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); + WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); + } + #endif + #ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { + NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); + WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen, + WC_DRBG_NEXT_SEED_EMPTY); + } + #endif + #endif /* WC_RNG_HAVE_NEXT_SEED */ } #endif @@ -4649,6 +4736,15 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) } #endif /* WC_RNG_HAVE_NEXT_SEED */ +#ifdef WC_RNG_HAVE_LOCK + if (WOLFSSL_ATOMIC_LOAD(rng->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED) { + if (PollAndReSeed(rng, NULL, 0) != DRBG_SUCCESS) { + rng->status = DRBG_FAILED; + return RNG_FAILURE_E; + } + } +#endif + #ifndef NO_SHA256 if (rng->drbgType == WC_DRBG_SHA256) { ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, @@ -4960,9 +5056,7 @@ static int wc_RNG_HealthTest_ex_internal(DRBG_internal* drbg, #endif if (reseed) { - if (Hash256_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0, - 1 /* credited */) != 0) - { + if (Hash256_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0) != 0) { goto exit_rng_ht; } } @@ -5451,8 +5545,7 @@ static int wc_RNG_HealthTest_SHA512_ex_internal(DRBG_SHA512_internal* drbg, #endif if (reseed) { - if (Hash512_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0, - 1 /* credited */) != 0) + if (Hash512_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0) != 0) { goto exit_rng_ht512; } @@ -5530,8 +5623,7 @@ int wc_RNG_HealthTest_SHA512_ex(int reseed, if (reseed) { if (seedB != NULL && seedBSz > 0) { - ret = Hash512_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0, - 1 /* credited */); + ret = Hash512_DRBG_Reseed(drbg, seedB, seedBSz, NULL, 0); if (ret != 0) goto exit_sha512_ex; } } @@ -5659,8 +5751,7 @@ int wc_RNG_HealthTest_SHA256_ex( /* Reseed 1 with additionalA, then Generate 1 with NULL (discard) */ if (entropyB != NULL && entropyBSz > 0) { ret = Hash256_DRBG_Reseed(drbg, entropyB, entropyBSz, - additionalA, additionalASz, - 1 /* credited */); + additionalA, additionalASz); if (ret != 0) goto exit_sha256_ex; } ret = Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0); @@ -5669,8 +5760,7 @@ int wc_RNG_HealthTest_SHA256_ex( /* Reseed 2 with additionalB, then Generate 2 with NULL (keep) */ if (entropyC != NULL && entropyCsz > 0) { ret = Hash256_DRBG_Reseed(drbg, entropyC, entropyCsz, - additionalB, additionalBSz, - 1 /* credited */); + additionalB, additionalBSz); if (ret != 0) goto exit_sha256_ex; } ret = Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0); @@ -5679,8 +5769,7 @@ int wc_RNG_HealthTest_SHA256_ex( /* Standard mode: explicit reseed, then two generates */ if (entropyB != NULL && entropyBSz > 0) { ret = Hash256_DRBG_Reseed(drbg, entropyB, entropyBSz, - additionalReseed, additionalReseedSz, - 1 /* credited */); + additionalReseed, additionalReseedSz); if (ret != 0) goto exit_sha256_ex; } @@ -5766,8 +5855,7 @@ int wc_RNG_HealthTest_SHA512_ex2( /* Reseed 1 with additionalA, then Generate 1 with NULL (discard) */ if (entropyB != NULL && entropyBSz > 0) { ret = Hash512_DRBG_Reseed(drbg, entropyB, entropyBSz, - additionalA, additionalASz, - 1 /* credited */); + additionalA, additionalASz); if (ret != 0) goto exit_sha512_ex2; } ret = Hash512_DRBG_Generate(drbg, output, outputSz, NULL, 0); @@ -5776,8 +5864,7 @@ int wc_RNG_HealthTest_SHA512_ex2( /* Reseed 2 with additionalB, then Generate 2 with NULL (keep) */ if (entropyC != NULL && entropyCsz > 0) { ret = Hash512_DRBG_Reseed(drbg, entropyC, entropyCsz, - additionalB, additionalBSz, - 1 /* credited */); + additionalB, additionalBSz); if (ret != 0) goto exit_sha512_ex2; } ret = Hash512_DRBG_Generate(drbg, output, outputSz, NULL, 0); @@ -5786,8 +5873,7 @@ int wc_RNG_HealthTest_SHA512_ex2( /* Standard mode: explicit reseed, then two generates */ if (entropyB != NULL && entropyBSz > 0) { ret = Hash512_DRBG_Reseed(drbg, entropyB, entropyBSz, - additionalReseed, additionalReseedSz, - 1 /* credited */); + additionalReseed, additionalReseedSz); if (ret != 0) goto exit_sha512_ex2; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 164549724f1..71727218937 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -29247,7 +29247,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - /* uncredited mixing preserves the counter */ + /* uncredited mixing never resets the counter -- it increments it by + * exactly one: a stir is a specified generate (additional_input, + * zero-length output), and a generate counts. */ if (present) { api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if (api_ret != 0) @@ -29258,7 +29260,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if (present) { api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c2); - if ((api_ret != 0) || (c2 != c1)) + if ((api_ret != 0) || (c2 != c1 + 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } @@ -29570,11 +29572,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - /* an uncredited reseed must not clear the flag; a credited one must. */ + /* an uncredited stir cannot run on a quarantined instance at all -- + * the latch (checked atomically, ahead of the racy counter) refuses + * it: NOT_READY_E, with counter and flag untouched by + * construction. */ XMEMSET(block, 0x5a, sizeof(block)); api_ret = wc_RNG_DRBG_Reseed_Uncredited(WC_RNG_BANK_INST_TO_RNG(held), block, sizeof(block)); - if (api_ret != 0) + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); if (api_ret != 0) @@ -30838,8 +30843,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) sizeof(frag64)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + /* consumption refuses on the quarantined instance (latch checked + * atomically ahead of the racy counter); the use-once aperture + * reopens EMPTY regardless -- stirs are best-effort, and + * accumulation simply resumes. */ api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(&leaf); - if (api_ret != 0) + if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_lock_read(&leaf, &lock_state); if (api_ret != 0) From 0d9e3c15edda1f89b1bb552f647b84c0934f1cb5 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 11 Sep 2026 13:39:28 -0500 Subject: [PATCH 052/102] RNG extras: rename "uncredited [re]seed" in all its spellings to "stir", appropriately spelled, for clarity and consistency. Stirring is implemented by Hash_DRBG_StirGenerate() an an SP 800-90A Rev. 1 10.1.1.4 generate, with the stir material as additional_input and no output requested. --- doc/dox_comments/header_files/random.h | 36 +++---- doc/dox_comments/header_files/rng_bank.h | 6 +- linuxkm/lkcapi_sha_glue.c | 78 +++++++-------- wolfcrypt/src/random.c | 122 +++++++++++------------ wolfcrypt/src/rng_bank.c | 14 +-- wolfcrypt/test/test.c | 84 ++++++++-------- wolfcrypt/test/test.h | 2 +- wolfssl/wolfcrypt/random.h | 48 ++++----- wolfssl/wolfcrypt/rng_bank.h | 8 +- 9 files changed, 196 insertions(+), 202 deletions(-) diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index 8e00f0ab595..b422a6243d3 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -949,7 +949,7 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz); \param nonceSz Length of nonce in bytes. \sa wc_RNG_DRBG_Reseed - \sa wc_RNG_DRBG_Reseed_Nonce_Uncredited + \sa wc_RNG_DRBG_Stir_Nonce */ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); @@ -971,14 +971,14 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, \param seedSz Length of seed in bytes. \sa wc_RNG_DRBG_Reseed - \sa wc_RNG_DRBG_Reseed_Nonce_Uncredited + \sa wc_RNG_DRBG_Stir_Nonce */ -int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz); +int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz); /*! \ingroup Random - \brief The nonce-bearing form of wc_RNG_DRBG_Reseed_Uncredited(). + \brief The nonce-bearing form of wc_RNG_DRBG_Stir(). \return 0 Success \return BAD_FUNC_ARG rng or seed is null. @@ -990,10 +990,10 @@ int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz); \param nonce Optional additional input. \param nonceSz Length of nonce in bytes. - \sa wc_RNG_DRBG_Reseed_Uncredited + \sa wc_RNG_DRBG_Stir \sa wc_RNG_DRBG_Reseed_Nonce */ -int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, const byte* seed, +int wc_RNG_DRBG_Stir_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); @@ -1116,8 +1116,8 @@ int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, are impossible by construction, consistent with SP 800-90C 7.1.2.2. Lateral (equal-stratum) and downgrading reseeds are refused with BAD_FUNC_ARG. Building WC_RNG_NO_RBGC_RESEED restricts credited chain - reseeds to primary-seeded roots. Uncredited chain reseeds - (wc_RNG_DRBG_ReseedRBGC_Uncredited()) are exempt from all of this: they + reseeds to primary-seeded roots. Uncredited chain stirs + (wc_RNG_DRBG_StirRBGC()) are exempt from all of this: they are stirs, claim nothing, and leave rng's stratum untouched. \return 0 Success @@ -1132,7 +1132,7 @@ int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, \param nonceSz Length of nonce in bytes. \sa wc_InitRngRBGC - \sa wc_RNG_DRBG_ReseedRBGC_Uncredited + \sa wc_RNG_DRBG_StirRBGC \sa wc_RNG_DRBG_Reseed_Now */ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, @@ -1154,7 +1154,7 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, \param nonceSz Length of nonce in bytes. \sa wc_RNG_DRBG_ReseedRBGC - \sa wc_RNG_DRBG_Reseed_Uncredited + \sa wc_RNG_DRBG_Stir \details Unrestricted by the credited no-downgrade rule: any source stratum is accepted, and rng's reseed counter, stratum, and entropy-invalidated state are all left untouched -- an uncredited @@ -1162,7 +1162,7 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, or promotion. */ -int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, WC_RNG* root, +int wc_RNG_DRBG_StirRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz); /*! @@ -1340,7 +1340,7 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, \ingroup Random \brief Bank caller-supplied material (up to - WC_DRBG_NEXT_UNCREDITED_SEED_LEN bytes) in the uncredited accumulator + WC_DRBG_NEXT_STIR_LEN bytes) in the uncredited accumulator beside the banked next seed. Writer-safe without a lease (read-copy-store); if the accumulator is already full, the material is absorbed by xor. Harvested entropy deposited here improves the instance @@ -1354,17 +1354,17 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, \param nonce Material to bank. \param nonceSz Length of nonce in bytes. - \sa wc_RNG_DRBG_NextUncreditedSeedNow - \sa wc_RNG_DRBG_Reseed_Uncredited + \sa wc_RNG_DRBG_NextStirNow + \sa wc_RNG_DRBG_Stir */ -int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, const byte *nonce, +int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, word32 nonceSz); /*! \ingroup Random \brief Stir the banked uncredited accumulator into the DRBG as an - uncredited, source-free reseed -- safe in atomic context; the reseed + uncredited, source-free mix-in -- safe in atomic context; the reseed counter is not reset. The caller must own the instance. \return 0 Success @@ -1375,9 +1375,9 @@ int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, const byte *nonce, \param rng The RNG object to stir. - \sa wc_RNG_DRBG_NextUncreditedSeedStore + \sa wc_RNG_DRBG_NextStirStore */ -int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng); +int wc_RNG_DRBG_NextStirNow(WC_RNG* rng); /*! \ingroup Random diff --git a/doc/dox_comments/header_files/rng_bank.h b/doc/dox_comments/header_files/rng_bank.h index 0eb863acf12..7ba20890de6 100644 --- a/doc/dox_comments/header_files/rng_bank.h +++ b/doc/dox_comments/header_files/rng_bank.h @@ -534,7 +534,7 @@ int wc_rng_bank_recover_inst(struct wc_rng_bank *bank, int inst_offset, high-resolution timer (e.g. Linux kernel random_get_entropy()). WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED composes: a ready banked seed is redeemed on the parent before the spawn draw. - WC_RNG_BANK_FLAG_SEED_UNCREDITED and WC_RNG_BANK_FLAG_FOR_RECOVERY are + WC_RNG_BANK_FLAG_STIR and WC_RNG_BANK_FLAG_FOR_RECOVERY are rejected. WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED is implied: the parent is guaranteed in-service, or an error is returned with no lease and no child. @@ -590,7 +590,7 @@ int wc_rng_bank_spawn_new(struct wc_rng_bank *bank, WC_RNG **child_rng, \ingroup Random \brief Reseed every instance with caller-supplied seed material. - WC_RNG_BANK_FLAG_SEED_UNCREDITED mixes the material in without + WC_RNG_BANK_FLAG_STIR mixes the material in without crediting it. \return 0 Success @@ -807,7 +807,7 @@ int wc_rng_bank_daemon_release(struct wc_rng_bank *bank, \brief Bind the daemon's RBG-chain root to the bank, for chain-sourced banking (wc_rng_bank_next_seed_generate_rbgc()) and harvest deposit - (wc_RNG_DRBG_NextUncreditedSeedStore() on the root). + (wc_RNG_DRBG_NextStirStore() on the root). \return 0 Success \return BAD_FUNC_ARG bank is null. diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 0b540a84d53..5b8909e5a6f 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2876,7 +2876,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) (pool_n < (word32)inst_rng->poolSize)) { unsigned long uncredited_nonce = random_get_entropy(); - (void)wc_RNG_DRBG_Reseed_Uncredited(local_root, + (void)wc_RNG_DRBG_Stir(local_root, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); @@ -3024,19 +3024,19 @@ static int wc_linuxkm_entropy_daemon(void *arg) pr_info("RNG INFO: wc_entropyd root total_bytes_requested=" WC_RNG_STAT_FMT "\n" " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" - " credited_reseeds=" WC_RNG_STAT_FMT - " uncredited_reseeds=" WC_RNG_STAT_FMT + " reseeds=" WC_RNG_STAT_FMT + " stirs=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" - " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT - " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n", + " nextstirs_banked=" WC_RNG_STAT_FMT + " nextstirs_redeemed=" WC_RNG_STAT_FMT "\n", s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, - s._stats_credited_reseeds, - s._stats_uncredited_reseeds, + s._stats_reseeds, + s._stats_stirs, s._stats_seed_failures, - s._stats_n_nextuncreditedseed_banked, - s._stats_n_nextuncreditedseed_redeemed); + s._stats_nextstirs_banked, + s._stats_nextstirs_redeemed); } #endif /* WC_RNG_DEBUG_STATS */ #ifdef WC_LINUXKM_VMGENID_POLL @@ -3200,19 +3200,19 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) pr_info("RNG INFO: wc_entropyd root total_bytes_requested=" WC_RNG_STAT_FMT "\n" " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" - " credited_reseeds=" WC_RNG_STAT_FMT - " uncredited_reseeds=" WC_RNG_STAT_FMT + " reseeds=" WC_RNG_STAT_FMT + " stirs=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" - " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT - " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n", + " stirs_banked=" WC_RNG_STAT_FMT + " stirs_redeemed=" WC_RNG_STAT_FMT "\n", s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, - s._stats_credited_reseeds, - s._stats_uncredited_reseeds, + s._stats_reseeds, + s._stats_stirs, s._stats_seed_failures, - s._stats_n_nextuncreditedseed_banked, - s._stats_n_nextuncreditedseed_redeemed); + s._stats_nextstirs_banked, + s._stats_nextstirs_redeemed); } } @@ -3220,8 +3220,8 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) pr_info("RNG INFO: default bank size=%d total_bytes_requested=" WC_RNG_STAT_FMT "\n" " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" - " credited_reseeds=" WC_RNG_STAT_FMT - " uncredited_reseeds=" WC_RNG_STAT_FMT + " reseeds=" WC_RNG_STAT_FMT + " stirs=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" " locks_taken=" WC_RNG_STAT_FMT " locks_released=" WC_RNG_STAT_FMT @@ -3235,19 +3235,19 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) " pool_bytes_missed=" WC_RNG_STAT_FMT "\n" #endif #ifdef WC_RNG_HAVE_NEXT_SEED - " n_nextseed_primary_redeemed=" WC_RNG_STAT_FMT - " n_nextseed_RBGC_redeemed=" WC_RNG_STAT_FMT "\n" - " n_nextseed_banked=" WC_RNG_STAT_FMT - " n_nextuncreditedseed_banked=" WC_RNG_STAT_FMT - " n_nextuncreditedseed_redeemed=" WC_RNG_STAT_FMT "\n" + " nextseedsprimary_redeemed=" WC_RNG_STAT_FMT + " nextseedsRBGC_redeemed=" WC_RNG_STAT_FMT "\n" + " nextseedsbanked=" WC_RNG_STAT_FMT + " nextstirs_banked=" WC_RNG_STAT_FMT + " nextstirs_redeemed=" WC_RNG_STAT_FMT "\n" #endif , ctx->n_rngs, s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, - s._stats_credited_reseeds, - s._stats_uncredited_reseeds, + s._stats_reseeds, + s._stats_stirs, s._stats_seed_failures, s._stats_locks_taken, s._stats_locks_released, @@ -3261,11 +3261,11 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) ,s._stats_pool_bytes_missed #endif #ifdef WC_RNG_HAVE_NEXT_SEED - ,s._stats_n_nextseed_primary_redeemed - ,s._stats_n_nextseed_RBGC_redeemed - ,s._stats_n_nextseed_banked - ,s._stats_n_nextuncreditedseed_banked - ,s._stats_n_nextuncreditedseed_redeemed + ,s._stats_nextseedsprimary_redeemed + ,s._stats_nextseedsRBGC_redeemed + ,s._stats_nextseedsbanked + ,s._stats_nextstirs_banked + ,s._stats_nextstirs_redeemed #endif ); } @@ -3540,10 +3540,10 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, * Mix it in without entropy credit -- the reseed counter is * unmodified, so only the module's own seed source resets the * reseed schedule. */ - ret = wc_RNG_DRBG_Reseed_Uncredited(WC_RNG_BANK_INST_TO_RNG(drbg), + ret = wc_RNG_DRBG_Stir(WC_RNG_BANK_INST_TO_RNG(drbg), src, slen); if (ret != 0) { - pr_warn_once("WARNING: wc_RNG_DRBG_Reseed_Uncredited returned %d\n",ret); + pr_warn_once("WARNING: wc_RNG_DRBG_Stir returned %d\n",ret); ret = -EINVAL; goto out; } @@ -3815,7 +3815,7 @@ static int wc_linuxkm_drbg_seed(struct wc_rng_bank *ctx, ret = wc_rng_bank_seed_range(ctx, 0, LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE, seed, slen, WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT | - WC_RNG_BANK_FLAG_SEED_UNCREDITED); + WC_RNG_BANK_FLAG_STIR); if (ret != 0) { pr_err("wc_rng_bank_seed() in wc_linuxkm_drbg_seed() returned err %d.\n", ret); ret = -EINVAL; @@ -4121,23 +4121,23 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { } /* Mix without crediting the contributed entropy -- - * wc_RNG_DRBG_Reseed_Uncredited() leaves the reseed counter unmodified, + * wc_RNG_DRBG_Stir() leaves the reseed counter unmodified, * so only the module's own seed source resets the reseed schedule. */ - ret = wc_RNG_DRBG_Reseed_Uncredited(WC_RNG_BANK_INST_TO_RNG(drbg), buf, + ret = wc_RNG_DRBG_Stir(WC_RNG_BANK_INST_TO_RNG(drbg), buf, (word32)len); #ifdef WC_RNG_HAVE_NEXT_SEED /* The leased instance was just stirred directly, above. The daemon root -- * the one node the harvest wire otherwise never reaches -- is single-owner * and can't be stirred from here; deposit the fragment into its uncredited * accumulator instead (writer-safe without a lease: read-copy-store, see - * wc_RNG_DRBG_NextUncreditedSeedStore()), for consumption at the root's own + * wc_RNG_DRBG_NextStirStore()), for consumption at the root's own * next generate. The supplied entropy is unconditionally absorbed by - * wc_RNG_DRBG_NextUncreditedSeedStore() -- if nextUncreditedSeedLen is + * wc_RNG_DRBG_NextStirStore() -- if nextStirLen is * already full, the absorption is by xorbuf(). */ if (len > 0) { WC_RNG *stir_root = wc_rng_bank_daemon_root_get(ctx); if (stir_root != NULL) - (void)wc_RNG_DRBG_NextUncreditedSeedStore(stir_root, (const byte *)buf, + (void)wc_RNG_DRBG_NextStirStore(stir_root, (const byte *)buf, (word32)len); } #endif /* WC_RNG_HAVE_NEXT_SEED */ diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 95054fad773..d5a35dbee83 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -891,7 +891,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, * divergence burden), and never zeroized (racy, and * interleaved entropy of compatible provenance is harmless). */ - WOLFSSL_ATOMIC_STORE(drbg->nextUncreditedSeedLen, + WOLFSSL_ATOMIC_STORE(drbg->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif @@ -899,10 +899,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, ret = Hash256_DRBG_Reseed(drbg, seed, seedSz, additional, additionalSz); #ifdef WC_RNG_DEBUG_STATS if (ret == 0) { - if (credited) - ++rng->_stats_credited_reseeds; - else - ++rng->_stats_uncredited_reseeds; + ++rng->_stats_reseeds; } #endif goto out; @@ -930,7 +927,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { NextSeedPurge(&drbg512->nextSeedLen); /* see the SHA-256 arm re best-effort and no-zeroize. */ - WOLFSSL_ATOMIC_STORE(drbg512->nextUncreditedSeedLen, + WOLFSSL_ATOMIC_STORE(drbg512->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif @@ -939,10 +936,7 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, additional, additionalSz); #ifdef WC_RNG_DEBUG_STATS if (ret == 0) { - if (credited) - ++rng->_stats_credited_reseeds; - else - ++rng->_stats_uncredited_reseeds; + ++rng->_stats_reseeds; } #endif goto out; @@ -1476,7 +1470,7 @@ static int Hash_DRBG_Instantiate(DRBG_internal* drbg, const byte* seed, XMEMSET(drbg, 0, sizeof(DRBG_internal)); #ifdef WC_RNG_HAVE_NEXT_SEED wolfSSL_Atomic_Int_Init(&drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); - wolfSSL_Atomic_Int_Init(&drbg->nextUncreditedSeedLen, + wolfSSL_Atomic_Int_Init(&drbg->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); #endif drbg->heap = heap; @@ -1990,7 +1984,7 @@ static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg, XMEMSET(drbg, 0, sizeof(DRBG_SHA512_internal)); #ifdef WC_RNG_HAVE_NEXT_SEED wolfSSL_Atomic_Int_Init(&drbg->nextSeedLen, WC_DRBG_NEXT_SEED_EMPTY); - wolfSSL_Atomic_Int_Init(&drbg->nextUncreditedSeedLen, + wolfSSL_Atomic_Int_Init(&drbg->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); #endif drbg->heap = heap; @@ -2081,13 +2075,13 @@ static int Hash_DRBG_StirGenerate(WC_RNG* rng, const byte* add, word32 addSz) #endif #ifdef WC_RNG_DEBUG_STATS if (ret == 0) - ++rng->_stats_uncredited_reseeds; /* counts uncredited + ++rng->_stats_stirs; /* counts uncredited * stir-generates. */ #endif return ret; } -int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, +int wc_RNG_DRBG_Stir_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz) { @@ -2115,9 +2109,9 @@ int wc_RNG_DRBG_Reseed_Nonce_Uncredited(WC_RNG* rng, } } -int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, const byte* seed, word32 seedSz) +int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz) { - return wc_RNG_DRBG_Reseed_Nonce_Uncredited(rng, seed, seedSz, NULL, + return wc_RNG_DRBG_Stir_Nonce(rng, seed, seedSz, NULL, 0); } @@ -3391,14 +3385,14 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); - WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen, + WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); - WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen, + WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif @@ -3805,7 +3799,7 @@ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, } } else { - ret = wc_RNG_DRBG_Reseed_Nonce_Uncredited(rng, seed, SEED_SZ, nonce, + ret = wc_RNG_DRBG_Stir_Nonce(rng, seed, SEED_SZ, nonce, nonceSz); } } @@ -3820,7 +3814,7 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, return wc_RNG_DRBG_ReseedRBGC_local(rng, root, nonce, nonceSz, 1); } -int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, WC_RNG* root, +int wc_RNG_DRBG_StirRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz) { return wc_RNG_DRBG_ReseedRBGC_local(rng, root, nonce, nonceSz, 0); @@ -3935,7 +3929,7 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, * module's own reseed function, and the reseed counter is reset iff the * reseed succeeds. If nonceSz > 0, nonce is incorporated into the same * reseed derivation as (uncredited) additional input, with the semantics of - * wc_RNG_DRBG_Reseed_Uncredited(), in a single state transition. On failure + * wc_RNG_DRBG_Stir(), in a single state transition. On failure * the reseed counter is not reset and rng->status reflects the failure * exactly as a generate-time reseed failure would. The caller must hold * exclusive access to rng, as for all WC_RNG operations. */ @@ -4060,25 +4054,25 @@ static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, return MISSING_RNG_E; } -static WC_INLINE int NextUncreditedSeedPtrs(WC_RNG* rng, byte** seed, +static WC_INLINE int NextStirPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, wolfSSL_Atomic_Int** len) { #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - *seed = ((DRBG_internal*)rng->drbg)->nextUncreditedSeed; + *seed = ((DRBG_internal*)rng->drbg)->nextStir; *nextSeedSz = - (word32)sizeof(((DRBG_internal*)rng->drbg)->nextUncreditedSeed); - *len = &((DRBG_internal*)rng->drbg)->nextUncreditedSeedLen; + (word32)sizeof(((DRBG_internal*)rng->drbg)->nextStir); + *len = &((DRBG_internal*)rng->drbg)->nextStirLen; return 0; } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { - *seed = ((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeed; + *seed = ((DRBG_SHA512_internal*)rng->drbg512)->nextStir; *nextSeedSz = - (word32)sizeof(((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeed); - *len = &((DRBG_SHA512_internal*)rng->drbg512)->nextUncreditedSeedLen; + (word32)sizeof(((DRBG_SHA512_internal*)rng->drbg512)->nextStir); + *len = &((DRBG_SHA512_internal*)rng->drbg512)->nextStirLen; return 0; } #endif @@ -4135,7 +4129,7 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, } if (nonce) - ret = NextUncreditedSeedPtrs(rng, &seed, &nextSeedSz, &lenp); + ret = NextStirPtrs(rng, &seed, &nextSeedSz, &lenp); else ret = NextSeedPtrs(rng, &seed, &nextSeedSz, &lenp, &nextSeedRBGCStratum_p); @@ -4296,7 +4290,7 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, if (nonce != NULL) { WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_READY); #ifdef WC_RNG_DEBUG_STATS - ++rng->_stats_n_nextuncreditedseed_banked; + ++rng->_stats_nextstirs_banked; #endif return 0; } @@ -4311,7 +4305,7 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, return ret; } #ifdef WC_RNG_DEBUG_STATS - ++rng->_stats_n_nextseed_banked; + ++rng->_stats_nextseedsbanked; #endif return 0; } @@ -4327,7 +4321,7 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, return ret; } #ifdef WC_RNG_DEBUG_STATS - ++rng->_stats_n_nextseed_banked; + ++rng->_stats_nextseedsbanked; #endif return 0; } @@ -4465,10 +4459,10 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, if (ret == 0) { #ifdef WC_RNG_HAVE_RBGC if (*nextSeedRBGCStratum_p > 0) - ++rng->_stats_n_nextseed_RBGC_redeemed; + ++rng->_stats_nextseedsRBGC_redeemed; else #endif - ++rng->_stats_n_nextseed_primary_redeemed; + ++rng->_stats_nextseedsprimary_redeemed; } #endif @@ -4511,7 +4505,7 @@ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng) { * interleaved fragments of compatible provenance are harmless. A full * accumulator publishes WC_DRBG_NEXT_SEED_READY (no health test -- no * claim is being made) and blocks further deposits until consumed. */ -int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, const byte *nonce, +int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, word32 nonceSz) { if ((nonce == NULL) || (nonceSz == 0)) @@ -4530,7 +4524,7 @@ int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, const byte *nonce, * Use-once: the material is consumed (accumulation reopens) whether or not * the reseed succeeds. The buffer is never zeroized (racy against * depositors, and always a net entropy loss). */ -int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng) +int wc_RNG_DRBG_NextStirNow(WC_RNG* rng) { byte* seed; wolfSSL_Atomic_Int* lenp; @@ -4549,7 +4543,7 @@ int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng) if (rng->status != DRBG_OK) return RNG_FAILURE_E; - ret = NextUncreditedSeedPtrs(rng, &seed, &nextSeedSz, &lenp); + ret = NextStirPtrs(rng, &seed, &nextSeedSz, &lenp); if (ret != 0) { /* No DRBG instantiated -- nothing to stir (RDRAND et al.). */ return ret; @@ -4566,7 +4560,7 @@ int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng) #ifdef WC_RNG_DEBUG_STATS if (ret == 0) - ++rng->_stats_n_nextuncreditedseed_redeemed; + ++rng->_stats_nextstirs_redeemed; #endif WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); @@ -4670,14 +4664,14 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); - WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen, + WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); - WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen, + WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif @@ -4717,7 +4711,7 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) int stir_ready = 0; #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL) && - (WOLFSSL_ATOMIC_LOAD(((DRBG_internal *)rng->drbg)->nextUncreditedSeedLen) + (WOLFSSL_ATOMIC_LOAD(((DRBG_internal *)rng->drbg)->nextStirLen) == WC_DRBG_NEXT_SEED_READY)) { stir_ready = 1; @@ -4725,14 +4719,14 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL) && - (WOLFSSL_ATOMIC_LOAD(((DRBG_SHA512_internal *)rng->drbg512)->nextUncreditedSeedLen) + (WOLFSSL_ATOMIC_LOAD(((DRBG_SHA512_internal *)rng->drbg512)->nextStirLen) == WC_DRBG_NEXT_SEED_READY)) { stir_ready = 1; } #endif if (stir_ready) - (void)wc_RNG_DRBG_NextUncreditedSeedNow(rng); + (void)wc_RNG_DRBG_NextStirNow(rng); } #endif /* WC_RNG_HAVE_NEXT_SEED */ @@ -8940,8 +8934,8 @@ WOLFSSL_API int wc_rng_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, s->_stats_total_bytes_requested = rng->_stats_total_bytes_requested; s->_stats_total_bytes_produced = rng->_stats_total_bytes_produced; s->_stats_total_requests = rng->_stats_total_requests; - s->_stats_credited_reseeds = rng->_stats_credited_reseeds; - s->_stats_uncredited_reseeds = rng->_stats_uncredited_reseeds; + s->_stats_reseeds = rng->_stats_reseeds; + s->_stats_stirs = rng->_stats_stirs; s->_stats_seed_failures = rng->_stats_seed_failures; s->_stats_locks_taken = rng->_stats_locks_taken; s->_stats_locks_released = rng->_stats_locks_released; @@ -8955,11 +8949,11 @@ WOLFSSL_API int wc_rng_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, s->_stats_pool_bytes_missed = rng->_stats_pool_bytes_missed; #endif #ifdef WC_RNG_HAVE_NEXT_SEED - s->_stats_n_nextseed_primary_redeemed = rng->_stats_n_nextseed_primary_redeemed; - s->_stats_n_nextseed_RBGC_redeemed = rng->_stats_n_nextseed_RBGC_redeemed; - s->_stats_n_nextuncreditedseed_redeemed = rng->_stats_n_nextuncreditedseed_redeemed; - s->_stats_n_nextseed_banked = rng->_stats_n_nextseed_banked; - s->_stats_n_nextuncreditedseed_banked = rng->_stats_n_nextuncreditedseed_banked; + s->_stats_nextseedsprimary_redeemed = rng->_stats_nextseedsprimary_redeemed; + s->_stats_nextseedsRBGC_redeemed = rng->_stats_nextseedsRBGC_redeemed; + s->_stats_nextstirs_redeemed = rng->_stats_nextstirs_redeemed; + s->_stats_nextseedsbanked = rng->_stats_nextseedsbanked; + s->_stats_nextstirs_banked = rng->_stats_nextstirs_banked; #endif return 0; @@ -8975,8 +8969,8 @@ WOLFSSL_API int wc_rng_debug_stats_restore( rng->_stats_total_bytes_requested = s->_stats_total_bytes_requested; rng->_stats_total_bytes_produced = s->_stats_total_bytes_produced; rng->_stats_total_requests = s->_stats_total_requests; - rng->_stats_credited_reseeds = s->_stats_credited_reseeds; - rng->_stats_uncredited_reseeds = s->_stats_uncredited_reseeds; + rng->_stats_reseeds = s->_stats_reseeds; + rng->_stats_stirs = s->_stats_stirs; rng->_stats_seed_failures = s->_stats_seed_failures; rng->_stats_locks_taken = s->_stats_locks_taken; rng->_stats_locks_released = s->_stats_locks_released; @@ -8990,11 +8984,11 @@ WOLFSSL_API int wc_rng_debug_stats_restore( rng->_stats_pool_bytes_missed = s->_stats_pool_bytes_missed; #endif #ifdef WC_RNG_HAVE_NEXT_SEED - rng->_stats_n_nextseed_primary_redeemed = s->_stats_n_nextseed_primary_redeemed; - rng->_stats_n_nextseed_RBGC_redeemed = s->_stats_n_nextseed_RBGC_redeemed; - rng->_stats_n_nextuncreditedseed_redeemed = s->_stats_n_nextuncreditedseed_redeemed; - rng->_stats_n_nextseed_banked = s->_stats_n_nextseed_banked; - rng->_stats_n_nextuncreditedseed_banked = s->_stats_n_nextuncreditedseed_banked; + rng->_stats_nextseedsprimary_redeemed = s->_stats_nextseedsprimary_redeemed; + rng->_stats_nextseedsRBGC_redeemed = s->_stats_nextseedsRBGC_redeemed; + rng->_stats_nextstirs_redeemed = s->_stats_nextstirs_redeemed; + rng->_stats_nextseedsbanked = s->_stats_nextseedsbanked; + rng->_stats_nextstirs_banked = s->_stats_nextstirs_banked; #endif return 0; @@ -9009,8 +9003,8 @@ WOLFSSL_API int wc_rng_debug_stats_sum(struct wc_rng_debug_stats_snapshot *s, s->_stats_total_bytes_requested += rng->_stats_total_bytes_requested; s->_stats_total_bytes_produced += rng->_stats_total_bytes_produced; s->_stats_total_requests += rng->_stats_total_requests; - s->_stats_credited_reseeds += rng->_stats_credited_reseeds; - s->_stats_uncredited_reseeds += rng->_stats_uncredited_reseeds; + s->_stats_reseeds += rng->_stats_reseeds; + s->_stats_stirs += rng->_stats_stirs; s->_stats_seed_failures += rng->_stats_seed_failures; s->_stats_locks_taken += rng->_stats_locks_taken; s->_stats_locks_released += rng->_stats_locks_released; @@ -9024,11 +9018,11 @@ WOLFSSL_API int wc_rng_debug_stats_sum(struct wc_rng_debug_stats_snapshot *s, s->_stats_pool_bytes_missed += rng->_stats_pool_bytes_missed; #endif #ifdef WC_RNG_HAVE_NEXT_SEED - s->_stats_n_nextseed_primary_redeemed += rng->_stats_n_nextseed_primary_redeemed; - s->_stats_n_nextseed_RBGC_redeemed += rng->_stats_n_nextseed_RBGC_redeemed; - s->_stats_n_nextuncreditedseed_redeemed += rng->_stats_n_nextuncreditedseed_redeemed; - s->_stats_n_nextseed_banked += rng->_stats_n_nextseed_banked; - s->_stats_n_nextuncreditedseed_banked += rng->_stats_n_nextuncreditedseed_banked; + s->_stats_nextseedsprimary_redeemed += rng->_stats_nextseedsprimary_redeemed; + s->_stats_nextseedsRBGC_redeemed += rng->_stats_nextseedsRBGC_redeemed; + s->_stats_nextstirs_redeemed += rng->_stats_nextstirs_redeemed; + s->_stats_nextseedsbanked += rng->_stats_nextseedsbanked; + s->_stats_nextstirs_banked += rng->_stats_nextstirs_banked; #endif return 0; diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 7fcc198a374..da679004d2c 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -648,7 +648,7 @@ WOLFSSL_API int wc_rng_bank_checkout( else { if (((flags | bank->flags) & WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE) && (((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT))) || - (flags & WC_RNG_BANK_FLAG_SEED_UNCREDITED))) + (flags & WC_RNG_BANK_FLAG_STIR))) { ret = BAD_FUNC_ARG; goto out; @@ -1806,7 +1806,7 @@ static int rng_bank_spawn( if ((leaf_stack == NULL) == (leaf_heap == NULL)) return BAD_FUNC_ARG; - if (flags & (WC_RNG_BANK_FLAG_SEED_UNCREDITED | + if (flags & (WC_RNG_BANK_FLAG_STIR | WC_RNG_BANK_FLAG_FOR_RECOVERY)) return BAD_FUNC_ARG; @@ -1972,7 +1972,7 @@ WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, struct wc_rng_bank_inst *drbg; ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, flags & ~(word32) - (WC_RNG_BANK_FLAG_SEED_UNCREDITED | + (WC_RNG_BANK_FLAG_STIR | WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED)); if (ret != 0) { #ifdef WC_VERBOSE_RNG @@ -2002,8 +2002,8 @@ WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, #endif ret = BAD_STATE_E; } - else if ((ret = ((flags & WC_RNG_BANK_FLAG_SEED_UNCREDITED) - ? wc_RNG_DRBG_Reseed_Uncredited( + else if ((ret = ((flags & WC_RNG_BANK_FLAG_STIR) + ? wc_RNG_DRBG_Stir( WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz) : wc_RNG_DRBG_Reseed( WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz))) @@ -2056,13 +2056,13 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, /* wc_rng_bank_reseed() must walk every instance by explicit index -- forbid * flags that would let wc_rng_bank_checkout() pick a different instance * than requested. Same restriction applies in wc_rng_bank_seed(). - * WC_RNG_BANK_FLAG_SEED_UNCREDITED applies only to wc_rng_bank_seed() -- + * WC_RNG_BANK_FLAG_STIR applies only to wc_rng_bank_seed() -- * a bank reseed is always from the module's own seed source, and always * credited. */ if (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | - WC_RNG_BANK_FLAG_SEED_UNCREDITED | + WC_RNG_BANK_FLAG_STIR | WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED | WC_RNG_BANK_FLAG_FOR_RECOVERY)) return BAD_FUNC_ARG; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 71727218937..e6dc549105c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -952,7 +952,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); #endif #ifdef WC_RNG_HAVE_NEXT_SEED -WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void); #endif #ifdef WC_RNG_HAVE_POOL WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); @@ -2641,7 +2641,7 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("RNGRBGC test passed!\n"); #endif #ifdef WC_RNG_HAVE_NEXT_SEED - if ((ret = rng_drbg_nextseed_test()) != 0) + if ((ret = rng_drbg_nextseedstest()) != 0) TEST_FAIL("RNGNXTS test failed!\n", ret); else TEST_PASS("RNGNXTS test passed!\n"); @@ -26995,9 +26995,9 @@ static wc_test_ret_t _rng_test(WC_RNG* rng) return WC_TEST_RET_ENC_EC(ret); /* the forced interval reseed is credited, and the request is * fully served */ - RNG_STATS_EXPECT(rng, _stats_credited_reseeds, 1, + RNG_STATS_EXPECT(rng, _stats_reseeds, 1, return WC_TEST_RET_ENC_I((int)rng_stats_d_)); - RNG_STATS_EXPECT(rng, _stats_uncredited_reseeds, 0, + RNG_STATS_EXPECT(rng, _stats_stirs, 0, return WC_TEST_RET_ENC_I((int)rng_stats_d_)); RNG_STATS_EXPECT(rng, _stats_total_requests, 1, return WC_TEST_RET_ENC_I((int)rng_stats_d_)); @@ -28783,7 +28783,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #endif - ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_SEED_UNCREDITED); + ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_STIR); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_FOR_RECOVERY); @@ -28875,7 +28875,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_SEED_UNCREDITED); + ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_STIR); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_rng_bank_checkout(bank, &rng_inst, 0, 10, WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_FOR_RECOVERY); @@ -29255,7 +29255,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - api_ret = wc_RNG_DRBG_Reseed_Uncredited(root, matter, sizeof(matter)); + api_ret = wc_RNG_DRBG_Stir(root, matter, sizeof(matter)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if (present) { @@ -29293,7 +29293,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) if ((api_ret != 0) || (c1 > 2)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* the scheduled reseed rides the generate, credited */ - RNG_STATS_EXPECT(root, _stats_credited_reseeds, 1, + RNG_STATS_EXPECT(root, _stats_reseeds, 1, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); RNG_STATS_EXPECT(root, _stats_total_requests, 1, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); @@ -29315,9 +29315,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* both credited; the nonce is additional input, not an * uncredited reseed */ - RNG_STATS_EXPECT(root, _stats_credited_reseeds, 2, + RNG_STATS_EXPECT(root, _stats_reseeds, 2, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); - RNG_STATS_EXPECT(root, _stats_uncredited_reseeds, 0, + RNG_STATS_EXPECT(root, _stats_stirs, 0, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } if (wc_RNG_DRBG_Reseed_Now(NULL, NULL, 0) != @@ -29577,7 +29577,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) * it: NOT_READY_E, with counter and flag untouched by * construction. */ XMEMSET(block, 0x5a, sizeof(block)); - api_ret = wc_RNG_DRBG_Reseed_Uncredited(WC_RNG_BANK_INST_TO_RNG(held), + api_ret = wc_RNG_DRBG_Stir(WC_RNG_BANK_INST_TO_RNG(held), block, sizeof(block)); if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30126,11 +30126,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* target: two credited chain reseeds; source: two fully-served * seed draws */ - RNG_STATS_EXPECT2(&leaf, _stats_credited_reseeds, 2, + RNG_STATS_EXPECT2(&leaf, _stats_reseeds, 2, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); RNG_STATS_EXPECT2(&leaf, _stats_RBGC_reseeds, 2, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); - RNG_STATS_EXPECT2(&leaf, _stats_uncredited_reseeds, 0, + RNG_STATS_EXPECT2(&leaf, _stats_stirs, 0, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); RNG_STATS_EXPECT(&root, _stats_total_requests, 2, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); @@ -30162,7 +30162,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (present) { /* the primary reseed precedes the byte production, so the served * bytes are not chain-provenance */ - RNG_STATS_EXPECT2(&leaf, _stats_credited_reseeds, 1, + RNG_STATS_EXPECT2(&leaf, _stats_reseeds, 1, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); RNG_STATS_EXPECT2(&leaf, _stats_total_bytes_produced, sizeof(buf), ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); @@ -30246,7 +30246,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) /* uncredited chained reseeds are unrestricted (stirs claim * nothing): any source stratum, target stratum untouched. */ - api_ret = wc_RNG_DRBG_ReseedRBGC_Uncredited(&extra, pleaf, NULL, 0); + api_ret = wc_RNG_DRBG_StirRBGC(&extra, pleaf, NULL, 0); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_DRBG_GetRBGCStratum(&extra); @@ -30536,7 +30536,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) * source-free consumption resetting the reseed counter, use-once * emptying, and the argument contracts. DRBG-internal probes are gated * at runtime on wc_RNG_DRBG_Present(). */ -WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) { wc_test_ret_t ret = 0; int api_ret; @@ -30552,7 +30552,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) RNG_STATS_DECLS; - WOLFSSL_ENTER("rng_drbg_nextseed_test"); + WOLFSSL_ENTER("rng_drbg_nextseedstest"); WC_ALLOC_VAR_EX(root, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, @@ -30671,12 +30671,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* redemption of a primary-provenance bank: credited, counted as a * primary redemption */ - RNG_STATS_EXPECT(root, _stats_credited_reseeds, 1, + RNG_STATS_EXPECT(root, _stats_reseeds, 1, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); - RNG_STATS_EXPECT(root, _stats_n_nextseed_primary_redeemed, 1, + RNG_STATS_EXPECT(root, _stats_nextseedsprimary_redeemed, 1, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); #ifdef WC_RNG_HAVE_RBGC - RNG_STATS_EXPECT(root, _stats_n_nextseed_RBGC_redeemed, 0, + RNG_STATS_EXPECT(root, _stats_nextseedsRBGC_redeemed, 0, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); #endif api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); @@ -30713,11 +30713,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* the nonce rides as additional input: the redemption is still one * credited, primary-provenance reseed */ - RNG_STATS_EXPECT(root, _stats_credited_reseeds, 1, + RNG_STATS_EXPECT(root, _stats_reseeds, 1, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); - RNG_STATS_EXPECT(root, _stats_uncredited_reseeds, 0, + RNG_STATS_EXPECT(root, _stats_stirs, 0, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); - RNG_STATS_EXPECT(root, _stats_n_nextseed_primary_redeemed, 1, + RNG_STATS_EXPECT(root, _stats_nextseedsprimary_redeemed, 1, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); if (api_ret != 0) @@ -30726,49 +30726,49 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) ERROR_OUT(WC_TEST_RET_ENC_I((int)cur), out); } - /* --- uncredited stir aperture (NextUncreditedSeed) lifecycle --- */ + /* --- uncredited stir aperture (NextStir) lifecycle --- */ if (present) { byte frag[16]; XMEMSET(frag, 0x71, sizeof(frag)); - if (wc_RNG_DRBG_NextUncreditedSeedStore(NULL, frag, sizeof(frag)) != + if (wc_RNG_DRBG_NextStirStore(NULL, frag, sizeof(frag)) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - if (wc_RNG_DRBG_NextUncreditedSeedStore(root, NULL, 1) != + if (wc_RNG_DRBG_NextStirStore(root, NULL, 1) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - if (wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, 0) != + if (wc_RNG_DRBG_NextStirStore(root, frag, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { ERROR_OUT(WC_TEST_RET_ENC_NC, out); } /* partial accumulation is not consumable. */ - api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, + api_ret = wc_RNG_DRBG_NextStirStore(root, frag, sizeof(frag)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + api_ret = wc_RNG_DRBG_NextStirNow(root); if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* fill to the top: READY; excess deposits absorbed by xorbuf(); * oversize deposits clamp. */ - for (i = 0; i < (int)(WC_DRBG_NEXT_UNCREDITED_SEED_LEN / + for (i = 0; i < (int)(WC_DRBG_NEXT_STIR_LEN / sizeof(frag)); i++) { - api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, + api_ret = wc_RNG_DRBG_NextStirStore(root, frag, sizeof(frag)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); } /* deposits on a READY accumulator fold in place using xorbuf() * (advisory sentinel): absorbed, never refused. */ - api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, sizeof(frag)); + api_ret = wc_RNG_DRBG_NextStirStore(root, frag, sizeof(frag)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30780,7 +30780,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) api_ret = wc_RNG_DRBG_GetReseedCtr(root, &ctr_before); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + api_ret = wc_RNG_DRBG_NextStirNow(root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_DRBG_GetReseedCtr(root, &ctr_after); @@ -30790,26 +30790,26 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } #else - api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + api_ret = wc_RNG_DRBG_NextStirNow(root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); #endif /* use-once: accumulation reopened. */ - api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + api_ret = wc_RNG_DRBG_NextStirNow(root); if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, + api_ret = wc_RNG_DRBG_NextStirStore(root, frag, sizeof(frag)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* top up and verify the universal opportunistic consume at * generate: post-generate, the accumulator is spent. */ - for (i = 0; i < (int)(WC_DRBG_NEXT_UNCREDITED_SEED_LEN / + for (i = 0; i < (int)(WC_DRBG_NEXT_STIR_LEN / sizeof(frag)); i++) { - api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(root, frag, + api_ret = wc_RNG_DRBG_NextStirStore(root, frag, sizeof(frag)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30817,7 +30817,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) api_ret = wc_RNG_GenerateBlock(root, buf, sizeof(buf)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(root); + api_ret = wc_RNG_DRBG_NextStirNow(root); if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); } @@ -30828,7 +30828,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) if (present) { WC_RNG leaf; WC_RNG_lock_arg_t lock_state; - byte frag64[WC_DRBG_NEXT_UNCREDITED_SEED_LEN]; + byte frag64[WC_DRBG_NEXT_STIR_LEN]; XMEMSET(frag64, 0x5e, sizeof(frag64)); api_ret = wc_InitRngNonceRBGC(&leaf, root, NULL, 0, @@ -30839,7 +30839,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* deposit post-event (the event purge emptied the accumulator). */ - api_ret = wc_RNG_DRBG_NextUncreditedSeedStore(&leaf, frag64, + api_ret = wc_RNG_DRBG_NextStirStore(&leaf, frag64, sizeof(frag64)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30847,7 +30847,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void) * atomically ahead of the racy counter); the use-once aperture * reopens EMPTY regardless -- stirs are best-effort, and * accumulation simply resumes. */ - api_ret = wc_RNG_DRBG_NextUncreditedSeedNow(&leaf); + api_ret = wc_RNG_DRBG_NextStirNow(&leaf); if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_lock_read(&leaf, &lock_state); diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index 73682bb5c89..c8931b71e5f 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -265,7 +265,7 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); #endif extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); -extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseed_test(void); +extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); #endif /* WC_NO_RNG */ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 15c3733f698..e24022a0653 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -369,7 +369,7 @@ struct OS_Seed { * source-fed (re)seeds in the module (gather SEED_SZ + SEED_BLOCK_SZ, apply * the block-offset remainder). */ #define WC_DRBG_NEXT_SEED_LEN (WC_DRBG_SEED_SZ + WC_DRBG_SEED_BLOCK_SZ) - #define WC_DRBG_NEXT_UNCREDITED_SEED_LEN 64 + #define WC_DRBG_NEXT_STIR_LEN 64 #endif #ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED @@ -392,8 +392,8 @@ struct DRBG_internal { #ifdef WC_RNG_HAVE_RBGC int nextSeedRBGCStratum; #endif - byte nextUncreditedSeed[WC_DRBG_NEXT_UNCREDITED_SEED_LEN]; - WC_DRBG_nextSeedLen_t nextUncreditedSeedLen; + byte nextStir[WC_DRBG_NEXT_STIR_LEN]; + WC_DRBG_nextSeedLen_t nextStirLen; #endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -418,8 +418,8 @@ struct DRBG_SHA512_internal { #ifdef WC_RNG_HAVE_RBGC int nextSeedRBGCStratum; #endif - byte nextUncreditedSeed[WC_DRBG_NEXT_UNCREDITED_SEED_LEN]; - WC_DRBG_nextSeedLen_t nextUncreditedSeedLen; + byte nextStir[WC_DRBG_NEXT_STIR_LEN]; + WC_DRBG_nextSeedLen_t nextStirLen; #endif void* heap; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -478,8 +478,8 @@ struct WC_RNG { wc_rng_debug_counter_t _stats_total_bytes_requested; wc_rng_debug_counter_t _stats_total_bytes_produced; wc_rng_debug_counter_t _stats_total_requests; - wc_rng_debug_counter_t _stats_credited_reseeds; - wc_rng_debug_counter_t _stats_uncredited_reseeds; + wc_rng_debug_counter_t _stats_reseeds; + wc_rng_debug_counter_t _stats_stirs; wc_rng_debug_counter_t _stats_seed_failures; #endif #ifdef WC_RNG_HAVE_RBGC @@ -516,11 +516,11 @@ struct WC_RNG { #ifdef WC_RNG_DEBUG_STATS #ifdef WC_RNG_HAVE_NEXT_SEED - wc_rng_debug_counter_t _stats_n_nextseed_primary_redeemed; - wc_rng_debug_counter_t _stats_n_nextseed_RBGC_redeemed; - wc_rng_debug_counter_t _stats_n_nextuncreditedseed_redeemed; - wc_rng_debug_counter_t _stats_n_nextseed_banked; - wc_rng_debug_counter_t _stats_n_nextuncreditedseed_banked; + wc_rng_debug_counter_t _stats_nextseedsprimary_redeemed; + wc_rng_debug_counter_t _stats_nextseedsRBGC_redeemed; + wc_rng_debug_counter_t _stats_nextstirs_redeemed; + wc_rng_debug_counter_t _stats_nextseedsbanked; + wc_rng_debug_counter_t _stats_nextstirs_banked; #endif #endif @@ -773,10 +773,10 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); WOLFSSL_API int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); - WOLFSSL_API int wc_RNG_DRBG_Reseed_Uncredited(WC_RNG* rng, + WOLFSSL_API int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz); - WOLFSSL_API int wc_RNG_DRBG_Reseed_Nonce_Uncredited( + WOLFSSL_API int wc_RNG_DRBG_Stir_Nonce( WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); WOLFSSL_API int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, @@ -920,7 +920,7 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); #endif /* !WC_NO_CONSTRUCTORS */ WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz); - WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC_Uncredited(WC_RNG* rng, + WOLFSSL_API int wc_RNG_DRBG_StirRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz); @@ -947,10 +947,10 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); const byte* nonce, word32 nonceSz); WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); - WOLFSSL_API int wc_RNG_DRBG_NextUncreditedSeedStore(WC_RNG* rng, + WOLFSSL_API int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, word32 nonceSz); - WOLFSSL_API int wc_RNG_DRBG_NextUncreditedSeedNow(WC_RNG* rng); + WOLFSSL_API int wc_RNG_DRBG_NextStirNow(WC_RNG* rng); #endif /* WC_RNG_HAVE_NEXT_SEED */ @@ -1007,8 +1007,8 @@ struct wc_rng_debug_stats_snapshot { wc_rng_debug_counter_t _stats_total_bytes_requested; wc_rng_debug_counter_t _stats_total_bytes_produced; wc_rng_debug_counter_t _stats_total_requests; - wc_rng_debug_counter_t _stats_credited_reseeds; - wc_rng_debug_counter_t _stats_uncredited_reseeds; + wc_rng_debug_counter_t _stats_reseeds; + wc_rng_debug_counter_t _stats_stirs; wc_rng_debug_counter_t _stats_seed_failures; wc_rng_debug_counter_t _stats_locks_taken; wc_rng_debug_counter_t _stats_locks_released; @@ -1022,11 +1022,11 @@ struct wc_rng_debug_stats_snapshot { wc_rng_debug_counter_t _stats_pool_bytes_missed; #endif #ifdef WC_RNG_HAVE_NEXT_SEED - wc_rng_debug_counter_t _stats_n_nextseed_primary_redeemed; - wc_rng_debug_counter_t _stats_n_nextseed_RBGC_redeemed; - wc_rng_debug_counter_t _stats_n_nextuncreditedseed_redeemed; - wc_rng_debug_counter_t _stats_n_nextseed_banked; - wc_rng_debug_counter_t _stats_n_nextuncreditedseed_banked; + wc_rng_debug_counter_t _stats_nextseedsprimary_redeemed; + wc_rng_debug_counter_t _stats_nextseedsRBGC_redeemed; + wc_rng_debug_counter_t _stats_nextstirs_redeemed; + wc_rng_debug_counter_t _stats_nextseedsbanked; + wc_rng_debug_counter_t _stats_nextstirs_banked; #endif }; diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 659ee9d5938..dd504f876c2 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -52,7 +52,7 @@ #define WC_RNG_BANK_FLAG_NO_VECTOR_OPS (1U << 3) #define WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST (1U << 4) #define WC_RNG_BANK_FLAG_AFFINITY_LOCK (1U << 5) -#define WC_RNG_BANK_FLAG_SEED_UNCREDITED (1U << 6) +#define WC_RNG_BANK_FLAG_STIR (1U << 6) #define WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED (1U << 7) #define WC_RNG_BANK_FLAG_FOR_RECOVERY (1U << 8) #define WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY (1U << 9) @@ -635,7 +635,7 @@ WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot * Pre-v7 FIPS boundaries do not export the DRBG accessor and reseed * scheduling services that wolfcrypt/src/random.c supplies as of FIPS v7 * (wc_RNG_GetStatus(), wc_RNG_DRBG_Present(), wc_RNG_DRBG_GetReseedCtr(), - * wc_RNG_DRBG_ScheduleReseed(), wc_RNG_DRBG_Reseed_Uncredited(), and + * wc_RNG_DRBG_ScheduleReseed(), wc_RNG_DRBG_Stir(), and * wc_RNG_DRBG_Reseed_Now()). Supply source-compatible static fallbacks * here, implemented via the public DRBG struct definitions in the legacy * random.h. These fallbacks are the historic direct-access mechanism, now @@ -784,7 +784,7 @@ WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) } #if FIPS_VERSION3_NE(5,2,4) -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Uncredited( +WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Stir( WC_RNG* rng, const byte* seed, word32 seedSz) { wc_drbg_reseed_ctr_t saved_ctr; @@ -837,7 +837,7 @@ WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Now( /* On the legacy boundary, nonce incorporation is a separate * (uncredited) transition following the reseed, rather than part of * the same reseed derivation. */ - ret = wc_RNG_DRBG_Reseed_Uncredited(rng, nonce, nonceSz); + ret = wc_RNG_DRBG_Stir(rng, nonce, nonceSz); } if ((ret != 0) && From 4dafa01992a5f1b7d5a933303b212839a8c821bf Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 09:16:04 +0000 Subject: [PATCH 053/102] test: correct FIPS gating of hkdf_cryptocb_async_test() FIPS builds force crypto callbacks off, so a test that asserts a callback took effect is structurally incompatible with any FIPS build, not just pre-v7 ones. The guard admitted FIPS v7 and later, where the test cannot pass. Narrow both the definition guard and the call site to !defined(HAVE_FIPS). --- wolfcrypt/test/test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index e6dc549105c..9189c5ff5e7 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -88399,7 +88399,7 @@ static wc_test_ret_t shake_cb_copy_free_test(myCryptoDevCtx* myCtx, #if defined(HAVE_HKDF) && !defined(NO_HMAC) && \ !defined(NO_SHA256) && !defined(HAVE_SELFTEST) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0)) && \ + !defined(HAVE_FIPS) && \ !defined(WC_TEST_NO_CRYPTOCB_SW_TEST) /* Bound retries so a broken contract fails instead of spinning. */ @@ -89287,7 +89287,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) if (ret == 0) ret = hkdf_test(); #if !defined(NO_SHA256) && !defined(HAVE_SELFTEST) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0)) + !defined(HAVE_FIPS) if (ret == 0) ret = hkdf_cryptocb_async_test(&myCtx); #endif From e035f6dc1e4f3ab270f847c152ee3f5bf31349a0 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 10:16:04 +0000 Subject: [PATCH 054/102] random: correct HAVE_HASHDRBG/NO_SHA256 guards in _InitRng() Several blocks in _InitRng() were guarded on the DRBG type alone, without also requiring HAVE_HASHDRBG. Configurations that build the SHA-512 DRBG without the SHA-256 one, or without the hash DRBG at all, could reach code referring to objects that do not exist in that configuration. No functional change in the configurations that already built. --- wolfcrypt/src/random.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index d5a35dbee83..09b4b577a30 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -2865,7 +2865,7 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, } if (ret != 0) { - #ifndef NO_SHA256 + #if defined(HAVE_HASHDRBG) && !defined(NO_SHA256) if (rng->drbgType == WC_DRBG_SHA256) { if (drbg_instantiated) { (void)Hash_DRBG_Uninstantiate( @@ -2886,8 +2886,8 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, rng->drbg_scratch = NULL; #endif /* WOLFSSL_SMALL_STACK_CACHE */ } - #endif /* !NO_SHA256 */ - #ifdef WOLFSSL_DRBG_SHA512 + #endif /* HAVE_HASHDRBG && !NO_SHA256 */ + #if defined(HAVE_HASHDRBG) && defined(WOLFSSL_DRBG_SHA512) if (rng->drbgType == WC_DRBG_SHA512) { if (drbg_instantiated) { (void)Hash512_DRBG_Uninstantiate( @@ -2907,7 +2907,7 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, rng->drbg512_scratch = NULL; #endif /* WOLFSSL_SMALL_STACK_CACHE */ } - #endif + #endif /* HAVE_HASHDRBG && WOLFSSL_DRBG_SHA512 */ #ifdef WOLFSSL_SMALL_STACK_CACHE XFREE(rng->newSeed_buf, rng->heap, DYNAMIC_TYPE_SEED); rng->newSeed_buf = NULL; From b50737a82e112654a988c0f3e9613b47ebe3841e Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 11:16:04 +0000 Subject: [PATCH 055/102] random: fix misattributed diagnostics and stale comment wording Hash256_DRBG_Reseed() carried two diagnostics naming Hash_DRBG_Reseed(), its caller, which sends anyone reading a log or a wc_MemZero_Add() report to the wrong function. Also reflows two declarations whose continuation lines were indented to a column that no longer matches their return type, and completes a sentence in the wc_RNG_DRBG_NextStirStore() comment. --- wolfcrypt/src/random.c | 6 +++--- wolfssl/wolfcrypt/random.h | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 09b4b577a30..fab30e4e80a 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -724,7 +724,7 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, #endif XMEMSET(newV, 0, DRBG_SEED_LEN); #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Add("Hash_DRBG_Reseed newV", newV, DRBG_SEED_LEN); + wc_MemZero_Add("Hash256_DRBG_Reseed newV", newV, DRBG_SEED_LEN); #endif ret = Hash_df(drbg, newV, DRBG_SEED_LEN, drbgReseed, @@ -752,7 +752,7 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, #ifdef WC_VERBOSE_RNG if (ret != 0) - WOLFSSL_DEBUG_PRINTF("ERROR: Hash_DRBG_Reseed failed with err %d.", + WOLFSSL_DEBUG_PRINTF("ERROR: Hash256_DRBG_Reseed failed with err %d.", ret); #endif @@ -4523,7 +4523,7 @@ int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, * is unchanged -- a stir must never masquerade as recovery or promotion. * Use-once: the material is consumed (accumulation reopens) whether or not * the reseed succeeds. The buffer is never zeroized (racy against - * depositors, and always a net entropy loss). */ + * depositors, and zeroing is always a net entropy loss). */ int wc_RNG_DRBG_NextStirNow(WC_RNG* rng) { byte* seed; diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index e24022a0653..e234f06e181 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -774,8 +774,8 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); word32 seedSz, const byte *nonce, word32 nonceSz); WOLFSSL_API int wc_RNG_DRBG_Stir(WC_RNG* rng, - const byte* seed, - word32 seedSz); + const byte* seed, + word32 seedSz); WOLFSSL_API int wc_RNG_DRBG_Stir_Nonce( WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); @@ -948,8 +948,7 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); word32 nonceSz); WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); WOLFSSL_API int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, - const byte *nonce, - word32 nonceSz); + const byte *nonce, word32 nonceSz); WOLFSSL_API int wc_RNG_DRBG_NextStirNow(WC_RNG* rng); #endif /* WC_RNG_HAVE_NEXT_SEED */ From ea75ee7d3d9d52c633980a1ab336ded79953ba29 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 12:16:04 +0000 Subject: [PATCH 056/102] random: treat a NextSeed consumption failure as a DRBG failure wc_RNG_GenerateBlock() discarded the return value of the banked-next-seed consumption path. A hard failure there leaves the instance's state in an indeterminate condition, which is precisely the situation the DRBG_FAILED status exists to represent, and callers were given output anyway. Set the status and return RNG_FAILURE_E so that the instance is taken out of service and the caller learns of it, matching how every other reseed-path failure in this file is handled. --- wolfcrypt/src/random.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index fab30e4e80a..ae64424e3c3 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -4691,11 +4691,15 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) * forced-reseed machinery. */ int banked_stratum = wc_RNG_DRBG_GetNextSeedRBGCStratum(rng); if (banked_stratum >= 0) { - if ((WOLFSSL_ATOMIC_LOAD(rng->lock) & - WC_RNG_LOCK_ENTROPY_INVALIDATED) || + if (((WOLFSSL_ATOMIC_LOAD(rng->lock) & + WC_RNG_LOCK_ENTROPY_INVALIDATED)) + || ((rng->RBGCStratum > 0) && (banked_stratum == 0))) { - (void)wc_RNG_DRBG_NextSeedNow(rng); + if (wc_RNG_DRBG_NextSeedNow(rng) != 0) { + rng->status = DRBG_FAILED; + return RNG_FAILURE_E; + } } } } From 4f905ecbbb1dcc8ca883c733740b9938c8023a61 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 13:16:04 +0000 Subject: [PATCH 057/102] test: enable the RBGC tests on the pre-v7 FIPS boundary The RBGC test entry points were gated on WC_RNG_HAVE_RBGC alone, and the pre-v7 copy of rng_drbg_rbgc_test() pulled rng_bank.h in with WC_RNG_BANK_SUPPORT defined locally and then undefined again, so that the RNG-level compat shims were visible only inside that one translation region. Gate the tests on the boundary version or on WC_RNG_BANK_SUPPORT actually being configured, and drop the local define/undef dance in favour of the real setting. The shims are needed by more than that one test. --- wolfcrypt/test/test.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 9189c5ff5e7..6350a3aa7b3 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -948,7 +948,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); #endif -#ifdef WC_RNG_HAVE_RBGC +#if defined(WC_RNG_HAVE_RBGC) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) || \ + defined(WC_RNG_BANK_SUPPORT)) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void); #endif #ifdef WC_RNG_HAVE_NEXT_SEED @@ -2634,7 +2636,9 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ else TEST_PASS("RNGINVAL test passed!\n"); #endif -#ifdef WC_RNG_HAVE_RBGC +#if defined(WC_RNG_HAVE_RBGC) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) || \ + defined(WC_RNG_BANK_SUPPORT)) if ((ret = rng_drbg_rbgc_test()) != 0) TEST_FAIL("RNGRBGC test failed!\n", ret); else @@ -30341,14 +30345,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) return ret; } -#else /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ +#elif defined(WC_RNG_BANK_SUPPORT) -#ifndef WC_RNG_BANK_SUPPORT - /* needed for compat setup */ - #define WC_RNG_BANK_SUPPORT - #include - #undef WC_RNG_BANK_SUPPORT -#endif +/* On old FIPS, WC_RNG_BANK_SUPPORT is needed for RNG-level compat shims. */ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) { From 395402e4492dea77f32811c609d23210d740eeb5 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 14:16:04 +0000 Subject: [PATCH 058/102] rng_bank: harden wc_rng_bank_seed_range() Several problems in one function, all of them about failing silently: - No argument validation. A buffer/length pair with a NULL pointer and a nonzero length reached the callees, where the STIR and reseed arms disagreed about what happens -- one returned BAD_FUNC_ARG after a checkout, the other dropped the input without an error. - An instance with no DRBG (the HAVE_INTEL_RDRAND bypass in _InitRng()) was "seeded" successfully, having absorbed nothing. Report NOT_COMPILED_IN instead: we have no way to seed it, so don't pretend we can. - The checkin diagnostic printed the reseed's error code rather than the checkin's, and was the only message in the loop not gated on WC_RNG_BANK_FLAG_QUIET. - Messages named wc_rng_bank_seed() from inside the _range() function. The no-DRBG case is a behaviour change: callers that previously got 0 now get NOT_COMPILED_IN. That is the point -- the previous answer was not true. --- wolfcrypt/src/rng_bank.c | 91 ++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 26 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index da679004d2c..6c1107be4a1 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -1914,9 +1914,10 @@ WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int bank_is_default = 0; #endif - /* wc_rng_bank_seed() must walk every instance by explicit index -- forbid - * flags that would let wc_rng_bank_checkout() pick a different instance - * than requested. Same restriction applies in wc_rng_bank_reseed(). + /* wc_rng_bank_seed_range() must walk every instance in range by explicit + * index -- forbid flags that would let wc_rng_bank_checkout() pick a + * different instance than requested. Same restriction applies in + * wc_rng_bank_reseed_range(). */ if (flags & (WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | @@ -1927,6 +1928,15 @@ WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, if (first_inst < 0) return BAD_INDEX_E; + if (((seedSz > 0) && (seed == NULL)) || + ((nonceSz > 0) && (nonce == NULL))) + { + return BAD_FUNC_ARG; + } + + if ((seedSz == 0) && (nonceSz > 0)) + return BAD_FUNC_ARG; + if (bank == NULL) { #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (seedSz == 0) { @@ -1971,52 +1981,81 @@ WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, for (n = last_inst; n >= first_inst; --n) { struct wc_rng_bank_inst *drbg; ret = wc_rng_bank_checkout(bank, &drbg, n, timeout_secs, - flags & ~(word32) - (WC_RNG_BANK_FLAG_STIR | - WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED)); + flags & ~(word32)WC_RNG_BANK_FLAG_STIR); if (ret != 0) { #ifdef WC_VERBOSE_RNG if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_seed(): wc_rng_bank_checkout() for " + "WARNING: wc_rng_bank_seed_range(): " + "wc_rng_bank_checkout() for " "inst#%d returned err %d.\n", n, ret); #endif break; } - /* Note that a NULL DRBG doesn't necessarily indicate failure: - * _InitRng() bypasses DRBG instantiation when the CPU has RDRAND - * (HAVE_INTEL_RDRAND), leaving a usable instance with drbg NULL and - * status WC_DRBG_OK. wc_RNG_DRBG_Reseed() gracefully handles that case - * itself (random.c returns success for a NULL DRBG under RDRAND), so - * let it through rather than calling it an error. - */ else if (wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(drbg)) != WC_DRBG_OK) { #ifdef WC_VERBOSE_RNG if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_seed(): inst#%d is out of service " + "WARNING: wc_rng_bank_seed_range(): inst#%d is out of service " "(status %d).\n", n, wc_RNG_GetStatus(WC_RNG_BANK_INST_TO_RNG(drbg))); #endif ret = BAD_STATE_E; } - else if ((ret = ((flags & WC_RNG_BANK_FLAG_STIR) - ? wc_RNG_DRBG_Stir( - WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz) - : wc_RNG_DRBG_Reseed( - WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz))) - != 0) - { + else if (! wc_RNG_DRBG_Present(WC_RNG_BANK_INST_TO_RNG(drbg))) { + /* Note that a NULL DRBG doesn't necessarily indicate a degraded + * RNG: _InitRng() bypasses DRBG instantiation for + * HAVE_INTEL_RDRAND. We just have no way to seed it, so don't + * pretend we can. + */ + ret = NOT_COMPILED_IN; + } + else { + if (flags & WC_RNG_BANK_FLAG_STIR) { + ret = wc_RNG_DRBG_Stir_Nonce( + WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz, + nonce, nonceSz); #ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_seed(): Hash_DRBG_Reseed() for inst#%d " - "returned %d\n", n, ret); + if ((ret != 0) && (! (bank->flags & WC_RNG_BANK_FLAG_QUIET))) { + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_seed_range(): " + "wc_RNG_DRBG_Stir_Nonce() for inst#%d " + "returned %d\n", n, ret); + } #endif + } + else { + ret = wc_RNG_DRBG_Reseed_Nonce( + WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz, + nonce, nonceSz); +#ifdef WC_VERBOSE_RNG + if ((ret != 0) && (! (bank->flags & WC_RNG_BANK_FLAG_QUIET))) { + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_seed_range(): " + "wc_RNG_DRBG_Reseed_Nonce() for inst#%d " + "returned %d\n", n, ret); + } +#endif + } } - (void)wc_rng_bank_checkin(bank, &drbg); + { + int checkin_ret = wc_rng_bank_checkin(bank, &drbg); + if (checkin_ret != 0) { + if (ret == 0) + ret = checkin_ret; +#ifdef WC_VERBOSE_RNG + if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET)) { + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_seed_range(): " + "wc_rng_bank_checkin() for " + "inst#%d returned err %d.\n", n, checkin_ret); + } +#endif + } + } if (ret != 0) break; From 30ee4c100db987c65ba8737ec914b9a0b0226442 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 15:16:04 +0000 Subject: [PATCH 059/102] random: partition the RBGC stratum space for user-supplied seed RBGCStratum was reset to 0 by any successful reseed, including one carrying a caller-supplied seed. Stratum 0 means "primary" -- an RNG seeded from the entropy source -- and it is what wc_RNG_DRBG_NextSeedGenerate_local() requires of a NextSeed root. So a single wc_RNG_DRBG_Reseed() call promoted an instance to a provenance it had not earned. Reserve a disjoint region of the stratum space for chains rooted in user-supplied entropy. A user-seeded reseed now sets WC_RNG_RBGC_USER_SEED_STRATUM rather than 0, and children descend from there, so unknown provenance is carried rather than erased. Source- gathered reseeds still promote to 0. Guards in _InitRng(), wc_RNG_DRBG_ReseedRBGC_local() and wc_RNG_DRBG_NextSeedGenerate_local() keep a natural chain from growing into the reserved region, and a static assert keeps the two regions from overlapping if the constant is overridden. Consequence worth stating: while an instance carries a user-seed stratum it cannot serve as a NextSeed root. That is state, not a brand -- a credited primary reseed promotes it back to 0, and an RBGC reseed places it one below its root. random_bank_test() covers both directions: a spawned child at USER_SEED_STRATUM + 1, and a prediction-resistance spawn whose parent is re-promoted to 0 by a fresh credited primary reseed immediately before the child's draw. --- wolfcrypt/src/random.c | 28 +++++++++++++++++++++++----- wolfcrypt/test/test.c | 4 ++-- wolfssl/wolfcrypt/random.h | 5 +++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index ae64424e3c3..c660dddbbef 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -987,8 +987,11 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz); #ifdef WC_RNG_HAVE_RBGC - if (ret == 0) - rng->RBGCStratum = 0; + if (ret == 0) { + /* User-supplied entropy is of unknown provenance. In RBGC builds, + * represent that fact using WC_RNG_RBGC_USER_SEED_STRATUM, preventing confusion with RNGs seeded by the ESV . */ + rng->RBGCStratum = WC_RNG_RBGC_USER_SEED_STRATUM; + } #endif return ret; @@ -2443,6 +2446,8 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, else { if (seedRng->RBGCStratum >= WC_MAX_SINT_OF(int)) return SEQ_OVERFLOW_E; + else if (seedRng->RBGCStratum == WC_RNG_RBGC_USER_SEED_STRATUM - 1) + return SEQ_OVERFLOW_E; rng->RBGCStratum = seedRng->RBGCStratum + 1; } #endif @@ -3761,10 +3766,22 @@ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, { return BAD_FUNC_ARG; } + ret = rng_lock_required_check(rng); if (ret != 0) return ret; + ret = rng_lock_required_check(root); + if (ret != 0) + return ret; + + if (credited) { + if (root->RBGCStratum >= WC_MAX_SINT_OF(int)) + return SEQ_OVERFLOW_E; + else if (root->RBGCStratum == WC_RNG_RBGC_USER_SEED_STRATUM - 1) + return SEQ_OVERFLOW_E; + } + #ifdef WOLFSSL_SMALL_STACK_CACHE seed = rng->newSeed_buf; #endif @@ -4120,9 +4137,10 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, { return BAD_FUNC_ARG; } - ret = rng_lock_required_check(root); - if (ret != 0) - return ret; + if (root->RBGCStratum >= WC_MAX_SINT_OF(int)) + return SEQ_OVERFLOW_E; + else if (root->RBGCStratum == WC_RNG_RBGC_USER_SEED_STRATUM - 1) + return SEQ_OVERFLOW_E; #else return NOT_COMPILED_IN; #endif diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 6350a3aa7b3..34b67bbc655 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28816,14 +28816,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifndef WC_NO_CONSTRUCTORS /* heap spawn from the second instance */ - ret = wc_rng_bank_spawn_new(bank, &spawned_rng, NULL, 0, 1, 0, 0); + ret = wc_rng_bank_spawn_new(bank, &spawned_rng, NULL, 0, NULL, 0, 1, 0, 0); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (spawned_rng == NULL) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) ret = wc_RNG_DRBG_GetRBGCStratum(spawned_rng); - if (ret != 1) + if (ret != WC_RNG_RBGC_USER_SEED_STRATUM + 1) ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); #endif ret = wc_RNG_GenerateBlock(spawned_rng, outbuf1, sizeof(outbuf1)); diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index e234f06e181..04b6329d151 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -456,6 +456,11 @@ enum wc_RngHealthState { #define WC_RNG_FLAG_BANKREF (1U << 2) #define WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3) +#ifndef WC_RNG_RBGC_USER_SEED_STRATUM + #define WC_RNG_RBGC_USER_SEED_STRATUM 65536 +#endif +wc_static_assert(WC_RNG_RBGC_USER_SEED_STRATUM >= 256); + #ifdef WC_RNG_WANT_DEBUG_STATS #define WC_RNG_DEBUG_STATS #endif From 46fc737cd9648088c34b3a2623a6a303cd8cba5b Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 16:16:04 +0000 Subject: [PATCH 060/102] random/rng_bank: fix a lost re-invalidation across reseed Hash_DRBG_Reseed() cleared WC_RNG_LOCK_ENTROPY_INVALIDATED whenever the bit had been observed set on entry and the reseed then succeeded. If a second invalidation event landed while the reseed was in flight, that event was swallowed: the clearing CAS cleared whatever it observed, so an instance whose seed predated the new event was presented as recovered. One bit cannot distinguish "the assertion I observed" from "a newer assertion", so add a second: WC_RNG_LOCK_ENTROPY_RECOVERING, asserted by the reseed path on entry and cleared by wc_RNG_invalidate_entropy(). It functions as a mutex and as an epoch witness -- finding it gone at exit means we were re-invalidated, so _INVALIDATED stands and the caller gets NEEDS_RECOVERY_E. Also refuses an undersized seed up front rather than reseeding and then silently declining to clear the latch, and returns BUSY_E rather than interleaving two recoveries. Both short-circuit before the mutex is taken. Every path below the acquire must reach the release, or the instance is stuck at BUSY_E. The pre-lock boundary keeps the latch bank-side and cannot see any of this, so the same protocol is reproduced around the four bank-side recovery paths: wc_rng_bank_inst_reseed_now(), _reseed_rbgc(), the wc_rng_bank_reseed_range() retry loop, and NextSeed consumption during checkout. WC_RNG_LOCK_EXTRA_SHIFT moves from 3 to 4 accordingly, in both copies of the definitions. --- wolfcrypt/src/random.c | 80 ++++++++++++++++++++++++++++++++-- wolfcrypt/src/rng_bank.c | 83 ++++++++++++++++++++++++++++++------ wolfssl/wolfcrypt/random.h | 3 +- wolfssl/wolfcrypt/rng_bank.h | 7 ++- 4 files changed, 155 insertions(+), 18 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index c660dddbbef..1080c3bc5b1 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -859,6 +859,38 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); #endif +#ifdef WC_RNG_HAVE_LOCK + /* Never allow an undersized seed to clear an invalidated state. */ + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && + (seedSz < WC_DRBG_SEED_SZ)) + { + /* Short-circuit return in case the _RECOVERING mutex below would have + * failed. */ + return NEEDS_RECOVERY_E; + } + + /* Iff _ENTROPY_INVALIDATED, assert the _ENTROPY_RECOVERING bit now -- if we + * are re-invalidated in the meantime, it will have been cleared by the + * invalidation at exit time, signaling that we are still invalidated. */ + for (;;) { + if (! (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + break; + if (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING) { + /* Must short-circuit return here, so that we don't improperly clear + * the _RECOVERING bit. */ + return BUSY_E; + } + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_ENTROPY_RECOVERING)) + { + /* We now have the _RECOVERING mutex -- record that fact. */ + cur_lock |= WC_RNG_LOCK_ENTROPY_RECOVERING; + break; + } + } +#endif /* WC_RNG_HAVE_LOCK */ + #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_POOL) /* Purge the pool on credited reseeds. A credited reseed is an epoch * boundary -- the pool must not serve output of a retired state @@ -957,11 +989,36 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, out: #ifdef WC_RNG_HAVE_LOCK - if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && (ret == 0)) { + /* The _RECOVERING bit functions as a mutex -- if it's set here, *we* + * set it, and must clear it. + * + * If we were invalidated on entry, and reseed succeeded, and provided we + * weren't re-invalidated in the meantime, clear the _INVALIDATED bit + * alongside the _RECOVERING bit. + * + * Otherwise, just clear the _RECOVERING bit (releasing the recovery mutex), + * and return with the success or failure code from above. + */ + if ((ret == 0) && (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { for (;;) { + if (! (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING)) { + ret = NEEDS_RECOVERY_E; + break; + } if (wolfSSL_Atomic_Uint_CompareExchange( &rng->lock, &cur_lock, - cur_lock & ~WC_RNG_LOCK_ENTROPY_INVALIDATED)) + cur_lock & ~(WC_RNG_LOCK_ENTROPY_INVALIDATED | + WC_RNG_LOCK_ENTROPY_RECOVERING))) + { + break; + } + } + } + else if (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING) { + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng->lock, &cur_lock, + cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING)) { break; } @@ -985,6 +1042,14 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, if (ret != 0) return ret; +#ifdef WC_RNG_HAVE_LOCK + /* Never allow an undersized seed to clear an invalidated state. */ + if (WOLFSSL_ATOMIC_LOAD(rng->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED) { + if (seedSz < WC_DRBG_SEED_SZ) + return NEEDS_RECOVERY_E; + } +#endif /* WC_RNG_HAVE_LOCK */ + ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz); #ifdef WC_RNG_HAVE_RBGC if (ret == 0) { @@ -3367,7 +3432,8 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { for (;;) { if (wolfSSL_Atomic_Uint_CompareExchange( &rng->lock, &cur_lock, - cur_lock | WC_RNG_LOCK_ENTROPY_INVALIDATED)) + (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING) | + WC_RNG_LOCK_ENTROPY_INVALIDATED)) { break; } @@ -3380,6 +3446,11 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { * If a lock is held, the holder will learn of the invalidation at unlock * time, and will implement its own mitigation strategy. We do not force it * into a synchronous reseed. + * + * In either case, the state purges here are best effort. With + * WC_RNG_HAVE_LOCK, these purges are repeated, strictly serialized against + * concurrent recovery attempts, in Hash_DRBG_Reseed() (the sole recovery + * path from _ENTROPY_INVALIDATED). */ if (! (cur_lock & WC_RNG_LOCK_HELD)) (void)wc_RNG_DRBG_ScheduleReseed(rng); @@ -4126,6 +4197,9 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, */ if (root) { + ret = rng_lock_required_check(root); + if (ret != 0) + return ret; #ifdef WC_RNG_HAVE_RBGC if ((root->RBGCStratum > 0) #ifndef WC_RNG_NO_RBGC_RESEED diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 6c1107be4a1..715c03760b9 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -26,6 +26,26 @@ #include #include +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + + /* backward-compat shim and helper declarations */ + + static int wc_rng_bank_inst_recovery_enter( + struct wc_rng_bank_inst *inst, int *recovering); + static int wc_rng_bank_inst_recovery_exit( + struct wc_rng_bank_inst *inst, int recovering, int ret); + static int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, + word32 seedSz, const byte *nonce, + word32 nonceSz); + #if FIPS_VERSION3_NE(5,2,4) + static int wc_RNG_DRBG_GetReseedCtr( + const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr); + #endif + static int wc_RNG_DRBG_Stir_Nonce( + WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, + word32 nonceSz); +#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ + /* DRBG status and reseed-counter access, and reseed forcing, are via the * wc_RNG_GetStatus() / wc_RNG_DRBG_*() services in wolfcrypt/src/random.c * (FIPS v7+ and non-FIPS builds). For pre-v7 FIPS boundaries, which lack @@ -830,15 +850,26 @@ WOLFSSL_API int wc_rng_bank_checkout( * out under incumbent bare-targeted semantics -- * identically to any other out-of-service instance. */ - if (wc_RNG_DRBG_NextSeedNow( - WC_RNG_BANK_INST_TO_RNG(*rng_inst)) == 0) - { #ifndef WC_RNG_HAVE_LOCK - /* consumption is a credited reseed; mirror the - * in-boundary clear (see wc_rng_bank_reseed_range()). */ - (void)wc_rng_bank_inst_lock_clear_invalidated(*rng_inst); -#endif + /* consumption is a credited reseed; mirror the in-boundary + * _RECOVERING protocol (see wc_rng_bank_reseed_range()). A + * BUSY_E here just means another claimant is recovering the + * instance -- nothing to do, and handled uniformly below. */ + { + int recovering; + if (wc_rng_bank_inst_recovery_enter(*rng_inst, + &recovering) == 0) + { + (void)wc_rng_bank_inst_recovery_exit( + *rng_inst, recovering, + wc_RNG_DRBG_NextSeedNow( + WC_RNG_BANK_INST_TO_RNG(*rng_inst))); + } } +#else + (void)wc_RNG_DRBG_NextSeedNow( + WC_RNG_BANK_INST_TO_RNG(*rng_inst)); +#endif } #endif /* WC_RNG_HAVE_NEXT_SEED */ @@ -2174,14 +2205,42 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, if (flags & WC_RNG_BANK_FLAG_CAN_WAIT) { for (;;) { time_t ts2; + +#ifndef WC_RNG_HAVE_LOCK + /* the pre-lock boundary can't see the inst-side latch: + * mirror the in-boundary _RECOVERING protocol around the + * reseed, so that a concurrent invalidation isn't swallowed + * by the clear. */ + { + int recovering; + ret = wc_rng_bank_inst_recovery_enter(drbg, &recovering); + if (ret == 0) { + #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + (void)nonce; + (void)nonceSz; + ret = wc_RNG_DRBG_Reseed_Now( + WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); + #else + ret = wc_RNG_DRBG_Reseed_Now( + WC_RNG_BANK_INST_TO_RNG(drbg), nonce, nonceSz); + #endif + ret = wc_rng_bank_inst_recovery_exit(drbg, recovering, + ret); + } + } +#else /* WC_RNG_HAVE_LOCK */ + #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + (void)nonce; + (void)nonceSz; ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); + #else + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), + nonce, nonceSz); + #endif +#endif /* WC_RNG_HAVE_LOCK */ + if (ret == 0) { -#ifndef WC_RNG_HAVE_LOCK - /* the pre-lock boundary can't see the inst-side latch: - * mirror the in-boundary clear-on-credited-reseed. */ - (void)wc_rng_bank_inst_lock_clear_invalidated(drbg); -#endif break; } if ((timeout_secs == 0) || diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 04b6329d151..8e9aedb7d20 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -963,8 +963,9 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); #define WC_RNG_LOCK_HELD (1U<<0) #define WC_RNG_LOCK_REQUIRED (1U<<1) #define WC_RNG_LOCK_ENTROPY_INVALIDATED (1U<<2) + #define WC_RNG_LOCK_ENTROPY_RECOVERING (1U<<3) /* consumers' annotation bits start here (see e.g. rng_bank.h) */ - #define WC_RNG_LOCK_EXTRA_SHIFT 3U + #define WC_RNG_LOCK_EXTRA_SHIFT 4U WOLFSSL_API int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); WOLFSSL_API int wc_RNG_lock_get_conditional(WC_RNG* rng, diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index dd504f876c2..2a149d8db0b 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -64,13 +64,16 @@ #define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 15) #define WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE (1U << 16) -#ifndef WC_RNG_HAVE_LOCK +#ifdef WC_RNG_HAVE_LOCK + wc_static_assert(WC_RNG_LOCK_EXTRA_SHIFT == 4U); +#else /* !WC_RNG_HAVE_LOCK */ /* Definitions for backward-compat / WC_RNG_NO_LOCK */ #define WC_RNG_LOCK_FREE 0 #define WC_RNG_LOCK_HELD (1U<<0) #define WC_RNG_LOCK_REQUIRED (1U<<1) #define WC_RNG_LOCK_ENTROPY_INVALIDATED (1U<<2) - #define WC_RNG_LOCK_EXTRA_SHIFT 3U + #define WC_RNG_LOCK_ENTROPY_RECOVERING (1U<<3) + #define WC_RNG_LOCK_EXTRA_SHIFT 4U #ifdef WOLFSSL_NO_ATOMICS typedef word32 WC_RNG_lock_t; typedef word32 WC_RNG_lock_arg_t; From f0ee19659b51ebccf5316a8b015f442d5e1522ed Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 17:16:04 +0000 Subject: [PATCH 061/102] random: health-test caller-supplied recovery seed Instantiation already runs wc_RNG_TestSeed() over caller-supplied seed material, and both the PollAndReSeed() and NextSeed paths run it over source-gathered material. The user-supplied reseed path was the only one that did not. Apply it when recovering an invalidated instance. The legacy wc_RNG_DRBG_Reseed() API conflates seed material of every provenance into one argument and credits it entirely on caller attestation, so this is the only structural check available there. Scoped to the invalidated case, so no incumbent path changes behaviour. On a full-width seed the RCT and APT cutoffs cannot be reached by chance, so only degenerate material -- long runs, constants -- is rejected. --- wolfcrypt/src/random.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 1080c3bc5b1..4d502e42a45 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1043,10 +1043,15 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, return ret; #ifdef WC_RNG_HAVE_LOCK - /* Never allow an undersized seed to clear an invalidated state. */ + /* Never allow an undersized seed to clear an invalidated state, and if + * invalidated, always assume potentially primary seed data -- test it with + * wc_RNG_TestSeed(). */ if (WOLFSSL_ATOMIC_LOAD(rng->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED) { if (seedSz < WC_DRBG_SEED_SZ) return NEEDS_RECOVERY_E; + ret = wc_RNG_TestSeed(seed, seedSz); + if (ret != 0) + return ret; } #endif /* WC_RNG_HAVE_LOCK */ From 945c33a85743af169874a271f6de8f51147162d4 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 18:16:04 +0000 Subject: [PATCH 062/102] random/rng_bank/linuxkm: add nonce and personalization-string plumbing Two related gaps in what callers could supply. The SP 800-90A personalization string was reachable only as the hardcoded NULL that _InitRng() passed to Hash_DRBG_Init(). Hash_df() already had the input; nothing could fill it. Thread it through the instantiate family -- wc_InitRngNonce_ex2(), wc_InitRngNonceRBGC{,_New}(), SpawnRngRBGC(), wc_rng_bank_init_nonce() and wc_rng_bank_spawn{,_new}(). Personalization is an instantiate-time input in SP 800-90A, so the reseed and stir APIs deliberately do not gain it; material of that kind belongs in their existing additional-input argument. Separately, the bank-level seed and reseed APIs had no way to pass a nonce to the per-instance reseed underneath them, so wc_rng_bank_seed{,_range}() and wc_rng_bank_reseed{,_range}() gain one. The index arguments keep their positions and the material sits immediately before timeout_secs. linuxkm uses it to carry a post-event timestamp into the recovery reseeds issued after a state invalidation, and zeroizes it on every path, matching the existing uncredited-nonce sites in that file. --- linuxkm/lkcapi_sha_glue.c | 36 +++++++++++++++++------ wolfcrypt/src/random.c | 53 ++++++++++++++++++++-------------- wolfcrypt/src/rng_bank.c | 54 +++++++++++++++++++++++++--------- wolfcrypt/test/test.c | 56 +++++++++++++++++++----------------- wolfssl/wolfcrypt/random.h | 11 ++++--- wolfssl/wolfcrypt/rng_bank.h | 16 +++++++---- 6 files changed, 147 insertions(+), 79 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 5b8909e5a6f..2074d679f8d 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2327,8 +2327,12 @@ static int wc_linuxkm_rng_state_invalidate(void) { /* daemon-less bank: recover synchronously -- the * FOR_RECOVERY claim path in wc_rng_bank_reseed_range()'s * checkouts claims the quarantined instances. */ - this_ret = wc_rng_bank_reseed_range(obj->bank, 0, -1, + unsigned long uncredited_nonce = random_get_entropy(); + this_ret = wc_rng_bank_reseed_range( + obj->bank, 0, -1, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); if ((this_ret != 0) && (ret == 0)) ret = this_ret; } @@ -2790,7 +2794,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) if ((wc_RNG_lock_read(local_root, &root_lock_state) == 0) && (root_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { - int inv_ret = wc_RNG_DRBG_Reseed_Now(local_root, NULL, 0); + unsigned long uncredited_nonce = random_get_entropy(); + int inv_ret = wc_RNG_DRBG_Reseed_Now( + local_root, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); if (inv_ret != 0) pr_err_ratelimited("wc_entropyd: post-invalidation " "local_root reseed failed: %d\n", inv_ret); @@ -2877,8 +2885,8 @@ static int wc_linuxkm_entropy_daemon(void *arg) { unsigned long uncredited_nonce = random_get_entropy(); (void)wc_RNG_DRBG_Stir(local_root, - (byte *)&uncredited_nonce, - (word32)sizeof uncredited_nonce); + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); ret = wc_RNG_Pool_Collect2(inst_rng, local_root, (word32)inst_rng->poolSize @@ -3094,7 +3102,7 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) flags | WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING | WC_RNG_BANK_FLAG_INIT_RBGC, WC_LINUXKM_INITRNG_TIMEOUT_SEC, NULL /* heap */, INVALID_DEVID, - (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, NULL, 0); if (ret == 0) { (void)wc_rng_bank_first_failover_inst_set(ctx, LINUXKM_RNG_BANK_FIRST_FAILOVER); @@ -3367,7 +3375,11 @@ static struct wc_rng_bank_inst *linuxkm_get_drbg(struct wc_rng_bank *ctx) { if ((err == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) && (ret != NULL)) { /* leased-but-quarantined per WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY: * we own the recovery obligation. */ - err = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(ret), NULL, 0); + unsigned long uncredited_nonce = random_get_entropy(); + err = wc_RNG_DRBG_Reseed_Now( + WC_RNG_BANK_INST_TO_RNG(ret), + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); if (err == 0) return ret; pr_err_ratelimited("ERROR: inline recovery reseed in " @@ -3422,6 +3434,7 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { int can_sleep = wc_linuxkm_can_block(); int ret = wc_rng_bank_spawn(NULL /* bank */, rng, (byte *)&uncredited_nonce, sizeof uncredited_nonce, + NULL, 0, 0 /* preferred_inst_offset */, 0 /* timeout_secs */, WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | @@ -3812,8 +3825,10 @@ static int wc_linuxkm_drbg_seed(struct wc_rng_bank *ctx, * additional input, never crediting it as entropy). Mix it into every * instance without credit; the reseed schedule stays governed solely by * the module's own seed source. */ - ret = wc_rng_bank_seed_range(ctx, 0, LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE, - seed, slen, WC_LINUXKM_INITRNG_TIMEOUT_SEC, + ret = wc_rng_bank_seed_range(ctx, 0, + LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE, + seed, slen, NULL, 0, + WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_STIR); if (ret != 0) { @@ -4158,6 +4173,7 @@ static int wc_crng_reseed(void) { struct wc_rng_bank *ctx; int can_sleep = wc_linuxkm_can_block(); int ret = wc_rng_bank_default_checkout(&ctx); + unsigned long uncredited_nonce; if (ret) { #ifdef WC_VERBOSE_RNG @@ -4167,14 +4183,18 @@ static int wc_crng_reseed(void) { return -EFAULT; } + uncredited_nonce = random_get_entropy(); ret = wc_rng_bank_reseed_range(ctx, 0, LINUXKM_RNG_BANK_LAST_SAFELY_CONTENDABLE, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce, WC_LINUXKM_INITRNG_TIMEOUT_SEC, can_sleep ? WC_RNG_BANK_FLAG_CAN_WAIT : WC_RNG_BANK_FLAG_NONE); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); (void)wc_rng_bank_default_checkin(&ctx); diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 4d502e42a45..95549f91a8d 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -2445,6 +2445,7 @@ int wc_Sha512Drbg_IsDisabled(void) * latch is held) and frees the mutex. */ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, void* heap, int devId, WC_RNG* seedRng, word32 flags) { int ret = 0; @@ -2463,6 +2464,8 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, (void)nonce; (void)nonceSz; + (void)perso; + (void)persoSz; /* seedRng is consumed only in the seed-acquisition arm; cast for * configurations that compile that arm out. */ (void)seedRng; @@ -2471,6 +2474,8 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, return BAD_FUNC_ARG; if (nonce == NULL && nonceSz != 0) return BAD_FUNC_ARG; + if (perso == NULL && persoSz != 0) + return BAD_FUNC_ARG; #ifndef WC_RNG_HAVE_NEXT_SEED if (flags & WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED) @@ -2857,7 +2862,7 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #else seed, seedSz, #endif - nonce, nonceSz, NULL, 0, rng->heap, devId); + nonce, nonceSz, perso, persoSz, rng->heap, devId); #endif #ifdef WOLFSSL_DRBG_SHA512 if (rng->drbgType == WC_DRBG_SHA512) @@ -2868,7 +2873,7 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #else seed, seedSz, #endif - nonce, nonceSz, NULL, 0, rng->heap, devId); + nonce, nonceSz, perso, persoSz, rng->heap, devId); #endif if (ret == 0) drbg_instantiated = 1; @@ -3028,7 +3033,7 @@ int wc_rng_new_ex(WC_RNG **rng, byte* nonce, word32 nonceSz, return MEMORY_E; } - ret = _InitRng(*rng, nonce, nonceSz, heap, devId, NULL, + ret = _InitRng(*rng, nonce, nonceSz, NULL, 0, heap, devId, NULL, WC_RNG_INIT_FLAGS_NONE); if (ret != 0) { XFREE(*rng, heap, DYNAMIC_TYPE_RNG); @@ -3055,21 +3060,21 @@ void wc_rng_free(WC_RNG* rng) WOLFSSL_ABI int wc_InitRng(WC_RNG* rng) { - return _InitRng(rng, NULL, 0, NULL, INVALID_DEVID, NULL, + return _InitRng(rng, NULL, 0, NULL, 0, NULL, INVALID_DEVID, NULL, WC_RNG_INIT_FLAGS_NONE); } int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId) { - return _InitRng(rng, NULL, 0, heap, devId, NULL, + return _InitRng(rng, NULL, 0, NULL, 0, heap, devId, NULL, WC_RNG_INIT_FLAGS_NONE); } int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz) { - return _InitRng(rng, nonce, nonceSz, NULL, INVALID_DEVID, NULL, + return _InitRng(rng, nonce, nonceSz, NULL, 0, NULL, INVALID_DEVID, NULL, WC_RNG_INIT_FLAGS_NONE); } @@ -3077,19 +3082,21 @@ int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz) int wc_InitRngNonce_ex(WC_RNG* rng, const byte* nonce, word32 nonceSz, void* heap, int devId) { - return _InitRng(rng, nonce, nonceSz, heap, devId, NULL, + return _InitRng(rng, nonce, nonceSz, NULL, 0, heap, devId, NULL, WC_RNG_INIT_FLAGS_NONE); } int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags) { - return _InitRng(rng, NULL, 0, heap, devId, NULL, flags); + return _InitRng(rng, NULL, 0, NULL, 0, heap, devId, NULL, flags); } int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, void* heap, int devId, word32 flags) { - return _InitRng(rng, nonce, nonceSz, heap, devId, NULL, flags); + return _InitRng(rng, nonce, nonceSz, perso, persoSz, + heap, devId, NULL, flags); } #ifdef WC_RNG_HAVE_LOCK @@ -3743,6 +3750,7 @@ int wc_RNG_Pool_Current(WC_RNG* rng, word32* n) */ static int SpawnRngRBGC(WC_RNG* new_child_stack, WC_RNG** new_child_heap, WC_RNG* parent, const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, word32 flags) { WC_RNG* child = new_child_stack; @@ -3768,7 +3776,7 @@ static int SpawnRngRBGC(WC_RNG* new_child_stack, WC_RNG** new_child_heap, child = *new_child_heap; } - ret = _InitRng(child, nonce, nonceSz, parent->heap, + ret = _InitRng(child, nonce, nonceSz, perso, persoSz, parent->heap, #if defined(WOLF_CRYPTO_CB) parent->devId, #else @@ -3788,25 +3796,29 @@ static int SpawnRngRBGC(WC_RNG* new_child_stack, WC_RNG** new_child_heap, int wc_InitRngRBGC(WC_RNG* child, WC_RNG* parent, word32 flags) { - return SpawnRngRBGC(child, NULL, parent, NULL, 0, flags); + return SpawnRngRBGC(child, NULL, parent, NULL, 0, NULL, 0, flags); } int wc_InitRngNonceRBGC(WC_RNG* child, WC_RNG* parent, const byte* nonce, - word32 nonceSz, word32 flags) + word32 nonceSz, const byte *perso, word32 persoSz, + word32 flags) { - return SpawnRngRBGC(child, NULL, parent, nonce, nonceSz, flags); + return SpawnRngRBGC(child, NULL, parent, nonce, nonceSz, perso, persoSz, + flags); } #ifndef WC_NO_CONSTRUCTORS int wc_InitRngRBGC_New(WC_RNG** child, WC_RNG* parent, word32 flags) { - return SpawnRngRBGC(NULL, child, parent, NULL, 0, flags); + return SpawnRngRBGC(NULL, child, parent, NULL, 0, NULL, 0, flags); } int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, - word32 nonceSz, word32 flags) + word32 nonceSz, const byte *perso, word32 persoSz, + word32 flags) { - return SpawnRngRBGC(NULL, child, parent, nonce, nonceSz, flags); + return SpawnRngRBGC(NULL, child, parent, nonce, nonceSz, perso, persoSz, + flags); } #endif /* !WC_NO_CONSTRUCTORS */ @@ -3883,7 +3895,7 @@ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, ret = wc_RNG_GenerateBlock(root, seed, SEED_SZ); if (ret == 0) { if (credited) { - ret = wc_RNG_DRBG_Reseed_Nonce(rng, seed, SEED_SZ, nonce, nonceSz); + ret = Hash_DRBG_Reseed(rng, seed, SEED_SZ, nonce, nonceSz); if (ret == 0) { rng->RBGCStratum = root->RBGCStratum + 1; #ifdef WC_RNG_DEBUG_STATS @@ -4602,15 +4614,14 @@ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng) { * interleaved fragments of compatible provenance are harmless. A full * accumulator publishes WC_DRBG_NEXT_SEED_READY (no health test -- no * claim is being made) and blocks further deposits until consumed. */ -int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, - word32 nonceSz) +int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, + const byte *nonce, word32 nonceSz) { if ((nonce == NULL) || (nonceSz == 0)) return BAD_FUNC_ARG; /* _local's nonce arm only reads the buffer; the parameter is non-const * for the benefit of the other arms. */ - return wc_RNG_DRBG_NextSeedGenerate_local(rng, NULL, nonce, - nonceSz); + return wc_RNG_DRBG_NextSeedGenerate_local(rng, NULL, nonce, nonceSz); } /* Consume a ready uncredited accumulator in an immediate uncredited diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 715c03760b9..2cca9dbf9af 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -63,8 +63,8 @@ WOLFSSL_API int wc_rng_bank_init_nonce( int timeout_secs, void *heap, int devId, - const byte *nonce, - word32 nonceSz) + const byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz) { int i; int ret; @@ -103,13 +103,22 @@ WOLFSSL_API int wc_rng_bank_init_nonce( #ifdef WC_RNG_HAVE_RBGC if ((ret == 0) && (flags & WC_RNG_BANK_FLAG_INIT_RBGC)) { + #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + ret = wc_InitRngNonce_ex2(&root, nonce, nonceSz, perso, persoSz, heap, + devId, WC_RNG_INIT_FLAGS_NONE); + #else + (void)perso; + (void)persoSz; ret = wc_InitRngNonce_ex(&root, nonce, nonceSz, heap, devId); + #endif if (ret == 0) root_inited = 1; } #else (void)nonce; (void)nonceSz; + (void)perso; + (void)persoSz; #endif if (ret == 0) { @@ -137,6 +146,7 @@ WOLFSSL_API int wc_rng_bank_init_nonce( &root, (byte *)&rng_inst, sizeof(byte *) #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + , NULL, 0 , WC_RNG_INIT_FLAGS_LOCK_REQUIRED #else , WC_RNG_INIT_FLAGS_NONE @@ -149,7 +159,8 @@ WOLFSSL_API int wc_rng_bank_init_nonce( #ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED ret = wc_InitRngNonce_ex2( WC_RNG_BANK_INST_TO_RNG(rng_inst), - (byte *)&rng_inst, sizeof(byte *), heap, devId, + (byte *)&rng_inst, sizeof(byte *), + NULL, 0, heap, devId, WC_RNG_INIT_FLAGS_LOCK_REQUIRED); #else ret = wc_InitRngNonce_ex( @@ -239,7 +250,7 @@ WOLFSSL_API int wc_rng_bank_init( { return wc_rng_bank_init_nonce(ctx, n_rngs, flags, timeout_secs, heap, devId, - NULL, 0); + NULL, 0, NULL, 0); } WOLFSSL_API int wc_rng_bank_first_failover_inst_set( @@ -1649,6 +1660,7 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( #ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED ret = wc_InitRngNonce_ex2(WC_RNG_BANK_INST_TO_RNG(rng_inst), (byte *)&rng_inst, sizeof(byte *), + NULL, 0, bank->heap, devId, WC_RNG_INIT_FLAGS_LOCK_REQUIRED | WC_RNG_INIT_FLAGS_LOCK_INITIALLY); @@ -1824,8 +1836,8 @@ static int rng_bank_spawn( struct wc_rng_bank *bank, WC_RNG *leaf_stack, WC_RNG **leaf_heap, - byte *nonce, - word32 nonceSz, + byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, int preferred_inst_offset, int timeout_secs, word32 flags) @@ -1834,6 +1846,11 @@ static int rng_bank_spawn( int ret; int checkin_ret; +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + (void)perso; + (void)persoSz; +#endif + if ((leaf_stack == NULL) == (leaf_heap == NULL)) return BAD_FUNC_ARG; @@ -1865,6 +1882,9 @@ static int rng_bank_spawn( ret = wc_InitRngNonceRBGC(leaf_stack, WC_RNG_BANK_INST_TO_RNG(rng_inst), nonce, nonceSz, +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + perso, persoSz, +#endif child_init_flags ); } @@ -1873,6 +1893,7 @@ static int rng_bank_spawn( ret = wc_InitRngNonceRBGC_New(leaf_heap, WC_RNG_BANK_INST_TO_RNG(rng_inst), nonce, nonceSz, + perso, persoSz, child_init_flags); #else /* Unreachable: wc_rng_bank_spawn_new() is absent under @@ -1905,13 +1926,14 @@ static int rng_bank_spawn( WOLFSSL_API int wc_rng_bank_spawn( struct wc_rng_bank *bank, WC_RNG *leaf_rng, - byte *nonce, - word32 nonceSz, + byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, int preferred_inst_offset, int timeout_secs, word32 flags) { return rng_bank_spawn(bank, leaf_rng, NULL, nonce, nonceSz, + perso, persoSz, preferred_inst_offset, timeout_secs, flags); } @@ -1919,13 +1941,14 @@ WOLFSSL_API int wc_rng_bank_spawn( WOLFSSL_API int wc_rng_bank_spawn_new( struct wc_rng_bank *bank, WC_RNG **leaf_rng, - byte *nonce, - word32 nonceSz, + byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, int preferred_inst_offset, int timeout_secs, word32 flags) { return rng_bank_spawn(bank, NULL, leaf_rng, nonce, nonceSz, + perso, persoSz, preferred_inst_offset, timeout_secs, flags); } #endif /* !WC_NO_CONSTRUCTORS */ @@ -1936,6 +1959,7 @@ WOLFSSL_API int wc_rng_bank_spawn_new( WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int first_inst, int last_inst, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags) { @@ -2104,15 +2128,17 @@ WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags) { - return wc_rng_bank_seed_range(bank, 0, -1, seed, seedSz, timeout_secs, - flags); + return wc_rng_bank_seed_range(bank, 0, -1, seed, seedSz, nonce, nonceSz, + timeout_secs, flags); } WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, int first_inst, int last_inst, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags) { @@ -2310,10 +2336,12 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, } WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags) { - return wc_rng_bank_reseed_range(bank, 0, -1, timeout_secs, flags); + return wc_rng_bank_reseed_range(bank, 0, -1, nonce, nonceSz, + timeout_secs, flags); } WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 34b67bbc655..d93eee8a41a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28258,7 +28258,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #endif #ifdef HAVE_HASHDRBG - ret = wc_rng_bank_reseed(NULL, 10, WC_RNG_BANK_FLAG_NONE); + ret = wc_rng_bank_reseed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_NONE); #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28267,7 +28267,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #endif - ret = wc_rng_bank_reseed(bank, 10, WC_RNG_BANK_FLAG_NONE); + ret = wc_rng_bank_reseed(bank, NULL, 0, 10, WC_RNG_BANK_FLAG_NONE); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28321,7 +28321,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #ifdef HAVE_HASHDRBG - ret = wc_rng_bank_seed(bank, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(bank, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28344,7 +28344,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_seed(NULL, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(NULL, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28353,14 +28353,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #endif - ret = wc_rng_bank_seed(bank, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(bank, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); /* seedSz == 0 short-circuits: no-op success for an explicit inited * bank, and for the default form while a default is set. (The * seed pointer is never read on these paths.) */ - ret = wc_rng_bank_seed(bank, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(bank, NULL, 0, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28484,15 +28484,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef HAVE_HASHDRBG - ret = wc_rng_bank_seed(NULL, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(NULL, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_reseed(NULL, 10, WC_RNG_BANK_FLAG_NONE); + ret = wc_rng_bank_reseed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_NONE); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_seed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(NULL, NULL, 0, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28505,7 +28505,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef HAVE_HASHDRBG /* seedSz == 0 probe with no default bank set: NO_DEFAULT_FOUND_E. */ - ret = wc_rng_bank_seed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(NULL, NULL, 0, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); @@ -28552,7 +28552,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef HAVE_HASHDRBG - ret = wc_rng_bank_seed(bank2, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(bank2, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28571,7 +28571,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_seed(bank2, (byte *)bank_arg, (word32)sizeof(bank_arg), 10, WC_RNG_BANK_FLAG_CAN_WAIT); + ret = wc_rng_bank_seed(bank2, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28779,25 +28779,25 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef WC_RNG_HAVE_RBGC /* RBGC spawn: argument and flag contracts */ - ret = wc_rng_bank_spawn(bank, NULL, NULL, 0, 0, 0, 0); + ret = wc_rng_bank_spawn(bank, NULL, NULL, 0, NULL, 0, 0, 0, 0); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #ifndef WC_NO_CONSTRUCTORS - ret = wc_rng_bank_spawn_new(bank, NULL, NULL, 0, 0, 0, 0); + ret = wc_rng_bank_spawn_new(bank, NULL, NULL, 0, NULL, 0, 0, 0, 0); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #endif - ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_STIR); + ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_STIR); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_FOR_RECOVERY); + ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, NULL, 0, 0, 0, WC_RNG_BANK_FLAG_FOR_RECOVERY); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); /* nonce-bearing stack spawn: the leaf is a tagged chain leaf, * generates, and is torn down independently of the bank */ - ret = wc_rng_bank_spawn(bank, leaf_rng, outbuf2, sizeof(outbuf2), 0, 0, - 0); + ret = wc_rng_bank_spawn(bank, leaf_rng, outbuf2, sizeof(outbuf2), + NULL, 0, 0, 0, 0); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); leaf_rng_inited = 1; @@ -28837,7 +28837,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) * immediately before the child's seed draw (the SP 800-90C Sec. 4.1.1 * pattern). The child is stratum 1; the parent instance's counter * shows reseed-then-one-draw. */ - ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, 0, 10, + ret = wc_rng_bank_spawn(bank, leaf_rng, NULL, 0, NULL, 0, 0, 10, WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE | WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) @@ -29581,10 +29581,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) * it: NOT_READY_E, with counter and flag untouched by * construction. */ XMEMSET(block, 0x5a, sizeof(block)); +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) api_ret = wc_RNG_DRBG_Stir(WC_RNG_BANK_INST_TO_RNG(held), block, sizeof(block)); if (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); +#endif api_ret = wc_rng_bank_inst_lock_read(held, &lock_state); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -29717,7 +29719,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) { ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - api_ret = wc_rng_bank_reseed_range(bank, 0, -1, 10, + api_ret = wc_rng_bank_reseed_range(bank, 0, -1, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -29859,7 +29861,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) api_ret = wc_InitRng(&proot); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_InitRngNonceRBGC(&flag_rng, &proot, NULL, 0, + api_ret = wc_InitRngNonceRBGC(&flag_rng, &proot, NULL, 0, NULL, 0, WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -29881,7 +29883,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* negative: unflagged leaf keeps its stratum. */ api_ret = wc_InitRngNonceRBGC(&flag_rng, &proot, NULL, 0, - WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_DRBG_NextSeedGenerate(&flag_rng, @@ -30296,7 +30298,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) wc_rng_free(pleaf); pleaf = NULL; api_ret = wc_InitRngNonceRBGC_New(&pleaf, &root, matter, 16, - WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if ((pleaf == NULL) || (wc_RNG_DRBG_GetRBGCStratum(pleaf) != 1)) @@ -30311,7 +30313,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_InitRngNonceRBGC(&leaf, &root, matter, 16, - WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; @@ -30477,7 +30479,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) wc_rng_free(pleaf); pleaf = NULL; api_ret = wc_InitRngNonceRBGC_New(&pleaf, &root, matter, 16, - WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if (pleaf == NULL) @@ -30523,7 +30525,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) return ret; } -#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ +#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) && WC_RNG_BANK_SUPPORT */ #endif /* WC_RNG_HAVE_RBGC */ @@ -30831,7 +30833,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) XMEMSET(frag64, 0x5e, sizeof(frag64)); api_ret = wc_InitRngNonceRBGC(&leaf, root, NULL, 0, - WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_invalidate_entropy(&leaf); diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 8e9aedb7d20..51134b264da 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -736,6 +736,7 @@ WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz) WOLFSSL_API int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags); WOLFSSL_API int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, void* heap, int devId, word32 flags); WOLFSSL_API int wc_InitRngNonce_ex(WC_RNG* rng, const byte* nonce, word32 nonceSz, void* heap, int devId); @@ -749,7 +750,7 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); #define wc_InitRngNonce(rng, n, s) NOT_COMPILED_IN #define wc_InitRngNonce_ex(rng, n, s, h, d) NOT_COMPILED_IN #define wc_InitRng_ex2(rng, h, d, f) NOT_COMPILED_IN -#define wc_InitRngNonce_ex2(rng, n, s, h, d, f) NOT_COMPILED_IN +#define wc_InitRngNonce_ex2(rng, n, s, p, ps, h, d, f) NOT_COMPILED_IN #if defined(__ghs__) || defined(WC_NO_RNG_SIMPLE) /* some older compilers do not like macro function in expression */ #define wc_RNG_GenerateBlock(rng, b, s) NOT_COMPILED_IN @@ -913,6 +914,7 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); WOLFSSL_API int wc_InitRngRBGC(WC_RNG* child, WC_RNG* parent, word32 flags); WOLFSSL_API int wc_InitRngNonceRBGC(WC_RNG* child, WC_RNG* parent, const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, word32 flags); #ifndef WC_NO_CONSTRUCTORS /* Flags are per-object (WC_RNG_INIT_FLAGS_*), deliberately not @@ -921,14 +923,15 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); word32 flags); WOLFSSL_API int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, word32 flags); #endif /* !WC_NO_CONSTRUCTORS */ WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz); WOLFSSL_API int wc_RNG_DRBG_StirRBGC(WC_RNG* rng, - WC_RNG* root, - const byte* nonce, - word32 nonceSz); + WC_RNG* root, + const byte* nonce, + word32 nonceSz); #endif /* WC_RNG_HAVE_RBGC */ #ifdef WC_RNG_HAVE_NEXT_SEED diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 2a149d8db0b..281131d52a0 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -181,8 +181,8 @@ WOLFSSL_API int wc_rng_bank_init_nonce( int timeout_secs, void *heap, int devId, - const byte *nonce, - word32 nonceSz); + const byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz); WOLFSSL_API int wc_rng_bank_first_failover_inst_set( struct wc_rng_bank *ctx, @@ -308,8 +308,8 @@ WOLFSSL_API int wc_rng_bank_recover_inst( WOLFSSL_API int wc_rng_bank_spawn( struct wc_rng_bank *bank, WC_RNG *child_rng, - byte *nonce, - word32 nonceSz, + byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, int preferred_inst_offset, int timeout_secs, word32 flags); @@ -318,8 +318,8 @@ WOLFSSL_API int wc_rng_bank_spawn( WOLFSSL_API int wc_rng_bank_spawn_new( struct wc_rng_bank *bank, WC_RNG **child_rng, - byte *nonce, - word32 nonceSz, + byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, int preferred_inst_offset, int timeout_secs, word32 flags); @@ -331,21 +331,25 @@ WOLFSSL_API int wc_rng_bank_spawn_new( WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags); WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int first_inst, int last_inst, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags); WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags); WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, int first_inst, int last_inst, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags); From 21b1531606be2af837e3ecfa8d0809b9e54dd834 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:16:04 +0000 Subject: [PATCH 063/102] rng_bank: move backward-compat implementations out of rng_bank.h The pre-v7 boundary shims had grown to roughly six hundred lines of static inline code in a public header, instantiated into every translation unit that included it whether or not any of it was reachable. Move the implementations into rng_bank.c and leave declarations behind. Pure relocation -- review with --color-moved; the only edits are the conversion from static inline to external linkage and the reindentation that follows from leaving the nested conditional blocks. --- wolfcrypt/src/rng_bank.c | 618 ++++++++++++++++++++++++++++++++ wolfssl/wolfcrypt/rng_bank.h | 662 ++++------------------------------- 2 files changed, 689 insertions(+), 591 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 2cca9dbf9af..36e393cfc76 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -2516,4 +2516,622 @@ WOLFSSL_API int wc_rng_bank_debug_stats_snap( #endif /* WC_RNG_DEBUG_STATS */ +#ifndef WC_RNG_HAVE_LOCK + +/* Backward compat: with a pre-v7 FIPS boundary (or WC_RNG_NO_LOCK), the + * latch lives in the bank instance rather than in the (frozen) WC_RNG. + * These are ports of the wc_RNG_lock_*() state machine, including + * WC_RNG_LOCK_ENTROPY_INVALIDATED quarantine/claim/report semantics. + * In every CAS below, the stored value derives only from the CAS-verified + * value and the caller's arguments -- never from a prior load. */ + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + + if (inst == NULL) + return BAD_FUNC_ARG; + + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && + (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_HELD | extra_bits))) + { + return 0; + } + + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else if (cur_lock & WC_RNG_LOCK_HELD) + return BUSY_E; + else + return UNEXPECTED_STATE_E; +} + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get_conditional( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, expected; + + if (inst == NULL) + return BAD_FUNC_ARG; + + expected_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED | WC_RNG_LOCK_ENTROPY_INVALIDATED; + want_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && + (! (expected_extra_bits & WC_RNG_LOCK_ENTROPY_INVALIDATED))) + { + return NEEDS_RECOVERY_E; + } + + expected = (cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & + ~(WC_RNG_LOCK_HELD | WC_RNG_LOCK_ENTROPY_INVALIDATED))) | + expected_extra_bits; + + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && + (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &expected, + expected | WC_RNG_LOCK_HELD | want_extra_bits))) + { + return 0; + } + + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) != + (expected & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + return NEEDS_RECOVERY_E; + } + else if (expected & WC_RNG_LOCK_HELD) + return BUSY_E; + else + return UNEXPECTED_STATE_E; +} + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) +{ + WC_RNG_lock_arg_t cur_lock, new_lock; + if (inst == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + if (! (cur_lock & WC_RNG_LOCK_HELD)) + return OBJECT_NOT_LOCKED_E; + + for (;;) { + new_lock = cur_lock & + ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD)); + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, new_lock)) + break; + } + + if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else + return 0; +} + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, expected, new_lock; + + if (inst == NULL) + return BAD_FUNC_ARG; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + if (! (cur_lock & WC_RNG_LOCK_HELD)) + return OBJECT_NOT_LOCKED_E; + + for (;;) { + new_lock = (cur_lock & + ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD))) | + (extra_bits & WC_RNG_LOCK_REQUIRED); + + expected = WC_RNG_LOCK_HELD | extra_bits | + (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); + + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &expected, new_lock)) + { + if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; + else + return 0; + } + if ((expected & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U)) != + (extra_bits & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U))) + { + break; + } + /* the CAS's failure feedback flows through expected; reseed the + * next reconstruction from it, else a concurrent invalidation + * loops forever. */ + cur_lock = expected; + } + /* conditional release failed: the caller is still the holder. */ + return UNEXPECTED_STATE_E; +} + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state) +{ + if ((inst == NULL) || (state == NULL)) + return BAD_FUNC_ARG; + *state = WOLFSSL_ATOMIC_LOAD(inst->lock); + return 0; +} + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock, new_lock; + if (inst == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + for (;;) { + new_lock = cur_lock & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + new_lock |= extra_bits; + + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, new_lock)) + break; + } + return 0; +} + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + if (inst == NULL) + return BAD_FUNC_ARG; + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | + WC_RNG_LOCK_REQUIRED; + + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock | extra_bits)) + break; + } + return 0; +} + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) +{ + WC_RNG_lock_arg_t cur_lock; + if (inst == NULL) + return BAD_FUNC_ARG; + if (extra_bits & WC_RNG_LOCK_REQUIRED) { + /* WC_RNG_LOCK_REQUIRED is sticky by contract */ + return BAD_FUNC_ARG; + } + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); + + for (;;) { + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock & ~extra_bits)) + break; + } + return 0; +} + +#ifdef HAVE_HASHDRBG + +/* Portable invalidation-recovery helpers. With the in-boundary latch + * (WC_RNG_HAVE_LOCK), invalidation and clear-on-credited-reseed are + * module-enforced and these merely forward; with the bank-side latch, + * the bit is set and cleared out here, clearing only on credited + * reseeds, under the lease, mirroring the in-boundary semantics. */ + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_invalidate_entropy( + struct wc_rng_bank_inst *inst) +{ + WC_RNG_lock_arg_t cur_lock; + + if (inst == NULL) + return BAD_FUNC_ARG; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + for (;;) { + /* Clearing _RECOVERING here is what makes it an epoch witness: an + * in-flight recovery discovers at exit that its seed predates this + * event, and leaves _INVALIDATED asserted. Mirrors + * wc_RNG_invalidate_entropy(). */ + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING) | + WC_RNG_LOCK_ENTROPY_INVALIDATED)) + break; + } + + /* If no lock is held, the saturated reseedCtr is the only way to force + * invalidation semantics on a lock-free consumer; if a lock is held, + * the holder learns at unlock time. */ + if (! (cur_lock & WC_RNG_LOCK_HELD)) + (void)wc_RNG_DRBG_ScheduleReseed(WC_RNG_BANK_INST_TO_RNG(inst)); + + return 0; +} + +/* Acquire the recovery mutex for a reseed that may clear _INVALIDATED. The + * pre-lock boundary's Hash_DRBG_Reseed() can't see the inst-side latch, so the + * _RECOVERING protocol it implements in-boundary is reproduced here. + * + * Sets *recovering iff we asserted _RECOVERING and must therefore release it. + * Returns BUSY_E if another claimant is already recovering -- reachable + * whenever callers are not serialized, so never treat it as unreachable. */ +static int wc_rng_bank_inst_recovery_enter( + struct wc_rng_bank_inst *inst, int *recovering) +{ + WC_RNG_lock_arg_t cur_lock; + + if ((inst == NULL) || (recovering == NULL)) + return BAD_FUNC_ARG; + + *recovering = 0; + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + for (;;) { + if (! (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + return 0; + if (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING) + return BUSY_E; + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_ENTROPY_RECOVERING)) + { + /* We now have the _RECOVERING mutex -- record that fact. */ + *recovering = 1; + return 0; + } + } +} + +/* Release the recovery mutex, and report. Must be called on every path out of + * a recovery_enter() that returned 0, or the instance is stuck at BUSY_E. + * + * On a successful reseed, clears _INVALIDATED alongside _RECOVERING -- unless + * _RECOVERING is already gone, which means wc_rng_bank_inst_invalidate_entropy() + * ran during the reseed: a new event our seed predates, so _INVALIDATED stands + * and we report NEEDS_RECOVERY_E. On a failed reseed, releases the mutex and + * leaves _INVALIDATED asserted, preserving the incoming error. */ +static int wc_rng_bank_inst_recovery_exit( + struct wc_rng_bank_inst *inst, int recovering, int ret) +{ + WC_RNG_lock_arg_t cur_lock; + + if (! recovering) + return ret; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + for (;;) { + if (! (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING)) { + if (ret == 0) + ret = NEEDS_RECOVERY_E; + break; + } + if (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + (ret == 0) + ? (cur_lock & ~(WC_RNG_LOCK_ENTROPY_INVALIDATED | + WC_RNG_LOCK_ENTROPY_RECOVERING)) + : (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING))) + { + break; + } + } + return ret; +} + +WOLFSSL_TEST_VIS int wc_rng_bank_inst_reseed_now( + struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz) +{ + int ret, recovering; + if (inst == NULL) + return BAD_FUNC_ARG; + ret = wc_rng_bank_inst_recovery_enter(inst, &recovering); + if (ret != 0) + return ret; + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), + nonce, nonceSz); + return wc_rng_bank_inst_recovery_exit(inst, recovering, ret); +} + +#ifdef WC_RNG_HAVE_RBGC +WOLFSSL_TEST_VIS int wc_rng_bank_inst_reseed_rbgc( + struct wc_rng_bank_inst *inst, WC_RNG* root, const byte* nonce, + word32 nonceSz) +{ + int ret, recovering; + if (inst == NULL) + return BAD_FUNC_ARG; + + ret = wc_rng_bank_inst_recovery_enter(inst, &recovering); + if (ret != 0) + return ret; + +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + (void)nonce; + (void)nonceSz; + ret = wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root); +#else + ret = wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root, + nonce, nonceSz); +#endif + + return wc_rng_bank_inst_recovery_exit(inst, recovering, ret); +} +#endif /* WC_RNG_HAVE_RBGC */ + +#endif /* HAVE_HASHDRBG */ + +#endif + +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + +/* ---- Legacy FIPS boundary compatibility -------------------------------- + * + * Pre-v7 FIPS boundaries do not export the DRBG accessor and reseed + * scheduling services that wolfcrypt/src/random.c supplies as of FIPS v7 + * (wc_RNG_GetStatus(), wc_RNG_DRBG_Present(), wc_RNG_DRBG_GetReseedCtr(), + * wc_RNG_DRBG_ScheduleReseed(), wc_RNG_DRBG_Stir(), and + * wc_RNG_DRBG_Reseed_Now()). Supply source-compatible static fallbacks + * here, implemented via the public DRBG struct definitions in the legacy + * random.h. These fallbacks are the historic direct-access mechanism, now + * confined to frozen pre-v7 boundaries, which cannot gain new services; + * wherever the in-boundary services exist, they are used instead. + */ + +/* WC_DRBG_OK predates some old FIPS editions, but is 1 in all of them -- force + * consistency. */ +#undef WC_DRBG_OK +#define WC_DRBG_OK 1 + +/* Helpers to access reseedCtr / null-check the active DRBG. The shape of + * struct WC_RNG and the DRBG_*_internal types varies by which DRBGs are + * compiled in; random.h gates the SHA-256 side on !NO_SHA256 and the SHA-512 + * side on WOLFSSL_DRBG_SHA512, so all three live combinations are handled + * separately here. */ +#if defined(WOLFSSL_DRBG_SHA512) && !defined(NO_SHA256) + /* Both DRBGs compiled in: dispatch on the runtime drbgType. */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((rng_ptr)->drbgType == WC_DRBG_SHA512) \ + ? ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + : ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + if ((rng_ptr)->drbgType == WC_DRBG_SHA512) \ + ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + = (val); \ + else \ + ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg == NULL && (rng_ptr)->drbg512 == NULL) +#elif defined(WOLFSSL_DRBG_SHA512) + /* SHA-512 DRBG only (NO_SHA256 defined); the SHA-256 struct and + * rng->drbg field do not exist in this build. */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ + = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg512 == NULL) +#else + /* SHA-256 DRBG only (the historical default). */ + #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ + (((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) + #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ + do { \ + ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ + } while (0) + #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ + ((rng_ptr)->drbg == NULL) +#endif + +/* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to + * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when + * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 + * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 + * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time + * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be + * defined with a (word64) cast (settings.h kernel path) that the preprocessor + * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips + * it without expanding that cast. */ +#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) + wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); +#endif + +#ifdef WC_RNG_HAVE_RBGC + +/* Note, no perso/persoSz in backported wc_InitRngNonceRBGC() */ +#define wc_InitRngRBGC(leaf, root, flags) \ + wc_InitRngNonceRBGC(leaf, root, NULL, 0, flags) + +WOLFSSL_TEST_VIS int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root, word32 flags) { + if ((leaf == NULL) || (root == NULL)) + return BAD_FUNC_ARG; + *leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); + if (*leaf == NULL) + return MEMORY_E; + else + return wc_InitRngNonceRBGC(*leaf, root, NULL, 0, flags); +} + +WOLFSSL_TEST_VIS int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, + const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, + word32 flags) +{ + if ((leaf == NULL) || (root == NULL)) + return BAD_FUNC_ARG; + (void)perso; + (void)persoSz; + *leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); + if (*leaf == NULL) + return MEMORY_E; + else + return wc_InitRngNonceRBGC(*leaf, root, nonce, nonceSz, flags); +} + +#endif /* WC_RNG_HAVE_RBGC */ + +WOLFSSL_TEST_VIS int wc_RNG_GetStatus(const WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + return (int)rng->status; +} + +WOLFSSL_TEST_VIS int wc_RNG_DRBG_Present(const WC_RNG* rng) +{ + return (rng != NULL) && (! WC_RNG_BANK_DRBG_NULL(rng)); +} + +static int wc_RNG_DRBG_Reseed_Nonce( + WC_RNG* rng, const byte* seed, + word32 seedSz, const byte *nonce, + word32 nonceSz) +{ + (void)nonce; + (void)nonceSz; + return wc_RNG_DRBG_Reseed(rng, seed, seedSz); +} + +#if FIPS_VERSION3_NE(5,2,4) +static int wc_RNG_DRBG_GetReseedCtr( + const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr) +{ + if ((rng == NULL) || (reseedCtr == NULL)) + return BAD_FUNC_ARG; + if (WC_RNG_BANK_DRBG_NULL(rng)) + *reseedCtr = 0; + else + *reseedCtr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + return 0; +} +#endif + +WOLFSSL_TEST_VIS int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) +{ + if (rng == NULL) + return BAD_FUNC_ARG; + if (! WC_RNG_BANK_DRBG_NULL(rng)) + WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); + return 0; +} + +#if FIPS_VERSION3_EQ(5,2,4) + +static int wc_RNG_DRBG_Stir_Nonce( + WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz) +{ + (void)nonce; + (void)nonceSz; + return wc_RNG_DRBG_Reseed_Uncredited(rng, seed, seedSz); +} + +WOLFSSL_TEST_VIS int wc_RNG_DRBG_Stir( + WC_RNG* rng, const byte* seed, word32 seedSz) +{ + return wc_RNG_DRBG_Reseed_Uncredited(rng, seed, seedSz); +} + +#else /* FIPS_VERSION3_NE(5,2,4) */ + +static int wc_RNG_DRBG_Stir_Nonce( + WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz) +{ + wc_drbg_reseed_ctr_t saved_ctr; + int ret; + + (void)nonce; + (void)nonceSz; + + if ((rng == NULL) || (seed == NULL)) + return BAD_FUNC_ARG; + if (WC_RNG_BANK_DRBG_NULL(rng)) { + /* defer to wc_RNG_DRBG_Reseed()'s RDRAND-config handling. */ + return wc_RNG_DRBG_Reseed(rng, seed, seedSz); + } + saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + ret = wc_RNG_DRBG_Reseed(rng, seed, seedSz); + /* wc_RNG_DRBG_Reseed() only resets the counter on success, so the + * unconditional restore is exact either way. */ + WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); + return ret; +} + +static int wc_RNG_DRBG_Reseed_Now( + WC_RNG* rng, const byte* nonce, word32 nonceSz) +{ + wc_drbg_reseed_ctr_t saved_ctr; + int ret; + byte scratch[4]; + + if (rng == NULL) + return BAD_FUNC_ARG; + if ((nonce == NULL) && (nonceSz > 0)) + return BAD_FUNC_ARG; + if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) + return RNG_FAILURE_E; + if (WC_RNG_BANK_DRBG_NULL(rng)) { + /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ + return 0; + } + + saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); + WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); + + /* The legacy boundary has no direct reseed-from-source service; a + * minimal generate at the forced counter performs the module's own + * PollAndReSeed() in-boundary. This consumes 4 bytes of output, so on + * success the fresh reseed counter is 2 rather than 1. scratch holds + * only discarded output bytes; XMEMSET suffices for it here. */ + ret = wc_RNG_GenerateBlock(rng, scratch, (word32)sizeof(scratch)); + XMEMSET(scratch, 0, sizeof(scratch)); + + if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { + /* On the legacy boundary, nonce incorporation is a separate + * (uncredited) transition following the reseed, rather than part of + * the same reseed derivation. */ + ret = wc_RNG_DRBG_Stir(rng, nonce, nonceSz); + } + + if ((ret != 0) && + ((wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng) >= + (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL)) + { + /* The reseed did not occur -- restore the counter, leaving it + * unmodified as the contract requires. */ + WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); + } + + return ret; +} +#endif /* FIPS_VERSION3_NE(5,2,4) */ + +#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ + #endif /* WC_RNG_BANK_SUPPORT */ diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 281131d52a0..e44d26a5898 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -378,608 +378,88 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); #endif #endif /* WC_HAVE_RNG_BANKREF */ -#define WC_RNG_BANK_INST_TO_RNG(rng_inst) (&(rng_inst)->rng) +#define WC_RNG_BANK_INST_TO_RNG(rng_inst) ((rng_inst) ? (&((struct wc_rng_bank_inst *)(rng_inst))->rng) : NULL) #ifdef WC_RNG_HAVE_LOCK - - static WC_INLINE int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { - return wc_RNG_lock_get(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); - } - static WC_INLINE int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) { - return wc_RNG_lock_put(WC_RNG_BANK_INST_TO_RNG(inst), 0); - } - static WC_INLINE int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expect_extra_bits) { - return wc_RNG_lock_put_conditional(WC_RNG_BANK_INST_TO_RNG(inst), expect_extra_bits, 0); - } - static WC_INLINE int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t *state) { - return wc_RNG_lock_read(WC_RNG_BANK_INST_TO_RNG(inst), state); - } - static WC_INLINE int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { - return wc_RNG_lock_set_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); - } - static WC_INLINE int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { - return wc_RNG_lock_add_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); - } - static WC_INLINE int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { - return wc_RNG_lock_clear_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); - } - static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_get_conditional( - struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, - WC_RNG_lock_arg_t want_extra_bits) - { - return wc_RNG_lock_get_conditional(WC_RNG_BANK_INST_TO_RNG(inst), - expected_extra_bits, want_extra_bits); - } - - + /* Trivial shims to native lock facility in WC_RNG */ + #define wc_rng_bank_inst_lock_get(inst, extra_bits) \ + wc_RNG_lock_get(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits) + #define wc_rng_bank_inst_lock_put(inst) \ + wc_RNG_lock_put(WC_RNG_BANK_INST_TO_RNG(inst), 0) + #define wc_rng_bank_inst_lock_put_conditional(inst, expect_extra_bits) \ + wc_RNG_lock_put_conditional(WC_RNG_BANK_INST_TO_RNG(inst), expect_extra_bits, 0) + #define wc_rng_bank_inst_lock_read(inst, state) \ + wc_RNG_lock_read(WC_RNG_BANK_INST_TO_RNG(inst), state) + #define wc_rng_bank_inst_lock_set_extra(inst, extra_bits) \ + wc_RNG_lock_set_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits) + #define wc_rng_bank_inst_lock_add_extra(inst, extra_bits) \ + wc_RNG_lock_add_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits) + #define wc_rng_bank_inst_lock_clear_extra(inst, extra_bits) \ + wc_RNG_lock_clear_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits) + #define wc_rng_bank_inst_lock_get_conditional(inst, expected_extra_bits, want_extra_bits) \ + wc_RNG_lock_get_conditional(WC_RNG_BANK_INST_TO_RNG(inst), expected_extra_bits, want_extra_bits) + #ifdef HAVE_HASHDRBG + #define wc_rng_bank_inst_invalidate_entropy(inst) \ + wc_RNG_invalidate_entropy(WC_RNG_BANK_INST_TO_RNG(inst)) + #define wc_rng_bank_inst_reseed_now(inst, nonce, nonceSz) \ + wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), nonce, nonceSz); + #ifdef WC_RNG_HAVE_RBGC + #define wc_rng_bank_inst_reseed_rbgc(inst, root, nonce, nonceSz) \ + wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root, nonce, nonceSz) + #endif /* WC_RNG_HAVE_RBGC */ + #endif /* HAVE_HASHDRBG */ #else /* !WC_RNG_HAVE_LOCK */ - -/* Backward compat: with a pre-v7 FIPS boundary (or WC_RNG_NO_LOCK), the - * latch lives in the bank instance rather than in the (frozen) WC_RNG. - * These are ports of the wc_RNG_lock_*() state machine, including - * WC_RNG_LOCK_ENTROPY_INVALIDATED quarantine/claim/report semantics. - * In every CAS below, the stored value derives only from the CAS-verified - * value and the caller's arguments -- never from a prior load. */ - -static WC_INLINE int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) -{ - WC_RNG_lock_arg_t cur_lock; - - if (inst == NULL) - return BAD_FUNC_ARG; - - extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | - WC_RNG_LOCK_REQUIRED; - - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - - if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) - return NEEDS_RECOVERY_E; - - if ((! (cur_lock & WC_RNG_LOCK_HELD)) && - (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - cur_lock | WC_RNG_LOCK_HELD | extra_bits))) - { - return 0; - } - - if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) - return NEEDS_RECOVERY_E; - else if (cur_lock & WC_RNG_LOCK_HELD) - return BUSY_E; - else - return UNEXPECTED_STATE_E; -} - -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_get_conditional( - struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, - WC_RNG_lock_arg_t want_extra_bits) -{ - WC_RNG_lock_arg_t cur_lock, expected; - - if (inst == NULL) - return BAD_FUNC_ARG; - - expected_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | - WC_RNG_LOCK_REQUIRED | WC_RNG_LOCK_ENTROPY_INVALIDATED; - want_extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | - WC_RNG_LOCK_REQUIRED; - - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - - if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && - (! (expected_extra_bits & WC_RNG_LOCK_ENTROPY_INVALIDATED))) - { - return NEEDS_RECOVERY_E; - } - - expected = (cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & - ~(WC_RNG_LOCK_HELD | WC_RNG_LOCK_ENTROPY_INVALIDATED))) | - expected_extra_bits; - - if ((! (cur_lock & WC_RNG_LOCK_HELD)) && - (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &expected, - expected | WC_RNG_LOCK_HELD | want_extra_bits))) - { - return 0; - } - - if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) != - (expected & WC_RNG_LOCK_ENTROPY_INVALIDATED)) - { - return NEEDS_RECOVERY_E; - } - else if (expected & WC_RNG_LOCK_HELD) - return BUSY_E; - else - return UNEXPECTED_STATE_E; -} - -static WC_INLINE int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) -{ - WC_RNG_lock_arg_t cur_lock, new_lock; - if (inst == NULL) - return BAD_FUNC_ARG; - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - if (! (cur_lock & WC_RNG_LOCK_HELD)) - return OBJECT_NOT_LOCKED_E; - - for (;;) { - new_lock = cur_lock & - ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD)); - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, new_lock)) - break; - } - - if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) - return NEEDS_RECOVERY_E; - else - return 0; -} - -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) -{ - WC_RNG_lock_arg_t cur_lock, expected, new_lock; - - if (inst == NULL) - return BAD_FUNC_ARG; - - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - if (! (cur_lock & WC_RNG_LOCK_HELD)) - return OBJECT_NOT_LOCKED_E; - - for (;;) { - new_lock = (cur_lock & - ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD))) | - (extra_bits & WC_RNG_LOCK_REQUIRED); - - expected = WC_RNG_LOCK_HELD | extra_bits | - (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED); - - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &expected, new_lock)) - { - if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) - return NEEDS_RECOVERY_E; - else - return 0; - } - if ((expected & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U)) != - (extra_bits & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U))) - { - break; - } - /* the CAS's failure feedback flows through expected; reseed the - * next reconstruction from it, else a concurrent invalidation - * loops forever. */ - cur_lock = expected; - } - /* conditional release failed: the caller is still the holder. */ - return UNEXPECTED_STATE_E; -} - -static WC_INLINE int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state) -{ - if ((inst == NULL) || (state == NULL)) - return BAD_FUNC_ARG; - *state = WOLFSSL_ATOMIC_LOAD(inst->lock); - return 0; -} - -static WC_INLINE int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) -{ - WC_RNG_lock_arg_t cur_lock, new_lock; - if (inst == NULL) - return BAD_FUNC_ARG; - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - - for (;;) { - new_lock = cur_lock & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); - extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | - WC_RNG_LOCK_REQUIRED; - new_lock |= extra_bits; - - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, new_lock)) - break; - } - return 0; -} - -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) -{ - WC_RNG_lock_arg_t cur_lock; - if (inst == NULL) - return BAD_FUNC_ARG; - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - - extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | - WC_RNG_LOCK_REQUIRED; - - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - cur_lock | extra_bits)) - break; - } - return 0; -} - -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) -{ - WC_RNG_lock_arg_t cur_lock; - if (inst == NULL) - return BAD_FUNC_ARG; - if (extra_bits & WC_RNG_LOCK_REQUIRED) { - /* WC_RNG_LOCK_REQUIRED is sticky by contract */ - return BAD_FUNC_ARG; - } - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - - extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); - - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - cur_lock & ~extra_bits)) - break; - } - return 0; -} - + /* Prototypes for backward compat implementations in rng_bank.c */ + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get_conditional( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits); + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst); + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state); + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); + #ifdef HAVE_HASHDRBG + WOLFSSL_TEST_VIS int wc_rng_bank_inst_invalidate_entropy( + struct wc_rng_bank_inst *inst); + WOLFSSL_TEST_VIS int wc_rng_bank_inst_reseed_now( + struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz); + #ifdef WC_RNG_HAVE_RBGC + WOLFSSL_TEST_VIS int wc_rng_bank_inst_reseed_rbgc( + struct wc_rng_bank_inst *inst, WC_RNG* root, const byte* nonce, + word32 nonceSz); + #endif /* WC_RNG_HAVE_RBGC */ + #endif /* HAVE_HASHDRBG */ #endif /* !WC_RNG_HAVE_LOCK */ -#ifdef WC_RNG_DEBUG_STATS -WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, - struct wc_rng_bank *bank); -#endif - -/* ---- Legacy FIPS boundary compatibility -------------------------------- - * - * Pre-v7 FIPS boundaries do not export the DRBG accessor and reseed - * scheduling services that wolfcrypt/src/random.c supplies as of FIPS v7 - * (wc_RNG_GetStatus(), wc_RNG_DRBG_Present(), wc_RNG_DRBG_GetReseedCtr(), - * wc_RNG_DRBG_ScheduleReseed(), wc_RNG_DRBG_Stir(), and - * wc_RNG_DRBG_Reseed_Now()). Supply source-compatible static fallbacks - * here, implemented via the public DRBG struct definitions in the legacy - * random.h. These fallbacks are the historic direct-access mechanism, now - * confined to frozen pre-v7 boundaries, which cannot gain new services; - * wherever the in-boundary services exist, they are used instead. - */ #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - -#include - -#ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED - #define WC_DRBG_RESEED_CTR_TYPE_DEFINED - #if defined(WORD64_AVAILABLE) && FIPS_VERSION3_GE(5,2,4) - typedef word64 wc_drbg_reseed_ctr_t; - #else - typedef word32 wc_drbg_reseed_ctr_t; + #ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED + #define WC_DRBG_RESEED_CTR_TYPE_DEFINED + #if defined(WORD64_AVAILABLE) && FIPS_VERSION3_GE(5,2,4) + typedef word64 wc_drbg_reseed_ctr_t; + #else + typedef word32 wc_drbg_reseed_ctr_t; + #endif #endif -#endif - -/* WC_DRBG_OK predates some old FIPS editions, but is 1 in all of them -- force - * consistency. */ -#undef WC_DRBG_OK -#define WC_DRBG_OK 1 - -/* Helpers to access reseedCtr / null-check the active DRBG. The shape of - * struct WC_RNG and the DRBG_*_internal types varies by which DRBGs are - * compiled in; random.h gates the SHA-256 side on !NO_SHA256 and the SHA-512 - * side on WOLFSSL_DRBG_SHA512, so all three live combinations are handled - * separately here. */ -#if defined(WOLFSSL_DRBG_SHA512) && !defined(NO_SHA256) - /* Both DRBGs compiled in: dispatch on the runtime drbgType. */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((rng_ptr)->drbgType == WC_DRBG_SHA512) \ - ? ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - : ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - if ((rng_ptr)->drbgType == WC_DRBG_SHA512) \ - ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - = (val); \ - else \ - ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg == NULL && (rng_ptr)->drbg512 == NULL) -#elif defined(WOLFSSL_DRBG_SHA512) - /* SHA-512 DRBG only (NO_SHA256 defined); the SHA-256 struct and - * rng->drbg field do not exist in this build. */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - ((struct DRBG_SHA512_internal *)(rng_ptr)->drbg512)->reseedCtr \ - = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg512 == NULL) -#else - /* SHA-256 DRBG only (the historical default). */ - #define WC_RNG_BANK_RESEED_CTR(rng_ptr) \ - (((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr) - #define WC_RNG_BANK_SET_RESEED_CTR(rng_ptr, val) \ - do { \ - ((struct DRBG_internal *)(rng_ptr)->drbg)->reseedCtr = (val); \ - } while (0) - #define WC_RNG_BANK_DRBG_NULL(rng_ptr) \ - ((rng_ptr)->drbg == NULL) -#endif - -/* WC_RNG_BANK_SET_RESEED_CTR drives reseedCtr up to WC_RESEED_INTERVAL to - * force a reseed. The SHA-256 DRBG's reseedCtr is 32-bit when - * WORD64_AVAILABLE is undefined (random.h), so a reseed interval above 2^32 - * would truncate to 0 and silently defeat the forced reseed (SP 800-90A Rev1 - * sec 9.3). Fail the build rather than mis-reseed. This is a compile-time - * assert rather than a preprocessor #if because WC_RESEED_INTERVAL may be - * defined with a (word64) cast (settings.h kernel path) that the preprocessor - * cannot evaluate; the outer #if uses only defined() so the 64-bit path skips - * it without expanding that cast. */ -#if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) - wc_static_assert((WC_RESEED_INTERVAL) <= 0xFFFFFFFFUL); -#endif - -#ifdef WC_RNG_HAVE_RBGC - -#define wc_InitRngRBGC(leaf, root, flags) \ - wc_InitRngNonceRBGC(leaf, root, NULL, 0, flags) - -WC_MAYBE_UNUSED static WC_INLINE int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root, word32 flags) { - if ((leaf == NULL) || (root == NULL)) - return BAD_FUNC_ARG; - *leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); - if (*leaf == NULL) - return MEMORY_E; - else - return wc_InitRngNonceRBGC(*leaf, root, NULL, 0, flags); -} - -WC_MAYBE_UNUSED static WC_INLINE int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, - const byte* nonce, word32 nonceSz, - word32 flags) -{ - if ((leaf == NULL) || (root == NULL)) - return BAD_FUNC_ARG; - *leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); - if (*leaf == NULL) - return MEMORY_E; - else - return wc_InitRngNonceRBGC(*leaf, root, nonce, nonceSz, flags); -} - -#endif /* WC_RNG_HAVE_RBGC */ - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_GetStatus(const WC_RNG* rng) -{ - if (rng == NULL) - return BAD_FUNC_ARG; - return (int)rng->status; -} - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Present(const WC_RNG* rng) -{ - return (rng != NULL) && (! WC_RNG_BANK_DRBG_NULL(rng)); -} - -#if FIPS_VERSION3_NE(5,2,4) -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_GetReseedCtr( - const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr) -{ - if ((rng == NULL) || (reseedCtr == NULL)) - return BAD_FUNC_ARG; - if (WC_RNG_BANK_DRBG_NULL(rng)) - *reseedCtr = 0; - else - *reseedCtr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); - return 0; -} -#endif - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) -{ - if (rng == NULL) - return BAD_FUNC_ARG; - if (! WC_RNG_BANK_DRBG_NULL(rng)) - WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); - return 0; -} - -#if FIPS_VERSION3_NE(5,2,4) -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Stir( - WC_RNG* rng, const byte* seed, word32 seedSz) -{ - wc_drbg_reseed_ctr_t saved_ctr; - int ret; - - if ((rng == NULL) || (seed == NULL)) - return BAD_FUNC_ARG; - if (WC_RNG_BANK_DRBG_NULL(rng)) { - /* defer to wc_RNG_DRBG_Reseed()'s RDRAND-config handling. */ - return wc_RNG_DRBG_Reseed(rng, seed, seedSz); - } - saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); - ret = wc_RNG_DRBG_Reseed(rng, seed, seedSz); - /* wc_RNG_DRBG_Reseed() only resets the counter on success, so the - * unconditional restore is exact either way. */ - WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); - return ret; -} - -WC_MAYBE_UNUSED static WC_INLINE int wc_RNG_DRBG_Reseed_Now( - WC_RNG* rng, const byte* nonce, word32 nonceSz) -{ - wc_drbg_reseed_ctr_t saved_ctr; - int ret; - byte scratch[4]; - - if (rng == NULL) - return BAD_FUNC_ARG; - if ((nonce == NULL) && (nonceSz > 0)) - return BAD_FUNC_ARG; - if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) - return RNG_FAILURE_E; - if (WC_RNG_BANK_DRBG_NULL(rng)) { - /* No DRBG instantiated -- nothing to reseed (RDRAND et al.). */ - return 0; - } - - saved_ctr = (wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng); - WC_RNG_BANK_SET_RESEED_CTR(rng, WC_RESEED_INTERVAL); - - /* The legacy boundary has no direct reseed-from-source service; a - * minimal generate at the forced counter performs the module's own - * PollAndReSeed() in-boundary. This consumes 4 bytes of output, so on - * success the fresh reseed counter is 2 rather than 1. scratch holds - * only discarded output bytes; XMEMSET suffices for it here. */ - ret = wc_RNG_GenerateBlock(rng, scratch, (word32)sizeof(scratch)); - XMEMSET(scratch, 0, sizeof(scratch)); - - if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { - /* On the legacy boundary, nonce incorporation is a separate - * (uncredited) transition following the reseed, rather than part of - * the same reseed derivation. */ - ret = wc_RNG_DRBG_Stir(rng, nonce, nonceSz); - } - - if ((ret != 0) && - ((wc_drbg_reseed_ctr_t)WC_RNG_BANK_RESEED_CTR(rng) >= - (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL)) - { - /* The reseed did not occur -- restore the counter, leaving it - * unmodified as the contract requires. */ - WC_RNG_BANK_SET_RESEED_CTR(rng, saved_ctr); - } - - return ret; -} -#endif /* FIPS_VERSION3_NE(5,2,4) */ + #define wc_InitRngRBGC(leaf, root, flags) \ + wc_InitRngNonceRBGC(leaf, root, NULL, 0, flags) + WOLFSSL_TEST_VIS int wc_RNG_GetStatus(const WC_RNG* rng); + WOLFSSL_TEST_VIS int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz); + WOLFSSL_TEST_VIS int wc_RNG_DRBG_Present(const WC_RNG* rng); + WOLFSSL_TEST_VIS int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, + const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, + word32 flags); + WOLFSSL_TEST_VIS int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root, word32 flags); + WOLFSSL_TEST_VIS int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); #endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ -#ifdef HAVE_HASHDRBG - -/* Portable invalidation-recovery helpers. With the in-boundary latch - * (WC_RNG_HAVE_LOCK), invalidation and clear-on-credited-reseed are - * module-enforced and these merely forward; with the bank-side latch, - * the bit is set and cleared out here, clearing only on credited - * reseeds, under the lease, mirroring the in-boundary semantics. */ - -#ifdef WC_RNG_HAVE_LOCK - -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_invalidate_entropy( - struct wc_rng_bank_inst *inst) -{ - if (inst == NULL) - return BAD_FUNC_ARG; - return wc_RNG_invalidate_entropy(WC_RNG_BANK_INST_TO_RNG(inst)); -} -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( - struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz) -{ - if (inst == NULL) - return BAD_FUNC_ARG; - return wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), - nonce, nonceSz); -} -#ifdef WC_RNG_HAVE_RBGC -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_rbgc( - struct wc_rng_bank_inst *inst, WC_RNG* root, const byte* nonce, - word32 nonceSz) -{ - if (inst == NULL) - return BAD_FUNC_ARG; - return wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root, - nonce, nonceSz); -} -#endif /* WC_RNG_HAVE_RBGC */ - -#else /* !WC_RNG_HAVE_LOCK */ - -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_lock_clear_invalidated( - struct wc_rng_bank_inst *inst) -{ - WC_RNG_lock_arg_t cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - cur_lock & ~WC_RNG_LOCK_ENTROPY_INVALIDATED)) - break; - } - return 0; -} - -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_invalidate_entropy( - struct wc_rng_bank_inst *inst) -{ - WC_RNG_lock_arg_t cur_lock; - - if (inst == NULL) - return BAD_FUNC_ARG; - - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - cur_lock | WC_RNG_LOCK_ENTROPY_INVALIDATED)) - break; - } - - /* If no lock is held, the saturated reseedCtr is the only way to force - * invalidation semantics on a lock-free consumer; if a lock is held, - * the holder learns at unlock time. */ - if (! (cur_lock & WC_RNG_LOCK_HELD)) - (void)wc_RNG_DRBG_ScheduleReseed(WC_RNG_BANK_INST_TO_RNG(inst)); - - return 0; -} - -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_now( - struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz) -{ - int ret; - if (inst == NULL) - return BAD_FUNC_ARG; - ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), - nonce, nonceSz); - if (ret == 0) - (void)wc_rng_bank_inst_lock_clear_invalidated(inst); - return ret; -} - -#ifdef WC_RNG_HAVE_RBGC -static WC_INLINE WC_MAYBE_UNUSED int wc_rng_bank_inst_reseed_rbgc( - struct wc_rng_bank_inst *inst, WC_RNG* root, const byte* nonce, - word32 nonceSz) -{ - int ret; - if (inst == NULL) - return BAD_FUNC_ARG; -#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - /* the pre-v7 boundary's wc_RNG_DRBG_ReseedRBGC() predates the nonce - * parameters; honest rejection, as with the boundary's Reseed_Now(). */ - if (nonceSz > 0) - return NOT_COMPILED_IN; - (void)nonce; - ret = wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root); -#else - ret = wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root, - nonce, nonceSz); +#ifdef WC_RNG_DEBUG_STATS +WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, + struct wc_rng_bank *bank); #endif - if (ret == 0) - (void)wc_rng_bank_inst_lock_clear_invalidated(inst); - return ret; -} -#endif /* WC_RNG_HAVE_RBGC */ - -#endif /* !WC_RNG_HAVE_LOCK */ - -#endif /* HAVE_HASHDRBG */ #endif /* WC_RNG_BANK_SUPPORT */ From 8d84c4e0fb9cffaa752db8a63e9a4d72ad5daab8 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:57:35 +0000 Subject: [PATCH 064/102] test: validate the NextSeedGenerate() result before overwriting it The banking loop assigned wc_RNG_DRBG_NextSeedGenerate()'s return value to api_ret and then immediately overwrote it with wc_RNG_DRBG_NextSeedCurrent()'s, so every check below -- the error classification, the ALREADY_E break, and the guard on the monotonic-progress assertion -- was reading the query's result, not the generation's. A hard failure from the generate could not fail the test. Keep the two results in separate variables and classify the generate's own return value. Reported by Fenrir. --- wolfcrypt/test/test.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index d93eee8a41a..05eeb787b60 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30626,23 +30626,25 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) * then the ready sentinel appears */ prev = cur; for (i = 0; i < 64; i++) { - api_ret = wc_RNG_DRBG_NextSeedGenerate( + int gen_ret = wc_RNG_DRBG_NextSeedGenerate( root, (word32)(WC_DRBG_NEXT_SEED_LEN / 7)); + if ((gen_ret != 0) && + (gen_ret != WC_NO_ERR_TRACE(ALREADY_E)) && + (gen_ret != WC_NO_ERR_TRACE(NOT_READY_E)) && + (gen_ret != WC_NO_ERR_TRACE(ENTROPY_RT_E)) && + (gen_ret != WC_NO_ERR_TRACE(ENTROPY_APT_E))) + { + ERROR_OUT(WC_TEST_RET_ENC_EC(gen_ret), out); + } api_ret = wc_RNG_DRBG_NextSeedCurrent(root, &cur); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if ((api_ret == WC_NO_ERR_TRACE(ALREADY_E)) || + if ((gen_ret == WC_NO_ERR_TRACE(ALREADY_E)) || (cur == WC_DRBG_NEXT_SEED_READY)) { break; } - if ((api_ret != 0) && (api_ret != WC_NO_ERR_TRACE(NOT_READY_E)) && - (api_ret != WC_NO_ERR_TRACE(ENTROPY_RT_E)) && - (api_ret != WC_NO_ERR_TRACE(ENTROPY_APT_E))) - { - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - } - if (api_ret == 0) { + if (gen_ret == 0) { if (cur <= prev) ERROR_OUT(WC_TEST_RET_ENC_NC, out); prev = cur; From e604292132a76c7251d9a7162aa0c0d5e0c55760 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:57:35 +0000 Subject: [PATCH 065/102] test: pin the stir's effect on the reseed counter The assertion rejected only a decreased reseed counter, which no implementation would produce, so it could not distinguish a stir from a credited reseed or from a stir that never ran. A stir is one SP 800-90A Rev.1 10.1.1.4 generate with the stir material as additional input, so step 7 advances the counter by exactly one. Require that: a reset to 1 would mean the stir had masqueraded as a credited reseed, and no change at all would mean it never executed. Reported by Fenrir, whose suggested assertion -- that the counter be unchanged -- does not hold: it fails against the current implementation, because incrementing is what the generate is required to do. --- wolfcrypt/test/test.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 05eeb787b60..ae4ff73c200 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30776,8 +30776,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); #if !defined(HAVE_INTEL_RDSEED) && !defined(HAVE_INTEL_RDRAND) - /* consumption is a stir, not an epoch: the reseed counter is not - * reset. */ + /* consumption is a stir, not an epoch: the reseed counter advances + * by the stir's one generate, and is not reset. */ { wc_drbg_reseed_ctr_t ctr_before = 0, ctr_after = 0; api_ret = wc_RNG_DRBG_GetReseedCtr(root, &ctr_before); @@ -30789,7 +30789,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) api_ret = wc_RNG_DRBG_GetReseedCtr(root, &ctr_after); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if (ctr_after < ctr_before) + /* A stir is one SP 800-90A 10.1.1.4 generate and no reseed, so + * the counter advances by exactly one: a reset to 1 would mean + * the stir had masqueraded as a credited reseed, and no change + * at all would mean it never ran. */ + if (ctr_after != ctr_before + 1) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } #else From 306eaea22f7de72d32cb50e18b3f2ce79f8793f7 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:57:35 +0000 Subject: [PATCH 066/102] test: free the leaf RNG from the shared teardown path leaf was declared inside the inner block that initializes it, so the ERROR_OUT() calls between its wc_InitRngNonceRBGC() and its wc_FreeRng() jumped to a teardown that could neither see it nor free it. Any failed assertion in that window leaked the DRBG allocation and whatever else the instance held. Hoist the declaration to function scope under the same guard, track initialization, and free it from out: alongside root. Reported by Fenrir. --- wolfcrypt/test/test.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index ae4ff73c200..5634173fca7 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30544,6 +30544,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) int present; int root_inited = 0; int i; +#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_RBGC) + WC_RNG leaf; + int leaf_inited = 0; +#endif WC_DECLARE_VAR(root, WC_RNG, 1, HEAP_HINT); WC_ATOMIC_INT_ARG cur = 0; WC_ATOMIC_INT_ARG prev = 0; @@ -30833,7 +30837,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) /* a stir must never masquerade as recovery or promotion: consumption * preserves WC_RNG_LOCK_ENTROPY_INVALIDATED and RBGCStratum. */ if (present) { - WC_RNG leaf; WC_RNG_lock_arg_t lock_state; byte frag64[WC_DRBG_NEXT_STIR_LEN]; XMEMSET(frag64, 0x5e, sizeof(frag64)); @@ -30842,6 +30845,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) NULL, 0, WC_RNG_INIT_FLAGS_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + leaf_inited = 1; api_ret = wc_RNG_invalidate_entropy(&leaf); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30868,6 +30872,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) api_ret = wc_RNG_DRBG_Reseed_Now(&leaf, NULL, 0); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + leaf_inited = 0; api_ret = wc_FreeRng(&leaf); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -30876,6 +30881,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) out: +#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_RBGC) + if (leaf_inited) { + int cleanup_ret = wc_FreeRng(&leaf); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } +#endif + if (root_inited) { int cleanup_ret = wc_FreeRng(root); if ((cleanup_ret != 0) && (ret == 0)) From 83f63fc0777d461599753e9548e5335692b20e2d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:57:35 +0000 Subject: [PATCH 067/102] random: reject a NULL nonce with a nonzero length in Reseed_Nonce wc_RNG_DRBG_Reseed_Nonce() accepted nonce == NULL with nonceSz > 0. Hash_df() skips an input whose pointer is NULL regardless of its stated length, so the caller's additional input was discarded and the reseed reported success -- the caller had no way to learn that the material it supplied never entered the derivation. Return BAD_FUNC_ARG, matching the check _InitRng(), SpawnRngRBGC() and wc_RNG_DRBG_ReseedRBGC_local() already make on the same pair. Reported by Fenrir. --- wolfcrypt/src/random.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 95549f91a8d..a477eff6a52 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1038,6 +1038,12 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, return BAD_FUNC_ARG; } + /* Hash_df() skips a NULL input regardless of its stated length, so + * without this the caller's additional input would be dropped and + * success reported. Matches _InitRng() and ReseedRBGC(). */ + if ((nonce == NULL) && (nonceSz > 0)) + return BAD_FUNC_ARG; + ret = rng_lock_required_check(rng); if (ret != 0) return ret; From dd800e0dd6b22acd8e15ec914d0e17014ec05415 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:57:35 +0000 Subject: [PATCH 068/102] random: route an _InitRng() mutex failure through the CSP cleanup The WC_RNG_INIT_FLAGS_USE_FULL_MUTEX arm returned BAD_MUTEX_E directly. By that point the DRBG is instantiated, and the common cleanup that uninstantiates it and frees the allocation sits below the return, so V and C survived a failed _InitRng() -- in memory owned by a WC_RNG the caller has been told is not initialized, and will therefore not pass to wc_FreeRng(). Set ret and fall through to that cleanup instead. SP 800-90A Rev.1 9.4 process step 2 requires the internal state to be erased; FIPS 140-3 IG D.L makes V and C CSPs. Reported by Fenrir; the sibling flag-validation failures were hoisted ahead of instantiation earlier, but this path was missed. --- wolfcrypt/src/random.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index a477eff6a52..fa7aec87ad9 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -2939,13 +2939,19 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX /* deliberately the last init step: no failure path can strand an * initialized mutex. */ - if (wc_InitMutex(&rng->mutex) != 0) - return BAD_MUTEX_E; - rng->flags |= WC_RNG_FLAG_FULL_MUTEX; - if (flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) { - /* born held at both layers: the constructor's caller holds - * the whole latch, mutex included. */ - (void)wc_LockMutex(&rng->mutex); + if (wc_InitMutex(&rng->mutex) != 0) { + /* fall through to the common cleanup below rather than + * returning here: the DRBG is instantiated by this point, and + * V and C must not survive a failed _InitRng(). */ + ret = BAD_MUTEX_E; + } + else { + rng->flags |= WC_RNG_FLAG_FULL_MUTEX; + if (flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) { + /* born held at both layers: the constructor's caller holds + * the whole latch, mutex included. */ + (void)wc_LockMutex(&rng->mutex); + } } #endif } From 235d20291d6e9f4200e546aaf2abeee11377e0f4 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:57:35 +0000 Subject: [PATCH 069/102] rng_bank: bound n_rngs against allocation-size wrap n_rngs was checked only for positivity before being multiplied by sizeof(struct wc_rng_bank_inst) for the instance-array allocation. Where size_t is narrow enough for that product to wrap, the allocation succeeds undersized and the initialization loop immediately runs off the end of it. Reject n_rngs above SIZE_MAX / sizeof(*ctx->rngs) with BAD_LENGTH_E, which is what the static-bank arm already returns for an oversized request. Reported by Fenrir. --- wolfcrypt/src/rng_bank.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 36e393cfc76..ce7e95de6a1 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -77,6 +77,12 @@ WOLFSSL_API int wc_rng_bank_init_nonce( if ((ctx == NULL) || (n_rngs <= 0)) return BAD_FUNC_ARG; + /* the allocation below is sizeof(*ctx->rngs) * n_rngs; on targets where + * size_t is narrow enough for that product to wrap, the initialization + * loop would then run off the end of an undersized array. */ + if ((size_t)n_rngs > (SIZE_MAX / sizeof(*ctx->rngs))) + return BAD_LENGTH_E; + XMEMSET(ctx, 0, sizeof(*ctx)); wolfSSL_RefInit(&ctx->refcount, &ret); From bcb8d1f0ba958f51f5ecf26f28dbd1c681700725 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:57:35 +0000 Subject: [PATCH 070/102] rng_bank: free *leaf when the pre-v7 RBGC constructors fail wc_InitRngRBGC_New() and wc_InitRngNonceRBGC_New() allocated *leaf and then returned wc_InitRngNonceRBGC()'s status directly. On failure the allocation was neither freed nor cleared, and the caller -- holding a non-zero return -- has no contract entitling it to free the pointer. Release it and NULL the output, as SpawnRngRBGC() already does for the mainline constructors. Reported by Fenrir. These are the pre-v7 boundary's compatibility constructors, so this only affects HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) builds. --- wolfcrypt/src/rng_bank.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index ce7e95de6a1..499e9078db7 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -2976,13 +2976,18 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_reseed_rbgc( wc_InitRngNonceRBGC(leaf, root, NULL, 0, flags) WOLFSSL_TEST_VIS int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root, word32 flags) { + int ret; if ((leaf == NULL) || (root == NULL)) return BAD_FUNC_ARG; *leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); if (*leaf == NULL) return MEMORY_E; - else - return wc_InitRngNonceRBGC(*leaf, root, NULL, 0, flags); + ret = wc_InitRngNonceRBGC(*leaf, root, NULL, 0, flags); + if (ret != 0) { + XFREE(*leaf, root->heap, DYNAMIC_TYPE_RNG); + *leaf = NULL; + } + return ret; } WOLFSSL_TEST_VIS int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, @@ -2990,6 +2995,7 @@ WOLFSSL_TEST_VIS int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, const byte *perso, word32 persoSz, word32 flags) { + int ret; if ((leaf == NULL) || (root == NULL)) return BAD_FUNC_ARG; (void)perso; @@ -2997,8 +3003,12 @@ WOLFSSL_TEST_VIS int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root, *leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG); if (*leaf == NULL) return MEMORY_E; - else - return wc_InitRngNonceRBGC(*leaf, root, nonce, nonceSz, flags); + ret = wc_InitRngNonceRBGC(*leaf, root, nonce, nonceSz, flags); + if (ret != 0) { + XFREE(*leaf, root->heap, DYNAMIC_TYPE_RNG); + *leaf = NULL; + } + return ret; } #endif /* WC_RNG_HAVE_RBGC */ From c3855604cad385bb3548dd449c5f72c191be5bb5 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 19:57:35 +0000 Subject: [PATCH 071/102] linuxkm: gate the RNG registry on the features it requires The invalidation registry was gated on the FIPS boundary version alone, but it calls wc_RNG_register_free_hook() and wc_RNG_invalidate_entropy() unconditionally. WC_RNG_NO_FREE_HOOK and WC_RNG_NO_LOCK remove those respectively, so either setting broke the build rather than dropping the feature. Require WC_RNG_HAVE_FREE_HOOK and WC_RNG_HAVE_LOCK as well, and say so in the comment that already explains the old-FIPS exclusion. Reported by Fenrir. --- linuxkm/lkcapi_sha_glue.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 2074d679f8d..32c9ab54a02 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2181,7 +2181,8 @@ static int linuxkm_affinity_unlock(void *arg) { #define WC_LINUXKM_ENTROPY_DAEMON_MAGIC 0x6f77666c -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \ + defined(WC_RNG_HAVE_FREE_HOOK) && defined(WC_RNG_HAVE_LOCK) #define WC_LINUXKM_HAVE_RNG_REGISTRY @@ -2193,7 +2194,9 @@ static int linuxkm_affinity_unlock(void *arg) { * may gather entropy under it. * * Not usable on old FIPS because the mechanism fundamentally depends on - * wc_RNG_register_free_hook(). + * wc_RNG_register_free_hook(), nor under WC_RNG_NO_FREE_HOOK or + * WC_RNG_NO_LOCK, which remove wc_RNG_register_free_hook() and + * wc_RNG_invalidate_entropy() respectively. */ struct linuxkm_rng_object { struct linuxkm_rng_object *prev, *next; From 2c02431529229616e041e8eceac415939dea186d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 22:18:48 -0500 Subject: [PATCH 072/102] linuxkm/lkcapi_sha_glue.c, wolfssl/wolfcrypt/rng_bank.h: * refactor wc_mix_pool_bytes(), when called with small inputs, to use lockless wc_RNG_DRBG_NextStirStore() to a round-robin-rotated target RNG counted using DEFINE_PER_CPU() and friends. * fix config-dependent undeclared-variable access (local_root) in wc_linuxkm_entropy_daemon. * add WC_RNG_BANK_OFFSET_TO_RNG(). --- linuxkm/lkcapi_sha_glue.c | 133 +++++++++++++++++++++++------------ wolfcrypt/src/rng_bank.c | 1 - wolfssl/wolfcrypt/rng_bank.h | 6 +- 3 files changed, 93 insertions(+), 47 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 32c9ab54a02..b5b0d9ca8c8 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -68,6 +68,7 @@ #endif #include #include + #include _Pragma("GCC diagnostic pop"); #endif @@ -2782,7 +2783,13 @@ static int wc_linuxkm_entropy_daemon(void *arg) break; #ifdef WC_LINUXKM_VMGENID_POLL - wc_linuxkm_vmgenid_poll(&vmgenid_poll_state, local_root); + wc_linuxkm_vmgenid_poll(&vmgenid_poll_state, + #if defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED) + local_root + #else + NULL + #endif + ); #endif #if defined(WC_RNG_HAVE_LOCK) && \ @@ -4096,20 +4103,33 @@ static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) { } /* Note, wc_mix_pool_bytes() only injects the supplied entropy into one RNG, - * CPU-local when uncontended. This routine can be pegged by unprivileged - * users, so its impact needs to stay as CPU-local as possible. */ + * selection dependent on WC_RNG_HAVE_NEXT_SEED and the size of the input. This + * routine can be pegged by unprivileged users, so with large input, it tries to + * keep its impact as CPU-local as possible. */ static int wc_mix_pool_bytes(const void *buf, size_t len) { int ret; struct wc_rng_bank *ctx = NULL; - word32 flags = - WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | - WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST; - struct wc_rng_bank_inst *drbg = NULL; + unsigned long uncredited_nonce; + int can_sleep = wc_linuxkm_can_block(); if (len > WC_MAX_UINT_OF(word32)) return -EFBIG; - /* Continue even if len == 0 -- churning the DRBG is still meaningful. */ + if (len == 0) { + uncredited_nonce = random_get_entropy(); + buf = &uncredited_nonce; + len = sizeof uncredited_nonce; + } + + if (! can_sleep) { +#ifdef WC_RNG_HAVE_NEXT_SEED + if (len > WC_DRBG_NEXT_STIR_LEN) + len = WC_DRBG_NEXT_STIR_LEN; +#else + if (len > 64) + len = 64; +#endif + } ret = wc_rng_bank_default_checkout(&ctx); if (ret) { @@ -4120,52 +4140,75 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { return -EFAULT; } - if (wc_linuxkm_can_block()) - flags |= WC_RNG_BANK_FLAG_AFFINITY_LOCK; +#ifdef WC_RNG_HAVE_NEXT_SEED + { + WC_RNG *stir_root = wc_rng_bank_daemon_root_get(ctx); + + if (len <= WC_DRBG_NEXT_STIR_LEN) { + static DEFINE_PER_CPU(int, stir_index); + unsigned int this_index = this_cpu_inc_return(stir_index); + if (this_index >= (unsigned int)ctx->n_rngs) { + this_index = 0; + /* we may have been migrated since this_cpu_inc_return() -- + * tolerate the harmless reset of a different counter. */ + this_cpu_write(stir_index, 0); + } + (void)wc_RNG_DRBG_NextStirStore( + WC_RNG_BANK_OFFSET_TO_RNG(ctx, this_index), (const byte *)buf, + (word32)len); + } + + if (stir_root != NULL) { + /* note that input beyond WC_DRBG_NEXT_STIR_LEN is discarded. */ + (void)wc_RNG_DRBG_NextStirStore(stir_root, (const byte *)buf, + (word32)len); + } + } + + if (len > WC_DRBG_NEXT_STIR_LEN) +#endif /* WC_RNG_HAVE_NEXT_SEED */ + { + word32 flags = + WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | + WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST; + struct wc_rng_bank_inst *drbg = NULL; + + if (can_sleep) + flags |= WC_RNG_BANK_FLAG_AFFINITY_LOCK; #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - else - flags |= WC_RNG_BANK_FLAG_NO_VECTOR_OPS; + else + flags |= WC_RNG_BANK_FLAG_NO_VECTOR_OPS; #endif - ret = wc_rng_bank_checkout(ctx, &drbg, 0, 0, flags); - if (ret != 0) { - ret = -EINVAL; - goto out; - } + ret = wc_rng_bank_checkout(ctx, &drbg, 0, 0, flags); + if (ret != 0) { + ret = -EINVAL; + goto out; + } - if (! wc_RNG_DRBG_Present(WC_RNG_BANK_INST_TO_RNG(drbg))) { - ret = 0; /* consistent with wc_RNG_DRBG_Reseed() behavior in RDRAND configs. */ - goto out; - } + if (! wc_RNG_DRBG_Present(WC_RNG_BANK_INST_TO_RNG(drbg))) { + ret = 0; /* consistent with wc_RNG_DRBG_Reseed() behavior in RDRAND configs. */ + goto out; + } - /* Mix without crediting the contributed entropy -- - * wc_RNG_DRBG_Stir() leaves the reseed counter unmodified, - * so only the module's own seed source resets the reseed schedule. */ - ret = wc_RNG_DRBG_Stir(WC_RNG_BANK_INST_TO_RNG(drbg), buf, - (word32)len); -#ifdef WC_RNG_HAVE_NEXT_SEED - /* The leased instance was just stirred directly, above. The daemon root -- - * the one node the harvest wire otherwise never reaches -- is single-owner - * and can't be stirred from here; deposit the fragment into its uncredited - * accumulator instead (writer-safe without a lease: read-copy-store, see - * wc_RNG_DRBG_NextStirStore()), for consumption at the root's own - * next generate. The supplied entropy is unconditionally absorbed by - * wc_RNG_DRBG_NextStirStore() -- if nextStirLen is - * already full, the absorption is by xorbuf(). */ - if (len > 0) { - WC_RNG *stir_root = wc_rng_bank_daemon_root_get(ctx); - if (stir_root != NULL) - (void)wc_RNG_DRBG_NextStirStore(stir_root, (const byte *)buf, - (word32)len); + /* Mix without crediting the contributed entropy -- + * wc_RNG_DRBG_Stir() leaves the reseed counter unmodified, + * so only the module's own seed source resets the reseed schedule. */ + ret = wc_RNG_DRBG_Stir(WC_RNG_BANK_INST_TO_RNG(drbg), buf, + (word32)len); + + out: + + if (drbg) + (void)wc_rng_bank_inst_checkin(&drbg); } -#endif /* WC_RNG_HAVE_NEXT_SEED */ + + if (buf == &uncredited_nonce) + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret != 0) ret = -EINVAL; -out: - - if (drbg) - (void)wc_rng_bank_inst_checkin(&drbg); if (ctx) (void)wc_rng_bank_default_checkin(&ctx); diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 499e9078db7..c1bb23a40d1 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -636,7 +636,6 @@ WOLFSSL_API int wc_rng_bank_checkout( #endif int maybe_recovery_claim = 0; - if (rng_inst == NULL) return BAD_FUNC_ARG; diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index e44d26a5898..0cf191e5af3 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -378,7 +378,11 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); #endif #endif /* WC_HAVE_RNG_BANKREF */ -#define WC_RNG_BANK_INST_TO_RNG(rng_inst) ((rng_inst) ? (&((struct wc_rng_bank_inst *)(rng_inst))->rng) : NULL) +#define WC_RNG_BANK_INST_TO_RNG(rng_inst) \ + ((rng_inst) ? (&((struct wc_rng_bank_inst *)(rng_inst))->rng) : NULL) +#define WC_RNG_BANK_OFFSET_TO_RNG(bank, n) \ + ((((n) >= 0) && ((unsigned)(n) < (unsigned)(bank)->n_rngs)) ? \ + WC_RNG_BANK_INST_TO_RNG(&(bank)->rngs[n]) : NULL) #ifdef WC_RNG_HAVE_LOCK /* Trivial shims to native lock facility in WC_RNG */ From 1d26b47b2cf6faef9e7a9ad834d542700e92e2d6 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 22:19:08 -0500 Subject: [PATCH 073/102] wolfcrypt/src/random.c: in wc_RNG_DRBG_NextStirNow(), add early NOT_READY_E return if reseed is due. --- wolfcrypt/src/random.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index fa7aec87ad9..014b70c4453 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -4172,8 +4172,8 @@ static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, } static WC_INLINE int NextStirPtrs(WC_RNG* rng, byte** seed, - word32 *nextSeedSz, - wolfSSL_Atomic_Int** len) + word32 *nextSeedSz, + wolfSSL_Atomic_Int** len) { #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { @@ -4650,6 +4650,7 @@ int wc_RNG_DRBG_NextStirNow(WC_RNG* rng) wolfSSL_Atomic_Int* lenp; word32 nextSeedSz; WC_ATOMIC_INT_ARG expected = WC_DRBG_NEXT_SEED_READY; + wc_drbg_reseed_ctr_t reseedCtr; int ret; if (rng == NULL) @@ -4663,6 +4664,13 @@ int wc_RNG_DRBG_NextStirNow(WC_RNG* rng) if (rng->status != DRBG_OK) return RNG_FAILURE_E; + /* If a reseed is due, the RNG is not ready for a stir. */ + ret = wc_RNG_DRBG_GetReseedCtr(rng, &reseedCtr); + if (ret < 0) + return ret; + if (reseedCtr >= WC_RESEED_INTERVAL) + return NOT_READY_E; + ret = NextStirPtrs(rng, &seed, &nextSeedSz, &lenp); if (ret != 0) { /* No DRBG instantiated -- nothing to stir (RDRAND et al.). */ From bb349fe7aac3d8705c2d96f5fc1fdda0f757c17e Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 12 Sep 2026 23:30:41 -0500 Subject: [PATCH 074/102] wolfcrypt/src/random.c: add explanatory comment to wc_RNG_DRBG_NextSeedGenerate_local() re seed test failures. --- wolfcrypt/src/random.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 014b70c4453..d6b34ac1988 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -4472,6 +4472,32 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, * both, and the health-test failure is the more * informative code and wins over the release's BUSY_E. */ (void)NextSeedProducerRelease(lenp, WC_DRBG_NEXT_SEED_EMPTY); + + /* The health-test failure belongs to the depositor's seed + * collection, not to the destination RNG. The depositor collects + * via wc_GenerateSeed(), banks into rng's aperture, and tests + * before publishing; on failure the aperture is reset to _EMPTY, + * so nothing untested is ever visible to rng. rng is a passive + * destination here -- it did not consume the material, and its + * state, status and reseed schedule are untouched. Do not mark it + * failed. + * + * PollAndReSeed() looks similar and is not: there, rng is reseeding + * itself from its own seed source, the tested material is on the + * path into its own state, and a failure indeed means that that + * instance's source has failed. DRBG_FAILED is correct there and + * wrong here. Per-instance attribution is meaningful, not + * arbitrary: seed sources are frequently core-local (RDSEED among + * them), so one instance's source can fail while its siblings' are + * healthy. + * + * Bigger picture: An RCT/APT failure is an entropy-source event (SP + * 800-90B 4.4), and wc_RNG_TestSeed's cutoffs carry a designed + * false-positive rate. A terminal response from a thread that is + * not the instance's owner would effectively be a remote kill + * primitive on a tuned statistical alarm. + */ + return ret; } else { From 76143ceb0232ba3b0f28b49c3906a6a6d40371e9 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sun, 13 Sep 2026 00:08:17 -0500 Subject: [PATCH 075/102] wolfcrypt/src/random.c: remove frivolous rng_lock_required_check() in wc_FreeRng(); wolfcrypt/src/rng_bank.c: in wc_rng_bank_fini(), fix comment around RNG teardown lease, and capture and report failure code from wc_FreeRng(), percolating to the caller as RNG_FAILURE_E (signaling that the bank was freed, but wc_FreeRng() had complaints along the way). --- wolfcrypt/src/random.c | 11 +++++------ wolfcrypt/src/rng_bank.c | 27 ++++++++++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index d6b34ac1988..0a9f37e043c 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -5011,15 +5011,14 @@ int wc_FreeRng(WC_RNG* rng) { int ret = 0; - if (rng != NULL) { - ret = rng_lock_required_check(rng); - if (ret != 0) - return ret; - } - if (rng == NULL) return BAD_FUNC_ARG; + /* Note, deallocation proceeds regardless of RNG lock status. Lifecycle + * management is the caller's responsibility, and a lock inside the object + * cannot arbitrate deallocation. + */ + #ifdef WC_HAVE_RNG_BANKREF if (rng->flags & WC_RNG_FLAG_BANKREF) return wc_BankRef_Release(rng); diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index c1bb23a40d1..b795c71c62c 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -330,6 +330,7 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { int i; int ret; WC_ATOMIC_INT_ARG new_refcount; + int rng_free_failed = 0; if (ctx == NULL) return BAD_FUNC_ARG; @@ -396,11 +397,10 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { } for (i = 0; i < ctx->n_rngs; ++i) { - /* Lease-taking teardown: wc_FreeRng() on a _LOCK_REQUIRED - * instance is (correctly) refused without the lease, so take - * it -- structurally uncontended at refcount zero with the - * held-check above passed. The latch dies held in dying - * memory, per the uncleared-on-free contract. */ + /* Lease-taking teardown, for internal consistency checking -- + * structurally uncontended at refcount zero with the held-check + * above passed. The latch dies held in dying memory, per the + * lock-uncleared-on-free contract of wc_FreeRng(). */ if ((wc_rng_bank_inst_lock_get(&ctx->rngs[i], 0) != 0) && (wc_rng_bank_inst_lock_get_conditional(&ctx->rngs[i], WC_RNG_LOCK_ENTROPY_INVALIDATED, 0) != 0)) @@ -414,7 +414,17 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { ret = BAD_STATE_E; continue; } - wc_FreeRng(&ctx->rngs[i].rng); + { + int free_ret = wc_FreeRng(&ctx->rngs[i].rng); + if (free_ret != 0) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "wc_rng_bank_fini(): wc_FreeRng() on RNG #%d returned " + "error %d.\n", i, free_ret); +#endif + ++rng_free_failed; + } + } } if (ret == WC_NO_ERR_TRACE(BAD_STATE_E)) return ret; @@ -431,7 +441,10 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { ctx->flags = WC_RNG_BANK_FLAG_NONE; ctx->cb_arg = NULL; - return 0; + if (rng_free_failed > 0) + return RNG_FAILURE_E; + else + return 0; } #ifndef WC_RNG_BANK_STATIC From 8ef87b481f0e907a1f7436a4d302bf06637bd6fd Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sun, 13 Sep 2026 01:23:40 -0500 Subject: [PATCH 076/102] wolfcrypt/src/random.c: implement rng_pid_change_check(), and use it in wc_RNG_Pool_Extract(), wc_RNG_DRBG_NextSeedNow_Nonce(), and wc_RNG_GenerateBlock(), to guard against duplicate internal state. --- wolfcrypt/src/random.c | 105 ++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 32 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 0a9f37e043c..b563e1944bd 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3111,6 +3111,60 @@ int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz, heap, devId, NULL, flags); } +#if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) + +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) +static int PollAndReSeed(WC_RNG* rng, const byte* additional, + word32 additionalSz); +#endif + +/* rng_pid_change_check() is used by wc_RNG_Pool_Extract(), + * wc_RNG_DRBG_NextSeedNow_Nonce(), and wc_RNG_GenerateBlock(), to assure that + * the RNG is freshly seeded after a fork(), to avoid seeding or generating from + * duplicated internal state. + */ +static int rng_pid_change_check(WC_RNG* rng) { + int ret; + int my_pid = getpid(); + + if (rng->pid == my_pid) + return 0; + + rng->pid = my_pid; +#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) + ret = PollAndReSeed(rng, NULL, 0); + if (ret != DRBG_SUCCESS) { + rng->status = DRBG_FAILED; + ret = RNG_FAILURE_E; + } +#else + ret = 0; +#endif + +#ifdef WC_RNG_HAVE_POOL + WOLFSSL_ATOMIC_STORE(rng->poolState, 0); +#endif +#ifdef WC_RNG_HAVE_NEXT_SEED + #ifndef NO_SHA256 + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { + NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); + WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextStirLen, + WC_DRBG_NEXT_SEED_EMPTY); + } + #endif + #ifdef WOLFSSL_DRBG_SHA512 + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { + NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); + WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextStirLen, + WC_DRBG_NEXT_SEED_EMPTY); + } + #endif +#endif /* WC_RNG_HAVE_NEXT_SEED */ + + return ret; +} +#endif /* HAVE_GETPID && !WOLFSSL_NO_GETPID */ + #ifdef WC_RNG_HAVE_LOCK /* Note, in CAS updates here, the stored value derives only from expected and @@ -3671,11 +3725,18 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) if (rng->pool == NULL) return BAD_STATE_E; - /* Fail closed: no serving output on behalf of an out-of-service DRBG, - * and its pooled output is unusable material at rest -- burn it. A - * concurrent writer's CAS fails against the store and abandons. */ +#if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) + { + int ret = rng_pid_change_check(rng); + if (ret != 0) + return ret; + } +#endif + + /* Fail closed: no serving output on behalf of an out-of-service DRBG, and + * its pooled output is unusable material at rest. A concurrent writer's + * CAS fails against the store and abandons. */ if (wc_RNG_DRBG_Present(rng) && (rng->status != DRBG_OK)) { - ForceZero(rng->pool, rng->poolSize); WOLFSSL_ATOMIC_STORE(rng->poolState, 0); return RNG_FAILURE_E; } @@ -4627,7 +4688,12 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, /* Identical outcome mapping to the generate-path reseed. */ if (ret == DRBG_SUCCESS) { +#if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) + /* Check for PID change after consuming the banked seed. */ + ret = rng_pid_change_check(rng); +#else ret = 0; +#endif } else if (ret == WC_NO_ERR_TRACE(DRBG_CONT_FAILURE)) { ret = DRBG_CONT_FIPS_E; @@ -4803,34 +4869,9 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #endif #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) - if (rng->pid != getpid()) { - rng->pid = getpid(); - ret = PollAndReSeed(rng, NULL, 0); - if (ret != DRBG_SUCCESS) { - rng->status = DRBG_FAILED; - return RNG_FAILURE_E; - } - - #ifdef WC_RNG_HAVE_POOL - WOLFSSL_ATOMIC_STORE(rng->poolState, 0); - #endif - #ifdef WC_RNG_HAVE_NEXT_SEED - #ifndef NO_SHA256 - if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); - WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextStirLen, - WC_DRBG_NEXT_SEED_EMPTY); - } - #endif - #ifdef WOLFSSL_DRBG_SHA512 - if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { - NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); - WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextStirLen, - WC_DRBG_NEXT_SEED_EMPTY); - } - #endif - #endif /* WC_RNG_HAVE_NEXT_SEED */ - } + ret = rng_pid_change_check(rng); + if (ret != 0) + return ret; #endif #if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_LOCK) && \ From 758cf13b500789bce1a6eb155894353e45dd1317 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sun, 13 Sep 2026 01:23:52 -0500 Subject: [PATCH 077/102] wolfcrypt/src/random.c: in wc_RNG_DRBG_NextStirNow(), burn the stir aperture after absorbing it. --- wolfcrypt/src/random.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index b563e1944bd..7da34c08450 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -4783,6 +4783,13 @@ int wc_RNG_DRBG_NextStirNow(WC_RNG* rng) ++rng->_stats_nextstirs_redeemed; #endif + /* Always burn consumed data before releasing it, even if it's uncredited + * noise. Unlike the seed aperture, the stir aperture has no producer + * claim: lease-free depositors xorbuf() into it at any time during + * _CONSUMING, so the burn here can only discard an incoming overflow + * fragment, after the full aperture has already been absorbed by + * the StirGenerate() above. */ + ForceZero(seed, nextSeedSz); WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); return ret; From 6ff1906a022889375e56efb8d39a94cd7ee93eae Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sun, 13 Sep 2026 13:07:51 -0500 Subject: [PATCH 078/102] wolfcrypt/src/random.c: in wc_RNG_Pool_Collect2(), reject inferior-stratum generators. --- wolfcrypt/src/random.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 7da34c08450..4a848403f2e 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3651,6 +3651,18 @@ int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, word32 n) return BAD_FUNC_ARG; if (rng_dest->pool == NULL) return BAD_STATE_E; + +#ifdef WC_RNG_HAVE_RBGC + /* Pool data is served directly as DRBG output, via wc_RNG_Pool_Extract(). + * To preserve the destination's provenance guarantee (SP 800-90C + * sect. 7.3.1 item 16, no output to a predecessor), the generator stratum + * must not be deeper than the destination stratum. Contrast with stir data + * (wc_RNG_DRBG_ReseedRBGC_local() uncredited path), which has and imparts + * no provenance. */ + if (rng_src->RBGCStratum > rng_dest->RBGCStratum) + return BAD_FUNC_ARG; +#endif + if (n == 0) return 0; @@ -3942,6 +3954,9 @@ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, else if (root->RBGCStratum == WC_RNG_RBGC_USER_SEED_STRATUM - 1) return SEQ_OVERFLOW_E; } + /* else the RBGC strata are irrelevant -- stir data has no implication of + * provenance, and can legitimately be wall clock time or even strings of + * zeros. */ #ifdef WOLFSSL_SMALL_STACK_CACHE seed = rng->newSeed_buf; From 64ea3bc6ca8ab4a0b544c9a6892eeb0bf89b7078 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sun, 13 Sep 2026 14:01:12 -0500 Subject: [PATCH 079/102] wolfcrypt/src/rng_bank.[ch]: * in struct wc_rng_bank, rename daemon_root to root_rng, and change its gate from WC_RNG_BANK_HAVE_DAEMON_SUPPORT to WC_RNG_HAVE_RBGC || WC_RNG_HAVE_NEXT_SEED || WC_RNG_HAVE_POOL. * add devId slot to struct wc_rng_bank. * rename WC_RNG_BANK_FLAG_INIT_RBGC to WC_RNG_BANK_FLAG_RBGC, and support it in wc_rng_bank_reseed_range(). * remove root argument from wc_rng_bank_next_seed_generate_rbgc() (bank->root_rng is now implicit). * remove wc_rng_bank_daemon_root_set() and wc_rng_bank_daemon_root_get(), and add wc_rng_bank_root_rng_init() and wc_rng_bank_root_rng_get(). * revert wc_rng_bank_inst_lock_get() et al from macros back to inlines, to assure type enforcement, and remove dangerous cast from WC_RNG_BANK_INST_TO_RNG(). * in wc_rng_bank_init_nonce(), use the persistent ctx->root_rng, removing the ephemeral-on-stack "WC_RNG root". * in wc_rng_bank_fini(), clean up ctx->root_rng. * in wc_rng_bank_reseed_range(), implement support for WC_RNG_BANK_FLAG_RBGC. * in wc_rng_bank_invalidate_entropy(), add invalidation of bank->root_rng. linuxkm/lkcapi_sha_glue.c: * in wc_linuxkm_rng_state_invalidate(), remove now-obsolete special-case "daemon_root" code, and pass WC_RNG_BANK_FLAG_RBGC to the inline (non-daemon) wc_rng_bank_reseed_range(). * in wc_linuxkm_entropy_daemon(), rename local_root to root_rng, assign it at entry from wc_rng_bank_root_rng_get(bank), and remove the in-daemon cleanup of local_root; move the periodic explicit reseed of the root_rng to precede the pooling pass. --- .wolfssl_known_macro_extras | 1 + linuxkm/lkcapi_sha_glue.c | 168 +++++++++++------------------- wolfcrypt/src/random.c | 2 - wolfcrypt/src/rng_bank.c | 195 ++++++++++++++++++++++++++--------- wolfcrypt/test/test.c | 10 ++ wolfssl/wolfcrypt/rng_bank.h | 115 ++++++++++++++------- 6 files changed, 296 insertions(+), 195 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 1416883e373..bbc58d08f1e 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -804,6 +804,7 @@ WC_PKCS12_PBKDF_USING_MP_API WC_PROTECT_ENCRYPTED_MEM WC_PUF_HELPER_COMPACT WC_PUF_SHA3 +WC_RNG_BANK_NO_DAEMON_SUPPORT WC_RNG_BANK_NO_DEFAULT_SUPPORT WC_RNG_BLOCKING WC_RNG_NO_FREE_HOOK diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index b5b0d9ca8c8..3794bdf5551 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2310,14 +2310,10 @@ static int wc_linuxkm_rng_state_invalidate(void) { mutex_lock(&wc_linuxkm_rng_registry_mutex); for (obj = wc_linuxkm_rng_registry_head; obj != NULL; obj = obj->next) { if (obj->is_bank) { - WC_RNG *daemon_root; int this_ret = wc_rng_bank_invalidate_entropy(obj->bank, 0); if ((this_ret != 0) && (ret == 0)) ret = this_ret; #ifndef WC_LINUXKM_NO_ENTROPY_DAEMON - daemon_root = wc_rng_bank_daemon_root_get(obj->bank); - if (daemon_root != NULL) - (void)wc_RNG_invalidate_entropy(daemon_root); if (WOLFSSL_ATOMIC_LOAD(obj->bank->daemon_magic) == WC_LINUXKM_ENTROPY_DAEMON_MAGIC) { @@ -2335,7 +2331,8 @@ static int wc_linuxkm_rng_state_invalidate(void) { this_ret = wc_rng_bank_reseed_range( obj->bank, 0, -1, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, - WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT); + WC_LINUXKM_INITRNG_TIMEOUT_SEC, + WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_RBGC); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); if ((this_ret != 0) && (ret == 0)) ret = this_ret; @@ -2726,37 +2723,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) if (WOLFSSL_ATOMIC_LOAD(bank->daemon_magic) != WC_LINUXKM_ENTROPY_DAEMON_MAGIC) return -EINVAL; -#if defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED) - /* Daemon-local source DRBG for pool top-offs: a full peer of the bank's - * instances, inline-reseeded at WC_LINUXKM_BONUS_RESEED_INTERVAL cadence in - * the daemon's task context, torn down through wc_FreeRng() at shutdown. - * wc_RNG_Pool_Collect2() is called to generate bytes into the destination - * pools with flow that stays confined within random.c, hence inside the - * FIPS boundary. */ - WC_RNG *local_root = (WC_RNG *)XMALLOC(sizeof(*local_root), NULL, - DYNAMIC_TYPE_RNG); - int local_root_reseed_countdown = WC_LINUXKM_BONUS_RESEED_INTERVAL; - - if (local_root != NULL) { - unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_InitRngNonce(local_root, (byte *)&uncredited_nonce, sizeof uncredited_nonce); - ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); - if (ret != 0) { - pr_err("wc_entropyd: pool source DRBG init failed: %d -- " - "pool top-off disabled\n", ret); - XFREE(local_root, NULL, DYNAMIC_TYPE_RNG); - local_root = NULL; - } - else { - /* published for wc_linuxkm_rng_state_invalidate(); retracted before - * teardown. safe: the random_bytes handlers are unregistered - * (and drained) before the daemon is stopped. */ - (void)wc_rng_bank_daemon_root_set(bank, local_root); - } - } + struct WC_RNG *root_rng = wc_rng_bank_root_rng_get(bank); + int root_rng_reseed_countdown = 0; #ifdef WC_RNG_HAVE_POOL - if (local_root != NULL) { + if (root_rng != NULL) { /* One-time pool allocation for every instance, before any * extractor can hold a lease against a nonempty ring. A * failure leaves that instance poolless: extract-side callers @@ -2773,7 +2744,6 @@ static int wc_linuxkm_entropy_daemon(void *arg) } } #endif /* WC_RNG_HAVE_POOL */ -#endif /* WC_RNG_HAVE_POOL || WC_RNG_HAVE_NEXT_SEED */ for (;;) { int progress = 0; @@ -2783,35 +2753,28 @@ static int wc_linuxkm_entropy_daemon(void *arg) break; #ifdef WC_LINUXKM_VMGENID_POLL - wc_linuxkm_vmgenid_poll(&vmgenid_poll_state, - #if defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED) - local_root - #else - NULL - #endif - ); + wc_linuxkm_vmgenid_poll(&vmgenid_poll_state, root_rng); #endif -#if defined(WC_RNG_HAVE_LOCK) && \ - (defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED)) - /* deterministic local_root recovery after a state-invalidation +#ifdef WC_RNG_HAVE_LOCK + /* deterministic root_rng recovery after a state-invalidation * event: the saturated reseedCtr from wc_RNG_invalidate_entropy() * also forces this, but that write races our own generates (the * root is unleased by design), so the flag is the authoritative * signal and this the authoritative response. */ - if (local_root != NULL) { + if (root_rng != NULL) { WC_RNG_lock_arg_t root_lock_state; - if ((wc_RNG_lock_read(local_root, &root_lock_state) == 0) && + if ((wc_RNG_lock_read(root_rng, &root_lock_state) == 0) && (root_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { unsigned long uncredited_nonce = random_get_entropy(); int inv_ret = wc_RNG_DRBG_Reseed_Now( - local_root, + root_rng, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); if (inv_ret != 0) pr_err_ratelimited("wc_entropyd: post-invalidation " - "local_root reseed failed: %d\n", inv_ret); + "root_rng reseed failed: %d\n", inv_ret); } } #endif @@ -2848,7 +2811,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) #endif /* HAVE_HASHDRBG */ #ifdef WC_RNG_HAVE_NEXT_SEED - if (local_root != NULL) { + if (root_rng != NULL) { /* congestion-triggered RBGC seed pass. */ for (i = 0; i < bank->n_rngs; i++) { wc_drbg_reseed_ctr_t this_reseedCtr; @@ -2863,7 +2826,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) (this_NextSeedCurrent != WC_DRBG_NEXT_SEED_CONSUMING)) { ret = wc_rng_bank_next_seed_generate_rbgc( - bank, i, WC_DRBG_NEXT_SEED_LEN, local_root); + bank, i, WC_DRBG_NEXT_SEED_LEN); congested_progress = 1; if (ret == 0) progress = 1; @@ -2873,19 +2836,42 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_NEXT_SEED */ + /* Periodic explicit reseed of the root_rng, with a fresh cycle-counter + * nonce -- scheduled fresh entropy in task context, rather than waiting + * for the counter-forced internal reseed. */ + if ((root_rng != NULL) && (--root_rng_reseed_countdown < 0) && (! congested_progress)) { +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + ret = wc_RNG_DRBG_Reseed_Now(root_rng, NULL, 0); +#else + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now(root_rng, + (byte *)&uncredited_nonce, + sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, sizeof uncredited_nonce); +#endif + if (ret == 0) { + root_rng_reseed_countdown = + WC_LINUXKM_BONUS_RESEED_INTERVAL; + } + else { + pr_err_ratelimited( + "wc_entropyd: pool source reseed failed: %d\n", ret); + } + } + #ifdef WC_RNG_HAVE_POOL /* pooling pass -- run this pass even if there was high-load seed - * generation, as it is good defense against reseedCtr exhaustion. + * generation, as it is good defense against high load scenarios. */ - for (i = 0; i < bank->n_rngs; i++) { - /* pool top-off: fill whatever free span the ring reports. - * The fullness peek is a lockless aperture load; Collect2() - * re-clamps against a fresh snapshot and publishes by CAS, - * so staleness costs at most a wasted attempt. Progress - * accounting keys on the peek, not the call: a full ring is - * not work, and NOT_READY_E means a racing consumer is making - * the progress. */ - if (local_root != NULL) { + if (root_rng != NULL) { + for (i = 0; i < bank->n_rngs; i++) { + /* pool top-off: fill whatever free span the ring reports. + * The fullness peek is a lockless aperture load; Collect2() + * re-clamps against a fresh snapshot and publishes by CAS, + * so staleness costs at most a wasted attempt. Progress + * accounting keys on the peek, not the call: a full ring is + * not work, and NOT_READY_E means a racing consumer is making + * the progress. */ word32 pool_n = 0; WC_RNG *inst_rng = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); @@ -2894,11 +2880,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) (pool_n < (word32)inst_rng->poolSize)) { unsigned long uncredited_nonce = random_get_entropy(); - (void)wc_RNG_DRBG_Stir(local_root, + (void)wc_RNG_DRBG_Stir(root_rng, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); - ret = wc_RNG_Pool_Collect2(inst_rng, local_root, + ret = wc_RNG_Pool_Collect2(inst_rng, root_rng, (word32)inst_rng->poolSize - pool_n); if (ret == 0) { @@ -2920,16 +2906,14 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_POOL */ - /* if we're coping with congestion hits, continue here, don't bog down - * in primary seed ops. */ #if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) - /* registered-leaf pass: bank RBGC seeds from local_root into + /* registered-leaf pass: bank RBGC seeds from root_rng into * long-lived leaves that are invalidated or chain-backed, so their * next generate recovers/promotes in place * (WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED). * Sleepable-mutex context; entropy gathers are legal under it by * the atomic-born exclusion rule. */ - if (local_root != NULL) { + if (root_rng != NULL) { WC_ATOMIC_INT_ARG needs_recovery_snapshot = WOLFSSL_ATOMIC_LOAD(wc_linuxkm_rng_registry_needs_recovery); if (needs_recovery_snapshot != 0) { @@ -2947,7 +2931,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) (wc_RNG_DRBG_GetRBGCStratum(obj->rng) > 0)) { if (wc_RNG_DRBG_NextSeedGenerate_RBGC(obj->rng, - local_root, WC_DRBG_NEXT_SEED_LEN) == 0) + root_rng, WC_DRBG_NEXT_SEED_LEN) == 0) progress = 1; } } @@ -2961,36 +2945,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_NEXT_SEED && WC_RNG_HAVE_RBGC */ + /* if we're coping with congestion hits, continue here, don't bog down + * in primary seed ops. */ if (congested_progress) goto next_pass; -#if defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED) - - /* Periodic explicit reseed of the daemon-local pool source, with - * a fresh cycle-counter nonce -- scheduled fresh entropy in task - * context, rather than waiting for the counter-forced internal - * reseed. */ - if ((local_root != NULL) && (--local_root_reseed_countdown < 0)) { -#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - ret = wc_RNG_DRBG_Reseed_Now(local_root, NULL, 0); -#else - unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_RNG_DRBG_Reseed_Now(local_root, - (byte *)&uncredited_nonce, - sizeof uncredited_nonce); - ForceZero(&uncredited_nonce, sizeof uncredited_nonce); -#endif - if (ret == 0) { - local_root_reseed_countdown = - WC_LINUXKM_BONUS_RESEED_INTERVAL; - } - else { - pr_err_ratelimited( - "wc_entropyd: pool source reseed failed: %d\n", ret); - } - } -#endif /* WC_RNG_HAVE_POOL || WC_RNG_HAVE_NEXT_SEED */ - #ifdef WC_RNG_HAVE_NEXT_SEED /* seed banking pass: one gather granule per instance per turn. */ for (i = 0; i < bank->n_rngs; i++) { @@ -3034,11 +2993,10 @@ static int wc_linuxkm_entropy_daemon(void *arg) } } -#if defined(WC_RNG_HAVE_POOL) || defined(WC_RNG_HAVE_NEXT_SEED) - if (local_root != NULL) { + if (root_rng != NULL) { #ifdef WC_RNG_DEBUG_STATS struct wc_rng_debug_stats_snapshot s; - if (wc_rng_debug_stats_snap(&s, local_root) == 0) { + if (wc_rng_debug_stats_snap(&s, root_rng) == 0) { pr_info("RNG INFO: wc_entropyd root total_bytes_requested=" WC_RNG_STAT_FMT "\n" " total_bytes_produced=" WC_RNG_STAT_FMT " total_requests=" WC_RNG_STAT_FMT "\n" @@ -3060,11 +3018,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) #ifdef WC_LINUXKM_VMGENID_POLL wc_linuxkm_vmgenid_poll_teardown(&vmgenid_poll_state); #endif - (void)wc_rng_bank_daemon_root_set(bank, NULL); - (void)wc_FreeRng(local_root); - XFREE(local_root, NULL, DYNAMIC_TYPE_RNG); } -#endif return 0; } @@ -3109,7 +3063,7 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) * on the readout hot path. */ ret = wc_rng_bank_init_nonce( ctx, LINUXKM_RNG_BANK_SIZE, - flags | WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING | WC_RNG_BANK_FLAG_INIT_RBGC, + flags | WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING | WC_RNG_BANK_FLAG_RBGC, WC_LINUXKM_INITRNG_TIMEOUT_SEC, NULL /* heap */, INVALID_DEVID, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, NULL, 0); @@ -3211,9 +3165,9 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) struct wc_rng_debug_stats_snapshot s; { - WC_RNG *daemon_root = wc_rng_bank_daemon_root_get(ctx); - if ((daemon_root != NULL) && - (wc_rng_debug_stats_snap(&s, daemon_root) == 0)) + WC_RNG *root_rng = wc_rng_bank_root_rng_get(ctx); + if ((root_rng != NULL) && + (wc_rng_debug_stats_snap(&s, root_rng) == 0)) { pr_info("RNG INFO: wc_entropyd root total_bytes_requested=" WC_RNG_STAT_FMT "\n" " total_bytes_produced=" WC_RNG_STAT_FMT @@ -4142,7 +4096,7 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { #ifdef WC_RNG_HAVE_NEXT_SEED { - WC_RNG *stir_root = wc_rng_bank_daemon_root_get(ctx); + WC_RNG *stir_root = wc_rng_bank_root_rng_get(ctx); if (len <= WC_DRBG_NEXT_STIR_LEN) { static DEFINE_PER_CPU(int, stir_index); diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 4a848403f2e..e6921bac8fa 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -803,7 +803,6 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, * plain-store purge: * stirs carry no claims, so resurrection there is benign by the * three-no-ops doctrine.) */ -#ifdef WC_RNG_HAVE_LOCK static void NextSeedPurge(wolfSSL_Atomic_Int *lenp) { WC_ATOMIC_INT_ARG cur = WOLFSSL_ATOMIC_LOAD(*lenp); @@ -818,7 +817,6 @@ static void NextSeedPurge(wolfSSL_Atomic_Int *lenp) /* cur was reloaded by the failed exchange; re-evaluate. */ } } -#endif /* WC_RNG_HAVE_LOCK */ /* Release a producer claim (PRODUCING) on the credited aperture, * installing val (a fill offset, the full length, READY, or EMPTY). diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index b795c71c62c..8beb07db927 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -26,14 +26,16 @@ #include #include -#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - - /* backward-compat shim and helper declarations */ - +#ifndef WC_RNG_HAVE_LOCK + /* feature-gap helper declarations */ static int wc_rng_bank_inst_recovery_enter( struct wc_rng_bank_inst *inst, int *recovering); static int wc_rng_bank_inst_recovery_exit( struct wc_rng_bank_inst *inst, int recovering, int ret); +#endif + +#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + /* backward-compat shim and helper declarations */ static int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); @@ -69,14 +71,20 @@ WOLFSSL_API int wc_rng_bank_init_nonce( int i; int ret; int need_reenable_vec = 0; -#ifdef WC_RNG_HAVE_RBGC - WC_RNG root; - int root_inited = 0; -#endif + wc_static_assert(WC_DRBG_NOT_INIT == 0); /* make sure assumptions are met */ if ((ctx == NULL) || (n_rngs <= 0)) return BAD_FUNC_ARG; +#ifndef WC_RNG_HAVE_RBGC + if (flags & WC_RNG_BANK_FLAG_RBGC) + return NOT_COMPILED_IN; + (void)nonce; + (void)nonceSz; + (void)perso; + (void)persoSz; +#endif + /* the allocation below is sizeof(*ctx->rngs) * n_rngs; on targets where * size_t is narrow enough for that product to wrap, the initialization * loop would then run off the end of an undersized array. */ @@ -94,6 +102,7 @@ WOLFSSL_API int wc_rng_bank_init_nonce( #endif ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; ctx->heap = heap; + ctx->devId = devId; ctx->first_failover_inst = -1; #ifdef WC_RNG_BANK_STATIC @@ -108,23 +117,8 @@ WOLFSSL_API int wc_rng_bank_init_nonce( #endif #ifdef WC_RNG_HAVE_RBGC - if ((ret == 0) && (flags & WC_RNG_BANK_FLAG_INIT_RBGC)) { - #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) - ret = wc_InitRngNonce_ex2(&root, nonce, nonceSz, perso, persoSz, heap, - devId, WC_RNG_INIT_FLAGS_NONE); - #else - (void)perso; - (void)persoSz; - ret = wc_InitRngNonce_ex(&root, nonce, nonceSz, heap, devId); - #endif - if (ret == 0) - root_inited = 1; - } -#else - (void)nonce; - (void)nonceSz; - (void)perso; - (void)persoSz; + if ((ret == 0) && (flags & WC_RNG_BANK_FLAG_RBGC)) + ret = wc_rng_bank_root_rng_init(ctx, nonce, nonceSz, perso, persoSz, 0); #endif if (ret == 0) { @@ -146,10 +140,10 @@ WOLFSSL_API int wc_rng_bank_init_nonce( need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0); #ifdef WC_RNG_HAVE_RBGC - if (flags & WC_RNG_BANK_FLAG_INIT_RBGC) { + if (flags & WC_RNG_BANK_FLAG_RBGC) { ret = wc_InitRngNonceRBGC( WC_RNG_BANK_INST_TO_RNG(rng_inst), - &root, + &ctx->root_rng, (byte *)&rng_inst, sizeof(byte *) #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) , NULL, 0 @@ -238,11 +232,6 @@ WOLFSSL_API int wc_rng_bank_init_nonce( if (ret != 0) (void)wc_rng_bank_fini(ctx); -#ifdef WC_RNG_HAVE_RBGC - if (root_inited) - wc_FreeRng(&root); -#endif - return ret; } @@ -359,6 +348,20 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { return ret; } +#if defined(WC_RNG_HAVE_RBGC) || defined(WC_RNG_HAVE_NEXT_SEED) + if (wc_RNG_GetStatus(&ctx->root_rng) != WC_DRBG_NOT_INIT) { + int free_ret = wc_FreeRng(&ctx->root_rng); + if (free_ret != 0) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "wc_rng_bank_fini(): wc_FreeRng() on root_rng returned " + "error %d.\n", free_ret); +#endif + ++rng_free_failed; + } + } +#endif + #ifndef WC_RNG_BANK_STATIC if (ctx->rngs) #endif @@ -1303,23 +1306,46 @@ WOLFSSL_API int wc_rng_bank_daemon_release(struct wc_rng_bank *bank, return 0; } -WOLFSSL_API int wc_rng_bank_daemon_root_set(struct wc_rng_bank *bank, - WC_RNG *daemon_root) +#endif /* WC_RNG_BANK_HAVE_DAEMON_SUPPORT */ + +#if defined(WC_RNG_HAVE_RBGC) || defined(WC_RNG_HAVE_NEXT_SEED) + +WOLFSSL_API int wc_rng_bank_root_rng_init(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, + word32 flags) { if (bank == NULL) return BAD_FUNC_ARG; - bank->daemon_root = daemon_root; - return 0; + + if (wc_RNG_GetStatus(&bank->root_rng) != WC_DRBG_NOT_INIT) + return ALREADY_E; + +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + return wc_InitRngNonce_ex2(&bank->root_rng, + nonce, nonceSz, + perso, persoSz, + bank->heap, bank->devId, flags); +#else + (void)perso; + (void)persoSz; + if (flags != 0) + return NOT_COMPILED_IN; + return wc_InitRngNonce_ex(&bank->root_rng, nonce, nonceSz, + bank->heap, bank->devId); +#endif } -WOLFSSL_API WC_RNG *wc_rng_bank_daemon_root_get(struct wc_rng_bank *bank) +WOLFSSL_API WC_RNG *wc_rng_bank_root_rng_get(struct wc_rng_bank *bank) { if (bank == NULL) return NULL; - return bank->daemon_root; + if (wc_RNG_GetStatus(&bank->root_rng) == WC_DRBG_NOT_INIT) + return NULL; + return &bank->root_rng; } -#endif /* WC_RNG_BANK_HAVE_DAEMON_SUPPORT */ +#endif /* WC_RNG_HAVE_RBGC || WC_RNG_HAVE_NEXT_SEED */ #ifdef WC_HAVE_RNG_BANKREF /* wc_local_rng_bank_checkout_for_bankref() is the shim to the real WC_RNG when @@ -1576,16 +1602,21 @@ static int wc_rng_bank_next_seed_generate_local( return ret; } +#ifdef WC_RNG_HAVE_RBGC WOLFSSL_API int wc_rng_bank_next_seed_generate_rbgc( struct wc_rng_bank *bank, int inst_offset, - word32 n, - WC_RNG *root) + word32 n) { - if (root == NULL) + if (bank == NULL) return BAD_FUNC_ARG; - return wc_rng_bank_next_seed_generate_local(bank, inst_offset, n, root); + + if (wc_RNG_GetStatus(&bank->root_rng) != WC_DRBG_OK) + return NOT_READY_E; + + return wc_rng_bank_next_seed_generate_local(bank, inst_offset, n, &bank->root_rng); } +#endif /* WC_RNG_HAVE_RBGC */ WOLFSSL_API int wc_rng_bank_next_seed_generate( struct wc_rng_bank *bank, @@ -2214,6 +2245,18 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, goto out; } +#ifdef WC_RNG_HAVE_RBGC + if ((flags & WC_RNG_BANK_FLAG_RBGC) && + (wc_RNG_GetStatus(&bank->root_rng) != WC_DRBG_OK)) + { + ret = NOT_READY_E; + goto out; + } +#else + if (flags & WC_RNG_BANK_FLAG_RBGC) + return NOT_COMPILED_IN; +#endif + if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) ts1 = XTIME(0); @@ -2262,11 +2305,30 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) (void)nonce; (void)nonceSz; - ret = wc_RNG_DRBG_Reseed_Now( - WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); + #ifdef WC_RNG_HAVE_RBGC + if (flags & WC_RNG_BANK_FLAG_RBGC) { + ret = wc_RNG_DRBG_ReseedRBGC( + WC_RNG_BANK_INST_TO_RNG(drbg), &bank->root_rng); + } + else + #endif + { + ret = wc_RNG_DRBG_Reseed_Now( + WC_RNG_BANK_INST_TO_RNG(drbg), NULL, 0); + } #else - ret = wc_RNG_DRBG_Reseed_Now( - WC_RNG_BANK_INST_TO_RNG(drbg), nonce, nonceSz); + #ifdef WC_RNG_HAVE_RBGC + if (flags & WC_RNG_BANK_FLAG_RBGC) { + ret = wc_RNG_DRBG_ReseedRBGC( + WC_RNG_BANK_INST_TO_RNG(drbg), &bank->root_rng, + NULL, 0); + } + else + #endif + { + ret = wc_RNG_DRBG_Reseed_Now( + WC_RNG_BANK_INST_TO_RNG(drbg), nonce, nonceSz); + } #endif ret = wc_rng_bank_inst_recovery_exit(drbg, recovering, ret); @@ -2276,11 +2338,30 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) (void)nonce; (void)nonceSz; - ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), - NULL, 0); + #ifdef WC_RNG_HAVE_RBGC + if (flags & WC_RNG_BANK_FLAG_RBGC) { + ret = wc_RNG_DRBG_ReseedRBGC( + WC_RNG_BANK_INST_TO_RNG(inst), &bank->root_rng); + } + else + #endif + { + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), + NULL, 0); + } #else - ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), - nonce, nonceSz); + #ifdef WC_RNG_HAVE_RBGC + if (flags & WC_RNG_BANK_FLAG_RBGC) { + ret = wc_RNG_DRBG_ReseedRBGC( + WC_RNG_BANK_INST_TO_RNG(drbg), &bank->root_rng, + NULL, 0); + } + else + #endif + { + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg), + nonce, nonceSz); + } #endif #endif /* WC_RNG_HAVE_LOCK */ @@ -2389,6 +2470,18 @@ WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, return BAD_STATE_E; } +#if defined(WC_RNG_HAVE_RBGC) || defined(WC_RNG_HAVE_NEXT_SEED) + { + if (wc_RNG_GetStatus(&bank->root_rng) != WC_DRBG_NOT_INIT) { + #if !defined(WC_RNG_HAVE_LOCK) + ret = wc_RNG_DRBG_ScheduleReseed(&bank->root_rng); + #else + ret = wc_RNG_invalidate_entropy(&bank->root_rng); + #endif + } + } +#endif + /* Best-effort-complete: an error on one instance must not leave the * rest un-flagged. First error wins the return. */ for (n = 0; n < bank->n_rngs; n++) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 5634173fca7..58f464773ee 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28803,8 +28803,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) leaf_rng_inited = 1; #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) ret = wc_RNG_DRBG_GetRBGCStratum(leaf_rng); + /* the _NEXT_SEED section above reseeds the bank root -- otherwise it's a + * user seed. */ +#ifdef WC_RNG_HAVE_NEXT_SEED if (ret != 1) +#else + if (ret != WC_RNG_RBGC_USER_SEED_STRATUM + 1) +#endif + { ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); + } #endif ret = wc_RNG_GenerateBlock(leaf_rng, outbuf1, sizeof(outbuf1)); if (ret != 0) @@ -28922,6 +28930,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) WC_RNG_BANK_INST_TO_RNG(rng_inst), &ns_ctr); if ((ret != 0) || (ns_ctr != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (WC_RNG_BANK_INST_TO_RNG(rng_inst) == NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* take the instance out of service while holding it */ WC_RNG_BANK_INST_TO_RNG(rng_inst)->status = WC_DRBG_FAILED; ret = wc_rng_bank_inst_checkin(&rng_inst); diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 0cf191e5af3..bfe01d5c142 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -40,7 +40,7 @@ #error WC_RNG_BANK_SUPPORT requires RNG support. #endif -#ifndef WOLFSSL_NO_ATOMICS +#if !defined(WOLFSSL_NO_ATOMICS) && !defined(WC_RNG_BANK_NO_DAEMON_SUPPORT) #define WC_RNG_BANK_HAVE_DAEMON_SUPPORT #define WC_RNG_BANK_DAEMON_MAGIC_FREE 0U #endif @@ -59,7 +59,7 @@ #define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1U << 10) #define WC_RNG_BANK_FLAG_QUIET (1U << 11) #define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 12) -#define WC_RNG_BANK_FLAG_INIT_RBGC (1U << 13) +#define WC_RNG_BANK_FLAG_RBGC (1U << 13) #define WC_RNG_BANK_FLAG_DEFAULT_BANK (1U << 14) #define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 15) #define WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE (1U << 16) @@ -129,6 +129,7 @@ struct wc_rng_bank_inst { struct wc_rng_bank { wolfSSL_Ref refcount; void *heap; + int devId; word32 flags; wc_rng_bank_free_hook_cb_t free_hook; void *free_hook_arg; @@ -149,10 +150,10 @@ struct wc_rng_bank { #ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT wolfSSL_Atomic_Uint daemon_magic; void *daemon; /* e.g. a task_struct* for a wc_linuxkm_entropy_daemon() */ - /* the daemon's private root DRBG, published for the state-invalidation - * handler (see wc_rng_bank_daemon_root_set()); the daemon owns its - * lifecycle and clears it before teardown. */ - WC_RNG *daemon_root; +#endif +#if defined(WC_RNG_HAVE_RBGC) || defined(WC_RNG_HAVE_NEXT_SEED) || \ + defined(WC_RNG_HAVE_POOL) + WC_RNG root_rng; #endif }; @@ -284,11 +285,12 @@ WOLFSSL_API int wc_rng_bank_next_seed_generate( struct wc_rng_bank *bank, int inst_offset, word32 n); +#ifdef WC_RNG_HAVE_RBGC WOLFSSL_API int wc_rng_bank_next_seed_generate_rbgc( struct wc_rng_bank *bank, int inst_offset, - word32 n, - WC_RNG *root); + word32 n); +#endif #endif WOLFSSL_API int wc_rng_bank_inst_reinit( @@ -358,10 +360,13 @@ WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, #endif /* HAVE_HASHDRBG */ -#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT -WOLFSSL_API int wc_rng_bank_daemon_root_set(struct wc_rng_bank *bank, - WC_RNG *daemon_root); -WOLFSSL_API WC_RNG *wc_rng_bank_daemon_root_get(struct wc_rng_bank *bank); +#if defined(WC_RNG_HAVE_RBGC) || defined(WC_RNG_HAVE_NEXT_SEED) || \ + defined(WC_RNG_HAVE_POOL) +WOLFSSL_API int wc_rng_bank_root_rng_init(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, + word32 flags); +WOLFSSL_API WC_RNG *wc_rng_bank_root_rng_get(struct wc_rng_bank *bank); #endif WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, @@ -379,37 +384,77 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); #endif /* WC_HAVE_RNG_BANKREF */ #define WC_RNG_BANK_INST_TO_RNG(rng_inst) \ - ((rng_inst) ? (&((struct wc_rng_bank_inst *)(rng_inst))->rng) : NULL) + ((rng_inst) ? (&(rng_inst)->rng) : NULL) #define WC_RNG_BANK_OFFSET_TO_RNG(bank, n) \ ((((n) >= 0) && ((unsigned)(n) < (unsigned)(bank)->n_rngs)) ? \ WC_RNG_BANK_INST_TO_RNG(&(bank)->rngs[n]) : NULL) #ifdef WC_RNG_HAVE_LOCK /* Trivial shims to native lock facility in WC_RNG */ - #define wc_rng_bank_inst_lock_get(inst, extra_bits) \ - wc_RNG_lock_get(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits) - #define wc_rng_bank_inst_lock_put(inst) \ - wc_RNG_lock_put(WC_RNG_BANK_INST_TO_RNG(inst), 0) - #define wc_rng_bank_inst_lock_put_conditional(inst, expect_extra_bits) \ - wc_RNG_lock_put_conditional(WC_RNG_BANK_INST_TO_RNG(inst), expect_extra_bits, 0) - #define wc_rng_bank_inst_lock_read(inst, state) \ - wc_RNG_lock_read(WC_RNG_BANK_INST_TO_RNG(inst), state) - #define wc_rng_bank_inst_lock_set_extra(inst, extra_bits) \ - wc_RNG_lock_set_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits) - #define wc_rng_bank_inst_lock_add_extra(inst, extra_bits) \ - wc_RNG_lock_add_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits) - #define wc_rng_bank_inst_lock_clear_extra(inst, extra_bits) \ - wc_RNG_lock_clear_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits) - #define wc_rng_bank_inst_lock_get_conditional(inst, expected_extra_bits, want_extra_bits) \ - wc_RNG_lock_get_conditional(WC_RNG_BANK_INST_TO_RNG(inst), expected_extra_bits, want_extra_bits) + static WC_INLINE int wc_rng_bank_inst_lock_get( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) + { + return wc_RNG_lock_get(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); + } + static WC_INLINE int wc_rng_bank_inst_lock_put( + struct wc_rng_bank_inst *inst) + { + return wc_RNG_lock_put(WC_RNG_BANK_INST_TO_RNG(inst), 0); + } + static WC_INLINE int wc_rng_bank_inst_lock_put_conditional( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expect_extra_bits) + { + return wc_RNG_lock_put_conditional( + WC_RNG_BANK_INST_TO_RNG(inst), expect_extra_bits, 0); + } + static WC_INLINE int wc_rng_bank_inst_lock_read( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t *state) + { + return wc_RNG_lock_read(WC_RNG_BANK_INST_TO_RNG(inst), state); + } + static WC_INLINE int wc_rng_bank_inst_lock_set_extra( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) + { + return wc_RNG_lock_set_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); + } + static WC_INLINE int wc_rng_bank_inst_lock_add_extra( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) + { + return wc_RNG_lock_add_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); + } + static WC_INLINE int wc_rng_bank_inst_lock_clear_extra( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) + { + return wc_RNG_lock_clear_extra( + WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); + } + static WC_INLINE int wc_rng_bank_inst_lock_get_conditional( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits) + { + return wc_RNG_lock_get_conditional( + WC_RNG_BANK_INST_TO_RNG(inst), expected_extra_bits, want_extra_bits); + } #ifdef HAVE_HASHDRBG - #define wc_rng_bank_inst_invalidate_entropy(inst) \ - wc_RNG_invalidate_entropy(WC_RNG_BANK_INST_TO_RNG(inst)) - #define wc_rng_bank_inst_reseed_now(inst, nonce, nonceSz) \ - wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(inst), nonce, nonceSz); + static WC_INLINE int wc_rng_bank_inst_invalidate_entropy( + struct wc_rng_bank_inst *inst) + { + return wc_RNG_invalidate_entropy(WC_RNG_BANK_INST_TO_RNG(inst)); + } + static WC_INLINE int wc_rng_bank_inst_reseed_now( + struct wc_rng_bank_inst *inst, const byte *nonce, word32 nonceSz) + { + return wc_RNG_DRBG_Reseed_Now( + WC_RNG_BANK_INST_TO_RNG(inst), nonce, nonceSz); + } #ifdef WC_RNG_HAVE_RBGC - #define wc_rng_bank_inst_reseed_rbgc(inst, root, nonce, nonceSz) \ - wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(inst), root, nonce, nonceSz) + static WC_INLINE int wc_rng_bank_inst_reseed_rbgc( + struct wc_rng_bank_inst *inst, WC_RNG *root, + const byte *nonce, word32 nonceSz) + { + return wc_RNG_DRBG_ReseedRBGC( + WC_RNG_BANK_INST_TO_RNG(inst), root, nonce, nonceSz); + } #endif /* WC_RNG_HAVE_RBGC */ #endif /* HAVE_HASHDRBG */ #else /* !WC_RNG_HAVE_LOCK */ From bcde61c7b6456c33cffa06fd62b293391b30e429 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sun, 13 Sep 2026 17:46:03 -0500 Subject: [PATCH 080/102] linuxkm/lkcapi_sha_glue.c: * add error percolation to wc_linuxkm_rng_registry_add_rng() and wc_linuxkm_rng_registry_add_bank(), and treat failures as fatal in wc_linuxkm_rng_bank_init() and linuxkm_InitRng_DefaultRBGC(). * bikeshed wc_mix_pool_bytes() with staggered-startup-offset. wolfcrypt/src/rng_bank.c: * add missing backward-compat WC_DRBG_NOT_INIT and WC_DRBG_OK definitions. * add missing old-old-FIPS wc_RNG_DRBG_Reseed_Now() prototype and wc_RNG_DRBG_Stir() implementation. wolfssl/wolfcrypt/rng_bank.h: fix sign-clash from WC_RNG_BANK_OFFSET_TO_RNG(). --- linuxkm/lkcapi_sha_glue.c | 76 ++++++++++++++++++++++++------------ wolfcrypt/src/rng_bank.c | 15 +++++++ wolfssl/wolfcrypt/rng_bank.h | 2 +- 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 3794bdf5551..c7a5d411ff5 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2253,20 +2253,22 @@ static int wc_linuxkm_rng_registry_free_hook(const WC_RNG *rng, void *arg) return 0; } -static void wc_linuxkm_rng_registry_add_rng(WC_RNG *rng) +static WARN_UNUSED_RESULT int wc_linuxkm_rng_registry_add_rng(WC_RNG *rng) { struct linuxkm_rng_object *obj = kmalloc(sizeof(*obj), GFP_KERNEL); + int ret; if (obj == NULL) - return; /* best-effort: an unregistered leaf is merely unprotected */ + return MEMORY_E; obj->is_bank = 0; obj->rng = rng; - if (wc_RNG_register_free_hook(rng, wc_linuxkm_rng_registry_free_hook, - obj) != 0) - { + ret = wc_RNG_register_free_hook(rng, wc_linuxkm_rng_registry_free_hook, + obj); + if (ret != 0) { kfree(obj); - return; + return ret; } wc_linuxkm_rng_registry_link(obj); + return 0; } static int wc_linuxkm_rng_registry_bank_free_hook( @@ -2279,20 +2281,22 @@ static int wc_linuxkm_rng_registry_bank_free_hook( return 0; } -static void wc_linuxkm_rng_registry_add_bank(struct wc_rng_bank *bank) +static WARN_UNUSED_RESULT int wc_linuxkm_rng_registry_add_bank(struct wc_rng_bank *bank) { struct linuxkm_rng_object *obj = kmalloc(sizeof(*obj), GFP_KERNEL); + int ret; if (obj == NULL) - return; /* best-effort: an unregistered bank is merely unprotected */ + return MEMORY_E; obj->is_bank = 1; obj->bank = bank; - if (wc_rng_bank_register_free_hook(bank, - wc_linuxkm_rng_registry_bank_free_hook, obj) != 0) - { + ret = wc_rng_bank_register_free_hook( + bank, wc_linuxkm_rng_registry_bank_free_hook, obj); + if (ret != 0) { kfree(obj); - return; + return ret; } wc_linuxkm_rng_registry_link(obj); + return 0; } /* platform announcement (VM fork/clone, resume from hibernation) that RNG @@ -3068,6 +3072,18 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) NULL /* heap */, INVALID_DEVID, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, NULL, 0); + #ifdef WC_LINUXKM_HAVE_RNG_REGISTRY + if (ret == 0) { + ret = wc_linuxkm_rng_registry_add_bank(ctx); + if (ret != 0) { + (void)wc_rng_bank_fini(ctx); + pr_err("ERROR: wc_linuxkm_rng_registry_add_bank() in " + "wc_linuxkm_rng_bank_init() returned err %d\n", ret); + return ret; + } + } + #endif + if (ret == 0) { (void)wc_rng_bank_first_failover_inst_set(ctx, LINUXKM_RNG_BANK_FIRST_FAILOVER); ret = wc_rng_bank_set_affinity_handlers( @@ -3147,11 +3163,6 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) ret = -EINVAL; } -#ifdef WC_LINUXKM_HAVE_RNG_REGISTRY - if (ret == 0) - wc_linuxkm_rng_registry_add_bank(ctx); -#endif - return ret; } @@ -3417,9 +3428,12 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { if (ret == 0) { /* Long-lived process-context leaves join the invalidation registry; * atomic-born leaves are excluded by rule (and are transient by - * nature). Registration is best-effort. */ - if (can_sleep) - wc_linuxkm_rng_registry_add_rng(rng); + * nature). */ + if (can_sleep) { + ret = wc_linuxkm_rng_registry_add_rng(rng); + if (ret != 0) + (void)wc_FreeRng(rng); + } } #endif return ret; @@ -4098,14 +4112,26 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { { WC_RNG *stir_root = wc_rng_bank_root_rng_get(ctx); + /* Small input, fast path: lock-free XMEMCPY/xorbuf. */ if (len <= WC_DRBG_NEXT_STIR_LEN) { - static DEFINE_PER_CPU(int, stir_index); - unsigned int this_index = this_cpu_inc_return(stir_index); - if (this_index >= (unsigned int)ctx->n_rngs) { - this_index = 0; + static DEFINE_PER_CPU(int, stir_index) = -2; + int this_index = this_cpu_inc_return(stir_index); + /* at startup, stagger them across the bank, to get wider spread and + * less contentious coverage. */ + if (this_index < 0) { + int stride = ctx->n_rngs / nr_cpu_ids; + if (stride < 1) + stride = 1; + this_index = raw_smp_processor_id() * stride; + this_cpu_write(stir_index, this_index); + } + /* Note, this_index can be >= ctx->n_rngs here even if this_index + * was < 0 on entry. */ + if (this_index >= ctx->n_rngs) { + this_index %= ctx->n_rngs; /* we may have been migrated since this_cpu_inc_return() -- * tolerate the harmless reset of a different counter. */ - this_cpu_write(stir_index, 0); + this_cpu_write(stir_index, this_index); } (void)wc_RNG_DRBG_NextStirStore( WC_RNG_BANK_OFFSET_TO_RNG(ctx, this_index), (const byte *)buf, diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 8beb07db927..bb2ab967bc4 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -40,12 +40,21 @@ word32 seedSz, const byte *nonce, word32 nonceSz); #if FIPS_VERSION3_NE(5,2,4) + static int wc_RNG_DRBG_Reseed_Now( + WC_RNG* rng, const byte* nonce, word32 nonceSz); static int wc_RNG_DRBG_GetReseedCtr( const WC_RNG* rng, wc_drbg_reseed_ctr_t* reseedCtr); #endif static int wc_RNG_DRBG_Stir_Nonce( WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); + + /* WC_DRBG_* predate some old FIPS editions, but all of them share the same + * values -- force consistency using macros. */ + #undef WC_DRBG_NOT_INIT + #define WC_DRBG_NOT_INIT 0 + #undef WC_DRBG_OK + #define WC_DRBG_OK 1 #endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ /* DRBG status and reseed-counter access, and reseed forcing, are via the @@ -3204,6 +3213,12 @@ static int wc_RNG_DRBG_Stir_Nonce( return ret; } +WOLFSSL_TEST_VIS int wc_RNG_DRBG_Stir( + WC_RNG* rng, const byte* seed, word32 seedSz) +{ + return wc_RNG_DRBG_Stir_Nonce(rng, seed, seedSz, NULL, 0); +} + static int wc_RNG_DRBG_Reseed_Now( WC_RNG* rng, const byte* nonce, word32 nonceSz) { diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index bfe01d5c142..c0d826a59a5 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -386,7 +386,7 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); #define WC_RNG_BANK_INST_TO_RNG(rng_inst) \ ((rng_inst) ? (&(rng_inst)->rng) : NULL) #define WC_RNG_BANK_OFFSET_TO_RNG(bank, n) \ - ((((n) >= 0) && ((unsigned)(n) < (unsigned)(bank)->n_rngs)) ? \ + ((((int)(n) >= 0) && ((int)(n) < (int)(bank)->n_rngs)) ? \ WC_RNG_BANK_INST_TO_RNG(&(bank)->rngs[n]) : NULL) #ifdef WC_RNG_HAVE_LOCK From 344a25dff21a3d0b9e1e2c9682c80e40172325b6 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 14 Sep 2026 11:04:02 -0500 Subject: [PATCH 081/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, linuxkm/lkcapi_sha_glue.c: refactor WC_RNG_HAVE_POOL facility as a CAS-synchronized-writer FIFO, preserving lock-free single-reader, while assiduously enforcing entropy invalidation locally for both readers and writers. --- linuxkm/lkcapi_sha_glue.c | 8 +- wolfcrypt/src/random.c | 378 +++++++++++++++++++++++++------------ wolfssl/wolfcrypt/random.h | 7 +- 3 files changed, 268 insertions(+), 125 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index c7a5d411ff5..0e7da3da9a3 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2894,10 +2894,13 @@ static int wc_linuxkm_entropy_daemon(void *arg) if (ret == 0) { progress = 1; } +#ifdef WC_VERBOSE_RNG else if ((ret == WC_NO_ERR_TRACE(NOT_READY_E)) || - (ret == WC_NO_ERR_TRACE(BAD_STATE_E))) + (ret == WC_NO_ERR_TRACE(BAD_STATE_E)) || + (ret == WC_NO_ERR_TRACE(BUSY_E))) { - /* contention (consumer active) or no pool -- + /* contention (other writer active), a purge landed + * while the collector was generating, or no pool -- * nothing to do here this turn. */ } else { @@ -2905,6 +2908,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) "wc_entropyd: pool top-off on DRBG inst %d " "returned %d\n", i, ret); } +#endif } } } diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index e6921bac8fa..46ab0a7cdff 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -365,6 +365,10 @@ int wc_RNG_DRBG_Present(const WC_RNG* rng) return 0; } +#ifdef WC_RNG_HAVE_POOL +static void PoolPurge(WC_RNG* rng); +#endif + /* Start NIST DRBG code */ #ifdef HAVE_HASHDRBG @@ -889,12 +893,24 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, } #endif /* WC_RNG_HAVE_LOCK */ -#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_POOL) - /* Purge the pool on credited reseeds. A credited reseed is an epoch - * boundary -- the pool must not serve output of a retired state - * (particularly pre-invalidation state). */ - WOLFSSL_ATOMIC_STORE(rng->poolState, 0); -#endif /* WC_RNG_HAVE_LOCK && WC_RNG_HAVE_POOL */ +#ifdef WC_RNG_HAVE_POOL + /* Purge the pool when recovering from invalidation, or if reseeding without + * locks (i.e. without an internal mechanism for tracking invalidation). + * + * The reseed counter tracks generates since the last reseed, and every + * pooled byte is itself a generate that incremented it. So the pool's + * contents are within the budget the counter enforces -- indeed they were + * authorized by the same accounting that later demanded the + * reseed. Discarding them treats output as retroactively over-budget when + * it was under-budget when produced. + */ + #ifdef WC_RNG_HAVE_LOCK + if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + #endif + { + PoolPurge(rng); + } +#endif /* WC_RNG_HAVE_POOL */ #ifndef NO_SHA256 if (rng->drbgType == WC_DRBG_SHA256) { @@ -3140,7 +3156,7 @@ static int rng_pid_change_check(WC_RNG* rng) { #endif #ifdef WC_RNG_HAVE_POOL - WOLFSSL_ATOMIC_STORE(rng->poolState, 0); + PoolPurge(rng); #endif #ifdef WC_RNG_HAVE_NEXT_SEED #ifndef NO_SHA256 @@ -3531,7 +3547,7 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { if (! (cur_lock & WC_RNG_LOCK_HELD)) (void)wc_RNG_DRBG_ScheduleReseed(rng); #ifdef WC_RNG_HAVE_POOL - WOLFSSL_ATOMIC_STORE(rng->poolState, 0); + PoolPurge(rng); #endif #ifdef WC_RNG_HAVE_NEXT_SEED #ifndef NO_SHA256 @@ -3572,79 +3588,153 @@ WOLFSSL_API int wc_RNG_register_free_hook(WC_RNG* rng, #endif /* WC_RNG_HAVE_FREE_HOOK */ #ifdef WC_RNG_HAVE_POOL - /* In-boundary asynchronous DRBG output pool. _Alloc() sizes the ring - * (2..65535 bytes; a second allocation is ALREADY_E). _Collect() tops it - * up from rng's own DRBG; _Collect2() tops dest's ring up from an - * independent src instance, generating directly into the free span - * and publishing with a single CAS -- callable WITHOUT any lease on - * dest (contending writers regenerate on CAS failure; the final write - * of every published byte is certified DRBG output). _Extract() - * (lease-holder only) delivers up to *n bytes destructively, burning - * each byte on the way out, and fails closed (burning the ring) on an - * out-of-service DRBG; *n = 0 on empty, for fall-through to a direct - * generate. _Current() reports the published count (racy snapshot). - * No free API: the ring lives until wc_FreeRng(), eliminating - * deallocation races by construction. */ - - /* In-boundary DRBG output pool (wc_RNG_Pool_*()): a circular buffer of - * pre-generated output, held and zeroized under the module's CSP - * discipline and consumed destructively (each delivered or discarded - * byte is burned). No internal synchronization: all pool operations - * require exclusive ownership of the instance (an rng_bank lease, or - * an intrinsically uncontended object). */ - - /* Asynchronous pool aperture: the low half of poolState is the - * published byte count, the high half the read offset, packed in one - * atomic word so reader updates are single release stores and writer - * publications are single CASes -- no torn {current,offset} snapshot - * is observable. Sole reader = the instance lease holder; writers - * (e.g. the entropy daemon via wc_RNG_Pool_Collect2()) never hold the - * instance. */ - -/* Asynchronous in-boundary DRBG output pool. See random.h for the - * aperture encoding. Protocol: the sole reader (instance lease holder) - * loads a snapshot, copies out, ForceZero()s the consumed span, then - * release-stores {current - m, offset + m}; writers load a snapshot, - * generate certified output directly into the unpublished span, and - * publish with one CAS of the whole word -- on CAS failure the written - * material is burned and regenerated fresh (never reused: no DRBG output - * may be deliverable twice). A reader store may overwrite a concurrent - * writer's publication; the loss is unidirectionally conservative (the - * count only ever drops), so no reader can claim unpublished bytes, and - * the clobbered bytes are benign unaccounted content awaiting - * overwrite. */ - -typedef union { - WC_ATOMIC_UINT_ARG state; - struct { - word16 current; - word16 offset; - } pool; -} wc_rng_pool_state_u; + + /* In-boundary asynchronous DRBG output pool (wc_RNG_Pool_*()): a circular + * buffer of pre-generated output, held and zeroized under the module's CSP + * discipline and consumed destructively (each delivered or discarded byte + * is burned). + * + * Writer state coherence is enforced with a CAS; reader exclusivity is + * enforced by the umbrella WC_RNG.lock, or absent that, by caller contract. + * + * The reader is lock-free -- two plain loads of {head, epoch} bracketing + * the copy, then a plain store of {tail, epoch} exclusively written by the + * reader. The writer CASes its publication, carrying the epoch it read; a + * purge during its generate results in an epoch mismatch, whereupon BUSY_E + * is returned to the caller. + * + * _Alloc() sizes the ring (2..32767 bytes), with positions running in [0, + * 2*size) and packing into 16 bits. _Collect() tops up the pool from rng's + * own DRBG; _Collect2() tops dest's ring up from an independent src + * instance, generating directly into the free span and publishing with a + * single CAS -- callable WITHOUT any lease on dest (contending writers + * return BUSY_E on CAS failure; the final write of every published byte is + * certified DRBG output). _Extract() (lease-holder only) delivers up to *n + * bytes destructively, burning each byte on the way out, and fails closed + * on an out-of-service DRBG; *n = 0 on empty, for fall-through to a direct + * generate. _Current() reports the published count (racy snapshot). No + * special free API: the ring lives until wc_FreeRng(), eliminating + * deallocation races by construction. + * + * Two words track FIFO state: + * + * poolHead = {head, epoch} written by the writer (publish, CAS) and by + * PoolPurge() (epoch bump, CAS) + * poolTail = {tail, epoch} written by the reader alone, plain store + * + * Position and epoch share one word, so the pairs are always mutually + * consistent -- there is no torn snapshot to reason about. + * + * head and tail are free-running positions in [0, 2*poolSize), advanced by + * conditional subtraction -- no division, and no modulus constraint on + * poolSize. The writer publishes only into free space, so head can never + * pass tail + poolSize and the two can never lap. + * + * wc_RNG_lock_put{,_conditional}() return NEEDS_RECOVERY_E to the reader if + * an invalidation occurred after lock but before release (contingent on + * WC_RNG_HAVE_LOCK). State coherence for the lock-free writers and purgers + * hinges on the CAS and epoch counter protocol in PoolPurge() and + * wc_RNG_Pool_Collect2(). + * + * The reader never writes poolHead and never CASes anything, leveraging + * exclusivity enforced by WC_RNG.lock or arranged by caller contract. It + * brackets its copy with leading and trailing loads of poolHead and + * compares the epoch: a purge that landed anywhere in between is caught, + * providing for early, pre-unlock failure upon invalidation. The epoch is + * 16 bits, so defeating this early failure requires exactly k * 65536 + * purges (k a positive integer) inside one copy-and-burn of at most + * poolSize bytes (implausible). + * + * PoolPurge() bumps epoch and touches nothing else. It does not reset the + * counters: leaving them monotonic keeps the reader's burn span [tail, + * tail+m) and the writer's generate span [head, head+m') disjoint across the + * event, so a purge can never cause one to erase the other's bytes. Stale + * pre-purge material is discarded by the reader instead, which resynchronizes + * tail to head on any epoch change -- whether it observed the purge mid-serve + * or merely arrives afterwards. + * + * The writer still CASes, and its publication carries the epoch it read. A + * purge during its generate makes that CAS fail, and it abandons rather + * than publishing material that predates the event. This protocol is + * airtight in the same sense as the credited next-seed aperture + * (NextSeedPurge()): no invalidation can go unobserved by either side. + * + * If multiple writers simultaneously write to the pool, their inputs are + * unpredictably but benignly interspersed, with one of the writers + * successfully finalizing its write with a CAS, while the rest fail their + * CAS and return BUSY_E. Because all writers are tested for provenance + * compatible with that of the destination RNG (particularly, by the stratum + * test in wc_RNG_Pool_Collect2()), this interspersal is intrinsically + * benign. It can be trivially avoided by single-writer caller contract; + * the fundamental benefit of this arrangement is the avoidance of an + * initial frivolous CAS at entry to _Collect2(). + */ + +#define WC_RNG_POOL_POS(w) ((word32)((word32)(w) & 0xFFFFU)) +#define WC_RNG_POOL_EPOCH(w) ((word32)(((word32)(w) >> 16) & 0xFFFFU)) +#define WC_RNG_POOL_PACK(pos, epoch) \ + ((WC_ATOMIC_UINT_ARG)((((word32)(pos)) & 0xFFFFU) | \ + ((((word32)(epoch)) & 0xFFFFU) << 16))) + +wc_static_assert(sizeof(WC_ATOMIC_UINT_ARG) >= 4); + +/* position -> ring index. Positions run in [0, 2*poolSize). */ +static WC_INLINE word32 PoolAt(word32 pos, word32 poolSize) +{ + return (pos >= poolSize) ? (pos - poolSize) : pos; +} + +/* advance a position, wrapping at 2*poolSize. */ +static WC_INLINE word32 PoolAdvance(word32 pos, word32 by, word32 poolSize) +{ + word32 lim = poolSize * 2U; + pos += by; + return (pos >= lim) ? (pos - lim) : pos; +} + +static WC_INLINE word32 PoolUsed(word32 head, word32 tail, word32 poolSize) +{ + /* Note, the modular subtraction is unambiguous because the only publisher + * (wc_RNG_Pool_Collect2()) carefully bounds itself to the free span, so + * head never passes tail + poolSize. */ + return (head >= tail) ? (head - tail) : (head + (poolSize * 2U) - tail); +} + +/* Retire pooled output: any event after which pre-event bytes must not be + * served -- state invalidation, fork, a credited reseed, the reader's + * fail-closed path. Bumping epoch is the whole operation; see above for why + * the counters are deliberately left alone. */ +static void PoolPurge(WC_RNG* rng) +{ + WC_ATOMIC_UINT_ARG cur = WOLFSSL_ATOMIC_LOAD(rng->poolHead); + for (;;) { + WC_ATOMIC_UINT_ARG want = + WC_RNG_POOL_PACK(WC_RNG_POOL_POS(cur), WC_RNG_POOL_EPOCH(cur) + 1U); + if (wolfSSL_Atomic_Uint_CompareExchange(&rng->poolHead, &cur, want)) + return; + /* cur was reloaded by the failed exchange; re-evaluate. */ + } +} int wc_RNG_Pool_Alloc(WC_RNG* rng, word32 size) { - if ((rng == NULL) || (size < 2) || (size > 65535U)) + if ((rng == NULL) || (size < 2) || (size > 32767U)) return BAD_FUNC_ARG; /* halves are word16; current in [0, size] */ - if (rng->pool != NULL) { - /* the requested condition already holds -- distinct from the - * BAD_STATE_E that pool operations report for a MISSING pool */ + if (rng->pool != NULL) return ALREADY_E; - } rng->pool = (byte*)XMALLOC(size, rng->heap, DYNAMIC_TYPE_RNG); if (rng->pool == NULL) return MEMORY_E; rng->poolSize = (word16)size; - wolfSSL_Atomic_Uint_Init(&rng->poolState, 0); + wolfSSL_Atomic_Uint_Init(&rng->poolHead, 0); + wolfSSL_Atomic_Uint_Init(&rng->poolTail, 0); return 0; } int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, word32 n) { - int retries; - if ((rng_dest == NULL) || (rng_src == NULL)) return BAD_FUNC_ARG; if (rng_dest->pool == NULL) @@ -3664,55 +3754,70 @@ int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, word32 n) if (n == 0) return 0; - for (retries = 0; retries < 8; retries++) { - wc_rng_pool_state_u snap, next; - word32 m, done = 0; + /* Note, a second writer can read the same head, generate into the same + * span, then lose the publication CAS, having already overwritten part of + * the winner's published bytes. This is benign -- every byte in the span + * is output of compatible provenance from one generate or the other, and + * the loser just returns BUSY_E, while no invalidation is lost either way. + * + * If interspersal of bytes from multiple producers is undesirable, the + * caller can simply arrange not to have multiple concurrent producxers -- + * this is the arrangement in the wolfSSL kernel module, for example, which + * has a single daemon (wc_linuxkm_entropy_daemon()) that is the sole pool + * collector. + */ + { + WC_ATOMIC_UINT_ARG snap; + word32 head, epoch, tail, free_sz, m, done = 0; int ret; - snap.state = WOLFSSL_ATOMIC_LOAD(rng_dest->poolState); - m = (word32)rng_dest->poolSize - (word32)snap.pool.current; - if (m == 0) + snap = WOLFSSL_ATOMIC_LOAD(rng_dest->poolHead); + head = WC_RNG_POOL_POS(snap); + epoch = WC_RNG_POOL_EPOCH(snap); + /* A stale-epoch tail is conservative: it can only understate the free + * span, never overstate it, so no unread byte is ever overwritten. */ + tail = WC_RNG_POOL_POS(WOLFSSL_ATOMIC_LOAD(rng_dest->poolTail)); + + free_sz = (word32)rng_dest->poolSize + - PoolUsed(head, tail, (word32)rng_dest->poolSize); + if (free_sz == 0) return 0; /* full: success no-op */ - if (m > n) - m = n; + m = (free_sz > n) ? n : free_sz; - /* generate directly into the unpublished span (up to two - * contiguous segments), then publish the whole of it with one - * CAS */ + /* generate directly into the unpublished span (up to two contiguous + * segments), then publish the whole of it with one CAS. */ while (done < m) { - word32 at = ((word32)snap.pool.offset + (word32)snap.pool.current - + done) % (word32)rng_dest->poolSize; + word32 at = PoolAt(PoolAdvance(head, done, (word32)rng_dest->poolSize), + (word32)rng_dest->poolSize); word32 chunk = (word32)rng_dest->poolSize - at; if (chunk > m - done) chunk = m - done; - ret = wc_RNG_GenerateBlock(rng_src, rng_dest->pool + at, - (word32)chunk); + ret = wc_RNG_GenerateBlock(rng_src, rng_dest->pool + at, chunk); if (ret != 0) { - /* Abandon in place. The written bytes MUST NOT be burned: - * a competing writer may have published a span overlapping - * them (final-write-wins), and zeroing published content - * would deliver zeros as randomness. Abandoned bytes are - * benign in-boundary content awaiting overwrite. */ + /* Abandon in place. The written bytes lie beyond head and + * are therefore unpublished -- benign in-boundary content + * awaiting overwrite. Not burned: the reader's burn span and + * ours are disjoint, and zeroing here would be + * indistinguishable from published zeros to the next writer. */ return ret; } done += chunk; } - next = snap; - next.pool.current = (word16)((word32)snap.pool.current + m); - if (wolfSSL_Atomic_Uint_CompareExchange(&rng_dest->poolState, - &snap.state, next.state)) + if (wolfSSL_Atomic_Uint_CompareExchange( + &rng_dest->poolHead, &snap, + WC_RNG_POOL_PACK( + PoolAdvance(head, m, (word32)rng_dest->poolSize), epoch))) { return 0; } - - /* Lost the publication race: abandon in place (see above -- never - * burn a span we may no longer own) and regenerate fresh against a - * new snapshot (never republish the same output: no DRBG output - * may be deliverable twice). */ + else { + /* Either we're competing with another writer, or the epoch changed + * (invalidation). In either case, we return BUSY_E. + */ + return BUSY_E; + } } - - return NOT_READY_E; /* persistent contention: retry on a later cycle */ } int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n) @@ -3722,8 +3827,8 @@ int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n) int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) { - wc_rng_pool_state_u snap, next; - word32 m, done = 0; + WC_ATOMIC_UINT_ARG w1, w2, tw; + word32 head, epoch, tail, avail, m, done = 0; if ((rng == NULL) || (out == NULL) || (n == NULL)) return BAD_FUNC_ARG; @@ -3744,43 +3849,71 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) #endif /* Fail closed: no serving output on behalf of an out-of-service DRBG, and - * its pooled output is unusable material at rest. A concurrent writer's - * CAS fails against the store and abandons. */ + * its pooled output is unusable material at rest. A writer mid-fill sees + * the epoch move and abandons rather than publishing. */ if (wc_RNG_DRBG_Present(rng) && (rng->status != DRBG_OK)) { - WOLFSSL_ATOMIC_STORE(rng->poolState, 0); + PoolPurge(rng); return RNG_FAILURE_E; } - snap.state = WOLFSSL_ATOMIC_LOAD(rng->poolState); - if (snap.pool.current == 0) { + tw = WOLFSSL_ATOMIC_LOAD(rng->poolTail); + w1 = WOLFSSL_ATOMIC_LOAD(rng->poolHead); + head = WC_RNG_POOL_POS(w1); + epoch = WC_RNG_POOL_EPOCH(w1); + + if (WC_RNG_POOL_EPOCH(tw) != epoch) { + /* A purge landed since our last visit. Everything published before + * it is retired: resynchronize to head and report empty. Anything + * the writer publishes after this point is post-event and stands. */ + WOLFSSL_ATOMIC_STORE(rng->poolTail, WC_RNG_POOL_PACK(head, epoch)); #ifdef WC_RNG_DEBUG_STATS rng->_stats_pool_bytes_missed += *n; #endif return NOT_READY_E; } - m = *n; - if (m > (word32)snap.pool.current) - m = (word32)snap.pool.current; + + tail = WC_RNG_POOL_POS(tw); + avail = PoolUsed(head, tail, (word32)rng->poolSize); + if (avail == 0) { +#ifdef WC_RNG_DEBUG_STATS + rng->_stats_pool_bytes_missed += *n; +#endif + return NOT_READY_E; + } + m = (avail > *n) ? *n : avail; while (done < m) { - word32 at = ((word32)snap.pool.offset + done) % - (word32)rng->poolSize; + word32 at = PoolAt(PoolAdvance(tail, done, (word32)rng->poolSize), + (word32)rng->poolSize); word32 chunk = (word32)rng->poolSize - at; if (chunk > m - done) chunk = m - done; XMEMCPY(out + done, rng->pool + at, chunk); - /* burn on the way out the door, before the span is republished */ + /* burn on the way out the door, before the span is republished. + * [tail, tail+m) and the writer's [head, head+m') are disjoint by + * construction, so this can never erase published bytes. */ ForceZero(rng->pool + at, chunk); done += chunk; } - next.pool.current = (word16)((word32)snap.pool.current - m); - next.pool.offset = (word16)(((word32)snap.pool.offset + m) % - (word32)rng->poolSize); - /* single release store: the burn above is visible before the space - * is. May clobber a concurrent writer's publication -- benign and - * conservative (see the protocol comment). */ - WOLFSSL_ATOMIC_STORE(rng->poolState, next.state); + /* The linearization point. Both loads read head and epoch as one word, + * so a purge anywhere in our window is caught here -- epoch moves and + * never moves back. Our bytes then predate the event and must not be + * served, so discard the whole pre-event span rather than advancing. */ + w2 = WOLFSSL_ATOMIC_LOAD(rng->poolHead); + if (WC_RNG_POOL_EPOCH(w2) != epoch) { + WOLFSSL_ATOMIC_STORE(rng->poolTail, + WC_RNG_POOL_PACK(WC_RNG_POOL_POS(w2), + WC_RNG_POOL_EPOCH(w2))); + *n = 0; + return BUSY_E; + } + + /* Sole writer of poolTail: a plain store, no CAS on the reader path. */ + WOLFSSL_ATOMIC_STORE(rng->poolTail, + WC_RNG_POOL_PACK( + PoolAdvance(tail, m, (word32)rng->poolSize), + epoch)); #ifdef WC_RNG_DEBUG_STATS rng->_stats_pool_bytes_produced += m; @@ -3796,9 +3929,13 @@ int wc_RNG_Pool_Current(WC_RNG* rng, word32* n) if ((rng == NULL) || (n == NULL)) return BAD_FUNC_ARG; if (rng->pool != NULL) { - wc_rng_pool_state_u snap; - snap.state = WOLFSSL_ATOMIC_LOAD(rng->poolState); - *n = (word32)snap.pool.current; + WC_ATOMIC_UINT_ARG w = WOLFSSL_ATOMIC_LOAD(rng->poolHead); + WC_ATOMIC_UINT_ARG tw = WOLFSSL_ATOMIC_LOAD(rng->poolTail); + /* a purge not yet observed by the reader retires everything + * published before it: report empty. */ + *n = (WC_RNG_POOL_EPOCH(tw) != WC_RNG_POOL_EPOCH(w)) ? 0 : + PoolUsed(WC_RNG_POOL_POS(w), WC_RNG_POOL_POS(tw), + (word32)rng->poolSize); } else { *n = 0; @@ -5103,7 +5240,8 @@ int wc_FreeRng(WC_RNG* rng) XFREE(rng->pool, rng->heap, DYNAMIC_TYPE_RNG); rng->pool = NULL; rng->poolSize = 0; - WOLFSSL_ATOMIC_STORE(rng->poolState, 0); + WOLFSSL_ATOMIC_STORE(rng->poolHead, 0); + WOLFSSL_ATOMIC_STORE(rng->poolTail, 0); } #endif diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 51134b264da..2b5b2ef015b 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -93,9 +93,9 @@ !defined(WC_RNG_NO_POOL) #define WC_RNG_HAVE_POOL #ifdef WOLFSSL_NO_ATOMICS - typedef word32 WC_RNG_pool_state_t; + typedef word32 WC_RNG_pool_pos_t; #else - typedef wolfSSL_Atomic_Uint WC_RNG_pool_state_t; + typedef wolfSSL_Atomic_Uint WC_RNG_pool_pos_t; #endif #else #undef WC_RNG_HAVE_POOL @@ -512,7 +512,8 @@ struct WC_RNG { #ifdef WC_RNG_HAVE_POOL byte* pool; word16 poolSize; - WC_RNG_pool_state_t poolState; + WC_RNG_pool_pos_t poolHead; /* written by writer/purger only, CAS */ + WC_RNG_pool_pos_t poolTail; /* written by reader only, plain. */ #ifdef WC_RNG_DEBUG_STATS wc_rng_debug_counter_t _stats_pool_bytes_produced; wc_rng_debug_counter_t _stats_pool_bytes_missed; From fb7d15b9927e29744fc65b435a1757943d01ea73 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 14 Sep 2026 17:46:41 -0500 Subject: [PATCH 082/102] wolfssl/wolfcrypt/wc_port.h: add WC_CAS_WITH_RETRY_BEGIN(), WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(), WC_CAS_WITH_RETRY_LOOP_UNTIL(), WC_CAS_WITH_RETRY_LOOP_FOREVER(), and WC_CAS_WITH_RETRY_END(), and port helpers WC_CAS_WITH_RETRY_EXTRA_DECLS, WC_CAS_WITH_RETRY_ITER_CLAUSE, and WC_CAS_WITH_RETRY_FOREVER_CLAUSE. wolfcrypt/src/random.c and wolfcrypt/src/rng_bank.c: refactor CAS loops using WC_CAS_WITH_RETRY_*() macros, except for wc_RNG_lock_put_conditional() and wc_rng_bank_inst_lock_put_conditional(), which have nonconformant code patterns and call the new hooks directly. --- wolfcrypt/src/random.c | 175 +++++++++++++++++++----------------- wolfcrypt/src/rng_bank.c | 125 ++++++++++++++------------ wolfssl/wolfcrypt/wc_port.h | 82 +++++++++++++++++ 3 files changed, 241 insertions(+), 141 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 46ab0a7cdff..72977525e08 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -809,17 +809,17 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, * three-no-ops doctrine.) */ static void NextSeedPurge(wolfSSL_Atomic_Int *lenp) { - WC_ATOMIC_INT_ARG cur = WOLFSSL_ATOMIC_LOAD(*lenp); - for (;;) { - WC_ATOMIC_INT_ARG want; - if (cur == WC_DRBG_NEXT_SEED_PURGED) + int ret; + WC_ATOMIC_INT_ARG cur_len, want_len; + + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(lenp, cur_len, ret) { + if (cur_len == WC_DRBG_NEXT_SEED_PURGED) return; /* already handed off to a producer's unwind. */ - want = (cur == WC_DRBG_NEXT_SEED_PRODUCING) ? + want_len = (cur_len == WC_DRBG_NEXT_SEED_PRODUCING) ? WC_DRBG_NEXT_SEED_PURGED : WC_DRBG_NEXT_SEED_EMPTY; - if (wolfSSL_Atomic_Int_CompareExchange(lenp, &cur, want)) - return; - /* cur was reloaded by the failed exchange; re-evaluate. */ - } + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Int_CompareExchange, + lenp, cur_len, want_len, ret); + } WC_CAS_WITH_RETRY_END; } /* Release a producer claim (PRODUCING) on the credited aperture, @@ -874,23 +874,24 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, /* Iff _ENTROPY_INVALIDATED, assert the _ENTROPY_RECOVERING bit now -- if we * are re-invalidated in the meantime, it will have been cleared by the * invalidation at exit time, signaling that we are still invalidated. */ - for (;;) { - if (! (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + + WC_CAS_WITH_RETRY_BEGIN(&rng->lock, cur_lock, ret) { + if (! (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { + ret = 0; break; + } if (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING) { /* Must short-circuit return here, so that we don't improperly clear * the _RECOVERING bit. */ return BUSY_E; } - if (wolfSSL_Atomic_Uint_CompareExchange( - &rng->lock, &cur_lock, - cur_lock | WC_RNG_LOCK_ENTROPY_RECOVERING)) - { - /* We now have the _RECOVERING mutex -- record that fact. */ - cur_lock |= WC_RNG_LOCK_ENTROPY_RECOVERING; - break; - } - } + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &rng->lock, cur_lock, + cur_lock | WC_RNG_LOCK_ENTROPY_RECOVERING, + ret); + /* We now have the _RECOVERING mutex -- record that fact. */ + cur_lock |= WC_RNG_LOCK_ENTROPY_RECOVERING; + } WC_CAS_WITH_RETRY_END; #endif /* WC_RNG_HAVE_LOCK */ #ifdef WC_RNG_HAVE_POOL @@ -1014,29 +1015,27 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, * and return with the success or failure code from above. */ if ((ret == 0) && (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { - for (;;) { + WC_CAS_WITH_RETRY_BEGIN(&rng->lock, cur_lock, ret) { if (! (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING)) { ret = NEEDS_RECOVERY_E; break; } - if (wolfSSL_Atomic_Uint_CompareExchange( - &rng->lock, &cur_lock, - cur_lock & ~(WC_RNG_LOCK_ENTROPY_INVALIDATED | - WC_RNG_LOCK_ENTROPY_RECOVERING))) - { - break; - } - } + WC_CAS_WITH_RETRY_LOOP_FOREVER( + wolfSSL_Atomic_Uint_CompareExchange, &rng->lock, cur_lock, + cur_lock & ~(WC_RNG_LOCK_ENTROPY_INVALIDATED | + WC_RNG_LOCK_ENTROPY_RECOVERING), + ret); + } WC_CAS_WITH_RETRY_END; } else if (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING) { - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &rng->lock, &cur_lock, - cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING)) - { - break; - } - } + /* ret carries the reseed's status here and must survive, so the + * release gets its own result variable. */ + int release_ret; + WC_CAS_WITH_RETRY_BEGIN(&rng->lock, cur_lock, release_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER( + wolfSSL_Atomic_Uint_CompareExchange, &rng->lock, cur_lock, + cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING, release_ret); + } WC_CAS_WITH_RETRY_END; } #endif /* WC_RNG_HAVE_LOCK */ @@ -3137,7 +3136,7 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, * the RNG is freshly seeded after a fork(), to avoid seeding or generating from * duplicated internal state. */ -static int rng_pid_change_check(WC_RNG* rng) { +static WC_MAYBE_UNUSED int rng_pid_change_check(WC_RNG* rng) { int ret; int my_pid = getpid(); @@ -3340,6 +3339,7 @@ int wc_RNG_lock_get_conditional(WC_RNG* rng, int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) { WC_RNG_lock_arg_t cur_lock, new_lock; + int cas_ret; if (rng == NULL) return BAD_FUNC_ARG; cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); @@ -3350,7 +3350,7 @@ int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) ++rng->_stats_locks_released; #endif - for (;;) { + WC_CAS_WITH_RETRY_BEGIN(&rng->lock, cur_lock, cas_ret) { new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD); /* extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the @@ -3358,10 +3358,9 @@ int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | WC_RNG_LOCK_REQUIRED; new_lock |= extra_bits; - if (wolfSSL_Atomic_Uint_CompareExchange( - &rng->lock, &cur_lock, new_lock)) - break; - } + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &rng->lock, cur_lock, new_lock, cas_ret); + } WC_CAS_WITH_RETRY_END; #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) @@ -3378,6 +3377,8 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, WC_RNG_lock_arg_t expected_extra_bits, WC_RNG_lock_arg_t want_extra_bits) { + int cas_ret; + WC_CAS_WITH_RETRY_EXTRA_DECLS; WC_RNG_lock_arg_t cur_lock, expected, new_lock; if (rng == NULL) @@ -3391,6 +3392,8 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, ++rng->_stats_locks_released; #endif + /* Note this CAS loop doesn't use WC_CAS_WITH_RETRY_*() (non-conformant code + * pattern), so the WC_CAS_WITH_RETRY_* hook macros are invoked directly. */ for (;;) { new_lock = cur_lock & (((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD); @@ -3427,6 +3430,11 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, } cur_lock = expected; + + cas_ret = WC_CAS_WITH_RETRY_FOREVER_CLAUSE; + if (cas_ret != 0) + return cas_ret; + WC_CAS_WITH_RETRY_ITER_CLAUSE(&rng->lock, cur_lock, new_lock, cas_ret); } /* conditional release failed: the caller is still the holder, at both @@ -3450,11 +3458,12 @@ int wc_RNG_lock_read(WC_RNG* rng, WC_RNG_lock_arg_t* state) int wc_RNG_lock_set_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) { WC_RNG_lock_arg_t cur_lock, new_lock; + int cas_ret; if (rng == NULL) return BAD_FUNC_ARG; cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); - for (;;) { + WC_CAS_WITH_RETRY_BEGIN(&rng->lock, cur_lock, cas_ret) { new_lock = cur_lock & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); /* extra_bits is allowed to assert WC_RNG_LOCK_REQUIRED, which is in the * reserved section. */ @@ -3462,17 +3471,16 @@ int wc_RNG_lock_set_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) WC_RNG_LOCK_REQUIRED; new_lock |= extra_bits; - if (wolfSSL_Atomic_Uint_CompareExchange( - &rng->lock, &cur_lock, - new_lock)) - break; - } + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &rng->lock, cur_lock, new_lock, cas_ret); + } WC_CAS_WITH_RETRY_END; return 0; } int wc_RNG_lock_add_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) { WC_RNG_lock_arg_t cur_lock; + int cas_ret; if (rng == NULL) return BAD_FUNC_ARG; cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); @@ -3482,18 +3490,18 @@ int wc_RNG_lock_add_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | WC_RNG_LOCK_REQUIRED; - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &rng->lock, &cur_lock, - cur_lock | extra_bits)) - break; - } + WC_CAS_WITH_RETRY_BEGIN(&rng->lock, cur_lock, cas_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &rng->lock, cur_lock, + cur_lock | extra_bits, cas_ret); + } WC_CAS_WITH_RETRY_END; return 0; } int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) { WC_RNG_lock_arg_t cur_lock; + int cas_ret; if (rng == NULL) return BAD_FUNC_ARG; if (extra_bits & WC_RNG_LOCK_REQUIRED) { @@ -3504,32 +3512,29 @@ int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &rng->lock, &cur_lock, - cur_lock & ~extra_bits)) - break; - } + WC_CAS_WITH_RETRY_BEGIN(&rng->lock, cur_lock, cas_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &rng->lock, cur_lock, + cur_lock & ~extra_bits, cas_ret); + } WC_CAS_WITH_RETRY_END; return 0; } #ifdef HAVE_HASHDRBG WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { WC_RNG_lock_arg_t cur_lock; + int cas_ret; if (rng == NULL) return BAD_FUNC_ARG; - cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &rng->lock, &cur_lock, + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->lock, cur_lock, cas_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER( + wolfSSL_Atomic_Uint_CompareExchange, &rng->lock, cur_lock, (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING) | - WC_RNG_LOCK_ENTROPY_INVALIDATED)) - { - break; - } - } + WC_RNG_LOCK_ENTROPY_INVALIDATED, + cas_ret); + } WC_CAS_WITH_RETRY_END; /* If no lock is held, either the RNG is in use without a lock, in which * case the reseedCtr is the only way to force invalidation semantics on the @@ -3706,14 +3711,15 @@ static WC_INLINE word32 PoolUsed(word32 head, word32 tail, word32 poolSize) * the counters are deliberately left alone. */ static void PoolPurge(WC_RNG* rng) { - WC_ATOMIC_UINT_ARG cur = WOLFSSL_ATOMIC_LOAD(rng->poolHead); - for (;;) { - WC_ATOMIC_UINT_ARG want = - WC_RNG_POOL_PACK(WC_RNG_POOL_POS(cur), WC_RNG_POOL_EPOCH(cur) + 1U); - if (wolfSSL_Atomic_Uint_CompareExchange(&rng->poolHead, &cur, want)) - return; - /* cur was reloaded by the failed exchange; re-evaluate. */ - } + WC_ATOMIC_UINT_ARG cur, want; + int cas_ret; + + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->poolHead, cur, cas_ret) { + want = WC_RNG_POOL_PACK(WC_RNG_POOL_POS(cur), + WC_RNG_POOL_EPOCH(cur) + 1U); + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &rng->poolHead, cur, want, cas_ret); + } WC_CAS_WITH_RETRY_END; } int wc_RNG_Pool_Alloc(WC_RNG* rng, word32 size) @@ -4419,6 +4425,7 @@ static WC_INLINE int NextStirPtrs(WC_RNG* rng, byte** seed, static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, const byte *nonce, word32 n) { + int claim_ret; byte* seed; wolfSSL_Atomic_Int* lenp; int *nextSeedRBGCStratum_p = NULL; @@ -4494,7 +4501,7 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, * produce-side exclusivity a CAS, matching the consume side's * READY -> CONSUMING claim. This lane only: the uncredited * accumulator above stays multi-writer by design. */ - for (;;) { + WC_CAS_WITH_RETRY_BEGIN(lenp, cur, claim_ret) { if ((cur == WC_DRBG_NEXT_SEED_PRODUCING) || (cur == WC_DRBG_NEXT_SEED_PURGED)) { @@ -4508,11 +4515,11 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, /* Ready or consuming -- nothing to do. */ return WC_NO_ERR_TRACE(ALREADY_E); /* not an error */ } - if (wolfSSL_Atomic_Int_CompareExchange(lenp, &cur, - WC_DRBG_NEXT_SEED_PRODUCING)) - break; - /* cur was reloaded by the failed exchange; re-evaluate. */ - } + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Int_CompareExchange, + lenp, cur, + WC_DRBG_NEXT_SEED_PRODUCING, + claim_ret); + } WC_CAS_WITH_RETRY_END; if (cur == (WC_ATOMIC_INT_ARG)nextSeedSz) { /* Complete but unpublished (interrupted between fill * completion and publication): retry the health test and diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index bb2ab967bc4..83789a1834c 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -2724,19 +2724,20 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get_conditional( WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) { WC_RNG_lock_arg_t cur_lock, new_lock; + int cas_ret; if (inst == NULL) return BAD_FUNC_ARG; cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); if (! (cur_lock & WC_RNG_LOCK_HELD)) return OBJECT_NOT_LOCKED_E; - for (;;) { + WC_CAS_WITH_RETRY_BEGIN(&inst->lock, cur_lock, cas_ret) { new_lock = cur_lock & ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD)); - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, new_lock)) - break; - } + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &inst->lock, cur_lock, new_lock, + cas_ret); + } WC_CAS_WITH_RETRY_END; if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) return NEEDS_RECOVERY_E; @@ -2746,6 +2747,8 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { + int cas_ret; + WC_CAS_WITH_RETRY_EXTRA_DECLS; WC_RNG_lock_arg_t cur_lock, expected, new_lock; if (inst == NULL) @@ -2755,6 +2758,8 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_in if (! (cur_lock & WC_RNG_LOCK_HELD)) return OBJECT_NOT_LOCKED_E; + /* Note this CAS loop doesn't use WC_CAS_WITH_RETRY_*() (non-conformant code + * pattern), so the WC_CAS_WITH_RETRY_* hook macros are invoked directly. */ for (;;) { new_lock = (cur_lock & ((((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) & ~WC_RNG_LOCK_HELD))) | @@ -2780,6 +2785,11 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_in * next reconstruction from it, else a concurrent invalidation * loops forever. */ cur_lock = expected; + + cas_ret = WC_CAS_WITH_RETRY_FOREVER_CLAUSE; + if (cas_ret != 0) + return cas_ret; + WC_CAS_WITH_RETRY_ITER_CLAUSE(&inst->lock, cur_lock, new_lock, cas_ret); } /* conditional release failed: the caller is still the holder. */ return UNEXPECTED_STATE_E; @@ -2796,26 +2806,28 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, W WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { WC_RNG_lock_arg_t cur_lock, new_lock; + int cas_ret; if (inst == NULL) return BAD_FUNC_ARG; cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - for (;;) { + WC_CAS_WITH_RETRY_BEGIN(&inst->lock, cur_lock, cas_ret) { new_lock = cur_lock & ((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | WC_RNG_LOCK_REQUIRED; new_lock |= extra_bits; - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, new_lock)) - break; - } + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &inst->lock, cur_lock, new_lock, + cas_ret); + } WC_CAS_WITH_RETRY_END; return 0; } WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { WC_RNG_lock_arg_t cur_lock; + int cas_ret; if (inst == NULL) return BAD_FUNC_ARG; cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); @@ -2823,18 +2835,18 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *in extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U) | WC_RNG_LOCK_REQUIRED; - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - cur_lock | extra_bits)) - break; - } + WC_CAS_WITH_RETRY_BEGIN(&inst->lock, cur_lock, cas_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &inst->lock, cur_lock, + cur_lock | extra_bits, cas_ret); + } WC_CAS_WITH_RETRY_END; return 0; } WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) { WC_RNG_lock_arg_t cur_lock; + int cas_ret; if (inst == NULL) return BAD_FUNC_ARG; if (extra_bits & WC_RNG_LOCK_REQUIRED) { @@ -2845,12 +2857,11 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst * extra_bits &= ~((1U << WC_RNG_LOCK_EXTRA_SHIFT) - 1U); - for (;;) { - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - cur_lock & ~extra_bits)) - break; - } + WC_CAS_WITH_RETRY_BEGIN(&inst->lock, cur_lock, cas_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, + &inst->lock, cur_lock, + cur_lock & ~extra_bits, cas_ret); + } WC_CAS_WITH_RETRY_END; return 0; } @@ -2866,22 +2877,22 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_invalidate_entropy( struct wc_rng_bank_inst *inst) { WC_RNG_lock_arg_t cur_lock; + int cas_ret; if (inst == NULL) return BAD_FUNC_ARG; - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - for (;;) { - /* Clearing _RECOVERING here is what makes it an epoch witness: an - * in-flight recovery discovers at exit that its seed predates this - * event, and leaves _INVALIDATED asserted. Mirrors - * wc_RNG_invalidate_entropy(). */ - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING) | - WC_RNG_LOCK_ENTROPY_INVALIDATED)) - break; - } + /* Clearing _RECOVERING here is what makes it an epoch witness: an + * in-flight recovery discovers at exit that its seed predates this + * event, and leaves _INVALIDATED asserted. Mirrors + * wc_RNG_invalidate_entropy(). */ + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&inst->lock, cur_lock, cas_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER( + wolfSSL_Atomic_Uint_CompareExchange, &inst->lock, cur_lock, + (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING) | + WC_RNG_LOCK_ENTROPY_INVALIDATED, + cas_ret); + } WC_CAS_WITH_RETRY_END; /* If no lock is held, the saturated reseedCtr is the only way to force * invalidation semantics on a lock-free consumer; if a lock is held, @@ -2903,26 +2914,25 @@ static int wc_rng_bank_inst_recovery_enter( struct wc_rng_bank_inst *inst, int *recovering) { WC_RNG_lock_arg_t cur_lock; + int cas_ret; if ((inst == NULL) || (recovering == NULL)) return BAD_FUNC_ARG; *recovering = 0; - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - for (;;) { + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&inst->lock, cur_lock, cas_ret) { if (! (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) return 0; if (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING) return BUSY_E; - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - cur_lock | WC_RNG_LOCK_ENTROPY_RECOVERING)) - { - /* We now have the _RECOVERING mutex -- record that fact. */ - *recovering = 1; - return 0; - } - } + WC_CAS_WITH_RETRY_LOOP_FOREVER( + wolfSSL_Atomic_Uint_CompareExchange, &inst->lock, cur_lock, + cur_lock | WC_RNG_LOCK_ENTROPY_RECOVERING, cas_ret); + /* success arm: we now have the _RECOVERING mutex -- record that. */ + *recovering = 1; + } WC_CAS_WITH_RETRY_END; + + return cas_ret; } /* Release the recovery mutex, and report. Must be called on every path out of @@ -2937,27 +2947,28 @@ static int wc_rng_bank_inst_recovery_exit( struct wc_rng_bank_inst *inst, int recovering, int ret) { WC_RNG_lock_arg_t cur_lock; + int cas_ret; if (! recovering) return ret; - cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); - for (;;) { + /* ret is the caller's incoming status and must survive, so the release + * uses its own result variable. */ + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&inst->lock, cur_lock, cas_ret) { if (! (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING)) { if (ret == 0) ret = NEEDS_RECOVERY_E; break; } - if (wolfSSL_Atomic_Uint_CompareExchange( - &inst->lock, &cur_lock, - (ret == 0) - ? (cur_lock & ~(WC_RNG_LOCK_ENTROPY_INVALIDATED | - WC_RNG_LOCK_ENTROPY_RECOVERING)) - : (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING))) - { - break; - } - } + WC_CAS_WITH_RETRY_LOOP_FOREVER( + wolfSSL_Atomic_Uint_CompareExchange, &inst->lock, cur_lock, + (ret == 0) + ? (cur_lock & ~(WC_RNG_LOCK_ENTROPY_INVALIDATED | + WC_RNG_LOCK_ENTROPY_RECOVERING)) + : (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING), + cas_ret); + } WC_CAS_WITH_RETRY_END; + return ret; } diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index 6cc33cac23e..c2b1f0ea6bf 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -790,6 +790,88 @@ } #endif +/*** Macro abstractions for compare-and-exchange retry loops, allowing ***/ +/*** platform-specific instrumentation and failure paths. ***/ + +/* WC_CAS_WITH_RETRY_EXTRA_DECLS allows declaration and initialization of + * variables (e.g. a counter) just above the retry loop in + * WC_CAS_WITH_RETRY_BEGIN(). + */ +#ifndef WC_CAS_WITH_RETRY_EXTRA_DECLS + #define WC_CAS_WITH_RETRY_EXTRA_DECLS \ + struct wc_cas_with_retry_dummy_struct +#endif + +/* Note that freeform code after WC_CAS_WITH_RETRY_BEGIN() and before + * WC_CAS_WITH_RETRY_LOOP_UNTIL() sits inside the loop -- continue + * in that span omits the refresh of cur_var by the CAS (potentially inducing an + * infinite loop), and break in that span without setting result_var leaves it + * at WC_FAILURE. return and goto both behave normally in the freeform span. + */ +#define WC_CAS_WITH_RETRY_BEGIN(targetvar_p, cur_var, result_var) \ + do { \ + int WC_CAS_WITH_RETRY_keep_looping = 1; \ + WC_CAS_WITH_RETRY_EXTRA_DECLS; \ + \ + (result_var) = WC_NO_ERR_TRACE(WC_FAILURE); \ + \ + while (WC_CAS_WITH_RETRY_keep_looping) + +#define WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(targetvar_p, cur_var, result_var) \ + do { \ + int WC_CAS_WITH_RETRY_keep_looping = 1; \ + WC_CAS_WITH_RETRY_EXTRA_DECLS; \ + \ + (cur_var) = WOLFSSL_ATOMIC_LOAD(*(targetvar_p)); \ + (result_var) = WC_NO_ERR_TRACE(WC_FAILURE); \ + \ + while (WC_CAS_WITH_RETRY_keep_looping) + +#ifndef WC_CAS_WITH_RETRY_ITER_CLAUSE + #define WC_CAS_WITH_RETRY_ITER_CLAUSE(targetvar_p, cur_var, \ + want_val, result_var) WC_DO_NOTHING +#endif + +/* The "until_clause" should be portable logic of overriding salience, used in + * situ by the direct (portable) user code. The _ITER_CLAUSE is for + * non-portable logic, such as CPU/scheduler relaxation/yield or deadlock + * detection, and is free to set result_var and break as it sees fit. + * + * Freeform code can appear between WC_CAS_WITH_RETRY_LOOP_UNTIL() and + * WC_CAS_WITH_RETRY_END(), and will be evaluated iff the CAS succeeds. + */ +#define WC_CAS_WITH_RETRY_LOOP_UNTIL(cmpxchg_method, targetvar_p, cur_var, \ + want_val, result_var, until_clause) \ + if (! cmpxchg_method(targetvar_p, &(cur_var), want_val)) { \ + (result_var) = (until_clause); \ + if ((result_var) != 0) \ + break; \ + { \ + WC_CAS_WITH_RETRY_ITER_CLAUSE(targetvar_p, cur_var, \ + want_val, result_var); \ + } \ + continue; \ + } \ + else { \ + (result_var) = 0; \ + WC_CAS_WITH_RETRY_keep_looping = 0; \ + } \ + WC_DO_NOTHING + +#ifndef WC_CAS_WITH_RETRY_FOREVER_CLAUSE + #define WC_CAS_WITH_RETRY_FOREVER_CLAUSE 0 +#endif + +#define WC_CAS_WITH_RETRY_LOOP_FOREVER(cmpxchg_method, targetvar_p, \ + cur_var, want_val, result_var) \ + WC_CAS_WITH_RETRY_LOOP_UNTIL(cmpxchg_method, targetvar_p, cur_var, \ + want_val, result_var, \ + WC_CAS_WITH_RETRY_FOREVER_CLAUSE) + +/* Note, WC_CAS_WITH_RETRY_END() has no side effects -- it's just the success + * arm that ends the loop. */ +#define WC_CAS_WITH_RETRY_END } while (0) + /* Reference counting. */ typedef struct wolfSSL_RefWithMutex { #if !defined(SINGLE_THREADED) From b25bd17474bb16edd17bf5f3094915461b4f8fcc Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 00:18:45 -0500 Subject: [PATCH 083/102] wolfcrypt/test/test.c: when TEST_SLEEP is not otherwise defined, define it to WC_RELAX_LONG_LOOP(). --- wolfcrypt/test/test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 58f464773ee..657305f4cf8 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -2261,7 +2261,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t nist_sp80056c_twostep_cmac(void) /* optional macro to add sleep between tests */ #ifndef TEST_SLEEP -#define TEST_SLEEP() WC_DO_NOTHING +#define TEST_SLEEP() WC_RELAX_LONG_LOOP() #else #define TEST_PASS test_pass #include /* for var args */ From f4af685ab92ccae8b914174de0d04ee056476bb4 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 00:19:43 -0500 Subject: [PATCH 084/102] linuxkm/lkcapi_sha_glue.c: * in wc_linuxkm_entropy_daemon(), add a top-priority patrol sweep to RBGC-reseed invalidated RNGs, reducing the global invalidation-induced blackout to <25 ms with wolfEntropy, and <5 ms with RDSEED (measured on 7960X). * in wc_linuxkm_rng_bank_init(), pass WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE to wc_rng_bank_init_nonce(), and pass WC_RNG_BANK_FLAG_RBGC | WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE to wc_rng_bank_recover_inst(); * in linuxkm_InitRng_DefaultRBGC(), remove !can_sleep condition on WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE. wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/src/random.c: * rename WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE to WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE; * in wc_RNG_lock_get(), grant lock on invalidated RNG if it can be inline-recovered from a banked seed; * in wc_RNG_Pool_Collect2(), fix to return NOT_READY_E if the epoch changed but isn't yet synchronized (anything written before resync is discarded) -- fixes futile daemon spinning after invalidation; * in wc_rng_bank_init_nonce(), when WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE, pass WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED to wc_InitRngNonceRBGC() and wc_InitRngNonce_ex2(); * in wc_rng_bank_inst_reinit(), when flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE, pass WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED to wc_InitRngNonce_ex2(); * in wc_rng_bank_recover_inst(), allow WC_RNG_BANK_FLAG_RBGC and WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE flags, and when WC_RNG_BANK_FLAG_RBGC, try to use wc_RNG_DRBG_ReseedRBGC() for simple reseed recovery. --- linuxkm/lkcapi_sha_glue.c | 76 +++++++++++++++++++++++++++++------- wolfcrypt/src/random.c | 60 ++++++++++++++++++++++------ wolfcrypt/src/rng_bank.c | 61 ++++++++++++++++++++++------- wolfssl/wolfcrypt/rng_bank.h | 16 ++++---- 4 files changed, 165 insertions(+), 48 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 0e7da3da9a3..3c6342e734b 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2749,13 +2749,10 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_POOL */ - for (;;) { + while (! kthread_should_stop()) { int progress = 0; int congested_progress = 0; - if (kthread_should_stop()) - break; - #ifdef WC_LINUXKM_VMGENID_POLL wc_linuxkm_vmgenid_poll(&vmgenid_poll_state, root_rng); #endif @@ -2772,18 +2769,70 @@ static int wc_linuxkm_entropy_daemon(void *arg) (root_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { unsigned long uncredited_nonce = random_get_entropy(); - int inv_ret = wc_RNG_DRBG_Reseed_Now( + int inv_ret; + +#ifdef WC_VERBOSE_RNG + pr_info("wc_linuxkm_entropy_daemon: starting root recovery reseed.\n"); +#endif + inv_ret = wc_RNG_DRBG_Reseed_Now( root_rng, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); - if (inv_ret != 0) + if (inv_ret == 0) { +#ifdef WC_VERBOSE_RNG + pr_info("wc_linuxkm_entropy_daemon: finished root recovery reseed.\n"); +#endif + progress = 1; + } + else pr_err_ratelimited("wc_entropyd: post-invalidation " "root_rng reseed failed: %d\n", inv_ret); } } #endif -#ifdef HAVE_HASHDRBG +#if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) + /* recovery pass: push RBGC seeds to all WC_RNG_LOCK_ENTROPY_INVALIDATED + * instances that need them. */ + if (root_rng != NULL) { +#ifdef WC_VERBOSE_RNG + int n_rbgc_recovered = 0; +#endif + for (i = 0; i < bank->n_rngs; i++) { + WC_RNG *rng = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); + WC_RNG_lock_arg_t rng_lock_state; + WC_ATOMIC_INT_ARG nextSeedLen; + + if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) + continue; + if (wc_RNG_lock_read(rng, &rng_lock_state) != 0) + continue; + if (! (rng_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + continue; + if (wc_RNG_DRBG_NextSeedCurrent(rng, &nextSeedLen) != 0) + continue; + if ((nextSeedLen == WC_DRBG_NEXT_SEED_READY) || + (nextSeedLen == WC_DRBG_NEXT_SEED_CONSUMING)) + { + continue; + } + if (wc_rng_bank_next_seed_generate_rbgc(bank, i, WC_DRBG_NEXT_SEED_LEN) == 0) { +#ifdef WC_VERBOSE_RNG + ++n_rbgc_recovered; +#endif + congested_progress = 1; + progress = 1; + } + } +#ifdef WC_VERBOSE_RNG + if (n_rbgc_recovered > 0) { + pr_info("wc_linuxkm_entropy_daemon: RBGC recovery of %d/%d insts.\n", + n_rbgc_recovered, bank->n_rngs); + } +#endif + } +#endif /* WC_RNG_HAVE_NEXT_SEED && WC_RNG_HAVE_RBGC */ + /* recovery pass: fix out-of-service instances. The status * peek is lockless and possibly stale -- worst case it sends a * recover_inst() at a healthy instance (no-op) or misses one @@ -2796,7 +2845,8 @@ static int wc_linuxkm_entropy_daemon(void *arg) continue; } ret = wc_rng_bank_recover_inst(bank, i, 0 /* timeout_secs */, - 0 /* flags */); + WC_RNG_BANK_FLAG_RBGC | + WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE); if (ret == 0) { (void)wc_rng_bank_inst_flags_down( &bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED); @@ -2812,7 +2862,6 @@ static int wc_linuxkm_entropy_daemon(void *arg) } } } -#endif /* HAVE_HASHDRBG */ #ifdef WC_RNG_HAVE_NEXT_SEED if (root_rng != NULL) { @@ -3036,7 +3085,8 @@ static int wc_linuxkm_entropy_daemon(void *arg) static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) { int ret; - word32 flags = WC_RNG_BANK_FLAG_CAN_WAIT; + word32 flags = WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE | + WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING | WC_RNG_BANK_FLAG_RBGC; unsigned long uncredited_nonce = random_get_entropy(); if (wc_linuxkm_rng_initing_default_bank_flag && (default_bank != NULL)) { @@ -3071,7 +3121,7 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) * on the readout hot path. */ ret = wc_rng_bank_init_nonce( ctx, LINUXKM_RNG_BANK_SIZE, - flags | WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING | WC_RNG_BANK_FLAG_RBGC, + flags, WC_LINUXKM_INITRNG_TIMEOUT_SEC, NULL /* heap */, INVALID_DEVID, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, NULL, 0); @@ -3418,9 +3468,7 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { 0 /* timeout_secs */, WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | - (can_sleep ? - WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE : - 0)); + WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); if (ret != 0) { pr_warn_ratelimited("WARNING: linuxkm_InitRng_DefaultRBGC() failed " diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 72977525e08..68c00204f83 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3213,14 +3213,26 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) cur_lock = WOLFSSL_ATOMIC_LOAD(rng->lock); if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { - #ifdef WC_RNG_DEBUG_STATS - ++rng->_stats_locks_refused; /* racy */ - #endif +#ifdef WC_RNG_HAVE_NEXT_SEED + WC_ATOMIC_INT_ARG NextSeedCurrent; + if ((rng->flags & WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED) && + (wc_RNG_DRBG_NextSeedCurrent(rng, &NextSeedCurrent) == 0) && + (NextSeedCurrent == WC_DRBG_NEXT_SEED_READY)) + { + /* cheap inline recovery available. */ + } + else +#endif + { + #ifdef WC_RNG_DEBUG_STATS + ++rng->_stats_locks_refused; /* racy */ + #endif #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX - if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) - (void)wc_UnLockMutex(&rng->mutex); + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) + (void)wc_UnLockMutex(&rng->mutex); #endif - return NEEDS_RECOVERY_E; + return NEEDS_RECOVERY_E; + } } if ((! (cur_lock & WC_RNG_LOCK_HELD)) && @@ -3231,6 +3243,15 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) #ifdef WC_RNG_DEBUG_STATS ++rng->_stats_locks_taken; #endif + + /* If we arrived here via _RECOVER_AND_PROMOTE_FROM_NEXT_SEED with a pending + * NextSeed, there is a finite though minuscule chance that a second + * invalidation left the RNG without a banked seed to consume. In that + * case, the holder's generate falls through to the regular inline + * forced-reseed machinery. This is the same outcome as an invalidation + * landing immediately after a successful acquire. + */ + return 0; } @@ -3245,10 +3266,10 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) (void)wc_UnLockMutex(&rng->mutex); #endif - if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) - return NEEDS_RECOVERY_E; - else if (cur_lock & WC_RNG_LOCK_HELD) + if (cur_lock & WC_RNG_LOCK_HELD) return BUSY_E; + else if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) + return NEEDS_RECOVERY_E; else /* not reachable */ return UNEXPECTED_STATE_E; } @@ -3777,12 +3798,27 @@ int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, word32 n) word32 head, epoch, tail, free_sz, m, done = 0; int ret; + WC_ATOMIC_UINT_ARG tw; + snap = WOLFSSL_ATOMIC_LOAD(rng_dest->poolHead); head = WC_RNG_POOL_POS(snap); epoch = WC_RNG_POOL_EPOCH(snap); - /* A stale-epoch tail is conservative: it can only understate the free - * span, never overstate it, so no unread byte is ever overwritten. */ - tail = WC_RNG_POOL_POS(WOLFSSL_ATOMIC_LOAD(rng_dest->poolTail)); + tw = WOLFSSL_ATOMIC_LOAD(rng_dest->poolTail); + + if (WC_RNG_POOL_EPOCH(tw) != epoch) { + /* A purge landed and the reader has not yet acknowledged it. Its + * resync discards everything published before the purge -- which + * would include anything we published now -- so there is no useful + * work here until a read happens. Reporting NOT_READY_E rather + * than success also keeps a collector that polls fullness from + * spinning: wc_RNG_Pool_Current() reports empty across this + * window, while tail still describes the retired span, so a + * free-span computation from it would say full. Those two + * disagree only here, and only until the reader resyncs. */ + return NOT_READY_E; + } + + tail = WC_RNG_POOL_POS(tw); free_sz = (word32)rng_dest->poolSize - PoolUsed(head, tail, (word32)rng_dest->poolSize); diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 83789a1834c..907e9c52c8f 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -81,6 +81,11 @@ WOLFSSL_API int wc_rng_bank_init_nonce( int ret; int need_reenable_vec = 0; wc_static_assert(WC_DRBG_NOT_INIT == 0); /* make sure assumptions are met */ +#ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED + word32 rng_flags = WC_RNG_INIT_FLAGS_LOCK_REQUIRED; +#else + word32 rng_flags = WC_RNG_INIT_FLAGS_NONE; +#endif if ((ctx == NULL) || (n_rngs <= 0)) return BAD_FUNC_ARG; @@ -110,6 +115,10 @@ WOLFSSL_API int wc_rng_bank_init_nonce( wolfSSL_Atomic_Int_Init(&ctx->inst_op_gate, 0); #endif ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; +#ifdef WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED + if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE) + rng_flags |= WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; +#endif ctx->heap = heap; ctx->devId = devId; ctx->first_failover_inst = -1; @@ -153,13 +162,11 @@ WOLFSSL_API int wc_rng_bank_init_nonce( ret = wc_InitRngNonceRBGC( WC_RNG_BANK_INST_TO_RNG(rng_inst), &ctx->root_rng, - (byte *)&rng_inst, sizeof(byte *) + (byte *)&rng_inst, sizeof(byte *), #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) - , NULL, 0 - , WC_RNG_INIT_FLAGS_LOCK_REQUIRED -#else - , WC_RNG_INIT_FLAGS_NONE + NULL, 0, #endif + rng_flags ); } else @@ -170,7 +177,7 @@ WOLFSSL_API int wc_rng_bank_init_nonce( WC_RNG_BANK_INST_TO_RNG(rng_inst), (byte *)&rng_inst, sizeof(byte *), NULL, 0, heap, devId, - WC_RNG_INIT_FLAGS_LOCK_REQUIRED); + rng_flags); #else ret = wc_InitRngNonce_ex( WC_RNG_BANK_INST_TO_RNG(rng_inst), @@ -1654,6 +1661,12 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( struct wc_rng_debug_stats_snapshot s; int stats_snap_ret; #endif +#ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED + word32 rng_flags = WC_RNG_INIT_FLAGS_LOCK_REQUIRED | + WC_RNG_INIT_FLAGS_LOCK_INITIALLY; +#else + word32 rng_flags = WC_RNG_INIT_FLAGS_NONE; +#endif if (rng_inst == NULL) return BAD_FUNC_ARG; @@ -1665,6 +1678,11 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( if (ret < 0) return ret; +#ifdef WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED + if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE) + rng_flags |= WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; +#endif + /* No DRBG-NULL rejection here. wc_FreeRng() below nulls the DRBG, so an * instance left that way by an earlier failed reinit needs another attempt. * Note that with HAVE_INTEL_RDRAND on an RDRAND-capable CPU, a NULL DRBG is @@ -1718,10 +1736,7 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( #ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED ret = wc_InitRngNonce_ex2(WC_RNG_BANK_INST_TO_RNG(rng_inst), (byte *)&rng_inst, sizeof(byte *), - NULL, 0, - bank->heap, devId, - WC_RNG_INIT_FLAGS_LOCK_REQUIRED | - WC_RNG_INIT_FLAGS_LOCK_INITIALLY); + NULL, 0, bank->heap, devId, rng_flags); #else ret = wc_InitRngNonce_ex(WC_RNG_BANK_INST_TO_RNG(rng_inst), (byte *)&rng_inst, sizeof(byte *), @@ -1841,7 +1856,9 @@ WOLFSSL_API int wc_rng_bank_recover_inst( if ((bank == NULL) || (flags & ~(word32)(WC_RNG_BANK_FLAG_CAN_WAIT | - WC_RNG_BANK_FLAG_AFFINITY_LOCK))) + WC_RNG_BANK_FLAG_AFFINITY_LOCK | + WC_RNG_BANK_FLAG_RBGC | + WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE))) { return BAD_FUNC_ARG; } @@ -1865,8 +1882,24 @@ WOLFSSL_API int wc_rng_bank_recover_inst( /* In service but quarantined: one credited reseed clears the * quarantine -- lighter than reinit, preserving instance * identity. */ - ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(rng_inst), - NULL, 0); +#ifdef WC_RNG_HAVE_RBGC + WC_RNG_lock_arg_t root_lock_state = 0; + if ((flags & WC_RNG_BANK_FLAG_RBGC) && + (wc_RNG_GetStatus(&bank->root_rng) == WC_DRBG_OK) && + (wc_RNG_lock_read(&bank->root_rng, + &root_lock_state) == 0) && + (! (root_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) && + (wc_RNG_DRBG_ReseedRBGC(WC_RNG_BANK_INST_TO_RNG(rng_inst), + &bank->root_rng, NULL, 0) == 0)) + { + ret = 0; + } + else +#endif + { + ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(rng_inst), + NULL, 0); + } } /* else: healthy -- a stale lockless status observation; no-op. */ } @@ -1932,7 +1965,7 @@ static int rng_bank_spawn( { word32 child_init_flags = WC_RNG_INIT_FLAGS_NONE; #ifdef WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED - if (flags & WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE) + if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE) child_init_flags |= WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; #endif diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index c0d826a59a5..b1328486b09 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -62,7 +62,7 @@ #define WC_RNG_BANK_FLAG_RBGC (1U << 13) #define WC_RNG_BANK_FLAG_DEFAULT_BANK (1U << 14) #define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 15) -#define WC_RNG_BANK_FLAG_SPAWN_RECOVER_AND_PROMOTE (1U << 16) +#define WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE (1U << 16) #ifdef WC_RNG_HAVE_LOCK wc_static_assert(WC_RNG_LOCK_EXTRA_SHIFT == 4U); @@ -396,6 +396,13 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); { return wc_RNG_lock_get(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); } + static WC_INLINE int wc_rng_bank_inst_lock_get_conditional( + struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, + WC_RNG_lock_arg_t want_extra_bits) + { + return wc_RNG_lock_get_conditional( + WC_RNG_BANK_INST_TO_RNG(inst), expected_extra_bits, want_extra_bits); + } static WC_INLINE int wc_rng_bank_inst_lock_put( struct wc_rng_bank_inst *inst) { @@ -428,13 +435,6 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); return wc_RNG_lock_clear_extra( WC_RNG_BANK_INST_TO_RNG(inst), extra_bits); } - static WC_INLINE int wc_rng_bank_inst_lock_get_conditional( - struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, - WC_RNG_lock_arg_t want_extra_bits) - { - return wc_RNG_lock_get_conditional( - WC_RNG_BANK_INST_TO_RNG(inst), expected_extra_bits, want_extra_bits); - } #ifdef HAVE_HASHDRBG static WC_INLINE int wc_rng_bank_inst_invalidate_entropy( struct wc_rng_bank_inst *inst) From fd14f12c06a204d7b54d5b7b78d80ad4c0b3985a Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 00:59:28 -0500 Subject: [PATCH 085/102] wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, wolfcrypt/test/test.c: fixes for old-FIPS compatibility. --- wolfcrypt/src/rng_bank.c | 7 +++++-- wolfcrypt/test/test.c | 2 +- wolfssl/wolfcrypt/rng_bank.h | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 907e9c52c8f..9d071bb9864 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -35,6 +35,9 @@ #endif #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) + #ifndef WC_RNG_INIT_FLAGS_NONE + #define WC_RNG_INIT_FLAGS_NONE 0 + #endif /* backward-compat shim and helper declarations */ static int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, @@ -84,7 +87,7 @@ WOLFSSL_API int wc_rng_bank_init_nonce( #ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED word32 rng_flags = WC_RNG_INIT_FLAGS_LOCK_REQUIRED; #else - word32 rng_flags = WC_RNG_INIT_FLAGS_NONE; + WC_MAYBE_UNUSED word32 rng_flags = WC_RNG_INIT_FLAGS_NONE; #endif if ((ctx == NULL) || (n_rngs <= 0)) @@ -1665,7 +1668,7 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( word32 rng_flags = WC_RNG_INIT_FLAGS_LOCK_REQUIRED | WC_RNG_INIT_FLAGS_LOCK_INITIALLY; #else - word32 rng_flags = WC_RNG_INIT_FLAGS_NONE; + WC_MAYBE_UNUSED word32 rng_flags = WC_RNG_INIT_FLAGS_NONE; #endif if (rng_inst == NULL) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 657305f4cf8..f64f56ec2e8 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -29553,7 +29553,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) if (lock_state & WC_RNG_LOCK_HELD) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #if (!defined(HAVE_INTEL_RDSEED) && !defined(HAVE_INTEL_RDRAND)) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) + (!defined(HAVE_FIPS) || FIPS_VERSION3_EQ(5,2,4) || FIPS_VERSION3_GE(7,0,0)) { wc_drbg_reseed_ctr_t reseed_ctr = 0; api_ret = wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(held), diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index b1328486b09..2361d1a9be7 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -485,7 +485,8 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) #ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED #define WC_DRBG_RESEED_CTR_TYPE_DEFINED - #if defined(WORD64_AVAILABLE) && FIPS_VERSION3_GE(5,2,4) + #if defined(WORD64_AVAILABLE) && FIPS_VERSION3_GE(5,2,4) && \ + FIPS_VERSION3_NE(6,0,0) typedef word64 wc_drbg_reseed_ctr_t; #else typedef word32 wc_drbg_reseed_ctr_t; From 4c387e5338be7f8d5b00654ccedd5ddfcfdf32a7 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 01:20:17 -0500 Subject: [PATCH 086/102] wolfcrypt/src/random.c: in wc_RNG_Pool_Extract(), fix (remove) argument overwrite on error return, and add missing _stats_pool_bytes_missed advance. --- wolfcrypt/src/random.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 68c00204f83..038361f11cc 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3947,7 +3947,9 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) WOLFSSL_ATOMIC_STORE(rng->poolTail, WC_RNG_POOL_PACK(WC_RNG_POOL_POS(w2), WC_RNG_POOL_EPOCH(w2))); - *n = 0; +#ifdef WC_RNG_DEBUG_STATS + rng->_stats_pool_bytes_missed += *n; +#endif return BUSY_E; } From 9913e49c3bfff66878e3ee145b97fd849920c497 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 01:25:05 -0500 Subject: [PATCH 087/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/src/rng_bank.c, linuxkm/lkcapi_sha_glue.c, wolfcrypt/test/test.c: 's/WC_RNG_INIT_FLAGS_/WC_RNG_INIT_FLAG_/g'. --- doc/dox_comments/header_files/random.h | 24 +++++++------- linuxkm/lkcapi_sha_glue.c | 2 +- wolfcrypt/src/random.c | 30 ++++++++--------- wolfcrypt/src/rng_bank.c | 36 ++++++++++---------- wolfcrypt/test/test.c | 46 +++++++++++++------------- wolfssl/wolfcrypt/random.h | 12 +++---- 6 files changed, 75 insertions(+), 75 deletions(-) diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index b422a6243d3..022b089dc41 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -769,11 +769,11 @@ int wc_Sha512Drbg_IsDisabled(void); \brief Initialize a WC_RNG with instantiation-time security attributes. Identical to wc_InitRng_ex(), with a flags argument fixing attributes at - birth: WC_RNG_INIT_FLAGS_LOCK_REQUIRED latches the sticky lock-required + birth: WC_RNG_INIT_FLAG_LOCK_REQUIRED latches the sticky lock-required policy bit, so there is no reachable state in which the instance serves - without its lock policy; WC_RNG_INIT_FLAGS_LOCK_INITIALLY constructs into + without its lock policy; WC_RNG_INIT_FLAG_LOCK_INITIALLY constructs into a held lease, to be released with wc_RNG_lock_put(); - WC_RNG_INIT_FLAGS_USE_FULL_MUTEX layers a blocking wolfSSL_Mutex + WC_RNG_INIT_FLAG_USE_FULL_MUTEX layers a blocking wolfSSL_Mutex outermost around the lock latch, for user-mode sharing of one instance among threads (requires WC_RNG_HAVE_LOCK_FULL_MUTEX). @@ -784,14 +784,14 @@ int wc_Sha512Drbg_IsDisabled(void); \param rng The RNG object to initialize. \param heap Heap hint for dynamic allocation. \param devId Device id, or INVALID_DEVID. - \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes. + \param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes. _Example_ \code WC_RNG rng; if (wc_InitRng_ex2(&rng, NULL, INVALID_DEVID, - WC_RNG_INIT_FLAGS_LOCK_REQUIRED | - WC_RNG_INIT_FLAGS_LOCK_INITIALLY) != 0) { + WC_RNG_INIT_FLAG_LOCK_REQUIRED | + WC_RNG_INIT_FLAG_LOCK_INITIALLY) != 0) { // error handling } // caller holds the lease from birth @@ -820,7 +820,7 @@ int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags); \param nonceSz Length of nonce in bytes. \param heap Heap hint for dynamic allocation. \param devId Device id, or INVALID_DEVID. - \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes. + \param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes. \sa wc_InitRng_ex2 \sa wc_InitRngNonce_ex @@ -1016,13 +1016,13 @@ int wc_RNG_DRBG_Stir_Nonce(WC_RNG* rng, const byte* seed, \param child The caller-provided WC_RNG to instantiate (uninitialized). \param parent The chain parent to draw seed material from. - \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes for the child. + \param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes for the child. _Example_ \code WC_RNG root, child; wc_InitRng(&root); - if (wc_InitRngRBGC(&child, &root, WC_RNG_INIT_FLAGS_NONE) == 0) { + if (wc_InitRngRBGC(&child, &root, WC_RNG_INIT_FLAG_NONE) == 0) { // child serves independently; release with wc_FreeRng(&child) } \endcode @@ -1049,7 +1049,7 @@ int wc_InitRngRBGC(WC_RNG* child, WC_RNG* parent, word32 flags); \param parent The chain parent to draw seed material from. \param nonce Additional instantiation input. \param nonceSz Length of nonce in bytes. - \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes for the child. + \param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes for the child. \sa wc_InitRngRBGC \sa wc_InitRngNonceRBGC_New @@ -1071,7 +1071,7 @@ int wc_InitRngNonceRBGC(WC_RNG* child, WC_RNG* parent, const byte* nonce, \param child Receives the allocated, instantiated WC_RNG. \param parent The chain parent to draw seed material from. - \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes for the child. + \param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes for the child. \sa wc_InitRngRBGC \sa wc_InitRngNonceRBGC_New @@ -1093,7 +1093,7 @@ int wc_InitRngRBGC_New(WC_RNG** child, WC_RNG* parent, word32 flags); \param parent The chain parent to draw seed material from. \param nonce Additional instantiation input. \param nonceSz Length of nonce in bytes. - \param flags Bitwise-or of WC_RNG_INIT_FLAGS_* attributes for the child. + \param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes for the child. \sa wc_InitRngRBGC_New \sa wc_InitRngNonceRBGC diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 3c6342e734b..857c8ced7a4 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2967,7 +2967,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) /* registered-leaf pass: bank RBGC seeds from root_rng into * long-lived leaves that are invalidated or chain-backed, so their * next generate recovers/promotes in place - * (WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED). + * (WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED). * Sleepable-mutex context; entropy gathers are legal under it by * the atomic-born exclusion rule. */ if (root_rng != NULL) { diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 038361f11cc..c32b0a9a61a 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -2497,29 +2497,29 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, return BAD_FUNC_ARG; #ifndef WC_RNG_HAVE_NEXT_SEED - if (flags & WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED) + if (flags & WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED) return NOT_COMPILED_IN; #endif #ifndef WC_RNG_HAVE_LOCK_FULL_MUTEX - if (flags & WC_RNG_INIT_FLAGS_USE_FULL_MUTEX) + if (flags & WC_RNG_INIT_FLAG_USE_FULL_MUTEX) return NOT_COMPILED_IN; #endif #ifdef WC_RNG_HAVE_LOCK - if (flags & (WC_RNG_INIT_FLAGS_LOCK_REQUIRED | - WC_RNG_INIT_FLAGS_LOCK_INITIALLY)) + if (flags & (WC_RNG_INIT_FLAG_LOCK_REQUIRED | + WC_RNG_INIT_FLAG_LOCK_INITIALLY)) { word32 initial_flags = - ((flags & WC_RNG_INIT_FLAGS_LOCK_REQUIRED) ? + ((flags & WC_RNG_INIT_FLAG_LOCK_REQUIRED) ? WC_RNG_LOCK_REQUIRED : 0) | - ((flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) ? + ((flags & WC_RNG_INIT_FLAG_LOCK_INITIALLY) ? WC_RNG_LOCK_HELD : 0); XMEMSET(rng, 0, WC_OFFSETOF(WC_RNG, lock)); XMEMSET((byte *)rng + WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock), 0, sizeof(*rng) - (WC_OFFSETOF(WC_RNG, lock) + sizeof(rng->lock))); #ifdef WC_RNG_DEBUG_STATS - if (flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) + if (flags & WC_RNG_INIT_FLAG_LOCK_INITIALLY) rng->_stats_locks_taken = 1; #endif #ifdef WOLFSSL_NO_ATOMICS @@ -2943,12 +2943,12 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #ifdef WC_RNG_HAVE_NEXT_SEED if ((ret == 0) && - (flags & WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED)) + (flags & WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED)) { rng->flags |= WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; } #endif - if ((ret == 0) && (flags & WC_RNG_INIT_FLAGS_USE_FULL_MUTEX)) { + if ((ret == 0) && (flags & WC_RNG_INIT_FLAG_USE_FULL_MUTEX)) { #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX /* deliberately the last init step: no failure path can strand an * initialized mutex. */ @@ -2960,7 +2960,7 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, } else { rng->flags |= WC_RNG_FLAG_FULL_MUTEX; - if (flags & WC_RNG_INIT_FLAGS_LOCK_INITIALLY) { + if (flags & WC_RNG_INIT_FLAG_LOCK_INITIALLY) { /* born held at both layers: the constructor's caller holds * the whole latch, mutex included. */ (void)wc_LockMutex(&rng->mutex); @@ -3059,7 +3059,7 @@ int wc_rng_new_ex(WC_RNG **rng, byte* nonce, word32 nonceSz, } ret = _InitRng(*rng, nonce, nonceSz, NULL, 0, heap, devId, NULL, - WC_RNG_INIT_FLAGS_NONE); + WC_RNG_INIT_FLAG_NONE); if (ret != 0) { XFREE(*rng, heap, DYNAMIC_TYPE_RNG); *rng = NULL; @@ -3086,21 +3086,21 @@ WOLFSSL_ABI int wc_InitRng(WC_RNG* rng) { return _InitRng(rng, NULL, 0, NULL, 0, NULL, INVALID_DEVID, NULL, - WC_RNG_INIT_FLAGS_NONE); + WC_RNG_INIT_FLAG_NONE); } int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId) { return _InitRng(rng, NULL, 0, NULL, 0, heap, devId, NULL, - WC_RNG_INIT_FLAGS_NONE); + WC_RNG_INIT_FLAG_NONE); } int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz) { return _InitRng(rng, nonce, nonceSz, NULL, 0, NULL, INVALID_DEVID, NULL, - WC_RNG_INIT_FLAGS_NONE); + WC_RNG_INIT_FLAG_NONE); } @@ -3108,7 +3108,7 @@ int wc_InitRngNonce_ex(WC_RNG* rng, const byte* nonce, word32 nonceSz, void* heap, int devId) { return _InitRng(rng, nonce, nonceSz, NULL, 0, heap, devId, NULL, - WC_RNG_INIT_FLAGS_NONE); + WC_RNG_INIT_FLAG_NONE); } int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 9d071bb9864..808b27d12ec 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -35,8 +35,8 @@ #endif #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - #ifndef WC_RNG_INIT_FLAGS_NONE - #define WC_RNG_INIT_FLAGS_NONE 0 + #ifndef WC_RNG_INIT_FLAG_NONE + #define WC_RNG_INIT_FLAG_NONE 0 #endif /* backward-compat shim and helper declarations */ static int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, @@ -84,10 +84,10 @@ WOLFSSL_API int wc_rng_bank_init_nonce( int ret; int need_reenable_vec = 0; wc_static_assert(WC_DRBG_NOT_INIT == 0); /* make sure assumptions are met */ -#ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED - word32 rng_flags = WC_RNG_INIT_FLAGS_LOCK_REQUIRED; +#ifdef WC_RNG_INIT_FLAG_LOCK_REQUIRED + word32 rng_flags = WC_RNG_INIT_FLAG_LOCK_REQUIRED; #else - WC_MAYBE_UNUSED word32 rng_flags = WC_RNG_INIT_FLAGS_NONE; + WC_MAYBE_UNUSED word32 rng_flags = WC_RNG_INIT_FLAG_NONE; #endif if ((ctx == NULL) || (n_rngs <= 0)) @@ -118,9 +118,9 @@ WOLFSSL_API int wc_rng_bank_init_nonce( wolfSSL_Atomic_Int_Init(&ctx->inst_op_gate, 0); #endif ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; -#ifdef WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED +#ifdef WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE) - rng_flags |= WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; + rng_flags |= WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; #endif ctx->heap = heap; ctx->devId = devId; @@ -175,7 +175,7 @@ WOLFSSL_API int wc_rng_bank_init_nonce( else #endif { -#ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED +#ifdef WC_RNG_INIT_FLAG_LOCK_REQUIRED ret = wc_InitRngNonce_ex2( WC_RNG_BANK_INST_TO_RNG(rng_inst), (byte *)&rng_inst, sizeof(byte *), @@ -1664,11 +1664,11 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( struct wc_rng_debug_stats_snapshot s; int stats_snap_ret; #endif -#ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED - word32 rng_flags = WC_RNG_INIT_FLAGS_LOCK_REQUIRED | - WC_RNG_INIT_FLAGS_LOCK_INITIALLY; +#ifdef WC_RNG_INIT_FLAG_LOCK_REQUIRED + word32 rng_flags = WC_RNG_INIT_FLAG_LOCK_REQUIRED | + WC_RNG_INIT_FLAG_LOCK_INITIALLY; #else - WC_MAYBE_UNUSED word32 rng_flags = WC_RNG_INIT_FLAGS_NONE; + WC_MAYBE_UNUSED word32 rng_flags = WC_RNG_INIT_FLAG_NONE; #endif if (rng_inst == NULL) @@ -1681,9 +1681,9 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( if (ret < 0) return ret; -#ifdef WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED +#ifdef WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE) - rng_flags |= WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; + rng_flags |= WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; #endif /* No DRBG-NULL rejection here. wc_FreeRng() below nulls the DRBG, so an @@ -1736,7 +1736,7 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( wc_FreeRng(&rng_inst->rng); for (;;) { -#ifdef WC_RNG_INIT_FLAGS_LOCK_REQUIRED +#ifdef WC_RNG_INIT_FLAG_LOCK_REQUIRED ret = wc_InitRngNonce_ex2(WC_RNG_BANK_INST_TO_RNG(rng_inst), (byte *)&rng_inst, sizeof(byte *), NULL, 0, bank->heap, devId, rng_flags); @@ -1966,11 +1966,11 @@ static int rng_bank_spawn( return ret; { - word32 child_init_flags = WC_RNG_INIT_FLAGS_NONE; -#ifdef WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED + word32 child_init_flags = WC_RNG_INIT_FLAG_NONE; +#ifdef WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE) child_init_flags |= - WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; + WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED; #endif if (leaf_stack != NULL) { ret = wc_InitRngNonceRBGC(leaf_stack, diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index f64f56ec2e8..8f126680f07 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -29831,12 +29831,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) #if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) && \ !defined(HAVE_INTEL_RDSEED) && !defined(HAVE_INTEL_RDRAND) - /* WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED: + /* WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED: * recovery-consumption and chain-promotion at generate. */ { WC_RNG flag_rng; api_ret = wc_InitRng_ex2(&flag_rng, HEAP_HINT, INVALID_DEVID, - WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED); + WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* recovery: invalidate (purges the aperture), bank a fresh primary @@ -29872,7 +29872,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_InitRngNonceRBGC(&flag_rng, &proot, NULL, 0, NULL, 0, - WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED); + WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_DRBG_GetRBGCStratum(&flag_rng); @@ -29893,7 +29893,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* negative: unflagged leaf keeps its stratum. */ api_ret = wc_InitRngNonceRBGC(&flag_rng, &proot, NULL, 0, - NULL, 0, WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_DRBG_NextSeedGenerate(&flag_rng, @@ -30064,17 +30064,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) present = wc_RNG_DRBG_Present(&root); /* spawn argument contracts */ - api_ret = wc_InitRngRBGC(NULL, &root, WC_RNG_INIT_FLAGS_NONE); + api_ret = wc_InitRngRBGC(NULL, &root, WC_RNG_INIT_FLAG_NONE); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_InitRngRBGC(&leaf, NULL, WC_RNG_INIT_FLAGS_NONE); + api_ret = wc_InitRngRBGC(&leaf, NULL, WC_RNG_INIT_FLAG_NONE); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_InitRngRBGC(&root, &root, WC_RNG_INIT_FLAGS_NONE); + api_ret = wc_InitRngRBGC(&root, &root, WC_RNG_INIT_FLAG_NONE); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); #ifndef WC_NO_CONSTRUCTORS - api_ret = wc_InitRngRBGC_New(NULL, &root, WC_RNG_INIT_FLAGS_NONE); + api_ret = wc_InitRngRBGC_New(NULL, &root, WC_RNG_INIT_FLAG_NONE); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); #endif @@ -30086,7 +30086,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } RNG_STATS_SNAP(&root); - api_ret = wc_InitRngRBGC(&leaf, &root, WC_RNG_INIT_FLAGS_NONE); + api_ret = wc_InitRngRBGC(&leaf, &root, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; @@ -30204,7 +30204,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); /* long-chained init is allowed; chained reseeds are governed by the * no-downgrade rule probed below. */ - ret = wc_InitRngRBGC_New(&pleaf, &extra, WC_RNG_INIT_FLAGS_NONE); + ret = wc_InitRngRBGC_New(&pleaf, &extra, WC_RNG_INIT_FLAG_NONE); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if ((pleaf == NULL) || (wc_RNG_DRBG_GetRBGCStratum(pleaf) != 2)) @@ -30297,7 +30297,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) } /* heap-allocated leaves, without and with a nonce */ - api_ret = wc_InitRngRBGC_New(&pleaf, &root, WC_RNG_INIT_FLAGS_NONE); + api_ret = wc_InitRngRBGC_New(&pleaf, &root, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if ((pleaf == NULL) || (wc_RNG_DRBG_GetRBGCStratum(pleaf) != 1)) @@ -30308,7 +30308,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) wc_rng_free(pleaf); pleaf = NULL; api_ret = wc_InitRngNonceRBGC_New(&pleaf, &root, matter, 16, - NULL, 0, WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if ((pleaf == NULL) || (wc_RNG_DRBG_GetRBGCStratum(pleaf) != 1)) @@ -30323,7 +30323,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_InitRngNonceRBGC(&leaf, &root, matter, 16, - NULL, 0, WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; @@ -30390,17 +30390,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) present = wc_RNG_DRBG_Present(&root); /* spawn argument contracts */ - if (wc_InitRngRBGC(NULL, &root, WC_RNG_INIT_FLAGS_NONE) != + if (wc_InitRngRBGC(NULL, &root, WC_RNG_INIT_FLAG_NONE) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_InitRngRBGC(&leaf, NULL, WC_RNG_INIT_FLAGS_NONE) != + if (wc_InitRngRBGC(&leaf, NULL, WC_RNG_INIT_FLAG_NONE) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - if (wc_InitRngRBGC(&root, &root, WC_RNG_INIT_FLAGS_NONE) != + if (wc_InitRngRBGC(&root, &root, WC_RNG_INIT_FLAG_NONE) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #ifndef WC_NO_CONSTRUCTORS - if (wc_InitRngRBGC_New(NULL, &root, WC_RNG_INIT_FLAGS_NONE) != + if (wc_InitRngRBGC_New(NULL, &root, WC_RNG_INIT_FLAG_NONE) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif @@ -30411,7 +30411,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } - api_ret = wc_InitRngRBGC(&leaf, &root, WC_RNG_INIT_FLAGS_NONE); + api_ret = wc_InitRngRBGC(&leaf, &root, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; @@ -30468,7 +30468,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); /* long-chained init is allowed, only chained reseed is forbidden. */ - ret = wc_InitRngRBGC_New(&pleaf, &extra, WC_RNG_INIT_FLAGS_NONE); + ret = wc_InitRngRBGC_New(&pleaf, &extra, WC_RNG_INIT_FLAG_NONE); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (pleaf == NULL) @@ -30478,7 +30478,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) } /* heap-allocated leaves, without and with a nonce */ - api_ret = wc_InitRngRBGC_New(&pleaf, &root, WC_RNG_INIT_FLAGS_NONE); + api_ret = wc_InitRngRBGC_New(&pleaf, &root, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if (pleaf == NULL) @@ -30489,7 +30489,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) wc_rng_free(pleaf); pleaf = NULL; api_ret = wc_InitRngNonceRBGC_New(&pleaf, &root, matter, 16, - NULL, 0, WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); if (pleaf == NULL) @@ -30504,7 +30504,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_InitRngNonceRBGC(&leaf, &root, matter, 16, - WC_RNG_INIT_FLAGS_NONE); + WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; @@ -30852,7 +30852,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) XMEMSET(frag64, 0x5e, sizeof(frag64)); api_ret = wc_InitRngNonceRBGC(&leaf, root, NULL, 0, - NULL, 0, WC_RNG_INIT_FLAGS_NONE); + NULL, 0, WC_RNG_INIT_FLAG_NONE); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 2b5b2ef015b..bf58ff09d8d 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -723,16 +723,16 @@ WOLFSSL_ABI WOLFSSL_API int wc_InitRng(WC_RNG* rng); WOLFSSL_API int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId); WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz); -#define WC_RNG_INIT_FLAGS_NONE 0 -#define WC_RNG_INIT_FLAGS_LOCK_REQUIRED (1U << 0) -#define WC_RNG_INIT_FLAGS_LOCK_INITIALLY (1U << 1) -#define WC_RNG_INIT_FLAGS_USE_FULL_MUTEX (1U << 2) +#define WC_RNG_INIT_FLAG_NONE 0 +#define WC_RNG_INIT_FLAG_LOCK_REQUIRED (1U << 0) +#define WC_RNG_INIT_FLAG_LOCK_INITIALLY (1U << 1) +#define WC_RNG_INIT_FLAG_USE_FULL_MUTEX (1U << 2) /* At each generate, if a banked next seed is READY, consume it when the * instance is flagged _ENTROPY_INVALIDATED (recovery; any provenance), or * when the instance is chain-backed and the banked seed is primary * (promotion). For externally-refreshed long-lived RNGs, e.g. the kernel * module's registered RBGC leaves. */ -#define WC_RNG_INIT_FLAGS_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3) +#define WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3) WOLFSSL_API int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags); @@ -918,7 +918,7 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng); const byte *perso, word32 persoSz, word32 flags); #ifndef WC_NO_CONSTRUCTORS - /* Flags are per-object (WC_RNG_INIT_FLAGS_*), deliberately not + /* Flags are per-object (WC_RNG_INIT_FLAG_*), deliberately not * inherited from the parent: a child's lock policy is its own. */ WOLFSSL_API int wc_InitRngRBGC_New(WC_RNG** child, WC_RNG* parent, word32 flags); From e4d25f8ee5f7c6a3f69bccae7abefb6c59108986 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 15:13:36 -0500 Subject: [PATCH 088/102] linuxkm/lkcapi_sha_glue.c: fix unused-value warning around can_sleep in linuxkm_InitRng_DefaultRBGC(). --- linuxkm/lkcapi_sha_glue.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 857c8ced7a4..a733909455b 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3460,7 +3460,6 @@ int wc_linux_kernel_rng_is_wolfcrypt(struct crypto_rng *rng) { WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { unsigned long uncredited_nonce = random_get_entropy(); - int can_sleep = wc_linuxkm_can_block(); int ret = wc_rng_bank_spawn(NULL /* bank */, rng, (byte *)&uncredited_nonce, sizeof uncredited_nonce, NULL, 0, @@ -3481,7 +3480,7 @@ WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { /* Long-lived process-context leaves join the invalidation registry; * atomic-born leaves are excluded by rule (and are transient by * nature). */ - if (can_sleep) { + if (wc_linuxkm_can_block()) { ret = wc_linuxkm_rng_registry_add_rng(rng); if (ret != 0) (void)wc_FreeRng(rng); From f20d55572e7d274be8bc63869427e95d7788fc6c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 15:36:13 -0500 Subject: [PATCH 089/102] wolfcrypt/src/random.c: Close the invalidation epoch race on the credited nextSeed aperture, and percolate or dispose of all remaining failable results. * wc_RNG_invalidate_entropy() now purges first and latches last, making the latch a provenance marker, and latches or condemns: any error return sets DRBG_FAILED, as the latch is then down and the reseedCtr is subject to lost-update races. wc_RNG_DRBG_NextSeedNow_Nonce() releases its _CONSUMING claim by one-shot CAS; failure proves a purge crossed the consume, so it re-latches, reschedules reseed, and returns NEEDS_RECOVERY_E, with stats and stratum adoption gated behind a successful release. Hash_DRBG_Reseed() takes a new in_bracketed_consume arg exempting the caller's own claim from its invalidated-entry purges. * PoolPurge() and NextSeedPurge() return int (ALREADY_E hand-off), with results percolated at all call sites; * WARN_UNUSED_RESULT on eligible statics; * Explicit dispositions after every WC_CAS_WITH_RETRY loop (bail before side effects, percolate, or DRBG_FAILED where an abort would strand _ENTROPY_RECOVERING); * Hash_DRBG_StirGenerate() now percolates failure from wc_RNG_DRBG_GetReseedCtr() rather than ignoring it; * free_hook and wc_LockMutex results captured and percolated. --- wolfcrypt/src/random.c | 482 ++++++++++++++++++++++++++++++----------- 1 file changed, 354 insertions(+), 128 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index c32b0a9a61a..e30daf8f5ac 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -366,7 +366,7 @@ int wc_RNG_DRBG_Present(const WC_RNG* rng) } #ifdef WC_RNG_HAVE_POOL -static void PoolPurge(WC_RNG* rng); +static WARN_UNUSED_RESULT int PoolPurge(WC_RNG* rng); #endif /* Start NIST DRBG code */ @@ -447,13 +447,14 @@ typedef struct DRBG_internal DRBG_internal; #ifdef WOLFSSL_DRBG_SHA512 typedef struct DRBG_SHA512_internal DRBG_SHA512_internal; -static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed, - word32 seedSz, +static WARN_UNUSED_RESULT int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, + const byte* seed, word32 seedSz, const byte* additional, word32 additionalSz); -static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out, - word32 outSz, +static WARN_UNUSED_RESULT int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, + byte* out, word32 outSz, const byte* additional, word32 additionalSz); -static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg, +static WARN_UNUSED_RESULT int Hash512_DRBG_Instantiate( + DRBG_SHA512_internal* drbg, const byte* seed, word32 seedSz, const byte* nonce, word32 nonceSz, const byte* perso, word32 persoSz, @@ -536,7 +537,7 @@ int wc_DrbgState_MutexFree(void) return 0; } -static int LockDrbgState(void) +static WARN_UNUSED_RESULT int LockDrbgState(void) { #ifndef SINGLE_THREADED return wc_LockMutex(&drbgStateMutex); @@ -556,11 +557,12 @@ static int UnlockDrbgState(void) #endif /* !HAVE_SELFTEST && (!HAVE_FIPS || FIPS v7+) */ -static int wc_RNG_HealthTestLocal(WC_RNG* rng, int reseed, void* heap, - int devId); +static WARN_UNUSED_RESULT int wc_RNG_HealthTestLocal(WC_RNG* rng, int reseed, + void* heap, int devId); #ifdef WOLFSSL_DRBG_SHA512 -static int wc_RNG_HealthTest_SHA512_ex_internal(DRBG_SHA512_internal* drbg, +static WARN_UNUSED_RESULT int wc_RNG_HealthTest_SHA512_ex_internal( + DRBG_SHA512_internal* drbg, int reseed, const byte* nonce, word32 nonceSz, const byte* perso, word32 persoSz, const byte* seedA, word32 seedASz, @@ -580,7 +582,8 @@ static int wc_RNG_HealthTest_SHA512_ex_internal(DRBG_SHA512_internal* drbg, /* Hash Derivation Function */ /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash_df(DRBG_internal* drbg, byte* out, word32 outSz, byte type, +static WARN_UNUSED_RESULT int Hash_df(DRBG_internal* drbg, byte* out, + word32 outSz, byte type, const byte* inA, word32 inASz, const byte* inB, word32 inBSz, const byte* inC, word32 inCSz) @@ -709,7 +712,8 @@ static int Hash_df(DRBG_internal* drbg, byte* out, word32 outSz, byte type, } /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, +static WARN_UNUSED_RESULT int Hash256_DRBG_Reseed(DRBG_internal* drbg, + const byte* seed, word32 seedSz, const byte* additional, word32 additionalSz) { @@ -807,19 +811,23 @@ static int Hash256_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, * plain-store purge: * stirs carry no claims, so resurrection there is benign by the * three-no-ops doctrine.) */ -static void NextSeedPurge(wolfSSL_Atomic_Int *lenp) +static WARN_UNUSED_RESULT int NextSeedPurge(wolfSSL_Atomic_Int *lenp) { int ret; WC_ATOMIC_INT_ARG cur_len, want_len; WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(lenp, cur_len, ret) { if (cur_len == WC_DRBG_NEXT_SEED_PURGED) - return; /* already handed off to a producer's unwind. */ + return ALREADY_E; /* already handed off to a producer's unwind. */ want_len = (cur_len == WC_DRBG_NEXT_SEED_PRODUCING) ? WC_DRBG_NEXT_SEED_PURGED : WC_DRBG_NEXT_SEED_EMPTY; WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Int_CompareExchange, lenp, cur_len, want_len, ret); } WC_CAS_WITH_RETRY_END; + + /* percolate the CAS result -- if the loop was aborted by user logic, the + * caller needs to know that the purge failed. */ + return ret; } /* Release a producer claim (PRODUCING) on the credited aperture, @@ -846,8 +854,17 @@ static int NextSeedProducerRelease(wolfSSL_Atomic_Int *lenp, } #endif /* WC_RNG_HAVE_NEXT_SEED */ -static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, - const byte* additional, word32 additionalSz) +/* in_bracketed_consume: nonzero when the caller feeds this reseed from its own + * _CONSUMING claim on the credited aperture (wc_RNG_DRBG_NextSeedNow_Nonce()). + * The invalidated-entry purges below must then exempt that aperture: purging + * it would steal the caller's claim and defeat its release CAS, converting + * the caller's own recovery into a false epoch violation. An external + * (event) purge still repaints the claim, which is exactly what the + * caller's release CAS exists to detect. */ +static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, + word32 seedSz, + const byte* additional, word32 additionalSz, + int in_bracketed_consume) { int ret; #ifdef WC_RNG_HAVE_LOCK @@ -877,6 +894,8 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, WC_CAS_WITH_RETRY_BEGIN(&rng->lock, cur_lock, ret) { if (! (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { + /* Not invalidated -- no recovery mutex needed. Explicit + * success: BEGIN initializes ret to a failure code. */ ret = 0; break; } @@ -892,6 +911,13 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, /* We now have the _RECOVERING mutex -- record that fact. */ cur_lock |= WC_RNG_LOCK_ENTROPY_RECOVERING; } WC_CAS_WITH_RETRY_END; + if (ret != 0) { + /* Aborted acquire (a port's retry clause): the lock word is + * untouched and no state has moved -- bail before the purges, + * which must not run unserialized against a concurrent + * recovery. */ + return ret; + } #endif /* WC_RNG_HAVE_LOCK */ #ifdef WC_RNG_HAVE_POOL @@ -909,7 +935,12 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) #endif { - PoolPurge(rng); + ret = PoolPurge(rng); + if ((ret != 0) && + (ret != WC_NO_ERR_TRACE(ALREADY_E))) + { + goto out; + } } #endif /* WC_RNG_HAVE_POOL */ @@ -930,8 +961,15 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, } #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_NEXT_SEED) - if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { - NextSeedPurge(&drbg->nextSeedLen); + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && + (! in_bracketed_consume)) + { + ret = NextSeedPurge(&drbg->nextSeedLen); + if ((ret != 0) && + (ret != WC_NO_ERR_TRACE(ALREADY_E))) + { + goto out; + } /* the uncredited stir aperture is purged too, for provenance * uniformity; best-effort (an in-flight depositor may * resurrect a partial fill -- benign, stirs carry no @@ -941,6 +979,8 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, WOLFSSL_ATOMIC_STORE(drbg->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } +#else + (void)in_bracketed_consume; #endif ret = Hash256_DRBG_Reseed(drbg, seed, seedSz, additional, additionalSz); @@ -971,8 +1011,15 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, } #if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_NEXT_SEED) - if (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) { - NextSeedPurge(&drbg512->nextSeedLen); + if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && + (! in_bracketed_consume)) + { + ret = NextSeedPurge(&drbg512->nextSeedLen); + if ((ret != 0) && + (ret != WC_NO_ERR_TRACE(ALREADY_E))) + { + goto out; + } /* see the SHA-256 arm re best-effort and no-zeroize. */ WOLFSSL_ATOMIC_STORE(drbg512->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); @@ -1026,6 +1073,16 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, WC_RNG_LOCK_ENTROPY_RECOVERING), ret); } WC_CAS_WITH_RETRY_END; + if ((ret != 0) && (ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E))) { + /* Aborted clear (a port's retry clause) strands _RECOVERING with no + * in-band path back: every future recovery would refuse BUSY_E. + * Condemn -- in kernel, DRBG_FAILED has an automated exit (the + * entropy daemon's recovery pass), and the owner can always reinit. + * A wedged mutex bit would have no visible indication that reinit + * is required. + */ + rng->status = DRBG_FAILED; + } } else if (cur_lock & WC_RNG_LOCK_ENTROPY_RECOVERING) { /* ret carries the reseed's status here and must survive, so the @@ -1036,6 +1093,13 @@ static int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, wolfSSL_Atomic_Uint_CompareExchange, &rng->lock, cur_lock, cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING, release_ret); } WC_CAS_WITH_RETRY_END; + if (release_ret != 0) { + /* Same stranded-_RECOVERING condemnation as the clear arm + * above; the reseed's own status still wins the return. */ + rng->status = DRBG_FAILED; + if (ret == 0) + ret = release_ret; + } } #endif /* WC_RNG_HAVE_LOCK */ @@ -1074,7 +1138,8 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, } #endif /* WC_RNG_HAVE_LOCK */ - ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz); + ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, + 0 /* in_bracketed_consume */); #ifdef WC_RNG_HAVE_RBGC if (ret == 0) { /* User-supplied entropy is of unknown provenance. In RBGC builds, @@ -1211,7 +1276,8 @@ static WC_INLINE void array_add_one(byte* data, word32 dataSz) #ifndef NO_SHA256 /* re-open SHA-256 Hash_DRBG core */ /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash_gen(DRBG_internal* drbg, byte* out, word32 outSz, const byte* V) +static WARN_UNUSED_RESULT int Hash_gen(DRBG_internal* drbg, byte* out, + word32 outSz, const byte* V) { int ret = WC_NO_ERR_TRACE(DRBG_FAILURE); word32 i; @@ -1349,7 +1415,8 @@ static WC_INLINE void array_add(byte* d, word32 dLen, const byte* s, word32 sLen #ifndef NO_SHA256 /* re-open SHA-256 Hash_DRBG core */ /* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */ -static int Hash_DRBG_Generate(DRBG_internal* drbg, byte* out, word32 outSz, +static WARN_UNUSED_RESULT int Hash_DRBG_Generate(DRBG_internal* drbg, + byte* out, word32 outSz, const byte* additional, word32 additionalSz) { int ret; @@ -1529,7 +1596,8 @@ static int Hash_DRBG_Generate(DRBG_internal* drbg, byte* out, word32 outSz, } /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash_DRBG_Init(DRBG_internal* drbg, const byte* seed, word32 seedSz, +static WARN_UNUSED_RESULT int Hash_DRBG_Init(DRBG_internal* drbg, + const byte* seed, word32 seedSz, const byte* nonce, word32 nonceSz, const byte* perso, word32 persoSz) { @@ -1552,10 +1620,11 @@ static int Hash_DRBG_Init(DRBG_internal* drbg, const byte* seed, word32 seedSz, } /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash_DRBG_Instantiate(DRBG_internal* drbg, const byte* seed, - word32 seedSz, const byte* nonce, - word32 nonceSz, const byte* perso, - word32 persoSz, void* heap, int devId) +static WARN_UNUSED_RESULT int Hash_DRBG_Instantiate(DRBG_internal* drbg, + const byte* seed, word32 seedSz, + const byte* nonce, word32 nonceSz, + const byte* perso, word32 persoSz, + void* heap, int devId) { int ret = WC_NO_ERR_TRACE(DRBG_FAILURE); @@ -1628,7 +1697,8 @@ static int Hash_DRBG_Uninstantiate(DRBG_internal* drbg) /* Hash Derivation Function using SHA-512 */ /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, +static WARN_UNUSED_RESULT int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, + word32 outSz, byte type, const byte* inA, word32 inASz, const byte* inB, word32 inBSz, @@ -1764,8 +1834,8 @@ static int Hash512_df(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, } /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed, - word32 seedSz, +static WARN_UNUSED_RESULT int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, + const byte* seed, word32 seedSz, const byte* additional, word32 additionalSz) { int ret; @@ -1814,8 +1884,9 @@ static int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* seed, } /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, - const byte* V) +static WARN_UNUSED_RESULT int Hash512_gen(DRBG_SHA512_internal* drbg, + byte* out, word32 outSz, + const byte* V) { int ret = WC_NO_ERR_TRACE(DRBG_FAILURE); word32 i; @@ -1912,8 +1983,8 @@ static int Hash512_gen(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, } /* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */ -static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out, - word32 outSz, +static WARN_UNUSED_RESULT int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, + byte* out, word32 outSz, const byte* additional, word32 additionalSz) { int ret; @@ -2042,8 +2113,9 @@ static int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out, } /* 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, +static WARN_UNUSED_RESULT 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) @@ -2065,7 +2137,8 @@ static int Hash512_DRBG_Init(DRBG_SHA512_internal* drbg, const byte* seed, } /* Returns: DRBG_SUCCESS or DRBG_FAILURE */ -static int Hash512_DRBG_Instantiate(DRBG_SHA512_internal* drbg, +static WARN_UNUSED_RESULT int Hash512_DRBG_Instantiate( + DRBG_SHA512_internal* drbg, const byte* seed, word32 seedSz, const byte* nonce, word32 nonceSz, const byte* perso, word32 persoSz, @@ -2134,7 +2207,9 @@ static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg) * transition. Refused with NOT_READY_E when the instance is quarantined or due * for a credited reseed: a generate must not run past the reseed interval. */ -static int Hash_DRBG_StirGenerate(WC_RNG* rng, const byte* add, word32 addSz) +static WARN_UNUSED_RESULT int Hash_DRBG_StirGenerate(WC_RNG* rng, + const byte* add, + word32 addSz) { wc_drbg_reseed_ctr_t ctr = 0; int ret; @@ -2147,10 +2222,11 @@ static int Hash_DRBG_StirGenerate(WC_RNG* rng, const byte* add, word32 addSz) if (WOLFSSL_ATOMIC_LOAD(rng->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED) return NOT_READY_E; #endif - if (wc_RNG_DRBG_GetReseedCtr(rng, &ctr) == 0) { - if (ctr >= WC_RESEED_INTERVAL) - return NOT_READY_E; - } + ret = wc_RNG_DRBG_GetReseedCtr(rng, &ctr); + if (ret != 0) + return ret; + if (ctr >= WC_RESEED_INTERVAL) + return NOT_READY_E; ret = RNG_FAILURE_E; #ifndef NO_SHA256 @@ -2463,7 +2539,8 @@ int wc_Sha512Drbg_IsDisabled(void) * _LOCK_INITIALLY (born held at both layers). wc_FreeRng() releases (if the * latch is held) and frees the mutex. */ -static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, +static WARN_UNUSED_RESULT int _InitRng(WC_RNG* rng, + const byte* nonce, word32 nonceSz, const byte *perso, word32 persoSz, void* heap, int devId, WC_RNG* seedRng, word32 flags) { @@ -2792,7 +2869,8 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #ifdef WOLFSSL_SMALL_STACK if (ret == 0) { - WC_ALLOC_VAR_EX(seed, byte, MAX_SEED_SZ, rng->heap, DYNAMIC_TYPE_SEED, WC_DO_NOTHING); + WC_ALLOC_VAR_EX(seed, byte, MAX_SEED_SZ, rng->heap, DYNAMIC_TYPE_SEED, + WC_DO_NOTHING); if (seed == NULL) { ret = MEMORY_E; rng->status = DRBG_FAILED; @@ -2952,18 +3030,13 @@ static int _InitRng(WC_RNG* rng, const byte* nonce, word32 nonceSz, #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX /* deliberately the last init step: no failure path can strand an * initialized mutex. */ - if (wc_InitMutex(&rng->mutex) != 0) { - /* fall through to the common cleanup below rather than - * returning here: the DRBG is instantiated by this point, and - * V and C must not survive a failed _InitRng(). */ - ret = BAD_MUTEX_E; - } - else { + ret = wc_InitMutex(&rng->mutex); + if (ret == 0) { rng->flags |= WC_RNG_FLAG_FULL_MUTEX; if (flags & WC_RNG_INIT_FLAG_LOCK_INITIALLY) { /* born held at both layers: the constructor's caller holds * the whole latch, mutex included. */ - (void)wc_LockMutex(&rng->mutex); + ret = wc_LockMutex(&rng->mutex); } } #endif @@ -3127,7 +3200,7 @@ int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz, #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) -static int PollAndReSeed(WC_RNG* rng, const byte* additional, +static WARN_UNUSED_RESULT int PollAndReSeed(WC_RNG* rng, const byte* additional, word32 additionalSz); #endif @@ -3136,38 +3209,59 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, * the RNG is freshly seeded after a fork(), to avoid seeding or generating from * duplicated internal state. */ -static WC_MAYBE_UNUSED int rng_pid_change_check(WC_RNG* rng) { - int ret; +static WARN_UNUSED_RESULT WC_MAYBE_UNUSED int rng_pid_change_check(WC_RNG* rng) { + int ret = 0; int my_pid = getpid(); if (rng->pid == my_pid) return 0; rng->pid = my_pid; + #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) ret = PollAndReSeed(rng, NULL, 0); if (ret != DRBG_SUCCESS) { rng->status = DRBG_FAILED; ret = RNG_FAILURE_E; } -#else - ret = 0; #endif #ifdef WC_RNG_HAVE_POOL - PoolPurge(rng); + { + int ret2 = PoolPurge(rng); + if ((ret == 0) && + (ret2 != 0) && + (ret2 != WC_NO_ERR_TRACE(ALREADY_E))) + { + ret = ret2; + } + } #endif + #ifdef WC_RNG_HAVE_NEXT_SEED #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); + int ret2 = NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); + if ((ret == 0) && + (ret2 != 0) && + (ret2 != WC_NO_ERR_TRACE(ALREADY_E))) + { + ret = ret2; + } WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { - NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); + int ret2 = + NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); + if ((ret == 0) && + (ret2 != 0) && + (ret2 != WC_NO_ERR_TRACE(ALREADY_E))) + { + ret = ret2; + } WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } @@ -3244,12 +3338,12 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) ++rng->_stats_locks_taken; #endif - /* If we arrived here via _RECOVER_AND_PROMOTE_FROM_NEXT_SEED with a pending - * NextSeed, there is a finite though minuscule chance that a second - * invalidation left the RNG without a banked seed to consume. In that - * case, the holder's generate falls through to the regular inline - * forced-reseed machinery. This is the same outcome as an invalidation - * landing immediately after a successful acquire. + /* If we arrived here via _RECOVER_AND_PROMOTE_FROM_NEXT_SEED with a + * pending NextSeed, there is a finite though minuscule chance that a + * second invalidation left the RNG without a banked seed to consume. + * In that case, the holder's generate falls through to the regular + * inline forced-reseed machinery. This is the same outcome as an + * invalidation landing immediately after a successful acquire. */ return 0; @@ -3495,7 +3589,9 @@ int wc_RNG_lock_set_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, &rng->lock, cur_lock, new_lock, cas_ret); } WC_CAS_WITH_RETRY_END; - return 0; + /* 0 unless a port's retry clause aborted; the lock word is then + * untouched, so percolation is the whole handling. */ + return cas_ret; } int wc_RNG_lock_add_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) @@ -3516,7 +3612,8 @@ int wc_RNG_lock_add_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) &rng->lock, cur_lock, cur_lock | extra_bits, cas_ret); } WC_CAS_WITH_RETRY_END; - return 0; + /* see wc_RNG_lock_set_extra() re nonzero cas_ret. */ + return cas_ret; } int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) @@ -3538,25 +3635,18 @@ int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) &rng->lock, cur_lock, cur_lock & ~extra_bits, cas_ret); } WC_CAS_WITH_RETRY_END; - return 0; + /* see wc_RNG_lock_set_extra() re nonzero cas_ret. */ + return cas_ret; } #ifdef HAVE_HASHDRBG WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { WC_RNG_lock_arg_t cur_lock; - int cas_ret; + int ret; if (rng == NULL) return BAD_FUNC_ARG; - WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->lock, cur_lock, cas_ret) { - WC_CAS_WITH_RETRY_LOOP_FOREVER( - wolfSSL_Atomic_Uint_CompareExchange, &rng->lock, cur_lock, - (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING) | - WC_RNG_LOCK_ENTROPY_INVALIDATED, - cas_ret); - } WC_CAS_WITH_RETRY_END; - /* If no lock is held, either the RNG is in use without a lock, in which * case the reseedCtr is the only way to force invalidation semantics on the * user, or it is not in use at all and scheduling a reseed is harmless. @@ -3570,32 +3660,88 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { * concurrent recovery attempts, in Hash_DRBG_Reseed() (the sole recovery * path from _ENTROPY_INVALIDATED). */ - if (! (cur_lock & WC_RNG_LOCK_HELD)) - (void)wc_RNG_DRBG_ScheduleReseed(rng); + ret = wc_RNG_DRBG_ScheduleReseed(rng); + #ifdef WC_RNG_HAVE_POOL - PoolPurge(rng); + { + int ret2 = PoolPurge(rng); + if ((ret2 != 0) && (ret == 0)) + ret = ret2; + } #endif #ifdef WC_RNG_HAVE_NEXT_SEED #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); + int ret2 = NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); + if ((ret2 != 0) && + (ret2 != WC_NO_ERR_TRACE(ALREADY_E)) && + (ret == 0)) + { + ret = ret2; + } WOLFSSL_ATOMIC_STORE(((DRBG_internal *)rng->drbg)->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { - NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); + int ret2 = + NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); + if ((ret2 != 0) && + (ret2 != WC_NO_ERR_TRACE(ALREADY_E)) && + (ret == 0)) + { + ret = ret2; + } WOLFSSL_ATOMIC_STORE(((DRBG_SHA512_internal *)rng->drbg512)->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } #endif #endif /* WC_RNG_HAVE_NEXT_SEED */ - return 0; + /* Assert _ENTROPY_INVALIDATED last: latch-after-purge makes the latch a + * provenance marker. Any consumer that observes the latch observes + * post-purge apertures, so a READY it then claims necessarily postdates + * this event; a consumer that raced ahead of the latch loses its claim + * to the purge above and is refused at its release CAS (see + * wc_RNG_DRBG_NextSeedNow_Nonce()); and an event landing mid-reseed + * strips _ENTROPY_RECOVERING here, so the recovery's exit CAS refuses + * the clear. No interleaving recovers from pre-event material. */ + + { + int cas_ret; + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->lock, cur_lock, cas_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER( + wolfSSL_Atomic_Uint_CompareExchange, &rng->lock, cur_lock, + (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING) | + WC_RNG_LOCK_ENTROPY_INVALIDATED, + cas_ret); + } WC_CAS_WITH_RETRY_END; + if ((cas_ret != 0) && (ret == 0)) + ret = cas_ret; + } + + /* Postcondition: latch or condemn. Zero return means _INVALIDATED is + * asserted; any nonzero return leaves the latch down (it is asserted last, + * above), so the instance is quarantined by counter saturation alone -- and + * the reseedCtr is subject to lost-update races. DRBG_FAILED is the + * remaining stop no interleaving can lift: sticky until a full recovery. + * + * This is the one leaseless outsider write of DRBG_FAILED; the race against + * a lease-holder is benign both ways (a completing reseed's OK overwrite + * means the state was genuinely re-derived from post-event material; a + * FAILED overwrite of OK costs one spurious reinit, never unsound output). + * In the kernel build, bank instances are retired and recovered by the + * entropy daemon's out-of-service pass; a leaf's owner sees hard + * RNG_FAILURE_E and reinitializes. + */ + if (ret != 0) + rng->status = DRBG_FAILED; + + return ret; } -#endif /* HAVE_HASHDRBG */ +#endif /* HAVE_HASHDRBG */ #endif /* WC_RNG_HAVE_LOCK */ #ifdef WC_RNG_HAVE_FREE_HOOK @@ -3673,11 +3819,11 @@ WOLFSSL_API int wc_RNG_register_free_hook(WC_RNG* rng, * * PoolPurge() bumps epoch and touches nothing else. It does not reset the * counters: leaving them monotonic keeps the reader's burn span [tail, - * tail+m) and the writer's generate span [head, head+m') disjoint across the - * event, so a purge can never cause one to erase the other's bytes. Stale - * pre-purge material is discarded by the reader instead, which resynchronizes - * tail to head on any epoch change -- whether it observed the purge mid-serve - * or merely arrives afterwards. + * tail+m) and the writer's generate span [head, head+m') disjoint across + * the event, so a purge can never cause one to erase the other's bytes. + * Stale pre-purge material is discarded by the reader instead, which + * resynchronizes tail to head on any epoch change -- whether it observed + * the purge mid-serve or merely arrives afterwards. * * The writer still CASes, and its publication carries the epoch it read. A * purge during its generate makes that CAS fail, and it abandons rather @@ -3730,17 +3876,19 @@ static WC_INLINE word32 PoolUsed(word32 head, word32 tail, word32 poolSize) * served -- state invalidation, fork, a credited reseed, the reader's * fail-closed path. Bumping epoch is the whole operation; see above for why * the counters are deliberately left alone. */ -static void PoolPurge(WC_RNG* rng) +static WARN_UNUSED_RESULT int PoolPurge(WC_RNG* rng) { WC_ATOMIC_UINT_ARG cur, want; - int cas_ret; + int ret; - WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->poolHead, cur, cas_ret) { + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->poolHead, cur, ret) { want = WC_RNG_POOL_PACK(WC_RNG_POOL_POS(cur), WC_RNG_POOL_EPOCH(cur) + 1U); WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Uint_CompareExchange, - &rng->poolHead, cur, want, cas_ret); + &rng->poolHead, cur, want, ret); } WC_CAS_WITH_RETRY_END; + + return ret; } int wc_RNG_Pool_Alloc(WC_RNG* rng, word32 size) @@ -3829,8 +3977,9 @@ int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, word32 n) /* generate directly into the unpublished span (up to two contiguous * segments), then publish the whole of it with one CAS. */ while (done < m) { - word32 at = PoolAt(PoolAdvance(head, done, (word32)rng_dest->poolSize), - (word32)rng_dest->poolSize); + word32 at = + PoolAt(PoolAdvance(head, done, (word32)rng_dest->poolSize), + (word32)rng_dest->poolSize); word32 chunk = (word32)rng_dest->poolSize - at; if (chunk > m - done) chunk = m - done; @@ -3894,8 +4043,11 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) * its pooled output is unusable material at rest. A writer mid-fill sees * the epoch move and abandons rather than publishing. */ if (wc_RNG_DRBG_Present(rng) && (rng->status != DRBG_OK)) { - PoolPurge(rng); - return RNG_FAILURE_E; + int ret = PoolPurge(rng); + if (ret != 0) + return ret; + else + return RNG_FAILURE_E; } tw = WOLFSSL_ATOMIC_LOAD(rng->poolTail); @@ -4012,7 +4164,8 @@ int wc_RNG_Pool_Current(WC_RNG* rng, word32* n) * to parent for the duration of the call, as for all WC_RNG operations; the * spawn debits parent's reseed counter by one generate. */ -static int SpawnRngRBGC(WC_RNG* new_child_stack, WC_RNG** new_child_heap, +static WARN_UNUSED_RESULT int SpawnRngRBGC( + WC_RNG* new_child_stack, WC_RNG** new_child_heap, WC_RNG* parent, const byte* nonce, word32 nonceSz, const byte *perso, word32 persoSz, word32 flags) @@ -4102,7 +4255,8 @@ int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, * Uncredited reseeds by wc_RNG_DRBG_ReseedRBGC_local() are unconditionally * permitted (these are just stirs). */ -static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, +static WARN_UNUSED_RESULT int wc_RNG_DRBG_ReseedRBGC_local( + WC_RNG* rng, WC_RNG* root, const byte* nonce, word32 nonceSz, int credited) { @@ -4162,7 +4316,8 @@ static int wc_RNG_DRBG_ReseedRBGC_local(WC_RNG* rng, WC_RNG* root, ret = wc_RNG_GenerateBlock(root, seed, SEED_SZ); if (ret == 0) { if (credited) { - ret = Hash_DRBG_Reseed(rng, seed, SEED_SZ, nonce, nonceSz); + ret = Hash_DRBG_Reseed(rng, seed, SEED_SZ, nonce, nonceSz, + 0 /* in_bracketed_consume */); if (ret == 0) { rng->RBGCStratum = root->RBGCStratum + 1; #ifdef WC_RNG_DEBUG_STATS @@ -4196,7 +4351,7 @@ int wc_RNG_DRBG_StirRBGC(WC_RNG* rng, WC_RNG* root, #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) -static int PollAndReSeed(WC_RNG* rng, const byte* additional, +static WARN_UNUSED_RESULT int PollAndReSeed(WC_RNG* rng, const byte* additional, word32 additionalSz) { int ret = WC_NO_ERR_TRACE(DRBG_NEED_RESEED); @@ -4262,7 +4417,8 @@ static int PollAndReSeed(WC_RNG* rng, const byte* additional, } if (ret == DRBG_SUCCESS) { ret = Hash_DRBG_Reseed(rng, newSeed + SEED_BLOCK_SZ, SEED_SZ, - additional, additionalSz); + additional, additionalSz, + 0 /* in_bracketed_consume */); #ifdef WC_RNG_HAVE_RBGC if (ret == 0) @@ -4386,7 +4542,8 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) /* Locate the aperture members for rng's live DRBG. Returns nonzero when no * DRBG is instantiated (RDRAND et al.). */ -static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, +static WARN_UNUSED_RESULT WC_INLINE int NextSeedPtrs(WC_RNG* rng, + byte** seed, word32 *nextSeedSz, wolfSSL_Atomic_Int** len, int **nextSeedRBGCStratum) { @@ -4426,7 +4583,7 @@ static WC_INLINE int NextSeedPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, return MISSING_RNG_E; } -static WC_INLINE int NextStirPtrs(WC_RNG* rng, byte** seed, +static WARN_UNUSED_RESULT WC_INLINE int NextStirPtrs(WC_RNG* rng, byte** seed, word32 *nextSeedSz, wolfSSL_Atomic_Int** len) { @@ -4460,7 +4617,8 @@ static WC_INLINE int NextStirPtrs(WC_RNG* rng, byte** seed, * published; a failed test consumes the material (use-once) and returns the * test's error, leaving an empty bank for the next cycle. A gather failure * leaves the partial bank intact for retry. */ -static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, +static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( + WC_RNG* rng, WC_RNG *root, const byte *nonce, word32 n) { int claim_ret; @@ -4558,6 +4716,13 @@ static int wc_RNG_DRBG_NextSeedGenerate_local(WC_RNG* rng, WC_RNG *root, WC_DRBG_NEXT_SEED_PRODUCING, claim_ret); } WC_CAS_WITH_RETRY_END; + if (claim_ret != 0) { + /* Aborted claim (a port's retry clause): nothing claimed, + * nothing moved. Proceeding would fill and adjudicate an + * unclaimed aperture -- the torn-claims disease the + * producer mutex exists to prevent. */ + return claim_ret; + } if (cur == (WC_ATOMIC_INT_ARG)nextSeedSz) { /* Complete but unpublished (interrupted between fill * completion and publication): retry the health test and @@ -4856,30 +5021,89 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, /* Identical byte accounting to PollAndReSeed(): the SEED_BLOCK_SZ * prefix was consumed by the bank-time health testing. */ - ret = Hash_DRBG_Reseed(rng, seed + SEED_BLOCK_SZ, SEED_SZ, nonce, nonceSz); + ret = Hash_DRBG_Reseed(rng, seed + SEED_BLOCK_SZ, SEED_SZ, nonce, nonceSz, + 1 /* in_bracketed_consume */); + /* Use-once: consumed by the attempt, success or not. The scrub must be + * visible before the aperture reopens. */ + ForceZero(seed, WC_DRBG_NEXT_SEED_LEN); + + /* Release by CAS -- one-shot, and its failure is information, not + * contention: only NextSeedPurge() writes over a _CONSUMING claim, so a + * failed release proves an invalidation event landed after this consume + * claimed the material, i.e. the seed just fed to the reseed was banked + * pre-event. The reseed's own latch handling could not see that (an epoch + * crossing shows only at the aperture's claim word, never in the lock + * word), and if it entered invalidated and ran undisturbed it has already + * cleared the latch -- so compensate: re-latch, and re-saturate the counter + * (the stale credited reseed reset it, leaving the latch as sole + * enforcement; wc_RNG_DRBG_ScheduleReseed() restores the second layer -- + * race-free here, under the exclusive lease). Claims (stats, stratum + * adoption) are made only behind a successful release. Do NOT re-run + * wc_RNG_invalidate_entropy() here: the event's purges already ran, and + * re-purging would discard post-event material the banker may have + * re-banked meanwhile. */ + { + int cas_ret; + WC_ATOMIC_INT_ARG expected_out = WC_DRBG_NEXT_SEED_CONSUMING; + WC_CAS_WITH_RETRY_BEGIN(lenp, expected_out, cas_ret) { + WC_CAS_WITH_RETRY_LOOP_UNTIL(wolfSSL_Atomic_Int_CompareExchange, + lenp, expected_out, + WC_DRBG_NEXT_SEED_EMPTY, cas_ret, + NEEDS_RECOVERY_E); + } WC_CAS_WITH_RETRY_END; + if (cas_ret != 0) { + #ifdef WC_RNG_HAVE_LOCK + { + WC_RNG_lock_arg_t cur_lock; + int relatch_ret; + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->lock, cur_lock, + relatch_ret) { + WC_CAS_WITH_RETRY_LOOP_FOREVER( + wolfSSL_Atomic_Uint_CompareExchange, &rng->lock, + cur_lock, + cur_lock | WC_RNG_LOCK_ENTROPY_INVALIDATED, + relatch_ret); + } WC_CAS_WITH_RETRY_END; + if (relatch_ret != 0) { + /* Aborted re-latch: latch down, counter scheduled. + * Latch-or-condemn (see wc_RNG_invalidate_entropy()): + * condemn. */ + rng->status = DRBG_FAILED; + } + } + #endif + { + int sched_ret = wc_RNG_DRBG_ScheduleReseed(rng); + if ((ret == DRBG_SUCCESS) && (sched_ret != 0)) + return sched_ret; + } + if (ret == DRBG_SUCCESS) { + /* The discarded recovery is the whole story. */ + return cas_ret; + } + /* Else the reseed's own failure is the more informative code: + * fall through to the standard outcome mapping. */ + } + else { #ifdef WC_RNG_DEBUG_STATS - if (ret == 0) { + if (ret == 0) { #ifdef WC_RNG_HAVE_RBGC - if (*nextSeedRBGCStratum_p > 0) - ++rng->_stats_nextseedsRBGC_redeemed; - else + if (*nextSeedRBGCStratum_p > 0) + ++rng->_stats_nextseedsRBGC_redeemed; + else #endif - ++rng->_stats_nextseedsprimary_redeemed; - } + ++rng->_stats_nextseedsprimary_redeemed; + } #endif - #ifdef WC_RNG_HAVE_RBGC - if (ret == 0) { - rng->RBGCStratum = *nextSeedRBGCStratum_p; - *nextSeedRBGCStratum_p = 0; - } + if (ret == 0) { + rng->RBGCStratum = *nextSeedRBGCStratum_p; + *nextSeedRBGCStratum_p = 0; + } #endif - - /* Use-once: consumed by the attempt, success or not. Release store: - * the ForceZero() must be visible before the empty aperture is. */ - ForceZero(seed, WC_DRBG_NEXT_SEED_LEN); - WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); + } + } /* Identical outcome mapping to the generate-path reseed. */ if (ret == DRBG_SUCCESS) { @@ -4996,7 +5220,9 @@ int wc_RNG_DRBG_NextStirNow(WC_RNG* rng) /* place a generated block in output */ #ifdef WC_HAVE_RNG_BANKREF -static int wc_local_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) +static WARN_UNUSED_RESULT int wc_local_RNG_GenerateBlock(WC_RNG* rng, + byte* output, + word32 sz) #else WOLFSSL_ABI int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) @@ -5273,7 +5499,7 @@ int wc_FreeRng(WC_RNG* rng) * (not that they would be a good idea) can't loop. */ wc_RNG_free_hook_cb_t free_hook = rng->free_hook; rng->free_hook = NULL; - (void)free_hook(rng, rng->free_hook_arg); + ret = free_hook(rng, rng->free_hook_arg); rng->free_hook_arg = NULL; } #endif @@ -5414,7 +5640,7 @@ int wc_RNG_HealthTest(int reseed, const byte* seedA, word32 seedASz, } -static int wc_RNG_HealthTest_ex_internal(DRBG_internal* drbg, +static WARN_UNUSED_RESULT int wc_RNG_HealthTest_ex_internal(DRBG_internal* drbg, int reseed, const byte* nonce, word32 nonceSz, const byte* seedA, word32 seedASz, const byte* seedB, word32 seedBSz, From d0c48dd8f5a0af574d61a010c71121a0136ed858 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 17:01:43 -0500 Subject: [PATCH 090/102] wolfcrypt/test/test.c: old-FIPS compat def for WC_RNG_INIT_FLAG_NONE. --- wolfcrypt/test/test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 8f126680f07..95bf197c99e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30361,6 +30361,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) /* On old FIPS, WC_RNG_BANK_SUPPORT is needed for RNG-level compat shims. */ +#ifndef WC_RNG_INIT_FLAG_NONE + #define WC_RNG_INIT_FLAG_NONE 0 +#endif + WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) { wc_test_ret_t ret = 0; From a010f7b2890ca7841710d4a3100eb11d63fbf22c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 17:09:01 -0500 Subject: [PATCH 091/102] wolfcrypt/src/random.c: zeroize event-abandoned entropy under the sibling-lineage doctrine: bytes a purge abandons here may be consumed by an identical clone/fork sibling there, and consumed-elsewhere is CSP. The events that abandon banked or pooled material (VM clone/resume, fork, credited reseed) are the same events that create sibling lineages holding identical copies, so "abandoned unconsumed" cannot be decided lineage-locally at exactly the purge sites: a sibling whose interleaving differs may consume its twin of the bytes, leaving their literal reseed input or served output at rest in this lineage's memory. Such material is wiped, under ownership only. The one principled exemption: material whose rejection is deterministic on the bytes (RCT/APT health-test failures) is rejected identically in every lineage, can never have a consumed copy, and stays sentinel-only. * NextSeedPurge() takes the seed buffer and wipes it: a READY or parked-fill word is first claimed _CONSUMING (the same CAS a consumer uses; producers claim only non-negative words, so no collision), wiped as owner, and reopened EMPTY. A _CONSUMING holder's material is left to that consumer's burn-before-release, and a _PRODUCING holder's to its unwind; the _CONSUMING repaint semantics (the epoch-crossing detection) are unchanged. * NextSeedProducerRelease() wipes in its unwind arm, before the EMPTY reopen, while the producer still owns the buffer. A failed release remains non-percolating by design -- the unwind is its own compensation -- with the disposition rationale now in a comment at the first (void) call site. * New PoolWipeRetired() wipes the retired span [tail, head) at the three reader-owned sites: the visit-time epoch resync, the mid-extract linearization-point discard, and the fail-closed status arm (which now also resynchronizes tail rather than leaving it stale). Race-free by ownership: the single reader is the only mover of tail; writers write only past head. * The health-test burn arm stays sentinel-only, now with the deterministic-rejection rationale in place of the old house-rule assertion. No architectural change: the aperture claim machinery was already an ownership protocol, so "who may wipe" inherits its answer from "who may write". --- wolfcrypt/src/random.c | 227 +++++++++++++++++++++++++++++------------ 1 file changed, 160 insertions(+), 67 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index e30daf8f5ac..affb6f113b4 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -769,49 +769,37 @@ static WARN_UNUSED_RESULT int Hash256_DRBG_Reseed(DRBG_internal* drbg, #endif /* !NO_SHA256 */ -/* WC_RNG_DEBUG_STATS collection points. - * - * Placement doctrine: each counter is maintained at the single funnel that - * owns the distinction it records -- - * - reseed counts in Hash_DRBG_Reseed() (every reseed flavor routes - * through it: interval backstop, Reseed_Now, RBGC, banked redemption); - * - request/byte counts in the DRBG arm of wc_RNG_GenerateBlock() - * (hardware-offload arms -- RDRAND, Silabs, async, cryptocb, custom -- - * are deliberately uncounted: these are DRBG-facility statistics); - * - banked-seed redemption provenance in wc_RNG_DRBG_NextSeedNow_Nonce(); - * - seed health failures at the two sites that observe them per-instance - * (PollAndReSeed(), NextSeedGenerate); - * - chain-provenance bytes (RBGC_bytes_produced: output generated while - * the instance's own RBGCStratum > 0) in the same generate funnel; - * - pool byte accounting in wc_RNG_Pool_Extract(), under the consumer's - * instance lock: bytes produced by reading from the pool, and bytes - * requested but not fulfilled (empty-pool and partial-serve shortfall), - * so requested == produced + missed on the capacity paths. The - * failed-DRBG burn path deliberately counts nothing: it is a failure - * event (visible via rng->status), not a capacity signal. - * - * Counters are plain (non-atomic) adds/increments, and update under the owner's - * exclusive access (the lock contract shared by all WC_RNG operations) except - * where labeled racy: those are unreliable under concurrency, by design. Every - * site carries its own #ifdef WC_RNG_DEBUG_STATS gate so the facility is - * removable outright with unifdef. */ - #ifdef WC_RNG_HAVE_NEXT_SEED -/* Purge the credited next-seed aperture. A plain store would race an - * in-flight producer: the producer's publish must lose against a purge, - * never the reverse, or material generated before a state-invalidation - * event could surface READY after it -- exactly the resurrection the - * provenance guarantee forbids. A producer mid-fill (PRODUCING) owns - * the buffer, so the purge only repaints the sentinel (PRODUCING -> - * PURGED); the producer's failed publish-CAS observes the repaint and - * reopens the aperture EMPTY (see NextSeedProducerRelease()) -- the - * sentinel alone suppresses the pre-event material; seed-aperture - * buffers are never zeroized outside consumption. All other states - * purge directly to EMPTY. (The uncredited stir aperture keeps its - * plain-store purge: - * stirs carry no claims, so resurrection there is benign by the - * three-no-ops doctrine.) */ -static WARN_UNUSED_RESULT int NextSeedPurge(wolfSSL_Atomic_Int *lenp) +/* Purge the credited next-seed aperture. A plain store would race an in-flight + * producer: the producer's publish must lose against a purge, never the + * reverse, or material generated before a state-invalidation event could + * surface READY after it -- exactly the resurrection the provenance guarantee + * forbids. A producer mid-fill (PRODUCING) owns the buffer, so the purge only + * repaints the sentinel (PRODUCING -> PURGED); the producer's failed + * publish-CAS observes the repaint and reopens the aperture EMPTY (see + * NextSeedProducerRelease()) -- the sentinel alone suppresses the pre-event + * material; abandoned seed-aperture buffers are zeroized as explained below. + * All other states purge directly to EMPTY. (The uncredited stir aperture + * keeps its plain-store purge: stirs carry no claims, so resurrection there is + * benign by the three-no-ops doctrine.) + * + * Zeroization doctrine for purges: abandonment here is event-driven (fork, + * VM clone/resume, credited reseed), and the event that abandons bytes in + * this lineage is the same event that created a sibling lineage that may + * consume its identical copy of them. Abandoned-here can be consumed-there, + * so purged material is treated as CSP and wiped -- under ownership only: + * READY or parked-fill words are claimed _CONSUMING first (the same CAS a + * consumer uses; producers claim only non-negative words, so the claim + * cannot collide), then wiped, then reopened EMPTY. A _CONSUMING holder's + * material is left to that consumer's own burn-before-release, and a + * _PRODUCING holder's to its unwind (see NextSeedProducerRelease()). + * Contrast the health-test burn arm, which stays sentinel-only: RCT/APT + * are deterministic on the bytes, so every sibling rejects the same + * material identically and no lineage can have consumed it. + */ +static WARN_UNUSED_RESULT int NextSeedPurge(wolfSSL_Atomic_Int *lenp, + byte *seed_buf, + word32 seed_buf_sz) { int ret; WC_ATOMIC_INT_ARG cur_len, want_len; @@ -819,10 +807,21 @@ static WARN_UNUSED_RESULT int NextSeedPurge(wolfSSL_Atomic_Int *lenp) WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(lenp, cur_len, ret) { if (cur_len == WC_DRBG_NEXT_SEED_PURGED) return ALREADY_E; /* already handed off to a producer's unwind. */ - want_len = (cur_len == WC_DRBG_NEXT_SEED_PRODUCING) ? - WC_DRBG_NEXT_SEED_PURGED : WC_DRBG_NEXT_SEED_EMPTY; - WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Int_CompareExchange, - lenp, cur_len, want_len, ret); + if ((cur_len == WC_DRBG_NEXT_SEED_READY) || (cur_len > 0)) { + /* Published or parked bytes with no owner: claim, wipe as + * owner, reopen. */ + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Int_CompareExchange, + lenp, cur_len, + WC_DRBG_NEXT_SEED_CONSUMING, ret); + ForceZero(seed_buf, seed_buf_sz); + WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); + } + else { + want_len = (cur_len == WC_DRBG_NEXT_SEED_PRODUCING) ? + WC_DRBG_NEXT_SEED_PURGED : WC_DRBG_NEXT_SEED_EMPTY; + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Int_CompareExchange, + lenp, cur_len, want_len, ret); + } } WC_CAS_WITH_RETRY_END; /* percolate the CAS result -- if the loop was aborted by user logic, the @@ -839,16 +838,19 @@ static WARN_UNUSED_RESULT int NextSeedPurge(wolfSSL_Atomic_Int *lenp) * a claim). The producer's material then predates the invalidation * event and must not surface: the aperture reopens EMPTY, which is * the whole suppression -- an EMPTY aperture is never consumed, and - * the next fill overwrites from offset zero. The buffer is never - * zeroized (house rule for the seed apertures): zeroization buys - * nothing here -- suppression is the sentinel's job, and a - * fork-sibling clone holds the same bytes regardless. */ + * the next fill overwrites from offset zero. The bytes themselves are + * wiped before the reopen, while this producer still owns the buffer: + * the purge that repainted the claim marks an event that created a + * sibling lineage, and the sibling's copy of a completed fill may be + * consumed there (see the doctrine at NextSeedPurge()). */ static int NextSeedProducerRelease(wolfSSL_Atomic_Int *lenp, + byte *seed_buf, word32 seed_buf_sz, WC_ATOMIC_INT_ARG val) { WC_ATOMIC_INT_ARG expected = WC_DRBG_NEXT_SEED_PRODUCING; if (wolfSSL_Atomic_Int_CompareExchange(lenp, &expected, val)) return 0; + ForceZero(seed_buf, seed_buf_sz); WOLFSSL_ATOMIC_STORE(*lenp, WC_DRBG_NEXT_SEED_EMPTY); return BUSY_E; } @@ -964,7 +966,8 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && (! in_bracketed_consume)) { - ret = NextSeedPurge(&drbg->nextSeedLen); + ret = NextSeedPurge(&drbg->nextSeedLen, drbg->nextSeed, + (word32)sizeof(drbg->nextSeed)); if ((ret != 0) && (ret != WC_NO_ERR_TRACE(ALREADY_E))) { @@ -1014,7 +1017,8 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, if ((cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) && (! in_bracketed_consume)) { - ret = NextSeedPurge(&drbg512->nextSeedLen); + ret = NextSeedPurge(&drbg512->nextSeedLen, drbg512->nextSeed, + (word32)sizeof(drbg512->nextSeed)); if ((ret != 0) && (ret != WC_NO_ERR_TRACE(ALREADY_E))) { @@ -3241,7 +3245,9 @@ static WARN_UNUSED_RESULT WC_MAYBE_UNUSED int rng_pid_change_check(WC_RNG* rng) #ifdef WC_RNG_HAVE_NEXT_SEED #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - int ret2 = NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); + int ret2 = NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen, + ((DRBG_internal *)rng->drbg)->nextSeed, + (word32)sizeof(((DRBG_internal *)rng->drbg)->nextSeed)); if ((ret == 0) && (ret2 != 0) && (ret2 != WC_NO_ERR_TRACE(ALREADY_E))) @@ -3255,7 +3261,9 @@ static WARN_UNUSED_RESULT WC_MAYBE_UNUSED int rng_pid_change_check(WC_RNG* rng) #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { int ret2 = - NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); + NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen, + ((DRBG_SHA512_internal *)rng->drbg512)->nextSeed, + (word32)sizeof(((DRBG_SHA512_internal *)rng->drbg512)->nextSeed)); if ((ret == 0) && (ret2 != 0) && (ret2 != WC_NO_ERR_TRACE(ALREADY_E))) @@ -3672,7 +3680,9 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { #ifdef WC_RNG_HAVE_NEXT_SEED #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { - int ret2 = NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen); + int ret2 = NextSeedPurge(&((DRBG_internal *)rng->drbg)->nextSeedLen, + ((DRBG_internal *)rng->drbg)->nextSeed, + (word32)sizeof(((DRBG_internal *)rng->drbg)->nextSeed)); if ((ret2 != 0) && (ret2 != WC_NO_ERR_TRACE(ALREADY_E)) && (ret == 0)) @@ -3686,7 +3696,9 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { int ret2 = - NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen); + NextSeedPurge(&((DRBG_SHA512_internal *)rng->drbg512)->nextSeedLen, + ((DRBG_SHA512_internal *)rng->drbg512)->nextSeed, + (word32)sizeof(((DRBG_SHA512_internal *)rng->drbg512)->nextSeed)); if ((ret2 != 0) && (ret2 != WC_NO_ERR_TRACE(ALREADY_E)) && (ret == 0)) @@ -3872,6 +3884,25 @@ static WC_INLINE word32 PoolUsed(word32 head, word32 tail, word32 poolSize) return (head >= tail) ? (head - tail) : (head + (poolSize * 2U) - tail); } +/* Wipe the retired span [tail, head): pooled bytes are finished DRBG output + * at rest, and a retiring event (invalidation, fork, credited reseed) has a + * sibling lineage that may serve its identical copy -- CSP by the doctrine + * at NextSeedPurge(). Race-free by ownership: the single reader is the + * only mover of tail, and writers write only past head. */ +static WC_INLINE void PoolWipeRetired(WC_RNG *rng, word32 tail, word32 head) +{ + word32 used = PoolUsed(head, tail, (word32)rng->poolSize); + word32 t = (tail >= (word32)rng->poolSize) ? + (tail - (word32)rng->poolSize) : tail; + word32 seg = (word32)rng->poolSize - t; + if (seg > used) + seg = used; + if (seg > 0) + ForceZero(rng->pool + t, seg); + if (used > seg) + ForceZero(rng->pool, used - seg); +} + /* Retire pooled output: any event after which pre-event bytes must not be * served -- state invalidation, fork, a credited reseed, the reader's * fail-closed path. Bumping epoch is the whole operation; see above for why @@ -4046,8 +4077,15 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) int ret = PoolPurge(rng); if (ret != 0) return ret; - else - return RNG_FAILURE_E; + /* Reader-owned fail-closed wipe and resync of whatever was + * pending. */ + tw = WOLFSSL_ATOMIC_LOAD(rng->poolTail); + w1 = WOLFSSL_ATOMIC_LOAD(rng->poolHead); + PoolWipeRetired(rng, WC_RNG_POOL_POS(tw), WC_RNG_POOL_POS(w1)); + WOLFSSL_ATOMIC_STORE(rng->poolTail, + WC_RNG_POOL_PACK(WC_RNG_POOL_POS(w1), + WC_RNG_POOL_EPOCH(w1))); + return RNG_FAILURE_E; } tw = WOLFSSL_ATOMIC_LOAD(rng->poolTail); @@ -4059,6 +4097,7 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) /* A purge landed since our last visit. Everything published before * it is retired: resynchronize to head and report empty. Anything * the writer publishes after this point is post-event and stands. */ + PoolWipeRetired(rng, WC_RNG_POOL_POS(tw), head); WOLFSSL_ATOMIC_STORE(rng->poolTail, WC_RNG_POOL_PACK(head, epoch)); #ifdef WC_RNG_DEBUG_STATS rng->_stats_pool_bytes_missed += *n; @@ -4096,6 +4135,9 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) * served, so discard the whole pre-event span rather than advancing. */ w2 = WOLFSSL_ATOMIC_LOAD(rng->poolHead); if (WC_RNG_POOL_EPOCH(w2) != epoch) { + /* We own [tail, old head) exclusively; the purged span is wiped + * before it is skipped. */ + PoolWipeRetired(rng, tail, head); WOLFSSL_ATOMIC_STORE(rng->poolTail, WC_RNG_POOL_PACK(WC_RNG_POOL_POS(w2), WC_RNG_POOL_EPOCH(w2))); @@ -4757,8 +4799,23 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( /* Partial bank preserved -- retry on a later cycle. * (If a purge landed meanwhile, the release discards * instead; the draw failure is the more informative - * code and wins over the release's BUSY_E.) */ - (void)NextSeedProducerRelease(lenp, cur); + * code and wins over the release's BUSY_E.) + * + * Note, a failed release cannot brick the RNG and needs no + * disposition at the several (void) sites below: its one cause + * is a purge's _PURGED repaint (producers cannot claim a + * PRODUCING word, consumers exchange only from READY, and the + * purge is the sole other writer), and the BUSY arm is its own + * compensation -- it wipes and reopens the aperture EMPTY + * before returning. Afterward the world is fully consistent: + * aperture empty and healthy, rng->status untouched, recovery + * proceeding through the event's normal channels. BUSY_E is + * information, not a condition awaiting action; the caller's + * own error outranks it wherever one is in flight. Cost of + * swallowing it: at worst one silent refill from offset + * zero. + */ + (void)NextSeedProducerRelease(lenp, seed, nextSeedSz, cur); return ret; } @@ -4805,7 +4862,7 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( * instead; the seed-gather failure is the more * informative code and wins over the release's * BUSY_E.) */ - (void)NextSeedProducerRelease(lenp, cur); + (void)NextSeedProducerRelease(lenp, seed, nextSeedSz, cur); return ret; } @@ -4825,7 +4882,7 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( * meanwhile discards instead, and the release's BUSY_E * percolates -- returning 0 would claim banked progress the * purge just evaporated. */ - return NextSeedProducerRelease(lenp, cur); + return NextSeedProducerRelease(lenp, seed, nextSeedSz, cur); } if (cur == (WC_ATOMIC_INT_ARG)nextSeedSz) { @@ -4840,7 +4897,8 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( /* If RBGC bytes were used for the reseed, then we can skip * wc_RNG_TestSeed(). */ if (*nextSeedRBGCStratum_p > 0) { - ret = NextSeedProducerRelease(lenp, WC_DRBG_NEXT_SEED_READY); + ret = NextSeedProducerRelease(lenp, seed, nextSeedSz, + WC_DRBG_NEXT_SEED_READY); if (ret != 0) { /* Purged while producing (BUSY_E): nothing banked; * post-event material wanted. Retryable. */ @@ -4856,7 +4914,8 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( * that wc_RNG_DRBG_NextSeedNow() is pure computation. */ ret = wc_RNG_TestSeed(seed, nextSeedSz); if (ret == 0) { - ret = NextSeedProducerRelease(lenp, WC_DRBG_NEXT_SEED_READY); + ret = NextSeedProducerRelease(lenp, seed, nextSeedSz, + WC_DRBG_NEXT_SEED_READY); if (ret != 0) { /* Purged while producing (BUSY_E): nothing banked; * post-event material wanted. Retryable. */ @@ -4872,7 +4931,7 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( * dispositive. Release complete-but-unpublished for a * later retry; a purge-discard's BUSY_E percolates (the * retry cause is then the purge, not the test). */ - ret = NextSeedProducerRelease(lenp, + ret = NextSeedProducerRelease(lenp, seed, nextSeedSz, (WC_ATOMIC_INT_ARG)nextSeedSz); if (ret != 0) return ret; @@ -4892,7 +4951,11 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( * purge-discard converge on EMPTY; the release handles * both, and the health-test failure is the more * informative code and wins over the release's BUSY_E. */ - (void)NextSeedProducerRelease(lenp, WC_DRBG_NEXT_SEED_EMPTY); + /* Sentinel-only by doctrine: rejection here is deterministic + * on the bytes (RCT/APT), so every sibling lineage rejects the + * identical material -- no copy is ever consumed anywhere. */ + (void)NextSeedProducerRelease(lenp, seed, nextSeedSz, + WC_DRBG_NEXT_SEED_EMPTY); /* The health-test failure belongs to the depositor's seed * collection, not to the destination RNG. The depositor collects @@ -4927,7 +4990,8 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( * re-adjudicates. The primary error is the more * informative code and wins over a purge-discard's * BUSY_E. */ - (void)NextSeedProducerRelease(lenp, (WC_ATOMIC_INT_ARG)nextSeedSz); + (void)NextSeedProducerRelease(lenp, seed, nextSeedSz, + (WC_ATOMIC_INT_ARG)nextSeedSz); return ret; } } @@ -9552,6 +9616,35 @@ int wc_hwrng_generate_block(byte *output, word32 sz) #ifdef WC_RNG_DEBUG_STATS +/* Note on WC_RNG_DEBUG_STATS collection points: + * + * Placement doctrine: each counter is maintained at the single funnel that + * owns the distinction it records -- + * + * - reseed counts in Hash_DRBG_Reseed() (every reseed flavor routes + * through it: interval backstop, Reseed_Now, RBGC, banked redemption); + * - request/byte counts in the DRBG arm of wc_RNG_GenerateBlock() + * (hardware-offload arms -- RDRAND, Silabs, async, cryptocb, custom -- + * are deliberately uncounted: these are DRBG-facility statistics); + * - banked-seed redemption provenance in wc_RNG_DRBG_NextSeedNow_Nonce(); + * - seed health failures at the two sites that observe them per-instance + * (PollAndReSeed(), NextSeedGenerate); + * - chain-provenance bytes (RBGC_bytes_produced: output generated while + * the instance's own RBGCStratum > 0) in the same generate funnel; + * - pool byte accounting in wc_RNG_Pool_Extract(), under the consumer's + * instance lock: bytes produced by reading from the pool, and bytes + * requested but not fulfilled (empty-pool and partial-serve shortfall), + * so requested == produced + missed on the capacity paths. The + * failed-DRBG burn path deliberately counts nothing: it is a failure + * event (visible via rng->status), not a capacity signal. + * + * Counters are plain (non-atomic) adds/increments, and update under the owner's + * exclusive access (the lock contract shared by all WC_RNG operations) except + * where labeled racy: those are unreliable under concurrency, by design. Every + * site carries its own #ifdef WC_RNG_DEBUG_STATS gate so the facility is + * removable outright with unifdef. + */ + WOLFSSL_API int wc_rng_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, const WC_RNG *rng) { From 54780d455100122c61408c2999f01f7d0c1e32ab Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 17:41:53 -0500 Subject: [PATCH 092/102] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h: refuse condemned and contradictory states at their entry points. * wc_RNG_DRBG_Reseed_Nonce(): return RNG_FAILURE_E on a DRBG_FAILED instance -- condemnation's exit is wc_FreeRng()/wc_InitRng(), not an in-place credited resurrection that would reset the counter, purge the pool, and clear quarantine. * random.h: #error on WC_RNG_WANT_NEXT_SEED or WC_RNG_WANT_POOL without WC_RNG_WANT_LOCK in FIPS builds -- pregenerated material requires the invalidation/purge protocol, which rides the lock facility. * Comment fixes: the zero-length stir generate banks a block for the continuous-test hook (no comparison state lives in this file); PoolPurge() purges on credited reseed only when recovering from invalidation. --- wolfcrypt/src/random.c | 13 +++++++++++-- wolfssl/wolfcrypt/random.h | 12 ++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index affb6f113b4..a51a231a19e 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1129,6 +1129,13 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, if (ret != 0) return ret; + /* A condemned instance does not accept a credited reseed: DRBG_FAILED's + * designed exit is wc_FreeRng()/wc_InitRng() (or the daemon's recovery + * pass), not in-place resurrection that would reset the counter, purge + * the pool, and clear quarantine on unvetted authority. */ + if (rng->status != DRBG_OK) + return RNG_FAILURE_E; + #ifdef WC_RNG_HAVE_LOCK /* Never allow an undersized seed to clear an invalidated state, and if * invalidated, always assume potentially primary seed data -- test it with @@ -2204,7 +2211,8 @@ static int Hash512_DRBG_Uninstantiate(DRBG_SHA512_internal* drbg) /* Uncredited stirring, per SP 800-90A 10.1.1.4 generate with additional_input * (step 2: V += Hash(0x02 || V || additional_input)). The generate is * zero-length: Hash_gen()'s (and Hash512_gen()'s) outSz==0 mode banks the - * generated block for the continuous test, so the stir also primes CRNGT, and + * generated block for the continuous-test hook (its consumer is + * configuration-dependent; no comparison state lives in this file), and * out is never dereferenced. The reseed counter is incremented as for any * generate, and quarantine/stratum are untouched, so the no-claims doctrine * holds as a theorem of the standard rather than a property of a custom @@ -3904,7 +3912,8 @@ static WC_INLINE void PoolWipeRetired(WC_RNG *rng, word32 tail, word32 head) } /* Retire pooled output: any event after which pre-event bytes must not be - * served -- state invalidation, fork, a credited reseed, the reader's + * served -- state invalidation, fork, a credited reseed recovering from + * invalidation, the reader's * fail-closed path. Bumping epoch is the whole operation; see above for why * the counters are deliberately left alone. */ static WARN_UNUSED_RESULT int PoolPurge(WC_RNG* rng) diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index bf58ff09d8d..82d054232f3 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -88,6 +88,12 @@ #else #undef WC_RNG_HAVE_NEXT_SEED #endif +#if defined(WC_RNG_HAVE_NEXT_SEED) && !defined(WC_RNG_HAVE_LOCK) && \ + defined(HAVE_FIPS) + /* FIPS builds: pregenerated output requires the full invalidation/purge + * protocol, which rides the lock facility. */ + #error WC_RNG_WANT_NEXT_SEED requires WC_RNG_WANT_LOCK in FIPS builds. +#endif #if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_POOL)) && \ !defined(WC_RNG_NO_POOL) @@ -100,6 +106,12 @@ #else #undef WC_RNG_HAVE_POOL #endif +#if defined(WC_RNG_HAVE_POOL) && !defined(WC_RNG_HAVE_LOCK) && \ + defined(HAVE_FIPS) + /* FIPS builds: pregenerated output requires the full invalidation/purge + * protocol, which rides the lock facility. */ + #error WC_RNG_WANT_POOL requires WC_RNG_WANT_LOCK in FIPS builds. +#endif #if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_FREE_HOOK)) && \ !defined(WC_RNG_NO_FREE_HOOK) From 10e6117d8eb4b0f2a8e027753b30d9f4de2301e8 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 19:16:24 -0500 Subject: [PATCH 093/102] wolfcrypt/src/random.c: * in Hash_DRBG_Generate() and Hash512_DRBG_Generate(), if an error occurs after state mutation (step-2 V add), preventing state finalization, set rng->status = DRBG_FAILED (adds a WC_RNG* rng arg to Hash*_DRBG_Generate()); * in Hash_DRBG_StirGenerate(), check rng->status after Hash*_DRBG_Generate() and if DRBG_FAILED, return RNG_FAILURE_E; * in wc_RNG_DRBG_NextStirNow(), if the _CONSUMING cmpxchg fails, code the retval to the cause; * in wc_RNG_GenerateBlock(), abort generation if wc_RNG_DRBG_NextStirNow() returns RNG_FAILURE_E. --- wolfcrypt/src/random.c | 98 ++++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 28 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index a51a231a19e..c8b212ce8ef 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -452,7 +452,8 @@ static WARN_UNUSED_RESULT int Hash512_DRBG_Reseed(DRBG_SHA512_internal* drbg, const byte* additional, word32 additionalSz); static WARN_UNUSED_RESULT int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, - const byte* additional, word32 additionalSz); + const byte* additional, word32 additionalSz, + WC_RNG* rng); static WARN_UNUSED_RESULT int Hash512_DRBG_Instantiate( DRBG_SHA512_internal* drbg, const byte* seed, word32 seedSz, @@ -1428,8 +1429,10 @@ static WC_INLINE void array_add(byte* d, word32 dLen, const byte* s, word32 sLen /* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */ static WARN_UNUSED_RESULT int Hash_DRBG_Generate(DRBG_internal* drbg, byte* out, word32 outSz, - const byte* additional, word32 additionalSz) + const byte* additional, word32 additionalSz, + WC_RNG* rng) { + int state_mutated = 0; int ret; #ifdef WOLFSSL_SMALL_STACK_CACHE wc_Sha256* sha = &drbg->sha256; @@ -1521,6 +1524,7 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Generate(DRBG_internal* drbg, if (ret == 0) { array_add(drbg->V, sizeof(drbg->V), digest, WC_SHA256_DIGEST_SIZE); + state_mutated = 1; } else { ForceZero(digest, WC_SHA256_DIGEST_SIZE); @@ -1603,6 +1607,15 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Generate(DRBG_internal* drbg, } #endif + /* Condemn iff the failure landed after the first state write (the + * step-2 V add): V then carries a half-applied transition that no + * in-band operation re-validates -- latch-or-condemn. Failures + * before the seam leave V/C/reseedCtr intact and stay retryable. + * (A bare reseedCtr++ on a pre-seam failure is benign: + * self-consistent state, one interval slot burned.) */ + if ((ret != 0) && state_mutated && (rng != NULL)) + rng->status = DRBG_FAILED; + return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE; } @@ -1996,8 +2009,10 @@ static WARN_UNUSED_RESULT int Hash512_gen(DRBG_SHA512_internal* drbg, /* Returns: DRBG_SUCCESS, DRBG_NEED_RESEED, or DRBG_FAILURE */ static WARN_UNUSED_RESULT int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, byte* out, word32 outSz, - const byte* additional, word32 additionalSz) + const byte* additional, word32 additionalSz, + WC_RNG* rng) { + int state_mutated = 0; int ret; #ifdef WOLFSSL_SMALL_STACK_CACHE wc_Sha512* sha = &drbg->sha512; @@ -2064,9 +2079,11 @@ static WARN_UNUSED_RESULT int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, #ifndef WOLFSSL_SMALL_STACK_CACHE wc_Sha512Free(sha); #endif - if (ret == 0) + if (ret == 0) { array_add(drbg->V, sizeof(drbg->V), digest, WC_SHA512_DIGEST_SIZE); + state_mutated = 1; + } } if (ret == 0) @@ -2120,6 +2137,15 @@ static WARN_UNUSED_RESULT int Hash512_DRBG_Generate(DRBG_SHA512_internal* drbg, #endif } + /* Condemn iff the failure landed after the first state write (the + * step-2 V add): V then carries a half-applied transition that no + * in-band operation re-validates -- latch-or-condemn. Failures + * before the seam leave V/C/reseedCtr intact and stay retryable. + * (A bare reseedCtr++ on a pre-seam failure is benign: + * self-consistent state, one interval slot burned.) */ + if ((ret != 0) && state_mutated && (rng != NULL)) + rng->status = DRBG_FAILED; + return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE; } @@ -2244,13 +2270,17 @@ static WARN_UNUSED_RESULT int Hash_DRBG_StirGenerate(WC_RNG* rng, #ifndef NO_SHA256 if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, NULL, 0, - add, addSz); + add, addSz, rng); + if ((ret != 0) && (rng->status == DRBG_FAILED)) + ret = RNG_FAILURE_E; } #endif #ifdef WOLFSSL_DRBG_SHA512 if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { ret = Hash512_DRBG_Generate((DRBG_SHA512_internal *)rng->drbg512, - NULL, 0, add, addSz); + NULL, 0, add, addSz, rng); + if ((ret != 0) && (rng->status == DRBG_FAILED)) + ret = RNG_FAILURE_E; } #endif #ifdef WC_RNG_DEBUG_STATS @@ -5264,8 +5294,15 @@ int wc_RNG_DRBG_NextStirNow(WC_RNG* rng) if (! wolfSSL_Atomic_Int_CompareExchange(lenp, &expected, WC_DRBG_NEXT_SEED_CONSUMING)) { - /* No ready accumulator -- nothing consumed; reported distinctly. */ - return NOT_READY_E; + /* Accumulator not READY. */ + if (expected < 0) { + /* claimed by a racing consumer. */ + return BUSY_E; + } + else { + /* empty or still accumulating -- nothing consumable yet. */ + return NOT_READY_E; + } } ret = Hash_DRBG_StirGenerate(rng, seed, nextSeedSz); @@ -5425,8 +5462,13 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) stir_ready = 1; } #endif - if (stir_ready) - (void)wc_RNG_DRBG_NextStirNow(rng); + if (stir_ready) { + int stir_ret = wc_RNG_DRBG_NextStirNow(rng); + if (stir_ret == WC_NO_ERR_TRACE(RNG_FAILURE_E)) { + /* The DRBG broke while we were stirring it. */ + return stir_ret; + } + } } #endif /* WC_RNG_HAVE_NEXT_SEED */ @@ -5442,12 +5484,12 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #ifndef NO_SHA256 if (rng->drbgType == WC_DRBG_SHA256) { ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, sz, - NULL, 0); + NULL, 0, rng); if (ret == WC_NO_ERR_TRACE(DRBG_NEED_RESEED)) { ret = PollAndReSeed(rng, NULL, 0); if (ret == DRBG_SUCCESS) ret = Hash_DRBG_Generate((DRBG_internal *)rng->drbg, output, - sz, NULL, 0); + sz, NULL, 0, rng); } } else @@ -5455,13 +5497,13 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) #ifdef WOLFSSL_DRBG_SHA512 if (rng->drbgType == WC_DRBG_SHA512) { ret = Hash512_DRBG_Generate((DRBG_SHA512_internal *)rng->drbg512, - output, sz, NULL, 0); + output, sz, NULL, 0, rng); if (ret == WC_NO_ERR_TRACE(DRBG_NEED_RESEED)) { ret = PollAndReSeed(rng, NULL, 0); if (ret == DRBG_SUCCESS) ret = Hash512_DRBG_Generate( (DRBG_SHA512_internal *)rng->drbg512, output, sz, - NULL, 0); + NULL, 0, rng); } } else @@ -5760,11 +5802,11 @@ static WARN_UNUSED_RESULT int wc_RNG_HealthTest_ex_internal(DRBG_internal* drbg, * answer test checks the second block of DRBG out of * the generator to ensure the internal state is updated * as expected. */ - if (Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0) != 0) { + if (Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0, NULL) != 0) { goto exit_rng_ht; } - if (Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0) != 0) { + if (Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0, NULL) != 0) { goto exit_rng_ht; } @@ -6247,13 +6289,13 @@ static int wc_RNG_HealthTest_SHA512_ex_internal(DRBG_SHA512_internal* drbg, /* First generate: output discarded per NIST DRBGVS procedure */ if (Hash512_DRBG_Generate(drbg, output, outputSz, - additionalA, additionalASz) != 0) { + additionalA, additionalASz, NULL) != 0) { goto exit_rng_ht512; } /* Second generate: this is the actual test output */ if (Hash512_DRBG_Generate(drbg, output, outputSz, - additionalB, additionalBSz) != 0) { + additionalB, additionalBSz, NULL) != 0) { goto exit_rng_ht512; } @@ -6324,12 +6366,12 @@ int wc_RNG_HealthTest_SHA512_ex(int reseed, /* First generate (output discarded per NIST procedure) */ ret = Hash512_DRBG_Generate(drbg, output, outputSz, - additionalA, additionalASz); + additionalA, additionalASz, NULL); if (ret != 0) goto exit_sha512_ex; /* Second generate (this is the actual output) */ ret = Hash512_DRBG_Generate(drbg, output, outputSz, - additionalB, additionalBSz); + additionalB, additionalBSz, NULL); exit_sha512_ex: (void)Hash512_DRBG_Uninstantiate(drbg); @@ -6448,7 +6490,7 @@ int wc_RNG_HealthTest_SHA256_ex( additionalA, additionalASz); if (ret != 0) goto exit_sha256_ex; } - ret = Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0); + ret = Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0, NULL); if (ret != 0) goto exit_sha256_ex; /* Reseed 2 with additionalB, then Generate 2 with NULL (keep) */ @@ -6457,7 +6499,7 @@ int wc_RNG_HealthTest_SHA256_ex( additionalB, additionalBSz); if (ret != 0) goto exit_sha256_ex; } - ret = Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0); + ret = Hash_DRBG_Generate(drbg, output, outputSz, NULL, 0, NULL); } else { /* Standard mode: explicit reseed, then two generates */ @@ -6469,12 +6511,12 @@ int wc_RNG_HealthTest_SHA256_ex( /* Generate 1 (output discarded per NIST DRBGVS procedure) */ ret = Hash_DRBG_Generate(drbg, output, outputSz, - additionalA, additionalASz); + additionalA, additionalASz, NULL); if (ret != 0) goto exit_sha256_ex; /* Generate 2 (this is the actual test output) */ ret = Hash_DRBG_Generate(drbg, output, outputSz, - additionalB, additionalBSz); + additionalB, additionalBSz, NULL); } exit_sha256_ex: @@ -6552,7 +6594,7 @@ int wc_RNG_HealthTest_SHA512_ex2( additionalA, additionalASz); if (ret != 0) goto exit_sha512_ex2; } - ret = Hash512_DRBG_Generate(drbg, output, outputSz, NULL, 0); + ret = Hash512_DRBG_Generate(drbg, output, outputSz, NULL, 0, NULL); if (ret != 0) goto exit_sha512_ex2; /* Reseed 2 with additionalB, then Generate 2 with NULL (keep) */ @@ -6561,7 +6603,7 @@ int wc_RNG_HealthTest_SHA512_ex2( additionalB, additionalBSz); if (ret != 0) goto exit_sha512_ex2; } - ret = Hash512_DRBG_Generate(drbg, output, outputSz, NULL, 0); + ret = Hash512_DRBG_Generate(drbg, output, outputSz, NULL, 0, NULL); } else { /* Standard mode: explicit reseed, then two generates */ @@ -6573,12 +6615,12 @@ int wc_RNG_HealthTest_SHA512_ex2( /* Generate 1 (output discarded per NIST DRBGVS procedure) */ ret = Hash512_DRBG_Generate(drbg, output, outputSz, - additionalA, additionalASz); + additionalA, additionalASz, NULL); if (ret != 0) goto exit_sha512_ex2; /* Generate 2 (this is the actual test output) */ ret = Hash512_DRBG_Generate(drbg, output, outputSz, - additionalB, additionalBSz); + additionalB, additionalBSz, NULL); } exit_sha512_ex2: From 9c65ab80946d80386c2baa5c1822c8a07f317a77 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 15 Sep 2026 21:44:04 -0500 Subject: [PATCH 094/102] update doc/dox_comments/header_files/random.h and doc/dox_comments/header_files/rng_bank.h. --- doc/dox_comments/header_files/random.h | 45 +++++++++---- doc/dox_comments/header_files/rng_bank.h | 81 +++++++++++++++--------- 2 files changed, 86 insertions(+), 40 deletions(-) diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index 022b089dc41..64f99e8a707 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -135,7 +135,7 @@ int wc_InitRng(WC_RNG* rng); \sa wc_FreeRng \sa wc_RNG_HealthTest */ -int wc_RNG_GenerateBlock(WC_RNG* rng, byte* b, word32 sz); +int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz); /*! \ingroup Random @@ -397,7 +397,7 @@ int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId); \sa wc_InitRng */ -int wc_InitRngNonce(WC_RNG* rng, byte* nonce, word32 nonceSz); +int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz); /*! \ingroup Random @@ -424,7 +424,7 @@ int wc_InitRngNonce(WC_RNG* rng, byte* nonce, word32 nonceSz); \sa wc_InitRngNonce */ -int wc_InitRngNonce_ex(WC_RNG* rng, byte* nonce, word32 nonceSz, +int wc_InitRngNonce_ex(WC_RNG* rng, const byte* nonce, word32 nonceSz, void* heap, int devId); /*! @@ -820,12 +820,15 @@ int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId, word32 flags); \param nonceSz Length of nonce in bytes. \param heap Heap hint for dynamic allocation. \param devId Device id, or INVALID_DEVID. + \param perso Optional personalization string (may be null). + \param persoSz Length of perso in bytes. \param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes. \sa wc_InitRng_ex2 \sa wc_InitRngNonce_ex */ int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz, + const byte *perso, word32 persoSz, void* heap, int devId, word32 flags); /*! @@ -939,6 +942,9 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz); the reseed counter resets. \return 0 Success + \return RNG_FAILURE_E rng is condemned (status DRBG_FAILED): a + condemned instance does not accept a credited reseed; recover with + wc_FreeRng() then wc_InitRng*(). \return BAD_FUNC_ARG rng or seed is null. \return WRONG_TYPE_OBJECT_E rng has no DRBG (RDRAND et al.). @@ -1320,6 +1326,9 @@ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); mixed in as uncredited additional input alongside the banked seed. \return 0 Success + \return NEEDS_RECOVERY_E A purge crossed the consume (an invalidation + epoch boundary): no material is adopted, the entropy-invalidated latch + is re-asserted, and a recovery reseed is scheduled. \return NOT_READY_E No bank is ready. \return BAD_FUNC_ARG rng is null, or nonce is null with nonceSz nonzero. \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). @@ -1368,10 +1377,15 @@ int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, counter is not reset. The caller must own the instance. \return 0 Success - \return NOT_READY_E The accumulator is empty. + \return NOT_READY_E The accumulator is empty or still accumulating, or + the stir is refused (reseed interval, entropy-invalidated quarantine). + \return BUSY_E The accumulator was claimed by a racing consumer -- the + stir is happening by another hand. \return BAD_FUNC_ARG rng is null. \return MISSING_RNG_E rng has no DRBG (RDRAND et al.). - \return RNG_FAILURE_E The DRBG is out of service. + \return RNG_FAILURE_E The DRBG is out of service, or its hash failed + mid-stir leaving a half-applied update -- the instance is then + condemned (status DRBG_FAILED). \param rng The RNG object to stir. @@ -1563,12 +1577,21 @@ int wc_RNG_lock_clear_extra(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits); \ingroup Random \brief Mark rng's seed material untrusted -- for VM fork/resume and - similar duplication events -- by latching the entropy-invalidated bit in - the lock word. An invalidated instance refuses service - (NEEDS_RECOVERY_E) until recovery-reseeded. - - \return 0 Success + similar duplication events -- opening a new invalidation epoch: banked + and pooled pre-event material is purged and wiped first, then the + entropy-invalidated bit is latched in the lock word. An invalidated + instance refuses service (NEEDS_RECOVERY_E) until recovery-reseeded. + Latch or condemn: on any error return the latch is down, and the + instance is instead condemned (status DRBG_FAILED). A condemned bank + instance is retired and recovered by the entropy daemon; a condemned + leaf gets no daemon rescue -- its owner sees RNG_FAILURE_E from + subsequent operations and recovers it with wc_FreeRng() then + wc_InitRng*(). + + \return 0 Success: purges complete, latch asserted. \return BAD_FUNC_ARG rng is null. + \return RNG_FAILURE_E (or other nonzero) A purge or the latch failed; + the instance is condemned as above. \param rng The RNG object to invalidate. @@ -1683,7 +1706,7 @@ int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src, word32 n); \brief Drain up to *n bytes from rng's pool into out -- atomic-context-safe. On success *n reports the bytes actually - delivered. + delivered; on any error return *n is left unmodified. \return 0 Success \return NOT_READY_E The pool is empty or being filled. diff --git a/doc/dox_comments/header_files/rng_bank.h b/doc/dox_comments/header_files/rng_bank.h index 7ba20890de6..ac35bd321a3 100644 --- a/doc/dox_comments/header_files/rng_bank.h +++ b/doc/dox_comments/header_files/rng_bank.h @@ -89,12 +89,15 @@ int wc_rng_bank_init(struct wc_rng_bank *ctx, int n_rngs, word32 flags, \param devId Device id, or INVALID_DEVID. \param nonce Additional instantiation input. \param nonceSz Length of nonce in bytes. + \param perso Optional personalization string (may be null). + \param persoSz Length of perso in bytes. \sa wc_rng_bank_init */ -int wc_rng_bank_init_nonce(struct wc_rng_bank *ctx, int n_rngs, word32 flags, - int timeout_secs, void *heap, int devId, - const byte *nonce, word32 nonceSz); +int wc_rng_bank_init_nonce(struct wc_rng_bank *ctx, int n_rngs, + word32 flags, int timeout_secs, void *heap, + int devId, const byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz); /*! \ingroup Random @@ -441,14 +444,12 @@ int wc_rng_bank_next_seed_generate(struct wc_rng_bank *bank, int inst_offset, \param bank The bank. \param inst_offset The instance to bank for. \param n Maximum bytes to bank this call. - \param root The chain parent to draw material from. \sa wc_rng_bank_next_seed_generate \sa wc_RNG_DRBG_NextSeedGenerate_RBGC */ int wc_rng_bank_next_seed_generate_rbgc(struct wc_rng_bank *bank, - int inst_offset, word32 n, - WC_RNG *root); + int inst_offset, word32 n); /*! \ingroup Random @@ -548,6 +549,8 @@ int wc_rng_bank_recover_inst(struct wc_rng_bank *bank, int inst_offset, \param child_rng The caller-provided WC_RNG to instantiate. \param nonce Optional additional instantiation input. \param nonceSz Length of nonce in bytes. + \param perso Optional personalization string (may be null). + \param persoSz Length of perso in bytes. \param preferred_inst_offset The preferred parent instance, or 0. \param timeout_secs Wait budget (with _CAN_WAIT). \param flags Bitwise-or of WC_RNG_BANK_FLAG_* per-call flags. @@ -556,7 +559,9 @@ int wc_rng_bank_recover_inst(struct wc_rng_bank *bank, int inst_offset, \sa wc_InitRngNonceRBGC */ int wc_rng_bank_spawn(struct wc_rng_bank *bank, WC_RNG *child_rng, - byte *nonce, word32 nonceSz, int preferred_inst_offset, + byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, + int preferred_inst_offset, int timeout_secs, word32 flags); /*! @@ -575,6 +580,8 @@ int wc_rng_bank_spawn(struct wc_rng_bank *bank, WC_RNG *child_rng, \param child_rng Receives the allocated, instantiated WC_RNG. \param nonce Optional additional instantiation input. \param nonceSz Length of nonce in bytes. + \param perso Optional personalization string (may be null). + \param persoSz Length of perso in bytes. \param preferred_inst_offset The preferred parent instance, or 0. \param timeout_secs Wait budget (with _CAN_WAIT). \param flags Bitwise-or of WC_RNG_BANK_FLAG_* per-call flags. @@ -583,6 +590,7 @@ int wc_rng_bank_spawn(struct wc_rng_bank *bank, WC_RNG *child_rng, */ int wc_rng_bank_spawn_new(struct wc_rng_bank *bank, WC_RNG **child_rng, byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, int preferred_inst_offset, int timeout_secs, word32 flags); @@ -601,6 +609,8 @@ int wc_rng_bank_spawn_new(struct wc_rng_bank *bank, WC_RNG **child_rng, \param bank The bank to seed. \param seed Seed material. \param seedSz Length of seed in bytes. + \param nonce Optional per-instance nonce material (may be null). + \param nonceSz Length of nonce in bytes. \param timeout_secs Wait budget per instance. \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. @@ -608,7 +618,8 @@ int wc_rng_bank_spawn_new(struct wc_rng_bank *bank, WC_RNG **child_rng, \sa wc_rng_bank_reseed */ int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, - word32 seedSz, int timeout_secs, word32 flags); + word32 seedSz, const byte *nonce, word32 nonceSz, + int timeout_secs, word32 flags); /*! \ingroup Random @@ -630,6 +641,8 @@ int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, \param last_inst The last instance offset. \param seed Seed material. \param seedSz Length of seed in bytes. + \param nonce Optional per-instance nonce material (may be null). + \param nonceSz Length of nonce in bytes. \param timeout_secs Wait budget per instance. \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. @@ -637,6 +650,7 @@ int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed, */ int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int first_inst, int last_inst, const byte* seed, word32 seedSz, + const byte *nonce, word32 nonceSz, int timeout_secs, word32 flags); /*! @@ -650,14 +664,17 @@ int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int first_inst, timeout_secs. \param bank The bank to reseed. + \param nonce Optional per-instance nonce material (may be null). + \param nonceSz Length of nonce in bytes. \param timeout_secs Wait budget per instance. \param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags. \sa wc_rng_bank_reseed_range \sa wc_rng_bank_seed */ -int wc_rng_bank_reseed(struct wc_rng_bank *bank, int timeout_secs, - word32 flags); +int wc_rng_bank_reseed(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz, + int timeout_secs, word32 flags); /*! \ingroup Random @@ -673,6 +690,8 @@ int wc_rng_bank_reseed(struct wc_rng_bank *bank, int timeout_secs, \return WC_TIMEOUT_E The walk exceeded timeout_secs. \param bank The bank to reseed. + \param nonce Optional per-instance nonce material (may be null). + \param nonceSz Length of nonce in bytes. \param first_inst The first instance offset. \param last_inst The last instance offset. \param timeout_secs Wait budget per instance. @@ -681,7 +700,9 @@ int wc_rng_bank_reseed(struct wc_rng_bank *bank, int timeout_secs, \sa wc_rng_bank_reseed */ int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, int first_inst, - int last_inst, int timeout_secs, word32 flags); + int last_inst, + const byte *nonce, word32 nonceSz, + int timeout_secs, word32 flags); /*! \ingroup Random @@ -803,39 +824,41 @@ int wc_rng_bank_daemon_release(struct wc_rng_bank *bank, WC_ATOMIC_UINT_ARG magic); /*! - \ingroup Random + \ingroup RNGBank - \brief Bind the daemon's RBG-chain root to the bank, for chain-sourced - banking (wc_rng_bank_next_seed_generate_rbgc()) and harvest deposit - (wc_RNG_DRBG_NextStirStore() on the root). + \brief Initialize the bank's root RNG -- the primary-sourced parent + that seeds and recovers the bank's instances. \return 0 Success \return BAD_FUNC_ARG bank is null. - \param bank The bank. - \param daemon_root The daemon's root WC_RNG, or null to unbind. - - \sa wc_rng_bank_daemon_root_get - \sa wc_rng_bank_daemon_reserve - \details The caller (the daemon) owns the ordering: bind after - successful root initialization, unbind before root teardown. + \param bank The bank whose root to initialize. + \param nonce Optional instantiation nonce (may be null). + \param nonceSz Length of nonce in bytes. + \param perso Optional personalization string (may be null). + \param persoSz Length of perso in bytes. + \param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes. + \sa wc_rng_bank_root_rng_get */ -int wc_rng_bank_daemon_root_set(struct wc_rng_bank *bank, - WC_RNG *daemon_root); +int wc_rng_bank_root_rng_init(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz, + const byte *perso, word32 persoSz, + word32 flags); /*! - \ingroup Random + \ingroup RNGBank - \brief Report the bank's bound daemon root. + \brief Report the bank's root RNG. - \return The daemon root, or null when none is bound or bank is null. + \return The root WC_RNG, or null when none is initialized or bank is + null. \param bank The bank to interrogate. - \sa wc_rng_bank_daemon_root_set + \sa wc_rng_bank_root_rng_init */ -WC_RNG *wc_rng_bank_daemon_root_get(struct wc_rng_bank *bank); +WC_RNG *wc_rng_bank_root_rng_get(struct wc_rng_bank *bank); /*! \ingroup Random From 645bedc74d4db0adcdd892a618a792469efa24f3 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 16 Sep 2026 00:01:16 -0500 Subject: [PATCH 095/102] tests/unit-mcdc/test_random_fault_whitebox.c and tests/unit-mcdc/test_random_whitebox.c: update for new Hash_DRBG_Generate() arity and WUR on Hash512_DRBG_Instantiate() (fixed by collecting and asserting errors). --- tests/unit-mcdc/test_random_fault_whitebox.c | 24 ++++++++++++++++---- tests/unit-mcdc/test_random_whitebox.c | 8 +++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/tests/unit-mcdc/test_random_fault_whitebox.c b/tests/unit-mcdc/test_random_fault_whitebox.c index a40e0f6ff89..af7268c827f 100644 --- a/tests/unit-mcdc/test_random_fault_whitebox.c +++ b/tests/unit-mcdc/test_random_fault_whitebox.c @@ -204,16 +204,24 @@ static void wb_hash_drbg_init_df_chain(void) /* Vector (F, -): the first Hash_df fails on its very first primitive * call, so the && short-circuits and never calls the second one. */ mcdc_fh_arm(1); - (void)Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed), + ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed), nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID); + if (ret == DRBG_SUCCESS) { + WB_NOTE("armed Hash_DRBG_Instantiate unexpectedly succeeded"); + wb_fail = 1; + } mcdc_fh_disarm(); (void)Hash_DRBG_Uninstantiate(&drbg); /* Vector (T, F): the first Hash_df's n1 primitive calls all succeed; * every call from n1+1 on -- the whole second Hash_df -- fails. */ mcdc_fh_arm(n1 + 1); - (void)Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed), + ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed), nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID); + if (ret == DRBG_SUCCESS) { + WB_NOTE("armed Hash_DRBG_Instantiate unexpectedly succeeded"); + wb_fail = 1; + } mcdc_fh_disarm(); (void)Hash_DRBG_Uninstantiate(&drbg); @@ -270,15 +278,23 @@ static void wb_hash512_drbg_init_df_chain(void) /* Vector (F, -). */ mcdc_fh_arm(1); - (void)Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed), + ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed), nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID); + if (ret == DRBG_SUCCESS) { + WB_NOTE("armed Hash512_DRBG_Instantiate unexpectedly succeeded"); + wb_fail = 1; + } mcdc_fh_disarm(); (void)Hash512_DRBG_Uninstantiate(&drbg); /* Vector (T, F). */ mcdc_fh_arm(n1 + 1); - (void)Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed), + ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed), nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID); + if (ret == DRBG_SUCCESS) { + WB_NOTE("armed Hash512_DRBG_Instantiate unexpectedly succeeded"); + wb_fail = 1; + } mcdc_fh_disarm(); (void)Hash512_DRBG_Uninstantiate(&drbg); diff --git a/tests/unit-mcdc/test_random_whitebox.c b/tests/unit-mcdc/test_random_whitebox.c index 3cf934c904c..eb729b8e206 100644 --- a/tests/unit-mcdc/test_random_whitebox.c +++ b/tests/unit-mcdc/test_random_whitebox.c @@ -300,7 +300,7 @@ static void wb_hash_drbg_generate_reseed(void) /* False side: reseedCtr below the interval -> generate proceeds. */ drbg.reseedCtr = 1; - ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0); + ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0, NULL); if (ret != DRBG_SUCCESS) { WB_NOTE("Hash_DRBG_Generate (below interval) failed"); wb_fail = 1; @@ -308,7 +308,7 @@ static void wb_hash_drbg_generate_reseed(void) /* True side: reseedCtr at the interval -> early DRBG_NEED_RESEED. */ drbg.reseedCtr = WC_RESEED_INTERVAL; - ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0); + ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0, NULL); if (ret != DRBG_NEED_RESEED) { WB_NOTE("Hash_DRBG_Generate did not signal DRBG_NEED_RESEED"); wb_fail = 1; @@ -448,14 +448,14 @@ static void wb_hash512_drbg_generate_reseed(void) } drbg.reseedCtr = 1; /* false side */ - ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0); + ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0, NULL); if (ret != DRBG_SUCCESS) { WB_NOTE("Hash512_DRBG_Generate (below interval) failed"); wb_fail = 1; } drbg.reseedCtr = WC_RESEED_INTERVAL; /* true side */ - ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0); + ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0, NULL); if (ret != DRBG_NEED_RESEED) { WB_NOTE("Hash512_DRBG_Generate did not signal DRBG_NEED_RESEED"); wb_fail = 1; From babdbc41d2886fd71c941bcdade2bafc3d350a39 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 16 Sep 2026 00:10:26 -0500 Subject: [PATCH 096/102] wolfcrypt/src/random.c: in wc_RNG_DRBG_NextSeedGenerate_local(), initialize seed and lenp to NULL to work around false positive from CI. --- wolfcrypt/src/random.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index c8b212ce8ef..859b61ddd20 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -4703,11 +4703,11 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( const byte *nonce, word32 n) { int claim_ret; - byte* seed; - wolfSSL_Atomic_Int* lenp; + byte* seed = NULL; + wolfSSL_Atomic_Int* lenp = NULL; int *nextSeedRBGCStratum_p = NULL; WC_ATOMIC_INT_ARG cur; - word32 nextSeedSz; + word32 nextSeedSz = 0; int ret; if ((rng == NULL) || (n == 0) || (rng == root)) From 5bda097a914531bc2a6c14299db0587f886290c9 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 16 Sep 2026 01:07:38 -0500 Subject: [PATCH 097/102] wolfcrypt/test/test.h: tweak WC_TEST_RET_ENC_I() to put a ceiling on i. --- wolfcrypt/test/test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index c8931b71e5f..c69dc211a94 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -75,7 +75,7 @@ wc_static_assert(-(long)MIN_CODE_E < 0x7ffL); #define WC_TEST_RET_ENC_NC WC_TEST_RET_ENC(WC_TEST_RET_LN, 0, WC_TEST_RET_TAG_NC) /* encode positive integer */ -#define WC_TEST_RET_ENC_I(i) WC_TEST_RET_ENC(WC_TEST_RET_LN, i, WC_TEST_RET_TAG_I) +#define WC_TEST_RET_ENC_I(i) WC_TEST_RET_ENC(WC_TEST_RET_LN, (i > 0x7ff) ? 0x7ff : (i), WC_TEST_RET_TAG_I) /* encode error code (negative integer) */ #define WC_TEST_RET_ENC_EC(ec) WC_TEST_RET_ENC(WC_TEST_RET_LN, -(ec), WC_TEST_RET_TAG_EC) From 622299a7ba90b238880d5cf405583ff94957f662 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 16 Sep 2026 01:09:11 -0500 Subject: [PATCH 098/102] wolfcrypt/src/random.c, wolfcrypt/src/rng_bank.c, wolfcrypt/test/test.c: fix RNG-extras support for HAVE_INTEL_RDRAND. --- wolfcrypt/src/random.c | 26 +++++++--- wolfcrypt/src/rng_bank.c | 2 +- wolfcrypt/test/test.c | 103 +++++++++++++++++++-------------------- 3 files changed, 71 insertions(+), 60 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 859b61ddd20..931078e64a3 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -347,9 +347,9 @@ int wc_RNG_GetStatus(const WC_RNG* rng) return (int)rng->status; } -/* Returns 1 if rng has an instantiated DRBG, else 0. An in-service WC_RNG - * can lack one: _InitRng() bypasses DRBG instantiation when the CPU has - * RDRAND (HAVE_INTEL_RDRAND). */ +/* Returns 1 if rng has an instantiated DRBG, else 0. An in-service WC_RNG can + * lack one: _InitRng() in HAVE_INTEL_RDRAND configurations bypasses DRBG + * instantiation when the CPU has RDRAND (). */ int wc_RNG_DRBG_Present(const WC_RNG* rng) { if (rng == NULL) @@ -1229,17 +1229,24 @@ int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng, { if ((rng == NULL) || (reseedCtr == NULL)) return BAD_FUNC_ARG; - *reseedCtr = 0; + if (! wc_RNG_DRBG_Present(rng)) + return WRONG_TYPE_OBJECT_E; + if (rng->status != DRBG_OK) + return BAD_STATE_E; #ifndef NO_SHA256 - if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) + if ((rng->drbgType == WC_DRBG_SHA256) && (rng->drbg != NULL)) { *reseedCtr = ((const DRBG_internal *)rng->drbg)->reseedCtr; + return 0; + } #endif #ifdef WOLFSSL_DRBG_SHA512 - if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) + if ((rng->drbgType == WC_DRBG_SHA512) && (rng->drbg512 != NULL)) { *reseedCtr = (wc_drbg_reseed_ctr_t) ((const DRBG_SHA512_internal *)rng->drbg512)->reseedCtr; + return 0; + } #endif - return 0; + return BAD_FUNC_ARG; } #if defined(WC_RESEED_INTERVAL) && !defined(WORD64_AVAILABLE) @@ -2756,6 +2763,11 @@ static WARN_UNUSED_RESULT int _InitRng(WC_RNG* rng, #ifdef HAVE_HASHDRBG rng->status = DRBG_OK; #endif +#ifdef WC_RNG_HAVE_RBGC + /* undo stratum increment */ + if (seedRng != NULL) + rng->RBGCStratum = 0; +#endif return 0; } #endif diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 808b27d12ec..534a9a2a98e 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -2159,7 +2159,7 @@ WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, * HAVE_INTEL_RDRAND. We just have no way to seed it, so don't * pretend we can. */ - ret = NOT_COMPILED_IN; + ret = WRONG_TYPE_OBJECT_E; } else { if (flags & WC_RNG_BANK_FLAG_STIR) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 95bf197c99e..3fc9e150e33 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -945,7 +945,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void); #endif #if defined(WC_RNG_BANK_SUPPORT) && defined(HAVE_HASHDRBG) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) && \ + !defined(HAVE_INTEL_RDRAND) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); #endif #if defined(WC_RNG_HAVE_RBGC) && \ @@ -2630,7 +2631,8 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("RNGSVC test passed!\n"); #endif #if defined(WC_RNG_BANK_SUPPORT) && defined(HAVE_HASHDRBG) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) && \ + !defined(HAVE_INTEL_RDRAND) if ((ret = rng_entropy_invalidate_test()) != 0) TEST_FAIL("RNGINVAL test failed!\n", ret); else @@ -28257,7 +28259,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif -#ifdef HAVE_HASHDRBG +#if defined(HAVE_HASHDRBG) && !defined(HAVE_INTEL_RDRAND) ret = wc_rng_bank_reseed(NULL, NULL, 0, 10, WC_RNG_BANK_FLAG_NONE); #ifdef WC_RNG_BANK_DEFAULT_SUPPORT if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) @@ -28271,26 +28273,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - for (i = 0; i < bank->n_rngs; ++i) { - #if defined(WOLFSSL_DRBG_SHA512) && !defined(HAVE_SELFTEST) && \ + #if !defined(HAVE_SELFTEST) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) - word64 bankReseedCtr; - if (bank->rngs[i].rng.drbgType == WC_DRBG_SHA512) - bankReseedCtr = ((struct DRBG_SHA512_internal *) - bank->rngs[i].rng.drbg512)->reseedCtr; - else - bankReseedCtr = ((struct DRBG_internal *) - bank->rngs[i].rng.drbg)->reseedCtr; + for (i = 0; i < bank->n_rngs; ++i) { + wc_drbg_reseed_ctr_t bankReseedCtr; + ret = wc_RNG_DRBG_GetReseedCtr(&bank->rngs[i].rng, &bankReseedCtr); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (bankReseedCtr != WC_RESEED_INTERVAL) - #else - if (((struct DRBG_internal *)bank->rngs[i].rng.drbg) - ->reseedCtr != WC_RESEED_INTERVAL) - #endif - { - ERROR_OUT(WC_TEST_RET_ENC_I(i), out); - } + ERROR_OUT(WC_TEST_RET_ENC_I(bankReseedCtr), out); } -#endif /* HAVE_HASHDRBG */ + #endif +#endif /* HAVE_HASHDRBG && !HAVE_INTEL_RDRAND */ rng_bank_affinity_get_id_id = 0; /* WC_RNG_BANK_FLAG_CAN_WAIT needed to avoiding warning message that the @@ -28320,7 +28314,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); -#ifdef HAVE_HASHDRBG +#if defined(HAVE_HASHDRBG) && !defined(HAVE_INTEL_RDRAND) ret = wc_rng_bank_seed(bank, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -28405,7 +28399,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#endif /* HAVE_HASHDRBG */ +#endif /* HAVE_HASHDRBG && !HAVE_INTEL_RDRAND */ ret = wc_rng_bank_checkout(NULL, &rng_inst, -1, 10, WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | WC_RNG_BANK_FLAG_AFFINITY_LOCK); #ifdef WC_RNG_BANK_DEFAULT_SUPPORT @@ -28482,7 +28476,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#ifdef HAVE_HASHDRBG +#if defined(HAVE_HASHDRBG) && !defined(HAVE_INTEL_RDRAND) ret = wc_rng_bank_seed(NULL, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) @@ -28496,20 +28490,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#endif /* HAVE_HASHDRBG */ +#endif /* HAVE_HASHDRBG && !HAVE_INTEL_RDRAND */ ret = wc_rng_bank_default_clear(bank); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#ifdef HAVE_HASHDRBG +#if defined(HAVE_HASHDRBG) && !defined(HAVE_INTEL_RDRAND) /* seedSz == 0 probe with no default bank set: NO_DEFAULT_FOUND_E. */ ret = wc_rng_bank_seed(NULL, NULL, 0, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != WC_NO_ERR_TRACE(NO_DEFAULT_FOUND_E)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); -#endif /* HAVE_HASHDRBG */ +#endif /* HAVE_HASHDRBG && !HAVE_INTEL_RDRAND */ #endif /* WC_RNG_BANK_DEFAULT_SUPPORT */ @@ -28550,7 +28544,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#ifdef HAVE_HASHDRBG +#if defined(HAVE_HASHDRBG) && !defined(HAVE_INTEL_RDRAND) ret = wc_rng_bank_seed(bank2, (byte *)bank_arg, (word32)sizeof(bank_arg), NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT); if (ret != 0) @@ -28605,7 +28599,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); -#endif /* HAVE_HASHDRBG */ +#endif /* HAVE_HASHDRBG && !HAVE_INTEL_RDRAND */ #if defined(WC_HAVE_RNG_BANKREF) && !defined(WC_NO_CONSTRUCTORS) ret = wc_rng_new_bankref(NULL, &rng2); @@ -28801,7 +28795,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); leaf_rng_inited = 1; -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && !defined(HAVE_INTEL_RDRAND) ret = wc_RNG_DRBG_GetRBGCStratum(leaf_rng); /* the _NEXT_SEED section above reseeds the bank root -- otherwise it's a * user seed. */ @@ -28829,7 +28823,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (spawned_rng == NULL) ERROR_OUT(WC_TEST_RET_ENC_NC, out); -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && !defined(HAVE_INTEL_RDRAND) ret = wc_RNG_DRBG_GetRBGCStratum(spawned_rng); if (ret != WC_RNG_RBGC_USER_SEED_STRATUM + 1) ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); @@ -28851,7 +28845,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); leaf_rng_inited = 1; -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && !defined(HAVE_INTEL_RDRAND) ret = wc_RNG_DRBG_GetRBGCStratum(leaf_rng); if (ret != 1) ERROR_OUT(WC_TEST_RET_ENC_I(ret), out); @@ -29268,36 +29262,33 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - } - api_ret = wc_RNG_DRBG_Stir(root, matter, sizeof(matter)); - if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if (present) { + api_ret = wc_RNG_DRBG_Stir(root, matter, sizeof(matter)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c2); if ((api_ret != 0) || (c2 != c1 + 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - } - /* credited reseed resets the counter */ - api_ret = wc_RNG_DRBG_Reseed(root, matter, sizeof(matter)); - if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if (present) { + /* credited reseed resets the counter */ + api_ret = wc_RNG_DRBG_Reseed(root, matter, sizeof(matter)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if ((api_ret != 0) || (c1 != 1)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); - } - /* schedule-then-generate performs a source reseed */ - api_ret = wc_RNG_DRBG_ScheduleReseed(root); - if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - if (present) { + /* schedule-then-generate performs a source reseed */ + api_ret = wc_RNG_DRBG_ScheduleReseed(root); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); + api_ret = wc_RNG_DRBG_GetReseedCtr(root, &c1); if ((api_ret != 0) || (c1 != (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); } + RNG_STATS_SNAP(root); api_ret = wc_RNG_GenerateBlock(root, buf, sizeof(buf)); if (api_ret != 0) @@ -29449,7 +29440,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) /* (!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) */ #if defined(WC_RNG_BANK_SUPPORT) && defined(HAVE_HASHDRBG) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(5,2,4)) && \ + !defined(HAVE_INTEL_RDRAND) /* Unit coverage for WC_RNG_LOCK_ENTROPY_INVALIDATED and the * invalidation-recovery protocol (VM fork / resume), exercised through the @@ -30022,7 +30014,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void) return ret; } -#endif /* WC_RNG_BANK_SUPPORT && (!HAVE_FIPS || FIPS_VERSION3_GE(5,2,4)) */ +#endif /* WC_RNG_BANK_SUPPORT && (!HAVE_FIPS || FIPS_VERSION3_GE(5,2,4)) && */ + /* !HAVE_INTEL_RDRAND */ #ifdef WC_RNG_HAVE_RBGC @@ -30103,12 +30096,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) _stats_total_bytes_produced, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } +#ifndef HAVE_INTEL_RDRAND api_ret = wc_RNG_DRBG_GetRBGCStratum(&leaf); if (api_ret != 1) ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); api_ret = wc_RNG_DRBG_GetRBGCStratum(&root); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); +#endif RNG_STATS_SNAP2(&leaf); api_ret = wc_RNG_GenerateBlock(&leaf, buf, sizeof(buf)); if (api_ret != 0) @@ -30122,6 +30117,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } +#ifndef HAVE_INTEL_RDRAND /* root(0) from leaf(1): refused -- no stratum downgrade. */ api_ret = wc_RNG_DRBG_ReseedRBGC(&root, &leaf, NULL, 0); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) @@ -30185,8 +30181,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) RNG_STATS_EXPECT2(&leaf, _stats_RBGC_bytes_produced, 0, ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_stats_d_), out)); } +#endif /* !HAVE_INTEL_RDRAND */ -#if !defined(WC_NO_CONSTRUCTORS) +#if !defined(WC_NO_CONSTRUCTORS) && !defined(HAVE_INTEL_RDRAND) /* chain-reseeding a source-born instance demotes it, one-way */ api_ret = wc_InitRng(&extra); if (api_ret != 0) @@ -30315,7 +30312,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); wc_rng_free(pleaf); pleaf = NULL; -#endif /* !WC_NO_CONSTRUCTORS */ +#endif /* !WC_NO_CONSTRUCTORS && !HAVE_INTEL_RDRAND */ /* nonce-bearing stack spawn */ api_ret = wc_FreeRng(&leaf); @@ -30327,9 +30324,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); leaf_inited = 1; +#ifndef HAVE_INTEL_RDRAND api_ret = wc_RNG_DRBG_GetRBGCStratum(&leaf); if (api_ret != 1) ERROR_OUT(WC_TEST_RET_ENC_I(api_ret), out); +#endif out: From 8d95ec5da02e0f5728da39d5d6bbab196c6a69c1 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 16 Sep 2026 01:15:05 -0500 Subject: [PATCH 099/102] wolfcrypt/test/test.c: in rng_drbg_svc_test(), fix gating on wc_ecc_set_rng(). --- wolfcrypt/test/test.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 3fc9e150e33..7c3b966b920 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -29366,7 +29366,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) ERROR_OUT(api_ret, out); } #endif /* !NO_RSA */ -#ifdef HAVE_ECC +#if defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT) { WC_DECLARE_VAR(eccKey, ecc_key, 1, HEAP_HINT); int eccKey_inited = 0; @@ -29380,9 +29380,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) eccKey_inited = 1; if ((wc_ecc_set_rng(eccKey, root) != 0) || (wc_ecc_clear_rng(eccKey) != 0) -#ifdef ECC_TIMING_RESISTANT || (wc_ecc_clear_rng(NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) -#endif ) { api_ret = WC_TEST_RET_ENC_NC; @@ -29394,7 +29392,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void) if (api_ret != 0) ERROR_OUT(api_ret, out); } -#endif /* HAVE_ECC */ +#endif /* HAVE_ECC && ECC_TIMING_RESISTANT */ #if defined(HAVE_CURVE25519) && defined(WOLFSSL_CURVE25519_BLINDING) { WC_DECLARE_VAR(cvKey, curve25519_key, 1, HEAP_HINT); From 8cddf2177eb7598fcb0613fe3dad3bcf52244ec0 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 16 Sep 2026 02:06:29 -0500 Subject: [PATCH 100/102] wolfcrypt/test/test.h: fix bugprone-macro-parentheses in revised WC_TEST_RET_ENC_I(). --- wolfcrypt/test/test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index c69dc211a94..800499d5de4 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -75,7 +75,7 @@ wc_static_assert(-(long)MIN_CODE_E < 0x7ffL); #define WC_TEST_RET_ENC_NC WC_TEST_RET_ENC(WC_TEST_RET_LN, 0, WC_TEST_RET_TAG_NC) /* encode positive integer */ -#define WC_TEST_RET_ENC_I(i) WC_TEST_RET_ENC(WC_TEST_RET_LN, (i > 0x7ff) ? 0x7ff : (i), WC_TEST_RET_TAG_I) +#define WC_TEST_RET_ENC_I(i) WC_TEST_RET_ENC(WC_TEST_RET_LN, ((i) > 0x7ff) ? 0x7ff : (i), WC_TEST_RET_TAG_I) /* encode error code (negative integer) */ #define WC_TEST_RET_ENC_EC(ec) WC_TEST_RET_ENC(WC_TEST_RET_LN, -(ec), WC_TEST_RET_TAG_EC) From 5ba5b5cbe103e981dda461fd5952a044b4f24f9a Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 16 Sep 2026 16:32:14 -0500 Subject: [PATCH 101/102] wolfcrypt/src/random.c, wolfcrypt/src/rng_bank.c, wolfcrypt/test/test.c, wolfssl/wolfcrypt/settings.h: fixes for a batch of static-analysis findings. * complete the WC_CAS_WITH_RETRY post-loop dispositions in rng_bank.c and the lock_put()s: aborted releases percolate before the lock word (and, in wc_RNG_lock_put(), the mutex) is reported free, keeping ownership with the caller for retry; the inst_lock_*_extra() setters return the CAS result; wc_rng_bank_inst_invalidate_entropy() latches or condemns, mirroring wc_RNG_invalidate_entropy(). * wc_RNG_invalidate_entropy(): tolerate WRONG_TYPE_OBJECT_E from wc_RNG_DRBG_ScheduleReseed() -- a no-DRBG (direct-RDRAND) instance has nothing to schedule and nothing whose staleness the latch would mark; don't condemn it. * wc_rng_bank_reseed_range(): fix undeclared identifier (inst -> drbg) in the HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) && WC_RNG_HAVE_RBGC branch. * _InitRng(): free and clear the full mutex when wc_LockMutex() fails after successful wc_InitMutex() (WC_RNG_INIT_FLAG_LOCK_INITIALLY). * settings.h: WC_RESEED_INTERVAL (1UL << 48UL) -> (W64LIT(1) << 48) -- unsigned long is 32 bits on LLP64 targets (64-bit MinGW), making the shift undefined. * test.c: silence unused i in random_bank_test() when the reseed-interval check is configured out. --- wolfcrypt/src/random.c | 23 +++++++++++++++++++++++ wolfcrypt/src/rng_bank.c | 27 +++++++++++++++++++++++---- wolfcrypt/test/test.c | 2 ++ wolfssl/wolfcrypt/settings.h | 2 +- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 931078e64a3..1ce98c1113a 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3097,6 +3097,14 @@ static WARN_UNUSED_RESULT int _InitRng(WC_RNG* rng, } if (ret != 0) { + #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX + if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) { + /* covers wc_LockMutex() failure after successful + * wc_InitMutex() (WC_RNG_INIT_FLAG_LOCK_INITIALLY). */ + (void)wc_FreeMutex(&rng->mutex); + rng->flags &= ~WC_RNG_FLAG_FULL_MUTEX; + } + #endif #if defined(HAVE_HASHDRBG) && !defined(NO_SHA256) if (rng->drbgType == WC_DRBG_SHA256) { if (drbg_instantiated) { @@ -3535,6 +3543,14 @@ int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) &rng->lock, cur_lock, new_lock, cas_ret); } WC_CAS_WITH_RETRY_END; + if (cas_ret != 0) { + /* Aborted release (a port's retry clause): the latch is still ours + * and new_lock was never installed. Keep ownership consistent -- + * mutex included -- and percolate so the caller can retry the + * put. */ + return cas_ret; + } + #ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX if (rng->flags & WC_RNG_FLAG_FULL_MUTEX) (void)wc_UnLockMutex(&rng->mutex); @@ -3719,6 +3735,13 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { * path from _ENTROPY_INVALIDATED). */ ret = wc_RNG_DRBG_ScheduleReseed(rng); + if (ret == WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) { + /* No DRBG (direct-RDRAND et al.): nothing to schedule, and nothing + * whose staleness the latch would mark -- not a condemnable + * failure. Any applicable auxiliary-state purges below still + * run. */ + ret = 0; + } #ifdef WC_RNG_HAVE_POOL { diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 534a9a2a98e..3bcd3e65839 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -2386,7 +2386,7 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, #ifdef WC_RNG_HAVE_RBGC if (flags & WC_RNG_BANK_FLAG_RBGC) { ret = wc_RNG_DRBG_ReseedRBGC( - WC_RNG_BANK_INST_TO_RNG(inst), &bank->root_rng); + WC_RNG_BANK_INST_TO_RNG(drbg), &bank->root_rng); } else #endif @@ -2775,6 +2775,13 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) cas_ret); } WC_CAS_WITH_RETRY_END; + if (cas_ret != 0) { + /* Aborted release: the latch is still ours and new_lock was never + * installed; percolate so the caller can retry (see + * wc_RNG_lock_put()). */ + return cas_ret; + } + if (new_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) return NEEDS_RECOVERY_E; else @@ -2857,7 +2864,9 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *in &inst->lock, cur_lock, new_lock, cas_ret); } WC_CAS_WITH_RETRY_END; - return 0; + /* 0 unless a port's retry clause aborted; the lock word is then + * untouched, so percolation is the whole handling. */ + return cas_ret; } WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) @@ -2876,7 +2885,8 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *in &inst->lock, cur_lock, cur_lock | extra_bits, cas_ret); } WC_CAS_WITH_RETRY_END; - return 0; + /* see wc_rng_bank_inst_lock_set_extra() re nonzero cas_ret. */ + return cas_ret; } WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits) @@ -2898,7 +2908,8 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst * &inst->lock, cur_lock, cur_lock & ~extra_bits, cas_ret); } WC_CAS_WITH_RETRY_END; - return 0; + /* see wc_rng_bank_inst_lock_set_extra() re nonzero cas_ret. */ + return cas_ret; } #ifdef HAVE_HASHDRBG @@ -2930,6 +2941,14 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_invalidate_entropy( cas_ret); } WC_CAS_WITH_RETRY_END; + if (cas_ret != 0) { + /* Latch or condemn, mirroring wc_RNG_invalidate_entropy(): an + * aborted latch leaves only the lost-update-racy counter guarding + * duplicated state. */ + WC_RNG_BANK_INST_TO_RNG(inst)->status = WC_DRBG_FAILED; + return cas_ret; + } + /* If no lock is held, the saturated reseedCtr is the only way to force * invalidation semantics on a lock-free consumer; if a lock is held, * the holder learns at unlock time. */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 7c3b966b920..3a19ec15b78 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28283,6 +28283,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) if (bankReseedCtr != WC_RESEED_INTERVAL) ERROR_OUT(WC_TEST_RET_ENC_I(bankReseedCtr), out); } + #else + (void)i; #endif #endif /* HAVE_HASHDRBG && !HAVE_INTEL_RDRAND */ diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 2352f7bcbba..ffdbb4c2546 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -4651,7 +4651,7 @@ #define WC_RESEED_INTERVAL UINT_MAX #elif defined(__x86_64__) || defined(__ia64__) || \ defined(__aarch64__) || defined(__mips64) - #define WC_RESEED_INTERVAL (1UL << 48UL) + #define WC_RESEED_INTERVAL (W64LIT(1) << 48) #else #define WC_RESEED_INTERVAL UINT_MAX #endif From 60f220abfdf2f31be70dca2524fe7844f670cef0 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 16 Sep 2026 17:55:36 -0500 Subject: [PATCH 102/102] linuxkm/lkcapi_sha_glue.c, wolfcrypt/src/random.c, wolfcrypt/src/rng_bank.c, wolfcrypt/test/test.c, wolfssl/wolfcrypt/random.h: fixes for a second batch of static-analysis findings. * vmgenid fallback poller: search the ACPI IDs the kernel vmgenid driver matches -- "VMGENCTR" and "VM_GEN_COUNTER" -- rather than the spec's mixed-case spellings. ACPICA upcases _HID/_CID strings when building the namespace and acpi_get_devices() matches by strcmp, so the old walk found QEMU only by its _HID and missed CID-only devices (Hyper-V, VMware). * wc_linuxkm_drbg_generate(): in non-vector builds, drop local_bh_disable() around the blockable CAN_WAIT reseed and reinit operations and retake it after, mirroring the vector arm's release/reacquire bracket; the checkout's migrate_disable() persists across the sleep, preserving CPU pinning preemptibly. * entropy daemon and sysfs stats dumps: gate the next-stir format fragments and arguments on WC_RNG_HAVE_NEXT_SEED. * new WC_LKM_BANK_RBGC_FLAG (WC_RNG_BANK_FLAG_RBGC when WC_RNG_HAVE_RBGC, else WC_RNG_BANK_FLAG_NONE): keep LKCAPI functional without RBGC support compiled in. * wc_rng_bank_init_nonce(): use (size_t)(-1) rather than SIZE_MAX in the allocation overflow check (C90 and NO_STDINT_H builds). * wc_rng_bank_fini(): pre-scan all instances for WC_RNG_LOCK_HELD before any mutation, returning BUSY_E (retryable) with refcount untouched, free hook unfired, and root intact. * random.h: #error on WC_RNG_DEBUG_STATS without the RNG lock facility. * wc_RNG_DRBG_Stir_Nonce(): reject nonce == NULL with nonzero nonceSz (BAD_FUNC_ARG), matching the other nonce-bearing APIs. * random_bank_test(): hoist held_inst to function scope and check it in from the shared teardown path, closing a leak on failing assertions. * wc_RNG_DRBG_NextSeedGenerate_local(): pointer declaration style cleanup. --- linuxkm/lkcapi_sha_glue.c | 75 ++++++++++++++++++++++++++++++-------- wolfcrypt/src/random.c | 8 ++-- wolfcrypt/src/rng_bank.c | 17 ++++++++- wolfcrypt/test/test.c | 6 ++- wolfssl/wolfcrypt/random.h | 4 ++ 5 files changed, 90 insertions(+), 20 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index a733909455b..99965e84bce 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2182,6 +2182,12 @@ static int linuxkm_affinity_unlock(void *arg) { #define WC_LINUXKM_ENTROPY_DAEMON_MAGIC 0x6f77666c +#ifdef WC_RNG_HAVE_RBGC + #define WC_LKM_BANK_RBGC_FLAG WC_RNG_BANK_FLAG_RBGC +#else + #define WC_LKM_BANK_RBGC_FLAG WC_RNG_BANK_FLAG_NONE +#endif + #if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \ defined(WC_RNG_HAVE_FREE_HOOK) && defined(WC_RNG_HAVE_LOCK) @@ -2336,7 +2342,7 @@ static int wc_linuxkm_rng_state_invalidate(void) { obj->bank, 0, -1, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, WC_LINUXKM_INITRNG_TIMEOUT_SEC, - WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_RBGC); + WC_RNG_BANK_FLAG_CAN_WAIT | WC_LKM_BANK_RBGC_FLAG); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); if ((this_ret != 0) && (ret == 0)) ret = this_ret; @@ -2592,15 +2598,18 @@ static void wc_linuxkm_vmgenid_poll(struct wc_linuxkm_vmgenid_poll_state *st, WC_RNG *local_root) { if (st->state == 0) { - /* one-time discovery, in daemon task context. The device's _CID - * is "VM_Gen_Counter" per the Microsoft spec (QEMU adds _HID - * "QEMUVGID"); acpi_get_devices() matches against both HID and - * CID lists. */ + /* one-time discovery, in daemon task context. ACPICA uppercases + * _HID/_CID strings when building the namespace, and + * acpi_get_devices() matches by strcmp, so the IDs here mirror + * the kernel vmgenid driver's own table verbatim: "VMGENCTR" + * (Microsoft spec _HID) and "VM_GEN_COUNTER" (the _CID as + * stored -- QEMU, Hyper-V, VMware all present it). */ void *found = NULL; - (void)acpi_get_devices("VM_Gen_Counter", wc_linuxkm_vmgenid_acpi_cb, + (void)acpi_get_devices("VMGENCTR", wc_linuxkm_vmgenid_acpi_cb, st, &found); if (found == NULL) - (void)acpi_get_devices("QEMUVGID", wc_linuxkm_vmgenid_acpi_cb, + (void)acpi_get_devices("VM_GEN_COUNTER", + wc_linuxkm_vmgenid_acpi_cb, st, &found); if (found != NULL) { memcpy(st->last, st->map, 16); @@ -2845,7 +2854,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) continue; } ret = wc_rng_bank_recover_inst(bank, i, 0 /* timeout_secs */, - WC_RNG_BANK_FLAG_RBGC | + WC_LKM_BANK_RBGC_FLAG | WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE); if (ret == 0) { (void)wc_rng_bank_inst_flags_down( @@ -3060,16 +3069,23 @@ static int wc_linuxkm_entropy_daemon(void *arg) " reseeds=" WC_RNG_STAT_FMT " stirs=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" +#ifdef WC_RNG_HAVE_NEXT_SEED " nextstirs_banked=" WC_RNG_STAT_FMT - " nextstirs_redeemed=" WC_RNG_STAT_FMT "\n", + " nextstirs_redeemed=" WC_RNG_STAT_FMT "\n" +#endif + , s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, s._stats_reseeds, s._stats_stirs, - s._stats_seed_failures, + s._stats_seed_failures +#ifdef WC_RNG_HAVE_NEXT_SEED + , s._stats_nextstirs_banked, - s._stats_nextstirs_redeemed); + s._stats_nextstirs_redeemed +#endif + ); } #endif /* WC_RNG_DEBUG_STATS */ #ifdef WC_LINUXKM_VMGENID_POLL @@ -3086,7 +3102,7 @@ static int wc_linuxkm_rng_bank_init(struct wc_rng_bank *ctx) { int ret; word32 flags = WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE | - WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING | WC_RNG_BANK_FLAG_RBGC; + WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING | WC_LKM_BANK_RBGC_FLAG; unsigned long uncredited_nonce = random_get_entropy(); if (wc_linuxkm_rng_initing_default_bank_flag && (default_bank != NULL)) { @@ -3240,16 +3256,23 @@ static void wc_linuxkm_rng_dump_stats(struct wc_rng_bank *ctx) " reseeds=" WC_RNG_STAT_FMT " stirs=" WC_RNG_STAT_FMT " seed_failures=" WC_RNG_STAT_FMT "\n" +#ifdef WC_RNG_HAVE_NEXT_SEED " stirs_banked=" WC_RNG_STAT_FMT - " stirs_redeemed=" WC_RNG_STAT_FMT "\n", + " stirs_redeemed=" WC_RNG_STAT_FMT "\n" +#endif + , s._stats_total_bytes_requested, s._stats_total_bytes_produced, s._stats_total_requests, s._stats_reseeds, s._stats_stirs, - s._stats_seed_failures, + s._stats_seed_failures +#ifdef WC_RNG_HAVE_NEXT_SEED + , s._stats_nextstirs_banked, - s._stats_nextstirs_redeemed); + s._stats_nextstirs_redeemed +#endif + ); } } @@ -3653,6 +3676,16 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, } #endif +#ifndef WOLFSSL_USE_SAVE_VECTOR_REGISTERS + /* The non-vector checkout hold is migrate_disable() + + * local_bh_disable(). Only BH-off blocks sleeping; drop it for + * the blockable reseed and retake it after. The checkout's + * migrate_disable() legally persists across the sleep, + * preserving the CPU pinning preemptibly -- the same property + * the vector arm's migrate_disable() bracket provides. */ + local_bh_enable(); +#endif + /* Reseed synchronously. wc_RNG_DRBG_Reseed_Now() resets the reseed * counter iff the reseed succeeds; on failure it leaves the counter * unmodified (the WC_RESEED_INTERVAL backstop still governs) and @@ -3702,6 +3735,9 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) migrate_enable(); #endif +#else /* !WOLFSSL_USE_SAVE_VECTOR_REGISTERS */ + /* retake the checkout's BH-off hold. */ + local_bh_disable(); #endif } } @@ -3760,6 +3796,12 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, if (lock_state & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) RESTORE_VECTOR_REGISTERS_MAYBE_INHIBITED(); } +#else /* !WOLFSSL_USE_SAVE_VECTOR_REGISTERS */ + /* The non-vector checkout hold is migrate_disable() + + * local_bh_disable(). Only BH-off blocks sleeping; drop it for + * the blockable reinit and retake it after (see the reseed leg + * above). */ + local_bh_enable(); #endif ret = wc_rng_bank_inst_reinit(NULL, drbg, @@ -3795,6 +3837,9 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, #if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)) migrate_enable(); #endif +#else /* !WOLFSSL_USE_SAVE_VECTOR_REGISTERS */ + /* retake the checkout's BH-off hold. */ + local_bh_disable(); #endif if (ret == 0) { diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 1ce98c1113a..b41e35ce562 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -2304,6 +2304,8 @@ int wc_RNG_DRBG_Stir_Nonce(WC_RNG* rng, { if (rng == NULL || seed == NULL) return BAD_FUNC_ARG; + if ((nonce == NULL) && (nonceSz != 0)) + return BAD_FUNC_ARG; if (rng->status != WC_DRBG_OK) return RNG_FAILURE_E; @@ -4734,13 +4736,13 @@ static WARN_UNUSED_RESULT WC_INLINE int NextStirPtrs(WC_RNG* rng, byte** seed, * test's error, leaving an empty bank for the next cycle. A gather failure * leaves the partial bank intact for retry. */ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( - WC_RNG* rng, WC_RNG *root, - const byte *nonce, word32 n) + WC_RNG* rng, WC_RNG* root, + const byte* nonce, word32 n) { int claim_ret; byte* seed = NULL; wolfSSL_Atomic_Int* lenp = NULL; - int *nextSeedRBGCStratum_p = NULL; + int* nextSeedRBGCStratum_p = NULL; WC_ATOMIC_INT_ARG cur; word32 nextSeedSz = 0; int ret; diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 3bcd3e65839..e43dc7d38cb 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -105,7 +105,7 @@ WOLFSSL_API int wc_rng_bank_init_nonce( /* the allocation below is sizeof(*ctx->rngs) * n_rngs; on targets where * size_t is narrow enough for that product to wrap, the initialization * loop would then run off the end of an undersized array. */ - if ((size_t)n_rngs > (SIZE_MAX / sizeof(*ctx->rngs))) + if ((size_t)n_rngs > ((size_t)(-1) / sizeof(*ctx->rngs))) return BAD_LENGTH_E; XMEMSET(ctx, 0, sizeof(*ctx)); @@ -354,6 +354,21 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { else if (wolfSSL_RefCur(ctx->refcount) < 1) return BAD_STATE_E; +#ifndef WC_RNG_BANK_STATIC + if (ctx->rngs) +#endif + { + /* A held instance aborts finalization with the bank fully intact -- + * refcount untouched, free hook unfired, root alive. BUSY_E: another + * actor holds a lease, definitively retryable. */ + for (i = 0; i < ctx->n_rngs; ++i) { + WC_RNG_lock_arg_t pre_lock_state = 0; + (void)wc_rng_bank_inst_lock_read(&ctx->rngs[i], &pre_lock_state); + if (pre_lock_state & WC_RNG_LOCK_HELD) + return BUSY_E; + } + } + wolfSSL_RefDec_IfEquals(&ctx->refcount, 1, &new_refcount, &ret); if (ret != 0) { #ifdef WC_VERBOSE_RNG diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 3a19ec15b78..c4f8826caa1 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28012,6 +28012,7 @@ static int rng_bank_affinity_unlock(void *arg) { WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) { + struct wc_rng_bank_inst *held_inst = NULL; int ret; WC_DECLARE_VAR(bank, struct wc_rng_bank, 1, HEAP_HINT); struct wc_rng_bank_inst *rng_inst = NULL; @@ -28193,7 +28194,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); { struct wc_rng_bank_inst *stale_inst = rng_inst; - struct wc_rng_bank_inst *held_inst = NULL; + /* held_inst hoisted to function scope (teardown checks in). */ ret = wc_rng_bank_checkout(bank, &held_inst, 2, 10, WC_RNG_BANK_FLAG_NONE); if (ret != 0) @@ -29126,6 +29127,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) out: + if (held_inst != NULL) + (void)wc_rng_bank_inst_checkin(&held_inst); + { int cleanup_ret; diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 82d054232f3..b8999f8736e 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -477,6 +477,10 @@ wc_static_assert(WC_RNG_RBGC_USER_SEED_STRATUM >= 256); #define WC_RNG_DEBUG_STATS #endif +#if defined(WC_RNG_DEBUG_STATS) && !defined(WC_RNG_HAVE_LOCK) + #error WC_RNG_DEBUG_STATS requires the RNG lock facility (WC_RNG_WANT_LOCK). +#endif + #ifdef WC_RNG_DEBUG_STATS #ifdef WORD64_AVAILABLE typedef word64 wc_rng_debug_counter_t;