File Coverage

src/rand/sysrng.c
Criterion Covered Total %
statement 19 44 43.1
branch 10 30 33.3
condition n/a
subroutine n/a
pod n/a
total 29 74 39.1


line stmt bran cond sub pod time code
1             /*
2             * Copyright (c) 2017 Thomas Pornin
3             *
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:
11             *
12             * The above copyright notice and this permission notice shall be
13             * included in all copies or substantial portions of the Software.
14             *
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
22             * SOFTWARE.
23             */
24              
25             #define BR_ENABLE_INTRINSICS 1
26             #include "inner.h"
27              
28             #if BR_USE_GETENTROPY
29             #include
30             #endif
31              
32             #if BR_USE_URANDOM
33             #include
34             #include
35             #include
36             #include
37             #endif
38              
39             #if BR_USE_WIN32_RAND
40             #include
41             #include
42             #pragma comment(lib, "advapi32")
43             #endif
44              
45             /*
46             * Seeder that uses the RDRAND opcodes (on x86 CPU).
47             */
48             #if BR_RDRAND
49             BR_TARGETS_X86_UP
50             BR_TARGET("rdrnd")
51             static int
52 6           seeder_rdrand(const br_prng_class **ctx)
53             {
54             unsigned char tmp[32];
55             size_t u;
56              
57 54 100         for (u = 0; u < sizeof tmp; u += sizeof(uint32_t)) {
58             int j;
59             uint32_t x;
60              
61             /*
62             * We use the 32-bit intrinsic so that code is compatible
63             * with both 32-bit and 64-bit architectures.
64             *
65             * Intel recommends trying at least 10 times in case of
66             * 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).
82             */
83 48 50         for (j = 0; j < 10; j ++) {
84 48 50         if (_rdrand32_step(&x) && x != 0 && x != (uint32_t)-1) {
    50          
    50          
85 48           goto next_word;
86             }
87             }
88 0           return 0;
89 48           next_word:
90 48           br_enc32le(tmp + u, x);
91             }
92 6           (*ctx)->update(ctx, tmp, sizeof tmp);
93 6           return 1;
94             }
95             BR_TARGETS_X86_DOWN
96              
97             static int
98 52           rdrand_supported(void)
99             {
100             /*
101             * The RDRND support is bit 30 of ECX, as returned by CPUID.
102             */
103 52           return br_cpuid(0, 0, 0x40000000, 0);
104             }
105             #endif
106              
107             /*
108             * Seeder that uses /dev/urandom (on Unix-like systems).
109             */
110             #if BR_USE_URANDOM
111             static int
112 0           seeder_urandom(const br_prng_class **ctx)
113             {
114             int f;
115              
116 0           f = open("/dev/urandom", O_RDONLY);
117 0 0         if (f >= 0) {
118             unsigned char tmp[32];
119             size_t u;
120              
121 0 0         for (u = 0; u < sizeof tmp;) {
122             ssize_t len;
123              
124 0           len = read(f, tmp + u, (sizeof tmp) - u);
125 0 0         if (len < 0) {
126 0 0         if (errno == EINTR) {
127 0           continue;
128             }
129 0           break;
130             }
131 0           u += (size_t)len;
132             }
133 0           close(f);
134 0 0         if (u == sizeof tmp) {
135 0           (*ctx)->update(ctx, tmp, sizeof tmp);
136 0           return 1;
137             }
138             }
139 0           return 0;
140             }
141             #endif
142              
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 0           seeder_getentropy(const br_prng_class **ctx)
151             {
152             unsigned char tmp[32];
153              
154 0 0         if (getentropy(tmp, sizeof tmp) == 0) {
155 0           (*ctx)->update(ctx, tmp, sizeof tmp);
156 0           return 1;
157             }
158             #if BR_USE_URANDOM
159 0           return seeder_urandom(ctx);
160             #else
161             return 0;
162             #endif
163             }
164             #endif
165              
166             /*
167             * Seeder that uses CryptGenRandom() (on Windows).
168             */
169             #if BR_USE_WIN32_RAND
170             static int
171             seeder_win32(const br_prng_class **ctx)
172             {
173             HCRYPTPROV hp;
174              
175             if (CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL,
176             CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
177             {
178             BYTE buf[32];
179             BOOL r;
180              
181             r = CryptGenRandom(hp, sizeof buf, buf);
182             CryptReleaseContext(hp, 0);
183             if (r) {
184             (*ctx)->update(ctx, buf, sizeof buf);
185             return 1;
186             }
187             }
188             return 0;
189             }
190             #endif
191              
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 6           seeder_rdrand_with_fallback(const br_prng_class **ctx)
199             {
200 6 50         if (!seeder_rdrand(ctx)) {
201             #if BR_USE_GETENTROPY
202 0           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 6           return 1;
212             }
213             #endif
214              
215             /* see bearssl_rand.h */
216             br_prng_seeder
217 52           br_prng_seeder_system(const char **name)
218             {
219             #if BR_RDRAND
220 52 50         if (rdrand_supported()) {
221 52 100         if (name != NULL) {
222 50           *name = "rdrand";
223             }
224             #if BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND
225 52           return &seeder_rdrand_with_fallback;
226             #else
227             return &seeder_rdrand;
228             #endif
229             }
230             #endif
231             #if BR_USE_GETENTROPY
232 0 0         if (name != NULL) {
233 0           *name = "getentropy";
234             }
235 0           return &seeder_getentropy;
236             #elif BR_USE_URANDOM
237             if (name != NULL) {
238             *name = "urandom";
239             }
240             return &seeder_urandom;
241             #elif BR_USE_WIN32_RAND
242             if (name != NULL) {
243             *name = "win32";
244             }
245             return &seeder_win32;
246             #else
247             if (name != NULL) {
248             *name = "none";
249             }
250             return 0;
251             #endif
252             }