/* $MirOS: src/sys/crypto/arc4random.c,v 1.8 2014/03/13 06:09:48 tg Exp $ */ /*- * Copyright © 2010, 2014 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un‐ * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person’s immediate fault when using the work as intended. *- * Always working arc4random(9) implementation; the arcfour state has * been initialised at kernel compile time by newvers.sh for us. Once * the random device has attached, arc4random_reinit() will be called * from a timeout, or when enough bytes are consumed, and restir with * entropy from both the rndpool and the lopool. */ #include #include #include #include #include /* import from random.c */ extern struct timeout arc4random_timeout; extern struct arcfour_status lopool_collapse; /* import from vers.c generated by conf/newvers.sh */ extern struct arcfour_status initial_arc4random; size_t arc4random_count = 0; /* * 8 MiB seems like a sane limit to protect against reading * enough stream cipher output to try and attack the key */ #ifndef ARC4RANDOM_MAXBYTES #define ARC4RANDOM_MAXBYTES (8 * 1048576) #endif u_int32_t arc4random(void) { uint32_t v; int s; s = splhigh(); rndstats.arc4_reads += 4; if (arc4random_count >= ARC4RANDOM_MAXBYTES - 4) arc4random_reinit(NULL); arc4random_count += 4; /* skip an output byte randomly for whitening */ if ((arcfour_byte(&lopool_collapse) & 1)) (void)arcfour_byte(&initial_arc4random); v = ((uint32_t)arcfour_byte(&initial_arc4random) << 24) | ((uint32_t)arcfour_byte(&initial_arc4random) << 16) | ((uint32_t)arcfour_byte(&initial_arc4random) << 8) | ((uint32_t)arcfour_byte(&initial_arc4random)); splx(s); return (v); } void arc4random_buf(void *buf_, size_t len) { size_t n; uint8_t *buf = (uint8_t *)buf_; int s; if (!len) return; s = splhigh(); rndstats.arc4_reads += len; goto into_the_loop; /* operate in chunks of 256 output bytes to give interrupts a chance */ while (len) { s = splhigh(); into_the_loop: /* skip a few output bytes randomly for whitening */ n = arcfour_byte(&lopool_collapse) & 3; while (n--) (void)arcfour_byte(&initial_arc4random); n = min(len, 256); len -= n; /* * using 256 here, not n, to speed up; the difference * is not worth mentioning, and it’s also more secure */ if (arc4random_count >= (ARC4RANDOM_MAXBYTES - 256)) arc4random_reinit(NULL); arc4random_count += n; while (n--) *buf++ = arcfour_byte(&initial_arc4random); splx(s); } } /* * Stir the arcfour state used by arc4random(9). This function is * called via a periodic timeout (approximately every 8½ minutes) * as well as from a consumer when approx. 8 MiB were used up. */ void arc4random_reinit(void *arg __unused) { size_t n; struct { uint8_t chars[192]; struct timeval tv; } buf; int s; s = splhigh(); if (!rnd_attached) { RNDEBUG(RD_ALWAYS, "rnd: reinit before attach\n"); goto out; } /* if we were called directly instead of from timeout */ timeout_del(&arc4random_timeout); /* half the poolbits, minimum 8, maximum 128 bytes */ n = max(8, min(128, (random_state.entropy_count / 16))); rndstats.arc4_stirs += n; RNDEBUG(RD_ARC4RANDOM, "rnd: reinit at %lu with %lu bytes\n", (u_long)arc4random_count, (u_long)n); get_random_bytes(buf.chars, n); while (n < 192) buf.chars[n++] = arcfour_byte(&lopool_collapse); buf.tv.tv_sec = mono_time.tv_sec; buf.tv.tv_usec = mono_time.tv_usec; /* carry over */ n = arcfour_byte(&initial_arc4random) & 7; /* re-key */ arcfour_ksa(&initial_arc4random, (void *)&buf, sizeof(buf)); arc4random_count = 0; ++rndstats.arc4_nstirs; /* * skip early keystream for security, * plus a random amount for whitening */ n += 256 * 12 + (arcfour_byte(&lopool_collapse) & 7) + (arcfour_byte(&initial_arc4random) & 7); while (n--) (void)arcfour_byte(&initial_arc4random); /* contribute our (lopool,rndpool,arc4pool) result */ n = ((uint32_t)arcfour_byte(&initial_arc4random) << 24) | ((uint32_t)arcfour_byte(&initial_arc4random) << 16) | ((uint32_t)arcfour_byte(&initial_arc4random) << 8) | ((uint32_t)arcfour_byte(&initial_arc4random)); enqueue_randomness(RND_SRC_LPC, (int)n); n = (arcfour_byte(&initial_arc4random) & 3) + (arcfour_byte(&lopool_collapse) & 3); while (n--) (void)arcfour_byte(&initial_arc4random); /* zero out temp. key buffer */ bzero(&buf, sizeof(buf)); /* call us again in approx. 8½ minutes, minus [0‥32[ seconds */ timeout_add(&arc4random_timeout, (hz << 9) - arc4random_uniform(hz << 5)); out: splx(s); }