Use macros for iobuf ioctls.
[gnupg.git] / g10 / keygen.c
index 4e8dd50..9d5d39d 100644 (file)
@@ -1,6 +1,6 @@
 /* keygen.c - generate a key pair
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- *               2006, 2007 Free Software Foundation, Inc.
+ *               2006, 2007, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include "keyserver-internal.h"
 #include "call-agent.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    GCRY_PK_RSA
+#define DEFAULT_STD_KEYSIZE 2048
+
 
 #define MAX_PREFS 30 
 
@@ -301,7 +307,7 @@ keygen_set_std_prefs (const char *string,int personal)
     byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS];
     int nsym=0, nhash=0, nzip=0, val, rc=0;
     int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */
-    char dummy_string[45+1]; /* Enough for 15 items. */
+    char dummy_string[20*4+1]; /* Enough for 20 items. */
 
     if (!string || !ascii_strcasecmp (string, "default"))
       {
@@ -345,14 +351,29 @@ keygen_set_std_prefs (const char *string,int personal)
            if ( !openpgp_cipher_test_algo (CIPHER_ALGO_IDEA) )
              strcat(dummy_string,"S1 ");
 
-           /* SHA-1 */
-           strcat(dummy_string,"H2 ");
 
-           if (!openpgp_md_test_algo(DIGEST_ALGO_SHA256))
-             strcat(dummy_string,"H8 ");
+            /* The default hash algo order is:
+                 SHA-256, SHA-1, SHA-384, SHA-512, SHA-224.
+               Ordering SHA-1 before SHA-384 might be viewed as a bit
+               strange; it is done because we expect that soon enough
+               SHA-3 will be available and at that point there should
+               be no more need for SHA-384 etc.  Anyway this order is
+               just a default and can easily be changed by a config
+               option.  */
+           if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256))
+             strcat (dummy_string, "H8 ");
+
+           strcat (dummy_string, "H2 "); /* SHA-1 */
+
+           if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384))
+             strcat (dummy_string, "H9 ");
+
+           if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512))
+             strcat (dummy_string, "H10 ");
+
+           if (!openpgp_md_test_algo (DIGEST_ALGO_SHA224))
+             strcat (dummy_string, "H11 ");
 
-           /* RIPEMD160 */
-           strcat(dummy_string,"H3 ");
 
            /* ZLIB */
            strcat(dummy_string,"Z2 ");
@@ -506,7 +527,8 @@ keygen_set_std_prefs (const char *string,int personal)
 
 /* Return a fake user ID containing the preferences.  Caller must
    free. */
-PKT_user_id *keygen_get_std_prefs(void)
+PKT_user_id *
+keygen_get_std_prefs(void)
 {
   int i,j=0;
   PKT_user_id *uid=xmalloc_clear(sizeof(PKT_user_id));
@@ -631,42 +653,45 @@ add_keyserver_modify (PKT_signature *sig,int enabled)
   xfree (buf);
 }
 
+
 int
-keygen_upd_std_prefs( PKT_signature *sig, void *opaque )
+keygen_upd_std_prefs (PKT_signature *sig, void *opaque)
 {
-    if (!prefs_initialized)
-        keygen_set_std_prefs (NULL, 0);
-
-    if (nsym_prefs) 
-        build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs);
-    else
-      {
-        delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM);
-        delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM);
-      }
-
-    if (nhash_prefs)
-        build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs);
-    else
-      {
-       delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH);
-       delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH);
-      }
-
-    if (nzip_prefs)
-        build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs);
-    else
-      {
-        delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR);
-        delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR);
-      }
+  (void)opaque;
+  
+  if (!prefs_initialized)
+    keygen_set_std_prefs (NULL, 0);
+  
+  if (nsym_prefs) 
+    build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs);
+  else
+    {
+      delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM);
+      delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM);
+    }
+  
+  if (nhash_prefs)
+    build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs);
+  else
+    {
+      delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH);
+      delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH);
+    }
 
-    /* Make sure that the MDC feature flag is set if needed */
-    add_feature_mdc (sig,mdc_available);
-    add_keyserver_modify (sig,ks_modify);
-    keygen_add_keyserver_url(sig,NULL);
+  if (nzip_prefs)
+    build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs);
+  else
+    {
+      delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR);
+      delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR);
+    }
+  
+  /* Make sure that the MDC feature flag is set if needed.  */
+  add_feature_mdc (sig,mdc_available);
+  add_keyserver_modify (sig,ks_modify);
+  keygen_add_keyserver_url(sig,NULL);
 
-    return 0;
+  return 0;
 }
 
 
@@ -1102,6 +1127,8 @@ genhelp_protect (DEK *dek, STRING2KEY *s2k, PKT_secret_key *sk)
 static void
 genhelp_factors (gcry_sexp_t misc_key_info, KBNODE sec_root)
 {
+  (void)misc_key_info;
+  (void)sec_root;
 #if 0 /* Not used anymore */
   size_t n;
   char *buf;
@@ -1257,7 +1284,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
   gcry_sexp_t misc_key_info;
   unsigned int qbits;
 
-  if ( nbits < 512 || (!opt.flags.dsa2 && nbits > 1024)
+  if ( nbits < 512) 
     {
       nbits = 1024;
       log_info(_("keysize invalid; using %u bits\n"), nbits );
@@ -1274,6 +1301,14 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
       log_info(_("keysize rounded up to %u bits\n"), nbits );
     }
 
+  /* To comply with FIPS rules we round up to the next value unless in
+     expert mode.  */
+  if (!opt.expert && nbits > 1024 && (nbits % 1024))
+    {
+      nbits = ((nbits + 1023) / 1024) * 1024;
+      log_info(_("keysize rounded up to %u bits\n"), nbits );
+    }
+
   /*
     Figure out a q size based on the key size.  FIPS 180-3 says:
  
@@ -1285,11 +1320,11 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
     2048/256 is an odd pair since there is also a 2048/224 and
     3072/256.  Matching sizes is not a very exact science.
       
-    We'll do 256 qbits for nbits over 2048, 224 for nbits over 1024
+    We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024
     but less than 2048, and 160 for 1024 (DSA1).
   */
  
-  if (nbits > 2048)
+  if (nbits > 2047)
     qbits = 256;
   else if ( nbits > 1024)
     qbits = 224;
@@ -1397,6 +1432,9 @@ gen_rsa (int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
 
   assert (is_RSA(algo));
 
+  if (!nbits)
+    nbits = DEFAULT_STD_KEYSIZE;
+
   if (nbits < 1024) 
     {
       nbits = 1024;
@@ -1549,7 +1587,7 @@ ask_key_flags(int algo,int subkey)
   unsigned int current=0;
   unsigned int possible=openpgp_pk_algo_usage(algo);
 
-  if ( strlen(togglers) != 7 )
+  if ( strlen(togglers) != 8 )
     {
       tty_printf ("NOTE: Bad translation at %s:%d. "
                   "Please report.\n", __FILE__, __LINE__);
@@ -1630,101 +1668,140 @@ ask_key_flags(int algo,int subkey)
 }
 
 
-/****************
- * Returns: 0 to create both a DSA and a Elgamal key.
- *          and only if key flags are to be written the desired usage.
- */
+/* Ask for an algorithm.  The function returns the algorithm id to
+ * create. If ADDMODE is false the function won't show an option to
+ * create the primary and subkey combined and won't set R_USAGE
+ * either.  If a combined algorithm has been selected, the subkey
+ * algorithm is stored at R_SUBKEY_ALGO.  */
 static int
-ask_algo (int addmode, unsigned int *r_usage)
+ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
 {
-    char *answer;
-    int algo;
-
-    *r_usage = 0;
-    tty_printf(_("Please select what kind of key you want:\n"));
-    if( !addmode )
-       tty_printf(_("   (%d) DSA and Elgamal (default)\n"), 1 );
-    tty_printf(    _("   (%d) DSA (sign only)\n"), 2 );
-    if (opt.expert)
-      tty_printf(  _("   (%d) DSA (set your own capabilities)\n"), 3 );
-    if( addmode )
-       tty_printf(_("   (%d) Elgamal (encrypt only)\n"), 4 );
-    tty_printf(    _("   (%d) RSA (sign only)\n"), 5 );
-    if (addmode)
-        tty_printf(_("   (%d) RSA (encrypt only)\n"), 6 );
-    if (opt.expert)
-      tty_printf(  _("   (%d) RSA (set your own capabilities)\n"), 7 );
+  char *answer;
+  int algo;
+  int dummy_algo;
 
-    for(;;) {
-       answer = cpr_get("keygen.algo",_("Your selection? "));
-       cpr_kill_prompt();
-       algo = *answer? atoi(answer): 1;
-       xfree(answer);
-       if( algo == 1 && !addmode ) {
-           algo = 0;   /* create both keys */
-           break;
+  if (!r_subkey_algo)
+    r_subkey_algo = &dummy_algo;
+  
+  tty_printf (_("Please select what kind of key you want:\n"));
+
+  if (!addmode)
+    tty_printf (_("   (%d) RSA and RSA (default)\n"), 1 );
+  if (!addmode)
+    tty_printf (_("   (%d) DSA and Elgamal\n"), 2 );
+
+  tty_printf (_("   (%d) DSA (sign only)\n"), 3 );
+  tty_printf (_("   (%d) RSA (sign only)\n"), 4 );
+
+  if (addmode)
+    {
+      tty_printf (_("   (%d) Elgamal (encrypt only)\n"), 5 );
+      tty_printf (_("   (%d) RSA (encrypt only)\n"), 6 );
+    }
+  if (opt.expert)
+    {
+      tty_printf (_("   (%d) DSA (set your own capabilities)\n"), 7 );
+      tty_printf (_("   (%d) RSA (set your own capabilities)\n"), 8 );
+    }
+  
+  for(;;)
+    {
+      *r_usage = 0;
+      *r_subkey_algo = 0;
+      answer = cpr_get ("keygen.algo", _("Your selection? "));
+      cpr_kill_prompt ();
+      algo = *answer? atoi (answer) : 1;
+      xfree(answer);
+      if (algo == 1 && !addmode)
+        {
+          algo = PUBKEY_ALGO_RSA;
+          *r_subkey_algo = PUBKEY_ALGO_RSA;
+          break;
        }
-       else if( algo == 7 && opt.expert ) {
-           algo = PUBKEY_ALGO_RSA;
-           *r_usage=ask_key_flags(algo,addmode);
-           break;
+      else if (algo == 2 && !addmode)
+        {
+          algo = PUBKEY_ALGO_DSA;
+          *r_subkey_algo = PUBKEY_ALGO_ELGAMAL_E;
+          break;
        }
-       else if( algo == 6 && addmode ) {
-           algo = PUBKEY_ALGO_RSA;
-            *r_usage = PUBKEY_USAGE_ENC;
-           break;
+      else if (algo == 3)
+        {
+          algo = PUBKEY_ALGO_DSA;
+          *r_usage = PUBKEY_USAGE_SIG;
+          break;
        }
-       else if( algo == 5 ) {
-           algo = PUBKEY_ALGO_RSA;
-            *r_usage = PUBKEY_USAGE_SIG;
-           break;
+      else if (algo == 4)
+        {
+          algo = PUBKEY_ALGO_RSA;
+          *r_usage = PUBKEY_USAGE_SIG;
+          break;
        }
-       else if( algo == 4 && addmode ) {
-           algo = PUBKEY_ALGO_ELGAMAL_E;
-            *r_usage = PUBKEY_USAGE_ENC;
-           break;
+      else if (algo == 5 && addmode)
+        {
+          algo = PUBKEY_ALGO_ELGAMAL_E;
+          *r_usage = PUBKEY_USAGE_ENC;
+          break;
        }
-       else if( algo == 3 && opt.expert ) {
-           algo = PUBKEY_ALGO_DSA;
-           *r_usage=ask_key_flags(algo,addmode);
-           break;
+      else if (algo == 6 && addmode)
+        {
+          algo = PUBKEY_ALGO_RSA;
+          *r_usage = PUBKEY_USAGE_ENC;
+          break;
        }
-       else if( algo == 2 ) {
-           algo = PUBKEY_ALGO_DSA;
-            *r_usage = PUBKEY_USAGE_SIG;
-           break;
+      else if (algo == 7 && opt.expert)
+        {
+          algo = PUBKEY_ALGO_DSA;
+          *r_usage = ask_key_flags (algo, addmode);
+          break;
        }
-       else
-           tty_printf(_("Invalid selection.\n"));
+      else if (algo == 8 && opt.expert)
+        {
+          algo = PUBKEY_ALGO_RSA;
+          *r_usage = ask_key_flags (algo, addmode);
+          break;
+       }
+      else
+        tty_printf (_("Invalid selection.\n"));
     }
-
-    return algo;
+  
+  return algo;
 }
 
 
+/* Ask for the key size.  ALGO is the algorithm.  If PRIMARY_KEYSIZE
+   is not 0, the function asks for the size of the encryption
+   subkey. */
 static unsigned
-ask_keysize( int algo )
+ask_keysize (int algo, unsigned int primary_keysize)
 {
-  unsigned int nbits, min, def=2048, max=4096;
+  unsigned int nbits, min, def = DEFAULT_STD_KEYSIZE, max=4096;
+  int for_subkey = !!primary_keysize;
+  int autocomp = 0;
 
   if(opt.expert)
     min=512;
   else
     min=1024;
 
+  if (primary_keysize && !opt.expert)
+    {
+      /* Deduce the subkey size from the primary key size.  */
+      if (algo == PUBKEY_ALGO_DSA && primary_keysize > 3072)
+        nbits = 3072; /* For performance reasons we don't support more
+                         than 3072 bit DSA.  However we won't see this
+                         case anyway because DSA can't be used as an
+                         encryption subkey ;-). */
+      else
+        nbits = primary_keysize;
+      autocomp = 1;
+      goto leave;
+    }
+
   switch(algo)
     {
     case PUBKEY_ALGO_DSA:
-      if(opt.flags.dsa2)
-       {
-         def=1024;
-         max=3072;
-       }
-      else
-       {
-         tty_printf(_("DSA keypair will have %u bits.\n"),1024);
-         return 1024;
-       }
+      def=2048;
+      max=3072;
       break;
 
     case PUBKEY_ALGO_RSA:
@@ -1737,18 +1814,16 @@ ask_keysize( int algo )
 
   for(;;)
     {
-      char *prompt,*answer;
+      char *prompt, *answer;
 
-#define PROMPTSTRING _("What keysize do you want? (%u) ")
-
-      prompt=xmalloc(strlen(PROMPTSTRING)+20);
-      sprintf(prompt,PROMPTSTRING,def);
-
-#undef PROMPTSTRING
-
-      answer = cpr_get("keygen.size",prompt);
-      cpr_kill_prompt();
-      nbits = *answer? atoi(answer): def;
+      if (for_subkey)
+        prompt = xasprintf (_("What keysize do you want "
+                              "for the subkey? (%u) "), def);
+      else
+        prompt = xasprintf (_("What keysize do you want? (%u) "), def);
+      answer = cpr_get ("keygen.size", prompt);
+      cpr_kill_prompt ();
+      nbits = *answer? atoi (answer): def;
       xfree(prompt);
       xfree(answer);
       
@@ -1761,15 +1836,18 @@ ask_keysize( int algo )
 
   tty_printf(_("Requested keysize is %u bits\n"), nbits );
 
+ leave:
   if( algo == PUBKEY_ALGO_DSA && (nbits % 64) )
     {
       nbits = ((nbits + 63) / 64) * 64;
-      tty_printf(_("rounded up to %u bits\n"), nbits );
+      if (!autocomp)
+        tty_printf(_("rounded up to %u bits\n"), nbits );
     }
   else if( (nbits % 32) )
     {
       nbits = ((nbits + 31) / 32) * 32;
-      tty_printf(_("rounded up to %u bits\n"), nbits );
+      if (!autocomp)
+        tty_printf(_("rounded up to %u bits\n"), nbits );
     }
 
   return nbits;
@@ -1789,21 +1867,27 @@ ask_keysize( int algo )
 u32
 parse_expire_string( const char *string )
 {
-    int mult;
-    u32 seconds,abs_date=0,curtime = make_timestamp();
-
-    if( !*string )
-      seconds = 0;
-    else if ( !strncmp (string, "seconds=", 8) )
-      seconds = atoi (string+8);
-    else if( (abs_date = scan_isodatestr(string)) && abs_date > curtime )
-      seconds = abs_date - curtime;
-    else if( (mult=check_valid_days(string)) )
-      seconds = atoi(string) * 86400L * mult;
-    else
-      seconds=(u32)-1;
-
-    return seconds;
+  int mult;
+  u32 seconds;
+  u32 abs_date = 0;
+  u32 curtime = make_timestamp ();
+  time_t tt;
+  
+  if (!*string)
+    seconds = 0;
+  else if (!strncmp (string, "seconds=", 8))
+    seconds = atoi (string+8);
+  else if ((abs_date = scan_isodatestr(string))
+           && (abs_date+86400/2) > curtime)
+    seconds = (abs_date+86400/2) - curtime;
+  else if ((tt = isotime2epoch (string)) != (time_t)(-1))
+    seconds = (u32)tt - curtime;
+  else if ((mult = check_valid_days (string)))
+    seconds = atoi (string) * 86400L * mult;
+  else
+    seconds = (u32)(-1);
+  
+  return seconds;
 }
 
 /* Parsean Creation-Date string which is either "1986-04-26" or
@@ -1916,7 +2000,13 @@ ask_expire_interval(int object,const char *def_expire)
              tty_printf (_("Your system can't display dates beyond 2038.\n"
                             "However, it will be correctly handled up to"
                             " 2106.\n"));
+            else
 #endif /*SIZEOF_TIME_T*/
+              if ( (time_t)((unsigned long)(curtime+interval)) < curtime )
+                {
+                  tty_printf (_("invalid value\n"));
+                  continue;
+                }
          }
 
        if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay",
@@ -1936,14 +2026,39 @@ ask_expiredate()
 }
 
 
+
+static PKT_user_id *
+uid_from_string (const char *string)
+{
+  size_t n;
+  PKT_user_id *uid;
+
+  n = strlen (string);
+  uid = xmalloc_clear (sizeof *uid + n);
+  uid->len = n;
+  strcpy (uid->name, string);
+  uid->ref = 1;
+  return uid;
+}
+
+
+/* Ask for a user ID.  With a MODE of 1 an extra help prompt is
+   printed for use during a new key creation.  If KEYBLOCK is not NULL
+   the function prevents the creation of an already existing user
+   ID.  */
 static char *
-ask_user_id( int mode )
+ask_user_id (int mode, KBNODE keyblock)
 {
     char *answer;
     char *aname, *acomment, *amail, *uid;
 
     if ( !mode )
       {
+        /* TRANSLATORS: This is the new string telling the user what
+           gpg is now going to do (i.e. ask for the parts of the user
+           ID).  Note that if you do not tyranslated this string, a
+           different string will be used used, which might still have
+           a correct transaltion.  */
        const char *s1 =
           N_("\n"
              "GnuPG needs to construct a user ID to identify your key.\n"
@@ -1956,6 +2071,10 @@ ask_user_id( int mode )
                the old info text.  gettext has no way to tell whether
                a translation is actually available, thus we need to
                to compare again. */
+            /* TRANSLATORS: This string is in general not anymore used
+               but you should keep your existing translation.  In case
+               the new string is not translated this old string will
+               be used. */
             const char *s3 = N_("\n"
 "You need a user ID to identify your key; "
                                         "the software constructs the user ID\n"
@@ -2044,14 +2163,29 @@ ask_user_id( int mode )
        }
 
        tty_printf(_("You selected this USER-ID:\n    \"%s\"\n\n"), uid);
-       /* fixme: add a warning if this user-id already exists */
+
        if( !*amail && !opt.allow_freeform_uid
            && (strchr( aname, '@' ) || strchr( acomment, '@'))) {
            fail = 1;
-           tty_printf(_("Please don't put the email address "
-                         "into the real name or the comment\n") );
+            tty_printf(_("Please don't put the email address "
+                         "into the real name or the comment\n") );
        }
 
+        if (!fail && keyblock)
+          {
+            PKT_user_id *uidpkt = uid_from_string (uid);
+            KBNODE node;
+
+            for (node=keyblock; node && !fail; node=node->next)
+              if (!is_deleted_kbnode (node)
+                  && node->pkt->pkttype == PKT_USER_ID
+                  && !cmp_user_ids (uidpkt, node->pkt->pkt.user_id))
+               fail = 1;
+            if (fail)
+              tty_printf (_("Such a user ID already exists on this key!\n"));
+            free_user_id (uidpkt);
+          }
+
        for(;;) {
             /* TRANSLATORS: These are the allowed answers in
                lower and uppercase.  Below you will find the matching
@@ -2069,7 +2203,7 @@ ask_user_id( int mode )
            if( strlen(ansstr) != 10 )
                BUG();
            if( cpr_enabled() ) {
-               answer = xstrdup(ansstr+6);
+                answer = xstrdup (ansstr + (fail?8:6));
                answer[1] = 0;
            }
            else {
@@ -2126,21 +2260,28 @@ ask_user_id( int mode )
 }
 
 
+/*  MODE  0 - standard
+          1 - Ask for passphrase of the card backup key.  */
 static DEK *
-do_ask_passphrase ( STRING2KEY **ret_s2k, int *r_canceled )
+do_ask_passphrase (STRING2KEY **ret_s2k, int mode, int *r_canceled)
 {
     DEK *dek = NULL;
     STRING2KEY *s2k;
     const char *errtext = NULL;
+    const char *custdesc = NULL;
 
     tty_printf(_("You need a Passphrase to protect your secret key.\n\n") );
 
+    if (mode == 1)
+      custdesc = _("Please enter a passphrase to protect the off-card "
+                   "backup of the new encryption key.");
+
     s2k = xmalloc_secure( sizeof *s2k );
     for(;;) {
        s2k->mode = opt.s2k_mode;
        s2k->hash_algo = S2K_DIGEST_ALGO;
-       dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k,2,
-                                 errtext, r_canceled);
+       dek = passphrase_to_dek_ext (NULL, 0, opt.s2k_cipher_algo, s2k, 2,
+                                     errtext, custdesc, NULL, r_canceled);
         if (!dek && *r_canceled) {
            xfree(dek); dek = NULL;
            xfree(s2k); s2k = NULL;
@@ -2199,25 +2340,18 @@ do_create (int algo, unsigned int nbits, KBNODE pub_root, KBNODE sec_root,
 }
 
 
-/****************
- * Generate a new user id packet, or return NULL if canceled
- */
+/* Generate a new user id packet or return NULL if canceled.  If
+   KEYBLOCK is not NULL the function prevents the creation of an
  already existing user ID.  */
 PKT_user_id *
-generate_user_id()
+generate_user_id (KBNODE keyblock)
 {
-    PKT_user_id *uid;
-    char *p;
-    size_t n;
-
-    p = ask_user_id( 1 );
-    if( !p )
-       return NULL;
-    n = strlen(p);
-    uid = xmalloc_clear( sizeof *uid + n );
-    uid->len = n;
-    strcpy(uid->name, p);
-    uid->ref = 1;
-    return uid;
+  char *p;
+  
+  p = ask_user_id (1, keyblock);
+  if (!p)
+    return NULL;  /* Canceled. */
+  return uid_from_string (p);
 }
 
 
@@ -2255,26 +2389,42 @@ get_parameter_value( struct para_data_s *para, enum para_name key )
 }
 
 static int
-get_parameter_algo( struct para_data_s *para, enum para_name key )
+get_parameter_algo( struct para_data_s *para, enum para_name key, 
+                    int *r_default)
 {
-    int i;
-    struct para_data_s *r = get_parameter( para, key );
-    if( !r )
-       return -1;
-    if( digitp( r->u.value ) )
-       i = atoi( r->u.value );
-    else if ( !strcmp ( r->u.value, "ELG-E")
-              || !strcmp ( r->u.value, "ELG") )
-        i = GCRY_PK_ELG_E;
-    else
-        i = gcry_pk_map_name (r->u.value);
-    if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S)
-      i = 0; /* we don't want to allow generation of these algorithms */
-    return i;
+  int i;
+  struct para_data_s *r = get_parameter( para, key );
+
+  if (r_default)
+    *r_default = 0;
+
+  if (!r)
+    return -1;
+
+  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;
+      if (r_default)
+        *r_default = 1;
+    }
+  else if (digitp (r->u.value))
+    i = atoi( r->u.value );
+  else if (!strcmp (r->u.value, "ELG-E")
+           || !strcmp (r->u.value, "ELG"))
+    i = GCRY_PK_ELG_E;
+  else
+    i = gcry_pk_map_name (r->u.value);
+
+  if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S)
+    i = 0; /* we don't want to allow generation of these algorithms */
+  return i;
 }
 
 /* 
- * parse the usage parameter and set the keyflags.  Return true on error.
+ * Parse the usage parameter and set the keyflags.  Returns -1 on
+ * error, 0 for no usage given or 1 for usage available.
  */
 static int
 parse_parameter_usage (const char *fname,
@@ -2413,63 +2563,92 @@ proc_parameter_file( struct para_data_s *para, const char *fname,
   const char *s1, *s2, *s3;
   size_t n;
   char *p;
-  int have_user_id=0,err,algo;
+  int is_default = 0;
+  int have_user_id = 0;
+  int err, algo;
 
   /* Check that we have all required parameters. */
   r = get_parameter( para, pKEYTYPE );
   if(r)
     {
-      algo=get_parameter_algo(para,pKEYTYPE);
+      algo = get_parameter_algo (para, pKEYTYPE, &is_default);
       if (openpgp_pk_test_algo2 (algo, PUBKEY_USAGE_SIG))
        {
-         log_error("%s:%d: invalid algorithm\n", fname, r->lnr );
+         log_error ("%s:%d: invalid algorithm\n", fname, r->lnr );
          return -1;
        }
     }
   else
     {
-      log_error("%s: no Key-Type specified\n",fname);
+      log_error ("%s: no Key-Type specified\n",fname);
       return -1;
     }
 
-  err=parse_parameter_usage (fname, para, pKEYUSAGE);
-  if(err==0)
+  err = parse_parameter_usage (fname, para, pKEYUSAGE);
+  if (!err)
     {
-      /* Default to algo capabilities if key-usage is not provided */
-      r=xmalloc_clear(sizeof(*r));
-      r->key=pKEYUSAGE;
-      r->u.usage=openpgp_pk_algo_usage(algo);
-      r->next=para;
-      para=r;
+      /* Default to algo capabilities if key-usage is not provided and
+         no default algorithm has been requested.  */
+      r = xmalloc_clear(sizeof(*r));
+      r->key = pKEYUSAGE;
+      r->u.usage = (is_default
+                    ? (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG)
+                    : openpgp_pk_algo_usage(algo));
+      r->next = para;
+      para = r;
     }
-  else if(err==-1)
+  else if (err == -1)
     return -1;
+  else
+    {
+      r = get_parameter (para, pKEYUSAGE);
+      if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo)))
+        {
+          log_error ("%s:%d: specified Key-Usage not allowed for algo %d\n",
+                     fname, r->lnr, algo);
+          return -1;
+        }
+    }
 
+  is_default = 0;
   r = get_parameter( para, pSUBKEYTYPE );
   if(r)
     {
-      algo=get_parameter_algo( para, pSUBKEYTYPE);
+      algo = get_parameter_algo (para, pSUBKEYTYPE, &is_default);
       if (openpgp_pk_test_algo (algo))
        {
-         log_error("%s:%d: invalid algorithm\n", fname, r->lnr );
+         log_error ("%s:%d: invalid algorithm\n", fname, r->lnr );
          return -1;
        }
 
-      err=parse_parameter_usage (fname, para, pSUBKEYUSAGE);
-      if(err==0)
+      err = parse_parameter_usage (fname, para, pSUBKEYUSAGE);
+      if (!err)
        {
          /* Default to algo capabilities if subkey-usage is not
             provided */
-         r=xmalloc_clear(sizeof(*r));
-         r->key=pSUBKEYUSAGE;
-         r->u.usage=openpgp_pk_algo_usage(algo);
-         r->next=para;
-         para=r;
+         r = xmalloc_clear (sizeof(*r));
+         r->key = pSUBKEYUSAGE;
+         r->u.usage = (is_default
+                        ? PUBKEY_USAGE_ENC
+                        : openpgp_pk_algo_usage (algo));
+         r->next = para;
+         para = r;
        }
-      else if(err==-1)
+      else if (err == -1)
        return -1;
+      else
+        {
+          r = get_parameter (para, pSUBKEYUSAGE);
+          if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo)))
+            {
+              log_error ("%s:%d: specified Subkey-Usage not allowed"
+                         " for algo %d\n", fname, r->lnr, algo);
+              return -1;
+            }
+        }
     }
 
+
   if( get_parameter_value( para, pUSERID ) )
     have_user_id=1;
   else
@@ -2538,7 +2717,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname,
       STRING2KEY *s2k;
       DEK *dek;
 
-      dek = do_ask_passphrase ( &s2k, &canceled );
+      dek = do_ask_passphrase (&s2k, 0, &canceled);
       if (dek)
         {
           r = xmalloc_clear( sizeof *r );
@@ -2698,7 +2877,7 @@ read_parameter_file( const char *fname )
       log_error (_("can't open `%s': %s\n"), fname, strerror(errno) );
       return;
     }
-    iobuf_ioctl (fp, 3, 1, NULL); /* No file caching. */
+    iobuf_ioctl (fp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
 
     lnr = 0;
     err = NULL;
@@ -2839,9 +3018,11 @@ read_parameter_file( const char *fname )
 
         /* Must invalidate that ugly cache to actually close it.  */
         if (outctrl.pub.fname)
-          iobuf_ioctl (NULL, 2, 0, (char*)outctrl.pub.fname);
+          iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 
+                       0, (char*)outctrl.pub.fname);
         if (outctrl.sec.fname)
-          iobuf_ioctl (NULL, 2, 0, (char*)outctrl.sec.fname);
+          iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE,
+                       0, (char*)outctrl.sec.fname);
 
        xfree( outctrl.pub.fname );
        xfree( outctrl.pub.newfname );
@@ -2947,16 +3128,19 @@ generate_keypair (const char *fname, const char *card_serialno,
     }
   else
     {
-      algo = ask_algo( 0, &use );
-      if( !algo )
-        { /* default: DSA with ElG subkey of the specified size */
+      int subkey_algo; 
+
+      algo = ask_algo (0, &subkey_algo, &use);
+      if (subkey_algo)
+        { 
+          /* Create primary and subkey at once.  */
           both = 1;
           r = xmalloc_clear( sizeof *r + 20 );
           r->key = pKEYTYPE;
-          sprintf( r->u.value, "%d", PUBKEY_ALGO_DSA );
+          sprintf( r->u.value, "%d", algo );
           r->next = para;
           para = r;
-         nbits = ask_keysize( PUBKEY_ALGO_DSA );
+         nbits = ask_keysize (algo, 0);
          r = xmalloc_clear( sizeof *r + 20 );
          r->key = pKEYLENGTH;
          sprintf( r->u.value, "%u", nbits);
@@ -2968,10 +3152,9 @@ generate_keypair (const char *fname, const char *card_serialno,
           r->next = para;
           para = r;
            
-          algo = PUBKEY_ALGO_ELGAMAL_E;
           r = xmalloc_clear( sizeof *r + 20 );
           r->key = pSUBKEYTYPE;
-          sprintf( r->u.value, "%d", algo );
+          sprintf( r->u.value, "%d", subkey_algo);
           r->next = para;
           para = r;
           r = xmalloc_clear( sizeof *r + 20 );
@@ -2999,10 +3182,10 @@ generate_keypair (const char *fname, const char *card_serialno,
               r->next = para;
               para = r;
             }
-           
+          nbits = 0;
         }
 
-      nbits = ask_keysize( algo );
+      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);
@@ -3022,7 +3205,7 @@ generate_keypair (const char *fname, const char *card_serialno,
   r->next = para;
   para = r;
 
-  uid = ask_user_id(0);
+  uid = ask_user_id (0, NULL);
   if( !uid ) 
     {
       log_error(_("Key generation canceled.\n"));
@@ -3036,7 +3219,7 @@ generate_keypair (const char *fname, const char *card_serialno,
   para = r;
     
   canceled = 0;
-  dek = card_serialno? NULL : do_ask_passphrase ( &s2k, &canceled );
+  dek = card_serialno? NULL : do_ask_passphrase (&s2k, 0, &canceled);
   if( dek )
     {
       r = xmalloc_clear( sizeof *r );
@@ -3094,7 +3277,7 @@ generate_raw_key (int algo, unsigned int nbits, u32 created_at,
       log_info(_("keysize rounded up to %u bits\n"), nbits );
     }
 
-  dek = do_ask_passphrase (&s2k, &canceled);
+  dek = do_ask_passphrase (&s2k, 1, &canceled);
   if (canceled)
     {
       rc = gpg_error (GPG_ERR_CANCELED);
@@ -3196,7 +3379,8 @@ do_generate_keypair (struct para_data_s *para,
           iobuf_close(outctrl->pub.stream);
           outctrl->pub.stream = NULL;
           if (outctrl->pub.fname)
-            iobuf_ioctl (NULL, 2, 0, (char*)outctrl->pub.fname);
+            iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE,
+                         0, (char*)outctrl->pub.fname);
           xfree( outctrl->pub.fname );
           outctrl->pub.fname =  outctrl->pub.newfname;
           outctrl->pub.newfname = NULL;
@@ -3227,7 +3411,8 @@ do_generate_keypair (struct para_data_s *para,
           iobuf_close(outctrl->sec.stream);
           outctrl->sec.stream = NULL;
           if (outctrl->sec.fname)
-            iobuf_ioctl (NULL, 2, 0, (char*)outctrl->sec.fname);
+            iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE,
+                         0, (char*)outctrl->sec.fname);
           xfree( outctrl->sec.fname );
           outctrl->sec.fname =  outctrl->sec.newfname;
           outctrl->sec.newfname = NULL;
@@ -3279,9 +3464,18 @@ do_generate_keypair (struct para_data_s *para,
   if (!timestamp)
     timestamp = make_timestamp ();
 
+  /* Note that, depending on the backend (i.e. the used scdaemon
+     version), the card key generation may update TIMESTAMP for each
+     key.  Thus we need to pass TIMESTAMP to all signing function to
+     make sure that the binding signature is done using the timestamp
+     of the corresponding (sub)key and not that of the primary key.
+     An alternative implementation could tell the signing function the
+     node of the subkey but that is more work than just to pass the
+     current timestamp.  */
+
   if (!card)
     {
-      rc = do_create (get_parameter_algo( para, pKEYTYPE ),
+      rc = do_create (get_parameter_algo( para, pKEYTYPE, NULL ),
                       get_parameter_uint( para, pKEYLENGTH ),
                       pub_root, sec_root,
                       get_parameter_dek( para, pPASSPHRASE_DEK ),
@@ -3292,8 +3486,6 @@ do_generate_keypair (struct para_data_s *para,
     }
   else
     {
-      /* Note, that depending on the backend, the card key generation
-         may update TIMESTAMP.  */
       rc = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, sec_root, NULL,
                          &timestamp,
                          get_parameter_u32 (para, pKEYEXPIRE), para);
@@ -3329,8 +3521,6 @@ do_generate_keypair (struct para_data_s *para,
 
   if (!rc && card && get_parameter (para, pAUTHKEYTYPE))
     {
-      /* Note, that depending on the backend, the card key generation
-         may update TIMESTAMP.  */
       rc = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, sec_root, NULL,
                          &timestamp,
                          get_parameter_u32 (para, pKEYEXPIRE), para);
@@ -3347,7 +3537,7 @@ do_generate_keypair (struct para_data_s *para,
     {
       if (!card)
         {
-          rc = do_create( get_parameter_algo( para, pSUBKEYTYPE ),
+          rc = do_create( get_parameter_algo( para, pSUBKEYTYPE, NULL ),
                           get_parameter_uint( para, pSUBKEYLENGTH ),
                           pub_root, sec_root,
                           get_parameter_dek( para, pPASSPHRASE_DEK ),
@@ -3372,8 +3562,6 @@ do_generate_keypair (struct para_data_s *para,
             }
           else
             {
-              /* Note, that depending on the backend, the card key
-                 generation may update TIMESTAMP.  */
               rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root,
                                  NULL,
                                  &timestamp,
@@ -3458,7 +3646,8 @@ do_generate_keypair (struct para_data_s *para,
           int no_enc_rsa;
           PKT_public_key *pk;
 
-          no_enc_rsa = (get_parameter_algo (para, pKEYTYPE) == PUBKEY_ALGO_RSA
+          no_enc_rsa = ((get_parameter_algo (para, pKEYTYPE, NULL)
+                         == PUBKEY_ALGO_RSA)
                         && get_parameter_uint (para, pKEYUSAGE)
                         && !((get_parameter_uint (para, pKEYUSAGE)
                               & PUBKEY_USAGE_ENC)) );
@@ -3480,7 +3669,7 @@ do_generate_keypair (struct para_data_s *para,
             
           
           if (!opt.batch
-              && (get_parameter_algo (para, pKEYTYPE) == PUBKEY_ALGO_DSA
+              && (get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA
                   || no_enc_rsa )
               && !get_parameter (para, pSUBKEYTYPE) )
             {
@@ -3498,6 +3687,7 @@ do_generate_keypair (struct para_data_s *para,
         log_error ("key generation failed: %s\n", g10_errstr(rc) );
       else
         tty_printf (_("Key generation failed: %s\n"), g10_errstr(rc) );
+      write_status_errcode (card? "card_key_generate":"key_generate", rc);
       print_status_key_not_created ( get_parameter_value (para, pHANDLE) );
     }
   else
@@ -3601,9 +3791,9 @@ generate_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock)
   if (rc)
     goto leave;
 
-  algo = ask_algo (1, &use);
+  algo = ask_algo (1, NULL, &use);
   assert (algo);
-  nbits = ask_keysize (algo);
+  nbits = ask_keysize (algo, 0);
   expire = ask_expire_interval (0, NULL);
   if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
                                                _("Really create? (y/N) ")))
@@ -3611,7 +3801,7 @@ generate_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock)
   
   canceled = 0;
   if (ask_pass)
-    dek = do_ask_passphrase (&s2k, &canceled);
+    dek = do_ask_passphrase (&s2k, 0, &canceled);
   else if (passphrase)
     {
       s2k = xmalloc_secure ( sizeof *s2k );
@@ -3902,19 +4092,35 @@ gen_card_key_with_backup (int algo, int keyno, int is_primary,
   PKT_public_key *pk;
   size_t n;
   int i;
+  unsigned int nbits;
+    
+  /* Get the size of the key directly from the card.  */
+  {
+    struct agent_card_info_s info;
+    
+    memset (&info, 0, sizeof info);
+    if (!agent_scd_getattr ("KEY-ATTR", &info)
+        && info.key_attr[1].algo)
+      nbits = info.key_attr[1].nbits;
+    else
+      nbits = 1024; /* All pre-v2.0 cards.  */
+    agent_release_card_info (&info);
+  }
 
-  rc = generate_raw_key (algo, 1024, timestamp,
+  /* Create a key of this size in memory.  */
+  rc = generate_raw_key (algo, nbits, timestamp,
                          &sk_unprotected, &sk_protected);
   if (rc)
     return rc;
 
-  /* First, store the key to the card. */
+  /* Store the key to the card. */
   rc = save_unprotected_key_to_card (sk_unprotected, keyno);
   if (rc)
     {
       log_error (_("storing key onto card failed: %s\n"), g10_errstr (rc));
       free_secret_key (sk_unprotected);
       free_secret_key (sk_protected);
+      write_status_errcode ("save_key_to_card", rc);
       return rc;
     }
 
@@ -3944,8 +4150,8 @@ gen_card_key_with_backup (int algo, int keyno, int is_primary,
     mode_t oldmask;
 
     keyid_from_sk (sk, NULL);
-    sprintf (name_buffer,"sk_%08lX%08lX.gpg",
-             (ulong)sk->keyid[0], (ulong)sk->keyid[1]);
+    snprintf (name_buffer, sizeof name_buffer, "sk_%08lX%08lX.gpg",
+              (ulong)sk->keyid[0], (ulong)sk->keyid[1]);
 
     fname = make_filename (backup_dir, name_buffer, NULL);
     oldmask = umask (077);
@@ -3985,7 +4191,7 @@ gen_card_key_with_backup (int algo, int keyno, int is_primary,
         char *fprbuf, *p;
        
         iobuf_close (fp);
-        iobuf_ioctl (NULL, 2, 0, (char*)fname);
+        iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname);
         log_info (_("NOTE: backup of card key saved to `%s'\n"), fname);
 
         fingerprint_from_sk (sk, array, &n);