xref: /netbsd-src/sys/dev/random.c (revision 4dd1804b635bbe8846667b211785d50bf6c150a6)
1*4dd1804bSriastradh /*	$NetBSD: random.c,v 1.12 2024/08/27 00:56:47 riastradh Exp $	*/
25084c1b5Sriastradh 
35084c1b5Sriastradh /*-
45084c1b5Sriastradh  * Copyright (c) 2019 The NetBSD Foundation, Inc.
55084c1b5Sriastradh  * All rights reserved.
65084c1b5Sriastradh  *
75084c1b5Sriastradh  * This code is derived from software contributed to The NetBSD Foundation
85084c1b5Sriastradh  * by Taylor R. Campbell.
95084c1b5Sriastradh  *
105084c1b5Sriastradh  * Redistribution and use in source and binary forms, with or without
115084c1b5Sriastradh  * modification, are permitted provided that the following conditions
125084c1b5Sriastradh  * are met:
135084c1b5Sriastradh  * 1. Redistributions of source code must retain the above copyright
145084c1b5Sriastradh  *    notice, this list of conditions and the following disclaimer.
155084c1b5Sriastradh  * 2. Redistributions in binary form must reproduce the above copyright
165084c1b5Sriastradh  *    notice, this list of conditions and the following disclaimer in the
175084c1b5Sriastradh  *    documentation and/or other materials provided with the distribution.
185084c1b5Sriastradh  *
195084c1b5Sriastradh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
205084c1b5Sriastradh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
215084c1b5Sriastradh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
225084c1b5Sriastradh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
235084c1b5Sriastradh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
245084c1b5Sriastradh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
255084c1b5Sriastradh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
265084c1b5Sriastradh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
275084c1b5Sriastradh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
285084c1b5Sriastradh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
295084c1b5Sriastradh  * POSSIBILITY OF SUCH DAMAGE.
305084c1b5Sriastradh  */
315084c1b5Sriastradh 
325084c1b5Sriastradh /*
335084c1b5Sriastradh  * /dev/random, /dev/urandom -- stateless version
345084c1b5Sriastradh  *
355084c1b5Sriastradh  *	For short reads from /dev/urandom, up to 256 bytes, read from a
365084c1b5Sriastradh  *	per-CPU NIST Hash_DRBG instance that is reseeded as soon as the
375084c1b5Sriastradh  *	system has enough entropy.
385084c1b5Sriastradh  *
395084c1b5Sriastradh  *	For all other reads, instantiate a fresh NIST Hash_DRBG from
405084c1b5Sriastradh  *	the global entropy pool, and draw from it.
415084c1b5Sriastradh  *
425084c1b5Sriastradh  *	Each read is independent; there is no per-open state.
435084c1b5Sriastradh  *	Concurrent reads from the same open run in parallel.
445084c1b5Sriastradh  *
455084c1b5Sriastradh  *	Reading from /dev/random may block until entropy is available.
465084c1b5Sriastradh  *	Either device may return short reads if interrupted.
475084c1b5Sriastradh  */
485084c1b5Sriastradh 
495084c1b5Sriastradh #include <sys/cdefs.h>
50*4dd1804bSriastradh __KERNEL_RCSID(0, "$NetBSD: random.c,v 1.12 2024/08/27 00:56:47 riastradh Exp $");
515084c1b5Sriastradh 
525084c1b5Sriastradh #include <sys/param.h>
535084c1b5Sriastradh #include <sys/types.h>
54ea26d8a7Sriastradh #include <sys/atomic.h>
555084c1b5Sriastradh #include <sys/conf.h>
565084c1b5Sriastradh #include <sys/cprng.h>
575084c1b5Sriastradh #include <sys/entropy.h>
585084c1b5Sriastradh #include <sys/errno.h>
595084c1b5Sriastradh #include <sys/event.h>
605084c1b5Sriastradh #include <sys/fcntl.h>
615084c1b5Sriastradh #include <sys/kauth.h>
621b74d9d4Sriastradh #include <sys/kmem.h>
635084c1b5Sriastradh #include <sys/lwp.h>
645084c1b5Sriastradh #include <sys/poll.h>
65bdad8b27Sriastradh #include <sys/random.h>
665084c1b5Sriastradh #include <sys/rnd.h>
675084c1b5Sriastradh #include <sys/rndsource.h>
685084c1b5Sriastradh #include <sys/signalvar.h>
695084c1b5Sriastradh #include <sys/systm.h>
70909ca466Sriastradh #include <sys/vnode.h>		/* IO_NDELAY */
715084c1b5Sriastradh 
725084c1b5Sriastradh #include "ioconf.h"
735084c1b5Sriastradh 
745084c1b5Sriastradh static dev_type_open(random_open);
755084c1b5Sriastradh static dev_type_close(random_close);
765084c1b5Sriastradh static dev_type_ioctl(random_ioctl);
775084c1b5Sriastradh static dev_type_poll(random_poll);
785084c1b5Sriastradh static dev_type_kqfilter(random_kqfilter);
795084c1b5Sriastradh static dev_type_read(random_read);
805084c1b5Sriastradh static dev_type_write(random_write);
815084c1b5Sriastradh 
825084c1b5Sriastradh const struct cdevsw rnd_cdevsw = {
835084c1b5Sriastradh 	.d_open = random_open,
845084c1b5Sriastradh 	.d_close = random_close,
855084c1b5Sriastradh 	.d_read = random_read,
865084c1b5Sriastradh 	.d_write = random_write,
875084c1b5Sriastradh 	.d_ioctl = random_ioctl,
885084c1b5Sriastradh 	.d_stop = nostop,
895084c1b5Sriastradh 	.d_tty = notty,
905084c1b5Sriastradh 	.d_poll = random_poll,
915084c1b5Sriastradh 	.d_mmap = nommap,
925084c1b5Sriastradh 	.d_kqfilter = random_kqfilter,
935084c1b5Sriastradh 	.d_discard = nodiscard,
945084c1b5Sriastradh 	.d_flag = D_OTHER|D_MPSAFE,
955084c1b5Sriastradh };
965084c1b5Sriastradh 
975084c1b5Sriastradh #define	RANDOM_BUFSIZE	512	/* XXX pulled from arse */
985084c1b5Sriastradh 
995084c1b5Sriastradh /* Entropy source for writes to /dev/random and /dev/urandom */
1005084c1b5Sriastradh static krndsource_t	user_rndsource;
1015084c1b5Sriastradh 
1025084c1b5Sriastradh void
1035084c1b5Sriastradh rndattach(int num)
1045084c1b5Sriastradh {
1055084c1b5Sriastradh 
1065084c1b5Sriastradh 	rnd_attach_source(&user_rndsource, "/dev/random", RND_TYPE_UNKNOWN,
1075084c1b5Sriastradh 	    RND_FLAG_COLLECT_VALUE);
1085084c1b5Sriastradh }
1095084c1b5Sriastradh 
1105084c1b5Sriastradh static int
1115084c1b5Sriastradh random_open(dev_t dev, int flags, int fmt, struct lwp *l)
1125084c1b5Sriastradh {
1135084c1b5Sriastradh 
1145084c1b5Sriastradh 	/* Validate minor.  */
1155084c1b5Sriastradh 	switch (minor(dev)) {
1165084c1b5Sriastradh 	case RND_DEV_RANDOM:
1175084c1b5Sriastradh 	case RND_DEV_URANDOM:
1185084c1b5Sriastradh 		break;
1195084c1b5Sriastradh 	default:
1205084c1b5Sriastradh 		return ENXIO;
1215084c1b5Sriastradh 	}
1225084c1b5Sriastradh 
1235084c1b5Sriastradh 	return 0;
1245084c1b5Sriastradh }
1255084c1b5Sriastradh 
1265084c1b5Sriastradh static int
1275084c1b5Sriastradh random_close(dev_t dev, int flags, int fmt, struct lwp *l)
1285084c1b5Sriastradh {
1295084c1b5Sriastradh 
1305084c1b5Sriastradh 	/* Success!  */
1315084c1b5Sriastradh 	return 0;
1325084c1b5Sriastradh }
1335084c1b5Sriastradh 
1345084c1b5Sriastradh static int
1355084c1b5Sriastradh random_ioctl(dev_t dev, unsigned long cmd, void *data, int flag, struct lwp *l)
1365084c1b5Sriastradh {
1375084c1b5Sriastradh 
1385084c1b5Sriastradh 	/*
1395084c1b5Sriastradh 	 * No non-blocking/async options; otherwise defer to
1405084c1b5Sriastradh 	 * entropy_ioctl.
1415084c1b5Sriastradh 	 */
1425084c1b5Sriastradh 	switch (cmd) {
1435084c1b5Sriastradh 	case FIONBIO:
1445084c1b5Sriastradh 	case FIOASYNC:
1455084c1b5Sriastradh 		return 0;
1465084c1b5Sriastradh 	default:
1475084c1b5Sriastradh 		return entropy_ioctl(cmd, data);
1485084c1b5Sriastradh 	}
1495084c1b5Sriastradh }
1505084c1b5Sriastradh 
1515084c1b5Sriastradh static int
1525084c1b5Sriastradh random_poll(dev_t dev, int events, struct lwp *l)
1535084c1b5Sriastradh {
1545084c1b5Sriastradh 
1555084c1b5Sriastradh 	/* /dev/random may block; /dev/urandom is always ready.  */
1565084c1b5Sriastradh 	switch (minor(dev)) {
1575084c1b5Sriastradh 	case RND_DEV_RANDOM:
1585084c1b5Sriastradh 		return entropy_poll(events);
1595084c1b5Sriastradh 	case RND_DEV_URANDOM:
1605084c1b5Sriastradh 		return events & (POLLIN|POLLRDNORM | POLLOUT|POLLWRNORM);
1615084c1b5Sriastradh 	default:
1625084c1b5Sriastradh 		return 0;
1635084c1b5Sriastradh 	}
1645084c1b5Sriastradh }
1655084c1b5Sriastradh 
1665084c1b5Sriastradh static int
1675084c1b5Sriastradh random_kqfilter(dev_t dev, struct knote *kn)
1685084c1b5Sriastradh {
1695084c1b5Sriastradh 
1705084c1b5Sriastradh 	/* Validate the event filter.  */
1715084c1b5Sriastradh 	switch (kn->kn_filter) {
1725084c1b5Sriastradh 	case EVFILT_READ:
1735084c1b5Sriastradh 	case EVFILT_WRITE:
1745084c1b5Sriastradh 		break;
1755084c1b5Sriastradh 	default:
1765084c1b5Sriastradh 		return EINVAL;
1775084c1b5Sriastradh 	}
1785084c1b5Sriastradh 
1795084c1b5Sriastradh 	/* /dev/random may block; /dev/urandom never does.  */
1805084c1b5Sriastradh 	switch (minor(dev)) {
1815084c1b5Sriastradh 	case RND_DEV_RANDOM:
1825084c1b5Sriastradh 		if (kn->kn_filter == EVFILT_READ)
1835084c1b5Sriastradh 			return entropy_kqfilter(kn);
1845084c1b5Sriastradh 		/* FALLTHROUGH */
1855084c1b5Sriastradh 	case RND_DEV_URANDOM:
1865084c1b5Sriastradh 		kn->kn_fop = &seltrue_filtops;
1875084c1b5Sriastradh 		return 0;
1885084c1b5Sriastradh 	default:
1895084c1b5Sriastradh 		return ENXIO;
1905084c1b5Sriastradh 	}
1915084c1b5Sriastradh }
1925084c1b5Sriastradh 
1935084c1b5Sriastradh /*
1945084c1b5Sriastradh  * random_read(dev, uio, flags)
1955084c1b5Sriastradh  *
1965084c1b5Sriastradh  *	Generate data from a PRNG seeded from the entropy pool.
1975084c1b5Sriastradh  *
1985084c1b5Sriastradh  *	- If /dev/random, block until we have full entropy, or fail
1995084c1b5Sriastradh  *	  with EWOULDBLOCK, and if `depleting' entropy, return at most
2005084c1b5Sriastradh  *	  the entropy pool's capacity at once.
2015084c1b5Sriastradh  *
2025084c1b5Sriastradh  *	- If /dev/urandom, generate data from whatever is in the
2035084c1b5Sriastradh  *	  entropy pool now.
2045084c1b5Sriastradh  *
2055084c1b5Sriastradh  *	On interrupt, return a short read, but not shorter than 256
2065084c1b5Sriastradh  *	bytes (actually, no shorter than RANDOM_BUFSIZE bytes, which is
2075084c1b5Sriastradh  *	512 for hysterical raisins).
2085084c1b5Sriastradh  */
2095084c1b5Sriastradh static int
2105084c1b5Sriastradh random_read(dev_t dev, struct uio *uio, int flags)
2115084c1b5Sriastradh {
212bdad8b27Sriastradh 	int gflags;
2135084c1b5Sriastradh 
214bdad8b27Sriastradh 	/* Set the appropriate GRND_* mode.  */
215bdad8b27Sriastradh 	switch (minor(dev)) {
216bdad8b27Sriastradh 	case RND_DEV_RANDOM:
217bdad8b27Sriastradh 		gflags = GRND_RANDOM;
2185084c1b5Sriastradh 		break;
219bdad8b27Sriastradh 	case RND_DEV_URANDOM:
220bdad8b27Sriastradh 		gflags = GRND_INSECURE;
2215084c1b5Sriastradh 		break;
222bdad8b27Sriastradh 	default:
223bdad8b27Sriastradh 		return ENXIO;
2245084c1b5Sriastradh 	}
2255084c1b5Sriastradh 
226909ca466Sriastradh 	/*
227909ca466Sriastradh 	 * Set GRND_NONBLOCK if the user requested IO_NDELAY (i.e., the
228909ca466Sriastradh 	 * file was opened with O_NONBLOCK).
229909ca466Sriastradh 	 */
230909ca466Sriastradh 	if (flags & IO_NDELAY)
231bdad8b27Sriastradh 		gflags |= GRND_NONBLOCK;
2327460c1deSriastradh 
233bdad8b27Sriastradh 	/* Defer to getrandom.  */
234bdad8b27Sriastradh 	return dogetrandom(uio, gflags);
2355084c1b5Sriastradh }
2365084c1b5Sriastradh 
2375084c1b5Sriastradh /*
2385084c1b5Sriastradh  * random_write(dev, uio, flags)
2395084c1b5Sriastradh  *
2405084c1b5Sriastradh  *	Enter data from uio into the entropy pool.
2415084c1b5Sriastradh  *
2425084c1b5Sriastradh  *	Assume privileged users provide full entropy, and unprivileged
2435084c1b5Sriastradh  *	users provide no entropy.  If you have a nonuniform source of
2445084c1b5Sriastradh  *	data with n bytes of min-entropy, hash it with an XOF like
2455084c1b5Sriastradh  *	SHAKE128 into exactly n bytes first.
2465084c1b5Sriastradh  */
2475084c1b5Sriastradh static int
2485084c1b5Sriastradh random_write(dev_t dev, struct uio *uio, int flags)
2495084c1b5Sriastradh {
2505084c1b5Sriastradh 	kauth_cred_t cred = kauth_cred_get();
2515084c1b5Sriastradh 	uint8_t *buf;
252bbed1747Sriastradh 	bool privileged = false, any = false;
2535084c1b5Sriastradh 	int error = 0;
2545084c1b5Sriastradh 
2555084c1b5Sriastradh 	/* Verify user's authorization to affect the entropy pool.  */
2565084c1b5Sriastradh 	error = kauth_authorize_device(cred, KAUTH_DEVICE_RND_ADDDATA,
2575084c1b5Sriastradh 	    NULL, NULL, NULL, NULL);
2585084c1b5Sriastradh 	if (error)
2595084c1b5Sriastradh 		return error;
2605084c1b5Sriastradh 
2615084c1b5Sriastradh 	/*
2625084c1b5Sriastradh 	 * Check whether user is privileged.  If so, assume user
2635084c1b5Sriastradh 	 * furnishes full-entropy data; if not, accept user's data but
2645084c1b5Sriastradh 	 * assume it has zero entropy when we do accounting.  If you
2655084c1b5Sriastradh 	 * want to specify less entropy, use ioctl(RNDADDDATA).
2665084c1b5Sriastradh 	 */
2675084c1b5Sriastradh 	if (kauth_authorize_device(cred, KAUTH_DEVICE_RND_ADDDATA_ESTIMATE,
2685084c1b5Sriastradh 		NULL, NULL, NULL, NULL) == 0)
2695084c1b5Sriastradh 		privileged = true;
2705084c1b5Sriastradh 
2715084c1b5Sriastradh 	/* Get a buffer for transfers.  */
2721b74d9d4Sriastradh 	buf = kmem_alloc(RANDOM_BUFSIZE, KM_SLEEP);
2735084c1b5Sriastradh 
2745084c1b5Sriastradh 	/* Consume data.  */
2755084c1b5Sriastradh 	while (uio->uio_resid) {
2767460c1deSriastradh 		size_t n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
2775084c1b5Sriastradh 
2787460c1deSriastradh 		/* Transfer n bytes in and enter them into the pool.  */
2797460c1deSriastradh 		error = uiomove(buf, n, uio);
2807460c1deSriastradh 		if (error)
2817460c1deSriastradh 			break;
2827460c1deSriastradh 		rnd_add_data(&user_rndsource, buf, n, privileged ? n*NBBY : 0);
2837460c1deSriastradh 		any = true;
2845084c1b5Sriastradh 
28564191176Sriastradh 		/* Now's a good time to yield if needed.  */
28664191176Sriastradh 		preempt_point();
2875084c1b5Sriastradh 
2885084c1b5Sriastradh 		/* Check for interruption.  */
2895084c1b5Sriastradh 		if (__predict_false(curlwp->l_flag & LW_PENDSIG) &&
2905084c1b5Sriastradh 		    sigispending(curlwp, 0)) {
2915729ed03Sriastradh 			error = EINTR;
2925084c1b5Sriastradh 			break;
2935084c1b5Sriastradh 		}
2945084c1b5Sriastradh 	}
2955084c1b5Sriastradh 
2961b74d9d4Sriastradh 	/* Zero the buffer and free it.  */
2975084c1b5Sriastradh 	explicit_memset(buf, 0, RANDOM_BUFSIZE);
2981b74d9d4Sriastradh 	kmem_free(buf, RANDOM_BUFSIZE);
299bbed1747Sriastradh 
300bbed1747Sriastradh 	/* If we added anything, consolidate entropy now.  */
301fd332dc4Sriastradh 	if (any && error == 0)
302*4dd1804bSriastradh 		error = entropy_consolidate();
303bbed1747Sriastradh 
3045084c1b5Sriastradh 	return error;
3055084c1b5Sriastradh }
306