Except for the switch from triple DES to AES-128, this results in an
actual implementation of the X9.31 Appendix A.2.4 generator, which is
the same as the X9.17 Appendix C one.

If a DT seed value is provided, it is the same fully deterministic
algorithm it has always been.  If DT is omitted (something already
allowed), it is generated fresh each time.

Signed-off-by: George Spelvin <li...@horizon.com>
---
 crypto/ansi_cprng.c | 67 +++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 50 insertions(+), 17 deletions(-)

diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c
index 7b6b263d..c2c285f3 100644
--- a/crypto/ansi_cprng.c
+++ b/crypto/ansi_cprng.c
@@ -1,7 +1,11 @@
 /*
  * PRNG: Pseudo Random Number Generator
- *       Based on NIST Recommended PRNG From ANSI X9.31 Appendix A.2.4 using
- *       AES 128 cipher
+ *       Based on "NIST Recommended PRNG From ANSI X9.31 Appendix A.2.4"
+ *       using AES 128 cipher. It may be seeded with a fixed DT value
+ *       for determinsitic generaton purposes, or if no DT value is
+ *       provided for seed material, it generates a fresh one each time
+ *       using a high-resolution timestamp, as specified in the X9.17
+ *       and X9.31 standards.
  *
  *  (C) Neil Horman <nhor...@tuxdriver.com>
  *
@@ -9,8 +13,6 @@
  *  under the terms of the GNU General Public License as published by the
  *  Free Software Foundation; either version 2 of the License, or (at your
  *  any later version.
- *
- *
  */
 
 #include <crypto/internal/rng.h>
@@ -41,8 +43,8 @@ union block {
 /*
  * Flags for the prng_context flags field
  */
-
-#define PRNG_NEED_RESET 0x1
+#define PRNG_NEED_RESET                0x1
+#define PRNG_DETERMINISTIC     0x2
 
 /*
  * Note: In addition to the fixed encryption key, there are three
@@ -104,6 +106,16 @@ static int _get_more_prng_bytes(struct prng_context *ctx, 
bool cont_test)
        hexdump("Input V: ", &ctx->V);
 
        /*
+        * get_random_int produces a result based on the system jiffies
+        * and random_get_entropy(), the highest-resolution timestamp
+        * available.  This meets the spirit of the X9.17/X9.31 generation
+        * specifications, but it's masked by hashing, so it can't be used
+        * to leak information about the seed to /dev/random.
+        */
+       if (!(ctx->flags & PRNG_DETERMINISTIC))
+               ctx->DT.ints[0] = get_random_int();
+
+       /*
         * Start by encrypting the counter value.
         * This gives us an intermediate value I.
         */
@@ -144,12 +156,19 @@ static int _get_more_prng_bytes(struct prng_context *ctx, 
bool cont_test)
        crypto_cipher_encrypt_one(ctx->tfm, ctx->V.bytes, ctx->V.bytes);
 
        /*
-        * Now update our DT value
+        * Now update our DT value.
         */
-       for (i = DEFAULT_BLK_SZ - 1; i >= 0; i--) {
-               ctx->DT.bytes[i] += 1;
-               if (ctx->DT.bytes[i] != 0)
-                       break;
+       if (ctx->flags & PRNG_DETERMINISTIC) {
+               /* The big-endian byte order matches the NIST test vectors */
+               for (i = DEFAULT_BLK_SZ - 1; i >= 0; i--) {
+                       ctx->DT.bytes[i] += 1;
+                       if (ctx->DT.bytes[i] != 0)
+                               break;
+               }
+       } else {
+               /* Prevent backtracking */
+               ctx->DT.ints[0] = 0;    /* Prevent backtracking */
+               memzero_explicit(tmp.bytes, DEFAULT_BLK_SZ);
        }
 
        dbgprint("Returning new block for context %p\n", ctx);
@@ -193,7 +212,9 @@ static int get_prng_bytes(unsigned char *buf, unsigned int 
nbytes,
        /* The final partial block */
        len = nbytes - pos;
        memcpy(buf + pos, ctx->rand_data.bytes + read_pos, len);
-       ctx->rand_read_pos = read_pos + len;
+       read_pos += len;
+       memzero_explicit(ctx->rand_data.bytes, read_pos);
+       ctx->rand_read_pos = read_pos;
        err = nbytes;
 
 done:
@@ -215,14 +236,26 @@ static int reset_prng_context(struct prng_context *ctx,
        int ret;
 
        spin_lock_bh(&ctx->prng_lock);
-       ctx->flags |= PRNG_NEED_RESET;
+       ctx->flags = PRNG_NEED_RESET;
 
        memcpy(ctx->V.bytes, V, DEFAULT_BLK_SZ);
 
-       if (DT)
+       if (DT) {
+               /* Predictable DT, when repeatability is desired */
+               ctx->flags |= PRNG_DETERMINISTIC;
                memcpy(ctx->DT.bytes, DT, DEFAULT_BLK_SZ);
-       else
-               memset(ctx->DT.bytes, 0, DEFAULT_BLK_SZ);
+       } else {
+               int i;
+               /*
+                * Generate a fresh DT based on timestamp each time.
+                * Also pad rest of buffer with seed, on general principles.
+                * We reserve the first int for fresh entropy, in case
+                * the key is not an even multiple of an int in size and
+                * the last int is partially ignored.
+                */
+               for (i = 1; i < BLK_INTS; i++)
+                       ctx->DT.ints[i] = get_random_int();
+       }
 
        memset(ctx->rand_data.bytes, 0, DEFAULT_BLK_SZ);
 
@@ -255,7 +288,7 @@ static int cprng_init(struct crypto_tfm *tfm)
         * After allocation, we always force the user to reset, which
         * completes initialization of the context.
         */
-       ctx->flags |= PRNG_NEED_RESET;
+       ctx->flags = PRNG_NEED_RESET;
        return 0;
 }
 
-- 
2.1.3

--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to