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