summaryrefslogtreecommitdiffstats
path: root/lib/nettle/rnd-fips.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:33:12 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:33:12 +0000
commit36082a2fe36ecd800d784ae44c14f1f18c66a7e9 (patch)
tree6c68e0c0097987aff85a01dabddd34b862309a7c /lib/nettle/rnd-fips.c
parentInitial commit. (diff)
downloadgnutls28-36082a2fe36ecd800d784ae44c14f1f18c66a7e9.tar.xz
gnutls28-36082a2fe36ecd800d784ae44c14f1f18c66a7e9.zip
Adding upstream version 3.7.9.upstream/3.7.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--lib/nettle/rnd-fips.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/lib/nettle/rnd-fips.c b/lib/nettle/rnd-fips.c
new file mode 100644
index 0000000..35ad618
--- /dev/null
+++ b/lib/nettle/rnd-fips.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2013-2017 Red Hat
+ *
+ * This file is part of GnuTLS.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <drbg-aes.h>
+#include <fips.h>
+
+#include "gnutls_int.h"
+#include "errors.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.
+ */
+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 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)
+{
+ int ret;
+
+ if ( _gnutls_detect_fork(fctx->forkid) != 0) {
+ ret = _rngfips_ctx_reinit(fctx);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ if (ctx->reseed_counter > DRBG_AES_RESEED_TIME) {
+ ret = drbg_reseed(fctx, ctx);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ ret = drbg_aes_random(ctx, length, buffer);
+ if (ret == 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+ }
+
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ 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_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ _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));
+
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ return 0;
+}
+
+#define PSTRING "gnutls-rng"
+#define PSTRING_SIZE (sizeof(PSTRING)-1)
+static int drbg_init(struct fips_ctx *fctx, struct drbg_aes_ctx *ctx)
+{
+ uint8_t buffer[DRBG_AES_SEED_SIZE];
+ int ret;
+
+ ret = get_entropy(fctx, buffer, sizeof(buffer));
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = drbg_aes_init(ctx, sizeof(buffer), buffer,
+ PSTRING_SIZE, (void*)PSTRING);
+ zeroize_key(buffer, sizeof(buffer));
+ if (ret == 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+ }
+
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ return GNUTLS_E_SUCCESS;
+}
+
+/* Reseed a generator. */
+static int drbg_reseed(struct fips_ctx *fctx, struct drbg_aes_ctx *ctx)
+{
+ uint8_t buffer[DRBG_AES_SEED_SIZE];
+ int ret;
+
+ ret = get_entropy(fctx, buffer, sizeof(buffer));
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = drbg_aes_reseed(ctx, sizeof(buffer), buffer, 0, NULL);
+ zeroize_key(buffer, sizeof(buffer));
+ if (ret == 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+ }
+
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ 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, &fctx->normal_context);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* nonce */
+ ret = drbg_init(fctx, &fctx->nonce_context);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ fctx->forkid = _gnutls_get_forkid();
+
+ return 0;
+}
+
+static int _rngfips_ctx_reinit(struct fips_ctx *fctx)
+{
+ int ret;
+
+ /* normal */
+ ret = drbg_reseed(fctx, &fctx->normal_context);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* nonce */
+ ret = drbg_reseed(fctx, &fctx->nonce_context);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ fctx->forkid = _gnutls_get_forkid();
+
+ return 0;
+}
+
+/* Initialize this random subsystem. */
+static int _rngfips_init(void **_ctx)
+{
+/* Basic initialization is required to
+ do a few checks on the implementation. */
+ struct fips_ctx *ctx;
+ int ret;
+
+ ctx = gnutls_calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ret = _rngfips_ctx_init(ctx);
+ if (ret < 0) {
+ gnutls_free(ctx);
+ return gnutls_assert_val(ret);
+ }
+
+ *_ctx = ctx;
+
+ return 0;
+}
+
+static int _rngfips_rnd(void *_ctx, int level, void *buffer, size_t length)
+{
+ struct fips_ctx *ctx = _ctx;
+ int ret;
+
+ switch (level) {
+ case GNUTLS_RND_RANDOM:
+ case GNUTLS_RND_KEY:
+ /* Unlike the chacha generator in rnd.c we do not need
+ * to explicitly protect against backtracking in GNUTLS_RND_KEY
+ * level. This protection is part of the DRBG generator. */
+ ret = get_random(&ctx->normal_context, ctx, buffer, length);
+ break;
+ default:
+ ret = get_random(&ctx->nonce_context, ctx, buffer, length);
+ break;
+ }
+
+ return ret;
+}
+
+static void _rngfips_deinit(void *_ctx)
+{
+ struct fips_ctx *ctx = _ctx;
+
+ zeroize_key(ctx, sizeof(*ctx));
+ free(ctx);
+}
+
+static void _rngfips_refresh(void *_ctx)
+{
+ /* this is predictable RNG. Don't refresh */
+ return;
+}
+
+static int selftest_kat(void)
+{
+ int ret;
+
+ ret = drbg_aes_self_test();
+
+ if (ret == 0) {
+ _gnutls_debug_log("DRBG-AES self test failed\n");
+ return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+ } else
+ _gnutls_debug_log("DRBG-AES self test succeeded\n");
+
+ return 0;
+}
+
+gnutls_crypto_rnd_st _gnutls_fips_rnd_ops = {
+ .init = _rngfips_init,
+ .deinit = _rngfips_deinit,
+ .rnd = _rngfips_rnd,
+ .rnd_refresh = _rngfips_refresh,
+ .self_test = selftest_kat,
+};
+