diff options
author | Daiki Ueno <dueno@redhat.com> | 2019-06-21 15:49:26 +0200 |
---|---|---|
committer | Daiki Ueno <dueno@redhat.com> | 2019-06-28 17:30:25 +0200 |
commit | c7a419e7868fd9342c1799a04d21c2ff6292c405 (patch) | |
tree | fee0cdb2a517dd20bcf87089d83b48d173cb5f83 /lib/nettle | |
parent | 7d8fd3aee4d71e1cd79ab5c980d137b363283a33 (diff) | |
download | gnutls-c7a419e7868fd9342c1799a04d21c2ff6292c405.tar.gz |
nettle/rnd-fips: add FIPS 140-2 continuous RNG testtmp-fips-drbg-continuous
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>
Diffstat (limited to 'lib/nettle')
-rw-r--r-- | lib/nettle/rnd-fips.c | 102 |
1 files changed, 81 insertions, 21 deletions
diff --git a/lib/nettle/rnd-fips.c b/lib/nettle/rnd-fips.c index ee68cf68d5..ccb92d25a2 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); |