Home | History | Annotate | Line # | Download | only in kern
subr_cprng.c revision 1.30
      1 /*	$NetBSD: subr_cprng.c,v 1.30 2019/07/10 17:32:37 maxv Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2011-2013 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Thor Lancelot Simon and Taylor R. Campbell.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: subr_cprng.c,v 1.30 2019/07/10 17:32:37 maxv Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/types.h>
     37 #include <sys/condvar.h>
     38 #include <sys/cprng.h>
     39 #include <sys/errno.h>
     40 #include <sys/event.h>		/* XXX struct knote */
     41 #include <sys/fcntl.h>		/* XXX FNONBLOCK */
     42 #include <sys/kernel.h>
     43 #include <sys/kmem.h>
     44 #include <sys/lwp.h>
     45 #include <sys/once.h>
     46 #include <sys/percpu.h>
     47 #include <sys/poll.h>		/* XXX POLLIN/POLLOUT/&c. */
     48 #include <sys/select.h>
     49 #include <sys/systm.h>
     50 #include <sys/sysctl.h>
     51 #include <sys/rndsink.h>
     52 #if DIAGNOSTIC
     53 #include <sys/rngtest.h>
     54 #endif
     55 
     56 #include <crypto/nist_ctr_drbg/nist_ctr_drbg.h>
     57 
     58 #if defined(__HAVE_CPU_COUNTER)
     59 #include <machine/cpu_counter.h>
     60 #endif
     61 
     62 static int sysctl_kern_urnd(SYSCTLFN_PROTO);
     63 static int sysctl_kern_arnd(SYSCTLFN_PROTO);
     64 
     65 static void	cprng_strong_generate(struct cprng_strong *, void *, size_t);
     66 static void	cprng_strong_reseed(struct cprng_strong *);
     67 static void	cprng_strong_reseed_from(struct cprng_strong *, const void *,
     68 		    size_t, bool);
     69 #if DIAGNOSTIC
     70 static void	cprng_strong_rngtest(struct cprng_strong *);
     71 #endif
     72 
     73 static rndsink_callback_t	cprng_strong_rndsink_callback;
     74 
     75 void
     76 cprng_init(void)
     77 {
     78 	static struct sysctllog *random_sysctllog;
     79 
     80 	nist_ctr_initialize();
     81 
     82 	sysctl_createv(&random_sysctllog, 0, NULL, NULL,
     83 		       CTLFLAG_PERMANENT,
     84 		       CTLTYPE_INT, "urandom",
     85 		       SYSCTL_DESCR("Random integer value"),
     86 		       sysctl_kern_urnd, 0, NULL, 0,
     87 		       CTL_KERN, KERN_URND, CTL_EOL);
     88 	sysctl_createv(&random_sysctllog, 0, NULL, NULL,
     89 		       CTLFLAG_PERMANENT,
     90 		       CTLTYPE_INT, "arandom",
     91 		       SYSCTL_DESCR("n bytes of random data"),
     92 		       sysctl_kern_arnd, 0, NULL, 0,
     93 		       CTL_KERN, KERN_ARND, CTL_EOL);
     94 }
     95 
     96 static inline uint32_t
     97 cprng_counter(void)
     98 {
     99 	struct timeval tv;
    100 
    101 #if defined(__HAVE_CPU_COUNTER)
    102 	if (cpu_hascounter())
    103 		return cpu_counter32();
    104 #endif
    105 	if (__predict_false(cold)) {
    106 		static int ctr;
    107 		/* microtime unsafe if clock not running yet */
    108 		return ctr++;
    109 	}
    110 	getmicrotime(&tv);
    111 	return (tv.tv_sec * 1000000 + tv.tv_usec);
    112 }
    113 
    114 struct cprng_strong {
    115 	char		cs_name[16];
    116 	int		cs_flags;
    117 	kmutex_t	cs_lock;
    118 	percpu_t	*cs_percpu;
    119 	kcondvar_t	cs_cv;
    120 	struct selinfo	cs_selq;
    121 	struct rndsink	*cs_rndsink;
    122 	bool		cs_ready;
    123 	NIST_CTR_DRBG	cs_drbg;
    124 
    125 	/* XXX Kludge for /dev/random `information-theoretic' properties.   */
    126 	unsigned int	cs_remaining;
    127 };
    128 
    129 struct cprng_strong *
    130 cprng_strong_create(const char *name, int ipl, int flags)
    131 {
    132 	const uint32_t cc = cprng_counter();
    133 	struct cprng_strong *const cprng = kmem_alloc(sizeof(*cprng),
    134 	    KM_SLEEP);
    135 
    136 	/*
    137 	 * rndsink_request takes a spin lock at IPL_VM, so we can be no
    138 	 * higher than that.
    139 	 */
    140 	KASSERT(ipl != IPL_SCHED && ipl != IPL_HIGH);
    141 
    142 	/* Initialize the easy fields.  */
    143 	memset(cprng->cs_name, 0, sizeof(cprng->cs_name));
    144 	(void)strlcpy(cprng->cs_name, name, sizeof(cprng->cs_name));
    145 	cprng->cs_flags = flags;
    146 	mutex_init(&cprng->cs_lock, MUTEX_DEFAULT, ipl);
    147 	cv_init(&cprng->cs_cv, cprng->cs_name);
    148 	selinit(&cprng->cs_selq);
    149 	cprng->cs_rndsink = rndsink_create(NIST_BLOCK_KEYLEN_BYTES,
    150 	    &cprng_strong_rndsink_callback, cprng);
    151 
    152 	/* Get some initial entropy.  Record whether it is full entropy.  */
    153 	uint8_t seed[NIST_BLOCK_KEYLEN_BYTES];
    154 	mutex_enter(&cprng->cs_lock);
    155 	cprng->cs_ready = rndsink_request(cprng->cs_rndsink, seed,
    156 	    sizeof(seed));
    157 	if (nist_ctr_drbg_instantiate(&cprng->cs_drbg, seed, sizeof(seed),
    158 		&cc, sizeof(cc), cprng->cs_name, sizeof(cprng->cs_name)))
    159 		/* XXX Fix nist_ctr_drbg API so this can't happen.  */
    160 		panic("cprng %s: NIST CTR_DRBG instantiation failed",
    161 		    cprng->cs_name);
    162 	explicit_memset(seed, 0, sizeof(seed));
    163 
    164 	if (ISSET(flags, CPRNG_HARD))
    165 		cprng->cs_remaining = NIST_BLOCK_KEYLEN_BYTES;
    166 	else
    167 		cprng->cs_remaining = 0;
    168 
    169 	if (!cprng->cs_ready && !ISSET(flags, CPRNG_INIT_ANY))
    170 		printf("cprng %s: creating with partial entropy\n",
    171 		    cprng->cs_name);
    172 	mutex_exit(&cprng->cs_lock);
    173 
    174 	return cprng;
    175 }
    176 
    177 void
    178 cprng_strong_destroy(struct cprng_strong *cprng)
    179 {
    180 
    181 	/*
    182 	 * Destroy the rndsink first to prevent calls to the callback.
    183 	 */
    184 	rndsink_destroy(cprng->cs_rndsink);
    185 
    186 	KASSERT(!cv_has_waiters(&cprng->cs_cv));
    187 #if 0
    188 	KASSERT(!select_has_waiters(&cprng->cs_selq)) /* XXX ? */
    189 #endif
    190 
    191 	nist_ctr_drbg_destroy(&cprng->cs_drbg);
    192 	seldestroy(&cprng->cs_selq);
    193 	cv_destroy(&cprng->cs_cv);
    194 	mutex_destroy(&cprng->cs_lock);
    195 
    196 	explicit_memset(cprng, 0, sizeof(*cprng)); /* paranoia */
    197 	kmem_free(cprng, sizeof(*cprng));
    198 }
    199 
    200 /*
    201  * Generate some data from cprng.  Block or return zero bytes,
    202  * depending on flags & FNONBLOCK, if cprng was created without
    203  * CPRNG_REKEY_ANY.
    204  */
    205 size_t
    206 cprng_strong(struct cprng_strong *cprng, void *buffer, size_t bytes, int flags)
    207 {
    208 	size_t result;
    209 
    210 	/* Caller must loop for more than CPRNG_MAX_LEN bytes.  */
    211 	bytes = MIN(bytes, CPRNG_MAX_LEN);
    212 
    213 	mutex_enter(&cprng->cs_lock);
    214 
    215 	if (ISSET(cprng->cs_flags, CPRNG_REKEY_ANY)) {
    216 		if (!cprng->cs_ready)
    217 			cprng_strong_reseed(cprng);
    218 	} else {
    219 		while (!cprng->cs_ready) {
    220 			if (ISSET(flags, FNONBLOCK) ||
    221 			    !ISSET(cprng->cs_flags, CPRNG_USE_CV) ||
    222 			    cv_wait_sig(&cprng->cs_cv, &cprng->cs_lock)) {
    223 				result = 0;
    224 				goto out;
    225 			}
    226 		}
    227 	}
    228 
    229 	/*
    230 	 * Debit the entropy if requested.
    231 	 *
    232 	 * XXX Kludge for /dev/random `information-theoretic' properties.
    233 	 */
    234 	if (__predict_false(ISSET(cprng->cs_flags, CPRNG_HARD))) {
    235 		KASSERT(0 < cprng->cs_remaining);
    236 		KASSERT(cprng->cs_remaining <= NIST_BLOCK_KEYLEN_BYTES);
    237 		if (bytes < cprng->cs_remaining) {
    238 			cprng->cs_remaining -= bytes;
    239 		} else {
    240 			bytes = cprng->cs_remaining;
    241 			cprng->cs_remaining = NIST_BLOCK_KEYLEN_BYTES;
    242 			cprng->cs_ready = false;
    243 			rndsink_schedule(cprng->cs_rndsink);
    244 		}
    245 		KASSERT(bytes <= NIST_BLOCK_KEYLEN_BYTES);
    246 		KASSERT(0 < cprng->cs_remaining);
    247 		KASSERT(cprng->cs_remaining <= NIST_BLOCK_KEYLEN_BYTES);
    248 	}
    249 
    250 	cprng_strong_generate(cprng, buffer, bytes);
    251 	result = bytes;
    252 
    253 out:	mutex_exit(&cprng->cs_lock);
    254 	return result;
    255 }
    256 
    257 static void
    258 filt_cprng_detach(struct knote *kn)
    259 {
    260 	struct cprng_strong *const cprng = kn->kn_hook;
    261 
    262 	mutex_enter(&cprng->cs_lock);
    263 	SLIST_REMOVE(&cprng->cs_selq.sel_klist, kn, knote, kn_selnext);
    264 	mutex_exit(&cprng->cs_lock);
    265 }
    266 
    267 static int
    268 filt_cprng_read_event(struct knote *kn, long hint)
    269 {
    270 	struct cprng_strong *const cprng = kn->kn_hook;
    271 	int ret;
    272 
    273 	if (hint == NOTE_SUBMIT)
    274 		KASSERT(mutex_owned(&cprng->cs_lock));
    275 	else
    276 		mutex_enter(&cprng->cs_lock);
    277 	if (cprng->cs_ready) {
    278 		kn->kn_data = CPRNG_MAX_LEN; /* XXX Too large?  */
    279 		ret = 1;
    280 	} else {
    281 		ret = 0;
    282 	}
    283 	if (hint == NOTE_SUBMIT)
    284 		KASSERT(mutex_owned(&cprng->cs_lock));
    285 	else
    286 		mutex_exit(&cprng->cs_lock);
    287 
    288 	return ret;
    289 }
    290 
    291 static int
    292 filt_cprng_write_event(struct knote *kn, long hint)
    293 {
    294 	struct cprng_strong *const cprng = kn->kn_hook;
    295 
    296 	if (hint == NOTE_SUBMIT)
    297 		KASSERT(mutex_owned(&cprng->cs_lock));
    298 	else
    299 		mutex_enter(&cprng->cs_lock);
    300 
    301 	kn->kn_data = 0;
    302 
    303 	if (hint == NOTE_SUBMIT)
    304 		KASSERT(mutex_owned(&cprng->cs_lock));
    305 	else
    306 		mutex_exit(&cprng->cs_lock);
    307 
    308 	return 0;
    309 }
    310 
    311 static const struct filterops cprng_read_filtops = {
    312 	.f_isfd = 1,
    313 	.f_attach = NULL,
    314 	.f_detach = filt_cprng_detach,
    315 	.f_event = filt_cprng_read_event,
    316 };
    317 
    318 static const struct filterops cprng_write_filtops = {
    319 	.f_isfd = 1,
    320 	.f_attach = NULL,
    321 	.f_detach = filt_cprng_detach,
    322 	.f_event = filt_cprng_write_event,
    323 };
    324 
    325 int
    326 cprng_strong_kqfilter(struct cprng_strong *cprng, struct knote *kn)
    327 {
    328 
    329 	switch (kn->kn_filter) {
    330 	case EVFILT_READ:
    331 		kn->kn_fop = &cprng_read_filtops;
    332 		break;
    333 	case EVFILT_WRITE:
    334 		kn->kn_fop = &cprng_write_filtops;
    335 		break;
    336 	default:
    337 		return EINVAL;
    338 	}
    339 
    340 	kn->kn_hook = cprng;
    341 	mutex_enter(&cprng->cs_lock);
    342 	SLIST_INSERT_HEAD(&cprng->cs_selq.sel_klist, kn, kn_selnext);
    343 	mutex_exit(&cprng->cs_lock);
    344 	return 0;
    345 }
    346 
    347 int
    348 cprng_strong_poll(struct cprng_strong *cprng, int events)
    349 {
    350 	int revents;
    351 
    352 	if (!ISSET(events, (POLLIN | POLLRDNORM)))
    353 		return 0;
    354 
    355 	mutex_enter(&cprng->cs_lock);
    356 	if (cprng->cs_ready) {
    357 		revents = (events & (POLLIN | POLLRDNORM));
    358 	} else {
    359 		selrecord(curlwp, &cprng->cs_selq);
    360 		revents = 0;
    361 	}
    362 	mutex_exit(&cprng->cs_lock);
    363 
    364 	return revents;
    365 }
    366 
    367 /*
    368  * XXX Move nist_ctr_drbg_reseed_advised_p and
    369  * nist_ctr_drbg_reseed_needed_p into the nist_ctr_drbg API and make
    370  * the NIST_CTR_DRBG structure opaque.
    371  */
    372 static bool
    373 nist_ctr_drbg_reseed_advised_p(NIST_CTR_DRBG *drbg)
    374 {
    375 
    376 	return (drbg->reseed_counter > (NIST_CTR_DRBG_RESEED_INTERVAL / 2));
    377 }
    378 
    379 static bool
    380 nist_ctr_drbg_reseed_needed_p(NIST_CTR_DRBG *drbg)
    381 {
    382 
    383 	return (drbg->reseed_counter >= NIST_CTR_DRBG_RESEED_INTERVAL);
    384 }
    385 
    386 /*
    387  * Generate some data from the underlying generator.
    388  */
    389 static void
    390 cprng_strong_generate(struct cprng_strong *cprng, void *buffer, size_t bytes)
    391 {
    392 	const uint32_t cc = cprng_counter();
    393 
    394 	KASSERT(bytes <= CPRNG_MAX_LEN);
    395 	KASSERT(mutex_owned(&cprng->cs_lock));
    396 
    397 	/*
    398 	 * Generate some data from the NIST CTR_DRBG.  Caller
    399 	 * guarantees reseed if we're not ready, and if we exhaust the
    400 	 * generator, we mark ourselves not ready.  Consequently, this
    401 	 * call to the CTR_DRBG should not fail.
    402 	 */
    403 	if (__predict_false(nist_ctr_drbg_generate(&cprng->cs_drbg, buffer,
    404 		    bytes, &cc, sizeof(cc))))
    405 		panic("cprng %s: NIST CTR_DRBG failed", cprng->cs_name);
    406 
    407 	/*
    408 	 * If we've been seeing a lot of use, ask for some fresh
    409 	 * entropy soon.
    410 	 */
    411 	if (__predict_false(nist_ctr_drbg_reseed_advised_p(&cprng->cs_drbg)))
    412 		rndsink_schedule(cprng->cs_rndsink);
    413 
    414 	/*
    415 	 * If we just exhausted the generator, inform the next user
    416 	 * that we need a reseed.
    417 	 */
    418 	if (__predict_false(nist_ctr_drbg_reseed_needed_p(&cprng->cs_drbg))) {
    419 		cprng->cs_ready = false;
    420 		rndsink_schedule(cprng->cs_rndsink); /* paranoia */
    421 	}
    422 }
    423 
    424 /*
    425  * Reseed with whatever we can get from the system entropy pool right now.
    426  */
    427 static void
    428 cprng_strong_reseed(struct cprng_strong *cprng)
    429 {
    430 	uint8_t seed[NIST_BLOCK_KEYLEN_BYTES];
    431 
    432 	KASSERT(mutex_owned(&cprng->cs_lock));
    433 
    434 	const bool full_entropy = rndsink_request(cprng->cs_rndsink, seed,
    435 	    sizeof(seed));
    436 	cprng_strong_reseed_from(cprng, seed, sizeof(seed), full_entropy);
    437 	explicit_memset(seed, 0, sizeof(seed));
    438 }
    439 
    440 /*
    441  * Reseed with the given seed.  If we now have full entropy, notify waiters.
    442  */
    443 static void
    444 cprng_strong_reseed_from(struct cprng_strong *cprng,
    445     const void *seed, size_t bytes, bool full_entropy)
    446 {
    447 	const uint32_t cc = cprng_counter();
    448 
    449 	KASSERT(bytes == NIST_BLOCK_KEYLEN_BYTES);
    450 	KASSERT(mutex_owned(&cprng->cs_lock));
    451 
    452 	/*
    453 	 * Notify anyone interested in the partiality of entropy in our
    454 	 * seed -- anyone waiting for full entropy, or any system
    455 	 * operators interested in knowing when the entropy pool is
    456 	 * running on fumes.
    457 	 */
    458 	if (full_entropy) {
    459 		if (!cprng->cs_ready) {
    460 			cprng->cs_ready = true;
    461 			cv_broadcast(&cprng->cs_cv);
    462 			selnotify(&cprng->cs_selq, (POLLIN | POLLRDNORM),
    463 			    NOTE_SUBMIT);
    464 		}
    465 	} else {
    466 		/*
    467 		 * XXX Is there is any harm in reseeding with partial
    468 		 * entropy when we had full entropy before?  If so,
    469 		 * remove the conditional on this message.
    470 		 */
    471 		if (!cprng->cs_ready &&
    472 		    !ISSET(cprng->cs_flags, CPRNG_REKEY_ANY))
    473 			printf("cprng %s: reseeding with partial entropy\n",
    474 			    cprng->cs_name);
    475 	}
    476 
    477 	if (nist_ctr_drbg_reseed(&cprng->cs_drbg, seed, bytes, &cc, sizeof(cc)))
    478 		/* XXX Fix nist_ctr_drbg API so this can't happen.  */
    479 		panic("cprng %s: NIST CTR_DRBG reseed failed", cprng->cs_name);
    480 
    481 #if DIAGNOSTIC
    482 	cprng_strong_rngtest(cprng);
    483 #endif
    484 }
    485 
    486 #if DIAGNOSTIC
    487 /*
    488  * Generate some output and apply a statistical RNG test to it.
    489  */
    490 static void
    491 cprng_strong_rngtest(struct cprng_strong *cprng)
    492 {
    493 
    494 	KASSERT(mutex_owned(&cprng->cs_lock));
    495 
    496 	/* XXX Switch to a pool cache instead?  */
    497 	rngtest_t *const rt = kmem_intr_alloc(sizeof(*rt), KM_NOSLEEP);
    498 	if (rt == NULL)
    499 		/* XXX Warn?  */
    500 		return;
    501 
    502 	(void)strlcpy(rt->rt_name, cprng->cs_name, sizeof(rt->rt_name));
    503 
    504 	if (nist_ctr_drbg_generate(&cprng->cs_drbg, rt->rt_b, sizeof(rt->rt_b),
    505 		NULL, 0))
    506 		panic("cprng %s: NIST CTR_DRBG failed after reseed",
    507 		    cprng->cs_name);
    508 
    509 	if (rngtest(rt)) {
    510 		printf("cprng %s: failed statistical RNG test\n",
    511 		    cprng->cs_name);
    512 		/* XXX Not clear that this does any good...  */
    513 		cprng->cs_ready = false;
    514 		rndsink_schedule(cprng->cs_rndsink);
    515 	}
    516 
    517 	explicit_memset(rt, 0, sizeof(*rt)); /* paranoia */
    518 	kmem_intr_free(rt, sizeof(*rt));
    519 }
    520 #endif
    521 
    522 /*
    523  * Feed entropy from an rndsink request into the CPRNG for which the
    524  * request was issued.
    525  */
    526 static void
    527 cprng_strong_rndsink_callback(void *context, const void *seed, size_t bytes)
    528 {
    529 	struct cprng_strong *const cprng = context;
    530 
    531 	mutex_enter(&cprng->cs_lock);
    532 	/* Assume that rndsinks provide only full-entropy output.  */
    533 	cprng_strong_reseed_from(cprng, seed, bytes, true);
    534 	mutex_exit(&cprng->cs_lock);
    535 }
    536 
    537 static cprng_strong_t *sysctl_prng;
    538 
    539 static int
    540 makeprng(void)
    541 {
    542 
    543 	/* can't create in cprng_init(), too early */
    544 	sysctl_prng = cprng_strong_create("sysctl", IPL_NONE,
    545 					  CPRNG_INIT_ANY|CPRNG_REKEY_ANY);
    546 	return 0;
    547 }
    548 
    549 /*
    550  * sysctl helper routine for kern.urandom node. Picks a random number
    551  * for you.
    552  */
    553 static int
    554 sysctl_kern_urnd(SYSCTLFN_ARGS)
    555 {
    556 	static ONCE_DECL(control);
    557 	int v, rv;
    558 
    559 	RUN_ONCE(&control, makeprng);
    560 	rv = cprng_strong(sysctl_prng, &v, sizeof(v), 0);
    561 	if (rv == sizeof(v)) {
    562 		struct sysctlnode node = *rnode;
    563 		node.sysctl_data = &v;
    564 		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
    565 	}
    566 	else
    567 		return (EIO);	/*XXX*/
    568 }
    569 
    570 /*
    571  * sysctl helper routine for kern.arandom node.  Fills the supplied
    572  * structure with random data for you.
    573  *
    574  * This node was originally declared as type "int" but its implementation
    575  * in OpenBSD, whence it came, would happily return up to 8K of data if
    576  * requested.  Evidently this was used to key RC4 in userspace.
    577  *
    578  * In NetBSD, the libc stack-smash-protection code reads 64 bytes
    579  * from here at every program startup.  So though it would be nice
    580  * to make this node return only 32 or 64 bits, we can't.  Too bad!
    581  */
    582 static int
    583 sysctl_kern_arnd(SYSCTLFN_ARGS)
    584 {
    585 	int error;
    586 	void *v;
    587 	struct sysctlnode node = *rnode;
    588 
    589 	switch (*oldlenp) {
    590 	    case 0:
    591 		return 0;
    592 	    default:
    593 		if (*oldlenp > 256) {
    594 			return E2BIG;
    595 		}
    596 		v = kmem_alloc(*oldlenp, KM_SLEEP);
    597 		cprng_fast(v, *oldlenp);
    598 		node.sysctl_data = v;
    599 		node.sysctl_size = *oldlenp;
    600 		error = sysctl_lookup(SYSCTLFN_CALL(&node));
    601 		kmem_free(v, *oldlenp);
    602 		return error;
    603 	}
    604 }
    605