summaryrefslogtreecommitdiff
path: root/crypto/drbg.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/drbg.c')
-rw-r--r--crypto/drbg.c217
1 files changed, 126 insertions, 91 deletions
diff --git a/crypto/drbg.c b/crypto/drbg.c
index 04836b4838ef..c6cbf1336d73 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -98,6 +98,7 @@
*/
#include <crypto/drbg.h>
+#include <linux/kernel.h>
/***************************************************************
* Backend cipher definitions available to DRBG
@@ -190,6 +191,8 @@ static const struct drbg_core drbg_cores[] = {
#endif /* CONFIG_CRYPTO_DRBG_HMAC */
};
+static int drbg_uninstantiate(struct drbg_state *drbg);
+
/******************************************************************
* Generic helper functions
******************************************************************/
@@ -1062,20 +1065,32 @@ static void drbg_async_seed(struct work_struct *work)
LIST_HEAD(seedlist);
struct drbg_state *drbg = container_of(work, struct drbg_state,
seed_work);
- int ret;
+ unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
+ unsigned char entropy[32];
- get_blocking_random_bytes(drbg->seed_buf, drbg->seed_buf_len);
+ BUG_ON(!entropylen);
+ BUG_ON(entropylen > sizeof(entropy));
+ get_random_bytes(entropy, entropylen);
- drbg_string_fill(&data, drbg->seed_buf, drbg->seed_buf_len);
+ drbg_string_fill(&data, entropy, entropylen);
list_add_tail(&data.list, &seedlist);
+
mutex_lock(&drbg->drbg_mutex);
- ret = __drbg_seed(drbg, &seedlist, true);
- if (!ret && drbg->jent) {
- crypto_free_rng(drbg->jent);
- drbg->jent = NULL;
- }
- memzero_explicit(drbg->seed_buf, drbg->seed_buf_len);
+
+ /* If nonblocking pool is initialized, deactivate Jitter RNG */
+ crypto_free_rng(drbg->jent);
+ drbg->jent = NULL;
+
+ /* Set seeded to false so that if __drbg_seed fails the
+ * next generate call will trigger a reseed.
+ */
+ drbg->seeded = false;
+
+ __drbg_seed(drbg, &seedlist, true);
+
mutex_unlock(&drbg->drbg_mutex);
+
+ memzero_explicit(entropy, entropylen);
}
/*
@@ -1092,7 +1107,9 @@ static void drbg_async_seed(struct work_struct *work)
static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
bool reseed)
{
- int ret = 0;
+ int ret;
+ unsigned char entropy[((32 + 16) * 2)];
+ unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
struct drbg_string data1;
LIST_HEAD(seedlist);
@@ -1108,23 +1125,39 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
drbg->test_data.len);
pr_devel("DRBG: using test entropy\n");
} else {
+ /*
+ * Gather entropy equal to the security strength of the DRBG.
+ * With a derivation function, a nonce is required in addition
+ * to the entropy. A nonce must be at least 1/2 of the security
+ * strength of the DRBG in size. Thus, entropy + nonce is 3/2
+ * of the strength. The consideration of a nonce is only
+ * applicable during initial seeding.
+ */
+ BUG_ON(!entropylen);
+ if (!reseed)
+ entropylen = ((entropylen + 1) / 2) * 3;
+ BUG_ON((entropylen * 2) > sizeof(entropy));
+
/* Get seed from in-kernel /dev/urandom */
- get_random_bytes(drbg->seed_buf, drbg->seed_buf_len);
-
- /* Get seed from Jitter RNG */
- if (!drbg->jent ||
- crypto_rng_get_bytes(drbg->jent,
- drbg->seed_buf + drbg->seed_buf_len,
- drbg->seed_buf_len)) {
- drbg_string_fill(&data1, drbg->seed_buf,
- drbg->seed_buf_len);
- pr_devel("DRBG: (re)seeding with %zu bytes of entropy\n",
- drbg->seed_buf_len);
+ get_random_bytes(entropy, entropylen);
+
+ if (!drbg->jent) {
+ drbg_string_fill(&data1, entropy, entropylen);
+ pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
+ entropylen);
} else {
- drbg_string_fill(&data1, drbg->seed_buf,
- drbg->seed_buf_len * 2);
- pr_devel("DRBG: (re)seeding with %zu bytes of entropy\n",
- drbg->seed_buf_len * 2);
+ /* Get seed from Jitter RNG */
+ ret = crypto_rng_get_bytes(drbg->jent,
+ entropy + entropylen,
+ entropylen);
+ if (ret) {
+ pr_devel("DRBG: jent failed with %d\n", ret);
+ return ret;
+ }
+
+ drbg_string_fill(&data1, entropy, entropylen * 2);
+ pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
+ entropylen * 2);
}
}
list_add_tail(&data1.list, &seedlist);
@@ -1146,26 +1179,8 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
ret = __drbg_seed(drbg, &seedlist, reseed);
- /*
- * Clear the initial entropy buffer as the async call may not overwrite
- * that buffer for quite some time.
- */
- memzero_explicit(drbg->seed_buf, drbg->seed_buf_len * 2);
- if (ret)
- goto out;
- /*
- * For all subsequent seeding calls, we only need the seed buffer
- * equal to the security strength of the DRBG. We undo the calculation
- * in drbg_alloc_state.
- */
- if (!reseed)
- drbg->seed_buf_len = drbg->seed_buf_len / 3 * 2;
-
- /* Invoke asynchronous seeding unless DRBG is in test mode. */
- if (!list_empty(&drbg->test_data.list) && !reseed)
- schedule_work(&drbg->seed_work);
+ memzero_explicit(entropy, entropylen * 2);
-out:
return ret;
}
@@ -1188,12 +1203,6 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg)
drbg->prev = NULL;
drbg->fips_primed = false;
#endif
- kzfree(drbg->seed_buf);
- drbg->seed_buf = NULL;
- if (drbg->jent) {
- crypto_free_rng(drbg->jent);
- drbg->jent = NULL;
- }
}
/*
@@ -1256,42 +1265,6 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
goto err;
}
- /*
- * Gather entropy equal to the security strength of the DRBG.
- * With a derivation function, a nonce is required in addition
- * to the entropy. A nonce must be at least 1/2 of the security
- * strength of the DRBG in size. Thus, entropy * nonce is 3/2
- * of the strength. The consideration of a nonce is only
- * applicable during initial seeding.
- */
- drbg->seed_buf_len = drbg_sec_strength(drbg->core->flags);
- if (!drbg->seed_buf_len) {
- ret = -EFAULT;
- goto err;
- }
- /*
- * Ensure we have sufficient buffer space for initial seed which
- * consists of the seed from get_random_bytes and the Jitter RNG.
- */
- drbg->seed_buf_len = ((drbg->seed_buf_len + 1) / 2) * 3;
- drbg->seed_buf = kzalloc(drbg->seed_buf_len * 2, GFP_KERNEL);
- if (!drbg->seed_buf)
- goto err;
-
- INIT_WORK(&drbg->seed_work, drbg_async_seed);
-
- drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
- if(IS_ERR(drbg->jent))
- {
- pr_info("DRBG: could not allocate Jitter RNG handle for seeding\n");
- /*
- * As the Jitter RNG is a module that may not be present, we
- * continue with the operation and do not fully tie the DRBG
- * to the Jitter RNG.
- */
- drbg->jent = NULL;
- }
-
return 0;
err:
@@ -1467,6 +1440,47 @@ static int drbg_generate_long(struct drbg_state *drbg,
return 0;
}
+static void drbg_schedule_async_seed(struct random_ready_callback *rdy)
+{
+ struct drbg_state *drbg = container_of(rdy, struct drbg_state,
+ random_ready);
+
+ schedule_work(&drbg->seed_work);
+}
+
+static int drbg_prepare_hrng(struct drbg_state *drbg)
+{
+ int err;
+
+ /* We do not need an HRNG in test mode. */
+ if (list_empty(&drbg->test_data.list))
+ return 0;
+
+ INIT_WORK(&drbg->seed_work, drbg_async_seed);
+
+ drbg->random_ready.owner = THIS_MODULE;
+ drbg->random_ready.func = drbg_schedule_async_seed;
+
+ err = add_random_ready_callback(&drbg->random_ready);
+
+ switch (err) {
+ case 0:
+ break;
+
+ case -EALREADY:
+ err = 0;
+ /* fall through */
+
+ default:
+ drbg->random_ready.func = NULL;
+ return err;
+ }
+
+ drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
+
+ return err;
+}
+
/*
* DRBG instantiation function as required by SP800-90A - this function
* sets up the DRBG handle, performs the initial seeding and all sanity
@@ -1517,15 +1531,25 @@ static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
if (drbg->d_ops->crypto_init(drbg))
goto err;
+ ret = drbg_prepare_hrng(drbg);
+ if (ret)
+ goto free_everything;
+
+ if (IS_ERR(drbg->jent)) {
+ ret = PTR_ERR(drbg->jent);
+ drbg->jent = NULL;
+ if (fips_enabled || ret != -ENOENT)
+ goto free_everything;
+ pr_info("DRBG: Continuing without Jitter RNG\n");
+ }
+
reseed = false;
}
ret = drbg_seed(drbg, pers, reseed);
- if (ret && !reseed) {
- drbg->d_ops->crypto_fini(drbg);
- goto err;
- }
+ if (ret && !reseed)
+ goto free_everything;
mutex_unlock(&drbg->drbg_mutex);
return ret;
@@ -1535,6 +1559,11 @@ err:
unlock:
mutex_unlock(&drbg->drbg_mutex);
return ret;
+
+free_everything:
+ mutex_unlock(&drbg->drbg_mutex);
+ drbg_uninstantiate(drbg);
+ return ret;
}
/*
@@ -1548,7 +1577,13 @@ unlock:
*/
static int drbg_uninstantiate(struct drbg_state *drbg)
{
- cancel_work_sync(&drbg->seed_work);
+ if (drbg->random_ready.func) {
+ del_random_ready_callback(&drbg->random_ready);
+ cancel_work_sync(&drbg->seed_work);
+ crypto_free_rng(drbg->jent);
+ drbg->jent = NULL;
+ }
+
if (drbg->d_ops)
drbg->d_ops->crypto_fini(drbg);
drbg_dealloc_state(drbg);