+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]
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
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)
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 \
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 */
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 */
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 */
};
#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
}
}
-/*%
- * 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.
*/
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);
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);
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)
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);
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:
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;
kill_lock:
DESTROYLOCK(&disp->lock);
deallocate:
+ if (disp->rngctx != NULL)
+ isc_rng_detach(&disp->rngctx);
isc_mempool_put(mgr->dpool, disp);
return (result);
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;
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);
* 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 {
${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 \
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 \
--- /dev/null
+/*
+ * 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
+ }
+}
#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);
/*%<
* 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 */
* 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();
}
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
}
void
-isc_random_get(isc_uint32_t *val)
-{
+isc_random_get(isc_uint32_t *val) {
REQUIRE(val != NULL);
initialize();
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);
+}
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@ \
${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}
--- /dev/null
+/*
+ * 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());
+}