gpg: First take on PKT_ENCRYPTED_AEAD.
[gnupg.git] / agent / cvt-openpgp.c
index 5a044c4..c4d0334 100644 (file)
@@ -1,6 +1,6 @@
 /* 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.
  *
@@ -15,7 +15,7 @@
  * 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/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <assert.h>
 
 #include "agent.h"
-#include "i18n.h"
+#include "../common/i18n.h"
 #include "cvt-openpgp.h"
+#include "../common/host2net.h"
 
 
 /* Helper to pass data via the callback to do_unprotect. */
-struct try_do_unprotect_arg_s 
+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;
@@ -53,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;
@@ -67,19 +70,34 @@ 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_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:
       err = gpg_error (GPG_ERR_PUBKEY_ALGO);
       break;
@@ -94,9 +112,11 @@ 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.  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;
@@ -128,6 +148,26 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
                              skey[5]);
       break;
 
+    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:
       err = gpg_error (GPG_ERR_PUBKEY_ALGO);
       break;
@@ -139,6 +179,80 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
 }
 
 
+/* Convert a secret key given as algorithm id, an array of key
+   parameters, and an S-expression of the original OpenPGP transfer
+   key into our s-expression based format.  This is a variant of
+   convert_secret_key which is used for the openpgp-native protection
+   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,
+                      const char *curve, gcry_sexp_t transfer_key)
+{
+  gpg_error_t err;
+  gcry_sexp_t s_skey = NULL;
+
+  *r_key = NULL;
+
+  switch (pubkey_algo)
+    {
+    case GCRY_PK_DSA:
+      err = gcry_sexp_build
+        (&s_skey, NULL,
+         "(protected-private-key(dsa(p%m)(q%m)(g%m)(y%m)"
+         "(protected openpgp-native%S)))",
+         skey[0], skey[1], skey[2], skey[3], transfer_key);
+      break;
+
+    case GCRY_PK_ELG:
+      err = gcry_sexp_build
+        (&s_skey, NULL,
+         "(protected-private-key(elg(p%m)(g%m)(y%m)"
+         "(protected openpgp-native%S)))",
+         skey[0], skey[1], skey[2], transfer_key);
+      break;
+
+
+    case GCRY_PK_RSA:
+      err = gcry_sexp_build
+        (&s_skey, NULL,
+         "(protected-private-key(rsa(n%m)(e%m)"
+         "(protected openpgp-native%S)))",
+         skey[0], skey[1], transfer_key );
+      break;
+
+    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:
+      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+      break;
+    }
+
+  if (!err)
+    *r_key = s_skey;
+  return err;
+}
+
 
 /* Hash the passphrase and set the key. */
 static gpg_error_t
@@ -154,7 +268,7 @@ hash_passphrase_and_set_key (const char *passphrase,
   keylen = gcry_cipher_get_algo_keylen (protect_algo);
   if (!keylen)
     return gpg_error (GPG_ERR_INTERNAL);
-  
+
   key = xtrymalloc_secure (keylen);
   if (!key)
     return gpg_error_from_syserror ();
@@ -174,33 +288,41 @@ static u16
 checksum (const unsigned char *p, unsigned int n)
 {
   u16 a;
-  
+
   for (a=0; n; n-- )
     a += *p++;
   return a;
 }
 
 
-/* Note that this function modified SKEY.  SKEYSIZE is the allocated
-   size of the array including the NULL item; this is used for a
-   bounds check.  On success a converted key is stored at R_KEY.  */
+/* 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
+   SKEY.  */
 static int
-do_unprotect (const char *passphrase,
-              int pkt_version, int pubkey_algo, int is_protected,
-              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)
+prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
+                   int s2k_mode,
+                   unsigned int *r_npkey, unsigned int *r_nskey,
+                   unsigned int *r_skeylen)
 {
-  gpg_error_t err;
   size_t npkey, nskey, skeylen;
-  gcry_cipher_hd_t cipher_hd = NULL;
-  u16 actual_csum;
-  size_t nbytes;
   int i;
-  gcry_mpi_t tmpmpi;
-
-  *r_key = NULL;
 
   /* Count the actual number of MPIs is in the array and set the
      remainder to NULL for easier processing later on.  */
@@ -219,9 +341,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);
@@ -230,39 +349,82 @@ do_unprotect (const char *passphrase,
   /* 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);
   if (nskey+1 >= skeysize)
     return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
-  
+
+  /* Check that the public key parameters are all available and not
+     encrypted.  */
+  for (i=0; i < npkey; i++)
+    {
+      if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
+        return gpg_error (GPG_ERR_BAD_SECKEY);
+    }
+
+  if (r_npkey)
+    *r_npkey = npkey;
+  if (r_nskey)
+    *r_nskey = nskey;
+  if (r_skeylen)
+    *r_skeylen = skeylen;
+  return 0;
+}
+
+
+/* Note that this function modifies SKEY.  SKEYSIZE is the allocated
+   size of the array including the NULL item; this is used for a
+   bounds check.  On success a converted key is stored at R_KEY.  */
+static int
+do_unprotect (const char *passphrase,
+              int pkt_version, int pubkey_algo, int is_protected,
+              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)
+{
+  gpg_error_t err;
+  unsigned int npkey, nskey, skeylen;
+  gcry_cipher_hd_t cipher_hd = NULL;
+  u16 actual_csum;
+  size_t nbytes;
+  int i;
+  gcry_mpi_t tmpmpi;
+
+  *r_key = NULL;
+
+  err = prepare_unprotect (pubkey_algo, skey, skeysize, s2k_mode,
+                           &npkey, &nskey, &skeylen);
+  if (err)
+    return err;
+
   /* Check whether SKEY is at all protected.  If it is not protected
      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);
@@ -270,10 +432,11 @@ do_unprotect (const char *passphrase,
           if (err)
             return err;
         }
-      
+
       if (actual_csum != desired_csum)
         return gpg_error (GPG_ERR_CHECKSUM);
-      return 0;
+
+      goto do_convert;
     }
 
 
@@ -283,7 +446,7 @@ do_unprotect (const char *passphrase,
          the OpenPGP algorithm numbers map one-to-one to the Libgcrypt
          numbers.  */
       log_info (_("protection algorithm %d (%s) is not supported\n"),
-                protect_algo, gcry_cipher_algo_name (protect_algo));
+                protect_algo, gnupg_cipher_algo_name (protect_algo));
       return gpg_error (GPG_ERR_CIPHER_ALGO);
     }
 
@@ -293,7 +456,7 @@ do_unprotect (const char *passphrase,
                 s2k_algo, gcry_md_algo_name (s2k_algo));
       return gpg_error (GPG_ERR_DIGEST_ALGO);
     }
-  
+
   err = gcry_cipher_open (&cipher_hd, protect_algo,
                           GCRY_CIPHER_MODE_CFB,
                           (GCRY_CIPHER_SECURE
@@ -312,16 +475,17 @@ do_unprotect (const char *passphrase,
     {
       gcry_cipher_close (cipher_hd);
       return err;
-    }  
+    }
 
   gcry_cipher_setiv (cipher_hd, protect_iv, protect_ivlen);
-  
+
   actual_csum = 0;
   if (pkt_version >= 4)
     {
       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 ))
@@ -333,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)
         {
@@ -348,15 +512,15 @@ do_unprotect (const char *passphrase,
         {
           /* This is the new SHA1 checksum method to detect tampering
              with the key as used by the Klima/Rosa attack.  */
-          desired_csum = 0; 
+          desired_csum = 0;
           actual_csum = 1;  /* Default to bad checksum.  */
 
-          if (ndata < 20) 
+          if (ndata < 20)
             log_error ("not enough bytes for SHA-1 checksum\n");
-          else 
+          else
             {
               gcry_md_hd_t h;
-              
+
               if (gcry_md_open (&h, GCRY_MD_SHA1, 1))
                 BUG(); /* Algo not available. */
               gcry_md_write (h, data, ndata - 20);
@@ -366,18 +530,18 @@ do_unprotect (const char *passphrase,
               gcry_md_close (h);
             }
         }
-      else 
+      else
         {
           /* Old 16 bit checksum method.  */
           if (ndata < 2)
             {
               log_error ("not enough bytes for checksum\n");
-              desired_csum = 0; 
+              desired_csum = 0;
               actual_csum = 1;  /* Mark checksum bad.  */
             }
           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)
                 {
@@ -386,7 +550,7 @@ do_unprotect (const char *passphrase,
                 }
             }
         }
-      
+
       /* Better check it here.  Otherwise the gcry_mpi_scan would fail
          because the length may have an arbitrary value.  */
       if (desired_csum == actual_csum)
@@ -420,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;
 
@@ -432,12 +596,12 @@ 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);
             }
-          
+
           buffer = xtrymalloc_secure (ndata);
           if (!buffer)
             {
@@ -445,7 +609,7 @@ do_unprotect (const char *passphrase,
               gcry_cipher_close (cipher_hd);
               return err;
             }
-              
+
           gcry_cipher_sync (cipher_hd);
           buffer[0] = p[0];
           buffer[1] = p[1];
@@ -470,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;
 
@@ -490,9 +655,9 @@ do_unprotect (const char *passphrase,
 }
 
 
-/* Callback function to try the unprotection from the passpharse query
+/* 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;
@@ -501,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,
@@ -514,24 +680,19 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
 }
 
 
-/* Convert an OpenPGP transfer key into our internal format.  Before
-   asking for a passphrase we check whether the key already exists in
-   our key storage.  S_PGP is the OpenPGP key in transfer format.  If
-   CACHE_NONCE is given the passphrase will be looked up in the cache.
-   On success R_KEY will receive a canonical encoded S-expression with
-   the unprotected key in our internal format; the caller needs to
-   release that memory.  The passphrase used to decrypt the OpenPGP
-   key will be returned at R_PASSPHRASE; the caller must release this
-   passphrase.  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, 
-                      unsigned char *grip, const char *prompt,
-                      const char *cache_nonce,
-                      unsigned char **r_key, char **r_passphrase)
+/* See convert_from_openpgp for the core of the description.  This
+   function adds an optional PASSPHRASE argument and uses this to
+   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, int dontcare_exist,
+                           unsigned char *grip, const char *prompt,
+                           const char *cache_nonce, const char *passphrase,
+                           unsigned char **r_key, char **r_passphrase)
 {
   gpg_error_t err;
+  int unattended;
+  int from_native;
   gcry_sexp_t top_list;
   gcry_sexp_t list = NULL;
   const char *value;
@@ -549,14 +710,16 @@ convert_from_openpgp (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;
-  struct pin_entry_info_s *pi;
-  struct try_do_unprotect_arg_s pi_arg;
+  gcry_sexp_t s_skey = NULL;
 
   *r_key = NULL;
-  *r_passphrase = NULL;
+  if (r_passphrase)
+    *r_passphrase = NULL;
+  unattended = !r_passphrase;
+  from_native = (!cache_nonce && passphrase && !r_passphrase);
 
   top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
   if (!top_list)
@@ -585,16 +748,15 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
     is_protected = 0;
   else
     goto bad_seckey;
+
   if (is_protected)
     {
       string = gcry_sexp_nth_string (list, 2);
       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);
       if (!value || !valuelen || valuelen > sizeof iv)
         goto bad_seckey;
@@ -635,11 +797,21 @@ convert_from_openpgp (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)
@@ -668,13 +840,11 @@ convert_from_openpgp (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
         {
@@ -703,94 +873,113 @@ convert_from_openpgp (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 (iv, ivlen, "XXX iv");
+  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 (s2k_salt, sizeof s2k_salt, "XXX 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 (!agent_key_available (grip))
+  if (!dontcare_exist && !from_native && !agent_key_available (grip))
     {
       err = gpg_error (GPG_ERR_EEXIST);
       goto leave;
     }
 
-  pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
-  if (!pi)
-    return gpg_error_from_syserror ();
-  pi->max_length = 100;
-  pi->min_digits = 0;  /* We want a real passphrase.  */
-  pi->max_digits = 16;
-  pi->max_tries = 3;
-  pi->check_cb = try_do_unprotect_cb;
-  pi->check_cb_arg = &pi_arg;
-  pi_arg.is_v4 = is_v4;
-  pi_arg.is_protected = is_protected;
-  pi_arg.pubkey_algo = pubkey_algo;
-  pi_arg.protect_algo = protect_algo;
-  pi_arg.iv = iv;
-  pi_arg.ivlen = ivlen;
-  pi_arg.s2k_mode = s2k_mode;
-  pi_arg.s2k_algo = s2k_algo;
-  pi_arg.s2k_salt = s2k_salt;
-  pi_arg.s2k_count = s2k_count;
-  pi_arg.desired_csum = desired_csum;
-  pi_arg.skey = skey;
-  pi_arg.skeysize = DIM (skey);
-  pi_arg.skeyidx = skeyidx;
-  pi_arg.r_key = &s_skey;
-
-  err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
-  if (cache_nonce)
+  if (unattended && !from_native)
     {
-      char *cache_value;
+      err = prepare_unprotect (pubkey_algo, skey, DIM(skey), s2k_mode,
+                               NULL, NULL, NULL);
+      if (err)
+        goto leave;
 
-      cache_value = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
-      if (cache_value)
-        {
-          if (strlen (cache_value) < pi->max_length)
-            strcpy (pi->pin, cache_value);
-          xfree (cache_value);
-        }
-      if (*pi->pin)
-        err = try_do_unprotect_cb (pi);
+      err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp);
+      if (err)
+        goto leave;
     }
-  if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
-    err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL);
-  skeyidx = pi_arg.skeyidx;
-  if (!err)
+  else
     {
-      *r_passphrase = xtrystrdup (pi->pin);
-      if (!*r_passphrase)
-        err = gpg_error_from_syserror ();
+      struct pin_entry_info_s *pi;
+      struct try_do_unprotect_arg_s pi_arg;
+
+      pi = xtrycalloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
+      if (!pi)
+        return gpg_error_from_syserror ();
+      pi->max_length = MAX_PASSPHRASE_LEN + 1;
+      pi->min_digits = 0;  /* We want a real passphrase.  */
+      pi->max_digits = 16;
+      pi->max_tries = 3;
+      pi->check_cb = try_do_unprotect_cb;
+      pi->check_cb_arg = &pi_arg;
+      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;
+      pi_arg.s2k_mode = s2k_mode;
+      pi_arg.s2k_algo = s2k_algo;
+      pi_arg.s2k_salt = s2k_salt;
+      pi_arg.s2k_count = s2k_count;
+      pi_arg.desired_csum = desired_csum;
+      pi_arg.skey = skey;
+      pi_arg.skeysize = DIM (skey);
+      pi_arg.skeyidx = skeyidx;
+      pi_arg.r_key = &s_skey;
+
+      err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
+      if (!is_protected)
+        {
+          err = try_do_unprotect_cb (pi);
+        }
+      else if (cache_nonce)
+        {
+          char *cache_value;
+
+          cache_value = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
+          if (cache_value)
+            {
+              if (strlen (cache_value) < pi->max_length)
+                strcpy (pi->pin, cache_value);
+              xfree (cache_value);
+            }
+          if (*pi->pin)
+            err = try_do_unprotect_cb (pi);
+        }
+      else if (from_native)
+        {
+          if (strlen (passphrase) < pi->max_length)
+            strcpy (pi->pin, passphrase);
+          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, NULL, 0);
+      skeyidx = pi_arg.skeyidx;
+      if (!err && r_passphrase && is_protected)
+        {
+          *r_passphrase = xtrystrdup (pi->pin);
+          if (!*r_passphrase)
+            err = gpg_error_from_syserror ();
+        }
+      xfree (pi);
+      if (err)
+        goto leave;
     }
-  xfree (pi);
-  if (err)
-    goto leave;
 
   /* Save some memory and get rid of the SKEY array now.  */
   for (idx=0; idx < skeyidx; idx++)
@@ -798,16 +987,17 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
   skeyidx = 0;
 
   /* Note that the padding is not required - we use it only because
-     that function allows us to created the result in secure memory.  */
+     that function allows us to create the result in secure memory.  */
   err = make_canon_sexp_pad (s_skey, 1, r_key, NULL);
-  gcry_sexp_release (s_skey);
 
  leave:
+  xfree (curve);
+  gcry_sexp_release (s_skey);
   gcry_sexp_release (list);
   gcry_sexp_release (top_list);
   for (idx=0; idx < skeyidx; idx++)
     gcry_mpi_release (skey[idx]);
-  if (err)
+  if (err && r_passphrase)
     {
       xfree (*r_passphrase);
       *r_passphrase = NULL;
@@ -817,7 +1007,7 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
  bad_seckey:
   err = gpg_error (GPG_ERR_BAD_SECKEY);
   goto leave;
-  
+
  outofmem:
   err = gpg_error (GPG_ERR_ENOMEM);
   goto leave;
@@ -825,42 +1015,71 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
 }
 
 
-\f
-static gpg_error_t
-key_from_sexp (gcry_sexp_t sexp, const char *elems, gcry_mpi_t *array)
+/* Convert an OpenPGP transfer key into our internal format.  Before
+   asking for a passphrase we check whether the key already exists in
+   our key storage.  S_PGP is the OpenPGP key in transfer format.  If
+   CACHE_NONCE is given the passphrase will be looked up in the cache.
+   On success R_KEY will receive a canonical encoded S-expression with
+   the unprotected key in our internal format; the caller needs to
+   release that memory.  The passphrase used to decrypt the OpenPGP
+   key will be returned at R_PASSPHRASE; the caller must release this
+   passphrase.  If R_PASSPHRASE is NULL the unattended conversion mode
+   will be used which uses the openpgp-native protection format for
+   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, 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, dontcare_exist, grip, prompt,
+                                    cache_nonce, NULL,
+                                    r_key, r_passphrase);
+}
+
+/* This function is called by agent_unprotect to re-protect an
+   openpgp-native protected private-key into the standard private-key
+   protection format.  */
+gpg_error_t
+convert_from_openpgp_native (ctrl_t ctrl,
+                             gcry_sexp_t s_pgp, const char *passphrase,
+                             unsigned char **r_key)
 {
-  gpg_error_t err = 0;
-  gcry_sexp_t l2;
-  int idx;
+  gpg_error_t err;
+  unsigned char grip[20];
+
+  if (!passphrase)
+    return gpg_error (GPG_ERR_INTERNAL);
 
-  for (idx=0; *elems; elems++, idx++)
+  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)
     {
-      l2 = gcry_sexp_find_token (sexp, elems, 1);
-      if (!l2)
+      if (*passphrase)
         {
-          err = gpg_error (GPG_ERR_NO_OBJ); /* Required parameter not found.  */
-          goto leave;
+          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);
         }
-      array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
-      gcry_sexp_release (l2);
-      if (!array[idx]) 
+      else
         {
-          err = gpg_error (GPG_ERR_INV_OBJ); /* Required parameter invalid.  */
-          goto leave;
+          /* Empty passphrase: write key without protection.  */
+          agent_write_private_key (grip,
+                                   *r_key,
+                                   gcry_sexp_canon_len (*r_key, 0, NULL,NULL),
+                                   1);
         }
     }
-  
- leave:
-  if (err)
-    {
-      int i;
 
-      for (i=0; i < idx; i++)
-        {
-          gcry_mpi_release (array[i]);
-          array[i] = NULL;
-        }
-    }
   return err;
 }
 
@@ -896,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;
@@ -957,37 +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;
-  int algo;
-  const char *algoname;
-  const char *elems;
+  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;
-
-  (void)ctrl;
+  gcry_sexp_t curve = NULL;
+  gcry_sexp_t flags = NULL;
 
-  *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;
@@ -997,33 +1233,119 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
       gcry_sexp_release (list);
       return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
     }
-  
-  algo = gcry_pk_map_name (name);
-  xfree (name);
 
-  switch (algo)
+  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.  */
+  strlwr (name);
+  if (!strcmp (name, "rsa"))
     {
-    case GCRY_PK_RSA:   algoname = "rsa";   npkey = 2; elems = "nedpqu";  break;
-    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;
-    default:            algoname = "";      npkey = 0; elems = NULL;      break;
+      algoname = "rsa";
+      format = "ned?p?q?u?";
+      npkey = 2;
+      nskey = 6;
+      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, 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, format,
+                                     array+0, array+1, array+2, array+3,
+                                     array+4, NULL);
+    }
+  else if (!strcmp (name, "ecc") || !strcmp (name, "ecdsa"))
+    {
+      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);
     }
-  assert (!elems || strlen (elems) < DIM (array) );
-  nskey = elems? strlen (elems) : 0;
-
-  if (!elems)
-    err = gpg_error (GPG_ERR_PUBKEY_ALGO);
   else
-    err = key_from_sexp (list, elems, array);
+    {
+      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+    }
+  xfree (name);
   gcry_sexp_release (list);
   if (err)
+    {
+      gcry_sexp_release (curve);
+      gcry_sexp_release (flags);
+      return err;
+    }
+  else
+    {
+      *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;
+    }
+}
+
+/* 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;
 
   gcry_create_nonce (protect_iv, sizeof protect_iv);
   gcry_create_nonce (salt, sizeof salt);
-  s2k_count = get_standard_s2k_count ();
+  /* We need to use the encoded S2k count.  It is not possible to
+     encode it after it has been used because the encoding procedure
+     may round the value up.  */
+  s2k_count = get_standard_s2k_count_rfc4880 ();
   err = apply_protection (array, npkey, nskey, passphrase,
                           GCRY_CIPHER_AES, protect_iv, sizeof protect_iv,
                           3, GCRY_MD_SHA1, salt, s2k_count);
@@ -1033,14 +1355,12 @@ 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];
-      size_t n;
-      gcry_sexp_t tmpkey, tmpsexp;
-      
+      gcry_sexp_t tmpkey;
+      gcry_sexp_t tmpsexp = NULL;
+
       snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
-      
+
       init_membuf (&mbuf, 50);
       put_membuf_str (&mbuf, "(skey");
       for (i=j=0; i < npkey; i++)
@@ -1048,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);
 
@@ -1070,10 +1387,11 @@ 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,
-                               tmpkey, 
+                               curve,
+                               tmpkey,
                                (int)sizeof protect_iv, protect_iv,
                                (int)sizeof salt, salt,
                                countbuf);
@@ -1085,7 +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;
 }
-