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