From aec75b55fe648a53684ceba1ca1d877a31ba9eb1 Mon Sep 17 00:00:00 2001 From: PeterKietzmann Date: Fri, 19 Jun 2020 17:04:00 +0200 Subject: [PATCH] sys/entropy_source: add new module for entropy sources --- sys/Kconfig | 1 + sys/Makefile.dep | 7 + sys/entropy_source/Kconfig | 37 +++ sys/entropy_source/Makefile | 9 + sys/entropy_source/adc_noise/Kconfig | 77 +++++ sys/entropy_source/adc_noise/Makefile | 3 + sys/entropy_source/adc_noise/adc_noise.c | 90 ++++++ sys/entropy_source/doc.txt | 46 +++ sys/entropy_source/entropy_source.c | 125 +++++++ sys/entropy_source/zero_entropy/Kconfig | 35 ++ sys/entropy_source/zero_entropy/Makefile | 3 + .../zero_entropy/zero_entropy.c | 72 +++++ sys/include/entropy_source.h | 306 ++++++++++++++++++ sys/include/entropy_source/adc_noise.h | 158 +++++++++ sys/include/entropy_source/zero_entropy.h | 92 ++++++ tests/entropy_source/Makefile | 10 + tests/entropy_source/Makefile.board.dep | 13 + tests/entropy_source/README.md | 62 ++++ tests/entropy_source/main.c | 98 ++++++ 19 files changed, 1244 insertions(+) create mode 100644 sys/entropy_source/Kconfig create mode 100644 sys/entropy_source/Makefile create mode 100644 sys/entropy_source/adc_noise/Kconfig create mode 100755 sys/entropy_source/adc_noise/Makefile create mode 100644 sys/entropy_source/adc_noise/adc_noise.c create mode 100644 sys/entropy_source/doc.txt create mode 100644 sys/entropy_source/entropy_source.c create mode 100644 sys/entropy_source/zero_entropy/Kconfig create mode 100755 sys/entropy_source/zero_entropy/Makefile create mode 100644 sys/entropy_source/zero_entropy/zero_entropy.c create mode 100644 sys/include/entropy_source.h create mode 100644 sys/include/entropy_source/adc_noise.h create mode 100644 sys/include/entropy_source/zero_entropy.h create mode 100644 tests/entropy_source/Makefile create mode 100644 tests/entropy_source/Makefile.board.dep create mode 100644 tests/entropy_source/README.md create mode 100644 tests/entropy_source/main.c diff --git a/sys/Kconfig b/sys/Kconfig index b544ea23f9..c13626c66b 100644 --- a/sys/Kconfig +++ b/sys/Kconfig @@ -12,6 +12,7 @@ rsource "benchmark/Kconfig" rsource "color/Kconfig" rsource "div/Kconfig" rsource "embunit/Kconfig" +rsource "entropy_source/Kconfig" rsource "event/Kconfig" rsource "fmt/Kconfig" rsource "isrpipe/Kconfig" diff --git a/sys/Makefile.dep b/sys/Makefile.dep index e8c33fffdf..b028fbde5a 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -706,6 +706,13 @@ ifneq (,$(filter conn_can,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter entropy_source_%,$(USEMODULE))) + USEMODULE += entropy_source + ifneq (,$(filter entropy_source_adc_noise,$(USEMODULE))) + FEATURES_REQUIRED += periph_adc + endif +endif + ifneq (,$(filter puf_sram,$(USEMODULE))) USEMODULE += hashes USEMODULE += random diff --git a/sys/entropy_source/Kconfig b/sys/entropy_source/Kconfig new file mode 100644 index 0000000000..69e925cb2b --- /dev/null +++ b/sys/entropy_source/Kconfig @@ -0,0 +1,37 @@ +# 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. + +menuconfig KCONFIG_USEMODULE_ENTROPY_SOURCE + bool "Configure entropy sources" + depends on USEMODULE_ENTROPY_SOURCE_ADC_NOISE || USEMODULE_ENTROPY_SOURCE_ZERO_ENTROPY + help + Configure entropy sources using Kconfig. + +if KCONFIG_USEMODULE_ENTROPY_SOURCE + +config ENTROPY_SOURCE_TESTS_WIN + int "Window size for Adaptive Proportion Test" + default 512 + depends on ENTROPY_SOURCE_ADC_HEALTH_TEST || ENTROPY_SOURCE_ZERO_HEALTH_TEST + help + In (NIST SP 800-90B 4.4.2) a window size of 512 samples is recommended for non-binary + sources. Typically, RIOT use cases will not request as many samples, thus, it might be worth + considering a smaller window size so the test is more likely to complete a cycle. It is + noteworthy that a cutoff value calculated by @ref entropy_source_test_prop_cutoff that is + greater than the window size may lead to undetected errors. + +config ENTROPY_SOURCE_NEUMANN_ABORT + int "Abort factor for von Neumann extractor" + default 5 + help + Abort factor for von Neumann extractor. The algorithms runs as long as no bit + changes appear in subsequent samples. This define adds a factor that + aborts the procedure after (factor * requested length) samples. + +rsource "adc_noise/Kconfig" +rsource "zero_entropy/Kconfig" + +endif # KCONFIG_USEMODULE_ENTROPY_SOURCE diff --git a/sys/entropy_source/Makefile b/sys/entropy_source/Makefile new file mode 100644 index 0000000000..392eaf16ef --- /dev/null +++ b/sys/entropy_source/Makefile @@ -0,0 +1,9 @@ +ifneq (,$(filter entropy_source_zero_entropy,$(USEMODULE))) + DIRS += zero_entropy +endif + +ifneq (,$(filter entropy_source_adc_noise,$(USEMODULE))) + DIRS += adc_noise +endif + +include $(RIOTBASE)/Makefile.base diff --git a/sys/entropy_source/adc_noise/Kconfig b/sys/entropy_source/adc_noise/Kconfig new file mode 100644 index 0000000000..11d7c3c585 --- /dev/null +++ b/sys/entropy_source/adc_noise/Kconfig @@ -0,0 +1,77 @@ +# 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. + +menuconfig KCONFIG_USEMODULE_ENTROPY_SOURCE_ADC_NOISE + bool "Configure ADC Noise entropy source module" + depends on USEMODULE_ENTROPY_SOURCE_ADC_NOISE + help + Configure the ADC Noise entropy source using Kconfig. + +if KCONFIG_USEMODULE_ENTROPY_SOURCE_ADC_NOISE + +choice + bool "ADC default sampling resolution" + default ENTROPY_SOURCE_ADC_RES_10BIT + help + This parameter sets the ADC sampling resolution. Please note that not all + platforms support every value. + +config ENTROPY_SOURCE_ADC_RES_6BIT + bool "6 Bit" + +config ENTROPY_SOURCE_ADC_RES_8BIT + bool "8 Bit" + +config ENTROPY_SOURCE_ADC_RES_10BIT + bool "10 Bit" + +config ENTROPY_SOURCE_ADC_RES_12BIT + bool "12 Bit" + +config ENTROPY_SOURCE_ADC_RES_14BIT + bool "14 Bit" + +config ENTROPY_SOURCE_ADC_RES_16BIT + bool "16 Bit" + +endchoice + +config ENTROPY_SOURCE_ADC_LINE_NUM + int "ADC line" + range 0 16 + default 0 + help + The ADC line maps to an I/O pin. This number acts as index to an array + of predefined ADC devices that contain the pin definition. Typically, + the array is defined by a board in a periph_conf.h file. Please note that + a board is not required to specify a minimum number of lines. + +config ENTROPY_SOURCE_ADC_HMIN + int "Estimated entropy per sample [2^16 * bit/sample]" + range 1 524288 + default 0 + help + The entropy value needs to be estimated and evaluated thoroughly before + deployment! To avoid float, the entropy value per one byte sample needs + to be manually multiplied by 2^16 before configuring it (e.g., to + an entropy value of 1 bit/sample, a value of 1 * 65536 needs to be set) . + We default to zero which is an invalid configuration to enforce a + thoughtful investigation on the actual entropy properties. + +config ENTROPY_SOURCE_ADC_HEALTH_TEST + bool "Enable health test" + help + Health tests are performed on every sample, if enabled. Thus, they + slow down the entropy gathering process. Detected failures are reported by return + value but they do not stop execution. + +config ENTROPY_SOURCE_ADC_COND + bool "Enable conditioning" + help + Conditioning increases runtime of the entropy generation process. Currently, a von + Neumann extractor is involved which has an nondeterministic runtime. + +endif # KCONFIG_USEMODULE_ENTROPY_SOURCE_ADC_NOISE diff --git a/sys/entropy_source/adc_noise/Makefile b/sys/entropy_source/adc_noise/Makefile new file mode 100755 index 0000000000..14f20ee344 --- /dev/null +++ b/sys/entropy_source/adc_noise/Makefile @@ -0,0 +1,3 @@ +MODULE := entropy_source_adc_noise + +include $(RIOTBASE)/Makefile.base diff --git a/sys/entropy_source/adc_noise/adc_noise.c b/sys/entropy_source/adc_noise/adc_noise.c new file mode 100644 index 0000000000..f243a77363 --- /dev/null +++ b/sys/entropy_source/adc_noise/adc_noise.c @@ -0,0 +1,90 @@ +/* + * 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. + */ +/** + * @ingroup sys_entropy_source_adc + * + * @{ + * @file + * + * @author Peter Kietzmann + * + * @} + */ + +#include "periph/adc.h" +#include "entropy_source.h" +#include "entropy_source/adc_noise.h" + +entropy_source_tests_rep_t adc_state_rep; +entropy_source_tests_prop_t adc_state_prop; + +static int _get_sample(uint8_t *out) +{ + int ret = ENTROPY_SOURCE_OK; + uint8_t byte = 0; + + for (unsigned bit = 0; bit < 8; bit++) { + int sample = adc_sample(CONFIG_ENTROPY_SOURCE_ADC_LINE, + CONFIG_ENTROPY_SOURCE_ADC_RES); + if (sample < 0) { + /* Resolution is not applicable */ + return ENTROPY_SOURCE_ERR_CONFIG; + } + /* use LSB of each ADC sample and shift it to build a value */ + byte |= (uint8_t)(sample & 0x01) << bit; + } + /* copy generated byte to out */ + *out = byte; + + if (IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_HEALTH_TEST)) { + ret = entropy_source_test(&adc_state_rep, &adc_state_prop, byte); + } + + return ret; +} + +int entropy_source_adc_init(void) +{ + /* init ADC */ + if (adc_init(CONFIG_ENTROPY_SOURCE_ADC_LINE) != 0) { + return ENTROPY_SOURCE_ERR_INIT; + } + + if (IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_HEALTH_TEST)) { + unsigned int cutoff; + cutoff = entropy_source_test_rep_cutoff(CONFIG_ENTROPY_SOURCE_ADC_HMIN); + entropy_source_test_rep_init(&adc_state_rep, cutoff); + cutoff = entropy_source_test_prop_cutoff(CONFIG_ENTROPY_SOURCE_ADC_HMIN); + entropy_source_test_prop_init(&adc_state_prop, cutoff); + } + + return ENTROPY_SOURCE_OK; +} + +int entropy_source_adc_get(uint8_t *out, size_t len) +{ + assert(out != NULL); + + int ret = ENTROPY_SOURCE_OK; + + if (IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_COND)) { + entropy_source_sample_func_t p = &_get_sample; + ret = entropy_source_neumann_unbias(p, out, len); + } + else { + for (unsigned iter = 0; iter < len; iter++) { + int tmp = _get_sample(&out[iter]); + /* Remember the worst failure during + * sampling multiple values to return */ + if (tmp < ret) { + ret = tmp; + } + } + } + return ret; +} diff --git a/sys/entropy_source/doc.txt b/sys/entropy_source/doc.txt new file mode 100644 index 0000000000..b5671b9f24 --- /dev/null +++ b/sys/entropy_source/doc.txt @@ -0,0 +1,46 @@ +/* + * 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_entropy_source Entropy Sources + * @ingroup sys + * @brief Collection of different Entropy Sources + * @warning Entropy sources need to be thoroughly evaluated before deployment! + * @experimental This API is in an early state - expect changes. + * @note This API is considered as internal. Only use it if you know what + * you are doing and expect API changes without deprecation. + * + * + * @brief Collection of entropy sources. + * + * + * This module adds support for additional entropy sources next to pure peripheral sources like + * @ref drivers_periph_hwrng and @ref sys_puf_sram. The concepts implemented here are heavily + * influenced by NIST SP 800-90B. Entropy sources can be used to feed more advanced entropy + * modules for cryptographic purposes, which typically accumulate multiple sources and safely + * maintain internal sates. Alternatively, these sources can be used directly or with internal + * conditioning enabled for non-cryptographic tasks like seed generation of general purpose PRNGs, + * in the absence of a hardware random number generator. The API, however, is not meant to face a user. + * + * A common component provides optional access to health tests and conditioning + * (@ref sys_entropy_source_config) that can be run on + * parallel instantiations. The conditioning currently implements a von Neumann extractor to + * unbias samples. It adds a variable runtime (dependent on the input samples) but is lightweight. + * In future, other conditioning mechanisms such as hash based derivation functions might be + * included. + * + * Entropy sources require thorough testing and evaluation for serious deployments which is out of + * scope of this module, and we refer to SP800-90B_EntropyAssessment for validation. Among other metrics, + * this tool will return an entropy estimation per sample that should be employed for every + * deployment scenario and must be configured in software accordingly. In the specific case + * of the ADC based entropy source, a developer needs to set @ref CONFIG_ENTROPY_SOURCE_ADC_HMIN + * accordingly. + * + */ diff --git a/sys/entropy_source/entropy_source.c b/sys/entropy_source/entropy_source.c new file mode 100644 index 0000000000..7183f9fe62 --- /dev/null +++ b/sys/entropy_source/entropy_source.c @@ -0,0 +1,125 @@ +/* + * 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. + */ +/** + * @ingroup sys_entropy_source_common + * + * @{ + * @file + * + * @author Peter Kietzmann + * + * @} + */ + +#include +#include "entropy_source.h" + +int entropy_source_neumann_unbias(entropy_source_sample_func_t func, + uint8_t *out, size_t len) +{ + assert(func != NULL && out != NULL); + + uint8_t old_sample, new_sample, sample_out = 0; + uint8_t bit1, bit2, bit_pos = 0; + size_t bytes_count = 0; + size_t sample_count = 0; + size_t abort = len * CONFIG_ENTROPY_SOURCE_NEUMANN_ABORT; + int tmp, ret = ENTROPY_SOURCE_OK; + + /* Get initial sample */ + tmp = func(&old_sample); + + /* Only return in case of failed configuration */ + if (ret == ENTROPY_SOURCE_ERR_CONFIG) { + return ret; + } + + while (bytes_count < len) { + /* Increment sample counter and abort if + * exceeds maximum number of iterations */ + sample_count++; + if (sample_count > abort) { + return ENTROPY_SOURCE_ERR_COND; + } + + /* Get next sample */ + tmp = func(&new_sample); + + /* Remember the worst failure during + * sampling multiple values to return */ + if (tmp < ret) { + ret = tmp; + } + + /* Iterate each bit in sample */ + for (unsigned j = 0; j < 8; j++) { + bit1 = (1 << j) & old_sample; + bit2 = (1 << j) & new_sample; + + /* Only save information if change occurred + * 0/1 change results in 1 + * 1/0 change results in 0 + */ + if (bit1 < bit2) { + sample_out |= (1 << bit_pos++); + } + else if (bit1 > bit2) { + sample_out &= ~(1 << bit_pos++); + } + + /* Once 8 bits have been gathered, write to output */ + if (bit_pos == 8) { + out[bytes_count] = sample_out; + bit_pos = 0; + bytes_count++; + } + } + /* Store recent sample for next iteration */ + old_sample = new_sample; + } + + return ret; +} + +int entropy_source_test_rep(entropy_source_tests_rep_t *state, uint8_t sample) +{ + assert(state != NULL); + + if (sample == state->old_sample && state->cnt_rep > 0) { + state->cnt_rep++; + if (state->cnt_rep >= state->c_rep) { + return ENTROPY_SOURCE_ERR_TEST_REP; + } + } + else { + state->old_sample = sample; + state->cnt_rep = 1; + } + return ENTROPY_SOURCE_OK; +} + +int entropy_source_test_prop(entropy_source_tests_prop_t *state, uint8_t sample) +{ + assert(state != NULL); + + if (state->cnt_window == CONFIG_ENTROPY_SOURCE_TESTS_WIN) { + state->old_sample = sample; + state->cnt_prop = 0; + state->cnt_window = 0; + } + else { + state->cnt_window++; + if (state->old_sample == sample) { + state->cnt_prop++; + } + if (state->cnt_prop >= state->c_prop) { + return ENTROPY_SOURCE_ERR_TEST_PROP; + } + } + return ENTROPY_SOURCE_OK; +} diff --git a/sys/entropy_source/zero_entropy/Kconfig b/sys/entropy_source/zero_entropy/Kconfig new file mode 100644 index 0000000000..9bbdacfb5f --- /dev/null +++ b/sys/entropy_source/zero_entropy/Kconfig @@ -0,0 +1,35 @@ +# 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. + +menuconfig KCONFIG_USEMODULE_ENTROPY_SOURCE_ZERO_ENTROPY + bool "Configure zero entropy source module" + depends on USEMODULE_ENTROPY_SOURCE_ZERO_ENTROPY + help + Configure the zero entropy source using Kconfig. + +if KCONFIG_USEMODULE_ENTROPY_SOURCE_ZERO_ENTROPY + +config ENTROPY_SOURCE_ZERO_HMIN + int "Estimated entropy per sample (byte)" + default 58982 + help + This is a dummy default value for testing. The zero entropy module does not + contain any entropy at all. + +config ENTROPY_SOURCE_ZERO_HEALTH_TEST + bool "Enable health test" + default y + help + Enable health test by default. Testing is the only purpose of this module. + +config ENTROPY_SOURCE_ZERO_COND + bool "Enable conditioning" + help + Disable conditioning by default. Conditioning is useless for zeros only. The von + Neumann extractor would never finish and wait for the stop criterion given by + @ref CONFIG_ENTROPY_SOURCE_NEUMANN_ABORT. + +endif # KCONFIG_USEMODULE_ENTROPY_SOURCE_ZERO_ENTROPY diff --git a/sys/entropy_source/zero_entropy/Makefile b/sys/entropy_source/zero_entropy/Makefile new file mode 100755 index 0000000000..0e7d9d7e49 --- /dev/null +++ b/sys/entropy_source/zero_entropy/Makefile @@ -0,0 +1,3 @@ +MODULE := entropy_source_zero_entropy + +include $(RIOTBASE)/Makefile.base diff --git a/sys/entropy_source/zero_entropy/zero_entropy.c b/sys/entropy_source/zero_entropy/zero_entropy.c new file mode 100644 index 0000000000..a688f02d51 --- /dev/null +++ b/sys/entropy_source/zero_entropy/zero_entropy.c @@ -0,0 +1,72 @@ +/* + * 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. + */ +/** + * @ingroup sys_entropy_source_zero + * @{ + * @file + * + * @author Peter Kietzmann + * + * @} + */ + +#include "kernel_defines.h" +#include "entropy_source.h" +#include "entropy_source/zero_entropy.h" + +entropy_source_tests_rep_t zero_state_rep; +entropy_source_tests_prop_t zero_state_prop; + +static int _get_sample(uint8_t *out) +{ + int ret = ENTROPY_SOURCE_OK; + uint8_t byte = 0; + + *out = byte; + + if (IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ZERO_HEALTH_TEST)) { + ret = entropy_source_test(&zero_state_rep, &zero_state_prop, byte); + } + + return ret; +} + +int entropy_source_zero_init(void) +{ + if (IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ZERO_HEALTH_TEST)) { + unsigned int cutoff; + cutoff = entropy_source_test_rep_cutoff(CONFIG_ENTROPY_SOURCE_ZERO_HMIN); + entropy_source_test_rep_init(&zero_state_rep, cutoff); + cutoff = entropy_source_test_prop_cutoff(CONFIG_ENTROPY_SOURCE_ZERO_HMIN); + entropy_source_test_prop_init(&zero_state_prop, cutoff); + } + + return ENTROPY_SOURCE_OK; +} + +int entropy_source_zero_get(uint8_t *out, size_t len) +{ + assert(out != NULL); + + int ret = ENTROPY_SOURCE_OK; + + if (IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ZERO_COND)) { + ret = entropy_source_neumann_unbias(_get_sample, out, len); + } + else { + for (unsigned iter = 0; iter < len; iter++) { + int tmp = _get_sample(&out[iter]); + /* Remember the worst failure during + * sampling multiple values to return */ + if (tmp < ret) { + ret = tmp; + } + } + } + return ret; +} diff --git a/sys/include/entropy_source.h b/sys/include/entropy_source.h new file mode 100644 index 0000000000..6e30609d7d --- /dev/null +++ b/sys/include/entropy_source.h @@ -0,0 +1,306 @@ +/* + * 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_entropy_source_common Entropy Source Common + * @ingroup sys_entropy_source + * @brief Common definitions and functions for entropy sources + * + * + * @{ + * @file + * + * @author Peter Kietzmann + */ + +#ifndef ENTROPY_SOURCE_H +#define ENTROPY_SOURCE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + * @brief Entropy source error codes. + */ +typedef enum { + ENTROPY_SOURCE_OK = 0, /**< Success */ + ENTROPY_SOURCE_ERR_INIT = -1, /**< Source initialization error */ + ENTROPY_SOURCE_ERR_CONFIG = -2, /**< Source configuration error */ + ENTROPY_SOURCE_ERR_TEST_REP = -3, /**< Repetition count test error */ + ENTROPY_SOURCE_ERR_TEST_PROP = -4, /**< Adaptive proportion test error */ + ENTROPY_SOURCE_ERR_TEST_BOTH = -5, /**< Repetition count and Adaptive + * proportion test error */ + ENTROPY_SOURCE_ERR_COND = -6, /**< Conditioning error */ +} entropy_source_error_t; + +/** + * @brief Data structure for Repetition Count Test (NIST SP 800-90B 4.4.1). + */ +typedef struct { + uint8_t old_sample; /**< Preceding sample to compare for repetition */ + uint16_t cnt_rep; /**< Counter to count repetition */ + uint8_t c_rep; /**< Cutoff threshold */ +} entropy_source_tests_rep_t; + +/** + * @brief Data structure for Adaptive Proportion Test (NIST SP 800-90B 4.4.2). + */ +typedef struct { + uint8_t old_sample; /**< Preceding sample to compare for repetition */ + uint16_t cnt_prop; /**< Counter to count proportion */ + uint16_t cnt_window; /**< Counter to count window size */ + uint16_t c_prop; /**< Cutoff threshold */ +} entropy_source_tests_prop_t; + +/** + * @brief Scale Min. Entropy to fixed point integer to avoid float. The + * entropy per sample (8 Byte) of a noise source can likely be smaller + * than 1 bit. + */ +#define ENTROPY_SOURCE_HMIN_SCALE(x) ((x * (1UL << 16))) + +/** + * @brief Scale internal fixed point Min. Entropy back to float. This macro is + * not required and only there for convenience. + */ +#define ENTROPY_SOURCE_HMIN_SCALE_BACK(x) ((float)x / (1UL << 16)) + +/** + * @defgroup sys_entropy_source_config Entropy Source compile configurations + * @ingroup config + * @{ + */ +/** + * @brief Window size for Adaptive Proportion Test (NIST SP 800-90B 4.4.2). + * + * In (NIST SP 800-90B 4.4.2) a window size of 512 samples is recommended for + * non-binary sources. Typically, RIOT use cases will not request as many + * samples, thus, it might be worth considering a smaller window size so the + * test is more likely to complete a cycle. It is noteworthy that a cutoff value + * calculated by @ref entropy_source_test_prop_cutoff that is greater than the + * window size may lead to undetected errors. + */ +#ifndef CONFIG_ENTROPY_SOURCE_TESTS_WIN +#define CONFIG_ENTROPY_SOURCE_TESTS_WIN (512) +#endif + +/** + * @brief Abort factor for von Neumann extractor. The algorithms runs as long + * as no bit changes appear in subsequent samples. This define adds a + * factor that aborts the procedure after (factor * requested length) + * samples. + */ +#ifndef CONFIG_ENTROPY_SOURCE_NEUMANN_ABORT +#define CONFIG_ENTROPY_SOURCE_NEUMANN_ABORT (5) +#endif +/** @} */ + +/** + * @brief Get one sample of the entropy source. + * + * This function is typically used by the entropy source internally. A + * conditioning component might need an interface to request a variable number + * of samples, e.g., depending on the contained amount of entropy. + * + * @param[out] sample pointer to write sample to. + * + * @return ENTROPY_SOURCE_OK on success + * @return negative @ref entropy_source_error_t code on error + */ +typedef int (*entropy_source_sample_func_t)(uint8_t *sample); + +/** + * @brief Applies von Neumann unbiasing. + * + * This function requests as many samples needed to create \p len unbiased bytes + * using \p func. The algorithm compares bits of consecutive samples. Only bit + * changes will be considered for the output value. An abort criterium stops + * sampling after (len * CONFIG_ENTROPY_SOURCE_NEUMANN_ABORT) iterations. + * + * @warning This function has a non-deterministic runtime. + * + * @param[in] func pointer to @ref entropy_source_sample_func_t function + that returns samples + * @param[out] out pointer to write unbiased bytes to + * @param[in] len number of bytes to generate + * + * @return ENTROPY_SOURCE_OK on success + * @return negative @ref entropy_source_error_t code on error + */ +int entropy_source_neumann_unbias(entropy_source_sample_func_t func, + uint8_t *out, size_t len); + +/** + * @brief Calculate cutoff value for Repetition Count Test (NIST SP 800-90B 4.4.1) + * + *~~~~ + * C = 1 + ( (-log2 a) / H) + *~~~~ + * + * C: Cutoff value. + * H: Min. entropy of the source + * + * SP800-90B EntropyAssessment. + * a: Probability of type I error. We assume 2^(-20). + * + * @param[in] entropy_per_sample Estimated min. entropy of one sample scaled + * by ENTROPY_SOURCE_HMIN_SCALE() + * + * @return Cutoff threshold + */ +static inline uint32_t entropy_source_test_rep_cutoff(uint32_t entropy_per_sample) +{ + return (1 + ((20 * 65536) / entropy_per_sample)); +} + +/** + * @brief Calculate cutoff value for Adaptive Proportion Test + * (NIST SP 800-90B 4.4.2) + * + * @param[in] entropy_per_sample Estimated min. entropy of one sample scaled + * by ENTROPY_SOURCE_HMIN_SCALE() + * + * @return Cutoff value + * @return ENTROPY_SOURCE_ERR_CONFIG if parameter + invalid + */ +static inline int entropy_source_test_prop_cutoff(uint32_t entropy_per_sample) +{ + int ret; + + if (entropy_per_sample < 49152UL) { /* 0.75 bit/sample */ + ret = 410; + } + else if (entropy_per_sample < 98304UL) { /* 1.5 bit/sample */ + ret = 311; + } + else if (entropy_per_sample < 196608UL) { /* 3 bit/sample */ + ret = 177; + } + else if (entropy_per_sample < 393216UL) { /* 6 bit/sample */ + ret = 62; + } + else if (entropy_per_sample <= 524288UL) { /* 8 bit/sample */ + ret = 13; + } + else { + ret = ENTROPY_SOURCE_ERR_CONFIG; + } + + return ret; +} + +/** + * @brief Initialize structure for Repetition Count Test + * + * @param[in, out] state Test structure of one entropy source. + * @param[in] c_rep Cutoff value calculated by + * @ref entropy_source_test_rep_cutoff. + */ +static inline void entropy_source_test_rep_init( + entropy_source_tests_rep_t *state, uint16_t c_rep) +{ + assert(state != NULL); + + state->old_sample = 0; + state->cnt_rep = 0; + state->c_rep = c_rep; +} + +/** + * @brief Initialize structure for Adaptive Proportion Test + * + * @param[in, out] state Test structure of one entropy source. + * @param[in] c_prop Cutoff value calculated by + * @ref entropy_source_test_prop_cutoff. + */ +static inline void entropy_source_test_prop_init( + entropy_source_tests_prop_t *state, uint16_t c_prop) +{ + assert(state != NULL); + + state->old_sample = 0; + state->cnt_prop = 0; + state->cnt_window = CONFIG_ENTROPY_SOURCE_TESTS_WIN; + state->c_prop = c_prop; +} + +/** + * @brief Performs Repetition Count Test (NIST SP 800-90B 4.4.1). + * + * This function will not block sampling. It only indicates detected errors. + * + * @param[in, out] state Test structure of one entropy source. + * @param[in] sample Current sample. + * + * @return ENTROPY_SOURCE_OK on success + * @return ENTROPY_SOURCE_ERR_TEST_REP on detected weakness + */ +int entropy_source_test_rep(entropy_source_tests_rep_t *state, uint8_t sample); + +/** + * @brief Performs Adaptive Proportion Test (NIST SP 800-90B 4.4.2). + * + * This function will not block the sampling. It only indicates detected errors. + * + * @param[in, out] state Test structure of one entropy source. + * @param[in] sample current sample. + * + * @return ENTROPY_SOURCE_OK on success + * @return ENTROPY_SOURCE_ERR_TEST_PROP on detected weakness + */ +int entropy_source_test_prop(entropy_source_tests_prop_t *state, + uint8_t sample); + +/** + * @brief Convenience function to perform @ref entropy_source_test_rep + * and @ref entropy_source_test_prop. + * + * This function will not block the sampling. It only indicates detected errors. + * + * @param[in, out] state_rep Repetition Count test structure of one + * entropy source. + * @param[in, out] state_prop Adaptive Proportion test structure of one + * entropy source. + * @param[in] sample Current sample. + * + * @return ENTROPY_SOURCE_OK on success + * @return negative @ref entropy_source_error_t code on error + */ +static inline int entropy_source_test(entropy_source_tests_rep_t *state_rep, + entropy_source_tests_prop_t *state_prop, + uint8_t sample) +{ + int ret = ENTROPY_SOURCE_OK; + + if (entropy_source_test_rep(state_rep, sample) < 0) { + ret = ENTROPY_SOURCE_ERR_TEST_REP; + } + if (entropy_source_test_prop(state_prop, sample) < 0) { + /* If repetition count failed before, indicate that both tests failed */ + if (ret == ENTROPY_SOURCE_ERR_TEST_REP) { + ret = ENTROPY_SOURCE_ERR_TEST_BOTH; + } + else { + ret = ENTROPY_SOURCE_ERR_TEST_PROP; + } + } + return ret; +} + +#ifdef __cplusplus +} +#endif + +#endif /* ENTROPY_SOURCE_H */ +/** @} */ diff --git a/sys/include/entropy_source/adc_noise.h b/sys/include/entropy_source/adc_noise.h new file mode 100644 index 0000000000..5fcef460fc --- /dev/null +++ b/sys/include/entropy_source/adc_noise.h @@ -0,0 +1,158 @@ +/* + * 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_entropy_source_adc ADC Noise Entropy Source + * @ingroup sys_entropy_source + * @brief Entropy Source based on LSB of ADC samples + * + * + * This module provides entropy from ADC samples. Thereby, only the LSB is considered for + * generation of output values. The available resolution as well as internal sampling rates, + * specific driver settings and device inaccuracies may lead to different behavior between + * different platforms. The configured ADC pin might be unconnected and floating, + * it can be exposed as an I/O pin or internally connected to a thermal noise source, an + * avalanche diode circuit or the receive path of an antenna. The possibilities are manifold, + * thus, configuration and deployment properties of this module need to be thoroughly + * validated. + * + * @note It is worth noting that ADC pins are typically vulnerable when physically + * accessible. Developers should consider additional tamper detection concepts + * and enclosures. + * + * @{ + * @file + * + * @author Peter Kietzmann + */ + +#ifndef ENTROPY_SOURCE_ADC_NOISE_H +#define ENTROPY_SOURCE_ADC_NOISE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "periph/adc.h" + +/** + * @ingroup sys_entropy_source_config + * @{ + */ +/** + * @brief ADC resolution default configuration. + */ +#if IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_RES_6BIT) +#define CONFIG_ENTROPY_SOURCE_ADC_RES ADC_RES_6BIT +#elif IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_RES_8BIT) +#define CONFIG_ENTROPY_SOURCE_ADC_RES ADC_RES_8BIT +#elif IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_RES_10BIT) +#define CONFIG_ENTROPY_SOURCE_ADC_RES ADC_RES_10BIT +#elif IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_RES_12BIT) +#define CONFIG_ENTROPY_SOURCE_ADC_RES ADC_RES_12BIT +#elif IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_RES_14BIT) +#define CONFIG_ENTROPY_SOURCE_ADC_RES ADC_RES_14BIT +#elif IS_ACTIVE(CONFIG_ENTROPY_SOURCE_ADC_RES_16BIT) +#define CONFIG_ENTROPY_SOURCE_ADC_RES ADC_RES_16BIT +#endif +#ifndef CONFIG_ENTROPY_SOURCE_ADC_RES +#define CONFIG_ENTROPY_SOURCE_ADC_RES ADC_RES_10BIT +#endif + +/** + * @brief ADC line default configuration. + * + * The ADC line maps to an I/O pin. This number acts as index to an array + * of predefined ADC devices that contain the pin definition. Typically, + * the array is defined by a board in a periph_conf.h file. Please note that + * a board is not required to specify a minimum number of lines. + */ +#ifdef CONFIG_ENTROPY_SOURCE_ADC_LINE_NUM +#define CONFIG_ENTROPY_SOURCE_ADC_LINE ADC_LINE(CONFIG_ENTROPY_SOURCE_ADC_LINE_NUM) +#else +#define CONFIG_ENTROPY_SOURCE_ADC_LINE ADC_LINE(0) +#endif + +/** + * @brief ADC estimated entropy per sample [2^16 * bit/sample]. + * + * The entropy value needs to be estimated and evaluated thoroughly beforehand + * deployment! To avoid float, the + * actual entropy value per one byte sample needs to be manually multiplied by + * 2^16 before before configuring it (e.g., to + * an entropy value of 1 bit/sample, a value of 1 * 65536 needs to be set) . + * We default to zero which is an invalid configuration to enforce a + * thoughtful investigation on the actual entropy properties. See + * @ref sys_entropy_source for further information about entropy source + * validation. + */ +#if !defined(CONFIG_KCONFIG_USEMODULE_ENTROPY_SOURCE_ADC_NOISE) || defined(DOXYGEN) +#ifndef CONFIG_ENTROPY_SOURCE_ADC_HMIN +#define CONFIG_ENTROPY_SOURCE_ADC_HMIN (0) /**< H_min=0 bit/sample * 2^16 + * is invalid and needs to + * set manually! + */ +#endif /* !CONFIG_ENTROPY_SOURCE_ADC_HMIN */ + +#ifndef CONFIG_ENTROPY_SOURCE_ADC_HEALTH_TEST +#define CONFIG_ENTROPY_SOURCE_ADC_HEALTH_TEST 0 /**< Disable ADC health test + * by default. + */ +#endif +#ifndef CONFIG_ENTROPY_SOURCE_ADC_COND +#define CONFIG_ENTROPY_SOURCE_ADC_COND 0 /**< Disable ADC conditioning + * test by default. + */ +#endif +#endif /* !CONFIG_KCONFIG_USEMODULE_ENTROPY_SOURCE_ADC_NOISE || DOXYGEN */ + +/* Throw warning if H_min has not been re-configured */ +#if !CONFIG_ENTROPY_SOURCE_ADC_HMIN +#warning The min. provided entropy must be set before using this module +#endif +/** @} */ + +/** + * @brief Initialize ADC and test structures, if tests are enabled. + * + * @return ENTROPY_SOURCE_OK on success + * @return ENTROPY_SOURCE_ERR_INIT on ADC initialization failure + */ +int entropy_source_adc_init(void); + +/** + * @brief Generates bytes from ADC noise + * + * @param[out] buf pointer to write noisy bytes to + * @param[in] len number of bytes to generate + * + * @return ENTROPY_SOURCE_OK on success + * @return negative @ref entropy_source_error_t code on error + */ +int entropy_source_adc_get(uint8_t *buf, size_t len); + +/** + * @brief Static entropy per sample value for this source + * [bit/sample * 2^16] + * + * @return entropy per sample + */ +static inline uint32_t entropy_source_adc_entropy_per_sample(void) +{ + return CONFIG_ENTROPY_SOURCE_ADC_HMIN; +} + +#ifdef __cplusplus +} +#endif + +#endif /* ENTROPY_SOURCE_ADC_NOISE_H */ +/** @} */ diff --git a/sys/include/entropy_source/zero_entropy.h b/sys/include/entropy_source/zero_entropy.h new file mode 100644 index 0000000000..aa76158a53 --- /dev/null +++ b/sys/include/entropy_source/zero_entropy.h @@ -0,0 +1,92 @@ +/* + * 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_entropy_source_zero Zero Entropy Source + * @ingroup sys_entropy_source + * @brief Zero Entropy Source for testing. + * + * This module produces zeros only and should be used for testing purposes only. + * + * @{ + * @file + * + * @author Peter Kietzmann + */ + +#ifndef ENTROPY_SOURCE_ZERO_ENTROPY_H +#define ENTROPY_SOURCE_ZERO_ENTROPY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @ingroup sys_entropy_source_config + * @{ + */ + +/** + * @brief Min. Entropy value for zero entropy module. + * + * H_min=0.9 bit/sample * 2^16 fake for testing! + */ +#if !defined(CONFIG_KCONFIG_USEMODULE_ENTROPY_SOURCE_ZERO_ENTROPY) || defined(DOXYGEN) +#ifndef CONFIG_ENTROPY_SOURCE_ZERO_ENTROPY_HMIN +#define CONFIG_ENTROPY_SOURCE_ZERO_HMIN (58982) +#endif + +/** + * @brief Enable health test by default. + * + * Testing is the only purpose of this module. + */ +#ifndef CONFIG_ENTROPY_SOURCE_ZERO_HEALTH_TEST +#define CONFIG_ENTROPY_SOURCE_ZERO_HEALTH_TEST 1 +#endif + +/** + * @brief Disable conditioning by default. + * + * Conditioning is useless for zeros only. The von Neumann extractor would + * never finish and wait for the stop criterion given by + * @ref CONFIG_ENTROPY_SOURCE_NEUMANN_ABORT. + */ +#ifndef CONFIG_ENTROPY_SOURCE_ZERO_COND +#define CONFIG_ENTROPY_SOURCE_ZERO_COND 0 +#endif +#endif /* !CONFIG_KCONFIG_USEMODULE_ENTROPY_SOURCE_ZERO_ENTROPY || DOXYGEN */ +/** @} */ + +/** + * @brief Initializes test structures, if tests are enabled. + * + * @return ENTROPY_SOURCE_OK always + */ +int entropy_source_zero_init(void); + +/** + * @brief Generates zeros. + * + * @param[out] buf pointer to write zeros to + * @param[in] len number of bytes to generate + * + * @return ENTROPY_SOURCE_OK on success + * @return negative @ref entropy_source_error_t code on error + */ +int entropy_source_zero_get(uint8_t *buf, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* ENTROPY_SOURCE_ZERO_ENTROPY_H */ +/** @} */ diff --git a/tests/entropy_source/Makefile b/tests/entropy_source/Makefile new file mode 100644 index 0000000000..2375780b95 --- /dev/null +++ b/tests/entropy_source/Makefile @@ -0,0 +1,10 @@ +DEVELHELP ?= 0 +include ../Makefile.tests_common + +BOARD ?= native + +USEMODULE += fmt +USEMODULE += entropy_source_zero_entropy +USEMODULE += xtimer + +include $(RIOTBASE)/Makefile.include diff --git a/tests/entropy_source/Makefile.board.dep b/tests/entropy_source/Makefile.board.dep new file mode 100644 index 0000000000..2c602ff2ea --- /dev/null +++ b/tests/entropy_source/Makefile.board.dep @@ -0,0 +1,13 @@ +# Only include adc noise source if BOARD has an adc +FEATURES_OPTIONAL += periph_adc + +ifneq (,$(filter periph_adc,$(FEATURES_USED))) + USEMODULE += entropy_source_adc_noise + ifndef CONFIG_KCONFIG_USEMODULE_ENTROPY_SOURCE_ADC_NOISE + # set a dummy default value for the provided amount of entropy + CFLAGS += -DCONFIG_ENTROPY_SOURCE_ADC_HMIN=65536 + # enable ADC health tests and conditioning + CFLAGS += -DCONFIG_ENTROPY_SOURCE_ADC_COND=1 + CFLAGS += -DCONFIG_ENTROPY_SOURCE_ADC_HEALTH_TEST=1 + endif +endif diff --git a/tests/entropy_source/README.md b/tests/entropy_source/README.md new file mode 100644 index 0000000000..abf5ab471f --- /dev/null +++ b/tests/entropy_source/README.md @@ -0,0 +1,62 @@ +# About +This test compiles and runs entropy sources. The first `zero entropy` source can be run on +`native` and does not provide real entropy values, though, it tests execution of the main +module and its common components including the optional health tests. Additional sources are +requested subsequently. As indicated in the documentation of the [entropy module](../../sys/entropy_source/doc.txt), +entropy is vulnerable and specific hardware sources require a priori validation. +The `ADC noise` source requires the `periph_adc` feature of a board. Its properties can vary widely as +depicted in the documentation of the [`ADC noise`](../../sys/include/entropy_source/adc_noise.h) +modules and proper testing and parametrization need to take place for every single platform and ideally +environmental properties before deployment. The test simply initializes the ADC noise entropy source +with the default ADC pin, requests and dumps many samples with enabled health tests of the source and +conditioning. + +# Expected results +The `zero entropy` is expected to indicate different errors after requesting more samples than +the cutoff value of the Repetition Count Test (NIST SP 800-90B 4.4.1) and the Adaptive +Proportion Test (NIST SP 800-90B 4.4.2). The expected output is: + +## native + +``` +# main(): This is RIOT! (Version: ) +# Zero entropy single request 0/311 returned: 0 +... +# Zero entropy single request 20/311 returned: -3 +... +# Zero entropy single request 310/311 returned: -3 +# Zero entropy single request 311/311 returned: -5 +# Zero entropy request 64 Bytes: -5 + +``` + +## Board + +The `ADC noise` source test should dump unpredictable values. The *von Neumann* conditioning internally +requests multiple samples so that subsequent values include bit changes. Thus, it has +a variable runtime. To explore this, one test requests a buffer with entropy values +and it measures the processing time. If no changes are in place, the conditioning function +will abort after exceeding a threshold. +Otherwise, no health test errors should be indicated. Please note that +missing errors do **not** reveal information about the quality of an entropy source. + +``` +# main(): This is RIOT! (Version: ) +# Zero entropy single request 0/311 returned: 0 +... +# Zero entropy single request 20/311 returned: -3 +... +# Zero entropy single request 311/311 returned: -5 +# Zero entropy request 64 Bytes: -5 +# ADC noise source entropy/sample: 65536 [2^16 * bit / sample] +# ADC noise source entropy/sample: 1 [bit / sample] +# 95 +# 9f +# a5 +# 01 +# e1 +# 3e +# 73 +... +# ADC noise request 64 Bytes returned: 0. Time: