Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .wolfssl_known_macro_extras
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ WC_PUF_HELPER_COMPACT
WC_PUF_SHA3
WC_RNG_BANK_NO_DEFAULT_SUPPORT
WC_RNG_BLOCKING
WC_RNG_EXCL_TIMEOUT_SEC
WC_RSA_NONBLOCK_TIME
WC_RSA_NO_FERMAT_CHECK
WC_RTL8735B_NO_DERIVE_CACHE
Expand Down Expand Up @@ -1361,6 +1362,7 @@ fipsCastStatus_get
noinline
ssize_t
sun
taskYIELD
versal
wc_Des3_SetKey
wc_Tls13_HKDF_Expand_Label
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3688,6 +3688,14 @@ if(WOLFSSL_RNG_BANK)
list(APPEND WOLFSSL_DEFINITIONS "-DWC_RNG_BANK_SUPPORT")
endif()

# Thread-safe DRBG (share one WC_RNG between threads)
add_option("WOLFSSL_THREADSAFE_DRBG"
"Enable sharing one WC_RNG between threads (default: enabled)"
"yes" "yes;no")
if(NOT WOLFSSL_THREADSAFE_DRBG)
list(APPEND WOLFSSL_DEFINITIONS "-DWC_NO_DRBG_THREAD_SAFE")
endif()

# Valgrind (for unit tests)
add_option("WOLFSSL_VALGRIND"
"Enable valgrind for unit tests (default: disabled)"
Expand Down
2 changes: 2 additions & 0 deletions cmake/options.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,8 @@ extern "C" {
#cmakedefine FP_ECC
#undef WC_RNG_BANK_SUPPORT
#cmakedefine WC_RNG_BANK_SUPPORT
#undef WC_NO_DRBG_THREAD_SAFE
#cmakedefine WC_NO_DRBG_THREAD_SAFE
#undef HAVE_VALGRIND
#cmakedefine HAVE_VALGRIND
#undef HAVE_CRL_MONITOR
Expand Down
27 changes: 27 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2721,6 +2721,33 @@ then
AM_CFLAGS="$AM_CFLAGS -DWC_RNG_BANK_SUPPORT"
fi

# Thread-safe DRBG. Serializes the generate/reseed path of a single WC_RNG so
# one instance can be shared between threads. Needs real atomics, so a
# single-threaded build elects it off in random.h regardless of this setting.
#
# Default off for --enable-linuxkm: the kernel module reaches the DRBG through
# the RNG bank, which checks out an instance exclusively, so the per-generate
# atomic is redundant there and its wait has no place in an atomic context.
# Enable it explicitly with --enable-threadsafe-drbg.
AC_ARG_ENABLE([threadsafe-drbg],
[AS_HELP_STRING([--enable-threadsafe-drbg],[Enable sharing one WC_RNG between threads (default: enabled, except linuxkm)])],
[ ENABLED_THREADSAFE_DRBG=$enableval ],
[ if test "$ENABLED_LINUXKM" = "no"; then
ENABLED_THREADSAFE_DRBG=yes
else
ENABLED_THREADSAFE_DRBG=no
fi ]
)

if test "$ENABLED_THREADSAFE_DRBG" = "no"
then
AM_CFLAGS="$AM_CFLAGS -DWC_NO_DRBG_THREAD_SAFE"
elif test "x$enable_threadsafe_drbg" = "xyes" && \
test "$ENABLED_SINGLETHREADED" = "yes"
then
AC_MSG_ERROR([--enable-threadsafe-drbg is incompatible with --enable-singlethreaded.])
fi


# DTLS-SCTP
AC_ARG_ENABLE([sctp],
Expand Down
7 changes: 7 additions & 0 deletions doc/dox_comments/header_files/random.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ int wc_InitRng(WC_RNG* rng);
\param output buffer to which the block is copied
\param sz size of output in bytes

\note One WC_RNG may be shared between threads: the DRBG generate and
reseed path is serialized per instance. Build with
--disable-threadsafe-drbg (WC_NO_DRBG_THREAD_SAFE) to opt out where each
instance is only used by one thread at a time. This covers generate and
reseed only -- wc_InitRng() and wc_FreeRng() must not run concurrently
with a generate on the same instance.

_Example_
\code
RNG rng;
Expand Down
150 changes: 150 additions & 0 deletions wolfcrypt/src/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -686,9 +686,94 @@ static int Hash_DRBG_Reseed(DRBG_internal* drbg, const byte* seed, word32 seedSz
* and array_add_one (shared utility) which both must
* remain available to SHA-512-only builds */

#ifndef WC_NO_DRBG_THREAD_SAFE
/* Thread-safe DRBG support. Serializes this instance's generate and reseed
* path so one WC_RNG can be shared between threads; it is not a lock discipline
* and makes no claim about atomic-context callers.
*
* The wait is bounded the way the rng_bank spins are: it breaks out on an
* interrupting signal, and on WC_RNG_EXCL_TIMEOUT_SEC when that is defined.
* No timeout is applied by default -- the holder can legitimately be blocked
* in wc_GenerateSeed() for as long as the OS entropy source takes, and failing
* a generate on a slow entropy read would be worse than waiting for it.
*
* Returns 1 when this call took the flag and the caller must release it, 0 when
* exclusivity comes from the owner, or a negative error code. */
static int RngExclEnter(WC_RNG* rng)
{
#if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID)
/* The lock word carries the owner's pid, so a hold inherited through
* fork() (the parent's pid, which no thread here can match) is told apart
* from a live hold by this process without a second variable to race
* against. */
WC_ATOMIC_INT_ARG self = (WC_ATOMIC_INT_ARG)getpid();
#else
WC_ATOMIC_INT_ARG self = WC_RNG_EXCL_HELD;
#endif
WC_ATOMIC_INT_ARG expected = WC_RNG_EXCL_FREE;
#ifdef WC_RNG_EXCL_TIMEOUT_SEC
time_t ts1 = XTIME(0);
#endif

if (WOLFSSL_ATOMIC_LOAD(rng->excl) == WC_RNG_EXCL_OWNER) {
return 0;
}

while (! wolfSSL_Atomic_Int_CompareExchange(&rng->excl, &expected, self))
{
int intr_ret;

#if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID)
/* A failed exchange leaves the observed value in expected. Reclaim
* only a hold stamped with a different pid, and only by exchanging
* from that exact value, so a live holder -- including a sibling
* thread of this process, which stamps this same pid -- is never
* displaced. */
if ((expected != WC_RNG_EXCL_FREE) &&
(expected != WC_RNG_EXCL_OWNER) &&
(expected != self))
{
if (wolfSSL_Atomic_Int_CompareExchange(&rng->excl, &expected,
self))
{
return 1;
}
}
#endif

intr_ret = WC_CHECK_FOR_INTR_SIGNALS();
if (intr_ret != 0) {
return intr_ret;
}

#ifdef WC_RNG_EXCL_TIMEOUT_SEC
if (XTIME(0) - ts1 > (time_t)WC_RNG_EXCL_TIMEOUT_SEC) {
return WC_TIMEOUT_E;
}
#endif

expected = WC_RNG_EXCL_FREE;
WC_SPIN_RELAX();
}

return 1;
}

/* Only ever called by the thread that got 1 from RngExclEnter(). */
static void RngExclExit(WC_RNG* rng)
{
WOLFSSL_ATOMIC_STORE(rng->excl, WC_RNG_EXCL_FREE);
}
#endif /* !WC_NO_DRBG_THREAD_SAFE */

/* Returns: DRBG_SUCCESS and DRBG_FAILURE or BAD_FUNC_ARG on fail */
int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz)
{
#ifndef WC_NO_DRBG_THREAD_SAFE
int ret;
int excl;
#endif

if (rng == NULL || seed == NULL) {
return BAD_FUNC_ARG;
}
Expand All @@ -704,8 +789,22 @@ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz)
#endif
return BAD_FUNC_ARG;
}
#ifndef WC_NO_DRBG_THREAD_SAFE
/* Serialize against Generate on the same instance. */
excl = RngExclEnter(rng);
if (excl < 0) {
return excl;
}
ret = Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, seed, seedSz,
NULL, 0);
if (excl) {
RngExclExit(rng);
}
return ret;
#else
return Hash_DRBG_Reseed((DRBG_internal *)rng->drbg, seed, seedSz,
NULL, 0);
#endif
}
#endif
#ifdef WOLFSSL_DRBG_SHA512
Expand All @@ -719,8 +818,22 @@ int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz)
#endif
return BAD_FUNC_ARG;
}
#ifndef WC_NO_DRBG_THREAD_SAFE
/* Serialize against Generate on the same instance. */
excl = RngExclEnter(rng);
if (excl < 0) {
return excl;
}
ret = Hash512_DRBG_Reseed((DRBG_SHA512_internal *)rng->drbg512,
seed, seedSz, NULL, 0);
if (excl) {
RngExclExit(rng);
}
return ret;
#else
return Hash512_DRBG_Reseed((DRBG_SHA512_internal *)rng->drbg512,
seed, seedSz, NULL, 0);
#endif
}
#endif

Expand Down Expand Up @@ -1913,6 +2026,7 @@ static int _InitRng(WC_RNG* rng, byte* nonce, word32 nonceSz,
if (nonce == NULL && nonceSz != 0)
return BAD_FUNC_ARG;

/* Also initializes rng->excl where the thread-safe DRBG is enabled. */
XMEMSET(rng, 0, sizeof(*rng));

#ifdef WOLFSSL_HEAP_TEST
Expand Down Expand Up @@ -2525,6 +2639,9 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz)
#endif
{
int ret;
#ifndef WC_NO_DRBG_THREAD_SAFE
int excl = 0;
#endif

if (rng == NULL || output == NULL)
return BAD_FUNC_ARG;
Expand Down Expand Up @@ -2583,12 +2700,34 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz)
if (rng->status != DRBG_OK)
return RNG_FAILURE_E;

#ifndef WC_NO_DRBG_THREAD_SAFE
/* Serialize the DRBG core for callers sharing this instance; the paths
* above touch no DRBG state and stay outside. */
excl = RngExclEnter(rng);
if (excl < 0) {
return excl;
}

/* Re-check: the instance may have changed state while we waited. */
if (rng->status != DRBG_OK) {
if (excl) {
RngExclExit(rng);
}
return RNG_FAILURE_E;
}
#endif

#if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID)
if (rng->pid != getpid()) {
rng->pid = getpid();
ret = PollAndReSeed(rng);
if (ret != DRBG_SUCCESS) {
rng->status = DRBG_FAILED;
#ifndef WC_NO_DRBG_THREAD_SAFE
if (excl) {
RngExclExit(rng);
}
#endif
return RNG_FAILURE_E;
}
}
Expand Down Expand Up @@ -2636,6 +2775,12 @@ int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz)
ret = RNG_FAILURE_E;
rng->status = DRBG_FAILED;
}

#ifndef WC_NO_DRBG_THREAD_SAFE
if (excl) {
RngExclExit(rng);
}
#endif
#else

/* if we get here then there is an RNG configuration error */
Expand Down Expand Up @@ -2791,6 +2936,11 @@ int wc_FreeRng(WC_RNG* rng)
}
#endif

#ifndef WC_NO_DRBG_THREAD_SAFE
/* Last, so a WC_RNG re-instantiated in place does not start out marked. */
WOLFSSL_ATOMIC_STORE(rng->excl, WC_RNG_EXCL_FREE);
#endif

return ret;
}

Expand Down
Loading
Loading