diff options
| author | Mark Murray <markm@FreeBSD.org> | 2017-04-16 09:11:02 +0000 |
|---|---|---|
| committer | Mark Murray <markm@FreeBSD.org> | 2017-04-16 09:11:02 +0000 |
| commit | 150890b0c677625d2a6ca0aec375e33814e60679 (patch) | |
| tree | 4f2ecde4a6266d6342bc723e5d00076ac6ec2b61 /sys/libkern | |
| parent | c6fd01fbbbf09a3b57e4ba79c4db2c1bf00165b0 (diff) | |
Notes
Diffstat (limited to 'sys/libkern')
| -rw-r--r-- | sys/libkern/arc4random.c | 246 |
1 files changed, 128 insertions, 118 deletions
diff --git a/sys/libkern/arc4random.c b/sys/libkern/arc4random.c index ccda6ad5bd50..ad4c6b057e4a 100644 --- a/sys/libkern/arc4random.c +++ b/sys/libkern/arc4random.c @@ -1,11 +1,28 @@ /*- - * THE BEER-WARE LICENSE + * Copyright (c) 2017 The FreeBSD Foundation + * All rights reserved. * - * <dan@FreeBSD.ORG> wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you - * think this stuff is worth it, you can buy me a beer in return. + * 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. * - * Dan Moschuk */ #include <sys/cdefs.h> @@ -14,173 +31,159 @@ __FBSDID("$FreeBSD$"); #include <sys/types.h> #include <sys/param.h> #include <sys/kernel.h> -#include <sys/random.h> #include <sys/libkern.h> +#include <sys/linker.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> -#include <sys/time.h> +#include <sys/random.h> #include <sys/smp.h> -#include <sys/malloc.h> +#include <sys/time.h> + +#include <crypto/chacha20/chacha.h> -#define ARC4_RESEED_BYTES 65536 -#define ARC4_RESEED_SECONDS 300 -#define ARC4_KEYBYTES 256 +#define CHACHA20_RESEED_BYTES 65536 +#define CHACHA20_RESEED_SECONDS 300 +#define CHACHA20_KEYBYTES 32 +#define CHACHA20_BUFFER_SIZE 64 + +CTASSERT(CHACHA20_KEYBYTES*8 >= CHACHA_MINKEYLEN); int arc4rand_iniseed_state = ARC4_ENTR_NONE; -MALLOC_DEFINE(M_ARC4RANDOM, "arc4random", "arc4random structures"); +MALLOC_DEFINE(M_CHACHA20RANDOM, "chacha20random", "chacha20random structures"); -struct arc4_s { +struct chacha20_s { struct mtx mtx; - u_int8_t i, j; - int numruns; - u_int8_t sbox[256]; + int numbytes; + int first_time_done; time_t t_reseed; - + u_int8_t m_buffer[CHACHA20_BUFFER_SIZE]; + struct chacha_ctx ctx; } __aligned(CACHE_LINE_SIZE); -static struct arc4_s *arc4inst = NULL; +static struct chacha20_s *chacha20inst = NULL; -#define ARC4_FOREACH(_arc4) \ - for (_arc4 = &arc4inst[0]; _arc4 <= &arc4inst[mp_maxid]; _arc4++) - -static u_int8_t arc4_randbyte(struct arc4_s *arc4); - -static __inline void -arc4_swap(u_int8_t *a, u_int8_t *b) -{ - u_int8_t c; - - c = *a; - *a = *b; - *b = c; -} +#define CHACHA20_FOREACH(_chacha20) \ + for (_chacha20 = &chacha20inst[0]; \ + _chacha20 <= &chacha20inst[mp_maxid]; \ + _chacha20++) /* - * Stir our S-box. + * Mix up the current context. */ static void -arc4_randomstir(struct arc4_s* arc4) +chacha20_randomstir(struct chacha20_s* chacha20) { - u_int8_t key[ARC4_KEYBYTES]; - int n; struct timeval tv_now; + size_t n, size; + u_int8_t key[CHACHA20_KEYBYTES], *data; + caddr_t keyfile; /* - * XXX: FIX!! This isn't brilliant. Need more confidence. - * This returns zero entropy before random(4) is seeded. + * This is making the best of what may be an insecure + * Situation. If the loader(8) did not have an entropy + * stash from the previous shutdown to load, then we will + * be improperly seeded. The answer is to make sure there + * is an entropy stash at shutdown time. */ - (void)read_random(key, ARC4_KEYBYTES); - getmicrouptime(&tv_now); - mtx_lock(&arc4->mtx); - for (n = 0; n < 256; n++) { - arc4->j = (arc4->j + arc4->sbox[n] + key[n]) % 256; - arc4_swap(&arc4->sbox[n], &arc4->sbox[arc4->j]); + (void)read_random(key, CHACHA20_KEYBYTES); + if (!chacha20->first_time_done) { + keyfile = preload_search_by_type(RANDOM_CACHED_BOOT_ENTROPY_MODULE); + if (keyfile != NULL) { + data = preload_fetch_addr(keyfile); + size = MIN(preload_fetch_size(keyfile), CHACHA20_KEYBYTES); + for (n = 0; n < size; n++) + key[n] ^= data[n]; + explicit_bzero(data, size); + if (bootverbose) + printf("arc4random: read %zu bytes from preloaded cache\n", size); + } else + printf("arc4random: no preloaded entropy cache\n"); + chacha20->first_time_done = 1; } - arc4->i = arc4->j = 0; + getmicrouptime(&tv_now); + mtx_lock(&chacha20->mtx); + chacha_keysetup(&chacha20->ctx, key, CHACHA20_KEYBYTES*8); + chacha_ivsetup(&chacha20->ctx, (u_char *)&tv_now.tv_sec, (u_char *)&tv_now.tv_usec); /* Reset for next reseed cycle. */ - arc4->t_reseed = tv_now.tv_sec + ARC4_RESEED_SECONDS; - arc4->numruns = 0; - /* - * Throw away the first N words of output, as suggested in the - * paper "Weaknesses in the Key Scheduling Algorithm of RC4" - * by Fluher, Mantin, and Shamir. (N = 768 in our case.) - * - * http://dl.acm.org/citation.cfm?id=646557.694759 - */ - for (n = 0; n < 768*4; n++) - arc4_randbyte(arc4); - - mtx_unlock(&arc4->mtx); + chacha20->t_reseed = tv_now.tv_sec + CHACHA20_RESEED_SECONDS; + chacha20->numbytes = 0; + mtx_unlock(&chacha20->mtx); } /* - * Initialize our S-box to its beginning defaults. + * Initialize the contexts. */ static void -arc4_init(void) +chacha20_init(void) { - struct arc4_s *arc4; - int n; - - arc4inst = malloc((mp_maxid + 1) * sizeof(struct arc4_s), - M_ARC4RANDOM, M_NOWAIT | M_ZERO); - KASSERT(arc4inst != NULL, ("arc4_init: memory allocation error")); + struct chacha20_s *chacha20; - ARC4_FOREACH(arc4) { - mtx_init(&arc4->mtx, "arc4_mtx", NULL, MTX_DEF); + chacha20inst = malloc((mp_maxid + 1) * sizeof(struct chacha20_s), + M_CHACHA20RANDOM, M_NOWAIT | M_ZERO); + KASSERT(chacha20inst != NULL, ("chacha20_init: memory allocation error")); - arc4->i = arc4->j = 0; - for (n = 0; n < 256; n++) - arc4->sbox[n] = (u_int8_t) n; - - arc4->t_reseed = -1; - arc4->numruns = 0; + CHACHA20_FOREACH(chacha20) { + mtx_init(&chacha20->mtx, "chacha20_mtx", NULL, MTX_DEF); + chacha20->t_reseed = -1; + chacha20->numbytes = 0; + chacha20->first_time_done = 0; + explicit_bzero(chacha20->m_buffer, CHACHA20_BUFFER_SIZE); + explicit_bzero(&chacha20->ctx, sizeof(chacha20->ctx)); } } -SYSINIT(arc4, SI_SUB_LOCK, SI_ORDER_ANY, arc4_init, NULL); +SYSINIT(chacha20, SI_SUB_LOCK, SI_ORDER_ANY, chacha20_init, NULL); static void -arc4_uninit(void) +chacha20_uninit(void) { - struct arc4_s *arc4; - - ARC4_FOREACH(arc4) { - mtx_destroy(&arc4->mtx); - } + struct chacha20_s *chacha20; - free(arc4inst, M_ARC4RANDOM); + CHACHA20_FOREACH(chacha20) + mtx_destroy(&chacha20->mtx); + free(chacha20inst, M_CHACHA20RANDOM); } - -SYSUNINIT(arc4, SI_SUB_LOCK, SI_ORDER_ANY, arc4_uninit, NULL); +SYSUNINIT(chacha20, SI_SUB_LOCK, SI_ORDER_ANY, chacha20_uninit, NULL); /* - * Generate a random byte. - */ -static u_int8_t -arc4_randbyte(struct arc4_s *arc4) -{ - u_int8_t arc4_t; - - arc4->i = (arc4->i + 1) % 256; - arc4->j = (arc4->j + arc4->sbox[arc4->i]) % 256; - - arc4_swap(&arc4->sbox[arc4->i], &arc4->sbox[arc4->j]); - - arc4_t = (arc4->sbox[arc4->i] + arc4->sbox[arc4->j]) % 256; - return arc4->sbox[arc4_t]; -} - -/* * MPSAFE */ void arc4rand(void *ptr, u_int len, int reseed) { - u_char *p; + struct chacha20_s *chacha20; struct timeval tv; - struct arc4_s *arc4; + u_int length; + u_int8_t *p; - if (reseed || atomic_cmpset_int(&arc4rand_iniseed_state, - ARC4_ENTR_HAVE, ARC4_ENTR_SEED)) { - ARC4_FOREACH(arc4) - arc4_randomstir(arc4); - } + if (reseed || atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_HAVE, ARC4_ENTR_SEED)) + CHACHA20_FOREACH(chacha20) + chacha20_randomstir(chacha20); - arc4 = &arc4inst[curcpu]; + chacha20 = &chacha20inst[curcpu]; getmicrouptime(&tv); - if ((arc4->numruns > ARC4_RESEED_BYTES) || - (tv.tv_sec > arc4->t_reseed)) - arc4_randomstir(arc4); + /* We may get unlucky and be migrated off this CPU, but that is expected to be infrequent */ + if ((chacha20->numbytes > CHACHA20_RESEED_BYTES) || (tv.tv_sec > chacha20->t_reseed)) + chacha20_randomstir(chacha20); - mtx_lock(&arc4->mtx); - arc4->numruns += len; + mtx_lock(&chacha20->mtx); p = ptr; - while (len--) - *p++ = arc4_randbyte(arc4); - mtx_unlock(&arc4->mtx); + while (len) { + length = MIN(CHACHA20_BUFFER_SIZE, len); + chacha_encrypt_bytes(&chacha20->ctx, chacha20->m_buffer, p, length); + p += length; + len -= length; + chacha20->numbytes += length; + if (chacha20->numbytes > CHACHA20_RESEED_BYTES) { + mtx_unlock(&chacha20->mtx); + chacha20_randomstir(chacha20); + mtx_lock(&chacha20->mtx); + } + } + mtx_unlock(&chacha20->mtx); } uint32_t @@ -188,6 +191,13 @@ arc4random(void) { uint32_t ret; - arc4rand(&ret, sizeof ret, 0); + arc4rand(&ret, sizeof(ret), 0); return ret; } + +void +arc4random_buf(void *ptr, size_t len) +{ + + arc4rand(ptr, len, 0); +} |
