1 /*
2  * Copyright (C) 2010-2012 Free Software Foundation, Inc.
3  * Copyright (C) 2016-2017 Red Hat, Inc.
4  *
5  * Author: Nikos Mavrogiannopoulos
6  *
7  * This file is part of GNUTLS.
8  *
9  * The GNUTLS library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>
21  *
22  */
23 
24 #include "gnutls_int.h"
25 #include "errors.h"
26 #include <locks.h>
27 #include <num.h>
28 #include <nettle/chacha.h>
29 #include <rnd-common.h>
30 #include <system.h>
31 #include <atfork.h>
32 #include <errno.h>
33 #include <minmax.h>
34 
35 #define PRNG_KEY_SIZE CHACHA_KEY_SIZE
36 
37 /* For a high level description see the documentation and
38  * the 'Random number generation' section of chapter
39  * 'Using GnuTLS as a cryptographic library'.
40  */
41 
42 /* We have two "refresh" operations for the PRNG:
43  *  re-seed: the random generator obtains a new key from the system or another PRNG
44  *           (occurs when a time or data-based limit is reached for the GNUTLS_RND_RANDOM
45  *            and GNUTLS_RND_KEY levels and data-based for the nonce level)
46  *  re-key:  the random generator obtains a new key by utilizing its own output.
47  *           This only happens for the GNUTLS_RND_KEY level, on every operation.
48  */
49 
50 /* after this number of bytes PRNG will rekey using the system RNG */
51 static const unsigned prng_reseed_limits[] = {
52 	[GNUTLS_RND_NONCE] = 16*1024*1024, /* 16 MB - we re-seed using the GNUTLS_RND_RANDOM output */
53 	[GNUTLS_RND_RANDOM] = 2*1024*1024, /* 2MB - we re-seed by time as well */
54 	[GNUTLS_RND_KEY] = 2*1024*1024 /* same as GNUTLS_RND_RANDOM - but we re-key on every operation */
55 };
56 
57 static const time_t prng_reseed_time[] = {
58 	[GNUTLS_RND_NONCE] = 14400, /* 4 hours */
59 	[GNUTLS_RND_RANDOM] = 7200, /* 2 hours */
60 	[GNUTLS_RND_KEY] = 7200 /* same as RANDOM */
61 };
62 
63 struct prng_ctx_st {
64 	struct chacha_ctx ctx;
65 	size_t counter;
66 	unsigned int forkid;
67 	time_t last_reseed;
68 };
69 
70 struct generators_ctx_st {
71 	struct prng_ctx_st nonce;  /* GNUTLS_RND_NONCE */
72 	struct prng_ctx_st normal; /* GNUTLS_RND_RANDOM, GNUTLS_RND_KEY */
73 };
74 
75 
wrap_nettle_rnd_deinit(void * _ctx)76 static void wrap_nettle_rnd_deinit(void *_ctx)
77 {
78 	gnutls_free(_ctx);
79 }
80 
81 /* Initializes the nonce level random generator.
82  *
83  * the @new_key must be provided.
84  *
85  * @init must be non zero on first initialization, and
86  * zero on any subsequent reinitializations.
87  */
single_prng_init(struct prng_ctx_st * ctx,uint8_t new_key[PRNG_KEY_SIZE],unsigned new_key_size,unsigned init)88 static int single_prng_init(struct prng_ctx_st *ctx,
89 			    uint8_t new_key[PRNG_KEY_SIZE],
90 			    unsigned new_key_size,
91 			    unsigned init)
92 {
93 	uint8_t nonce[CHACHA_NONCE_SIZE];
94 
95 	memset(nonce, 0, sizeof(nonce)); /* to prevent valgrind from whinning */
96 
97 	if (init == 0) {
98 		/* use the previous key to generate IV as well */
99 		chacha_crypt(&ctx->ctx, sizeof(nonce), nonce, nonce);
100 
101 		/* Add key continuity by XORing the new key with data generated
102 		 * from the old key */
103 		chacha_crypt(&ctx->ctx, new_key_size, new_key, new_key);
104 	} else {
105 		struct timespec now; /* current time */
106 
107 		ctx->forkid = _gnutls_get_forkid();
108 
109 		gnutls_gettime(&now);
110 		memcpy(nonce, &now, MIN(sizeof(nonce), sizeof(now)));
111 		ctx->last_reseed = now.tv_sec;
112 	}
113 
114 	chacha_set_key(&ctx->ctx, new_key);
115 	chacha_set_nonce(&ctx->ctx, nonce);
116 
117 	zeroize_key(new_key, new_key_size);
118 
119 	ctx->counter = 0;
120 
121 	return 0;
122 }
123 
124 /* API functions */
125 
wrap_nettle_rnd_init(void ** _ctx)126 static int wrap_nettle_rnd_init(void **_ctx)
127 {
128 	int ret;
129 	uint8_t new_key[PRNG_KEY_SIZE*2];
130 	struct generators_ctx_st *ctx;
131 
132 	ctx = calloc(1, sizeof(*ctx));
133 	if (ctx == NULL)
134 		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
135 
136 	/* initialize the nonce RNG */
137 	ret = _rnd_get_system_entropy(new_key, sizeof(new_key));
138 	if (ret < 0) {
139 		gnutls_assert();
140 		goto fail;
141 	}
142 
143 	ret = single_prng_init(&ctx->nonce, new_key, PRNG_KEY_SIZE, 1);
144 	if (ret < 0) {
145 		gnutls_assert();
146 		goto fail;
147 	}
148 
149 	/* initialize the random/key RNG */
150 	ret = single_prng_init(&ctx->normal, new_key+PRNG_KEY_SIZE, PRNG_KEY_SIZE, 1);
151 	if (ret < 0) {
152 		gnutls_assert();
153 		goto fail;
154 	}
155 
156 	*_ctx = ctx;
157 
158 	return 0;
159  fail:
160 	gnutls_free(ctx);
161 	return ret;
162 }
163 
164 static int
wrap_nettle_rnd(void * _ctx,int level,void * data,size_t datasize)165 wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize)
166 {
167 	struct generators_ctx_st *ctx = _ctx;
168 	struct prng_ctx_st *prng_ctx;
169 	int ret, reseed = 0;
170 	uint8_t new_key[PRNG_KEY_SIZE];
171 	time_t now;
172 
173 	if (level == GNUTLS_RND_RANDOM || level == GNUTLS_RND_KEY)
174 		prng_ctx = &ctx->normal;
175 	else if (level == GNUTLS_RND_NONCE)
176 		prng_ctx = &ctx->nonce;
177 	else
178 		return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
179 
180 	/* Two reasons for this memset():
181 	 *  1. avoid getting filled with valgrind warnings
182 	 *  2. avoid a cipher/PRNG failure to expose stack data
183 	 */
184 	memset(data, 0, datasize);
185 
186 	now = gnutls_time(0);
187 
188 	/* We re-seed based on time in addition to output data. That is,
189 	 * to prevent a temporal state compromise to become permanent for low
190 	 * traffic sites */
191 	if (unlikely(_gnutls_detect_fork(prng_ctx->forkid))) {
192 		reseed = 1;
193 	} else {
194 		if (now > prng_ctx->last_reseed + prng_reseed_time[level])
195 			reseed = 1;
196 	}
197 
198 	if (reseed != 0 || prng_ctx->counter > prng_reseed_limits[level]) {
199 		if (level == GNUTLS_RND_NONCE) {
200 			ret = wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, new_key, sizeof(new_key));
201 		} else {
202 
203 			/* we also use the system entropy to reduce the impact
204 			 * of a temporal state compromise for these two levels. */
205 			ret = _rnd_get_system_entropy(new_key, sizeof(new_key));
206 		}
207 
208 		if (ret < 0) {
209 			gnutls_assert();
210 			goto cleanup;
211 		}
212 
213 		ret = single_prng_init(prng_ctx, new_key, sizeof(new_key), 0);
214 		if (ret < 0) {
215 			gnutls_assert();
216 			goto cleanup;
217 		}
218 
219 		prng_ctx->last_reseed = now;
220 		prng_ctx->forkid = _gnutls_get_forkid();
221 	}
222 
223 	chacha_crypt(&prng_ctx->ctx, datasize, data, data);
224 	prng_ctx->counter += datasize;
225 
226 	if (level == GNUTLS_RND_KEY) { /* prevent backtracking */
227 		ret = wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, new_key, sizeof(new_key));
228 		if (ret < 0) {
229 			gnutls_assert();
230 			goto cleanup;
231 		}
232 
233 		ret = single_prng_init(prng_ctx, new_key, sizeof(new_key), 0);
234 		if (ret < 0) {
235 			gnutls_assert();
236 			goto cleanup;
237 		}
238 	}
239 
240 	ret = 0;
241 
242 cleanup:
243 	return ret;
244 }
245 
wrap_nettle_rnd_refresh(void * _ctx)246 static void wrap_nettle_rnd_refresh(void *_ctx)
247 {
248 	struct generators_ctx_st *ctx = _ctx;
249 	char tmp;
250 
251 	/* force reseed */
252 	ctx->nonce.counter = prng_reseed_limits[GNUTLS_RND_NONCE]+1;
253 	ctx->normal.counter = prng_reseed_limits[GNUTLS_RND_RANDOM]+1;
254 
255 	wrap_nettle_rnd(_ctx, GNUTLS_RND_NONCE, &tmp, 1);
256 	wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, &tmp, 1);
257 
258 	return;
259 }
260 
261 int crypto_rnd_prio = INT_MAX;
262 
263 gnutls_crypto_rnd_st _gnutls_rnd_ops = {
264 	.init = wrap_nettle_rnd_init,
265 	.deinit = wrap_nettle_rnd_deinit,
266 	.rnd = wrap_nettle_rnd,
267 	.rnd_refresh = wrap_nettle_rnd_refresh,
268 	.self_test = NULL,
269 };
270