Coverage Report

Created: 2023-06-07 06:47

/src/sudo/lib/util/arc4random.c
Line
Count
Source (jump to first uncovered line)
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
 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
26
 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
27
 */
28
29
/*
30
 * ChaCha based random number generator for OpenBSD.
31
 */
32
33
#include <config.h>
34
35
#ifndef HAVE_ARC4RANDOM
36
37
#ifdef HAVE_SYS_RANDOM_H
38
# include <sys/random.h>
39
#endif
40
41
#include <fcntl.h>
42
#include <limits.h>
43
#include <signal.h>
44
#include <stdlib.h>
45
#include <string.h>
46
#include <unistd.h>
47
#if defined(HAVE_STDINT_H)
48
# include <stdint.h>
49
#elif defined(HAVE_INTTYPES_H)
50
# include <inttypes.h>
51
#endif
52
53
#include "sudo_compat.h"
54
#include "sudo_fatal.h"
55
#include "sudo_rand.h"
56
57
#define KEYSTREAM_ONLY
58
#include "chacha_private.h"
59
60
28.7k
#define minimum(a, b) ((a) < (b) ? (a) : (b))
61
62
#ifdef __GNUC__
63
# define inline __inline
64
#else       /* !__GNUC__ */
65
# define inline
66
#endif        /* !__GNUC__ */
67
68
/* Sudo isn't multithreaded */
69
#define _ARC4_LOCK()
70
#define _ARC4_UNLOCK()
71
72
3.45k
#define KEYSZ 32
73
2.30k
#define IVSZ  8
74
#define BLOCKSZ 64
75
#define RSBUFSZ (16*BLOCKSZ)
76
static int rs_initialized;
77
static pid_t rs_stir_pid;
78
static chacha_ctx rs;   /* chacha context for random keystream */
79
static u_char rs_buf[RSBUFSZ];  /* keystream blocks */
80
static size_t rs_have;    /* valid bytes at end of rs_buf */
81
static size_t rs_count;   /* bytes till reseed */
82
83
static inline void _rs_rekey(unsigned char *dat, size_t datlen);
84
85
static inline void
86
_rs_init(unsigned char *buf, size_t n)
87
578
{
88
578
  if (n < KEYSZ + IVSZ)
89
0
    return;
90
578
  chacha_keysetup(&rs, buf, KEYSZ * 8, 0);
91
578
  chacha_ivsetup(&rs, buf + KEYSZ);
92
578
}
93
94
static void
95
_rs_stir(void)
96
4
{
97
4
  unsigned char rnd[KEYSZ + IVSZ];
98
99
4
  if (getentropy(rnd, sizeof rnd) == -1)
100
0
    sudo_fatal_nodebug("getentropy");
101
102
4
  if (!rs_initialized) {
103
4
    rs_initialized = 1;
104
4
    _rs_init(rnd, sizeof(rnd));
105
4
  } else
106
0
    _rs_rekey(rnd, sizeof(rnd));
107
4
  explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
108
109
  /* invalidate rs_buf */
110
4
  rs_have = 0;
111
4
  memset(rs_buf, 0, sizeof(rs_buf));
112
113
4
  rs_count = 1600000;
114
4
}
115
116
static inline void
117
_rs_stir_if_needed(size_t len)
118
28.4k
{
119
28.4k
  pid_t pid = getpid();
120
121
28.4k
  if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) {
122
4
    rs_stir_pid = pid;
123
4
    _rs_stir();
124
4
  } else
125
28.4k
    rs_count -= len;
126
28.4k
}
127
128
static inline void
129
_rs_rekey(unsigned char *dat, size_t datlen)
130
574
{
131
#ifndef KEYSTREAM_ONLY
132
  memset(rs_buf, 0, sizeof(rs_buf));
133
#endif
134
  /* fill rs_buf with the keystream */
135
574
  chacha_encrypt_bytes(&rs, rs_buf, rs_buf, sizeof(rs_buf));
136
  /* mix in optional user provided data */
137
574
  if (dat) {
138
0
    size_t i, m;
139
140
0
    m = minimum(datlen, KEYSZ + IVSZ);
141
0
    for (i = 0; i < m; i++)
142
0
      rs_buf[i] ^= dat[i];
143
0
  }
144
  /* immediately reinit for backtracking resistance */
145
574
  _rs_init(rs_buf, KEYSZ + IVSZ);
146
574
  memset(rs_buf, 0, KEYSZ + IVSZ); // -V::512, 1086
147
574
  rs_have = sizeof(rs_buf) - KEYSZ - IVSZ;
148
574
}
149
150
static inline void
151
_rs_random_buf(void *_buf, size_t n)
152
28.4k
{
153
28.4k
  unsigned char *buf = _buf;
154
28.4k
  unsigned char *keystream;
155
28.4k
  size_t m;
156
157
28.4k
  _rs_stir_if_needed(n);
158
57.2k
  while (n > 0) {
159
28.7k
    if (rs_have > 0) {
160
28.7k
      m = minimum(n, rs_have);
161
28.7k
      keystream = rs_buf + sizeof(rs_buf) - rs_have;
162
28.7k
      memcpy(buf, keystream, m);
163
28.7k
      memset(keystream, 0, m);
164
28.7k
      buf += m;
165
28.7k
      n -= m;
166
28.7k
      rs_have -= m;
167
28.7k
    }
168
28.7k
    if (rs_have == 0)
169
574
      _rs_rekey(NULL, 0);
170
28.7k
  }
171
28.4k
}
172
173
static inline void
174
_rs_random_u32(uint32_t *val)
175
0
{
176
0
  unsigned char *keystream;
177
178
0
  _rs_stir_if_needed(sizeof(*val));
179
0
  if (rs_have < sizeof(*val))
180
0
    _rs_rekey(NULL, 0);
181
0
  keystream = rs_buf + sizeof(rs_buf) - rs_have;
182
0
  memcpy(val, keystream, sizeof(*val));
183
0
  memset(keystream, 0, sizeof(*val));
184
0
  rs_have -= sizeof(*val);
185
0
}
186
187
uint32_t
188
sudo_arc4random(void)
189
0
{
190
0
  uint32_t val;
191
192
0
  _ARC4_LOCK();
193
0
  _rs_random_u32(&val);
194
0
  _ARC4_UNLOCK();
195
0
  return val;
196
0
}
197
198
void
199
sudo_arc4random_buf(void *buf, size_t n)
200
28.4k
{
201
28.4k
  _ARC4_LOCK();
202
28.4k
  _rs_random_buf(buf, n);
203
28.4k
  _ARC4_UNLOCK();
204
28.4k
}
205
206
#endif /* HAVE_ARC4RANDOM */