Coverage Report

Created: 2023-06-07 06:46

/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
2.04k
#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
399
#define KEYSZ 32
73
265
#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
67
{
88
67
  if (n < KEYSZ + IVSZ)
89
0
    return;
90
67
  chacha_keysetup(&rs, buf, KEYSZ * 8, 0);
91
67
  chacha_ivsetup(&rs, buf + KEYSZ);
92
67
}
93
94
static void
95
_rs_stir(void)
96
1
{
97
1
  unsigned char rnd[KEYSZ + IVSZ];
98
99
1
  if (getentropy(rnd, sizeof rnd) == -1)
100
0
    sudo_fatal_nodebug("getentropy");
101
102
1
  if (!rs_initialized) {
103
1
    rs_initialized = 1;
104
1
    _rs_init(rnd, sizeof(rnd));
105
1
  } else
106
0
    _rs_rekey(rnd, sizeof(rnd));
107
1
  explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
108
109
  /* invalidate rs_buf */
110
1
  rs_have = 0;
111
1
  memset(rs_buf, 0, sizeof(rs_buf));
112
113
1
  rs_count = 1600000;
114
1
}
115
116
static inline void
117
_rs_stir_if_needed(size_t len)
118
1.99k
{
119
1.99k
  pid_t pid = getpid();
120
121
1.99k
  if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) {
122
1
    rs_stir_pid = pid;
123
1
    _rs_stir();
124
1
  } else
125
1.99k
    rs_count -= len;
126
1.99k
}
127
128
static inline void
129
_rs_rekey(unsigned char *dat, size_t datlen)
130
66
{
131
#ifndef KEYSTREAM_ONLY
132
  memset(rs_buf, 0, sizeof(rs_buf));
133
#endif
134
  /* fill rs_buf with the keystream */
135
66
  chacha_encrypt_bytes(&rs, rs_buf, rs_buf, sizeof(rs_buf));
136
  /* mix in optional user provided data */
137
66
  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
66
  _rs_init(rs_buf, KEYSZ + IVSZ);
146
66
  memset(rs_buf, 0, KEYSZ + IVSZ); // -V::512, 1086
147
66
  rs_have = sizeof(rs_buf) - KEYSZ - IVSZ;
148
66
}
149
150
static inline void
151
_rs_random_buf(void *_buf, size_t n)
152
1.99k
{
153
1.99k
  unsigned char *buf = _buf;
154
1.99k
  unsigned char *keystream;
155
1.99k
  size_t m;
156
157
1.99k
  _rs_stir_if_needed(n);
158
4.04k
  while (n > 0) {
159
2.04k
    if (rs_have > 0) {
160
2.04k
      m = minimum(n, rs_have);
161
2.04k
      keystream = rs_buf + sizeof(rs_buf) - rs_have;
162
2.04k
      memcpy(buf, keystream, m);
163
2.04k
      memset(keystream, 0, m);
164
2.04k
      buf += m;
165
2.04k
      n -= m;
166
2.04k
      rs_have -= m;
167
2.04k
    }
168
2.04k
    if (rs_have == 0)
169
66
      _rs_rekey(NULL, 0);
170
2.04k
  }
171
1.99k
}
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
1.99k
{
201
1.99k
  _ARC4_LOCK();
202
1.99k
  _rs_random_buf(buf, n);
203
1.99k
  _ARC4_UNLOCK();
204
1.99k
}
205
206
#endif /* HAVE_ARC4RANDOM */