Coverage Report

Created: 2026-04-01 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libevent/arc4random.c
Line
Count
Source
1
/* Portable arc4random.c based on arc4random.c from OpenBSD.
2
 * Portable version by Chris Davis, adapted for Libevent by Nick Mathewson
3
 * Copyright (c) 2010 Chris Davis, Niels Provos, and Nick Mathewson
4
 * Copyright (c) 2010-2012 Niels Provos and Nick Mathewson
5
 *
6
 * Note that in Libevent, this file isn't compiled directly.  Instead,
7
 * it's included from evutil_rand.c
8
 */
9
10
/*
11
 * Copyright (c) 1996, David Mazieres <dm@uun.org>
12
 * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
13
 *
14
 * Permission to use, copy, modify, and distribute this software for any
15
 * purpose with or without fee is hereby granted, provided that the above
16
 * copyright notice and this permission notice appear in all copies.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
19
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
20
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
21
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25
 */
26
27
/*
28
 * Arc4 random number generator for OpenBSD.
29
 *
30
 * This code is derived from section 17.1 of Applied Cryptography,
31
 * second edition, which describes a stream cipher allegedly
32
 * compatible with RSA Labs "RC4" cipher (the actual description of
33
 * which is a trade secret).  The same algorithm is used as a stream
34
 * cipher called "arcfour" in Tatu Ylonen's ssh package.
35
 *
36
 * Here the stream cipher has been modified always to include the time
37
 * when initializing the state.  That makes it impossible to
38
 * regenerate the same random sequence twice, so this can't be used
39
 * for encryption, but will generate good random numbers.
40
 *
41
 * RC4 is a registered trademark of RSA Laboratories.
42
 */
43
44
#ifndef ARC4RANDOM_EXPORT
45
#define ARC4RANDOM_EXPORT
46
#endif
47
48
#ifndef ARC4RANDOM_UINT32
49
#define ARC4RANDOM_UINT32 uint32_t
50
#endif
51
52
#ifndef ARC4RANDOM_NO_INCLUDES
53
#include "evconfig-private.h"
54
#ifdef _WIN32
55
#ifndef EVENT__HAVE_BCRYPTGENRANDOM
56
#include <wincrypt.h>
57
#else
58
#include <bcrypt.h>
59
#endif
60
#include <process.h>
61
#include <winerror.h>
62
#else
63
#include <fcntl.h>
64
#include <unistd.h>
65
#include <sys/param.h>
66
#include <sys/time.h>
67
#ifdef EVENT__HAVE_SYS_SYSCTL_H
68
#include <sys/sysctl.h>
69
#endif
70
#ifdef EVENT__HAVE_SYS_RANDOM_H
71
#include <sys/random.h>
72
#endif
73
#endif
74
#include <limits.h>
75
#include <stdlib.h>
76
#include <string.h>
77
#endif
78
79
/* Add platform entropy 32 bytes (256 bits) at a time. */
80
0
#define ADD_ENTROPY 32
81
82
0
#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
83
84
struct arc4_stream {
85
  unsigned char i;
86
  unsigned char j;
87
  unsigned char s[256];
88
};
89
90
#ifdef _WIN32
91
#define getpid _getpid
92
#define pid_t int
93
#endif
94
95
#ifndef O_RDONLY
96
#define O_RDONLY _O_RDONLY
97
#endif
98
99
static int rs_initialized;
100
static struct arc4_stream rs;
101
static pid_t arc4_stir_pid;
102
static int arc4_count;
103
104
static inline unsigned char arc4_getbyte(void);
105
106
static inline void
107
arc4_init(void)
108
0
{
109
0
  int     n;
110
111
0
  for (n = 0; n < 256; n++)
112
0
    rs.s[n] = n;
113
0
  rs.i = 0;
114
0
  rs.j = 0;
115
0
}
116
117
static inline void
118
arc4_addrandom(const unsigned char *dat, int datlen)
119
0
{
120
0
  int     n;
121
0
  unsigned char si;
122
123
0
  rs.i--;
124
0
  for (n = 0; n < 256; n++) {
125
0
    rs.i = (rs.i + 1);
126
0
    si = rs.s[rs.i];
127
0
    rs.j = (rs.j + si + dat[n % datlen]);
128
0
    rs.s[rs.i] = rs.s[rs.j];
129
0
    rs.s[rs.j] = si;
130
0
  }
131
0
  rs.j = rs.i;
132
0
}
133
134
#ifndef _WIN32
135
static ssize_t
136
read_all(int fd, unsigned char *buf, size_t count)
137
0
{
138
0
  size_t numread = 0;
139
0
  ssize_t result;
140
141
0
  while (numread < count) {
142
0
    result = read(fd, buf+numread, count-numread);
143
0
    if (result<0)
144
0
      return -1;
145
0
    else if (result == 0)
146
0
      break;
147
0
    numread += result;
148
0
  }
149
150
0
  return (ssize_t)numread;
151
0
}
152
#endif
153
154
#ifdef _WIN32
155
#define TRY_SEED_WIN32
156
static int
157
arc4_seed_win32(void)
158
{
159
  unsigned char buf[ADD_ENTROPY];
160
#ifndef EVENT__HAVE_BCRYPTGENRANDOM
161
  /* This is adapted from Tor's crypto_seed_rng() */
162
  static int provider_set = 0;
163
  static HCRYPTPROV provider;
164
165
  if (!provider_set) {
166
    if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
167
      CRYPT_VERIFYCONTEXT)) {
168
      if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
169
        return -1;
170
    }
171
    provider_set = 1;
172
  }
173
  if (!CryptGenRandom(provider, sizeof(buf), buf))
174
#else
175
176
  if (BCryptGenRandom(NULL, buf, sizeof(buf),
177
    BCRYPT_USE_SYSTEM_PREFERRED_RNG))
178
#endif
179
    return -1;
180
  arc4_addrandom(buf, sizeof(buf));
181
  evutil_memclear_(buf, sizeof(buf));
182
  return 0;
183
}
184
#endif
185
186
#if defined(EVENT__HAVE_GETRANDOM)
187
#define TRY_SEED_GETRANDOM
188
static int
189
arc4_seed_getrandom(void)
190
0
{
191
0
  unsigned char buf[ADD_ENTROPY];
192
0
  size_t len;
193
0
  ssize_t n = 0;
194
195
0
  for (len = 0; len < sizeof(buf); len += n) {
196
0
    n = getrandom(&buf[len], sizeof(buf) - len, 0);
197
0
    if (n < 0)
198
0
      return -1;
199
0
  }
200
0
  arc4_addrandom(buf, sizeof(buf));
201
0
  evutil_memclear_(buf, sizeof(buf));
202
0
  return 0;
203
0
}
204
#endif /* EVENT__HAVE_GETRANDOM */
205
206
#if defined(EVENT__HAVE_SYS_SYSCTL_H) && defined(EVENT__HAVE_SYSCTL)
207
#if EVENT__HAVE_DECL_CTL_KERN && EVENT__HAVE_DECL_KERN_ARND
208
#define TRY_SEED_SYSCTL_BSD
209
static int
210
arc4_seed_sysctl_bsd(void)
211
{
212
  /* Based on code from William Ahern and from OpenBSD, this function
213
   * tries to use the KERN_ARND syscall to get entropy from the kernel.
214
   * This can work even if /dev/urandom is inaccessible for some reason
215
   * (e.g., we're running in a chroot). */
216
  int mib[] = { CTL_KERN, KERN_ARND };
217
  unsigned char buf[ADD_ENTROPY];
218
  size_t len, n;
219
  int i, any_set;
220
221
  memset(buf, 0, sizeof(buf));
222
223
  len = sizeof(buf);
224
  if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
225
    for (len = 0; len < sizeof(buf); len += sizeof(unsigned)) {
226
      n = sizeof(unsigned);
227
      if (n + len > sizeof(buf))
228
          n = len - sizeof(buf);
229
      if (sysctl(mib, 2, &buf[len], &n, NULL, 0) == -1)
230
        return -1;
231
    }
232
  }
233
  /* make sure that the buffer actually got set. */
234
  for (i=any_set=0; i<sizeof(buf); ++i) {
235
    any_set |= buf[i];
236
  }
237
  if (!any_set)
238
    return -1;
239
240
  arc4_addrandom(buf, sizeof(buf));
241
  evutil_memclear_(buf, sizeof(buf));
242
  return 0;
243
}
244
#endif
245
#endif /* defined(EVENT__HAVE_SYS_SYSCTL_H) */
246
247
#ifdef __linux__
248
#define TRY_SEED_PROC_SYS_KERNEL_RANDOM_UUID
249
static int
250
arc4_seed_proc_sys_kernel_random_uuid(void)
251
0
{
252
  /* Occasionally, somebody will make /proc/sys accessible in a chroot,
253
   * but not /dev/urandom.  Let's try /proc/sys/kernel/random/uuid.
254
   * Its format is stupid, so we need to decode it from hex.
255
   */
256
0
  int fd;
257
0
  char buf[128];
258
0
  unsigned char entropy[64];
259
0
  int bytes, n, i, nybbles;
260
0
  for (bytes = 0; bytes<ADD_ENTROPY; ) {
261
0
    fd = evutil_open_closeonexec_("/proc/sys/kernel/random/uuid", O_RDONLY, 0);
262
0
    if (fd < 0)
263
0
      return -1;
264
0
    n = read(fd, buf, sizeof(buf));
265
0
    close(fd);
266
0
    if (n<=0)
267
0
      return -1;
268
0
    memset(entropy, 0, sizeof(entropy));
269
0
    for (i=nybbles=0; i<n; ++i) {
270
0
      if (EVUTIL_ISXDIGIT_(buf[i])) {
271
0
        int nyb = evutil_hex_char_to_int_(buf[i]);
272
0
        if (nybbles & 1) {
273
0
          entropy[nybbles/2] |= nyb;
274
0
        } else {
275
0
          entropy[nybbles/2] |= nyb<<4;
276
0
        }
277
0
        ++nybbles;
278
0
      }
279
0
    }
280
0
    if (nybbles < 2)
281
0
      return -1;
282
0
    arc4_addrandom(entropy, nybbles/2);
283
0
    bytes += nybbles/2;
284
0
  }
285
0
  evutil_memclear_(entropy, sizeof(entropy));
286
0
  evutil_memclear_(buf, sizeof(buf));
287
0
  return 0;
288
0
}
289
#endif
290
291
#ifndef _WIN32
292
#define TRY_SEED_URANDOM
293
static char *arc4random_urandom_filename = NULL;
294
295
static int arc4_seed_urandom_helper_(const char *fname)
296
0
{
297
0
  unsigned char buf[ADD_ENTROPY];
298
0
  int fd;
299
0
  size_t n;
300
301
0
  fd = evutil_open_closeonexec_(fname, O_RDONLY, 0);
302
0
  if (fd<0)
303
0
    return -1;
304
0
  n = read_all(fd, buf, sizeof(buf));
305
0
  close(fd);
306
0
  if (n != sizeof(buf))
307
0
    return -1;
308
0
  arc4_addrandom(buf, sizeof(buf));
309
0
  evutil_memclear_(buf, sizeof(buf));
310
0
  return 0;
311
0
}
312
313
static int
314
arc4_seed_urandom(void)
315
0
{
316
  /* This is adapted from Tor's crypto_seed_rng() */
317
0
  static const char *filenames[] = {
318
0
    "/dev/srandom", "/dev/urandom", "/dev/random", NULL
319
0
  };
320
0
  int i;
321
0
  if (arc4random_urandom_filename)
322
0
    return arc4_seed_urandom_helper_(arc4random_urandom_filename);
323
324
0
  for (i = 0; filenames[i]; ++i) {
325
0
    if (arc4_seed_urandom_helper_(filenames[i]) == 0) {
326
0
      return 0;
327
0
    }
328
0
  }
329
330
0
  return -1;
331
0
}
332
#endif
333
334
static int
335
arc4_seed(void)
336
0
{
337
0
  int ok = 0;
338
  /* We try every method that might work, and don't give up even if one
339
   * does seem to work.  There's no real harm in over-seeding, and if
340
   * one of these sources turns out to be broken, that would be bad. */
341
#ifdef TRY_SEED_WIN32
342
  if (0 == arc4_seed_win32())
343
    ok = 1;
344
#endif
345
0
#ifdef TRY_SEED_GETRANDOM
346
0
  if (0 == arc4_seed_getrandom())
347
0
    ok = 1;
348
0
#endif
349
0
#ifdef TRY_SEED_URANDOM
350
0
  if (0 == arc4_seed_urandom())
351
0
    ok = 1;
352
0
#endif
353
0
#ifdef TRY_SEED_PROC_SYS_KERNEL_RANDOM_UUID
354
0
  if (arc4random_urandom_filename == NULL &&
355
0
      0 == arc4_seed_proc_sys_kernel_random_uuid())
356
0
    ok = 1;
357
0
#endif
358
#ifdef TRY_SEED_SYSCTL_BSD
359
  if (0 == arc4_seed_sysctl_bsd())
360
    ok = 1;
361
#endif
362
0
  return ok ? 0 : -1;
363
0
}
364
365
static inline unsigned int
366
arc4_getword(void);
367
static int
368
arc4_stir(void)
369
0
{
370
0
  int     i;
371
0
  ARC4RANDOM_UINT32 rekey_fuzz; 
372
373
0
  if (!rs_initialized) {
374
0
    arc4_init();
375
0
    rs_initialized = 1;
376
0
  }
377
378
0
  if (0 != arc4_seed())
379
0
    return -1;
380
381
  /*
382
   * Discard early keystream, as per recommendations in
383
   * "Weaknesses in the Key Scheduling Algorithm of RC4" by
384
   * Scott Fluhrer, Itsik Mantin, and Adi Shamir.
385
   * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
386
   *
387
   * Ilya Mironov's "(Not So) Random Shuffles of RC4" suggests that
388
   * we drop at least 2*256 bytes, with 12*256 as a conservative
389
   * value.
390
   *
391
   * RFC4345 says to drop 6*256.
392
   *
393
   * At least some versions of this code drop 4*256, in a mistaken
394
   * belief that "words" in the Fluhrer/Mantin/Shamir paper refers
395
   * to processor words.
396
   *
397
   * We add another sect to the cargo cult, and choose 16*256.
398
   */
399
0
  for (i = 0; i < 16*256; i++)
400
0
    (void)arc4_getbyte();
401
402
0
  rekey_fuzz = arc4_getword();
403
  /* rekey interval should not be predictable */
404
0
  arc4_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
405
406
0
  return 0;
407
0
}
408
409
410
static void
411
arc4_stir_if_needed(void)
412
0
{
413
0
  pid_t pid = getpid();
414
415
0
  if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid)
416
0
  {
417
0
    arc4_stir_pid = pid;
418
0
    arc4_stir();
419
0
  }
420
0
}
421
422
static inline unsigned char
423
arc4_getbyte(void)
424
0
{
425
0
  unsigned char si, sj;
426
427
0
  rs.i = (rs.i + 1);
428
0
  si = rs.s[rs.i];
429
0
  rs.j = (rs.j + si);
430
0
  sj = rs.s[rs.j];
431
0
  rs.s[rs.i] = sj;
432
0
  rs.s[rs.j] = si;
433
0
  return (rs.s[(si + sj) & 0xff]);
434
0
}
435
436
static inline unsigned int
437
arc4_getword(void)
438
0
{
439
0
  unsigned int val;
440
441
0
  val = (unsigned)arc4_getbyte() << 24;
442
0
  val |= arc4_getbyte() << 16;
443
0
  val |= arc4_getbyte() << 8;
444
0
  val |= arc4_getbyte();
445
446
0
  return val;
447
0
}
448
449
#ifndef ARC4RANDOM_NOSTIR
450
ARC4RANDOM_EXPORT int
451
arc4random_stir(void)
452
{
453
  int val;
454
  ARC4_LOCK_();
455
  val = arc4_stir();
456
  ARC4_UNLOCK_();
457
  return val;
458
}
459
#endif
460
461
#ifndef ARC4RANDOM_NOADDRANDOM
462
ARC4RANDOM_EXPORT void
463
arc4random_addrandom(const unsigned char *dat, int datlen)
464
0
{
465
0
  int j;
466
0
  ARC4_LOCK_();
467
0
  if (!rs_initialized)
468
0
    arc4_stir();
469
0
  for (j = 0; j < datlen; j += 256) {
470
    /* arc4_addrandom() ignores all but the first 256 bytes of
471
     * its input.  We want to make sure to look at ALL the
472
     * data in 'dat', just in case the user is doing something
473
     * crazy like passing us all the files in /var/log. */
474
0
    arc4_addrandom(dat + j, datlen - j);
475
0
  }
476
0
  ARC4_UNLOCK_();
477
0
}
478
#endif
479
480
#ifndef ARC4RANDOM_NORANDOM
481
ARC4RANDOM_EXPORT ARC4RANDOM_UINT32
482
arc4random(void)
483
{
484
  ARC4RANDOM_UINT32 val;
485
  ARC4_LOCK_();
486
  arc4_count -= 4;
487
  arc4_stir_if_needed();
488
  val = arc4_getword();
489
  ARC4_UNLOCK_();
490
  return val;
491
}
492
#endif
493
494
#ifndef EVENT__HAVE_ARC4RANDOM_BUF
495
ARC4RANDOM_EXPORT void
496
arc4random_buf(void *buf_, size_t n)
497
0
{
498
0
  unsigned char *buf = buf_;
499
0
  ARC4_LOCK_();
500
0
  arc4_stir_if_needed();
501
0
  while (n--) {
502
0
    if (--arc4_count <= 0)
503
0
      arc4_stir();
504
0
    buf[n] = arc4_getbyte();
505
0
  }
506
0
  ARC4_UNLOCK_();
507
0
}
508
#endif  /* #ifndef EVENT__HAVE_ARC4RANDOM_BUF */
509
510
#ifndef ARC4RANDOM_NOUNIFORM
511
/*
512
 * Calculate a uniformly distributed random number less than upper_bound
513
 * avoiding "modulo bias".
514
 *
515
 * Uniformity is achieved by generating new random numbers until the one
516
 * returned is outside the range [0, 2**32 % upper_bound).  This
517
 * guarantees the selected random number will be inside
518
 * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
519
 * after reduction modulo upper_bound.
520
 */
521
ARC4RANDOM_EXPORT unsigned int
522
arc4random_uniform(unsigned int upper_bound)
523
{
524
  ARC4RANDOM_UINT32 r, min;
525
526
  if (upper_bound < 2)
527
    return 0;
528
529
  /* 2**32 % x == (2**32 - x) % x */
530
  min = -upper_bound % upper_bound;
531
532
  /*
533
   * This could theoretically loop forever but each retry has
534
   * p > 0.5 (worst case, usually far better) of selecting a
535
   * number inside the range we need, so it should rarely need
536
   * to re-roll.
537
   */
538
  for (;;) {
539
    r = arc4random();
540
    if (r >= min)
541
      break;
542
  }
543
544
  return r % upper_bound;
545
}
546
#endif