]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
nettle/rnd: introduced time limit for key generator
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Wed, 15 Mar 2017 13:21:43 +0000 (14:21 +0100)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Thu, 16 Mar 2017 10:46:46 +0000 (10:46 +0000)
That is, force re-key of the KEY and RANDOM PRNG after 2 hours
of operation, irrespective of the amount of data having been output.
At the same time, increase limits for key and nonce generators,
to prevent a large amount of system calls in busy servers.

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
lib/nettle/rnd.c

index 71390645ea149047e32b2848b2d902a68d71d673..b7d53b69ca5fa66c04c952878d510abe064e563e 100644 (file)
  * 'Using GnuTLS as a cryptographic library'.
  */
 
-/* after this number of bytes PRNG will rekey */
+/* We have two "refresh" operations for the PRNG:
+ *  re-seed: the random generator obtains a new key from the system or another PRNG
+ *           (occurs when a time or data-based limit is reached for the GNUTLS_RND_RANDOM
+ *            and GNUTLS_RND_KEY levels and data-based for the nonce level)
+ *  re-key:  the random generator obtains a new key by utilizing its own output.
+ *           This only happens for the GNUTLS_RND_KEY level, on every operation.
+ */
+
+/* after this number of bytes PRNG will rekey using the system RNG */
 static const unsigned prng_reseed_limits[] = {
-       [GNUTLS_RND_NONCE] = 1024*1024, /* 1 MB */
-       [GNUTLS_RND_RANDOM] = 16*1024, /* 16 kb */
-       [GNUTLS_RND_KEY] = 16*1024 /* same as GNUTLS_RND_RANDOM */
+       [GNUTLS_RND_NONCE] = 16*1024*1024, /* 16 MB - we re-seed using the GNUTLS_RND_RANDOM output */
+       [GNUTLS_RND_RANDOM] = 2*1024*1024, /* 2MB - we re-seed by time as well */
+       [GNUTLS_RND_KEY] = 2*1024*1024 /* same as GNUTLS_RND_RANDOM - but we re-key on every operation */
 };
 
+#define NON_NONCE_PRNG_RESEED_TIME 7200
+
 struct prng_ctx_st {
        struct chacha_ctx ctx;
        size_t counter;
        unsigned int forkid;
+       time_t last_reseed;
 };
 
 struct generators_ctx_st {
        struct prng_ctx_st nonce;  /* GNUTLS_RND_NONCE */
-       struct prng_ctx_st normal; /* GNUTLS_RND_RANDOM */
+       struct prng_ctx_st normal; /* GNUTLS_RND_RANDOM, GNUTLS_RND_KEY */
 };
 
 
@@ -93,6 +104,7 @@ static int single_prng_init(struct prng_ctx_st *ctx,
 
                gettime(&now);
                memcpy(nonce, &now, MIN(sizeof(nonce), sizeof(now)));
+               ctx->last_reseed = now.tv_sec;
        }
 
        chacha_set_key(&ctx->ctx, new_key);
@@ -161,20 +173,33 @@ wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize)
                return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
 
 
-       /* we don't really need memset here, but otherwise we
-        * get filled with valgrind warnings */
+       /* Two reasons for this memset():
+        *  1. avoid getting filled with valgrind warnings
+        *  2. avoid a cipher/PRNG failure to expose stack data
+        */
        memset(data, 0, datasize);
 
        if (_gnutls_detect_fork(prng_ctx->forkid)) {
                reseed = 1;
+
+       } else if (level != GNUTLS_RND_NONCE) {
+               /* for KEY and RANDOM levels we re-seed based on time in
+                * addition to data. That is, to prevent a temporal state
+                * compromise to become permanent for low traffic sites */
+               time_t now = gnutls_time(0);
+               if (now > prng_ctx->last_reseed + NON_NONCE_PRNG_RESEED_TIME) {
+                       reseed = 1;
+                       prng_ctx->last_reseed = now;
+               }
        }
 
        if (reseed != 0 || prng_ctx->counter > prng_reseed_limits[level]) {
                if (level == GNUTLS_RND_NONCE) {
                        ret = wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, new_key, sizeof(new_key));
                } else {
-                       /* we use the system entropy for KEY and RANDOM to reduce
-                        * the impact of a temporal state compromise for these two levels. */
+
+                       /* we also use the system entropy to reduce the impact
+                        * of a temporal state compromise for these two levels. */
                        ret = _rnd_get_system_entropy(new_key, sizeof(new_key));
                }