Index: head/sys/dev/random/fortuna.c =================================================================== --- head/sys/dev/random/fortuna.c (revision 349137) +++ head/sys/dev/random/fortuna.c (revision 349138) @@ -1,492 +1,541 @@ /*- * 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 */ 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 void random_fortuna_read(uint8_t *, size_t); static bool random_fortuna_seeded(void); static bool random_fortuna_seeded_internal(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); 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_internal(); 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); 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. + * + * If Chacha20 is used, output size is unrestricted. If AES-CTR is used, + * output size MUST be <= 1MB and a multiple of RANDOM_BLOCKSIZE. The + * reasoning for this is discussed in FS&K 9.4; the significant distinction + * between the two ciphers is that AES has a *block* size of 128 bits while + * Chacha has a *block* size of 512 bits. */ static __inline void -random_fortuna_genrandom(uint8_t *buf, u_int bytecount) +random_fortuna_genrandom(uint8_t *buf, size_t bytecount) { - uint8_t temp[RANDOM_BLOCKSIZE * RANDOM_KEYS_PER_BLOCK]; - u_int blockcount; + uint8_t newkey[RANDOM_KEYSIZE]; RANDOM_RESEED_ASSERT_LOCK_OWNED(); + /*- - * FS&K - assert(n < 2^20 (== 1 MB) + * FS&K - assert(n < 2^20 (== 1 MB)) when 128-bit block cipher is used * - 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)); + KASSERT(random_chachamode || bytecount <= RANDOM_FORTUNA_MAX_READ, + ("%s: invalid large read request: %zu bytes", __func__, + bytecount)); + + /* + * This is where FS&K would invoke GenerateBlocks(). GenerateBlocks() + * doesn't make a lot of sense or have much value if we use bytecount + * for the API (which is useful for ciphers that do not require + * block-sized output, like Chacha20). + * + * Just invoke our PRF abstraction directly, which is responsible for + * updating fs_counter ('C'). + */ + randomdev_keystream(&fortuna_state.fs_key, &fortuna_state.fs_counter, + buf, bytecount); + randomdev_keystream(&fortuna_state.fs_key, &fortuna_state.fs_counter, + newkey, sizeof(newkey)); + randomdev_encrypt_init(&fortuna_state.fs_key, newkey); + explicit_bzero(newkey, sizeof(newkey)); } /*- * 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, but do * not block initial seeding (fs_lasttime == 0). */ || (__predict_true(fortuna_state.fs_lasttime != 0) && 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! + * + * The supplied buf MAY not be a multiple of RANDOM_BLOCKSIZE in size; it is + * the responsibility of the algorithm to accommodate partial block reads, if a + * block output mode is used. */ void -random_fortuna_read(uint8_t *buf, u_int bytecount) +random_fortuna_read(uint8_t *buf, size_t bytecount) { + uint8_t remainder_buf[RANDOM_BLOCKSIZE]; + size_t read_directly_len, read_chunk; - KASSERT((bytecount % RANDOM_BLOCKSIZE) == 0, ("%s(): bytecount (= %d) must be a multiple of %d", __func__, bytecount, RANDOM_BLOCKSIZE )); + /* + * The underlying AES generator expects multiples of RANDOM_BLOCKSIZE. + */ + if (random_chachamode) + read_directly_len = bytecount; + else + read_directly_len = rounddown(bytecount, RANDOM_BLOCKSIZE); + RANDOM_RESEED_LOCK(); - random_fortuna_genrandom(buf, bytecount); + KASSERT(!uint128_is_zero(fortuna_state.fs_counter), ("FS&K: C != 0")); + + while (read_directly_len > 0) { + /* + * 128-bit block ciphers like AES must be re-keyed at 1MB + * intervals to avoid unacceptable statistical differentiation + * from true random data. + * + * 512-bit block ciphers like Chacha20 do not have this + * problem. (FS&K 9.4) + */ + if (random_chachamode) + read_chunk = read_directly_len; + else + read_chunk = MIN(read_directly_len, + RANDOM_FORTUNA_MAX_READ); + + /* + * For now, we hold the global Fortuna mutex, so yield + * periodically to provide vague availability to other lock + * users. PAGE_SIZE is chosen to match existing behavior. + */ + read_chunk = MIN(read_chunk, PAGE_SIZE); + + random_fortuna_genrandom(buf, read_chunk); + buf += read_chunk; + read_directly_len -= read_chunk; + bytecount -= read_chunk; + + /* Perform the actual yield. */ + if (read_directly_len != 0) { + RANDOM_RESEED_UNLOCK(); + RANDOM_RESEED_LOCK(); + } + } + + if (bytecount > 0) + random_fortuna_genrandom(remainder_buf, sizeof(remainder_buf)); + RANDOM_RESEED_UNLOCK(); + + if (bytecount > 0) { + memcpy(buf, remainder_buf, bytecount); + explicit_bzero(remainder_buf, sizeof(remainder_buf)); + } } #ifdef _KERNEL static bool block_seeded_status = false; SYSCTL_BOOL(_kern_random, OID_AUTO, block_seeded_status, CTLFLAG_RWTUN, &block_seeded_status, 0, "If non-zero, pretend Fortuna is in an unseeded state. By setting " "this as a tunable, boot can be tested as if the random device is " "unavailable."); #endif static bool random_fortuna_seeded_internal(void) { return (!uint128_is_zero(fortuna_state.fs_counter)); } static bool random_fortuna_seeded(void) { #ifdef _KERNEL if (block_seeded_status) return (false); #endif if (__predict_true(random_fortuna_seeded_internal())) return (true); /* * Maybe we have enough entropy in the zeroth pool but just haven't * kicked the initial seed step. Do so now. */ random_fortuna_pre_read(); return (random_fortuna_seeded_internal()); } Index: head/sys/dev/random/hash.c =================================================================== --- head/sys/dev/random/hash.c (revision 349137) +++ head/sys/dev/random/hash.c (revision 349138) @@ -1,226 +1,247 @@ /*- * 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 #include #include #define KASSERT(x, y) assert(x) #define CTASSERT(x) _Static_assert(x, "CTASSERT " #x) #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(union randomdev_key *context, const void *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 + * Create a psuedorandom output stream of 'bytecount' bytes 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. + * 'ctr.' The output stream goes to 'd_out.' + * + * If AES is used, 'bytecount' is guaranteed to be a multiple of + * RANDOM_BLOCKSIZE. */ void randomdev_keystream(union randomdev_key *context, uint128_t *ctr, - void *d_out, u_int blockcount) + void *d_out, size_t bytecount) { - u_int i; + size_t i, blockcount, read_chunk; 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. */ le128enc(&lectr, *ctr); chacha_ivsetup(&context->chacha, NULL, (const void *)&lectr); - chacha_encrypt_bytes(&context->chacha, NULL, d_out, - RANDOM_BLOCKSIZE * blockcount); + while (bytecount > 0) { + /* + * We are limited by the chacha_encrypt_bytes API to + * u32 bytes per chunk. + */ + read_chunk = MIN(bytecount, + rounddown((size_t)UINT32_MAX, CHACHA_BLOCKLEN)); + chacha_encrypt_bytes(&context->chacha, NULL, d_out, + read_chunk); + + d_out = (char *)d_out + read_chunk; + bytecount -= read_chunk; + } + /* * 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); + + explicit_bzero(&lectr, sizeof(lectr)); } else { + KASSERT(bytecount % RANDOM_BLOCKSIZE == 0, + ("%s: AES mode invalid bytecount, not a multiple of native " + "block size", __func__)); + + blockcount = bytecount / RANDOM_BLOCKSIZE; 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 349137) +++ head/sys/dev/random/hash.h (revision 349138) @@ -1,67 +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; }; union randomdev_key { struct { keyInstance key; /* Key schedule */ cipherInstance cipher; /* Rijndael internal */ }; struct chacha_ctx chacha; }; extern bool random_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(union randomdev_key *, const void *); -void randomdev_keystream(union randomdev_key *context, uint128_t *, void *, u_int); +void randomdev_keystream(union randomdev_key *context, uint128_t *, void *, size_t); void randomdev_getkey(union randomdev_key *, const void **, size_t *); #endif /* SYS_DEV_RANDOM_HASH_H_INCLUDED */ Index: head/sys/dev/random/other_algorithm.c =================================================================== --- head/sys/dev/random/other_algorithm.c (revision 349137) +++ head/sys/dev/random/other_algorithm.c (revision 349138) @@ -1,227 +1,227 @@ /*- * Copyright (c) 2015-2018 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 is a skeleton for folks who wish to build a loadable module * containing an alternative entropy-processing algorithm for random(4). * * The functions below should be completed with the appropriate code, * and the nearby fortuna.c may be consulted for examples of working code. * * The author is willing to provide reasonable help to those wishing to * write such a module for themselves. Please use the markm@ FreeBSD * email address, and ensure that you are developing this on a suitably * supported branch (This is currently 12-CURRENT, and may be no * older than 12-STABLE in the future). */ #include __FBSDID("$FreeBSD$"); #include #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #else /* !_KERNEL */ #include #include #include #include #include #include #include "unit_test.h" #include #include #include #include #include #include #endif /* _KERNEL */ static void random_other_pre_read(void); -static void random_other_read(uint8_t *, u_int); +static void random_other_read(uint8_t *, size_t); static bool random_other_seeded(void); static void random_other_process_event(struct harvest_event *); static void random_other_init_alg(void *); static void random_other_deinit_alg(void *); /* * RANDOM_OTHER_NPOOLS is used when reading hardware random * number sources to ensure that each pool gets one read sample * per loop iteration. Fortuna has 32 (0-31). */ #define RANDOM_OTHER_NPOOLS 1 struct random_algorithm random_alg_context = { .ra_ident = "other", .ra_init_alg = random_other_init_alg, .ra_deinit_alg = random_other_deinit_alg, .ra_pre_read = random_other_pre_read, .ra_read = random_other_read, .ra_seeded = random_other_seeded, .ra_event_processor = random_other_process_event, .ra_poolcount = RANDOM_OTHER_NPOOLS, }; /* Use a mutex to protect your reseed variables? */ static mtx_t other_mtx; /* * void random_other_init_alg(void *unused __unused) * * Do algorithm-specific initialisation here. */ void random_other_init_alg(void *unused __unused) { RANDOM_RESEED_INIT_LOCK(); /* * Do set-up work here! */ } /* * void random_other_deinit_alg(void *unused __unused) * * Do algorithm-specific deinitialisation here. */ static void random_other_deinit_alg(void *unused __unused) { /* * Do tear-down work here! */ RANDOM_RESEED_DEINIT_LOCK(); } /* * void random_other_pre_read(void) * * Do any pre-read preparation you need to. This will be called * before >=1 calls to random_other_read() corresponding to one * read(2). * * This routine will be called periodically while the generator is * still blocked and a read is being attempted, giving you an * opportunity to unblock. */ static void random_other_pre_read(void) { RANDOM_RESEED_LOCK(); /* * Do pre-read housekeeping work here! * You may use this as a chance to unblock the generator. */ RANDOM_RESEED_UNLOCK(); } /* - * void random_other_read(uint8_t *buf, u_int count) + * void random_other_read(uint8_t *buf, size_t count) * * Generate bytes of output into <*buf>. - * You may use the fact that will be a multiple of + * You may NOT use the fact that will be a multiple of * RANDOM_BLOCKSIZE for optimization purposes. * * This function will always be called with your generator * unblocked and ready. If you are not ready to generate * output here, then feel free to KASSERT() or panic(). */ static void -random_other_read(uint8_t *buf, u_int count) +random_other_read(uint8_t *buf, size_t count) { RANDOM_RESEED_LOCK(); /* * Do random-number generation work here! */ RANDOM_RESEED_UNLOCK(); } /* * bool random_other_seeded(void) * * Return true if your generator is ready to generate * output, and false otherwise. */ static bool random_other_seeded(void) { bool seeded = false; /* * Find out if your generator is seeded here! */ return (seeded); } /* * void random_other_process_event(struct harvest_event *event) * * Process one stochastic event <*event> into your entropy * processor. * * The structure of the event may change, so it is easier to * just grab the whole thing into your accumulation system. * You may pick-and-choose bits, but please don't complain * when/if these change. */ static void random_other_process_event(struct harvest_event *event) { RANDOM_RESEED_LOCK(); /* * Do entropy accumulation work here! * You may use this as a chance to unblock the generator. */ RANDOM_RESEED_UNLOCK(); } Index: head/sys/dev/random/randomdev.c =================================================================== --- head/sys/dev/random/randomdev.c (revision 349137) +++ head/sys/dev/random/randomdev.c (revision 349138) @@ -1,496 +1,503 @@ /*- * Copyright (c) 2017 Oliver Pinter * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RANDOM_UNIT 0 #if defined(RANDOM_LOADABLE) #define READ_RANDOM_UIO _read_random_uio #define READ_RANDOM _read_random #define IS_RANDOM_SEEDED _is_random_seeded static int READ_RANDOM_UIO(struct uio *, bool); static void READ_RANDOM(void *, u_int); static bool IS_RANDOM_SEEDED(void); #else #define READ_RANDOM_UIO read_random_uio #define READ_RANDOM read_random #define IS_RANDOM_SEEDED is_random_seeded #endif static d_read_t randomdev_read; static d_write_t randomdev_write; static d_poll_t randomdev_poll; static d_ioctl_t randomdev_ioctl; static struct cdevsw random_cdevsw = { .d_name = "random", .d_version = D_VERSION, .d_read = randomdev_read, .d_write = randomdev_write, .d_poll = randomdev_poll, .d_ioctl = randomdev_ioctl, }; /* For use with make_dev(9)/destroy_dev(9). */ static struct cdev *random_dev; static void random_alg_context_ra_init_alg(void *data) { p_random_alg_context = &random_alg_context; p_random_alg_context->ra_init_alg(data); #if defined(RANDOM_LOADABLE) random_infra_init(READ_RANDOM_UIO, READ_RANDOM, IS_RANDOM_SEEDED); #endif } static void random_alg_context_ra_deinit_alg(void *data) { #if defined(RANDOM_LOADABLE) random_infra_uninit(); #endif p_random_alg_context->ra_deinit_alg(data); p_random_alg_context = NULL; } SYSINIT(random_device, SI_SUB_RANDOM, SI_ORDER_THIRD, random_alg_context_ra_init_alg, NULL); SYSUNINIT(random_device, SI_SUB_RANDOM, SI_ORDER_THIRD, random_alg_context_ra_deinit_alg, NULL); static struct selinfo rsel; /* * This is the read uio(9) interface for random(4). */ /* ARGSUSED */ static int randomdev_read(struct cdev *dev __unused, struct uio *uio, int flags) { return (READ_RANDOM_UIO(uio, (flags & O_NONBLOCK) != 0)); } /* * If the random device is not seeded, blocks until it is seeded. * * Returns zero when the random device is seeded. * * If the 'interruptible' parameter is true, and the device is unseeded, this * routine may be interrupted. If interrupted, it will return either ERESTART * or EINTR. */ #define SEEDWAIT_INTERRUPTIBLE true #define SEEDWAIT_UNINTERRUPTIBLE false static int randomdev_wait_until_seeded(bool interruptible) { int error, spamcount, slpflags; slpflags = interruptible ? PCATCH : 0; error = 0; spamcount = 0; while (!p_random_alg_context->ra_seeded()) { /* keep tapping away at the pre-read until we seed/unblock. */ p_random_alg_context->ra_pre_read(); /* Only bother the console every 10 seconds or so */ if (spamcount == 0) printf("random: %s unblock wait\n", __func__); spamcount = (spamcount + 1) % 100; error = tsleep(&random_alg_context, slpflags, "randseed", hz / 10); if (error == ERESTART || error == EINTR) { KASSERT(interruptible, ("unexpected wake of non-interruptible sleep")); break; } /* Squash tsleep timeout condition */ if (error == EWOULDBLOCK) error = 0; KASSERT(error == 0, ("unexpected tsleep error %d", error)); } return (error); } int READ_RANDOM_UIO(struct uio *uio, bool nonblock) { - uint8_t *random_buf; - int error; - ssize_t read_len, total_read, c; /* 16 MiB takes about 0.08 s CPU time on my 2017 AMD Zen CPU */ #define SIGCHK_PERIOD (16 * 1024 * 1024) const size_t sigchk_period = SIGCHK_PERIOD; - CTASSERT(SIGCHK_PERIOD % PAGE_SIZE == 0); #undef SIGCHK_PERIOD - random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); + uint8_t *random_buf; + size_t total_read, read_len; + ssize_t bufsize; + int error; + + + KASSERT(uio->uio_rw == UIO_READ, ("%s: bogus write", __func__)); + KASSERT(uio->uio_resid >= 0, ("%s: bogus negative resid", __func__)); + p_random_alg_context->ra_pre_read(); error = 0; /* (Un)Blocking logic */ if (!p_random_alg_context->ra_seeded()) { if (nonblock) error = EWOULDBLOCK; else error = randomdev_wait_until_seeded( SEEDWAIT_INTERRUPTIBLE); } - if (error == 0) { - read_rate_increment((uio->uio_resid + sizeof(uint32_t))/sizeof(uint32_t)); - total_read = 0; - while (uio->uio_resid && !error) { - read_len = uio->uio_resid; - /* - * Belt-and-braces. - * Round up the read length to a crypto block size multiple, - * which is what the underlying generator is expecting. - * See the random_buf size requirements in the Fortuna code. - */ - read_len = roundup(read_len, RANDOM_BLOCKSIZE); - /* Work in chunks page-sized or less */ - read_len = MIN(read_len, PAGE_SIZE); - p_random_alg_context->ra_read(random_buf, read_len); - c = MIN(uio->uio_resid, read_len); - /* - * uiomove() may yield the CPU before each 'c' bytes - * (up to PAGE_SIZE) are copied out. - */ - error = uiomove(random_buf, c, uio); - total_read += c; - /* - * Poll for signals every few MBs to avoid very long - * uninterruptible syscalls. - */ - if (error == 0 && uio->uio_resid != 0 && - total_read % sigchk_period == 0) { - error = tsleep_sbt(&random_alg_context, PCATCH, - "randrd", SBT_1NS, 0, C_HARDCLOCK); - /* Squash tsleep timeout condition */ - if (error == EWOULDBLOCK) - error = 0; - } + if (error != 0) + return (error); + + read_rate_increment(howmany(uio->uio_resid + 1, sizeof(uint32_t))); + total_read = 0; + + /* Easy to deal with the trivial 0 byte case. */ + if (__predict_false(uio->uio_resid == 0)) + return (0); + + /* + * If memory is plentiful, use maximally sized requests to avoid + * per-call algorithm overhead. But fall back to a single page + * allocation if the full request isn't immediately available. + */ + bufsize = MIN(sigchk_period, (size_t)uio->uio_resid); + random_buf = malloc(bufsize, M_ENTROPY, M_NOWAIT); + if (random_buf == NULL) { + bufsize = PAGE_SIZE; + random_buf = malloc(bufsize, M_ENTROPY, M_WAITOK); + } + + error = 0; + while (uio->uio_resid > 0 && error == 0) { + read_len = MIN((size_t)uio->uio_resid, bufsize); + + p_random_alg_context->ra_read(random_buf, read_len); + + /* + * uiomove() may yield the CPU before each 'read_len' bytes (up + * to bufsize) are copied out. + */ + error = uiomove(random_buf, read_len, uio); + total_read += read_len; + + /* + * Poll for signals every few MBs to avoid very long + * uninterruptible syscalls. + */ + if (error == 0 && uio->uio_resid != 0 && + total_read % sigchk_period == 0) { + error = tsleep_sbt(&random_alg_context, PCATCH, + "randrd", SBT_1NS, 0, C_HARDCLOCK); + /* Squash tsleep timeout condition */ + if (error == EWOULDBLOCK) + error = 0; } - if (error == ERESTART || error == EINTR) - error = 0; } + + /* + * Short reads due to signal interrupt should not indicate error. + * Instead, the uio will reflect that the read was shorter than + * requested. + */ + if (error == ERESTART || error == EINTR) + error = 0; + + explicit_bzero(random_buf, bufsize); free(random_buf, M_ENTROPY); return (error); } /*- * Kernel API version of read_random(). This is similar to read_random_uio(), * except it doesn't interface with uio(9). It cannot assumed that random_buf * is a multiple of RANDOM_BLOCKSIZE bytes. * * If the tunable 'kern.random.initial_seeding.bypass_before_seeding' is set * non-zero, silently fail to emit random data (matching the pre-r346250 * behavior). If read_random is called prior to seeding and bypassed because * of this tunable, the condition is reported in the read-only sysctl * 'kern.random.initial_seeding.read_random_bypassed_before_seeding'. */ void READ_RANDOM(void *random_buf, u_int len) { - u_int read_directly_len; KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__)); p_random_alg_context->ra_pre_read(); if (len == 0) return; /* (Un)Blocking logic */ if (__predict_false(!p_random_alg_context->ra_seeded())) { if (random_bypass_before_seeding) { if (!read_random_bypassed_before_seeding) { if (!random_bypass_disable_warnings) printf("read_random: WARNING: bypassing" " request for random data because " "the random device is not yet " "seeded and the knob " "'bypass_before_seeding' was " "enabled.\n"); read_random_bypassed_before_seeding = true; } /* Avoid potentially leaking stack garbage */ memset(random_buf, 0, len); return; } (void)randomdev_wait_until_seeded(SEEDWAIT_UNINTERRUPTIBLE); } read_rate_increment(roundup2(len, sizeof(uint32_t))); - /* - * The underlying generator expects multiples of - * RANDOM_BLOCKSIZE. - */ - read_directly_len = rounddown(len, RANDOM_BLOCKSIZE); - if (read_directly_len > 0) - p_random_alg_context->ra_read(random_buf, read_directly_len); - if (read_directly_len < len) { - uint8_t remainder_buf[RANDOM_BLOCKSIZE]; - - p_random_alg_context->ra_read(remainder_buf, - sizeof(remainder_buf)); - memcpy((char *)random_buf + read_directly_len, remainder_buf, - len - read_directly_len); - - explicit_bzero(remainder_buf, sizeof(remainder_buf)); - } + p_random_alg_context->ra_read(random_buf, len); } bool IS_RANDOM_SEEDED(void) { return (p_random_alg_context->ra_seeded()); } static __inline void randomdev_accumulate(uint8_t *buf, u_int count) { static u_int destination = 0; static struct harvest_event event; static struct randomdev_hash hash; static uint32_t entropy_data[RANDOM_KEYSIZE_WORDS]; uint32_t timestamp; int i; /* Extra timing here is helpful to scrape scheduler jitter entropy */ randomdev_hash_init(&hash); timestamp = (uint32_t)get_cyclecount(); randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); randomdev_hash_iterate(&hash, buf, count); timestamp = (uint32_t)get_cyclecount(); randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); randomdev_hash_finish(&hash, entropy_data); for (i = 0; i < RANDOM_KEYSIZE_WORDS; 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_source = RANDOM_CACHED; event.he_destination = destination++; /* Harmless cheating */ memcpy(event.he_entropy, entropy_data + i, sizeof(event.he_entropy)); p_random_alg_context->ra_event_processor(&event); } explicit_bzero(&event, sizeof(event)); explicit_bzero(entropy_data, sizeof(entropy_data)); } /* ARGSUSED */ static int randomdev_write(struct cdev *dev __unused, struct uio *uio, int flags __unused) { uint8_t *random_buf; int c, error = 0; ssize_t nbytes; random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); nbytes = uio->uio_resid; while (uio->uio_resid > 0 && error == 0) { c = MIN(uio->uio_resid, PAGE_SIZE); error = uiomove(random_buf, c, uio); if (error) break; randomdev_accumulate(random_buf, c); tsleep(&random_alg_context, 0, "randwr", hz/10); } if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR)) /* Partial write, not error. */ error = 0; free(random_buf, M_ENTROPY); return (error); } /* ARGSUSED */ static int randomdev_poll(struct cdev *dev __unused, int events, struct thread *td __unused) { if (events & (POLLIN | POLLRDNORM)) { if (p_random_alg_context->ra_seeded()) events &= (POLLIN | POLLRDNORM); else selrecord(td, &rsel); } return (events); } /* This will be called by the entropy processor when it seeds itself and becomes secure */ void randomdev_unblock(void) { selwakeuppri(&rsel, PUSER); wakeup(&random_alg_context); printf("random: unblocking device.\n"); /* Do random(9) a favour while we are about it. */ (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE); } /* ARGSUSED */ static int randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused, int flags __unused, struct thread *td __unused) { int error = 0; switch (cmd) { /* Really handled in upper layer */ case FIOASYNC: case FIONBIO: break; default: error = ENOTTY; } return (error); } void random_source_register(struct random_source *rsource) { struct random_sources *rrs; KASSERT(rsource != NULL, ("invalid input to %s", __func__)); rrs = malloc(sizeof(*rrs), M_ENTROPY, M_WAITOK); rrs->rrs_source = rsource; random_harvest_register_source(rsource->rs_source); printf("random: registering fast source %s\n", rsource->rs_ident); LIST_INSERT_HEAD(&source_list, rrs, rrs_entries); } void random_source_deregister(struct random_source *rsource) { struct random_sources *rrs = NULL; KASSERT(rsource != NULL, ("invalid input to %s", __func__)); random_harvest_deregister_source(rsource->rs_source); LIST_FOREACH(rrs, &source_list, rrs_entries) if (rrs->rrs_source == rsource) { LIST_REMOVE(rrs, rrs_entries); break; } if (rrs != NULL) free(rrs, M_ENTROPY); } static int random_source_handler(SYSCTL_HANDLER_ARGS) { struct random_sources *rrs; struct sbuf sbuf; int error, count; sbuf_new_for_sysctl(&sbuf, NULL, 64, req); count = 0; LIST_FOREACH(rrs, &source_list, rrs_entries) { sbuf_cat(&sbuf, (count++ ? ",'" : "'")); sbuf_cat(&sbuf, rrs->rrs_source->rs_ident); sbuf_cat(&sbuf, "'"); } error = sbuf_finish(&sbuf); sbuf_delete(&sbuf); return (error); } SYSCTL_PROC(_kern_random, OID_AUTO, random_sources, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, random_source_handler, "A", "List of active fast entropy sources."); /* ARGSUSED */ static int randomdev_modevent(module_t mod __unused, int type, void *data __unused) { int error = 0; switch (type) { case MOD_LOAD: printf("random: entropy device external interface\n"); random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw, RANDOM_UNIT, NULL, UID_ROOT, GID_WHEEL, 0644, "random"); make_dev_alias(random_dev, "urandom"); /* compatibility */ break; case MOD_UNLOAD: destroy_dev(random_dev); break; case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t randomdev_mod = { "random_device", randomdev_modevent, 0 }; DECLARE_MODULE(random_device, randomdev_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); MODULE_VERSION(random_device, 1); MODULE_DEPEND(random_device, crypto, 1, 1, 1); MODULE_DEPEND(random_device, random_harvestq, 1, 1, 1); Index: head/sys/dev/random/randomdev.h =================================================================== --- head/sys/dev/random/randomdev.h (revision 349137) +++ head/sys/dev/random/randomdev.h (revision 349138) @@ -1,136 +1,136 @@ /*- * 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_RANDOMDEV_H_INCLUDED #define SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED #ifdef _KERNEL /* This header contains only those definitions that are global * and non algorithm-specific for the entropy processor */ #ifdef SYSCTL_DECL /* from sysctl.h */ SYSCTL_DECL(_kern_random); SYSCTL_DECL(_kern_random_initial_seeding); #define RANDOM_CHECK_UINT(name, min, max) \ static int \ random_check_uint_##name(SYSCTL_HANDLER_ARGS) \ { \ if (oidp->oid_arg1 != NULL) { \ if (*(u_int *)(oidp->oid_arg1) <= (min)) \ *(u_int *)(oidp->oid_arg1) = (min); \ else if (*(u_int *)(oidp->oid_arg1) > (max)) \ *(u_int *)(oidp->oid_arg1) = (max); \ } \ return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, \ req)); \ } #endif /* SYSCTL_DECL */ MALLOC_DECLARE(M_ENTROPY); extern bool random_bypass_before_seeding; extern bool read_random_bypassed_before_seeding; extern bool arc4random_bypassed_before_seeding; extern bool random_bypass_disable_warnings; #endif /* _KERNEL */ struct harvest_event; typedef void random_alg_init_t(void *); typedef void random_alg_deinit_t(void *); typedef void random_alg_pre_read_t(void); -typedef void random_alg_read_t(uint8_t *, u_int); +typedef void random_alg_read_t(uint8_t *, size_t); typedef bool random_alg_seeded_t(void); typedef void random_alg_reseed_t(void); typedef void random_alg_eventprocessor_t(struct harvest_event *); typedef u_int random_source_read_t(void *, u_int); /* * Random Algorithm is a processor of randomness for the kernel * and for userland. */ struct random_algorithm { const char *ra_ident; u_int ra_poolcount; void (*ra_init_alg)(void *); void (*ra_deinit_alg)(void *); random_alg_pre_read_t *ra_pre_read; random_alg_read_t *ra_read; random_alg_seeded_t *ra_seeded; random_alg_eventprocessor_t *ra_event_processor; }; extern struct random_algorithm random_alg_context, *p_random_alg_context; #ifdef _KERNEL /* * Random Source is a source of entropy that can provide * specified or approximate amount of entropy immediately * upon request. */ struct random_source { const char *rs_ident; enum random_entropy_source rs_source; random_source_read_t *rs_read; }; struct random_sources { LIST_ENTRY(random_sources) rrs_entries; struct random_source *rrs_source; }; LIST_HEAD(sources_head, random_sources); extern struct sources_head source_list; void random_source_register(struct random_source *); void random_source_deregister(struct random_source *); #if defined(RANDOM_LOADABLE) extern struct sx randomdev_config_lock; #define RANDOM_CONFIG_INIT_LOCK(x) sx_init(&randomdev_config_lock, "configuration change lock") #define RANDOM_CONFIG_X_LOCK(x) sx_xlock(&randomdev_config_lock) #define RANDOM_CONFIG_X_UNLOCK(x) sx_xunlock(&randomdev_config_lock) #define RANDOM_CONFIG_S_LOCK(x) sx_slock(&randomdev_config_lock) #define RANDOM_CONFIG_S_UNLOCK(x) sx_sunlock(&randomdev_config_lock) #define RANDOM_CONFIG_DEINIT_LOCK(x) sx_destroy(&randomdev_config_lock) void random_infra_init(int (*)(struct uio *, bool), void (*)(void *, u_int), bool (*)(void)); void random_infra_uninit(void); #endif #endif /* _KERNEL */ void randomdev_unblock(void); #endif /* SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED */ Index: head/tests/sys/devrandom/uint128_test.c =================================================================== --- head/tests/sys/devrandom/uint128_test.c (revision 349137) +++ head/tests/sys/devrandom/uint128_test.c (revision 349138) @@ -1,225 +1,224 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 Conrad Meyer * 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. * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$"); #include #include #include #include #include #include #include #include #include #include #include #include static void vec_u32_tole128(uint8_t dst[static 16], const uint32_t src[static 4]) { le32enc(dst, src[0]); le32enc(&dst[4], src[1]); le32enc(&dst[8], src[2]); le32enc(&dst[12], src[3]); } static void le128_to_vec_u32(uint32_t dst[static 4], const uint8_t src[static 16]) { dst[0] = le32dec(src); dst[1] = le32dec(&src[4]); dst[2] = le32dec(&src[8]); dst[3] = le32dec(&src[12]); } static void formatu128(char buf[static 52], uint128_t x) { uint8_t le128x[16]; uint32_t vx[4]; size_t sz, i; int rc; le128enc(le128x, x); le128_to_vec_u32(vx, le128x); sz = 52; for (i = 0; i < 4; i++) { rc = snprintf(buf, sz, "0x%x ", vx[i]); ATF_REQUIRE(rc > 0 && (size_t)rc < sz); buf += rc; sz -= rc; } /* Delete last trailing space */ buf[-1] = '\0'; } static void u128_check_equality(uint128_t a, uint128_t b, const char *descr) { char fmtbufa[52], fmtbufb[52]; formatu128(fmtbufa, a); formatu128(fmtbufb, b); ATF_CHECK_MSG(uint128_equals(a, b), "Expected: [%s] != Actual: [%s]: %s", fmtbufa, fmtbufb, descr); } ATF_TC_WITHOUT_HEAD(uint128_inc); ATF_TC_BODY(uint128_inc, tc) { static const struct u128_inc_tc { uint32_t input[4]; uint32_t expected[4]; const char *descr; } tests[] = { { .input = { 0, 0, 0, 0 }, .expected = { 1, 0, 0, 0 }, .descr = "0 -> 1", }, { .input = { 1, 0, 0, 0 }, .expected = { 2, 0, 0, 0 }, .descr = "0 -> 2", }, { .input = { 0xff, 0, 0, 0 }, .expected = { 0x100, 0, 0, 0 }, .descr = "0xff -> 0x100 (byte carry)", }, { .input = { UINT32_MAX, 0, 0, 0 }, .expected = { 0, 1, 0, 0 }, .descr = "2^32 - 1 -> 2^32 (word carry)", }, { .input = { UINT32_MAX, UINT32_MAX, 0, 0 }, .expected = { 0, 0, 1, 0 }, .descr = "2^64 - 1 -> 2^64 (u128t_word0 carry)", }, { .input = { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, .expected = { 0, 0, 0, 1 }, .descr = "2^96 - 1 -> 2^96 (word carry)", }, }; uint8_t inputle[16], expectedle[16]; uint128_t a; size_t i; for (i = 0; i < nitems(tests); i++) { vec_u32_tole128(inputle, tests[i].input); vec_u32_tole128(expectedle, tests[i].expected); a = le128dec(inputle); uint128_increment(&a); u128_check_equality(le128dec(expectedle), a, tests[i].descr); } } /* * Test assumptions about Chacha incrementing counter in the same way as * uint128.h */ ATF_TC_WITHOUT_HEAD(uint128_chacha_ctr); ATF_TC_BODY(uint128_chacha_ctr, tc) { static const struct u128_chacha_tc { uint32_t input[4]; uint32_t expected[4]; const char *descr; } tests[] = { { .input = { 0, 0, 0, 0 }, .expected = { 1, 0, 0, 0 }, .descr = "Single block", }, { .input = { 1, 0, 0, 0 }, .expected = { 2, 0, 0, 0 }, .descr = "0 -> 2", }, { .input = { 0xff, 0, 0, 0 }, .expected = { 0x100, 0, 0, 0 }, .descr = "0xff -> 0x100 (byte carry)", }, { .input = { UINT32_MAX, 0, 0, 0 }, .expected = { 0, 1, 0, 0 }, .descr = "2^32 - 1 -> 2^32 (word carry)", }, { .input = { UINT32_MAX, UINT32_MAX, 0, 0 }, .expected = { 0, 0, 1, 0 }, .descr = "2^64 - 1 -> 2^64 (u128t_word0 carry)", }, { .input = { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, .expected = { 0, 0, 0, 1 }, .descr = "2^96 - 1 -> 2^96 (word carry)", }, }; union randomdev_key context; uint8_t inputle[16], expectedle[16], trash[CHACHA_BLOCKLEN]; uint8_t notrandomkey[RANDOM_KEYSIZE] = { 0 }; uint128_t a; size_t i; random_chachamode = true; randomdev_encrypt_init(&context, notrandomkey); for (i = 0; i < nitems(tests); i++) { vec_u32_tole128(inputle, tests[i].input); vec_u32_tole128(expectedle, tests[i].expected); a = le128dec(inputle); - randomdev_keystream(&context, &a, trash, sizeof(trash) / - RANDOM_BLOCKSIZE); + randomdev_keystream(&context, &a, trash, sizeof(trash)); u128_check_equality(le128dec(expectedle), a, tests[i].descr); } } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, uint128_inc); ATF_TP_ADD_TC(tp, uint128_chacha_ctr); return (atf_no_error()); }