1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Conrad Meyer <[email protected]>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 #include <sys/param.h>
30 #include <sys/fail.h>
31 #include <sys/limits.h>
32 #include <sys/lock.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/mutex.h>
36 #include <sys/random.h>
37 #include <sys/sdt.h>
38 #include <sys/sysctl.h>
39 #include <sys/systm.h>
40
41 #include <machine/cpu.h>
42 #include <machine/stdarg.h>
43
44 #define CHACHA_EMBED
45 #define KEYSTREAM_ONLY
46 #define CHACHA_NONCE0_CTR128
47 #include <crypto/chacha20/chacha.h>
48 #include <crypto/rijndael/rijndael-api-fst.h>
49 #include <crypto/sha2/sha256.h>
50
51 #include <dev/random/hash.h>
52 #include <dev/random/randomdev.h>
53 #include <dev/random/random_harvestq.h>
54 #include <dev/random/uint128.h>
55
56 #include <dev/random/fenestrasX/fx_hash.h>
57 #include <dev/random/fenestrasX/fx_priv.h>
58 #include <dev/random/fenestrasX/fx_rng.h>
59
60 _Static_assert(FX_CHACHA20_KEYSIZE == RANDOM_KEYSIZE, "");
61
62 #include <crypto/chacha20/chacha.c>
63
64 static void
fxrng_rng_keystream_internal(struct chacha_ctx * prf,void * buf,size_t nbytes)65 fxrng_rng_keystream_internal(struct chacha_ctx *prf, void *buf, size_t nbytes)
66 {
67 size_t chunklen;
68
69 while (nbytes > 0) {
70 chunklen = MIN(nbytes,
71 rounddown((size_t)UINT32_MAX, CHACHA_BLOCKLEN));
72
73 chacha_encrypt_bytes(prf, NULL, buf, chunklen);
74 buf = (uint8_t *)buf + chunklen;
75 nbytes -= chunklen;
76 }
77 }
78
79 /*
80 * This subroutine pulls the counter out of Chacha, which for whatever reason
81 * always encodes and decodes counters in a little endian format, and adds
82 * 'addend' to it, saving the result in Chacha.
83 */
84 static void
fxrng_chacha_nonce_add64(struct chacha_ctx * ctx,uint64_t addend)85 fxrng_chacha_nonce_add64(struct chacha_ctx *ctx, uint64_t addend)
86 {
87 uint128_t ctr; /* Native-endian. */
88 #if BYTE_ORDER == BIG_ENDIAN
89 uint128_t lectr;
90
91 chacha_ctrsave(ctx, (void *)&lectr);
92 ctr = le128dec(&lectr);
93 #else
94 chacha_ctrsave(ctx, (void *)&ctr);
95 #endif
96
97 uint128_add64(&ctr, addend);
98
99 /* chacha_ivsetup() does not modify the key, and we rely on that. */
100 #if BYTE_ORDER == BIG_ENDIAN
101 le128enc(&lectr, ctr);
102 chacha_ivsetup(ctx, NULL, (const void *)&lectr);
103 explicit_bzero(&lectr, sizeof(lectr));
104 #else
105 chacha_ivsetup(ctx, NULL, (const void *)&ctr);
106 #endif
107 explicit_bzero(&ctr, sizeof(ctr));
108 }
109
110 /*
111 * Generate from the unbuffered source PRNG.
112 *
113 * Handles fast key erasure (rekeys the PRF with a generated key under lock).
114 *
115 * RNG lock is required on entry. If return_unlocked is true, RNG lock will
116 * be dropped on return.
117 */
118 void
fxrng_rng_genrandom_internal(struct fxrng_basic_rng * rng,void * buf,size_t nbytes,bool return_unlocked)119 fxrng_rng_genrandom_internal(struct fxrng_basic_rng *rng, void *buf,
120 size_t nbytes, bool return_unlocked)
121 {
122 struct chacha_ctx ctx_copy, *p_ctx;
123 uint8_t newkey[FX_CHACHA20_KEYSIZE];
124 size_t blockcount;
125
126 FXRNG_RNG_ASSERT(rng);
127
128 /* Save off the initial output of the generator for rekeying. */
129 fxrng_rng_keystream_internal(&rng->rng_prf, newkey, sizeof(newkey));
130
131 if (return_unlocked) {
132 memcpy(&ctx_copy, &rng->rng_prf, sizeof(ctx_copy));
133 p_ctx = &ctx_copy;
134
135 /*
136 * Forward the Chacha counter state over the blocks we promise
137 * to generate for the caller without the lock.
138 */
139 blockcount = howmany(nbytes, CHACHA_BLOCKLEN);
140 fxrng_chacha_nonce_add64(&rng->rng_prf, blockcount);
141
142 /* Re-key before dropping the lock. */
143 chacha_keysetup(&rng->rng_prf, newkey, sizeof(newkey) * 8);
144 explicit_bzero(newkey, sizeof(newkey));
145
146 FXRNG_RNG_UNLOCK(rng);
147 } else {
148 p_ctx = &rng->rng_prf;
149 }
150
151 fxrng_rng_keystream_internal(p_ctx, buf, nbytes);
152
153 if (return_unlocked) {
154 explicit_bzero(&ctx_copy, sizeof(ctx_copy));
155 FXRNG_RNG_ASSERT_NOT(rng);
156 } else {
157 /* Re-key before exit. */
158 chacha_keysetup(&rng->rng_prf, newkey, sizeof(newkey) * 8);
159 explicit_bzero(newkey, sizeof(newkey));
160 FXRNG_RNG_ASSERT(rng);
161 }
162 }
163
164 /*
165 * Helper to reseed the root RNG, incorporating the existing RNG state.
166 *
167 * The root RNG is locked on entry and locked on return.
168 */
169 static void
fxrng_rng_reseed_internal(struct fxrng_basic_rng * rng,bool seeded,const void * src,size_t sz,...)170 fxrng_rng_reseed_internal(struct fxrng_basic_rng *rng, bool seeded,
171 const void *src, size_t sz, ...)
172 {
173 union {
174 uint8_t root_state[FX_CHACHA20_KEYSIZE];
175 uint8_t hash_out[FXRNG_HASH_SZ];
176 } u;
177 struct fxrng_hash mix;
178 va_list ap;
179
180 _Static_assert(FX_CHACHA20_KEYSIZE <= FXRNG_HASH_SZ, "");
181
182 FXRNG_RNG_ASSERT(rng);
183
184 fxrng_hash_init(&mix);
185 if (seeded) {
186 fxrng_rng_keystream_internal(&rng->rng_prf, u.root_state,
187 sizeof(u.root_state));
188 fxrng_hash_update(&mix, u.root_state, sizeof(u.root_state));
189 }
190 fxrng_hash_update(&mix, src, sz);
191
192 va_start(ap, sz);
193 while (true) {
194 src = va_arg(ap, const void *);
195 if (src == NULL)
196 break;
197 sz = va_arg(ap, size_t);
198 fxrng_hash_update(&mix, src, sz);
199 }
200 va_end(ap);
201
202 fxrng_hash_finish(&mix, u.hash_out, sizeof(u.hash_out));
203
204 /*
205 * Take the first keysize (32) bytes of our digest (64 bytes). It is
206 * also possible to just have Blake2 emit fewer bytes, but our wrapper
207 * API doesn't provide that functionality and there isn't anything
208 * obviously wrong with emitting more hash bytes.
209 *
210 * keysetup does not reset the embedded counter, and we rely on that
211 * property.
212 */
213 chacha_keysetup(&rng->rng_prf, u.hash_out, FX_CHACHA20_KEYSIZE * 8);
214
215 /* 'mix' zeroed by fxrng_hash_finish(). */
216 explicit_bzero(u.hash_out, sizeof(u.hash_out));
217
218 FXRNG_RNG_ASSERT(rng);
219 }
220
221 /*
222 * Directly reseed the root RNG from a first-time entropy source,
223 * incorporating the existing RNG state, called by fxrng_brng_src_reseed.
224 *
225 * The root RNG is locked on entry and locked on return.
226 */
227 void
fxrng_rng_src_reseed(struct fxrng_basic_rng * rng,const struct harvest_event * event)228 fxrng_rng_src_reseed(struct fxrng_basic_rng *rng,
229 const struct harvest_event *event)
230 {
231 fxrng_rng_reseed_internal(rng, true, &event->he_somecounter,
232 sizeof(event->he_somecounter), (const void *)event->he_entropy,
233 (size_t)event->he_size, NULL);
234 }
235
236 /*
237 * Reseed the root RNG from pooled entropy, incorporating the existing RNG
238 * state, called by fxrng_brng_reseed.
239 *
240 * The root RNG is locked on entry and locked on return.
241 */
242 void
fxrng_rng_reseed(struct fxrng_basic_rng * rng,bool seeded,const void * entr,size_t sz)243 fxrng_rng_reseed(struct fxrng_basic_rng *rng, bool seeded, const void *entr,
244 size_t sz)
245 {
246 fxrng_rng_reseed_internal(rng, seeded, entr, sz, NULL);
247 }
248