summaryrefslogtreecommitdiffstats
path: root/sys/dev/random/fortuna.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/random/fortuna.c')
-rw-r--r--sys/dev/random/fortuna.c582
1 files changed, 315 insertions, 267 deletions
diff --git a/sys/dev/random/fortuna.c b/sys/dev/random/fortuna.c
index 2bb31f4..00beaac 100644
--- a/sys/dev/random/fortuna.c
+++ b/sys/dev/random/fortuna.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2013-2014 Mark R V Murray
+ * Copyright (c) 2013-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,25 +25,24 @@
*
*/
-/* This implementation of Fortuna is based on the descriptions found in
- * ISBN 0-471-22357-3 "Practical Cryptography" by Ferguson and Schneier
- * ("F&S").
- *
- * The above book is superseded by ISBN 978-0-470-47424-2 "Cryptography
- * Engineering" by Ferguson, Schneier and Kohno ("FS&K"). The code has
- * not yet fully caught up with FS&K.
+/*
+ * This implementation of Fortuna is based on the descriptions found in
+ * ISBN 978-0-470-47424-2 "Cryptography Engineering" by Ferguson, Schneier
+ * and Kohno ("FS&K").
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#ifdef _KERNEL
-#include "opt_random.h"
+#include <sys/limits.h>
+#ifdef _KERNEL
#include <sys/param.h>
#include <sys/kernel.h>
+#include <sys/conf.h>
#include <sys/lock.h>
#include <sys/malloc.h>
+#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/random.h>
#include <sys/sysctl.h>
@@ -56,13 +55,10 @@ __FBSDID("$FreeBSD$");
#include <dev/random/hash.h>
#include <dev/random/randomdev.h>
-#include <dev/random/random_adaptors.h>
#include <dev/random/random_harvestq.h>
#include <dev/random/uint128.h>
#include <dev/random/fortuna.h>
#else /* !_KERNEL */
-#include <sys/param.h>
-#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
@@ -79,351 +75,405 @@ __FBSDID("$FreeBSD$");
#include <dev/random/fortuna.h>
#endif /* _KERNEL */
-#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA)
-#define RANDOM_YARROW
-#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA)
-#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA"
-#endif
-
-#if defined(RANDOM_FORTUNA)
-
-#define NPOOLS 32
-#define MINPOOLSIZE 64
-#define DEFPOOLSIZE 256
-#define MAXPOOLSIZE 65536
+/* Defined in FS&K */
+#define RANDOM_FORTUNA_NPOOLS 32 /* The number of accumulation pools */
+#define RANDOM_FORTUNA_DEFPOOLSIZE 64 /* The default pool size/length for a (re)seed */
+#define RANDOM_FORTUNA_MAX_READ (1 << 20) /* Max bytes in a single read */
-/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */
-CTASSERT(BLOCKSIZE == sizeof(uint128_t));
-CTASSERT(KEYSIZE == 2*BLOCKSIZE);
-
-/* This is the beastie that needs protecting. It contains all of the
- * state that we are excited about.
- * Exactly one is instantiated.
+/*
+ * The allowable range of RANDOM_FORTUNA_DEFPOOLSIZE. The default value is above.
+ * Making RANDOM_FORTUNA_DEFPOOLSIZE too large will mean a long time between reseeds,
+ * and too small may compromise initial security but get faster reseeds.
+ */
+#define RANDOM_FORTUNA_MINPOOLSIZE 16
+#define RANDOM_FORTUNA_MAXPOOLSIZE UINT_MAX
+CTASSERT(RANDOM_FORTUNA_MINPOOLSIZE <= RANDOM_FORTUNA_DEFPOOLSIZE);
+CTASSERT(RANDOM_FORTUNA_DEFPOOLSIZE <= RANDOM_FORTUNA_MAXPOOLSIZE);
+
+/* This algorithm (and code) presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */
+CTASSERT(RANDOM_BLOCKSIZE == sizeof(uint128_t));
+CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE);
+
+/*
+ * This is the beastie that needs protecting. It contains all of the
+ * state that we are excited about. Exactly one is instantiated.
*/
static struct fortuna_state {
- /* P_i */
- struct pool {
- u_int length;
- struct randomdev_hash hash;
- } pool[NPOOLS];
-
- /* ReseedCnt */
- u_int reseedcount;
-
- /* C - 128 bits */
- union {
- uint8_t byte[BLOCKSIZE];
- uint128_t whole;
- } counter;
-
- /* K */
- struct randomdev_key key;
-
- /* Extras */
- u_int minpoolsize;
-
+ struct fs_pool { /* P_i */
+ u_int fsp_length; /* Only the first one is used by Fortuna */
+ struct randomdev_hash fsp_hash;
+ } fs_pool[RANDOM_FORTUNA_NPOOLS];
+ u_int fs_reseedcount; /* ReseedCnt */
+ uint128_t fs_counter; /* C */
+ struct randomdev_key fs_key; /* K */
+ u_int fs_minpoolsize; /* Extras */
/* Extras for the OS */
-
#ifdef _KERNEL
/* For use when 'pacing' the reseeds */
- sbintime_t lasttime;
+ sbintime_t fs_lasttime;
#endif
+ /* Reseed lock */
+ mtx_t fs_mtx;
} fortuna_state;
-/* The random_reseed_mtx mutex protects seeding and polling/blocking. */
-static mtx_t random_reseed_mtx;
+#ifdef _KERNEL
+static struct sysctl_ctx_list random_clist;
+RANDOM_CHECK_UINT(fs_minpoolsize, RANDOM_FORTUNA_MINPOOLSIZE, RANDOM_FORTUNA_MAXPOOLSIZE);
+#else
+static uint8_t zero_region[RANDOM_ZERO_BLOCKSIZE];
+#endif
-static struct fortuna_start_cache {
- uint8_t junk[PAGE_SIZE];
- size_t length;
- struct randomdev_hash hash;
-} fortuna_start_cache;
+static void random_fortuna_pre_read(void);
+static void random_fortuna_read(uint8_t *, u_int);
+static void random_fortuna_post_read(void);
+static void random_fortuna_write(uint8_t *, u_int);
+static void random_fortuna_reseed(void);
+static int random_fortuna_seeded(void);
+static void random_fortuna_process_event(struct harvest_event *);
#ifdef _KERNEL
-static struct sysctl_ctx_list random_clist;
-RANDOM_CHECK_UINT(minpoolsize, MINPOOLSIZE, MAXPOOLSIZE);
+/* Interface to Adaptors system */
+struct random_algorithm random_alg_context = {
+ .ra_ident = "Fortuna",
+ .ra_pre_read = random_fortuna_pre_read,
+ .ra_read = random_fortuna_read,
+ .ra_post_read = random_fortuna_post_read,
+ .ra_write = random_fortuna_write,
+ .ra_reseed = random_fortuna_reseed,
+ .ra_seeded = random_fortuna_seeded,
+ .ra_event_processor = random_fortuna_process_event,
+ .ra_poolcount = RANDOM_FORTUNA_NPOOLS,
+};
#endif
-void
-random_fortuna_init_alg(void)
+/* ARGSUSED */
+static void
+random_fortuna_init_alg(void *unused __unused)
{
int i;
#ifdef _KERNEL
struct sysctl_oid *random_fortuna_o;
#endif
- memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk));
- fortuna_start_cache.length = 0U;
- randomdev_hash_init(&fortuna_start_cache.hash);
-
- /* Set up a lock for the reseed process */
-#ifdef _KERNEL
- mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF);
-#else /* !_KERNEL */
- mtx_init(&random_reseed_mtx, mtx_plain);
-#endif /* _KERNEL */
-
-#ifdef _KERNEL
- /* Fortuna parameters. Do not adjust these unless you have
+ RANDOM_RESEED_INIT_LOCK();
+ /*
+ * Fortuna parameters. Do not adjust these unless you have
* have a very good clue about what they do!
*/
+ fortuna_state.fs_minpoolsize = RANDOM_FORTUNA_DEFPOOLSIZE;
+#ifdef _KERNEL
+ fortuna_state.fs_lasttime = 0;
random_fortuna_o = SYSCTL_ADD_NODE(&random_clist,
SYSCTL_STATIC_CHILDREN(_kern_random),
OID_AUTO, "fortuna", CTLFLAG_RW, 0,
"Fortuna Parameters");
-
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_fortuna_o), OID_AUTO,
- "minpoolsize", CTLTYPE_UINT|CTLFLAG_RW,
- &fortuna_state.minpoolsize, DEFPOOLSIZE,
- random_check_uint_minpoolsize, "IU",
- "Minimum pool size necessary to cause a reseed automatically");
-
- fortuna_state.lasttime = 0U;
+ "minpoolsize", CTLTYPE_UINT | CTLFLAG_RWTUN,
+ &fortuna_state.fs_minpoolsize, RANDOM_FORTUNA_DEFPOOLSIZE,
+ random_check_uint_fs_minpoolsize, "IU",
+ "Minimum pool size necessary to cause a reseed");
+ KASSERT(fortuna_state.fs_minpoolsize > 0, ("random: Fortuna threshold must be > 0 at startup"));
#endif
- fortuna_state.minpoolsize = DEFPOOLSIZE;
-
- /* F&S - InitializePRNG() */
-
- /* F&S - P_i = \epsilon */
- for (i = 0; i < NPOOLS; i++) {
- randomdev_hash_init(&fortuna_state.pool[i].hash);
- fortuna_state.pool[i].length = 0U;
+ /*-
+ * FS&K - InitializePRNG()
+ * - P_i = \epsilon
+ * - ReseedCNT = 0
+ */
+ for (i = 0; i < RANDOM_FORTUNA_NPOOLS; i++) {
+ randomdev_hash_init(&fortuna_state.fs_pool[i].fsp_hash);
+ fortuna_state.fs_pool[i].fsp_length = 0;
}
-
- /* F&S - ReseedCNT = 0 */
- fortuna_state.reseedcount = 0U;
-
- /* F&S - InitializeGenerator() */
-
- /* F&S - C = 0 */
- uint128_clear(&fortuna_state.counter.whole);
-
- /* F&S - K = 0 */
- memset(&fortuna_state.key, 0, sizeof(fortuna_state.key));
+ fortuna_state.fs_reseedcount = 0;
+ /*-
+ * FS&K - InitializeGenerator()
+ * - C = 0
+ * - K = 0
+ */
+ fortuna_state.fs_counter = UINT128_ZERO;
+ explicit_bzero(&fortuna_state.fs_key, sizeof(fortuna_state.fs_key));
}
+#ifdef _KERNEL
+SYSINIT(random_fortuna, SI_SUB_RANDOM, SI_ORDER_THIRD, random_fortuna_init_alg, NULL);
+#endif
-void
-random_fortuna_deinit_alg(void)
+/* ARGSUSED */
+static void
+random_fortuna_deinit_alg(void *unused __unused)
{
- mtx_destroy(&random_reseed_mtx);
- memset(&fortuna_state, 0, sizeof(fortuna_state));
+ RANDOM_RESEED_DEINIT_LOCK();
+ explicit_bzero(&fortuna_state, sizeof(fortuna_state));
+#ifdef _KERNEL
+ sysctl_ctx_free(&random_clist);
+#endif
}
+#ifdef _KERNEL
+SYSUNINIT(random_fortuna, SI_SUB_RANDOM, SI_ORDER_THIRD, random_fortuna_deinit_alg, NULL);
+#endif
-/* F&S - AddRandomEvent() */
-/* Process a single stochastic event off the harvest queue */
+/*-
+ * FS&K - AddRandomEvent()
+ * Process a single stochastic event off the harvest queue
+ */
void
random_fortuna_process_event(struct harvest_event *event)
{
u_int pl;
- /* We must be locked for all this as plenty of state gets messed with */
- mtx_lock(&random_reseed_mtx);
-
- /* Accumulate the event into the appropriate pool
- * where each event carries the destination information
+ RANDOM_RESEED_LOCK();
+ /*-
+ * FS&K - P_i = P_i|<harvested stuff>
+ * Accumulate the event into the appropriate pool
+ * where each event carries the destination information.
+ *
+ * The hash_init() and hash_finish() calls are done in
+ * random_fortuna_pre_read().
+ *
+ * We must be locked against pool state modification which can happen
+ * during accumulation/reseeding and reading/regating.
+ */
+ pl = event->he_destination % RANDOM_FORTUNA_NPOOLS;
+ randomdev_hash_iterate(&fortuna_state.fs_pool[pl].fsp_hash, event, sizeof(*event));
+ /*-
+ * Don't wrap the length. Doing the the hard way so as not to wrap at MAXUINT.
+ * This is a "saturating" add.
+ * XXX: FIX!!: We don't actually need lengths for anything but fs_pool[0],
+ * but it's been useful debugging to see them all.
*/
- /* F&S - P_i = P_i|<harvested stuff> */
- /* The hash_init and hash_finish are done in random_fortuna_read() below */
- pl = event->he_destination % NPOOLS;
- randomdev_hash_iterate(&fortuna_state.pool[pl].hash, event, sizeof(*event));
- /* No point in counting above the outside maximum */
- fortuna_state.pool[pl].length += event->he_size;
- fortuna_state.pool[pl].length = MIN(fortuna_state.pool[pl].length, MAXPOOLSIZE);
-
- /* Done with state-messing */
- mtx_unlock(&random_reseed_mtx);
+ if (RANDOM_FORTUNA_MAXPOOLSIZE - fortuna_state.fs_pool[pl].fsp_length > event->he_size)
+ fortuna_state.fs_pool[pl].fsp_length += event->he_size;
+ else
+ fortuna_state.fs_pool[pl].fsp_length = RANDOM_FORTUNA_MAXPOOLSIZE;
+ explicit_bzero(event, sizeof(*event));
+ RANDOM_RESEED_UNLOCK();
+}
+
+/*-
+ * Process a block of data suspected to be slightly stochastic.
+ * Do this by breaking it up and inserting the pieces as if
+ * they were separate events.
+ */
+static void
+random_fortuna_process_buffer(uint32_t *buf, u_int wordcount)
+{
+ static struct harvest_event event;
+ static u_int destination = 0;
+ int i;
+
+ for (i = 0; i < wordcount; i += sizeof(event.he_entropy)/sizeof(event.he_entropy[0])) {
+ event.he_somecounter = (uint32_t)get_cyclecount();
+ event.he_size = sizeof(event.he_entropy);
+ event.he_bits = event.he_size/8;
+ event.he_source = RANDOM_CACHED;
+ event.he_destination = destination++; /* Harmless cheating */
+ memcpy(event.he_entropy, buf + i, sizeof(event.he_entropy));
+ random_fortuna_process_event(&event);
+ }
}
-/* F&S - Reseed() */
-/* Reseed Mutex is held */
+/*-
+ * FS&K - Reseed()
+ * This introduces new key material into the output generator.
+ * Additionaly it increments the output generator's counter
+ * variable C. When C > 0, the output generator is seeded and
+ * will deliver output.
+ * The entropy_data buffer passed is a very specific size; the
+ * product of RANDOM_FORTUNA_NPOOLS and RANDOM_KEYSIZE.
+ */
static void
-reseed(uint8_t *junk, u_int length)
+random_fortuna_reseed_internal(uint32_t *entropy_data, u_int blockcount)
{
struct randomdev_hash context;
- uint8_t hash[KEYSIZE];
+ uint8_t hash[RANDOM_KEYSIZE];
- KASSERT(fortuna_state.minpoolsize > 0, ("random: Fortuna threshold = 0"));
-#ifdef _KERNEL
- mtx_assert(&random_reseed_mtx, MA_OWNED);
-#endif
-
- /* FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m)) */
+ RANDOM_RESEED_ASSERT_LOCK_OWNED();
+ /*-
+ * FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m))
+ * - C = C + 1
+ */
randomdev_hash_init(&context);
- randomdev_hash_iterate(&context, zero_region, 512/8);
- randomdev_hash_iterate(&context, &fortuna_state.key, sizeof(fortuna_state.key));
- randomdev_hash_iterate(&context, junk, length);
+ randomdev_hash_iterate(&context, zero_region, RANDOM_ZERO_BLOCKSIZE);
+ randomdev_hash_iterate(&context, &fortuna_state.fs_key, sizeof(fortuna_state.fs_key));
+ randomdev_hash_iterate(&context, entropy_data, RANDOM_KEYSIZE*blockcount);
randomdev_hash_finish(&context, hash);
randomdev_hash_init(&context);
- randomdev_hash_iterate(&context, hash, KEYSIZE);
+ randomdev_hash_iterate(&context, hash, RANDOM_KEYSIZE);
randomdev_hash_finish(&context, hash);
- randomdev_encrypt_init(&fortuna_state.key, hash);
- memset(hash, 0, sizeof(hash));
-
- /* Unblock the device if it was blocked due to being unseeded */
- if (uint128_is_zero(fortuna_state.counter.whole))
- random_adaptor_unblock();
- /* FS&K - C = C + 1 */
- uint128_increment(&fortuna_state.counter.whole);
+ randomdev_encrypt_init(&fortuna_state.fs_key, hash);
+ explicit_bzero(hash, sizeof(hash));
+ /* Unblock the device if this is the first time we are reseeding. */
+ if (uint128_is_zero(fortuna_state.fs_counter))
+ randomdev_unblock();
+ uint128_increment(&fortuna_state.fs_counter);
}
-/* F&S - GenerateBlocks() */
-/* Reseed Mutex is held, and buf points to a whole number of blocks. */
+/*-
+ * FS&K - GenerateBlocks()
+ * Generate a number of complete blocks of random output.
+ */
static __inline void
random_fortuna_genblocks(uint8_t *buf, u_int blockcount)
{
u_int i;
- for (i = 0u; i < blockcount; i++) {
- /* F&S - r = r|E(K,C) */
- randomdev_encrypt(&fortuna_state.key, fortuna_state.counter.byte, buf, BLOCKSIZE);
- buf += BLOCKSIZE;
-
- /* F&S - C = C + 1 */
- uint128_increment(&fortuna_state.counter.whole);
+ RANDOM_RESEED_ASSERT_LOCK_OWNED();
+ for (i = 0; i < blockcount; i++) {
+ /*-
+ * FS&K - r = r|E(K,C)
+ * - C = C + 1
+ */
+ randomdev_encrypt(&fortuna_state.fs_key, &fortuna_state.fs_counter, buf, RANDOM_BLOCKSIZE);
+ buf += RANDOM_BLOCKSIZE;
+ uint128_increment(&fortuna_state.fs_counter);
}
}
-/* F&S - PseudoRandomData() */
-/* Reseed Mutex is held, and buf points to a whole number of blocks. */
+/*-
+ * FS&K - PseudoRandomData()
+ * This generates no more than 2^20 bytes of data, and cleans up its
+ * internal state when finished. It is assumed that a whole number of
+ * blocks are available for writing; any excess generated will be
+ * ignored.
+ */
static __inline void
random_fortuna_genrandom(uint8_t *buf, u_int bytecount)
{
- static uint8_t temp[BLOCKSIZE*(KEYSIZE/BLOCKSIZE)];
+ static uint8_t temp[RANDOM_BLOCKSIZE*(RANDOM_KEYS_PER_BLOCK)];
u_int blockcount;
- /* F&S - assert(n < 2^20) */
- KASSERT((bytecount <= (1 << 20)), ("invalid single read request to fortuna of %d bytes", bytecount));
-
- /* F&S - r = first-n-bytes(GenerateBlocks(ceil(n/16))) */
- blockcount = bytecount / BLOCKSIZE;
+ RANDOM_RESEED_ASSERT_LOCK_OWNED();
+ /*-
+ * FS&K - assert(n < 2^20 (== 1 MB)
+ * - r = first-n-bytes(GenerateBlocks(ceil(n/16)))
+ * - K = GenerateBlocks(2)
+ */
+ KASSERT((bytecount <= RANDOM_FORTUNA_MAX_READ), ("invalid single read request to Fortuna of %d bytes", bytecount));
+ blockcount = (bytecount + RANDOM_BLOCKSIZE - 1)/RANDOM_BLOCKSIZE;
random_fortuna_genblocks(buf, blockcount);
- /* TODO: FIX! remove memcpy()! */
- if (bytecount % BLOCKSIZE > 0) {
- random_fortuna_genblocks(temp, 1);
- memcpy(buf + (blockcount * BLOCKSIZE), temp, bytecount % BLOCKSIZE);
- }
-
- /* F&S - K = GenerateBlocks(2) */
- random_fortuna_genblocks(temp, KEYSIZE/BLOCKSIZE);
- randomdev_encrypt_init(&fortuna_state.key, temp);
- memset(temp, 0, sizeof(temp));
+ random_fortuna_genblocks(temp, RANDOM_KEYS_PER_BLOCK);
+ randomdev_encrypt_init(&fortuna_state.fs_key, temp);
+ explicit_bzero(temp, sizeof(temp));
}
-/* F&S - RandomData() */
-/* Used to return processed entropy from the PRNG */
-/* The argument buf points to a whole number of blocks. */
+/*-
+ * FS&K - RandomData()
+ * Used to return processed entropy from the PRNG.
+ * There is a pre_read and a post_read required to be present
+ * (but they can be null functions) in order to allow specific
+ * actions at the begin or the end of a read. Fortuna does its
+ * reseeding in the _pre_read() part, and _post_read() is not
+ * used.
+ */
void
-random_fortuna_read(uint8_t *buf, u_int bytecount)
+random_fortuna_pre_read(void)
{
#ifdef _KERNEL
- sbintime_t thistime;
+ sbintime_t now;
#endif
struct randomdev_hash context;
- uint8_t s[NPOOLS*KEYSIZE], temp[KEYSIZE];
- int i;
- u_int seedlength;
+ uint32_t s[RANDOM_FORTUNA_NPOOLS*RANDOM_KEYSIZE_WORDS];
+ uint8_t temp[RANDOM_KEYSIZE];
+ u_int i;
- /* We must be locked for all this as plenty of state gets messed with */
- mtx_lock(&random_reseed_mtx);
+ KASSERT(fortuna_state.fs_minpoolsize > 0, ("random: Fortuna threshold must be > 0"));
+#ifdef _KERNEL
+ /* FS&K - Use 'getsbinuptime()' to prevent reseed-spamming. */
+ now = getsbinuptime();
+#endif
+ RANDOM_RESEED_LOCK();
- /* if buf == NULL and bytecount == 0 then this is the pre-read. */
- /* if buf == NULL and bytecount != 0 then this is the post-read; ignore. */
- if (buf == NULL) {
- if (bytecount == 0) {
- if (fortuna_state.pool[0].length >= fortuna_state.minpoolsize
+ if (fortuna_state.fs_pool[0].fsp_length >= fortuna_state.fs_minpoolsize
#ifdef _KERNEL
- /* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */
- && ((thistime = getsbinuptime()) - fortuna_state.lasttime > hz/10)
+ /* FS&K - Use 'getsbinuptime()' to prevent reseed-spamming. */
+ && (now - fortuna_state.fs_lasttime > hz/10)
#endif
- ) {
+ ) {
#ifdef _KERNEL
- fortuna_state.lasttime = thistime;
+ fortuna_state.fs_lasttime = now;
#endif
- seedlength = 0U;
- /* F&S - ReseedCNT = ReseedCNT + 1 */
- fortuna_state.reseedcount++;
- /* s = \epsilon by default */
- for (i = 0; i < NPOOLS; i++) {
- /* F&S - if Divides(ReseedCnt, 2^i) ... */
- if ((fortuna_state.reseedcount % (1 << i)) == 0U) {
- seedlength += KEYSIZE;
- /* F&S - temp = (P_i) */
- randomdev_hash_finish(&fortuna_state.pool[i].hash, temp);
- /* F&S - P_i = \epsilon */
- randomdev_hash_init(&fortuna_state.pool[i].hash);
- fortuna_state.pool[i].length = 0U;
- /* F&S - s = s|H(temp) */
- randomdev_hash_init(&context);
- randomdev_hash_iterate(&context, temp, KEYSIZE);
- randomdev_hash_finish(&context, s + i*KEYSIZE);
- }
- else
- break;
- }
+ /* FS&K - ReseedCNT = ReseedCNT + 1 */
+ fortuna_state.fs_reseedcount++;
+ /* s = \epsilon at start */
+ for (i = 0; i < RANDOM_FORTUNA_NPOOLS; i++) {
+ /* FS&K - if Divides(ReseedCnt, 2^i) ... */
+ if ((fortuna_state.fs_reseedcount % (1 << i)) == 0) {
+ /*-
+ * FS&K - temp = (P_i)
+ * - P_i = \epsilon
+ * - s = s|H(temp)
+ */
+ randomdev_hash_finish(&fortuna_state.fs_pool[i].fsp_hash, temp);
+ randomdev_hash_init(&fortuna_state.fs_pool[i].fsp_hash);
+ fortuna_state.fs_pool[i].fsp_length = 0;
+ randomdev_hash_init(&context);
+ randomdev_hash_iterate(&context, temp, RANDOM_KEYSIZE);
+ randomdev_hash_finish(&context, s + i*RANDOM_KEYSIZE_WORDS);
+ } else
+ break;
+ }
#ifdef RANDOM_DEBUG
- printf("random: active reseed: reseedcount [%d] ", fortuna_state.reseedcount);
- for (i = 0; i < NPOOLS; i++)
- printf(" %d", fortuna_state.pool[i].length);
- printf("\n");
-#endif
- /* F&S */
- reseed(s, seedlength);
-
- /* Clean up */
- memset(s, 0, seedlength);
- seedlength = 0U;
- memset(temp, 0, sizeof(temp));
- memset(&context, 0, sizeof(context));
- }
+ {
+ u_int j;
+
+ printf("random: reseedcount [%d]", fortuna_state.fs_reseedcount);
+ for (j = 0; j < RANDOM_FORTUNA_NPOOLS; j++)
+ printf(" %X", fortuna_state.fs_pool[j].fsp_length);
+ printf("\n");
}
+#endif
+ /* FS&K */
+ random_fortuna_reseed_internal(s, i < RANDOM_FORTUNA_NPOOLS ? i + 1 : RANDOM_FORTUNA_NPOOLS);
+ /* Clean up and secure */
+ explicit_bzero(s, sizeof(s));
+ explicit_bzero(temp, sizeof(temp));
+ explicit_bzero(&context, sizeof(context));
}
- /* if buf != NULL do a regular read. */
- else
- random_fortuna_genrandom(buf, bytecount);
-
- mtx_unlock(&random_reseed_mtx);
+ RANDOM_RESEED_UNLOCK();
}
-/* Internal function to hand external entropy to the PRNG */
+/*-
+ * Main read from Fortuna.
+ * The supplied buf MUST be a multiple (>=0) of RANDOM_BLOCKSIZE in size.
+ * Lots of code presumes this for efficiency, both here and in other
+ * routines. You are NOT allowed to break this!
+ */
void
-random_fortuna_write(uint8_t *buf, u_int count)
+random_fortuna_read(uint8_t *buf, u_int bytecount)
{
- uint8_t temp[KEYSIZE];
- int i;
- uintmax_t timestamp;
-
- timestamp = get_cyclecount();
- randomdev_hash_iterate(&fortuna_start_cache.hash, &timestamp, sizeof(timestamp));
- randomdev_hash_iterate(&fortuna_start_cache.hash, buf, count);
- timestamp = get_cyclecount();
- randomdev_hash_iterate(&fortuna_start_cache.hash, &timestamp, sizeof(timestamp));
- randomdev_hash_finish(&fortuna_start_cache.hash, temp);
- for (i = 0; i < KEYSIZE; i++)
- fortuna_start_cache.junk[(fortuna_start_cache.length + i)%PAGE_SIZE] ^= temp[i];
- fortuna_start_cache.length += KEYSIZE;
-#ifdef RANDOM_DEBUG
- printf("random: %s - ", __func__);
- for (i = 0; i < KEYSIZE; i++)
- printf("%02X", temp[i]);
- printf("\n");
-#endif
-
- memset(temp, 0, KEYSIZE);
-
- /* We must be locked for all this as plenty of state gets messed with */
- mtx_lock(&random_reseed_mtx);
+ RANDOM_RESEED_LOCK();
+ random_fortuna_genrandom(buf, bytecount);
+ RANDOM_RESEED_UNLOCK();
+}
- randomdev_hash_init(&fortuna_start_cache.hash);
+void
+random_fortuna_post_read(void)
+{
- reseed(fortuna_start_cache.junk, MIN(PAGE_SIZE, fortuna_start_cache.length));
- memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk));
+ /* CWOT */
+}
- mtx_unlock(&random_reseed_mtx);
+/* Internal function to hand external entropy to the PRNG. */
+void
+random_fortuna_write(uint8_t *buf, u_int count)
+{
+ struct randomdev_hash hash;
+ uint32_t entropy_data[RANDOM_KEYSIZE_WORDS], timestamp;
+
+ /* Extra timing here is helpful to scrape scheduler timing entropy */
+ randomdev_hash_init(&hash);
+ timestamp = (uint32_t)get_cyclecount();
+ randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
+ randomdev_hash_iterate(&hash, buf, count);
+ timestamp = (uint32_t)get_cyclecount();
+ randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
+ randomdev_hash_finish(&hash, entropy_data);
+ explicit_bzero(&hash, sizeof(hash));
+ random_fortuna_process_buffer(entropy_data, sizeof(entropy_data)/sizeof(entropy_data[0]));
+ explicit_bzero(entropy_data, sizeof(entropy_data));
}
void
@@ -437,7 +487,5 @@ int
random_fortuna_seeded(void)
{
- return (!uint128_is_zero(fortuna_state.counter.whole));
+ return (!uint128_is_zero(fortuna_state.fs_counter));
}
-
-#endif /* RANDOM_FORTUNA */
OpenPOWER on IntegriCloud