/src/gnutls/lib/nettle/rnd.c
Line | Count | Source |
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 | 0 | #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] = |
53 | | 16 * 1024 * |
54 | | 1024, /* 16 MB - we re-seed using the GNUTLS_RND_RANDOM output */ |
55 | | [GNUTLS_RND_RANDOM] = |
56 | | 2 * 1024 * 1024, /* 2MB - we re-seed by time as well */ |
57 | | [GNUTLS_RND_KEY] = |
58 | | 2 * 1024 * |
59 | | 1024 /* same as GNUTLS_RND_RANDOM - but we re-key on every operation */ |
60 | | }; |
61 | | |
62 | | static const time_t prng_reseed_time[] = { |
63 | | [GNUTLS_RND_NONCE] = 14400, /* 4 hours */ |
64 | | [GNUTLS_RND_RANDOM] = 7200, /* 2 hours */ |
65 | | [GNUTLS_RND_KEY] = 7200 /* same as RANDOM */ |
66 | | }; |
67 | | |
68 | | struct prng_ctx_st { |
69 | | struct chacha_ctx ctx; |
70 | | size_t counter; |
71 | | unsigned int forkid; |
72 | | time_t last_reseed; |
73 | | }; |
74 | | |
75 | | struct generators_ctx_st { |
76 | | struct prng_ctx_st nonce; /* GNUTLS_RND_NONCE */ |
77 | | struct prng_ctx_st normal; /* GNUTLS_RND_RANDOM, GNUTLS_RND_KEY */ |
78 | | }; |
79 | | |
80 | | static void wrap_nettle_rnd_deinit(void *_ctx) |
81 | 0 | { |
82 | 0 | zeroize_key(_ctx, sizeof(struct generators_ctx_st)); |
83 | 0 | gnutls_free(_ctx); |
84 | 0 | } |
85 | | |
86 | | /* Initializes the nonce level random generator. |
87 | | * |
88 | | * the @new_key must be provided. |
89 | | * |
90 | | * @init must be non zero on first initialization, and |
91 | | * zero on any subsequent reinitializations. |
92 | | */ |
93 | | static int single_prng_init(struct prng_ctx_st *ctx, |
94 | | uint8_t new_key[PRNG_KEY_SIZE], |
95 | | unsigned new_key_size, unsigned init) |
96 | 0 | { |
97 | 0 | uint8_t nonce[CHACHA_NONCE_SIZE]; |
98 | |
|
99 | 0 | memset(nonce, 0, sizeof(nonce)); /* to prevent valgrind from whinning */ |
100 | |
|
101 | 0 | if (init == 0) { |
102 | | /* use the previous key to generate IV as well */ |
103 | 0 | chacha_crypt(&ctx->ctx, sizeof(nonce), nonce, nonce); |
104 | | |
105 | | /* Add key continuity by XORing the new key with data generated |
106 | | * from the old key */ |
107 | 0 | chacha_crypt(&ctx->ctx, new_key_size, new_key, new_key); |
108 | 0 | } else { |
109 | 0 | struct timespec now; /* current time */ |
110 | |
|
111 | 0 | ctx->forkid = _gnutls_get_forkid(); |
112 | |
|
113 | 0 | gnutls_gettime(&now); |
114 | 0 | memcpy(nonce, &now, MIN(sizeof(nonce), sizeof(now))); |
115 | 0 | ctx->last_reseed = now.tv_sec; |
116 | 0 | } |
117 | |
|
118 | 0 | chacha_set_key(&ctx->ctx, new_key); |
119 | 0 | chacha_set_nonce(&ctx->ctx, nonce); |
120 | |
|
121 | 0 | zeroize_key(new_key, new_key_size); |
122 | 0 | zeroize_key(nonce, sizeof(nonce)); |
123 | |
|
124 | 0 | ctx->counter = 0; |
125 | |
|
126 | 0 | return 0; |
127 | 0 | } |
128 | | |
129 | | /* API functions */ |
130 | | |
131 | | static int wrap_nettle_rnd_init(void **_ctx) |
132 | 0 | { |
133 | 0 | int ret = 0; |
134 | 0 | uint8_t new_key[PRNG_KEY_SIZE * 2]; |
135 | 0 | struct generators_ctx_st *ctx; |
136 | |
|
137 | 0 | ctx = calloc(1, sizeof(*ctx)); |
138 | 0 | if (ctx == NULL) |
139 | 0 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
140 | | |
141 | | /* initialize the nonce RNG */ |
142 | 0 | ret = _rnd_get_system_entropy(new_key, sizeof(new_key)); |
143 | 0 | if (ret < 0) { |
144 | 0 | gnutls_assert(); |
145 | 0 | goto cleanup; |
146 | 0 | } |
147 | | |
148 | 0 | ret = single_prng_init(&ctx->nonce, new_key, PRNG_KEY_SIZE, 1); |
149 | 0 | if (ret < 0) { |
150 | 0 | gnutls_assert(); |
151 | 0 | goto cleanup; |
152 | 0 | } |
153 | | |
154 | | /* initialize the random/key RNG */ |
155 | 0 | ret = single_prng_init(&ctx->normal, new_key + PRNG_KEY_SIZE, |
156 | 0 | PRNG_KEY_SIZE, 1); |
157 | 0 | if (ret < 0) { |
158 | 0 | gnutls_assert(); |
159 | 0 | goto cleanup; |
160 | 0 | } |
161 | | |
162 | 0 | *_ctx = _gnutls_steal_pointer((void **)&ctx); |
163 | |
|
164 | 0 | cleanup: |
165 | 0 | zeroize_key(new_key, sizeof(new_key)); |
166 | 0 | gnutls_free(ctx); |
167 | 0 | return ret; |
168 | 0 | } |
169 | | |
170 | | static int wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize) |
171 | 0 | { |
172 | 0 | struct generators_ctx_st *ctx = _ctx; |
173 | 0 | struct prng_ctx_st *prng_ctx; |
174 | 0 | int ret, reseed = 0; |
175 | 0 | uint8_t new_key[PRNG_KEY_SIZE]; |
176 | 0 | time_t now; |
177 | |
|
178 | 0 | if (level == GNUTLS_RND_RANDOM || level == GNUTLS_RND_KEY) |
179 | 0 | prng_ctx = &ctx->normal; |
180 | 0 | else if (level == GNUTLS_RND_NONCE) |
181 | 0 | prng_ctx = &ctx->nonce; |
182 | 0 | else { |
183 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); |
184 | 0 | return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED); |
185 | 0 | } |
186 | | |
187 | | /* Two reasons for this memset(): |
188 | | * 1. avoid getting filled with valgrind warnings |
189 | | * 2. avoid a cipher/PRNG failure to expose stack data |
190 | | */ |
191 | 0 | memset(data, 0, datasize); |
192 | |
|
193 | 0 | now = gnutls_time(0); |
194 | | |
195 | | /* We re-seed based on time in addition to output data. That is, |
196 | | * to prevent a temporal state compromise to become permanent for low |
197 | | * traffic sites */ |
198 | 0 | if (unlikely(_gnutls_detect_fork(prng_ctx->forkid))) { |
199 | 0 | reseed = 1; |
200 | 0 | } else { |
201 | 0 | if (now > prng_ctx->last_reseed + prng_reseed_time[level]) |
202 | 0 | reseed = 1; |
203 | 0 | } |
204 | |
|
205 | 0 | if (reseed != 0 || prng_ctx->counter > prng_reseed_limits[level]) { |
206 | 0 | if (level == GNUTLS_RND_NONCE) { |
207 | 0 | ret = wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, new_key, |
208 | 0 | sizeof(new_key)); |
209 | 0 | } else { |
210 | | /* we also use the system entropy to reduce the impact |
211 | | * of a temporal state compromise for these two levels. */ |
212 | 0 | ret = _rnd_get_system_entropy(new_key, sizeof(new_key)); |
213 | 0 | } |
214 | |
|
215 | 0 | if (ret < 0) { |
216 | 0 | gnutls_assert(); |
217 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); |
218 | 0 | goto cleanup; |
219 | 0 | } |
220 | | |
221 | 0 | ret = single_prng_init(prng_ctx, new_key, sizeof(new_key), 0); |
222 | 0 | if (ret < 0) { |
223 | 0 | gnutls_assert(); |
224 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); |
225 | 0 | goto cleanup; |
226 | 0 | } |
227 | | |
228 | 0 | prng_ctx->last_reseed = now; |
229 | 0 | prng_ctx->forkid = _gnutls_get_forkid(); |
230 | 0 | } |
231 | | |
232 | 0 | chacha_crypt(&prng_ctx->ctx, datasize, data, data); |
233 | 0 | prng_ctx->counter += datasize; |
234 | |
|
235 | 0 | if (level == GNUTLS_RND_KEY) { /* prevent backtracking */ |
236 | 0 | ret = wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, new_key, |
237 | 0 | sizeof(new_key)); |
238 | 0 | if (ret < 0) { |
239 | 0 | gnutls_assert(); |
240 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); |
241 | 0 | goto cleanup; |
242 | 0 | } |
243 | | |
244 | 0 | ret = single_prng_init(prng_ctx, new_key, sizeof(new_key), 0); |
245 | 0 | if (ret < 0) { |
246 | 0 | gnutls_assert(); |
247 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); |
248 | 0 | goto cleanup; |
249 | 0 | } |
250 | 0 | } |
251 | | |
252 | 0 | ret = 0; |
253 | 0 | _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED); |
254 | |
|
255 | 0 | cleanup: |
256 | 0 | zeroize_key(new_key, sizeof(new_key)); |
257 | 0 | return ret; |
258 | 0 | } |
259 | | |
260 | | static void wrap_nettle_rnd_refresh(void *_ctx) |
261 | 0 | { |
262 | 0 | struct generators_ctx_st *ctx = _ctx; |
263 | 0 | char tmp; |
264 | | |
265 | | /* force reseed */ |
266 | 0 | ctx->nonce.counter = prng_reseed_limits[GNUTLS_RND_NONCE] + 1; |
267 | 0 | ctx->normal.counter = prng_reseed_limits[GNUTLS_RND_RANDOM] + 1; |
268 | |
|
269 | 0 | wrap_nettle_rnd(_ctx, GNUTLS_RND_NONCE, &tmp, 1); |
270 | 0 | wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, &tmp, 1); |
271 | 0 | } |
272 | | |
273 | | int crypto_rnd_prio = INT_MAX; |
274 | | |
275 | | gnutls_crypto_rnd_st _gnutls_rnd_ops = { |
276 | | .init = wrap_nettle_rnd_init, |
277 | | .deinit = wrap_nettle_rnd_deinit, |
278 | | .rnd = wrap_nettle_rnd, |
279 | | .rnd_refresh = wrap_nettle_rnd_refresh, |
280 | | .self_test = NULL, |
281 | | }; |