[PATCH] Truncate hash values for ECDSA signature scheme

Dmitry Eremin-Solenikov dbaryshkov at gmail.com
Mon Dec 16 17:34:46 CET 2013


* cipher/ecc-ecdsa.c (_gcry_ecc_ecdsa_sign, _gcry_ecc_ecdsa_verify):
  as required by ECDSA scheme, truncate hash values to bitlength of
  used curve.

* tests/pubkey.c (check_ecc_sample_key): add a testcase for hash
  truncation.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov at gmail.com>
---
 cipher/ecc-ecdsa.c | 22 ++++++++++++++++++++--
 tests/pubkey.c     | 45 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/cipher/ecc-ecdsa.c b/cipher/ecc-ecdsa.c
index b4bbe2c..0cdd12b 100644
--- a/cipher/ecc-ecdsa.c
+++ b/cipher/ecc-ecdsa.c
@@ -67,7 +67,16 @@ _gcry_ecc_ecdsa_sign (gcry_mpi_t input, ECC_secret_key *skey,
         mpi_rshift (hash, hash, abits - qbits);
     }
   else
-    hash = input;
+    {
+      abits = mpi_get_nbits (input);
+      if (abits > qbits)
+        {
+          hash = _gcry_mpi_copy (input);
+          mpi_rshift (hash, hash, mpi_get_nbits (input) - qbits);
+        }
+      else
+        hash = input;
+    }
 
 
   k = NULL;
@@ -164,6 +173,7 @@ _gcry_ecc_ecdsa_verify (gcry_mpi_t input, ECC_public_key *pkey,
   gcry_mpi_t h, h1, h2, x;
   mpi_point_struct Q, Q1, Q2;
   mpi_ec_t ctx;
+  unsigned int abits, nbits;
 
   if( !(mpi_cmp_ui (r, 0) > 0 && mpi_cmp (r, pkey->E.n) < 0) )
     return GPG_ERR_BAD_SIGNATURE; /* Assertion	0 < r < n  failed.  */
@@ -183,8 +193,16 @@ _gcry_ecc_ecdsa_verify (gcry_mpi_t input, ECC_public_key *pkey,
 
   /* h  = s^(-1) (mod n) */
   mpi_invm (h, s, pkey->E.n);
+  abits = mpi_get_nbits (input);
+  nbits = mpi_get_nbits (pkey->E.n);
   /* h1 = hash * s^(-1) (mod n) */
-  mpi_mulm (h1, input, h, pkey->E.n);
+  if (abits > nbits)
+    {
+      mpi_rshift (h1, input, abits - nbits);
+      mpi_mulm (h1, h1, h, pkey->E.n);
+    }
+  else
+    mpi_mulm (h1, input, h, pkey->E.n);
   /* Q1 = [ hash * s^(-1) ]G  */
   _gcry_mpi_ec_mul_point (&Q1, h1, &pkey->E.G, ctx);
   /* h2 = r * s^(-1) (mod n) */
diff --git a/tests/pubkey.c b/tests/pubkey.c
index 4e12dfd..3150669 100644
--- a/tests/pubkey.c
+++ b/tests/pubkey.c
@@ -980,9 +980,23 @@ check_ecc_sample_key (void)
     "(data (flags raw)\n"
     " (value #00112233445566778899AABBCCDDEEFF"
     /* */    "000102030405060708090A0B0C0D0E0F#))";
+  static const char hash2_string[] =
+    "(data (flags raw)\n"
+    " (value #00112233445566778899AABBCCDDEEFF"
+    /* */    "000102030405060708090A0B0C0D0E0F"
+    /* */    "000102030405060708090A0B0C0D0E0F"
+    /* */    "00112233445566778899AABBCCDDEEFF#))";
+  /* hash2, but longer than curve length, so it will be truncated */
+  static const char hash3_string[] =
+    "(data (flags raw)\n"
+    " (value #00112233445566778899AABBCCDDEEFF"
+    /* */    "000102030405060708090A0B0C0D0E0F"
+    /* */    "000102030405060708090A0B0C0D0E0F"
+    /* */    "00112233445566778899AABBCCDDEEFF"
+    /* */    "000102030405060708090A0B0C0D0E0F#))";
 
   gpg_error_t err;
-  gcry_sexp_t key, hash, sig;
+  gcry_sexp_t key, hash, hash2, hash3, sig, sig2;
 
   if (verbose)
     fprintf (stderr, "Checking sample ECC key.\n");
@@ -990,6 +1004,12 @@ check_ecc_sample_key (void)
   if ((err = gcry_sexp_new (&hash, hash_string, 0, 1)))
     die ("line %d: %s", __LINE__, gpg_strerror (err));
 
+  if ((err = gcry_sexp_new (&hash2, hash2_string, 0, 1)))
+    die ("line %d: %s", __LINE__, gpg_strerror (err));
+
+  if ((err = gcry_sexp_new (&hash3, hash3_string, 0, 1)))
+    die ("line %d: %s", __LINE__, gpg_strerror (err));
+
   if ((err = gcry_sexp_new (&key, ecc_private_key, 0, 1)))
     die ("line %d: %s", __LINE__, gpg_strerror (err));
 
@@ -1003,6 +1023,27 @@ check_ecc_sample_key (void)
   if ((err = gcry_pk_verify (sig, hash, key)))
     die ("gcry_pk_verify failed: %s", gpg_strerror (err));
 
+  /* Verify hash truncation */
+  gcry_sexp_release (key);
+  if ((err = gcry_sexp_new (&key, ecc_private_key, 0, 1)))
+    die ("line %d: %s", __LINE__, gpg_strerror (err));
+
+  if ((err = gcry_pk_sign (&sig2, hash2, key)))
+    die ("gcry_pk_sign failed: %s", gpg_strerror (err));
+
+  if ((err = gcry_pk_sign (&sig, hash3, key)))
+    die ("gcry_pk_sign failed: %s", gpg_strerror (err));
+
+  gcry_sexp_release (key);
+  if ((err = gcry_sexp_new (&key, ecc_public_key, 0, 1)))
+    die ("line %d: %s", __LINE__, gpg_strerror (err));
+
+  if ((err = gcry_pk_verify (sig, hash2, key)))
+    die ("gcry_pk_verify failed 2: %s", gpg_strerror (err));
+
+  if ((err = gcry_pk_verify (sig2, hash3, key)))
+    die ("gcry_pk_verify failed 3: %s", gpg_strerror (err));
+
   /* Now try signing without the Q parameter.  */
 
   gcry_sexp_release (key);
@@ -1021,8 +1062,10 @@ check_ecc_sample_key (void)
     die ("gcry_pk_verify signed without Q failed: %s", gpg_strerror (err));
 
   gcry_sexp_release (sig);
+  gcry_sexp_release (sig2);
   gcry_sexp_release (key);
   gcry_sexp_release (hash);
+  gcry_sexp_release (hash2);
 }
 
 
-- 
1.8.5.1




More information about the Gcrypt-devel mailing list