Ask for the keysize when generating a new card key.
authorWerner Koch <wk@gnupg.org>
Wed, 5 Aug 2009 11:24:43 +0000 (11:24 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 5 Aug 2009 11:24:43 +0000 (11:24 +0000)
common/yesno.c
g10/ChangeLog
g10/call-agent.c
g10/card-util.c
g10/gpg.c
scd/ChangeLog
scd/app-openpgp.c

index fdbf7dd..a7131b7 100644 (file)
 #include "i18n.h"
 #include "util.h"
 
+
+/* Check the string S for a YES or NO answer and take care of
+   localization.  If no valid string is given the value of DEF_ANSWER
+   is returned.  Returns 1 for yes and 0 for no.  */
 int
-answer_is_yes_no_default( const char *s, int def_answer )
+answer_is_yes_no_default (const char *s, int def_answer)
 {
   /* TRANSLATORS: See doc/TRANSLATE about this string. */
   const char *long_yes = _("yes");
index 6e67287..781ec57 100644 (file)
@@ -1,3 +1,16 @@
+2009-08-05  Werner Koch  <wk@g10code.com>
+
+       * gpg.c: Add --key-edit alias.
+
+       * call-agent.c (scd_genkey_cb): Forward progress status lines.
+
+       * card-util.c (generate_card_keys): Remove special case for
+       GnuPG-2.  Ask for the keysize and change it.
+       (card_generate_subkey): Ask for the keysize and change it.
+       (get_info_for_key_operation): Read KEY-ATTR.
+       (show_keysize_warning, ask_card_keysize): New.
+       (do_change_keysize): New.
+
 2009-07-31  David Shaw  <dshaw@jabberwocky.com>
 
        * gpg.c (main): --pgp6 includes --disable-mdc.
index 1b75781..8a0b21a 100644 (file)
@@ -667,7 +667,7 @@ scd_genkey_cb (void *opaque, const char *line)
     {
       parm->fprvalid = unhexify_fpr (line, parm->fpr);
     }
-  if (keywordlen == 8 && !memcmp (keyword, "KEY-DATA", keywordlen))
+  else if (keywordlen == 8 && !memcmp (keyword, "KEY-DATA", keywordlen))
     {
       gcry_mpi_t a;
       const char *name = line;
@@ -694,6 +694,10 @@ scd_genkey_cb (void *opaque, const char *line)
     {
       parm->created_at = (u32)strtoul (line, NULL, 10);
     }
+  else if (keywordlen == 8 && !memcmp (keyword, "PROGRESS", keywordlen))
+    {
+      write_status_text (STATUS_PROGRESS, line);
+    }
 
   return 0;
 }
index d03de0b..5ba42b8 100644 (file)
@@ -1158,6 +1158,8 @@ get_info_for_key_operation (struct agent_card_info_s *info)
     rc = agent_scd_getattr ("DISP-NAME", info);
   if (!rc)
     rc = agent_scd_getattr ("EXTCAP", info);
+  if (!rc)
+    rc = agent_scd_getattr ("KEY-ATTR", info);
   if (rc)
     log_error (_("error getting current key info: %s\n"), gpg_strerror (rc));
   return rc;
@@ -1254,33 +1256,113 @@ replace_existing_key_p (struct agent_card_info_s *info, int keyno)
 
 
 static void
+show_keysize_warning (void)
+{
+  static int shown;
+
+  if (shown)
+    return;
+  shown = 1;
+  tty_printf
+    (_("NOTE: There is no guarantee that the card "
+       "supports the requested size.\n"
+       "      If the key generation does not succeed, "
+       "please check the\n"
+       "      documentation of your card to see what "
+       "sizes are allowed.\n"));
+}
+
+
+/* Ask for the size of a card key.  NBITS is the current size
+   configured for the card.  KEYNO is the number of the key used to
+   select the prompt.  Returns 0 to use the default size (i.e. NBITS)
+   or the selected size.  */
+static unsigned int
+ask_card_keysize (int keyno, unsigned int nbits)
+{
+  unsigned int min_nbits = 1024;
+  unsigned int max_nbits = 3072; /* GnuPG limit due to Assuan.  */
+  char *prompt, *answer;
+  unsigned int req_nbits;
+
+  for (;;)
+    {
+      prompt = xasprintf 
+        (keyno == 0?
+         _("What keysize do you want for the Signature key? (%u) "):
+         keyno == 1?
+         _("What keysize do you want for the Encryption key? (%u) "):
+         _("What keysize do you want for the Authentication key? (%u) "),
+         nbits);
+      answer = cpr_get ("cardedit.genkeys.size", prompt);
+      cpr_kill_prompt ();
+      req_nbits = *answer? atoi (answer): nbits;
+      xfree (prompt);
+      xfree (answer);
+      
+      if (req_nbits != nbits && (req_nbits % 32) )
+        {
+          req_nbits = ((req_nbits + 31) / 32) * 32;
+          tty_printf (_("rounded up to %u bits\n"), req_nbits);
+        }
+  
+      if (req_nbits == nbits)
+        return 0;  /* Use default.  */
+      
+      if (req_nbits < min_nbits || req_nbits > max_nbits)
+        {
+          tty_printf (_("%s keysizes must be in the range %u-%u\n"),
+                      "RSA", min_nbits, max_nbits);
+        }
+      else
+        {
+          tty_printf (_("The card will now be re-configured "
+                        "to generate a key of %u bits\n"), req_nbits);
+          show_keysize_warning ();
+          return req_nbits;
+        }
+    }
+}
+
+
+/* Change the size of key KEYNO (0..2) to NBITS and show an error
+   message if that fails.  */
+static gpg_error_t
+do_change_keysize (int keyno, unsigned int nbits) 
+{
+  gpg_error_t err;
+  char args[100];
+  
+  snprintf (args, sizeof args, "--force %d 1 %u", keyno+1, nbits);
+  err = agent_scd_setattr ("KEY-ATTR", args, strlen (args), NULL);
+  if (err)
+    log_error (_("error changing size of key %d to %u bits: %s\n"), 
+               keyno+1, nbits, gpg_strerror (err));
+  return err;
+}
+
+static void
 generate_card_keys (void)
 {
   struct agent_card_info_s info;
   int forced_chv1;
   int want_backup;
+  int keyno;
 
   if (get_info_for_key_operation (&info))
     return;
 
   if (info.extcap.ki)
     {
-#if GNUPG_MAJOR_VERSION == 1
       char *answer;
 
-
       answer = cpr_get ("cardedit.genkeys.backup_enc",
                         _("Make off-card backup of encryption key? (Y/n) "));
 
-      want_backup=answer_is_yes_no_default(answer,1);
-      cpr_kill_prompt();
-      xfree(answer);
-#else
-      want_backup = cpr_get_answer_is_yes 
-          ( "cardedit.genkeys.backup_enc",
-                    _("Make off-card backup of encryption key? (Y/n) "));
-  /*FIXME: we need answer_is_yes_no_default()*/
-#endif
+      want_backup = answer_is_yes_no_default (answer, 1/*(default to Yes)*/);
+      cpr_kill_prompt ();
+      xfree (answer);
     }
   else
     want_backup = 0;
@@ -1290,16 +1372,19 @@ generate_card_keys (void)
        || (info.fpr3valid && !fpr_is_zero (info.fpr3)))
     {
       tty_printf ("\n");
-      log_info ("NOTE: keys are already stored on the card!\n");
+      log_info (_("NOTE: keys are already stored on the card!\n"));
       tty_printf ("\n");
-      if ( !cpr_get_answer_is_yes"cardedit.genkeys.replace_keys",
-                                  _("Replace existing keys? (y/N) ")))
+      if ( !cpr_get_answer_is_yes ("cardedit.genkeys.replace_keys",
+                                   _("Replace existing keys? (y/N) ")))
         {
           agent_release_card_info (&info);
           return;
         }
     }
-  else if (!info.disp_name || !*info.disp_name)
+
+  /* If no displayed name has been set, we assume that this is a fresh
+     card and print a hint about the default PINs.  */
+  if (!info.disp_name || !*info.disp_name)
     {
       tty_printf ("\n");
       tty_printf (_("Please note that the factory settings of the PINs are\n"
@@ -1311,9 +1396,31 @@ generate_card_keys (void)
 
   if (check_pin_for_key_operation (&info, &forced_chv1))
     goto leave;
-  
-  generate_keypair (NULL, info.serialno,
-                    want_backup? opt.homedir:NULL);
+
+  /* If the cards features changeable key attributes, we ask for the
+     key size.  */
+  if (info.is_v2 && info.extcap.aac)
+    {
+      unsigned int nbits;
+
+      for (keyno = 0; keyno < DIM (info.key_attr); keyno++)
+        {
+          nbits = ask_card_keysize (keyno, info.key_attr[keyno].nbits);
+          if (nbits && do_change_keysize (keyno, nbits))
+            {
+              /* Error: Better read the default key size again.  */
+              agent_release_card_info (&info);
+              if (get_info_for_key_operation (&info))
+                goto leave;
+              /* Ask again for this key size. */
+              keyno--;
+            }
+        }
+      /* Note that INFO has not be synced.  However we will only use
+         the serialnumber and thus it won't harm.  */
+    }
+     
+  generate_keypair (NULL, info.serialno, want_backup? opt.homedir:NULL);
 
  leave:
   agent_release_card_info (&info);
@@ -1365,6 +1472,26 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
   if (check_pin_for_key_operation (&info, &forced_chv1))
     goto leave;
 
+  /* If the cards features changeable key attributes, we ask for the
+     key size.  */
+  if (info.is_v2 && info.extcap.aac)
+    {
+      unsigned int nbits;
+
+    ask_again:
+      nbits = ask_card_keysize (keyno-1, info.key_attr[keyno-1].nbits);
+      if (nbits && do_change_keysize (keyno-1, nbits))
+        {
+          /* Error: Better read the default key size again.  */
+          agent_release_card_info (&info);
+          if (get_info_for_key_operation (&info))
+            goto leave;
+          goto ask_again;
+        }
+      /* Note that INFO has not be synced.  However we will only use
+         the serialnumber and thus it won't harm.  */
+    }
+
   okay = generate_card_subkeypair (pub_keyblock, sec_keyblock,
                                    keyno, info.serialno);
 
index 08c9110..640490b 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -394,6 +394,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_c (aSignKey,  "sign-key"   ,N_("sign a key")),
   ARGPARSE_c (aLSignKey, "lsign-key"  ,N_("sign a key locally")),
   ARGPARSE_c (aEditKey,  "edit-key"   ,N_("sign or edit a key")),
+  ARGPARSE_c (aEditKey,  "key-edit"   ,"@"),
   ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")),
   ARGPARSE_c (aDesigRevoke, "desig-revoke","@" ),
   ARGPARSE_c (aExport, "export"           , N_("export keys") ),
index 62af9cf..e7bf653 100644 (file)
@@ -1,3 +1,8 @@
+2009-08-05  Werner Koch  <wk@g10code.com>
+
+       * app-openpgp.c (change_keyattr_from_string): New.
+       (do_setattr): Support KEY-ATTR.
+
 2009-07-29  Marcus Brinkmann  <marcus@g10code.com>
 
        * ccid-driver.c (print_pr_data): Fix 64 bit compat problem.
index d2b2bdd..2c10cd9 100644 (file)
@@ -214,6 +214,11 @@ static gpg_error_t do_auth (app_t app, const char *keyidstr,
                             const void *indata, size_t indatalen,
                             unsigned char **outdata, size_t *outdatalen);
 static void parse_algorithm_attribute (app_t app, int keyno);
+static gpg_error_t change_keyattr_from_string
+                           (app_t app, 
+                            gpg_error_t (*pincb)(void*, const char *, char **),
+                            void *pincb_arg,
+                            const void *value, size_t valuelen);
 
 
 
@@ -1793,6 +1798,7 @@ do_setattr (app_t app, const char *name,
     { "CERT-3",       0x7F21, 3, 0, 1 },
     { "SM-KEY-ENC",   0x00D1, 3, 0, 1 },
     { "SM-KEY-MAC",   0x00D2, 3, 0, 1 },
+    { "KEY-ATTR",     0,      0, 3, 1 },
     { NULL, 0 }
   };
   int exmode;
@@ -1804,6 +1810,9 @@ do_setattr (app_t app, const char *name,
   if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
     return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported.  */
 
+  if (table[idx].special == 3)
+    return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen);
+
   switch (table[idx].need_chv)
     {
     case 2:
@@ -2404,6 +2413,45 @@ change_keyattr (app_t app, int keyno, unsigned int nbits,
 }
 
 
+/* Helper to process an setattr command for name KEY-ATTR.  It expects
+   a string "--force <keyno> <algo> <nbits>" in (VALUE,VALUELEN).  */
+static gpg_error_t 
+change_keyattr_from_string (app_t app, 
+                            gpg_error_t (*pincb)(void*, const char *, char **),
+                            void *pincb_arg,
+                            const void *value, size_t valuelen)
+{
+  gpg_error_t err;
+  char *string;
+  int keyno, algo;
+  unsigned int nbits;
+
+  /* VALUE is expected to be a string but not guaranteed to be
+     terminated.  Thus copy it to an allocated buffer first. */
+  string = xtrymalloc (valuelen+1);
+  if (!string)
+    return gpg_error_from_syserror ();
+  memcpy (string, value, valuelen);
+  string[valuelen] = 0;
+
+  /* Because this function deletes the key we require the string
+     "--force" in the data to make clear that something serious might
+     happen.  */
+  if (sscanf (string, " --force %d %d %u", &keyno, &algo, &nbits) != 3)
+    err = gpg_error (GPG_ERR_INV_DATA);
+  else if (keyno < 1 || keyno > 3)
+    err = gpg_error (GPG_ERR_INV_ID);
+  else if (algo != 1)
+    err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Not RSA.  */
+  else if (nbits < 1024)
+    err = gpg_error (GPG_ERR_TOO_SHORT);
+  else
+    err = change_keyattr (app, keyno-1, nbits, pincb, pincb_arg);
+
+  xfree (string);
+  return err;
+}
+
 
 /* Handle the WRITEKEY command for OpenPGP.  This function expects a
    canonical encoded S-expression with the secret key in KEYDATA and