]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[35942] Update random number generator to ChaCha based (and add tests)
authorMukund Sivaraman <muks@isc.org>
Wed, 4 Jun 2014 08:08:59 +0000 (13:38 +0530)
committerMukund Sivaraman <muks@isc.org>
Wed, 4 Jun 2014 08:14:10 +0000 (13:44 +0530)
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

CHANGES
configure
configure.in
lib/dns/Makefile.in
lib/dns/dispatch.c
lib/isc/Makefile.in
lib/isc/chacha_private.h [new file with mode: 0644]
lib/isc/include/isc/random.h
lib/isc/random.c
lib/isc/tests/Makefile.in
lib/isc/tests/random_test.c [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index c8ac23797a01797870c05eb037c6e2d201b2d686..36d398f2284da797e13df1b6f301865356ec8d8c 100644 (file)
--- a/CHANGES
+++ b/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]
 
index a4015796cf5aebeb6e8d501e5ed04bb1cbafd8a2..c14d3decd7a73933764c12cf98b113e69569242d 100755 (executable)
--- a/configure
+++ b/configure
@@ -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
 
index 01e0f8b2d8a55a64b8fc01d58d1595858a62e650..050943ecd6ef489a9cccee8d28e6c134d7a0c510 100644 (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)
index c687d87723406f1c496f923f47e465f8b5d7e21a..bae423eec343ae2e01448e9cb226bb0e2666907a 100644 (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 \
index e510dc27d8d5005ac15093dc670e18a992b059f0..b1d93788d10be9d90946c1dbcdc5e7266adcfa25 100644 (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 {
index ba0838f65fe6599945cb3dfeaf553094bb738554..c6649ae945104d2515db8a3d9175334c833a1ffb 100644 (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 \
diff --git a/lib/isc/chacha_private.h b/lib/isc/chacha_private.h
new file mode 100644 (file)
index 0000000..d79329d
--- /dev/null
@@ -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
+  }
+}
index 1f9572d30ebf6c020db70bf1e516c788a4aa14b2..8d74f25cae6860e743cb26605664e8c04678e503 100644 (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
 
 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 */
index d18204d6e33754d5ba2ad63bf08a0a1a833d7ac2..d0cbdc872f860ee04e46eafb9ec02f9510a7d9e1 100644 (file)
  * 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 */
 
 #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);
+}
index 1233420b75d05cff275956adbb2f30504d6d016b..e16e592977cccb87a766a500043d631834cddd2d 100644 (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}
diff --git a/lib/isc/tests/random_test.c b/lib/isc/tests/random_test.c
new file mode 100644 (file)
index 0000000..5a51605
--- /dev/null
@@ -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());
+}