From ea8c77a55bcf3ac0744ac8724ca2476818787330 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bodo=20M=C3=B6ller?= <bodo@openssl.org>
Date: Fri, 2 Dec 2011 12:41:17 +0000
Subject: [PATCH] Fix ecdsatest.c.

Submitted by: Emilia Kasper
---
 CHANGES                  |  6 +++
 crypto/ecdsa/ecdsatest.c | 85 ++++++++++++++++++++++++++++++++++++----
 2 files changed, 84 insertions(+), 7 deletions(-)

diff --git a/CHANGES b/CHANGES
index b824ab8ccc..ab83fa21d2 100644
--- a/CHANGES
+++ b/CHANGES
@@ -487,6 +487,9 @@
 
  Changes between 1.0.0e and 1.0.0f [xx XXX xxxx]
 
+  *) Fix spurious failures in ecdsatest.c.
+     [Emilia K�sper (Google)]
+
   *) Fix the BIO_f_buffer() implementation (which was mixing different
      interpretations of the '..._len' fields).
      [Adam Langley (Google)]
@@ -1411,6 +1414,9 @@
   
  Changes between 0.9.8r and 0.9.8s [xx XXX xxxx]
 
+  *) Fix spurious failures in ecdsatest.c.
+     [Emilia K�sper (Google)]
+
   *) Fix the BIO_f_buffer() implementation (which was mixing different
      interpretations of the '..._len' fields).
      [Adam Langley (Google)]
diff --git a/crypto/ecdsa/ecdsatest.c b/crypto/ecdsa/ecdsatest.c
index 42852ce6e8..fdadbd9614 100644
--- a/crypto/ecdsa/ecdsatest.c
+++ b/crypto/ecdsa/ecdsatest.c
@@ -295,9 +295,12 @@ int test_builtin(BIO *out)
 	size_t		crv_len = 0, n = 0;
 	EC_KEY		*eckey = NULL, *wrong_eckey = NULL;
 	EC_GROUP	*group;
+	ECDSA_SIG	*ecdsa_sig = NULL;
 	unsigned char	digest[20], wrong_digest[20];
-	unsigned char	*signature = NULL; 
-	unsigned int	sig_len;
+	unsigned char	*signature = NULL;
+	unsigned char	*sig_ptr;
+	unsigned char	*raw_buf = NULL;
+	unsigned int	sig_len, degree, r_len, s_len, bn_len, buf_len;
 	int		nid, ret =  0;
 	
 	/* fill digest values with some random data */
@@ -347,7 +350,8 @@ int test_builtin(BIO *out)
 		if (EC_KEY_set_group(eckey, group) == 0)
 			goto builtin_err;
 		EC_GROUP_free(group);
-		if (EC_GROUP_get_degree(EC_KEY_get0_group(eckey)) < 160)
+		degree = EC_GROUP_get_degree(EC_KEY_get0_group(eckey));
+		if (degree < 160)
 			/* drop the curve */ 
 			{
 			EC_KEY_free(eckey);
@@ -423,26 +427,89 @@ int test_builtin(BIO *out)
 			}
 		BIO_printf(out, ".");
 		(void)BIO_flush(out);
-		/* modify a single byte of the signature */
-		offset = signature[10] % sig_len;
-		dirt   = signature[11];
-		signature[offset] ^= dirt ? dirt : 1; 
+		/* wrong length */
+		if (ECDSA_verify(0, digest, 20, signature, sig_len - 1,
+			eckey) == 1)
+			{
+			BIO_printf(out, " failed\n");
+			goto builtin_err;
+			}
+		BIO_printf(out, ".");
+		(void)BIO_flush(out);
+
+		/* Modify a single byte of the signature: to ensure we don't
+		 * garble the ASN1 structure, we read the raw signature and
+		 * modify a byte in one of the bignums directly. */
+		sig_ptr = signature;
+		if ((ecdsa_sig = d2i_ECDSA_SIG(NULL, &sig_ptr, sig_len)) == NULL)
+			{
+			BIO_printf(out, " failed\n");
+			goto builtin_err;
+			}
+
+		/* Store the two BIGNUMs in raw_buf. */
+		r_len = BN_num_bytes(ecdsa_sig->r);
+		s_len = BN_num_bytes(ecdsa_sig->s);
+		bn_len = (degree + 7) / 8;
+		if ((r_len > bn_len) || (s_len > bn_len))
+			{
+			BIO_printf(out, " failed\n");
+			goto builtin_err;
+			}
+		buf_len = 2 * bn_len;
+		if ((raw_buf = OPENSSL_malloc(buf_len)) == NULL)
+			goto builtin_err;
+		/* Pad the bignums with leading zeroes. */
+		memset(raw_buf, 0, buf_len);
+		BN_bn2bin(ecdsa_sig->r, raw_buf + bn_len - r_len);
+		BN_bn2bin(ecdsa_sig->s, raw_buf + buf_len - s_len);
+
+		/* Modify a single byte in the buffer. */
+		offset = raw_buf[10] % buf_len;
+		dirt   = raw_buf[11] ? raw_buf[11] : 1;
+		raw_buf[offset] ^= dirt;
+		/* Now read the BIGNUMs back in from raw_buf. */
+		if ((BN_bin2bn(raw_buf, bn_len, ecdsa_sig->r) == NULL) ||
+			(BN_bin2bn(raw_buf + bn_len, bn_len, ecdsa_sig->s) == NULL))
+			goto builtin_err;
+
+		sig_ptr = signature;
+		sig_len = i2d_ECDSA_SIG(ecdsa_sig, &sig_ptr);
 		if (ECDSA_verify(0, digest, 20, signature, sig_len, eckey) == 1)
 			{
 			BIO_printf(out, " failed\n");
 			goto builtin_err;
 			}
+		/* Sanity check: undo the modification and verify signature. */
+		raw_buf[offset] ^= dirt;
+		if ((BN_bin2bn(raw_buf, bn_len, ecdsa_sig->r) == NULL) ||
+			(BN_bin2bn(raw_buf + bn_len, bn_len, ecdsa_sig->s) == NULL))
+			goto builtin_err;
+
+		sig_ptr = signature;
+		sig_len = i2d_ECDSA_SIG(ecdsa_sig, &sig_ptr);
+		if (ECDSA_verify(0, digest, 20, signature, sig_len, eckey) != 1)
+			{
+			BIO_printf(out, " failed\n");
+			goto builtin_err;
+			}
 		BIO_printf(out, ".");
 		(void)BIO_flush(out);
 		
 		BIO_printf(out, " ok\n");
 		/* cleanup */
+		/* clean bogus errors */
+		ERR_clear_error();
 		OPENSSL_free(signature);
 		signature = NULL;
 		EC_KEY_free(eckey);
 		eckey = NULL;
 		EC_KEY_free(wrong_eckey);
 		wrong_eckey = NULL;
+		ECDSA_SIG_free(ecdsa_sig);
+		ecdsa_sig = NULL;
+		OPENSSL_free(raw_buf);
+		raw_buf = NULL;
 		}
 
 	ret = 1;	
@@ -451,8 +518,12 @@ builtin_err:
 		EC_KEY_free(eckey);
 	if (wrong_eckey)
 		EC_KEY_free(wrong_eckey);
+	if (ecdsa_sig)
+		ECDSA_SIG_free(ecdsa_sig);
 	if (signature)
 		OPENSSL_free(signature);
+	if (raw_buf)
+		OPENSSL_free(raw_buf);
 	if (curves)
 		OPENSSL_free(curves);