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