Integrating http://code.google.com/p/gnupg-ecc/source/detail?r=15 .
authorAndrey Jivsov <openpgp@brainhub.org>
Thu, 6 Jan 2011 01:33:17 +0000 (17:33 -0800)
committerAndrey Jivsov <openpgp@brainhub.org>
Thu, 6 Jan 2011 01:33:17 +0000 (17:33 -0800)
The following works:
   gpg2 --gen-key (ECC)
   gpg2 --list-keys
   gpg2 --list-packets ~/.gnupg/pubring.gpg
   gpg2 --list-packets <private key from http://sites.google.com/site/brainhub/pgpecckeys>

ECDH doesn't work yet as the code must be re-written to adjust for gpg-agent refactoring.

34 files changed:
agent/cvt-openpgp.c
agent/findkey.c
agent/protect.c
common/convert.c
common/util.h
configure.ac
dirmngr/Makefile.am
g10/Makefile.am
g10/armor.c
g10/build-packet.c
g10/call-agent.c
g10/call-agent.h
g10/ecdh.c [new file with mode: 0644]
g10/encrypt.c
g10/export.c
g10/getkey.c
g10/gpg.c
g10/keygen.c
g10/keyid.c
g10/main.h
g10/mainproc.c
g10/misc.c
g10/parse-packet.c
g10/passphrase.c
g10/pkglue.c
g10/pkglue.h
g10/pubkey-enc.c
g10/seskey.c
g10/sign.c
g10/verify-stubs.c [new file with mode: 0644]
g13/utils.c
g13/utils.h
include/cipher.h
kbx/keybox-openpgp.c

index e6a14c4..3dba79e 100644 (file)
@@ -27,6 +27,7 @@
 #include "agent.h"
 #include "i18n.h"
 #include "cvt-openpgp.h"
+#include "../include/cipher.h" /* for PUBKEY_ALGO_ECDSA, PUBKEY_ALGO_ECDH */
 
 
 /* Helper to pass data via the callback to do_unprotect. */
@@ -49,7 +50,12 @@ struct try_do_unprotect_arg_s
   gcry_sexp_t *r_key;
 };
 
-
+/* TODO: it is also in misc, which is not linked with the agent */
+static int
+map_pk_openpgp_to_gcry (int algo)
+{
+  return (algo==PUBKEY_ALGO_ECDSA ? GCRY_PK_ECDSA : (algo==PUBKEY_ALGO_ECDH ? GCRY_PK_ECDH : algo));
+}
 
 /* Compute the keygrip from the public key and store it at GRIP.  */
 static gpg_error_t
@@ -80,6 +86,12 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
                              "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]);
       break;
 
+   case GCRY_PK_ECDSA:
+   case GCRY_PK_ECDH:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(ecc(c%m)(q%m)))", pkey[0], pkey[1]);
+      break;
+
     default:
       err = gpg_error (GPG_ERR_PUBKEY_ALGO);
       break;
@@ -94,7 +106,9 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
 
 
 /* Convert a secret key given as algorithm id and an array of key
-   parameters into our s-expression based format.  */
+   parameters into our s-expression based format. 
+   pubkey_algo is a libgcrypt ID
+ */
 static gpg_error_t
 convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
 {
@@ -103,6 +117,8 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
 
   *r_key = NULL;
 
+  pubkey_algo = map_pk_openpgp_to_gcry( pubkey_algo );
+
   switch (pubkey_algo)
     {
     case GCRY_PK_DSA:
@@ -128,6 +144,18 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
                              skey[5]);
       break;
 
+    case GCRY_PK_ECDSA:
+      err = gcry_sexp_build (&s_skey, NULL,
+                             "(private-key(ecdsa(c%m)(q%m)(d%m)))",
+                             skey[0], skey[1], skey[2]);
+      break;
+
+    case GCRY_PK_ECDH:
+      err = gcry_sexp_build (&s_skey, NULL,
+                             "(private-key(ecdh(c%m)(q%m)(p%m)(d%m)))",
+                             skey[0], skey[1], skey[2], skey[3]);
+      break;
+
     default:
       err = gpg_error (GPG_ERR_PUBKEY_ALGO);
       break;
@@ -202,6 +230,10 @@ do_unprotect (const char *passphrase,
 
   *r_key = NULL;
 
+ /* Unfortunately, the OpenPGP PK algorithm numbers need to be re-mapped for Libgcrypt
+  */
+  pubkey_algo = map_pk_openpgp_to_gcry( pubkey_algo );
+
   /* Count the actual number of MPIs is in the array and set the
      remainder to NULL for easier processing later on.  */
   for (skeylen = 0; skey[skeylen]; skeylen++)
@@ -219,9 +251,6 @@ do_unprotect (const char *passphrase,
 
   if (gcry_pk_test_algo (pubkey_algo))
     {
-      /* The algorithm numbers are Libgcrypt numbers but fortunately
-         the OpenPGP algorithm numbers map one-to-one to the Libgcrypt
-         numbers.  */
       log_info (_("public key algorithm %d (%s) is not supported\n"),
                 pubkey_algo, gcry_pk_algo_name (pubkey_algo));
       return gpg_error (GPG_ERR_PUBKEY_ALGO);
@@ -632,7 +661,7 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
   string = gcry_sexp_nth_string (list, 1);
   if (!string)
     goto bad_seckey;
-  pubkey_algo = gcry_pk_map_name (string);
+  pubkey_algo = gcry_pk_map_name (string);     /* ligcrypt IDs */
   xfree (string);
 
   if (gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
@@ -999,6 +1028,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
     }
   
   algo = gcry_pk_map_name (name);
+  log_debug ( "convert to openpgp begin for algo=%s\n", name );
   xfree (name);
 
   switch (algo)
@@ -1007,7 +1037,8 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
     case GCRY_PK_ELG:   algoname = "elg";   npkey = 3; elems = "pgyx";    break;
     case GCRY_PK_ELG_E: algoname = "elg";   npkey = 3; elems = "pgyx";    break;
     case GCRY_PK_DSA:   algoname = "dsa";   npkey = 4; elems = "pqgyx";   break;
-    case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 6; elems = "pabgnqd"; break;
+    case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 2; elems = "cqd";     break;
+    case GCRY_PK_ECDH:  algoname = "ecdh";  npkey = 3; elems = "cqpd";    break;
     default:            algoname = "";      npkey = 0; elems = NULL;      break;
     }
   assert (!elems || strlen (elems) < DIM (array) );
@@ -1027,6 +1058,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
   err = apply_protection (array, npkey, nskey, passphrase,
                           GCRY_CIPHER_AES, protect_iv, sizeof protect_iv,
                           3, GCRY_MD_SHA1, salt, s2k_count);
+  ///log_debug ( "convert to openpgp: after applying protection, err = %d\n", err );
   /* Turn it into the transfer key S-expression.  Note that we always
      return a protected key.  */
   if (!err)
@@ -1037,7 +1069,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
       int   format_args_buf_int[1];
       void *format_args[10+2];
       size_t n;
-      gcry_sexp_t tmpkey, tmpsexp;
+      gcry_sexp_t tmpkey, tmpsexp = NULL;
       
       snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
       
@@ -1056,6 +1088,8 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
       put_membuf_str (&mbuf, ")\n");
       put_membuf (&mbuf, "", 1);
 
+      ///log_debug ( "convert to openpgp: calling gcry_sexp_build\n" );
+
       tmpkey = NULL;
       {
         char *format = get_membuf (&mbuf, NULL);
@@ -1065,6 +1099,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
           err = gcry_sexp_build_array (&tmpkey, NULL, format, format_args);
         xfree (format);
       }
+      ///log_debug ( "convert to openpgp: calling gcry_sexp_build before err=%d\n", err );
       if (!err)
         err = gcry_sexp_build (&tmpsexp, NULL,
                                "(openpgp-private-key\n"
@@ -1077,6 +1112,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
                                (int)sizeof protect_iv, protect_iv,
                                (int)sizeof salt, salt,
                                countbuf);
+      ///log_debug ( "convert to openpgp: after gcry_sexp_build, err = %d\n", err );
       gcry_sexp_release (tmpkey);
       if (!err)
         err = make_canon_sexp_pad (tmpsexp, 0, r_transferkey, r_transferkeylen);
@@ -1085,6 +1121,8 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
 
   for (i=0; i < DIM (array); i++)
     gcry_mpi_release (array[i]);
+
+  log_debug ( "convert to openpgp end with err=%d\n", err );
   
   return err;
 }
index 91fb8c1..02e938e 100644 (file)
@@ -726,6 +726,16 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
       algoname = "dsa";
       elems = "pqgy";
     }
+  else if (n==5 && !memcmp (name, "ecdsa", 5))
+    {
+      algoname = "ecdsa";
+      elems = "cq";
+    }
+  else if (n==4 && !memcmp (name, "ecdh", 4))
+    {
+      algoname = "ecdh";
+      elems = "cqp";
+    }
   else if (n==3 && !memcmp (name, "elg", 3))
     {
       algoname = "elg";
index 795d062..d146653 100644 (file)
@@ -52,6 +52,8 @@ static struct {
   { "rsa",  "nedpqu", 2, 5 },
   { "dsa",  "pqgyx", 4, 4 },
   { "elg",  "pgyx", 3, 3 },
+  { "ecdsa","cqd", 2, 2 },
+  { "ecdh", "cqpd", 3, 3 },
   { NULL }
 };
 
@@ -426,6 +428,9 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   unsigned char *p;
   gcry_md_hd_t md;
 
+  if (opt.debug & DBG_CRYPTO_VALUE)
+   log_info ("Protecting key=%s, passphrase=%s\n", plainkey, passphrase);
+
   /* Create an S-expression with the protected-at timestamp.  */
   memcpy (timestamp_exp, "(12:protected-at15:", 19);
   gnupg_get_isotime (timestamp_exp+19);
@@ -454,37 +459,51 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   for (infidx=0; protect_info[infidx].algo
               && !smatch (&s, n, protect_info[infidx].algo); infidx++)
     ;
-  if (!protect_info[infidx].algo)
+  if (!protect_info[infidx].algo)  {
+    log_info ("Unsupported alg %d for protection\n", protect_info[infidx].algo);
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); 
+  }
 
   prot_begin = prot_end = NULL;
   for (i=0; (c=protect_info[infidx].parmlist[i]); i++)
     {
       if (i == protect_info[infidx].prot_from)
         prot_begin = s;
-      if (*s != '(')
+      if (*s != '(')  {
+        log_info ("Unbalanced bracket in S-expression #1\n");
         return gpg_error (GPG_ERR_INV_SEXP);
+      }
       depth++;
       s++;
       n = snext (&s);
-      if (!n)
+      if (!n)  {
+        log_info ("Cannot get the length of S-expression field\n");
         return gpg_error (GPG_ERR_INV_SEXP); 
-      if (n != 1 || c != *s)
+      }
+      if (n != 1 || c != *s)  {
+        log_info ("Invalid length in S-expression field\n");
         return gpg_error (GPG_ERR_INV_SEXP); 
-      s += n;
+     } 
+     s += n;
       n = snext (&s);
-      if (!n)
+      if (!n)  {
+        log_info ("Invalid fieled in S-expression field\n");
         return gpg_error (GPG_ERR_INV_SEXP); 
+      }
       s +=n; /* skip value */
-      if (*s != ')')
+      if (*s != ')')  {
+        log_info ("Unbalanced bracket in S-expression #2\n");
         return gpg_error (GPG_ERR_INV_SEXP); 
+      }
       depth--;
       if (i == protect_info[infidx].prot_to)
         prot_end = s;
       s++;
     }
-  if (*s != ')' || !prot_begin || !prot_end )
+  if (*s != ')' || !prot_begin || !prot_end )  {
+    log_info ("Unbalanced bracket in S-expression #3\n");
     return gpg_error (GPG_ERR_INV_SEXP); 
+  }
   depth--;
   hash_end = s;
   s++;
index aa3a3a8..0a0c46f 100644 (file)
@@ -23,6 +23,7 @@
 #include <ctype.h>
 
 #include "util.h"
+#include "gcrypt.h"
 
 
 #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
@@ -245,5 +246,31 @@ hex2str_alloc (const char *hexstring, size_t *r_count)
   return result;
 }
 
+/* returns hex representation of the MPI; 
+ * caller must free with xfree 
+ * Returns NULL on error, never throws
+ */
+char *mpi2hex( gcry_mpi_t m )  {
+  size_t nbytes;
+  size_t nbytes2;
+  int rc;
+  byte *p;
+
+  nbytes = (mpi_get_nbits ( m )+7)/8;
+  if( nbytes == 0 )
+    return NULL;
+  p = xtrymalloc( nbytes*3+1 );
+  if( p==NULL )
+    return NULL;
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, p+2*nbytes+1, nbytes, &nbytes2, m);
+  if( rc )  {
+      xfree( p );
+      return NULL;
+  }
 
+  bin2hex( p+2*nbytes+1, nbytes2, p );
+  p[nbytes2*2] = '\0';
+//printf("%s:%d>>>> Created the string %s from %d bytes %02x %02x ..., MPI was %d bytes\n", __FILE__, __LINE__, p, nbytes2, p[2*nbytes+1], p[2*nbytes+2], nbytes);
+  return p;
+}    
 
index 7c58b15..44a72d9 100644 (file)
@@ -192,6 +192,7 @@ gpg_error_t get_pk_algo_from_canon_sexp (const unsigned char *keydata,
 int hex2bin (const char *string, void *buffer, size_t length);
 int hexcolon2bin (const char *string, void *buffer, size_t length);
 char *bin2hex (const void *buffer, size_t length, char *stringbuf);
+char *mpi2hex (gcry_mpi_t m);
 char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf);
 const char *hex2str (const char *hexstring,
                      char *buffer, size_t bufsize, size_t *buflen);
index 9757330..12545e2 100644 (file)
@@ -24,7 +24,7 @@ min_automake_version="1.10"
 # Remember to change the version number immediately *after* a release.
 # Set my_issvn to "yes" for non-released code.  Remember to run an
 # "svn up" and "autogen.sh" right before creating a distribution.
-m4_define([my_version], [2.1.0])
+m4_define([my_version], [2.2.0])
 m4_define([my_issvn], [yes])
 
 m4_define([svn_revision], m4_esyscmd([printf "%d" $(svn info 2>/dev/null \
@@ -43,7 +43,7 @@ development_version=no
 NEED_GPG_ERROR_VERSION=1.8
 
 NEED_LIBGCRYPT_API=1
-NEED_LIBGCRYPT_VERSION=1.4.0
+NEED_LIBGCRYPT_VERSION=1.6.0
 
 NEED_LIBASSUAN_API=2
 NEED_LIBASSUAN_VERSION=2.0.0
@@ -1466,7 +1466,7 @@ AC_ARG_ENABLE(optimization,
    AC_HELP_STRING([--disable-optimization],
                   [disable compiler optimization]),
                   [if test $enableval = no ; then
-                      CFLAGS=`echo $CFLAGS | sed s/-O[[1-9]]\ /-O0\ /g`
+                      CFLAGS=`echo $CFLAGS | sed s%-O[[1-9]]%-O0\ %g`
                    fi])
 
 #
index 5b1fe30..0285fc8 100644 (file)
@@ -61,7 +61,7 @@ endif
 dirmngr_LDADD = $(libcommonpth) ../gl/libgnu.a $(DNSLIBS) $(LIBASSUAN_LIBS) \
        $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(PTH_LIBS) $(LIBINTL) $(LIBICONV)
 if !USE_LDAPWRAPPER
-dirmngr_LDADD += $(LDAPLIBS)
+dirmngr_LDADD += $(LDAPLIBS) -llber
 endif
 dirmngr_LDFLAGS = $(extra_bin_ldflags)
 
index c8fc482..b82fe07 100644 (file)
@@ -72,7 +72,8 @@ common_source =  \
              plaintext.c       \
              sig-check.c       \
              keylist.c         \
-             pkglue.c pkglue.h 
+             pkglue.c pkglue.h \
+             ecdh.c
 
 gpg2_SOURCES  = gpg.c          \
              server.c          \
@@ -109,7 +110,8 @@ gpg2_SOURCES  = gpg.c               \
 
 gpgv2_SOURCES = gpgv.c           \
              $(common_source)  \
-             verify.c          
+             verify.c          \
+             verify-stubs.c
 
 #gpgd_SOURCES = gpgd.c \
 #             ks-proto.h \
index a6195fc..8cfd35c 100644 (file)
@@ -1079,7 +1079,7 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a,afx->eol);
            if( !opt.no_version )
              {
-               iobuf_writestr(a, "Version: GnuPG v"  VERSION " ("
+               iobuf_writestr(a, "Version: GnuPG v"  VERSION "-ecc ("
                               PRINTABLE_OS_NAME ")" );
                iobuf_writestr(a,afx->eol);
              }
index 83d6c7a..3a2c206 100644 (file)
@@ -178,6 +178,16 @@ mpi_write (iobuf_t out, gcry_mpi_t a)
   return rc;
 }
 
+/*
+ * Write the name OID, encoded as an mpi, to OUT. The format of the content of the MPI is
+ * one byte LEN, following by LEN bytes that are DER representation of an ASN.1 OID. 
+ * This is true for each of the 3 following functions.
+ */
+#define iobuf_name_oid_write iobuf_write_size_body_mpi
+/* Write the value of KEK fields for ECDH.  */
+#define ecdh_kek_params_write iobuf_write_size_body_mpi
+/* Write the value of encrypted filed for ECDH. */
+#define ecdh_esk_write iobuf_write_size_body_mpi
 
 
 /****************
@@ -290,10 +300,24 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
     }
   assert (npkey < nskey);
 
-  /* Writing the public parameters is easy. */
-  for (i=0; i < npkey; i++ )
-    if ((err = mpi_write (a, pk->pkey[i])))
-      goto leave;
+  if( pk->pubkey_algo != PUBKEY_ALGO_ECDSA && pk->pubkey_algo != PUBKEY_ALGO_ECDH )  {
+    /* Writing the public parameters is easy, */
+    for (i=0; i < npkey; i++ )
+      if ((err = mpi_write (a, pk->pkey[i])))
+        goto leave;
+  }
+  else  {
+     /* ... except we do an adjustment for ECC OID and possibly KEK params for ECDH */
+    if( (err=iobuf_name_oid_write(a, pk->pkey[0])) || /* DER of OID with preceeding length byte */
+        (err = mpi_write (a, pk->pkey[1])) )    /* point Q, the public key */
+    {
+       goto leave;
+    }
+    if( pk->pubkey_algo == PUBKEY_ALGO_ECDH && (err=ecdh_kek_params_write(a,pk->pkey[2])))  {  /* one more public field for ECDH */
+       goto leave;
+    }
+    /* followed by possibly protected private scalar */
+  }
   
   if (pk->seckey_info)
     {
@@ -458,8 +482,18 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
   n = pubkey_get_nenc( enc->pubkey_algo );
   if ( !n )
     write_fake_data( a, enc->data[0] );
-  for (i=0; i < n && !rc ; i++ )
-    rc = mpi_write(a, enc->data[i] );
+
+  if( enc->pubkey_algo != PUBKEY_ALGO_ECDH )  {
+    for (i=0; i < n && !rc ; i++ )
+      rc = mpi_write(a, enc->data[i] );
+  }
+  else  {
+    /* the second field persists as a LEN+field structure, even though it is 
+     * stored for uniformity as an MPI internally */
+    assert( n==2 );
+    rc = mpi_write(a, enc->data[0] );
+    if( !rc ) rc = ecdh_esk_write(a, enc->data[1] ); 
+  }
 
   if (!rc)
     {
index 9528e14..25f9a53 100644 (file)
@@ -1744,6 +1744,7 @@ inq_ciphertext_cb (void *opaque, const char *line)
 gpg_error_t
 agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
                  gcry_sexp_t s_ciphertext,
+                       const byte sk_fp[MAX_FINGERPRINT_LEN],
                  unsigned char **r_buf, size_t *r_buflen)
 {
   gpg_error_t err;
@@ -1751,6 +1752,8 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
   membuf_t data;
   size_t n, len;
   char *p, *buf, *endp;
+
+  /*TODO: use sk_fp */
   
   if (!keygrip || strlen(keygrip) != 40 || !s_ciphertext || !r_buf || !r_buflen)
     return gpg_error (GPG_ERR_INV_VALUE);
index e09c309..45e593b 100644 (file)
@@ -168,6 +168,7 @@ gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce,
 /* Decrypt a ciphertext.  */
 gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
                              gcry_sexp_t s_ciphertext,
+                               const byte sk_fp[MAX_FINGERPRINT_LEN],
                              unsigned char **r_buf, size_t *r_buflen);
 
 /* Retrieve a key encryption key.  */
diff --git a/g10/ecdh.c b/g10/ecdh.c
new file mode 100644 (file)
index 0000000..6615b75
--- /dev/null
@@ -0,0 +1,477 @@
+/* ecdh.c - ECDH public key operations used in public key glue code
+ *     Copyright (C) 2000, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "gpg.h"
+#include "util.h"
+#include "pkglue.h"
+#include "main.h"
+#include "options.h"
+
+gcry_mpi_t
+pk_ecdh_default_params_to_mpi( int qbits )  {
+  gpg_error_t err;
+  gcry_mpi_t result;
+  /* Defaults are the strongest possible choices. Performance is not an issue here, only interoperability. */
+  byte kek_params[4] = { 
+       3       /*size of following field*/, 
+       1       /*fixed version for KDF+AESWRAP*/, 
+       DIGEST_ALGO_SHA512      /* KEK MD */, 
+       CIPHER_ALGO_AES256      /*KEK AESWRAP alg*/
+  };
+  int i;
+
+  static const struct {
+    int qbits;
+    int openpgp_hash_id;
+    int openpgp_cipher_id;
+  } kek_params_table[] = {
+    { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES    },
+    { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 },
+    { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 }    // 528 is 521 rounded to the 8 bit boundary
+  };
+
+  for( i=0; i<sizeof(kek_params_table)/sizeof(kek_params_table[0]); i++ )  {
+    if( kek_params_table[i].qbits >= qbits )  {
+      kek_params[2] = kek_params_table[i].openpgp_hash_id;
+      kek_params[3] = kek_params_table[i].openpgp_cipher_id;
+      break;
+    }
+  }
+  if( DBG_CIPHER )
+      log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params) );
+
+  err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, kek_params, sizeof(kek_params), NULL);
+  if (err)
+    log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err));
+
+  return result;
+}
+
+/* returns allocated (binary) KEK parameters; the size is returned in sizeout. 
+ * The caller must free returned value with xfree. 
+ * Returns NULL on error 
+ */
+byte *
+pk_ecdh_default_params( int qbits, size_t *sizeout )  {
+  gpg_error_t err;
+  gcry_mpi_t result;
+  /* Defaults are the strongest possible choices. Performance is not an issue here, only interoperability. */
+  byte kek_params[4] = { 
+       3       /*size of following field*/, 
+       1       /*fixed version for KDF+AESWRAP*/, 
+       DIGEST_ALGO_SHA512      /* KEK MD */, 
+       CIPHER_ALGO_AES256      /*KEK AESWRAP alg*/
+  };
+  int i;
+
+  static const struct {
+    int qbits;
+    int openpgp_hash_id;
+    int openpgp_cipher_id;
+  } kek_params_table[] = {
+    { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES    },
+    { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 },
+    { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 }    // 528 is 521 rounded to the 8 bit boundary
+  };
+
+  byte *p;
+
+  *sizeout = 0;
+
+  for( i=0; i<sizeof(kek_params_table)/sizeof(kek_params_table[0]); i++ )  {
+    if( kek_params_table[i].qbits >= qbits )  {
+      kek_params[2] = kek_params_table[i].openpgp_hash_id;
+      kek_params[3] = kek_params_table[i].openpgp_cipher_id;
+      break;
+    }
+  }
+  if( DBG_CIPHER )
+      log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params) );
+
+  p = xtrymalloc( sizeof(kek_params) );
+  if( p == NULL )
+    return NULL;
+  memcpy( p, kek_params, sizeof(kek_params) );
+  *sizeout = sizeof(kek_params);
+  return p;
+}
+
+/* Encrypts/decrypts 'data' with a key derived from shared_mpi ECC point using FIPS SP 800-56A compliant method, which is
+ * key derivation + key wrapping. The direction is determined by the first parameter (is_encrypt=1 --> this is encryption).
+ * The result is returned in out as a size+value MPI.
+ * TODO: memory leaks (x_secret).
+ */
+static int
+pk_ecdh_encrypt_with_shared_point ( int is_encrypt, gcry_mpi_t shared_mpi, 
+       const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t data, gcry_mpi_t * pkey, gcry_mpi_t *out)
+{
+  byte *secret_x;
+  int secret_x_size;
+  byte kdf_params[256];
+  int kdf_params_size=0;
+  int nbits;
+  int kdf_hash_algo;
+  int kdf_encr_algo;
+  int rc;
+
+  *out = NULL;
+
+  nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey );
+
+  {
+    size_t nbytes;
+    /* extract x component of the shared point: this is the actual shared secret */
+    nbytes = (mpi_get_nbits (pkey[1] /* public point */)+7)/8;
+    secret_x = xmalloc_secure( nbytes );
+    rc = gcry_mpi_print (GCRYMPI_FMT_USG, secret_x, nbytes, &nbytes, shared_mpi);
+    if( rc )  {
+      xfree( secret_x );
+      log_error ("ec ephemeral export of shared point failed: %s\n", gpg_strerror (rc) );
+      return rc;
+    }
+    secret_x_size = (nbits+7)/8; 
+    assert( nbytes > secret_x_size );
+    memmove( secret_x, secret_x+1, secret_x_size );
+    memset( secret_x+secret_x_size, 0, nbytes-secret_x_size );
+
+    if( DBG_CIPHER )
+        log_printhex ("ecdh shared secret X is:", secret_x, secret_x_size );
+  }
+
+  /*** We have now the shared secret bytes in secret_x ***/
+
+  /* At this point we are done with PK encryption and the rest of the function uses symmetric 
+   *  key encryption techniques to protect the input 'data'. The following two sections will
+   *  simply replace current secret_x with a value derived from it. This will become a KEK. 
+   */
+  {
+    IOBUF obuf = iobuf_temp(); 
+    rc = iobuf_write_size_body_mpi ( obuf, pkey[2]  ); /* KEK params */
+
+    kdf_params_size = iobuf_temp_to_buffer( obuf, kdf_params, sizeof(kdf_params) );
+
+    if( DBG_CIPHER )
+        log_printhex ("ecdh KDF public key params are:", kdf_params, kdf_params_size );
+
+    if( kdf_params_size != 4 || kdf_params[0] != 3 || kdf_params[1] != 1  )    /* expect 4 bytes  03 01 hash_alg symm_alg */
+      return GPG_ERR_BAD_PUBKEY;
+
+    kdf_hash_algo = kdf_params[2];
+    kdf_encr_algo = kdf_params[3];
+
+    if( DBG_CIPHER )
+      log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n", gcry_md_algo_name (kdf_hash_algo), openpgp_cipher_algo_name (kdf_encr_algo) );
+
+    if( kdf_hash_algo != GCRY_MD_SHA256 && kdf_hash_algo != GCRY_MD_SHA384 && kdf_hash_algo != GCRY_MD_SHA512 )
+      return GPG_ERR_BAD_PUBKEY;
+    if( kdf_encr_algo != GCRY_CIPHER_AES128 && kdf_encr_algo != GCRY_CIPHER_AES192 && kdf_encr_algo != GCRY_CIPHER_AES256 )
+      return GPG_ERR_BAD_PUBKEY;
+  }
+
+  /* build kdf_params */
+  {
+    IOBUF obuf;
+
+    obuf = iobuf_temp();
+    /* variable-length field 1, curve name OID */
+    rc = iobuf_write_size_body_mpi ( obuf, pkey[0] );
+    /* fixed-length field 2 */
+    iobuf_put (obuf, PUBKEY_ALGO_ECDH);
+    /* variable-length field 3, KDF params */
+    rc = (rc ? rc : iobuf_write_size_body_mpi ( obuf, pkey[2] ));
+    /* fixed-length field 4 */
+    iobuf_write (obuf, "Anonymous Sender    ", 20);
+    /* fixed-length field 5, recipient fp */
+    iobuf_write (obuf, pk_fp, 20);     
+
+    kdf_params_size = iobuf_temp_to_buffer( obuf, kdf_params, sizeof(kdf_params) );
+    iobuf_close( obuf );
+    if( rc )  {
+      return rc;
+    }
+    if( DBG_CIPHER )
+        log_printhex ("ecdh KDF message params are:", kdf_params, kdf_params_size );
+  }
+
+  /* Derive a KEK (key wrapping key) using kdf_params and secret_x. */
+  {
+    gcry_md_hd_t h;
+    int old_size;
+
+    rc = gcry_md_open (&h, kdf_hash_algo, 0);
+    if(rc)
+       log_bug ("gcry_md_open failed for algo %d: %s",
+                       kdf_hash_algo, gpg_strerror (gcry_error(rc)));
+    gcry_md_write(h, "\x00\x00\x00\x01", 4);   /* counter = 1 */
+    gcry_md_write(h, secret_x, secret_x_size); /* x of the point X */
+    gcry_md_write(h, kdf_params, kdf_params_size);     /* KDF parameters */
+
+    gcry_md_final (h);
+
+    assert( gcry_md_get_algo_dlen (kdf_hash_algo) >= 32 );
+
+    memcpy (secret_x, gcry_md_read (h, kdf_hash_algo), gcry_md_get_algo_dlen (kdf_hash_algo));
+    gcry_md_close (h);
+
+    old_size = secret_x_size;
+    assert( old_size >= gcry_cipher_get_algo_keylen( kdf_encr_algo ) );
+    secret_x_size = gcry_cipher_get_algo_keylen( kdf_encr_algo );
+    assert( secret_x_size <= gcry_md_get_algo_dlen (kdf_hash_algo) );
+
+    memset( secret_x+secret_x_size, old_size-secret_x_size, 0 );       /* we could have allocated more, so clean the tail before returning */
+    if( DBG_CIPHER )
+      log_printhex ("ecdh KEK is:", secret_x, secret_x_size );
+   }
+
+  /* And, finally, aeswrap with key secret_x */
+  {
+    gcry_cipher_hd_t hd;
+    size_t nbytes;
+
+    byte *data_buf;
+    int data_buf_size;
+
+    gcry_mpi_t result;
+
+    rc = gcry_cipher_open (&hd, kdf_encr_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
+    if (rc)
+    {
+      log_error( "ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (rc));
+      return rc;
+    }
+
+    rc = gcry_cipher_setkey (hd, secret_x, secret_x_size);
+    xfree( secret_x );
+    if (rc)
+    {
+      gcry_cipher_close (hd);
+      log_error("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (rc));
+      return rc;
+    }
+
+    data_buf_size = (gcry_mpi_get_nbits(data)+7)/8;
+    assert( (data_buf_size & 7) == (is_encrypt ? 0 : 1) );
+
+    data_buf = xmalloc_secure( 1 + 2*data_buf_size + 8 );
+    if( !data_buf )  {
+      gcry_cipher_close (hd);
+      return GPG_ERR_ENOMEM;
+    }
+
+    if( is_encrypt )  {
+      byte *in = data_buf+1+data_buf_size+8;
+
+      /* write data MPI into the end of data_buf. data_buf is  size aeswrap data */
+      rc = gcry_mpi_print (GCRYMPI_FMT_USG, in, data_buf_size, &nbytes, data/*in*/);
+      if( rc )   {
+        log_error("ecdh failed to export DEK: %s\n", gpg_strerror (rc));
+        gcry_cipher_close (hd);
+        xfree( data_buf );
+        return rc;
+      }
+
+      if( DBG_CIPHER )
+         log_printhex ("ecdh encrypting  :", in, data_buf_size );
+
+      rc = gcry_cipher_encrypt (hd, data_buf+1, data_buf_size+8, in, data_buf_size);
+      memset( in, 0, data_buf_size);
+      gcry_cipher_close (hd);
+      if(rc)
+      {
+        log_error("ecdh failed in gcry_cipher_encrypt: %s\n", gpg_strerror (rc));
+        xfree( data_buf );
+        return rc;
+      }
+      data_buf[0] = data_buf_size+8;
+
+      if( DBG_CIPHER )
+         log_printhex ("ecdh encrypted to:", data_buf+1, data_buf[0] );
+
+      rc = gcry_mpi_scan ( &result, GCRYMPI_FMT_USG, data_buf, 1+data_buf[0], NULL); /* (byte)size + aeswrap of DEK */
+      xfree( data_buf );
+      if(rc)
+      {
+        log_error("ecdh failed to create an MPI: %s\n", gpg_strerror (rc));
+        return rc;
+      }
+
+      *out = result;
+    }
+    else  {
+      byte *in;
+
+      rc = gcry_mpi_print (GCRYMPI_FMT_USG, data_buf, data_buf_size, &nbytes, data/*in*/);
+      if( nbytes != data_buf_size || data_buf[0] != data_buf_size-1 )  {
+        log_error("ecdh inconsistent size\n");
+        xfree( data_buf );
+        return GPG_ERR_BAD_MPI;
+      }
+      in = data_buf+data_buf_size;
+      data_buf_size = data_buf[0];
+
+      if( DBG_CIPHER )
+         log_printhex ("ecdh decrypting :", data_buf+1, data_buf_size );
+      
+      rc = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, data_buf_size );
+      gcry_cipher_close (hd);
+      if(rc)
+      {
+        log_error("ecdh failed in gcry_cipher_decrypt: %s\n", gpg_strerror (rc));
+        xfree( data_buf );
+        return rc;
+      }
+
+      data_buf_size-=8;
+
+      if( DBG_CIPHER )
+         log_printhex ("ecdh decrypted to :", in, data_buf_size );
+
+      /* padding is removed later */
+      //if( in[data_buf_size-1] > 8 )  {
+      //  log_error("ecdh failed at decryption: invalid padding. %02x > 8\n", in[data_buf_size-1] );
+      //  return GPG_ERR_BAD_KEY;
+      //}
+      rc = gcry_mpi_scan ( &result, GCRYMPI_FMT_USG, in, data_buf_size, NULL);
+      xfree( data_buf );
+      if(rc)
+      {
+        log_error("ecdh failed to create a plain text MPI: %s\n", gpg_strerror (rc));
+        return rc;
+      }
+
+      *out = result;
+    }
+  }
+
+  return rc;
+}
+
+/* Perform ECDH encryption, which involves ECDH key generation.
+ */
+int
+pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t data, gcry_mpi_t * pkey)
+{
+  gcry_sexp_t s_ciph, s_data, s_pkey;
+
+  PKT_public_key *pk_eph;
+  int nbits;
+  int rc;
+
+  nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey );
+  /*** Generate an ephemeral key ***/
+
+  rc = pk_ecc_keypair_gen( &pk_eph, PUBKEY_ALGO_ECDH, KEYGEN_FLAG_TRANSIENT_KEY | KEYGEN_FLAG_NO_PROTECTION /*this is ephemeral*/, "", nbits );
+  if( rc )
+    return rc;
+  if( DBG_CIPHER )  {
+       unsigned char *buffer;
+       if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, pk_eph->pkey[1]))
+          BUG ();
+        log_debug("ephemeral key MPI #0: %s\n", buffer);
+       gcry_free( buffer );
+  }
+  free_public_key (pk_eph);
+
+  /*** Done with ephemeral key generation. 
+   * Now use ephemeral secret to get the shared secret. ***/
+
+  rc = gcry_sexp_build (&s_pkey, NULL,
+                   "(public-key(ecdh(c%m)(q%m)(p%m)))", pkey[0], pkey[1], pkey[2]);
+  if (rc)
+    BUG ();
+  /* put the data into a simple list */
+  if (gcry_sexp_build (&s_data, NULL, "%m", pk_eph->pkey[3]))  /* ephemeral scalar goes as data */
+    BUG ();
+
+  /* pass it to libgcrypt */
+  rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
+  gcry_sexp_release (s_data);
+  gcry_sexp_release (s_pkey);
+  if (rc)
+    return rc;
+
+  /* finally, perform encryption */
+
+  {
+    gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a");           /* ... and get the shared point */
+    gcry_sexp_release (s_ciph);
+    resarr[0] = pk_eph->pkey[1];       /* ephemeral public key */
+
+    if( DBG_CIPHER )  {
+       unsigned char *buffer;
+       if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, resarr[0]))
+          BUG ();
+        log_debug("ephemeral key MPI: %s\n", buffer);
+       gcry_free( buffer );
+    }
+
+    rc = pk_ecdh_encrypt_with_shared_point ( 1 /*=encrypton*/, shared, pk_fp, data, pkey, resarr+1 );
+    mpi_release( shared );
+  }
+
+  return rc;
+}
+
+/* Perform ECDH decryption. 
+ */
+int
+pk_ecdh_decrypt (gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t *data, gcry_mpi_t * skey)  {
+  gcry_sexp_t s_skey, s_data, s_ciph;
+  int rc;
+
+  if (!data[0] || !data[1])
+    return gpg_error (GPG_ERR_BAD_MPI);
+
+  rc = gcry_sexp_build (&s_skey, NULL,
+                           "(public-key(ecdh(c%m)(q%m)(p%m)))",
+                           skey[0]/*curve*/, data[0]/*ephemeral key*/, skey[2]/*KDF params*/);
+  if (rc)
+    BUG ();
+
+  /* put the data into a simple list */
+  if (gcry_sexp_build (&s_data, NULL, "%m", skey[3]))  /* static private key (scalar) goes as data */
+    BUG ();
+
+  rc = gcry_pk_encrypt (&s_ciph, s_data, s_skey);      /* encrypting ephemeral key with our private scalar yields the shared point */
+  gcry_sexp_release (s_skey);
+  gcry_sexp_release (s_data);
+  if (rc)
+    return rc;
+
+  {
+    gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a");           /* get the shared point */
+    gcry_sexp_release (s_ciph);
+    rc = pk_ecdh_encrypt_with_shared_point ( 0 /*=decryption*/, shared, sk_fp, data[1]/*encr data as an MPI*/, skey, result );
+    mpi_release( shared );
+  }
+
+  return rc;
+}
+
+
index 55f9b27..3c16309 100644 (file)
@@ -876,7 +876,9 @@ write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out)
   for ( ; pk_list; pk_list = pk_list->next )
     {
       gcry_mpi_t frame;
-      
+      byte fp[MAX_FINGERPRINT_LEN]; 
+      size_t fpn;
+
       pk = pk_list->pk;
       
       print_pubkey_algo_note ( pk->pubkey_algo );
@@ -892,6 +894,9 @@ write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out)
           compliance_failure();
         }
 
+      fingerprint_from_pk( pk, fp, &fpn );
+      assert( fpn == 20 );
+
       /* Okay, what's going on: We have the session key somewhere in
        * the structure DEK and want to encode this session key in an
        * integer value of n bits. pubkey_nbits gives us the number of
@@ -904,9 +909,9 @@ write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out)
        * for Elgamal).  We don't need frame anymore because we have
        * everything now in enc->data which is the passed to
        * build_packet().  */
-      frame = encode_session_key (dek, 
+      frame = encode_session_key (pk->pubkey_algo, dek, 
                                   pubkey_nbits (pk->pubkey_algo, pk->pkey));
-      rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk->pkey);
+      rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, fp, pk->pkey);
       gcry_mpi_release (frame);
       if (rc)
         log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
index 91c6a73..82d9751 100644 (file)
@@ -1161,6 +1161,18 @@ build_sexp_seckey (iobuf_t out, PACKET *pkt, int *indent)
   /*     iobuf_put (out,')'); iobuf_put (out,'\n'); */
   /*     (*indent)--; */
   /*   } */
+/*
+  else if (sk->pubkey_algo == PUBKEY_ALGO_ECDSA && !sk->is_protected)
+    {
+      write_sexp_line (out, indent, "(ecdsa\n");
+      (*indent)++; 
+      write_sexp_keyparm (out, indent, "c", sk->skey[0]); iobuf_put (out,'\n');
+      write_sexp_keyparm (out, indent, "q", sk->skey[6]); iobuf_put (out,'\n');
+      write_sexp_keyparm (out, indent, "d", sk->skey[7]);
+      iobuf_put (out,')'); iobuf_put (out,'\n');
+      (*indent)--;
+    }
+*/
   /* else if (is_ELGAMAL (sk->pubkey_algo) && !sk->is_protected) */
   /*   { */
   /*     write_sexp_line (out, indent, "(elg\n"); */
index f114920..65f5829 100644 (file)
@@ -138,7 +138,10 @@ cache_public_key (PKT_public_key * pk)
     return;
 
   if (is_ELGAMAL (pk->pubkey_algo)
-      || pk->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA (pk->pubkey_algo))
+      || pk->pubkey_algo == PUBKEY_ALGO_DSA
+      || pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+      || pk->pubkey_algo == PUBKEY_ALGO_ECDH
+      || is_RSA (pk->pubkey_algo))
     {
       keyid_from_pk (pk, keyid);
     }
index 4a17b29..23b1934 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -813,7 +813,7 @@ my_strusage( int level )
   const char *p;
 
     switch( level ) {
-      case 11: p = "gpg (GnuPG)";
+      case 11: p = "gpg (GnuPG) ecc";
        break;
       case 13: p = VERSION; break;
       case 17: p = PRINTABLE_OS_NAME; break;
@@ -857,7 +857,7 @@ my_strusage( int level )
       case 34:
        if (!pubkeys)
             pubkeys = build_list (_("Pubkey: "), 0,
-                                  gcry_pk_algo_name,
+                                  openpgp_pk_algo_name,
                                   openpgp_pk_test_algo );
        p = pubkeys;
        break;
index ec7e7e7..f7f1526 100644 (file)
@@ -42,6 +42,8 @@
 #include "i18n.h"
 #include "keyserver-internal.h"
 #include "call-agent.h"
+#include "pkglue.h"
+#include "gcrypt.h"
 
 /* The default algorithms.  If you change them remember to change them
    also in gpg.c:gpgconf_list.  You should also check that the value
 #define DEFAULT_STD_ALGO    GCRY_PK_RSA
 #define DEFAULT_STD_KEYSIZE 2048
 
-#define KEYGEN_FLAG_NO_PROTECTION 1
-#define KEYGEN_FLAG_TRANSIENT_KEY 2
-
-
 #define MAX_PREFS 30 
 
 enum para_name {
@@ -1130,17 +1128,15 @@ key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp,
 }
 
 
-
-/* Common code for the key generation fucntion gen_xxx.  */
 static int
-common_gen (const char *keyparms, int algo, const char *algoelem,
-            kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey,
-            int keygen_flags, char **cache_nonce_addr)
+common_key_gen (const char *keyparms, int algo, const char *algoelem,
+            int keygen_flags, char **cache_nonce_addr, PKT_public_key **pk_out)
 {
   int err;
-  PACKET *pkt;
   PKT_public_key *pk;
   gcry_sexp_t s_key;
+
+  *pk_out = NULL;
   
   err = agent_genkey (NULL, cache_nonce_addr, keyparms,
                       !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), &s_key);
@@ -1158,10 +1154,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem,
       return err;
     }
 
-  pk->timestamp = timestamp;
   pk->version = 4;
-  if (expireval) 
-    pk->expiredate = pk->timestamp + expireval;
   pk->pubkey_algo = algo;
 
   err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem);
@@ -1174,21 +1167,45 @@ common_gen (const char *keyparms, int algo, const char *algoelem,
     }
   gcry_sexp_release (s_key);
   
-  pkt = xtrycalloc (1, sizeof *pkt);
-  if (!pkt)
-    {
-      err = gpg_error_from_syserror ();
-      free_public_key (pk);
-      return err;
-    }
-
-  pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
-  pkt->pkt.public_key = pk;
-  add_kbnode (pub_root, new_kbnode (pkt));
+  *pk_out = pk;
 
   return 0;
 }
 
+/* Common code for the key generation fucntion gen_xxx.  */
+static int
+common_gen (const char *keyparms, int algo, const char *algoelem,
+            kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey,
+            int keygen_flags, char **cache_nonce_addr)
+{
+  PKT_public_key *pk;  
+  int err; 
+
+  err = common_key_gen( keyparms, algo, algoelem, keygen_flags, cache_nonce_addr, &pk );
+
+  if( !err )  {
+    PACKET *pkt;
+
+    pk->timestamp = timestamp;
+    if (expireval) 
+      pk->expiredate = pk->timestamp + expireval;
+
+    pkt = xtrycalloc (1, sizeof *pkt);
+    if (!pkt)
+      {
+        err = gpg_error_from_syserror ();
+        free_public_key (pk);
+        return err;
+      }
+
+    pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
+    pkt->pkt.public_key = pk;
+
+    add_kbnode (pub_root, new_kbnode (pkt));
+  }
+
+  return err;
+}
 
 /*
  * Generate an Elgamal key.
@@ -1326,6 +1343,186 @@ gen_dsa (unsigned int nbits, KBNODE pub_root,
   return err;
 }
 
+/* Returns allocated ECC key generation S-explression 
+   call gcry_sexp_release ( out ) to free it.
+ */
+static int 
+delme__pk_ecc_build_sexp( int qbits, int algo, int is_long_term, gcry_sexp_t *out )  {
+  gcry_mpi_t kek_params;
+  char *kek_params_s;
+  int rc;
+
+  if( is_long_term && algo == PUBKEY_ALGO_ECDH )
+    kek_params = pk_ecdh_default_params_to_mpi( qbits );
+  else
+    kek_params = NULL;
+
+  if( kek_params )  {
+    kek_params_s = mpi2hex( kek_params );
+    mpi_release( kek_params );
+  }
+
+  rc = gcry_sexp_build (out, NULL,
+                       algo == PUBKEY_ALGO_ECDSA ? 
+                        "(genkey(ecdsa(nbits %d)(qbits %d)))" : 
+                        "(genkey(ecdh(nbits %d)(qbits %d)(transient-key %d)(kek-params %s)))",
+                        (int)qbits, (int)qbits, (int)(is_long_term==0), kek_params_s);
+  xfree( kek_params_s );
+  if (rc)  {
+    log_debug("ec gen gcry_sexp_build failed: %s\n", gpg_strerror (rc));
+    return rc;
+  }
+  return 0;
+}
+
+static char * 
+pk_ecc_build_key_params( int qbits, int algo, int transient )  {
+  byte *kek_params = NULL;
+  size_t kek_params_size;
+  char nbitsstr[35];
+  char qbitsstr[35];
+  char *keyparms;
+  int n;
+
+  /* KEK parameters are only needed for long term key generation */
+  if( !transient && algo == PUBKEY_ALGO_ECDH )
+    kek_params = pk_ecdh_default_params( qbits, &kek_params_size );
+  else
+    kek_params = NULL;
+
+  snprintf (nbitsstr, sizeof nbitsstr, "%u", qbits);
+  snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits);
+  if( algo == PUBKEY_ALGO_ECDSA || kek_params == NULL )
+    keyparms = xtryasprintf (
+               "(genkey(%s(nbits %zu:%s)(qbits %zu:%s)(transient-key 1:%d)))", 
+                          algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh",
+                           strlen (nbitsstr), nbitsstr,
+                           strlen (qbitsstr), qbitsstr,
+                          transient );
+  else  {
+    assert( kek_params != NULL );
+    keyparms = xtryasprintf (
+               "(genkey(ecdh(nbits %zu:%s)(qbits %zu:%s)(transient-key 1:%d)(kek-params %u:",
+                           strlen (nbitsstr), nbitsstr,
+                           strlen (qbitsstr), qbitsstr,
+                          transient,
+                          (unsigned)kek_params_size );
+    if( keyparms != NULL )  {
+       n = strlen(keyparms);
+       keyparms = xtryrealloc( keyparms, n + kek_params_size + 4 );
+    }
+    if( keyparms == NULL )  {
+      xfree( kek_params );
+      return NULL;
+    }
+    memcpy( keyparms+n, kek_params, kek_params_size );
+    xfree( kek_params );
+    memcpy( keyparms+n+kek_params_size, ")))", 4 );
+  }
+  return keyparms;
+}
+
+/* This common function is used in this file and also to generate ephemeral keys for ECDH.
+ * Caller must call free_public_key and free_secret_key */
+int
+pk_ecc_keypair_gen( PKT_public_key **pk_out, int algo, int keygen_flags, char **cache_nonce_addr, unsigned nbits)  {
+  int err;
+  unsigned int qbits;
+  char *keyparms;
+  // PUBKEY_ALGO_ECDH, PUBKEY_ALGO_ECDSA
+  static const char * const ec_pub_params[2] =  { "cqp",  "cq" };
+  //static const char * const ec_priv_params[2] = { "cqpd", "cqd" };
+
+  assert( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH );
+  assert( PUBKEY_ALGO_ECDSA == PUBKEY_ALGO_ECDH + 1 );
+
+  *pk_out = NULL;
+
+  if( pubkey_get_npkey (PUBKEY_ALGO_ECDSA) != 2 || pubkey_get_nskey (PUBKEY_ALGO_ECDSA) != 3 ||
+      pubkey_get_npkey (PUBKEY_ALGO_ECDH)  != 3 || pubkey_get_nskey (PUBKEY_ALGO_ECDH)  != 4 )  
+  {
+    log_info(_("incompatible version of gcrypt library (expect named curve logic for ECC)\n") );
+    return GPG_ERR_EPROGMISMATCH;
+  }
+
+  if ( nbits != 256 && nbits != 384 && nbits != 521 ) 
+    {
+      log_info(_("keysize invalid; using 256 bits instead of passed in %d\n"), nbits );
+    }
+
+  /*
+    Figure out a q size based on the key size. See gen_dsa for more details.
+    Due to 8-bit rounding we may get 528 here instead of 521
+  */
+  nbits = qbits = (nbits < 521 ? nbits : 521 );
+
+  keyparms = pk_ecc_build_key_params(qbits, algo, !!((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION)) );
+  if (!keyparms)  {
+    err = gpg_error_from_syserror ();
+      log_error ("ec pk_ecc_build_key_params failed: %s\n", gpg_strerror (err) );
+  }
+  else
+    {
+      err = common_key_gen (keyparms, algo, ec_pub_params[algo-PUBKEY_ALGO_ECDH],
+                        keygen_flags, cache_nonce_addr, pk_out);
+      xfree (keyparms);
+    }
+
+#if 0
+  /* always allocase seckey_info for EC keys. TODO: is this needed? */
+  if( *pk_out )  {
+    struct seckey_info *ski;
+
+    (*pk_out)->seckey_info = ski = xtrycalloc (1, sizeof *ski);
+    if (!(*pk_out)->seckey_info)  {
+      free_public_key(*pk_out);
+      *pk_out = NULL;
+      return gpg_error_from_syserror ();
+    }
+
+    ski->is_protected = 0;
+    ski->algo = 0;
+  }
+#endif
+
+  return err;
+}
+
+
+/****************
+ * Generate an ECC OpenPGP key
+ */
+static gpg_error_t
+gen_ecc (int algo, unsigned int nbits, KBNODE pub_root,
+         u32 timestamp, u32 expireval, int is_subkey, 
+         int keygen_flags, char **cache_nonce_addr)
+{
+  int rc;
+  PACKET *pkt;
+  PKT_public_key *pk;
+
+  rc = pk_ecc_keypair_gen( &pk, algo, keygen_flags, cache_nonce_addr, nbits );
+  if( rc )
+    return rc;
+
+  /* the rest is very similar to common_gen */
+
+  pk->timestamp = timestamp;
+  if (expireval) 
+    pk->expiredate = pk->timestamp + expireval;
+
+  //assert( pk->seckey_info != NULL );
+  /// TODO: the new agent-based model doesn't return private portion here (the pkey array is allocated, but private MPIs are NULL, so this will cause a crash... )
+  ///pk->seckey_info->csum = checksum_mpi ( pk->pkey[algo==PUBKEY_ALGO_ECDSA ? 2 : 3] );       /* corresponds to 'd' in 'cqd' or 'cqpd' */
+
+  pkt = xmalloc_clear(sizeof *pkt);
+  pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
+  pkt->pkt.public_key = pk;
+  add_kbnode(pub_root, new_kbnode( pkt ));
+
+  return 0;
+}
+
 
 /* 
  * Generate an RSA key.
@@ -1557,6 +1754,8 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
       tty_printf (_("   (%d) RSA (set your own capabilities)\n"), 8 );
     }
   
+  tty_printf (_("   (%d) ECDSA and ECDH\n"), 9 );
+  
   for(;;)
     {
       *r_usage = 0;
@@ -1613,6 +1812,12 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
           *r_usage = ask_key_flags (algo, addmode);
           break;
        }
+      else if (algo == 9)
+        {
+          algo = PUBKEY_ALGO_ECDSA;
+          *r_subkey_algo = PUBKEY_ALGO_ECDH;
+          break;
+       }
       else
         tty_printf (_("Invalid selection.\n"));
     }
@@ -1657,13 +1862,20 @@ ask_keysize (int algo, unsigned int primary_keysize)
       max=3072;
       break;
 
+    case PUBKEY_ALGO_ECDSA:
+    case PUBKEY_ALGO_ECDH:
+      min=256;
+      def=256;
+      max=521;
+      break;
+
     case PUBKEY_ALGO_RSA:
       min=1024;
       break;
     }
 
   tty_printf(_("%s keys may be between %u and %u bits long.\n"),
-            gcry_pk_algo_name (algo), min, max);
+            openpgp_pk_algo_name (algo), min, max);
 
   for(;;)
     {
@@ -1682,7 +1894,7 @@ ask_keysize (int algo, unsigned int primary_keysize)
       
       if(nbits<min || nbits>max)
        tty_printf(_("%s keysizes must be in the range %u-%u\n"),
-                  gcry_pk_algo_name (algo), min, max);
+                  openpgp_pk_algo_name (algo), min, max);
       else
        break;
     }
@@ -1692,10 +1904,18 @@ ask_keysize (int algo, unsigned int primary_keysize)
  leave:
   if( algo == PUBKEY_ALGO_DSA && (nbits % 64) )
     {
-      nbits = ((nbits + 63) / 64) * 64;
-      if (!autocomp)
-        tty_printf(_("rounded up to %u bits\n"), nbits );
+      if( !(algo == PUBKEY_ALGO_ECDSA && nbits==521) )  {
+        nbits = ((nbits + 63) / 64) * 64;
+        if (!autocomp)
+          tty_printf(_("rounded up to %u bits\n"), nbits );
+      }
     }
+  else if( algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA )  {
+     if( nbits != 256 && nbits != 384 && nbits != 521 )  {
+        nbits = min;
+        tty_printf(_("unsupported ECDH value, corrected to the minimum %u bits\n"), nbits );
+     }
+  }
   else if( (nbits % 32) )
     {
       nbits = ((nbits + 31) / 32) * 32;
@@ -2185,6 +2405,9 @@ do_create (int algo, unsigned int nbits, KBNODE pub_root,
   else if (algo == PUBKEY_ALGO_DSA)
     err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey,
                    keygen_flags, cache_nonce_addr);
+  else if( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH )
+    err = gen_ecc (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
+                   keygen_flags, cache_nonce_addr);
   else if (algo == PUBKEY_ALGO_RSA)
     err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
                    keygen_flags, cache_nonce_addr);
index 62ce036..2a9bd19 100644 (file)
@@ -57,6 +57,8 @@ pubkey_letter( int algo )
     case PUBKEY_ALGO_ELGAMAL_E: return 'g';
     case PUBKEY_ALGO_ELGAMAL:   return 'G' ;
     case PUBKEY_ALGO_DSA:      return 'D' ;
+    case PUBKEY_ALGO_ECDSA:    return 'E' ;    // ECC DSA (sign only)
+    case PUBKEY_ALGO_ECDH:     return 'e' ;    // ECC DH (encrypt only)
     default: return '?';
     }
 }
@@ -74,6 +76,8 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk)
   unsigned int nbits;
   size_t nbytes;
   int npkey = pubkey_get_npkey (pk->pubkey_algo);
+  /* name OID, MPI of public point, [for ECDH only: KEK params] */
+  enum gcry_mpi_format ecc_pub_format[3] = {GCRYMPI_FMT_USG, GCRYMPI_FMT_PGP, GCRYMPI_FMT_USG};
 
   /* Two extra bytes for the expiration date in v3 */
   if(pk->version<4)
@@ -90,11 +94,13 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk)
     {
       for(i=0; i < npkey; i++ )
         {
-          if (gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, pk->pkey[i]))
+         const enum gcry_mpi_format fmt = 
+            ((pk->pubkey_algo==PUBKEY_ALGO_ECDSA || pk->pubkey_algo==PUBKEY_ALGO_ECDH) ? ecc_pub_format[i] : GCRYMPI_FMT_PGP);
+
+          if (gcry_mpi_print (fmt, NULL, 0, &nbytes, pk->pkey[i]))
             BUG ();
           pp[i] = xmalloc (nbytes);
-          if (gcry_mpi_print (GCRYMPI_FMT_PGP, pp[i], nbytes,
-                              &nbytes, pk->pkey[i]))
+          if (gcry_mpi_print (fmt, pp[i], nbytes, &nbytes, pk->pkey[i]))
             BUG ();
           nn[i] = nbytes;
           n += nn[i];
@@ -712,6 +718,20 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array)
                              pk->pkey[0], pk->pkey[1]);
       break;
 
+    case PUBKEY_ALGO_ECDSA:
+    case PUBKEY_ALGO_ECDH:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(ecc(c%m)(q%m)))",
+                             pk->pkey[0], pk->pkey[1]);
+      break;
+/* 
+   case PUBKEY_ALGO_ECDH:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(ecdh(c%m)(q%m)(p%m)))",
+                             pk->pkey[0], pk->pkey[1], pk->pkey[2]);
+      break;
+*/
+
     default:
       err = gpg_error (GPG_ERR_PUBKEY_ALGO);
       break;
index b673cf5..e336e5c 100644 (file)
@@ -93,11 +93,12 @@ int map_cipher_openpgp_to_gcry (int algo);
 int openpgp_cipher_blocklen (int algo);
 int openpgp_cipher_test_algo( int algo );
 const char *openpgp_cipher_algo_name (int algo);
+int map_pk_openpgp_to_gcry (int algo);
 int openpgp_pk_test_algo( int algo );
 int openpgp_pk_test_algo2 ( int algo, unsigned int use );
 int openpgp_pk_algo_usage ( int algo );
-const char *openpgp_pk_algo_name (int algo);
 int openpgp_md_test_algo( int algo );
+const char *openpgp_pk_algo_name (int algo);
 const char *openpgp_md_algo_name (int algo);
 
 #ifdef USE_IDEA
@@ -157,6 +158,10 @@ int pubkey_get_nsig( int algo );
 int pubkey_get_nenc( int algo );
 unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey );
 int mpi_print (estream_t stream, gcry_mpi_t a, int mode);
+int iobuf_write_size_body_mpi (iobuf_t out, gcry_mpi_t a);
+int iobuf_read_size_body(iobuf_t inp, byte *body, int body_max_size, int pktlen, gcry_mpi_t *out);
+
+int ecdsa_qbits_from_Q( int qbits );
 
 /*-- status.c --*/
 void set_status_fd ( int fd );
@@ -251,6 +256,10 @@ gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock,
 int save_unprotected_key_to_card (PKT_public_key *sk, int keyno);
 #endif
 
+#define KEYGEN_FLAG_NO_PROTECTION 1
+#define KEYGEN_FLAG_TRANSIENT_KEY 2
+int pk_ecc_keypair_gen( PKT_public_key **pk_out, int algo, int keygen_flags, char **cache_nonce_addr, unsigned nbits);
+
 /*-- openfile.c --*/
 int overwrite_filep( const char *fname );
 char *make_outfile_name( const char *iname );
@@ -261,7 +270,7 @@ void try_make_homedir( const char *fname );
 
 /*-- seskey.c --*/
 void make_session_key( DEK *dek );
-gcry_mpi_t encode_session_key( DEK *dek, unsigned nbits );
+gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits );
 gcry_mpi_t encode_md_value (PKT_public_key *pk, 
                             gcry_md_hd_t md, int hash_algo );
 
index 72cefce..dcbc4b4 100644 (file)
@@ -384,6 +384,8 @@ proc_pubkey_enc( CTX c, PACKET *pkt )
     }
     else if( is_ELGAMAL(enc->pubkey_algo)
              || enc->pubkey_algo == PUBKEY_ALGO_DSA
+             || enc->pubkey_algo == PUBKEY_ALGO_ECDSA
+             || enc->pubkey_algo == PUBKEY_ALGO_ECDH
              || is_RSA(enc->pubkey_algo)
              || enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL) {
       /* Note that we also allow type 20 Elgamal keys for decryption.
@@ -450,7 +452,7 @@ print_pkenc_list( struct kidlist_item *list, int failed )
         if ( !failed && list->reason )
             continue;
 
-        algstr = gcry_pk_algo_name ( list->pubkey_algo );
+        algstr = openpgp_pk_algo_name ( list->pubkey_algo );
         pk = xmalloc_clear( sizeof *pk );
 
        if( !algstr )
@@ -1616,7 +1618,7 @@ check_sig_and_print( CTX c, KBNODE node )
 
   /* (Indendation below not yet changed to GNU style.) */
 
-    astr = gcry_pk_algo_name ( sig->pubkey_algo );
+    astr = openpgp_pk_algo_name ( sig->pubkey_algo );
     if(keystrlen()>8)
       {
        log_info(_("Signature made %s\n"),asctimestamp(sig->timestamp));
index 1725258..a09636b 100644 (file)
@@ -64,6 +64,7 @@
 #include "call-agent.h"
 #include "i18n.h"
 
+#include <assert.h>
 
 static int
 string_count_chr (const char *string, int c)
@@ -294,7 +295,7 @@ print_pubkey_algo_note( int algo )
        {
          warn=1;
          log_info (_("WARNING: using experimental public key algorithm %s\n"),
-                   gcry_pk_algo_name (algo));
+                   openpgp_cipher_algo_name (algo));
        }
     }
   else if (algo == 20)
@@ -365,6 +366,12 @@ map_cipher_gcry_to_openpgp (int algo)
     }
 }
 
+int
+map_pk_openpgp_to_gcry (int algo)
+{
+  return (algo==PUBKEY_ALGO_ECDSA ? GCRY_PK_ECDSA : (algo==PUBKEY_ALGO_ECDH ? GCRY_PK_ECDH : algo));
+}
+
 
 /* Return the block length of an OpenPGP cipher algorithm.  */
 int 
@@ -409,7 +416,13 @@ openpgp_cipher_test_algo( int algo )
 const char *
 openpgp_cipher_algo_name (int algo) 
 {
-  return gnupg_cipher_algo_name (map_cipher_openpgp_to_gcry (algo));
+  return gcry_cipher_algo_name (map_cipher_openpgp_to_gcry (algo));
+}
+
+const char *
+openpgp_pk_algo_name (int algo) 
+{
+  return gcry_pk_algo_name ( algo == PUBKEY_ALGO_ECDSA ? GCRY_PK_ECDSA : ( algo == PUBKEY_ALGO_ECDH ? GCRY_PK_ECDH : algo ) );
 }
 
 int
@@ -424,7 +437,13 @@ openpgp_pk_test_algo( int algo )
 
   if (algo < 0 || algo > 110)
     return gpg_error (GPG_ERR_PUBKEY_ALGO);
-  return gcry_pk_test_algo (algo);
+
+  if( algo == PUBKEY_ALGO_ECDSA )
+    algo = GCRY_PK_ECDSA;
+  else if( algo == PUBKEY_ALGO_ECDH )
+    algo = GCRY_PK_ECDH;
+
+  return gcry_pk_test_algo ( algo );
 }
 
 int
@@ -442,7 +461,12 @@ openpgp_pk_test_algo2( int algo, unsigned int use )
   if (algo < 0 || algo > 110)
     return gpg_error (GPG_ERR_PUBKEY_ALGO);
 
-  return gcry_pk_algo_info (algo, GCRYCTL_TEST_ALGO, NULL, &use_buf);
+  if( algo == PUBKEY_ALGO_ECDSA )
+    algo = GCRY_PK_ECDSA;
+  else if( algo == PUBKEY_ALGO_ECDH )
+    algo = GCRY_PK_ECDH;
+
+  return gcry_pk_algo_info ( algo, GCRYCTL_TEST_ALGO, NULL, &use_buf);
 }
 
 int 
@@ -457,6 +481,7 @@ openpgp_pk_algo_usage ( int algo )
                  | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH);
           break;
       case PUBKEY_ALGO_RSA_E:
+      case PUBKEY_ALGO_ECDH:
           use = PUBKEY_USAGE_ENC;
           break;
       case PUBKEY_ALGO_RSA_S:
@@ -472,6 +497,8 @@ openpgp_pk_algo_usage ( int algo )
       case PUBKEY_ALGO_DSA:  
           use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
           break;
+      case PUBKEY_ALGO_ECDSA:
+          use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
       default:
           break;
     }
@@ -480,7 +507,7 @@ openpgp_pk_algo_usage ( int algo )
 
 /* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a
    string representation of the algorithm name.  For unknown algorithm
-   IDs this function returns "?".  */
+   IDs this function returns "?".  
 const char *
 openpgp_pk_algo_name (int algo) 
 {
@@ -498,6 +525,7 @@ openpgp_pk_algo_name (int algo)
     default: return "?";
     }
 }
+*/
 
 
 int
@@ -1348,6 +1376,10 @@ pubkey_get_npkey( int algo )
 
   if (algo == GCRY_PK_ELG_E)
     algo = GCRY_PK_ELG;
+  else if (algo == PUBKEY_ALGO_ECDSA)
+    algo = GCRY_PK_ECDSA;
+  else if (algo == PUBKEY_ALGO_ECDH)
+    algo = GCRY_PK_ECDH;
   if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n))
     n = 0;
   return n;
@@ -1361,6 +1393,10 @@ pubkey_get_nskey( int algo )
 
   if (algo == GCRY_PK_ELG_E)
     algo = GCRY_PK_ELG;
+  else if (algo == PUBKEY_ALGO_ECDSA)
+    algo = GCRY_PK_ECDSA;
+  else if (algo == PUBKEY_ALGO_ECDH)
+    algo = GCRY_PK_ECDH;
   if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &n ))
     n = 0;
   return n;
@@ -1374,6 +1410,10 @@ pubkey_get_nsig( int algo )
 
   if (algo == GCRY_PK_ELG_E)
     algo = GCRY_PK_ELG;
+  else if (algo == PUBKEY_ALGO_ECDSA)
+    algo = GCRY_PK_ECDSA;
+  else if (algo == PUBKEY_ALGO_ECDH)
+    algo = GCRY_PK_ECDH;
   if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &n))
     n = 0;
   return n;
@@ -1387,6 +1427,10 @@ pubkey_get_nenc( int algo )
   
   if (algo == GCRY_PK_ELG_E)
     algo = GCRY_PK_ELG;
+  else if (algo == PUBKEY_ALGO_ECDSA)
+    algo = GCRY_PK_ECDSA;
+  else if (algo == PUBKEY_ALGO_ECDH)
+    algo = GCRY_PK_ECDH;
   if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, &n ))
     n = 0;
   return n;
@@ -1400,6 +1444,8 @@ pubkey_nbits( int algo, gcry_mpi_t *key )
     int rc, nbits;
     gcry_sexp_t sexp;
 
+    assert( algo != GCRY_PK_ECDSA && algo != GCRY_PK_ECDH );
+
     if( algo == GCRY_PK_DSA ) {
        rc = gcry_sexp_build ( &sexp, NULL,
                              "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))",
@@ -1415,6 +1461,11 @@ pubkey_nbits( int algo, gcry_mpi_t *key )
                              "(public-key(rsa(n%m)(e%m)))",
                                  key[0], key[1] );
     }
+    else if( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH ) {
+       rc = gcry_sexp_build ( &sexp, NULL,
+                             "(public-key(ecc(c%m)(q%m)))",
+                                 key[0], key[1] /* not affecting the size calculation, so use 'ecc' == 'ecdsa' */ );
+    }
     else
        return 0;
 
@@ -1455,3 +1506,89 @@ mpi_print (estream_t fp, gcry_mpi_t a, int mode)
   return n;
 }
 
+/*
+ * Write a special size+body mpi a, to OUT. The format of the content of the MPI is
+ * one byte LEN, following by LEN bytes 
+ */
+int
+iobuf_write_size_body_mpi (iobuf_t out, gcry_mpi_t a)
+{
+  byte buffer[256]; /* Fixed buffer for a public parameter, max possible */
+  size_t nbytes = (mpi_get_nbits (a)+7)/8;
+  int rc;
+
+  if( nbytes > sizeof(buffer) )  {
+      log_error("mpi with size+body is too large (%u bytes)\n", nbytes);
+      return gpg_error (GPG_ERR_TOO_LARGE);
+  }
+  
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, sizeof(buffer), &nbytes, a);
+  if( rc )  {
+    log_error("Failed to exported size+body mpi\n");
+    return rc;
+  }
+  if( nbytes < 2 || buffer[0] != nbytes-1 )  {
+    if( nbytes > 2 )
+      log_error("Internal size mismatch in mpi size+body: %02x != %02x (other bytes: %02x %02x ... %02x %02x)\n", 
+       buffer[0], nbytes-1, buffer[1], buffer[2], buffer[nbytes-2], buffer[nbytes-1]);
+    else 
+      log_error("Internal size mismatch in mpi size+body: only %d bytes\n", nbytes );
+    return gpg_error (GPG_ERR_INV_DATA);
+  }
+  return iobuf_write( out, buffer, nbytes );
+}
+
+/*
+ * Read a special size+body from inp into body[body_max_size] and return it in a buffer and as MPI.
+ * On success the number of consumed bytes will body[0]+1. 
+ * The format of the content of the returned MPI is one byte LEN, following by LEN bytes.
+ * Caller is expected to pre-allocate fixed-size 255 byte buffer (or smaller when appropriate).
+ */
+int
+iobuf_read_size_body( iobuf_t inp, byte *body, int body_max_size, int pktlen, gcry_mpi_t *out )  {
+  unsigned n;
+  int rc;
+  gcry_mpi_t result;
+
+  *out = NULL;
+
+  if( (n = iobuf_readbyte(inp)) == -1 ) {
+    return G10ERR_INVALID_PACKET;
+  }
+  if( n >= body_max_size || n < 2)  {
+    log_error("invalid size+body field\n");
+    return G10ERR_INVALID_PACKET;
+  }
+  body[0] = n; 
+  if( (n = iobuf_read(inp, body+1, n)) == -1 ) {
+    log_error("invalid size+body field\n");
+    return G10ERR_INVALID_PACKET;
+  }
+  if( n+1 > pktlen )  {
+    log_error("size+body field is larger than the packet\n");
+    return G10ERR_INVALID_PACKET;
+  }
+  rc = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, body, n+1, NULL);
+  if (rc)
+    log_fatal ("mpi_scan failed: %s\n", gpg_strerror (rc));
+
+  *out = result;
+
+  return rc;
+}
+
+
+/* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point, i.e.  04 <x> <y> */
+int ecdsa_qbits_from_Q( int qbits )  {
+       if( qbits%8>3 )  {
+               log_error(_("ECDSA public key is expected to be in SEC encoding multiple of 8 bits\n"));
+               return 0;
+       }
+       qbits -= qbits%8;
+       qbits /= 2;
+       return qbits;
+}
+
+
+
+
index 3714739..42d680a 100644 (file)
@@ -939,20 +939,40 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
     }
   else
     {
-      for (i = 0; i < ndata; i++)
-       {
-         n = pktlen;
-         k->data[i] = mpi_read (inp, &n, 0);
-         pktlen -= n;
-         if (list_mode)
-           {
-             es_fprintf (listfp, "\tdata: ");
-             mpi_print (listfp, k->data[i], mpi_print_mode);
-             es_putc ('\n', listfp);
-           }
-         if (!k->data[i])
-           rc = gpg_error (GPG_ERR_INV_PACKET);
-       }
+      if( k->pubkey_algo != PUBKEY_ALGO_ECDH )  {
+        for (i = 0; i < ndata; i++)
+         {
+           n = pktlen;
+           k->data[i] = mpi_read (inp, &n, 0);
+           pktlen -= n;
+           if (list_mode)
+             {
+               es_fprintf (listfp, "\tdata: ");
+               mpi_print (listfp, k->data[i], mpi_print_mode);
+               es_putc ('\n', listfp);
+             }
+           if (!k->data[i])
+             rc = gpg_error (GPG_ERR_INV_PACKET);
+         }
+      }
+      else  
+        {
+               byte encr_buf[255];
+               assert( ndata == 2 );
+               n = pktlen; k->data[0] = mpi_read(inp, &n, 0); pktlen -=n;
+               rc = iobuf_read_size_body( inp, encr_buf, sizeof(encr_buf), pktlen, k->data+1 );
+               if( rc )
+                       goto leave;
+               if( list_mode ) {
+                       es_fprintf (listfp, "\tdata: ");
+                       mpi_print(listfp, k->data[0], mpi_print_mode );
+                       es_putc ('\n', listfp);
+                       es_fprintf (listfp, "\tdata: [% 3d bytes] ", encr_buf[0]);
+                       mpi_print(listfp, k->data[1], mpi_print_mode );
+                       es_putc ('\n', listfp);
+               }
+               pktlen -= (encr_buf[0]+1);
+      }
     }
 
  leave:
@@ -1926,20 +1946,61 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
   else
     {
       /* Fill in public key parameters.  */
-      for (i = 0; i < npkey; i++)
-       {
-         n = pktlen;
-         pk->pkey[i] = mpi_read (inp, &n, 0);
-         pktlen -= n;
-         if (list_mode)
-           {
-             es_fprintf (listfp, "\tpkey[%d]: ", i);
-             mpi_print (listfp, pk->pkey[i], mpi_print_mode);
-             es_putc ('\n', listfp);
+      if( algorithm != PUBKEY_ALGO_ECDSA && algorithm != PUBKEY_ALGO_ECDH )  {
+        for (i = 0; i < npkey; i++)
+         {
+           n = pktlen;
+           pk->pkey[i] = mpi_read (inp, &n, 0);
+           pktlen -= n;
+           if (list_mode)
+             {
+               es_fprintf (listfp, "\tpkey[%d]: ", i);
+               mpi_print (listfp, pk->pkey[i], mpi_print_mode);
+               es_putc ('\n', listfp);
+             }
+           if (!pk->pkey[i])
+             err = gpg_error (GPG_ERR_INV_PACKET);
+         }
+      }
+      else  {
+            /* note that the code in this function ignores the errors */
+           byte name_oid[256];
+           err = iobuf_read_size_body( inp, name_oid, sizeof(name_oid), pktlen, pk->pkey+0 );
+           if( err )
+               goto leave;
+           n = name_oid[0];
+           if( list_mode )
+              es_fprintf (listfp,   "\tpkey[0]: curve OID [%d] ...%02x %02x\n", 
+                       n, name_oid[1+n-2], name_oid[1+n-1] );
+           pktlen -= (n+1);
+           /* set item [1], which corresponds to the public key; these two fields are all we need to uniquely define the key */
+           // log_debug("Parsing ecc public key in the public packet, pktlen=%lu\n", pktlen);
+           n = pktlen; pk->pkey[1] = mpi_read( inp, &n, 0 ); pktlen -=n;
+           if( pk->pkey[1]==NULL )
+               err = gpg_error(G10ERR_INVALID_PACKET);
+           else if( list_mode ) {
+               es_fprintf (listfp,   "\tpkey[1]: ");
+               mpi_print(listfp, pk->pkey[1], mpi_print_mode);
+               es_putc ('\n', listfp);
            }
-         if (!pk->pkey[i])
-           err = gpg_error (GPG_ERR_INV_PACKET);
-       }
+           /* One more field for ECDH */
+           if( algorithm == PUBKEY_ALGO_ECDH )  {
+#define kek_params name_oid
+              err = iobuf_read_size_body( inp, kek_params, sizeof(kek_params), pktlen, pk->pkey+2 );
+              if( err )
+                goto leave;
+              n = kek_params[0];
+              if( kek_params[1] != 1 ) {
+                       log_error("invalid ecdh KEK parameters field type in private key: understand type 1, but found 0x%02x\n", kek_params[1]);
+                       err = gpg_error(G10ERR_INVALID_PACKET);
+                       goto leave;
+              }
+              if( list_mode )
+                es_fprintf (listfp,   "\tpkey[2]: KEK params type=01 hash:%d sym-algo:%d\n", kek_params[1+n-2], kek_params[1+n-1] );
+              pktlen -= (n+1);
+#undef kek_params
+           }
+     }
       if (err)
        goto leave;
     }
index 9f1218b..f29fca7 100644 (file)
@@ -323,7 +323,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat,
     { 
       char *uid;
       size_t uidlen;
-      const char *algo_name = gcry_pk_algo_name ( pk->pubkey_algo );
+      const char *algo_name = openpgp_pk_algo_name ( pk->pubkey_algo );
       const char *timestr;
       char *maink;
       
@@ -585,7 +585,7 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo,
 
       if ( !get_pubkey( pk, keyid ) )
         {
-          const char *s = gcry_pk_algo_name ( pk->pubkey_algo );
+          const char *s = openpgp_pk_algo_name ( pk->pubkey_algo );
           
           tty_printf (_("%u-bit %s key, ID %s, created %s"),
                       nbits_from_pk( pk ), s?s:"?", keystr(keyid),
@@ -690,7 +690,7 @@ gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped)
   char *desc;
   const char *prompt;
       
-  algo_name = gcry_pk_algo_name (pk->pubkey_algo);
+  algo_name = openpgp_pk_algo_name (pk->pubkey_algo);
   timestr = strtimestamp (pk->timestamp);
   uid = get_user_id (pk->keyid, &uidlen); 
 
index 14a2753..f785919 100644 (file)
 #include "gpg.h"
 #include "util.h"
 #include "pkglue.h"
+#include "main.h"
 
 
-static gcry_mpi_t
+gcry_mpi_t
 mpi_from_sexp (gcry_sexp_t sexp, const char * item)
 {
   gcry_sexp_t list;
@@ -44,6 +45,70 @@ mpi_from_sexp (gcry_sexp_t sexp, const char * item)
 }
 
 
+/****************
+ * Emulate our old PK interface here - sometime in the future we might
+ * change the internal design to directly fit to libgcrypt.
+ */
+int
+pk_sign (int algo, gcry_mpi_t * data, gcry_mpi_t hash, gcry_mpi_t * skey)
+{
+  gcry_sexp_t s_sig, s_hash, s_skey;
+  int rc;
+  int gcry_pkalgo = map_pk_openpgp_to_gcry( algo );
+
+  /* make a sexp from skey */
+  if (gcry_pkalgo == GCRY_PK_DSA)
+    {
+      rc = gcry_sexp_build (&s_skey, NULL,
+                           "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))",
+                           skey[0], skey[1], skey[2], skey[3], skey[4]);
+    }
+  else if (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == GCRY_PK_RSA_S)
+    {
+      rc = gcry_sexp_build (&s_skey, NULL,
+                           "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
+                           skey[0], skey[1], skey[2], skey[3], skey[4],
+                           skey[5]);
+    }
+  else if (gcry_pkalgo == GCRY_PK_ELG || gcry_pkalgo == GCRY_PK_ELG_E)
+    {
+      rc = gcry_sexp_build (&s_skey, NULL,
+                           "(private-key(elg(p%m)(g%m)(y%m)(x%m)))",
+                           skey[0], skey[1], skey[2], skey[3]);
+    }
+  else if (gcry_pkalgo == GCRY_PK_ECDSA)
+    {
+      rc = gcry_sexp_build (&s_skey, NULL,
+                           "(private-key(ecdsa(c%m)(q%m)(d%m)))",
+                           skey[0], skey[1], skey[2] );
+    }
+  else
+    return GPG_ERR_PUBKEY_ALGO;
+
+  if (rc)
+    BUG ();
+
+  /* put hash into a S-Exp s_hash */
+  if (gcry_sexp_build (&s_hash, NULL, "%m", hash))
+    BUG ();
+
+  rc = gcry_pk_sign (&s_sig, s_hash, s_skey);
+  gcry_sexp_release (s_hash);
+  gcry_sexp_release (s_skey);
+
+  if (rc)
+    ;
+  else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_S)
+    data[0] = mpi_from_sexp (s_sig, "s");
+  else
+    {
+      data[0] = mpi_from_sexp (s_sig, "r");
+      data[1] = mpi_from_sexp (s_sig, "s");
+    }
+
+  gcry_sexp_release (s_sig);
+  return rc;
+}
 
 /****************
  * Emulate our old PK interface here - sometime in the future we might
@@ -54,25 +119,31 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey)
 {
   gcry_sexp_t s_sig, s_hash, s_pkey;
   int rc;
+  const int gcry_pkalgo = map_pk_openpgp_to_gcry( algo );
 
   /* make a sexp from pkey */
-  if (algo == GCRY_PK_DSA)
+  if (gcry_pkalgo == GCRY_PK_DSA)
     {
       rc = gcry_sexp_build (&s_pkey, NULL,
                            "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))",
                            pkey[0], pkey[1], pkey[2], pkey[3]);
     }
-  else if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E)
+  else if (gcry_pkalgo == GCRY_PK_ELG || gcry_pkalgo == GCRY_PK_ELG_E)
     {
       rc = gcry_sexp_build (&s_pkey, NULL,
                            "(public-key(elg(p%m)(g%m)(y%m)))",
                            pkey[0], pkey[1], pkey[2]);
     }
-  else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_S)
+  else if (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == GCRY_PK_RSA_S)
     {
       rc = gcry_sexp_build (&s_pkey, NULL,
                            "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]);
     }
+  else if (gcry_pkalgo == GCRY_PK_ECDSA)       /* same as GCRY_PK_ECDH */
+    {
+      rc = gcry_sexp_build (&s_pkey, NULL,
+                           "(public-key(ecdsa(c%m)(q%m)))", pkey[0], pkey[1]);
+    }
   else
     return GPG_ERR_PUBKEY_ALGO;
 
@@ -85,7 +156,7 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey)
 
   /* Put data into a S-Exp s_sig. */
   s_sig = NULL;
-  if (algo == GCRY_PK_DSA)
+  if (gcry_pkalgo == GCRY_PK_DSA)
     {
       if (!data[0] || !data[1])
         rc = gpg_error (GPG_ERR_BAD_MPI);
@@ -93,7 +164,15 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey)
         rc = gcry_sexp_build (&s_sig, NULL,
                               "(sig-val(dsa(r%m)(s%m)))", data[0], data[1]);
     }
-  else if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E)
+  else if (gcry_pkalgo == GCRY_PK_ECDSA)
+    {
+      if (!data[0] || !data[1])
+        rc = gpg_error (GPG_ERR_BAD_MPI);
+      else
+        rc = gcry_sexp_build (&s_sig, NULL,
+                              "(sig-val(ecdsa(r%m)(s%m)))", data[0], data[1]);
+    }
+  else if (gcry_pkalgo == GCRY_PK_ELG || gcry_pkalgo == GCRY_PK_ELG_E)
     {
       if (!data[0] || !data[1])
         rc = gpg_error (GPG_ERR_BAD_MPI);
@@ -101,7 +180,7 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey)
         rc = gcry_sexp_build (&s_sig, NULL,
                               "(sig-val(elg(r%m)(s%m)))", data[0], data[1]);
     }
-  else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_S)
+  else if (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == GCRY_PK_RSA_S)
     {
       if (!data[0])
         rc = gpg_error (GPG_ERR_BAD_MPI);
@@ -128,7 +207,7 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey)
  * change the internal design to directly fit to libgcrypt.
  */
 int
-pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey)
+pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t * pkey)
 {
   gcry_sexp_t s_ciph, s_data, s_pkey;
   int rc;
@@ -146,6 +225,10 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey)
                            "(public-key(rsa(n%m)(e%m)))",
                            pkey[0], pkey[1]);
     }
+  else if (algo == PUBKEY_ALGO_ECDH)   
+    {
+      return pk_ecdh_encrypt( resarr, pk_fp, data, pkey );
+    }
   else
     return GPG_ERR_PUBKEY_ALGO;
 
@@ -166,7 +249,7 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey)
   else
     { /* add better error handling or make gnupg use S-Exp directly */
       resarr[0] = mpi_from_sexp (s_ciph, "a");
-      if (algo != GCRY_PK_RSA && algo != GCRY_PK_RSA_E)
+      if (algo != GCRY_PK_RSA && algo != GCRY_PK_RSA_E && algo != PUBKEY_ALGO_ECDH)
         resarr[1] = mpi_from_sexp (s_ciph, "b");
     }
 
@@ -181,7 +264,7 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey)
  * change the internal design to directly fit to libgcrypt.
  */
 int
-pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data,
+pk_decrypt (int algo, gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t * data,
            gcry_mpi_t * skey)
 {
   gcry_sexp_t s_skey, s_data, s_plain;
@@ -202,6 +285,9 @@ pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data,
                            skey[0], skey[1], skey[2], skey[3], skey[4],
                            skey[5]);
     }
+  else if( algo == PUBKEY_ALGO_ECDH )  {
+      return pk_ecdh_decrypt( result, sk_fp, data, skey );
+  }
   else
     return GPG_ERR_PUBKEY_ALGO;
 
@@ -244,3 +330,48 @@ pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data,
   return 0;
 }
 
+
+/* Check whether SKEY is a suitable secret key. */
+int
+pk_check_secret_key (int algo, gcry_mpi_t *skey)
+{
+  gcry_sexp_t s_skey;
+  int rc;
+  const int gcry_pkalgo = map_pk_openpgp_to_gcry( algo );
+
+  if (gcry_pkalgo == GCRY_PK_DSA)
+    {
+      rc = gcry_sexp_build (&s_skey, NULL,
+                           "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))",
+                           skey[0], skey[1], skey[2], skey[3], skey[4]);
+    }
+  else if (gcry_pkalgo == GCRY_PK_ELG || gcry_pkalgo == GCRY_PK_ELG_E)
+    {
+      rc = gcry_sexp_build (&s_skey, NULL,
+                           "(private-key(elg(p%m)(g%m)(y%m)(x%m)))",
+                           skey[0], skey[1], skey[2], skey[3]);
+    }
+  else if (gcry_pkalgo == GCRY_PK_RSA
+           || gcry_pkalgo == GCRY_PK_RSA_S || gcry_pkalgo == GCRY_PK_RSA_E)
+    {
+      rc = gcry_sexp_build (&s_skey, NULL,
+                           "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
+                           skey[0], skey[1], skey[2], skey[3], skey[4],
+                           skey[5]);
+    }
+  else if (gcry_pkalgo == GCRY_PK_ECDSA || gcry_pkalgo == GCRY_PK_ECDH)
+    {
+      rc = gcry_sexp_build (&s_skey, NULL,
+                           "(private-key(ecdsa(c%m)(q%m)(d%m)))",
+                           skey[0], skey[1], skey[2] );
+    }
+  else
+    return GPG_ERR_PUBKEY_ALGO;
+
+  if (!rc)
+    {
+      rc = gcry_pk_testkey (s_skey);
+      gcry_sexp_release (s_skey);
+    }
+  return rc;
+}
index f97def1..0d51948 100644 (file)
 #ifndef GNUPG_G10_PKGLUE_H
 #define GNUPG_G10_PKGLUE_H
 
+gcry_mpi_t mpi_from_sexp (gcry_sexp_t sexp, const char * item);
+
+int pk_sign (int algo, gcry_mpi_t *data, gcry_mpi_t hash,
+             gcry_mpi_t *skey);
 int pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data,
                gcry_mpi_t *pkey);
 int pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data,
+               const byte fp[MAX_FINGERPRINT_LEN],
                 gcry_mpi_t *pkey);
-int pk_decrypt (int algo, gcry_mpi_t *result, gcry_mpi_t *data,
+int pk_decrypt (int algo, gcry_mpi_t *result, const byte fp[MAX_FINGERPRINT_LEN], gcry_mpi_t *data,
                 gcry_mpi_t *skey);
 int pk_check_secret_key (int algo, gcry_mpi_t *skey);
 
+int pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t  data, gcry_mpi_t * pkey);
+int pk_ecdh_decrypt (gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t *data, gcry_mpi_t * skey);
+
+gcry_mpi_t pk_ecdh_default_params_to_mpi( int qbits );
+byte *pk_ecdh_default_params( int qbits, size_t *sizeout );
 
 #endif /*GNUPG_G10_PKGLUE_H*/
index 312b591..a5224e2 100644 (file)
@@ -145,6 +145,8 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
   gcry_sexp_t s_data;
   char *desc;
   char *keygrip;
+  byte fp[MAX_FINGERPRINT_LEN]; 
+  size_t fpn;
 
   /* Get the keygrip.  */
   err = hexkeygrip_from_pk (sk, &keygrip);
@@ -174,9 +176,12 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
   if (err)
     goto leave;
 
+  fingerprint_from_pk( sk, fp, &fpn );
+  assert( fpn == 20 );
+
   /* Decrypt. */
   desc = gpg_format_keydesc (sk, 0, 1);
-  err = agent_pkdecrypt (NULL, keygrip, desc, s_data, &frame, &nframe);
+  err = agent_pkdecrypt (NULL, keygrip, desc, s_data, fp, &frame, &nframe);
   xfree (desc);
   gcry_sexp_release (s_data);
   if (err)
@@ -202,28 +207,41 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
   if (DBG_CIPHER)
     log_printhex ("DEK frame:", frame, nframe);
   n = 0;
-  if (!card)
-    {
-      if (n + 7 > nframe)
-        {
-          err = gpg_error (G10ERR_WRONG_SECKEY);
-          goto leave;
-        }
-      if (frame[n] == 1 && frame[nframe - 1] == 2)
-        {
-          log_info (_("old encoding of the DEK is not supported\n"));
-          err = gpg_error (G10ERR_CIPHER_ALGO);
-          goto leave;
-        }
-      if (frame[n] != 2) /* Something went wrong.  */
-        {
-          err = gpg_error (G10ERR_WRONG_SECKEY);
-          goto leave;
-        }
-      for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes.  */
-        ;
-      n++; /* Skip the zero byte.  */
+
+  if( sk->pubkey_algo != PUBKEY_ALGO_ECDH )  {
+    if (!card)
+      {
+        if (n + 7 > nframe)
+          {
+            err = gpg_error (G10ERR_WRONG_SECKEY);
+            goto leave;
+          }
+        if (frame[n] == 1 && frame[nframe - 1] == 2)
+          {
+            log_info (_("old encoding of the DEK is not supported\n"));
+            err = gpg_error (G10ERR_CIPHER_ALGO);
+            goto leave;
+          }
+        if (frame[n] != 2) /* Something went wrong.  */
+          {
+            err = gpg_error (G10ERR_WRONG_SECKEY);
+            goto leave;
+          }
+        for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes.  */
+          ;
+        n++; /* Skip the zero byte.  */
+      }
+  }
+  else  {
+    /* Allow double padding for the benefit of DEK size concealment.
+     * Higher than this is wasteful.  
+     */
+    if( frame[nframe-1] > 8*2 || nframe <= 8 )  {
+      err = G10ERR_WRONG_SECKEY; goto leave; 
     }
+    nframe -= frame[nframe-1]; /* remove padding */
+    assert( n==0 );    /* used just bellow */
+  }
 
   if (n + 4 > nframe)
     {
index ee5584c..4cc9158 100644 (file)
@@ -27,6 +27,7 @@
 #include "gpg.h"
 #include "util.h"
 #include "cipher.h"
+#include "options.h"
 #include "main.h"
 #include "i18n.h"
 
@@ -73,15 +74,48 @@ make_session_key( DEK *dek )
  * returns: A mpi with the session key (caller must free)
  */
 gcry_mpi_t
-encode_session_key (DEK *dek, unsigned int nbits)
+encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits)
 {
     size_t nframe = (nbits+7) / 8;
     byte *p;
     byte *frame;
     int i,n;
-    u16 csum;
+    u16 csum = 0;
     gcry_mpi_t a;
 
+    if( DBG_CIPHER )
+       log_debug("encode_session_key: encoding %d byte DEK", dek->keylen);
+
+    for( p = dek->key, i=0; i < dek->keylen; i++ )
+       csum += *p++;
+
+    /* Shortcut for ECDH. It's padding is minimal to simply make the output be a multiple of 8 bytes. */
+    if( openpgp_pk_algo == PUBKEY_ALGO_ECDH )  {
+       /* pad to 8 byte granulatiry; the padding byte is the number of padded bytes.
+        * A  DEK(k bytes)  CSUM(2 bytes) 0x 0x 0x 0x ... 0x
+        *                                +---- x times ---+
+        */
+       nframe = ( 1 + dek->keylen + 2 /* the value so far is always odd */ + 7 ) & (~7);
+       assert( !(nframe%8) && nframe > 1 + dek->keylen + 2 );  /* alg+key+csum fit and the size is congruent to 8 */
+       frame = xmalloc_secure( nframe );
+       n = 0;
+       frame[n++] = dek->algo;
+       memcpy( frame+n, dek->key, dek->keylen ); n += dek->keylen;
+       frame[n++] = csum >>8;
+       frame[n++] = csum;
+       i = nframe - n;         /* number padded bytes */
+       memset( frame+n, i, i );/* use it as the value of each padded byte */
+       assert( n+i == nframe );
+
+       if( DBG_CIPHER )
+               log_debug("encode_session_key: [%d] %02x  %02x %02x ...  %02x %02x %02x", nframe, frame[0],frame[1],frame[2], frame[nframe-3],frame[nframe-2],frame[nframe-1]);
+
+       if (gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, nframe, &nframe))
+               BUG();
+       xfree(frame);
+       return a;
+    }
+
     /* The current limitation is that we can only use a session key
      * whose length is a multiple of BITS_PER_MPI_LIMB
      * I think we can live with that.
@@ -103,9 +137,6 @@ encode_session_key (DEK *dek, unsigned int nbits)
      *    cipher algorithm (20 is used with blowfish160).
      * CSUM is the 16 bit checksum over the DEK
      */
-    csum = 0;
-    for( p = dek->key, i=0; i < dek->keylen; i++ )
-       csum += *p++;
 
     frame = xmalloc_secure( nframe );
     n = 0;
@@ -161,8 +192,8 @@ do_encode_md( gcry_md_hd_t md, int algo, size_t len, unsigned nbits,
     gcry_mpi_t a;
 
     if( len + asnlen + 4  > nframe )
-       log_bug("can't encode a %d bit MD into a %d bits frame\n",
-                   (int)(len*8), (int)nbits);
+       log_bug("can't encode a %d bit MD into a %d bits frame, algo=%d\n",
+                   (int)(len*8), (int)nbits, algo);
 
     /* We encode the MD in this way:
      *
@@ -209,16 +240,23 @@ gcry_mpi_t
 encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo)
 {
   gcry_mpi_t frame;
+  int gcry_pkalgo;
 
   assert (hash_algo);
   assert (pk);
 
-  if (pk->pubkey_algo == GCRY_PK_DSA)
+  gcry_pkalgo = map_pk_openpgp_to_gcry( pk->pubkey_algo );
+
+  if (gcry_pkalgo == GCRY_PK_DSA || gcry_pkalgo == GCRY_PK_ECDSA )
     {
       /* It's a DSA signature, so find out the size of q. */
 
       size_t qbytes = gcry_mpi_get_nbits (pk->pkey[1]);
 
+      /* pkey[1] is Q for ECDSA, which is an uncompressed point, i.e.  04 <x> <y> */
+      if( gcry_pkalgo==GCRY_PK_ECDSA )
+         qbytes = ecdsa_qbits_from_Q( qbytes );
+
       /* Make sure it is a multiple of 8 bits. */
 
       if(qbytes%8)
@@ -236,7 +274,8 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo)
         DSA. ;) */
       if (qbytes < 160)
        {
-         log_error (_("DSA key %s uses an unsafe (%zu bit) hash\n"),
+         log_error (_("%s key %s uses an unsafe (%zu bit) hash\n"),
+                       gcry_pk_algo_name( gcry_pkalgo ),
                      keystr_from_pk (pk), qbytes);
          return NULL;
        }
@@ -245,10 +284,16 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo)
 
       /* Check if we're too short.  Too long is safe as we'll
         automatically left-truncate. */
-      if (gcry_md_get_algo_dlen (hash_algo) < qbytes)
+      /* This checks would require the use of SHA512 with ECDSA 512. I think this is overkill to fail in this case.
+       * Therefore, relax the check, but only for ECDSA keys. We may need to adjust it later for general case.
+       * ( Note that the check will never pass for ECDSA 521 anyway as the only hash that intended to match it is SHA 512, but 512 < 521 ).
+       */
+      //if (gcry_md_get_algo_dlen (hash_algo) < qbytes )
+      if (gcry_md_get_algo_dlen (hash_algo) < ((gcry_pkalgo==GCRY_PK_ECDSA && qbytes>(521)/8) ? 512/8 : qbytes) )
        {
-         log_error (_("DSA key %s requires a %zu bit or larger hash\n"),
-                     keystr_from_pk(pk), qbytes*8);
+         log_error (_("%s key %s requires a %zu bit or larger hash, used hash-algo=%d\n"),
+                       gcry_pk_algo_name( gcry_pkalgo ),
+                     keystr_from_pk(pk), qbytes*8, hash_algo);
          return NULL;
        }
 
index 5c00424..ccf7964 100644 (file)
@@ -227,21 +227,6 @@ hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig)
     }
 }
 
-
-static gcry_mpi_t
-mpi_from_sexp (gcry_sexp_t sexp, const char * item)
-{
-  gcry_sexp_t list;
-  gcry_mpi_t data;
-  
-  list = gcry_sexp_find_token (sexp, item, 0);
-  assert (list);
-  data = gcry_sexp_nth_mpi (list, 1, 0);
-  assert (data);
-  gcry_sexp_release (list);
-  return data;
-}
-
 /* Perform the sign operation.  If CACHE_NONCE is given the agent is
    advised to use that cached passphrase fro the key.  */
 static int
@@ -418,7 +403,7 @@ match_dsa_hash (unsigned int qbytes)
   if (qbytes <= 48)
     return DIGEST_ALGO_SHA384;
 
-  if (qbytes <= 64)
+  if (qbytes <= 66 )   /* 66 corresponds to 521 (64 to 512) */
     return DIGEST_ALGO_SHA512;
 
   return DEFAULT_DIGEST_ALGO;
@@ -451,9 +436,13 @@ hash_for (PKT_public_key *pk)
     {
       return recipient_digest_algo;
     }
-  else if (pk->pubkey_algo == PUBKEY_ALGO_DSA)
+  else if(pk->pubkey_algo==PUBKEY_ALGO_DSA || pk->pubkey_algo==PUBKEY_ALGO_ECDSA )
     {
-      unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]) / 8;
+      unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]);
+
+      if( pk->pubkey_algo==PUBKEY_ALGO_ECDSA )
+        qbytes = ecdsa_qbits_from_Q(qbytes);
+      qbytes = qbytes/8;
 
       /* It's a DSA key, so find a hash that is the same size as q or
         larger.  If q is 160, assume it is an old DSA key and use a
@@ -935,10 +924,13 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
 
            for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
              {
-               if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA)
+               if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA )
                  {
-                   int temp_hashlen = gcry_mpi_get_nbits
-                      (sk_rover->pk->pkey[1])+7/8;
+                   int temp_hashlen = gcry_mpi_get_nbits(sk_rover->pk->pkey[1]);
+
+                   if( sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA )
+                     temp_hashlen = ecdsa_qbits_from_Q( temp_hashlen );
+                   temp_hashlen = (temp_hashlen+7)/8;
 
                    /* Pick a hash that is large enough for our
                       largest q */
@@ -1494,7 +1486,9 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
                && pk->version<4 && sigversion<4)
          digest_algo = DIGEST_ALGO_MD5;
        else if(pksk->pubkey_algo==PUBKEY_ALGO_DSA)
-         digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8);
+         digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8 );
+        else if(pksk->pubkey_algo==PUBKEY_ALGO_ECDSA )
+         digest_algo = match_dsa_hash (ecdsa_qbits_from_Q( gcry_mpi_get_nbits (pksk->pkey[1]) ) / 8);
        else
          digest_algo = DIGEST_ALGO_SHA1;
       }
diff --git a/g10/verify-stubs.c b/g10/verify-stubs.c
new file mode 100644 (file)
index 0000000..d1f0aa1
--- /dev/null
@@ -0,0 +1,30 @@
+/* To satisfy the linker for the gpgv target 
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006,
+ *               2007 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include "gpg.h"
+#include "main.h"
+
+int
+pk_ecc_keypair_gen( PKT_public_key **pk_out, int algo, int keygen_flags, char **cache_nonce_addr, unsigned nbits)  {
+       return GPG_ERR_NOT_IMPLEMENTED;
+}
index ef0c572..1ea7f32 100644 (file)
@@ -176,5 +176,7 @@ next_tuple (tupledesc_t tupledesc, unsigned int *r_tag, size_t *r_length)
     }
   
   return NULL;
-}            
+} 
+
+    
 
index ef718d6..528ce16 100644 (file)
@@ -38,7 +38,7 @@ const void *find_tuple (tupledesc_t tupledesc,
                         unsigned int tag, size_t *r_length);
 const void *next_tuple (tupledesc_t tupledesc, 
                         unsigned int *r_tag, size_t *r_length);
-
+char *mpi2hex( gcry_mpi_t m );
 
 
 #endif /*G13_UTILS_H*/
index 8e19828..65cd59e 100644 (file)
@@ -56,6 +56,8 @@
 #define PUBKEY_ALGO_RSA_S        /*  3 */ GCRY_PK_RSA_S /* RSA sign only.    */
 #define PUBKEY_ALGO_ELGAMAL_E    /* 16 */ GCRY_PK_ELG_E /* Elgamal encr only */
 #define PUBKEY_ALGO_DSA          /* 17 */ GCRY_PK_DSA                          
+#define PUBKEY_ALGO_ECDH         18    /* corresponds to GCRY_PK_ECDH   ECC DH; encrypt only */
+#define PUBKEY_ALGO_ECDSA        19    /* corresponds to GCRY_PK_ECDSA  ECC DSA; sign only */
 #define PUBKEY_ALGO_ELGAMAL      /* 20 */ GCRY_PK_ELG   /* Elgamal encr+sign */
 
 #define PUBKEY_USAGE_SIG     GCRY_PK_USAGE_SIGN  /* Good for signatures. */
index 0968cf8..6c9410e 100644 (file)
@@ -186,7 +186,7 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
 }
 
 
-/* Parse a key packet and store the ionformation in KI. */
+/* Parse a key packet and store the information in KI. */
 static gpg_error_t
 parse_key (const unsigned char *data, size_t datalen,
            struct _keybox_openpgp_key_info *ki)
@@ -243,6 +243,11 @@ parse_key (const unsigned char *data, size_t datalen,
     case 17: /* DSA */
       npkey = 4;
       break;
+    case 18: /* ECDH */
+      npkey = 3;
+    case 19: /* ECDSA */
+      npkey = 2;
+      break;
     default: /* Unknown algorithm. */
       return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
     }