1*be1d1982Stb /* $OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $ */
2af7d2391Sdm
3af7d2391Sdm /*
4b9472f3eSkjell * Copyright (c) 1996, David Mazieres <dm@uun.org>
5f7b5bfc7Sotto * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
690c1fad7Smarkus * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
708735ac4Sderaadt * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
8af7d2391Sdm *
9e6ea6f36Skjell * Permission to use, copy, modify, and distribute this software for any
10e6ea6f36Skjell * purpose with or without fee is hereby granted, provided that the above
11e6ea6f36Skjell * copyright notice and this permission notice appear in all copies.
12e6ea6f36Skjell *
13e6ea6f36Skjell * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14e6ea6f36Skjell * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15e6ea6f36Skjell * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16e6ea6f36Skjell * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17e6ea6f36Skjell * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18e6ea6f36Skjell * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19e6ea6f36Skjell * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20af7d2391Sdm */
21af7d2391Sdm
22af7d2391Sdm /*
2390c1fad7Smarkus * ChaCha based random number generator for OpenBSD.
24af7d2391Sdm */
25af7d2391Sdm
26af7d2391Sdm #include <fcntl.h>
27f7b5bfc7Sotto #include <limits.h>
2851205cb4Smatthew #include <signal.h>
292cc388baSbcook #include <stdint.h>
3058720cefSmillert #include <stdlib.h>
3190c1fad7Smarkus #include <string.h>
3258720cefSmillert #include <unistd.h>
33af7d2391Sdm #include <sys/types.h>
34af7d2391Sdm #include <sys/time.h>
35af7d2391Sdm
3690c1fad7Smarkus #define KEYSTREAM_ONLY
3790c1fad7Smarkus #include "chacha_private.h"
3890c1fad7Smarkus
399d355e46Sbcook #define minimum(a, b) ((a) < (b) ? (a) : (b))
409d355e46Sbcook
419d355e46Sbcook #if defined(__GNUC__) || defined(_MSC_VER)
42af7d2391Sdm #define inline __inline
439d355e46Sbcook #else /* __GNUC__ || _MSC_VER */
44af7d2391Sdm #define inline
459d355e46Sbcook #endif /* !__GNUC__ && !_MSC_VER */
46af7d2391Sdm
4790c1fad7Smarkus #define KEYSZ 32
4890c1fad7Smarkus #define IVSZ 8
4990c1fad7Smarkus #define BLOCKSZ 64
5090c1fad7Smarkus #define RSBUFSZ (16*BLOCKSZ)
514caf194bSmatthew
52d9204e62Sdjm #define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
53d9204e62Sdjm
546e3588f2Smatthew /* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
552ef411b6Sderaadt static struct _rs {
564caf194bSmatthew size_t rs_have; /* valid bytes at end of rs_buf */
574caf194bSmatthew size_t rs_count; /* bytes till reseed */
584caf194bSmatthew } *rs;
596e3588f2Smatthew
6040e6dbbdSderaadt /* Maybe be preserved in fork children, if _rs_allocate() decides. */
6140e6dbbdSderaadt static struct _rsx {
626e3588f2Smatthew chacha_ctx rs_chacha; /* chacha context for random keystream */
636e3588f2Smatthew u_char rs_buf[RSBUFSZ]; /* keystream blocks */
646e3588f2Smatthew } *rsx;
65af7d2391Sdm
6640e6dbbdSderaadt static inline int _rs_allocate(struct _rs **, struct _rsx **);
6740e6dbbdSderaadt static inline void _rs_forkdetect(void);
6840e6dbbdSderaadt #include "arc4random.h"
6940e6dbbdSderaadt
7090c1fad7Smarkus static inline void _rs_rekey(u_char *dat, size_t datlen);
7184310223Sdjm
7298694cd0Sbcook static inline void
_rs_init(u_char * buf,size_t n)7390c1fad7Smarkus _rs_init(u_char *buf, size_t n)
74af7d2391Sdm {
7590c1fad7Smarkus if (n < KEYSZ + IVSZ)
7690c1fad7Smarkus return;
77c8f7bca9Stedu
784caf194bSmatthew if (rs == NULL) {
7940e6dbbdSderaadt if (_rs_allocate(&rs, &rsx) == -1)
801bdfaae3Sderaadt _exit(1);
814caf194bSmatthew }
82c8f7bca9Stedu
83822b16c6Sdtucker chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);
846e3588f2Smatthew chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
85af7d2391Sdm }
86af7d2391Sdm
87af7d2391Sdm static void
_rs_stir(void)8890c1fad7Smarkus _rs_stir(void)
89af7d2391Sdm {
9090c1fad7Smarkus u_char rnd[KEYSZ + IVSZ];
91d9204e62Sdjm uint32_t rekey_fuzz = 0;
92229f4f6eSkurt
9351205cb4Smatthew if (getentropy(rnd, sizeof rnd) == -1)
94d976644eSbcook _getentropy_fail();
95af7d2391Sdm
964caf194bSmatthew if (!rs)
9790c1fad7Smarkus _rs_init(rnd, sizeof(rnd));
984caf194bSmatthew else
9990c1fad7Smarkus _rs_rekey(rnd, sizeof(rnd));
10099cf665fSderaadt explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
10184310223Sdjm
10290c1fad7Smarkus /* invalidate rs_buf */
1034caf194bSmatthew rs->rs_have = 0;
1046e3588f2Smatthew memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
10590c1fad7Smarkus
106d9204e62Sdjm /* rekey interval should not be predictable */
107d9204e62Sdjm chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
108d9204e62Sdjm (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
109d9204e62Sdjm rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
110af7d2391Sdm }
111af7d2391Sdm
11290c1fad7Smarkus static inline void
_rs_stir_if_needed(size_t len)11390c1fad7Smarkus _rs_stir_if_needed(size_t len)
1142e3c4f77Sguenther {
1152ef411b6Sderaadt _rs_forkdetect();
1164caf194bSmatthew if (!rs || rs->rs_count <= len)
11790c1fad7Smarkus _rs_stir();
1184caf194bSmatthew if (rs->rs_count <= len)
1194caf194bSmatthew rs->rs_count = 0;
1204caf194bSmatthew else
1214caf194bSmatthew rs->rs_count -= len;
1222e3c4f77Sguenther }
1232e3c4f77Sguenther
12490c1fad7Smarkus static inline void
_rs_rekey(u_char * dat,size_t datlen)12590c1fad7Smarkus _rs_rekey(u_char *dat, size_t datlen)
126af7d2391Sdm {
12790c1fad7Smarkus #ifndef KEYSTREAM_ONLY
1286e3588f2Smatthew memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
12990c1fad7Smarkus #endif
13090c1fad7Smarkus /* fill rs_buf with the keystream */
1316e3588f2Smatthew chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
1326e3588f2Smatthew rsx->rs_buf, sizeof(rsx->rs_buf));
13390c1fad7Smarkus /* mix in optional user provided data */
13490c1fad7Smarkus if (dat) {
13590c1fad7Smarkus size_t i, m;
136af7d2391Sdm
1379d355e46Sbcook m = minimum(datlen, KEYSZ + IVSZ);
13890c1fad7Smarkus for (i = 0; i < m; i++)
1396e3588f2Smatthew rsx->rs_buf[i] ^= dat[i];
14090c1fad7Smarkus }
14190c1fad7Smarkus /* immediately reinit for backtracking resistance */
1426e3588f2Smatthew _rs_init(rsx->rs_buf, KEYSZ + IVSZ);
1436e3588f2Smatthew memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
1446e3588f2Smatthew rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
145af7d2391Sdm }
146af7d2391Sdm
14790c1fad7Smarkus static inline void
_rs_random_buf(void * _buf,size_t n)14890c1fad7Smarkus _rs_random_buf(void *_buf, size_t n)
149af7d2391Sdm {
15090c1fad7Smarkus u_char *buf = (u_char *)_buf;
1516e3588f2Smatthew u_char *keystream;
15290c1fad7Smarkus size_t m;
15390c1fad7Smarkus
15490c1fad7Smarkus _rs_stir_if_needed(n);
15590c1fad7Smarkus while (n > 0) {
1564caf194bSmatthew if (rs->rs_have > 0) {
1579d355e46Sbcook m = minimum(n, rs->rs_have);
1586e3588f2Smatthew keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
1596e3588f2Smatthew - rs->rs_have;
1606e3588f2Smatthew memcpy(buf, keystream, m);
1616e3588f2Smatthew memset(keystream, 0, m);
16290c1fad7Smarkus buf += m;
16390c1fad7Smarkus n -= m;
1644caf194bSmatthew rs->rs_have -= m;
16590c1fad7Smarkus }
1664caf194bSmatthew if (rs->rs_have == 0)
16790c1fad7Smarkus _rs_rekey(NULL, 0);
16890c1fad7Smarkus }
16990c1fad7Smarkus }
17090c1fad7Smarkus
17190c1fad7Smarkus static inline void
_rs_random_u32(uint32_t * val)1722cc388baSbcook _rs_random_u32(uint32_t *val)
17390c1fad7Smarkus {
1746e3588f2Smatthew u_char *keystream;
175323bb523Sderaadt
17690c1fad7Smarkus _rs_stir_if_needed(sizeof(*val));
1774caf194bSmatthew if (rs->rs_have < sizeof(*val))
17890c1fad7Smarkus _rs_rekey(NULL, 0);
1796e3588f2Smatthew keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
1806e3588f2Smatthew memcpy(val, keystream, sizeof(*val));
1816e3588f2Smatthew memset(keystream, 0, sizeof(*val));
1824caf194bSmatthew rs->rs_have -= sizeof(*val);
183af7d2391Sdm }
184af7d2391Sdm
1852cc388baSbcook uint32_t
arc4random(void)186f723aa39Sderaadt arc4random(void)
187af7d2391Sdm {
1882cc388baSbcook uint32_t val;
18990c1fad7Smarkus
190229f4f6eSkurt _ARC4_LOCK();
19190c1fad7Smarkus _rs_random_u32(&val);
192229f4f6eSkurt _ARC4_UNLOCK();
193229f4f6eSkurt return val;
194af7d2391Sdm }
1950d943ef0Sguenther DEF_WEAK(arc4random);
196af7d2391Sdm
197f7b5bfc7Sotto void
arc4random_buf(void * buf,size_t n)19890c1fad7Smarkus arc4random_buf(void *buf, size_t n)
199f7b5bfc7Sotto {
200f7b5bfc7Sotto _ARC4_LOCK();
20190c1fad7Smarkus _rs_random_buf(buf, n);
202f7b5bfc7Sotto _ARC4_UNLOCK();
203f7b5bfc7Sotto }
2040d943ef0Sguenther DEF_WEAK(arc4random_buf);
205