1/* $OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $ */ 2 3/* 4 * Copyright (c) 1996, David Mazieres <dm@uun.org> 5 * Copyright (c) 2008, Damien Miller <djm@openbsd.org> 6 * Copyright (c) 2013, Markus Friedl <markus@openbsd.org> 7 * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22/* 23 * ChaCha based random number generator for OpenBSD. 24 */ 25#define _BSD_SOURCE 26 27#include <sys/cdefs.h> 28#if defined(__FreeBSD__) 29#include <assert.h> 30#endif 31#include <fcntl.h> 32#include <limits.h> 33#include <pthread.h> 34#include <signal.h> 35#include <stdint.h> 36#include <stdlib.h> 37#include <string.h> 38#include <unistd.h> 39#include <sys/types.h> 40#include <sys/time.h> 41 42/* 43 * Unfortunately, pthread_atfork() is broken on FreeBSD (at least 9 and 10) if 44 * a program does not link to -lthr. Callbacks registered with pthread_atfork() 45 * appear to fail silently. So, it is not always possible to detect a PID 46 * wraparound. 47 */ 48#define _ARC4_ATFORK(f) pthread_atfork(NULL, NULL, (f)) 49 50#define CHACHA_EMBED 51#define KEYSTREAM_ONLY 52#if defined(__FreeBSD__) 53#define ARC4RANDOM_FXRNG 1 54#else 55#define ARC4RANDOM_FXRNG 0 56#endif 57#include "chacha_private.h" 58 59#define minimum(a, b) ((a) < (b) ? (a) : (b)) 60 61#if defined(__GNUC__) || defined(_MSC_VER) 62#define inline __inline 63#else /* __GNUC__ || _MSC_VER */ 64#define inline 65#endif /* !__GNUC__ && !_MSC_VER */ 66 67#define KEYSZ 32 68#define IVSZ 8 69#define BLOCKSZ 64 70#define RSBUFSZ (16*BLOCKSZ) 71 72#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */ 73 74/* Marked INHERIT_ZERO, so zero'd out in fork children. */ 75static struct _rs { 76 size_t rs_have; /* valid bytes at end of rs_buf */ 77 size_t rs_count; /* bytes till reseed */ 78} *rs; 79 80/* Maybe be preserved in fork children, if _rs_allocate() decides. */ 81static struct _rsx { 82 chacha_ctx rs_chacha; /* chacha context for random keystream */ 83 u_char rs_buf[RSBUFSZ]; /* keystream blocks */ 84#ifdef __FreeBSD__ 85 uint32_t rs_seed_generation; /* 32-bit userspace RNG version */ 86#endif 87} *rsx; 88 89static inline int _rs_allocate(struct _rs **, struct _rsx **); 90static inline void _rs_forkdetect(void); 91#include "arc4random.h" 92 93static inline void _rs_rekey(u_char *dat, size_t datlen); 94 95static inline void 96_rs_init(u_char *buf, size_t n) 97{ 98 if (n < KEYSZ + IVSZ) 99 return; 100 101 if (rs == NULL) { 102 if (_rs_allocate(&rs, &rsx) == -1) 103 _exit(1); 104 } 105 106 chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8); 107 chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ); 108} 109 110static void 111_rs_stir(void) 112{ 113 u_char rnd[KEYSZ + IVSZ]; 114 uint32_t rekey_fuzz = 0; 115 116#if defined(__FreeBSD__) 117 bool need_init; 118 119 /* 120 * De-couple allocation (which locates the vdso_fxrngp pointer in 121 * auxinfo) from initialization. This allows us to read the root seed 122 * version before we fetch system entropy, maintaining the invariant 123 * that the PRF was seeded with entropy from rs_seed_generation or a 124 * later generation. But never seeded from an earlier generation. 125 * This invariant prevents us from missing a root reseed event. 126 */ 127 need_init = false; 128 if (rs == NULL) { 129 if (_rs_allocate(&rs, &rsx) == -1) 130 abort(); 131 need_init = true; 132 } 133 /* 134 * Transition period: new userspace on old kernel. This should become 135 * a hard error at some point, if the scheme is adopted. 136 */ 137 if (vdso_fxrngp != NULL) 138 rsx->rs_seed_generation = 139 fxrng_load_acq_generation(&vdso_fxrngp->fx_generation32); 140#endif 141 142 if (getentropy(rnd, sizeof rnd) == -1) 143 _getentropy_fail(); 144 145#if !defined(__FreeBSD__) 146 if (!rs) 147 _rs_init(rnd, sizeof(rnd)); 148#else /* __FreeBSD__ */ 149 assert(rs != NULL); 150 if (need_init) 151 _rs_init(rnd, sizeof(rnd)); 152#endif 153 else 154 _rs_rekey(rnd, sizeof(rnd)); 155 explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */ 156 157 /* invalidate rs_buf */ 158 rs->rs_have = 0; 159 memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); 160 161 /* rekey interval should not be predictable */ 162 chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz, 163 (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz)); 164 rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE); 165} 166 167static inline void 168_rs_stir_if_needed(size_t len) 169{ 170 _rs_forkdetect(); 171 if (!rs || rs->rs_count <= len) 172 _rs_stir(); 173 if (rs->rs_count <= len) 174 rs->rs_count = 0; 175 else 176 rs->rs_count -= len; 177} 178 179static inline void 180_rs_rekey(u_char *dat, size_t datlen) 181{ 182#ifndef KEYSTREAM_ONLY 183 memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); 184#endif 185 /* fill rs_buf with the keystream */ 186 chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf, 187 rsx->rs_buf, sizeof(rsx->rs_buf)); 188 /* mix in optional user provided data */ 189 if (dat) { 190 size_t i, m; 191 192 m = minimum(datlen, KEYSZ + IVSZ); 193 for (i = 0; i < m; i++) 194 rsx->rs_buf[i] ^= dat[i]; 195 } 196 /* immediately reinit for backtracking resistance */ 197 _rs_init(rsx->rs_buf, KEYSZ + IVSZ); 198 memset(rsx->rs_buf, 0, KEYSZ + IVSZ); 199 rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ; 200} 201 202static inline void 203_rs_random_buf(void *_buf, size_t n) 204{ 205 u_char *buf = (u_char *)_buf; 206 u_char *keystream; 207 size_t m; 208 209 _rs_stir_if_needed(n); 210 while (n > 0) { 211 if (rs->rs_have > 0) { 212 m = minimum(n, rs->rs_have); 213 keystream = rsx->rs_buf + sizeof(rsx->rs_buf) 214 - rs->rs_have; 215 memcpy(buf, keystream, m); 216 memset(keystream, 0, m); 217 buf += m; 218 n -= m; 219 rs->rs_have -= m; 220 } 221 if (rs->rs_have == 0) 222 _rs_rekey(NULL, 0); 223 } 224} 225 226static inline void 227_rs_random_u32(uint32_t *val) 228{ 229 u_char *keystream; 230 231 _rs_stir_if_needed(sizeof(*val)); 232 if (rs->rs_have < sizeof(*val)) 233 _rs_rekey(NULL, 0); 234 keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have; 235 memcpy(val, keystream, sizeof(*val)); 236 memset(keystream, 0, sizeof(*val)); 237 rs->rs_have -= sizeof(*val); 238} 239 240uint32_t 241arc4random(void) 242{ 243 uint32_t val; 244 245 _ARC4_LOCK(); 246 _rs_random_u32(&val); 247 _ARC4_UNLOCK(); 248 return val; 249} 250 251void 252arc4random_buf(void *buf, size_t n) 253{ 254 _ARC4_LOCK(); 255 _rs_random_buf(buf, n); 256 _ARC4_UNLOCK(); 257} 258