Index: head/sys/dev/random/fortuna.c =================================================================== --- head/sys/dev/random/fortuna.c (revision 344912) +++ head/sys/dev/random/fortuna.c (revision 344913) @@ -1,456 +1,467 @@ /*- * Copyright (c) 2017 W. Dean Freeman * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * 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 __FBSDID("$FreeBSD$"); #include #include #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include #else /* !_KERNEL */ #include #include #include #include #include #include #include "unit_test.h" #endif /* _KERNEL */ #include #include #include #include #ifdef _KERNEL #include #endif #include #include /* 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 */ /* * 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 INT_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); /* Probes for dtrace(1) */ #ifdef _KERNEL SDT_PROVIDER_DECLARE(random); SDT_PROVIDER_DEFINE(random); SDT_PROBE_DEFINE2(random, fortuna, event_processor, debug, "u_int", "struct fs_pool *"); #endif /* _KERNEL */ /* * 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 { 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 */ + union randomdev_key fs_key; /* K */ u_int fs_minpoolsize; /* Extras */ /* Extras for the OS */ #ifdef _KERNEL /* For use when 'pacing' the reseeds */ sbintime_t fs_lasttime; #endif /* Reseed lock */ mtx_t fs_mtx; } fortuna_state; #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 void random_fortuna_pre_read(void); static void random_fortuna_read(uint8_t *, u_int); static bool random_fortuna_seeded(void); static void random_fortuna_process_event(struct harvest_event *); static void random_fortuna_init_alg(void *); static void random_fortuna_deinit_alg(void *); static void random_fortuna_reseed_internal(uint32_t *entropy_data, u_int blockcount); struct random_algorithm random_alg_context = { .ra_ident = "Fortuna", .ra_init_alg = random_fortuna_init_alg, .ra_deinit_alg = random_fortuna_deinit_alg, .ra_pre_read = random_fortuna_pre_read, .ra_read = random_fortuna_read, .ra_seeded = random_fortuna_seeded, .ra_event_processor = random_fortuna_process_event, .ra_poolcount = RANDOM_FORTUNA_NPOOLS, }; /* ARGSUSED */ static void random_fortuna_init_alg(void *unused __unused) { int i; #ifdef _KERNEL struct sysctl_oid *random_fortuna_o; #endif 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_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 /*- * 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; } 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)); } /* ARGSUSED */ static void random_fortuna_deinit_alg(void *unused __unused) { RANDOM_RESEED_DEINIT_LOCK(); explicit_bzero(&fortuna_state, sizeof(fortuna_state)); #ifdef _KERNEL sysctl_ctx_free(&random_clist); #endif } /*- * FS&K - AddRandomEvent() * Process a single stochastic event off the harvest queue */ static void random_fortuna_process_event(struct harvest_event *event) { u_int pl; RANDOM_RESEED_LOCK(); /*- * FS&K - P_i = P_i| * 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; /* * We ignore low entropy static/counter fields towards the end of the * he_event structure in order to increase measurable entropy when * conducting SP800-90B entropy analysis measurements of seed material * fed into PRNG. * -- wdf */ KASSERT(event->he_size <= sizeof(event->he_entropy), ("%s: event->he_size: %hhu > sizeof(event->he_entropy): %zu\n", __func__, event->he_size, sizeof(event->he_entropy))); randomdev_hash_iterate(&fortuna_state.fs_pool[pl].fsp_hash, &event->he_somecounter, sizeof(event->he_somecounter)); randomdev_hash_iterate(&fortuna_state.fs_pool[pl].fsp_hash, event->he_entropy, event->he_size); /*- * Don't wrap the length. 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. */ fortuna_state.fs_pool[pl].fsp_length = MIN(RANDOM_FORTUNA_MAXPOOLSIZE, fortuna_state.fs_pool[pl].fsp_length + sizeof(event->he_somecounter) + event->he_size); explicit_bzero(event, sizeof(*event)); RANDOM_RESEED_UNLOCK(); } /*- * FS&K - Reseed() * This introduces new key material into the output generator. * Additionally 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 random_fortuna_reseed_internal(uint32_t *entropy_data, u_int blockcount) { struct randomdev_hash context; uint8_t hash[RANDOM_KEYSIZE]; + const void *keymaterial; + size_t keysz; + bool seeded; RANDOM_RESEED_ASSERT_LOCK_OWNED(); + + seeded = random_fortuna_seeded(); + if (seeded) { + randomdev_getkey(&fortuna_state.fs_key, &keymaterial, &keysz); + KASSERT(keysz == RANDOM_KEYSIZE, ("%s: key size %zu not %u", + __func__, keysz, (unsigned)RANDOM_KEYSIZE)); + } + /*- * 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, RANDOM_ZERO_BLOCKSIZE); - randomdev_hash_iterate(&context, &fortuna_state.fs_key.key.keyMaterial, - fortuna_state.fs_key.key.keyLen / 8); + if (seeded) + randomdev_hash_iterate(&context, keymaterial, keysz); randomdev_hash_iterate(&context, entropy_data, RANDOM_KEYSIZE*blockcount); randomdev_hash_finish(&context, hash); randomdev_hash_init(&context); randomdev_hash_iterate(&context, hash, RANDOM_KEYSIZE); randomdev_hash_finish(&context, hash); 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); } /*- * FS&K - GenerateBlocks() * Generate a number of complete blocks of random output. */ static __inline void random_fortuna_genblocks(uint8_t *buf, u_int blockcount) { RANDOM_RESEED_ASSERT_LOCK_OWNED(); KASSERT(!uint128_is_zero(fortuna_state.fs_counter), ("FS&K: C != 0")); /* * Fills buf with RANDOM_BLOCKSIZE * blockcount bytes of keystream. * Increments fs_counter as it goes. */ randomdev_keystream(&fortuna_state.fs_key, &fortuna_state.fs_counter, buf, blockcount); } /*- * 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) { uint8_t temp[RANDOM_BLOCKSIZE * RANDOM_KEYS_PER_BLOCK]; u_int blockcount; 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 = howmany(bytecount, RANDOM_BLOCKSIZE); random_fortuna_genblocks(buf, blockcount); random_fortuna_genblocks(temp, RANDOM_KEYS_PER_BLOCK); randomdev_encrypt_init(&fortuna_state.fs_key, temp); explicit_bzero(temp, sizeof(temp)); } /*- * FS&K - RandomData() (Part 1) * Used to return processed entropy from the PRNG. There is a pre_read * required to be present (but it can be a stub) in order to allow * specific actions at the begin of the read. */ void random_fortuna_pre_read(void) { #ifdef _KERNEL sbintime_t now; #endif struct randomdev_hash context; uint32_t s[RANDOM_FORTUNA_NPOOLS*RANDOM_KEYSIZE_WORDS]; uint8_t temp[RANDOM_KEYSIZE]; u_int i; KASSERT(fortuna_state.fs_minpoolsize > 0, ("random: Fortuna threshold must be > 0")); RANDOM_RESEED_LOCK(); #ifdef _KERNEL /* FS&K - Use 'getsbinuptime()' to prevent reseed-spamming. */ now = getsbinuptime(); #endif if (fortuna_state.fs_pool[0].fsp_length < fortuna_state.fs_minpoolsize #ifdef _KERNEL /* FS&K - Use 'getsbinuptime()' to prevent reseed-spamming. */ || (now - fortuna_state.fs_lasttime <= SBT_1S/10) #endif ) { RANDOM_RESEED_UNLOCK(); return; } #ifdef _KERNEL /* * When set, pretend we do not have enough entropy to reseed yet. */ KFAIL_POINT_CODE(DEBUG_FP, random_fortuna_pre_read, { if (RETURN_VALUE != 0) { RANDOM_RESEED_UNLOCK(); return; } }); #endif #ifdef _KERNEL fortuna_state.fs_lasttime = now; #endif /* 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 _KERNEL SDT_PROBE2(random, fortuna, event_processor, debug, fortuna_state.fs_reseedcount, fortuna_state.fs_pool); #endif /* FS&K */ random_fortuna_reseed_internal(s, i); RANDOM_RESEED_UNLOCK(); /* Clean up and secure */ explicit_bzero(s, sizeof(s)); explicit_bzero(temp, sizeof(temp)); } /*- * FS&K - RandomData() (Part 2) * Main read from Fortuna, continued. May be called multiple times after * the random_fortuna_pre_read() above. * The supplied buf MUST be a multiple 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_read(uint8_t *buf, u_int bytecount) { KASSERT((bytecount % RANDOM_BLOCKSIZE) == 0, ("%s(): bytecount (= %d) must be a multiple of %d", __func__, bytecount, RANDOM_BLOCKSIZE )); RANDOM_RESEED_LOCK(); random_fortuna_genrandom(buf, bytecount); RANDOM_RESEED_UNLOCK(); } bool random_fortuna_seeded(void) { #ifdef _KERNEL /* When set, act as if we are not seeded. */ KFAIL_POINT_CODE(DEBUG_FP, random_fortuna_seeded, { if (RETURN_VALUE != 0) fortuna_state.fs_counter = UINT128_ZERO; }); #endif return (!uint128_is_zero(fortuna_state.fs_counter)); } Index: head/sys/dev/random/hash.c =================================================================== --- head/sys/dev/random/hash.c (revision 344912) +++ head/sys/dev/random/hash.c (revision 344913) @@ -1,113 +1,222 @@ /*- * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #ifdef _KERNEL #include +#include +#include +#include #include #else /* !_KERNEL */ #include #include #include #include #include #include #include #include "unit_test.h" #endif /* _KERNEL */ +#define CHACHA_EMBED +#define KEYSTREAM_ONLY +#define CHACHA_NONCE0_CTR128 +#include #include #include #include +#ifdef _KERNEL +#include +#endif /* This code presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */ CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE); +/* Validate that full Chacha IV is as large as the 128-bit counter */ +_Static_assert(CHACHA_STATELEN == RANDOM_BLOCKSIZE, ""); + +/* + * Experimental Chacha20-based PRF for Fortuna keystream primitive. For now, + * disabled by default. But we may enable it in the future. + * + * Benefits include somewhat faster keystream generation compared with + * unaccelerated AES-ICM. + */ +bool random_chachamode = false; +#ifdef _KERNEL +SYSCTL_BOOL(_kern_random, OID_AUTO, use_chacha20_cipher, CTLFLAG_RDTUN, + &random_chachamode, 0, + "If non-zero, use the ChaCha20 cipher for randomdev PRF. " + "If zero, use AES-ICM cipher for randomdev PRF (default)."); +#endif + /* Initialise the hash */ void randomdev_hash_init(struct randomdev_hash *context) { SHA256_Init(&context->sha); } /* Iterate the hash */ void randomdev_hash_iterate(struct randomdev_hash *context, const void *data, size_t size) { SHA256_Update(&context->sha, data, size); } /* Conclude by returning the hash in the supplied <*buf> which must be * RANDOM_KEYSIZE bytes long. */ void randomdev_hash_finish(struct randomdev_hash *context, void *buf) { SHA256_Final(buf, &context->sha); } /* Initialise the encryption routine by setting up the key schedule * from the supplied <*data> which must be RANDOM_KEYSIZE bytes of binary * data. */ void -randomdev_encrypt_init(struct randomdev_key *context, const void *data) +randomdev_encrypt_init(union randomdev_key *context, const void *data) { - rijndael_cipherInit(&context->cipher, MODE_ECB, NULL); - rijndael_makeKey(&context->key, DIR_ENCRYPT, RANDOM_KEYSIZE*8, data); + if (random_chachamode) { + chacha_keysetup(&context->chacha, data, RANDOM_KEYSIZE * 8); + } else { + rijndael_cipherInit(&context->cipher, MODE_ECB, NULL); + rijndael_makeKey(&context->key, DIR_ENCRYPT, RANDOM_KEYSIZE*8, data); + } } /* * Create a psuedorandom output stream of 'blockcount' blocks using a CTR-mode * cipher or similar. The 128-bit counter is supplied in the in-out parmeter * 'ctr.' The output stream goes to 'd_out.' 'blockcount' RANDOM_BLOCKSIZE * bytes are generated. */ void -randomdev_keystream(struct randomdev_key *context, uint128_t *ctr, +randomdev_keystream(union randomdev_key *context, uint128_t *ctr, void *d_out, u_int blockcount) { u_int i; - for (i = 0; i < blockcount; i++) { - /*- - * FS&K - r = r|E(K,C) - * - C = C + 1 + if (random_chachamode) { + uint128_t lectr; + + /* + * Chacha always encodes and increments the counter little + * endian. So on BE machines, we must provide a swapped + * counter to chacha, and swap the output too. */ - rijndael_blockEncrypt(&context->cipher, &context->key, - (void *)ctr, RANDOM_BLOCKSIZE * 8, d_out); - d_out = (char *)d_out + RANDOM_BLOCKSIZE; - uint128_increment(ctr); + le128enc(&lectr, *ctr); + + chacha_ivsetup(&context->chacha, NULL, (const void *)&lectr); + chacha_encrypt_bytes(&context->chacha, NULL, d_out, + RANDOM_BLOCKSIZE * blockcount); + + /* + * Decode Chacha-updated LE counter to native endian and store + * it back in the caller's in-out parameter. + */ + chacha_ctrsave(&context->chacha, (void *)&lectr); + *ctr = le128dec(&lectr); + } else { + for (i = 0; i < blockcount; i++) { + /*- + * FS&K - r = r|E(K,C) + * - C = C + 1 + */ + rijndael_blockEncrypt(&context->cipher, &context->key, + (void *)ctr, RANDOM_BLOCKSIZE * 8, d_out); + d_out = (char *)d_out + RANDOM_BLOCKSIZE; + uint128_increment(ctr); + } } +} + +/* + * Fetch a pointer to the relevant key material and its size. + * + * This API is expected to only be used only for reseeding, where the + * endianness does not matter; the goal is to simply incorporate the key + * material into the hash iterator that will produce key'. + * + * Do not expect the buffer pointed to by this API to match the exact + * endianness, etc, as the key material that was supplied to + * randomdev_encrypt_init(). + */ +void +randomdev_getkey(union randomdev_key *context, const void **keyp, size_t *szp) +{ + + if (!random_chachamode) { + *keyp = &context->key.keyMaterial; + *szp = context->key.keyLen / 8; + return; + } + + /* Chacha20 mode */ + *keyp = (const void *)&context->chacha.input[4]; + + /* Sanity check keysize */ + if (context->chacha.input[0] == U8TO32_LITTLE(sigma) && + context->chacha.input[1] == U8TO32_LITTLE(&sigma[4]) && + context->chacha.input[2] == U8TO32_LITTLE(&sigma[8]) && + context->chacha.input[3] == U8TO32_LITTLE(&sigma[12])) { + *szp = 32; + return; + } + +#if 0 + /* + * Included for the sake of completeness; as-implemented, Fortuna + * doesn't need or use 128-bit Chacha20. + */ + if (context->chacha->input[0] == U8TO32_LITTLE(tau) && + context->chacha->input[1] == U8TO32_LITTLE(&tau[4]) && + context->chacha->input[2] == U8TO32_LITTLE(&tau[8]) && + context->chacha->input[3] == U8TO32_LITTLE(&tau[12])) { + *szp = 16; + return; + } +#endif + +#ifdef _KERNEL + panic("%s: Invalid chacha20 keysize: %16D\n", __func__, + (void *)context->chacha.input, " "); +#else + raise(SIGKILL); +#endif } Index: head/sys/dev/random/hash.h =================================================================== --- head/sys/dev/random/hash.h (revision 344912) +++ head/sys/dev/random/hash.h (revision 344913) @@ -1,59 +1,67 @@ /*- * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef SYS_DEV_RANDOM_HASH_H_INCLUDED #define SYS_DEV_RANDOM_HASH_H_INCLUDED +#include #include /* Keys are formed from cipher blocks */ #define RANDOM_KEYSIZE 32 /* (in bytes) == 256 bits */ #define RANDOM_KEYSIZE_WORDS (RANDOM_KEYSIZE/sizeof(uint32_t)) #define RANDOM_BLOCKSIZE 16 /* (in bytes) == 128 bits */ #define RANDOM_BLOCKSIZE_WORDS (RANDOM_BLOCKSIZE/sizeof(uint32_t)) #define RANDOM_KEYS_PER_BLOCK (RANDOM_KEYSIZE/RANDOM_BLOCKSIZE) /* The size of the zero block portion used to form H_d(m) */ #define RANDOM_ZERO_BLOCKSIZE 64 /* (in bytes) == 512 zero bits */ struct randomdev_hash { SHA256_CTX sha; }; -struct randomdev_key { - keyInstance key; /* Key schedule */ - cipherInstance cipher; /* Rijndael internal */ +union randomdev_key { + struct { + keyInstance key; /* Key schedule */ + cipherInstance cipher; /* Rijndael internal */ + }; + struct chacha_ctx chacha; }; +extern bool fortuna_chachamode; + void randomdev_hash_init(struct randomdev_hash *); void randomdev_hash_iterate(struct randomdev_hash *, const void *, size_t); void randomdev_hash_finish(struct randomdev_hash *, void *); -void randomdev_encrypt_init(struct randomdev_key *, const void *); -void randomdev_keystream(struct randomdev_key *context, uint128_t *, void *, u_int); + +void randomdev_encrypt_init(union randomdev_key *, const void *); +void randomdev_keystream(union randomdev_key *context, uint128_t *, void *, u_int); +void randomdev_getkey(union randomdev_key *, const void **, size_t *); #endif /* SYS_DEV_RANDOM_HASH_H_INCLUDED */ Index: head/sys/dev/random/uint128.h =================================================================== --- head/sys/dev/random/uint128.h (revision 344912) +++ head/sys/dev/random/uint128.h (revision 344913) @@ -1,76 +1,114 @@ /*- * Copyright (c) 2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef SYS_DEV_RANDOM_UINT128_H_INCLUDED #define SYS_DEV_RANDOM_UINT128_H_INCLUDED +#include + /* This whole thing is a crock :-( * * Everyone knows you always need the __uint128_t types! */ #ifdef __SIZEOF_INT128__ #define USE_REAL_UINT128_T #endif #ifdef USE_REAL_UINT128_T typedef __uint128_t uint128_t; #define UINT128_ZERO 0ULL #else typedef struct { /* Ignore endianness */ uint64_t u128t_word0; uint64_t u128t_word1; } uint128_t; static const uint128_t very_long_zero = {0UL,0UL}; #define UINT128_ZERO very_long_zero #endif static __inline void uint128_increment(uint128_t *big_uintp) { #ifdef USE_REAL_UINT128_T (*big_uintp)++; #else big_uintp->u128t_word0++; if (big_uintp->u128t_word0 == 0UL) big_uintp->u128t_word1++; #endif } +static __inline bool +uint128_equals(uint128_t a, uint128_t b) +{ +#ifdef USE_REAL_UINT128_T + return (a == b); +#else + return (a.u128t_word0 == b.u128t_word0 && + a.u128t_word1 == b.u128t_word1); +#endif +} + static __inline int uint128_is_zero(uint128_t big_uint) { + return (uint128_equals(big_uint, UINT128_ZERO)); +} + +static __inline uint128_t +le128dec(const void *pp) +{ + const uint8_t *p = pp; + #ifdef USE_REAL_UINT128_T - return (big_uint == UINT128_ZERO); + return (((uint128_t)le64dec(p + 8) << 64) | le64dec(p)); #else - return (big_uint.u128t_word0 == 0UL && big_uint.u128t_word1 == 0UL); + return ((uint128_t){ + .u128t_word0 = le64dec(p), + .u128t_word1 = le64dec(p + 8), + }); +#endif +} + +static __inline void +le128enc(void *pp, uint128_t u) +{ + uint8_t *p = pp; + +#ifdef USE_REAL_UINT128_T + le64enc(p, (uint64_t)(u & UINT64_MAX)); + le64enc(p + 8, (uint64_t)(u >> 64)); +#else + le64enc(p, u.u128t_word0); + le64enc(p + 8, u.u128t_word1); #endif } #endif /* SYS_DEV_RANDOM_UINT128_H_INCLUDED */