Coverage Report

Created: 2023-03-26 07:33

/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
};