/* $MirOS: src/sys/crypto/randcore.c,v 1.12 2014/02/20 00:57:23 tg Exp $ */ /*- * Copyright © 2010, 2011, 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. *- * This file contains the core random device implementation and stee‐ * ring point for the random subsystem in MirBSD. It also contains an * implementation of the “lopool”, i.e. a hashed pool where userspace * non-root processes and the kernel can send “untrusted” entropy to, * which then is periodically “collapsed” into an arcfour state named * lopool_collapse which in turn is used by arc4random_reinit when it * stirs the main arcfour state used in arc4random(9). */ #define IN_RANDOM_CORE #include #include #include #include #include #include #include /* import from vers.c generated by conf/newvers.sh */ extern uint32_t _randseed; /* only bytes actually changed by config(8) */ extern unsigned char initial_entropy[16]; struct rnd_pooladd { union { struct timeval tv; #if defined(I586_CPU) || defined(I686_CPU) unsigned long long tsc; #endif } u; const void *sp, *dp; size_t sz; }; void rnd_lopool_dequeue(void *); /* * This variable secures the following things: rndpool (tytso code) is * operational; timeouts for arc4random and lopool-collapse can be run; * lopool-collapse arcfour state has been initialised */ int rnd_attached = 0; size_t lopool_content = 0; uint32_t lopool_uhash[32]; uint8_t lopool_uptr = 0; struct arcfour_status lopool_collapse; struct timeout arc4random_timeout; struct timeout rnd_lopool_timeout; /* * Backend routine for adding some bytes, together with pointer, * size, stack, and timing information, into the hash lopool. * We use the BAFH transformation only for folding the user- * specified information into 32 BAFH states (128 bytes) * and count the bytes we added. (rnd_lopool_addh adds an BAFH * hash of the data, not the data itself.) Once the content is * 128 bytes or more, rnd_lopool_dequeue collapses them into * lopool_collapse where it will be picked up by the next run * of the arc4random(9) stir function. */ static void rnd_pool_add(struct rnd_pooladd *sa, const void *d, size_t n) { int s; #if defined(I586_CPU) || defined(I686_CPU) if (pentium_mhz) { __asm __volatile("rdtsc" : "=A" (sa->u.tsc)); } else #endif { /* cannot use memcpy since mono_time is volatile */ sa->u.tv.tv_sec = mono_time.tv_sec; sa->u.tv.tv_usec = mono_time.tv_usec; } sa->sp = sa; RNDEBUG(RD_HASHLOPOOL, "rnd: lopool += %lu(%lu) bytes\n", (u_long)n, (u_long)sa->sz); s = splhigh(); arc4random_roundhash(lopool_uhash, &lopool_uptr, sa, sizeof(*sa)); arc4random_roundhash(lopool_uhash, &lopool_uptr, d, n); lopool_content += n; rndstats.lopool_bytes += n; ++rndstats.lopool_enq; splx(s); } /* * called from most of the initialisation code; historical because * we used to be unable to run either lopool’s predecessor or * arc4random itself early in the boot process; nowadays, this is * merely a “big data” function, e.g. for the pre-start log message * buffer, which may have leftovers from the last kernel, or some * filesystem superblock or other data of few entropic value */ void rnd_lopool_addh(const void *vp, size_t n) { struct rnd_pooladd pa; uint32_t h; h = arc4random(); BAFHUpdateMem_reg(h, vp, n); BAFHFinish_reg(h); pa.dp = vp; pa.sz = n; rnd_pool_add(&pa, &h, sizeof(h)); } /* these two directly add their argument into the lopool */ /* pointer and length, read data */ void rnd_lopool_add(const void *buf, size_t len) { struct rnd_pooladd pa; pa.dp = buf; pa.sz = len; rnd_pool_add(&pa, buf, len); } /* integral argument, passed on the stack */ void rnd_lopool_addv(unsigned long v) { struct rnd_pooladd pa; pa.sz = 0; rnd_pool_add(&pa, &v, sizeof(v)); } #ifndef rnd_lopool_addvq /* for use by the hardclockent routines: no RNDEBUG, no counting */ void rnd_lopool_addvq(unsigned long v) { int s; s = splhigh(); arc4random_roundhash(lopool_uhash, &lopool_uptr, &v, sizeof(v)); splx(s); } #endif /* * This is the function called all 1‥3 seconds to collapse the * lopool’s hash into its arcfour state if there was enough data. */ void rnd_lopool_dequeue(void *arg __unused) { int s; struct timeval tv[2]; s = splhigh(); if (!rnd_attached) { RNDEBUG(RD_ALWAYS, "rnd: lopool dequeue before attach\n"); goto out; } /* if we were called directly instead of from timeout */ timeout_del(&rnd_lopool_timeout); if (lopool_content >= 128) { RNDEBUG(RD_ARC4LOPOOL, "rnd: lopool dequeues %lu bytes\n", (u_long)lopool_content); /* add current time (mono and TAI) first */ tv[0].tv_sec = mono_time.tv_sec; tv[0].tv_usec = mono_time.tv_usec; tv[1].tv_sec = time.tv_sec; tv[1].tv_usec = time.tv_usec; arc4random_roundhash(lopool_uhash, &lopool_uptr, tv, sizeof(tv)); /* finalise all 32 one-at-a-time hashes */ for (lopool_uptr = 0; lopool_uptr < 32; ++lopool_uptr) BAFHFinish_mem(lopool_uhash[lopool_uptr]); /* stir the collapse pool with the result */ arcfour_ksa(&lopool_collapse, (void *)lopool_uhash, sizeof(lopool_uhash)); /* assert(lopool_uptr == 32); */ while (lopool_uptr--) /* re-initialise all 32 BAFH states from RNG */ lopool_uhash[lopool_uptr] = arc4random(); /* sync statistic data / counter */ lopool_content = 0; lopool_uptr = 0; /* * note: we don't throw away the “early keystream” like * with arc4random(9) because this is never used/exposed * to anything save arc4random_reinit, and we don’t care * about bias here either, this is just for distribution */ ++rndstats.lopool_deq; } /* 1 + [0;2[ seconds */ timeout_add(&rnd_lopool_timeout, hz + arc4random_uniform(hz << 1)); out: splx(s); } void randomattach(void) { if (rnd_attached) { RNDEBUG(RD_ALWAYS, "random: second attach\n"); return; } timeout_set(&arc4random_timeout, arc4random_reinit, NULL); timeout_set(&rnd_lopool_timeout, rnd_lopool_dequeue, NULL); /* dev/rnd.c */ rndpool_init(); arcfour_init(&lopool_collapse); /* this dword is where also locore.s writes to */ add_true_randomness( ((uint32_t)initial_entropy[3] << 24) | ((uint32_t)initial_entropy[2] << 16) | ((uint32_t)initial_entropy[1] << 8) | (uint32_t)initial_entropy[0]); memcpy(initial_entropy, &_randseed, 4); arcfour_ksa(&lopool_collapse, initial_entropy, 16); /* just in case there’s something actually using these */ arc4random_buf(initial_entropy, 16); _randseed = arc4random(); rnd_attached = 1; /* initialises both lopool and arc4random timeouts */ rnd_flush(); } /* called from RNDSTIRARC4 ioctl by pid 1, during boot and reboot */ void rnd_flush(void) { int s; RNDEBUG(RD_ALWAYS, "rnd: flushing\n"); s = splhigh(); if (lopool_content < 128) /* force rnd_lopool_dequeue() to act */ lopool_content = 128; rnd_lopool_dequeue(NULL); arc4random_reinit(NULL); splx(s); }