Coverage Report

Created: 2025-07-11 06:57

/src/libevent/arc4random.c
Line
Count
Source (jump to first uncovered line)
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
#include <bcrypt.h>
56
#include <process.h>
57
#include <winerror.h>
58
#else
59
#include <fcntl.h>
60
#include <unistd.h>
61
#include <sys/param.h>
62
#include <sys/time.h>
63
#ifdef EVENT__HAVE_SYS_SYSCTL_H
64
#include <sys/sysctl.h>
65
#endif
66
#ifdef EVENT__HAVE_SYS_RANDOM_H
67
#include <sys/random.h>
68
#endif
69
#endif
70
#include <limits.h>
71
#include <stdlib.h>
72
#include <string.h>
73
#endif
74
75
/* Add platform entropy 32 bytes (256 bits) at a time. */
76
96
#define ADD_ENTROPY 32
77
78
64
#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
79
80
struct arc4_stream {
81
  unsigned char i;
82
  unsigned char j;
83
  unsigned char s[256];
84
};
85
86
#ifdef _WIN32
87
#define getpid _getpid
88
#define pid_t int
89
#endif
90
91
#ifndef O_RDONLY
92
#define O_RDONLY _O_RDONLY
93
#endif
94
95
static int rs_initialized;
96
static struct arc4_stream rs;
97
static pid_t arc4_stir_pid;
98
static int arc4_count;
99
100
static inline unsigned char arc4_getbyte(void);
101
102
static inline void
103
arc4_init(void)
104
16
{
105
16
  int     n;
106
107
4.11k
  for (n = 0; n < 256; n++)
108
4.09k
    rs.s[n] = n;
109
16
  rs.i = 0;
110
16
  rs.j = 0;
111
16
}
112
113
static inline void
114
arc4_addrandom(const unsigned char *dat, int datlen)
115
144
{
116
144
  int     n;
117
144
  unsigned char si;
118
119
144
  rs.i--;
120
37.0k
  for (n = 0; n < 256; n++) {
121
36.8k
    rs.i = (rs.i + 1);
122
36.8k
    si = rs.s[rs.i];
123
36.8k
    rs.j = (rs.j + si + dat[n % datlen]);
124
36.8k
    rs.s[rs.i] = rs.s[rs.j];
125
36.8k
    rs.s[rs.j] = si;
126
36.8k
  }
127
144
  rs.j = rs.i;
128
144
}
129
130
#ifndef _WIN32
131
static ssize_t
132
read_all(int fd, unsigned char *buf, size_t count)
133
32
{
134
32
  size_t numread = 0;
135
32
  ssize_t result;
136
137
64
  while (numread < count) {
138
32
    result = read(fd, buf+numread, count-numread);
139
32
    if (result<0)
140
0
      return -1;
141
32
    else if (result == 0)
142
0
      break;
143
32
    numread += result;
144
32
  }
145
146
32
  return (ssize_t)numread;
147
32
}
148
#endif
149
150
#ifdef _WIN32
151
#define TRY_SEED_WIN32
152
static int
153
arc4_seed_win32(void)
154
{
155
  unsigned char buf[ADD_ENTROPY];
156
157
  if (BCryptGenRandom(NULL, buf, sizeof(buf),
158
    BCRYPT_USE_SYSTEM_PREFERRED_RNG))
159
    return -1;
160
  arc4_addrandom(buf, sizeof(buf));
161
  evutil_memclear_(buf, sizeof(buf));
162
  return 0;
163
}
164
#endif
165
166
#if defined(EVENT__HAVE_GETRANDOM)
167
#define TRY_SEED_GETRANDOM
168
static int
169
arc4_seed_getrandom(void)
170
32
{
171
32
  unsigned char buf[ADD_ENTROPY];
172
32
  size_t len;
173
32
  ssize_t n = 0;
174
175
64
  for (len = 0; len < sizeof(buf); len += n) {
176
32
    n = getrandom(&buf[len], sizeof(buf) - len, 0);
177
32
    if (n < 0)
178
0
      return -1;
179
32
  }
180
32
  arc4_addrandom(buf, sizeof(buf));
181
32
  evutil_memclear_(buf, sizeof(buf));
182
32
  return 0;
183
32
}
184
#endif /* EVENT__HAVE_GETRANDOM */
185
186
#if defined(EVENT__HAVE_SYS_SYSCTL_H) && defined(EVENT__HAVE_SYSCTL)
187
#if EVENT__HAVE_DECL_CTL_KERN && EVENT__HAVE_DECL_KERN_ARND
188
#define TRY_SEED_SYSCTL_BSD
189
static int
190
arc4_seed_sysctl_bsd(void)
191
{
192
  /* Based on code from William Ahern and from OpenBSD, this function
193
   * tries to use the KERN_ARND syscall to get entropy from the kernel.
194
   * This can work even if /dev/urandom is inaccessible for some reason
195
   * (e.g., we're running in a chroot). */
196
  int mib[] = { CTL_KERN, KERN_ARND };
197
  unsigned char buf[ADD_ENTROPY];
198
  size_t len, n;
199
  int i, any_set;
200
201
  memset(buf, 0, sizeof(buf));
202
203
  len = sizeof(buf);
204
  if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
205
    for (len = 0; len < sizeof(buf); len += sizeof(unsigned)) {
206
      n = sizeof(unsigned);
207
      if (n + len > sizeof(buf))
208
          n = len - sizeof(buf);
209
      if (sysctl(mib, 2, &buf[len], &n, NULL, 0) == -1)
210
        return -1;
211
    }
212
  }
213
  /* make sure that the buffer actually got set. */
214
  for (i=any_set=0; i<sizeof(buf); ++i) {
215
    any_set |= buf[i];
216
  }
217
  if (!any_set)
218
    return -1;
219
220
  arc4_addrandom(buf, sizeof(buf));
221
  evutil_memclear_(buf, sizeof(buf));
222
  return 0;
223
}
224
#endif
225
#endif /* defined(EVENT__HAVE_SYS_SYSCTL_H) */
226
227
#ifdef __linux__
228
#define TRY_SEED_PROC_SYS_KERNEL_RANDOM_UUID
229
static int
230
arc4_seed_proc_sys_kernel_random_uuid(void)
231
32
{
232
  /* Occasionally, somebody will make /proc/sys accessible in a chroot,
233
   * but not /dev/urandom.  Let's try /proc/sys/kernel/random/uuid.
234
   * Its format is stupid, so we need to decode it from hex.
235
   */
236
32
  int fd;
237
32
  char buf[128];
238
32
  unsigned char entropy[64];
239
32
  int bytes, n, i, nybbles;
240
96
  for (bytes = 0; bytes<ADD_ENTROPY; ) {
241
64
    fd = evutil_open_closeonexec_("/proc/sys/kernel/random/uuid", O_RDONLY, 0);
242
64
    if (fd < 0)
243
0
      return -1;
244
64
    n = read(fd, buf, sizeof(buf));
245
64
    close(fd);
246
64
    if (n<=0)
247
0
      return -1;
248
64
    memset(entropy, 0, sizeof(entropy));
249
2.43k
    for (i=nybbles=0; i<n; ++i) {
250
2.36k
      if (EVUTIL_ISXDIGIT_(buf[i])) {
251
2.04k
        int nyb = evutil_hex_char_to_int_(buf[i]);
252
2.04k
        if (nybbles & 1) {
253
1.02k
          entropy[nybbles/2] |= nyb;
254
1.02k
        } else {
255
1.02k
          entropy[nybbles/2] |= nyb<<4;
256
1.02k
        }
257
2.04k
        ++nybbles;
258
2.04k
      }
259
2.36k
    }
260
64
    if (nybbles < 2)
261
0
      return -1;
262
64
    arc4_addrandom(entropy, nybbles/2);
263
64
    bytes += nybbles/2;
264
64
  }
265
32
  evutil_memclear_(entropy, sizeof(entropy));
266
32
  evutil_memclear_(buf, sizeof(buf));
267
32
  return 0;
268
32
}
269
#endif
270
271
#ifndef _WIN32
272
#define TRY_SEED_URANDOM
273
static char *arc4random_urandom_filename = NULL;
274
275
static int arc4_seed_urandom_helper_(const char *fname)
276
64
{
277
64
  unsigned char buf[ADD_ENTROPY];
278
64
  int fd;
279
64
  size_t n;
280
281
64
  fd = evutil_open_closeonexec_(fname, O_RDONLY, 0);
282
64
  if (fd<0)
283
32
    return -1;
284
32
  n = read_all(fd, buf, sizeof(buf));
285
32
  close(fd);
286
32
  if (n != sizeof(buf))
287
0
    return -1;
288
32
  arc4_addrandom(buf, sizeof(buf));
289
32
  evutil_memclear_(buf, sizeof(buf));
290
32
  return 0;
291
32
}
292
293
static int
294
arc4_seed_urandom(void)
295
32
{
296
  /* This is adapted from Tor's crypto_seed_rng() */
297
32
  static const char *filenames[] = {
298
32
    "/dev/srandom", "/dev/urandom", "/dev/random", NULL
299
32
  };
300
32
  int i;
301
32
  if (arc4random_urandom_filename)
302
0
    return arc4_seed_urandom_helper_(arc4random_urandom_filename);
303
304
64
  for (i = 0; filenames[i]; ++i) {
305
64
    if (arc4_seed_urandom_helper_(filenames[i]) == 0) {
306
32
      return 0;
307
32
    }
308
64
  }
309
310
0
  return -1;
311
32
}
312
#endif
313
314
static int
315
arc4_seed(void)
316
32
{
317
32
  int ok = 0;
318
  /* We try every method that might work, and don't give up even if one
319
   * does seem to work.  There's no real harm in over-seeding, and if
320
   * one of these sources turns out to be broken, that would be bad. */
321
#ifdef TRY_SEED_WIN32
322
  if (0 == arc4_seed_win32())
323
    ok = 1;
324
#endif
325
32
#ifdef TRY_SEED_GETRANDOM
326
32
  if (0 == arc4_seed_getrandom())
327
32
    ok = 1;
328
32
#endif
329
32
#ifdef TRY_SEED_URANDOM
330
32
  if (0 == arc4_seed_urandom())
331
32
    ok = 1;
332
32
#endif
333
32
#ifdef TRY_SEED_PROC_SYS_KERNEL_RANDOM_UUID
334
32
  if (arc4random_urandom_filename == NULL &&
335
32
      0 == arc4_seed_proc_sys_kernel_random_uuid())
336
32
    ok = 1;
337
32
#endif
338
#ifdef TRY_SEED_SYSCTL_BSD
339
  if (0 == arc4_seed_sysctl_bsd())
340
    ok = 1;
341
#endif
342
32
  return ok ? 0 : -1;
343
32
}
344
345
static inline unsigned int
346
arc4_getword(void);
347
static int
348
arc4_stir(void)
349
32
{
350
32
  int     i;
351
32
  ARC4RANDOM_UINT32 rekey_fuzz; 
352
353
32
  if (!rs_initialized) {
354
16
    arc4_init();
355
16
    rs_initialized = 1;
356
16
  }
357
358
32
  if (0 != arc4_seed())
359
0
    return -1;
360
361
  /*
362
   * Discard early keystream, as per recommendations in
363
   * "Weaknesses in the Key Scheduling Algorithm of RC4" by
364
   * Scott Fluhrer, Itsik Mantin, and Adi Shamir.
365
   * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
366
   *
367
   * Ilya Mironov's "(Not So) Random Shuffles of RC4" suggests that
368
   * we drop at least 2*256 bytes, with 12*256 as a conservative
369
   * value.
370
   *
371
   * RFC4345 says to drop 6*256.
372
   *
373
   * At least some versions of this code drop 4*256, in a mistaken
374
   * belief that "words" in the Fluhrer/Mantin/Shamir paper refers
375
   * to processor words.
376
   *
377
   * We add another sect to the cargo cult, and choose 16*256.
378
   */
379
131k
  for (i = 0; i < 16*256; i++)
380
131k
    (void)arc4_getbyte();
381
382
32
  rekey_fuzz = arc4_getword();
383
  /* rekey interval should not be predictable */
384
32
  arc4_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
385
386
32
  return 0;
387
32
}
388
389
390
static void
391
arc4_stir_if_needed(void)
392
16
{
393
16
  pid_t pid = getpid();
394
395
16
  if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid)
396
16
  {
397
16
    arc4_stir_pid = pid;
398
16
    arc4_stir();
399
16
  }
400
16
}
401
402
static inline unsigned char
403
arc4_getbyte(void)
404
135k
{
405
135k
  unsigned char si, sj;
406
407
135k
  rs.i = (rs.i + 1);
408
135k
  si = rs.s[rs.i];
409
135k
  rs.j = (rs.j + si);
410
135k
  sj = rs.s[rs.j];
411
135k
  rs.s[rs.i] = sj;
412
135k
  rs.s[rs.j] = si;
413
135k
  return (rs.s[(si + sj) & 0xff]);
414
135k
}
415
416
static inline unsigned int
417
arc4_getword(void)
418
32
{
419
32
  unsigned int val;
420
421
32
  val = (unsigned)arc4_getbyte() << 24;
422
32
  val |= arc4_getbyte() << 16;
423
32
  val |= arc4_getbyte() << 8;
424
32
  val |= arc4_getbyte();
425
426
32
  return val;
427
32
}
428
429
#ifndef ARC4RANDOM_NOSTIR
430
ARC4RANDOM_EXPORT int
431
arc4random_stir(void)
432
{
433
  int val;
434
  ARC4_LOCK_();
435
  val = arc4_stir();
436
  ARC4_UNLOCK_();
437
  return val;
438
}
439
#endif
440
441
#ifndef ARC4RANDOM_NOADDRANDOM
442
ARC4RANDOM_EXPORT void
443
arc4random_addrandom(const unsigned char *dat, int datlen)
444
16
{
445
16
  int j;
446
16
  ARC4_LOCK_();
447
16
  if (!rs_initialized)
448
0
    arc4_stir();
449
32
  for (j = 0; j < datlen; j += 256) {
450
    /* arc4_addrandom() ignores all but the first 256 bytes of
451
     * its input.  We want to make sure to look at ALL the
452
     * data in 'dat', just in case the user is doing something
453
     * crazy like passing us all the files in /var/log. */
454
16
    arc4_addrandom(dat + j, datlen - j);
455
16
  }
456
16
  ARC4_UNLOCK_();
457
16
}
458
#endif
459
460
#ifndef ARC4RANDOM_NORANDOM
461
ARC4RANDOM_EXPORT ARC4RANDOM_UINT32
462
arc4random(void)
463
{
464
  ARC4RANDOM_UINT32 val;
465
  ARC4_LOCK_();
466
  arc4_count -= 4;
467
  arc4_stir_if_needed();
468
  val = arc4_getword();
469
  ARC4_UNLOCK_();
470
  return val;
471
}
472
#endif
473
474
#ifndef EVENT__HAVE_ARC4RANDOM_BUF
475
ARC4RANDOM_EXPORT void
476
arc4random_buf(void *buf_, size_t n)
477
16
{
478
16
  unsigned char *buf = buf_;
479
16
  ARC4_LOCK_();
480
16
  arc4_stir_if_needed();
481
4.11k
  while (n--) {
482
4.09k
    if (--arc4_count <= 0)
483
0
      arc4_stir();
484
4.09k
    buf[n] = arc4_getbyte();
485
4.09k
  }
486
16
  ARC4_UNLOCK_();
487
16
}
488
#endif  /* #ifndef EVENT__HAVE_ARC4RANDOM_BUF */
489
490
#ifndef ARC4RANDOM_NOUNIFORM
491
/*
492
 * Calculate a uniformly distributed random number less than upper_bound
493
 * avoiding "modulo bias".
494
 *
495
 * Uniformity is achieved by generating new random numbers until the one
496
 * returned is outside the range [0, 2**32 % upper_bound).  This
497
 * guarantees the selected random number will be inside
498
 * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
499
 * after reduction modulo upper_bound.
500
 */
501
ARC4RANDOM_EXPORT unsigned int
502
arc4random_uniform(unsigned int upper_bound)
503
{
504
  ARC4RANDOM_UINT32 r, min;
505
506
  if (upper_bound < 2)
507
    return 0;
508
509
  /* 2**32 % x == (2**32 - x) % x */
510
  min = -upper_bound % upper_bound;
511
512
  /*
513
   * This could theoretically loop forever but each retry has
514
   * p > 0.5 (worst case, usually far better) of selecting a
515
   * number inside the range we need, so it should rarely need
516
   * to re-roll.
517
   */
518
  for (;;) {
519
    r = arc4random();
520
    if (r >= min)
521
      break;
522
  }
523
524
  return r % upper_bound;
525
}
526
#endif