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 <linux@xxxxxxxxxxx> --- 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 <nhorman@xxxxxxxxxxxxx> * @@ -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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html