Implement /run/user/UID/gnupg based sockets.
[gnupg.git] / agent / cvt-openpgp.c
index 4c34130..40d9a3e 100644 (file)
@@ -1,6 +1,5 @@
 /* cvt-openpgp.c - Convert an OpenPGP key to our internal format.
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009,
- *               2010 Free Software Foundation, Inc.
+ * Copyright (C) 1998-2002, 2006, 2009, 2010 Free Software Foundation, Inc.
  * Copyright (C) 2013, 2014 Werner Koch
  *
  * This file is part of GnuPG.
@@ -28,6 +27,7 @@
 #include "agent.h"
 #include "i18n.h"
 #include "cvt-openpgp.h"
+#include "host2net.h"
 
 
 /* Helper to pass data via the callback to do_unprotect. */
@@ -36,6 +36,7 @@ struct try_do_unprotect_arg_s
   int  is_v4;
   int  is_protected;
   int  pubkey_algo;
+  const char *curve;
   int  protect_algo;
   char *iv;
   int  ivlen;
@@ -54,7 +55,8 @@ struct try_do_unprotect_arg_s
 
 /* Compute the keygrip from the public key and store it at GRIP.  */
 static gpg_error_t
-get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
+get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey,
+             unsigned char *grip)
 {
   gpg_error_t err;
   gcry_sexp_t s_pkey = NULL;
@@ -68,25 +70,32 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
       break;
 
     case GCRY_PK_ELG:
-    case GCRY_PK_ELG_E:
       err = gcry_sexp_build (&s_pkey, NULL,
                              "(public-key(elg(p%m)(g%m)(y%m)))",
                              pkey[0], pkey[1], pkey[2]);
       break;
 
     case GCRY_PK_RSA:
-    case GCRY_PK_RSA_E:
-    case GCRY_PK_RSA_S:
       err = gcry_sexp_build (&s_pkey, NULL,
                              "(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(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))",
-                             pkey[0], pkey[1], pkey[2], pkey[3], pkey[4],
-                             pkey[5]);
+    case GCRY_PK_ECC:
+      if (!curve)
+        err = gpg_error (GPG_ERR_BAD_SECKEY);
+      else
+        {
+          const char *format;
+
+          if (!strcmp (curve, "Ed25519"))
+            format = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))";
+          else if (!strcmp (curve, "Curve25519"))
+            format = "(public-key(ecc(curve %s)(flags djb-tweak)(q%m)))";
+          else
+            format = "(public-key(ecc(curve %s)(q%m)))";
+
+          err = gcry_sexp_build (&s_pkey, NULL, format, curve, pkey[0]);
+        }
       break;
 
     default:
@@ -106,7 +115,8 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip)
    parameters into our s-expression based format.  Note that
    PUBKEY_ALGO has an gcrypt algorithm number. */
 static gpg_error_t
-convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
+convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
+                    const char *curve)
 {
   gpg_error_t err;
   gcry_sexp_t s_skey = NULL;
@@ -138,16 +148,24 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
                              skey[5]);
       break;
 
-    case GCRY_PK_ECDSA:
-    case GCRY_PK_ECDH:
-      /* Although our code would work with "ecc" we explicitly use
-         "ecdh" or "ecdsa" to implicitly set the key capabilities.  */
-      err = gcry_sexp_build (&s_skey, NULL,
-                             "(private-key(%s(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)"
-                             "(d%m)))",
-                             pubkey_algo == GCRY_PK_ECDSA?"ecdsa":"ecdh",
-                             skey[0], skey[1], skey[2], skey[3], skey[4],
-                             skey[5], skey[6]);
+    case GCRY_PK_ECC:
+      if (!curve)
+        err = gpg_error (GPG_ERR_BAD_SECKEY);
+      else
+        {
+          const char *format;
+
+          if (!strcmp (curve, "Ed25519"))
+            /* Do not store the OID as name but the real name and the
+               EdDSA flag.  */
+            format = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))";
+          else if (!strcmp (curve, "Curve25519"))
+            format = "(private-key(ecc(curve %s)(flags djb-tweak)(q%m)(d%m)))";
+          else
+            format = "(private-key(ecc(curve %s)(q%m)(d%m)))";
+
+          err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], skey[1]);
+        }
       break;
 
     default:
@@ -168,7 +186,7 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
    mode.  Note that PUBKEY_ALGO has an gcrypt algorithm number. */
 static gpg_error_t
 convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
-                      gcry_sexp_t transfer_key)
+                      const char *curve, gcry_sexp_t transfer_key)
 {
   gpg_error_t err;
   gcry_sexp_t s_skey = NULL;
@@ -186,7 +204,6 @@ convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
       break;
 
     case GCRY_PK_ELG:
-    case GCRY_PK_ELG_E:
       err = gcry_sexp_build
         (&s_skey, NULL,
          "(protected-private-key(elg(p%m)(g%m)(y%m)"
@@ -196,25 +213,34 @@ convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
 
 
     case GCRY_PK_RSA:
-    case GCRY_PK_RSA_E:
-    case GCRY_PK_RSA_S:
       err = gcry_sexp_build
         (&s_skey, NULL,
-         "(protected-private-key(rsa(n%m)(e%m)",
+         "(protected-private-key(rsa(n%m)(e%m)"
          "(protected openpgp-native%S)))",
          skey[0], skey[1], transfer_key );
       break;
 
-    case GCRY_PK_ECDSA:
-    case GCRY_PK_ECDH:
-      /* Although our code would work with "ecc" we explicitly use
-         "ecdh" or "ecdsa" to implicitly set the key capabilities.  */
-      err = gcry_sexp_build
-        (&s_skey, NULL,
-         "(protected-private-key(%s(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)"
-         "(protected openpgp-native%S)))",
-         pubkey_algo == GCRY_PK_ECDSA?"ecdsa":"ecdh",
-         skey[0], skey[1], skey[2], skey[3], skey[4], skey[5], transfer_key);
+    case GCRY_PK_ECC:
+      if (!curve)
+        err = gpg_error (GPG_ERR_BAD_SECKEY);
+      else
+        {
+          const char *format;
+
+          if (!strcmp (curve, "Ed25519"))
+            /* Do not store the OID as name but the real name and the
+               EdDSA flag.  */
+            format = "(protected-private-key(ecc(curve %s)(flags eddsa)(q%m)"
+              "(protected openpgp-native%S)))";
+          else if (!strcmp (curve, "Curve25519"))
+            format = "(protected-private-key(ecc(curve %s)(flags djb-tweak)(q%m)"
+              "(protected openpgp-native%S)))";
+          else
+            format = "(protected-private-key(ecc(curve %s)(q%m)"
+              "(protected openpgp-native%S)))";
+
+          err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], transfer_key);
+        }
       break;
 
     default:
@@ -269,6 +295,22 @@ checksum (const unsigned char *p, unsigned int n)
 }
 
 
+/* Return the number of expected key parameters.  */
+static void
+get_npkey_nskey (int pubkey_algo, size_t *npkey, size_t *nskey)
+{
+  switch (pubkey_algo)
+    {
+    case GCRY_PK_RSA:   *npkey = 2; *nskey = 6; break;
+    case GCRY_PK_ELG:   *npkey = 3; *nskey = 4; break;
+    case GCRY_PK_ELG_E: *npkey = 3; *nskey = 4; break;
+    case GCRY_PK_DSA:   *npkey = 4; *nskey = 5; break;
+    case GCRY_PK_ECC:   *npkey = 1; *nskey = 2; break;
+    default:            *npkey = 0; *nskey = 0; break;
+    }
+}
+
+
 /* Helper for do_unprotect.  PUBKEY_ALOGO is the gcrypt algo number.
    On success R_NPKEY and R_NSKEY receive the number or parameters for
    the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of
@@ -279,7 +321,6 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
                    unsigned int *r_npkey, unsigned int *r_nskey,
                    unsigned int *r_skeylen)
 {
-  gpg_error_t err;
   size_t npkey, nskey, skeylen;
   int i;
 
@@ -308,12 +349,8 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
   /* Get properties of the public key algorithm and do some
      consistency checks.  Note that we need at least NPKEY+1 elements
      in the SKEY array. */
-  if ( (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY,
-                                 NULL, &npkey))
-       || (err = gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY,
-                                    NULL, &nskey)))
-    return err;
-  if (!npkey || npkey >= nskey)
+  get_npkey_nskey (pubkey_algo, &npkey, &nskey);
+  if (!npkey || !nskey || npkey >= nskey)
     return gpg_error (GPG_ERR_INTERNAL);
   if (skeylen <= npkey)
     return gpg_error (GPG_ERR_MISSING_VALUE);
@@ -324,7 +361,7 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
      encrypted.  */
   for (i=0; i < npkey; i++)
     {
-      if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
+      if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
         return gpg_error (GPG_ERR_BAD_SECKEY);
     }
 
@@ -344,7 +381,7 @@ prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
 static int
 do_unprotect (const char *passphrase,
               int pkt_version, int pubkey_algo, int is_protected,
-              gcry_mpi_t *skey, size_t skeysize,
+              const char *curve, gcry_mpi_t *skey, size_t skeysize,
               int protect_algo, void *protect_iv, size_t protect_ivlen,
               int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
               u16 desired_csum, gcry_sexp_t *r_key)
@@ -359,10 +396,6 @@ 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);
-
   err = prepare_unprotect (pubkey_algo, skey, skeysize, s2k_mode,
                            &npkey, &nskey, &skeylen);
   if (err)
@@ -372,23 +405,26 @@ do_unprotect (const char *passphrase,
      merely verify the checksum.  */
   if (!is_protected)
     {
-      unsigned char *buffer;
-
       actual_csum = 0;
       for (i=npkey; i < nskey; i++)
         {
-          if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
+          if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
             return gpg_error (GPG_ERR_BAD_SECKEY);
 
-          err = gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, skey[i]);
-          if (!err)
+          if (gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
             {
-              buffer = (gcry_is_secure (skey[i])?
-                        xtrymalloc_secure (nbytes) : xtrymalloc (nbytes));
-              if (!buffer)
-                return gpg_error_from_syserror ();
-              err = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes,
-                                    NULL, skey[i]);
+              unsigned int nbits;
+              const unsigned char *buffer;
+              buffer = gcry_mpi_get_opaque (skey[i], &nbits);
+              nbytes = (nbits+7)/8;
+              actual_csum += checksum (buffer, nbytes);
+            }
+          else
+            {
+              unsigned char *buffer;
+
+              err = gcry_mpi_aprint (GCRYMPI_FMT_PGP, &buffer, &nbytes,
+                                     skey[i]);
               if (!err)
                 actual_csum += checksum (buffer, nbytes);
               xfree (buffer);
@@ -399,7 +435,8 @@ do_unprotect (const char *passphrase,
 
       if (actual_csum != desired_csum)
         return gpg_error (GPG_ERR_CHECKSUM);
-      return 0;
+
+      goto do_convert;
     }
 
 
@@ -447,7 +484,8 @@ do_unprotect (const char *passphrase,
     {
       int ndata;
       unsigned int ndatabits;
-      unsigned char *p, *data;
+      const unsigned char *p;
+      unsigned char *data;
       u16 csum_pgp7 = 0;
 
       if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE ))
@@ -459,7 +497,7 @@ do_unprotect (const char *passphrase,
       ndata = (ndatabits+7)/8;
 
       if (ndata > 1)
-        csum_pgp7 = p[ndata-2] << 8 | p[ndata-1];
+        csum_pgp7 = buf16_to_u16 (p+ndata-2);
       data = xtrymalloc_secure (ndata);
       if (!data)
         {
@@ -503,7 +541,7 @@ do_unprotect (const char *passphrase,
             }
           else
             {
-              desired_csum = (data[ndata-2] << 8 | data[ndata-1]);
+              desired_csum = buf16_to_u16 (data+ndata-2);
               actual_csum = checksum (data, ndata-2);
               if (desired_csum != actual_csum)
                 {
@@ -546,7 +584,7 @@ do_unprotect (const char *passphrase,
 
       for (i = npkey; i < nskey; i++)
         {
-          unsigned char *p;
+          const unsigned char *p;
           size_t ndata;
           unsigned int ndatabits;
 
@@ -558,7 +596,7 @@ do_unprotect (const char *passphrase,
           p = gcry_mpi_get_opaque (skey[i], &ndatabits);
           ndata = (ndatabits+7)/8;
 
-          if (!(ndata >= 2) || !(ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2))
+          if (!(ndata >= 2) || !(ndata == (buf16_to_ushort (p) + 7)/8 + 2))
             {
               gcry_cipher_close (cipher_hd);
               return gpg_error (GPG_ERR_BAD_SECKEY);
@@ -596,10 +634,11 @@ do_unprotect (const char *passphrase,
   if (actual_csum != desired_csum)
     return gpg_error (GPG_ERR_BAD_PASSPHRASE);
 
+ do_convert:
   if (nskey != skeylen)
     err = gpg_error (GPG_ERR_BAD_SECKEY);
   else
-    err = convert_secret_key (r_key, pubkey_algo, skey);
+    err = convert_secret_key (r_key, pubkey_algo, skey, curve);
   if (err)
     return err;
 
@@ -618,7 +657,7 @@ do_unprotect (const char *passphrase,
 
 /* Callback function to try the unprotection from the passphrase query
    code.  */
-static int
+static gpg_error_t
 try_do_unprotect_cb (struct pin_entry_info_s *pi)
 {
   gpg_error_t err;
@@ -627,6 +666,7 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
   err = do_unprotect (pi->pin,
                       arg->is_v4? 4:3,
                       arg->pubkey_algo, arg->is_protected,
+                      arg->curve,
                       arg->skey, arg->skeysize,
                       arg->protect_algo, arg->iv, arg->ivlen,
                       arg->s2k_mode, arg->s2k_algo,
@@ -645,7 +685,7 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
    silently decrypt the key; CACHE_NONCE and R_PASSPHRASE must both be
    NULL in this mode.  */
 static gpg_error_t
-convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
+convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist,
                            unsigned char *grip, const char *prompt,
                            const char *cache_nonce, const char *passphrase,
                            unsigned char **r_key, char **r_passphrase)
@@ -670,6 +710,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   u32  s2k_count = 0;
   size_t npkey, nskey;
   gcry_mpi_t skey[10];  /* We support up to 9 parameters.  */
+  char *curve = NULL;
   u16 desired_csum;
   int skeyidx = 0;
   gcry_sexp_t s_skey = NULL;
@@ -714,8 +755,6 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       if (!string)
         goto bad_seckey;
       protect_algo = gcry_cipher_map_name (string);
-      if (!protect_algo && !!strcmp (string, "IDEA"))
-        protect_algo = GCRY_CIPHER_IDEA;
       xfree (string);
 
       value = gcry_sexp_nth_data (list, 3, &valuelen);
@@ -758,11 +797,21 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   pubkey_algo = gcry_pk_map_name (string);
   xfree (string);
 
-  if (gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
-      || gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
-      || !npkey || npkey >= nskey)
+  get_npkey_nskey (pubkey_algo, &npkey, &nskey);
+  if (!npkey || !nskey || npkey >= nskey)
     goto bad_seckey;
 
+  if (npkey == 1) /* This is ECC */
+    {
+      gcry_sexp_release (list);
+      list = gcry_sexp_find_token (top_list, "curve", 0);
+      if (!list)
+        goto bad_seckey;
+      curve = gcry_sexp_nth_string (list, 1);
+      if (!curve)
+        goto bad_seckey;
+    }
+
   gcry_sexp_release (list);
   list = gcry_sexp_find_token (top_list, "skey", 0);
   if (!list)
@@ -791,13 +840,11 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
         goto bad_seckey;
       if (is_enc)
         {
-          void *p = xtrymalloc (valuelen);
-          if (!p)
-            goto outofmem;
-          memcpy (p, value, valuelen);
-          skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8);
+          /* Encrypted parameters need to be stored as opaque.  */
+          skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8);
           if (!skey[skeyidx])
             goto outofmem;
+          gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1);
         }
       else
         {
@@ -826,37 +873,28 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   gcry_sexp_release (list); list = NULL;
   gcry_sexp_release (top_list); top_list = NULL;
 
-  /* log_debug ("XXX is_v4=%d\n", is_v4); */
-  /* log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); */
-  /* log_debug ("XXX is_protected=%d\n", is_protected); */
-  /* log_debug ("XXX protect_algo=%d\n", protect_algo); */
-  /* log_printhex ("XXX iv", iv, ivlen); */
-  /* log_debug ("XXX ivlen=%d\n", ivlen); */
-  /* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */
-  /* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */
-  /* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */
-  /* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */
-  /* for (idx=0; skey[idx]; idx++) */
-  /*   { */
-  /*     int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */
-  /*     log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */
-  /*     if (is_enc) */
-  /*       { */
-  /*         void *p; */
-  /*         unsigned int nbits; */
-  /*         p = gcry_mpi_get_opaque (skey[idx], &nbits); */
-  /*         log_printhex (NULL, p, (nbits+7)/8); */
-  /*       } */
-  /*     else */
-  /*       gcry_mpi_dump (skey[idx]); */
-  /*     log_printf ("\n"); */
-  /*   } */
-
-  err = get_keygrip (pubkey_algo, skey, grip);
+#if 0
+  log_debug ("XXX is_v4=%d\n", is_v4);
+  log_debug ("XXX pubkey_algo=%d\n", pubkey_algo);
+  log_debug ("XXX is_protected=%d\n", is_protected);
+  log_debug ("XXX protect_algo=%d\n", protect_algo);
+  log_printhex ("XXX iv", iv, ivlen);
+  log_debug ("XXX ivlen=%d\n", ivlen);
+  log_debug ("XXX s2k_mode=%d\n", s2k_mode);
+  log_debug ("XXX s2k_algo=%d\n", s2k_algo);
+  log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt);
+  log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count);
+  log_debug ("XXX curve='%s'\n", curve);
+  for (idx=0; skey[idx]; idx++)
+    gcry_log_debugmpi (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_USER1)
+                       ? "skey(e)" : "skey(_)", skey[idx]);
+#endif /*0*/
+
+  err = get_keygrip (pubkey_algo, curve, skey, grip);
   if (err)
     goto leave;
 
-  if (!from_native && !agent_key_available (grip))
+  if (!dontcare_exist && !from_native && !agent_key_available (grip))
     {
       err = gpg_error (GPG_ERR_EEXIST);
       goto leave;
@@ -864,14 +902,12 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
 
   if (unattended && !from_native)
     {
-      int pubkey_g_algo = map_pk_openpgp_to_gcry (pubkey_algo);
-
-      err = prepare_unprotect (pubkey_g_algo, skey, DIM(skey), s2k_mode,
+      err = prepare_unprotect (pubkey_algo, skey, DIM(skey), s2k_mode,
                                NULL, NULL, NULL);
       if (err)
         goto leave;
 
-      err = convert_transfer_key (&s_skey, pubkey_g_algo, skey, s_pgp);
+      err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp);
       if (err)
         goto leave;
     }
@@ -880,10 +916,10 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       struct pin_entry_info_s *pi;
       struct try_do_unprotect_arg_s pi_arg;
 
-      pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
+      pi = xtrycalloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
       if (!pi)
         return gpg_error_from_syserror ();
-      pi->max_length = 100;
+      pi->max_length = MAX_PASSPHRASE_LEN + 1;
       pi->min_digits = 0;  /* We want a real passphrase.  */
       pi->max_digits = 16;
       pi->max_tries = 3;
@@ -892,6 +928,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       pi_arg.is_v4 = is_v4;
       pi_arg.is_protected = is_protected;
       pi_arg.pubkey_algo = pubkey_algo;
+      pi_arg.curve = curve;
       pi_arg.protect_algo = protect_algo;
       pi_arg.iv = iv;
       pi_arg.ivlen = ivlen;
@@ -906,7 +943,11 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       pi_arg.r_key = &s_skey;
 
       err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
-      if (cache_nonce)
+      if (!is_protected)
+        {
+          err = try_do_unprotect_cb (pi);
+        }
+      else if (cache_nonce)
         {
           char *cache_value;
 
@@ -927,9 +968,9 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
           err = try_do_unprotect_cb (pi);
         }
       if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE && !from_native)
-        err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
+        err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL, 0);
       skeyidx = pi_arg.skeyidx;
-      if (!err && r_passphrase)
+      if (!err && r_passphrase && is_protected)
         {
           *r_passphrase = xtrystrdup (pi->pin);
           if (!*r_passphrase)
@@ -950,6 +991,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   err = make_canon_sexp_pad (s_skey, 1, r_key, NULL);
 
  leave:
+  xfree (curve);
   gcry_sexp_release (s_skey);
   gcry_sexp_release (list);
   gcry_sexp_release (top_list);
@@ -986,12 +1028,12 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
    the key.  The keygrip will be stored at the 20 byte buffer pointed
    to by GRIP.  On error NULL is stored at all return arguments.  */
 gpg_error_t
-convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
+convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist,
                       unsigned char *grip, const char *prompt,
                       const char *cache_nonce,
                       unsigned char **r_key, char **r_passphrase)
 {
-  return convert_from_openpgp_main (ctrl, s_pgp, grip, prompt,
+  return convert_from_openpgp_main (ctrl, s_pgp, dontcare_exist, grip, prompt,
                                     cache_nonce, NULL,
                                     r_key, r_passphrase);
 }
@@ -1010,20 +1052,32 @@ convert_from_openpgp_native (ctrl_t ctrl,
   if (!passphrase)
     return gpg_error (GPG_ERR_INTERNAL);
 
-  err = convert_from_openpgp_main (ctrl, s_pgp, grip, NULL,
+  err = convert_from_openpgp_main (ctrl, s_pgp, 0, grip, NULL,
                                    NULL, passphrase,
                                    r_key, NULL);
 
   /* On success try to re-write the key.  */
   if (!err)
     {
-      unsigned char *protectedkey = NULL;
-      size_t protectedkeylen;
-
-      if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen,
-                          ctrl->s2k_count))
-        agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
-      xfree (protectedkey);
+      if (*passphrase)
+        {
+          unsigned char *protectedkey = NULL;
+          size_t protectedkeylen;
+
+          if (!agent_protect (*r_key, passphrase,
+                              &protectedkey, &protectedkeylen,
+                              ctrl->s2k_count, -1))
+            agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
+          xfree (protectedkey);
+        }
+      else
+        {
+          /* Empty passphrase: write key without protection.  */
+          agent_write_private_key (grip,
+                                   *r_key,
+                                   gcry_sexp_canon_len (*r_key, 0, NULL,NULL),
+                                   1);
+        }
     }
 
   return err;
@@ -1061,7 +1115,6 @@ apply_protection (gcry_mpi_t *array, int npkey, int nskey,
       err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]);
       if (err)
         {
-          err = gpg_error_from_syserror ();
           for (i = 0; i < j; i++)
             xfree (bufarr[i]);
           return err;
@@ -1122,35 +1175,55 @@ apply_protection (gcry_mpi_t *array, int npkey, int nskey,
 }
 
 
-/* Convert our key S_KEY into an OpenPGP key transfer format.  On
-   success a canonical encoded S-expression is stored at R_TRANSFERKEY
-   and its length at R_TRANSFERKEYLEN; this S-expression is also
-   padded to a multiple of 64 bits.  */
+/*
+ * Examining S_KEY in S-Expression and extract data.
+ * When REQ_PRIVATE_KEY_DATA == 1, S_KEY's CAR should be 'private-key',
+ * but it also allows shadowed or protected versions.
+ * On success, it returns 0, otherwise error number.
+ * R_ALGONAME is static string which is no need to free by caller.
+ * R_NPKEY is pointer to number of public key data.
+ * R_NSKEY is pointer to number of private key data.
+ * R_ELEMS is static string which is no need to free by caller.
+ * ARRAY contains public and private key data.
+ * ARRAYSIZE is the allocated size of the array for cross-checking.
+ * R_CURVE is pointer to S-Expression of the curve (can be NULL).
+ * R_FLAGS is pointer to S-Expression of the flags (can be NULL).
+ */
 gpg_error_t
-convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
-                    unsigned char **r_transferkey, size_t *r_transferkeylen)
+extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
+                     const char **r_algoname, int *r_npkey, int *r_nskey,
+                     const char **r_elems,
+                     gcry_mpi_t *array, int arraysize,
+                     gcry_sexp_t *r_curve, gcry_sexp_t *r_flags)
 {
   gpg_error_t err;
   gcry_sexp_t list, l2;
   char *name;
-  const char *algoname;
+  const char *algoname, *format;
   int npkey, nskey;
-  gcry_mpi_t array[10];
-  char protect_iv[16];
-  char salt[8];
-  unsigned long s2k_count;
-  int i, j;
+  gcry_sexp_t curve = NULL;
+  gcry_sexp_t flags = NULL;
 
-  (void)ctrl;
-
-  *r_transferkey = NULL;
+  *r_curve = NULL;
+  *r_flags = NULL;
 
-  for (i=0; i < DIM (array); i++)
-    array[i] = NULL;
+  if (!req_private_key_data)
+    {
+      list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 );
+      if (!list)
+        list = gcry_sexp_find_token (s_key, "protected-private-key", 0 );
+      if (!list)
+        list = gcry_sexp_find_token (s_key, "private-key", 0 );
+    }
+  else
+    list = gcry_sexp_find_token (s_key, "private-key", 0);
 
-  list = gcry_sexp_find_token (s_key, "private-key", 0);
   if (!list)
-    return gpg_error (GPG_ERR_NO_OBJ); /* Does not contain a key object.  */
+    {
+      log_error ("invalid private key format\n");
+      return gpg_error (GPG_ERR_BAD_SECKEY);
+    }
+
   l2 = gcry_sexp_cadr (list);
   gcry_sexp_release (list);
   list = l2;
@@ -1161,6 +1234,9 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
       return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
     }
 
+  if (arraysize < 7)
+    BUG ();
+
   /* Map NAME to a name as used by Libgcrypt.  We do not use the
      Libgcrypt function here because we need a lowercase name and
      require special treatment for some algorithms.  */
@@ -1168,64 +1244,99 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
   if (!strcmp (name, "rsa"))
     {
       algoname = "rsa";
+      format = "ned?p?q?u?";
       npkey = 2;
       nskey = 6;
-      err = gcry_sexp_extract_param (list, NULL, "nedpqu",
+      err = gcry_sexp_extract_param (list, NULL, format,
                                      array+0, array+1, array+2, array+3,
                                      array+4, array+5, NULL);
     }
   else if (!strcmp (name, "elg"))
     {
       algoname = "elg";
+      format = "pgyx?";
       npkey = 3;
       nskey = 4;
-      err = gcry_sexp_extract_param (list, NULL, "pgyx",
+      err = gcry_sexp_extract_param (list, NULL, format,
                                      array+0, array+1, array+2, array+3,
                                      NULL);
     }
   else if (!strcmp (name, "dsa"))
     {
       algoname = "dsa";
+      format = "pqgyx?";
       npkey = 4;
       nskey = 5;
-      err = gcry_sexp_extract_param (list, NULL, "pqgyx",
+      err = gcry_sexp_extract_param (list, NULL, format,
                                      array+0, array+1, array+2, array+3,
                                      array+4, NULL);
     }
   else if (!strcmp (name, "ecc"))
     {
-      /* FIXME: We need to use the curve parameter.  */
-      algoname = "?"; /* Decide later by checking the usage.  */
-      npkey = 6;
-      nskey = 7;
-      err = gcry_sexp_extract_param (list, NULL, "pabgnqd",
-                                     array+0, array+1, array+2, array+3,
-                                     array+4, array+5, array+6, NULL);
+      algoname = "ecc";
+      format = "qd?";
+      npkey = 1;
+      nskey = 2;
+      curve = gcry_sexp_find_token (list, "curve", 0);
+      flags = gcry_sexp_find_token (list, "flags", 0);
+      err = gcry_sexp_extract_param (list, NULL, format,
+                                     array+0, array+1, NULL);
     }
-  else if (!strcmp (name, "ecdsa"))
+  else
     {
-      algoname = "ecdsa";
-      npkey = 6;
-      nskey = 7;
-      err = gcry_sexp_extract_param (list, NULL, "pabgnqd",
-                                     array+0, array+1, array+2, array+3,
-                                     array+4, array+5, array+6, NULL);
+      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
     }
-  else if (!strcmp (name, "ecdh"))
+  xfree (name);
+  gcry_sexp_release (list);
+  if (err)
     {
-      algoname = "ecdh";
-      npkey = 6;
-      nskey= 7;
-      err = gcry_sexp_extract_param (list, NULL, "pabgnqd",
-                                     array+0, array+1, array+2, array+3,
-                                     array+4, array+5, array+6, NULL);
+      gcry_sexp_release (curve);
+      gcry_sexp_release (flags);
+      return err;
     }
   else
     {
-      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+      *r_algoname = algoname;
+      if (r_elems)
+        *r_elems = format;
+      *r_npkey = npkey;
+      if (r_nskey)
+        *r_nskey = nskey;
+      *r_curve = curve;
+      *r_flags = flags;
+
+      return 0;
     }
-  xfree (name);
-  gcry_sexp_release (list);
+}
+
+/* Convert our key S_KEY into an OpenPGP key transfer format.  On
+   success a canonical encoded S-expression is stored at R_TRANSFERKEY
+   and its length at R_TRANSFERKEYLEN; this S-expression is also
+   padded to a multiple of 64 bits.  */
+gpg_error_t
+convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
+                    unsigned char **r_transferkey, size_t *r_transferkeylen)
+{
+  gpg_error_t err;
+  const char *algoname;
+  int npkey, nskey;
+  gcry_mpi_t array[10];
+  gcry_sexp_t curve = NULL;
+  gcry_sexp_t flags = NULL;
+  char protect_iv[16];
+  char salt[8];
+  unsigned long s2k_count;
+  int i, j;
+
+  (void)ctrl;
+
+  *r_transferkey = NULL;
+
+  for (i=0; i < DIM (array); i++)
+    array[i] = NULL;
+
+  err = extract_private_key (s_key, 1, &algoname, &npkey, &nskey, NULL,
+                             array, DIM (array), &curve, &flags);
   if (err)
     return err;
 
@@ -1244,11 +1355,9 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
     {
       char countbuf[35];
       membuf_t mbuf;
-      void *format_args_buf_ptr[1];
-      int   format_args_buf_int[1];
       void *format_args[10+2];
-      unsigned int n;
-      gcry_sexp_t tmpkey, tmpsexp = NULL;
+      gcry_sexp_t tmpkey;
+      gcry_sexp_t tmpsexp = NULL;
 
       snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
 
@@ -1259,11 +1368,8 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
           put_membuf_str (&mbuf, " _ %m");
           format_args[j++] = array + i;
         }
-      put_membuf_str (&mbuf, " e %b");
-      format_args_buf_ptr[0] = gcry_mpi_get_opaque (array[npkey], &n);
-      format_args_buf_int[0] = (n+7)/8;
-      format_args[j++] = format_args_buf_int;
-      format_args[j++] = format_args_buf_ptr;
+      put_membuf_str (&mbuf, " e %m");
+      format_args[j++] = array + npkey;
       put_membuf_str (&mbuf, ")\n");
       put_membuf (&mbuf, "", 1);
 
@@ -1281,9 +1387,10 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
                                "(openpgp-private-key\n"
                                " (version 1:4)\n"
                                " (algo %s)\n"
-                               " %S\n"
+                               " %S%S\n"
                                " (protection sha1 aes %b 1:3 sha1 %b %s))\n",
                                algoname,
+                               curve,
                                tmpkey,
                                (int)sizeof protect_iv, protect_iv,
                                (int)sizeof salt, salt,
@@ -1296,6 +1403,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]);
+  gcry_sexp_release (curve);
+  gcry_sexp_release (flags);
 
   return err;
 }