1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/tests/rng/test.c
2018-05-29 18:40:59 +02:00

379 lines
8.3 KiB
C

/*
* Copyright (C) 2017 Bas Stottelaar <basstottelaar@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <stdint.h>
#include <math.h>
#include <stdio.h>
#include "test.h"
#include "fmt.h"
#include "random.h"
#include "xtimer.h"
#ifdef MODULE_PERIPH_HWRNG
#include "periph/hwrng.h"
#endif
/**
* @brief Seed for initializing random module.
*/
uint32_t seed = 0;
/**
* @brief Source of randomness.
*/
rng_source_t source = 0;
/**
* @brief Initialize the RNG, is needed.
*
* @param[in] name The test name.
*/
static void test_init(char *name)
{
/* prepare RNG source */
if (source == RNG_PRNG) {
random_init(seed);
}
#ifdef MODULE_PERIPH_HWRNG
else if (source == RNG_HWRNG) {
hwrng_init();
}
#endif
/* print test overview */
printf("Running %s test, with seed %" PRIu32 " using ", name, seed);
if (source == RNG_PRNG) {
#if MODULE_PRNG_FORTUNA
puts("Fortuna PRNG.\n");
#elif MODULE_PRNG_MERSENNE
puts("Mersenne Twister PRNG.\n");
#elif MODULE_PRNG_MINSTD
puts("Park & Miller Minimal Standard PRNG.\n");
#elif MODULE_PRNG_MUSL_LCG
puts("Musl C PRNG.\n");
#elif MODULE_PRNG_TINYMT32
puts("Tiny Mersenne Twister PRNG.\n");
#elif MODULE_PRNG_XORSHIFT
puts("XOR Shift PRNG.\n");
#else
puts("unknown PRNG.\n");
#endif
}
#ifdef MODULE_PERIPH_HWRNG
else if (source == RNG_HWRNG) {
puts("HW RNG.\n");
}
#endif
else if (source == RNG_CONSTANT) {
puts("constant value.\n");
}
}
/**
* @brief Retrieve a 32-bit number of the source RNG.
*/
static inline uint32_t test_get_uint32(void)
{
if (source == RNG_PRNG) {
return random_uint32();
}
#ifdef MODULE_PERIPH_HWRNG
else if (source == RNG_HWRNG) {
uint32_t result;
hwrng_read(&result, 4);
return result;
}
#endif
else if (source == RNG_CONSTANT) {
/* use the seed as the constant value */
return seed;
}
return 0;
}
/**
* @brief Helper for printing `passed` or `failed` depending on condition
*
* @param[in] condition The test condition.
*/
void test_pass_fail(bool condition)
{
if (condition) {
puts("passed");
}
else {
puts("failed");
}
}
void test_distributions(uint32_t samples)
{
char tmp[16] = { 0 };
uint32_t distributions[32] = { 0 };
/* initialize test */
test_init("distributions");
/* take random samples */
while (samples--) {
uint32_t value = test_get_uint32();
/* count bits */
for (int i = 0; i < 32; i++) {
if (value & (1 << i)) {
distributions[i]++;
}
}
}
/* sum the total number of ones */
uint64_t total = 0;
uint32_t min = UINT32_MAX;
uint32_t max = 0;
for (int i = 0; i < 32; i++) {
total += distributions[i];
if (distributions[i] < min) {
min = distributions[i];
}
if (distributions[i] > max) {
max = distributions[i];
}
}
/* if total is zero, it would yield a division by zero */
if ((total / 100) == 0) {
puts("Total ones is zero.\n");
return;
}
/* print the distribution to screen */
fmt_u64_dec(tmp, total / 32);
printf("For 32-bit samples (min = %" PRIu32 ", max = %" PRIu32 ", avg = %s):\n", min, max, tmp);
/* print a bar for each bin, scaling max to 100% */
for (int i = 0; i < 32; i++) {
printf("%02d: ", i);
/* calculate the width of the bar (max 4 + 75 chars) */
uint8_t bars = (distributions[i] / (max / 75));
for (unsigned int j = 0; j < bars; j++) {
printf("#");
}
puts("");
}
puts("");
}
void test_dump(uint32_t samples)
{
test_init("dump");
while (samples--) {
printf("%" PRIu32 "\n", test_get_uint32());
}
}
void test_fips(void)
{
uint8_t last_bit = UINT8_MAX;
uint32_t ones = 0;
uint32_t poker[16] = { 0 };
uint32_t runs = 1;
uint32_t runs_ones[6] = { 0 };
uint32_t runs_zeroes[6] = { 0 };
uint32_t longruns = 1;
uint32_t longruns_max = 0;
/* initialize test */
test_init("FIPS 140-2");
/* FIPS 140-2 needs 20.000 bits, which are 625 32-bit random numbers */
for (int i = 0; i < 625; i++) {
uint32_t value = test_get_uint32();
for (int j = 0; j < 4; j++) {
uint8_t byte = ((uint8_t *) &value)[j];
/* poker */
poker[byte >> 4]++;
poker[byte & 0x0f]++;
for (int k = 0; k < 8; k++) {
uint8_t bit = (byte >> k) & 0x01;
/* monobit */
if (bit) {
ones++;
}
/* run length */
if (bit == last_bit) {
runs++;
}
else {
if (runs > 6) {
runs = 6;
}
if (last_bit) {
runs_ones[runs - 1]++;
}
else {
runs_zeroes[runs - 1]++;
}
runs = 1;
}
/* longruns */
if (bit == last_bit) {
longruns++;
}
else {
if (longruns > longruns_max) {
longruns_max = longruns;
}
longruns = 1;
}
last_bit = bit;
}
}
}
/* for a constant stream of bits, the last (long)run must be added */
if (runs > 6) {
runs = 6;
}
if (last_bit) {
runs_ones[runs - 1]++;
}
else {
runs_zeroes[runs - 1]++;
}
if (longruns > longruns_max) {
longruns_max = longruns;
}
/* monobit test result */
printf("- Monobit test: ");
test_pass_fail(!((ones >= 10275) || (ones <= 9725)));
/* poker test result */
uint32_t result = 0;
for (int i = 0; i < 16; i++) {
result += poker[i] * poker[i];
}
printf("- Poker test: ");
test_pass_fail(!((result > 1576928) || (result < 1563176)));
/* runs test result */
bool passed = true;
uint32_t min[6] = { 2343, 1135, 542, 251, 111, 111 };
uint32_t max[6] = { 2657, 1365, 708, 373, 201, 201 };
for (int i = 0; i < 6; i++) {
if (runs_ones[i] < min[i] || runs_ones[i] > max[i]) {
passed = false;
}
if (runs_zeroes[i] < min[i] || runs_zeroes[i] > max[i]) {
passed = false;
}
}
printf("- Run test: ");
test_pass_fail(passed);
/* longruns test result */
printf("- Longrun test: ");
test_pass_fail(longruns_max < 26);
}
void test_entropy(uint32_t samples)
{
uint8_t buffer[256] = { 0 };
uint32_t length = 0;
/* initialize test */
test_init("entropy");
/* take samples */
for (uint32_t i = 0; i < samples; i++) {
uint32_t value = test_get_uint32();
buffer[((uint8_t *) &value)[0]]++;
buffer[((uint8_t *) &value)[1]]++;
buffer[((uint8_t *) &value)[2]]++;
buffer[((uint8_t *) &value)[3]]++;
length += 4;
}
/* calculate entropy */
float entropy = 0.0;
for (int i = 0; i < 256; i++) {
if (buffer[i] != 0) {
float count = (float) buffer[i] / (float) length;
entropy += (-count * log2f(count));
}
}
/* print results */
printf("Calculated %02f bits of entropy from %" PRIu32 " samples.\n", (double) entropy, samples);
}
void test_speed(uint32_t duration)
{
char tmp1[16] = { 0 }, tmp2[16] = { 0 };
uint64_t timeout = 0;
uint64_t samples = 0;
/* initialize test */
test_init("speed");
/* collect samples as long as timer has not expired */
timeout = xtimer_now_usec64() + (duration * US_PER_SEC);
while (xtimer_now_usec64() < timeout) {
test_get_uint32();
samples++;
}
/* print results */
fmt_u64_dec(tmp1, samples);
fmt_u64_dec(tmp2, (samples * 4 / 1024) / duration);
printf("Collected %s samples in %" PRIu32 " seconds (%s KiB/s).\n", tmp1, duration, tmp2);
}