xen-blkback: fix persistent grants negotiation
[linux-2.6-microblaze.git] / crypto / drbg.c
index f72f340..177983b 100644 (file)
 #include <crypto/drbg.h>
 #include <crypto/internal/cipher.h>
 #include <linux/kernel.h>
+#include <linux/jiffies.h>
 
 /***************************************************************
  * Backend cipher definitions available to DRBG
@@ -1036,17 +1037,39 @@ static const struct drbg_state_ops drbg_hash_ops = {
  ******************************************************************/
 
 static inline int __drbg_seed(struct drbg_state *drbg, struct list_head *seed,
-                             int reseed)
+                             int reseed, enum drbg_seed_state new_seed_state)
 {
        int ret = drbg->d_ops->update(drbg, seed, reseed);
 
        if (ret)
                return ret;
 
-       drbg->seeded = true;
+       drbg->seeded = new_seed_state;
+       drbg->last_seed_time = jiffies;
        /* 10.1.1.2 / 10.1.1.3 step 5 */
        drbg->reseed_ctr = 1;
 
+       switch (drbg->seeded) {
+       case DRBG_SEED_STATE_UNSEEDED:
+               /* Impossible, but handle it to silence compiler warnings. */
+               fallthrough;
+       case DRBG_SEED_STATE_PARTIAL:
+               /*
+                * Require frequent reseeds until the seed source is
+                * fully initialized.
+                */
+               drbg->reseed_threshold = 50;
+               break;
+
+       case DRBG_SEED_STATE_FULL:
+               /*
+                * Seed source has become fully initialized, frequent
+                * reseeds no longer required.
+                */
+               drbg->reseed_threshold = drbg_max_requests(drbg);
+               break;
+       }
+
        return ret;
 }
 
@@ -1066,12 +1089,10 @@ static inline int drbg_get_random_bytes(struct drbg_state *drbg,
        return 0;
 }
 
-static void drbg_async_seed(struct work_struct *work)
+static int drbg_seed_from_random(struct drbg_state *drbg)
 {
        struct drbg_string data;
        LIST_HEAD(seedlist);
-       struct drbg_state *drbg = container_of(work, struct drbg_state,
-                                              seed_work);
        unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
        unsigned char entropy[32];
        int ret;
@@ -1082,26 +1103,35 @@ static void drbg_async_seed(struct work_struct *work)
        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;
+               goto out;
 
-       /* Set seeded to false so that if __drbg_seed fails the
-        * next generate call will trigger a reseed.
-        */
-       drbg->seeded = false;
+       ret = __drbg_seed(drbg, &seedlist, true, DRBG_SEED_STATE_FULL);
 
-       __drbg_seed(drbg, &seedlist, true);
+out:
+       memzero_explicit(entropy, entropylen);
+       return ret;
+}
 
-       if (drbg->seeded)
-               drbg->reseed_threshold = drbg_max_requests(drbg);
+static bool drbg_nopr_reseed_interval_elapsed(struct drbg_state *drbg)
+{
+       unsigned long next_reseed;
 
-unlock:
-       mutex_unlock(&drbg->drbg_mutex);
+       /* Don't ever reseed from get_random_bytes() in test mode. */
+       if (list_empty(&drbg->test_data.list))
+               return false;
 
-       memzero_explicit(entropy, entropylen);
+       /*
+        * Obtain fresh entropy for the nopr DRBGs after 300s have
+        * elapsed in order to still achieve sort of partial
+        * prediction resistance over the time domain at least. Note
+        * that the period of 300s has been chosen to match the
+        * CRNG_RESEED_INTERVAL of the get_random_bytes()' chacha
+        * rngs.
+        */
+       next_reseed = drbg->last_seed_time + 300 * HZ;
+       return time_after(jiffies, next_reseed);
 }
 
 /*
@@ -1123,6 +1153,7 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
        unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
        struct drbg_string data1;
        LIST_HEAD(seedlist);
+       enum drbg_seed_state new_seed_state = DRBG_SEED_STATE_FULL;
 
        /* 9.1 / 9.2 / 9.3.1 step 3 */
        if (pers && pers->len > (drbg_max_addtl(drbg))) {
@@ -1150,6 +1181,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 */
+               if (!rng_is_initialized())
+                       new_seed_state = DRBG_SEED_STATE_PARTIAL;
+
                ret = drbg_get_random_bytes(drbg, entropy, entropylen);
                if (ret)
                        goto out;
@@ -1159,11 +1193,14 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
                        pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
                                 entropylen);
                } else {
-                       /* Get seed from Jitter RNG */
+                       /*
+                        * Get seed from Jitter RNG, failures are
+                        * fatal only in FIPS mode.
+                        */
                        ret = crypto_rng_get_bytes(drbg->jent,
                                                   entropy + entropylen,
                                                   entropylen);
-                       if (ret) {
+                       if (fips_enabled && ret) {
                                pr_devel("DRBG: jent failed with %d\n", ret);
 
                                /*
@@ -1206,7 +1243,7 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
                memset(drbg->C, 0, drbg_statelen(drbg));
        }
 
-       ret = __drbg_seed(drbg, &seedlist, reseed);
+       ret = __drbg_seed(drbg, &seedlist, reseed, new_seed_state);
 
 out:
        memzero_explicit(entropy, entropylen * 2);
@@ -1386,19 +1423,26 @@ static int drbg_generate(struct drbg_state *drbg,
         * here. The spec is a bit convoluted here, we make it simpler.
         */
        if (drbg->reseed_threshold < drbg->reseed_ctr)
-               drbg->seeded = false;
+               drbg->seeded = DRBG_SEED_STATE_UNSEEDED;
 
-       if (drbg->pr || !drbg->seeded) {
+       if (drbg->pr || drbg->seeded == DRBG_SEED_STATE_UNSEEDED) {
                pr_devel("DRBG: reseeding before generation (prediction "
                         "resistance: %s, state %s)\n",
                         drbg->pr ? "true" : "false",
-                        drbg->seeded ? "seeded" : "unseeded");
+                        (drbg->seeded ==  DRBG_SEED_STATE_FULL ?
+                         "seeded" : "unseeded"));
                /* 9.3.1 steps 7.1 through 7.3 */
                len = drbg_seed(drbg, addtl, true);
                if (len)
                        goto err;
                /* 9.3.1 step 7.4 */
                addtl = NULL;
+       } else if (rng_is_initialized() &&
+                  (drbg->seeded == DRBG_SEED_STATE_PARTIAL ||
+                   drbg_nopr_reseed_interval_elapsed(drbg))) {
+               len = drbg_seed_from_random(drbg);
+               if (len)
+                       goto err;
        }
 
        if (addtl && 0 < addtl->len)
@@ -1491,51 +1535,23 @@ 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;
 
        drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
+       if (IS_ERR(drbg->jent)) {
+               const int err = PTR_ERR(drbg->jent);
 
-       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;
-               fallthrough;
-
-       default:
-               drbg->random_ready.func = NULL;
-               return err;
+               drbg->jent = NULL;
+               if (fips_enabled || err != -ENOENT)
+                       return err;
+               pr_info("DRBG: Continuing without Jitter RNG\n");
        }
 
-       /*
-        * Require frequent reseeds until the seed source is fully
-        * initialized.
-        */
-       drbg->reseed_threshold = 50;
-
-       return err;
+       return 0;
 }
 
 /*
@@ -1578,7 +1594,8 @@ static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
        if (!drbg->core) {
                drbg->core = &drbg_cores[coreref];
                drbg->pr = pr;
-               drbg->seeded = false;
+               drbg->seeded = DRBG_SEED_STATE_UNSEEDED;
+               drbg->last_seed_time = 0;
                drbg->reseed_threshold = drbg_max_requests(drbg);
 
                ret = drbg_alloc_state(drbg);
@@ -1589,14 +1606,6 @@ static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
                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;
        }
 
@@ -1629,11 +1638,6 @@ free_everything:
  */
 static int drbg_uninstantiate(struct drbg_state *drbg)
 {
-       if (drbg->random_ready.func) {
-               del_random_ready_callback(&drbg->random_ready);
-               cancel_work_sync(&drbg->seed_work);
-       }
-
        if (!IS_ERR_OR_NULL(drbg->jent))
                crypto_free_rng(drbg->jent);
        drbg->jent = NULL;