Added support for getrandom()/getentropy(), and a fix for the RDRAND bug on AMD CPU (family 22).
This commit is contained in:
parent
924921d1be
commit
e4edfb84ed
3 changed files with 128 additions and 8 deletions
22
src/config.h
22
src/config.h
|
|
@ -109,9 +109,27 @@
|
|||
#define BR_RDRAND 1
|
||||
*/
|
||||
|
||||
/*
|
||||
* When BR_USE_GETENTROPY is enabled, the SSL engine will use the
|
||||
* getentropy() function to obtain quality randomness for seeding its
|
||||
* internal PRNG. On Linux and FreeBSD, getentropy() is implemented by
|
||||
* the standard library with the system call getrandom(); on OpenBSD,
|
||||
* getentropy() is the system call, and there is no getrandom() wrapper,
|
||||
* hence the use of the getentropy() function for maximum portability.
|
||||
*
|
||||
* If the getentropy() call fails, and BR_USE_URANDOM is not explicitly
|
||||
* disabled, then /dev/urandom will be used as a fallback mechanism. On
|
||||
* FreeBSD and OpenBSD, this does not change much, since /dev/urandom
|
||||
* will block if not enough entropy has been obtained since last boot.
|
||||
* On Linux, /dev/urandom might not block, which can be troublesome in
|
||||
* early boot stages, which is why getentropy() is preferred.
|
||||
*
|
||||
#define BR_USE_GETENTROPY 1
|
||||
*/
|
||||
|
||||
/*
|
||||
* When BR_USE_URANDOM is enabled, the SSL engine will use /dev/urandom
|
||||
* to automatically obtain quality randomness for seedings its internal
|
||||
* to automatically obtain quality randomness for seeding its internal
|
||||
* PRNG.
|
||||
*
|
||||
#define BR_USE_URANDOM 1
|
||||
|
|
@ -120,7 +138,7 @@
|
|||
/*
|
||||
* When BR_USE_WIN32_RAND is enabled, the SSL engine will use the Win32
|
||||
* (CryptoAPI) functions (CryptAcquireContext(), CryptGenRandom()...) to
|
||||
* automatically obtain quality randomness for seedings its internal PRNG.
|
||||
* automatically obtain quality randomness for seeding its internal PRNG.
|
||||
*
|
||||
* Note: if both BR_USE_URANDOM and BR_USE_WIN32_RAND are defined, the
|
||||
* former takes precedence.
|
||||
|
|
|
|||
26
src/inner.h
26
src/inner.h
|
|
@ -309,9 +309,20 @@
|
|||
* values are documented on:
|
||||
* https://sourceforge.net/p/predef/wiki/OperatingSystems/
|
||||
*
|
||||
* TODO: enrich the list of detected system. Also add detection for
|
||||
* alternate system calls like getentropy(), which are usually
|
||||
* preferable when available.
|
||||
* Win32's CryptGenRandom() should be available on Windows systems.
|
||||
*
|
||||
* /dev/urandom should work on all Unix-like systems (including macOS X).
|
||||
*
|
||||
* getentropy() is present on Linux (Glibc 2.25+), FreeBSD (12.0+) and
|
||||
* OpenBSD (5.6+). For OpenBSD, there does not seem to be easy to use
|
||||
* macros to test the minimum version, so we just assume that it is
|
||||
* recent enough (last version without getentropy() has gone out of
|
||||
* support in May 2015).
|
||||
*
|
||||
* Ideally we should use getentropy() on macOS (10.12+) too, but I don't
|
||||
* know how to test the exact OS version with preprocessor macros.
|
||||
*
|
||||
* TODO: enrich the list of detected system.
|
||||
*/
|
||||
|
||||
#ifndef BR_USE_URANDOM
|
||||
|
|
@ -328,6 +339,15 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef BR_USE_GETENTROPY
|
||||
#if (defined __linux__ \
|
||||
&& (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) \
|
||||
|| (defined __FreeBSD__ && __FreeBSD__ >= 12) \
|
||||
|| defined __OpenBSD__
|
||||
#define BR_USE_GETENTROPY 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef BR_USE_WIN32_RAND
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#define BR_USE_WIN32_RAND 1
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@
|
|||
#define BR_ENABLE_INTRINSICS 1
|
||||
#include "inner.h"
|
||||
|
||||
#if BR_USE_GETENTROPY
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if BR_USE_URANDOM
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -38,6 +42,9 @@
|
|||
#pragma comment(lib, "advapi32")
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Seeder that uses the RDRAND opcodes (on x86 CPU).
|
||||
*/
|
||||
#if BR_RDRAND
|
||||
BR_TARGETS_X86_UP
|
||||
BR_TARGET("rdrnd")
|
||||
|
|
@ -57,9 +64,24 @@ seeder_rdrand(const br_prng_class **ctx)
|
|||
*
|
||||
* Intel recommends trying at least 10 times in case of
|
||||
* failure.
|
||||
*
|
||||
* AMD bug: there are reports that some AMD processors
|
||||
* have a bug that makes them fail silently after a
|
||||
* suspend/resume cycle, in which case RDRAND will report
|
||||
* a success but always return 0xFFFFFFFF.
|
||||
* see: https://bugzilla.kernel.org/show_bug.cgi?id=85911
|
||||
*
|
||||
* As a mitigation, if the 32-bit value is 0 or -1, then
|
||||
* it is considered a failure and tried again. This should
|
||||
* reliably detect the buggy case, at least. This also
|
||||
* implies that the selected seed values can never be
|
||||
* 0x00000000 or 0xFFFFFFFF, which is not a problem since
|
||||
* we are generating a seed for a PRNG, and we overdo it
|
||||
* a bit (we generate 32 bytes of randomness, and 256 bits
|
||||
* of entropy are really overkill).
|
||||
*/
|
||||
for (j = 0; j < 10; j ++) {
|
||||
if (_rdrand32_step(&x)) {
|
||||
if (_rdrand32_step(&x) && x != 0 && x != (uint32_t)-1) {
|
||||
goto next_word;
|
||||
}
|
||||
}
|
||||
|
|
@ -80,9 +102,11 @@ rdrand_supported(void)
|
|||
*/
|
||||
return br_cpuid(0, 0, 0x40000000, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Seeder that uses /dev/urandom (on Unix-like systems).
|
||||
*/
|
||||
#if BR_USE_URANDOM
|
||||
static int
|
||||
seeder_urandom(const br_prng_class **ctx)
|
||||
|
|
@ -116,6 +140,32 @@ seeder_urandom(const br_prng_class **ctx)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Seeder that uses getentropy() (backed by getrandom() on some systems,
|
||||
* e.g. Linux). On failure, it will use the /dev/urandom seeder (if
|
||||
* enabled).
|
||||
*/
|
||||
#if BR_USE_GETENTROPY
|
||||
static int
|
||||
seeder_getentropy(const br_prng_class **ctx)
|
||||
{
|
||||
unsigned char tmp[32];
|
||||
|
||||
if (getentropy(tmp, sizeof tmp) == 0) {
|
||||
(*ctx)->update(ctx, tmp, sizeof tmp);
|
||||
return 1;
|
||||
}
|
||||
#if BR_USE_URANDOM
|
||||
return seeder_urandom(ctx);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Seeder that uses CryptGenRandom() (on Windows).
|
||||
*/
|
||||
#if BR_USE_WIN32_RAND
|
||||
static int
|
||||
seeder_win32(const br_prng_class **ctx)
|
||||
|
|
@ -139,6 +189,29 @@ seeder_win32(const br_prng_class **ctx)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An aggregate seeder that uses RDRAND, and falls back to an OS-provided
|
||||
* source if RDRAND fails.
|
||||
*/
|
||||
#if BR_RDRAND && (BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND)
|
||||
static int
|
||||
seeder_rdrand_with_fallback(const br_prng_class **ctx)
|
||||
{
|
||||
if (!seeder_rdrand(ctx)) {
|
||||
#if BR_USE_GETENTROPY
|
||||
return seeder_getentropy(ctx);
|
||||
#elif BR_USE_URANDOM
|
||||
return seeder_urandom(ctx);
|
||||
#elif BR_USE_WIN32_RAND
|
||||
return seeder_win32(ctx);
|
||||
#else
|
||||
#error "macro selection has gone wrong"
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* see bearssl_rand.h */
|
||||
br_prng_seeder
|
||||
br_prng_seeder_system(const char **name)
|
||||
|
|
@ -148,10 +221,19 @@ br_prng_seeder_system(const char **name)
|
|||
if (name != NULL) {
|
||||
*name = "rdrand";
|
||||
}
|
||||
#if BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND
|
||||
return &seeder_rdrand_with_fallback;
|
||||
#else
|
||||
return &seeder_rdrand;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if BR_USE_URANDOM
|
||||
#if BR_USE_GETENTROPY
|
||||
if (name != NULL) {
|
||||
*name = "getentropy";
|
||||
}
|
||||
return &seeder_getentropy;
|
||||
#elif BR_USE_URANDOM
|
||||
if (name != NULL) {
|
||||
*name = "urandom";
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue