Merge pull request #9616 from douzzer/20251230-persistent-drbg

20251230-persistent-drbg
This commit is contained in:
David Garske
2026-01-08 10:54:45 -08:00
committed by GitHub
24 changed files with 1646 additions and 595 deletions

View File

@@ -790,6 +790,7 @@ WOLFSSL_MONT_RED_CT
WOLFSSL_MP_COND_COPY
WOLFSSL_MP_INVMOD_CONSTANT_TIME
WOLFSSL_MULTICIRCULATE_ALTNAMELIST
WOLFSSL_NEW_PRIME_CHECK
WOLFSSL_NONBLOCK_OCSP
WOLFSSL_NOSHA3_384
WOLFSSL_NOT_WINDOWS_API

View File

@@ -1428,6 +1428,7 @@ then
test "$enable_md5" = "" && enable_md5=yes
test "$enable_anon" = "" && enable_anon=yes
test "$enable_ssh" = "" && test "$enable_hmac" != "no" && enable_ssh=yes
test "$enable_rng_bank" = "" && enable_rng_bank=yes
# the compiler optimizer generates a weird out-of-bounds bss reference for
# find_hole() in the FP_ECC implementation.
@@ -2208,6 +2209,19 @@ then
AM_CFLAGS="$AM_CFLAGS -DWC_NO_RNG"
fi
AC_ARG_ENABLE([rng-bank],
[AS_HELP_STRING([--enable-rng-bank],[Enable compiling and using RNG banks (default: disabled)])],
[ ENABLED_RNG_BANK=$enableval ],
[ ENABLED_RNG_BANK=$KERNEL_MODE_DEFAULTS ]
)
if test "$ENABLED_RNG_BANK" = "yes"
then
AS_IF([test "$ENABLED_RNG" = "no"],
AC_MSG_ERROR([--enable-rng-bank requires --enable-rng]))
AM_CFLAGS="$AM_CFLAGS -DWC_RNG_BANK_SUPPORT"
fi
# DTLS-SCTP
AC_ARG_ENABLE([sctp],
@@ -3984,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"
@@ -11079,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"])

View File

@@ -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)
@@ -1072,9 +1074,7 @@
#endif /* !WOLFCRYPT_ONLY && !NO_CERTS */
#ifdef WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES
typeof(dump_stack) *dump_stack;
#endif
#ifdef CONFIG_ARM64
#ifndef CONFIG_ARCH_TEGRA
@@ -1269,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)
@@ -1345,9 +1347,7 @@
#endif /* !WOLFCRYPT_ONLY && !NO_CERTS */
#ifdef WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES
#define dump_stack WC_PIE_INDIRECT_SYM(dump_stack)
#endif
#undef preempt_count /* just in case -- not a macro on x86. */
#define preempt_count WC_PIE_INDIRECT_SYM(preempt_count)
@@ -1729,4 +1729,13 @@
#error unexpected BITS_PER_LONG value.
#endif
/* WC_DUMP_BACKTRACE_NONDEBUG is intended to dump a backtrace only if it hasn't
* already been dumped by the called function.
*/
#if defined(WOLFSSL_DEBUG_TRACE_ERROR_CODES) && defined(WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES)
#define WC_DUMP_BACKTRACE_NONDEBUG WC_DO_NOTHING
#else
#define WC_DUMP_BACKTRACE_NONDEBUG dump_stack()
#endif
#endif /* LINUXKM_WC_PORT_H */

View File

@@ -749,10 +749,7 @@ static int km_ffdhe_init(struct crypto_kpp *tfm, int name, word32 nbits)
ctx->name = name;
ctx->nbits = nbits;
if (WOLFSSL_ATOMIC_LOAD(linuxkm_lkcapi_registering_now))
err = LKCAPI_INITRNG_FOR_SELFTEST(&ctx->rng);
else
err = wc_InitRng(&ctx->rng);
err = LKCAPI_INITRNG(&ctx->rng);
if (err) {
#ifdef WOLFKM_DEBUG_DH
pr_err("%s: init rng returned: %d\n", WOLFKM_DH_DRIVER, err);

View File

@@ -387,10 +387,7 @@ static int km_ecdh_init(struct crypto_kpp *tfm, int curve_id)
ctx->curve_len = (word32) ret;
}
if (WOLFSSL_ATOMIC_LOAD(linuxkm_lkcapi_registering_now))
ret = LKCAPI_INITRNG_FOR_SELFTEST(&ctx->rng);
else
ret = wc_InitRng(&ctx->rng);
ret = LKCAPI_INITRNG(&ctx->rng);
if (ret) {
#ifdef WOLFKM_DEBUG_ECDH
pr_err("%s: init rng returned: %d\n", WOLFKM_ECDH_DRIVER, ret);

View File

@@ -634,14 +634,13 @@ out:
static inline int km_rsa_ctx_init_rng(struct km_rsa_ctx * ctx) {
switch (ctx->rng.status) {
case WC_DRBG_OK:
#ifdef WC_RNG_BANK_SUPPORT
case WC_DRBG_BANKREF:
#endif
return 0;
case WC_DRBG_NOT_INIT:
{
int err;
if (WOLFSSL_ATOMIC_LOAD(linuxkm_lkcapi_registering_now))
err = LKCAPI_INITRNG_FOR_SELFTEST(&ctx->rng);
else
err = wc_InitRng(&ctx->rng);
int err = LKCAPI_INITRNG(&ctx->rng);
if (err) {
pr_err("%s: init rng returned: %d\n", WOLFKM_RSA_DRIVER, err);
if (err == WC_NO_ERR_TRACE(MEMORY_E))
@@ -2105,7 +2104,7 @@ static int linuxkm_test_rsa_driver(const char * driver, int nbits)
memset(&rng, 0, sizeof(rng));
memset(key, 0, sizeof(RsaKey));
ret = LKCAPI_INITRNG_FOR_SELFTEST(&rng);
ret = LKCAPI_INITRNG(&rng);
if (ret) {
pr_err("error: init rng returned: %d\n", ret);
@@ -2483,7 +2482,7 @@ static int linuxkm_test_pkcs1pad_driver(const char * driver, int nbits,
memset(&rng, 0, sizeof(rng));
memset(key, 0, sizeof(RsaKey));
ret = LKCAPI_INITRNG_FOR_SELFTEST(&rng);
ret = LKCAPI_INITRNG(&rng);
if (ret) {
pr_err("error: init rng returned: %d\n", ret);
goto test_pkcs1_end;
@@ -3007,7 +3006,7 @@ static int linuxkm_test_pkcs1_driver(const char * driver, int nbits,
memset(&rng, 0, sizeof(rng));
memset(key, 0, sizeof(RsaKey));
ret = LKCAPI_INITRNG_FOR_SELFTEST(&rng);
ret = LKCAPI_INITRNG(&rng);
if (ret) {
pr_err("error: init rng returned: %d\n", ret);
goto test_pkcs1_end;

View File

@@ -955,38 +955,7 @@ struct wc_swallow_the_semicolon
#include <wolfssl/wolfcrypt/wolfentropy.h>
#endif
#include <wolfssl/wolfcrypt/random.h>
struct wc_linuxkm_drbg_ctx {
size_t n_rngs;
struct wc_rng_inst {
wolfSSL_Atomic_Int lock;
WC_RNG rng;
} *rngs; /* one per CPU ID */
};
static inline void wc_linuxkm_drbg_ctx_clear(struct wc_linuxkm_drbg_ctx * ctx)
{
unsigned int i;
if (ctx->rngs) {
for (i = 0; i < ctx->n_rngs; ++i) {
if (ctx->rngs[i].lock != 0) {
/* better to leak than to crash. */
pr_err("BUG: wc_linuxkm_drbg_ctx_clear called with DRBG #%d still locked.", i);
ctx->rngs = NULL;
ctx->n_rngs = 0;
return;
}
else
wc_FreeRng(&ctx->rngs[i].rng);
}
free(ctx->rngs);
ctx->rngs = NULL;
ctx->n_rngs = 0;
}
return;
}
#include <wolfssl/wolfcrypt/rng_bank.h>
static volatile int wc_linuxkm_drbg_init_tfm_disable_vector_registers = 0;
@@ -994,65 +963,71 @@ static volatile int wc_linuxkm_drbg_init_tfm_disable_vector_registers = 0;
#define WC_LINUXKM_INITRNG_TIMEOUT_SEC 30
#endif
static int linuxkm_affinity_lock(void *arg) {
(void)arg;
if (preempt_count() != 0)
return ALREADY_E;
#if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
migrate_disable(); /* this actually makes irq_count() nonzero, so that
* DISABLE_VECTOR_REGISTERS() is superfluous, but
* don't depend on that.
*/
#endif
local_bh_disable();
return 0;
}
static int linuxkm_affinity_get_id(void *arg, int *id) {
(void)arg;
*id = raw_smp_processor_id();
return 0;
}
static int linuxkm_affinity_unlock(void *arg) {
(void)arg;
local_bh_enable();
#if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
migrate_enable();
#endif
return 0;
}
static int wc_linuxkm_drbg_init_tfm(struct crypto_tfm *tfm)
{
struct wc_linuxkm_drbg_ctx *ctx = (struct wc_linuxkm_drbg_ctx *)crypto_tfm_ctx(tfm);
unsigned int i;
struct wc_rng_bank *ctx = (struct wc_rng_bank *)crypto_tfm_ctx(tfm);
int ret;
int need_reenable_vec = 0;
int can_sleep = (preempt_count() == 0);
word32 flags = WC_RNG_BANK_FLAG_CAN_WAIT;
ctx->n_rngs = nr_cpu_ids + 4;
ctx->rngs = (struct wc_rng_inst *)malloc(sizeof(*ctx->rngs) * ctx->n_rngs);
if (! ctx->rngs) {
ctx->n_rngs = 0;
return -ENOMEM;
}
XMEMSET(ctx->rngs, 0, sizeof(*ctx->rngs) * ctx->n_rngs);
if (wc_linuxkm_drbg_init_tfm_disable_vector_registers)
flags |= WC_RNG_BANK_FLAG_NO_VECTOR_OPS;
for (i = 0; i < ctx->n_rngs; ++i) {
int nretries = 0;
u64 ts1 = ktime_get_ns();
for (;;) {
u64 ts2;
if (wc_linuxkm_drbg_init_tfm_disable_vector_registers)
need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0);
ret = wc_InitRng(&ctx->rngs[i].rng);
if (need_reenable_vec)
REENABLE_VECTOR_REGISTERS();
if (can_sleep) {
/* if we're allowed to sleep, relax the loop between each inner
* iteration even on success, assuring relaxation of the outer
* iterations.
*/
cond_resched();
}
if (ret == 0)
break;
if (can_sleep) {
/* 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.
*/
if (WC_CHECK_FOR_INTR_SIGNALS() == WC_NO_ERR_TRACE(INTERRUPTED_E)) {
ret = -EINTR;
break;
}
}
ts2 = ktime_get_ns();
if (ts2 - ts1 > 1000000000L * WC_LINUXKM_INITRNG_TIMEOUT_SEC)
break;
++nretries;
}
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(
ctx,
linuxkm_affinity_lock,
linuxkm_affinity_get_id,
linuxkm_affinity_unlock,
NULL);
if (ret != 0) {
pr_warn("WARNING: wc_InitRng returned %d after %d retries.\n", ret, nretries);
ret = -EINVAL;
break;
(void)wc_rng_bank_fini(ctx);
pr_err("ERROR: wc_rng_bank_set_affinity_handlers() in wc_linuxkm_drbg_init_tfm() returned err %d\n", ret);
WC_DUMP_BACKTRACE_NONDEBUG;
}
}
if (ret != 0) {
wc_linuxkm_drbg_ctx_clear(ctx);
else {
pr_err("ERROR: wc_rng_bank_init() in wc_linuxkm_drbg_init_tfm() 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))
ret = -ETIMEDOUT;
else if (ret == WC_NO_ERR_TRACE(INTERRUPTED_E))
ret = -EINTR;
else
ret = -EINVAL;
}
return ret;
@@ -1060,101 +1035,54 @@ static int wc_linuxkm_drbg_init_tfm(struct crypto_tfm *tfm)
static void wc_linuxkm_drbg_exit_tfm(struct crypto_tfm *tfm)
{
struct wc_linuxkm_drbg_ctx *ctx = (struct wc_linuxkm_drbg_ctx *)crypto_tfm_ctx(tfm);
struct wc_rng_bank *ctx = (struct wc_rng_bank *)crypto_tfm_ctx(tfm);
int ret = wc_rng_bank_fini(ctx);
wc_linuxkm_drbg_ctx_clear(ctx);
if (ret != 0)
pr_err("ERROR: wc_rng_bank_fini() in wc_linuxkm_drbg_exit_tfm() returned err %d\n", ret);
return;
}
static int wc_linuxkm_drbg_default_instance_registered = 0;
/* get_drbg() 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. It starts by trying the DRBG matching the current
* CPU ID, and if that doesn't immediately succeed, 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.
*
* Note that wc_linuxkm_drbg_init_tfm() allocates at least 4 DRBGs, regardless
* of nominal core count, to avoid stalling generators on unicore targets.
*/
static struct wc_rng_bank_inst *linuxkm_get_drbg(struct crypto_rng *tfm) {
struct wc_rng_bank *ctx = (struct wc_rng_bank *)crypto_rng_ctx(tfm);
int err;
struct wc_rng_bank_inst *ret;
word32 flags =
WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST |
WC_RNG_BANK_FLAG_CAN_WAIT |
WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST;
static inline struct wc_rng_inst *get_drbg(struct crypto_rng *tfm) {
struct wc_linuxkm_drbg_ctx *ctx = (struct wc_linuxkm_drbg_ctx *)crypto_rng_ctx(tfm);
int n, new_lock_value;
/* check for mismatched handler or missing instance array. */
if ((tfm->base.__crt_alg->cra_init != wc_linuxkm_drbg_init_tfm) ||
(ctx->rngs == NULL))
{
/* check for mismatched handler. */
if (tfm->base.__crt_alg->cra_init != wc_linuxkm_drbg_init_tfm) {
pr_err("BUG: linuxkm_get_drbg() called on foreign tfm.\n");
return NULL;
}
if ((tfm == crypto_default_rng) && (preempt_count() == 0)) {
#if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
migrate_disable(); /* this actually makes irq_count() nonzero, so that
* DISABLE_VECTOR_REGISTERS() is superfluous, but
* don't depend on that.
*/
#endif
local_bh_disable();
new_lock_value = 2;
}
if (preempt_count() == 0)
flags |= WC_RNG_BANK_FLAG_AFFINITY_LOCK;
else
{
new_lock_value = 1;
flags |= WC_RNG_BANK_FLAG_NO_VECTOR_OPS;
err = wc_rng_bank_checkout(ctx, &ret, 0, WC_LINUXKM_INITRNG_TIMEOUT_SEC, flags);
if (err != 0) {
pr_err("ERROR: wc_rng_bank_checkout() in linuxkm_get_drbg() returned err %d.\n", err);
WC_DUMP_BACKTRACE_NONDEBUG;
return NULL;
}
n = raw_smp_processor_id();
for (;;) {
int expected = 0;
if (likely(__atomic_compare_exchange_n(&ctx->rngs[n].lock, &expected, new_lock_value, 0, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE)))
return &ctx->rngs[n];
++n;
if (n >= (int)ctx->n_rngs)
n = 0;
cpu_relax();
}
__builtin_unreachable();
return ret;
}
/* get_drbg_n() is used by bulk seed, mix-in, and reseed operations. It expects
* the caller to be able to wait until the requested DRBG is available. If the
* caller can't sleep and the requested DRBG is busy, it returns immediately --
* this avoids priority inversions and deadlocks.
*/
static inline struct wc_rng_inst *get_drbg_n(struct wc_linuxkm_drbg_ctx *ctx, int n, int can_spin) {
int can_sleep = (preempt_count() == 0);
for (;;) {
int expected = 0;
if (likely(__atomic_compare_exchange_n(&ctx->rngs[n].lock, &expected, 1, 0, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE)))
return &ctx->rngs[n];
if (can_sleep && can_spin) {
if (signal_pending(current))
return NULL;
cond_resched();
}
else
return NULL;
}
__builtin_unreachable();
}
static inline void put_drbg(struct wc_rng_inst *drbg) {
int migration_disabled = (drbg->lock == 2);
__atomic_store_n(&(drbg->lock),0,__ATOMIC_RELEASE);
if (migration_disabled) {
local_bh_enable();
#if defined(CONFIG_SMP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
migrate_enable();
#endif
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) {
pr_err("ERROR: wc_rng_bank_checkin() in linuxkm_put_drbg() returned err %d.\n", ret);
WC_DUMP_BACKTRACE_NONDEBUG;
}
}
@@ -1188,196 +1116,43 @@ static inline struct crypto_rng *get_crypto_default_rng(void) {
return current_crypto_default_rng;
}
static int drbg_init_from(WC_RNG *source_rng, struct DRBG_internal* dest_drbg) {
int ret;
int need_vec_reenable;
XMEMSET(dest_drbg, 0, sizeof(struct DRBG_internal));
need_vec_reenable = (DISABLE_VECTOR_REGISTERS() == 0);
/* Don't copy out the low level DRBG itself -- it contains sensitive secret
* state. Instead, use it to generate fresh V and C values in a
* non-intrusive way.
*/
ret = wc_RNG_GenerateBlock(source_rng, dest_drbg->V, sizeof dest_drbg->V);
if (ret != 0) {
pr_err("drbg_init_from: wc_RNG_GenerateBlock for V returned %d\n", ret);
goto out;
}
ret = wc_RNG_GenerateBlock(source_rng, dest_drbg->C, sizeof dest_drbg->C);
if (ret != 0) {
pr_err("drbg_init_from: wc_RNG_GenerateBlock for C returned %d\n", ret);
goto out;
}
dest_drbg->heap = source_rng->heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
dest_drbg->devId = source_rng->devId;
#ifndef WC_DRBG_BANKREF
#error LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT requires WC_DRBG_BANKREF support.
#endif
ret = wc_InitSha256_ex(&dest_drbg->sha256, dest_drbg->heap,
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
source_rng->dev_id
#else
INVALID_DEVID
#endif
);
if (ret != 0)
goto out;
dest_drbg->reseedCtr = 1;
ret = 0;
out:
if (need_vec_reenable)
REENABLE_VECTOR_REGISTERS();
return ret;
}
/* fork_default_rng() is a non-FIPS-compliant helper function to initialize an
* RNG for glue layer POSTs. Direct replacement for wc_InitRng(), and secure in
* principle, but not permissible to use as such in FIPS runtimes.
*/
static WC_MAYBE_UNUSED int fork_default_rng(WC_RNG *forked_rng) {
struct crypto_rng *current_crypto_default_rng;
struct wc_rng_inst *rng = NULL;
struct DRBG_internal *drbg = NULL;
struct DRBG_internal *drbg_scratch = NULL;
byte *health_check_scratch = NULL;
byte *newSeed_buf = NULL;
WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRef(WC_RNG* rng) {
int ret;
if (forked_rng == NULL)
return BAD_FUNC_ARG;
XMEMSET(forked_rng, 0, sizeof *forked_rng);
health_check_scratch =
(byte *)XMALLOC(RNG_HEALTH_TEST_CHECK_SIZE, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (health_check_scratch == NULL) {
ret = MEMORY_E;
goto out;
}
newSeed_buf = (byte*)XMALLOC(WC_DRBG_SEED_SZ +
WC_DRBG_SEED_BLOCK_SZ,
NULL,
DYNAMIC_TYPE_SEED);
if (newSeed_buf == NULL) {
ret = MEMORY_E;
goto out;
}
drbg = (struct DRBG_internal *)XMALLOC(sizeof *drbg, NULL,
DYNAMIC_TYPE_RNG);
if (drbg == NULL) {
ret = MEMORY_E;
goto out;
}
drbg_scratch =
(struct DRBG_internal *)XMALLOC(sizeof *drbg_scratch, NULL,
DYNAMIC_TYPE_RNG);
if (drbg_scratch == NULL) {
ret = MEMORY_E;
goto out;
}
current_crypto_default_rng = get_crypto_default_rng();
struct crypto_rng *current_crypto_default_rng = get_crypto_default_rng();
if (current_crypto_default_rng == NULL) {
ret = BAD_STATE_E;
goto out;
pr_warn_once("WARNING: get_crypto_default_rng() failed in linuxkm_InitRng_DefaultRef(); falling through to wc_InitRng().\n");
return wc_InitRng(rng);
}
rng = get_drbg(current_crypto_default_rng);
if (rng == NULL) {
ret = BAD_STATE_E;
goto out;
}
if (rng->rng.status != WC_DRBG_OK) {
pr_err("fork_default_rng: rng->rng.status = %d\n", rng->rng.status);
ret = RNG_FAILURE_E;
goto out;
}
XMEMCPY(forked_rng, &rng->rng, sizeof *forked_rng);
forked_rng->drbg = (struct DRBG *)drbg;
forked_rng->drbg_scratch = drbg_scratch;
forked_rng->health_check_scratch = health_check_scratch;
forked_rng->newSeed_buf = newSeed_buf;
ret = drbg_init_from(&rng->rng, (struct DRBG_internal*)forked_rng->drbg);
if (ret != 0)
goto out;
ret = drbg_init_from(&rng->rng, (struct DRBG_internal*)forked_rng->drbg_scratch);
if (ret != 0)
goto out;
put_drbg(rng);
rng = NULL;
{
byte scratch[4];
ret = wc_RNG_GenerateBlock(forked_rng, scratch, sizeof scratch);
if (ret != 0)
goto out;
}
ret = 0;
out:
if (ret == 0)
return ret;
else {
if (rng)
put_drbg(rng);
XFREE(drbg, rng->rng.heap, DYNAMIC_TYPE_RNG);
XFREE(drbg_scratch, rng->rng.heap, DYNAMIC_TYPE_RNG);
XFREE(health_check_scratch, rng->rng.heap, DYNAMIC_TYPE_RNG);
XFREE(newSeed_buf, rng->rng.heap, DYNAMIC_TYPE_RNG);
pr_warn("WARNING: fork_default_rng: ret=%d; falling through to wc_InitRng()\n", ret);
return wc_InitRng(forked_rng);
struct wc_rng_bank *default_bank = (struct wc_rng_bank *)crypto_rng_ctx(current_crypto_default_rng);
ret = wc_InitRng_BankRef(default_bank, rng);
return ret;
}
__builtin_unreachable();
}
#define LKCAPI_INITRNG(rng) linuxkm_InitRng_DefaultRef(rng)
#define LKCAPI_INITRNG_FOR_SELFTEST(rng) fork_default_rng(rng)
#else /* !LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT || !HAVE_HASHDRBG */
#define LKCAPI_INITRNG_FOR_SELFTEST(rng) wc_InitRng(rng)
#endif /* !LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT || !HAVE_HASHDRBG */
#endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT && HAVE_HASHDRBG */
static int wc_linuxkm_drbg_generate(struct crypto_rng *tfm,
const u8 *src, unsigned int slen,
u8 *dst, unsigned int dlen)
{
int ret, retried = 0;
int need_fpu_restore;
struct wc_rng_inst *drbg = get_drbg(tfm);
struct wc_rng_bank_inst *drbg = linuxkm_get_drbg(tfm);
if (! drbg) {
pr_err_once("BUG: get_drbg() failed.");
pr_err_once("BUG: linuxkm_get_drbg() failed.");
return -EFAULT;
}
/* for the default RNG, make sure we don't cache an underlying SHA256
* method that uses vector insns (forbidden from irq handlers).
*/
need_fpu_restore = (tfm == crypto_default_rng) ? (DISABLE_VECTOR_REGISTERS() == 0) : 0;
retry:
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;
@@ -1388,7 +1163,7 @@ retry:
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;
@@ -1396,38 +1171,48 @@ retry:
}
#undef RNG_MAX_BLOCK_LEN_ROUNDED
else {
ret = wc_RNG_GenerateBlock(&drbg->rng, dst, dlen);
dlen -= dlen;
ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(drbg), dst, dlen);
if (ret == 0)
dlen = 0;
}
if (dlen == 0)
break;
if (ret == 0)
continue;
if (unlikely(ret == WC_NO_ERR_TRACE(RNG_FAILURE_E)) && (! retried)) {
if (slen > 0)
break;
retried = 1;
wc_FreeRng(&drbg->rng);
ret = wc_InitRng(&drbg->rng);
ret = wc_rng_bank_inst_reinit((struct wc_rng_bank *)crypto_rng_ctx(tfm),
drbg,
WC_LINUXKM_INITRNG_TIMEOUT_SEC,
WC_RNG_BANK_FLAG_CAN_WAIT);
if (ret == 0) {
pr_warn("WARNING: reinitialized DRBG #%d after RNG_FAILURE_E.", raw_smp_processor_id());
goto retry;
pr_warn("WARNING: reinitialized DRBG #%d after RNG_FAILURE_E from wc_RNG_GenerateBlock().", raw_smp_processor_id());
continue;
}
else {
pr_warn_once("ERROR: reinitialization of DRBG #%d after RNG_FAILURE_E failed with ret %d.", raw_smp_processor_id(), ret);
ret = -EINVAL;
break;
}
}
else if (ret != 0) {
pr_warn_once("WARNING: wc_RNG_GenerateBlock returned %d\n",ret);
else {
pr_warn_once("ERROR: wc_linuxkm_drbg_generate() wc_RNG_GenerateBlock returned %d.\n",ret);
ret = -EINVAL;
break;
}
if (! dlen)
break;
}
out:
if (need_fpu_restore)
REENABLE_VECTOR_REGISTERS();
put_drbg(drbg);
linuxkm_put_drbg(tfm, &drbg);
return ret;
}
@@ -1435,13 +1220,10 @@ out:
static int wc_linuxkm_drbg_seed(struct crypto_rng *tfm,
const u8 *seed, unsigned int slen)
{
struct wc_linuxkm_drbg_ctx *ctx = (struct wc_linuxkm_drbg_ctx *)crypto_rng_ctx(tfm);
u8 *seed_copy = NULL;
int ret = 0;
int n;
struct wc_rng_bank *ctx = (struct wc_rng_bank *)crypto_rng_ctx(tfm);
int ret;
if ((tfm->base.__crt_alg->cra_init != wc_linuxkm_drbg_init_tfm) ||
(ctx->rngs == NULL))
if (tfm->base.__crt_alg->cra_init != wc_linuxkm_drbg_init_tfm)
{
pr_err_once("BUG: mismatched tfm.");
return -EFAULT;
@@ -1450,51 +1232,12 @@ static int wc_linuxkm_drbg_seed(struct crypto_rng *tfm,
if (slen == 0)
return 0;
seed_copy = (u8 *)malloc(slen + 2);
if (! seed_copy)
return -ENOMEM;
XMEMCPY(seed_copy + 2, seed, slen);
/* 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 = ctx->n_rngs - 1; n >= 0; --n) {
struct wc_rng_inst *drbg = get_drbg_n(ctx, n, 1);
if (! drbg) {
ret = -EINTR;
break;
}
/* perturb the seed with the CPU ID, so that no DRBG has the exact same
* seed.
*/
seed_copy[0] = (u8)(n >> 8);
seed_copy[1] = (u8)n;
{
/* for the default RNG, make sure we don't cache an underlying SHA256
* method that uses vector insns (forbidden from irq handlers).
*/
int need_fpu_restore = (tfm == crypto_default_rng) ? (DISABLE_VECTOR_REGISTERS() == 0) : 0;
ret = wc_RNG_DRBG_Reseed(&drbg->rng, seed_copy, slen + 2);
if (need_fpu_restore)
REENABLE_VECTOR_REGISTERS();
}
if (ret != 0) {
pr_warn_once("WARNING: wc_RNG_DRBG_Reseed returned %d\n",ret);
ret = -EINVAL;
}
put_drbg(drbg);
if (ret != 0)
break;
ret = wc_rng_bank_seed(ctx, seed, slen, WC_LINUXKM_INITRNG_TIMEOUT_SEC, WC_RNG_BANK_FLAG_CAN_WAIT);
if (ret != 0) {
pr_err("wc_rng_bank_seed() in wc_linuxkm_drbg_seed() returned err %d.\n", ret);
ret = -EINVAL;
}
free(seed_copy);
return ret;
}
@@ -1506,7 +1249,7 @@ static struct rng_alg wc_linuxkm_drbg = {
.cra_name = WOLFKM_STDRNG_NAME,
.cra_driver_name = WOLFKM_STDRNG_DRIVER,
.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY,
.cra_ctxsize = sizeof(struct wc_linuxkm_drbg_ctx),
.cra_ctxsize = sizeof(struct wc_rng_bank),
.cra_init = wc_linuxkm_drbg_init_tfm,
.cra_exit = wc_linuxkm_drbg_exit_tfm,
.cra_module = THIS_MODULE
@@ -1549,9 +1292,9 @@ static int wc_linuxkm_drbg_loaded = 0;
#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
static inline struct wc_linuxkm_drbg_ctx *get_default_drbg_ctx(void) {
static inline struct wc_rng_bank *get_default_drbg_ctx(void) {
struct crypto_rng *current_crypto_default_rng = get_crypto_default_rng();
struct wc_linuxkm_drbg_ctx *ctx = (current_crypto_default_rng ? (struct wc_linuxkm_drbg_ctx *)crypto_rng_ctx(current_crypto_default_rng) : NULL);
struct wc_rng_bank *ctx = (current_crypto_default_rng ? (struct wc_rng_bank *)crypto_rng_ctx(current_crypto_default_rng) : NULL);
if (ctx && (! ctx->rngs)) {
pr_err_once("BUG: get_default_drbg_ctx() found null ctx->rngs.");
return NULL;
@@ -1681,7 +1424,7 @@ static ssize_t wc_extract_crng_user(void __user *buf, size_t nbytes) {
}
static int wc_mix_pool_bytes(const void *buf, size_t len) {
struct wc_linuxkm_drbg_ctx *ctx;
struct wc_rng_bank *ctx;
size_t i;
int n;
int can_sleep = (preempt_count() == 0);
@@ -1693,19 +1436,20 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) {
return -EFAULT;
for (n = ctx->n_rngs - 1; n >= 0; --n) {
struct wc_rng_inst *drbg = get_drbg_n(ctx, n, 0);
struct wc_rng_bank_inst *drbg;
int V_offset;
if (! drbg)
if (wc_rng_bank_checkout(ctx, &drbg, n, 0, WC_RNG_BANK_FLAG_NONE) != 0)
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;
}
put_drbg(drbg);
wc_rng_bank_checkin(ctx, &drbg);
if (can_sleep) {
if (signal_pending(current))
return -EINTR;
@@ -1717,40 +1461,23 @@ static int wc_mix_pool_bytes(const void *buf, size_t len) {
}
static int wc_crng_reseed(void) {
struct wc_linuxkm_drbg_ctx *ctx = get_default_drbg_ctx();
int n;
struct wc_rng_bank *ctx = get_default_drbg_ctx();
int can_sleep = (preempt_count() == 0);
int ret;
if (! ctx)
return -EFAULT;
for (n = ctx->n_rngs - 1; n >= 0; --n) {
struct wc_rng_inst *drbg = get_drbg_n(ctx, n, 1);
if (! drbg)
return -EINTR;
((struct DRBG_internal *)drbg->rng.drbg)->reseedCtr = WC_RESEED_INTERVAL;
if (can_sleep) {
byte scratch[4];
int need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0);
int ret = wc_RNG_GenerateBlock(&drbg->rng, scratch, (word32)sizeof(scratch));
if (need_reenable_vec)
REENABLE_VECTOR_REGISTERS();
if (ret != 0)
pr_err("ERROR: wc_crng_reseed() wc_RNG_GenerateBlock() for DRBG #%d returned %d.", n, ret);
put_drbg(drbg);
if (signal_pending(current))
return -EINTR;
cond_resched();
}
else {
put_drbg(drbg);
}
ret = wc_rng_bank_reseed(ctx, WC_LINUXKM_INITRNG_TIMEOUT_SEC,
can_sleep
?
WC_RNG_BANK_FLAG_CAN_WAIT
:
WC_RNG_BANK_FLAG_NONE);
if (ret != 0) {
pr_err("ERROR: wc_rng_bank_reseed() returned err %d.\n", ret);
return -EINVAL;
}
else {
return 0;
}
return 0;
}
struct wolfssl_linuxkm_random_bytes_handlers random_bytes_handlers = {
@@ -2266,4 +1993,8 @@ static int wc_linuxkm_drbg_cleanup(void) {
#endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG */
#ifndef LKCAPI_INITRNG
#define LKCAPI_INITRNG(rng) wc_InitRng(rng)
#endif
#endif /* !WC_SKIP_INCLUDED_C_FILES */

View File

@@ -66,6 +66,9 @@
#include <wolfssl/wolfcrypt/wolfentropy.h>
#endif
#include <wolfssl/wolfcrypt/random.h>
#ifdef WC_RNG_BANK_SUPPORT
#include <wolfssl/wolfcrypt/rng_bank.h>
#endif
#endif
#include <wolfssl/wolfcrypt/coding.h>
#include <wolfssl/wolfcrypt/signature.h>

View File

@@ -297,8 +297,10 @@ void wc_linuxkm_relax_long_loop(void) {
*/
}
#endif
return;
}
#endif
cpu_relax();
}
#if defined(WC_LINUXKM_WOLFENTROPY_IN_GLUE_LAYER)
@@ -1286,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;
@@ -1475,9 +1479,7 @@ static int set_up_wolfssl_linuxkm_pie_redirect_table(void) {
#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
#endif /* !WOLFCRYPT_ONLY && !NO_CERTS */
#ifdef WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES
wolfssl_linuxkm_pie_redirect_table.dump_stack = dump_stack;
#endif
wolfssl_linuxkm_pie_redirect_table.preempt_count = my_preempt_count;
#ifndef _raw_spin_lock_irqsave

View File

@@ -332,7 +332,8 @@ WARN_UNUSED_RESULT int wc_save_vector_registers_x86(enum wc_svr_flags flags)
* a second look at preempt_count().
*/
if (((preempt_count() & (NMI_MASK | HARDIRQ_MASK)) != 0) || (task_pid_nr(current) == 0)) {
VRG_PR_WARN_X("WARNING: wc_save_vector_registers_x86 called with preempt_count 0x%x and pid %d on CPU %d.\n", preempt_count(), task_pid_nr(current), raw_smp_processor_id());
if (! (flags & WC_SVR_FLAG_INHIBIT))
VRG_PR_WARN_X("WARNING: wc_save_vector_registers_x86(0x%x) called with preempt_count 0x%x and pid %d on CPU %d.\n", (unsigned)flags, preempt_count(), task_pid_nr(current), raw_smp_processor_id());
return WC_ACCEL_INHIBIT_E;
}

View File

@@ -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

View File

@@ -659,6 +659,12 @@ const char* wc_GetErrorString(int error)
case MLKEM_PUB_HASH_E:
return "ML-KEM priv key's stored hash doesn't match encoded pub key";
case BUSY_E:
return "Object is busy";
case ALREADY_E:
return "Operation was redundant or preempted";
case MAX_CODE_E:
case WC_SPAN1_MIN_CODE_E:
case MIN_CODE_E:

View File

@@ -68,6 +68,9 @@ This library contains implementation for the random number generator.
#include <wolfssl/wolfcrypt/random.h>
#ifdef WC_RNG_BANK_SUPPORT
#include <wolfssl/wolfcrypt/rng_bank.h>
#endif
#include <wolfssl/wolfcrypt/cpuid.h>
#ifndef WC_NO_RNG /* if not FIPS and RNG is disabled then do not compile */
@@ -371,7 +374,8 @@ static int Hash_df(DRBG_internal* drbg, byte* out, word32 outSz, byte type,
#ifdef WC_VERBOSE_RNG
if (ret != 0)
WOLFSSL_DEBUG_PRINTF("%s failed with err = %d", __FUNCTION__, ret);
WOLFSSL_DEBUG_PRINTF("ERROR: %s failed with err = %d", __FUNCTION__,
ret);
#endif
return (ret == 0) ? DRBG_SUCCESS : DRBG_FAILURE;
@@ -414,7 +418,8 @@ static int Hash_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz
#ifdef WC_VERBOSE_RNG
if (ret != 0)
WOLFSSL_DEBUG_PRINTF("Hash_DRBG_Reseed failed with err %d.", ret);
WOLFSSL_DEBUG_PRINTF("ERROR: Hash_DRBG_Reseed failed with err %d.",
ret);
#endif
return ret;
@@ -545,7 +550,7 @@ static int Hash_gen(DRBG_internal* drbg, byte* out, word32 outSz, const byte* V)
* and (2) the caller will actually see the DRBG_FAILURE code, and is
* free to (and probably will) log it itself.
*/
WOLFSSL_DEBUG_PRINTF("Hash_gen failed with err %d.", ret);
WOLFSSL_DEBUG_PRINTF("ERROR: Hash_gen failed with err %d.", ret);
}
#endif
@@ -662,7 +667,8 @@ static int Hash_DRBG_Generate(DRBG_internal* drbg, byte* out, word32 outSz)
#ifdef WC_VERBOSE_RNG
if ((ret != DRBG_SUCCESS) && (ret != DRBG_FAILURE)) {
/* see note above regarding log spam reduction */
WOLFSSL_DEBUG_PRINTF("Hash_DRBG_Generate failed with err %d.", ret);
WOLFSSL_DEBUG_PRINTF("ERROR: Hash_DRBG_Generate failed with err %d.",
ret);
}
#endif
@@ -962,7 +968,9 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
ret = seedCb(&rng->seed, seed, seedSz);
if (ret != 0) {
#ifdef WC_VERBOSE_RNG
WOLFSSL_DEBUG_PRINTF("seedCb in _InitRng() failed with err = %d", ret);
WOLFSSL_DEBUG_PRINTF(
"ERROR: seedCb in _InitRng() failed with err = %d",
ret);
#endif
ret = DRBG_FAILURE;
}
@@ -974,7 +982,9 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG_EX("Seed generation failed... %d", ret);
#elif defined(WC_VERBOSE_RNG)
WOLFSSL_DEBUG_PRINTF("wc_GenerateSeed() in _InitRng() failed with err %d", ret);
WOLFSSL_DEBUG_PRINTF(
"ERROR: wc_GenerateSeed() in _InitRng() failed with err %d",
ret);
#endif
ret = DRBG_FAILURE;
rng->status = DRBG_FAILED;
@@ -988,7 +998,9 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
}
#elif defined(WC_VERBOSE_RNG)
if (ret != DRBG_SUCCESS) {
WOLFSSL_DEBUG_PRINTF("wc_RNG_TestSeed() in _InitRng() returned err %d.", ret);
WOLFSSL_DEBUG_PRINTF(
"ERROR: wc_RNG_TestSeed() in _InitRng() returned err %d.",
ret);
}
#endif
@@ -1166,7 +1178,8 @@ static int PollAndReSeed(WC_RNG* rng)
ret = seedCb(&rng->seed, newSeed, SEED_SZ + SEED_BLOCK_SZ);
if (ret != 0) {
#ifdef WC_VERBOSE_RNG
WOLFSSL_DEBUG_PRINTF("seedCb() in PollAndReSeed() failed with err %d", ret);
WOLFSSL_DEBUG_PRINTF("ERROR: seedCb() in PollAndReSeed() "
"failed with err %d", ret);
#endif
ret = DRBG_FAILURE;
}
@@ -1176,7 +1189,9 @@ static int PollAndReSeed(WC_RNG* rng)
SEED_SZ + SEED_BLOCK_SZ);
if (ret != 0) {
#ifdef WC_VERBOSE_RNG
WOLFSSL_DEBUG_PRINTF("wc_GenerateSeed() in PollAndReSeed() failed with err %d", ret);
WOLFSSL_DEBUG_PRINTF(
"ERROR: wc_GenerateSeed() in PollAndReSeed() failed with "
"err %d", ret);
#endif
ret = DRBG_FAILURE;
}
@@ -1186,7 +1201,9 @@ static int PollAndReSeed(WC_RNG* rng)
ret = wc_RNG_TestSeed(newSeed, SEED_SZ + SEED_BLOCK_SZ);
#ifdef WC_VERBOSE_RNG
if (ret != DRBG_SUCCESS)
WOLFSSL_DEBUG_PRINTF("wc_RNG_TestSeed() in PollAndReSeed() returned err %d.", ret);
WOLFSSL_DEBUG_PRINTF(
"ERROR: wc_RNG_TestSeed() in PollAndReSeed() returned "
"err %d.", ret);
#endif
}
if (ret == DRBG_SUCCESS)
@@ -1210,8 +1227,12 @@ static int PollAndReSeed(WC_RNG* rng)
#endif
/* place a generated block in output */
#ifdef WC_RNG_BANK_SUPPORT
static 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)
#endif
{
int ret;
@@ -1260,7 +1281,8 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz)
ret = (int)CUSTOM_RAND_GENERATE_BLOCK(output, sz);
#ifdef WC_VERBOSE_RNG
if (ret != 0)
WOLFSSL_DEBUG_PRINTF("CUSTOM_RAND_GENERATE_BLOCK failed with err %d.", ret);
WOLFSSL_DEBUG_PRINTF(
"ERROR: CUSTOM_RAND_GENERATE_BLOCK failed with err %d.", ret);
#endif
#else
@@ -1311,6 +1333,42 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz)
return ret;
}
#ifdef WC_RNG_BANK_SUPPORT
WOLFSSL_ABI
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 ret;
struct wc_rng_bank_inst *bank_inst = NULL;
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;
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);
if (checkin_ret != 0) {
#ifdef WC_VERBOSE_RNG
WOLFSSL_DEBUG_PRINTF(
"ERROR: wc_RNG_GenerateBlock() wc_rng_bank_checkin() "
"failed with err %d.", checkin_ret);
#endif
if (ret == 0)
ret = checkin_ret;
}
}
return ret;
}
else
return wc_local_RNG_GenerateBlock(rng, output, sz);
}
#endif
int wc_RNG_GenerateByte(WC_RNG* rng, byte* b)
{
@@ -1325,6 +1383,11 @@ int wc_FreeRng(WC_RNG* rng)
if (rng == NULL)
return BAD_FUNC_ARG;
#ifdef WC_RNG_BANK_SUPPORT
if (rng->status == WC_DRBG_BANKREF)
return wc_BankRef_Release(rng);
#endif /* WC_RNG_BANK_SUPPORT */
#if defined(WOLFSSL_ASYNC_CRYPT)
wolfAsync_DevCtxFree(&rng->asyncDev, WOLFSSL_ASYNC_MARKER_RNG);
#endif
@@ -3071,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 <linux/random.h>
#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 <linux/random.h>
int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
{
(void)os;
@@ -3104,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
@@ -3116,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 <sys/random.h>
int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)

723
wolfcrypt/src/rng_bank.c Normal file
View File

@@ -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 <wolfssl/wolfcrypt/libwolfssl_sources.h>
#ifdef WC_RNG_BANK_SUPPORT
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/rng_bank.h>
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 */

View File

@@ -1698,6 +1698,7 @@ void wolfSSL_RefWithMutexFree(wolfSSL_RefWithMutex* ref)
if (wc_FreeMutex(&ref->mutex) != 0) {
WOLFSSL_MSG("Failed to free mutex of reference counting!");
}
ref->count = 0;
}
void wolfSSL_RefWithMutexInc(wolfSSL_RefWithMutex* ref, int* err)

View File

@@ -325,6 +325,9 @@ static const byte const_byte_array[] = "A+Gd\0\0\0";
#include <wolfssl/wolfcrypt/arc4.h>
#if !defined(WC_NO_RNG)
#include <wolfssl/wolfcrypt/random.h>
#ifdef WC_RNG_BANK_SUPPORT
#include <wolfssl/wolfcrypt/rng_bank.h>
#endif
#endif
#include <wolfssl/wolfcrypt/wolfmath.h>
#include <wolfssl/wolfcrypt/coding.h>
@@ -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)

View File

@@ -186,7 +186,7 @@ struct WC_ASYNC_DEV;
typedef struct WC_ASYNC_SW {
void* ctx;
#if HAVE_ANONYMOUS_INLINE_AGGREGATES
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
union {
#endif
#ifdef HAVE_ECC
@@ -211,7 +211,7 @@ struct WC_ASYNC_DEV;
#ifndef NO_DES3
struct AsyncCryptSwDes des;
#endif /* !NO_DES3 */
#if HAVE_ANONYMOUS_INLINE_AGGREGATES
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
}; /* union */
#endif
byte type; /* enum WC_ASYNC_SW_TYPE */

View File

@@ -309,9 +309,11 @@ enum wolfCrypt_ErrorCodes {
INTERRUPTED_E = -1004, /* Process interrupted */
MLKEM_PUB_HASH_E = -1005, /* Encoded public key in decapsulation key does
* not match stored hash*/
BUSY_E = -1006, /* Object is busy */
ALREADY_E = -1007, /* Operation was redundant or preempted */
WC_SPAN2_LAST_E = -1005, /* Update to indicate last used error code */
WC_LAST_E = -1005, /* the last code used either here or in
WC_SPAN2_LAST_E = -1007, /* Update to indicate last used error code */
WC_LAST_E = -1007, /* the last code used either here or in
* error-ssl.h */
WC_SPAN2_MIN_CODE_E = -1999, /* Last usable code in span 2 */

View File

@@ -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 \

View File

@@ -247,12 +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
struct DRBG_internal {
#ifdef WORD64_AVAILABLE
word64 reseedCtr;
@@ -271,26 +265,57 @@ struct DRBG_internal {
byte digest_scratch[WC_SHA256_DIGEST_SIZE];
#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 */
struct WC_RNG {
struct OS_Seed seed;
void* heap;
#ifdef HAVE_HASHDRBG
/* Hash-based Deterministic Random Bit Generator */
struct DRBG* drbg;
#if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY)
struct DRBG_internal drbg_data;
#endif
#ifdef WOLFSSL_SMALL_STACK_CACHE
/* Scratch buffer slots -- everything is preallocated by _InitRng(). */
struct DRBG_internal *drbg_scratch;
byte *health_check_scratch;
byte *newSeed_buf;
#endif
byte status;
#endif /* HAVE_HASHDRBG */
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
union {
#endif
#ifdef WC_RNG_BANK_SUPPORT
struct wc_rng_bank *bankref;
#endif
#ifdef HAVE_HASHDRBG
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
struct {
#endif
/* Hash-based Deterministic Random Bit Generator */
struct DRBG* drbg;
#if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY)
struct DRBG_internal drbg_data;
#endif
#ifdef WOLFSSL_SMALL_STACK_CACHE
/* Scratch buffers -- all preallocated by _InitRng(). */
struct DRBG_internal *drbg_scratch;
byte *health_check_scratch;
byte *newSeed_buf;
#endif
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
};
#endif
#endif /* HAVE_HASHDRBG */
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
};
#endif
#if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID)
pid_t pid;
#endif

View File

@@ -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 <wolfssl/wolfcrypt/types.h>
#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 */

View File

@@ -431,6 +431,8 @@
(WOLFSSL_FIPS_VERSION2_CODE >= WOLFSSL_MAKE_FIPS_VERSION(major,minor))
#define FIPS_VERSION_GT(major,minor) \
(WOLFSSL_FIPS_VERSION2_CODE > WOLFSSL_MAKE_FIPS_VERSION(major,minor))
#define FIPS_VERSION_NE(major,minor) \
(WOLFSSL_FIPS_VERSION2_CODE != WOLFSSL_MAKE_FIPS_VERSION(major,minor))
#define FIPS_VERSION3_LT(major,minor,patch) \
(WOLFSSL_FIPS_VERSION_CODE < WOLFSSL_MAKE_FIPS_VERSION3(major,minor,patch))
@@ -3680,9 +3682,6 @@ extern void uITRON4_free(void *p) ;
#ifndef WOLFSSL_KERNEL_MODE
#define WOLFSSL_KERNEL_MODE
#endif
#ifndef WOLFSSL_API_PREFIX_MAP
#define WOLFSSL_API_PREFIX_MAP
#endif
#if defined(WOLFSSL_LINUXKM_VERBOSE_DEBUG) && \
!defined(WOLFSSL_KERNEL_VERBOSE_DEBUG)
#define WOLFSSL_KERNEL_VERBOSE_DEBUG
@@ -3715,21 +3714,11 @@ extern void uITRON4_free(void *p) ;
#ifndef USE_WOLF_STRTOK
#define USE_WOLF_STRTOK
#endif
#ifndef WOLFSSL_OLD_PRIME_CHECK
#define WOLFSSL_OLD_PRIME_CHECK
#endif
#ifdef LINUXKM_LKCAPI_REGISTER
#ifndef WC_TEST_EXPORT_SUBTESTS
#define WC_TEST_EXPORT_SUBTESTS
#endif
#endif
#ifndef WOLFSSL_TEST_SUBROUTINE
#ifdef WC_TEST_EXPORT_SUBTESTS
#define WOLFSSL_TEST_SUBROUTINE
#else
#define WOLFSSL_TEST_SUBROUTINE static
#endif
#endif
#undef HAVE_PTHREAD
/* linuxkm uses linux/string.h, included by linuxkm_wc_port.h. */
#undef HAVE_STRINGS_H
@@ -3790,21 +3779,6 @@ extern void uITRON4_free(void *p) ;
#undef HAVE_PUBLIC_FFDHE
#endif
#ifndef NO_OLD_WC_NAMES
#define NO_OLD_WC_NAMES
#endif
#ifndef NO_OLD_SHA_NAMES
#define NO_OLD_SHA_NAMES
#endif
#ifndef NO_OLD_MD5_NAME
#define NO_OLD_MD5_NAME
#endif
#ifndef OPENSSL_COEXIST
#define OPENSSL_COEXIST
#endif
#ifndef NO_OLD_SSL_NAMES
#define NO_OLD_SSL_NAMES
#endif
#undef WOLFSSL_MIN_AUTH_TAG_SZ
#define WOLFSSL_MIN_AUTH_TAG_SZ 4
@@ -3814,22 +3788,10 @@ extern void uITRON4_free(void *p) ;
*/
#define WOLFSSL_ASN_INT_LEAD_0_ANY
#endif
#if !defined(WC_RESEED_INTERVAL) && defined(LINUXKM_LKCAPI_REGISTER)
/* If installing handlers, use the maximum reseed interval allowed by
* NIST SP 800-90A Rev. 1, to avoid unnecessary delays in DRBG
* generation.
*/
#if defined(HAVE_FIPS) && FIPS_VERSION_LT(6,0)
#define WC_RESEED_INTERVAL UINT_MAX
#else
#define WC_RESEED_INTERVAL (((word64)1UL)<<48UL)
#endif
#endif
#if defined(__aarch64__) && !defined(WOLFSSL_AARCH64_PRIVILEGE_MODE)
#define WOLFSSL_AARCH64_PRIVILEGE_MODE
#endif
#endif
#endif /* WOLFSSL_LINUXKM */
/* FreeBSD Kernel Module */
#ifdef WOLFSSL_BSDKM
@@ -3869,16 +3831,6 @@ extern void uITRON4_free(void *p) ;
#ifndef USE_WOLF_STRTOK
#define USE_WOLF_STRTOK
#endif
#ifndef WOLFSSL_OLD_PRIME_CHECK
#define WOLFSSL_OLD_PRIME_CHECK
#endif
#ifndef WOLFSSL_TEST_SUBROUTINE
#ifndef NO_CRYPT_TEST
#define WOLFSSL_TEST_SUBROUTINE
#else
#define WOLFSSL_TEST_SUBROUTINE static
#endif
#endif
/* bsdkm uses kernel headers, included in bsdkm_wc_port.h. */
#undef HAVE_PTHREAD
#undef HAVE_STRINGS_H
@@ -3909,6 +3861,19 @@ extern void uITRON4_free(void *p) ;
#define WOLFSSL_SP_DIV_WORD_HALF
#endif
/* FreeBSD kernel defines its own min, max functions in sys/libkern.h */
#undef WOLFSSL_HAVE_MIN
#define WOLFSSL_HAVE_MIN
#undef WOLFSSL_HAVE_MAX
#define WOLFSSL_HAVE_MAX
#endif /* WOLFSSL_BSDKM */
/* Common setup for kernel mode builds */
#ifdef WOLFSSL_KERNEL_MODE
#ifndef WOLFSSL_API_PREFIX_MAP
#define WOLFSSL_API_PREFIX_MAP
#endif
#ifndef NO_OLD_WC_NAMES
#define NO_OLD_WC_NAMES
#endif
@@ -3925,18 +3890,36 @@ extern void uITRON4_free(void *p) ;
#define NO_OLD_SSL_NAMES
#endif
/* FreeBSD kernel defines its own min, max functions in sys/libkern.h */
#undef WOLFSSL_HAVE_MIN
#define WOLFSSL_HAVE_MIN
#ifndef WOLFSSL_TEST_SUBROUTINE
#ifdef WC_TEST_EXPORT_SUBTESTS
#define WOLFSSL_TEST_SUBROUTINE
#else
#define WOLFSSL_TEST_SUBROUTINE static
#endif
#endif
#undef WOLFSSL_HAVE_MAX
#define WOLFSSL_HAVE_MAX
#endif
#if !defined(WOLFSSL_OLD_PRIME_CHECK) && \
!defined(WOLFSSL_NEW_PRIME_CHECK) && !defined(HAVE_FIPS)
#define WOLFSSL_OLD_PRIME_CHECK
#endif
#if defined(WOLFSSL_KERNEL_MODE) && !defined(WC_NO_VERBOSE_RNG) && \
!defined(WC_VERBOSE_RNG)
#define WC_VERBOSE_RNG
#endif
#ifndef WC_RESEED_INTERVAL
/* In kernel mode, use the maximum reseed interval 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
#else
#define WC_RESEED_INTERVAL (((word64)1UL)<<48UL)
#endif
#endif
#if !defined(WC_NO_VERBOSE_RNG) && !defined(WC_VERBOSE_RNG)
#define WC_VERBOSE_RNG
#endif
#endif /* WOLFSSL_KERNEL_MODE */
#if defined(WC_SYM_RELOC_TABLES) && defined(HAVE_FIPS) && \
!defined(WC_PIE_RELOC_TABLES)

View File

@@ -125,7 +125,7 @@ typedef const char wcchar[];
/* if a version is available, pivot on the version, otherwise guess it's
* disallowed, subject to override.
*/
#if !defined(WOLF_C89) && (!defined(__STDC__) \
#if !defined(WOLF_C89) && !defined(_MSC_VER) && (!defined(__STDC__) \
|| (!defined(__STDC_VERSION__) && !defined(__cplusplus)) \
|| (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201101L)) \
|| (defined(__cplusplus) && (__cplusplus >= 201103L)))

View File

@@ -694,13 +694,15 @@ typedef struct wolfSSL_RefWithMutex {
#endif
int count;
} wolfSSL_RefWithMutex;
#define wolfSSL_RefWithMutexCur(ref) ((ref).count)
#if defined(WOLFSSL_ATOMIC_OPS) && !defined(SINGLE_THREADED)
typedef struct wolfSSL_Ref {
wolfSSL_Atomic_Int count;
} wolfSSL_Ref;
#define wolfSSL_RefCur(ref) WOLFSSL_ATOMIC_LOAD((ref).count)
#else
typedef struct wolfSSL_RefWithMutex wolfSSL_Ref;
#define wolfSSL_RefCur(ref) wolfSSL_RefWithMutexCur(ref)
#endif
#if defined(SINGLE_THREADED) || defined(WOLFSSL_ATOMIC_OPS)
@@ -710,7 +712,10 @@ typedef struct wolfSSL_RefWithMutex wolfSSL_Ref;
wolfSSL_Atomic_Int_Init(&(ref)->count, 1); \
*(err) = 0; \
} while(0)
#define wolfSSL_RefFree(ref) WC_DO_NOTHING
#define wolfSSL_RefFree(ref) \
do { \
wolfSSL_Atomic_Int_Init(&(ref)->count, 0); \
} while(0)
#define wolfSSL_RefInc(ref, err) \
do { \
(void)wolfSSL_Atomic_Int_FetchAdd(&(ref)->count, 1); \