Coverage Report

Created: 2026-09-14 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/BearSSL/src/rand/sysrng.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
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 <unistd.h>
30
#endif
31
32
#if BR_USE_URANDOM
33
#include <sys/types.h>
34
#include <unistd.h>
35
#include <fcntl.h>
36
#include <errno.h>
37
#endif
38
39
#if BR_USE_WIN32_RAND
40
#include <windows.h>
41
#include <wincrypt.h>
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
seeder_rdrand(const br_prng_class **ctx)
53
2
{
54
2
  unsigned char tmp[32];
55
2
  size_t u;
56
57
18
  for (u = 0; u < sizeof tmp; u += sizeof(uint32_t)) {
58
16
    int j;
59
16
    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
16
    for (j = 0; j < 10; j ++) {
84
16
      if (_rdrand32_step(&x) && x != 0 && x != (uint32_t)-1) {
85
16
        goto next_word;
86
16
      }
87
16
    }
88
0
    return 0;
89
16
  next_word:
90
16
    br_enc32le(tmp + u, x);
91
16
  }
92
2
  (*ctx)->update(ctx, tmp, sizeof tmp);
93
2
  return 1;
94
2
}
95
BR_TARGETS_X86_DOWN
96
97
static int
98
rdrand_supported(void)
99
2
{
100
  /*
101
   * The RDRND support is bit 30 of ECX, as returned by CPUID.
102
   */
103
2
  return br_cpuid(0, 0, 0x40000000, 0);
104
2
}
105
#endif
106
107
/*
108
 * Seeder that uses /dev/urandom (on Unix-like systems).
109
 */
110
#if BR_USE_URANDOM
111
static int
112
seeder_urandom(const br_prng_class **ctx)
113
0
{
114
0
  int f;
115
116
0
  f = open("/dev/urandom", O_RDONLY);
117
0
  if (f >= 0) {
118
0
    unsigned char tmp[32];
119
0
    size_t u;
120
121
0
    for (u = 0; u < sizeof tmp;) {
122
0
      ssize_t len;
123
124
0
      len = read(f, tmp + u, (sizeof tmp) - u);
125
0
      if (len < 0) {
126
0
        if (errno == EINTR) {
127
0
          continue;
128
0
        }
129
0
        break;
130
0
      }
131
0
      u += (size_t)len;
132
0
    }
133
0
    close(f);
134
0
    if (u == sizeof tmp) {
135
0
      (*ctx)->update(ctx, tmp, sizeof tmp);
136
0
      return 1;
137
0
    }
138
0
  }
139
0
  return 0;
140
0
}
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
seeder_getentropy(const br_prng_class **ctx)
151
0
{
152
0
  unsigned char tmp[32];
153
154
0
  if (getentropy(tmp, sizeof tmp) == 0) {
155
0
    (*ctx)->update(ctx, tmp, sizeof tmp);
156
0
    return 1;
157
0
  }
158
0
#if BR_USE_URANDOM
159
0
  return seeder_urandom(ctx);
160
#else
161
  return 0;
162
#endif
163
0
}
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
seeder_rdrand_with_fallback(const br_prng_class **ctx)
199
2
{
200
2
  if (!seeder_rdrand(ctx)) {
201
0
#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
0
  }
211
2
  return 1;
212
2
}
213
#endif
214
215
/* see bearssl_rand.h */
216
br_prng_seeder
217
br_prng_seeder_system(const char **name)
218
2
{
219
2
#if BR_RDRAND
220
2
  if (rdrand_supported()) {
221
2
    if (name != NULL) {
222
0
      *name = "rdrand";
223
0
    }
224
2
#if BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND
225
2
    return &seeder_rdrand_with_fallback;
226
#else
227
    return &seeder_rdrand;
228
#endif
229
2
  }
230
0
#endif
231
0
#if BR_USE_GETENTROPY
232
0
  if (name != NULL) {
233
0
    *name = "getentropy";
234
0
  }
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
2
}