patch 3/3: modify cprng to make contnuity check fips compliant and allow for a disabling of the continuity test when the RNG is placed in FIPS mode Signed-off-by: Neil Horman <nhorman@xxxxxxxxxxxxx> ansi_cprng.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c index 3aa6e38..c1ba5e8 100644 --- a/crypto/ansi_cprng.c +++ b/crypto/ansi_cprng.c @@ -33,6 +33,7 @@ #define PRNG_FIXED_SIZE 0x1 #define PRNG_NEED_RESET 0x2 +#define PRNG_DISABLE_CONT_TEST 0x3 /* * Note: DT is our counter value @@ -85,7 +86,7 @@ static void xor_vectors(unsigned char *in1, unsigned char *in2, * Returns DEFAULT_BLK_SZ bytes of random data per call * returns 0 if generation succeded, <0 if something went wrong */ -static int _get_more_prng_bytes(struct prng_context *ctx) +static int _get_more_prng_bytes(struct prng_context *ctx, int test) { int i; unsigned char tmp[DEFAULT_BLK_SZ]; @@ -130,6 +131,9 @@ static int _get_more_prng_bytes(struct prng_context *ctx) * First check that we didn't produce the same * random data that we did last time around through this */ + if (ctx->flags & PRNG_DISABLE_CONT_TEST) + goto skip_test; + if (!memcmp(ctx->rand_data, ctx->last_rand_data, DEFAULT_BLK_SZ)) { if (fips_enabled) { @@ -146,7 +150,7 @@ static int _get_more_prng_bytes(struct prng_context *ctx) } memcpy(ctx->last_rand_data, ctx->rand_data, DEFAULT_BLK_SZ); - +skip_test: /* * Lastly xor the random data with I * and encrypt that to obtain a new secret vector V @@ -220,7 +224,7 @@ static int get_prng_bytes(char *buf, size_t nbytes, struct prng_context *ctx) remainder: if (ctx->rand_data_valid == DEFAULT_BLK_SZ) { - if (_get_more_prng_bytes(ctx) < 0) { + if (_get_more_prng_bytes(ctx, 1) < 0) { memset(buf, 0, nbytes); err = -EINVAL; goto done; @@ -247,7 +251,7 @@ empty_rbuf: */ for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) { if (ctx->rand_data_valid == DEFAULT_BLK_SZ) { - if (_get_more_prng_bytes(ctx) < 0) { + if (_get_more_prng_bytes(ctx, 1) < 0) { memset(buf, 0, nbytes); err = -EINVAL; goto done; @@ -306,7 +310,6 @@ static int reset_prng_context(struct prng_context *ctx, memset(ctx->rand_data, 0, DEFAULT_BLK_SZ); memset(ctx->last_rand_data, 0, DEFAULT_BLK_SZ); - ctx->rand_data_valid = DEFAULT_BLK_SZ; ret = crypto_cipher_setkey(ctx->tfm, prng_key, klen); if (ret) { @@ -317,6 +320,18 @@ static int reset_prng_context(struct prng_context *ctx, ret = 0; ctx->flags &= ~PRNG_NEED_RESET; + + /* + * If we don't disable the continuity test + * we need to seed the n=0 iteration for test + * comparison, so we get a first block here that + * we never return to the user + */ + ctx->rand_data_valid = DEFAULT_BLK_SZ; + if (!(ctx->flags & PRNG_DISABLE_CONT_TEST)) + _get_more_prng_bytes(ctx, 0); + + ctx->rand_data_valid = DEFAULT_BLK_SZ; out: spin_unlock_bh(&ctx->prng_lock); return ret; @@ -384,6 +399,35 @@ static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen) return 0; } + /* + * These are the registered functions which export behavioral flags + * to the crypto api. Most rng's don't have flags, but some might, like + * the cprng which implements a 'test mode' for validation of vecotors + * which disables the internal continuity tests + */ +static int cprng_set_flags(struct crypto_rng *tfm, u8 flags) +{ + struct prng_context *prng = crypto_rng_ctx(tfm); + + if (flags & CRYPTO_RNG_TEST_MODE) + prng->flags |= PRNG_DISABLE_CONT_TEST; + + return 0; +} + + static int cprng_get_flags(struct crypto_rng *tfm, u8 *flags) +{ + struct prng_context *prng = crypto_rng_ctx(tfm); + + *flags = 0; + + if (prng->flags & PRNG_DISABLE_CONT_TEST) + *flags |= CRYPTO_RNG_TEST_MODE; + + return 0; +} + + static struct crypto_alg rng_alg = { .cra_name = "stdrng", .cra_driver_name = "ansi_cprng", @@ -399,6 +443,8 @@ static struct crypto_alg rng_alg = { .rng = { .rng_make_random = cprng_get_random, .rng_reset = cprng_reset, + .rng_set_flags = cprng_set_flags, + .rng_get_flags = cprng_get_flags, .seedsize = DEFAULT_PRNG_KSZ + 2*DEFAULT_BLK_SZ, } } -- 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