mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 05:11:48 +00:00
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 wase192be9d9a
. 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 wasb361476305
. 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: Stephan Mueller <smueller@chronox.de> Reviewed-by: Yann Droneaud <ydroneaud@opteya.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
parent
a7cd942bb6
commit
db07cd26ac
@ -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.
|
* Convert an integer into a byte representation of this integer.
|
||||||
* The byte representation is big-endian
|
* 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;
|
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)
|
static void drbg_async_seed(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct drbg_string data;
|
struct drbg_string data;
|
||||||
@ -1006,16 +1073,20 @@ static void drbg_async_seed(struct work_struct *work)
|
|||||||
seed_work);
|
seed_work);
|
||||||
unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
|
unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
|
||||||
unsigned char entropy[32];
|
unsigned char entropy[32];
|
||||||
|
int ret;
|
||||||
|
|
||||||
BUG_ON(!entropylen);
|
BUG_ON(!entropylen);
|
||||||
BUG_ON(entropylen > sizeof(entropy));
|
BUG_ON(entropylen > sizeof(entropy));
|
||||||
get_random_bytes(entropy, entropylen);
|
|
||||||
|
|
||||||
drbg_string_fill(&data, entropy, entropylen);
|
drbg_string_fill(&data, entropy, entropylen);
|
||||||
list_add_tail(&data.list, &seedlist);
|
list_add_tail(&data.list, &seedlist);
|
||||||
|
|
||||||
mutex_lock(&drbg->drbg_mutex);
|
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 */
|
/* If nonblocking pool is initialized, deactivate Jitter RNG */
|
||||||
crypto_free_rng(drbg->jent);
|
crypto_free_rng(drbg->jent);
|
||||||
drbg->jent = NULL;
|
drbg->jent = NULL;
|
||||||
@ -1030,6 +1101,7 @@ static void drbg_async_seed(struct work_struct *work)
|
|||||||
if (drbg->seeded)
|
if (drbg->seeded)
|
||||||
drbg->reseed_threshold = drbg_max_requests(drbg);
|
drbg->reseed_threshold = drbg_max_requests(drbg);
|
||||||
|
|
||||||
|
unlock:
|
||||||
mutex_unlock(&drbg->drbg_mutex);
|
mutex_unlock(&drbg->drbg_mutex);
|
||||||
|
|
||||||
memzero_explicit(entropy, entropylen);
|
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));
|
BUG_ON((entropylen * 2) > sizeof(entropy));
|
||||||
|
|
||||||
/* Get seed from in-kernel /dev/urandom */
|
/* 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) {
|
if (!drbg->jent) {
|
||||||
drbg_string_fill(&data1, entropy, entropylen);
|
drbg_string_fill(&data1, entropy, entropylen);
|
||||||
@ -1094,7 +1168,7 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
|
|||||||
entropylen);
|
entropylen);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_devel("DRBG: jent failed with %d\n", ret);
|
pr_devel("DRBG: jent failed with %d\n", ret);
|
||||||
return ret;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
drbg_string_fill(&data1, entropy, entropylen * 2);
|
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);
|
ret = __drbg_seed(drbg, &seedlist, reseed);
|
||||||
|
|
||||||
|
out:
|
||||||
memzero_explicit(entropy, entropylen * 2);
|
memzero_explicit(entropy, entropylen * 2);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1142,6 +1217,11 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg)
|
|||||||
drbg->reseed_ctr = 0;
|
drbg->reseed_ctr = 0;
|
||||||
drbg->d_ops = NULL;
|
drbg->d_ops = NULL;
|
||||||
drbg->core = 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);
|
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;
|
return 0;
|
||||||
|
|
||||||
fini:
|
fini:
|
||||||
|
@ -129,6 +129,8 @@ struct drbg_state {
|
|||||||
|
|
||||||
bool seeded; /* DRBG fully seeded? */
|
bool seeded; /* DRBG fully seeded? */
|
||||||
bool pr; /* Prediction resistance enabled? */
|
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 work_struct seed_work; /* asynchronous seeding support */
|
||||||
struct crypto_rng *jent;
|
struct crypto_rng *jent;
|
||||||
const struct drbg_state_ops *d_ops;
|
const struct drbg_state_ops *d_ops;
|
||||||
|
Loading…
Reference in New Issue
Block a user