/*- * Copyright (c) 2010, 2011, 2012, 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. *- * The idea of an arc4random(3) stems from David Mazieres for OpenBSD * but this has been reimplemented, improved, corrected, modularised. * The idea of pushing entropy back to the kernel on stir and after a * fork or before an exit is MirBSD specific. */ #include #include #include #include #include #include #include #include #include "arc4random.h" #include "thread_private.h" __RCSID("$MirOS: src/lib/libc/crypt/arc4random_base.c,v 1.15 2014/04/09 23:10:51 tg Exp $"); /* zero-initialises */ struct arc4random_status a4state; void arc4random_atexit(void) { arc4random_ctl(1); } u_int32_t arc4random(void) { uint32_t v; pid_t mypid = 0; _ARC4_LOCK(); /* reinitialise after fork, (count) bytes or if not initialised */ a4state.a4s_count -= 4; if (a4state.a4s_count <= 0 || !a4state.a4s_initialised || (a4state.a4s_stir_pid != (mypid = getpid()))) arc4random_stir_locked(mypid); /* randomly skip a byte or two */ /*XXX this should be constant-time */ if (arcfour_byte(&a4state.cipher) & 1) (void)arcfour_byte(&a4state.cipher); v = ((uint32_t)arcfour_byte(&a4state.cipher) << 24) | ((uint32_t)arcfour_byte(&a4state.cipher) << 16) | ((uint32_t)arcfour_byte(&a4state.cipher) << 8) | ((uint32_t)arcfour_byte(&a4state.cipher)); _ARC4_UNLOCK(); return (v); } void arc4random_stir_locked(pid_t mypid) { size_t n; unsigned int carry; int mib[2]; union { uint8_t charbuf[256]; uint32_t intbuf[64]; struct { struct timespec rtime; #if 0 struct timespec vtime; struct timespec ptime; #endif struct timespec ntime; pid_t thepid; } tmpbuf; } sbuf; if (!a4state.a4s_initialised) { arcfour_init(&a4state.cipher); a4state.a4s_poolptr = 0; } carry = arcfour_byte(&a4state.cipher); /* throw stuff into the roundhash pool */ arc4random_roundhash(a4state.pool, &a4state.a4s_poolptr, &a4state.otherinfo, sizeof(a4state.otherinfo)); clock_gettime(CLOCK_REALTIME, &sbuf.tmpbuf.rtime); #if 0 clock_gettime(CLOCK_VIRTUAL, &sbuf.tmpbuf.vtime); clock_gettime(CLOCK_PROF, &sbuf.tmpbuf.ptime); #endif clock_gettime(CLOCK_MONOTONIC, &sbuf.tmpbuf.ntime); sbuf.tmpbuf.thepid = mypid = mypid ? mypid : getpid(); arc4random_roundhash(a4state.pool, &a4state.a4s_poolptr, &sbuf.tmpbuf, sizeof(sbuf.tmpbuf)); /* fill first 128 bytes with roundhash pool */ n = 1 + (carry & 1); carry >>= 1; while (n--) (void)arcfour_byte(&a4state.cipher); for (n = 0; n < 32; ++n) { register uint32_t h; h = a4state.pool[n]; /* mask and mix */ BAFHUpdateOctet_reg(h, arcfour_byte(&a4state.cipher)); BAFHFinish_reg(h); sbuf.intbuf[n] = h; } /* fill second 128 bytes with local random stuff */ n = 1 + (carry & 1); carry >>= 1; while (n--) (void)arcfour_byte(&a4state.cipher); for (n = 128; n < 256; ++n) sbuf.charbuf[n] = arcfour_byte(&a4state.cipher); n = carry & 3; carry &= 0x3C; while (n--) (void)arcfour_byte(&a4state.cipher); /* exchange full 256 bytes with kernel */ mib[0] = CTL_KERN; mib[1] = KERN_ARND; n = sizeof(sbuf); sysctl(mib, 2, sbuf.charbuf, &n, sbuf.charbuf, sizeof(sbuf)); /* if (n != sizeof(sbuf)) something went wrong inside the kernel */ /* mix first 128 bytes with/into roundhash pool */ for (n = 0; n < 32; ++n) { register uint32_t ha, hr; ha = (a4state.pool[n] & 0xFFFF0000) | (sbuf.intbuf[n] & 0x0000FFFF); hr = (a4state.pool[n] & 0x0000FFFF) | (sbuf.intbuf[n] & 0xFFFF0000); /* mix then mask for arc4random */ BAFHFinish_reg(ha); BAFHUpdateOctet_reg(ha, arcfour_byte(&a4state.cipher)); sbuf.intbuf[n] = ha; /* mix then mask for roundhash */ BAFHFinish_reg(hr); BAFHUpdateOctet_reg(hr, arcfour_byte(&a4state.cipher)); a4state.pool[n] = hr; } /* mix full 256 bytes into arc4random pool */ arcfour_ksa(&a4state.cipher, sbuf.charbuf, 256); /* trash stack */ bzero(sbuf.charbuf, sizeof(sbuf)); /* discard early keystream */ carry += 256 * 12 + (arcfour_byte(&a4state.cipher) & 0x1F); while (carry--) (void)arcfour_byte(&a4state.cipher); a4state.a4s_poolptr = 0; a4state.a4s_initialised = 1; a4state.a4s_stir_pid = mypid; a4state.a4s_count = 1600000; } /* * This is a deliberately undocumented internal API. * - whence & 7 = mode * + 0 = shuffle user-space only * + 1 = move stuff to kernel (e.g. for reboot) * + 2 = schedule stirring upon next access * - rest MBZ */ void arc4random_ctl(unsigned int whence) { size_t n; uint8_t *buf; struct { struct timespec tp; const void *dp, *sp; unsigned int wi; struct arcfour_otherinfo oi; } pbuf; int mib[2]; clock_gettime(CLOCK_MONOTONIC, &pbuf.tp); pbuf.sp = &pbuf; pbuf.dp = &whence; pbuf.wi = whence; _ARC4_LOCK(); memcpy(&pbuf.oi, &a4state.otherinfo, sizeof(struct arcfour_otherinfo)); if (!a4state.a4s_initialised) arc4random_stir_locked(0); arc4random_roundhash(a4state.pool, &a4state.a4s_poolptr, &pbuf, sizeof(pbuf)); switch (whence /* & 7 */) { default: break; case 2: a4state.a4s_count = -1; break; case 1: /* first put everything we have into the pool */ arc4random_roundhash(a4state.pool, &a4state.a4s_poolptr, &a4state.cipher, sizeof(a4state.cipher)); /* then write the pool into the kernel and overwrite it */ mib[0] = CTL_KERN; mib[1] = KERN_ARND; n = sizeof(a4state.pool); sysctl(mib, 2, a4state.pool, &n, a4state.pool, sizeof(a4state.pool)); /* then blind a bit for continued use, just in case */ a4state.a4s_count = n * 24 + 1024; /* FALLTHROUGH */ case 0: n = arcfour_byte(&a4state.cipher); buf = (void *)a4state.pool; arcfour_ksa(&a4state.cipher, buf, sizeof(a4state.pool)); while (n--) (void)arcfour_byte(&a4state.cipher); n = sizeof(a4state.pool); while (n--) *buf++ = arcfour_byte(&a4state.cipher); a4state.a4s_poolptr = 0; break; } _ARC4_UNLOCK(); }