From c7a419e7868fd9342c1799a04d21c2ff6292c405 Mon Sep 17 00:00:00 2001 From: Daiki Ueno 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 --- 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 -#include -#include +#include #include #include +/* 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