SEC: Add high-level interface for secure random numbers generation.
authorrasky <rasky@38d2e660-2303-0410-9eaa-f027e97ec537>
Tue, 28 Sep 2010 18:32:43 +0000 (18:32 +0000)
committerrasky <rasky@38d2e660-2303-0410-9eaa-f027e97ec537>
Tue, 28 Sep 2010 18:32:43 +0000 (18:32 +0000)
git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4336 38d2e660-2303-0410-9eaa-f027e97ec537

bertos/sec/random.c [new file with mode: 0644]
bertos/sec/random.h [new file with mode: 0644]
bertos/sec/random_p.h [new file with mode: 0644]

diff --git a/bertos/sec/random.c b/bertos/sec/random.c
new file mode 100644 (file)
index 0000000..421688d
--- /dev/null
@@ -0,0 +1,174 @@
+#include "random.h"
+#include "random_p.h"
+
+#include <cfg/macros.h>
+#include <drv/timer.h>
+#include <sec/random.h>
+#include <sec/prng.h>
+#include <sec/entropy.h>
+#include <sec/util.h>
+#include <sec/hash/sha1.h>
+#include <sec/prng/isaac.h>
+
+/********************************************************************************/
+/* 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 (file)
index 0000000..8708f35
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * \file
+ * <!--
+ * This file is part of BeRTOS.
+ *
+ * Bertos is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction.  Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License.  This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ *
+ * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
+ *
+ * -->
+ *
+ * \brief High-level random number generation functions.
+ * \author Giovanni Bajo <rasky@develer.com>
+ *
+ */
+
+#ifndef SEC_RANDOM_H
+#define SEC_RANDOM_H
+
+#include <cfg/compiler.h>
+
+#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 (file)
index 0000000..d3e39c2
--- /dev/null
@@ -0,0 +1,121 @@
+/**
+ * \file
+ * <!--
+ * This file is part of BeRTOS.
+ *
+ * Bertos is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction.  Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License.  This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ *
+ * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
+ *
+ * -->
+ *
+ * \brief Internal function definitions for random
+ * \author Giovanni Bajo <rasky@develer.com>
+ *
+ */
+
+#ifndef SEC_RANDOM_P_H
+#define SEC_RANDOM_P_H
+
+#include <cfg/compiler.h>
+#include <sec/random.h>
+
+/********************************************************************************/
+/* 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 */