/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 | 21.9k | #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 | 2.13k | #define KEYSZ 32 |
73 | 1.42k | #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 | 356 | { |
88 | 356 | if (n < KEYSZ + IVSZ) |
89 | 0 | return; |
90 | 356 | chacha_keysetup(&rs, buf, KEYSZ * 8, 0); |
91 | 356 | chacha_ivsetup(&rs, buf + KEYSZ); |
92 | 356 | } |
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 | 21.7k | { |
119 | 21.7k | pid_t pid = getpid(); |
120 | | |
121 | 21.7k | if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) { |
122 | 1 | rs_stir_pid = pid; |
123 | 1 | _rs_stir(); |
124 | 1 | } else |
125 | 21.7k | rs_count -= len; |
126 | 21.7k | } |
127 | | |
128 | | static inline void |
129 | | _rs_rekey(unsigned char *dat, size_t datlen) |
130 | 355 | { |
131 | | #ifndef KEYSTREAM_ONLY |
132 | | memset(rs_buf, 0, sizeof(rs_buf)); |
133 | | #endif |
134 | | /* fill rs_buf with the keystream */ |
135 | 355 | chacha_encrypt_bytes(&rs, rs_buf, rs_buf, sizeof(rs_buf)); |
136 | | /* mix in optional user provided data */ |
137 | 355 | 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 | 355 | _rs_init(rs_buf, KEYSZ + IVSZ); |
146 | 355 | memset(rs_buf, 0, KEYSZ + IVSZ); // -V::512, 1086 |
147 | 355 | rs_have = sizeof(rs_buf) - KEYSZ - IVSZ; |
148 | 355 | } |
149 | | |
150 | | static inline void |
151 | | _rs_random_buf(void *_buf, size_t n) |
152 | 21.7k | { |
153 | 21.7k | unsigned char *buf = _buf; |
154 | 21.7k | unsigned char *keystream; |
155 | 21.7k | size_t m; |
156 | | |
157 | 21.7k | _rs_stir_if_needed(n); |
158 | 43.7k | while (n > 0) { |
159 | 21.9k | if (rs_have > 0) { |
160 | 21.9k | m = minimum(n, rs_have); |
161 | 21.9k | keystream = rs_buf + sizeof(rs_buf) - rs_have; |
162 | 21.9k | memcpy(buf, keystream, m); |
163 | 21.9k | memset(keystream, 0, m); |
164 | 21.9k | buf += m; |
165 | 21.9k | n -= m; |
166 | 21.9k | rs_have -= m; |
167 | 21.9k | } |
168 | 21.9k | if (rs_have == 0) |
169 | 355 | _rs_rekey(NULL, 0); |
170 | 21.9k | } |
171 | 21.7k | } |
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 | 21.7k | { |
201 | 21.7k | _ARC4_LOCK(); |
202 | 21.7k | _rs_random_buf(buf, n); |
203 | 21.7k | _ARC4_UNLOCK(); |
204 | 21.7k | } |
205 | | |
206 | | #endif /* HAVE_ARC4RANDOM */ |