[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:
Mukund Sivaraman
2014-06-04 13:38:59 +05:30
parent b925be3e54
commit 84dc4b3e7e
11 changed files with 1360 additions and 207 deletions

View File

@@ -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
View File

@@ -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

View File

@@ -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)

View File

@@ -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 \

View File

@@ -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 {

View File

@@ -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
View 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
}
}

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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
View 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());
}