ecc: Support use of Ed25519 with ECDSA.
authorWerner Koch <wk@gnupg.org>
Tue, 15 Oct 2013 07:08:31 +0000 (09:08 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 15 Oct 2013 07:08:31 +0000 (09:08 +0200)
* src/cipher.h (PUBKEY_FLAG_ECDSA): New.
* cipher/pubkey-util.c (_gcry_pk_util_parse_flaglist): Add flag "ecdsa".
* cipher/ecc.c (verify_ecdsa, verify_eddsa): Remove some debug output.
(ecc_generate, ecc_sign, ecc_verify): Support Ed25519 with ECDSA.
* tests/keygen.c (check_ecc_keys): Create such a test key.
* tests/pubkey.c (fail, info, data_from_hex, extract_cmp_data): New.
Take from dsa-6979.c
(check_ed25519ecdsa_sample_key): new.
(main): Call new test.

Signed-off-by: Werner Koch <wk@gnupg.org>
cipher/ecc.c
cipher/pubkey-util.c
src/cipher.h
tests/keygen.c
tests/pubkey.c

index da384e8..3b75fea 100644 (file)
@@ -558,13 +558,10 @@ verify_ecdsa (gcry_mpi_t input, ECC_public_key *pkey,
           log_mpidump ("     x", x);
           log_mpidump ("     r", r);
           log_mpidump ("     s", s);
-          log_debug ("ecc verify: Not verified\n");
         }
       err = GPG_ERR_BAD_SIGNATURE;
       goto leave;
     }
-  if (DBG_CIPHER)
-    log_debug ("ecc verify: Accepted\n");
 
  leave:
   _gcry_mpi_ec_free (ctx);
@@ -1208,14 +1205,10 @@ verify_eddsa (gcry_mpi_t input, ECC_public_key *pkey,
     goto leave;
   if (tlen != rlen || memcmp (tbuf, rbuf, tlen))
     {
-      if (DBG_CIPHER)
-        log_debug ("eddsa verify: Not verified\n");
       rc = GPG_ERR_BAD_SIGNATURE;
       goto leave;
     }
 
-  if (DBG_CIPHER)
-    log_debug ("eddsa verify: Accepted\n");
   rc = 0;
 
  leave:
@@ -1250,10 +1243,12 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
   gcry_random_level_t random_level;
   mpi_ec_t ctx = NULL;
   gcry_sexp_t curve_info = NULL;
+  gcry_sexp_t curve_flags = NULL;
   gcry_mpi_t base = NULL;
   gcry_mpi_t public = NULL;
   gcry_mpi_t secret = NULL;
   int flags = 0;
+  int ed25519_with_ecdsa = 0;
 
   memset (&E, 0, sizeof E);
   memset (&sk, 0, sizeof sk);
@@ -1328,7 +1323,13 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
       rc = nist_generate_key (&sk, &E, ctx, random_level, nbits);
       break;
     case ECC_DIALECT_ED25519:
-      rc = eddsa_generate_key (&sk, &E, ctx, random_level);
+      if ((flags & PUBKEY_FLAG_ECDSA))
+        {
+          ed25519_with_ecdsa = 1;
+          rc = nist_generate_key (&sk, &E, ctx, random_level, nbits);
+        }
+      else
+        rc = eddsa_generate_key (&sk, &E, ctx, random_level);
       break;
     default:
       rc = GPG_ERR_INTERNAL;
@@ -1341,7 +1342,7 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
   if (_gcry_mpi_ec_get_affine (x, y, &sk.E.G, ctx))
     log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "G");
   base = _gcry_ecc_ec2os (x, y, sk.E.p);
-  if (sk.E.dialect == ECC_DIALECT_ED25519)
+  if (sk.E.dialect == ECC_DIALECT_ED25519 && !ed25519_with_ecdsa)
     {
       unsigned char *encpk;
       unsigned int encpklen;
@@ -1367,16 +1368,23 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
         goto leave;
     }
 
+  if (ed25519_with_ecdsa)
+    {
+      rc = gcry_sexp_build (&curve_info, NULL, "(flags ecdsa)");
+      if (rc)
+        goto leave;
+    }
+
   rc = gcry_sexp_build (r_skey, NULL,
                         "(key-data"
                         " (public-key"
-                        "  (ecc%S(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))"
+                        "  (ecc%S%S(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))"
                         " (private-key"
-                        "  (ecc%S(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)(d%m)))"
+                        "  (ecc%S%S(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)(d%m)))"
                         " )",
-                        curve_info,
+                        curve_info, curve_flags,
                         sk.E.p, sk.E.a, sk.E.b, base, sk.E.n, public,
-                        curve_info,
+                        curve_info, curve_flags,
                         sk.E.p, sk.E.a, sk.E.b, base, sk.E.n, public, secret);
   if (rc)
     goto leave;
@@ -1390,6 +1398,8 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
       log_printmpi ("ecgen result  n", sk.E.n);
       log_printmpi ("ecgen result  Q", public);
       log_printmpi ("ecgen result  d", secret);
+      if (ed25519_with_ecdsa)
+        log_debug ("ecgen result  using Ed25519/ECDSA\n");
     }
 
  leave:
@@ -1580,9 +1590,11 @@ ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     }
   if (DBG_CIPHER)
     {
-      log_debug ("ecc_sign   info: %s/%s\n",
+      log_debug ("ecc_sign   info: %s/%s%s\n",
                  _gcry_ecc_model2str (sk.E.model),
-                 _gcry_ecc_dialect2str (sk.E.dialect));
+                 _gcry_ecc_dialect2str (sk.E.dialect),
+                 (sk.E.dialect == ECC_DIALECT_ED25519
+                  && (ctx.flags & PUBKEY_FLAG_ECDSA))? "ECDSA":"");
       if (sk.E.name)
         log_debug  ("ecc_sign   name: %s\n", sk.E.name);
       log_printmpi ("ecc_sign      p", sk.E.p);
@@ -1733,9 +1745,11 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
 
   if (DBG_CIPHER)
     {
-      log_debug ("ecc_verify info: %s/%s\n",
+      log_debug ("ecc_verify info: %s/%s%s\n",
                  _gcry_ecc_model2str (pk.E.model),
-                 _gcry_ecc_dialect2str (pk.E.dialect));
+                 _gcry_ecc_dialect2str (pk.E.dialect),
+                 (pk.E.dialect == ECC_DIALECT_ED25519
+                  && !(sigflags & PUBKEY_FLAG_EDDSA))? "/ECDSA":"");
       if (pk.E.name)
         log_debug  ("ecc_verify name: %s\n", pk.E.name);
       log_printmpi ("ecc_verify    p", pk.E.p);
index 3dfc027..caf715e 100644 (file)
@@ -75,6 +75,10 @@ _gcry_pk_util_parse_flaglist (gcry_sexp_t list,
           encoding = PUBKEY_ENC_RAW;
           flags |= PUBKEY_FLAG_EDDSA;
         }
+      else if (n == 5 && !memcmp (s, "ecdsa", 5))
+        {
+          flags |= PUBKEY_FLAG_ECDSA;
+        }
       else if (n == 3 && !memcmp (s, "raw", 3)
                && encoding == PUBKEY_ENC_UNKNOWN)
         {
index b3469e5..077af98 100644 (file)
 
 #define PUBKEY_FLAG_NO_BLINDING    (1 << 0)
 #define PUBKEY_FLAG_RFC6979        (1 << 1)
-#define PUBKEY_FLAG_EDDSA          (1 << 2)
-#define PUBKEY_FLAG_FIXEDLEN       (1 << 3)
-#define PUBKEY_FLAG_LEGACYRESULT   (1 << 4)
-#define PUBKEY_FLAG_RAW_FLAG       (1 << 5)
-#define PUBKEY_FLAG_TRANSIENT_KEY  (1 << 6)
-#define PUBKEY_FLAG_USE_X931       (1 << 7)
-#define PUBKEY_FLAG_USE_FIPS186    (1 << 8)
-#define PUBKEY_FLAG_USE_FIPS186_2  (1 << 9)
+#define PUBKEY_FLAG_FIXEDLEN       (1 << 2)
+#define PUBKEY_FLAG_LEGACYRESULT   (1 << 3)
+#define PUBKEY_FLAG_RAW_FLAG       (1 << 4)
+#define PUBKEY_FLAG_TRANSIENT_KEY  (1 << 5)
+#define PUBKEY_FLAG_USE_X931       (1 << 6)
+#define PUBKEY_FLAG_USE_FIPS186    (1 << 7)
+#define PUBKEY_FLAG_USE_FIPS186_2  (1 << 8)
+#define PUBKEY_FLAG_ECDSA          (1 << 9)
+#define PUBKEY_FLAG_EDDSA          (1 << 10)
 
 
 enum pk_operation
index b955116..2b98c42 100644 (file)
@@ -394,6 +394,23 @@ check_ecc_keys (void)
 
       gcry_sexp_release (key);
     }
+
+  if (verbose)
+    show ("creating ECC key using curve Ed25519 for ECDSA\n");
+  rc = gcry_sexp_build (&keyparm, NULL,
+                        "(genkey(ecc(curve Ed25519)(flags ecdsa)))");
+  if (rc)
+    die ("error creating S-expression: %s\n", gpg_strerror (rc));
+  rc = gcry_pk_genkey (&key, keyparm);
+  gcry_sexp_release (keyparm);
+  if (rc)
+    die ("error generating ECC key using curve Ed25519 for ECDSA: %s\n",
+         gpg_strerror (rc));
+
+  if (verbose > 1)
+    show_sexp ("ECC key:\n", key);
+
+  gcry_sexp_release (key);
 }
 
 
index baf234c..4dadf88 100644 (file)
 
 #include "../src/gcrypt-int.h"
 
+#define my_isascii(c) (!((c) & 0x80))
+#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a)                     \
+                      || (*(a) >= 'A' && *(a) <= 'F')  \
+                      || (*(a) >= 'a' && *(a) <= 'f'))
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+#define DIM(v)              (sizeof(v)/sizeof((v)[0]))
+#define DIMof(type,member)   DIM(((type *)0)->member)
+
+
 /* Sample RSA keys, taken from basic.c.  */
 
 static const char sample_private_key_1[] =
@@ -101,6 +113,7 @@ static const char sample_public_key_1[] =
 
 
 static int verbose;
+static int error_count;
 
 static void
 die (const char *format, ...)
@@ -116,6 +129,27 @@ die (const char *format, ...)
 }
 
 static void
+fail (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  error_count++;
+}
+
+static void
+info (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+}
+
+static void
 show_sexp (const char *prefix, gcry_sexp_t a)
 {
   char *buf;
@@ -132,6 +166,59 @@ show_sexp (const char *prefix, gcry_sexp_t a)
 }
 
 
+/* Convert STRING consisting of hex characters into its binary
+   representation and return it as an allocated buffer. The valid
+   length of the buffer is returned at R_LENGTH.  The string is
+   delimited by end of string.  The function returns NULL on
+   error.  */
+static void *
+data_from_hex (const char *string, size_t *r_length)
+{
+  const char *s;
+  unsigned char *buffer;
+  size_t length;
+
+  buffer = gcry_xmalloc (strlen(string)/2+1);
+  length = 0;
+  for (s=string; *s; s +=2 )
+    {
+      if (!hexdigitp (s) || !hexdigitp (s+1))
+        die ("error parsing hex string `%s'\n", string);
+      ((unsigned char*)buffer)[length++] = xtoi_2 (s);
+    }
+  *r_length = length;
+  return buffer;
+}
+
+
+static void
+extract_cmp_data (gcry_sexp_t sexp, const char *name, const char *expected)
+{
+  gcry_sexp_t l1;
+  const void *a;
+  size_t alen;
+  void *b;
+  size_t blen;
+
+  l1 = gcry_sexp_find_token (sexp, name, 0);
+  a = gcry_sexp_nth_data (l1, 1, &alen);
+  b = data_from_hex (expected, &blen);
+  if (!a)
+    fail ("parameter \"%s\" missing in key\n", name);
+  else if ( alen != blen || memcmp (a, b, alen) )
+    {
+      fail ("parameter \"%s\" does not match expected value\n", name);
+      if (verbose)
+        {
+          info ("expected: %s\n", expected);
+          show_sexp ("sexp: ", sexp);
+        }
+    }
+  gcry_free (b);
+  gcry_sexp_release (l1);
+}
+
+
 static void
 check_keys_crypt (gcry_sexp_t pkey, gcry_sexp_t skey,
                  gcry_sexp_t plain0, gpg_err_code_t decrypt_fail_code)
@@ -939,6 +1026,85 @@ check_ecc_sample_key (void)
 }
 
 
+static void
+check_ed25519ecdsa_sample_key (void)
+{
+  static const char ecc_private_key[] =
+    "(private-key\n"
+    " (ecc\n"
+    "  (curve \"Ed25519\")\n"
+    "  (q #044C056555BE4084BB3D8D8895FDF7C2893DFE0256251923053010977D12658321"
+    "        156D1ADDC07987713A418783658B476358D48D582DB53233D9DED3C1C2577B04#)"
+    "  (d #09A0C38E0F1699073541447C19DA12E3A07A7BFDB0C186E4AC5BCE6F23D55252#)"
+    "))";
+  static const char ecc_private_key_wo_q[] =
+    "(private-key\n"
+    " (ecc\n"
+    "  (curve \"Ed25519\")\n"
+    "  (d #09A0C38E0F1699073541447C19DA12E3A07A7BFDB0C186E4AC5BCE6F23D55252#)"
+    "))";
+  static const char ecc_public_key[] =
+    "(public-key\n"
+    " (ecc\n"
+    "  (curve \"Ed25519\")\n"
+    "  (q #044C056555BE4084BB3D8D8895FDF7C2893DFE0256251923053010977D12658321"
+    "        156D1ADDC07987713A418783658B476358D48D582DB53233D9DED3C1C2577B04#)"
+    "))";
+  static const char hash_string[] =
+    "(data (flags ecdsa rfc6979)\n"
+    " (hash sha256 #00112233445566778899AABBCCDDEEFF"
+    /* */          "000102030405060708090A0B0C0D0E0F#))";
+
+  gpg_error_t err;
+  gcry_sexp_t key, hash, sig;
+
+  if (verbose)
+    fprintf (stderr, "Checking sample Ed25519/ECDSA key.\n");
+
+  if ((err = gcry_sexp_new (&hash, hash_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));
+
+  if ((err = gcry_pk_sign (&sig, hash, 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, hash, key)))
+    die ("gcry_pk_verify failed: %s", gpg_strerror (err));
+
+  /* Now try signing without the Q parameter.  */
+
+  gcry_sexp_release (key);
+  if ((err = gcry_sexp_new (&key, ecc_private_key_wo_q, 0, 1)))
+    die ("line %d: %s", __LINE__, gpg_strerror (err));
+
+  gcry_sexp_release (sig);
+  if ((err = gcry_pk_sign (&sig, hash, key)))
+    die ("gcry_pk_sign without Q 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, hash, key)))
+    die ("gcry_pk_verify signed without Q failed: %s", gpg_strerror (err));
+
+  extract_cmp_data (sig, "r", ("a63123a783ef29b8276e08987daca4"
+                               "655d0179e22199bf63691fd88eb64e15"));
+  extract_cmp_data (sig, "s", ("0d9b45c696ab90b96b08812b485df185"
+                               "623ddaf5d02fa65ca5056cb6bd0f16f1"));
+
+  gcry_sexp_release (sig);
+  gcry_sexp_release (key);
+  gcry_sexp_release (hash);
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -969,6 +1135,7 @@ main (int argc, char **argv)
     check_x931_derived_key (i);
 
   check_ecc_sample_key ();
+  check_ed25519ecdsa_sample_key ();
 
-  return 0;
+  return !!error_count;
 }