transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
{
  gpg_error_t err = 0;
  void *kek = NULL;
  size_t keklen;
  kbnode_t ctx = NULL;
  kbnode_t node;
  PKT_public_key *main_pk, *pk;
  struct seckey_info *ski;
  int nskey;
  membuf_t mbuf;
  int i, j;
  unsigned int n;
  void *format_args_buf_ptr[PUBKEY_MAX_NSKEY];
  int   format_args_buf_int[PUBKEY_MAX_NSKEY];
  void *format_args[2*PUBKEY_MAX_NSKEY];
  gcry_sexp_t skey, prot, tmpsexp;
  unsigned char *transferkey = NULL;
  size_t transferkeylen;
  gcry_cipher_hd_t cipherhd = NULL;
  unsigned char *wrappedkey = NULL;
  size_t wrappedkeylen;
  char *cache_nonce = NULL;
  gcry_mpi_t ecc_params[5] = {NULL, NULL, NULL, NULL, NULL};

  /* Get the current KEK.  */
  err = agent_keywrap_key (ctrl, 0, &kek, &keklen);
  if (err)
    {
      log_error ("error getting the KEK: %s\n", gpg_strerror (err));
      goto leave;
    }

  /* Prepare a cipher context.  */
  err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
                          GCRY_CIPHER_MODE_AESWRAP, 0);
  if (!err)
    err = gcry_cipher_setkey (cipherhd, kek, keklen);
  if (err)
    goto leave;
  xfree (kek);
  kek = NULL;

  main_pk = NULL;
  while ((node = walk_kbnode (sec_keyblock, &ctx, 0)))
    {
      if (node->pkt->pkttype != PKT_SECRET_KEY
          && node->pkt->pkttype != PKT_SECRET_SUBKEY)
        continue;
      pk = node->pkt->pkt.public_key;
      if (!main_pk)
        main_pk = pk;

      /* Make sure the keyids are available.  */
      keyid_from_pk (pk, NULL);
      if (node->pkt->pkttype == PKT_SECRET_KEY)
        {
          pk->main_keyid[0] = pk->keyid[0];
          pk->main_keyid[1] = pk->keyid[1];
        }
      else
        {
          pk->main_keyid[0] = main_pk->keyid[0];
          pk->main_keyid[1] = main_pk->keyid[1];
        }


      ski = pk->seckey_info;
      if (!ski)
        BUG ();

      stats->count++;
      stats->secret_read++;

      /* We ignore stub keys.  The way we handle them in other parts
         of the code is by asking the agent whether any secret key is
         available for a given keyblock and then concluding that we
         have a secret key; all secret (sub)keys of the keyblock the
         agent does not know of are then stub keys.  This works also
         for card stub keys.  The learn command or the card-status
         command may be used to check with the agent whether a card
         has been inserted and a stub key is in turn generated by the
         agent.  */
      if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002)
        continue;

      /* Convert our internal secret key object into an S-expression.  */
      nskey = pubkey_get_nskey (pk->pubkey_algo);
      if (!nskey || nskey > PUBKEY_MAX_NSKEY)
        {
          err = gpg_error (GPG_ERR_BAD_SECKEY);
          log_error ("internal error: %s\n", gpg_strerror (err));
          goto leave;
        }

      init_membuf (&mbuf, 50);
      put_membuf_str (&mbuf, "(skey");
      if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
          || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
        {
          /* We need special treatment for ECC algorithms.  OpenPGP
             stores only the curve name but the agent expects a full
             key.  This is so that we can keep all curve name
             validation code out of gpg-agent.  */
#if PUBKEY_MAX_NSKEY < 7
#error  PUBKEY_MAX_NSKEY too low for ECC
#endif
          char *curve = openpgp_oid_to_str (pk->pkey[0]);
          if (!curve)
            err = gpg_error_from_syserror ();
          else
            {
              gcry_sexp_t cparam = gcry_pk_get_param (GCRY_PK_ECDSA, curve);

              xfree (curve);
              if (!cparam)
                err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
              else
                {
                  const char *s;

                  /* Append the curve parameters P, A, B, G and N.  */
                  for (i=j=0; !err && *(s = "pabgn"+i); i++)
                    {
                      ecc_params[i] = one_mpi_from_pkey (cparam, s, 1);
                      if (!ecc_params[i])
                        err = gpg_error (GPG_ERR_INV_CURVE);
                      else
                        {
                          put_membuf_str (&mbuf, " _ %m");
                          format_args[j++] = ecc_params+i;
                        }
                    }
                  gcry_sexp_release (cparam);
                  if (!err)
                    {
                      /* Append the public key element Q.  */
                      put_membuf_str (&mbuf, " _ %m");
                      format_args[j++] = pk->pkey + 1;

                      /* Append the secret key element D.  Note that
                         for ECDH we need to skip PKEY[2] because this
                         holds the KEK which is not needed.  */
                      i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2;
                      if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
                        {
                          put_membuf_str (&mbuf, " e %b");
                          format_args_buf_ptr[i]
                            = gcry_mpi_get_opaque (pk->pkey[i],&n);
                          format_args_buf_int[i] = (n+7)/8;
                          format_args[j++] = format_args_buf_int + i;
                          format_args[j++] = format_args_buf_ptr + i;
                        }
                      else
                        {
                          put_membuf_str (&mbuf, " _ %m");
                          format_args[j++] = pk->pkey + i;
                        }
                    }
                }
            }
        }
      else
        {
          /* Standard case for the old (non-ECC) algorithms.  */
          for (i=j=0; i < nskey; i++)
            {
              if (!pk->pkey[i])
                ; /* Protected keys only have NPKEY+1 elements.  */
              else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
                {
                  put_membuf_str (&mbuf, " e %b");
                  format_args_buf_ptr[i] = gcry_mpi_get_opaque (pk->pkey[i],&n);
                  format_args_buf_int[i] = (n+7)/8;
                  format_args[j++] = format_args_buf_int + i;
                  format_args[j++] = format_args_buf_ptr + i;
                }
              else
                {
                  put_membuf_str (&mbuf, " _ %m");
                  format_args[j++] = pk->pkey + i;
                }
            }
        }
      put_membuf_str (&mbuf, ")\n");
      put_membuf (&mbuf, "", 1);
      if (err)
        xfree (get_membuf (&mbuf, NULL));
      else
        {
          char *format = get_membuf (&mbuf, NULL);
          if (!format)
            err = gpg_error_from_syserror ();
          else
            err = gcry_sexp_build_array (&skey, NULL, format, format_args);
          xfree (format);
        }
      if (err)
        {
          log_error ("error building skey array: %s\n", gpg_strerror (err));
          goto leave;
        }

      if (ski->is_protected)
        {
          char countbuf[35];

          /* Note that the IVLEN may be zero if we are working on a
             dummy key.  We can't express that in an S-expression and
             thus we send dummy data for the IV.  */
          snprintf (countbuf, sizeof countbuf, "%lu",
                    (unsigned long)ski->s2k.count);
          err = gcry_sexp_build
            (&prot, NULL,
             " (protection %s %s %b %d %s %b %s)\n",
             ski->sha1chk? "sha1":"sum",
             openpgp_cipher_algo_name (ski->algo),
             ski->ivlen? (int)ski->ivlen:1,
             ski->ivlen? ski->iv: (const unsigned char*)"X",
             ski->s2k.mode,
             openpgp_md_algo_name (ski->s2k.hash_algo),
             (int)sizeof (ski->s2k.salt), ski->s2k.salt,
             countbuf);
        }
      else
        err = gcry_sexp_build (&prot, NULL, " (protection none)\n");

      tmpsexp = NULL;
      xfree (transferkey);
      transferkey = NULL;
      if (!err)
        err = gcry_sexp_build (&tmpsexp, NULL,
                               "(openpgp-private-key\n"
                               " (version %d)\n"
                               " (algo %s)\n"
                               " %S\n"
                               " (csum %d)\n"
                               " %S)\n",
                               pk->version,
                               openpgp_pk_algo_name (pk->pubkey_algo),
                               skey, (int)(unsigned long)ski->csum, prot);
      gcry_sexp_release (skey);
      gcry_sexp_release (prot);
      if (!err)
        err = make_canon_sexp_pad (tmpsexp, 1, &transferkey, &transferkeylen);
      gcry_sexp_release (tmpsexp);
      if (err)
        {
          log_error ("error building transfer key: %s\n", gpg_strerror (err));
          goto leave;
        }

      /* Wrap the key.  */
      wrappedkeylen = transferkeylen + 8;
      xfree (wrappedkey);
      wrappedkey = xtrymalloc (wrappedkeylen);
      if (!wrappedkey)
        err = gpg_error_from_syserror ();
      else
        err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen,
                                   transferkey, transferkeylen);
      if (err)
        goto leave;
      xfree (transferkey);
      transferkey = NULL;

      /* Send the wrapped key to the agent.  */
      {
        char *desc = gpg_format_keydesc (pk, 1, 1);
        err = agent_import_key (ctrl, desc, &cache_nonce,
                                wrappedkey, wrappedkeylen);
        xfree (desc);
      }
      if (!err)
        {
          if (opt.verbose)
            log_info (_("key %s: secret key imported\n"),
                      keystr_from_pk_with_sub (main_pk, pk));
          stats->secret_imported++;
        }
      else if ( gpg_err_code (err) == GPG_ERR_EEXIST )
        {
          if (opt.verbose)
            log_info (_("key %s: secret key already exists\n"),
                      keystr_from_pk_with_sub (main_pk, pk));
          err = 0;
          stats->secret_dups++;
        }
      else
        {
          log_error (_("key %s: error sending to agent: %s\n"),
                     keystr_from_pk_with_sub (main_pk, pk),
                     gpg_strerror (err));
          if (gpg_err_code (err) == GPG_ERR_CANCELED
              || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
            break; /* Don't try the other subkeys.  */
        }
    }

 leave:
  for (i=0; i < DIM (ecc_params); i++)
    gcry_mpi_release (ecc_params[i]);
  xfree (cache_nonce);
  xfree (wrappedkey);
  xfree (transferkey);
  gcry_cipher_close (cipherhd);
  xfree (kek);
  return err;
}