xref: /third_party/FreeBSD/lib/libc/gen/arc4random.c (revision f9f848fa)
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