Blob Blame History Raw
From c7a419e7868fd9342c1799a04d21c2ff6292c405 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <dueno@redhat.com>
Date: Fri, 21 Jun 2019 15:49:26 +0200
Subject: [PATCH] nettle/rnd-fips: add FIPS 140-2 continuous RNG test

This adds a continuous random number generator test as defined in FIPS
140-2 4.9.2, by iteratively fetching fixed sized block from the system
and comparing consecutive blocks.

Signed-off-by: Daiki Ueno <dueno@redhat.com>
---
 lib/nettle/rnd-fips.c | 102 +++++++++++++++++++++++++++++++++---------
 1 file changed, 81 insertions(+), 21 deletions(-)

diff --git a/lib/nettle/rnd-fips.c b/lib/nettle/rnd-fips.c
index ee68cf68d..ccb92d25a 100644
--- a/lib/nettle/rnd-fips.c
+++ b/lib/nettle/rnd-fips.c
@@ -27,12 +27,13 @@
 
 #include "gnutls_int.h"
 #include "errors.h"
-#include <nettle/aes.h>
-#include <nettle/memxor.h>
-#include <locks.h>
+#include <nettle/sha2.h>
 #include <atfork.h>
 #include <rnd-common.h>
 
+/* The block size is chosen arbitrarily */
+#define ENTROPY_BLOCK_SIZE SHA256_DIGEST_SIZE
+
 /* This provides a random generator for gnutls. It uses
  * two instances of the DRBG-AES-CTR generator, one for
  * nonce level and another for the other levels of randomness.
@@ -41,11 +42,13 @@ struct fips_ctx {
 	struct drbg_aes_ctx nonce_context;
 	struct drbg_aes_ctx normal_context;
 	unsigned int forkid;
+	uint8_t entropy_hash[SHA256_DIGEST_SIZE];
 };
 
 static int _rngfips_ctx_reinit(struct fips_ctx *fctx);
 static int _rngfips_ctx_init(struct fips_ctx *fctx);
-static int drbg_reseed(struct drbg_aes_ctx *ctx);
+static int drbg_reseed(struct fips_ctx *fctx, struct drbg_aes_ctx *ctx);
+static int get_entropy(struct fips_ctx *fctx, uint8_t *buffer, size_t length);
 
 static int get_random(struct drbg_aes_ctx *ctx, struct fips_ctx *fctx,
 		      void *buffer, size_t length)
@@ -59,7 +62,7 @@ static int get_random(struct drbg_aes_ctx *ctx, struct fips_ctx *fctx,
 	}
 
 	if (ctx->reseed_counter > DRBG_AES_RESEED_TIME) {
-		ret = drbg_reseed(ctx);
+		ret = drbg_reseed(fctx, ctx);
 		if (ret < 0)
 			return gnutls_assert_val(ret);
 	}
@@ -71,54 +74,111 @@ static int get_random(struct drbg_aes_ctx *ctx, struct fips_ctx *fctx,
 	return 0;
 }
 
+static int get_entropy(struct fips_ctx *fctx, uint8_t *buffer, size_t length)
+{
+	int ret;
+	uint8_t block[ENTROPY_BLOCK_SIZE];
+	uint8_t hash[SHA256_DIGEST_SIZE];
+	struct sha256_ctx ctx;
+	size_t total = 0;
+
+	/* For FIPS 140-2 4.9.2 continuous random number generator
+	 * test, iteratively fetch fixed sized block from the system
+	 * RNG and compare consecutive blocks.
+	 *
+	 * Note that we store the hash of the entropy block rather
+	 * than the block itself for backward secrecy.
+	 */
+	while (total < length) {
+		ret = _rnd_get_system_entropy(block, ENTROPY_BLOCK_SIZE);
+		if (ret < 0)
+			return gnutls_assert_val(ret);
+
+		sha256_init(&ctx);
+		sha256_update(&ctx, sizeof(block), block);
+		sha256_digest(&ctx, sizeof(hash), hash);
+
+		if (memcmp(hash, fctx->entropy_hash, sizeof(hash)) == 0) {
+			_gnutls_switch_lib_state(LIB_STATE_ERROR);
+			return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+		}
+		memcpy(fctx->entropy_hash, hash, sizeof(hash));
+
+		memcpy(buffer, block, MIN(length - total, sizeof(block)));
+		total += sizeof(block);
+		buffer += sizeof(block);
+	}
+	zeroize_key(block, sizeof(block));
+
+	return 0;
+}
+
 #define PSTRING "gnutls-rng"
 #define PSTRING_SIZE (sizeof(PSTRING)-1)
-static int drbg_init(struct drbg_aes_ctx *ctx)
+static int drbg_init(struct fips_ctx *fctx, struct drbg_aes_ctx *ctx)
 {
 	uint8_t buffer[DRBG_AES_SEED_SIZE];
 	int ret;
 
-	/* Get a key from the standard RNG or from the entropy source.  */
-	ret = _rnd_get_system_entropy(buffer, sizeof(buffer));
+	ret = get_entropy(fctx, buffer, sizeof(buffer));
 	if (ret < 0)
 		return gnutls_assert_val(ret);
 
-	ret = drbg_aes_init(ctx, sizeof(buffer), buffer, PSTRING_SIZE, (void*)PSTRING);
+	ret = drbg_aes_init(ctx, sizeof(buffer), buffer,
+			    PSTRING_SIZE, (void*)PSTRING);
+	zeroize_key(buffer, sizeof(buffer));
 	if (ret == 0)
 		return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
 
-	zeroize_key(buffer, sizeof(buffer));
-
-	return 0;
+	return GNUTLS_E_SUCCESS;
 }
 
 /* Reseed a generator. */
-static int drbg_reseed(struct drbg_aes_ctx *ctx)
+static int drbg_reseed(struct fips_ctx *fctx, struct drbg_aes_ctx *ctx)
 {
 	uint8_t buffer[DRBG_AES_SEED_SIZE];
 	int ret;
 
-	/* The other two generators are seeded from /dev/random.  */
-	ret = _rnd_get_system_entropy(buffer, sizeof(buffer));
+	ret = get_entropy(fctx, buffer, sizeof(buffer));
 	if (ret < 0)
 		return gnutls_assert_val(ret);
 
-	drbg_aes_reseed(ctx, sizeof(buffer), buffer, 0, NULL);
+	ret = drbg_aes_reseed(ctx, sizeof(buffer), buffer, 0, NULL);
+	zeroize_key(buffer, sizeof(buffer));
+	if (ret == 0)
+		return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
 
-	return 0;
+	return GNUTLS_E_SUCCESS;
 }
 
 static int _rngfips_ctx_init(struct fips_ctx *fctx)
 {
+	uint8_t block[ENTROPY_BLOCK_SIZE];
+	struct sha256_ctx ctx;
 	int ret;
 
+	/* For FIPS 140-2 4.9.2 continuous random number generator
+	 * test, get the initial entropy from the system RNG and keep
+	 * it for comparison.
+	 *
+	 * Note that we store the hash of the entropy block rather
+	 * than the block itself for backward secrecy.
+	 */
+	ret = _rnd_get_system_entropy(block, sizeof(block));
+	if (ret < 0)
+		return gnutls_assert_val(ret);
+	sha256_init(&ctx);
+	sha256_update(&ctx, sizeof(block), block);
+	zeroize_key(block, sizeof(block));
+	sha256_digest(&ctx, sizeof(fctx->entropy_hash), fctx->entropy_hash);
+
 	/* normal */
-	ret = drbg_init(&fctx->normal_context);
+	ret = drbg_init(fctx, &fctx->normal_context);
 	if (ret < 0)
 		return gnutls_assert_val(ret);
 
 	/* nonce */
-	ret = drbg_init(&fctx->nonce_context);
+	ret = drbg_init(fctx, &fctx->nonce_context);
 	if (ret < 0)
 		return gnutls_assert_val(ret);
 
@@ -132,12 +192,12 @@ static int _rngfips_ctx_reinit(struct fips_ctx *fctx)
 	int ret;
 
 	/* normal */
-	ret = drbg_reseed(&fctx->normal_context);
+	ret = drbg_reseed(fctx, &fctx->normal_context);
 	if (ret < 0)
 		return gnutls_assert_val(ret);
 
 	/* nonce */
-	ret = drbg_reseed(&fctx->nonce_context);
+	ret = drbg_reseed(fctx, &fctx->nonce_context);
 	if (ret < 0)
 		return gnutls_assert_val(ret);
 
-- 
2.21.0