gpg: New command --quick-addkey.
authorWerner Koch <wk@gnupg.org>
Thu, 2 Jun 2016 13:54:48 +0000 (15:54 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 2 Jun 2016 14:01:48 +0000 (16:01 +0200)
* g10/keygen.c (DEFAULT_STD_SUBKEYUSE): New.
(ask_keysize): Factor code out to ...
(get_keysize_range, fixup_keysize): new.
(parse_parameter_usage): Factor parsing out to  ...
(parse_usagestr): new.  Allow use of "encr" as alias for "encrypt".
(parse_subkey_algostr_usagestr): New.
(generate_subkeypair): Add new args.  Implement unattended mode.

* g10/keyedit.c (keyedit_quick_sign): Factor some code out to ...
(find_by_primary_fpr): new.
(keyedit_quick_addkey): New.
* g10/gpg.c (aQuickAddKey): New.
(opts): Add --quick-addkey.
(main): Implement.

Signed-off-by: Werner Koch <wk@gnupg.org>
doc/gpg.texi
g10/gpg.c
g10/keyedit.c
g10/keygen.c
g10/main.h

index a09e610..9b0f1ba 100644 (file)
@@ -620,6 +620,35 @@ supplied passphrase is used for the new key and the agent does not ask
 for it.  To create a key without any protection @code{--passphrase ''}
 may be used.
 
+@item --quick-addkey @code{fpr} [@code{algo} [@code{usage} [@code{expire}]]]
+@opindex quick-addkey
+Directly add a subkey to the key identified by the fingerprint
+@code{fpr}.  Without the optional arguments an encryption subkey is
+added.  If any of the arguments are given a more specific subkey is
+added.
+
+@code{algo} may be any of the supported algorithms or curve names given
+in the format as used by key listings.  To use the default algorithm
+the string ``default'' or ``-'' can be used. Supported algorithms are
+``rsa'', ``dsa'', ``elg'', ``ed25519'', ``cv25519'', and other ECC
+curves.  For example the string ``rsa'' adds an RSA key with the
+default key length; a string ``rsa4096'' requests that the key length
+is 4096 bits.
+
+Depending on the given @code{algo} the subkey may either be an
+encryption subkey or a signing subkey.  If an algorithm is capable of
+signing and encryption and such a subkey is desired, a @code{usage}
+string must be given.  This string is either ``default'' or ``-'' to
+keep the default or a comma delimited list of keywords: ``sign'' for a
+signing subkey, ``auth'' for an authentication subkey, and ``encr''
+for an encryption subkey (``encrypt'' can be used as alias for
+``encr'').  The valid combinations depend on the algorithm.
+
+The @code{expire} argument can be used to specify an expiration date
+for the subkey.  Several formats are supported; commonly the ISO
+YYYY-MM-DD format is used.  The values ``never'', ``none'', or ``-''
+can be used for no expiration date.
+
 @item --gen-key
 @opindex gen-key
 Generate a new key pair using the current default parameters.  This is
@@ -636,6 +665,7 @@ There is also a feature which allows you to create keys in batch
 mode. See the manual section ``Unattended key generation'' on how
 to use this.
 
+
 @item --gen-revoke @code{name}
 @opindex gen-revoke
 Generate a revocation certificate for the complete key.  To only revoke
index a88499a..2795330 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -117,6 +117,7 @@ enum cmd_and_opt_values
     aQuickSignKey,
     aQuickLSignKey,
     aQuickAddUid,
+    aQuickAddKey,
     aListConfig,
     aListGcryptConfig,
     aGPGConfList,
@@ -426,6 +427,7 @@ static ARGPARSE_OPTS opts[] = {
               N_("quickly generate a new key pair")),
   ARGPARSE_c (aQuickAddUid,  "quick-adduid",
               N_("quickly add a new user-id")),
+  ARGPARSE_c (aQuickAddKey,  "quick-addkey", "@"),
   ARGPARSE_c (aFullKeygen,  "full-gen-key" ,
               N_("full featured key pair generation")),
   ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")),
@@ -2433,6 +2435,7 @@ main (int argc, char **argv)
          case aStore:
          case aQuickKeygen:
          case aQuickAddUid:
+         case aQuickAddKey:
          case aExportOwnerTrust:
          case aImportOwnerTrust:
           case aRebuildKeydbCaches:
@@ -3775,6 +3778,7 @@ main (int argc, char **argv)
       case aDeleteSecretAndPublicKeys:
       case aQuickKeygen:
       case aQuickAddUid:
+      case aQuickAddKey:
       case aFullKeygen:
       case aKeygen:
       case aImport:
@@ -4148,6 +4152,30 @@ main (int argc, char **argv)
         }
        break;
 
+      case aQuickAddKey:
+        {
+          const char *x_fpr, *x_algo, *x_usage, *x_expire;
+
+          if (argc < 1 || argc > 4)
+            wrong_args ("--quick-addkey FINGERPRINT [ALGO [USAGE [EXPIRE]]]");
+          x_fpr = *argv++; argc--;
+          x_algo = "";
+          x_usage = "";
+          x_expire = "";
+          if (argc)
+            {
+              x_algo = *argv++; argc--;
+              if (argc)
+                {
+                  x_usage = *argv++; argc--;
+                  if (argc)
+                    x_expire = *argv++; argc--;
+                }
+            }
+          keyedit_quick_addkey (ctrl, x_fpr, x_algo, x_usage, x_expire);
+        }
+       break;
+
       case aFastImport:
         opt.import_options |= IMPORT_FAST;
       case aImport:
index c78f8a3..16dbf62 100644 (file)
@@ -1,6 +1,6 @@
 /* keyedit.c - Edit properties of a key
  * Copyright (C) 1998-2010 Free Software Foundation, Inc.
- * Copyright (C) 1998-2015 Werner Koch
+ * Copyright (C) 1998-2016 Werner Koch
  * Copyright (C) 2015, 2016 g10 Code GmbH
  *
  * This file is part of GnuPG.
@@ -2349,7 +2349,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          break;
 
        case cmdADDKEY:
-         if (!generate_subkeypair (ctrl, keyblock))
+         if (!generate_subkeypair (ctrl, keyblock, NULL, NULL, NULL))
            {
              redisplay = 1;
              modified = 1;
@@ -2935,6 +2935,75 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
 }
 
 
+/* Find a keyblock by fingerprint because only this uniquely
+ * identifies a key and may thus be used to select a key for
+ * unattended subkey creation os key signing.  */
+static gpg_error_t
+find_by_primary_fpr (ctrl_t ctrl, const char *fpr,
+                     kbnode_t *r_keyblock, KEYDB_HANDLE *r_kdbhd)
+{
+  gpg_error_t err;
+  kbnode_t keyblock = NULL;
+  KEYDB_HANDLE kdbhd = NULL;
+  KEYDB_SEARCH_DESC desc;
+  byte fprbin[MAX_FINGERPRINT_LEN];
+  size_t fprlen;
+
+  *r_keyblock = NULL;
+  *r_kdbhd = NULL;
+
+  if (classify_user_id (fpr, &desc, 1)
+      || !(desc.mode == KEYDB_SEARCH_MODE_FPR
+           || desc.mode == KEYDB_SEARCH_MODE_FPR16
+           || desc.mode == KEYDB_SEARCH_MODE_FPR20))
+    {
+      log_error (_("\"%s\" is not a fingerprint\n"), fpr);
+      err = gpg_error (GPG_ERR_INV_NAME);
+      goto leave;
+    }
+  err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1);
+  if (err)
+    {
+      log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err));
+      goto leave;
+    }
+
+  /* Check that the primary fingerprint has been given. */
+  fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen);
+  if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16
+      && !memcmp (fprbin, desc.u.fpr, 16))
+    ;
+  else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR
+           && !memcmp (fprbin, desc.u.fpr, 16)
+           && !desc.u.fpr[16]
+           && !desc.u.fpr[17]
+           && !desc.u.fpr[18]
+           && !desc.u.fpr[19])
+    ;
+  else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20
+                            || desc.mode == KEYDB_SEARCH_MODE_FPR)
+           && !memcmp (fprbin, desc.u.fpr, 20))
+    ;
+  else
+    {
+      log_error (_("\"%s\" is not the primary fingerprint\n"), fpr);
+      err = gpg_error (GPG_ERR_INV_NAME);
+      goto leave;
+    }
+
+  *r_keyblock = keyblock;
+  keyblock = NULL;
+  *r_kdbhd = kdbhd;
+  kdbhd = NULL;
+  err = 0;
+
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+  return err;
+}
+
+
 /* Unattended key signing function.  If the key specifified by FPR is
    available and FPR is the primary fingerprint all user ids of the
    key are signed using the default signing key.  If UIDS is an empty
@@ -2949,7 +3018,6 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
   kbnode_t keyblock = NULL;
   KEYDB_HANDLE kdbhd = NULL;
   int modified = 0;
-  KEYDB_SEARCH_DESC desc;
   PKT_public_key *pk;
   kbnode_t node;
   strlist_t sl;
@@ -2963,47 +3031,8 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
   /* We require a fingerprint because only this uniquely identifies a
      key and may thus be used to select a key for unattended key
      signing.  */
-  if (classify_user_id (fpr, &desc, 1)
-      || !(desc.mode == KEYDB_SEARCH_MODE_FPR
-           || desc.mode == KEYDB_SEARCH_MODE_FPR16
-           || desc.mode == KEYDB_SEARCH_MODE_FPR20))
-    {
-      log_error (_("\"%s\" is not a fingerprint\n"), fpr);
-      goto leave;
-    }
-  err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1);
-  if (err)
-    {
-      log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err));
-      goto leave;
-    }
-
-  /* Check that the primary fingerprint has been given. */
-  {
-    byte fprbin[MAX_FINGERPRINT_LEN];
-    size_t fprlen;
-
-    fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen);
-    if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16
-        && !memcmp (fprbin, desc.u.fpr, 16))
-      ;
-    else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR
-             && !memcmp (fprbin, desc.u.fpr, 16)
-             && !desc.u.fpr[16]
-             && !desc.u.fpr[17]
-             && !desc.u.fpr[18]
-             && !desc.u.fpr[19])
-      ;
-    else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20
-                              || desc.mode == KEYDB_SEARCH_MODE_FPR)
-             && !memcmp (fprbin, desc.u.fpr, 20))
-      ;
-    else
-      {
-        log_error (_("\"%s\" is not the primary fingerprint\n"), fpr);
-        goto leave;
-      }
-  }
+  if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd))
+    goto leave;
 
   if (fix_keyblock (&keyblock))
     modified++;
@@ -3129,6 +3158,67 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
 }
 
 
+/* Unattended subkey creation function.
+ *
+ */
+void
+keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr,
+                      const char *usagestr, const char *expirestr)
+{
+  gpg_error_t err;
+  kbnode_t keyblock;
+  KEYDB_HANDLE kdbhd;
+  int modified = 0;
+  PKT_public_key *pk;
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  /* We require a fingerprint because only this uniquely identifies a
+   * key and may thus be used to select a key for unattended subkey
+   * creation.  */
+  if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd))
+    goto leave;
+
+  if (fix_keyblock (&keyblock))
+    modified++;
+
+  pk = keyblock->pkt->pkt.public_key;
+  if (pk->flags.revoked)
+    {
+      if (!opt.verbose)
+        show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
+      log_error ("%s%s", _("Key is revoked."), "\n");
+      goto leave;
+    }
+
+  /* Create the subkey.  Noet that the called function already prints
+   * an error message. */
+  if (!generate_subkeypair (ctrl, keyblock, algostr, usagestr, expirestr))
+    modified = 1;
+  es_fflush (es_stdout);
+
+  /* Store.  */
+  if (modified)
+    {
+      err = keydb_update_keyblock (kdbhd, keyblock);
+      if (err)
+        {
+          log_error (_("update failed: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+    }
+  else
+    log_info (_("Key not changed so no update needed.\n"));
+
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
 \f
 static void
 tty_print_notations (int indent, PKT_signature * sig)
index f9cbf21..2ef80a7 100644 (file)
@@ -1,6 +1,6 @@
 /* keygen.c - Generate a key pair
  * Copyright (C) 1998-2007, 2009-2011  Free Software Foundation, Inc.
- * Copyright (C) 2014, 2015  Werner Koch
+ * Copyright (C) 2014, 2015, 2016  Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -54,6 +54,7 @@
 #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
 
 /* Flag bits used during key generation.  */
@@ -2017,88 +2018,47 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
 }
 
 
-/* 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, unsigned int primary_keysize)
+static void
+get_keysize_range (int algo,
+                   unsigned int *min, unsigned int *def, unsigned int *max)
 {
-  unsigned int nbits;
-  unsigned int min = 1024;
-  unsigned int def = DEFAULT_STD_KEYSIZE;
-  unsigned int max = 4096;
-  int for_subkey = !!primary_keysize;
-  int autocomp = 0;
-
-  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;
-    }
+  *min = 1024;
+  *def = DEFAULT_STD_KEYSIZE;
+  *max = 4096;
 
   /* Deviations from the standard values.  */
   switch(algo)
     {
     case PUBKEY_ALGO_DSA:
-      min = opt.expert? 768 : 1024;
-      def=2048;
-      max=3072;
+      *min = opt.expert? 768 : 1024;
+      *def=2048;
+      *max=3072;
       break;
 
     case PUBKEY_ALGO_ECDSA:
     case PUBKEY_ALGO_ECDH:
-      min=256;
-      def=256;
-      max=521;
+      *min=256;
+      *def=256;
+      *max=521;
       break;
 
     case PUBKEY_ALGO_EDDSA:
-      min=255;
-      def=255;
-      max=441;
+      *min=255;
+      *def=255;
+      *max=441;
       break;
     }
+}
 
-  tty_printf(_("%s keys may be between %u and %u bits long.\n"),
-            openpgp_pk_algo_name (algo), min, max);
-
-  for (;;)
-    {
-      char *prompt, *answer;
-
-      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);
-
-      if(nbits<min || nbits>max)
-       tty_printf(_("%s keysizes must be in the range %u-%u\n"),
-                  openpgp_pk_algo_name (algo), min, max);
-      else
-       break;
-    }
-
-  tty_printf (_("Requested keysize is %u bits\n"), nbits);
 
- leave:
+/* Return a fixed up keysize depending on ALGO.  */
+static unsigned int
+fixup_keysize (unsigned int nbits, int algo, int silent)
+{
   if (algo == PUBKEY_ALGO_DSA && (nbits % 64))
     {
       nbits = ((nbits + 63) / 64) * 64;
-      if (!autocomp)
+      if (!silent)
         tty_printf (_("rounded up to %u bits\n"), nbits);
     }
   else if (algo == PUBKEY_ALGO_EDDSA)
@@ -2109,7 +2069,7 @@ ask_keysize (int algo, unsigned int primary_keysize)
             nbits = 255;
           else
             nbits = 441;
-          if (!autocomp)
+          if (!silent)
             tty_printf (_("rounded to %u bits\n"), nbits);
         }
     }
@@ -2123,14 +2083,14 @@ ask_keysize (int algo, unsigned int primary_keysize)
             nbits = 384;
           else
             nbits = 521;
-          if (!autocomp)
+          if (!silent)
             tty_printf (_("rounded to %u bits\n"), nbits);
         }
     }
   else if ((nbits % 32))
     {
       nbits = ((nbits + 31) / 32) * 32;
-      if (!autocomp)
+      if (!silent)
         tty_printf (_("rounded up to %u bits\n"), nbits );
     }
 
@@ -2138,6 +2098,66 @@ ask_keysize (int algo, unsigned int primary_keysize)
 }
 
 
+/* 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, unsigned int primary_keysize)
+{
+  unsigned int nbits;
+  unsigned int min, def, max;
+  int for_subkey = !!primary_keysize;
+  int autocomp = 0;
+
+  get_keysize_range (algo, &min, &def, &max);
+
+  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;
+    }
+
+  tty_printf(_("%s keys may be between %u and %u bits long.\n"),
+            openpgp_pk_algo_name (algo), min, max);
+
+  for (;;)
+    {
+      char *prompt, *answer;
+
+      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);
+
+      if(nbits<min || nbits>max)
+       tty_printf(_("%s keysizes must be in the range %u-%u\n"),
+                  openpgp_pk_algo_name (algo), min, max);
+      else
+       break;
+    }
+
+  tty_printf (_("Requested keysize is %u bits\n"), nbits);
+
+ leave:
+  nbits = fixup_keysize (nbits, algo, autocomp);
+  return nbits;
+}
+
+
 /* Ask for the curve.  ALGO is the selected algorithm which this
    function may adjust.  Returns a malloced string with the name of
    the curve.  BOTH tells that gpg creates a primary and subkey. */
@@ -2885,6 +2905,50 @@ get_parameter_algo( struct para_data_s *para, enum para_name key,
   return i;
 }
 
+
+/* 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/  */
+static int
+parse_usagestr (const char *usagestr)
+{
+  gpg_error_t err;
+  char **tokens = NULL;
+  const char *s;
+  int i;
+  unsigned int use = 0;
+
+  tokens = strtokenize (usagestr, " \t,");
+  if (!tokens)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("strtokenize failed: %s\n", gpg_strerror (err));
+      return -1;
+    }
+
+  for (i=0; (s = tokens[i]); i++)
+    {
+      if (!*s)
+        ;
+      else if (!ascii_strcasecmp (s, "sign"))
+        use |= PUBKEY_USAGE_SIG;
+      else if (!ascii_strcasecmp (s, "encrypt")
+                || !ascii_strcasecmp (s, "encr"))
+        use |= PUBKEY_USAGE_ENC;
+      else if (!ascii_strcasecmp (s, "auth"))
+        use |= PUBKEY_USAGE_AUTH;
+      else
+        {
+          xfree (tokens);
+          return -1; /* error */
+        }
+    }
+
+  xfree (tokens);
+  return use;
+}
+
+
 /*
  * Parse the usage parameter and set the keyflags.  Returns -1 on
  * error, 0 for no usage given or 1 for usage available.
@@ -2893,33 +2957,24 @@ static int
 parse_parameter_usage (const char *fname,
                        struct para_data_s *para, enum para_name key)
 {
-    struct para_data_s *r = get_parameter( para, key );
-    char *p, *pn;
-    unsigned int use;
-
-    if( !r )
-       return 0; /* none (this is an optional parameter)*/
-
-    use = 0;
-    pn = r->u.value;
-    while ( (p = strsep (&pn, " \t,")) ) {
-        if ( !*p)
-            ;
-        else if ( !ascii_strcasecmp (p, "sign") )
-            use |= PUBKEY_USAGE_SIG;
-        else if ( !ascii_strcasecmp (p, "encrypt") )
-            use |= PUBKEY_USAGE_ENC;
-        else if ( !ascii_strcasecmp (p, "auth") )
-            use |= PUBKEY_USAGE_AUTH;
-        else {
-            log_error("%s:%d: invalid usage list\n", fname, r->lnr );
-            return -1; /* error */
-        }
+  struct para_data_s *r = get_parameter( para, key );
+  int i;
+
+  if (!r)
+    return 0; /* none (this is an optional parameter)*/
+
+  i = parse_usagestr (r->u.value);
+  if (i == -1)
+    {
+      log_error ("%s:%d: invalid usage list\n", fname, r->lnr );
+      return -1; /* error */
     }
-    r->u.usage = use;
-    return 1;
+
+  r->u.usage = i;
+  return 1;
 }
 
+
 static int
 parse_revocation_key (const char *fname,
                      struct para_data_s *para, enum para_name key)
@@ -4260,12 +4315,119 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
 }
 
 
+gpg_error_t
+parse_subkey_algostr_usagestr (ctrl_t ctrl, const char *algostr,
+                               const char *usagestr,
+                               int *r_algo, unsigned int *r_usage,
+                               unsigned int *r_nbits, char **r_curve)
+{
+  int algo;
+  unsigned int use, nbits;
+  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 = DEFAULT_STD_SUBALGO;
+      use = DEFAULT_STD_SUBKEYUSE;
+    }
+  else if (*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 = DEFAULT_STD_SUBKEYUSE;
+      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);
+
+  /* Parse the usage string.  */
+  if (!usagestr || !*usagestr
+      || !strcmp (usagestr, "default") || !strcmp (usagestr, "-"))
+    ; /* Keep default usage */
+  else if ((wantuse = parse_usagestr (usagestr)) != -1)
+    {
+      use = wantuse;
+      if (eccalgo && !(use & PUBKEY_USAGE_ENC))
+        algo = PUBKEY_ALGO_ECDSA; /* Switch from ECDH to ECDSA.  */
+    }
+  else
+    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);
+      if (!*r_curve)
+        return gpg_error_from_syserror ();
+    }
+  *r_algo = algo;
+  *r_usage = use;
+  *r_nbits = nbits;
+  return 0;
+}
+
+
 /* Add a new subkey to an existing key.  Returns 0 if a new key has
-   been generated and put into the keyblocks.  */
+   been generated and put into the keyblocks.  If any of ALGOSTR,
+   USAGESTR, or EXPIRESTR is NULL interactive mode is used. */
 gpg_error_t
-generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
+generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr,
+                     const char *usagestr, const char *expirestr)
 {
   gpg_error_t err = 0;
+  int interactive;
   kbnode_t node;
   PKT_public_key *pri_psk = NULL;
   PKT_public_key *sub_psk = NULL;
@@ -4278,6 +4440,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
   char *hexgrip = NULL;
   char *serialno = NULL;
 
+  interactive = (!algostr || !usagestr || !expirestr);
+
   /* Break out the primary key.  */
   node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
   if (!node)
@@ -4317,32 +4481,72 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
     goto leave;
   if (agent_get_keyinfo (NULL, hexgrip, &serialno))
     {
-      tty_printf (_("Secret parts of primary key are not available.\n"));
+      if (interactive)
+        tty_printf (_("Secret parts of primary key are not available.\n"));
+      else
+        log_info (  _("Secret parts of primary key are not available.\n"));
+      err = gpg_error (GPG_ERR_NO_SECKEY);
       goto leave;
     }
   if (serialno)
-    tty_printf (_("Secret parts of primary key are stored on-card.\n"));
+    {
+      if (interactive)
+        tty_printf (_("Secret parts of primary key are stored on-card.\n"));
+      else
+        log_info (  _("Secret parts of primary key are stored on-card.\n"));
+    }
 
   xfree (hexgrip);
   hexgrip = NULL;
-  algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip);
-  log_assert (algo);
-
-  if (hexgrip)
-    nbits = 0;
-  else if (algo == PUBKEY_ALGO_ECDSA
-           || algo == PUBKEY_ALGO_EDDSA
-           || algo == PUBKEY_ALGO_ECDH)
-    curve = ask_curve (&algo, NULL);
-  else
-    nbits = ask_keysize (algo, 0);
+  if (interactive)
+    {
+      algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip);
+      log_assert (algo);
+
+      if (hexgrip)
+        nbits = 0;
+      else if (algo == PUBKEY_ALGO_ECDSA
+               || algo == PUBKEY_ALGO_EDDSA
+               || algo == PUBKEY_ALGO_ECDH)
+        curve = ask_curve (&algo, NULL);
+      else
+        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) ")))
+      expire = ask_expire_interval (0, NULL);
+      if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
+                                                   _("Really create? (y/N) ")))
+        {
+          err = gpg_error (GPG_ERR_CANCELED);
+          goto leave;
+        }
+    }
+  else /* Unattended mode.  */
     {
-      err = gpg_error (GPG_ERR_CANCELED);
-      goto leave;
+      err = parse_subkey_algostr_usagestr (ctrl, algostr, usagestr,
+                                           &algo, &use, &nbits, &curve);
+      if (err)
+        goto leave;
+
+      if (!expirestr || !*expirestr || !strcmp (expirestr, "none")
+          || !strcmp (expirestr, "never") || !strcmp (expirestr, "-"))
+        expire = 0;
+      else
+        expire = parse_expire_string (expirestr);
+      if (expire == (u32)-1 )
+       {
+          err = gpg_error (GPG_ERR_INV_VALUE);
+         goto leave;
+       }
+
+      /* Check that usage is possible.  */
+      if ( ((use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT))
+            && !pubkey_get_nsig (algo))
+           || ((use & PUBKEY_USAGE_ENC)
+               && !pubkey_get_nenc (algo)))
+        {
+          err = gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+          goto leave;
+        }
     }
 
   if (hexgrip)
index 5b5947e..0ca4d39 100644 (file)
@@ -287,6 +287,8 @@ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
 void keyedit_passwd (ctrl_t ctrl, const char *username);
 void keyedit_quick_adduid (ctrl_t ctrl, const char *username,
                            const char *newuid);
+void keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr,
+                           const char *usagestr, const char *expirestr);
 void keyedit_quick_sign (ctrl_t ctrl, const char *fpr,
                          strlist_t uids, strlist_t locusr, int local);
 void show_basic_key_info (KBNODE keyblock);
@@ -311,7 +313,10 @@ int keygen_add_revkey(PKT_signature *sig, void *opaque);
 gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk,
                           PKT_public_key *sub_pk, PKT_public_key *sub_psk,
                           u32 timestamp, const char *cache_nonce);
-gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock);
+gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock,
+                                 const char *algostr,
+                                 const char *usagestr,
+                                 const char *expirestr);
 #ifdef ENABLE_CARD_SUPPORT
 gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock,
                                       int keyno, const char *serialno);