Skip to content

Commit e4edfb8

Browse files
committed
Added support for getrandom()/getentropy(), and a fix for the RDRAND bug on AMD CPU (family 22).
1 parent 924921d commit e4edfb8

File tree

3 files changed

+128
-8
lines changed

3 files changed

+128
-8
lines changed

src/config.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,27 @@
109109
#define BR_RDRAND 1
110110
*/
111111

112+
/*
113+
* When BR_USE_GETENTROPY is enabled, the SSL engine will use the
114+
* getentropy() function to obtain quality randomness for seeding its
115+
* internal PRNG. On Linux and FreeBSD, getentropy() is implemented by
116+
* the standard library with the system call getrandom(); on OpenBSD,
117+
* getentropy() is the system call, and there is no getrandom() wrapper,
118+
* hence the use of the getentropy() function for maximum portability.
119+
*
120+
* If the getentropy() call fails, and BR_USE_URANDOM is not explicitly
121+
* disabled, then /dev/urandom will be used as a fallback mechanism. On
122+
* FreeBSD and OpenBSD, this does not change much, since /dev/urandom
123+
* will block if not enough entropy has been obtained since last boot.
124+
* On Linux, /dev/urandom might not block, which can be troublesome in
125+
* early boot stages, which is why getentropy() is preferred.
126+
*
127+
#define BR_USE_GETENTROPY 1
128+
*/
129+
112130
/*
113131
* When BR_USE_URANDOM is enabled, the SSL engine will use /dev/urandom
114-
* to automatically obtain quality randomness for seedings its internal
132+
* to automatically obtain quality randomness for seeding its internal
115133
* PRNG.
116134
*
117135
#define BR_USE_URANDOM 1
@@ -120,7 +138,7 @@
120138
/*
121139
* When BR_USE_WIN32_RAND is enabled, the SSL engine will use the Win32
122140
* (CryptoAPI) functions (CryptAcquireContext(), CryptGenRandom()...) to
123-
* automatically obtain quality randomness for seedings its internal PRNG.
141+
* automatically obtain quality randomness for seeding its internal PRNG.
124142
*
125143
* Note: if both BR_USE_URANDOM and BR_USE_WIN32_RAND are defined, the
126144
* former takes precedence.

src/inner.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,20 @@
309309
* values are documented on:
310310
* https://sourceforge.net/p/predef/wiki/OperatingSystems/
311311
*
312-
* TODO: enrich the list of detected system. Also add detection for
313-
* alternate system calls like getentropy(), which are usually
314-
* preferable when available.
312+
* Win32's CryptGenRandom() should be available on Windows systems.
313+
*
314+
* /dev/urandom should work on all Unix-like systems (including macOS X).
315+
*
316+
* getentropy() is present on Linux (Glibc 2.25+), FreeBSD (12.0+) and
317+
* OpenBSD (5.6+). For OpenBSD, there does not seem to be easy to use
318+
* macros to test the minimum version, so we just assume that it is
319+
* recent enough (last version without getentropy() has gone out of
320+
* support in May 2015).
321+
*
322+
* Ideally we should use getentropy() on macOS (10.12+) too, but I don't
323+
* know how to test the exact OS version with preprocessor macros.
324+
*
325+
* TODO: enrich the list of detected system.
315326
*/
316327

317328
#ifndef BR_USE_URANDOM
@@ -328,6 +339,15 @@
328339
#endif
329340
#endif
330341

342+
#ifndef BR_USE_GETENTROPY
343+
#if (defined __linux__ \
344+
&& (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) \
345+
|| (defined __FreeBSD__ && __FreeBSD__ >= 12) \
346+
|| defined __OpenBSD__
347+
#define BR_USE_GETENTROPY 1
348+
#endif
349+
#endif
350+
331351
#ifndef BR_USE_WIN32_RAND
332352
#if defined _WIN32 || defined _WIN64
333353
#define BR_USE_WIN32_RAND 1

src/rand/sysrng.c

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
#define BR_ENABLE_INTRINSICS 1
2626
#include "inner.h"
2727

28+
#if BR_USE_GETENTROPY
29+
#include <unistd.h>
30+
#endif
31+
2832
#if BR_USE_URANDOM
2933
#include <sys/types.h>
3034
#include <unistd.h>
@@ -38,6 +42,9 @@
3842
#pragma comment(lib, "advapi32")
3943
#endif
4044

45+
/*
46+
* Seeder that uses the RDRAND opcodes (on x86 CPU).
47+
*/
4148
#if BR_RDRAND
4249
BR_TARGETS_X86_UP
4350
BR_TARGET("rdrnd")
@@ -57,9 +64,24 @@ seeder_rdrand(const br_prng_class **ctx)
5764
*
5865
* Intel recommends trying at least 10 times in case of
5966
* failure.
67+
*
68+
* AMD bug: there are reports that some AMD processors
69+
* have a bug that makes them fail silently after a
70+
* suspend/resume cycle, in which case RDRAND will report
71+
* a success but always return 0xFFFFFFFF.
72+
* see: https://bugzilla.kernel.org/show_bug.cgi?id=85911
73+
*
74+
* As a mitigation, if the 32-bit value is 0 or -1, then
75+
* it is considered a failure and tried again. This should
76+
* reliably detect the buggy case, at least. This also
77+
* implies that the selected seed values can never be
78+
* 0x00000000 or 0xFFFFFFFF, which is not a problem since
79+
* we are generating a seed for a PRNG, and we overdo it
80+
* a bit (we generate 32 bytes of randomness, and 256 bits
81+
* of entropy are really overkill).
6082
*/
6183
for (j = 0; j < 10; j ++) {
62-
if (_rdrand32_step(&x)) {
84+
if (_rdrand32_step(&x) && x != 0 && x != (uint32_t)-1) {
6385
goto next_word;
6486
}
6587
}
@@ -80,9 +102,11 @@ rdrand_supported(void)
80102
*/
81103
return br_cpuid(0, 0, 0x40000000, 0);
82104
}
83-
84105
#endif
85106

107+
/*
108+
* Seeder that uses /dev/urandom (on Unix-like systems).
109+
*/
86110
#if BR_USE_URANDOM
87111
static int
88112
seeder_urandom(const br_prng_class **ctx)
@@ -116,6 +140,32 @@ seeder_urandom(const br_prng_class **ctx)
116140
}
117141
#endif
118142

143+
/*
144+
* Seeder that uses getentropy() (backed by getrandom() on some systems,
145+
* e.g. Linux). On failure, it will use the /dev/urandom seeder (if
146+
* enabled).
147+
*/
148+
#if BR_USE_GETENTROPY
149+
static int
150+
seeder_getentropy(const br_prng_class **ctx)
151+
{
152+
unsigned char tmp[32];
153+
154+
if (getentropy(tmp, sizeof tmp) == 0) {
155+
(*ctx)->update(ctx, tmp, sizeof tmp);
156+
return 1;
157+
}
158+
#if BR_USE_URANDOM
159+
return seeder_urandom(ctx);
160+
#else
161+
return 0;
162+
#endif
163+
}
164+
#endif
165+
166+
/*
167+
* Seeder that uses CryptGenRandom() (on Windows).
168+
*/
119169
#if BR_USE_WIN32_RAND
120170
static int
121171
seeder_win32(const br_prng_class **ctx)
@@ -139,6 +189,29 @@ seeder_win32(const br_prng_class **ctx)
139189
}
140190
#endif
141191

192+
/*
193+
* An aggregate seeder that uses RDRAND, and falls back to an OS-provided
194+
* source if RDRAND fails.
195+
*/
196+
#if BR_RDRAND && (BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND)
197+
static int
198+
seeder_rdrand_with_fallback(const br_prng_class **ctx)
199+
{
200+
if (!seeder_rdrand(ctx)) {
201+
#if BR_USE_GETENTROPY
202+
return seeder_getentropy(ctx);
203+
#elif BR_USE_URANDOM
204+
return seeder_urandom(ctx);
205+
#elif BR_USE_WIN32_RAND
206+
return seeder_win32(ctx);
207+
#else
208+
#error "macro selection has gone wrong"
209+
#endif
210+
}
211+
return 1;
212+
}
213+
#endif
214+
142215
/* see bearssl_rand.h */
143216
br_prng_seeder
144217
br_prng_seeder_system(const char **name)
@@ -148,10 +221,19 @@ br_prng_seeder_system(const char **name)
148221
if (name != NULL) {
149222
*name = "rdrand";
150223
}
224+
#if BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND
225+
return &seeder_rdrand_with_fallback;
226+
#else
151227
return &seeder_rdrand;
228+
#endif
152229
}
153230
#endif
154-
#if BR_USE_URANDOM
231+
#if BR_USE_GETENTROPY
232+
if (name != NULL) {
233+
*name = "getentropy";
234+
}
235+
return &seeder_getentropy;
236+
#elif BR_USE_URANDOM
155237
if (name != NULL) {
156238
*name = "urandom";
157239
}

0 commit comments

Comments
 (0)