Squashed commit of the following: commit 219a904fea95c74016229b6f4436d4f09de1bfd0 Author: Evan Hunt <each@isc.org> Date: Mon Jun 2 12:20:54 2014 -0700 [rt35942] style commit 90bc77185e9798af4595989abb8698efef8c70d7 Author: Mukund Sivaraman <muks@isc.org> Date: Mon Jun 2 18:01:30 2014 +0530 Return p-value=0 when prerequisite (monobit) fails commit 5594669728f1181a447616f60b835e4a043d1b21 Author: Mukund Sivaraman <muks@isc.org> Date: Mon Jun 2 17:44:25 2014 +0530 Print proportion of test sequences passing too commit 9e94b67a4114651224a8285f7c4a7fb03907f376 Author: Mukund Sivaraman <muks@isc.org> Date: Mon Jun 2 17:34:03 2014 +0530 Check uniform distribution of p-values commit acf911b32dd84ac1c30c57d8937cfeb6b3ff972f Author: Mukund Sivaraman <muks@isc.org> Date: Mon Jun 2 17:17:39 2014 +0530 Check proportion of sequences passing a test commit 7289eb441fc4ec623364ad882e22b240ba8da308 Author: Mukund Sivaraman <muks@isc.org> Date: Mon Jun 2 04:33:37 2014 +0530 Refactor common setup code into random_test() No behavioral change is made. commit 51feef3e08c233d34a6b8b9d25a72d43110b4eed Author: Mukund Sivaraman <muks@isc.org> Date: Sun Jun 1 17:31:57 2014 +0530 Fix binary rank computation commit 0ea3c03dea353f309d13c38e26aa0abbffdcff2b Author: Mukund Sivaraman <muks@isc.org> Date: Tue May 27 06:01:10 2014 +0530 Add binary matrix rank RNG test commit eb4e7c53540ac97436d94714d30084907eeff01a Author: Mukund Sivaraman <muks@isc.org> Date: Mon May 26 15:45:31 2014 +0530 Add function to find rank of a binary matrix commit 1292a06e0e09ebd37d4ecf5337814951dcacc4a4 Author: Evan Hunt <each@isc.org> Date: Thu May 29 16:21:51 2014 -0700 [rt35942] style; check whether we need libm for exp() commit c19788e5a89235e937a5aedf2ebea50f33406609 Author: Evan Hunt <each@isc.org> Date: Thu May 29 15:31:19 2014 -0700 [rt35942] incidental spelling error fixed commit c833326ad0df21e2a8b35958e85ccc0a692e38be Author: Mukund Sivaraman <muks@isc.org> Date: Thu May 29 11:34:37 2014 +0530 Revert "Add function to find rank of a binary matrix" This reverts commit 21b2f230e17f7fc638f81d9a34bcb148b0c4a6fb. This test will be added in RT#36125. commit cf786a533d34fdcd9e1c5650356e56d33e93a29f Author: Mukund Sivaraman <muks@isc.org> Date: Thu May 29 11:33:18 2014 +0530 Revert "Add binary matrix rank RNG test" This reverts commit dd843b9ca84fa9af80ec39631152f82778f0b97c. This test will be added in RT#36125. commit dd843b9ca84fa9af80ec39631152f82778f0b97c Author: Mukund Sivaraman <muks@isc.org> Date: Tue May 27 06:01:10 2014 +0530 Add binary matrix rank RNG test commit 21b2f230e17f7fc638f81d9a34bcb148b0c4a6fb Author: Mukund Sivaraman <muks@isc.org> Date: Mon May 26 15:45:31 2014 +0530 Add function to find rank of a binary matrix commit 313c30088d6ba933bde3abb920f2a6d16b9b77e1 Author: Mukund Sivaraman <muks@isc.org> Date: Mon May 26 13:38:44 2014 +0530 Add block frequency random test commit 0d279c60ed3eabe52cf3e1435bf14ec62752536f Author: Mukund Sivaraman <muks@isc.org> Date: Mon May 26 13:04:03 2014 +0530 Add preconditions from NIST spec commit 7a6c5f2ce5078814d5cf0fea30596e58171174c1 Author: Mukund Sivaraman <muks@isc.org> Date: Mon May 26 12:51:03 2014 +0530 Add functions to use in RNG tests commit 8c5cb5594f904f6669cdffaa364f799b4a2c6b58 Author: Mukund Sivaraman <muks@isc.org> Date: Thu May 22 00:26:10 2014 +0530 Add runs RNG test commit 4882f078cc2596c0911066ffb783e4dd145a63ec Author: Mukund Sivaraman <muks@isc.org> Date: Wed May 21 23:58:20 2014 +0530 Pre-compute bitcounts LUT commit 896db3809fba2d9884a4a3a2fa847a73e007ad7f Author: Mukund Sivaraman <muks@isc.org> Date: Wed May 21 23:30:23 2014 +0530 Fix the bit value being checked (this shouldn't affect the test) commit b932cbb5dae39eb819db29cf9490fb51d59b7c56 Author: Mukund Sivaraman <muks@isc.org> Date: Wed May 21 19:35:12 2014 +0530 Add monobits RNG test commit 7bef19fd8b095aa567a975ef5c97d5812162d92e Author: Mukund Sivaraman <muks@isc.org> Date: Wed May 21 16:53:02 2014 +0530 Add API documentation commit 54483f7feb64b5646dd1da45b1fd396e7d04b926 Author: Mukund Sivaraman <muks@isc.org> Date: Wed May 21 16:39:03 2014 +0530 Rename isc_rngctx_t to isc_rng_t commit 7c5031b53555137a82c6b6218cd4dd5e95acf94d Author: Evan Hunt <each@isc.org> Date: Tue May 20 23:29:53 2014 -0700 [rt35942] use attach/detach with isc_rngctx_t commit 8aabae5e09888e6af651ed27bd6b4e9f76334d55 Author: Mukund Sivaraman <muks@isc.org> Date: Tue May 20 18:32:42 2014 +0530 Move RNG from dispatch.c to libisc commit e6d4ad4f389998b91d46e95e258cf420cb21d977 Author: Mukund Sivaraman <muks@isc.org> Date: Mon May 12 19:16:27 2014 +0530 Replace old arc4random with new ChaCha implementation from OpenBSD
420 lines
10 KiB
C
420 lines
10 KiB
C
/*
|
|
* Copyright (C) 2004, 2005, 2007, 2009, 2013, 2014 Internet Systems Consortium, Inc. ("ISC")
|
|
* Copyright (C) 1999-2003 Internet Software Consortium.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
/*%
|
|
* ChaCha based random number generator derived from OpenBSD.
|
|
*
|
|
* The original copyright follows:
|
|
* Copyright (c) 1996, David Mazieres <dm@uun.org>
|
|
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
|
|
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <time.h> /* Required for time(). */
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <isc/magic.h>
|
|
#include <isc/mutex.h>
|
|
#include <isc/once.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/entropy.h>
|
|
#include <isc/random.h>
|
|
#include <isc/string.h>
|
|
#include <isc/util.h>
|
|
|
|
#define RNG_MAGIC ISC_MAGIC('R', 'N', 'G', 'x')
|
|
#define VALID_RNG(r) ISC_MAGIC_VALID(r, RNG_MAGIC)
|
|
|
|
#define KEYSTREAM_ONLY
|
|
#include "chacha_private.h"
|
|
|
|
#define CHACHA_KEYSIZE 32
|
|
#define CHACHA_IVSIZE 8
|
|
#define CHACHA_BLOCKSIZE 64
|
|
#define CHACHA_BUFFERSIZE (16 * CHACHA_BLOCKSIZE)
|
|
|
|
/* ChaCha RNG state */
|
|
struct isc_rng {
|
|
unsigned int magic;
|
|
isc_mem_t *mctx;
|
|
chacha_ctx cpctx;
|
|
isc_uint8_t buffer[CHACHA_BUFFERSIZE];
|
|
size_t have;
|
|
unsigned int references;
|
|
int count;
|
|
isc_entropy_t *entropy; /*%< entropy source */
|
|
isc_mutex_t lock;
|
|
};
|
|
|
|
static isc_once_t once = ISC_ONCE_INIT;
|
|
|
|
static void
|
|
initialize_rand(void) {
|
|
#ifndef HAVE_ARC4RANDOM
|
|
unsigned int pid = getpid();
|
|
|
|
/*
|
|
* The low bits of pid generally change faster.
|
|
* Xor them with the high bits of time which change slowly.
|
|
*/
|
|
pid = ((pid << 16) & 0xffff0000) | ((pid >> 16) & 0xffff);
|
|
|
|
srand((unsigned)time(NULL) ^ pid);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
initialize(void) {
|
|
RUNTIME_CHECK(isc_once_do(&once, initialize_rand) == ISC_R_SUCCESS);
|
|
}
|
|
|
|
void
|
|
isc_random_seed(isc_uint32_t seed) {
|
|
initialize();
|
|
|
|
#ifndef HAVE_ARC4RANDOM
|
|
srand(seed);
|
|
#elif defined(HAVE_ARC4RANDOM_ADDRANDOM)
|
|
arc4random_addrandom((u_char *) &seed, sizeof(isc_uint32_t));
|
|
#else
|
|
/*
|
|
* If arcrandom() is available and no corresponding seeding
|
|
* function arc4random_addrandom() is available, no seeding is
|
|
* done on such platforms (e.g., OpenBSD 5.5). This is because
|
|
* the OS itself is supposed to seed the RNG and it is assumed
|
|
* that no explicit seeding is required.
|
|
*/
|
|
#endif
|
|
}
|
|
|
|
void
|
|
isc_random_get(isc_uint32_t *val) {
|
|
REQUIRE(val != NULL);
|
|
|
|
initialize();
|
|
|
|
#ifndef HAVE_ARC4RANDOM
|
|
/*
|
|
* rand()'s lower bits are not random.
|
|
* rand()'s upper bit is zero.
|
|
*/
|
|
#if RAND_MAX >= 0xfffff
|
|
/* We have at least 20 bits. Use lower 16 excluding lower most 4 */
|
|
*val = ((rand() >> 4) & 0xffff) | ((rand() << 12) & 0xffff0000);
|
|
#elif RAND_MAX >= 0x7fff
|
|
/* We have at least 15 bits. Use lower 10/11 excluding lower most 4 */
|
|
*val = ((rand() >> 4) & 0x000007ff) | ((rand() << 7) & 0x003ff800) |
|
|
((rand() << 18) & 0xffc00000);
|
|
#else
|
|
#error RAND_MAX is too small
|
|
#endif
|
|
#else
|
|
*val = arc4random();
|
|
#endif
|
|
}
|
|
|
|
isc_uint32_t
|
|
isc_random_jitter(isc_uint32_t max, isc_uint32_t jitter) {
|
|
isc_uint32_t rnd;
|
|
|
|
REQUIRE(jitter < max || (jitter == 0 && max == 0));
|
|
|
|
if (jitter == 0)
|
|
return (max);
|
|
|
|
isc_random_get(&rnd);
|
|
return (max - rnd % jitter);
|
|
}
|
|
|
|
static void
|
|
chacha_reinit(isc_rng_t *rng, isc_uint8_t *buffer, size_t n) {
|
|
REQUIRE(rng != NULL);
|
|
|
|
if (n < CHACHA_KEYSIZE + CHACHA_IVSIZE)
|
|
return;
|
|
|
|
chacha_keysetup(&rng->cpctx, buffer, CHACHA_KEYSIZE * 8, 0);
|
|
chacha_ivsetup(&rng->cpctx, buffer + CHACHA_KEYSIZE);
|
|
}
|
|
|
|
isc_result_t
|
|
isc_rng_create(isc_mem_t *mctx, isc_entropy_t *entropy, isc_rng_t **rngp) {
|
|
union {
|
|
unsigned char rnd[128];
|
|
isc_uint32_t rnd32[32];
|
|
} rnd;
|
|
isc_result_t result;
|
|
isc_rng_t *rng;
|
|
|
|
REQUIRE(mctx != NULL);
|
|
REQUIRE(rngp != NULL && *rngp == NULL);
|
|
|
|
if (entropy != NULL) {
|
|
/*
|
|
* We accept any quality of random data to avoid blocking.
|
|
*/
|
|
result = isc_entropy_getdata(entropy, rnd.rnd,
|
|
sizeof(rnd), NULL, 0);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
} else {
|
|
int i;
|
|
for (i = 0; i < 32; i++)
|
|
isc_random_get(&rnd.rnd32[i]);
|
|
}
|
|
|
|
rng = isc_mem_get(mctx, sizeof(*rng));
|
|
if (rng == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
chacha_reinit(rng, rnd.rnd, sizeof(rnd.rnd));
|
|
|
|
rng->have = 0;
|
|
memset(rng->buffer, 0, CHACHA_BUFFERSIZE);
|
|
|
|
/* Create lock */
|
|
result = isc_mutex_init(&rng->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_mem_put(mctx, rng, sizeof(*rng));
|
|
return (result);
|
|
}
|
|
|
|
/* Attach to memory context */
|
|
rng->mctx = NULL;
|
|
isc_mem_attach(mctx, &rng->mctx);
|
|
|
|
/* Local non-algorithm initializations. */
|
|
rng->count = 0;
|
|
rng->entropy = entropy; /* don't have to attach */
|
|
rng->references = 1;
|
|
rng->magic = RNG_MAGIC;
|
|
|
|
*rngp = rng;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
void
|
|
isc_rng_attach(isc_rng_t *source, isc_rng_t **targetp) {
|
|
REQUIRE(VALID_RNG(source));
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
|
|
LOCK(&source->lock);
|
|
source->references++;
|
|
UNLOCK(&source->lock);
|
|
|
|
*targetp = (isc_rng_t *)source;
|
|
}
|
|
|
|
static void
|
|
destroy(isc_rng_t **rngp) {
|
|
isc_rng_t *rng = *rngp;
|
|
|
|
REQUIRE(VALID_RNG(rng));
|
|
|
|
isc_mutex_destroy(&rng->lock);
|
|
rng->magic = 0;
|
|
isc_mem_putanddetach(&rng->mctx, rng, sizeof(isc_rng_t));
|
|
*rngp = NULL;
|
|
}
|
|
|
|
void
|
|
isc_rng_detach(isc_rng_t **rngp) {
|
|
isc_rng_t *rng = *rngp;
|
|
isc_boolean_t dest = ISC_FALSE;
|
|
|
|
REQUIRE(VALID_RNG(rng));
|
|
|
|
LOCK(&rng->lock);
|
|
|
|
INSIST(rng->references > 0);
|
|
rng->references--;
|
|
if (rng->references == 0)
|
|
dest = ISC_TRUE;
|
|
|
|
if (dest)
|
|
destroy(rngp);
|
|
else {
|
|
UNLOCK(&rng->lock);
|
|
*rngp = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
chacha_rekey(isc_rng_t *rng, u_char *dat, size_t datlen) {
|
|
REQUIRE(VALID_RNG(rng));
|
|
|
|
#ifndef KEYSTREAM_ONLY
|
|
memset(rng->buffer, 0, CHACHA_BUFFERSIZE);
|
|
#endif
|
|
|
|
/* Fill buffer with the keystream. */
|
|
chacha_encrypt_bytes(&rng->cpctx, rng->buffer, rng->buffer,
|
|
CHACHA_BUFFERSIZE);
|
|
|
|
/* Mix in optional user provided data. */
|
|
if (dat != NULL) {
|
|
size_t i, m;
|
|
|
|
m = ISC_MIN(datlen, CHACHA_KEYSIZE + CHACHA_IVSIZE);
|
|
for (i = 0; i < m; i++)
|
|
rng->buffer[i] ^= dat[i];
|
|
}
|
|
|
|
/* Immediately reinit for backtracking resistance. */
|
|
chacha_reinit(rng, rng->buffer,
|
|
CHACHA_KEYSIZE + CHACHA_IVSIZE);
|
|
memset(rng->buffer, 0, CHACHA_KEYSIZE + CHACHA_IVSIZE);
|
|
rng->have = CHACHA_BUFFERSIZE - CHACHA_KEYSIZE - CHACHA_IVSIZE;
|
|
}
|
|
|
|
static inline isc_uint16_t
|
|
chacha_getuint16(isc_rng_t *rng) {
|
|
isc_uint16_t val;
|
|
|
|
REQUIRE(VALID_RNG(rng));
|
|
|
|
if (rng->have < sizeof(val))
|
|
chacha_rekey(rng, NULL, 0);
|
|
|
|
memcpy(&val, rng->buffer + CHACHA_BUFFERSIZE - rng->have,
|
|
sizeof(val));
|
|
/* Clear the copied region. */
|
|
memset(rng->buffer + CHACHA_BUFFERSIZE - rng->have,
|
|
0, sizeof(val));
|
|
rng->have -= sizeof(val);
|
|
|
|
return (val);
|
|
}
|
|
|
|
static void
|
|
chacha_stir(isc_rng_t *rng) {
|
|
union {
|
|
unsigned char rnd[128];
|
|
isc_uint32_t rnd32[32];
|
|
} rnd;
|
|
isc_result_t result;
|
|
|
|
REQUIRE(VALID_RNG(rng));
|
|
|
|
if (rng->entropy != NULL) {
|
|
/*
|
|
* We accept any quality of random data to avoid blocking.
|
|
*/
|
|
result = isc_entropy_getdata(rng->entropy, rnd.rnd,
|
|
sizeof(rnd), NULL, 0);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
} else {
|
|
int i;
|
|
for (i = 0; i < 32; i++)
|
|
isc_random_get(&rnd.rnd32[i]);
|
|
}
|
|
|
|
chacha_rekey(rng, rnd.rnd, sizeof(rnd.rnd));
|
|
|
|
/*
|
|
* The OpenBSD implementation explicit_bzero()s the random seed
|
|
* rnd.rnd at this point, but it may not be required here. This
|
|
* memset() may also be optimized away by the compiler as
|
|
* rnd.rnd is not used further.
|
|
*/
|
|
memset(rnd.rnd, 0, sizeof(rnd.rnd));
|
|
|
|
/* Invalidate the buffer too. */
|
|
rng->have = 0;
|
|
memset(rng->buffer, 0, CHACHA_BUFFERSIZE);
|
|
|
|
/*
|
|
* Derived from OpenBSD's implementation. The rationale is not clear,
|
|
* but should be conservative enough in safety, and reasonably large
|
|
* for efficiency.
|
|
*/
|
|
rng->count = 1600000;
|
|
}
|
|
|
|
isc_uint16_t
|
|
isc_rng_random(isc_rng_t *rng) {
|
|
isc_uint16_t result;
|
|
|
|
REQUIRE(VALID_RNG(rng));
|
|
|
|
LOCK(&rng->lock);
|
|
|
|
rng->count -= sizeof(isc_uint16_t);
|
|
if (rng->count <= 0)
|
|
chacha_stir(rng);
|
|
result = chacha_getuint16(rng);
|
|
|
|
UNLOCK(&rng->lock);
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_uint16_t
|
|
isc_rng_uniformrandom(isc_rng_t *rng, isc_uint16_t upper_bound) {
|
|
isc_uint16_t min, r;
|
|
|
|
REQUIRE(VALID_RNG(rng));
|
|
|
|
if (upper_bound < 2)
|
|
return (0);
|
|
|
|
/*
|
|
* Ensure the range of random numbers [min, 0xffff] be a multiple of
|
|
* upper_bound and contain at least a half of the 16 bit range.
|
|
*/
|
|
|
|
if (upper_bound > 0x8000)
|
|
min = 1 + ~upper_bound; /* 0x8000 - upper_bound */
|
|
else
|
|
min = (isc_uint16_t)(0x10000 % (isc_uint32_t)upper_bound);
|
|
|
|
/*
|
|
* This could theoretically loop forever but each retry has
|
|
* p > 0.5 (worst case, usually far better) of selecting a
|
|
* number inside the range we need, so it should rarely need
|
|
* to re-roll.
|
|
*/
|
|
for (;;) {
|
|
r = isc_rng_random(rng);
|
|
if (r >= min)
|
|
break;
|
|
}
|
|
|
|
return (r % upper_bound);
|
|
}
|