extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
                     const char **r_algoname, int *r_npkey, int *r_nskey,
                     const char **r_elems,
                     gcry_mpi_t *array, int arraysize,
                     gcry_sexp_t *r_curve, gcry_sexp_t *r_flags)
{
  gpg_error_t err;
  gcry_sexp_t list, l2;
  char *name;
  const char *algoname, *format;
  int npkey, nskey;
  gcry_sexp_t curve = NULL;
  gcry_sexp_t flags = NULL;

  *r_curve = NULL;
  *r_flags = 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);

  if (!list)
    {
      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;
  name = gcry_sexp_nth_string (list, 0);
  if (!name)
    {
      gcry_sexp_release (list);
      return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
    }

  if (arraysize < 7)
    BUG ();

  /* Map NAME to a name as used by Libgcrypt.  We do not use the
     Libgcrypt function here because we need a lowercase name and
     require special treatment for some algorithms.  */
  strlwr (name);
  if (!strcmp (name, "rsa"))
    {
      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"))
    {
      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);
      if (flags)
        {
          gcry_sexp_t param = gcry_sexp_find_token (flags, "param", 0);
          if (param)
            {
              gcry_sexp_release (param);
              array[6] = array[0];
              array[7] = array[1];
              err = gcry_sexp_extract_param (list, NULL, "pabgnh?",
                                             array+0, array+1, array+2, array+3,
                                             array+4, array+5, NULL);
              if (array[5] == NULL)
                {
                  array[5] = GCRYMPI_CONST_ONE;
                  npkey += 6;
                  nskey += 6;
                }
              format = "pabgnhqd?";
            }
        }
    }
  else if (!strcmp (name, "ecdsa"))
    {
      algoname = "ecdsa";
      format = "pabgnqd?";
      npkey = 6;
      nskey = 7;
      err = gcry_sexp_extract_param (list, NULL, format,
                                     array+0, array+1, array+2, array+3,
                                     array+4, array+5, array+6, NULL);
    }
  else if (!strcmp (name, "ecdh"))
    {
      algoname = "ecdh";
      format = "pabgnqd?";
      npkey = 6;
      nskey= 7;
      err = gcry_sexp_extract_param (list, NULL, format,
                                     array+0, array+1, array+2, array+3,
                                     array+4, array+5, array+6, NULL);
    }
  else
    {
      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)
        {
          if (format[0] == '/') /* It is opaque data qualifier, skip it.  */
            *r_elems = format+1;
          else
            *r_elems = format;
        }
      *r_npkey = npkey;
      if (r_nskey)
        *r_nskey = nskey;
      *r_curve = curve;
      *r_flags = flags;

      return 0;
    }
}