Coverage Report

Created: 2025-10-10 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/lib/util/arc4random.c
Line
Count
Source
1
/*  $OpenBSD: arc4random.c,v 1.54 2015/09/13 08:31:47 guenther Exp $  */
2
3
/*
4
 * SPDX-License-Identifier: ISC
5
 *
6
 * Copyright (c) 1996, David Mazieres <dm@uun.org>
7
 * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
8
 * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
9
 * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
10
 *
11
 * Permission to use, copy, modify, and distribute this software for any
12
 * purpose with or without fee is hereby granted, provided that the above
13
 * copyright notice and this permission notice appear in all copies.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
 */
23
24
/*
25
 * ChaCha based random number generator for OpenBSD.
26
 */
27
28
#include <config.h>
29
30
#ifndef HAVE_ARC4RANDOM
31
32
#ifdef HAVE_SYS_RANDOM_H
33
# include <sys/random.h>
34
#endif
35
36
#include <fcntl.h>
37
#include <limits.h>
38
#include <signal.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
#if defined(HAVE_STDINT_H)
43
# include <stdint.h>
44
#elif defined(HAVE_INTTYPES_H)
45
# include <inttypes.h>
46
#endif
47
48
#include <sudo_compat.h>
49
#include <sudo_fatal.h>
50
#include <sudo_rand.h>
51
52
#define KEYSTREAM_ONLY
53
#include "chacha_private.h"
54
55
20.6k
#define minimum(a, b) ((a) < (b) ? (a) : (b))
56
57
#ifdef __GNUC__
58
# define inline __inline
59
#else       /* !__GNUC__ */
60
# define inline
61
#endif        /* !__GNUC__ */
62
63
/* Sudo isn't multithreaded */
64
#define _ARC4_LOCK()
65
#define _ARC4_UNLOCK()
66
67
2.00k
#define KEYSZ 32
68
1.33k
#define IVSZ  8
69
#define BLOCKSZ 64
70
#define RSBUFSZ (16*BLOCKSZ)
71
static int rs_initialized;
72
static pid_t rs_stir_pid;
73
static chacha_ctx rs;   /* chacha context for random keystream */
74
static u_char rs_buf[RSBUFSZ];  /* keystream blocks */
75
static size_t rs_have;    /* valid bytes at end of rs_buf */
76
static size_t rs_count;   /* bytes till reseed */
77
78
static inline void _rs_rekey(unsigned char *dat, size_t datlen);
79
80
static inline void
81
_rs_init(unsigned char *buf, size_t n)
82
335
{
83
335
  if (n < KEYSZ + IVSZ)
84
0
    return;
85
335
  chacha_keysetup(&rs, buf, KEYSZ * 8, 0);
86
335
  chacha_ivsetup(&rs, buf + KEYSZ);
87
335
}
88
89
static void
90
_rs_stir(void)
91
1
{
92
1
  unsigned char rnd[KEYSZ + IVSZ];
93
94
1
  if (getentropy(rnd, sizeof rnd) == -1)
95
0
    sudo_fatal_nodebug("getentropy");
96
97
1
  if (!rs_initialized) {
98
1
    rs_initialized = 1;
99
1
    _rs_init(rnd, sizeof(rnd));
100
1
  } else
101
0
    _rs_rekey(rnd, sizeof(rnd));
102
1
  explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
103
104
  /* invalidate rs_buf */
105
1
  rs_have = 0;
106
1
  memset(rs_buf, 0, sizeof(rs_buf));
107
108
1
  rs_count = 1600000;
109
1
}
110
111
static inline void
112
_rs_stir_if_needed(size_t len)
113
20.4k
{
114
20.4k
  pid_t pid = getpid();
115
116
20.4k
  if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) {
117
1
    rs_stir_pid = pid;
118
1
    _rs_stir();
119
1
  } else
120
20.4k
    rs_count -= len;
121
20.4k
}
122
123
static inline void
124
_rs_rekey(unsigned char *dat, size_t datlen)
125
334
{
126
#ifndef KEYSTREAM_ONLY
127
  memset(rs_buf, 0, sizeof(rs_buf));
128
#endif
129
  /* fill rs_buf with the keystream */
130
334
  chacha_encrypt_bytes(&rs, rs_buf, rs_buf, sizeof(rs_buf));
131
  /* mix in optional user provided data */
132
334
  if (dat) {
133
0
    size_t i, m;
134
135
0
    m = minimum(datlen, KEYSZ + IVSZ);
136
0
    for (i = 0; i < m; i++)
137
0
      rs_buf[i] ^= dat[i];
138
0
  }
139
  /* immediately reinit for backtracking resistance */
140
334
  _rs_init(rs_buf, KEYSZ + IVSZ);
141
334
  memset(rs_buf, 0, KEYSZ + IVSZ); // -V::512, 1086
142
334
  rs_have = sizeof(rs_buf) - KEYSZ - IVSZ;
143
334
}
144
145
static inline void
146
_rs_random_buf(void *_buf, size_t n)
147
20.4k
{
148
20.4k
  unsigned char *buf = _buf;
149
20.4k
  unsigned char *keystream;
150
20.4k
  size_t m;
151
152
20.4k
  _rs_stir_if_needed(n);
153
41.1k
  while (n > 0) {
154
20.6k
    if (rs_have > 0) {
155
20.6k
      m = minimum(n, rs_have);
156
20.6k
      keystream = rs_buf + sizeof(rs_buf) - rs_have;
157
20.6k
      memcpy(buf, keystream, m);
158
20.6k
      memset(keystream, 0, m);
159
20.6k
      buf += m;
160
20.6k
      n -= m;
161
20.6k
      rs_have -= m;
162
20.6k
    }
163
20.6k
    if (rs_have == 0)
164
334
      _rs_rekey(NULL, 0);
165
20.6k
  }
166
20.4k
}
167
168
static inline void
169
_rs_random_u32(uint32_t *val)
170
0
{
171
0
  unsigned char *keystream;
172
173
0
  _rs_stir_if_needed(sizeof(*val));
174
0
  if (rs_have < sizeof(*val))
175
0
    _rs_rekey(NULL, 0);
176
0
  keystream = rs_buf + sizeof(rs_buf) - rs_have;
177
0
  memcpy(val, keystream, sizeof(*val));
178
0
  memset(keystream, 0, sizeof(*val));
179
0
  rs_have -= sizeof(*val);
180
0
}
181
182
uint32_t
183
sudo_arc4random(void)
184
0
{
185
0
  uint32_t val;
186
187
0
  _ARC4_LOCK();
188
0
  _rs_random_u32(&val);
189
0
  _ARC4_UNLOCK();
190
0
  return val;
191
0
}
192
193
void
194
sudo_arc4random_buf(void *buf, size_t n)
195
20.4k
{
196
20.4k
  _ARC4_LOCK();
197
20.4k
  _rs_random_buf(buf, n);
198
20.4k
  _ARC4_UNLOCK();
199
20.4k
}
200
201
#endif /* HAVE_ARC4RANDOM */