From 0059f1647e60cd385b1a18f12bdbc147ecd096a1 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 7 Jan 2026 22:53:12 -0600 Subject: [PATCH] move WC_RNG_BANK_SUPPORT implementation from wolfcrypt/src/random.c and wolfssl/wolfcrypt/random.h to new files wolfcrypt/src/rng_bank.c and wolfssl/wolfcrypt/rng_bank.h; wolfcrypt/src/rng_bank.c: * add wc_local_rng_bank_checkout_for_bankref, wc_BankRef_Release(), wc_rng_bank_new(), and wc_rng_bank_free(); * in wc_rng_bank_checkin(), take a struct wc_rng_bank_inst **rng_inst and NULL it before return; * in wc_rng_bank_init(), add a devId arg, and handle devId in wc_rng_bank_inst_reinit(); * add WC_RNG_BANK_INST_LOCK_* and use them in wc_rng_bank_checkout() and wc_rng_bank_checkin(); * fix order of operations in wc_rng_bank_checkout() re DISABLE_VECTOR_REGISTERS(); wolfcrypt/src/random.c: * refactor per-instance salting for wc_rng_bank_inst: remove changes in Hash_df(), Hash_DRBG_Instantiate(), and _InitRng(), and in wc_rng_bank_init() and wc_rng_bank_inst_reinit(), use wc_InitRngNonce_ex() and pass the wc_rng_bank_inst pointer as the nonce; * simplify the WC_RNG_BANK_SUPPORT variant of wc_RNG_GenerateBlock() -- delegate to wc_local_rng_bank_checkout_for_bankref() and remove supplementary error checking; * in wc_FreeRng(), call wc_BankRef_Release() when WC_DRBG_BANKREF, and in wc_BankRef_Release(), fix refcount flub (not wolfSSL_RefFree, rather wolfSSL_RefDec); * streamline the WOLFSSL_LINUXKM wc_GenerateSeed(); wolfcrypt/test/test.c: add random_bank_test(); linuxkm/lkcapi_sha_glue.c: use WC_RNG_BANK_INST_TO_RNG() opportunistically; configure.ac: add --enable-amdrdseed as a synonym for --enable-amdrand; linuxkm/linuxkm_wc_port.h: when LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT, don't include get_random_bytes() in struct wolfssl_linuxkm_pie_redirect_table; add various comments for clarity. --- configure.ac | 10 +- linuxkm/linuxkm_wc_port.h | 4 + linuxkm/lkcapi_sha_glue.c | 26 +- linuxkm/module_exports.c.template | 3 + linuxkm/module_hooks.c | 2 + src/include.am | 11 + wolfcrypt/src/random.c | 751 ++---------------------------- wolfcrypt/src/rng_bank.c | 723 ++++++++++++++++++++++++++++ wolfcrypt/test/test.c | 347 +++++++++++++- wolfssl/wolfcrypt/include.am | 1 + wolfssl/wolfcrypt/random.h | 113 +---- wolfssl/wolfcrypt/rng_bank.h | 145 ++++++ 12 files changed, 1305 insertions(+), 831 deletions(-) create mode 100644 wolfcrypt/src/rng_bank.c create mode 100644 wolfssl/wolfcrypt/rng_bank.h diff --git a/configure.ac b/configure.ac index 446927502..70bc42b18 100644 --- a/configure.ac +++ b/configure.ac @@ -3998,12 +3998,17 @@ then fi # AMD RDSEED -AC_ARG_ENABLE([amdrand], - [AS_HELP_STRING([--enable-amdrand],[Enable AMD rdseed as preferred RNG seeding source (default: disabled)])], +AC_ARG_ENABLE([amdrdseed], + [AS_HELP_STRING([--enable-amdrdseed],[Enable AMD rdseed as preferred RNG seeding source (default: disabled)])], [ ENABLED_AMDRDSEED=$enableval ], [ ENABLED_AMDRDSEED=no ] ) +AC_ARG_ENABLE([amdrand], + [AS_HELP_STRING([--enable-amdrand],[Enable AMD rdseed as preferred RNG seeding source (default: disabled)])], + [ ENABLED_AMDRDSEED=$enableval ] + ) + if test "$ENABLED_AMDRDSEED" = "yes" then AM_CFLAGS="$AM_CFLAGS -DHAVE_AMD_RDSEED" @@ -11093,6 +11098,7 @@ AM_CONDITIONAL([BUILD_ECCSI],[test "x$ENABLED_ECCSI" = "xyes" || test "x$ENABLED AM_CONDITIONAL([BUILD_SAKKE],[test "x$ENABLED_SAKKE" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"]) AM_CONDITIONAL([BUILD_MEMORY],[test "x$ENABLED_MEMORY" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"]) AM_CONDITIONAL([BUILD_MEMUSE],[test "x$ENABLED_ENTROPY_MEMUSE" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"]) +AM_CONDITIONAL([BUILD_RNG_BANK],[test "$ENABLED_RNG_BANK" = "yes" || test "$ENABLED_USERSETTINGS" = "yes"]) AM_CONDITIONAL([BUILD_RSA],[test "x$ENABLED_RSA" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"]) AM_CONDITIONAL([BUILD_DH],[test "x$ENABLED_DH" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"]) AM_CONDITIONAL([BUILD_ASN],[test "x$ENABLED_ASN" != "xno" || test "x$ENABLED_RSA" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"]) diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 4e6497597..bb9dd03a7 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -937,7 +937,9 @@ typeof(kfree) *kfree; typeof(ksize) *ksize; +#ifndef LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT typeof(get_random_bytes) *get_random_bytes; +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) typeof(getnstimeofday) *getnstimeofday; #elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) @@ -1267,7 +1269,9 @@ #endif #define ksize WC_PIE_INDIRECT_SYM(ksize) +#ifndef LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT #define get_random_bytes WC_PIE_INDIRECT_SYM(get_random_bytes) +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) #define getnstimeofday WC_PIE_INDIRECT_SYM(getnstimeofday) #elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 846a9d5a0..1bdc56a8a 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -955,6 +955,7 @@ struct wc_swallow_the_semicolon #include #endif #include +#include static volatile int wc_linuxkm_drbg_init_tfm_disable_vector_registers = 0; @@ -1000,8 +1001,9 @@ static int wc_linuxkm_drbg_init_tfm(struct crypto_tfm *tfm) if (wc_linuxkm_drbg_init_tfm_disable_vector_registers) flags |= WC_RNG_BANK_FLAG_NO_VECTOR_OPS; - ret = wc_rng_bank_init(ctx, nr_cpu_ids + 4, flags, - WC_LINUXKM_INITRNG_TIMEOUT_SEC, NULL /* heap */); + ret = wc_rng_bank_init( + ctx, nr_cpu_ids + 4, flags, WC_LINUXKM_INITRNG_TIMEOUT_SEC, + NULL /* heap */, INVALID_DEVID); if (ret == 0) { ret = wc_rng_bank_set_affinity_handlers( @@ -1075,7 +1077,7 @@ static struct wc_rng_bank_inst *linuxkm_get_drbg(struct crypto_rng *tfm) { return ret; } -static void linuxkm_put_drbg(struct crypto_rng *tfm, struct wc_rng_bank_inst *drbg) { +static void linuxkm_put_drbg(struct crypto_rng *tfm, struct wc_rng_bank_inst **drbg) { struct wc_rng_bank *ctx = (struct wc_rng_bank *)crypto_rng_ctx(tfm); int ret = wc_rng_bank_checkin(ctx, drbg); if (ret != 0) { @@ -1114,6 +1116,10 @@ static inline struct crypto_rng *get_crypto_default_rng(void) { return current_crypto_default_rng; } +#ifndef WC_DRBG_BANKREF + #error LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT requires WC_DRBG_BANKREF support. +#endif + WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRef(WC_RNG* rng) { int ret; struct crypto_rng *current_crypto_default_rng = get_crypto_default_rng(); @@ -1146,7 +1152,7 @@ static int wc_linuxkm_drbg_generate(struct crypto_rng *tfm, } if (slen > 0) { - ret = wc_RNG_DRBG_Reseed(&drbg->rng, src, slen); + ret = wc_RNG_DRBG_Reseed(WC_RNG_BANK_INST_TO_RNG(drbg), src, slen); if (ret != 0) { pr_warn_once("WARNING: wc_RNG_DRBG_Reseed returned %d\n",ret); ret = -EINVAL; @@ -1157,7 +1163,7 @@ static int wc_linuxkm_drbg_generate(struct crypto_rng *tfm, for (;;) { #define RNG_MAX_BLOCK_LEN_ROUNDED (RNG_MAX_BLOCK_LEN & ~0xfU) if (dlen > RNG_MAX_BLOCK_LEN_ROUNDED) { - ret = wc_RNG_GenerateBlock(&drbg->rng, 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; @@ -1165,7 +1171,7 @@ static int wc_linuxkm_drbg_generate(struct crypto_rng *tfm, } #undef RNG_MAX_BLOCK_LEN_ROUNDED else { - ret = wc_RNG_GenerateBlock(&drbg->rng, dst, dlen); + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(drbg), dst, dlen); if (ret == 0) dlen = 0; } @@ -1206,7 +1212,7 @@ static int wc_linuxkm_drbg_generate(struct crypto_rng *tfm, out: - linuxkm_put_drbg(tfm, drbg); + linuxkm_put_drbg(tfm, &drbg); return ret; } @@ -1438,12 +1444,12 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) { continue; for (i = 0, V_offset = 0; i < len; ++i) { - ((struct DRBG_internal *)drbg->rng.drbg)->V[V_offset++] += ((byte *)buf)[i]; - if (V_offset == (int)sizeof ((struct DRBG_internal *)drbg->rng.drbg)->V) + ((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; } - wc_rng_bank_checkin(ctx, drbg); + wc_rng_bank_checkin(ctx, &drbg); if (can_sleep) { if (signal_pending(current)) return -EINTR; diff --git a/linuxkm/module_exports.c.template b/linuxkm/module_exports.c.template index 2784041f8..6af571d94 100644 --- a/linuxkm/module_exports.c.template +++ b/linuxkm/module_exports.c.template @@ -66,6 +66,9 @@ #include #endif #include + #ifdef WC_RNG_BANK_SUPPORT + #include + #endif #endif #include #include diff --git a/linuxkm/module_hooks.c b/linuxkm/module_hooks.c index 54cb0a36e..df7dad320 100644 --- a/linuxkm/module_hooks.c +++ b/linuxkm/module_hooks.c @@ -1288,7 +1288,9 @@ static int set_up_wolfssl_linuxkm_pie_redirect_table(void) { wolfssl_linuxkm_pie_redirect_table.kvfree = kvfree; #endif +#ifndef LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT wolfssl_linuxkm_pie_redirect_table.get_random_bytes = get_random_bytes; +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) wolfssl_linuxkm_pie_redirect_table.getnstimeofday = getnstimeofday; diff --git a/src/include.am b/src/include.am index 2b4c5faf6..fe9732063 100644 --- a/src/include.am +++ b/src/include.am @@ -185,6 +185,10 @@ if BUILD_MEMUSE src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/wolfentropy.c endif +if BUILD_RNG_BANK +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/rng_bank.c +endif + src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/kdf.c if BUILD_RSA @@ -434,6 +438,10 @@ if BUILD_MEMUSE src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/wolfentropy.c endif +if BUILD_RNG_BANK +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/rng_bank.c +endif + src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/kdf.c if BUILD_RSA @@ -789,6 +797,9 @@ src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/random.c if BUILD_MEMUSE src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/wolfentropy.c endif +if BUILD_RNG_BANK +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/rng_bank.c +endif endif endif !BUILD_FIPS_V2_PLUS diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 51feeca2b..4a50de6cb 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -68,6 +68,9 @@ This library contains implementation for the random number generator. #include +#ifdef WC_RNG_BANK_SUPPORT + #include +#endif #include #ifndef WC_NO_RNG /* if not FIPS and RNG is disabled then do not compile */ @@ -345,12 +348,6 @@ static int Hash_df(DRBG_internal* drbg, byte* out, word32 outSz, byte type, if (inB != NULL && inBSz > 0) ret = wc_Sha256Update(sha, inB, inBSz); } -#ifdef WC_RNG_BANK_SUPPORT - if (ret == 0) { - if (drbg->saltSz > 0) - ret = wc_Sha256Update(sha, drbg->salt, drbg->saltSz); - } -#endif if (ret == 0) ret = wc_Sha256Final(sha, digest); @@ -701,9 +698,6 @@ 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, -#ifdef WC_RNG_BANK_SUPPORT - const byte *salt, word32 saltSz, -#endif void* heap, int devId) { int ret = DRBG_FAILURE; @@ -716,16 +710,6 @@ static int Hash_DRBG_Instantiate(DRBG_internal* drbg, const byte* seed, word32 s (void)devId; #endif -#ifdef WC_RNG_BANK_SUPPORT - if ((salt == NULL) && (saltSz > 0)) - return BAD_FUNC_ARG; - else if (saltSz > sizeof(drbg->salt)) - return BAD_LENGTH_E; - XMEMSET(drbg->salt, 0, sizeof(drbg->salt)); - if (saltSz > 0) - XMEMCPY(drbg->salt, salt, saltSz); -#endif - #ifdef WOLFSSL_SMALL_STACK_CACHE #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) ret = wc_InitSha256_ex(&drbg->sha256, drbg->heap, drbg->devId); @@ -787,9 +771,6 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz) static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, -#ifdef WC_RNG_BANK_SUPPORT - const byte* salt, word32 saltSz, -#endif void* heap, int devId) { int ret = 0; @@ -928,11 +909,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, if (ret == 0) { ret = Hash_DRBG_Instantiate((DRBG_internal *)rng->drbg_scratch, - NULL /* seed */, 0, NULL /* nonce */, 0, -#ifdef WC_RNG_BANK_SUPPORT - salt, saltSz, -#endif - rng->heap, devId); + NULL /* seed */, 0, NULL /* nonce */, 0, rng->heap, devId); if (ret == 0) drbg_scratch_instantiated = 1; } @@ -1034,11 +1011,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz, #else seed, seedSz, #endif - nonce, nonceSz, - #ifdef WC_RNG_BANK_SUPPORT - salt, saltSz, - #endif - rng->heap, devId); + nonce, nonceSz, rng->heap, devId); } /* ret == 0 */ #ifdef WOLFSSL_SMALL_STACK @@ -1128,11 +1101,7 @@ int wc_rng_new_ex(WC_RNG **rng, byte* nonce, word32 nonceSz, return MEMORY_E; } - ret = _InitRng(*rng, nonce, nonceSz, -#ifdef WC_RNG_BANK_SUPPORT - NULL /* salt */, 0, -#endif - heap, devId); + ret = _InitRng(*rng, nonce, nonceSz, heap, devId); if (ret != 0) { XFREE(*rng, heap, DYNAMIC_TYPE_RNG); *rng = NULL; @@ -1158,42 +1127,26 @@ void wc_rng_free(WC_RNG* rng) WOLFSSL_ABI int wc_InitRng(WC_RNG* rng) { - return _InitRng(rng, NULL, 0, -#ifdef WC_RNG_BANK_SUPPORT - NULL, 0, -#endif - NULL, INVALID_DEVID); + return _InitRng(rng, NULL, 0, NULL, INVALID_DEVID); } int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId) { - return _InitRng(rng, NULL, 0, -#ifdef WC_RNG_BANK_SUPPORT - NULL, 0, -#endif - heap, devId); + return _InitRng(rng, NULL, 0, heap, devId); } int wc_InitRngNonce(WC_RNG* rng, byte* nonce, word32 nonceSz) { - return _InitRng(rng, nonce, nonceSz, -#ifdef WC_RNG_BANK_SUPPORT - NULL, 0, -#endif - NULL, INVALID_DEVID); + return _InitRng(rng, nonce, nonceSz, NULL, INVALID_DEVID); } int wc_InitRngNonce_ex(WC_RNG* rng, byte* nonce, word32 nonceSz, void* heap, int devId) { - return _InitRng(rng, nonce, nonceSz, -#ifdef WC_RNG_BANK_SUPPORT - NULL, 0, -#endif - heap, devId); + return _InitRng(rng, nonce, nonceSz, heap, devId); } #ifdef HAVE_HASHDRBG @@ -1384,35 +1337,22 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) WOLFSSL_ABI int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz) { - if ((rng == NULL) || (output == NULL)) + if (rng == NULL) return BAD_FUNC_ARG; if (rng->status == WC_DRBG_BANKREF) { int ret; struct wc_rng_bank_inst *bank_inst = NULL; - if ((rng->bankref == NULL) || - (! (rng->bankref->flags & WC_RNG_BANK_FLAG_INITED))) - { - return BAD_FUNC_ARG; - } - - ret = wc_rng_bank_checkout(rng->bankref, &bank_inst, 0, 0, - WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST | - WC_RNG_BANK_FLAG_CAN_WAIT | - WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | - WC_RNG_BANK_FLAG_AFFINITY_LOCK); + ret = wc_local_rng_bank_checkout_for_bankref(rng->bankref, &bank_inst); if (ret != 0) return ret; if (bank_inst == NULL) return BAD_STATE_E; - if (bank_inst->rng.status != WC_DRBG_OK) { - (void)wc_rng_bank_checkin(rng->bankref, bank_inst); - return BAD_STATE_E; - } - ret = wc_local_RNG_GenerateBlock(&bank_inst->rng, output, 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_checkin(rng->bankref, &bank_inst); if (checkin_ret != 0) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( @@ -1444,14 +1384,8 @@ int wc_FreeRng(WC_RNG* rng) return BAD_FUNC_ARG; #ifdef WC_RNG_BANK_SUPPORT - if (rng->status == WC_DRBG_BANKREF) { - if (rng->bankref == NULL) - return BAD_FUNC_ARG; - wolfSSL_RefFree(&rng->bankref->refcount); - rng->bankref = NULL; - rng->status = DRBG_NOT_INIT; - return 0; - } + if (rng->status == WC_DRBG_BANKREF) + return wc_BankRef_Release(rng); #endif /* WC_RNG_BANK_SUPPORT */ #if defined(WOLFSSL_ASYNC_CRYPT) @@ -1547,9 +1481,6 @@ static int wc_RNG_HealthTest_ex_internal(DRBG_internal* drbg, } #else if (Hash_DRBG_Instantiate(drbg, seedA, seedASz, nonce, nonceSz, -#ifdef WC_RNG_BANK_SUPPORT - NULL /* salt */, 0, -#endif heap, devId) != 0) { goto exit_rng_ht; } @@ -1613,11 +1544,7 @@ int wc_RNG_HealthTest_ex(int reseed, const byte* nonce, word32 nonceSz, #ifdef WOLFSSL_SMALL_STACK_CACHE ret = Hash_DRBG_Instantiate(drbg, - NULL /* seed */, 0, NULL /* nonce */, 0, -#ifdef WC_RNG_BANK_SUPPORT - NULL /* salt */, 0, -#endif - heap, devId); + NULL /* seed */, 0, NULL /* nonce */, 0, heap, devId); if (ret == 0) #endif { @@ -3207,32 +3134,10 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) #elif defined(WOLFSSL_LINUXKM) - /* When registering the kernel default DRBG with a native/intrinsic entropy - * source, fallback to get_random_bytes() isn't allowed because we replace - * it with our DRBG. - */ + #ifndef LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT + #include + #endif - #if defined(HAVE_ENTROPY_MEMUSE) && \ - defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) - - int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) - { - (void)os; - return wc_Entropy_Get(MAX_ENTROPY_BITS, output, sz); - } - - #elif (defined(HAVE_INTEL_RDSEED) || defined(HAVE_AMD_RDSEED)) && \ - defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) - - int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) - { - (void)os; - return wc_GenerateSeed_IntelRD(NULL, output, sz); - } - - #else /* !((HAVE_ENTROPY_MEMUSE || HAVE_*_RDSEED) && LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) */ - - #include int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) { (void)os; @@ -3240,11 +3145,9 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) #ifdef HAVE_ENTROPY_MEMUSE ret = wc_Entropy_Get(MAX_ENTROPY_BITS, output, sz); - if (ret == 0) { + if (ret == 0) return 0; - } #ifdef ENTROPY_MEMUSE_FORCE_FAILURE - /* Don't fallback to /dev/urandom. */ return ret; #endif #endif @@ -3252,23 +3155,30 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) #if defined(HAVE_INTEL_RDSEED) || defined(HAVE_AMD_RDSEED) if (IS_INTEL_RDSEED(intel_flags)) { ret = wc_GenerateSeed_IntelRD(NULL, output, sz); - #ifndef FORCE_FAILURE_RDSEED if (ret == 0) - #endif - { - return ret; - } + return 0; + #ifdef FORCE_FAILURE_RDSEED + return ret; + #endif } #endif /* HAVE_INTEL_RDSEED || HAVE_AMD_RDSEED */ + #ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT + #if !defined(HAVE_ENTROPY_MEMUSE) && \ + !defined(HAVE_INTEL_RDSEED) && \ + !defined(HAVE_AMD_RDSEED) + #error LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT requires an intrinsic entropy source. + #else + return ret; + #endif + #else (void)ret; get_random_bytes(output, sz); return 0; + #endif } - #endif /* !(HAVE_*_RDSEED && LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) */ - #elif defined(WOLFSSL_BSDKM) #include int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) @@ -3841,593 +3751,4 @@ int wc_hwrng_generate_block(byte *output, word32 sz) } #endif -#ifdef WC_RNG_BANK_SUPPORT - -WOLFSSL_API int wc_rng_bank_init( - struct wc_rng_bank *ctx, - int n_rngs, - word32 flags, - int timeout_secs, - void *heap) -{ - int i; - int ret; - int need_reenable_vec = 0; - - if ((ctx == NULL) || (n_rngs <= 0)) - return BAD_FUNC_ARG; - - XMEMSET(ctx, 0, sizeof(*ctx)); - - wolfSSL_RefInit(&ctx->refcount, &ret); - if (ret != 0) - return ret; - - ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; - ctx->heap = heap; - - ctx->rngs = (struct wc_rng_bank_inst *) - XMALLOC(sizeof(*ctx->rngs) * (size_t)n_rngs, - heap, DYNAMIC_TYPE_RNG); - if (! ctx->rngs) - ret = MEMORY_E; - - if (ret == 0) { - XMEMSET(ctx->rngs, 0, sizeof(*ctx->rngs) * (size_t)n_rngs); - ctx->n_rngs = n_rngs; - - for (i = 0; i < n_rngs; ++i) { -#ifdef WC_VERBOSE_RNG - int nretries = 0; -#endif - time_t ts1 = XTIME(0); - for (;;) { - time_t ts2; - if (flags & WC_RNG_BANK_FLAG_NO_VECTOR_OPS) - need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0); - ret = _InitRng(&ctx->rngs[i].rng, NULL, 0, (const byte *)&i, - sizeof(i), heap, INVALID_DEVID); - - if (need_reenable_vec) - REENABLE_VECTOR_REGISTERS(); - /* if we're allowed to sleep, relax the loop between each inner - * iteration even on success, assuring relaxation of the outer - * iterations. - */ - WC_RELAX_LONG_LOOP(); - if (ret == 0) - break; - /* 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. - */ - ret = WC_CHECK_FOR_INTR_SIGNALS(); - if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) - break; - ts2 = XTIME(0); - if (ts2 - ts1 > timeout_secs) { - ret = WC_TIMEOUT_E; - break; - } -#ifdef WC_VERBOSE_RNG - ++nretries; -#endif - } - if (ret != 0) { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "ERROR: wc_InitRng returned %d after %d retries.\n", ret, - nretries); -#endif - break; - } - } - } - - if (ret != 0) - (void)wc_rng_bank_fini(ctx); - - return ret; -} - -WOLFSSL_API 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) -{ - if ((ctx == NULL) || - (! (ctx->flags & WC_RNG_BANK_FLAG_INITED))) - { - return BAD_FUNC_ARG; - } - if ((affinity_lock_cb == NULL) ^ (affinity_unlock_cb == NULL)) - return BAD_FUNC_ARG; - if (wolfSSL_RefCur(ctx->refcount) != 1) - return BUSY_E; - ctx->affinity_lock_cb = affinity_lock_cb; - ctx->affinity_get_id_cb = affinity_get_id_cb; - ctx->affinity_unlock_cb = affinity_unlock_cb; - ctx->cb_arg = cb_arg; - return 0; -} - -WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { - int i; - - if ((ctx == NULL) || - (! (ctx->flags & WC_RNG_BANK_FLAG_INITED))) - { - return BAD_FUNC_ARG; - } - - if (wolfSSL_RefCur(ctx->refcount) > 1) { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_fini() called with refcount %d.\n", - wolfSSL_RefCur(ctx->refcount)); -#endif - return BUSY_E; - } - - if (ctx->rngs) { - for (i = 0; i < ctx->n_rngs; ++i) { - if (ctx->rngs[i].lock != 0) { - /* better to leak than to crash. */ -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_fini() called with RNG #%d still " - "locked.\n", i); -#endif - return BUSY_E; - } - } - - for (i = 0; i < ctx->n_rngs; ++i) { - wc_FreeRng(&ctx->rngs[i].rng); - } - - XFREE(ctx->rngs, ctx->heap, DYNAMIC_TYPE_RNG); - ctx->rngs = NULL; - ctx->n_rngs = 0; - } - - wolfSSL_RefFree(&ctx->refcount); - - ctx->flags = WC_RNG_BANK_FLAG_NONE; - ctx->cb_arg = NULL; - - return 0; -} - -/* wc_rng_bank_checkout() uses atomic operations to get exclusive ownership of a - * DRBG without delay. It expects to be called in uninterruptible context, - * though works fine in any context. When _PREFER_AFFINITY_INST, it starts by - * trying the DRBG matching the local DRBG (usually the current CPU ID, returned - * by bank->affinity_get_id_cb()), and if that doesn't immediately succeed, and - * _CAN_FAIL_OVER_INST, it iterates upward until one succeeds. The first - * attempt will always succeed, even under intense load, unless there is or has - * recently been a reseed or mix-in operation competing with generators. - */ -WOLFSSL_API int wc_rng_bank_checkout( - struct wc_rng_bank *bank, - struct wc_rng_bank_inst **rng, - int preferred_inst_offset, - int timeout_secs, - word32 flags) -{ - int new_lock_value, ret = 0; - time_t ts1, ts2; - int n_rngs_tried = 0; - - if ((bank == NULL) || - (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || - (rng == NULL)) - { - return BAD_FUNC_ARG; - } - - if ((flags & WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST) && - (bank->affinity_get_id_cb == NULL)) - { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_checkout() called with _PREFER_AFFINITY_INST but " - "no _get_id_cb.\n"); -#endif - return BAD_FUNC_ARG; - } - - if (flags & WC_RNG_BANK_FLAG_AFFINITY_LOCK) { - if ((bank->affinity_lock_cb == NULL) || - (bank->affinity_unlock_cb == NULL)) - { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_checkout() called with _AFFINITY_LOCK but " - "missing _lock_cb.\n"); -#endif - return BAD_FUNC_ARG; - } - ret = bank->affinity_lock_cb(bank->cb_arg); - if (ret == 0) - new_lock_value = 2; - else if (ret == WC_NO_ERR_TRACE(ALREADY_E)) - new_lock_value = 1; - else - return ret; - } - else { - new_lock_value = 1; - } - - 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); - if ((ret == 0) && ((preferred_inst_offset < 0) || - (preferred_inst_offset >= bank->n_rngs))) - { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "BUG: bank->affinity_get_id_cb() returned out-of-range inst ID " - "%d, with bank->n_rngs %d.\n", preferred_inst_offset, - bank->n_rngs); -#endif - ret = BAD_INDEX_E; - } - } - else { - if ((preferred_inst_offset < 0) || - (preferred_inst_offset >= bank->n_rngs)) - { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "BUG: wc_rng_bank_checkout() passed out-of-range inst ID %d, " - "with bank->n_rngs %d.\n", preferred_inst_offset, bank->n_rngs); -#endif - ret = BAD_INDEX_E; - } - } - - if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) - ts1 = XTIME(0); - else - ts1 = 0; /* mollify -Wmaybe-uninitialized... */ - - for (; ret == 0;) { - int expected = 0; - - if (wolfSSL_Atomic_Int_CompareExchange( - &bank->rngs[preferred_inst_offset].lock, - &expected, - new_lock_value)) - { - *rng = &bank->rngs[preferred_inst_offset]; - if ((flags | bank->flags) & WC_RNG_BANK_FLAG_NO_VECTOR_OPS) { - if (DISABLE_VECTOR_REGISTERS() == 0) - WOLFSSL_ATOMIC_STORE((*rng)->lock, new_lock_value | 4); - } - - if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && - (((DRBG_internal *)(*rng)->rng.drbg)->reseedCtr >= - WC_RESEED_INTERVAL) && - (flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) && - (n_rngs_tried < bank->n_rngs)) - { - WOLFSSL_ATOMIC_STORE((*rng)->lock, 0); - } - else { -#ifdef WC_VERBOSE_RNG - if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && - (((DRBG_internal *)(*rng)->rng.drbg)->reseedCtr >= - WC_RESEED_INTERVAL)) - { - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_checkout() returning RNG ID %d, " - "currently marked for reseed, to !_CAN_WAIT caller.\n", - preferred_inst_offset); - } -#endif - return 0; - } - } - - if (flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) { - if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && - (n_rngs_tried >= bank->n_rngs)) - { - ret = BUSY_E; - break; - } - ++preferred_inst_offset; - if (preferred_inst_offset >= bank->n_rngs) - preferred_inst_offset = 0; - ++n_rngs_tried; - } - else { - if (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) { - ret = BUSY_E; - break; - } - } - - if (flags & WC_RNG_BANK_FLAG_AFFINITY_LOCK) - (void)bank->affinity_unlock_cb(bank->cb_arg); - - ret = WC_CHECK_FOR_INTR_SIGNALS(); - if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) - return ret; - - if (timeout_secs > 0) { - ts2 = XTIME(0); - if (ts2 - ts1 >= timeout_secs) - return WC_TIMEOUT_E; - } - WC_RELAX_LONG_LOOP(); - - if (flags & WC_RNG_BANK_FLAG_AFFINITY_LOCK) { - ret = bank->affinity_lock_cb(bank->cb_arg); - if (ret) - return ret; - } - } - - if (new_lock_value & 2) - (void)bank->affinity_unlock_cb(bank->cb_arg); - return ret; -} - -WOLFSSL_API int wc_rng_bank_checkin( - struct wc_rng_bank *bank, - struct wc_rng_bank_inst *rng_inst) -{ - int migration_disabled = (WOLFSSL_ATOMIC_LOAD(rng_inst->lock) & 2); - int vec_ops_disabled = (WOLFSSL_ATOMIC_LOAD(rng_inst->lock) & 4); - - WOLFSSL_ATOMIC_STORE(rng_inst->lock, 0); - - if (vec_ops_disabled) - REENABLE_VECTOR_REGISTERS(); - - if (migration_disabled) - return bank->affinity_unlock_cb(bank->cb_arg); - else - return 0; -} - -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) -{ - int ret; - time_t ts1 = 0; - word32 saltSz; - DRBG_internal *drbg_internal; - byte salt[sizeof((DRBG_internal *)rng_inst->rng.drbg)->salt]; - - if ((rng_inst == NULL) || - (rng_inst->rng.drbg == NULL)) - { - return BAD_FUNC_ARG; - } - - if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) - ts1 = XTIME(0); - - drbg_internal = (DRBG_internal *)rng_inst->rng.drbg; - saltSz = drbg_internal->saltSz; - XMEMCPY(salt, drbg_internal->salt, saltSz); - - wc_FreeRng(&rng_inst->rng); - - for (;;) { - ret = _InitRng(&rng_inst->rng, NULL, 0, salt, - saltSz, bank->heap, INVALID_DEVID); - if (ret == 0) - break; - if (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_inst_reinit() returning err %d.\n", ret); -#endif - break; - } - - if (timeout_secs > 0) { - 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); -#endif - break; - } - } - } - - return ret; -} - -WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, - const byte* seed, word32 seedSz, - int timeout_secs, - word32 flags) -{ - int ret = 0; - int n; - - if ((bank == NULL) || - (! (bank->flags & WC_RNG_BANK_FLAG_INITED))) - { - return BAD_FUNC_ARG; - } - - 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. - */ - 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); - 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); -#endif - break; - } - else if (drbg->rng.drbg == NULL) { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_seed(): inst#%d has null .drbg.\n", n); -#endif - ret = BAD_STATE_E; - } - else if ((ret = Hash_DRBG_Reseed((DRBG_internal *)drbg->rng.drbg, seed, - seedSz)) != 0) - { -#ifdef WC_VERBOSE_RNG - WOLFSSL_DEBUG_PRINTF( - "WARNING: wc_rng_bank_seed(): Hash_DRBG_Reseed() for inst#%d " - "returned %d\n", n, ret); -#endif - } - - (void)wc_rng_bank_checkin(bank, drbg); - - if (ret != 0) - break; - } - - return ret; -} - -WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, - int timeout_secs, - word32 flags) -{ - int n; - int ret; - time_t ts1 = 0; - - if (! bank) - return BAD_FUNC_ARG; - - if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) - ts1 = XTIME(0); - - 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); - if (ret != 0) - return ret; - - ((struct DRBG_internal *)drbg->rng.drbg)->reseedCtr = - WC_RESEED_INTERVAL; - - if (flags & WC_RNG_BANK_FLAG_CAN_WAIT) { - byte scratch[4]; - for (;;) { - time_t ts2; - ret = wc_RNG_GenerateBlock(&drbg->rng, scratch, - (word32)sizeof(scratch)); - if (ret == 0) - break; - if ((timeout_secs <= 0) || - (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT))) - { - break; - } - ts2 = XTIME(0); - 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); -#endif - ret = WC_TIMEOUT_E; - break; - } - } -#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() " - "for DRBG #%d returned %d.", n, ret); -#endif - wc_rng_bank_checkin(bank, drbg); - if (ret == WC_NO_ERR_TRACE(WC_TIMEOUT_E)) - return ret; - ret = WC_CHECK_FOR_INTR_SIGNALS(); - if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) - return ret; - WC_RELAX_LONG_LOOP(); - } - else { - wc_rng_bank_checkin(bank, drbg); - } - } - - return 0; -} - -WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng) -{ - int ret; - - if ((bank == NULL) || - (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || - (rng == NULL)) - { - return BAD_FUNC_ARG; - } - - XMEMSET(rng, 0, sizeof(*rng)); - - wolfSSL_RefInc(&bank->refcount, &ret); - - if (ret != 0) - return ret; - - rng->heap = bank->heap; - rng->status = WC_DRBG_BANKREF; - rng->bankref = bank; - - return 0; -} - -WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng) { - int ret; - - if ((bank == NULL) || - (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || - (rng == NULL)) - { - return BAD_FUNC_ARG; - } - - *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; -} - -#endif /* WC_RNG_BANK_SUPPORT */ - #endif /* WC_NO_RNG */ diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c new file mode 100644 index 000000000..f23c80584 --- /dev/null +++ b/wolfcrypt/src/rng_bank.c @@ -0,0 +1,723 @@ +/* rng_bank.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include + +#ifdef WC_RNG_BANK_SUPPORT + +#include +#include + +WOLFSSL_API int wc_rng_bank_init( + struct wc_rng_bank *ctx, + int n_rngs, + word32 flags, + int timeout_secs, + void *heap, + int devId) +{ + int i; + int ret; + int need_reenable_vec = 0; + + if ((ctx == NULL) || (n_rngs <= 0)) + return BAD_FUNC_ARG; + + XMEMSET(ctx, 0, sizeof(*ctx)); + + wolfSSL_RefInit(&ctx->refcount, &ret); + if (ret != 0) + return ret; + + ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; + ctx->heap = heap; + + ctx->rngs = (struct wc_rng_bank_inst *) + XMALLOC(sizeof(*ctx->rngs) * (size_t)n_rngs, + heap, DYNAMIC_TYPE_RNG); + if (! ctx->rngs) + ret = MEMORY_E; + + if (ret == 0) { + XMEMSET(ctx->rngs, 0, sizeof(*ctx->rngs) * (size_t)n_rngs); + ctx->n_rngs = n_rngs; + + for (i = 0; i < n_rngs; ++i) { +#ifdef WC_VERBOSE_RNG + int nretries = 0; +#endif + time_t ts1 = XTIME(0); + for (;;) { + time_t ts2; + + if (flags & WC_RNG_BANK_FLAG_NO_VECTOR_OPS) + need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0); + ret = wc_InitRngNonce_ex( + WC_RNG_BANK_INST_TO_RNG(ctx->rngs + i), + (byte *)&ctx->rngs[i], sizeof(byte *), heap, devId); + + if (need_reenable_vec) + REENABLE_VECTOR_REGISTERS(); + /* if we're allowed to sleep, relax the loop between each inner + * iteration even on success, assuring relaxation of the outer + * iterations. + */ + WC_RELAX_LONG_LOOP(); + if (ret == 0) + break; + /* 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. + */ + ret = WC_CHECK_FOR_INTR_SIGNALS(); + if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) + break; + ts2 = XTIME(0); + if (ts2 - ts1 > timeout_secs) { + ret = WC_TIMEOUT_E; + break; + } +#ifdef WC_VERBOSE_RNG + ++nretries; +#endif + } + if (ret != 0) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "ERROR: wc_InitRng returned %d after %d retries.\n", ret, + nretries); +#endif + break; + } + } + } + + if (ret != 0) + (void)wc_rng_bank_fini(ctx); + + return ret; +} + +WOLFSSL_API int wc_rng_bank_new( + struct wc_rng_bank **ctx, + int n_rngs, + word32 flags, + int timeout_secs, + void *heap, + int devId) +{ + int ret; + + 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); + if (*ctx == NULL) + return MEMORY_E; + + ret = wc_rng_bank_init(*ctx, n_rngs, flags, timeout_secs, heap, devId); + + if (ret != 0) { + XFREE(*ctx, heap, DYNAMIC_TYPE_RNG); + *ctx = NULL; + } + + return ret; +} + +WOLFSSL_API 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) +{ + if ((ctx == NULL) || + (! (ctx->flags & WC_RNG_BANK_FLAG_INITED))) + { + return BAD_FUNC_ARG; + } + if ((affinity_lock_cb == NULL) ^ (affinity_unlock_cb == NULL)) + return BAD_FUNC_ARG; + if (wolfSSL_RefCur(ctx->refcount) != 1) + return BUSY_E; + ctx->affinity_lock_cb = affinity_lock_cb; + ctx->affinity_get_id_cb = affinity_get_id_cb; + ctx->affinity_unlock_cb = affinity_unlock_cb; + ctx->cb_arg = cb_arg; + return 0; +} + +WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) { + int i; + + if (ctx == NULL) + return BAD_FUNC_ARG; + + if (ctx->flags == WC_RNG_BANK_FLAG_NONE) + return 0; + + if (! (ctx->flags & WC_RNG_BANK_FLAG_INITED)) + return BAD_FUNC_ARG; + + if (wolfSSL_RefCur(ctx->refcount) > 1) + return BUSY_E; + + if (ctx->rngs) { + for (i = 0; i < ctx->n_rngs; ++i) { + if (ctx->rngs[i].lock != 0) { + /* better to leak than to crash. */ +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "BUG: wc_rng_bank_fini() called with RNG #%d still " + "locked.\n", i); +#endif + return BUSY_E; + } + } + + for (i = 0; i < ctx->n_rngs; ++i) { + wc_FreeRng(&ctx->rngs[i].rng); + } + + XFREE(ctx->rngs, ctx->heap, DYNAMIC_TYPE_RNG); + ctx->rngs = NULL; + ctx->n_rngs = 0; + } + + wolfSSL_RefFree(&ctx->refcount); + + ctx->flags = WC_RNG_BANK_FLAG_NONE; + ctx->cb_arg = NULL; + + return 0; +} + +WOLFSSL_API int wc_rng_bank_free(struct wc_rng_bank **ctx) { + int ret; + void *heap; + + if (ctx == NULL) + return BAD_FUNC_ARG; + + if (*ctx == NULL) + return 0; + + heap = (*ctx)->heap; + + ret = wc_rng_bank_fini(*ctx); + + if (ret == 0) { + XFREE(*ctx, heap, DYNAMIC_TYPE_RNG); + *ctx = NULL; + } + + return ret; +} + +/* wc_rng_bank_checkout() uses atomic operations to get exclusive ownership of a + * DRBG without delay. It expects to be called in uninterruptible context, + * though works fine in any context. When _PREFER_AFFINITY_INST, it starts by + * trying the DRBG matching the local DRBG (usually the current CPU ID, returned + * by bank->affinity_get_id_cb()), and if that doesn't immediately succeed, and + * _CAN_FAIL_OVER_INST, it iterates upward until one succeeds. The first + * attempt will always succeed, even under intense load, unless there is or has + * recently been a reseed or mix-in operation competing with generators. + */ +WOLFSSL_API 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) +{ + int new_lock_value = WC_RNG_BANK_INST_LOCK_HELD; + int ret = 0; + time_t ts1, ts2; + int n_rngs_tried = 0; + + if ((bank == NULL) || + (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || + (rng_inst == NULL)) + { + return BAD_FUNC_ARG; + } + + if ((flags & WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST) && + (bank->affinity_get_id_cb == NULL)) + { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "BUG: wc_rng_bank_checkout() called with _PREFER_AFFINITY_INST but " + "no _get_id_cb.\n"); +#endif + return BAD_FUNC_ARG; + } + + if (flags & WC_RNG_BANK_FLAG_AFFINITY_LOCK) { + if ((bank->affinity_lock_cb == NULL) || + (bank->affinity_unlock_cb == NULL)) + { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "BUG: wc_rng_bank_checkout() called with _AFFINITY_LOCK but " + "missing _lock_cb.\n"); +#endif + return BAD_FUNC_ARG; + } + 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)) + return ret; + } + + 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); + if (ret != 0) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "BUG: bank->affinity_get_id_cb() returned err %d.\n", ret); +#endif + } + else if (((preferred_inst_offset < 0) || + (preferred_inst_offset >= bank->n_rngs))) + { + ret = BAD_INDEX_E; + } + } + else { + if ((preferred_inst_offset < 0) || + (preferred_inst_offset >= bank->n_rngs)) + { + ret = BAD_INDEX_E; + } + } + + if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) + ts1 = XTIME(0); + else + ts1 = 0; /* mollify -Wmaybe-uninitialized... */ + + for (; ret == 0;) { + int expected = 0; + + if (wolfSSL_Atomic_Int_CompareExchange( + &bank->rngs[preferred_inst_offset].lock, + &expected, + new_lock_value)) + { + *rng_inst = &bank->rngs[preferred_inst_offset]; + + if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && + (((struct DRBG_internal *)(*rng_inst)->rng.drbg)->reseedCtr >= + WC_RESEED_INTERVAL) && + (flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) && + (n_rngs_tried < bank->n_rngs)) + { + WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); + } + else { +#ifdef WC_VERBOSE_RNG + if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && + (((struct DRBG_internal *)(*rng_inst)->rng.drbg)->reseedCtr >= + WC_RESEED_INTERVAL)) + { + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_checkout() returning RNG ID %d, " + "currently marked for reseed, to !_CAN_WAIT caller.\n", + preferred_inst_offset); + } + + /* Note that a caller can still encounter a PollAndReSeed() via + * wc_RNG_GenerateBlock() if a call bumps reseedCtr up to + * WC_RESEED_INTERVAL. In kernel mode, the default interval is + * the SP 800-90A max of 2.81E+14, which is unlikely to be + * reached in practice. + */ +#endif + + if ((flags | bank->flags) & WC_RNG_BANK_FLAG_NO_VECTOR_OPS) { + if (DISABLE_VECTOR_REGISTERS() == 0) + WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, new_lock_value | + WC_RNG_BANK_INST_LOCK_VEC_OPS_INH); + } + + return 0; /* Short-circuit return, holding onto RNG and affinity + * locks and vector register inhibition. + */ + } + } + + if (flags & WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) { + if ((! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) && + (n_rngs_tried >= bank->n_rngs)) + { + ret = BUSY_E; + break; /* jump to cleanup. */ + } + ++preferred_inst_offset; + if (preferred_inst_offset >= bank->n_rngs) + preferred_inst_offset = 0; + ++n_rngs_tried; + } + else { + if (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) { + ret = BUSY_E; + break; /* jump to cleanup. */ + } + } + + if (flags & WC_RNG_BANK_FLAG_AFFINITY_LOCK) + (void)bank->affinity_unlock_cb(bank->cb_arg); + + ret = WC_CHECK_FOR_INTR_SIGNALS(); + if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) + return ret; /* immediate return -- no locks held */ + + if (timeout_secs > 0) { + ts2 = XTIME(0); + if (ts2 - ts1 >= timeout_secs) + return WC_TIMEOUT_E; /* immediate return -- no locks held */ + } + WC_RELAX_LONG_LOOP(); + + if (flags & WC_RNG_BANK_FLAG_AFFINITY_LOCK) { + ret = bank->affinity_lock_cb(bank->cb_arg); + if (ret) + return ret; /* immediate return -- no locks held */ + } + + /* Note that we may have been migrated at this point, but it doesn't + * matter -- we only reach this point if we have to retry/iterate. + */ + } + + if (flags & WC_RNG_BANK_FLAG_AFFINITY_LOCK) + (void)bank->affinity_unlock_cb(bank->cb_arg); + + return ret; +} + +#ifdef WC_DRBG_BANKREF +WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref( + struct wc_rng_bank *bank, + struct wc_rng_bank_inst **rng_inst) +{ + return 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)); +} +#endif /* WC_DRBG_BANKREF */ + +WOLFSSL_API int wc_rng_bank_checkin( + struct wc_rng_bank *bank, + struct wc_rng_bank_inst **rng_inst) +{ + int lockval; + + if ((bank == NULL) || (rng_inst == NULL) || (*rng_inst == NULL)) + return BAD_FUNC_ARG; + + lockval = (int)WOLFSSL_ATOMIC_LOAD((*rng_inst)->lock); + + WOLFSSL_ATOMIC_STORE((*rng_inst)->lock, WC_RNG_BANK_INST_LOCK_FREE); + + *rng_inst = NULL; + + if (lockval & WC_RNG_BANK_INST_LOCK_VEC_OPS_INH) + REENABLE_VECTOR_REGISTERS(); + + if (lockval & WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED) + return bank->affinity_unlock_cb(bank->cb_arg); + else + return 0; +} + +/* 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. + */ +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) +{ + int ret; + time_t ts1 = 0; + int devId; + + if ((rng_inst == NULL) || + (rng_inst->rng.drbg == NULL)) + { + return BAD_FUNC_ARG; + } + + if ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) + ts1 = XTIME(0); + +#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) + devId = rng_inst->rng.devId; +#else + devId = INVALID_DEVID; +#endif + + wc_FreeRng(&rng_inst->rng); + + for (;;) { + ret = wc_InitRngNonce_ex(WC_RNG_BANK_INST_TO_RNG(rng_inst), + (byte *)&rng_inst, sizeof(byte *), + bank->heap, devId); + if (ret == 0) + break; + if (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_inst_reinit() returning err %d.\n", ret); +#endif + break; + } + + if (timeout_secs > 0) { + 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); +#endif + break; + } + } + } + + return ret; +} + +WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank, + const byte* seed, word32 seedSz, + int timeout_secs, + word32 flags) +{ + int ret = 0; + int n; + + if ((bank == NULL) || + (! (bank->flags & WC_RNG_BANK_FLAG_INITED))) + { + return BAD_FUNC_ARG; + } + + 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. + */ + 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); + 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); +#endif + break; + } + else if (drbg->rng.drbg == NULL) { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_seed(): inst#%d has null .drbg.\n", n); +#endif + ret = BAD_STATE_E; + } + else if ((ret = wc_RNG_DRBG_Reseed(WC_RNG_BANK_INST_TO_RNG(drbg), seed, + seedSz)) != 0) + { +#ifdef WC_VERBOSE_RNG + WOLFSSL_DEBUG_PRINTF( + "WARNING: wc_rng_bank_seed(): Hash_DRBG_Reseed() for inst#%d " + "returned %d\n", n, ret); +#endif + } + + (void)wc_rng_bank_checkin(bank, &drbg); + + if (ret != 0) + break; + } + + return ret; +} + +WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, + int timeout_secs, + word32 flags) +{ + int n; + int ret; + time_t ts1 = 0; + + if (! bank) + 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 ((timeout_secs > 0) && (flags & WC_RNG_BANK_FLAG_CAN_WAIT)) + ts1 = XTIME(0); + + 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); + if (ret != 0) + return ret; + + ((struct DRBG_internal *)drbg->rng.drbg)->reseedCtr = + 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)); + if (ret == 0) + break; + if ((timeout_secs <= 0) || + (! (flags & WC_RNG_BANK_FLAG_CAN_WAIT))) + { + break; + } + ts2 = XTIME(0); + 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); +#endif + ret = WC_TIMEOUT_E; + break; + } + } +#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() " + "for DRBG #%d returned %d.", n, ret); +#endif + wc_rng_bank_checkin(bank, &drbg); + if (ret == WC_NO_ERR_TRACE(WC_TIMEOUT_E)) + return ret; + ret = WC_CHECK_FOR_INTR_SIGNALS(); + if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E)) + return ret; + WC_RELAX_LONG_LOOP(); + } + else { + wc_rng_bank_checkin(bank, &drbg); + } + } + + return 0; +} + +#ifdef WC_DRBG_BANKREF + +WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng) +{ + int ret; + + if ((bank == NULL) || + (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || + (rng == NULL)) + { + return BAD_FUNC_ARG; + } + + XMEMSET(rng, 0, sizeof(*rng)); + + wolfSSL_RefInc(&bank->refcount, &ret); + + if (ret != 0) + return ret; + + rng->heap = bank->heap; + rng->status = WC_DRBG_BANKREF; + rng->bankref = bank; + + return 0; +} + +WOLFSSL_API int wc_BankRef_Release(WC_RNG *rng) +{ + int isZero = 0; + int ret = 0; + if (rng->bankref == NULL) + return BAD_FUNC_ARG; + wolfSSL_RefDec(&rng->bankref->refcount, &isZero, &ret); +#ifdef WC_VERBOSE_RNG + if (isZero) + WOLFSSL_DEBUG_PRINTF( + "BUG: wc_BankRef_Release() popped refcount to zero.\n"); +#else + (void)isZero; +#endif + rng->heap = NULL; + rng->status = WC_DRBG_NOT_INIT; + rng->bankref = NULL; + return ret; +} + +WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng) { + int ret; + + if ((bank == NULL) || + (! (bank->flags & WC_RNG_BANK_FLAG_INITED)) || + (rng == NULL)) + { + return BAD_FUNC_ARG; + } + + *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; +} + +#endif /* WC_DRBG_BANKREF */ + +#endif /* WC_RNG_BANK_SUPPORT */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 69176ad82..286c6348e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -325,6 +325,9 @@ static const byte const_byte_array[] = "A+Gd\0\0\0"; #include #if !defined(WC_NO_RNG) #include + #ifdef WC_RNG_BANK_SUPPORT + #include + #endif #endif #include #include @@ -687,6 +690,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dsa_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t srp_test(void); #ifndef WC_NO_RNG WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void); +#ifdef WC_RNG_BANK_SUPPORT +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void); +#endif #endif /* WC_NO_RNG */ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); #if defined(USE_CERT_BUFFERS_2048) && \ @@ -2133,6 +2139,12 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_FAIL("RANDOM test failed!\n", ret); else TEST_PASS("RANDOM test passed!\n"); +#ifdef WC_RNG_BANK_SUPPORT + if ((ret = random_bank_test()) != 0) + TEST_FAIL("RNGBANK test failed!\n", ret); + else + TEST_PASS("RNGBANK test passed!\n"); +#endif #endif /* WC_NO_RNG */ #ifdef WOLFSSL_SHAKE128 @@ -20133,7 +20145,338 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void) } #endif /* !HAVE_HASHDRBG || CUSTOM_RAND_GENERATE_BLOCK || HAVE_INTEL_RDRAND */ -#endif /* WC_NO_RNG */ + +#ifdef WC_RNG_BANK_SUPPORT + +static char *rng_bank_affinity_lock_lock; +static int rng_bank_affinity_lock(void *arg) { + rng_bank_affinity_lock_lock = (char *)arg; + return 0; +} + +static int rng_bank_affinity_get_id_id; +static int rng_bank_affinity_get_id(void *arg, int *id) { + if (rng_bank_affinity_lock_lock != (char *)arg) + return BAD_STATE_E; + rng_bank_affinity_lock_lock = (char *)arg + 1; + *id = rng_bank_affinity_get_id_id; + return 0; +} + +static int rng_bank_affinity_unlock(void *arg) { + rng_bank_affinity_lock_lock = (char *)arg + 2; + return 0; +} + +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) +{ + + int ret; + WC_DECLARE_VAR(bank, struct wc_rng_bank, 1, HEAP_HINT); + struct wc_rng_bank_inst *rng_inst = NULL; + struct wc_rng_bank *bank2 = NULL; + struct wc_rng_bank_inst *rng_inst2 = NULL; +#ifdef WC_DRBG_BANKREF + WC_RNG *rng = NULL, *rng2 = NULL; +#endif + static const char bank_arg[] = "hi"; + byte outbuf1[16], outbuf2[16]; + int i; + + WC_ALLOC_VAR_EX(bank, struct wc_rng_bank, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, + return WC_TEST_RET_ENC_EC(MEMORY_E)); + XMEMSET(bank, 0, sizeof(*bank)); + + ret = wc_rng_bank_init(NULL, 4, WC_RNG_BANK_FLAG_CAN_WAIT, 10, HEAP_HINT, INVALID_DEVID); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + ret = wc_rng_bank_init(bank, 4, WC_RNG_BANK_FLAG_CAN_WAIT, 10, HEAP_HINT, INVALID_DEVID); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + ret = wc_rng_bank_set_affinity_handlers( + bank, + rng_bank_affinity_lock, + rng_bank_affinity_get_id, + rng_bank_affinity_unlock, + (char *)bank_arg); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + ret = wc_rng_bank_new(&bank2, 4, WC_RNG_BANK_FLAG_NO_VECTOR_OPS, 10, HEAP_HINT, INVALID_DEVID); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + ret = wc_rng_bank_set_affinity_handlers( + bank2, + rng_bank_affinity_lock, + rng_bank_affinity_get_id, + rng_bank_affinity_unlock, + (char *)bank_arg); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + rng_bank_affinity_get_id_id = 4; + ret = wc_rng_bank_checkout(bank2, &rng_inst2, -1, 10, WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST | WC_RNG_BANK_FLAG_AFFINITY_LOCK); + if (ret != WC_NO_ERR_TRACE(BAD_INDEX_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + rng_bank_affinity_get_id_id = 2; + ret = wc_rng_bank_checkout(bank2, &rng_inst2, -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); + if (rng_inst2 != bank2->rngs + 2) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (rng_bank_affinity_lock_lock != bank_arg + 1) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + /* if we can, confirm that WC_RNG_BANK_FLAG_NO_VECTOR_OPS worked. */ +#if defined(WC_HAVE_VECTOR_SPEEDUPS) && \ + defined(WOLFSSL_KERNEL_MODE) && \ + defined(WC_C_DYNAMIC_FALLBACK) && \ + defined(HAVE_HASHDRBG) && \ + defined(WC_NO_INTERNAL_FUNCTION_POINTERS) + if (((struct DRBG_internal *)rng_inst2->rng.drbg)->sha256.sha_method != 7 /* SHA256_C */) + ERROR_OUT(WC_TEST_RET_ENC_I(((struct DRBG_internal *)rng_inst2->rng.drbg)->sha256.sha_method), out); +#endif + + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(rng_inst2), outbuf1, sizeof(outbuf1)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + ret = wc_rng_bank_checkin(bank2, &rng_inst2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (rng_inst2 != NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (rng_bank_affinity_lock_lock != bank_arg + 2) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + ret = wc_rng_bank_checkout(bank2, &rng_inst2, 3, 10, WC_RNG_BANK_FLAG_NONE); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (rng_inst2 != bank2->rngs + 3) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = wc_rng_bank_checkin(bank2, &rng_inst2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + rng_bank_affinity_get_id_id = 3; + ret = wc_rng_bank_checkout(bank2, &rng_inst2, -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); + if (rng_inst2 != bank2->rngs + 3) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(rng_inst2), outbuf2, sizeof(outbuf2)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + ret = wc_rng_bank_checkin(bank2, &rng_inst2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (rng_inst2 != NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + +#ifdef WC_DRBG_BANKREF + ret = wc_rng_new_bankref(bank2, &rng2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + rng_bank_affinity_get_id_id = 1; + ret = wc_RNG_GenerateBlock(rng2, outbuf1, sizeof(outbuf1)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif + + ret = wc_rng_bank_reseed(bank2, 10, WC_RNG_BANK_FLAG_NONE); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + for (i = 0; i < bank2->n_rngs; ++i) { + if (((struct DRBG_internal *)bank2->rngs[i].rng.drbg) + ->reseedCtr != WC_RESEED_INTERVAL) + { + ERROR_OUT(WC_TEST_RET_ENC_I(i), out); + } + } + + rng_bank_affinity_get_id_id = 0; + /* WC_RNG_BANK_FLAG_CAN_WAIT needed to avoiding warning message that the + * instance needs reseed. + */ + ret = wc_rng_bank_checkout(bank2, &rng_inst2, -1, 10, WC_RNG_BANK_FLAG_CAN_WAIT | 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); + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(rng_inst2), outbuf1, sizeof(outbuf1)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_checkin(bank2, &rng_inst2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + rng_bank_affinity_get_id_id = 1; + ret = wc_rng_bank_checkout(bank2, &rng_inst2, -1, 10, WC_RNG_BANK_FLAG_CAN_WAIT | 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); + ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(rng_inst2), outbuf2, sizeof(outbuf2)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_rng_bank_checkin(bank2, &rng_inst2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + +#ifdef WC_DRBG_BANKREF + if (wolfSSL_RefCur(bank2->refcount) != 2) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + ret = wc_rng_bank_free(&bank2); + if (ret != WC_NO_ERR_TRACE(BUSY_E)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + wc_rng_free(rng2); + rng2 = NULL; + + if (wolfSSL_RefCur(bank2->refcount) != 1) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif + + ret = wc_rng_bank_free(&bank2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (bank2 != NULL) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + rng_bank_affinity_get_id_id = 0; + + 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); + + 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); + + /* can't wc_rng_bank_seed() with _FLAG_CAN_WAIT while holding an inst -- + * deadlocks then times 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_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); + + 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); + + 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); + + if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, 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_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); + + 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); + + 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); + + /* even though we passed in the same seed, the state is different, because + * Hash_DRBG_Reseed() chains in the previous state, and also churns in the + * "type" only on reseed. + */ + if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + ret = wc_rng_bank_inst_reinit(bank, rng_inst, 10, WC_RNG_BANK_FLAG_CAN_WAIT); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), 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); + + if (XMEMCMP(outbuf1, outbuf2, sizeof(outbuf1)) == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + + ret = wc_rng_bank_checkin(bank, &rng_inst); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + +out: + + { + int cleanup_ret; + +#ifdef WC_DRBG_BANKREF + if (rng) { + cleanup_ret = wc_FreeRng(rng); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (rng2) { + cleanup_ret = wc_FreeRng(rng2); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } +#endif + + if (rng_inst) { + cleanup_ret = wc_rng_bank_checkin(bank, &rng_inst); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + if ((rng_inst != NULL) && (ret == 0)) + ret = WC_TEST_RET_ENC_NC; + } + if (rng_inst2) { + cleanup_ret = wc_rng_bank_checkin(bank2, &rng_inst2); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + if ((rng_inst2 != NULL) && (ret == 0)) + ret = WC_TEST_RET_ENC_NC; + } + cleanup_ret = wc_rng_bank_fini(bank); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + WC_FREE_VAR_EX(bank, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + cleanup_ret = wc_rng_bank_free(&bank2); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + if ((bank2 != NULL) && (ret == 0)) + ret = WC_TEST_RET_ENC_NC; + } + + return ret; +} + +#endif /* WC_RNG_BANK_SUPPORT */ + +#endif /* !WC_NO_RNG */ #ifndef MEM_TEST_SZ #define MEM_TEST_SZ 1024 @@ -31983,7 +32326,7 @@ static wc_test_ret_t ecc_test_deterministic_k(WC_RNG* rng) #endif WC_ALLOC_VAR_EX(key, ecc_key, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, - return MEMORY_E); + return WC_TEST_RET_ENC_EC(MEMORY_E)); ret = wc_ecc_init_ex(key, HEAP_HINT, devId); if (ret != 0) diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index ff1dc50ca..fd23791b9 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/wolfssl/wolfcrypt/include.am @@ -48,6 +48,7 @@ nobase_include_HEADERS+= \ wolfssl/wolfcrypt/chacha20_poly1305.h \ wolfssl/wolfcrypt/random.h \ wolfssl/wolfcrypt/wolfentropy.h \ + wolfssl/wolfcrypt/rng_bank.h \ wolfssl/wolfcrypt/ripemd.h \ wolfssl/wolfcrypt/rsa.h \ wolfssl/wolfcrypt/rc2.h \ diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index a76738245..3a337ece4 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -247,15 +247,6 @@ struct OS_Seed { #define RNG_HEALTH_TEST_CHECK_SIZE (WC_SHA256_DIGEST_SIZE * 4) -/* RNG health states */ -#define WC_DRBG_NOT_INIT 0 -#define WC_DRBG_OK 1 -#define WC_DRBG_FAILED 2 -#define WC_DRBG_CONT_FAILED 3 -#ifdef WC_RNG_BANK_SUPPORT - #define WC_DRBG_BANKREF 4 -#endif - struct DRBG_internal { #ifdef WORD64_AVAILABLE word64 reseedCtr; @@ -273,14 +264,18 @@ struct DRBG_internal { byte seed_scratch[DRBG_SEED_LEN]; byte digest_scratch[WC_SHA256_DIGEST_SIZE]; #endif -#ifdef WC_RNG_BANK_SUPPORT - #ifndef WC_DRBG_MAX_SALT_SZ - #define WC_DRBG_MAX_SALT_SZ 4 - #endif - word32 saltSz; - byte salt[WC_DRBG_MAX_SALT_SZ]; -#endif }; +#endif /* HAVE_HASHDRBG */ + +/* RNG health states */ +#define WC_DRBG_NOT_INIT 0 +#define WC_DRBG_OK 1 +#define WC_DRBG_FAILED 2 +#define WC_DRBG_CONT_FAILED 3 +#ifdef WC_RNG_BANK_SUPPORT + #define WC_DRBG_BANKREF 4 /* Marks the WC_RNG as a ref to a wc_rng_bank, + * with no usable DRBG of its own. + */ #endif /* RNG context */ @@ -407,92 +402,6 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng); void* heap, int devId); #endif /* HAVE_HASHDRBG */ -#ifdef WC_RNG_BANK_SUPPORT - -/* This facility allocates and manages a bank of persistent RNGs with thread - * safety and provisions for automatic affinity. It is typically used in kernel - * applications. - */ - -#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) - -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_inst { - wolfSSL_Atomic_Int lock; - WC_RNG rng; -}; - -struct wc_rng_bank { - wolfSSL_Ref refcount; - void *heap; - word32 flags; - 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; /* if mutable, caller is responsible for thread safety. */ - int n_rngs; - struct wc_rng_bank_inst *rngs; /* typically one per CPU ID, plus a few */ -}; - -WOLFSSL_API int wc_rng_bank_init( - struct wc_rng_bank *ctx, - int n_rngs, - word32 flags, - int timeout_secs, - void *heap); - -WOLFSSL_API 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); - -WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx); - -WOLFSSL_API int wc_rng_bank_checkout( - struct wc_rng_bank *bank, - struct wc_rng_bank_inst **rng, - int preferred_inst_offset, - int timeout_secs, - word32 flags); - -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_reinit( - struct wc_rng_bank *bank, - struct wc_rng_bank_inst *rng_inst, - 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); - -WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, - int timeout_secs, - word32 flags); - -WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng); - -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) - -#endif /* WC_DRBG_BANK_SUPPORT */ - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h new file mode 100644 index 000000000..205c1e5e3 --- /dev/null +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -0,0 +1,145 @@ +/* rng_bank.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/*! + \file wolfssl/wolfcrypt/rng_bank.h +*/ + +/* This facility allocates and manages a bank of persistent RNGs with thread + * safety and provisions for automatic affinity. It is typically used in kernel + * applications. + */ + +#ifndef WOLF_CRYPT_RNG_BANK_H +#define WOLF_CRYPT_RNG_BANK_H + +#include + +#ifdef WC_RNG_BANK_SUPPORT + +#ifdef WC_NO_RNG + #error WC_RNG_BANK_SUPPORT requires RNG support. +#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_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) + +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_inst { + wolfSSL_Atomic_Int lock; + WC_RNG rng; +}; + +struct wc_rng_bank { + wolfSSL_Ref refcount; + void *heap; + word32 flags; + 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; /* if mutable, caller is responsible for thread safety. */ + int n_rngs; + struct wc_rng_bank_inst *rngs; /* typically one per CPU ID, plus a few */ +}; + +WOLFSSL_API int wc_rng_bank_new( + struct wc_rng_bank **ctx, + int n_rngs, + word32 flags, + int timeout_secs, + void *heap, + int devId); + +WOLFSSL_API int wc_rng_bank_init( + struct wc_rng_bank *ctx, + int n_rngs, + word32 flags, + int timeout_secs, + void *heap, + int devId); + +WOLFSSL_API 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); + +WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx); + +WOLFSSL_API int wc_rng_bank_free(struct wc_rng_bank **ctx); + +WOLFSSL_API 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); + +WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref( + struct wc_rng_bank *bank, + struct wc_rng_bank_inst **rng_inst); + +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_reinit( + struct wc_rng_bank *bank, + struct wc_rng_bank_inst *rng_inst, + 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); + +WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank, + int timeout_secs, + word32 flags); + +#ifdef WC_DRBG_BANKREF +WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng); + +WOLFSSL_API int wc_BankRef_Release(WC_RNG *rng); + +WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); +#endif /* WC_DRBG_BANKREF */ + +#define WC_RNG_BANK_INST_TO_RNG(rng_inst) (&(rng_inst)->rng) + +#endif /* WC_RNG_BANK_SUPPORT */ + +#endif /* WOLF_CRYPT_RNG_BANK_H */