[PATCH v2 19/25] crypto: ansi_cprng - simplify get_prng_bytes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The crypto is so slow that there's no point unrolling this function.

A simpler and clearer implementation will do just fine.

Two other changes:
1. Move the debug print outside the spinlock;
2. Move all modification of rand_data_valid out of _get_more_prng_bytes;
   that variable belongs to the byte-at-a-time layer outside the
   block-oriented primitive.

This is the code that gave us CVE-2013-4345; it's full of corner cases
which the standard test vectors don't exercise.  The stutter test was
created to exercise it.  (And yes, it did catch problems during
development.)

Signed-off-by: George Spelvin <linux@xxxxxxxxxxx>
---
 crypto/ansi_cprng.c | 74 ++++++++++++++++++-----------------------------------
 1 file changed, 25 insertions(+), 49 deletions(-)

diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c
index c1c81266..a8cf98a5 100644
--- a/crypto/ansi_cprng.c
+++ b/crypto/ansi_cprng.c
@@ -145,7 +145,6 @@ static int _get_more_prng_bytes(struct prng_context *ctx, bool cont_test)
 	hexdump("DT'", ctx->DT);
 
 	dbgprint("Returning new block for context %p\n", ctx);
-	ctx->rand_data_valid = 0;
 
 	return 0;
 }
@@ -156,68 +155,45 @@ static int get_prng_bytes(u8 *buf, unsigned int nbytes,
 {
 	u8 *ptr = buf;
 	unsigned int byte_count = nbytes;
-	int err;
+	unsigned int read_pos;
+	int err = -EINVAL;
 
+	dbgprint(KERN_CRIT "getting %u random bytes for context %p\n",
+		nbytes, ctx);
 
 	spin_lock_bh(&ctx->prng_lock);
 
-	err = -EINVAL;
 	if (ctx->flags & PRNG_NEED_RESET)
 		goto done;
 
-	err = byte_count;
-
-	dbgprint(KERN_CRIT "getting %d random bytes for context %p\n",
-		byte_count, ctx);
-
-
-remainder:
-	if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
-		if (_get_more_prng_bytes(ctx, do_cont_test) < 0) {
-			memset(buf, 0, nbytes);
-			err = -EINVAL;
-			goto done;
-		}
-	}
-
-	/*
-	 * Copy any data less than an entire block
-	 */
-	if (byte_count < DEFAULT_BLK_SZ) {
-empty_rbuf:
-		while (ctx->rand_data_valid < DEFAULT_BLK_SZ) {
-			*ptr = ctx->rand_data[ctx->rand_data_valid];
-			ptr++;
-			byte_count--;
-			ctx->rand_data_valid++;
-			if (byte_count == 0)
-				goto done;
-		}
-	}
-
-	/*
-	 * Now copy whole blocks
-	 */
-	for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) {
-		if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
+	read_pos = ctx->rand_data_valid;
+	if (byte_count > DEFAULT_BLK_SZ - read_pos) {
+		/* Leading partial block */
+		unsigned int avail = DEFAULT_BLK_SZ - read_pos;
+
+		memcpy(ptr, ctx->rand_data + read_pos, avail);
+		ptr += avail;
+		byte_count -= avail;
+		read_pos = 0;
+
+		/* Intermediate full blocks */
+		for (;;) {
 			if (_get_more_prng_bytes(ctx, do_cont_test) < 0) {
 				memset(buf, 0, nbytes);
-				err = -EINVAL;
 				goto done;
 			}
+			if (byte_count < DEFAULT_BLK_SZ)
+				break;
+			memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
+			ptr += DEFAULT_BLK_SZ;
+			byte_count -= DEFAULT_BLK_SZ;
 		}
-		if (ctx->rand_data_valid > 0)
-			goto empty_rbuf;
-		memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
-		ctx->rand_data_valid += DEFAULT_BLK_SZ;
-		ptr += DEFAULT_BLK_SZ;
 	}
 
-	/*
-	 * Now go back and get any remaining partial block
-	 */
-	if (byte_count)
-		goto remainder;
+	/* The final partial block; read_pos + byte_count <= DEFAULT_BLK_SZ */
+	memcpy(ptr, ctx->rand_data + read_pos, byte_count);
+	ctx->rand_data_valid = read_pos + byte_count;
+	err = nbytes;
 
 done:
 	spin_unlock_bh(&ctx->prng_lock);
-- 
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




[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux