From: rasky Date: Tue, 28 Sep 2010 18:32:43 +0000 (+0000) Subject: SEC: Add high-level interface for secure random numbers generation. X-Git-Tag: 2.6.0~65 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=281c567a202fbfbf6f1416fbae02c8e6757fddca;p=bertos.git SEC: Add high-level interface for secure random numbers generation. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4336 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/sec/random.c b/bertos/sec/random.c new file mode 100644 index 00000000..421688dc --- /dev/null +++ b/bertos/sec/random.c @@ -0,0 +1,174 @@ +#include "random.h" +#include "random_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/********************************************************************************/ +/* Configuration of the random module */ +/********************************************************************************/ + +#define POOL_CONTEXT PP_CAT(PP_CAT(PRNG_NAME, CONFIG_RANDOM_POOL), _Context) +#define POOL_INIT PP_CAT(PP_CAT(PRNG_NAME, CONFIG_RANDOM_POOL), _init) + +#define EXTRACTOR_STACKINIT PP_CAT(PP_CAT(EXTRACTOR_NAME, CONFIG_RANDOM_EXTRACTOR), _stackinit) + +#define PRNG_CONTEXT PP_CAT(PP_CAT(PRNG_NAME, CONFIG_RANDOM_PRNG), _Context) +#define PRNG_INIT PP_CAT(PP_CAT(PRNG_NAME, CONFIG_RANDOM_PRNG), _init) + + +/********************************************************************************/ +/* Global state */ +/********************************************************************************/ + +#if CONFIG_RANDOM_POOL != POOL_NONE +static POOL_CONTEXT epool_ctx; +static EntropyPool_Context * const epool = (EntropyPool_Context *)&epool_ctx; +#endif + +static PRNG_CONTEXT prng_ctx; +static PRNG * const prng = (PRNG*)&prng_ctx; + +static bool initialized = 0; + + +/********************************************************************************/ +/* Code */ +/********************************************************************************/ + +/* + * Reseed the PRNG if there is enough entropy available at this time. + * + * Some designs (eg: fortuna) suggest to artificially limit the frequency of + * this operation to something like 0.1s, to avoid attacks that try to exhaust + * the entropy pool. + * + * We don't believe such attacks are available in an embedded system (as an attacker + * does not have a way to ask random numbers from the pool) but we will play safe + * here in case eg. the user does something wrong. + */ +static void optional_reseeding(void) +{ +#if CONFIG_RANDOM_POOL != POOL_NONE + static ticks_t last_reseed = 0; + + // We don't allow more than 10 reseedings per second + // (as suggested by Fortuna) + ticks_t current = timer_clock(); + if (ticks_to_ms(current - last_reseed) < 100) + return; + + if (entropy_seeding_ready(epool)) + { + uint8_t seed[prng_seed_len(prng)]; + + entropy_make_seed(epool, seed, sizeof(seed)); + prng_reseed(prng, seed); + + last_reseed = current; + PURGE(seed); + } +#endif +} + + +/* + * Perform the initial seeding of the PRNG. + * + * At startup, we want to immediately seed the PRNG to a point where it can + * generate safe-enough random numbers. To do this, we rely on a hw-dependent + * function to pull entropy from available hw sources, and then feed it + * through an extractor (if any is configured). + */ +static void initial_seeding(void) +{ +#if CONFIG_RANDOM_POOL != POOL_NONE + + // We feed entropy into the pool, until it is ready to perform a seeding. + do + { + uint8_t buf[16]; + random_pull_entropy(buf, sizeof(buf)); + entropy_add(epool, 0, buf, sizeof(buf), sizeof(buf)*8); + } while (!entropy_seeding_ready(epool)); + + optional_reseeding(); + +#elif CONFIG_RANDOM_EXTRACTOR != EXTRACTOR_NONE + + uint8_t seed[prng_seed_len(prng)]; + Hash *h = EXTRACTOR_STACKINIT(); + + // "Randomness Extraction and Key Derivation Using the CBC, Cascade and + // HMAC Modes" by Yevgeniy Dodis et al. suggests that an padded cascaded hash + // function with N bits of output must have at least 2n bits of min-entropy as input + // to be a randomness extractor (it generates an output that is computationally + // indistiguishable from an uniform distribution). + size_t hlen = hash_digest_len(h); + uint8_t buf[hlen*2]; + size_t sidx = 0; + + while (sidx < sizeof(seed)) + { + size_t cnt = MIN(sizeof(seed) - sidx, hlen); + + random_pull_entropy(buf, sizeof(buf)); + + hash_begin(h); + hash_update(h, buf, sizeof(buf)); + memcpy(seed+sidx, hash_final(h), cnt); + sidx += cnt; + } + + prng_reseed(prng, seed); + PURGE(buf); + PURGE(seed); + +#else + + // If we have been configured without a proper randomness extractor, we do not + // have many solutions but feeding the entropy bits directly to the PRNG, and + // hoping for the best. + uint8_t seed[prng_seed_len(prng)]; + random_pull_entropy(seed, sizeof(seed)); + prng_reseed(prng, seed); + PURGE(seed); + +#endif +} + +void random_init(void) +{ +#if CONFIG_RANDOM_POOL != POOL_NONE + POOL_INIT(&epool_ctx); +#endif + PRNG_INIT(&prng_ctx); + + initialized = 1; + initial_seeding(); +} + +void random_gen(uint8_t *out, size_t len) +{ + ASSERT(initialized); + + optional_reseeding(); + prng_generate(prng, out, len); +} + +#if CONFIG_RANDOM_POOL != POOL_NONE + +void random_add_entropy(enum EntropySource source_idx, + const uint8_t *data, size_t len, + int entropy) +{ + entropy_add(epool, source_idx, data, len, entropy); +} + +#endif diff --git a/bertos/sec/random.h b/bertos/sec/random.h new file mode 100644 index 00000000..8708f35a --- /dev/null +++ b/bertos/sec/random.h @@ -0,0 +1,106 @@ +/** + * \file + * + * + * \brief High-level random number generation functions. + * \author Giovanni Bajo + * + */ + +#ifndef SEC_RANDOM_H +#define SEC_RANDOM_H + +#include + +#define RANDOM_SECURITY_MINIMUM 0 +#define RANDOM_SECURITY_MEDIUM 1 +#define RANDOM_SECURITY_STRONG 2 + +/** + * Configure the security level required by the application. + * + * Application developers are suggested to keep the strongest + * setting (default) unless there are memory or code size issues. + * + * Available settings are: + * + * * \a RANDOM_SECURITY_STRONG: The random library will use + * an entropy pool, automatically feeded by drivers, to gather + * entropy from hardware sources. Data from the pool will + * be used to reseed a secure random number generator. Moreover, + * the generator will be automatically initialised + * with enough entropy to generate safe random numbers even + * immediately after hw reset. + * The overall structure is the same as used by modern + * desktop PCs for generating secure random numbers. + * + * * \a RANDOM_SECURITY_MEDIUM: This intermediate settings will + * avoid usage of an entropy pool, to reduce memory and code + * usage. The security of this settings relies only on the + * good behaviour of the random number generator (even though + * it will be well-seeded at startup). + * + * * \a RANDOM_SECURITY_MINIMUM: This is the lighter setting that + * allows minimal memory and code usage, and it suggested only + * for extremely constrained systems, that only generates few + * random numbers. Even if the generator is still secure on + * paper, its seeding will not be safe (though still entropic + * to allow different sequences to be generated after each reset). + */ +#define RANDOM_SECURITY_LEVEL RANDOM_SECURITY_MEDIUM // FIXME: RANDOM_SECURITY_STRONG + + +void random_init(void); + +void random_gen(uint8_t *out, size_t len); + +INLINE uint8_t random_gen8(void) +{ + uint8_t x; + random_gen(&x, 1); + return x; +} + +INLINE uint16_t random_gen16(void) +{ + uint8_t x; + random_gen(&x, 2); + return x; +} + +INLINE uint32_t random_gen32(void) +{ + uint8_t x; + random_gen(&x, 4); + return x; +} + +#endif /* SEC_RANDOM_H */ diff --git a/bertos/sec/random_p.h b/bertos/sec/random_p.h new file mode 100644 index 00000000..d3e39c2f --- /dev/null +++ b/bertos/sec/random_p.h @@ -0,0 +1,121 @@ +/** + * \file + * + * + * \brief Internal function definitions for random + * \author Giovanni Bajo + * + */ + +#ifndef SEC_RANDOM_P_H +#define SEC_RANDOM_P_H + +#include +#include + +/********************************************************************************/ +/* Configuration of the random module */ +/********************************************************************************/ + +#define POOL_NONE 0 +#define POOL_YARROW 1 +#define POOL_NAME1 Yarrow + +#define PRNG_ISAAC 1 +#define PRNG_X917 2 +#define PRNG_YARROW 3 +#define PRNG_NAME1 ISAAC +#define PRNG_NAME2 X917 +#define PRNG_NAME3 Yarrow + +#define EXTRACTOR_NONE 0 +#define EXTRACTOR_SHA1 1 +#define EXTRACTOR_NAME1 SHA1 + +#if RANDOM_SECURITY_LEVEL == RANDOM_SECURITY_STRONG + #define CONFIG_RANDOM_POOL POOL_YARROW + #define CONFIG_RANDOM_EXTRACTOR EXTRACTOR_NONE // not required with a pool + #define CONFIG_RANDOM_PRNG PRNG_ISAAC // FIXME: PRNG_YARROW +#elif RANDOM_SECURITY_LEVEL == RANDOM_SECURITY_MEDIUM + #define CONFIG_RANDOM_POOL POOL_NONE + #define CONFIG_RANDOM_EXTRACTOR EXTRACTOR_SHA1 + #define CONFIG_RANDOM_PRNG PRNG_ISAAC // FIXME: PRNG_X917 +#elif RANDOM_SECURITY_LEVEL == RANDOM_SECURITY_MINIMUM + #define CONFIG_RANDOM_POOL POOL_NONE + #define CONFIG_RANDOM_EXTRACTOR EXTRACTOR_NONE + #define CONFIG_RANDOM_PRNG PRNG_ISAAC +#else + #error Unsupported random security level value +#endif + +/***************************************************************************/ +/* Internal functions used by BeRTOS drivers to push data into */ +/* the entropy pool */ +/***************************************************************************/ + +#if CONFIG_RANDOM_POOL != POOL_NONE + +enum EntropySource +{ + ENTROPY_SOURCE_IRQ, + ENTROPY_SOURCE_ADC, +}; + +/* + * Add entropy to the global entropy pool. + */ +void random_add_entropy(enum EntropySource source_idx, + const uint8_t *data, size_t len, + int entropy); + + +/* + * Add entropy to the global interrupt pool based on the IRQ + * call time. + * + * This function can be called from interrupt handlers that are + * triggered at unpredictable intervals (so it should not be + * called from clock-driven interrupts like ADC, PWM, etc.). + * + */ +void random_add_entropy_irq(int irq); + +#endif + +/* + * This hardware-dependent function can be used to pull raw + * entropy from a hardware source at startup only. It is used + * for initial seeding of the random generator and should not + * be used in different situations. + */ +void random_pull_entropy(uint8_t *entropy, size_t len); + +#endif /* SEC_RANDOM_P_H */