nettle/rnd-fips: add FIPS 140-2 continuous RNG test
authorDaiki Ueno <dueno@redhat.com>
Fri, 21 Jun 2019 13:49:26 +0000 (15:49 +0200)
committerDaiki Ueno <dueno@redhat.com>
Fri, 28 Jun 2019 15:30:25 +0000 (17:30 +0200)
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

index ee68cf68d5c4c2a239c3f7a4dd0961312e14a0d8..ccb92d25a29bcaa74fb69c13be3c6436439e7662 100644 (file)
 
 #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);