From ade5dd2baceb451592f785cfb9108dab8683bdd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se>
Date: Sun, 24 Nov 2013 21:21:01 +0100
Subject: [PATCH] dsa: Allow arbitrary digest sizes. Support 224-bit q.

---
 ChangeLog                   | 17 ++++++++++++++
 Makefile.in                 |  2 +-
 dsa-hash.c                  | 47 +++++++++++++++++++++++++++++++++++++
 dsa-keygen.c                |  3 ++-
 dsa-sign.c                  | 35 +++++++++++++--------------
 dsa-verify.c                |  5 +---
 dsa.h                       |  5 ++++
 testsuite/dsa-keygen-test.c |  9 +++++++
 8 files changed, 98 insertions(+), 25 deletions(-)
 create mode 100644 dsa-hash.c

diff --git a/ChangeLog b/ChangeLog
index 50f910c1..f7e6eb22 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2013-11-24  Niels Möller  <nisse@lysator.liu.se>
+
+	* testsuite/dsa-keygen-test.c (test_main): Test generating a
+	key with 224-bit q.
+
+	* dsa-verify.c (_dsa_verify): Use _dsa_hash.
+
+	* dsa-sign.c (_dsa_sign): Use _dsa_hash. Fix memory leak in
+	error case, spotted by Nikos.
+
+	* dsa-keygen.c (dsa_generate_keypair): Allow q_bits == 224.
+
+	* dsa-hash.c (_dsa_hash): New file and function. Allows digest
+	sizes not matching the bitsize of q.
+	* dsa.h (_dsa_hash): Declare it.
+	* Makefile.in (hogweed_SOURCES): Added dsa-hash.c.
+
 2013-11-23  Niels Möller  <nisse@lysator.liu.se>
 
 	* configure.ac: Check also for openssl/ecdsa.h.
diff --git a/Makefile.in b/Makefile.in
index 13acde64..05e6adeb 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -129,7 +129,7 @@ hogweed_SOURCES = sexp.c sexp-format.c \
 		  rsa-encrypt.c rsa-decrypt.c rsa-decrypt-tr.c \
 		  rsa-keygen.c rsa-compat.c rsa-blind.c \
 		  rsa2sexp.c sexp2rsa.c \
-		  dsa.c dsa-sign.c dsa-verify.c dsa-keygen.c \
+		  dsa.c dsa-sign.c dsa-verify.c dsa-keygen.c dsa-hash.c \
 		  dsa-sha1-sign.c dsa-sha1-verify.c \
 		  dsa-sha256-sign.c dsa-sha256-verify.c  \
 		  dsa2sexp.c sexp2dsa.c \
diff --git a/dsa-hash.c b/dsa-hash.c
new file mode 100644
index 00000000..f61cfd82
--- /dev/null
+++ b/dsa-hash.c
@@ -0,0 +1,47 @@
+/* dsa-hash.c */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright (C) 2013 Niels Möller
+ *
+ * The nettle library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The nettle library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the nettle library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02111-1301, USA.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "dsa.h"
+
+#include "bignum.h"
+
+/* Convert hash value to an integer. The general description of DSA in
+   FIPS186-3 allows both larger and smaller q; in the the latter case,
+   the hash must be truncated to the right number of bits. */
+void
+_dsa_hash (mpz_t h, unsigned bit_size,
+	   size_t length, const uint8_t *digest)
+{
+  
+  if (length > (bit_size + 7) / 8)
+    length = (bit_size + 7) / 8;
+
+  nettle_mpz_set_str_256_u(h, length, digest);
+
+  if (8 * length > bit_size)
+    /* We got a few extra bits, at the low end. Discard them. */
+    mpz_tdiv_q_2exp (h, h, 8*length - bit_size);
+}
diff --git a/dsa-keygen.c b/dsa-keygen.c
index 1b84e497..0a9cf406 100644
--- a/dsa-keygen.c
+++ b/dsa-keygen.c
@@ -36,7 +36,7 @@
 #include "nettle-internal.h"
 
 
-/* Valid sizes, according to FIPS 186-3 are (1024, 160), (2048. 224),
+/* Valid sizes, according to FIPS 186-3 are (1024, 160), (2048, 224),
    (2048, 256), (3072, 256). Currenty, we use only q_bits of 160 or
    256. */
 int
@@ -56,6 +56,7 @@ dsa_generate_keypair(struct dsa_public_key *pub,
       if (p_bits < DSA_SHA1_MIN_P_BITS)
 	return 0;
       break;
+    case 224:
     case 256:
       if (p_bits < DSA_SHA256_MIN_P_BITS)
 	return 0;
diff --git a/dsa-sign.c b/dsa-sign.c
index 93b36530..2165ace7 100644
--- a/dsa-sign.c
+++ b/dsa-sign.c
@@ -46,14 +46,8 @@ _dsa_sign(const struct dsa_public_key *pub,
   mpz_t k;
   mpz_t h;
   mpz_t tmp;
-
-  /* Require precise match of bitsize of q and hash size. The general
-     description of DSA in FIPS186-3 allows both larger and smaller q;
-     in the the latter case, the hash must be truncated to the right
-     number of bits. */
-  if (mpz_sizeinbase(pub->q, 2) != 8 * digest_size)
-    return 0;
-
+  int res;
+  
   /* Select k, 0<k<q, randomly */
   mpz_init_set(tmp, pub->q);
   mpz_sub_ui(tmp, tmp, 1);
@@ -68,23 +62,26 @@ _dsa_sign(const struct dsa_public_key *pub,
 
   /* Compute hash */
   mpz_init(h);
-  nettle_mpz_set_str_256_u(h, digest_size, digest);
+  _dsa_hash (h, mpz_sizeinbase(pub->q, 2), digest_size, digest);
 
   /* Compute k^-1 (mod q) */
-  if (!mpz_invert(k, k, pub->q))
+  if (mpz_invert(k, k, pub->q))
+    {
+      /* Compute signature s = k^-1 (h + xr) (mod q) */
+      mpz_mul(tmp, signature->r, key->x);
+      mpz_fdiv_r(tmp, tmp, pub->q);
+      mpz_add(tmp, tmp, h);
+      mpz_mul(tmp, tmp, k);
+      mpz_fdiv_r(signature->s, tmp, pub->q);
+      res = 1;
+    }
+  else
     /* What do we do now? The key is invalid. */
-    return 0;
-
-  /* Compute signature s = k^-1 (h + xr) (mod q) */
-  mpz_mul(tmp, signature->r, key->x);
-  mpz_fdiv_r(tmp, tmp, pub->q);
-  mpz_add(tmp, tmp, h);
-  mpz_mul(tmp, tmp, k);
-  mpz_fdiv_r(signature->s, tmp, pub->q);
+    res = 0;
 
   mpz_clear(k);
   mpz_clear(h);
   mpz_clear(tmp);
 
-  return 1;
+  return res;
 }
diff --git a/dsa-verify.c b/dsa-verify.c
index 8dac16d8..89232e8f 100644
--- a/dsa-verify.c
+++ b/dsa-verify.c
@@ -45,9 +45,6 @@ _dsa_verify(const struct dsa_public_key *key,
 
   int res;
 
-  if (mpz_sizeinbase(key->q, 2) != 8 * digest_size)
-    return 0;
-
   /* Check that r and s are in the proper range */
   if (mpz_sgn(signature->r) <= 0 || mpz_cmp(signature->r, key->q) >= 0)
     return 0;
@@ -71,7 +68,7 @@ _dsa_verify(const struct dsa_public_key *key,
   mpz_init(v);
 
   /* The message digest */
-  nettle_mpz_set_str_256_u(tmp, digest_size, digest);
+  _dsa_hash (tmp, mpz_sizeinbase (key->q, 2), digest_size, digest);
   
   /* v = g^{w * h (mod q)} (mod p)  */
   mpz_mul(tmp, tmp, w);
diff --git a/dsa.h b/dsa.h
index b30f941d..59ddbec2 100644
--- a/dsa.h
+++ b/dsa.h
@@ -62,6 +62,7 @@ extern "C" {
 #define dsa_public_key_from_der_iterator nettle_dsa_public_key_from_der_iterator
 #define dsa_openssl_private_key_from_der_iterator nettle_dsa_openssl_private_key_from_der_iterator 
 #define dsa_openssl_private_key_from_der nettle_openssl_provate_key_from_der
+#define _dsa_hash _nettle_dsa_hash
 #define _dsa_sign _nettle_dsa_sign
 #define _dsa_verify _nettle_dsa_verify
 
@@ -272,6 +273,10 @@ dsa_openssl_private_key_from_der(struct dsa_public_key *pub,
 
 
 /* Internal functions. */
+void
+_dsa_hash (mpz_t h, unsigned bit_size,
+	   size_t length, const uint8_t *digest);
+
 int
 _dsa_sign(const struct dsa_public_key *pub,
 	  const struct dsa_private_key *key,
diff --git a/testsuite/dsa-keygen-test.c b/testsuite/dsa-keygen-test.c
index a4db5994..d57c1107 100644
--- a/testsuite/dsa-keygen-test.c
+++ b/testsuite/dsa-keygen-test.c
@@ -39,6 +39,15 @@ test_main(void)
   test_dsa_key(&pub, &key, 256);
   test_dsa256(&pub, &key, NULL);
   
+  ASSERT (dsa_generate_keypair(&pub, &key,
+			       &lfib,
+			       (nettle_random_func *) knuth_lfib_random,
+			       NULL, verbose ? progress : NULL,
+			       2048, 224));
+
+  test_dsa_key(&pub, &key, 224);
+  test_dsa256(&pub, &key, NULL);
+  
   dsa_public_key_clear(&pub);
   dsa_private_key_clear(&key);
 }
-- 
GitLab