Coverage Report

Created: 2026-01-31 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
};