[35942] Update random number generator to ChaCha based (and add tests)
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
This commit is contained in:
6
CHANGES
6
CHANGES
@@ -1,3 +1,9 @@
|
||||
3870. [func] Updated the random number generator used in
|
||||
the resolver to use the updated ChaCha based one
|
||||
(similar to OpenBSD's changes). Also moved the
|
||||
RNG to libisc and added unit tests for it.
|
||||
[RT #35942]
|
||||
|
||||
3869. [doc] Document that in-view zones cannot be used for
|
||||
response policy zones. [RT #35941]
|
||||
|
||||
|
||||
45
configure
vendored
45
configure
vendored
@@ -20269,6 +20269,51 @@ $as_echo "#define ATF_TEST 1" >>confdefs.h
|
||||
STD_CINCLUDES="$STD_CINCLUDES -I$atf/include"
|
||||
ATFBIN="$atf/bin"
|
||||
ATFLIBS="-L$atf/lib -latf-c"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exp in -lm" >&5
|
||||
$as_echo_n "checking for exp in -lm... " >&6; }
|
||||
if ${ac_cv_lib_m_exp+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lm $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char exp ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return exp ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_lib_m_exp=yes
|
||||
else
|
||||
ac_cv_lib_m_exp=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_exp" >&5
|
||||
$as_echo "$ac_cv_lib_m_exp" >&6; }
|
||||
if test "x$ac_cv_lib_m_exp" = xyes; then :
|
||||
libm=yes
|
||||
else
|
||||
libm=no
|
||||
fi
|
||||
|
||||
if test "$libm" = "yes"; then
|
||||
ATFLIBS="$ATFLIBS -lm"
|
||||
fi
|
||||
UNITTESTS=tests
|
||||
fi
|
||||
|
||||
|
||||
@@ -4000,6 +4000,10 @@ if test "$atf" != no; then
|
||||
STD_CINCLUDES="$STD_CINCLUDES -I$atf/include"
|
||||
ATFBIN="$atf/bin"
|
||||
ATFLIBS="-L$atf/lib -latf-c"
|
||||
AC_CHECK_LIB(m, exp, libm=yes, libm=no)
|
||||
if test "$libm" = "yes"; then
|
||||
ATFLIBS="$ATFLIBS -lm"
|
||||
fi
|
||||
UNITTESTS=tests
|
||||
fi
|
||||
AC_SUBST(ATFBIN)
|
||||
|
||||
@@ -98,7 +98,7 @@ DSTSRCS = @DST_EXTRA_SRCS@ @OPENSSLLINKSRCS@ @PKCS11LINKSRCS@ \
|
||||
dst_result.c gssapi_link.c gssapictx.c \
|
||||
hmac_link.c key.c
|
||||
|
||||
GEOIOLINKSRCS = geoip.c
|
||||
GEOIPLINKSRCS = geoip.c
|
||||
|
||||
DNSSRCS = acache.c acl.c adb.c byaddr.c \
|
||||
cache.c callbacks.c clientinfo.c compress.c \
|
||||
|
||||
@@ -57,16 +57,6 @@ typedef ISC_LIST(dispsocket_t) dispsocketlist_t;
|
||||
typedef struct dispportentry dispportentry_t;
|
||||
typedef ISC_LIST(dispportentry_t) dispportlist_t;
|
||||
|
||||
/* ARC4 Random generator state */
|
||||
typedef struct arc4ctx {
|
||||
isc_uint8_t i;
|
||||
isc_uint8_t j;
|
||||
isc_uint8_t s[256];
|
||||
int count;
|
||||
isc_entropy_t *entropy; /*%< entropy source for ARC4 */
|
||||
isc_mutex_t *lock;
|
||||
} arc4ctx_t;
|
||||
|
||||
typedef struct dns_qid {
|
||||
unsigned int magic;
|
||||
unsigned int qid_nbuckets; /*%< hash table size */
|
||||
@@ -90,11 +80,11 @@ struct dns_dispatchmgr {
|
||||
unsigned int state;
|
||||
ISC_LIST(dns_dispatch_t) list;
|
||||
|
||||
/* Locked by arc4_lock. */
|
||||
isc_mutex_t arc4_lock;
|
||||
arc4ctx_t arc4ctx; /*%< ARC4 context for QID */
|
||||
/* Locked by rng_lock. */
|
||||
isc_mutex_t rng_lock;
|
||||
isc_rng_t *rngctx; /*%< RNG context for QID */
|
||||
|
||||
/* locked by buffer lock */
|
||||
/* locked by buffer_lock */
|
||||
dns_qid_t *qid;
|
||||
isc_mutex_t buffer_lock;
|
||||
unsigned int buffers; /*%< allocated buffers */
|
||||
@@ -257,7 +247,7 @@ struct dns_dispatch {
|
||||
unsigned int tcpbuffers; /*%< allocated buffers */
|
||||
dns_tcpmsg_t tcpmsg; /*%< for tcp streams */
|
||||
dns_qid_t *qid;
|
||||
arc4ctx_t arc4ctx; /*%< for QID/UDP port num */
|
||||
isc_rng_t *rngctx; /*%< for QID/UDP port num */
|
||||
dispportlist_t *port_table; /*%< hold ports 'owned' by us */
|
||||
isc_mempool_t *portpool; /*%< port table entries */
|
||||
};
|
||||
@@ -279,8 +269,8 @@ struct dns_dispatch {
|
||||
|
||||
#define DNS_QID(disp) ((disp)->socktype == isc_sockettype_tcp) ? \
|
||||
(disp)->qid : (disp)->mgr->qid
|
||||
#define DISP_ARC4CTX(disp) ((disp)->socktype == isc_sockettype_udp) ? \
|
||||
(&(disp)->arc4ctx) : (&(disp)->mgr->arc4ctx)
|
||||
#define DISP_RNGCTX(disp) ((disp)->socktype == isc_sockettype_udp) ? \
|
||||
((disp)->rngctx) : ((disp)->mgr->rngctx)
|
||||
|
||||
/*%
|
||||
* Locking a query port buffer is a bit tricky. We access the buffer without
|
||||
@@ -433,169 +423,6 @@ request_log(dns_dispatch_t *disp, dns_dispentry_t *resp,
|
||||
}
|
||||
}
|
||||
|
||||
/*%
|
||||
* ARC4 random number generator derived from OpenBSD.
|
||||
* Only dispatch_random() and dispatch_uniformrandom() are expected
|
||||
* to be called from general dispatch routines; the rest of them are subroutines
|
||||
* for these two.
|
||||
*
|
||||
* The original copyright follows:
|
||||
* Copyright (c) 1996, David Mazieres <dm@uun.org>
|
||||
* Copyright (c) 2008, Damien Miller <djm@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.
|
||||
*/
|
||||
static void
|
||||
dispatch_initrandom(arc4ctx_t *actx, isc_entropy_t *entropy,
|
||||
isc_mutex_t *lock)
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < 256; n++)
|
||||
actx->s[n] = n;
|
||||
actx->i = 0;
|
||||
actx->j = 0;
|
||||
actx->count = 0;
|
||||
actx->entropy = entropy; /* don't have to attach */
|
||||
actx->lock = lock;
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_arc4addrandom(arc4ctx_t *actx, unsigned char *dat, int datlen) {
|
||||
int n;
|
||||
isc_uint8_t si;
|
||||
|
||||
actx->i--;
|
||||
for (n = 0; n < 256; n++) {
|
||||
actx->i = (actx->i + 1);
|
||||
si = actx->s[actx->i];
|
||||
actx->j = (actx->j + si + dat[n % datlen]);
|
||||
actx->s[actx->i] = actx->s[actx->j];
|
||||
actx->s[actx->j] = si;
|
||||
}
|
||||
actx->j = actx->i;
|
||||
}
|
||||
|
||||
static inline isc_uint8_t
|
||||
dispatch_arc4get8(arc4ctx_t *actx) {
|
||||
isc_uint8_t si, sj;
|
||||
|
||||
actx->i = (actx->i + 1);
|
||||
si = actx->s[actx->i];
|
||||
actx->j = (actx->j + si);
|
||||
sj = actx->s[actx->j];
|
||||
actx->s[actx->i] = sj;
|
||||
actx->s[actx->j] = si;
|
||||
|
||||
return (actx->s[(si + sj) & 0xff]);
|
||||
}
|
||||
|
||||
static inline isc_uint16_t
|
||||
dispatch_arc4get16(arc4ctx_t *actx) {
|
||||
isc_uint16_t val;
|
||||
|
||||
val = dispatch_arc4get8(actx) << 8;
|
||||
val |= dispatch_arc4get8(actx);
|
||||
|
||||
return (val);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_arc4stir(arc4ctx_t *actx) {
|
||||
int i;
|
||||
union {
|
||||
unsigned char rnd[128];
|
||||
isc_uint32_t rnd32[32];
|
||||
} rnd;
|
||||
isc_result_t result;
|
||||
|
||||
if (actx->entropy != NULL) {
|
||||
/*
|
||||
* We accept any quality of random data to avoid blocking.
|
||||
*/
|
||||
result = isc_entropy_getdata(actx->entropy, rnd.rnd,
|
||||
sizeof(rnd), NULL, 0);
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
} else {
|
||||
for (i = 0; i < 32; i++)
|
||||
isc_random_get(&rnd.rnd32[i]);
|
||||
}
|
||||
dispatch_arc4addrandom(actx, rnd.rnd, sizeof(rnd.rnd));
|
||||
|
||||
/*
|
||||
* Discard early keystream, as per recommendations in:
|
||||
* http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
|
||||
*/
|
||||
for (i = 0; i < 256; i++)
|
||||
(void)dispatch_arc4get8(actx);
|
||||
|
||||
/*
|
||||
* Derived from OpenBSD's implementation. The rationale is not clear,
|
||||
* but should be conservative enough in safety, and reasonably large
|
||||
* for efficiency.
|
||||
*/
|
||||
actx->count = 1600000;
|
||||
}
|
||||
|
||||
static isc_uint16_t
|
||||
dispatch_random(arc4ctx_t *actx) {
|
||||
isc_uint16_t result;
|
||||
|
||||
if (actx->lock != NULL)
|
||||
LOCK(actx->lock);
|
||||
|
||||
actx->count -= sizeof(isc_uint16_t);
|
||||
if (actx->count <= 0)
|
||||
dispatch_arc4stir(actx);
|
||||
result = dispatch_arc4get16(actx);
|
||||
|
||||
if (actx->lock != NULL)
|
||||
UNLOCK(actx->lock);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static isc_uint16_t
|
||||
dispatch_uniformrandom(arc4ctx_t *actx, isc_uint16_t upper_bound) {
|
||||
isc_uint16_t min, r;
|
||||
|
||||
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 = dispatch_random(actx);
|
||||
if (r >= min)
|
||||
break;
|
||||
}
|
||||
|
||||
return (r % upper_bound);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a hash of the destination and message id.
|
||||
*/
|
||||
@@ -898,8 +725,7 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
|
||||
qid = DNS_QID(disp);
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
port = ports[dispatch_uniformrandom(DISP_ARC4CTX(disp),
|
||||
nports)];
|
||||
port = ports[isc_rng_uniformrandom(DISP_RNGCTX(disp), nports)];
|
||||
isc_sockaddr_setport(&localaddr, port);
|
||||
|
||||
LOCK(&qid->lock);
|
||||
@@ -1817,7 +1643,9 @@ destroy_mgr(dns_dispatchmgr_t **mgrp) {
|
||||
DESTROYLOCK(&mgr->lock);
|
||||
mgr->state = 0;
|
||||
|
||||
DESTROYLOCK(&mgr->arc4_lock);
|
||||
if (mgr->rngctx != NULL)
|
||||
isc_rng_detach(&mgr->rngctx);
|
||||
DESTROYLOCK(&mgr->rng_lock);
|
||||
|
||||
isc_mempool_destroy(&mgr->depool);
|
||||
isc_mempool_destroy(&mgr->rpool);
|
||||
@@ -1948,18 +1776,19 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy,
|
||||
|
||||
mgr->blackhole = NULL;
|
||||
mgr->stats = NULL;
|
||||
mgr->rngctx = NULL;
|
||||
|
||||
result = isc_mutex_init(&mgr->lock);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
goto deallocate;
|
||||
|
||||
result = isc_mutex_init(&mgr->arc4_lock);
|
||||
result = isc_mutex_init(&mgr->rng_lock);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
goto kill_lock;
|
||||
|
||||
result = isc_mutex_init(&mgr->buffer_lock);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
goto kill_arc4_lock;
|
||||
goto kill_rng_lock;
|
||||
|
||||
result = isc_mutex_init(&mgr->depool_lock);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
@@ -2054,7 +1883,9 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy,
|
||||
if (entropy != NULL)
|
||||
isc_entropy_attach(entropy, &mgr->entropy);
|
||||
|
||||
dispatch_initrandom(&mgr->arc4ctx, mgr->entropy, &mgr->arc4_lock);
|
||||
result = isc_rng_create(mctx, mgr->entropy, &mgr->rngctx);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
goto kill_dpool;
|
||||
|
||||
*mgrp = mgr;
|
||||
return (ISC_R_SUCCESS);
|
||||
@@ -2077,8 +1908,8 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy,
|
||||
DESTROYLOCK(&mgr->depool_lock);
|
||||
kill_buffer_lock:
|
||||
DESTROYLOCK(&mgr->buffer_lock);
|
||||
kill_arc4_lock:
|
||||
DESTROYLOCK(&mgr->arc4_lock);
|
||||
kill_rng_lock:
|
||||
DESTROYLOCK(&mgr->rng_lock);
|
||||
kill_lock:
|
||||
DESTROYLOCK(&mgr->lock);
|
||||
deallocate:
|
||||
@@ -2583,7 +2414,8 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests,
|
||||
ISC_LIST_INIT(disp->activesockets);
|
||||
ISC_LIST_INIT(disp->inactivesockets);
|
||||
disp->nsockets = 0;
|
||||
dispatch_initrandom(&disp->arc4ctx, mgr->entropy, NULL);
|
||||
disp->rngctx = NULL;
|
||||
isc_rng_attach(mgr->rngctx, &disp->rngctx);
|
||||
disp->port_table = NULL;
|
||||
disp->portpool = NULL;
|
||||
disp->dscp = -1;
|
||||
@@ -2609,6 +2441,8 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests,
|
||||
kill_lock:
|
||||
DESTROYLOCK(&disp->lock);
|
||||
deallocate:
|
||||
if (disp->rngctx != NULL)
|
||||
isc_rng_detach(&disp->rngctx);
|
||||
isc_mempool_put(mgr->dpool, disp);
|
||||
|
||||
return (result);
|
||||
@@ -2659,6 +2493,9 @@ dispatch_free(dns_dispatch_t **dispp) {
|
||||
if (disp->portpool != NULL)
|
||||
isc_mempool_destroy(&disp->portpool);
|
||||
|
||||
if (disp->rngctx != NULL)
|
||||
isc_rng_detach(&disp->rngctx);
|
||||
|
||||
disp->mgr = NULL;
|
||||
DESTROYLOCK(&disp->lock);
|
||||
disp->magic = 0;
|
||||
@@ -2905,9 +2742,8 @@ get_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp,
|
||||
for (i = 0; i < 1024; i++) {
|
||||
in_port_t prt;
|
||||
|
||||
prt = ports[dispatch_uniformrandom(
|
||||
DISP_ARC4CTX(disp),
|
||||
nports)];
|
||||
prt = ports[isc_rng_uniformrandom(DISP_RNGCTX(disp),
|
||||
nports)];
|
||||
isc_sockaddr_setport(&localaddr_bound, prt);
|
||||
result = open_socket(sockmgr, &localaddr_bound,
|
||||
0, &sock, NULL);
|
||||
@@ -3281,7 +3117,7 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
|
||||
* Try somewhat hard to find an unique ID.
|
||||
*/
|
||||
LOCK(&qid->lock);
|
||||
id = (dns_messageid_t)dispatch_random(DISP_ARC4CTX(disp));
|
||||
id = (dns_messageid_t)isc_rng_random(DISP_RNGCTX(disp));
|
||||
ok = ISC_FALSE;
|
||||
i = 0;
|
||||
do {
|
||||
|
||||
@@ -72,6 +72,8 @@ OBJS = @ISC_EXTRA_OBJS@ @ISC_PK11_O@ @ISC_PK11_RESULT_O@ \
|
||||
${UNIXOBJS} ${NLSOBJS} ${THREADOBJS}
|
||||
SYMTBLOBJS = backtrace-emptytbl.@O@
|
||||
|
||||
CHACHASRCS = chacha_private.h
|
||||
|
||||
# Alphabetically
|
||||
SRCS = @ISC_EXTRA_SRCS@ @ISC_PK11_C@ @ISC_PK11_RESULT_C@ \
|
||||
aes.c assertions.c backtrace.c base32.c base64.c bind9.c \
|
||||
@@ -81,7 +83,7 @@ SRCS = @ISC_EXTRA_SRCS@ @ISC_PK11_C@ @ISC_PK11_RESULT_C@ \
|
||||
lex.c lfsr.c lib.c log.c \
|
||||
md5.c mem.c mutexblock.c \
|
||||
netaddr.c netscope.c pool.c ondestroy.c \
|
||||
parseint.c portset.c quota.c radix.c random.c \
|
||||
parseint.c portset.c quota.c radix.c random.c ${CHACHASRCS} \
|
||||
ratelimiter.c refcount.c region.c regex.c result.c rwlock.c \
|
||||
safe.c serial.c sha1.c sha2.c sockaddr.c stats.c string.c \
|
||||
strtoul.c symtab.c task.c taskpool.c timer.c \
|
||||
|
||||
227
lib/isc/chacha_private.h
Normal file
227
lib/isc/chacha_private.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Taken from OpenBSD CVS src/lib/libc/crypt/chacha_private.h on
|
||||
* May 12, 2014.
|
||||
*/
|
||||
|
||||
/*
|
||||
chacha-merged.c version 20080118
|
||||
D. J. Bernstein
|
||||
Public domain.
|
||||
*/
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 input[16]; /* could be compressed */
|
||||
} chacha_ctx;
|
||||
|
||||
#define U8C(v) (v##U)
|
||||
#define U32C(v) (v##U)
|
||||
|
||||
#define U8V(v) ((u8)(v) & U8C(0xFF))
|
||||
#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
|
||||
|
||||
#define ROTL32(v, n) \
|
||||
(U32V((v) << (n)) | ((v) >> (32 - (n))))
|
||||
|
||||
#define U8TO32_LITTLE(p) \
|
||||
(((u32)((p)[0]) ) | \
|
||||
((u32)((p)[1]) << 8) | \
|
||||
((u32)((p)[2]) << 16) | \
|
||||
((u32)((p)[3]) << 24))
|
||||
|
||||
#define U32TO8_LITTLE(p, v) \
|
||||
do { \
|
||||
(p)[0] = U8V((v) ); \
|
||||
(p)[1] = U8V((v) >> 8); \
|
||||
(p)[2] = U8V((v) >> 16); \
|
||||
(p)[3] = U8V((v) >> 24); \
|
||||
} while (0)
|
||||
|
||||
#define ROTATE(v,c) (ROTL32(v,c))
|
||||
#define XOR(v,w) ((v) ^ (w))
|
||||
#define PLUS(v,w) (U32V((v) + (w)))
|
||||
#define PLUSONE(v) (PLUS((v),1))
|
||||
|
||||
#define QUARTERROUND(a,b,c,d) \
|
||||
a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
|
||||
c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
|
||||
a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
|
||||
c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
|
||||
|
||||
static const char sigma[16] = "expand 32-byte k";
|
||||
static const char tau[16] = "expand 16-byte k";
|
||||
|
||||
static void
|
||||
chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ivbits)
|
||||
{
|
||||
const char *constants;
|
||||
|
||||
UNUSED(ivbits);
|
||||
|
||||
x->input[4] = U8TO32_LITTLE(k + 0);
|
||||
x->input[5] = U8TO32_LITTLE(k + 4);
|
||||
x->input[6] = U8TO32_LITTLE(k + 8);
|
||||
x->input[7] = U8TO32_LITTLE(k + 12);
|
||||
if (kbits == 256) { /* recommended */
|
||||
k += 16;
|
||||
constants = sigma;
|
||||
} else { /* kbits == 128 */
|
||||
constants = tau;
|
||||
}
|
||||
x->input[8] = U8TO32_LITTLE(k + 0);
|
||||
x->input[9] = U8TO32_LITTLE(k + 4);
|
||||
x->input[10] = U8TO32_LITTLE(k + 8);
|
||||
x->input[11] = U8TO32_LITTLE(k + 12);
|
||||
x->input[0] = U8TO32_LITTLE(constants + 0);
|
||||
x->input[1] = U8TO32_LITTLE(constants + 4);
|
||||
x->input[2] = U8TO32_LITTLE(constants + 8);
|
||||
x->input[3] = U8TO32_LITTLE(constants + 12);
|
||||
}
|
||||
|
||||
static void
|
||||
chacha_ivsetup(chacha_ctx *x,const u8 *iv)
|
||||
{
|
||||
x->input[12] = 0;
|
||||
x->input[13] = 0;
|
||||
x->input[14] = U8TO32_LITTLE(iv + 0);
|
||||
x->input[15] = U8TO32_LITTLE(iv + 4);
|
||||
}
|
||||
|
||||
static void
|
||||
chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes)
|
||||
{
|
||||
u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||
u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
|
||||
u8 *ctarget = NULL;
|
||||
u8 tmp[64];
|
||||
u_int i;
|
||||
|
||||
if (!bytes) return;
|
||||
|
||||
j0 = x->input[0];
|
||||
j1 = x->input[1];
|
||||
j2 = x->input[2];
|
||||
j3 = x->input[3];
|
||||
j4 = x->input[4];
|
||||
j5 = x->input[5];
|
||||
j6 = x->input[6];
|
||||
j7 = x->input[7];
|
||||
j8 = x->input[8];
|
||||
j9 = x->input[9];
|
||||
j10 = x->input[10];
|
||||
j11 = x->input[11];
|
||||
j12 = x->input[12];
|
||||
j13 = x->input[13];
|
||||
j14 = x->input[14];
|
||||
j15 = x->input[15];
|
||||
|
||||
for (;;) {
|
||||
if (bytes < 64) {
|
||||
for (i = 0;i < bytes;++i) tmp[i] = m[i];
|
||||
m = tmp;
|
||||
ctarget = c;
|
||||
c = tmp;
|
||||
}
|
||||
x0 = j0;
|
||||
x1 = j1;
|
||||
x2 = j2;
|
||||
x3 = j3;
|
||||
x4 = j4;
|
||||
x5 = j5;
|
||||
x6 = j6;
|
||||
x7 = j7;
|
||||
x8 = j8;
|
||||
x9 = j9;
|
||||
x10 = j10;
|
||||
x11 = j11;
|
||||
x12 = j12;
|
||||
x13 = j13;
|
||||
x14 = j14;
|
||||
x15 = j15;
|
||||
for (i = 20;i > 0;i -= 2) {
|
||||
QUARTERROUND( x0, x4, x8,x12)
|
||||
QUARTERROUND( x1, x5, x9,x13)
|
||||
QUARTERROUND( x2, x6,x10,x14)
|
||||
QUARTERROUND( x3, x7,x11,x15)
|
||||
QUARTERROUND( x0, x5,x10,x15)
|
||||
QUARTERROUND( x1, x6,x11,x12)
|
||||
QUARTERROUND( x2, x7, x8,x13)
|
||||
QUARTERROUND( x3, x4, x9,x14)
|
||||
}
|
||||
x0 = PLUS(x0,j0);
|
||||
x1 = PLUS(x1,j1);
|
||||
x2 = PLUS(x2,j2);
|
||||
x3 = PLUS(x3,j3);
|
||||
x4 = PLUS(x4,j4);
|
||||
x5 = PLUS(x5,j5);
|
||||
x6 = PLUS(x6,j6);
|
||||
x7 = PLUS(x7,j7);
|
||||
x8 = PLUS(x8,j8);
|
||||
x9 = PLUS(x9,j9);
|
||||
x10 = PLUS(x10,j10);
|
||||
x11 = PLUS(x11,j11);
|
||||
x12 = PLUS(x12,j12);
|
||||
x13 = PLUS(x13,j13);
|
||||
x14 = PLUS(x14,j14);
|
||||
x15 = PLUS(x15,j15);
|
||||
|
||||
#ifndef KEYSTREAM_ONLY
|
||||
x0 = XOR(x0,U8TO32_LITTLE(m + 0));
|
||||
x1 = XOR(x1,U8TO32_LITTLE(m + 4));
|
||||
x2 = XOR(x2,U8TO32_LITTLE(m + 8));
|
||||
x3 = XOR(x3,U8TO32_LITTLE(m + 12));
|
||||
x4 = XOR(x4,U8TO32_LITTLE(m + 16));
|
||||
x5 = XOR(x5,U8TO32_LITTLE(m + 20));
|
||||
x6 = XOR(x6,U8TO32_LITTLE(m + 24));
|
||||
x7 = XOR(x7,U8TO32_LITTLE(m + 28));
|
||||
x8 = XOR(x8,U8TO32_LITTLE(m + 32));
|
||||
x9 = XOR(x9,U8TO32_LITTLE(m + 36));
|
||||
x10 = XOR(x10,U8TO32_LITTLE(m + 40));
|
||||
x11 = XOR(x11,U8TO32_LITTLE(m + 44));
|
||||
x12 = XOR(x12,U8TO32_LITTLE(m + 48));
|
||||
x13 = XOR(x13,U8TO32_LITTLE(m + 52));
|
||||
x14 = XOR(x14,U8TO32_LITTLE(m + 56));
|
||||
x15 = XOR(x15,U8TO32_LITTLE(m + 60));
|
||||
#endif
|
||||
|
||||
j12 = PLUSONE(j12);
|
||||
if (!j12) {
|
||||
j13 = PLUSONE(j13);
|
||||
/* stopping at 2^70 bytes per nonce is user's responsibility */
|
||||
}
|
||||
|
||||
U32TO8_LITTLE(c + 0,x0);
|
||||
U32TO8_LITTLE(c + 4,x1);
|
||||
U32TO8_LITTLE(c + 8,x2);
|
||||
U32TO8_LITTLE(c + 12,x3);
|
||||
U32TO8_LITTLE(c + 16,x4);
|
||||
U32TO8_LITTLE(c + 20,x5);
|
||||
U32TO8_LITTLE(c + 24,x6);
|
||||
U32TO8_LITTLE(c + 28,x7);
|
||||
U32TO8_LITTLE(c + 32,x8);
|
||||
U32TO8_LITTLE(c + 36,x9);
|
||||
U32TO8_LITTLE(c + 40,x10);
|
||||
U32TO8_LITTLE(c + 44,x11);
|
||||
U32TO8_LITTLE(c + 48,x12);
|
||||
U32TO8_LITTLE(c + 52,x13);
|
||||
U32TO8_LITTLE(c + 56,x14);
|
||||
U32TO8_LITTLE(c + 60,x15);
|
||||
|
||||
if (bytes <= 64) {
|
||||
if (bytes < 64) {
|
||||
for (i = 0;i < bytes;++i) ctarget[i] = c[i];
|
||||
}
|
||||
x->input[12] = j12;
|
||||
x->input[13] = j13;
|
||||
return;
|
||||
}
|
||||
bytes -= 64;
|
||||
c += 64;
|
||||
#ifndef KEYSTREAM_ONLY
|
||||
m += 64;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@
|
||||
|
||||
#include <isc/lang.h>
|
||||
#include <isc/types.h>
|
||||
#include <isc/entropy.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/mutex.h>
|
||||
|
||||
/*! \file isc/random.h
|
||||
* \brief Implements a random state pool which will let the caller return a
|
||||
@@ -35,6 +38,11 @@
|
||||
|
||||
ISC_LANG_BEGINDECLS
|
||||
|
||||
typedef struct isc_rng isc_rng_t;
|
||||
/*%<
|
||||
* Opaque type
|
||||
*/
|
||||
|
||||
void
|
||||
isc_random_seed(isc_uint32_t seed);
|
||||
/*%<
|
||||
@@ -57,6 +65,66 @@ isc_random_jitter(isc_uint32_t max, isc_uint32_t jitter);
|
||||
* This is useful for jittering timer values.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_rng_create(isc_mem_t *mctx, isc_entropy_t *entropy, isc_rng_t **rngp);
|
||||
/*%<
|
||||
* Creates and initializes a pseudo random number generator. The
|
||||
* returned RNG can be used to generate pseudo random numbers.
|
||||
*
|
||||
* The reference count of the returned RNG is set to 1.
|
||||
*
|
||||
* Requires:
|
||||
* \li mctx is a pointer to a valid memory context.
|
||||
* \li entropy is an optional entopy source (can be NULL)
|
||||
* \li rngp != NULL && *rngp == NULL is where a pointer to the RNG is
|
||||
* returned.
|
||||
*
|
||||
* Ensures:
|
||||
*\li If result is ISC_R_SUCCESS:
|
||||
* *rngp points to a valid RNG.
|
||||
*
|
||||
*\li If result is failure:
|
||||
* *rngp does not point to a valid RNG.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS Success
|
||||
*\li #ISC_R_NOMEMORY Resource limit: Out of Memory
|
||||
*/
|
||||
|
||||
void
|
||||
isc_rng_attach(isc_rng_t *source, isc_rng_t **targetp);
|
||||
/*%<
|
||||
* Increments a reference count on the passed RNG.
|
||||
*
|
||||
* Requires:
|
||||
* \li source the RNG struct to attach to (is refcount is incremented)
|
||||
* \li targetp != NULL && *targetp == NULL where a pointer to the
|
||||
* reference incremented RNG is returned.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_rng_detach(isc_rng_t **rngp);
|
||||
/*%<
|
||||
* Decrements a reference count on the passed RNG. If the reference
|
||||
* count reaches 0, the RNG is destroyed.
|
||||
*
|
||||
* Requires:
|
||||
* \li rngp != NULL the RNG struct to decrement reference for
|
||||
*/
|
||||
|
||||
isc_uint16_t
|
||||
isc_rng_random(isc_rng_t *rngctx);
|
||||
/*%<
|
||||
* Returns a pseudo random 16-bit unsigned integer.
|
||||
*/
|
||||
|
||||
isc_uint16_t
|
||||
isc_rng_uniformrandom(isc_rng_t *rngctx, isc_uint16_t upper_bound);
|
||||
/*%<
|
||||
* Returns a uniformly distributed pseudo random 16-bit unsigned
|
||||
* integer.
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
||||
#endif /* ISC_RANDOM_H */
|
||||
|
||||
318
lib/isc/random.c
318
lib/isc/random.c
@@ -14,8 +14,26 @@
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: random.c,v 1.28 2009/07/16 05:52:46 marka Exp $ */
|
||||
/*%
|
||||
* 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 */
|
||||
|
||||
@@ -30,17 +48,43 @@
|
||||
#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)
|
||||
{
|
||||
initialize_rand(void) {
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
unsigned int pid = getpid();
|
||||
|
||||
@@ -55,14 +99,12 @@ initialize_rand(void)
|
||||
}
|
||||
|
||||
static void
|
||||
initialize(void)
|
||||
{
|
||||
initialize(void) {
|
||||
RUNTIME_CHECK(isc_once_do(&once, initialize_rand) == ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
void
|
||||
isc_random_seed(isc_uint32_t seed)
|
||||
{
|
||||
isc_random_seed(isc_uint32_t seed) {
|
||||
initialize();
|
||||
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
@@ -81,8 +123,7 @@ isc_random_seed(isc_uint32_t seed)
|
||||
}
|
||||
|
||||
void
|
||||
isc_random_get(isc_uint32_t *val)
|
||||
{
|
||||
isc_random_get(isc_uint32_t *val) {
|
||||
REQUIRE(val != NULL);
|
||||
|
||||
initialize();
|
||||
@@ -119,3 +160,260 @@ isc_random_jitter(isc_uint32_t max, isc_uint32_t jitter) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -36,14 +36,14 @@ LIBS = @LIBS@ @ATFLIBS@
|
||||
|
||||
OBJS = isctest.@O@
|
||||
SRCS = isctest.c taskpool_test.c socket_test.c hash_test.c \
|
||||
lex_test.c \
|
||||
lex_test.c random_test.c \
|
||||
sockaddr_test.c symtab_test.c task_test.c queue_test.c \
|
||||
parse_test.c pool_test.c regex_test.c socket_test.c \
|
||||
safe_test.c time_test.c aes_test.c
|
||||
|
||||
SUBDIRS =
|
||||
TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \
|
||||
lex_test@EXEEXT@ \
|
||||
lex_test@EXEEXT@ random_test@EXEEXT@ \
|
||||
sockaddr_test@EXEEXT@ symtab_test@EXEEXT@ task_test@EXEEXT@ \
|
||||
queue_test@EXEEXT@ parse_test@EXEEXT@ pool_test@EXEEXT@ \
|
||||
regex_test@EXEEXT@ socket_test@EXEEXT@ safe_test@EXEEXT@ \
|
||||
@@ -75,6 +75,10 @@ queue_test@EXEEXT@: queue_test.@O@ isctest.@O@ ${ISCDEPLIBS}
|
||||
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
|
||||
queue_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS}
|
||||
|
||||
random_test@EXEEXT@: random_test.@O@ isctest.@O@ ${ISCDEPLIBS}
|
||||
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
|
||||
random_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS}
|
||||
|
||||
symtab_test@EXEEXT@: symtab_test.@O@ isctest.@O@ ${ISCDEPLIBS}
|
||||
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
|
||||
symtab_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS}
|
||||
|
||||
663
lib/isc/tests/random_test.c
Normal file
663
lib/isc/tests/random_test.c
Normal file
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <isc/random.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include <atf-c.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
typedef double (pvalue_func_t)(isc_mem_t *mctx,
|
||||
uint16_t *values, size_t length);
|
||||
|
||||
/* igamc(), igam(), etc. were adapted (and cleaned up) from the Cephes
|
||||
* math library:
|
||||
*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1985, 1987, 2000 by Stephen L. Moshier
|
||||
*
|
||||
* The Cephes math library was released into the public domain as part
|
||||
* of netlib.
|
||||
*/
|
||||
|
||||
static double MACHEP = 1.11022302462515654042E-16;
|
||||
static double MAXLOG = 7.09782712893383996843E2;
|
||||
static double big = 4.503599627370496e15;
|
||||
static double biginv = 2.22044604925031308085e-16;
|
||||
|
||||
static double igamc(double a, double x);
|
||||
static double igam(double a, double x);
|
||||
|
||||
static double
|
||||
igamc(double a, double x) {
|
||||
double ans, ax, c, yc, r, t, y, z;
|
||||
double pk, pkm1, pkm2, qk, qkm1, qkm2;
|
||||
|
||||
if ((x <= 0) || (a <= 0))
|
||||
return (1.0);
|
||||
|
||||
if ((x < 1.0) || (x < a))
|
||||
return (1.0 - igam(a, x));
|
||||
|
||||
ax = a * log(x) - x - lgamma(a);
|
||||
if (ax < -MAXLOG) {
|
||||
fprintf(stderr, "igamc: UNDERFLOW, ax=%f\n", ax);
|
||||
return (0.0);
|
||||
}
|
||||
ax = exp(ax);
|
||||
|
||||
/* continued fraction */
|
||||
y = 1.0 - a;
|
||||
z = x + y + 1.0;
|
||||
c = 0.0;
|
||||
pkm2 = 1.0;
|
||||
qkm2 = x;
|
||||
pkm1 = x + 1.0;
|
||||
qkm1 = z * x;
|
||||
ans = pkm1 / qkm1;
|
||||
|
||||
do {
|
||||
c += 1.0;
|
||||
y += 1.0;
|
||||
z += 2.0;
|
||||
yc = y * c;
|
||||
pk = pkm1 * z - pkm2 * yc;
|
||||
qk = qkm1 * z - qkm2 * yc;
|
||||
if (qk != 0) {
|
||||
r = pk / qk;
|
||||
t = fabs((ans - r) / r);
|
||||
ans = r;
|
||||
} else
|
||||
t = 1.0;
|
||||
|
||||
pkm2 = pkm1;
|
||||
pkm1 = pk;
|
||||
qkm2 = qkm1;
|
||||
qkm1 = qk;
|
||||
|
||||
if (fabs(pk) > big) {
|
||||
pkm2 *= biginv;
|
||||
pkm1 *= biginv;
|
||||
qkm2 *= biginv;
|
||||
qkm1 *= biginv;
|
||||
}
|
||||
} while (t > MACHEP);
|
||||
|
||||
return (ans * ax);
|
||||
}
|
||||
|
||||
static double
|
||||
igam(double a, double x) {
|
||||
double ans, ax, c, r;
|
||||
|
||||
if ((x <= 0) || (a <= 0))
|
||||
return (0.0);
|
||||
|
||||
if ((x > 1.0) && (x > a))
|
||||
return (1.0 - igamc(a, x));
|
||||
|
||||
/* Compute x**a * exp(-x) / md_gamma(a) */
|
||||
ax = a * log(x) - x - lgamma(a);
|
||||
if( ax < -MAXLOG ) {
|
||||
fprintf(stderr, "igam: UNDERFLOW, ax=%f\n", ax);
|
||||
return (0.0);
|
||||
}
|
||||
ax = exp(ax);
|
||||
|
||||
/* power series */
|
||||
r = a;
|
||||
c = 1.0;
|
||||
ans = 1.0;
|
||||
|
||||
do {
|
||||
r += 1.0;
|
||||
c *= x / r;
|
||||
ans += c;
|
||||
} while (c / ans > MACHEP);
|
||||
|
||||
return (ans * ax / a);
|
||||
}
|
||||
|
||||
static int8_t scounts_table[65536];
|
||||
static uint8_t bitcounts_table[65536];
|
||||
|
||||
static int8_t
|
||||
scount_calculate(uint16_t n) {
|
||||
int i;
|
||||
int8_t sc;
|
||||
|
||||
sc = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
uint16_t lsb;
|
||||
|
||||
lsb = n & 1;
|
||||
if (lsb != 0)
|
||||
sc += 1;
|
||||
else
|
||||
sc -= 1;
|
||||
|
||||
n >>= 1;
|
||||
}
|
||||
|
||||
return (sc);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
bitcount_calculate(uint16_t n) {
|
||||
int i;
|
||||
uint8_t bc;
|
||||
|
||||
bc = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
uint16_t lsb;
|
||||
|
||||
lsb = n & 1;
|
||||
if (lsb != 0)
|
||||
bc += 1;
|
||||
|
||||
n >>= 1;
|
||||
}
|
||||
|
||||
return (bc);
|
||||
}
|
||||
|
||||
static void
|
||||
tables_init(void) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < 65536; i++) {
|
||||
scounts_table[i] = scount_calculate(i);
|
||||
bitcounts_table[i] = bitcount_calculate(i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The following code for computing Marsaglia's rank is based on the
|
||||
* implementation in cdbinrnk.c from the diehard tests by George
|
||||
* Marsaglia.
|
||||
*
|
||||
* This function destroys (modifies) the data passed in bits.
|
||||
*/
|
||||
static uint32_t
|
||||
matrix_binaryrank(uint32_t *bits, ssize_t rows, ssize_t cols) {
|
||||
ssize_t i, j, k;
|
||||
int rt = 0;
|
||||
uint32_t rank = 0;
|
||||
uint32_t tmp;
|
||||
|
||||
for (k = 0; k < rows; k++) {
|
||||
i = k;
|
||||
|
||||
while (((bits[i] >> rt) & 1) == 0) {
|
||||
i++;
|
||||
|
||||
if (i < rows)
|
||||
continue;
|
||||
else {
|
||||
rt++;
|
||||
if (rt < cols) {
|
||||
i = k;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return (rank);
|
||||
}
|
||||
|
||||
rank++;
|
||||
if (i != k) {
|
||||
tmp = bits[i];
|
||||
bits[i] = bits[k];
|
||||
bits[k] = tmp;
|
||||
}
|
||||
|
||||
for (j = i + 1; j < rows; j++) {
|
||||
if (((bits[j] >> rt) & 1) == 0)
|
||||
continue;
|
||||
else
|
||||
bits[j] ^= bits[k];
|
||||
}
|
||||
|
||||
rt++;
|
||||
}
|
||||
|
||||
return (rank);
|
||||
}
|
||||
|
||||
static void
|
||||
random_test(pvalue_func_t *func) {
|
||||
isc_mem_t *mctx = NULL;
|
||||
isc_result_t result;
|
||||
isc_rng_t *rng;
|
||||
uint32_t m;
|
||||
uint32_t j;
|
||||
uint32_t histogram[11];
|
||||
uint32_t passed;
|
||||
double proportion;
|
||||
double p_hat;
|
||||
double lower_confidence;
|
||||
double chi_square;
|
||||
double p_value_t;
|
||||
|
||||
tables_init();
|
||||
|
||||
result = isc_mem_create(0, 0, &mctx);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
|
||||
rng = NULL;
|
||||
result = isc_rng_create(mctx, NULL, &rng);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
|
||||
m = 1000;
|
||||
passed = 0;
|
||||
|
||||
for (j = 0; j < 11; j++)
|
||||
histogram[j] = 0;
|
||||
|
||||
for (j = 0; j < m; j++) {
|
||||
uint32_t i;
|
||||
uint16_t values[128000];
|
||||
double p_value;
|
||||
|
||||
for (i = 0; i < 128000; i++)
|
||||
values[i] = isc_rng_random(rng);
|
||||
|
||||
p_value = (*func)(mctx, values, 128000);
|
||||
if (p_value >= 0.01)
|
||||
passed++;
|
||||
|
||||
ATF_REQUIRE(p_value >= 0.0);
|
||||
ATF_REQUIRE(p_value <= 1.0);
|
||||
|
||||
i = (int) floor(p_value * 10);
|
||||
histogram[i]++;
|
||||
}
|
||||
|
||||
isc_rng_detach(&rng);
|
||||
|
||||
/* Fold histogram[10] (p_value = 1.0) into histogram[9] for
|
||||
* interval [0.9, 1.0]
|
||||
*/
|
||||
histogram[9] += histogram[10];
|
||||
histogram[10] = 0;
|
||||
|
||||
/*
|
||||
* Check proportion of sequences passing a test (see section
|
||||
* 4.2.1 in NIST SP 800-22).
|
||||
*/
|
||||
proportion = (double) passed / (double) m;
|
||||
p_hat = 1 - 0.01; /* alpha is 0.01 in the NIST tests */
|
||||
lower_confidence = p_hat - (3.0 * sqrt((p_hat * (1 - p_hat)) / m));
|
||||
|
||||
/* Debug message, not displayed when running via atf-run */
|
||||
printf("passed=%u/1000\n", passed);
|
||||
printf("lower_confidence=%f, proportion=%f\n",
|
||||
lower_confidence, proportion);
|
||||
|
||||
ATF_REQUIRE(proportion >= lower_confidence);
|
||||
|
||||
/*
|
||||
* Check uniform distribution of p-values (see section 4.2.2 in
|
||||
* NIST SP 800-22).
|
||||
*/
|
||||
|
||||
/* Pre-requisite that at least 55 sequences are processed. */
|
||||
ATF_REQUIRE(m >= 55);
|
||||
|
||||
chi_square = 0.0;
|
||||
for (j = 0; j < 10; j++) {
|
||||
double numer;
|
||||
double denom;
|
||||
|
||||
/* Debug message, not displayed when running via atf-run */
|
||||
printf("hist%u=%u ", j, histogram[j]);
|
||||
|
||||
numer = (histogram[j] - (m / 10.0)) *
|
||||
(histogram[j] - (m / 10.0));
|
||||
denom = m / 10.0;
|
||||
chi_square += numer / denom;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
p_value_t = igamc(9 / 2.0, chi_square / 2.0);
|
||||
|
||||
ATF_REQUIRE(p_value_t >= 0.0001);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a frequency (monobits) test taken from the NIST SP 800-22
|
||||
* RNG test suite.
|
||||
*/
|
||||
static double
|
||||
monobit(isc_mem_t *mctx, uint16_t *values, size_t length) {
|
||||
size_t i;
|
||||
int32_t scount;
|
||||
uint32_t numbits;
|
||||
double s_obs;
|
||||
double p_value;
|
||||
|
||||
UNUSED(mctx);
|
||||
|
||||
numbits = length * 16;
|
||||
scount = 0;
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
scount += scounts_table[values[i]];
|
||||
|
||||
/* Preconditions (section 2.1.7 in NIST SP 800-22) */
|
||||
ATF_REQUIRE(numbits >= 100);
|
||||
|
||||
/* Debug message, not displayed when running via atf-run */
|
||||
printf("numbits=%u, scount=%d\n", numbits, scount);
|
||||
|
||||
s_obs = fabs(scount) / sqrt(numbits);
|
||||
p_value = erfc(s_obs / sqrt(2.0));
|
||||
|
||||
return (p_value);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the runs test taken from the NIST SP 800-22 RNG test suite.
|
||||
*/
|
||||
static double
|
||||
runs(isc_mem_t *mctx, uint16_t *values, size_t length) {
|
||||
size_t i;
|
||||
uint32_t bcount;
|
||||
uint32_t numbits;
|
||||
double pi;
|
||||
double tau;
|
||||
uint32_t j;
|
||||
uint32_t b;
|
||||
uint8_t bit_this;
|
||||
uint8_t bit_prev;
|
||||
uint32_t v_obs;
|
||||
double numer;
|
||||
double denom;
|
||||
double p_value;
|
||||
|
||||
UNUSED(mctx);
|
||||
|
||||
numbits = length * 16;
|
||||
bcount = 0;
|
||||
|
||||
for (i = 0; i < 128000; i++)
|
||||
bcount += bitcounts_table[values[i]];
|
||||
|
||||
/* Debug message, not displayed when running via atf-run */
|
||||
printf("numbits=%u, bcount=%u\n", numbits, bcount);
|
||||
|
||||
pi = (double) bcount / (double) numbits;
|
||||
tau = 2.0 / sqrt(numbits);
|
||||
|
||||
/* Preconditions (section 2.3.7 in NIST SP 800-22) */
|
||||
ATF_REQUIRE(numbits >= 100);
|
||||
|
||||
/*
|
||||
* Pre-condition implied from the monobit test. This can fail
|
||||
* for some sequences, and the p-value is taken as 0 in these
|
||||
* cases.
|
||||
*/
|
||||
if (fabs(pi - 0.5) >= tau)
|
||||
return (0.0);
|
||||
|
||||
/* Compute v_obs */
|
||||
j = 0;
|
||||
b = 14;
|
||||
bit_prev = (values[j] & (1U << 15)) == 0 ? 0 : 1;
|
||||
|
||||
v_obs = 0;
|
||||
|
||||
for (i = 1; i < numbits; i++) {
|
||||
bit_this = (values[j] & (1U << b)) == 0 ? 0 : 1;
|
||||
if (b == 0) {
|
||||
b = 15;
|
||||
j++;
|
||||
} else {
|
||||
b--;
|
||||
}
|
||||
|
||||
v_obs += bit_this ^ bit_prev;
|
||||
|
||||
bit_prev = bit_this;
|
||||
}
|
||||
|
||||
v_obs += 1;
|
||||
|
||||
numer = fabs(v_obs - (2.0 * numbits * pi * (1.0 - pi)));
|
||||
denom = 2.0 * sqrt(2.0 * numbits) * pi * (1.0 - pi);
|
||||
|
||||
p_value = erfc(numer / denom);
|
||||
|
||||
return (p_value);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the block frequency test taken from the NIST SP 800-22 RNG
|
||||
* test suite.
|
||||
*/
|
||||
static double
|
||||
blockfrequency(isc_mem_t *mctx, uint16_t *values, size_t length) {
|
||||
uint32_t i;
|
||||
uint32_t numbits;
|
||||
uint32_t mbits;
|
||||
uint32_t mwords;
|
||||
uint32_t numblocks;
|
||||
double *pi;
|
||||
uint32_t cur_word;
|
||||
double chi_square;
|
||||
double p_value;
|
||||
|
||||
numbits = length * 16;
|
||||
mbits = 32000;
|
||||
mwords = mbits / 16;
|
||||
numblocks = numbits / mbits;
|
||||
|
||||
/* Debug message, not displayed when running via atf-run */
|
||||
printf("numblocks=%u\n", numblocks);
|
||||
|
||||
/* Preconditions (section 2.2.7 in NIST SP 800-22) */
|
||||
ATF_REQUIRE(numbits >= 100);
|
||||
ATF_REQUIRE(mbits >= 20);
|
||||
ATF_REQUIRE((double) mbits > (0.01 * numbits));
|
||||
ATF_REQUIRE(numblocks < 100);
|
||||
ATF_REQUIRE(numbits >= (mbits * numblocks));
|
||||
|
||||
pi = isc_mem_get(mctx, numblocks * sizeof(double));
|
||||
|
||||
cur_word = 0;
|
||||
for (i = 0; i < numblocks; i++) {
|
||||
uint32_t j;
|
||||
pi[i] = 0.0;
|
||||
for (j = 0; j < mwords; j++) {
|
||||
uint32_t idx;
|
||||
|
||||
idx = i * mwords + j;
|
||||
pi[i] += bitcounts_table[values[idx]];
|
||||
cur_word++;
|
||||
}
|
||||
pi[i] /= mbits;
|
||||
}
|
||||
|
||||
/* Compute chi_square */
|
||||
chi_square = 0.0;
|
||||
for (i = 0; i < numblocks; i++)
|
||||
chi_square += (pi[i] - 0.5) * (pi[i] - 0.5);
|
||||
|
||||
chi_square *= 4 * mbits;
|
||||
|
||||
isc_mem_put(mctx, pi, numblocks * sizeof(double));
|
||||
|
||||
/* Debug message, not displayed when running via atf-run */
|
||||
printf("chi_square=%f\n", chi_square);
|
||||
|
||||
p_value = igamc(numblocks * 0.5, chi_square * 0.5);
|
||||
|
||||
return (p_value);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the binary matrix rank test taken from the NIST SP 800-22 RNG
|
||||
* test suite.
|
||||
*/
|
||||
static double
|
||||
binarymatrixrank(isc_mem_t *mctx, uint16_t *values, size_t length) {
|
||||
uint32_t i;
|
||||
size_t matrix_m;
|
||||
size_t matrix_q;
|
||||
uint32_t num_matrices;
|
||||
size_t numbits;
|
||||
uint32_t fm_0;
|
||||
uint32_t fm_1;
|
||||
uint32_t fm_rest;
|
||||
double term1;
|
||||
double term2;
|
||||
double term3;
|
||||
double chi_square;
|
||||
double p_value;
|
||||
|
||||
UNUSED(mctx);
|
||||
|
||||
matrix_m = 32;
|
||||
matrix_q = 32;
|
||||
num_matrices = length / ((matrix_m * matrix_q) / 16);
|
||||
numbits = num_matrices * matrix_m * matrix_q;
|
||||
|
||||
/* Preconditions (section 2.5.7 in NIST SP 800-22) */
|
||||
ATF_REQUIRE(matrix_m == 32);
|
||||
ATF_REQUIRE(matrix_q == 32);
|
||||
ATF_REQUIRE(numbits >= (38 * matrix_m * matrix_q));
|
||||
|
||||
fm_0 = 0;
|
||||
fm_1 = 0;
|
||||
fm_rest = 0;
|
||||
for (i = 0; i < num_matrices; i++) {
|
||||
/*
|
||||
* Each uint32_t supplies 32 bits, so a 32x32 bit matrix
|
||||
* takes up uint32_t array of size 32.
|
||||
*/
|
||||
uint32_t bits[32];
|
||||
int j;
|
||||
uint32_t rank;
|
||||
|
||||
for (j = 0; j < 32; j++) {
|
||||
size_t idx;
|
||||
uint32_t r1;
|
||||
uint32_t r2;
|
||||
|
||||
idx = i * ((matrix_m * matrix_q) / 16);
|
||||
idx += j * 2;
|
||||
|
||||
r1 = values[idx];
|
||||
r2 = values[idx + 1];
|
||||
bits[j] = (r1 << 16) | r2;
|
||||
}
|
||||
|
||||
rank = matrix_binaryrank(bits, matrix_m, matrix_q);
|
||||
|
||||
if (rank == matrix_m)
|
||||
fm_0++;
|
||||
else if (rank == (matrix_m - 1))
|
||||
fm_1++;
|
||||
else
|
||||
fm_rest++;
|
||||
}
|
||||
|
||||
/* Compute chi_square */
|
||||
term1 = ((fm_0 - (0.2888 * num_matrices)) *
|
||||
(fm_0 - (0.2888 * num_matrices))) / (0.2888 * num_matrices);
|
||||
term2 = ((fm_1 - (0.5776 * num_matrices)) *
|
||||
(fm_1 - (0.5776 * num_matrices))) / (0.5776 * num_matrices);
|
||||
term3 = ((fm_rest - (0.1336 * num_matrices)) *
|
||||
(fm_rest - (0.1336 * num_matrices))) / (0.1336 * num_matrices);
|
||||
|
||||
chi_square = term1 + term2 + term3;
|
||||
|
||||
/* Debug message, not displayed when running via atf-run */
|
||||
printf("fm_0=%u, fm_1=%u, fm_rest=%u, chi_square=%f\n",
|
||||
fm_0, fm_1, fm_rest, chi_square);
|
||||
|
||||
p_value = exp(-chi_square * 0.5);
|
||||
|
||||
return (p_value);
|
||||
}
|
||||
|
||||
ATF_TC(isc_rng_monobit);
|
||||
ATF_TC_HEAD(isc_rng_monobit, tc) {
|
||||
atf_tc_set_md_var(tc, "descr", "Monobit test for the RNG");
|
||||
}
|
||||
|
||||
ATF_TC_BODY(isc_rng_monobit, tc) {
|
||||
UNUSED(tc);
|
||||
|
||||
random_test(monobit);
|
||||
}
|
||||
|
||||
ATF_TC(isc_rng_runs);
|
||||
ATF_TC_HEAD(isc_rng_runs, tc) {
|
||||
atf_tc_set_md_var(tc, "descr", "Runs test for the RNG");
|
||||
}
|
||||
|
||||
ATF_TC_BODY(isc_rng_runs, tc) {
|
||||
UNUSED(tc);
|
||||
|
||||
random_test(runs);
|
||||
}
|
||||
|
||||
ATF_TC(isc_rng_blockfrequency);
|
||||
ATF_TC_HEAD(isc_rng_blockfrequency, tc) {
|
||||
atf_tc_set_md_var(tc, "descr", "Block frequency test for the RNG");
|
||||
}
|
||||
|
||||
ATF_TC_BODY(isc_rng_blockfrequency, tc) {
|
||||
UNUSED(tc);
|
||||
|
||||
random_test(blockfrequency);
|
||||
}
|
||||
|
||||
ATF_TC(isc_rng_binarymatrixrank);
|
||||
ATF_TC_HEAD(isc_rng_binarymatrixrank, tc) {
|
||||
atf_tc_set_md_var(tc, "descr", "Binary matrix rank test for the RNG");
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the binary matrix rank test taken from the NIST SP 800-22 RNG
|
||||
* test suite.
|
||||
*/
|
||||
ATF_TC_BODY(isc_rng_binarymatrixrank, tc) {
|
||||
UNUSED(tc);
|
||||
|
||||
random_test(binarymatrixrank);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main
|
||||
*/
|
||||
ATF_TP_ADD_TCS(tp) {
|
||||
ATF_TP_ADD_TC(tp, isc_rng_monobit);
|
||||
ATF_TP_ADD_TC(tp, isc_rng_runs);
|
||||
ATF_TP_ADD_TC(tp, isc_rng_blockfrequency);
|
||||
ATF_TP_ADD_TC(tp, isc_rng_binarymatrixrank);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
Reference in New Issue
Block a user