#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
/*
* 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.
* 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
#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
#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>
#pragma comment(lib, "advapi32")
#endif
+/*
+ * Seeder that uses the RDRAND opcodes (on x86 CPU).
+ */
#if BR_RDRAND
BR_TARGETS_X86_UP
BR_TARGET("rdrnd")
*
* 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;
}
}
*/
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)
}
#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)
}
#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)
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";
}