dirmngr: Fix error handling.
[gnupg.git] / g10 / keygen.c
index 5ff89f6..44f139a 100644 (file)
@@ -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 <unistd.h>
 
 #include "gpg.h"
-#include "util.h"
+#include "../common/util.h"
 #include "main.h"
 #include "packet.h"
-#include "ttyio.h"
+#include "../common/ttyio.h"
 #include "options.h"
 #include "keydb.h"
 #include "trustdb.h"
-#include "status.h"
-#include "i18n.h"
+#include "../common/status.h"
+#include "../common/i18n.h"
 #include "keyserver-internal.h"
 #include "call-agent.h"
 #include "pkglue.h"
 #include "../common/shareddefs.h"
-#include "host2net.h"
-#include "mbox-util.h"
+#include "../common/host2net.h"
+#include "../common/mbox-util.h"
 
 
 /* The default algorithms.  If you change them remember to change them
    also in gpg.c:gpgconf_list.  You should also check that the value
-   is inside the bounds enforced by ask_keysize and gen_xxx.  */
-#define DEFAULT_STD_ALGO       PUBKEY_ALGO_RSA
-#define DEFAULT_STD_KEYSIZE    2048
-#define DEFAULT_STD_KEYUSE     (PUBKEY_USAGE_CERT|PUBKEY_USAGE_SIG)
-#define DEFAULT_STD_CURVE      NULL
-#define DEFAULT_STD_SUBALGO    PUBKEY_ALGO_RSA
-#define DEFAULT_STD_SUBKEYSIZE 2048
-#define DEFAULT_STD_SUBKEYUSE  PUBKEY_USAGE_ENC
-#define DEFAULT_STD_SUBCURVE   NULL
-
-#define FUTURE_STD_ALGO        PUBKEY_ALGO_EDDSA
-#define FUTURE_STD_KEYSIZE     0
-#define FUTURE_STD_KEYUSE      (PUBKEY_USAGE_CERT|PUBKEY_USAGE_SIG)
-#define FUTURE_STD_CURVE       "Ed25519"
-#define FUTURE_STD_SUBALGO     PUBKEY_ALGO_ECDH
-#define FUTURE_STD_SUBKEYSIZE  0
-#define FUTURE_STD_SUBKEYUSE   PUBKEY_USAGE_ENC
-#define FUTURE_STD_SUBCURVE    "Curve25519"
+   is inside the bounds enforced by ask_keysize and gen_xxx.  See also
+   get_keysize_range which encodes the allowed ranges.  */
+#define DEFAULT_STD_KEY_PARAM  "rsa2048/cert,sign+rsa2048/encr"
+#define FUTURE_STD_KEY_PARAM   "ed25519/cert,sign+cv25519/encr"
+
+/* When generating keys using the streamlined key generation dialog,
+   use this as a default expiration interval.  */
+const char *default_expiration_interval = "2y";
 
 /* Flag bits used during key generation.  */
 #define KEYGEN_FLAG_NO_PROTECTION 1
@@ -99,7 +89,8 @@ enum para_name {
   pSERIALNO,
   pCARDBACKUPKEY,
   pHANDLE,
-  pKEYSERVER
+  pKEYSERVER,
+  pKEYGRIP
 };
 
 struct para_data_s {
@@ -152,11 +143,32 @@ static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey,
                                      u32 *r_expire,
                                      unsigned int *r_nbits, char **r_curve);
 static void do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
-                                struct output_control_s *outctrl, int card );
+                                 struct output_control_s *outctrl, int card );
 static int write_keyblock (iobuf_t out, kbnode_t node);
-static gpg_error_t gen_card_key (int algo, int keyno, int is_primary,
-                                 kbnode_t pub_root,
-                                 u32 *timestamp, u32 expireval);
+static gpg_error_t gen_card_key (int keyno, int algo, int is_primary,
+                                 kbnode_t pub_root, u32 *timestamp,
+                                 u32 expireval);
+static unsigned int get_keysize_range (int algo,
+                                       unsigned int *min, unsigned int *max);
+
+
+\f
+/* Return the algo string for a default new key.  */
+const char *
+get_default_pubkey_algo (void)
+{
+  if (opt.def_new_key_algo)
+    {
+      if (*opt.def_new_key_algo && !strchr (opt.def_new_key_algo, ':'))
+        return opt.def_new_key_algo;
+      /* To avoid checking that option every time we delay that until
+       * here.  The only thing we really need to make sure is that
+       * there is no colon in the string so that the --gpgconf-list
+       * command won't mess up its output.  */
+      log_info (_("invalid value for option '%s'\n"), "--default-new-key-algo");
+    }
+  return DEFAULT_STD_KEY_PARAM;
+}
 
 
 static void
@@ -180,8 +192,9 @@ print_status_key_created (int letter, PKT_public_key *pk, const char *handle)
           *p++ = ' ';
           fingerprint_from_pk (pk, array, &n);
           s = array;
+          /* Fixme: Use bin2hex */
           for (i=0; i < n ; i++, s++, p += 2)
-            sprintf (p, "%02X", *s);
+            snprintf (p, 3, "%02X", *s);
         }
     }
   if (*handle)
@@ -256,7 +269,7 @@ keygen_add_key_expire (PKT_signature *sig, void *opaque)
 
       buf[0] = (u >> 24) & 0xff;
       buf[1] = (u >> 16) & 0xff;
-      buf[2] = (u >>   8) & 0xff;
+      buf[2] = (u >>  8) & 0xff;
       buf[3] = u & 0xff;
       build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4);
     }
@@ -422,9 +435,11 @@ keygen_set_std_prefs (const char *string,int personal)
 
     if(strlen(string))
       {
-       char *dup, *tok, *prefstring;
+       char *prefstringbuf;
+        char *tok, *prefstring;
 
-       dup = prefstring = xstrdup (string); /* need a writable string! */
+        /* We need a writable string. */
+       prefstring = prefstringbuf = xstrdup (string);
 
        while((tok=strsep(&prefstring," ,")))
          {
@@ -458,7 +473,7 @@ keygen_set_std_prefs (const char *string,int personal)
              }
          }
 
-       xfree (dup);
+       xfree (prefstringbuf);
       }
 
     if(!rc)
@@ -1090,7 +1105,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
 {
   gpg_error_t err;
   gcry_sexp_t list, l2;
-  char *curve;
+  char *curve = NULL;
   int i;
   const char *oidstr;
   unsigned int nbits;
@@ -1159,6 +1174,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
     }
 
  leave:
+  xfree (curve);
   if (err)
     {
       for (i=0; i < 3; i++)
@@ -1601,7 +1617,7 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root,
   log_assert (is_RSA(algo));
 
   if (!nbits)
-    nbits = DEFAULT_STD_KEYSIZE;
+    nbits = get_keysize_range (algo, NULL, NULL);
 
   if (nbits < 1024)
     {
@@ -1770,7 +1786,7 @@ ask_key_flags (int algo, int subkey, unsigned int current)
               else if (!subkey && *s == 'c')
                 {
                   /* Accept 'c' for the primary key because USAGE_CERT
-                     will will be set anyway.  This is for folks who
+                     will be set anyway.  This is for folks who
                      want to experiment with a cert-only primary key.  */
                   current |= PUBKEY_USAGE_CERT;
                 }
@@ -1823,7 +1839,7 @@ check_keygrip (ctrl_t ctrl, const char *hexgrip)
   gpg_error_t err;
   unsigned char *public;
   size_t publiclen;
-  const char *algostr;
+  int algo;
 
   if (hexgrip[0] == '&')
     hexgrip++;
@@ -1833,26 +1849,10 @@ check_keygrip (ctrl_t ctrl, const char *hexgrip)
     return 0;
   publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
 
-  get_pk_algo_from_canon_sexp (public, publiclen, &algostr);
+  algo = get_pk_algo_from_canon_sexp (public, publiclen);
   xfree (public);
 
-  /* FIXME: Mapping of ECC algorithms is probably not correct. */
-  if (!algostr)
-    return 0;
-  else if (!strcmp (algostr, "rsa"))
-    return PUBKEY_ALGO_RSA;
-  else if (!strcmp (algostr, "dsa"))
-    return PUBKEY_ALGO_DSA;
-  else if (!strcmp (algostr, "elg"))
-    return PUBKEY_ALGO_ELGAMAL_E;
-  else if (!strcmp (algostr, "ecc"))
-    return PUBKEY_ALGO_ECDH;
-  else if (!strcmp (algostr, "ecdsa"))
-    return PUBKEY_ALGO_ECDSA;
-  else if (!strcmp (algostr, "eddsa"))
-    return PUBKEY_ALGO_EDDSA;
-  else
-    return 0;
+  return map_pk_gcry_to_openpgp (algo);
 }
 
 
@@ -1884,24 +1884,27 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
     tty_printf (_("   (%d) RSA and RSA (default)\n"), 1 );
 #endif
 
-  if (!addmode)
+  if (!addmode && opt.compliance != CO_DE_VS)
     tty_printf (_("   (%d) DSA and Elgamal\n"), 2 );
 
-  tty_printf (_("   (%d) DSA (sign only)\n"), 3 );
+  if (opt.compliance != CO_DE_VS)
+    tty_printf (_("   (%d) DSA (sign only)\n"), 3 );
 #if GPG_USE_RSA
   tty_printf (_("   (%d) RSA (sign only)\n"), 4 );
 #endif
 
   if (addmode)
     {
-      tty_printf (_("   (%d) Elgamal (encrypt only)\n"), 5 );
+      if (opt.compliance != CO_DE_VS)
+        tty_printf (_("   (%d) Elgamal (encrypt only)\n"), 5 );
 #if GPG_USE_RSA
       tty_printf (_("   (%d) RSA (encrypt only)\n"), 6 );
 #endif
     }
   if (opt.expert)
     {
-      tty_printf (_("   (%d) DSA (set your own capabilities)\n"), 7 );
+      if (opt.compliance != CO_DE_VS)
+        tty_printf (_("   (%d) DSA (set your own capabilities)\n"), 7 );
 #if GPG_USE_RSA
       tty_printf (_("   (%d) RSA (set your own capabilities)\n"), 8 );
 #endif
@@ -1929,7 +1932,13 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
       answer = cpr_get ("keygen.algo", _("Your selection? "));
       cpr_kill_prompt ();
       algo = *answer? atoi (answer) : 1;
-      if ((algo == 1 || !strcmp (answer, "rsa+rsa")) && !addmode)
+
+      if (opt.compliance == CO_DE_VS
+          && (algo == 2 || algo == 3 || algo == 5 || algo == 7))
+        {
+          tty_printf (_("Invalid selection.\n"));
+        }
+      else if ((algo == 1 || !strcmp (answer, "rsa+rsa")) && !addmode)
         {
           algo = PUBKEY_ALGO_RSA;
           *r_subkey_algo = PUBKEY_ALGO_RSA;
@@ -2046,36 +2055,46 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
 }
 
 
-static void
-get_keysize_range (int algo,
-                   unsigned int *min, unsigned int *def, unsigned int *max)
+static unsigned int
+get_keysize_range (int algo, unsigned int *min, unsigned int *max)
 {
-  *min = 1024;
-  *def = DEFAULT_STD_KEYSIZE;
-  *max = 4096;
+  unsigned int def;
+  unsigned int dummy1, dummy2;
+
+  if (!min)
+    min = &dummy1;
+  if (!max)
+    max = &dummy2;
 
-  /* Deviations from the standard values.  */
   switch(algo)
     {
     case PUBKEY_ALGO_DSA:
       *min = opt.expert? 768 : 1024;
-      *def=2048;
       *max=3072;
+      def=2048;
       break;
 
     case PUBKEY_ALGO_ECDSA:
     case PUBKEY_ALGO_ECDH:
       *min=256;
-      *def=256;
       *max=521;
+      def=256;
       break;
 
     case PUBKEY_ALGO_EDDSA:
       *min=255;
-      *def=255;
       *max=441;
+      def=255;
+      break;
+
+    default:
+      *min = opt.compliance == CO_DE_VS ? 2048: 1024;
+      *max = 4096;
+      def = 2048;
       break;
     }
+
+  return def;
 }
 
 
@@ -2137,7 +2156,7 @@ ask_keysize (int algo, unsigned int primary_keysize)
   int for_subkey = !!primary_keysize;
   int autocomp = 0;
 
-  get_keysize_range (algo, &min, &def, &max);
+  def = get_keysize_range (algo, &min, &max);
 
   if (primary_keysize && !opt.expert)
     {
@@ -2196,26 +2215,27 @@ ask_curve (int *algo, int *subkey_algo)
      numbers in the menu regardless on how Gpg was configured.  */
   struct {
     const char *name;
-    int available;   /* Available in Libycrypt (runtime checked) */
-    int expert_only;
     const char* eddsa_curve; /* Corresponding EdDSA curve.  */
     const char *pretty_name;
-    int supported;   /* Supported by gpg.  */
+    unsigned int supported : 1;   /* Supported by gpg.     */
+    unsigned int de_vs : 1;       /* Allowed in CO_DE_VS.  */
+    unsigned int expert_only : 1; /* Only with --expert    */
+    unsigned int available : 1;   /* Available in Libycrypt (runtime checked) */
   } curves[] = {
 #if GPG_USE_ECDSA || GPG_USE_ECDH
 # define MY_USE_ECDSADH 1
 #else
 # define MY_USE_ECDSADH 0
 #endif
-    { "Curve25519",      0, 0, "Ed25519", "Curve 25519", GPG_USE_EDDSA  },
-    { "Curve448",        0, 1, "Ed448",   "Curve 448",   0/*reserved*/  },
-    { "NIST P-256",      0, 1, NULL, NULL,               MY_USE_ECDSADH },
-    { "NIST P-384",      0, 0, NULL, NULL,               MY_USE_ECDSADH },
-    { "NIST P-521",      0, 1, NULL, NULL,               MY_USE_ECDSADH },
-    { "brainpoolP256r1", 0, 1, NULL, "Brainpool P-256",  MY_USE_ECDSADH },
-    { "brainpoolP384r1", 0, 1, NULL, "Brainpool P-384",  MY_USE_ECDSADH },
-    { "brainpoolP512r1", 0, 1, NULL, "Brainpool P-512",  MY_USE_ECDSADH },
-    { "secp256k1",       0, 1, NULL, NULL,               MY_USE_ECDSADH },
+    { "Curve25519",      "Ed25519", "Curve 25519", !!GPG_USE_EDDSA, 0, 0, 0 },
+    { "Curve448",        "Ed448",   "Curve 448",   0/*reserved*/  , 0, 1, 0 },
+    { "NIST P-256",      NULL, NULL,               MY_USE_ECDSADH,  0, 1, 0 },
+    { "NIST P-384",      NULL, NULL,               MY_USE_ECDSADH,  0, 0, 0 },
+    { "NIST P-521",      NULL, NULL,               MY_USE_ECDSADH,  0, 1, 0 },
+    { "brainpoolP256r1", NULL, "Brainpool P-256",  MY_USE_ECDSADH,  1, 1, 0 },
+    { "brainpoolP384r1", NULL, "Brainpool P-384",  MY_USE_ECDSADH,  1, 1, 0 },
+    { "brainpoolP512r1", NULL, "Brainpool P-512",  MY_USE_ECDSADH,  1, 1, 0 },
+    { "secp256k1",       NULL, NULL,               MY_USE_ECDSADH,  0, 1, 0 },
   };
 #undef MY_USE_ECDSADH
   int idx;
@@ -2233,7 +2253,13 @@ ask_curve (int *algo, int *subkey_algo)
       curves[idx].available = 0;
       if (!curves[idx].supported)
         continue;
-      if (!opt.expert && curves[idx].expert_only)
+
+      if (opt.compliance==CO_DE_VS)
+        {
+          if (!curves[idx].de_vs)
+            continue; /* Not allowed.  */
+        }
+      else if (!opt.expert && curves[idx].expert_only)
         continue;
 
       /* We need to switch from the ECDH name of the curve to the
@@ -2428,13 +2454,7 @@ ask_expire_interval(int object,const char *def_expire)
          {
            char *prompt;
 
-#define PROMPTSTRING _("Signature is valid for? (%s) ")
-           /* This will actually end up larger than necessary because
-              of the 2 bytes for '%s' */
-           prompt=xmalloc(strlen(PROMPTSTRING)+strlen(def_expire)+1);
-           sprintf(prompt,PROMPTSTRING,def_expire);
-#undef PROMPTSTRING
-
+           prompt = xasprintf (_("Signature is valid for? (%s) "), def_expire);
            answer = cpr_get("siggen.valid",prompt);
            xfree(prompt);
 
@@ -2843,6 +2863,301 @@ generate_user_id (KBNODE keyblock, const char *uidstr)
 }
 
 
+/* Helper for parse_key_parameter_string for one part of the
+ * specification string; i.e.  ALGO/FLAGS.  If STRING is NULL or empty
+ * success is returned.  On error an error code is returned.  Note
+ * that STRING may be modified by this function.  NULL may be passed
+ * for any parameter.  FOR_SUBKEY shall be true if this is used as a
+ * subkey.  */
+static gpg_error_t
+parse_key_parameter_part (char *string, int for_subkey,
+                          int *r_algo, unsigned int *r_size,
+                          unsigned int *r_keyuse,
+                          char const **r_curve)
+{
+  char *flags;
+  int algo;
+  char *endp;
+  const char *curve = NULL;
+  int ecdh_or_ecdsa = 0;
+  unsigned int size;
+  int keyuse;
+  int i;
+  const char *s;
+
+  if (!string || !*string)
+    return 0; /* Success.  */
+
+  flags = strchr (string, '/');
+  if (flags)
+    *flags++ = 0;
+
+  algo = 0;
+  if (strlen (string) >= 3 && (digitp (string+3) || !string[3]))
+    {
+      if (!ascii_memcasecmp (string, "rsa", 3))
+        algo = PUBKEY_ALGO_RSA;
+      else if (!ascii_memcasecmp (string, "dsa", 3))
+        algo = PUBKEY_ALGO_DSA;
+      else if (!ascii_memcasecmp (string, "elg", 3))
+        algo = PUBKEY_ALGO_ELGAMAL_E;
+    }
+  if (algo)
+    {
+      if (!string[3])
+        size = get_keysize_range (algo, NULL, NULL);
+      else
+        {
+          size = strtoul (string+3, &endp, 10);
+          if (size < 512 || size > 16384 || *endp)
+            return gpg_error (GPG_ERR_INV_VALUE);
+        }
+    }
+  else if ((curve = openpgp_is_curve_supported (string, &algo, &size)))
+    {
+      if (!algo)
+        {
+          algo = PUBKEY_ALGO_ECDH; /* Default ECC algorithm.  */
+          ecdh_or_ecdsa = 1;       /* We may need to switch the algo.  */
+        }
+    }
+  else
+    return gpg_error (GPG_ERR_UNKNOWN_CURVE);
+
+  /* Parse the flags.  */
+  keyuse = 0;
+  if (flags)
+    {
+      char **tokens = NULL;
+
+      tokens = strtokenize (flags, ",");
+      if (!tokens)
+        return gpg_error_from_syserror ();
+
+      for (i=0; (s = tokens[i]); i++)
+        {
+          if (!*s)
+            ;
+          else if (!ascii_strcasecmp (s, "sign"))
+            keyuse |= PUBKEY_USAGE_SIG;
+          else if (!ascii_strcasecmp (s, "encrypt")
+                   || !ascii_strcasecmp (s, "encr"))
+            keyuse |= PUBKEY_USAGE_ENC;
+          else if (!ascii_strcasecmp (s, "auth"))
+            keyuse |= PUBKEY_USAGE_AUTH;
+          else if (!ascii_strcasecmp (s, "cert"))
+            keyuse |= PUBKEY_USAGE_CERT;
+          else if (!ascii_strcasecmp (s, "ecdsa"))
+            {
+              if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA)
+                algo = PUBKEY_ALGO_ECDSA;
+              else
+                {
+                  xfree (tokens);
+                  return gpg_error (GPG_ERR_INV_FLAG);
+                }
+              ecdh_or_ecdsa = 0;
+            }
+          else if (!ascii_strcasecmp (s, "ecdh"))
+            {
+              if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA)
+                algo = PUBKEY_ALGO_ECDH;
+              else
+                {
+                  xfree (tokens);
+                  return gpg_error (GPG_ERR_INV_FLAG);
+                }
+              ecdh_or_ecdsa = 0;
+            }
+          else if (!ascii_strcasecmp (s, "eddsa"))
+            {
+              /* Not required but we allow it for consistency.  */
+              if (algo == PUBKEY_ALGO_EDDSA)
+                ;
+              else
+                {
+                  xfree (tokens);
+                  return gpg_error (GPG_ERR_INV_FLAG);
+                }
+            }
+          else
+            {
+              xfree (tokens);
+              return gpg_error (GPG_ERR_UNKNOWN_FLAG);
+            }
+        }
+
+      xfree (tokens);
+    }
+
+  /* If not yet decided switch between ecdh and ecdsa.  */
+  if (ecdh_or_ecdsa && keyuse)
+    algo = (keyuse & PUBKEY_USAGE_ENC)? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA;
+  else if (ecdh_or_ecdsa)
+    algo = for_subkey? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA;
+
+  /* Set or fix key usage.  */
+  if (!keyuse)
+    {
+      if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA
+          || algo == PUBKEY_ALGO_DSA)
+        keyuse = PUBKEY_USAGE_SIG;
+      else if (algo == PUBKEY_ALGO_RSA)
+        keyuse = for_subkey? PUBKEY_USAGE_ENC : PUBKEY_USAGE_SIG;
+      else
+        keyuse = PUBKEY_USAGE_ENC;
+    }
+  else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA
+           || algo == PUBKEY_ALGO_DSA)
+    {
+      keyuse &= ~PUBKEY_USAGE_ENC; /* Forbid encryption.  */
+    }
+  else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ELGAMAL_E)
+    {
+      keyuse = PUBKEY_USAGE_ENC;   /* Allow only encryption.  */
+    }
+
+  /* Make sure a primary key can certify.  */
+  if (!for_subkey)
+    keyuse |= PUBKEY_USAGE_CERT;
+
+  /* Check that usage is actually possible.  */
+  if (/**/((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT))
+           && !pubkey_get_nsig (algo))
+       || ((keyuse & PUBKEY_USAGE_ENC)
+           && !pubkey_get_nenc (algo))
+       || (for_subkey && (keyuse & PUBKEY_USAGE_CERT)))
+    return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+
+  /* Return values.  */
+  if (r_algo)
+    *r_algo = algo;
+  if (r_size)
+    {
+      unsigned int min, def, max;
+
+      /* Make sure the keysize is in the allowed range.  */
+      def = get_keysize_range (algo, &min, &max);
+      if (!size)
+        size = def;
+      else if (size < min)
+        size = min;
+      else if (size > max)
+        size = max;
+
+      *r_size = fixup_keysize (size, algo, 1);
+    }
+  if (r_keyuse)
+    *r_keyuse = keyuse;
+  if (r_curve)
+    *r_curve = curve;
+
+  return 0;
+}
+
+/* Parse and return the standard key generation parameter.
+ * The string is expected to be in this format:
+ *
+ *   ALGO[/FLAGS][+SUBALGO[/FLAGS]]
+ *
+ * Here ALGO is a string in the same format as printed by the
+ * keylisting.  For example:
+ *
+ *   rsa3072 := RSA with 3072 bit.
+ *   dsa2048 := DSA with 2048 bit.
+ *   elg2048 := Elgamal with 2048 bit.
+ *   ed25519 := EDDSA using curve Ed25519.
+ *   cv25519 := ECDH using curve Curve25519.
+ *   nistp256:= ECDSA or ECDH using curve NIST P-256
+ *
+ * All strings with an unknown prefix are considered an elliptic
+ * curve.  Curves which have no implicit algorithm require that FLAGS
+ * is given to select whether ECDSA or ECDH is used; this can eoither
+ * be done using an algorithm keyword or usage keywords.
+ *
+ * FLAGS is a comma delimited string of keywords:
+ *
+ *   cert := Allow usage Certify
+ *   sign := Allow usage Sign
+ *   encr := Allow usage Encrypt
+ *   auth := Allow usage Authentication
+ *   encrypt := Alias for "encr"
+ *   ecdsa := Use algorithm ECDSA.
+ *   eddsa := Use algorithm EdDSA.
+ *   ecdh  := Use algorithm ECDH.
+ *
+ * There are several defaults and fallbacks depending on the
+ * algorithm.  PART can be used to select which part of STRING is
+ * used:
+ *   -1 := Both parts
+ *    0 := Only the part of the primary key
+ *    1 := If there is one part parse that one, if there are
+ *         two parts parse the second part.  Always return
+ *         in the args for the primary key (R_ALGO,....).
+ *
+ */
+gpg_error_t
+parse_key_parameter_string (const char *string, int part,
+                            int *r_algo, unsigned int *r_size,
+                            unsigned *r_keyuse,
+                            char const **r_curve,
+                            int *r_subalgo, unsigned int *r_subsize,
+                            unsigned *r_subkeyuse,
+                            char const **r_subcurve)
+{
+  gpg_error_t err = 0;
+  char *primary, *secondary;
+
+  if (r_algo)
+    *r_algo = 0;
+  if (r_size)
+    *r_size = 0;
+  if (r_keyuse)
+    *r_keyuse = 0;
+  if (r_curve)
+    *r_curve = NULL;
+  if (r_subalgo)
+    *r_subalgo = 0;
+  if (r_subsize)
+    *r_subsize = 0;
+  if (r_subkeyuse)
+    *r_subkeyuse = 0;
+  if (r_subcurve)
+    *r_subcurve = NULL;
+
+  if (!string || !*string
+      || !strcmp (string, "default") || !strcmp (string, "-"))
+    string = get_default_pubkey_algo ();
+  else if (!strcmp (string, "future-default"))
+    string = FUTURE_STD_KEY_PARAM;
+
+  primary = xstrdup (string);
+  secondary = strchr (primary, '+');
+  if (secondary)
+    *secondary++ = 0;
+  if (part == -1 || part == 0)
+    {
+      err = parse_key_parameter_part (primary, 0, r_algo, r_size,
+                                      r_keyuse, r_curve);
+      if (!err && part == -1)
+        err = parse_key_parameter_part (secondary, 1, r_subalgo, r_subsize,
+                                        r_subkeyuse, r_subcurve);
+    }
+  else if (part == 1)
+    {
+      /* If we have SECONDARY, use that part.  If there is only one
+       * part consider this to be the subkey algo.  */
+      err = parse_key_parameter_part (secondary? secondary : primary, 1,
+                                      r_algo, r_size, r_keyuse, r_curve);
+    }
+
+  xfree (primary);
+
+  return err;
+}
+
+
+
 /* Append R to the linked list PARA.  */
 static void
 append_to_parameter (struct para_data_s *para, struct para_data_s *r)
@@ -2915,8 +3230,15 @@ get_parameter_algo( struct para_data_s *para, enum para_name key,
   if (!ascii_strcasecmp (r->u.value, "default"))
     {
       /* Note: If you change this default algo, remember to change it
-         also in gpg.c:gpgconf_list.  */
-      i = DEFAULT_STD_ALGO;
+       * also in gpg.c:gpgconf_list.  */
+      /* FIXME: We only allow the algo here and have a separate thing
+       * for the curve etc.  That is a ugly but demanded for backward
+       * compatibility with the batch key generation.  It would be
+       * better to make full use of parse_key_parameter_string.  */
+      parse_key_parameter_string (NULL, 0,
+                                  &i, NULL, NULL, NULL,
+                                  NULL, NULL, NULL, NULL);
+
       if (r_default)
         *r_default = 1;
     }
@@ -2941,8 +3263,8 @@ get_parameter_algo( struct para_data_s *para, enum para_name key,
 
 
 /* Parse a usage string.  The usage keywords "auth", "sign", "encr"
- * may be elimited by space, tab, or comma.  On error -1 is returned
- * instead of the usage flags/  */
+ * may be delimited by space, tab, or comma.  On error -1 is returned
+ * instead of the usage flags.  */
 static int
 parse_usagestr (const char *usagestr)
 {
@@ -3316,8 +3638,9 @@ read_parameter_file (ctrl_t ctrl, const char *fname )
        { "Preferences",    pPREFERENCES },
        { "Revoker",        pREVOKER },
         { "Handle",         pHANDLE },
-       { "Keyserver",      pKEYSERVER },
-       { NULL, 0 }
+        { "Keyserver",      pKEYSERVER },
+        { "Keygrip",        pKEYGRIP },
+        { NULL, 0 }
     };
     IOBUF fp;
     byte *line;
@@ -3622,31 +3945,35 @@ quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr,
       }
   }
 
+  if (!*expirestr || strcmp (expirestr, "-") == 0)
+    expirestr = default_expiration_interval;
 
   if ((!*algostr || !strcmp (algostr, "default")
        || !strcmp (algostr, "future-default"))
       && (!*usagestr || !strcmp (usagestr, "default")
           || !strcmp (usagestr, "-")))
     {
-      if (!strcmp (algostr, "future-default"))
-        {
-          para = quickgen_set_para (para, 0,
-                                    FUTURE_STD_ALGO, FUTURE_STD_KEYSIZE,
-                                    FUTURE_STD_CURVE, 0);
-          para = quickgen_set_para (para, 1,
-                                    FUTURE_STD_SUBALGO,  FUTURE_STD_SUBKEYSIZE,
-                                    FUTURE_STD_SUBCURVE, 0);
-        }
-      else
+      /* Use default key parameters.  */
+      int algo, subalgo;
+      unsigned int size, subsize;
+      unsigned int keyuse, subkeyuse;
+      const char *curve, *subcurve;
+
+      err = parse_key_parameter_string (algostr, -1,
+                                        &algo, &size, &keyuse, &curve,
+                                        &subalgo, &subsize, &subkeyuse,
+                                        &subcurve);
+      if (err)
         {
-          para = quickgen_set_para (para, 0,
-                                    DEFAULT_STD_ALGO, DEFAULT_STD_KEYSIZE,
-                                    DEFAULT_STD_CURVE, 0);
-          para = quickgen_set_para (para, 1,
-                                    DEFAULT_STD_SUBALGO, DEFAULT_STD_SUBKEYSIZE,
-                                    DEFAULT_STD_SUBCURVE, 0);
+          log_error (_("Key generation failed: %s\n"), gpg_strerror (err));
+          goto leave;
         }
 
+      para = quickgen_set_para (para, 0, algo, size, curve, keyuse);
+      if (subalgo)
+        para = quickgen_set_para (para, 1,
+                                  subalgo, subsize, subcurve, subkeyuse);
+
       if (*expirestr)
         {
           u32 expire;
@@ -3725,6 +4052,7 @@ void
 generate_keypair (ctrl_t ctrl, int full, const char *fname,
                   const char *card_serialno, int card_backup_key)
 {
+  gpg_error_t err;
   unsigned int nbits;
   char *uid = NULL;
   int algo;
@@ -3757,17 +4085,26 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
   if (card_serialno)
     {
 #ifdef ENABLE_CARD_SUPPORT
+      struct agent_card_info_s info;
+
+      memset (&info, 0, sizeof (info));
+      err = agent_scd_getattr ("KEY-ATTR", &info);
+      if (err)
+        {
+          log_error (_("error getting current key info: %s\n"),
+                     gpg_strerror (err));
+          return;
+        }
+
       r = xcalloc (1, sizeof *r + strlen (card_serialno) );
       r->key = pSERIALNO;
       strcpy( r->u.value, card_serialno);
       r->next = para;
       para = r;
 
-      algo = PUBKEY_ALGO_RSA;
-
       r = xcalloc (1, sizeof *r + 20 );
       r->key = pKEYTYPE;
-      sprintf( r->u.value, "%d", algo );
+      sprintf( r->u.value, "%d", info.key_attr[0].algo );
       r->next = para;
       para = r;
       r = xcalloc (1, sizeof *r + 20 );
@@ -3778,7 +4115,7 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
 
       r = xcalloc (1, sizeof *r + 20 );
       r->key = pSUBKEYTYPE;
-      sprintf( r->u.value, "%d", algo );
+      sprintf( r->u.value, "%d", info.key_attr[1].algo );
       r->next = para;
       para = r;
       r = xcalloc (1, sizeof *r + 20 );
@@ -3786,10 +4123,28 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
       strcpy (r->u.value, "encrypt");
       r->next = para;
       para = r;
+      if (info.key_attr[1].algo == PUBKEY_ALGO_RSA)
+        {
+          r = xcalloc (1, sizeof *r + 20 );
+          r->key = pSUBKEYLENGTH;
+          sprintf( r->u.value, "%u", info.key_attr[1].nbits);
+          r->next = para;
+          para = r;
+        }
+      else if (info.key_attr[1].algo == PUBKEY_ALGO_ECDSA
+               || info.key_attr[1].algo == PUBKEY_ALGO_EDDSA
+               || info.key_attr[1].algo == PUBKEY_ALGO_ECDH)
+        {
+          r = xcalloc (1, sizeof *r + strlen (info.key_attr[1].curve));
+          r->key = pSUBKEYCURVE;
+          strcpy (r->u.value, info.key_attr[1].curve);
+          r->next = para;
+          para = r;
+        }
 
       r = xcalloc (1, sizeof *r + 20 );
       r->key = pAUTHKEYTYPE;
-      sprintf( r->u.value, "%d", algo );
+      sprintf( r->u.value, "%d", info.key_attr[2].algo );
       r->next = para;
       para = r;
 
@@ -3806,140 +4161,174 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
   else if (full)  /* Full featured key generation.  */
     {
       int subkey_algo;
-      char *curve = NULL;
-
-      /* Fixme: To support creating a primary key by keygrip we better
-         also define the keyword for the parameter file.  Note that
-         the subkey case will never be asserted if a keygrip has been
-         given.  */
-      algo = ask_algo (ctrl, 0, &subkey_algo, &use, NULL);
-      if (subkey_algo)
+      char *key_from_hexgrip = NULL;
+
+      algo = ask_algo (ctrl, 0, &subkey_algo, &use, &key_from_hexgrip);
+      if (key_from_hexgrip)
         {
-          /* Create primary and subkey at once.  */
-          both = 1;
-          if (algo == PUBKEY_ALGO_ECDSA
-              || algo == PUBKEY_ALGO_EDDSA
-              || algo == PUBKEY_ALGO_ECDH)
+          r = xmalloc_clear( sizeof *r + 20 );
+          r->key = pKEYTYPE;
+          sprintf( r->u.value, "%d", algo);
+          r->next = para;
+          para = r;
+
+          if (use)
             {
-              curve = ask_curve (&algo, &subkey_algo);
-              r = xmalloc_clear( sizeof *r + 20 );
-              r->key = pKEYTYPE;
-              sprintf( r->u.value, "%d", algo);
-              r->next = para;
-              para = r;
-              nbits = 0;
-              r = xmalloc_clear (sizeof *r + strlen (curve));
-              r->key = pKEYCURVE;
-              strcpy (r->u.value, curve);
+              r = xmalloc_clear( sizeof *r + 25 );
+              r->key = pKEYUSAGE;
+              sprintf( r->u.value, "%s%s%s",
+                       (use & PUBKEY_USAGE_SIG)? "sign ":"",
+                       (use & PUBKEY_USAGE_ENC)? "encrypt ":"",
+                       (use & PUBKEY_USAGE_AUTH)? "auth":"" );
               r->next = para;
               para = r;
             }
-          else
+
+          r = xmalloc_clear( sizeof *r + 40 );
+          r->key = pKEYGRIP;
+          strcpy (r->u.value, key_from_hexgrip);
+          r->next = para;
+          para = r;
+
+          xfree (key_from_hexgrip);
+        }
+      else
+        {
+          char *curve = NULL;
+
+          if (subkey_algo)
             {
+              /* Create primary and subkey at once.  */
+              both = 1;
+              if (algo == PUBKEY_ALGO_ECDSA
+                  || algo == PUBKEY_ALGO_EDDSA
+                  || algo == PUBKEY_ALGO_ECDH)
+                {
+                  curve = ask_curve (&algo, &subkey_algo);
+                  r = xmalloc_clear( sizeof *r + 20 );
+                  r->key = pKEYTYPE;
+                  sprintf( r->u.value, "%d", algo);
+                  r->next = para;
+                  para = r;
+                  nbits = 0;
+                  r = xmalloc_clear (sizeof *r + strlen (curve));
+                  r->key = pKEYCURVE;
+                  strcpy (r->u.value, curve);
+                  r->next = para;
+                  para = r;
+                }
+              else
+                {
+                  r = xmalloc_clear( sizeof *r + 20 );
+                  r->key = pKEYTYPE;
+                  sprintf( r->u.value, "%d", algo);
+                  r->next = para;
+                  para = r;
+                  nbits = ask_keysize (algo, 0);
+                  r = xmalloc_clear( sizeof *r + 20 );
+                  r->key = pKEYLENGTH;
+                  sprintf( r->u.value, "%u", nbits);
+                  r->next = para;
+                  para = r;
+                }
               r = xmalloc_clear( sizeof *r + 20 );
-              r->key = pKEYTYPE;
-              sprintf( r->u.value, "%d", algo);
+              r->key = pKEYUSAGE;
+              strcpy( r->u.value, "sign" );
               r->next = para;
               para = r;
-              nbits = ask_keysize (algo, 0);
+
               r = xmalloc_clear( sizeof *r + 20 );
-              r->key = pKEYLENGTH;
-              sprintf( r->u.value, "%u", nbits);
+              r->key = pSUBKEYTYPE;
+              sprintf( r->u.value, "%d", subkey_algo);
+              r->next = para;
+              para = r;
+              r = xmalloc_clear( sizeof *r + 20 );
+              r->key = pSUBKEYUSAGE;
+              strcpy( r->u.value, "encrypt" );
               r->next = para;
               para = r;
-            }
-          r = xmalloc_clear( sizeof *r + 20 );
-          r->key = pKEYUSAGE;
-          strcpy( r->u.value, "sign" );
-          r->next = para;
-          para = r;
-
-          r = xmalloc_clear( sizeof *r + 20 );
-          r->key = pSUBKEYTYPE;
-          sprintf( r->u.value, "%d", subkey_algo);
-          r->next = para;
-          para = r;
-          r = xmalloc_clear( sizeof *r + 20 );
-          r->key = pSUBKEYUSAGE;
-          strcpy( r->u.value, "encrypt" );
-          r->next = para;
-          para = r;
 
-          if (algo == PUBKEY_ALGO_ECDSA
-              || algo == PUBKEY_ALGO_EDDSA
-              || algo == PUBKEY_ALGO_ECDH)
+              if (algo == PUBKEY_ALGO_ECDSA
+                  || algo == PUBKEY_ALGO_EDDSA
+                  || algo == PUBKEY_ALGO_ECDH)
+                {
+                  if (algo == PUBKEY_ALGO_EDDSA
+                      && subkey_algo == PUBKEY_ALGO_ECDH)
+                    {
+                      /* Need to switch to a different curve for the
+                         encryption key.  */
+                      xfree (curve);
+                      curve = xstrdup ("Curve25519");
+                    }
+                  r = xmalloc_clear (sizeof *r + strlen (curve));
+                  r->key = pSUBKEYCURVE;
+                  strcpy (r->u.value, curve);
+                  r->next = para;
+                  para = r;
+                }
+            }
+          else /* Create only a single key.  */
             {
-              if (algo == PUBKEY_ALGO_EDDSA
-                  && subkey_algo == PUBKEY_ALGO_ECDH)
+              /* For ECC we need to ask for the curve before storing the
+                 algo because ask_curve may change the algo.  */
+              if (algo == PUBKEY_ALGO_ECDSA
+                  || algo == PUBKEY_ALGO_EDDSA
+                  || algo == PUBKEY_ALGO_ECDH)
                 {
-                  /* Need to switch to a different curve for the
-                     encryption key.  */
-                  xfree (curve);
-                  curve = xstrdup ("Curve25519");
+                  curve = ask_curve (&algo, NULL);
+                  r = xmalloc_clear (sizeof *r + strlen (curve));
+                  r->key = pKEYCURVE;
+                  strcpy (r->u.value, curve);
+                  r->next = para;
+                  para = r;
                 }
-              r = xmalloc_clear (sizeof *r + strlen (curve));
-              r->key = pSUBKEYCURVE;
-              strcpy (r->u.value, curve);
+
+              r = xmalloc_clear( sizeof *r + 20 );
+              r->key = pKEYTYPE;
+              sprintf( r->u.value, "%d", algo );
               r->next = para;
               para = r;
+
+              if (use)
+                {
+                  r = xmalloc_clear( sizeof *r + 25 );
+                  r->key = pKEYUSAGE;
+                  sprintf( r->u.value, "%s%s%s",
+                           (use & PUBKEY_USAGE_SIG)? "sign ":"",
+                           (use & PUBKEY_USAGE_ENC)? "encrypt ":"",
+                           (use & PUBKEY_USAGE_AUTH)? "auth":"" );
+                  r->next = para;
+                  para = r;
+                }
+              nbits = 0;
             }
-        }
-      else /* Create only a single key.  */
-        {
-          /* For ECC we need to ask for the curve before storing the
-             algo because ask_curve may change the algo.  */
+
           if (algo == PUBKEY_ALGO_ECDSA
               || algo == PUBKEY_ALGO_EDDSA
               || algo == PUBKEY_ALGO_ECDH)
             {
-              curve = ask_curve (&algo, NULL);
-              r = xmalloc_clear (sizeof *r + strlen (curve));
-              r->key = pKEYCURVE;
-              strcpy (r->u.value, curve);
-              r->next = para;
-              para = r;
+              /* The curve has already been set.  */
             }
-
-          r = xmalloc_clear( sizeof *r + 20 );
-          r->key = pKEYTYPE;
-          sprintf( r->u.value, "%d", algo );
-          r->next = para;
-          para = r;
-
-          if (use)
+          else
             {
-              r = xmalloc_clear( sizeof *r + 25 );
-              r->key = pKEYUSAGE;
-              sprintf( r->u.value, "%s%s%s",
-                       (use & PUBKEY_USAGE_SIG)? "sign ":"",
-                       (use & PUBKEY_USAGE_ENC)? "encrypt ":"",
-                       (use & PUBKEY_USAGE_AUTH)? "auth":"" );
+              nbits = ask_keysize (both? subkey_algo : algo, nbits);
+              r = xmalloc_clear( sizeof *r + 20 );
+              r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
+              sprintf( r->u.value, "%u", nbits);
               r->next = para;
               para = r;
             }
-          nbits = 0;
-        }
 
-      if (algo == PUBKEY_ALGO_ECDSA
-          || algo == PUBKEY_ALGO_EDDSA
-          || algo == PUBKEY_ALGO_ECDH)
-        {
-          /* The curve has already been set.  */
-        }
-      else
-        {
-          nbits = ask_keysize (both? subkey_algo : algo, nbits);
-          r = xmalloc_clear( sizeof *r + 20 );
-          r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
-          sprintf( r->u.value, "%u", nbits);
-          r->next = para;
-          para = r;
+          xfree (curve);
         }
-
-      xfree (curve);
     }
   else /* Default key generation.  */
     {
+      int subalgo;
+      unsigned int size, subsize;
+      unsigned int keyuse, subkeyuse;
+      const char *curve, *subcurve;
+
       tty_printf ( _("Note: Use \"%s %s\""
                      " for a full featured key generation dialog.\n"),
 #if USE_GPG2_HACK
@@ -3947,17 +4336,28 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
 #else
                    GPG_NAME
 #endif
-                   , "--full-gen-key" );
-      para = quickgen_set_para (para, 0,
-                                DEFAULT_STD_ALGO, DEFAULT_STD_KEYSIZE,
-                                DEFAULT_STD_CURVE, 0);
-      para = quickgen_set_para (para, 1,
-                                DEFAULT_STD_SUBALGO, DEFAULT_STD_SUBKEYSIZE,
-                                DEFAULT_STD_SUBCURVE, 0);
+                   , "--full-generate-key" );
+
+      err = parse_key_parameter_string (NULL, -1,
+                                        &algo, &size, &keyuse, &curve,
+                                        &subalgo, &subsize,
+                                        &subkeyuse, &subcurve);
+      if (err)
+        {
+          log_error (_("Key generation failed: %s\n"), gpg_strerror (err));
+          return;
+        }
+      para = quickgen_set_para (para, 0, algo, size, curve, keyuse);
+      if (subalgo)
+        para = quickgen_set_para (para, 1,
+                                  subalgo, subsize, subcurve, subkeyuse);
+
+
     }
 
 
-  expire = full? ask_expire_interval (0, NULL) : 0;
+  expire = full? ask_expire_interval (0, NULL)
+               : parse_expire_string (default_expiration_interval);
   r = xcalloc (1, sizeof *r + 20);
   r->key = pKEYEXPIRE;
   r->u.expire = expire;
@@ -4162,6 +4562,9 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
   int did_sub = 0;
   u32 timestamp;
   char *cache_nonce = NULL;
+  int algo;
+  u32 expire;
+  const char *key_from_hexgrip = NULL;
 
   if (outctrl->dryrun)
     {
@@ -4227,20 +4630,26 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
      node of the subkey but that is more work than just to pass the
      current timestamp.  */
 
-  if (!card)
-    err = do_create (get_parameter_algo( para, pKEYTYPE, NULL ),
+  algo = get_parameter_algo( para, pKEYTYPE, NULL );
+  expire = get_parameter_u32( para, pKEYEXPIRE );
+  key_from_hexgrip = get_parameter_value (para, pKEYGRIP);
+  if (key_from_hexgrip)
+    err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip,
+                                  pub_root, timestamp, expire, 0);
+  else if (!card)
+    err = do_create (algo,
                      get_parameter_uint( para, pKEYLENGTH ),
                      get_parameter_value (para, pKEYCURVE),
                      pub_root,
                      timestamp,
-                     get_parameter_u32( para, pKEYEXPIRE ), 0,
+                     expire, 0,
                      outctrl->keygen_flags,
                      get_parameter_passphrase (para),
                      &cache_nonce, NULL);
   else
-    err = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root,
-                        &timestamp,
-                        get_parameter_u32 (para, pKEYEXPIRE));
+    err = gen_card_key (1, algo,
+                        1, pub_root, &timestamp,
+                        expire);
 
   /* Get the pointer to the generated public key packet.  */
   if (!err)
@@ -4277,8 +4686,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
 
   if (!err && card && get_parameter (para, pAUTHKEYTYPE))
     {
-      err = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root,
-                          &timestamp,
+      err = gen_card_key (3, get_parameter_algo( para, pAUTHKEYTYPE, NULL ),
+                          0, pub_root, &timestamp,
                           get_parameter_u32 (para, pKEYEXPIRE));
       if (!err)
         err = write_keybinding (pub_root, pri_psk, NULL,
@@ -4317,7 +4726,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
         }
       else
         {
-          err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, &timestamp,
+          err = gen_card_key (2, get_parameter_algo (para, pSUBKEYTYPE, NULL),
+                              0, pub_root, &timestamp,
                               get_parameter_u32 (para, pKEYEXPIRE));
         }
 
@@ -4381,8 +4791,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
           keyid_from_pk (pk, pk->main_keyid);
           register_trusted_keyid (pk->main_keyid);
 
-          update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK)
-                                  | TRUST_ULTIMATE ));
+         update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK)
+                                 | TRUST_ULTIMATE ));
 
           gen_standard_revoke (pk, cache_nonce);
 
@@ -4440,87 +4850,38 @@ parse_algo_usage_expire (ctrl_t ctrl, int for_subkey,
                          int *r_algo, unsigned int *r_usage, u32 *r_expire,
                          unsigned int *r_nbits, char **r_curve)
 {
+  gpg_error_t err;
   int algo;
   unsigned int use, nbits;
   u32 expire;
   int wantuse;
-  unsigned int min, def, max;
   const char *curve = NULL;
-  int eccalgo = 0;
 
   *r_curve = NULL;
 
   nbits = 0;
+
   /* Parse the algo string.  */
-  if (!algostr || !*algostr
-      || !strcmp (algostr, "default") || !strcmp (algostr, "-"))
-    {
-      algo  = for_subkey? DEFAULT_STD_SUBALGO    : DEFAULT_STD_ALGO;
-      use   = for_subkey? DEFAULT_STD_SUBKEYUSE  : DEFAULT_STD_KEYUSE;
-      nbits = for_subkey? DEFAULT_STD_SUBKEYSIZE : DEFAULT_STD_KEYSIZE;
-      curve = for_subkey? DEFAULT_STD_SUBCURVE   : DEFAULT_STD_CURVE;
-    }
-  else if (!strcmp (algostr, "future-default"))
-    {
-      algo  = for_subkey? FUTURE_STD_SUBALGO    : FUTURE_STD_ALGO;
-      use   = for_subkey? FUTURE_STD_SUBKEYUSE  : FUTURE_STD_KEYUSE;
-      nbits = for_subkey? FUTURE_STD_SUBKEYSIZE : FUTURE_STD_KEYSIZE;
-      curve = for_subkey? FUTURE_STD_SUBCURVE   : FUTURE_STD_CURVE;
-    }
-  else if (*algostr == '&' && strlen (algostr) == 41)
+  if (algostr && *algostr == '&' && strlen (algostr) == 41)
     {
       /* Take algo from existing key.  */
       algo = check_keygrip (ctrl, algostr+1);
       /* FIXME: We need the curve name as well.  */
       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
     }
-  else if (!strncmp (algostr, "rsa", 3))
-    {
-      algo = PUBKEY_ALGO_RSA;
-      use = for_subkey? DEFAULT_STD_SUBKEYUSE : DEFAULT_STD_KEYUSE;
-      if (algostr[3])
-        nbits = atoi (algostr + 3);
-    }
-  else if (!strncmp (algostr, "elg", 3))
-    {
-      algo = PUBKEY_ALGO_ELGAMAL_E;
-      use = PUBKEY_USAGE_ENC;
-      if (algostr[3])
-        nbits = atoi (algostr + 3);
-    }
-  else if (!strncmp (algostr, "dsa", 3))
-    {
-      algo = PUBKEY_ALGO_DSA;
-      use = PUBKEY_USAGE_SIG;
-      if (algostr[3])
-        nbits = atoi (algostr + 3);
-    }
-  else if ((curve = openpgp_is_curve_supported (algostr, &algo)))
-    {
-      if (!algo)
-        {
-          algo = PUBKEY_ALGO_ECDH; /* Default ECC algorithm.  */
-          eccalgo = 1;  /* Remember - we may need to fix it up.  */
-        }
 
-      if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA)
-        use = PUBKEY_USAGE_SIG;
-      else
-        use = PUBKEY_USAGE_ENC;
-    }
-  else
-    return gpg_error (GPG_ERR_INV_CURVE);
+  err = parse_key_parameter_string (algostr, for_subkey? 1 : 0,
+                                    &algo, &nbits, &use, &curve,
+                                    NULL, NULL, NULL, NULL);
+  if (err)
+    return err;
 
   /* Parse the usage string.  */
   if (!usagestr || !*usagestr
       || !strcmp (usagestr, "default") || !strcmp (usagestr, "-"))
-    ; /* Keep default usage */
+    ; /* Keep usage from parse_key_parameter_string.  */
   else if ((wantuse = parse_usagestr (usagestr)) != -1)
-    {
-      use = wantuse;
-      if (eccalgo && !(use & PUBKEY_USAGE_ENC))
-        algo = PUBKEY_ALGO_ECDSA; /* Switch from ECDH to ECDSA.  */
-    }
+    use = wantuse;
   else
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -4528,7 +4889,9 @@ parse_algo_usage_expire (ctrl_t ctrl, int for_subkey,
   if (!for_subkey)
     use |= PUBKEY_USAGE_CERT;
 
-  /* Check that usage is possible.  */
+  /* Check that usage is possible.  NB: We have the same check in
+   * parse_key_parameter_string but need it here again in case the
+   * separate usage value has been given. */
   if (/**/((use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT))
            && !pubkey_get_nsig (algo))
        || ((use & PUBKEY_USAGE_ENC)
@@ -4541,17 +4904,6 @@ parse_algo_usage_expire (ctrl_t ctrl, int for_subkey,
   if (expire == (u32)-1 )
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  /* Make sure the keysize is in the allowed range.  */
-  get_keysize_range (algo, &min, &def, &max);
-  if (!nbits)
-    nbits = def;
-  else if (nbits < min)
-    nbits = min;
-  else if (nbits > max)
-    nbits = max;
-
-  nbits = fixup_keysize (nbits, algo, 1);
-
   if (curve)
     {
       *r_curve = xtrystrdup (curve);
@@ -4749,15 +5101,25 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
   gpg_error_t err = 0;
   kbnode_t node;
   PKT_public_key *pri_pk = NULL;
-  int algo;
   unsigned int use;
   u32 expire;
   u32 cur_time;
   struct para_data_s *para = NULL;
   PKT_public_key *sub_pk = NULL;
+  int algo;
+  struct agent_card_info_s info;
 
   log_assert (keyno >= 1 && keyno <= 3);
 
+  memset (&info, 0, sizeof (info));
+  err = agent_scd_getattr ("KEY-ATTR", &info);
+  if (err)
+    {
+      log_error (_("error getting current key info: %s\n"), gpg_strerror (err));
+      return err;
+    }
+  algo = info.key_attr[keyno-1].algo;
+
   para = xtrycalloc (1, sizeof *para + strlen (serialno) );
   if (!para)
     {
@@ -4771,7 +5133,7 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
   node = find_kbnode (pub_keyblock, PKT_PUBLIC_KEY);
   if (!node)
     {
-      log_error ("Oops; publkic key lost!\n");
+      log_error ("Oops; public key lost!\n");
       err = gpg_error (GPG_ERR_INTERNAL);
       goto leave;
     }
@@ -4800,7 +5162,6 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
       goto leave;
     }
 
-  algo = PUBKEY_ALGO_RSA;
   expire = ask_expire_interval (0, NULL);
   if (keyno == 1)
     use = PUBKEY_USAGE_SIG;
@@ -4817,7 +5178,7 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
 
   /* Note, that depending on the backend, the card key generation may
      update CUR_TIME.  */
-  err = gen_card_key (algo, keyno, 0, pub_keyblock, &cur_time, expire);
+  err = gen_card_key (keyno, algo, 0, pub_keyblock, &cur_time, expire);
   /* Get the pointer to the generated public subkey packet.  */
   if (!err)
     {
@@ -4865,7 +5226,7 @@ write_keyblock( IOBUF out, KBNODE node )
 
 /* Note that timestamp is an in/out arg. */
 static gpg_error_t
-gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
+gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root,
               u32 *timestamp, u32 expireval)
 {
 #ifdef ENABLE_CARD_SUPPORT
@@ -4878,9 +5239,6 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
 
   snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno);
 
-  if (algo != PUBKEY_ALGO_RSA)
-    return gpg_error (GPG_ERR_PUBKEY_ALGO);
-
   pk = xtrycalloc (1, sizeof *pk );
   if (!pk)
     return gpg_error_from_syserror ();
@@ -4928,8 +5286,8 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
   if (algo == PUBKEY_ALGO_RSA)
     err = key_from_sexp (pk->pkey, s_key, "public-key", "ne");
   else if (algo == PUBKEY_ALGO_ECDSA
-          || algo == PUBKEY_ALGO_EDDSA
-          || algo == PUBKEY_ALGO_ECDH )
+           || algo == PUBKEY_ALGO_EDDSA
+           || algo == PUBKEY_ALGO_ECDH )
     err = ecckey_from_sexp (pk->pkey, s_key, algo);
   else
     err = gpg_error (GPG_ERR_PUBKEY_ALGO);
@@ -4954,7 +5312,6 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
 
   return 0;
 #else
-  (void)algo;
   (void)keyno;
   (void)is_primary;
   (void)pub_root;