g10: Fix print_pubkey_info new line output.
[gnupg.git] / g10 / card-util.c
index 90178dd..eca2484 100644 (file)
@@ -212,9 +212,13 @@ get_manufacturer (unsigned int no)
     case 0x000A: return "Dangerous Things";
 
     case 0x002A: return "Magrathea";
+    case 0x0042: return "GnuPG e.V.";
 
     case 0x1337: return "Warsaw Hackerspace";
     case 0x2342: return "warpzone"; /* hackerspace Muenster.  */
+    case 0x4354: return "Confidential Technologies";   /* cotech.de */
+    case 0x63AF: return "Trustica";
+    case 0xBD0E: return "Paranoidlabs";
     case 0xF517: return "FSIJ";
 
       /* 0x0000 and 0xFFFF are defined as test cards per spec,
@@ -228,13 +232,14 @@ get_manufacturer (unsigned int no)
 
 
 static void
-print_sha1_fpr (estream_t fp, const unsigned char *fpr)
+print_shax_fpr (estream_t fp, const unsigned char *fpr, unsigned int fprlen)
 {
   int i;
 
   if (fpr)
     {
-      for (i=0; i < 20 ; i+=2, fpr += 2 )
+      /* FIXME: Fix formatting for FPRLEN != 20 */
+      for (i=0; i < fprlen ; i+=2, fpr += 2 )
         {
           if (i == 10 )
             tty_fprintf (fp, " ");
@@ -248,13 +253,14 @@ print_sha1_fpr (estream_t fp, const unsigned char *fpr)
 
 
 static void
-print_sha1_fpr_colon (estream_t fp, const unsigned char *fpr)
+print_shax_fpr_colon (estream_t fp,
+                      const unsigned char *fpr, unsigned int fprlen)
 {
   int i;
 
   if (fpr)
     {
-      for (i=0; i < 20 ; i++, fpr++)
+      for (i=0; i < fprlen ; i++, fpr++)
         es_fprintf (fp, "%02X", *fpr);
     }
   es_putc (':', fp);
@@ -262,6 +268,21 @@ print_sha1_fpr_colon (estream_t fp, const unsigned char *fpr)
 
 
 static void
+print_keygrip (estream_t fp, const unsigned char *grp)
+{
+  int i;
+
+  if (opt.with_keygrip)
+    {
+      tty_fprintf (fp, "      keygrip ....: ");
+      for (i=0; i < 20 ; i++, grp++)
+        tty_fprintf (fp, "%02X", *grp);
+      tty_fprintf (fp, "\n");
+    }
+}
+
+
+static void
 print_name (estream_t fp, const char *text, const char *name)
 {
   tty_fprintf (fp, "%s", text);
@@ -338,25 +359,25 @@ print_isoname (estream_t fp, const char *text,
 
 /* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
 static int
-fpr_is_zero (const char *fpr)
+fpr_is_zero (const char *fpr, unsigned int fprlen)
 {
   int i;
 
-  for (i=0; i < 20 && !fpr[i]; i++)
+  for (i=0; i < fprlen && !fpr[i]; i++)
     ;
-  return (i == 20);
+  return (i == fprlen);
 }
 
 
-/* Return true if the SHA1 fingerprint FPR consists only of 0xFF. */
+/* Return true if the fingerprint FPR consists only of 0xFF. */
 static int
-fpr_is_ff (const char *fpr)
+fpr_is_ff (const char *fpr, unsigned int fprlen)
 {
   int i;
 
-  for (i=0; i < 20 && fpr[i] == '\xff'; i++)
+  for (i=0; i < fprlen && fpr[i] == '\xff'; i++)
     ;
-  return (i == 20);
+  return (i == fprlen);
 }
 
 
@@ -371,6 +392,7 @@ current_card_status (ctrl_t ctrl, estream_t fp,
   int rc;
   unsigned int uval;
   const unsigned char *thefpr;
+  unsigned int thefprlen;
   int i;
 
   if (serialno && serialnobuflen)
@@ -490,6 +512,15 @@ current_card_status (ctrl_t ctrl, estream_t fp,
       es_fprintf (fp, "pinretry:%d:%d:%d:\n",
                   info.chvretry[0], info.chvretry[1], info.chvretry[2]);
       es_fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
+      if (info.extcap.kdf)
+        {
+          es_fprintf (fp, "kdf:%s:\n", info.kdf_do_enabled ? "on" : "off");
+        }
+      if (info.extcap.bt)
+        {
+          es_fprintf (fp, "uif:%d:%d:%d:\n",
+                      info.uif[0], info.uif[1], info.uif[2]);
+        }
 
       for (i=0; i < 4; i++)
         {
@@ -503,18 +534,26 @@ current_card_status (ctrl_t ctrl, estream_t fp,
         }
 
       es_fputs ("cafpr:", fp);
-      print_sha1_fpr_colon (fp, info.cafpr1valid? info.cafpr1:NULL);
-      print_sha1_fpr_colon (fp, info.cafpr2valid? info.cafpr2:NULL);
-      print_sha1_fpr_colon (fp, info.cafpr3valid? info.cafpr3:NULL);
+      print_shax_fpr_colon (fp, info.cafpr1len? info.cafpr1:NULL,
+                            info.cafpr2len);
+      print_shax_fpr_colon (fp, info.cafpr2len? info.cafpr2:NULL,
+                            info.cafpr2len);
+      print_shax_fpr_colon (fp, info.cafpr3len? info.cafpr3:NULL,
+                            info.cafpr3len);
       es_putc ('\n', fp);
       es_fputs ("fpr:", fp);
-      print_sha1_fpr_colon (fp, info.fpr1valid? info.fpr1:NULL);
-      print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL);
-      print_sha1_fpr_colon (fp, info.fpr3valid? info.fpr3:NULL);
+      print_shax_fpr_colon (fp, info.fpr1len? info.fpr1:NULL, info.fpr1len);
+      print_shax_fpr_colon (fp, info.fpr2len? info.fpr2:NULL, info.fpr2len);
+      print_shax_fpr_colon (fp, info.fpr3len? info.fpr3:NULL, info.fpr3len);
       es_putc ('\n', fp);
       es_fprintf (fp, "fprtime:%lu:%lu:%lu:\n",
                (unsigned long)info.fpr1time, (unsigned long)info.fpr2time,
                (unsigned long)info.fpr3time);
+      es_fputs ("grp:", fp);
+      print_shax_fpr_colon (fp, info.grp1, sizeof info.grp1);
+      print_shax_fpr_colon (fp, info.grp2, sizeof info.grp2);
+      print_shax_fpr_colon (fp, info.grp3, sizeof info.grp3);
+      es_putc ('\n', fp);
     }
   else
     {
@@ -530,9 +569,9 @@ current_card_status (ctrl_t ctrl, estream_t fp,
 
       print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
       print_name (fp, "Language prefs ...: ", info.disp_lang);
-      tty_fprintf (fp,    "Sex ..............: %s\n",
-                   info.disp_sex == 1? _("male"):
-                   info.disp_sex == 2? _("female") : _("unspecified"));
+      tty_fprintf (fp, "Salutation .......: %s\n",
+                   info.disp_sex == 1? _("Mr."):
+                   info.disp_sex == 2? _("Mrs.") : "");
       print_name (fp, "URL of public key : ", info.pubkey_url);
       print_name (fp, "Login data .......: ", info.login_data);
       if (info.private_do[0])
@@ -543,20 +582,20 @@ current_card_status (ctrl_t ctrl, estream_t fp,
         print_name (fp, "Private DO 3 .....: ", info.private_do[2]);
       if (info.private_do[3])
         print_name (fp, "Private DO 4 .....: ", info.private_do[3]);
-      if (info.cafpr1valid)
+      if (info.cafpr1len)
         {
           tty_fprintf (fp, "CA fingerprint %d .:", 1);
-          print_sha1_fpr (fp, info.cafpr1);
+          print_shax_fpr (fp, info.cafpr1, info.cafpr1len);
         }
-      if (info.cafpr2valid)
+      if (info.cafpr2len)
         {
           tty_fprintf (fp, "CA fingerprint %d .:", 2);
-          print_sha1_fpr (fp, info.cafpr2);
+          print_shax_fpr (fp, info.cafpr2, info.cafpr2len);
         }
-      if (info.cafpr3valid)
+      if (info.cafpr3len)
         {
           tty_fprintf (fp, "CA fingerprint %d .:", 3);
-          print_sha1_fpr (fp, info.cafpr3);
+          print_shax_fpr (fp, info.cafpr3, info.cafpr3len);
         }
       tty_fprintf (fp,    "Signature PIN ....: %s\n",
                    info.chv1_cached? _("not forced"): _("forced"));
@@ -588,29 +627,51 @@ current_card_status (ctrl_t ctrl, estream_t fp,
       tty_fprintf (fp,    "PIN retry counter : %d %d %d\n",
                    info.chvretry[0], info.chvretry[1], info.chvretry[2]);
       tty_fprintf (fp,    "Signature counter : %lu\n", info.sig_counter);
+      if (info.extcap.kdf)
+        {
+          tty_fprintf (fp, "KDF setting ......: %s\n",
+                       info.kdf_do_enabled ? "on" : "off");
+        }
+      if (info.extcap.bt)
+        {
+          tty_fprintf (fp, "UIF setting ......: Sign=%s Decrypt=%s Auth=%s\n",
+                       info.uif[0] ? "on" : "off", info.uif[1] ? "on" : "off",
+                       info.uif[2] ? "on" : "off");
+        }
       tty_fprintf (fp, "Signature key ....:");
-      print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL);
-      if (info.fpr1valid && info.fpr1time)
-        tty_fprintf (fp, "      created ....: %s\n",
-                     isotimestamp (info.fpr1time));
+      print_shax_fpr (fp, info.fpr1len? info.fpr1:NULL, info.fpr1len);
+      if (info.fpr1len && info.fpr1time)
+        {
+          tty_fprintf (fp, "      created ....: %s\n",
+                       isotimestamp (info.fpr1time));
+          print_keygrip (fp, info.grp1);
+        }
       tty_fprintf (fp, "Encryption key....:");
-      print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
-      if (info.fpr2valid && info.fpr2time)
-        tty_fprintf (fp, "      created ....: %s\n",
-                     isotimestamp (info.fpr2time));
+      print_shax_fpr (fp, info.fpr2len? info.fpr2:NULL, info.fpr2len);
+      if (info.fpr2len && info.fpr2time)
+        {
+          tty_fprintf (fp, "      created ....: %s\n",
+                       isotimestamp (info.fpr2time));
+          print_keygrip (fp, info.grp2);
+        }
       tty_fprintf (fp, "Authentication key:");
-      print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
-      if (info.fpr3valid && info.fpr3time)
-        tty_fprintf (fp, "      created ....: %s\n",
-                     isotimestamp (info.fpr3time));
+      print_shax_fpr (fp, info.fpr3len? info.fpr3:NULL, info.fpr3len);
+      if (info.fpr3len && info.fpr3time)
+        {
+          tty_fprintf (fp, "      created ....: %s\n",
+                       isotimestamp (info.fpr3time));
+          print_keygrip (fp, info.grp3);
+        }
       tty_fprintf (fp, "General key info..: ");
 
-      thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 :
-                info.fpr3valid? info.fpr3 : NULL);
-      /* If the fingerprint is all 0xff, the key has no asssociated
+      thefpr = (info.fpr1len? info.fpr1 : info.fpr2len? info.fpr2 :
+                info.fpr3len? info.fpr3 : NULL);
+      thefprlen = (info.fpr1len? info.fpr1len : info.fpr2len? info.fpr2len :
+                   info.fpr3len? info.fpr3len : 0);
+      /* If the fingerprint is all 0xff, the key has no associated
          OpenPGP certificate.  */
-      if ( thefpr && !fpr_is_ff (thefpr)
-           && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, 20))
+      if ( thefpr && !fpr_is_ff (thefpr, thefprlen)
+           && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, thefprlen))
         {
           print_pubkey_info (ctrl, fp, pk);
           if (keyblock)
@@ -628,13 +689,13 @@ current_card_status (ctrl_t ctrl, estream_t fp,
 
 /* Print all available information for specific card with SERIALNO.
    Print all available information for current card when SERIALNO is NULL.
-   Or print llfor all cards when SERIALNO is "all".  */
+   Or print for all cards when SERIALNO is "all".  */
 void
 card_status (ctrl_t ctrl, estream_t fp, const char *serialno)
 {
   int err;
   strlist_t card_list, sl;
-  char *serialno0;
+  char *serialno0, *serialno1;
   int all_cards = 0;
 
   if (serialno == NULL)
@@ -660,8 +721,6 @@ card_status (ctrl_t ctrl, estream_t fp, const char *serialno)
 
   for (sl = card_list; sl; sl = sl->next)
     {
-      char *serialno1;
-
       if (!all_cards && strcmp (serialno, sl->d))
         continue;
 
@@ -682,7 +741,8 @@ card_status (ctrl_t ctrl, estream_t fp, const char *serialno)
     }
 
   /* Select the original card again.  */
-  err = agent_scd_serialno (&serialno0, serialno0);
+  err = agent_scd_serialno (&serialno1, serialno0);
+  xfree (serialno1);
 
  leave:
   xfree (serialno0);
@@ -813,12 +873,14 @@ fetch_url (ctrl_t ctrl)
           rc = keyserver_fetch (ctrl, sl, KEYORG_URL);
           free_strlist (sl);
         }
-      else if (info.fpr1valid)
+      else if (info.fpr1len)
        {
-          rc = keyserver_import_fprint (ctrl, info.fpr1, 20, opt.keyserver, 0);
+          rc = keyserver_import_fprint (ctrl, info.fpr1, info.fpr1len,
+                                        opt.keyserver, 0);
        }
     }
 
+  agent_release_card_info (&info);
   return rc;
 }
 
@@ -1087,7 +1149,7 @@ change_sex (void)
   int rc;
 
   data = cpr_get ("cardedit.change_sex",
-                  _("Sex ((M)ale, (F)emale or space): "));
+                  _("Salutation (M = Mr., F = Mrs., or space): "));
   if (!data)
     return -1;
   trim_spaces (data);
@@ -1108,7 +1170,7 @@ change_sex (void)
 
   rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL );
   if (rc)
-    log_error ("error setting sex: %s\n", gpg_strerror (rc));
+    log_error ("error setting salutation: %s\n", gpg_strerror (rc));
   xfree (data);
   write_sc_op_status (rc);
   return rc;
@@ -1276,11 +1338,11 @@ static void
 show_card_key_info (struct agent_card_info_s *info)
 {
   tty_fprintf (NULL, "Signature key ....:");
-  print_sha1_fpr (NULL, info->fpr1valid? info->fpr1:NULL);
+  print_shax_fpr (NULL, info->fpr1len? info->fpr1:NULL, info->fpr1len);
   tty_fprintf (NULL, "Encryption key....:");
-  print_sha1_fpr (NULL, info->fpr2valid? info->fpr2:NULL);
+  print_shax_fpr (NULL, info->fpr2len? info->fpr2:NULL, info->fpr2len);
   tty_fprintf (NULL, "Authentication key:");
-  print_sha1_fpr (NULL, info->fpr3valid? info->fpr3:NULL);
+  print_shax_fpr (NULL, info->fpr3len? info->fpr3:NULL, info->fpr3len);
   tty_printf ("\n");
 }
 
@@ -1291,9 +1353,9 @@ replace_existing_key_p (struct agent_card_info_s *info, int keyno)
 {
   log_assert (keyno >= 0 && keyno <= 3);
 
-  if ((keyno == 1 && info->fpr1valid)
-      || (keyno == 2 && info->fpr2valid)
-      || (keyno == 3 && info->fpr3valid))
+  if ((keyno == 1 && info->fpr1len)
+      || (keyno == 2 && info->fpr2len)
+      || (keyno == 3 && info->fpr3len))
     {
       tty_printf ("\n");
       log_info ("WARNING: such a key has already been stored on the card!\n");
@@ -1316,21 +1378,19 @@ show_keysize_warning (void)
     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"));
+    (_("Note: There is no guarantee that the card supports the requested\n"
+       "      key type or size.  If the key generation does not succeed,\n"
+       "      please check the documentation of your card to see which\n"
+       "      key types and sizes are supported.\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.  */
+   configured for the card.  Returns 0 to use the default size
+   (i.e. NBITS) or the selected size.  */
 static unsigned int
-ask_card_keyattr (int keyno, unsigned int nbits)
+ask_card_rsa_keysize (unsigned int nbits)
 {
   unsigned int min_nbits = 1024;
   unsigned int max_nbits = 4096;
@@ -1339,13 +1399,7 @@ ask_card_keyattr (int keyno, unsigned int 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);
+      prompt = xasprintf (_("What keysize do you want? (%u) "), nbits);
       answer = cpr_get ("cardedit.genkeys.size", prompt);
       cpr_kill_prompt ();
       req_nbits = *answer? atoi (answer): nbits;
@@ -1367,40 +1421,214 @@ ask_card_keyattr (int keyno, unsigned int nbits)
                       "RSA", min_nbits, max_nbits);
         }
       else
+        return req_nbits;
+    }
+}
+
+/* Ask for the key attribute of a card key.  CURRENT is the current
+   attribute configured for the card.  KEYNO is the number of the key
+   used to select the prompt.  Returns NULL to use the default
+   attribute or the selected attribute structure.  */
+static struct key_attr *
+ask_card_keyattr (int keyno, const struct key_attr *current)
+{
+  struct key_attr *key_attr = NULL;
+  char *answer = NULL;
+  int algo;
+
+  tty_printf (_("Changing card key attribute for: "));
+  if (keyno == 0)
+    tty_printf (_("Signature key\n"));
+  else if (keyno == 1)
+    tty_printf (_("Encryption key\n"));
+  else
+    tty_printf (_("Authentication key\n"));
+
+  tty_printf (_("Please select what kind of key you want:\n"));
+  tty_printf (_("   (%d) RSA\n"), 1 );
+  tty_printf (_("   (%d) ECC\n"), 2 );
+
+  for (;;)
+    {
+      xfree (answer);
+      answer = cpr_get ("cardedit.genkeys.algo", _("Your selection? "));
+      cpr_kill_prompt ();
+      algo = *answer? atoi (answer) : 0;
+
+      if (!*answer || algo == 1 || algo == 2)
+        break;
+      else
+        tty_printf (_("Invalid selection.\n"));
+    }
+
+  if (algo == 0)
+    goto leave;
+
+  key_attr = xmalloc (sizeof (struct key_attr));
+
+  if (algo == 1)
+    {
+      unsigned int nbits, result_nbits;
+
+      if (current->algo == PUBKEY_ALGO_RSA)
+        nbits = current->nbits;
+      else
+        nbits = 2048;
+
+      result_nbits = ask_card_rsa_keysize (nbits);
+      if (result_nbits == 0)
         {
-          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;
+          if (current->algo == PUBKEY_ALGO_RSA)
+            {
+              xfree (key_attr);
+              key_attr = NULL;
+            }
+          else
+            result_nbits = nbits;
+        }
+
+      if (key_attr)
+        {
+          key_attr->algo = PUBKEY_ALGO_RSA;
+          key_attr->nbits = result_nbits;
         }
     }
+  else
+    {
+      const char *curve;
+      const char *oid_str;
+
+      if (current->algo == PUBKEY_ALGO_RSA)
+        {
+          if (keyno == 1)
+            /* Encryption key */
+            algo = PUBKEY_ALGO_ECDH;
+          else /* Signature key or Authentication key */
+            algo = PUBKEY_ALGO_ECDSA;
+          curve = NULL;
+        }
+      else
+        {
+          algo = current->algo;
+          curve = current->curve;
+        }
+
+      curve = ask_curve (&algo, NULL, curve);
+      if (curve)
+        {
+          key_attr->algo = algo;
+          oid_str = openpgp_curve_to_oid (curve, NULL);
+          key_attr->curve = openpgp_oid_to_curve (oid_str, 0);
+        }
+      else
+        {
+          xfree (key_attr);
+          key_attr = NULL;
+        }
+    }
+
+ leave:
+  if (key_attr)
+    {
+      if (key_attr->algo == PUBKEY_ALGO_RSA)
+        tty_printf (_("The card will now be re-configured"
+                      " to generate a key of %u bits\n"), key_attr->nbits);
+      else if (key_attr->algo == PUBKEY_ALGO_ECDH
+               || key_attr->algo == PUBKEY_ALGO_ECDSA
+               || key_attr->algo == PUBKEY_ALGO_EDDSA)
+        tty_printf (_("The card will now be re-configured"
+                      " to generate a key of type: %s\n"), key_attr->curve),
+
+      show_keysize_warning ();
+    }
+
+  return key_attr;
 }
 
 
-/* Change the size of key KEYNO (0..2) to NBITS and show an error
-   message if that fails.  */
+
+/* Change the key attribute of key KEYNO (0..2) and show an error
+ * message if that fails.  */
 static gpg_error_t
-do_change_keyattr (int keyno, unsigned int nbits)
+do_change_keyattr (int keyno, const struct key_attr *key_attr)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   char args[100];
 
-  snprintf (args, sizeof args, "--force %d 1 rsa%u", keyno+1, nbits);
+  if (key_attr->algo == PUBKEY_ALGO_RSA)
+    snprintf (args, sizeof args, "--force %d 1 rsa%u", keyno+1,
+              key_attr->nbits);
+  else if (key_attr->algo == PUBKEY_ALGO_ECDH
+           || key_attr->algo == PUBKEY_ALGO_ECDSA
+           || key_attr->algo == PUBKEY_ALGO_EDDSA)
+    snprintf (args, sizeof args, "--force %d %d %s",
+              keyno+1, key_attr->algo, key_attr->curve);
+  else
+    {
+      log_error (_("public key algorithm %d (%s) is not supported\n"),
+                 key_attr->algo, gcry_pk_algo_name (key_attr->algo));
+      return gpg_error (GPG_ERR_PUBKEY_ALGO);
+    }
+
   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));
+    log_error (_("error changing key attribute for key %d: %s\n"),
+               keyno+1, gpg_strerror (err));
   return err;
 }
 
 
 static void
+key_attr (void)
+{
+  struct agent_card_info_s info;
+  gpg_error_t err;
+  int keyno;
+
+  err = get_info_for_key_operation (&info);
+  if (err)
+    {
+      log_error (_("error getting card info: %s\n"), gpg_strerror (err));
+      return;
+    }
+
+  if (!(info.is_v2 && info.extcap.aac))
+    {
+      log_error (_("This command is not supported by this card\n"));
+      goto leave;
+    }
+
+  for (keyno = 0; keyno < DIM (info.key_attr); keyno++)
+    {
+      struct key_attr *key_attr;
+
+      if ((key_attr = ask_card_keyattr (keyno, &info.key_attr[keyno])))
+        {
+          err = do_change_keyattr (keyno, key_attr);
+          xfree (key_attr);
+          if (err)
+            {
+              /* Error: Better read the default key attribute again.  */
+              agent_release_card_info (&info);
+              if (get_info_for_key_operation (&info))
+                goto leave;
+              /* Ask again for this key. */
+              keyno--;
+            }
+        }
+    }
+
+ leave:
+  agent_release_card_info (&info);
+}
+
+
+static void
 generate_card_keys (ctrl_t ctrl)
 {
   struct agent_card_info_s info;
   int forced_chv1;
   int want_backup;
-  int keyno;
 
   if (get_info_for_key_operation (&info))
     return;
@@ -1421,9 +1649,9 @@ generate_card_keys (ctrl_t ctrl)
   else
     want_backup = 0;
 
-  if ( (info.fpr1valid && !fpr_is_zero (info.fpr1))
-       || (info.fpr2valid && !fpr_is_zero (info.fpr2))
-       || (info.fpr3valid && !fpr_is_zero (info.fpr3)))
+  if ( (info.fpr1len && !fpr_is_zero (info.fpr1, info.fpr1len))
+       || (info.fpr2len && !fpr_is_zero (info.fpr2, info.fpr2len))
+       || (info.fpr3len && !fpr_is_zero (info.fpr3, info.fpr3len)))
     {
       tty_printf ("\n");
       log_info (_("Note: keys are already stored on the card!\n"));
@@ -1448,35 +1676,10 @@ generate_card_keys (ctrl_t ctrl)
       tty_printf ("\n");
     }
 
+
   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;
-
-      for (keyno = 0; keyno < DIM (info.key_attr); keyno++)
-        {
-          if (info.key_attr[keyno].algo == PUBKEY_ALGO_RSA)
-            {
-              nbits = ask_card_keyattr (keyno, info.key_attr[keyno].nbits);
-              if (nbits && do_change_keyattr (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 (ctrl, 1, NULL, info.serialno, want_backup);
 
  leave:
@@ -1535,30 +1738,6 @@ card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock)
   if (err)
     goto leave;
 
-  /* If the cards features changeable key attributes, we ask for the
-     key size.  */
-  if (info.is_v2 && info.extcap.aac)
-    {
-      if (info.key_attr[keyno-1].algo == PUBKEY_ALGO_RSA)
-        {
-          unsigned int nbits;
-
-        ask_again:
-          nbits = ask_card_keyattr (keyno-1, info.key_attr[keyno-1].nbits);
-          if (nbits && do_change_keyattr (keyno-1, nbits))
-            {
-              /* Error: Better read the default key size again.  */
-              agent_release_card_info (&info);
-              err = get_info_for_key_operation (&info);
-              if (err)
-                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.  */
-    }
-
   err = generate_card_subkeypair (ctrl, pub_keyblock, keyno, info.serialno);
 
  leave:
@@ -1730,6 +1909,7 @@ factory_reset (void)
         scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
         scd apdu 00 e6 00 00
         scd apdu 00 44 00 00
+        scd reset
         /echo Card has been reset to factory defaults
 
       but tries to find out something about the card first.
@@ -1742,7 +1922,7 @@ factory_reset (void)
   else if (err)
     {
       log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err));
-      return;
+      goto leave;
     }
 
   if (!termstate)
@@ -1792,10 +1972,16 @@ factory_reset (void)
          command because there is no machinery in scdaemon to catch
          the verify command and ask for the PIN when the "APDU"
          command is used. */
+      /* Here, the length of dummy wrong PIN is 32-byte, also
+         supporting authentication with KDF DO.  */
       for (i=0; i < 4; i++)
-        send_apdu ("00200081084040404040404040", "VERIFY", 0xffff);
+        send_apdu ("0020008120"
+                   "40404040404040404040404040404040"
+                   "40404040404040404040404040404040", "VERIFY", 0xffff);
       for (i=0; i < 4; i++)
-        send_apdu ("00200083084040404040404040", "VERIFY", 0xffff);
+        send_apdu ("0020008320"
+                   "40404040404040404040404040404040"
+                   "40404040404040404040404040404040", "VERIFY", 0xffff);
 
       /* Send terminate datafile command.  */
       err = send_apdu ("00e60000", "TERMINATE DF", 0x6985);
@@ -1811,8 +1997,16 @@ factory_reset (void)
 
   /* Finally we reset the card reader once more.  */
   err = send_apdu (NULL, "RESET", 0);
-  if (err)
-    goto leave;
+
+  /* Then, connect the card again.  */
+  if (!err)
+    {
+      char *serialno0;
+
+      err = agent_scd_serialno (&serialno0, NULL);
+      if (!err)
+        xfree (serialno0);
+    }
 
  leave:
   xfree (answer);
@@ -1820,6 +2014,165 @@ factory_reset (void)
 }
 
 
+#define USER_PIN_DEFAULT "123456"
+#define ADMIN_PIN_DEFAULT "12345678"
+#define KDF_DATA_LENGTH_MIN  90
+#define KDF_DATA_LENGTH_MAX 110
+
+/* Generate KDF data.  */
+static gpg_error_t
+gen_kdf_data (unsigned char *data, int single_salt)
+{
+  const unsigned char h0[] = { 0x81, 0x01, 0x03,
+                               0x82, 0x01, 0x08,
+                               0x83, 0x04 };
+  const unsigned char h1[] = { 0x84, 0x08 };
+  const unsigned char h2[] = { 0x85, 0x08 };
+  const unsigned char h3[] = { 0x86, 0x08 };
+  const unsigned char h4[] = { 0x87, 0x20 };
+  const unsigned char h5[] = { 0x88, 0x20 };
+  unsigned char *p, *salt_user, *salt_admin;
+  unsigned char s2k_char;
+  unsigned int iterations;
+  unsigned char count_4byte[4];
+  gpg_error_t err = 0;
+
+  p = data;
+
+  s2k_char = encode_s2k_iterations (0);
+  iterations = S2K_DECODE_COUNT (s2k_char);
+  count_4byte[0] = (iterations >> 24) & 0xff;
+  count_4byte[1] = (iterations >> 16) & 0xff;
+  count_4byte[2] = (iterations >>  8) & 0xff;
+  count_4byte[3] = (iterations & 0xff);
+
+  memcpy (p, h0, sizeof h0);
+  p += sizeof h0;
+  memcpy (p, count_4byte, sizeof count_4byte);
+  p += sizeof count_4byte;
+  memcpy (p, h1, sizeof h1);
+  salt_user = (p += sizeof h1);
+  gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+  p += 8;
+
+  if (single_salt)
+    salt_admin = salt_user;
+  else
+    {
+      memcpy (p, h2, sizeof h2);
+      p += sizeof h2;
+      gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+      p += 8;
+      memcpy (p, h3, sizeof h3);
+      salt_admin = (p += sizeof h3);
+      gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+      p += 8;
+    }
+
+  memcpy (p, h4, sizeof h4);
+  p += sizeof h4;
+  err = gcry_kdf_derive (USER_PIN_DEFAULT, strlen (USER_PIN_DEFAULT),
+                         GCRY_KDF_ITERSALTED_S2K, DIGEST_ALGO_SHA256,
+                         salt_user, 8, iterations, 32, p);
+  p += 32;
+  if (!err)
+    {
+      memcpy (p, h5, sizeof h5);
+      p += sizeof h5;
+      err = gcry_kdf_derive (ADMIN_PIN_DEFAULT, strlen (ADMIN_PIN_DEFAULT),
+                             GCRY_KDF_ITERSALTED_S2K, DIGEST_ALGO_SHA256,
+                             salt_admin, 8, iterations, 32, p);
+    }
+
+  return err;
+}
+
+/* Setup KDF data object which is used for PIN authentication.  */
+static void
+kdf_setup (const char *args)
+{
+  struct agent_card_info_s info;
+  gpg_error_t err;
+  unsigned char kdf_data[KDF_DATA_LENGTH_MAX];
+  int single = (*args != 0);
+
+  memset (&info, 0, sizeof info);
+
+  err = agent_scd_getattr ("EXTCAP", &info);
+  if (err)
+    {
+      log_error (_("error getting card info: %s\n"), gpg_strerror (err));
+      return;
+    }
+
+  if (!info.extcap.kdf)
+    {
+      log_error (_("This command is not supported by this card\n"));
+      goto leave;
+    }
+
+  err = gen_kdf_data (kdf_data, single);
+  if (err)
+    goto leave_error;
+
+  err = agent_scd_setattr ("KDF", kdf_data,
+                           single ? KDF_DATA_LENGTH_MIN : KDF_DATA_LENGTH_MAX,
+                           NULL);
+  if (err)
+    goto leave_error;
+
+  err = agent_scd_getattr ("KDF", &info);
+
+ leave_error:
+  if (err)
+    log_error (_("error for setup KDF: %s\n"), gpg_strerror (err));
+
+ leave:
+  agent_release_card_info (&info);
+}
+
+static void
+uif (int arg_number, const char *arg_rest)
+{
+  struct agent_card_info_s info;
+  int feature_available;
+  gpg_error_t err;
+  char name[100];
+  unsigned char data[2];
+
+  memset (&info, 0, sizeof info);
+
+  err = agent_scd_getattr ("EXTCAP", &info);
+  if (err)
+    {
+      log_error (_("error getting card info: %s\n"), gpg_strerror (err));
+      return;
+    }
+
+  feature_available = info.extcap.bt;
+  agent_release_card_info (&info);
+
+  if (!feature_available)
+    {
+      log_error (_("This command is not supported by this card\n"));
+      tty_printf ("\n");
+      return;
+    }
+
+  snprintf (name, sizeof name, "UIF-%d", arg_number);
+  if ( !strcmp (arg_rest, "off") )
+    data[0] = 0x00;
+  else if ( !strcmp (arg_rest, "on") )
+    data[0] = 0x01;
+  else if ( !strcmp (arg_rest, "permanent") )
+    data[0] = 0x02;
+
+  data[1] = 0x20;
+
+  err = agent_scd_setattr (name, data, 2, NULL);
+  if (err)
+    log_error (_("error for setup UIF: %s\n"), gpg_strerror (err));
+}
 \f
 /* Data used by the command parser.  This needs to be outside of the
    function scope to allow readline based command completion.  */
@@ -1829,7 +2182,8 @@ enum cmdids
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
-    cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET,
+    cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP,
+    cmdKEYATTR, cmdUIF,
     cmdINVCMD
   };
 
@@ -1854,14 +2208,18 @@ static struct
     { "fetch"   , cmdFETCH , 0, N_("fetch the key specified in the card URL")},
     { "login"   , cmdLOGIN , 1, N_("change the login name")},
     { "lang"    , cmdLANG  , 1, N_("change the language preferences")},
-    { "sex"     , cmdSEX   , 1, N_("change card holder's sex")},
+    { "salutation",cmdSEX  , 1, N_("change card holder's salutation")},
+    { "sex"       ,cmdSEX  , 1, NULL },  /* Backward compatibility.  */
     { "cafpr"   , cmdCAFPR , 1, N_("change a CA fingerprint")},
     { "forcesig", cmdFORCESIG, 1, N_("toggle the signature force PIN flag")},
     { "generate", cmdGENERATE, 1, N_("generate new keys")},
     { "passwd"  , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
     { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
-    { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
+    { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code")},
     { "factory-reset", cmdFACTORYRESET, 1, N_("destroy all keys and data")},
+    { "kdf-setup", cmdKDFSETUP, 1, N_("setup KDF for PIN authentication")},
+    { "key-attr", cmdKEYATTR, 1, N_("change the key attribute")},
+    { "uif", cmdUIF, 1, N_("change the User Interaction Flag")},
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
     { "readcert", cmdREADCERT, 0, NULL },
@@ -2145,6 +2503,22 @@ card_edit (ctrl_t ctrl, strlist_t commands)
           factory_reset ();
           break;
 
+        case cmdKDFSETUP:
+          kdf_setup (arg_string);
+          break;
+
+        case cmdKEYATTR:
+          key_attr ();
+          break;
+
+        case cmdUIF:
+          if ( arg_number < 1 || arg_number > 3 )
+            tty_printf ("usage: uif N [on|off|permanent]\n"
+                        "       1 <= N <= 3\n");
+          else
+            uif (arg_number, arg_rest);
+          break;
+
         case cmdQUIT:
           goto leave;