diff --git a/sys/include/random.h b/sys/include/random.h index 48b978782f..252a8379cf 100644 --- a/sys/include/random.h +++ b/sys/include/random.h @@ -92,10 +92,7 @@ void random_bytes(uint8_t *buf, size_t size); * * @return a random number on [a,b)-interval */ -static inline uint32_t random_uint32_range(uint32_t a, uint32_t b) -{ - return (random_uint32() % (b - a)) + a; -} +uint32_t random_uint32_range(uint32_t a, uint32_t b); #if PRNG_FLOAT /* These real versions are due to Isaku Wada, 2002/01/09 added */ diff --git a/sys/random/random.c b/sys/random/random.c index dc5e6cf38d..ad80880167 100644 --- a/sys/random/random.c +++ b/sys/random/random.c @@ -54,3 +54,30 @@ void random_bytes(uint8_t *target, size_t n) *target++ = *random_pos++; } } + +uint32_t random_uint32_range(uint32_t a, uint32_t b) +{ + assert(a < b); + + uint32_t divisor, rand_val, range = b - a; + uint8_t range_msb = bitarithm_msb(range); + + /* check if range is a power of two */ + if (!(range & (range - 1))) { + divisor = (1 << range_msb) - 1; + } + else if (range_msb < 31) { + /* leftshift for next power of two interval */ + divisor = (1 << (range_msb + 1)) -1; + } + else { + /* disable modulo operation in loop below */ + divisor = UINT32_MAX; + } + /* get random numbers until value is smaller than range */ + do { + rand_val = (random_uint32() & divisor); + } while (rand_val >= range); + /* return random in range [a,b] */ + return (rand_val + a); +} diff --git a/tests/rng/README.md b/tests/rng/README.md index c263a840b5..16a15166a8 100644 --- a/tests/rng/README.md +++ b/tests/rng/README.md @@ -3,12 +3,14 @@ Test application for the RNG sourcs. ## Supported commands * distributions [N] — Take N samples and print a bit distribution graph on the terminal. -* dump [N] — Take N samples and print them on the terminal. +* dump [N][A B] — Take N samples and print them on the terminal. If A and B are +set, the PRNG returns values in the [A,B)-interval. * fips — Run the FIPS 140-2 random number tests. * entropy [N] — Calculate Shannon's entropy from N samples. * seed [N] — Set the random seed to use. * source [N] — Select the RNG source, or list them all. -* speed [N] — Run a PRNG for N seconds and print the number of KiB/sec afterwards. +* speed [N][A B] — Run a PRNG for N seconds and print the number of KiB/sec +afterwards. If A and B are set, the PRNG returns values in the [A,B)-interval. ## Sources The following sources are supported: diff --git a/tests/rng/main.c b/tests/rng/main.c index fe0e3ba43e..ff26a95a40 100644 --- a/tests/rng/main.c +++ b/tests/rng/main.c @@ -90,12 +90,26 @@ static int cmd_dump(int argc, char **argv) { uint32_t samples = 100; - if (argc > 1) { + if (argc < 2) { + /* run the test */ + test_dump(samples); + } + else if (argc == 2) { samples = strtoul(argv[1], NULL, 0); + /* run the test */ + test_dump(samples); + } + else if (argc == 4) { + samples = strtoul(argv[1], NULL, 0); + uint32_t low_thresh = strtoul(argv[2], NULL, 0); + uint32_t high_thresh = strtoul(argv[3], NULL, 0); + /* run the test */ + test_dump_range(samples, low_thresh, high_thresh); + } + else { + printf("usage: %s [samples] [lower-bound upper-bound]\n", argv[0]); } - /* run the test */ - test_dump(samples); return 0; } @@ -220,12 +234,25 @@ static int cmd_speed(int argc, char **argv) { uint32_t duration = 10; - if (argc > 1) { - duration = strtoul(argv[1], NULL, 0); + if (argc < 2) { + /* run the test */ + test_speed(duration); + } + else if (argc == 2) { + duration = strtoul(argv[1], NULL, 0); + /* run the test */ + test_speed(duration); + } + else if (argc == 4) { + duration = strtoul(argv[1], NULL, 0); + uint32_t low_thresh = strtoul(argv[2], NULL, 0); + uint32_t high_thresh = strtoul(argv[3], NULL, 0); + /* run the test */ + test_speed_range(duration, low_thresh, high_thresh); + } + else { + printf("usage: %s [duration] [lower-bound upper-bound]\n", argv[0]); } - - /* run the test */ - test_speed(duration); return 0; } diff --git a/tests/rng/test.c b/tests/rng/test.c index a4eb04fc07..9fc2bc433c 100644 --- a/tests/rng/test.c +++ b/tests/rng/test.c @@ -103,6 +103,27 @@ static inline uint32_t test_get_uint32(void) return 0; } +/** + * @brief Retrieve a 32-bit number of the source RNG on [a,b)-interval + */ +static inline uint32_t test_get_uint32_range(uint32_t a, uint32_t b) +{ + if (source == RNG_PRNG) { + return random_uint32_range(a, b); + } +#ifdef MODULE_PERIPH_HWRNG + else if (source == RNG_HWRNG) { + puts("Range feature not supported by HWRNG"); + } +#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 * @@ -192,6 +213,15 @@ void test_dump(uint32_t samples) } } +void test_dump_range(uint32_t samples, uint32_t low_thresh, uint32_t high_thresh) +{ + test_init("dump range"); + + while (samples--) { + printf("%" PRIu32 "\n", test_get_uint32_range(low_thresh, high_thresh)); + } +} + void test_fips(void) { uint8_t last_bit = UINT8_MAX; @@ -378,3 +408,27 @@ void test_speed(uint32_t duration) fmt_u64_dec(tmp2, (samples * 4 / 1024) / duration); printf("Collected %s samples in %" PRIu32 " seconds (%s KiB/s).\n", tmp1, duration, tmp2); } + +void test_speed_range(uint32_t duration, uint32_t low_thresh, uint32_t high_thresh) +{ + char tmp1[16] = { 0 }, tmp2[16] = { 0 }; + + uint64_t timeout = 0; + uint64_t samples = 0; + + /* initialize test */ + test_init("speed range"); + + /* 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_range(low_thresh, high_thresh); + 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); +} diff --git a/tests/rng/test.h b/tests/rng/test.h index 93a6eafb83..0bb648bf0c 100644 --- a/tests/rng/test.h +++ b/tests/rng/test.h @@ -73,6 +73,18 @@ void test_distributions(uint32_t samples); */ void test_dump(uint32_t samples); +/** + * @brief Test for dumping random number r with a <= r < b. Each number + * is printed on a separate line as an unsigned 32-bit number. + * + * @param[in] samples Number of samples to print. + * @param[in] a Minimum for random number + * @param[in] b Upper bound for random number + * + * @pre a < b + */ +void test_dump_range(uint32_t samples, uint32_t a, uint32_t b); + /** * @brief Run the FIPS 140-2 battery of test. * @@ -104,6 +116,18 @@ void test_entropy(uint32_t samples); */ void test_speed(uint32_t duration); +/** + * @brief Run the speed test for random numbers r with a <= r < b and a given duration. + * It utillizes xtimer for setting an alarm. + * + * @param[in] duration Test duration (in seconds) + * @param[in] a Minimum for random number + * @param[in] b Upper bound for random number + * + * @pre a < b + */ +void test_speed_range(uint32_t duration, uint32_t a, uint32_t b); + #ifdef __cplusplus } #endif