1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #14488 from PeterKietzmann/pr_add_sha256prng

sys/random: add SHA256 mode to SHA1PRNG & tests
This commit is contained in:
Martine Lenders 2020-07-29 11:20:55 +02:00 committed by GitHub
commit 05b13f5029
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 324 additions and 24 deletions

View File

@ -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

32
sys/random/doc.txt Normal file
View File

@ -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.
*/

View File

@ -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 <peter.kietzmann@haw-hamburg.de>
* @}
*/
@ -24,10 +22,52 @@
#include <string.h>
#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;
}

View File

@ -0,0 +1,6 @@
include ../Makefile.tests_common
USEMODULE += random
USEMODULE += prng_sha1prng
include $(RIOTBASE)/Makefile.include

View File

@ -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 <peter.kietzmann@haw-hamburg.de>
*
*/
#include <stdio.h>
#include <string.h>
#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;
}

View File

@ -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))

View File

@ -0,0 +1,6 @@
include ../Makefile.tests_common
USEMODULE += random
USEMODULE += prng_sha256prng
include $(RIOTBASE)/Makefile.include

View File

@ -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 <peter.kietzmann@haw-hamburg.de>
*
*/
#include <stdio.h>
#include <string.h>
#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;
}

View File

@ -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))

View File

@ -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