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