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;
  size_t valuelen;
  char *string;
  int  idx;
  int  is_v4, is_protected;
  int  pubkey_algo;
  int  protect_algo = 0;
  char iv[16];
  int  ivlen = 0;
  int  s2k_mode = 0;
  int  s2k_algo = 0;
  byte s2k_salt[8];
  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;

  *r_key = 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)
    goto bad_seckey;

  list = gcry_sexp_find_token (top_list, "version", 0);
  if (!list)
    goto bad_seckey;
  value = gcry_sexp_nth_data (list, 1, &valuelen);
  if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4'))
    goto bad_seckey;
  is_v4 = (value[0] == '4');

  gcry_sexp_release (list);
  list = gcry_sexp_find_token (top_list, "protection", 0);
  if (!list)
    goto bad_seckey;
  value = gcry_sexp_nth_data (list, 1, &valuelen);
  if (!value)
    goto bad_seckey;
  if (valuelen == 4 && !memcmp (value, "sha1", 4))
    is_protected = 2;
  else if (valuelen == 3 && !memcmp (value, "sum", 3))
    is_protected = 1;
  else if (valuelen == 4 && !memcmp (value, "none", 4))
    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);
      xfree (string);

      value = gcry_sexp_nth_data (list, 3, &valuelen);
      if (!value || !valuelen || valuelen > sizeof iv)
        goto bad_seckey;
      memcpy (iv, value, valuelen);
      ivlen = valuelen;

      string = gcry_sexp_nth_string (list, 4);
      if (!string)
        goto bad_seckey;
      s2k_mode = strtol (string, NULL, 10);
      xfree (string);

      string = gcry_sexp_nth_string (list, 5);
      if (!string)
        goto bad_seckey;
      s2k_algo = gcry_md_map_name (string);
      xfree (string);

      value = gcry_sexp_nth_data (list, 6, &valuelen);
      if (!value || !valuelen || valuelen > sizeof s2k_salt)
        goto bad_seckey;
      memcpy (s2k_salt, value, valuelen);

      string = gcry_sexp_nth_string (list, 7);
      if (!string)
        goto bad_seckey;
      s2k_count = strtoul (string, NULL, 10);
      xfree (string);
    }

  gcry_sexp_release (list);
  list = gcry_sexp_find_token (top_list, "algo", 0);
  if (!list)
    goto bad_seckey;
  string = gcry_sexp_nth_string (list, 1);
  if (!string)
    goto bad_seckey;
  pubkey_algo = gcry_pk_map_name (string);
  xfree (string);

  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)
    goto bad_seckey;
  for (idx=0;;)
    {
      int is_enc;

      value = gcry_sexp_nth_data (list, ++idx, &valuelen);
      if (!value && skeyidx >= npkey)
        break;  /* Ready.  */

      /* Check for too many parameters.  Note that depending on the
         protection mode and version number we may see less than NSKEY
         (but at least NPKEY+1) parameters.  */
      if (idx >= 2*nskey)
        goto bad_seckey;
      if (skeyidx >= DIM (skey)-1)
        goto bad_seckey;

      if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e'))
        goto bad_seckey;
      is_enc = (value[0] == 'e');
      value = gcry_sexp_nth_data (list, ++idx, &valuelen);
      if (!value || !valuelen)
        goto bad_seckey;
      if (is_enc || curve)
        {
          /* 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
        {
          if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD,
                             value, valuelen, NULL))
            goto bad_seckey;
        }
      skeyidx++;
    }
  skey[skeyidx++] = NULL;

  gcry_sexp_release (list);
  list = gcry_sexp_find_token (top_list, "csum", 0);
  if (list)
    {
      string = gcry_sexp_nth_string (list, 1);
      if (!string)
        goto bad_seckey;
      desired_csum = strtoul (string, NULL, 10);
      xfree (string);
    }
  else
    desired_csum = 0;


  gcry_sexp_release (list); list = NULL;
  gcry_sexp_release (top_list); top_list = NULL;

#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))
    {
      err = gpg_error (GPG_ERR_EEXIST);
      goto leave;
    }

  if (unattended && !from_native)
    {
      err = prepare_unprotect (pubkey_algo, skey, DIM(skey), s2k_mode,
                               NULL, NULL, NULL);
      if (err)
        goto leave;

      err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp);
      if (err)
        goto leave;
    }
  else
    {
      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;
    }

  /* Save some memory and get rid of the SKEY array now.  */
  for (idx=0; idx < skeyidx; idx++)
    gcry_mpi_release (skey[idx]);
  skeyidx = 0;

  /* Note that the padding is not required - we use it only because
     that function allows us to create the result in secure memory.  */
  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);
  for (idx=0; idx < skeyidx; idx++)
    gcry_mpi_release (skey[idx]);
  if (err && r_passphrase)
    {
      xfree (*r_passphrase);
      *r_passphrase = NULL;
    }
  return err;

 bad_seckey:
  err = gpg_error (GPG_ERR_BAD_SECKEY);
  goto leave;

 outofmem:
  err = gpg_error (GPG_ERR_ENOMEM);
  goto leave;

}