Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1871

kernel-2.6.18-238.el5.src.rpm

From: Neil Horman <nhorman@redhat.com>
Date: Tue, 15 Sep 2009 09:55:32 -0400
Subject: [misc] cprng: fix cont test to be fips compliant
Message-id: 20090915135532.GB1366@hmsreliant.think-freely.org
O-Subject: Re: [RHEL5.5 PATCH] cprng: fix cont test to be fips compliant (bz 523259)
Bugzilla: 523259
RH-Acked-by: Jarod Wilson <jarod@redhat.com>

On Tue, Sep 15, 2009 at 08:49:19AM -0400, Jarod Wilson wrote:
> On 09/14/2009 03:53 PM, Neil Horman wrote:
>> Hey-
>> 	Patch to bring ansi_cprng into compliance with fips-140 continuous test
>> requirements.  Its not upstream yet, but I'll be pushing it there soon.  Posting
>> here to make the wed deadline for 5.4.z.  Fixes bz 523259
>
> 2) do we really need to disable the continuity test, or can we simply
> skip it (or compare vs. 0) for the first hunk of cprng data like we've
> been doing thus far? I guess if we're being non-compliant anyway though,
> we might as well save the overhead of the continuity check. But it might
> be worth an expanded comment on what this flag does -- not only does it
> disable the continuity check, but in the process it will alter the
> behaviour of the *deterministic* crpng, and yield a *different* result
> for the same seed value and iteration count depending on this flag. :\
>
I'm not really sure how to best answer this quesiton.  I think the shortest
answer I can give you is 'no we don't need to disable the continuity test,
assuming we can think of a better way to make the continuity test FIPS compliant
while still being able to pass the certification tests.  My initial thought on
that was that we could treat the contiuity test like a single entry fifo, so
that after seeding the test, you returned the n-1'th random block on the nth
iteration.  That would pass the cert tests, but the FIPS requirement states that
the n=0 random block should never be used external to the cprng, so we can't
really handle it that way.

Heres an updated version of the patch, fixing the typo and adding the comments
you requested.  If you could run this through your test suite, that would be
fantastic.  Thank you!

Neil

P.S. note you'll have to modify your test suite to set the TEST_MODE flag on any
cprng instance you allocate

Thanks!

diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c
index c858f4f..c0ced1a 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) {
@@ -150,7 +154,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
@@ -225,7 +229,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;
@@ -252,7 +256,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;
@@ -323,7 +327,6 @@ static int reset_prng_context(struct prng_context *ctx,
 		goto out;
 	}
 
-	ctx->rand_data_valid = DEFAULT_BLK_SZ;
 
 	ret = crypto_cipher_setkey(ctx->tfm, prng_key, klen);
 	if (ret) {
@@ -335,6 +338,18 @@ static int reset_prng_context(struct prng_context *ctx,
 
 	rc = 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(&ctx->prng_lock);
 
@@ -398,6 +413,42 @@ 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 vectors
+ *  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);
+
+	/*
+	 * Disable any internal testing on the output of this instance
+	 * of a cprng.  Note that given that this rng is deterministic
+	 * setting this flag changes the output sequence.  Specifically
+	 * the continuity check never returns the first random block, saving
+	 * it instead as a seed on the continuity test.  By disabling this 
+	 * the 0th iteration will be returned
+	 */
+	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 ncrypto_alg rng_alg = {
 	.cra_name		= "stdrng",
 	.cra_driver_name	= "ansi_cprng",
@@ -423,6 +474,9 @@ static int __init prng_mod_init(void)
 
 	alg->rng_make_random = cprng_get_random;
 	alg->rng_reset = cprng_reset;
+	alg->rng_set_flags = cprng_set_flags;
+	alg->rng_get_flags = cprng_get_flags;
+
 	alg->seedsize =	DEFAULT_PRNG_KSZ + 2*DEFAULT_BLK_SZ;
 
 	ret = ncrypto_register_alg(&rng_alg);
diff --git a/crypto/rng.c b/crypto/rng.c
index de82046..a7d8e5e 100644
--- a/crypto/rng.c
+++ b/crypto/rng.c
@@ -46,6 +46,16 @@ static int rngapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
 	return err;
 }
 
+static int rngapi_set_flags(struct crypto_rng *tfm, u8 flags)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rngapi_get_flags(struct crypto_rng *tfm, u8 *flags)
+{
+	return -EOPNOTSUPP;
+}
+
 static int crypto_init_rng_ops(struct ncrypto_tfm *tfm, u32 type, u32 mask)
 {
 	struct rng_alg *alg = (struct rng_alg *)&tfm->__crt_alg->cra_u;
@@ -53,6 +63,8 @@ static int crypto_init_rng_ops(struct ncrypto_tfm *tfm, u32 type, u32 mask)
 
 	ops->rng_gen_random = alg->rng_make_random;
 	ops->rng_reset = rngapi_reset;
+	ops->rng_set_flags = alg->rng_set_flags ? : rngapi_set_flags;
+	ops->rng_get_flags = alg->rng_get_flags ? : rngapi_get_flags;
 
 	return 0;
 }
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 7eb47ee..7ad6d0b 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -1102,6 +1102,8 @@ static int alg_test_cprng(const struct alg_test_desc *desc, const char *driver,
 		return PTR_ERR(rng);
 	}
 
+	crypto_rng_set_flags(rng, CRYPTO_RNG_TEST_MODE);
+
 	err = test_cprng(rng, desc->suite.cprng.vecs, desc->suite.cprng.count);
 
 	crypto_free_rng(rng);
diff --git a/include/crypto/rng.h b/include/crypto/rng.h
index 8edc163..2b8d9b6 100644
--- a/include/crypto/rng.h
+++ b/include/crypto/rng.h
@@ -17,6 +17,17 @@
 
 #define NCRYPTO_ALG_TYPE_RNG		0x0000000c
 
+/*
+ * RNG behavioral flags
+ * CRYPTO_RNG_TEST_MODE
+ *   places the RNG into a test mode for various certification tests.  Some
+ *   RNG's (most notably Deterministic RNGs) Can have internal tests which are 
+ *   required in normal operation mode, but affect the deterministic output 
+ *   of the RNG which throws some test vectors off, as they may not account for
+ *   these tests.  This flag allows us to disable the internal tests of an RNG.
+ */
+#define CRYPTO_RNG_TEST_MODE	0x01
+
 struct crypto_rng;
 
 struct rng_alg {
@@ -24,6 +35,10 @@ struct rng_alg {
 			       unsigned int dlen);
 	int (*rng_reset)(struct crypto_rng *tfm, u8 *seed, unsigned int slen);
 
+	int (*rng_set_flags)(struct crypto_rng *tfm, u8 flags);
+
+	int (*rng_get_flags)(struct crypto_rng *tfm, u8 *flags);
+
 	unsigned int seedsize;
 };
 
@@ -31,6 +46,10 @@ struct rng_tfm {
 	int (*rng_gen_random)(struct crypto_rng *tfm, u8 *rdata,
 			      unsigned int dlen);
 	int (*rng_reset)(struct crypto_rng *tfm, u8 *seed, unsigned int slen);
+
+	int (*rng_set_flags)(struct crypto_rng *tfm, u8 flags);
+
+	int (*rng_get_flags)(struct crypto_rng *tfm, u8 *flags);
 };
 
 struct crypto_rng {
@@ -94,4 +113,14 @@ static inline int crypto_rng_seedsize(struct crypto_rng *tfm)
 	return crypto_rng_alg(tfm)->seedsize;
 }
 
+static inline int crypto_rng_set_flags(struct crypto_rng *tfm, u8 flags)
+{
+	return crypto_rng_alg(tfm)->rng_set_flags(tfm, flags);
+}
+
+static inline int crypto_rng_get_flags(struct crypto_rng *tfm, u8 *flags)
+{
+	return crypto_rng_alg(tfm)->rng_get_flags(tfm, flags);
+}
+
 #endif