2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 #define BR_ENABLE_INTRINSICS 1
33 #include <sys/types.h>
42 #pragma comment(lib, "advapi32")
46 * Seeder that uses the RDRAND opcodes (on x86 CPU).
52 seeder_rdrand(const br_prng_class
**ctx
)
54 unsigned char tmp
[32];
57 for (u
= 0; u
< sizeof tmp
; u
+= sizeof(uint32_t)) {
62 * We use the 32-bit intrinsic so that code is compatible
63 * with both 32-bit and 64-bit architectures.
65 * Intel recommends trying at least 10 times in case of
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
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).
83 for (j
= 0; j
< 10; j
++) {
84 if (_rdrand32_step(&x
) && x
!= 0 && x
!= (uint32_t)-1) {
90 br_enc32le(tmp
+ u
, x
);
92 (*ctx
)->update(ctx
, tmp
, sizeof tmp
);
98 rdrand_supported(void)
101 * The RDRND support is bit 30 of ECX, as returned by CPUID.
103 return br_cpuid(0, 0, 0x40000000, 0);
108 * Seeder that uses /dev/urandom (on Unix-like systems).
112 seeder_urandom(const br_prng_class
**ctx
)
116 f
= open("/dev/urandom", O_RDONLY
);
118 unsigned char tmp
[32];
121 for (u
= 0; u
< sizeof tmp
;) {
124 len
= read(f
, tmp
+ u
, (sizeof tmp
) - u
);
126 if (errno
== EINTR
) {
134 if (u
== sizeof tmp
) {
135 (*ctx
)->update(ctx
, tmp
, sizeof tmp
);
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
148 #if BR_USE_GETENTROPY
150 seeder_getentropy(const br_prng_class
**ctx
)
152 unsigned char tmp
[32];
154 if (getentropy(tmp
, sizeof tmp
) == 0) {
155 (*ctx
)->update(ctx
, tmp
, sizeof tmp
);
159 return seeder_urandom(ctx
);
167 * Seeder that uses CryptGenRandom() (on Windows).
169 #if BR_USE_WIN32_RAND
171 seeder_win32(const br_prng_class
**ctx
)
175 if (CryptAcquireContext(&hp
, 0, 0, PROV_RSA_FULL
,
176 CRYPT_VERIFYCONTEXT
| CRYPT_SILENT
))
181 r
= CryptGenRandom(hp
, sizeof buf
, buf
);
182 CryptReleaseContext(hp
, 0);
184 (*ctx
)->update(ctx
, buf
, sizeof buf
);
193 * An aggregate seeder that uses RDRAND, and falls back to an OS-provided
194 * source if RDRAND fails.
196 #if BR_RDRAND && (BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND)
198 seeder_rdrand_with_fallback(const br_prng_class
**ctx
)
200 if (!seeder_rdrand(ctx
)) {
201 #if BR_USE_GETENTROPY
202 return seeder_getentropy(ctx
);
204 return seeder_urandom(ctx
);
205 #elif BR_USE_WIN32_RAND
206 return seeder_win32(ctx
);
208 #error "macro selection has gone wrong"
215 /* see bearssl_rand.h */
217 br_prng_seeder_system(const char **name
)
220 if (rdrand_supported()) {
224 #if BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND
225 return &seeder_rdrand_with_fallback
;
227 return &seeder_rdrand
;
231 #if BR_USE_GETENTROPY
233 *name
= "getentropy";
235 return &seeder_getentropy
;
240 return &seeder_urandom
;
241 #elif BR_USE_WIN32_RAND
245 return &seeder_win32
;