xref: /openbsd-src/lib/libc/crypt/arc4random.c (revision be1d1982dd83d04eb51a63726cd066959e1d1907)
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