Edit

IABSD.fr/src/sys/dev/rnd.c

Branch :

  • Show log

    Commit

  • Author : tobhe
    Date : 2020-03-06 16:04:14
    Hash : 5ab2f231
    Message : Make sure 'ts' is initialized. ok deraadt@

  • sys/dev/rnd.c
  • /*	$OpenBSD: rnd.c,v 1.204 2020/03/06 16:04:14 tobhe Exp $	*/
    
    /*
     * Copyright (c) 2011 Theo de Raadt.
     * Copyright (c) 2008 Damien Miller.
     * Copyright (c) 1996, 1997, 2000-2002 Michael Shalayeff.
     * Copyright (c) 2013 Markus Friedl.
     * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999.
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, and the entire permission notice in its entirety,
     *    including the disclaimer of warranties.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. The name of the author may not be used to endorse or promote
     *    products derived from this software without specific prior
     *    written permission.
     *
     * ALTERNATIVELY, this product may be distributed under the terms of
     * the GNU Public License, in which case the provisions of the GPL are
     * required INSTEAD OF the above restrictions.  (This clause is
     * necessary due to a potential bad interaction between the GPL and
     * the restrictions contained in a BSD-style copyright.)
     *
     * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
     * OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /*
     * Computers are very predictable devices.  Hence it is extremely hard
     * to produce truly random numbers on a computer --- as opposed to
     * pseudo-random numbers, which can be easily generated by using an
     * algorithm.  Unfortunately, it is very easy for attackers to guess
     * the sequence of pseudo-random number generators, and for some
     * applications this is not acceptable.  Instead, we must try to
     * gather "environmental noise" from the computer's environment, which
     * must be hard for outside attackers to observe and use to
     * generate random numbers.  In a Unix environment, this is best done
     * from inside the kernel.
     *
     * Sources of randomness from the environment include inter-keyboard
     * timings, inter-interrupt timings from some interrupts, and other
     * events which are both (a) non-deterministic and (b) hard for an
     * outside observer to measure.  Randomness from these sources is
     * added to the "rnd states" queue; this is used as much of the
     * source material which is mixed on occasion using a CRC-like function
     * into the "entropy pool".  This is not cryptographically strong, but
     * it is adequate assuming the randomness is not chosen maliciously,
     * and it is very fast because the interrupt-time event is only to add
     * a small random token to the "rnd states" queue.
     *
     * When random bytes are desired, they are obtained by pulling from
     * the entropy pool and running a SHA512 hash. The SHA512 hash avoids
     * exposing the internal state of the entropy pool.  Even if it is
     * possible to analyze SHA512 in some clever way, as long as the amount
     * of data returned from the generator is less than the inherent
     * entropy in the pool, the output data is totally unpredictable.  For
     * this reason, the routine decreases its internal estimate of how many
     * bits of "true randomness" are contained in the entropy pool as it
     * outputs random numbers.
     *
     * If this estimate goes to zero, the SHA512 hash will continue to generate
     * output since there is no true risk because the SHA512 output is not
     * exported outside this subsystem.  It is next used as input to seed a
     * ChaCha20 stream cipher, which is re-seeded from time to time.  This
     * design provides very high amounts of output data from a potentially
     * small entropy base, at high enough speeds to encourage use of random
     * numbers in nearly any situation.  Before OpenBSD 5.5, the RC4 stream
     * cipher (also known as ARC4) was used instead of ChaCha20.
     *
     * The output of this single ChaCha20 engine is then shared amongst many
     * consumers in the kernel and userland via a few interfaces:
     * arc4random_buf(), arc4random(), arc4random_uniform(), randomread()
     * for the set of /dev/random nodes and the system call getentropy(),
     * which provides seeds for process-context pseudorandom generators.
     *
     * Acknowledgements:
     * =================
     *
     * Ideas for constructing this random number generator were derived
     * from Pretty Good Privacy's random number generator, and from private
     * discussions with Phil Karn.  Colin Plumb provided a faster random
     * number generator, which speeds up the mixing function of the entropy
     * pool, taken from PGPfone.  Dale Worley has also contributed many
     * useful ideas and suggestions to improve this driver.
     *
     * Any flaws in the design are solely my responsibility, and should
     * not be attributed to the Phil, Colin, or any of the authors of PGP.
     *
     * Further background information on this topic may be obtained from
     * RFC 1750, "Randomness Recommendations for Security", by Donald
     * Eastlake, Steve Crocker, and Jeff Schiller.
     *
     * Using a RC4 stream cipher as 2nd stage after the MD5 (now SHA512) output
     * is the result of work by David Mazieres.
     */
    
    #include <sys/param.h>
    #include <sys/systm.h>
    #include <sys/disk.h>
    #include <sys/event.h>
    #include <sys/limits.h>
    #include <sys/time.h>
    #include <sys/ioctl.h>
    #include <sys/malloc.h>
    #include <sys/fcntl.h>
    #include <sys/timeout.h>
    #include <sys/mutex.h>
    #include <sys/task.h>
    #include <sys/msgbuf.h>
    #include <sys/mount.h>
    #include <sys/syscallargs.h>
    
    #include <crypto/sha2.h>
    
    #define KEYSTREAM_ONLY
    #include <crypto/chacha_private.h>
    
    #include <dev/rndvar.h>
    
    #include <uvm/uvm_param.h>
    #include <uvm/uvm_extern.h>
    
    /*
     * For the purposes of better mixing, we use the CRC-32 polynomial as
     * well to make a twisted Generalized Feedback Shift Register
     *
     * (See M. Matsumoto & Y. Kurita, 1992.  Twisted GFSR generators.  ACM
     * Transactions on Modeling and Computer Simulation 2(3):179-194.
     * Also see M. Matsumoto & Y. Kurita, 1994.  Twisted GFSR generators
     * II.  ACM Transactions on Modeling and Computer Simulation 4:254-266)
     *
     * Thanks to Colin Plumb for suggesting this.
     *
     * We have not analyzed the resultant polynomial to prove it primitive;
     * in fact it almost certainly isn't.  Nonetheless, the irreducible factors
     * of a random large-degree polynomial over GF(2) are more than large enough
     * that periodicity is not a concern.
     *
     * The input hash is much less sensitive than the output hash.  All
     * we want from it is to be a good non-cryptographic hash -
     * i.e. to not produce collisions when fed "random" data of the sort
     * we expect to see.  As long as the pool state differs for different
     * inputs, we have preserved the input entropy and done a good job.
     * The fact that an intelligent attacker can construct inputs that
     * will produce controlled alterations to the pool's state is not
     * important because we don't consider such inputs to contribute any
     * randomness.  The only property we need with respect to them is that
     * the attacker can't increase his/her knowledge of the pool's state.
     * Since all additions are reversible (knowing the final state and the
     * input, you can reconstruct the initial state), if an attacker has
     * any uncertainty about the initial state, he/she can only shuffle
     * that uncertainty about, but never cause any collisions (which would
     * decrease the uncertainty).
     *
     * The chosen system lets the state of the pool be (essentially) the input
     * modulo the generator polynomial.  Now, for random primitive polynomials,
     * this is a universal class of hash functions, meaning that the chance
     * of a collision is limited by the attacker's knowledge of the generator
     * polynomial, so if it is chosen at random, an attacker can never force
     * a collision.  Here, we use a fixed polynomial, but we *can* assume that
     * ###--> it is unknown to the processes generating the input entropy. <-###
     * Because of this important property, this is a good, collision-resistant
     * hash; hash collisions will occur no more often than chance.
     */
    
    /*
     * Stirring polynomials over GF(2) for various pool sizes. Used in
     * add_entropy_words() below.
     *
     * The polynomial terms are chosen to be evenly spaced (minimum RMS
     * distance from evenly spaced; except for the last tap, which is 1 to
     * get the twisting happening as fast as possible.
     *
     * The resultant polynomial is:
     *   2^POOLWORDS + 2^POOL_TAP1 + 2^POOL_TAP2 + 2^POOL_TAP3 + 2^POOL_TAP4 + 1
     */
    #define POOLWORDS	2048
    #define POOLBYTES	(POOLWORDS*4)
    #define POOLMASK	(POOLWORDS - 1)
    #define	POOL_TAP1	1638
    #define	POOL_TAP2	1231
    #define	POOL_TAP3	819
    #define	POOL_TAP4	411
    
    /*
     * Raw entropy collection from device drivers; at interrupt context or not.
     * enqueue_randomness() provide data which is put into the entropy queue.
     */
    
    #define QEVLEN	128		 /* must be a power of 2 */
    #define QEVSLOW (QEVLEN * 3 / 4) /* yet another 0.75 for 60-minutes hour /-; */
    
    #define KEYSZ	32
    #define IVSZ	8
    #define BLOCKSZ	64
    #define RSBUFSZ	(16*BLOCKSZ)
    #define EBUFSIZE KEYSZ + IVSZ
    
    struct rand_event {
    	u_int re_time;
    	u_int re_val;
    } rnd_event_space[QEVLEN];
    
    u_int rnd_event_cons;
    u_int rnd_event_prod;
    
    struct mutex rnd_enqlck = MUTEX_INITIALIZER(IPL_HIGH);
    struct mutex rnd_deqlck = MUTEX_INITIALIZER(IPL_HIGH);
    
    struct timeout rnd_timeout;
    
    static u_int32_t entropy_pool[POOLWORDS];
    u_int32_t entropy_pool0[POOLWORDS] __attribute__((section(".openbsd.randomdata")));
    u_int	entropy_add_ptr;
    u_char	entropy_input_rotate;
    
    void	dequeue_randomness(void *);
    void	add_entropy_words(const u_int32_t *, u_int);
    void	extract_entropy(u_int8_t *)
        __attribute__((__bounded__(__minbytes__,1,EBUFSIZE)));
    
    int	filt_randomread(struct knote *, long);
    void	filt_randomdetach(struct knote *);
    int	filt_randomwrite(struct knote *, long);
    
    static void _rs_seed(u_char *, size_t);
    static void _rs_clearseed(const void *p, size_t s);
    
    const struct filterops randomread_filtops = {
    	.f_flags	= FILTEROP_ISFD,
    	.f_attach	= NULL,
    	.f_detach	= filt_randomdetach,
    	.f_event	= filt_randomread,
    };
    
    const struct filterops randomwrite_filtops = {
    	.f_flags	= FILTEROP_ISFD,
    	.f_attach	= NULL,
    	.f_detach	= filt_randomdetach,
    	.f_event	= filt_randomwrite,
    };
    
    static inline struct rand_event *
    rnd_get(void)
    {
    	u_int idx;
    
    	/* nothing to do if queue is empty */
    	if (rnd_event_prod == rnd_event_cons)
    		return NULL;
    
    	if (rnd_event_prod - rnd_event_cons > QEVLEN)
    		rnd_event_cons = rnd_event_prod - QEVLEN;
    	idx = rnd_event_cons++;
    	return &rnd_event_space[idx & (QEVLEN - 1)];
    }
    
    static inline struct rand_event *
    rnd_put(void)
    {
    	u_int idx = rnd_event_prod++;
    
    	/* allow wrapping. caller will mix it in. */
    	return &rnd_event_space[idx & (QEVLEN - 1)];
    }
    
    static inline u_int
    rnd_qlen(void)
    {
    	return rnd_event_prod - rnd_event_cons;
    }
    
    /*
     * This function adds entropy to the entropy pool by using timing delays.
     *
     * The number "val" is also added to the pool - it should somehow describe
     * the type of event which just happened.  Currently the values of 0-255
     * are for keyboard scan codes, 256 and upwards - for interrupts.
     */
    void
    enqueue_randomness(u_int val)
    {
    	struct rand_event *rep;
    	struct timespec	ts;
    	u_int qlen;
    
    	timespecclear(&ts);
    	if (timeout_initialized(&rnd_timeout))
    		nanotime(&ts);
    
    	mtx_enter(&rnd_enqlck);
    	rep = rnd_put();
    	rep->re_time += ts.tv_nsec ^ (ts.tv_sec << 20);
    	rep->re_val += val;
    	qlen = rnd_qlen();
    	mtx_leave(&rnd_enqlck);
    
    	if (qlen > QEVSLOW/2 && timeout_initialized(&rnd_timeout) &&
    	    !timeout_pending(&rnd_timeout))
    		timeout_add(&rnd_timeout, 1);
    }
    
    /*
     * This function adds a byte into the entropy pool.  It does not
     * update the entropy estimate.  The caller must do this if appropriate.
     *
     * The pool is stirred with a polynomial of degree POOLWORDS over GF(2);
     * see POOL_TAP[1-4] above
     *
     * Rotate the input word by a changing number of bits, to help assure
     * that all bits in the entropy get toggled.  Otherwise, if the pool
     * is consistently fed small numbers (such as keyboard scan codes)
     * then the upper bits of the entropy pool will frequently remain
     * untouched.
     */
    void
    add_entropy_words(const u_int32_t *buf, u_int n)
    {
    	/* derived from IEEE 802.3 CRC-32 */
    	static const u_int32_t twist_table[8] = {
    		0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
    		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
    	};
    
    	for (; n--; buf++) {
    		u_int32_t w = (*buf << entropy_input_rotate) |
    		    (*buf >> ((32 - entropy_input_rotate) & 31));
    		u_int i = entropy_add_ptr =
    		    (entropy_add_ptr - 1) & POOLMASK;
    		/*
    		 * Normally, we add 7 bits of rotation to the pool.
    		 * At the beginning of the pool, add an extra 7 bits
    		 * rotation, so that successive passes spread the
    		 * input bits across the pool evenly.
    		 */
    		entropy_input_rotate =
    		    (entropy_input_rotate + (i ? 7 : 14)) & 31;
    
    		/* XOR pool contents corresponding to polynomial terms */
    		w ^= entropy_pool[(i + POOL_TAP1) & POOLMASK] ^
    		     entropy_pool[(i + POOL_TAP2) & POOLMASK] ^
    		     entropy_pool[(i + POOL_TAP3) & POOLMASK] ^
    		     entropy_pool[(i + POOL_TAP4) & POOLMASK] ^
    		     entropy_pool[(i + 1) & POOLMASK] ^
    		     entropy_pool[i]; /* + 2^POOLWORDS */
    
    		entropy_pool[i] = (w >> 3) ^ twist_table[w & 7];
    	}
    }
    
    /*
     * Pulls entropy out of the queue and merges it into the pool
     * with the CRC.
     */
    /* ARGSUSED */
    void
    dequeue_randomness(void *v)
    {
    	struct rand_event *rep;
    	u_int32_t buf[2];
    
    	if (timeout_initialized(&rnd_timeout))
    		timeout_del(&rnd_timeout);
    
    	mtx_enter(&rnd_deqlck);
    	while ((rep = rnd_get())) {
    		buf[0] = rep->re_time;
    		buf[1] = rep->re_val;
    		mtx_leave(&rnd_deqlck);
    		add_entropy_words(buf, 2);
    		mtx_enter(&rnd_deqlck);
    	}
    	mtx_leave(&rnd_deqlck);
    }
    
    /*
     * Grabs a chunk from the entropy_pool[] and slams it through SHA512 when
     * requested.
     */
    void
    extract_entropy(u_int8_t *buf)
    {
    	static u_int32_t extract_pool[POOLWORDS];
    	u_char digest[SHA512_DIGEST_LENGTH];
    	SHA2_CTX shactx;
    
    #if SHA512_DIGEST_LENGTH < EBUFSIZE
    #error "need more bigger hash output"
    #endif
    
    	/*
    	 * INTENTIONALLY not protected by any lock.  Races during
    	 * memcpy() result in acceptable input data; races during
    	 * SHA512Update() would create nasty data dependencies.  We
    	 * do not rely on this as a benefit, but if it happens, cool.
    	 */
    	memcpy(extract_pool, entropy_pool, sizeof(extract_pool));
    
    	/* Hash the pool to get the output */
    	SHA512Init(&shactx);
    	SHA512Update(&shactx, (u_int8_t *)extract_pool, sizeof(extract_pool));
    	SHA512Final(digest, &shactx);
    
    	/* Copy data to destination buffer */
    	memcpy(buf, digest, EBUFSIZE);
    
    	/* Modify pool so next hash will produce different results */
    	enqueue_randomness(EBUFSIZE);
    	dequeue_randomness(NULL);
    
    	/* Wipe data from memory */
    	explicit_bzero(extract_pool, sizeof(extract_pool));
    	explicit_bzero(digest, sizeof(digest));
    }
    
    /* random keystream by ChaCha */
    
    void rnd_reinit(void *v);		/* timeout to start reinit */
    void rnd_init(void *);			/* actually do the reinit */
    
    struct mutex rndlock = MUTEX_INITIALIZER(IPL_HIGH);
    struct timeout rndreinit_timeout;
    struct task rnd_task = TASK_INITIALIZER(rnd_init, NULL);
    
    static chacha_ctx rs;		/* chacha context for random keystream */
    /* keystream blocks (also chacha seed from boot) */
    static u_char rs_buf[RSBUFSZ];
    u_char rs_buf0[RSBUFSZ] __attribute__((section(".openbsd.randomdata")));
    static size_t rs_have;		/* valid bytes at end of rs_buf */
    static size_t rs_count;		/* bytes till reseed */
    
    void
    suspend_randomness(void)
    {
    	struct timespec ts;
    
    	getnanotime(&ts);
    	enqueue_randomness(ts.tv_sec);
    	enqueue_randomness(ts.tv_nsec);
    
    	dequeue_randomness(NULL);
    	rs_count = 0;
    	arc4random_buf(entropy_pool, sizeof(entropy_pool));
    }
    
    void
    resume_randomness(char *buf, size_t buflen)
    {
    	struct timespec ts;
    
    	if (buf && buflen)
    		_rs_seed(buf, buflen);
    	getnanotime(&ts);
    	enqueue_randomness(ts.tv_sec);
    	enqueue_randomness(ts.tv_nsec);
    
    	dequeue_randomness(NULL);
    	rs_count = 0;
    }
    
    static inline void _rs_rekey(u_char *dat, size_t datlen);
    
    static inline void
    _rs_init(u_char *buf, size_t n)
    {
    	KASSERT(n >= KEYSZ + IVSZ);
    	chacha_keysetup(&rs, buf, KEYSZ * 8);
    	chacha_ivsetup(&rs, buf + KEYSZ, NULL);
    }
    
    static void
    _rs_seed(u_char *buf, size_t n)
    {
    	_rs_rekey(buf, n);
    
    	/* invalidate rs_buf */
    	rs_have = 0;
    	memset(rs_buf, 0, RSBUFSZ);
    
    	rs_count = 1600000;
    }
    
    static void
    _rs_stir(int do_lock)
    {
    	struct timespec ts;
    	u_int8_t buf[EBUFSIZE], *p;
    	int i;
    
    	/*
    	 * Use SHA512 PRNG data and a system timespec; early in the boot
    	 * process this is the best we can do -- some architectures do
    	 * not collect entropy very well during this time, but may have
    	 * clock information which is better than nothing.
    	 */
    	extract_entropy(buf);
    
    	nanotime(&ts);
    	for (p = (u_int8_t *)&ts, i = 0; i < sizeof(ts); i++)
    		buf[i] ^= p[i];
    
    	if (do_lock)
    		mtx_enter(&rndlock);
    	_rs_seed(buf, sizeof(buf));
    	if (do_lock)
    		mtx_leave(&rndlock);
    
    	explicit_bzero(buf, sizeof(buf));
    }
    
    static inline void
    _rs_stir_if_needed(size_t len)
    {
    	static int rs_initialized;
    
    	if (!rs_initialized) {
    		memcpy(entropy_pool, entropy_pool0, sizeof entropy_pool);
    		memcpy(rs_buf, rs_buf0, sizeof rs_buf);
    		/* seeds cannot be cleaned yet, random_start() will do so */
    		_rs_init(rs_buf, KEYSZ + IVSZ);
    		rs_count = 1024 * 1024 * 1024;	/* until main() runs */
    		rs_initialized = 1;
    	} else if (rs_count <= len)
    		_rs_stir(0);
    	else
    		rs_count -= len;
    }
    
    static void
    _rs_clearseed(const void *p, size_t s)
    {
    	struct kmem_dyn_mode kd_avoidalias;
    	vaddr_t va = trunc_page((vaddr_t)p);
    	vsize_t off = (vaddr_t)p - va;
    	vsize_t len;
    	vaddr_t rwva;
    	paddr_t pa;
    
    	while (s > 0) {
    		pmap_extract(pmap_kernel(), va, &pa);
    
    		memset(&kd_avoidalias, 0, sizeof kd_avoidalias);
    		kd_avoidalias.kd_prefer = pa;
    		kd_avoidalias.kd_waitok = 1;
    		rwva = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any, &kp_none,
    		    &kd_avoidalias);
    		if (!rwva)
    			panic("_rs_clearseed");
    
    		pmap_kenter_pa(rwva, pa, PROT_READ | PROT_WRITE);
    		pmap_update(pmap_kernel());
    
    		len = MIN(s, PAGE_SIZE - off);
    		explicit_bzero((void *)(rwva + off), len);
    
    		pmap_kremove(rwva, PAGE_SIZE);
    		km_free((void *)rwva, PAGE_SIZE, &kv_any, &kp_none);
    
    		va += PAGE_SIZE;
    		s -= len;
    		off = 0;
    	}
    }
    
    static inline void
    _rs_rekey(u_char *dat, size_t datlen)
    {
    #ifndef KEYSTREAM_ONLY
    	memset(rs_buf, 0, RSBUFSZ);
    #endif
    	/* fill rs_buf with the keystream */
    	chacha_encrypt_bytes(&rs, rs_buf, rs_buf, RSBUFSZ);
    	/* mix in optional user provided data */
    	if (dat) {
    		size_t i, m;
    
    		m = MIN(datlen, KEYSZ + IVSZ);
    		for (i = 0; i < m; i++)
    			rs_buf[i] ^= dat[i];
    	}
    	/* immediately reinit for backtracking resistance */
    	_rs_init(rs_buf, KEYSZ + IVSZ);
    	memset(rs_buf, 0, KEYSZ + IVSZ);
    	rs_have = RSBUFSZ - KEYSZ - IVSZ;
    }
    
    static inline void
    _rs_random_buf(void *_buf, size_t n)
    {
    	u_char *buf = (u_char *)_buf;
    	size_t m;
    
    	_rs_stir_if_needed(n);
    	while (n > 0) {
    		if (rs_have > 0) {
    			m = MIN(n, rs_have);
    			memcpy(buf, rs_buf + RSBUFSZ - rs_have, m);
    			memset(rs_buf + RSBUFSZ - rs_have, 0, m);
    			buf += m;
    			n -= m;
    			rs_have -= m;
    		}
    		if (rs_have == 0)
    			_rs_rekey(NULL, 0);
    	}
    }
    
    static inline void
    _rs_random_u32(u_int32_t *val)
    {
    	_rs_stir_if_needed(sizeof(*val));
    	if (rs_have < sizeof(*val))
    		_rs_rekey(NULL, 0);
    	memcpy(val, rs_buf + RSBUFSZ - rs_have, sizeof(*val));
    	memset(rs_buf + RSBUFSZ - rs_have, 0, sizeof(*val));
    	rs_have -= sizeof(*val);
    }
    
    /* Return one word of randomness from a ChaCha20 generator */
    u_int32_t
    arc4random(void)
    {
    	u_int32_t ret;
    
    	mtx_enter(&rndlock);
    	_rs_random_u32(&ret);
    	mtx_leave(&rndlock);
    	return ret;
    }
    
    /*
     * Fill a buffer of arbitrary length with ChaCha20-derived randomness.
     */
    void
    arc4random_buf(void *buf, size_t n)
    {
    	mtx_enter(&rndlock);
    	_rs_random_buf(buf, n);
    	mtx_leave(&rndlock);
    }
    
    /*
     * Allocate a new ChaCha20 context for the caller to use.
     */
    struct arc4random_ctx *
    arc4random_ctx_new()
    {
    	char keybuf[KEYSZ + IVSZ];
    
    	chacha_ctx *ctx = malloc(sizeof(chacha_ctx), M_TEMP, M_WAITOK);
    	arc4random_buf(keybuf, KEYSZ + IVSZ);
    	chacha_keysetup(ctx, keybuf, KEYSZ * 8);
    	chacha_ivsetup(ctx, keybuf + KEYSZ, NULL);
    	explicit_bzero(keybuf, sizeof(keybuf));
    	return (struct arc4random_ctx *)ctx;
    }
    
    /*
     * Free a ChaCha20 context created by arc4random_ctx_new()
     */
    void
    arc4random_ctx_free(struct arc4random_ctx *ctx)
    {
    	explicit_bzero(ctx, sizeof(chacha_ctx));
    	free(ctx, M_TEMP, sizeof(chacha_ctx));
    }
    
    /*
     * Use a given ChaCha20 context to fill a buffer
     */
    void
    arc4random_ctx_buf(struct arc4random_ctx *ctx, void *buf, size_t n)
    {
    	chacha_encrypt_bytes((chacha_ctx *)ctx, buf, buf, n);
    }
    
    /*
     * Calculate a uniformly distributed random number less than upper_bound
     * avoiding "modulo bias".
     *
     * Uniformity is achieved by generating new random numbers until the one
     * returned is outside the range [0, 2**32 % upper_bound).  This
     * guarantees the selected random number will be inside
     * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
     * after reduction modulo upper_bound.
     */
    u_int32_t
    arc4random_uniform(u_int32_t upper_bound)
    {
    	u_int32_t r, min;
    
    	if (upper_bound < 2)
    		return 0;
    
    	/* 2**32 % x == (2**32 - x) % x */
    	min = -upper_bound % upper_bound;
    
    	/*
    	 * This could theoretically loop forever but each retry has
    	 * p > 0.5 (worst case, usually far better) of selecting a
    	 * number inside the range we need, so it should rarely need
    	 * to re-roll.
    	 */
    	for (;;) {
    		r = arc4random();
    		if (r >= min)
    			break;
    	}
    
    	return r % upper_bound;
    }
    
    /* ARGSUSED */
    void
    rnd_init(void *null)
    {
    	_rs_stir(1);
    }
    
    /*
     * Called by timeout to mark arc4 for stirring,
     */
    void
    rnd_reinit(void *v)
    {
    	task_add(systq, &rnd_task);
    	/* 10 minutes, per dm@'s suggestion */
    	timeout_add_sec(&rndreinit_timeout, 10 * 60);
    }
    
    /*
     * Start periodic services inside the random subsystem, which pull
     * entropy forward, hash it, and re-seed the random stream as needed.
     */
    void
    random_start(void)
    {
    	extern char etext[];
    
    #if !defined(NO_PROPOLICE)
    	extern long __guard_local;
    
    	if (__guard_local == 0)
    		printf("warning: no entropy supplied by boot loader\n");
    #endif
    
    	_rs_clearseed(entropy_pool0, sizeof entropy_pool0);
    	_rs_clearseed(rs_buf0, sizeof rs_buf0);
    
    	/* Message buffer may contain data from previous boot */
    	if (msgbufp->msg_magic == MSG_MAGIC)
    		add_entropy_words((u_int32_t *)msgbufp->msg_bufc,
    		    msgbufp->msg_bufs / sizeof(u_int32_t));
    	add_entropy_words((u_int32_t *)etext - 32*1024,
    	    8192/sizeof(u_int32_t));
    
    	dequeue_randomness(NULL);
    	rnd_init(NULL);
    	timeout_set(&rndreinit_timeout, rnd_reinit, NULL);
    	rnd_reinit(NULL);
    	timeout_set(&rnd_timeout, dequeue_randomness, NULL);
    }
    
    int
    randomopen(dev_t dev, int flag, int mode, struct proc *p)
    {
    	return 0;
    }
    
    int
    randomclose(dev_t dev, int flag, int mode, struct proc *p)
    {
    	return 0;
    }
    
    /*
     * Maximum number of bytes to serve directly from the main ChaCha
     * pool. Larger requests are served from a discrete ChaCha instance keyed
     * from the main pool.
     */
    #define RND_MAIN_MAX_BYTES	2048
    
    int
    randomread(dev_t dev, struct uio *uio, int ioflag)
    {
    	u_char		lbuf[KEYSZ+IVSZ];
    	chacha_ctx	lctx;
    	size_t		total = uio->uio_resid;
    	u_char		*buf;
    	int		myctx = 0, ret = 0;
    
    	if (uio->uio_resid == 0)
    		return 0;
    
    	buf = malloc(POOLBYTES, M_TEMP, M_WAITOK);
    	if (total > RND_MAIN_MAX_BYTES) {
    		arc4random_buf(lbuf, sizeof(lbuf));
    		chacha_keysetup(&lctx, lbuf, KEYSZ * 8);
    		chacha_ivsetup(&lctx, lbuf + KEYSZ, NULL);
    		explicit_bzero(lbuf, sizeof(lbuf));
    		myctx = 1;
    	}
    
    	while (ret == 0 && uio->uio_resid > 0) {
    		size_t	n = ulmin(POOLBYTES, uio->uio_resid);
    
    		if (myctx) {
    #ifndef KEYSTREAM_ONLY
    			memset(buf, 0, n);
    #endif
    			chacha_encrypt_bytes(&lctx, buf, buf, n);
    		} else
    			arc4random_buf(buf, n);
    		ret = uiomove(buf, n, uio);
    		if (ret == 0 && uio->uio_resid > 0)
    			yield();
    	}
    	if (myctx)
    		explicit_bzero(&lctx, sizeof(lctx));
    	explicit_bzero(buf, POOLBYTES);
    	free(buf, M_TEMP, POOLBYTES);
    	return ret;
    }
    
    int
    randomwrite(dev_t dev, struct uio *uio, int flags)
    {
    	int		ret = 0, newdata = 0;
    	u_int32_t	*buf;
    
    	if (uio->uio_resid == 0)
    		return 0;
    
    	buf = malloc(POOLBYTES, M_TEMP, M_WAITOK);
    
    	while (ret == 0 && uio->uio_resid > 0) {
    		size_t	n = ulmin(POOLBYTES, uio->uio_resid);
    
    		ret = uiomove(buf, n, uio);
    		if (ret != 0)
    			break;
    		while (n % sizeof(u_int32_t))
    			((u_int8_t *)buf)[n++] = 0;
    		add_entropy_words(buf, n / 4);
    		if (uio->uio_resid > 0)
    			yield();
    		newdata = 1;
    	}
    
    	if (newdata)
    		rnd_init(NULL);
    
    	explicit_bzero(buf, POOLBYTES);
    	free(buf, M_TEMP, POOLBYTES);
    	return ret;
    }
    
    int
    randomkqfilter(dev_t dev, struct knote *kn)
    {
    	switch (kn->kn_filter) {
    	case EVFILT_READ:
    		kn->kn_fop = &randomread_filtops;
    		break;
    	case EVFILT_WRITE:
    		kn->kn_fop = &randomwrite_filtops;
    		break;
    	default:
    		return (EINVAL);
    	}
    
    	return (0);
    }
    
    void
    filt_randomdetach(struct knote *kn)
    {
    }
    
    int
    filt_randomread(struct knote *kn, long hint)
    {
    	kn->kn_data = RND_MAIN_MAX_BYTES;
    	return (1);
    }
    
    int
    filt_randomwrite(struct knote *kn, long hint)
    {
    	kn->kn_data = POOLBYTES;
    	return (1);
    }
    
    int
    randomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
    {
    	switch (cmd) {
    	case FIOASYNC:
    		/* No async flag in softc so this is a no-op. */
    		break;
    	case FIONBIO:
    		/* Handled in the upper FS layer. */
    		break;
    	default:
    		return ENOTTY;
    	}
    	return 0;
    }
    
    int
    sys_getentropy(struct proc *p, void *v, register_t *retval)
    {
    	struct sys_getentropy_args /* {
    		syscallarg(void *) buf;
    		syscallarg(size_t) nbyte;
    	} */ *uap = v;
    	char buf[256];
    	int error;
    
    	if (SCARG(uap, nbyte) > sizeof(buf))
    		return (EIO);
    	arc4random_buf(buf, SCARG(uap, nbyte));
    	if ((error = copyout(buf, SCARG(uap, buf), SCARG(uap, nbyte))) != 0)
    		return (error);
    	explicit_bzero(buf, sizeof(buf));
    	retval[0] = 0;
    	return (0);
    }