csprng - If not enough entropy is available, sleep
[dragonfly.git] / sys / kern / subr_csprng.c
1 #include <sys/param.h>
2 #include <sys/systm.h>
3 #include <sys/kernel.h>
4 #include <sys/spinlock.h>
5 #include <sys/spinlock2.h>
6 #include <sys/csprng.h>
7
8 /*
9  * Minimum amount of bytes in pool before we consider it
10  * good enough.
11  * It's 64 + the hash digest size because we always
12  * reinitialize the pools with a hash of the previous chunk
13  * of entropy.
14  */
15 #define MIN_POOL_SIZE   64 + SHA256_DIGEST_LENGTH
16
17 /* Minimum reseed interval */
18 #define MIN_RESEED_INTERVAL     hz/10
19
20 /* Lock macros */
21 #define POOL_LOCK_INIT(pool) \
22     spin_init(&(pool)->lock)
23
24 #define POOL_LOCK(pool)      \
25     spin_lock(&pool->lock)
26
27 #define POOL_TRYLOCK(pool)   \
28     spin_trylock(&pool->lock)
29
30 #define POOL_UNLOCK(pool)    \
31     spin_unlock(&pool->lock)
32
33
34 #define STATE_LOCK_INIT(state)  \
35     spin_init(&state->lock)
36
37 #define STATE_LOCK(state)       \
38     spin_lock(&state->lock)
39
40 #define STATE_UNLOCK(state)     \
41     spin_unlock(&state->lock)
42
43 #define STATE_SLEEP(state, wmesg, timo) \
44     ssleep(state, &state->lock, 0, wmesg, timo)
45
46 #define STATE_WAKEUP(state)     \
47     wakeup(state)
48
49 static void csprng_reseed_callout(void *arg);
50 static int csprng_reseed(struct csprng_state *state);
51
52 static struct timeval csprng_reseed_interval = { 0, 100000 };
53
54 static
55 int
56 csprng_pool_init(struct csprng_pool *pool, uint8_t *buf, size_t len)
57 {
58         pool->bytes = 0;
59         SHA256_Init(&pool->hash_ctx);
60
61         if (len > 0)
62                 SHA256_Update(&pool->hash_ctx, buf, len);
63
64         return 0;
65 }
66
67 int
68 csprng_init(struct csprng_state *state)
69 {
70         int i, r;
71
72         bzero(state->key, sizeof(state->key));
73         bzero(&state->cipher_ctx, sizeof(state->cipher_ctx));
74         bzero(state->src_pool_idx, sizeof(state->src_pool_idx));
75         bzero(&state->last_reseed, sizeof(state->last_reseed));
76
77         state->nonce = 0;
78         state->ctr   = 0;
79         state->reseed_cnt = 0;
80         state->failed_reseeds = 0;
81         state->callout_based_reseed = 0;
82
83         STATE_LOCK_INIT(state);
84
85         for (i = 0; i < 32; i++) {
86                 r = csprng_pool_init(&state->pool[i], NULL, 0);
87                 if (r != 0)
88                         break;
89                 POOL_LOCK_INIT(&state->pool[i]);
90         }
91
92         return r;
93 }
94
95 int
96 csprng_init_reseed(struct csprng_state *state)
97 {
98         state->callout_based_reseed = 1;
99
100         callout_init_mp(&state->reseed_callout);
101         callout_reset(&state->reseed_callout, MIN_RESEED_INTERVAL,
102             csprng_reseed_callout, state);
103
104         return 0;
105 }
106
107 /*
108  * XXX:
109  * Sources don't really a uniquely-allocated src id...
110  * another way we could do that is by simply using
111  * (uint8_t)__LINE__ as the source id... cheap & cheerful.
112  */
113
114 static
115 int
116 encrypt_bytes(struct csprng_state *state, uint8_t *out, uint8_t *in, size_t bytes)
117 {
118         /* Update nonce whenever the counter is about to overflow */
119         if (chacha_check_counter(&state->cipher_ctx)) {
120                 ++state->nonce;
121                 chacha_ivsetup(&state->cipher_ctx, (const uint8_t *)&state->nonce);
122         }
123
124         chacha_encrypt_bytes(&state->cipher_ctx, in, out, (uint32_t)bytes);
125
126         return 0;
127 }
128
129 /*
130  * XXX: flags is currently unused, but could be used to know whether
131  *      it's a /dev/random or /dev/urandom read, and make sure that
132  *      enough entropy has been collected recently, etc.
133  */
134 int
135 csprng_get_random(struct csprng_state *state, uint8_t *out, int bytes,
136     int flags __unused)
137 {
138         int cnt;
139         int total_bytes = 0;
140         int r;
141
142         /*
143          * XXX: can optimize a bit by digging into chacha_encrypt_bytes
144          *      and removing the xor of the stream with the input - that
145          *      way we don't have to xor the output (which we provide
146          *      as input).
147          */
148         bzero(out, bytes);
149
150         STATE_LOCK(state);
151
152 again:
153         if (!state->callout_based_reseed &&
154              ratecheck(&state->last_reseed, &csprng_reseed_interval)) {
155                 if ((r = csprng_reseed(state)) != 0) {
156                         STATE_SLEEP(state, "csprngrsd", 0);
157                         goto again;
158                 }
159         }
160
161         KKASSERT(state->reseed_cnt >= 0);
162
163         if (state->reseed_cnt == 0) {
164                 STATE_SLEEP(state, "csprngrsd", 0);
165                 goto again;
166         }
167
168         while (bytes > 0) {
169                 /* Limit amount of output without rekeying to 2^20 */
170                 cnt = (bytes > (1 << 20)) ? (1 << 20) : bytes;
171
172                 encrypt_bytes(state, out, out, cnt);
173
174                 /* Update key and rekey cipher */
175                 encrypt_bytes(state, state->key, state->key, sizeof(state->key));
176                 chacha_keysetup(&state->cipher_ctx, state->key,
177                     8*sizeof(state->key));
178
179                 out += cnt;
180                 bytes -= cnt;
181                 total_bytes += cnt;
182         }
183
184         STATE_UNLOCK(state);
185
186         return total_bytes;
187 }
188
189 static
190 int
191 csprng_reseed(struct csprng_state *state)
192 {
193         int i;
194         struct csprng_pool *pool;
195         SHA256_CTX hash_ctx;
196         uint8_t digest[SHA256_DIGEST_LENGTH];
197
198         /*
199          * If there's not enough entropy in the first
200          * pool, don't reseed.
201          */
202         if (state->pool[0].bytes < MIN_POOL_SIZE) {
203                 ++state->failed_reseeds;
204                 return 1;
205         }
206
207         SHA256_Init(&hash_ctx);
208
209         /*
210          * Update hash that will result in new key with the
211          * old key.
212          */
213         SHA256_Update(&hash_ctx, state->key, sizeof(state->key));
214
215         state->reseed_cnt++;
216
217         for (i = 0; i < 32; i++) {
218                 if ((state->reseed_cnt % (1 << i)) != 0)
219                         break;
220
221                 pool = &state->pool[i];
222                 POOL_LOCK(pool);
223
224                 /*
225                  * Finalize hash of the entropy in this pool.
226                  */
227                 SHA256_Final(digest, &pool->hash_ctx);
228
229                 /*
230                  * Reinitialize pool with a hash of the old pool digest.
231                  * This is a slight deviation from Fortuna as per reference,
232                  * but is in line with other Fortuna implementations.
233                  */
234                 csprng_pool_init(pool, digest, sizeof(digest));
235
236                 POOL_UNLOCK(pool);
237
238                 /*
239                  * Update hash that will result in new key with this
240                  * pool's hashed entropy.
241                  */
242                 SHA256_Update(&hash_ctx, digest, sizeof(digest));
243         }
244
245         SHA256_Final(state->key, &hash_ctx);
246
247         /* Update key and rekey cipher */
248         chacha_keysetup(&state->cipher_ctx, state->key,
249             8*sizeof(state->key));
250
251         /* Increment the nonce if the counter overflows */
252         if (chacha_incr_counter(&state->cipher_ctx)) {
253                 ++state->nonce;
254                 chacha_ivsetup(&state->cipher_ctx, (const uint8_t *)&state->nonce);
255         }
256
257         return 0;
258 }
259
260 static
261 void
262 csprng_reseed_callout(void *arg)
263 {
264         struct csprng_state *state = (struct csprng_state *)arg;
265         int reseed_interval = MIN_RESEED_INTERVAL;
266
267         STATE_LOCK(state);
268
269         csprng_reseed(arg);
270
271         STATE_WAKEUP(state);
272         STATE_UNLOCK(state);
273
274         callout_reset(&state->reseed_callout, reseed_interval,
275             csprng_reseed_callout, state);
276 }
277
278 int
279 csprng_add_entropy(struct csprng_state *state, int src_id,
280     const uint8_t *entropy, size_t bytes, int flags)
281 {
282         struct csprng_pool *pool;
283         int pool_id;
284
285         /*
286          * Pick the next pool for this source on a round-robin
287          * basis.
288          */
289         src_id &= 0xff;
290         pool_id = state->src_pool_idx[src_id]++ & 0x1f;
291         pool = &state->pool[pool_id];
292
293         if (flags & CSPRNG_TRYLOCK) {
294                 /*
295                  * If we are asked to just try the lock instead
296                  * of spinning until we get it, return if we
297                  * can't get a hold of the lock right now.
298                  */
299                 if (!POOL_TRYLOCK(pool))
300                         return -1;
301         } else {
302                 POOL_LOCK(pool);
303         }
304
305         SHA256_Update(&pool->hash_ctx, (const uint8_t *)&src_id, sizeof(src_id));
306         SHA256_Update(&pool->hash_ctx, (const uint8_t *)&bytes, sizeof(bytes));
307         SHA256_Update(&pool->hash_ctx, entropy, bytes);
308
309         pool->bytes += bytes;
310
311         POOL_UNLOCK(pool);
312
313         /*
314          * If a wakeup is missed, it doesn't matter too much - it'll get woken
315          * up by the next add_entropy() call.
316          */
317         STATE_WAKEUP(state);
318
319         return 0;
320 }