diff --git a/Makefile.dep b/Makefile.dep index 70b9c28cf5..873f3ab98b 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -778,7 +778,8 @@ ifneq (,$(filter random,$(USEMODULE))) USEMODULE += tinymt32 endif - ifneq (,$(filter prng_sha1prng,$(USEMODULE))) + ifneq (,$(filter prng_sha%prng,$(USEMODULE))) + USEMODULE += prng_shaxprng USEMODULE += hashes endif diff --git a/sys/random/doc.txt b/sys/random/doc.txt new file mode 100644 index 0000000000..216d9d7cad --- /dev/null +++ b/sys/random/doc.txt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 HAW Hamburg + * + * 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. + */ + +/** + * @defgroup sys_random_shaxprng SHAX random number generator + * @ingroup sys_random + * + * @brief SHA based random number generator implementation(CSPRNG). + * + * The generator bases on an internal structure that has been presented in + * FIPS 186-1 Appendix 3.2, which is why it is sometimes named as "DSA PRNG" or + * "FIPS PRNG" in the literature. Outputs are generated by hashing the internal + * generator state, and the feedback path applies a linear transformation to the + * state which is hashed again to create further next outputs. Thus, a potential + * state compromise may allow recovering preceding generator outputs, because + * linear operations in the feedback path are invertible. Thereby, this generator + * gets along with a single hash computation per block which makes the generator + * lightweight in comparison to more advanced CSPRNGs. + * + * This implementation can be run with the SHA-1 or SHA-256 hash function + * for creating outputs. SHA-1 has been deprecated by NIST in 2011 due to + * a collision- and potential brute-force attack. Thus, SHA-256 can be used as + * an alternative. To select one or the other, export + * `USEMODULE += prng_sha1prng` or + * `USEMODULE += prng_sha256prng` + * during compilation. + */ \ No newline at end of file diff --git a/sys/random/sha1prng.c b/sys/random/shaxprng.c similarity index 51% rename from sys/random/sha1prng.c rename to sys/random/shaxprng.c index 6e7156715f..dfbe01e2f9 100644 --- a/sys/random/sha1prng.c +++ b/sys/random/shaxprng.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 HAW Hamburg + * Copyright (C) 2018, 2020 HAW Hamburg * * 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 @@ -10,12 +10,10 @@ */ /** - * @ingroup sys_random + * @ingroup sys_random_shaxprng * @{ * @file * - * @brief SHA1PRNG random number generator implementation - * * @author Peter Kietzmann * @} */ @@ -24,10 +22,52 @@ #include #include "hashes/sha1.h" +#include "hashes/sha256.h" +#include "kernel_defines.h" -#define STATE_SIZE (SHA1_DIGEST_LENGTH) +#if IS_USED(MODULE_PRNG_SHA1PRNG) +/* state size is digset length of SHA-1 */ +#define STATE_SIZE (SHA1_DIGEST_LENGTH) +typedef sha1_context shax_context_t; +#elif IS_USED(MODULE_PRNG_SHA256PRNG) +/* state size is digest length of SHA-256 */ +#define STATE_SIZE (SHA256_DIGEST_LENGTH) +typedef sha256_context_t shax_context_t; +#endif + +static inline void _shax_init(shax_context_t *ctx) +{ + if (IS_USED(MODULE_PRNG_SHA1PRNG)) { + sha1_init((sha1_context *)ctx); + } + else if (IS_USED(MODULE_PRNG_SHA256PRNG)) { + sha256_init((sha256_context_t *)ctx); + } +} + +static inline void _shax_update(shax_context_t *ctx, const void *data, size_t len) +{ + if (IS_USED(MODULE_PRNG_SHA1PRNG)) { + sha1_update((sha1_context *)ctx, data, len); + } + else if (IS_USED(MODULE_PRNG_SHA256PRNG)) { + sha256_update((sha256_context_t *)ctx, data, len); + } +} + +static inline void _shax_final(shax_context_t *ctx, void *digest) +{ + if (IS_USED(MODULE_PRNG_SHA1PRNG)) { + sha1_final((sha1_context *)ctx, digest); + } + else if (IS_USED(MODULE_PRNG_SHA256PRNG)) { + sha256_final((sha256_context_t *)ctx, digest); + } +} + +/* allocate SHA context */ +static shax_context_t ctx; -static sha1_context ctx; static uint32_t datapos = STATE_SIZE; static int8_t digestdata[STATE_SIZE]; static int8_t prng_state[STATE_SIZE]; @@ -62,7 +102,7 @@ void _updatestate(int8_t *state) } } -void _random_bytes(uint8_t *bytes, size_t size) /* TODO: use with global API */ +void _random_bytes(uint8_t *bytes, size_t size) { uint32_t loc = 0; while (loc < size) @@ -88,14 +128,14 @@ void _random_bytes(uint8_t *bytes, size_t size) /* TODO: use with global API */ /* no out data ready, (re)fill internal buffer */ else { - /* reset SHA1 internal state */ - sha1_init(&ctx); + /* reset SHA internal state */ + _shax_init(&ctx); - /* update SHA1 internal state with PRNG state */ - sha1_update(&ctx, (void *)prng_state, sizeof(prng_state)); + /* update SHA internal state with PRNG state */ + _shax_update(&ctx, prng_state, sizeof(prng_state)); /* get the digest */ - sha1_final(&ctx, digestdata); + _shax_final(&ctx, digestdata); /* update PRNG state for next round */ _updatestate(prng_state); @@ -108,12 +148,15 @@ void _random_bytes(uint8_t *bytes, size_t size) /* TODO: use with global API */ void random_init_by_array(uint32_t init_key[], int key_length) { - sha1_init(&ctx); - sha1_update(&ctx, (void *)init_key, key_length); - sha1_final(&ctx, digestdata); + _shax_init(&ctx); + _shax_update(&ctx, init_key, key_length); + _shax_final(&ctx, digestdata); - /* copy seeded SHA1 state to PRNG state */ - memcpy(prng_state, &ctx.state, STATE_SIZE); + /* copy SHA digestdata to PRNG state */ + memcpy(prng_state, digestdata, STATE_SIZE); + + /* reset position indicator */ + datapos = STATE_SIZE; } void random_init(uint32_t seed) @@ -124,13 +167,13 @@ void random_init(uint32_t seed) uint32_t random_uint32(void) { uint32_t ret; - int8_t bytes[sizeof(uint32_t)]; - _random_bytes((uint8_t *)bytes, sizeof(uint32_t)); + uint8_t bytes[sizeof(uint32_t)]; + _random_bytes(bytes, sizeof(bytes)); - ret = ((bytes[0] & 0xff) << 24) - | ((bytes[1] & 0xff) << 16) - | ((bytes[2] & 0xff) << 8) - | (bytes[3] & 0xff); + ret = ((uint32_t)(bytes[0] & 0xff) << 24) + | ((uint32_t)(bytes[1] & 0xff) << 16) + | ((uint32_t)(bytes[2] & 0xff) << 8) + | ((uint32_t)(bytes[3] & 0xff)); return ret; } diff --git a/tests/prng_sha1prng/Makefile b/tests/prng_sha1prng/Makefile new file mode 100644 index 0000000000..c1bab91de0 --- /dev/null +++ b/tests/prng_sha1prng/Makefile @@ -0,0 +1,6 @@ +include ../Makefile.tests_common + +USEMODULE += random +USEMODULE += prng_sha1prng + +include $(RIOTBASE)/Makefile.include diff --git a/tests/prng_sha1prng/main.c b/tests/prng_sha1prng/main.c new file mode 100644 index 0000000000..8bd4828f4d --- /dev/null +++ b/tests/prng_sha1prng/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 HAW Hamburg + * + * 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. + */ + +/** + * + * @file + * @brief Test cases for the SHA1PRNG pseudo random number generator + * + * @author Peter Kietzmann + * + */ + +#include +#include + +#include "kernel_defines.h" +#include "random.h" + +/** + * @brief expected sequence for seed=1. This sequence was generated running the + * following java program (openjdk 11.0.7) as a reference. + * + *~~~~ + * import java.security.SecureRandom; + * + * public class SHA1PRNGTEST { + * public static void main(String args[]) throws Exception { + * SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + * random.setSeed(1); + * int number = 0; + * for (int i = 0; i < 20; i++) { + * number = random.nextInt(); + * System.out.print(Integer.toUnsignedString(number) + " "); + * } + * System.out.println(""); + * } + * } + *~~~~ + */ +static const uint32_t seq_seed1[] = + {2529905901, 3336014406, 1714755920, 3709666991, 1432426612, 554064022, + 1614405352, 861636861, 3689098857, 3893737371, 3138964692, 506954022, + 3469584855, 4144207589, 2031557795, 3248917850, 2384338299, 3341545824, + 2454801916, 3985646079}; + +static void test_prng_sha1prng_java_u32(void) +{ + uint32_t seed[2] = {1, 0}; + + uint32_t test32[ARRAY_SIZE(seq_seed1)]; + + /* seed the generator with 8 bytes similar to the java reference + * implementation + */ + random_init_by_array(seed, sizeof(seed)); + + /* request random samples */ + for (unsigned i = 0; i < ARRAY_SIZE(seq_seed1); i++) { + test32[i] = random_uint32(); + } + + /* compare generator output and reference */ + if (!(memcmp(test32, seq_seed1, sizeof(seq_seed1)))) { + printf("%s:SUCCESS\n", __func__); + } + else { + printf("%s:FAILURE\n", __func__); + } +} + +int main(void) +{ + + test_prng_sha1prng_java_u32(); + + return 0; +} diff --git a/tests/prng_sha1prng/tests/01-run.py b/tests/prng_sha1prng/tests/01-run.py new file mode 100755 index 0000000000..c497b3f3df --- /dev/null +++ b/tests/prng_sha1prng/tests/01-run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2020 HAW Hamburg +# +# 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. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect("test_prng_sha1prng_java_u32:SUCCESS\r\n") + + +if __name__ == "__main__": + sys.exit(run(testfunc)) diff --git a/tests/prng_sha256prng/Makefile b/tests/prng_sha256prng/Makefile new file mode 100644 index 0000000000..4508da343f --- /dev/null +++ b/tests/prng_sha256prng/Makefile @@ -0,0 +1,6 @@ +include ../Makefile.tests_common + +USEMODULE += random +USEMODULE += prng_sha256prng + +include $(RIOTBASE)/Makefile.include diff --git a/tests/prng_sha256prng/main.c b/tests/prng_sha256prng/main.c new file mode 100644 index 0000000000..87022e837d --- /dev/null +++ b/tests/prng_sha256prng/main.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 HAW Hamburg + * + * 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. + */ + +/** + * + * @file + * @brief Test cases for the SHA256PRNG pseudo random number generator + * + * @author Peter Kietzmann + * + */ + +#include +#include + +#include "kernel_defines.h" +#include "random.h" + +/** + * @brief expected sequence for seed=1. This is only a regression test. The + * expected output was generated while porting the SHA256PRNG to RIOT. + */ +static const uint32_t seq_seed1[] = + {1106729202, 3855353741, 932558076, 213257261, 1935649068, 3223344939, + 3700960722, 3580154139, 3802991633, 3783537878, 3862557448, 3401019389, + 3269475530, 260491589, 2254706846, 3754733214, 1693392656, 3020931263, + 2202015546, 2031345158}; +/** + * @brief expected sequence for seed=11799121 (generated at random.org). This is + * only a regression test. The expected output was generated while porting + * the SHA256PRNG to RIOT. + */ +static const uint8_t seq_seed2[] = + {0x04, 0x8b, 0xc0, 0x91, 0x7b, 0x08, 0x4a, 0x2f, 0x8f, 0x9a, 0xd0, 0xa6, 0x65, 0x18, + 0x7b, 0x89, 0x9b, 0x74, 0x52, 0x45, 0x44, 0x74, 0xbd, 0x4a, 0x0c, 0x74, 0x8a, 0x0e, + 0xee, 0xdc, 0x76, 0x50, 0x67, 0xe8, 0x50, 0xce, 0x26, 0xdb, 0x0d, 0xef, 0x33, 0x38, + 0xba, 0x6b, 0x68, 0x8d, 0x5d, 0x83, 0xfd, 0xe3, 0xef, 0x19, 0x20, 0x59, 0xd9, 0xc9, + 0x12, 0x6b, 0x5f, 0x8c, 0xf9, 0x05, 0x36, 0x8f}; + +static void test_prng_sha256prng_seed1_u32(void) +{ + uint32_t seed = 1; + uint32_t test32[ARRAY_SIZE(seq_seed1)]; + + random_init(seed); + + /* request random samples */ + for (unsigned i = 0; i < ARRAY_SIZE(seq_seed1); i++) { + test32[i] = random_uint32(); + } + + /* compare generator output and reference */ + if (!(memcmp(test32, seq_seed1, sizeof(seq_seed1)))) { + printf("%s:SUCCESS\n", __func__); + } + else { + printf("%s:FAILURE\n", __func__); + } +} + +static void test_prng_sha256prng_seed2_u8(void) +{ + uint32_t seed = 11799121; + uint8_t test8[sizeof(seq_seed2)]; + + random_init(seed); + + /* request random bytes */ + random_bytes(test8, sizeof(seq_seed2)); + + /* compare generator output and reference */ + if (!(memcmp(test8, seq_seed2, sizeof(seq_seed2)))) { + printf("%s:SUCCESS\n", __func__); + } + else { + printf("%s:FAILURE\n", __func__); + } +} + +int main(void) +{ + test_prng_sha256prng_seed1_u32(); + test_prng_sha256prng_seed2_u8(); + + return 0; +} diff --git a/tests/prng_sha256prng/tests/01-run.py b/tests/prng_sha256prng/tests/01-run.py new file mode 100755 index 0000000000..0101d0c091 --- /dev/null +++ b/tests/prng_sha256prng/tests/01-run.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2020 HAW Hamburg +# +# 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. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect("test_prng_sha256prng_seed1_u32:SUCCESS\r\n") + child.expect("test_prng_sha256prng_seed2_u8:SUCCESS\r\n") + + +if __name__ == "__main__": + sys.exit(run(testfunc)) diff --git a/tests/rng/test.c b/tests/rng/test.c index 29521c715a..08a54f32c0 100644 --- a/tests/rng/test.c +++ b/tests/rng/test.c @@ -61,6 +61,8 @@ static void test_init(char *name) puts("Musl C PRNG.\n"); #elif MODULE_PRNG_SHA1PRNG puts("SHA1 PRNG.\n"); +#elif MODULE_PRNG_SHA256PRNG + puts("SHA256 PRNG.\n"); #elif MODULE_PRNG_TINYMT32 puts("Tiny Mersenne Twister PRNG.\n"); #elif MODULE_PRNG_XORSHIFT