xref: /dragonfly/sys/kern/subr_csprng.c (revision c880cbaf)
1 /*
2  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <alex@alexhornung.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/spinlock.h>
38 #include <sys/spinlock2.h>
39 #include <sys/csprng.h>
40 
41 /*
42  * Minimum amount of bytes in pool before we consider it
43  * good enough.
44  * It's 64 + the hash digest size because we always
45  * reinitialize the pools with a hash of the previous chunk
46  * of entropy.
47  */
48 #define MIN_POOL_SIZE	(64 + SHA256_DIGEST_LENGTH)
49 
50 /* Minimum reseed interval */
51 #define MIN_RESEED_INTERVAL	hz/10
52 
53 #if 0
54 static void csprng_reseed_callout(void *arg);
55 #endif
56 static int csprng_reseed(struct csprng_state *state);
57 
58 static struct timeval csprng_reseed_interval = { 0, 100000 };
59 
60 static
61 int
62 csprng_pool_init(struct csprng_pool *pool, uint8_t *buf, size_t len)
63 {
64 	pool->bytes = 0;
65 	SHA256_Init(&pool->hash_ctx);
66 
67 	if (len > 0)
68 		SHA256_Update(&pool->hash_ctx, buf, len);
69 
70 	return 0;
71 }
72 
73 int
74 csprng_init(struct csprng_state *state)
75 {
76 	int i, r;
77 
78 	bzero(state->key, sizeof(state->key));
79 	bzero(&state->cipher_ctx, sizeof(state->cipher_ctx));
80 	bzero(state->src_pool_idx, sizeof(state->src_pool_idx));
81 	bzero(&state->last_reseed, sizeof(state->last_reseed));
82 
83 	state->nonce = 0;
84 	state->ctr   = 0;
85 	state->reseed_cnt = 0;
86 	state->failed_reseeds = 0;
87 	state->callout_based_reseed = 0;
88 
89 	for (i = 0; i < 32; i++) {
90 		r = csprng_pool_init(&state->pool[i], NULL, 0);
91 		if (r != 0)
92 			break;
93 	}
94 
95 	return r;
96 }
97 
98 #if 0
99 int
100 csprng_init_reseed(struct csprng_state *state)
101 {
102 	state->callout_based_reseed = 1;
103 
104 	callout_init_mp(&state->reseed_callout);
105 	callout_reset(&state->reseed_callout, MIN_RESEED_INTERVAL,
106 		      csprng_reseed_callout, state);
107 
108 	return 0;
109 }
110 #endif
111 
112 /*
113  * XXX:
114  * Sources don't really a uniquely-allocated src id...
115  * another way we could do that is by simply using
116  * (uint8_t)__LINE__ as the source id... cheap & cheerful.
117  */
118 
119 static
120 int
121 encrypt_bytes(struct csprng_state *state, uint8_t *out, uint8_t *in,
122 	      size_t bytes)
123 {
124 	/* Update nonce whenever the counter is about to overflow */
125 	if (chacha_check_counter(&state->cipher_ctx)) {
126 		++state->nonce;
127 		chacha_ivsetup(&state->cipher_ctx,
128 			       (const uint8_t *)&state->nonce);
129 	}
130 
131 	chacha_encrypt_bytes(&state->cipher_ctx, in, out, (uint32_t)bytes);
132 
133 	return 0;
134 }
135 
136 /*
137  *
138  * Called with state->spin held.
139  *
140  * XXX: flags is currently unused, but could be used to know whether
141  *      it's a /dev/random or /dev/urandom read, and make sure that
142  *      enough entropy has been collected recently, etc.
143  */
144 int
145 csprng_get_random(struct csprng_state *state, uint8_t *out, int bytes,
146 		  int flags __unused, int unlimited)
147 {
148 	int cnt;
149 	int total_bytes = 0;
150 
151 	/*
152 	 * XXX: can optimize a bit by digging into chacha_encrypt_bytes
153 	 *      and removing the xor of the stream with the input - that
154 	 *      way we don't have to xor the output (which we provide
155 	 *      as input).
156 	 */
157 	bzero(out, bytes);
158 
159 again:
160 	if (!state->callout_based_reseed &&
161 	     ratecheck(&state->last_reseed, &csprng_reseed_interval)) {
162 		csprng_reseed(state);
163 	}
164 
165 	/*
166 	 * If no reseed has occurred yet, we can't possibly give out
167 	 * any random data.
168 	 * Sleep until entropy is added to the pools (or a callout-based
169 	 * reseed, if enabled, occurs).
170 	 */
171 	if (unlimited == 0 && state->reseed_cnt == 0) {
172 		ssleep(state, &state->spin, 0, "csprngrsd", 0);
173 		goto again;
174 	}
175 
176 	while (bytes > 0) {
177 		/* Limit amount of output without rekeying to 2^20 */
178 		cnt = (bytes > (1 << 20)) ? (1 << 20) : bytes;
179 
180 		encrypt_bytes(state, out, out, cnt);
181 
182 		/* Update key and rekey cipher */
183 		encrypt_bytes(state, state->key, state->key,
184 			      sizeof(state->key));
185 		chacha_keysetup(&state->cipher_ctx, state->key,
186 		    8*sizeof(state->key));
187 
188 		out += cnt;
189 		bytes -= cnt;
190 		total_bytes += cnt;
191 	}
192 
193 	return total_bytes;
194 }
195 
196 /*
197  * Called with state->spin held.
198  */
199 static
200 int
201 csprng_reseed(struct csprng_state *state)
202 {
203 	int i;
204 	struct csprng_pool *pool;
205 	SHA256_CTX hash_ctx;
206 	uint8_t digest[SHA256_DIGEST_LENGTH];
207 
208 	/*
209 	 * If there's not enough entropy in the first
210 	 * pool, don't reseed.
211 	 */
212 	if (state->pool[0].bytes < MIN_POOL_SIZE) {
213 		++state->failed_reseeds;
214 		return 1;
215 	}
216 
217 	SHA256_Init(&hash_ctx);
218 
219 	/*
220 	 * Update hash that will result in new key with the
221 	 * old key.
222 	 */
223 	SHA256_Update(&hash_ctx, state->key, sizeof(state->key));
224 
225 	state->reseed_cnt++;
226 
227 	for (i = 0; i < 32; i++) {
228 		if ((state->reseed_cnt % (1 << i)) != 0)
229 			break;
230 
231 		pool = &state->pool[i];
232 
233 		/*
234 		 * Finalize hash of the entropy in this pool.
235 		 */
236 		SHA256_Final(digest, &pool->hash_ctx);
237 
238 		/*
239 		 * Reinitialize pool with a hash of the old pool digest.
240 		 * This is a slight deviation from Fortuna as per reference,
241 		 * but is in line with other Fortuna implementations.
242 		 */
243 		csprng_pool_init(pool, digest, sizeof(digest));
244 
245 		/*
246 		 * Update hash that will result in new key with this
247 		 * pool's hashed entropy.
248 		 */
249 		SHA256_Update(&hash_ctx, digest, sizeof(digest));
250 	}
251 
252 	SHA256_Final(state->key, &hash_ctx);
253 
254 	/* Update key and rekey cipher */
255 	chacha_keysetup(&state->cipher_ctx, state->key,
256 	    8*sizeof(state->key));
257 
258 	/* Increment the nonce if the counter overflows */
259 	if (chacha_incr_counter(&state->cipher_ctx)) {
260 		++state->nonce;
261 		chacha_ivsetup(&state->cipher_ctx,
262 			       (const uint8_t *)&state->nonce);
263 	}
264 
265 	return 0;
266 }
267 
268 #if 0
269 static
270 void
271 csprng_reseed_callout(void *arg)
272 {
273 	struct csprng_state *state = (struct csprng_state *)arg;
274 	int reseed_interval = MIN_RESEED_INTERVAL;
275 
276 	spin_lock(&state->spin);
277 	csprng_reseed(arg);
278 	spin_unlock(&state->spin);
279 	wakeup(state);
280 
281 	callout_reset(&state->reseed_callout, reseed_interval,
282 		      csprng_reseed_callout, state);
283 }
284 #endif
285 
286 /*
287  * Called with state->spin held
288  */
289 int
290 csprng_add_entropy(struct csprng_state *state, int src_id,
291 		   const uint8_t *entropy, size_t bytes, int flags)
292 {
293 	struct csprng_pool *pool;
294 	int pool_id;
295 
296 	/*
297 	 * Pick the next pool for this source on a round-robin
298 	 * basis.
299 	 */
300 	src_id &= 0xff;
301 	pool_id = state->src_pool_idx[src_id]++ & 0x1f;
302 	pool = &state->pool[pool_id];
303 
304 	SHA256_Update(&pool->hash_ctx, (const uint8_t *)&src_id,
305 		      sizeof(src_id));
306 	SHA256_Update(&pool->hash_ctx, (const uint8_t *)&bytes,
307 		      sizeof(bytes));
308 	SHA256_Update(&pool->hash_ctx, entropy, bytes);
309 
310 	pool->bytes += bytes;
311 
312 	return 0;
313 }
314