#define CHACHA_IVSIZE 8U
#define CHACHA_BLOCKSIZE 64
#define CHACHA_BUFFERSIZE (16 * CHACHA_BLOCKSIZE)
+#define CHACHA_MAXHAVE (CHACHA_BUFFERSIZE - CHACHA_KEYSIZE - CHACHA_IVSIZE)
+/*
+ * Derived from OpenBSD's implementation. The rationale is not clear,
+ * but should be conservative enough in safety, and reasonably large for
+ * efficiency.
+ */
+#define CHACHA_MAXLENGTH 1600000
/* ChaCha RNG state */
struct isc_rng {
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;
+ rng->have = CHACHA_MAXHAVE;
}
-static inline isc_uint16_t
-chacha_getuint16(isc_rng_t *rng) {
- isc_uint16_t val;
-
+static void
+chacha_getbytes(isc_rng_t *rng, isc_uint8_t *output, size_t length) {
REQUIRE(VALID_RNG(rng));
- if (rng->have < sizeof(val))
+ while (ISC_UNLIKELY(length > CHACHA_MAXHAVE)) {
chacha_rekey(rng, NULL, 0);
+ memmove(output, rng->buffer + CHACHA_BUFFERSIZE - rng->have,
+ CHACHA_MAXHAVE);
+ output += CHACHA_MAXHAVE;
+ length -= CHACHA_MAXHAVE;
+ rng->have = 0;
+ }
- memmove(&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);
+ if (rng->have < length)
+ chacha_rekey(rng, NULL, 0);
- return (val);
+ memmove(output, rng->buffer + CHACHA_BUFFERSIZE - rng->have, length);
+ /* Clear the copied region. */
+ memset(rng->buffer + CHACHA_BUFFERSIZE - rng->have, 0, length);
+ rng->have -= length;
}
static void
* but should be conservative enough in safety, and reasonably large
* for efficiency.
*/
- rng->count = 1600000;
+ rng->count = CHACHA_MAXLENGTH;
}
-isc_uint16_t
-isc_rng_random(isc_rng_t *rng) {
- isc_uint16_t result;
+void
+isc_rng_randombytes(isc_rng_t *rng, void *output, size_t length) {
+ isc_uint8_t *ptr = output;
REQUIRE(VALID_RNG(rng));
+ REQUIRE(output != NULL && length > 0);
LOCK(&rng->lock);
- rng->count -= sizeof(isc_uint16_t);
+ while (ISC_UNLIKELY(length > CHACHA_MAXLENGTH)) {
+ chacha_stir(rng);
+ chacha_getbytes(rng, ptr, CHACHA_MAXLENGTH);
+ ptr += CHACHA_MAXLENGTH;
+ length -= CHACHA_MAXLENGTH;
+ rng->count = 0;
+ }
+
+ rng->count -= length;
if (rng->count <= 0)
chacha_stir(rng);
- result = chacha_getuint16(rng);
+
+ chacha_getbytes(rng, ptr, length);
UNLOCK(&rng->lock);
+}
+
+isc_uint16_t
+isc_rng_random(isc_rng_t *rng) {
+ isc_uint16_t result;
+
+ isc_rng_randombytes(rng, &result, sizeof(result));
return (result);
}
* to re-roll.
*/
for (;;) {
- r = isc_rng_random(rng);
+ isc_rng_randombytes(rng, &r, sizeof(r));
if (r >= min)
break;
}
}
static void
-random_test(pvalue_func_t *func) {
+random_test(pvalue_func_t *func, isc_boolean_t word_sized) {
isc_mem_t *mctx = NULL;
isc_result_t result;
isc_rng_t *rng;
isc_uint16_t values[128000];
double p_value;
- for (i = 0; i < 128000; i++)
- values[i] = isc_rng_random(rng);
+ if (word_sized) {
+ for (i = 0; i < 128000; i++)
+ isc_rng_randombytes(rng, &values[i],
+ sizeof(values[i]));
+ } else {
+ isc_rng_randombytes(rng, values, sizeof(values));
+ }
p_value = (*func)(mctx, values, 128000);
if (p_value >= 0.01)
return (p_value);
}
-ATF_TC(isc_rng_monobit);
-ATF_TC_HEAD(isc_rng_monobit, tc) {
+ATF_TC(isc_rng_monobit_16);
+ATF_TC_HEAD(isc_rng_monobit_16, tc) {
+ atf_tc_set_md_var(tc, "descr", "Monobit test for the RNG");
+}
+
+ATF_TC_BODY(isc_rng_monobit_16, tc) {
+ UNUSED(tc);
+
+ random_test(monobit, ISC_TRUE);
+}
+
+ATF_TC(isc_rng_runs_16);
+ATF_TC_HEAD(isc_rng_runs_16, tc) {
+ atf_tc_set_md_var(tc, "descr", "Runs test for the RNG");
+}
+
+ATF_TC_BODY(isc_rng_runs_16, tc) {
+ UNUSED(tc);
+
+ random_test(runs, ISC_TRUE);
+}
+
+ATF_TC(isc_rng_blockfrequency_16);
+ATF_TC_HEAD(isc_rng_blockfrequency_16, tc) {
+ atf_tc_set_md_var(tc, "descr", "Block frequency test for the RNG");
+}
+
+ATF_TC_BODY(isc_rng_blockfrequency_16, tc) {
+ UNUSED(tc);
+
+ random_test(blockfrequency, ISC_TRUE);
+}
+
+ATF_TC(isc_rng_binarymatrixrank_16);
+ATF_TC_HEAD(isc_rng_binarymatrixrank_16, 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_16, tc) {
+ UNUSED(tc);
+
+ random_test(binarymatrixrank, ISC_TRUE);
+}
+
+ATF_TC(isc_rng_monobit_bytes);
+ATF_TC_HEAD(isc_rng_monobit_bytes, tc) {
atf_tc_set_md_var(tc, "descr", "Monobit test for the RNG");
}
-ATF_TC_BODY(isc_rng_monobit, tc) {
+ATF_TC_BODY(isc_rng_monobit_bytes, tc) {
UNUSED(tc);
- random_test(monobit);
+ random_test(monobit, ISC_FALSE);
}
-ATF_TC(isc_rng_runs);
-ATF_TC_HEAD(isc_rng_runs, tc) {
+ATF_TC(isc_rng_runs_bytes);
+ATF_TC_HEAD(isc_rng_runs_bytes, tc) {
atf_tc_set_md_var(tc, "descr", "Runs test for the RNG");
}
-ATF_TC_BODY(isc_rng_runs, tc) {
+ATF_TC_BODY(isc_rng_runs_bytes, tc) {
UNUSED(tc);
- random_test(runs);
+ random_test(runs, ISC_FALSE);
}
-ATF_TC(isc_rng_blockfrequency);
-ATF_TC_HEAD(isc_rng_blockfrequency, tc) {
+ATF_TC(isc_rng_blockfrequency_bytes);
+ATF_TC_HEAD(isc_rng_blockfrequency_bytes, tc) {
atf_tc_set_md_var(tc, "descr", "Block frequency test for the RNG");
}
-ATF_TC_BODY(isc_rng_blockfrequency, tc) {
+ATF_TC_BODY(isc_rng_blockfrequency_bytes, tc) {
UNUSED(tc);
- random_test(blockfrequency);
+ random_test(blockfrequency, ISC_FALSE);
}
-ATF_TC(isc_rng_binarymatrixrank);
-ATF_TC_HEAD(isc_rng_binarymatrixrank, tc) {
+ATF_TC(isc_rng_binarymatrixrank_bytes);
+ATF_TC_HEAD(isc_rng_binarymatrixrank_bytes, 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) {
+ATF_TC_BODY(isc_rng_binarymatrixrank_bytes, tc) {
UNUSED(tc);
- random_test(binarymatrixrank);
+ random_test(binarymatrixrank, ISC_FALSE);
}
/*
* 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);
+ ATF_TP_ADD_TC(tp, isc_rng_monobit_16);
+ ATF_TP_ADD_TC(tp, isc_rng_runs_16);
+ ATF_TP_ADD_TC(tp, isc_rng_blockfrequency_16);
+ ATF_TP_ADD_TC(tp, isc_rng_binarymatrixrank_16);
+ ATF_TP_ADD_TC(tp, isc_rng_monobit_bytes);
+ ATF_TP_ADD_TC(tp, isc_rng_runs_bytes);
+ ATF_TP_ADD_TC(tp, isc_rng_blockfrequency_bytes);
+ ATF_TP_ADD_TC(tp, isc_rng_binarymatrixrank_bytes);
return (atf_no_error());
}