Add open card manufacturer 0x0008.
[gnupg.git] / agent / cvt-openpgp.c
index 1595a32..671dd4c 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.
  *
@@ -35,6 +35,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;
@@ -53,7 +54,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,25 +69,27 @@ 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 if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL)))
+        err = gcry_sexp_build (&s_pkey, NULL,
+                               "(public-key(ecc(curve %s)(flags eddsa)(q%m)))",
+                               "Ed25519", pkey[0]);
+      else
+        err = gcry_sexp_build (&s_pkey, NULL,
+                               "(public-key(ecc(curve %s)(q%m)))",
+                               curve, pkey[0]);
       break;
 
     default:
@@ -105,7 +109,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;
@@ -137,16 +142,22 @@ 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 if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL)))
+        {
+          /* Do not store the OID as name but the real name and the
+             EdDSA flag.  */
+          err = gcry_sexp_build (&s_skey, NULL,
+                                 "(private-key(ecc(curve%s)(flags eddsa)"
+                                 "(q%m)(d%m)))",
+                                 "Ed25519", skey[0], skey[1]);
+        }
+      else
+        err = gcry_sexp_build (&s_skey, NULL,
+                               "(private-key(ecc(curve%s)(q%m)(d%m)))",
+                               curve, skey[0], skey[1]);
       break;
 
     default:
@@ -160,6 +171,78 @@ 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 if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL)))
+        {
+          /* Do not store the OID as name but the real name and the
+             EdDSA flag.  */
+          err = gcry_sexp_build
+            (&s_skey, NULL,
+             "(protected-private-key(ecc(curve%s)(flags eddsa)(q%m)"
+             "(protected openpgp-native%S)))",
+             "Ed25519", skey[0], transfer_key);
+        }
+      else
+        err = gcry_sexp_build
+          (&s_skey, NULL,
+           "(protected-private-key(ecc(curve%s)(q%m)"
+           "(protected openpgp-native%S)))",
+           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
@@ -202,30 +285,34 @@ checksum (const unsigned char *p, unsigned int n)
 }
 
 
-/* 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;
-
- /* 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.  */
@@ -252,39 +339,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);
@@ -295,7 +425,8 @@ do_unprotect (const char *passphrase,
 
       if (actual_csum != desired_csum)
         return gpg_error (GPG_ERR_CHECKSUM);
-      return 0;
+
+      goto do_convert;
     }
 
 
@@ -343,7 +474,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 ))
@@ -442,7 +574,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;
 
@@ -492,10 +624,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;
 
@@ -512,7 +645,7 @@ 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
 try_do_unprotect_cb (struct pin_entry_info_s *pi)
@@ -523,6 +656,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,
@@ -536,24 +670,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,
+                           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;
@@ -571,14 +700,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)
@@ -607,14 +738,13 @@ 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);
@@ -657,11 +787,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)
@@ -688,15 +828,15 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
       value = gcry_sexp_nth_data (list, ++idx, &valuelen);
       if (!value || !valuelen)
         goto bad_seckey;
-      if (is_enc)
+      if (is_enc || curve)
         {
-          void *p = xtrymalloc (valuelen);
-          if (!p)
-            goto outofmem;
-          memcpy (p, value, valuelen);
-          skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8);
+          /* Encrypted parameters and ECC parameters need or can be
+             stored as opaque.  */
+          skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8);
           if (!skey[skeyidx])
             goto outofmem;
+          if (is_enc)
+            gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1);
         }
       else
         {
@@ -725,94 +865,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 ("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 (!agent_key_available (grip))
+  if (!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);
-  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) + 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.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);
+      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++)
@@ -820,16 +979,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;
@@ -847,42 +1007,59 @@ 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,
+                      unsigned char *grip, const char *prompt,
+                      const char *cache_nonce,
+                      unsigned char **r_key, char **r_passphrase)
 {
-  gpg_error_t err = 0;
-  gcry_sexp_t l2;
-  int idx;
+  return convert_from_openpgp_main (ctrl, s_pgp, grip, prompt,
+                                    cache_nonce, NULL,
+                                    r_key, r_passphrase);
+}
 
-  for (idx=0; *elems; elems++, idx++)
-    {
-      l2 = gcry_sexp_find_token (sexp, elems, 1);
-      if (!l2)
-        {
-          err = gpg_error (GPG_ERR_NO_OBJ); /* Required parameter not found.  */
-          goto leave;
-        }
-      array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
-      gcry_sexp_release (l2);
-      if (!array[idx])
-        {
-          err = gpg_error (GPG_ERR_INV_OBJ); /* Required parameter invalid.  */
-          goto leave;
-        }
-    }
+/* 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;
+  unsigned char grip[20];
 
- leave:
-  if (err)
+  if (!passphrase)
+    return gpg_error (GPG_ERR_INTERNAL);
+
+  err = convert_from_openpgp_main (ctrl, s_pgp, grip, NULL,
+                                   NULL, passphrase,
+                                   r_key, NULL);
+
+  /* On success try to re-write the key.  */
+  if (!err)
     {
-      int i;
+      unsigned char *protectedkey = NULL;
+      size_t protectedkeylen;
 
-      for (i=0; i < idx; i++)
-        {
-          gcry_mpi_release (array[i]);
-          array[i] = NULL;
-        }
+      if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen,
+                          ctrl->s2k_count))
+        agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
+      xfree (protectedkey);
     }
+
   return err;
 }
 
@@ -915,15 +1092,36 @@ apply_protection (gcry_mpi_t *array, int npkey, int nskey,
   ndata = 20; /* Space for the SHA-1 checksum.  */
   for (i = npkey, j = 0; i < nskey; i++, j++ )
     {
-      err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]);
-      if (err)
+      if (gcry_mpi_get_flag (array[i], GCRYMPI_FLAG_OPAQUE))
         {
-          err = gpg_error_from_syserror ();
-          for (i = 0; i < j; i++)
-            xfree (bufarr[i]);
-          return err;
+          const void *s;
+          unsigned int n;
+
+          s = gcry_mpi_get_opaque (array[i], &n);
+          nbits[j] = n;
+          n = (n+7)/8;
+          narr[j] = n;
+          bufarr[j] = gcry_is_secure (s)? xtrymalloc_secure (n):xtrymalloc (n);
+          if (!bufarr[j])
+            {
+              err = gpg_error_from_syserror ();
+              for (i = 0; i < j; i++)
+                xfree (bufarr[i]);
+              return err;
+            }
+          memcpy (bufarr[j], s, n);
+        }
+      else
+        {
+          err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]);
+          if (err)
+            {
+              for (i = 0; i < j; i++)
+                xfree (bufarr[i]);
+              return err;
+            }
+          nbits[j] = gcry_mpi_get_nbits (array[i]);
         }
-      nbits[j] = gcry_mpi_get_nbits (array[i]);
       ndata += 2 + narr[j];
     }
 
@@ -990,11 +1188,10 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
   gpg_error_t err;
   gcry_sexp_t list, l2;
   char *name;
-  int algo;
   const char *algoname;
-  const char *elems;
   int npkey, nskey;
   gcry_mpi_t array[10];
+  gcry_sexp_t curve = NULL;
   char protect_iv[16];
   char salt[8];
   unsigned long s2k_count;
@@ -1020,33 +1217,94 @@ 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. */
     }
 
-  algo = gcry_pk_map_name (name);
-  xfree (name);
-
-  switch (algo)
+  /* 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;
-    case GCRY_PK_ECDH:  algoname = "ecdh";  npkey = 6; elems = "pabgnqd"; break;
-    default:            algoname = "";      npkey = 0; elems = NULL;      break;
+      algoname = "rsa";
+      npkey = 2;
+      nskey = 6;
+      err = gcry_sexp_extract_param (list, NULL, "nedpqu",
+                                     array+0, array+1, array+2, array+3,
+                                     array+4, array+5, NULL);
+    }
+  else if (!strcmp (name, "elg"))
+    {
+      algoname = "elg";
+      npkey = 3;
+      nskey = 4;
+      err = gcry_sexp_extract_param (list, NULL, "pgyx",
+                                     array+0, array+1, array+2, array+3,
+                                     NULL);
+    }
+  else if (!strcmp (name, "dsa"))
+    {
+      algoname = "dsa";
+      npkey = 4;
+      nskey = 5;
+      err = gcry_sexp_extract_param (list, NULL, "pqgyx",
+                                     array+0, array+1, array+2, array+3,
+                                     array+4, NULL);
+    }
+  else if (!strcmp (name, "ecc"))
+    {
+      gcry_buffer_t iob;
+      char iobbuf[32];
+
+      algoname = "ecc"; /* Decide later by checking the usage.  */
+      npkey = 1;
+      nskey = 2;
+      iob.data = iobbuf;
+      iob.size = sizeof iobbuf - 1;
+      iob.off = 0;
+      iob.len = 0;
+      err = gcry_sexp_extract_param (list, NULL, "&'curve'/qd",
+                                     &iob, array+0, array+1, NULL);
+      if (!err)
+        {
+          assert (iob.len < sizeof iobbuf -1);
+          iobbuf[iob.len] = 0;
+          err = gcry_sexp_build (&curve, NULL, "(curve %s)", iobbuf);
+        }
+    }
+  else if (!strcmp (name, "ecdsa"))
+    {
+      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);
+    }
+  else if (!strcmp (name, "ecdh"))
+    {
+      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);
     }
-  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);
-  gcry_sexp_release (list);
+    {
+      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+    }
+  xfree (name);
+  gcry_sexp_release (list); list = NULL;
   if (err)
-    return err;
+    {
+      gcry_sexp_release (curve);
+      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);
@@ -1056,11 +1314,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];
-      size_t n;
-      gcry_sexp_t tmpkey, tmpsexp = NULL;
+      gcry_sexp_t tmpkey;
+      gcry_sexp_t tmpsexp = NULL;
 
       snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
 
@@ -1071,11 +1327,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);
 
@@ -1093,9 +1346,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,
@@ -1108,6 +1362,7 @@ 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);
 
   return err;
 }