421688dc553f515bb5fe966065a3c7be3442e8f9
[bertos.git] / bertos / sec / random.c
1 #include "random.h"
2 #include "random_p.h"
3
4 #include <cfg/macros.h>
5 #include <drv/timer.h>
6 #include <sec/random.h>
7 #include <sec/prng.h>
8 #include <sec/entropy.h>
9 #include <sec/util.h>
10 #include <sec/hash/sha1.h>
11 #include <sec/prng/isaac.h>
12
13 /********************************************************************************/
14 /* Configuration of the random module                                           */
15 /********************************************************************************/
16
17 #define POOL_CONTEXT          PP_CAT(PP_CAT(PRNG_NAME, CONFIG_RANDOM_POOL), _Context)
18 #define POOL_INIT             PP_CAT(PP_CAT(PRNG_NAME, CONFIG_RANDOM_POOL), _init)
19
20 #define EXTRACTOR_STACKINIT   PP_CAT(PP_CAT(EXTRACTOR_NAME, CONFIG_RANDOM_EXTRACTOR), _stackinit)
21
22 #define PRNG_CONTEXT          PP_CAT(PP_CAT(PRNG_NAME, CONFIG_RANDOM_PRNG), _Context)
23 #define PRNG_INIT             PP_CAT(PP_CAT(PRNG_NAME, CONFIG_RANDOM_PRNG), _init)
24
25
26 /********************************************************************************/
27 /* Global state                                                                 */
28 /********************************************************************************/
29
30 #if CONFIG_RANDOM_POOL != POOL_NONE
31 static POOL_CONTEXT epool_ctx;
32 static EntropyPool_Context * const epool = (EntropyPool_Context *)&epool_ctx;
33 #endif
34
35 static PRNG_CONTEXT prng_ctx;
36 static PRNG * const prng = (PRNG*)&prng_ctx;
37
38 static bool initialized = 0;
39
40
41 /********************************************************************************/
42 /* Code                                                                         */
43 /********************************************************************************/
44
45 /*
46  * Reseed the PRNG if there is enough entropy available at this time.
47  * 
48  * Some designs (eg: fortuna) suggest to artificially limit the frequency of 
49  * this operation to something like 0.1s, to avoid attacks that try to exhaust
50  * the entropy pool.
51  * 
52  * We don't believe such attacks are available in an embedded system (as an attacker
53  * does not have a way to ask random numbers from the pool) but we will play safe
54  * here in case eg. the user does something wrong.
55  */
56 static void optional_reseeding(void)
57 {
58 #if CONFIG_RANDOM_POOL != POOL_NONE
59         static ticks_t last_reseed = 0;
60
61         // We don't allow more than 10 reseedings per second 
62         // (as suggested by Fortuna)
63         ticks_t current = timer_clock();
64         if (ticks_to_ms(current - last_reseed) < 100)
65                 return;
66         
67         if (entropy_seeding_ready(epool))
68         {
69                 uint8_t seed[prng_seed_len(prng)];
70                 
71                 entropy_make_seed(epool, seed, sizeof(seed));
72                 prng_reseed(prng, seed);
73                 
74                 last_reseed = current;
75                 PURGE(seed);
76         }
77 #endif
78 }
79
80
81 /*
82  * Perform the initial seeding of the PRNG.
83  * 
84  * At startup, we want to immediately seed the PRNG to a point where it can
85  * generate safe-enough random numbers. To do this, we rely on a hw-dependent
86  * function to pull entropy from available hw sources, and then feed it
87  * through an extractor (if any is configured).
88  */
89 static void initial_seeding(void)
90 {
91 #if CONFIG_RANDOM_POOL != POOL_NONE
92
93         // We feed entropy into the pool, until it is ready to perform a seeding.
94         do
95         {
96                 uint8_t buf[16];
97                 random_pull_entropy(buf, sizeof(buf));
98                 entropy_add(epool, 0, buf, sizeof(buf), sizeof(buf)*8);
99         } while (!entropy_seeding_ready(epool));
100         
101         optional_reseeding();
102
103 #elif CONFIG_RANDOM_EXTRACTOR != EXTRACTOR_NONE
104
105         uint8_t seed[prng_seed_len(prng)];
106         Hash *h = EXTRACTOR_STACKINIT();
107
108         // "Randomness Extraction and Key Derivation Using the CBC, Cascade and
109         // HMAC Modes" by Yevgeniy Dodis et al. suggests that an padded cascaded hash
110         // function with N bits of output must have at least 2n bits of min-entropy as input
111         // to be a randomness extractor (it generates an output that is computationally
112         // indistiguishable from an uniform distribution).
113         size_t hlen = hash_digest_len(h);
114         uint8_t buf[hlen*2];
115         size_t sidx = 0;
116
117         while (sidx < sizeof(seed))
118         {
119                 size_t cnt = MIN(sizeof(seed) - sidx, hlen);
120
121                 random_pull_entropy(buf, sizeof(buf));
122
123                 hash_begin(h);
124                 hash_update(h, buf, sizeof(buf));
125                 memcpy(seed+sidx, hash_final(h), cnt);
126                 sidx += cnt;
127         }
128
129         prng_reseed(prng, seed);
130         PURGE(buf);
131         PURGE(seed);
132
133 #else
134
135         // If we have been configured without a proper randomness extractor, we do not
136         // have many solutions but feeding the entropy bits directly to the PRNG, and
137         // hoping for the best.
138         uint8_t seed[prng_seed_len(prng)];
139         random_pull_entropy(seed, sizeof(seed));
140         prng_reseed(prng, seed);
141         PURGE(seed);
142
143 #endif
144 }
145
146 void random_init(void)
147 {
148 #if CONFIG_RANDOM_POOL != POOL_NONE
149         POOL_INIT(&epool_ctx);
150 #endif
151         PRNG_INIT(&prng_ctx);
152
153         initialized = 1;
154         initial_seeding();
155 }
156
157 void random_gen(uint8_t *out, size_t len)
158 {
159         ASSERT(initialized);
160
161         optional_reseeding();
162         prng_generate(prng, out, len);
163 }
164
165 #if CONFIG_RANDOM_POOL != POOL_NONE
166
167 void random_add_entropy(enum EntropySource source_idx,
168                         const uint8_t *data, size_t len,
169                         int entropy)
170 {
171         entropy_add(epool, source_idx, data, len, entropy);
172 }
173
174 #endif