Commit db07cd26 authored by Stephan Mueller's avatar Stephan Mueller Committed by Herbert Xu
Browse files

crypto: drbg - add FIPS 140-2 CTRNG for noise source



FIPS 140-2 section 4.9.2 requires a continuous self test of the noise
source. Up to kernel 4.8 drivers/char/random.c provided this continuous
self test. Afterwards it was moved to a location that is inconsistent
with the FIPS 140-2 requirements. The relevant patch was
e192be9d .

Thus, the FIPS 140-2 CTRNG is added to the DRBG when it obtains the
seed. This patch resurrects the function drbg_fips_continous_test that
existed some time ago and applies it to the noise sources. The patch
that removed the drbg_fips_continous_test was
b3614763 .

The Jitter RNG implements its own FIPS 140-2 self test and thus does not
need to be subjected to the test in the DRBG.

The patch contains a tiny fix to ensure proper zeroization in case of an
error during the Jitter RNG data gathering.

Signed-off-by: default avatarStephan Mueller <smueller@chronox.de>
Reviewed-by: default avatarYann Droneaud <ydroneaud@opteya.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent a7cd942b
Loading
Loading
Loading
Loading
+91 −3
Original line number Diff line number Diff line
@@ -219,6 +219,57 @@ static inline unsigned short drbg_sec_strength(drbg_flag_t flags)
	}
}

/*
 * FIPS 140-2 continuous self test for the noise source
 * The test is performed on the noise source input data. Thus, the function
 * implicitly knows the size of the buffer to be equal to the security
 * strength.
 *
 * Note, this function disregards the nonce trailing the entropy data during
 * initial seeding.
 *
 * drbg->drbg_mutex must have been taken.
 *
 * @drbg DRBG handle
 * @entropy buffer of seed data to be checked
 *
 * return:
 *	0 on success
 *	-EAGAIN on when the CTRNG is not yet primed
 *	< 0 on error
 */
static int drbg_fips_continuous_test(struct drbg_state *drbg,
				     const unsigned char *entropy)
{
	unsigned short entropylen = drbg_sec_strength(drbg->core->flags);
	int ret = 0;

	if (!IS_ENABLED(CONFIG_CRYPTO_FIPS))
		return 0;

	/* skip test if we test the overall system */
	if (list_empty(&drbg->test_data.list))
		return 0;
	/* only perform test in FIPS mode */
	if (!fips_enabled)
		return 0;

	if (!drbg->fips_primed) {
		/* Priming of FIPS test */
		memcpy(drbg->prev, entropy, entropylen);
		drbg->fips_primed = true;
		/* priming: another round is needed */
		return -EAGAIN;
	}
	ret = memcmp(drbg->prev, entropy, entropylen);
	if (!ret)
		panic("DRBG continuous self test failed\n");
	memcpy(drbg->prev, entropy, entropylen);

	/* the test shall pass when the two values are not equal */
	return 0;
}

/*
 * Convert an integer into a byte representation of this integer.
 * The byte representation is big-endian
@@ -998,6 +1049,22 @@ static inline int __drbg_seed(struct drbg_state *drbg, struct list_head *seed,
	return ret;
}

static inline int drbg_get_random_bytes(struct drbg_state *drbg,
					unsigned char *entropy,
					unsigned int entropylen)
{
	int ret;

	do {
		get_random_bytes(entropy, entropylen);
		ret = drbg_fips_continuous_test(drbg, entropy);
		if (ret && ret != -EAGAIN)
			return ret;
	} while (ret);

	return 0;
}

static void drbg_async_seed(struct work_struct *work)
{
	struct drbg_string data;
@@ -1006,16 +1073,20 @@ static void drbg_async_seed(struct work_struct *work)
					       seed_work);
	unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
	unsigned char entropy[32];
	int ret;

	BUG_ON(!entropylen);
	BUG_ON(entropylen > sizeof(entropy));
	get_random_bytes(entropy, entropylen);

	drbg_string_fill(&data, entropy, entropylen);
	list_add_tail(&data.list, &seedlist);

	mutex_lock(&drbg->drbg_mutex);

	ret = drbg_get_random_bytes(drbg, entropy, entropylen);
	if (ret)
		goto unlock;

	/* If nonblocking pool is initialized, deactivate Jitter RNG */
	crypto_free_rng(drbg->jent);
	drbg->jent = NULL;
@@ -1030,6 +1101,7 @@ static void drbg_async_seed(struct work_struct *work)
	if (drbg->seeded)
		drbg->reseed_threshold = drbg_max_requests(drbg);

unlock:
	mutex_unlock(&drbg->drbg_mutex);

	memzero_explicit(entropy, entropylen);
@@ -1081,7 +1153,9 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
		BUG_ON((entropylen * 2) > sizeof(entropy));

		/* Get seed from in-kernel /dev/urandom */
		get_random_bytes(entropy, entropylen);
		ret = drbg_get_random_bytes(drbg, entropy, entropylen);
		if (ret)
			goto out;

		if (!drbg->jent) {
			drbg_string_fill(&data1, entropy, entropylen);
@@ -1094,7 +1168,7 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
						   entropylen);
			if (ret) {
				pr_devel("DRBG: jent failed with %d\n", ret);
				return ret;
				goto out;
			}

			drbg_string_fill(&data1, entropy, entropylen * 2);
@@ -1121,6 +1195,7 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,

	ret = __drbg_seed(drbg, &seedlist, reseed);

out:
	memzero_explicit(entropy, entropylen * 2);

	return ret;
@@ -1142,6 +1217,11 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg)
	drbg->reseed_ctr = 0;
	drbg->d_ops = NULL;
	drbg->core = NULL;
	if (IS_ENABLED(CONFIG_CRYPTO_FIPS)) {
		kzfree(drbg->prev);
		drbg->prev = NULL;
		drbg->fips_primed = false;
	}
}

/*
@@ -1211,6 +1291,14 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
		drbg->scratchpad = PTR_ALIGN(drbg->scratchpadbuf, ret + 1);
	}

	if (IS_ENABLED(CONFIG_CRYPTO_FIPS)) {
		drbg->prev = kzalloc(drbg_sec_strength(drbg->core->flags),
				     GFP_KERNEL);
		if (!drbg->prev)
			goto fini;
		drbg->fips_primed = false;
	}

	return 0;

fini:
+2 −0
Original line number Diff line number Diff line
@@ -129,6 +129,8 @@ struct drbg_state {

	bool seeded;		/* DRBG fully seeded? */
	bool pr;		/* Prediction resistance enabled? */
	bool fips_primed;	/* Continuous test primed? */
	unsigned char *prev;	/* FIPS 140-2 continuous test value */
	struct work_struct seed_work;	/* asynchronous seeding support */
	struct crypto_rng *jent;
	const struct drbg_state_ops *d_ops;