scd: Support KDF DO setup.
[gnupg.git] / g10 / card-util.c
index a291a07..d78e9bd 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -23,7 +23,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #ifdef HAVE_LIBREADLINE
 # define GNUPG_LIBREADLINE_H_INCLUDED
 # include <readline/readline.h>
 #if GNUPG_MAJOR_VERSION != 1
 # include "gpg.h"
 #endif /*GNUPG_MAJOR_VERSION != 1*/
-#include "util.h"
-#include "i18n.h"
-#include "ttyio.h"
-#include "status.h"
+#include "../common/util.h"
+#include "../common/i18n.h"
+#include "../common/ttyio.h"
+#include "../common/status.h"
 #include "options.h"
 #include "main.h"
 #include "keyserver-internal.h"
@@ -73,7 +72,7 @@ write_sc_op_status (gpg_error_t err)
 }
 
 
-/* Change the PIN of a an OpenPGP card.  This is an interactive
+/* Change the PIN of an OpenPGP card.  This is an interactive
    function. */
 void
 change_pin (int unblock_v2, int allow_admin)
@@ -145,7 +144,6 @@ change_pin (int unblock_v2, int allow_admin)
        if (strlen (answer) != 1)
          continue;
 
-       rc = 0;
        if (*answer == '1')
          {
             /* Change PIN.  */
@@ -210,11 +208,15 @@ get_manufacturer (unsigned int no)
     case 0x0006: return "Yubico";
     case 0x0007: return "OpenKMS";
     case 0x0008: return "LogoEmail";
+    case 0x0009: return "Fidesmo";
+    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 0xBD0E: return "Paranoidlabs";
     case 0xF517: return "FSIJ";
 
       /* 0x0000 and 0xFFFF are defined as test cards per spec,
@@ -262,6 +264,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++)
+        es_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);
@@ -361,11 +378,13 @@ fpr_is_ff (const char *fpr)
 
 
 /* Print all available information about the current card. */
-void
-card_status (estream_t fp, char *serialno, size_t serialnobuflen)
+static void
+current_card_status (ctrl_t ctrl, estream_t fp,
+                     char *serialno, size_t serialnobuflen)
 {
   struct agent_card_info_s info;
   PKT_public_key *pk = xcalloc (1, sizeof *pk);
+  kbnode_t keyblock = NULL;
   int rc;
   unsigned int uval;
   const unsigned char *thefpr;
@@ -385,6 +404,11 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
     }
 
   if (opt.with_colons)
+    es_fprintf (fp, "Reader:%s:", info.reader? info.reader : "");
+  else
+    tty_fprintf (fp, "Reader ...........: %s\n",
+                 info.reader? info.reader : "[none]");
+  if (opt.with_colons)
     es_fprintf (fp, "AID:%s:", info.serialno? info.serialno : "");
   else
     tty_fprintf (fp, "Application ID ...: %s\n",
@@ -429,7 +453,7 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
 
   if (!serialno)
     ;
-  else if (strlen (serialno)+1 > serialnobuflen)
+  else if (strlen (info.serialno)+1 > serialnobuflen)
     log_error ("serial number longer than expected\n");
   else
     strcpy (serialno, info.serialno);
@@ -470,9 +494,14 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
 
       es_fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
       for (i=0; i < DIM (info.key_attr); i++)
-        if (info.key_attr[0].algo)
+        if (info.key_attr[i].algo == PUBKEY_ALGO_RSA)
           es_fprintf (fp, "keyattr:%d:%d:%u:\n", i+1,
                       info.key_attr[i].algo, info.key_attr[i].nbits);
+        else if (info.key_attr[i].algo == PUBKEY_ALGO_ECDH
+                 || info.key_attr[i].algo == PUBKEY_ALGO_ECDSA
+                 || info.key_attr[i].algo == PUBKEY_ALGO_EDDSA)
+          es_fprintf (fp, "keyattr:%d:%d:%s:\n", i+1,
+                      info.key_attr[i].algo, info.key_attr[i].curve);
       es_fprintf (fp, "maxpinlen:%d:%d:%d:\n",
                   info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
       es_fprintf (fp, "pinretry:%d:%d:%d:\n",
@@ -503,6 +532,11 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
       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_sha1_fpr_colon (fp, info.grp1);
+      print_sha1_fpr_colon (fp, info.grp2);
+      print_sha1_fpr_colon (fp, info.grp3);
+      es_putc ('\n', fp);
     }
   else
     {
@@ -552,12 +586,23 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
         {
           tty_fprintf (fp,    "Key attributes ...:");
           for (i=0; i < DIM (info.key_attr); i++)
-            tty_fprintf (fp, " %u%c",
-                         info.key_attr[i].nbits,
-                         info.key_attr[i].algo == 1? 'R':
-                         info.key_attr[i].algo == 17? 'D':
-                         info.key_attr[i].algo == 18? 'e':
-                         info.key_attr[i].algo == 19? 'E': '?');
+            if (info.key_attr[i].algo == PUBKEY_ALGO_RSA)
+              tty_fprintf (fp, " rsa%u", info.key_attr[i].nbits);
+            else if (info.key_attr[i].algo == PUBKEY_ALGO_ECDH
+                     || info.key_attr[i].algo == PUBKEY_ALGO_ECDSA
+                     || info.key_attr[i].algo == PUBKEY_ALGO_EDDSA)
+              {
+                const char *curve_for_print = "?";
+
+                if (info.key_attr[i].curve)
+                  {
+                    const char *oid;
+                    oid = openpgp_curve_to_oid (info.key_attr[i].curve, NULL);
+                    if (oid)
+                      curve_for_print = openpgp_oid_to_curve (oid, 0);
+                  }
+                tty_fprintf (fp, " %s", curve_for_print);
+              }
           tty_fprintf (fp, "\n");
         }
       tty_fprintf (fp,    "Max. PIN lengths .: %d %d %d\n",
@@ -568,18 +613,27 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
       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));
+        {
+          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));
+        {
+          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));
+        {
+          tty_fprintf (fp, "      created ....: %s\n",
+                       isotimestamp (info.fpr3time));
+          print_keygrip (fp, info.grp2);
+        }
       tty_fprintf (fp, "General key info..: ");
 
       thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 :
@@ -587,46 +641,86 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
       /* If the fingerprint is all 0xff, the key has no asssociated
          OpenPGP certificate.  */
       if ( thefpr && !fpr_is_ff (thefpr)
-           && !get_pubkey_byfprint (pk, thefpr, 20))
+           && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, 20))
         {
-          kbnode_t keyblock = NULL;
-
-          print_pubkey_info (fp, pk);
-
-#if GNUPG_MAJOR_VERSION == 1
-          if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
+          print_pubkey_info (ctrl, fp, pk);
+          if (keyblock)
             print_card_key_info (fp, keyblock);
-          else if ( !get_keyblock_byfprint (&keyblock, thefpr, 20) )
-            {
-              release_kbnode (keyblock);
-              keyblock = NULL;
-
-              if (!auto_create_card_key_stub (info.serialno,
-                                              info.fpr1valid? info.fpr1:NULL,
-                                              info.fpr2valid? info.fpr2:NULL,
-                                              info.fpr3valid? info.fpr3:NULL))
-                {
-                  if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
-                    print_card_key_info (fp, keyblock);
-                }
-            }
-
-#else /* GNUPG_MAJOR_VERSION != 1 */
-          if (!get_keyblock_byfprint (&keyblock, thefpr, 20))
-            print_card_key_info (fp, keyblock);
-#endif /* GNUPG_MAJOR_VERSION != 1 */
-
-          release_kbnode (keyblock);
         }
       else
         tty_fprintf (fp, "[none]\n");
     }
 
+  release_kbnode (keyblock);
   free_public_key (pk);
   agent_release_card_info (&info);
 }
 
 
+/* Print all available information for specific card with SERIALNO.
+   Print all available information for current card when SERIALNO is NULL.
+   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;
+  int all_cards = 0;
+
+  if (serialno == NULL)
+    {
+      current_card_status (ctrl, fp, NULL, 0);
+      return;
+    }
+
+  if (!strcmp (serialno, "all"))
+    all_cards = 1;
+
+  err = agent_scd_serialno (&serialno0, NULL);
+  if (err)
+    {
+      if (gpg_err_code (err) != GPG_ERR_ENODEV && opt.verbose)
+        log_info (_("error getting serial number of card: %s\n"),
+                  gpg_strerror (err));
+      /* Nothing available.  */
+      return;
+    }
+
+  err = agent_scd_cardlist (&card_list);
+
+  for (sl = card_list; sl; sl = sl->next)
+    {
+      char *serialno1;
+
+      if (!all_cards && strcmp (serialno, sl->d))
+        continue;
+
+      err = agent_scd_serialno (&serialno1, sl->d);
+      if (err)
+        {
+          if (opt.verbose)
+            log_info (_("error getting serial number of card: %s\n"),
+                      gpg_strerror (err));
+          continue;
+        }
+
+      current_card_status (ctrl, fp, NULL, 0);
+      xfree (serialno1);
+
+      if (!all_cards)
+        goto leave;
+    }
+
+  /* Select the original card again.  */
+  err = agent_scd_serialno (&serialno0, serialno0);
+
+ leave:
+  xfree (serialno0);
+  free_strlist (card_list);
+}
+
+
 static char *
 get_one_name (const char *prompt1, const char *prompt2)
 {
@@ -714,14 +808,6 @@ change_url (void)
   trim_spaces (url);
   cpr_kill_prompt ();
 
-  if (strlen (url) > 254 )
-    {
-      tty_printf (_("Error: URL too long "
-                    "(limit is %d characters).\n"), 254);
-      xfree (url);
-      return -1;
-    }
-
   rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url), NULL );
   if (rc)
     log_error ("error setting URL: %s\n", gpg_strerror (rc));
@@ -746,31 +832,21 @@ fetch_url (ctrl_t ctrl)
     log_error("error retrieving URL from card: %s\n",gpg_strerror(rc));
   else
     {
-      struct keyserver_spec *spec=NULL;
-
       rc=agent_scd_getattr("KEY-FPR",&info);
       if(rc)
        log_error("error retrieving key fingerprint from card: %s\n",
                  gpg_strerror(rc));
       else if (info.pubkey_url && *info.pubkey_url)
-       {
-         spec = parse_keyserver_uri (info.pubkey_url, 1);
-         if(spec && info.fpr1valid)
-           {
-             /* This is not perfectly right.  Currently, all card
-                fingerprints are 20 digits, but what about
-                fingerprints for a future v5 key?  We should get the
-                length from somewhere lower in the code.  In any
-                event, the fpr/keyid is not meaningful for straight
-                HTTP fetches, but using it allows the card to point
-                to HKP and LDAP servers as well. */
-             rc = keyserver_import_fprint (ctrl, info.fpr1, 20, spec);
-             free_keyserver_spec(spec);
-           }
-       }
+        {
+          strlist_t sl = NULL;
+
+          add_to_strlist (&sl, info.pubkey_url);
+          rc = keyserver_fetch (ctrl, sl, KEYORG_URL);
+          free_strlist (sl);
+        }
       else if (info.fpr1valid)
        {
-          rc = keyserver_import_fprint (ctrl, info.fpr1, 20, opt.keyserver);
+          rc = keyserver_import_fprint (ctrl, info.fpr1, 20, opt.keyserver, 0);
        }
     }
 
@@ -778,12 +854,14 @@ fetch_url (ctrl_t ctrl)
 }
 
 
-/* Read data from file FNAME up to MAXLEN characters.  On error return
-   -1 and store NULL at R_BUFFER; on success return the number of
-   bytes read and store the address of a newly allocated buffer at
-   R_BUFFER. */
+#define MAX_GET_DATA_FROM_FILE 16384
+
+/* Read data from file FNAME up to MAX_GET_DATA_FROM_FILE characters.
+   On error return -1 and store NULL at R_BUFFER; on success return
+   the number of bytes read and store the address of a newly allocated
+   buffer at R_BUFFER. */
 static int
-get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
+get_data_from_file (const char *fname, char **r_buffer)
 {
   estream_t fp;
   char *data;
@@ -806,7 +884,7 @@ get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
       return -1;
     }
 
-  data = xtrymalloc (maxlen? maxlen:1);
+  data = xtrymalloc (MAX_GET_DATA_FROM_FILE);
   if (!data)
     {
       tty_printf (_("error allocating enough memory: %s\n"), strerror (errno));
@@ -814,10 +892,7 @@ get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
       return -1;
     }
 
-  if (maxlen)
-    n = es_fread (data, 1, maxlen, fp);
-  else
-    n = 0;
+  n = es_fread (data, 1, MAX_GET_DATA_FROM_FILE, fp);
   es_fclose (fp);
   if (n < 0)
     {
@@ -874,7 +949,7 @@ change_login (const char *args)
     {
       for (args++; spacep (args); args++)
         ;
-      n = get_data_from_file (args, 254, &data);
+      n = get_data_from_file (args, &data);
       if (n < 0)
         return -1;
     }
@@ -889,14 +964,6 @@ change_login (const char *args)
       n = strlen (data);
     }
 
-  if (n > 254 )
-    {
-      tty_printf (_("Error: Login data too long "
-                    "(limit is %d characters).\n"), 254);
-      xfree (data);
-      return -1;
-    }
-
   rc = agent_scd_setattr ("LOGIN-DATA", data, n, NULL );
   if (rc)
     log_error ("error setting login data: %s\n", gpg_strerror (rc));
@@ -913,14 +980,14 @@ change_private_do (const char *args, int nr)
   int n;
   int rc;
 
-  assert (nr >= 1 && nr <= 4);
+  log_assert (nr >= 1 && nr <= 4);
   do_name[11] = '0' + nr;
 
   if (args && (args = strchr (args, '<')))  /* Read it from a file */
     {
       for (args++; spacep (args); args++)
         ;
-      n = get_data_from_file (args, 254, &data);
+      n = get_data_from_file (args, &data);
       if (n < 0)
         return -1;
     }
@@ -935,14 +1002,6 @@ change_private_do (const char *args, int nr)
       n = strlen (data);
     }
 
-  if (n > 254 )
-    {
-      tty_printf (_("Error: Private DO too long "
-                    "(limit is %d characters).\n"), 254);
-      xfree (data);
-      return -1;
-    }
-
   rc = agent_scd_setattr (do_name, data, n, NULL );
   if (rc)
     log_error ("error setting private DO: %s\n", gpg_strerror (rc));
@@ -963,7 +1022,7 @@ change_cert (const char *args)
     {
       for (args++; spacep (args); args++)
         ;
-      n = get_data_from_file (args, 16384, &data);
+      n = get_data_from_file (args, &data);
       if (n < 0)
         return -1;
     }
@@ -1259,7 +1318,7 @@ show_card_key_info (struct agent_card_info_s *info)
 static int
 replace_existing_key_p (struct agent_card_info_s *info, int keyno)
 {
-  assert (keyno >= 0 && keyno <= 3);
+  log_assert (keyno >= 0 && keyno <= 3);
 
   if ((keyno == 1 && info->fpr1valid)
       || (keyno == 2 && info->fpr2valid)
@@ -1300,7 +1359,7 @@ show_keysize_warning (void)
    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)
+ask_card_keyattr (int keyno, unsigned int nbits)
 {
   unsigned int min_nbits = 1024;
   unsigned int max_nbits = 4096;
@@ -1322,40 +1381,61 @@ ask_card_keysize (int keyno, unsigned int nbits)
       xfree (prompt);
       xfree (answer);
 
-      if (req_nbits != nbits && (req_nbits % 32) )
+      if (req_nbits == 25519)
         {
-          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 == 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);
+          tty_printf (_("The card will now be re-configured"
+                        " to generate a key of type: %s\n"),
+                      keyno==1? "cv25519":"ed25519");
+          show_keysize_warning ();
+          return req_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;
+          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.  */
+ * message if that fails.  Using the magic value 25519 for NBITS
+ * switches to ed25519 or cv25519 depending on the KEYNO.  */
 static gpg_error_t
-do_change_keysize (int keyno, unsigned int nbits)
+do_change_keyattr (int keyno, unsigned int nbits)
 {
   gpg_error_t err;
   char args[100];
 
-  snprintf (args, sizeof args, "--force %d 1 %u", keyno+1, nbits);
+  if (nbits == 25519)
+    snprintf (args, sizeof args, "--force %d %d %s",
+              keyno+1,
+              keyno == 1? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_EDDSA,
+              keyno == 1? "cv25519" : "ed25519");
+  else
+    snprintf (args, sizeof args, "--force %d 1 rsa%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"),
@@ -1429,15 +1509,24 @@ generate_card_keys (ctrl_t ctrl)
 
       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))
+          if (info.key_attr[keyno].algo == PUBKEY_ALGO_RSA
+              || info.key_attr[keyno].algo == PUBKEY_ALGO_ECDH
+              || info.key_attr[keyno].algo == PUBKEY_ALGO_EDDSA)
             {
-              /* 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--;
+              if (info.key_attr[keyno].algo == PUBKEY_ALGO_RSA)
+                nbits = ask_card_keyattr (keyno, info.key_attr[keyno].nbits);
+              else
+                nbits = ask_card_keyattr (keyno, 25519 /* magic */);
+
+              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
@@ -1455,7 +1544,7 @@ generate_card_keys (ctrl_t ctrl)
 /* This function is used by the key edit menu to generate an arbitrary
    subkey. */
 gpg_error_t
-card_generate_subkey (KBNODE pub_keyblock)
+card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock)
 {
   gpg_error_t err;
   struct agent_card_info_s info;
@@ -1506,24 +1595,33 @@ card_generate_subkey (KBNODE pub_keyblock)
      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))
+      if (info.key_attr[keyno-1].algo == PUBKEY_ALGO_RSA
+          || info.key_attr[keyno].algo == PUBKEY_ALGO_ECDH
+          || info.key_attr[keyno].algo == PUBKEY_ALGO_EDDSA)
         {
-          /* 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;
+          unsigned int nbits;
+
+        ask_again:
+          if (info.key_attr[keyno].algo == PUBKEY_ALGO_RSA)
+            nbits = ask_card_keyattr (keyno-1, info.key_attr[keyno-1].nbits);
+          else
+            nbits = ask_card_keyattr (keyno-1, 25519);
+
+          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 (pub_keyblock, keyno, info.serialno);
+  err = generate_card_subkeypair (ctrl, pub_keyblock, keyno, info.serialno);
 
  leave:
   agent_release_card_info (&info);
@@ -1550,8 +1648,8 @@ card_store_subkey (KBNODE node, int use)
   int rc;
   gnupg_isotime_t timebuf;
 
-  assert (node->pkt->pkttype == PKT_PUBLIC_KEY
-          || node->pkt->pkttype == PKT_PUBLIC_SUBKEY);
+  log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY
+              || node->pkt->pkttype == PKT_PUBLIC_SUBKEY);
 
   pk = node->pkt->pkt.public_key;
 
@@ -1693,10 +1791,8 @@ factory_reset (void)
         scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
         scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
         scd apdu 00 e6 00 00
-        scd reset
-        scd serialno undefined
-        scd apdu 00 A4 04 00 06 D2 76 00 01 24 01
         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.
@@ -1709,7 +1805,7 @@ factory_reset (void)
   else if (err)
     {
       log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err));
-      return;
+      goto leave;
     }
 
   if (!termstate)
@@ -1759,10 +1855,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);
@@ -1770,17 +1872,6 @@ factory_reset (void)
         goto leave;
     }
 
-  /* The card is in termination state - reset and select again.  */
-  err = send_apdu (NULL, "RESET", 0);
-  if (err)
-    goto leave;
-  err = send_apdu ("undefined", "dummy select", 0);
-  if (err)
-    goto leave;
-
-  /* Select the OpenPGP application. (no error checking here). */
-  send_apdu ("00A4040006D27600012401", "SELECT AID", 0xffff);
-
   /* Send activate datafile command.  This is used without
      confirmation if the card is already in termination state.  */
   err = send_apdu ("00440000", "ACTIVATE DF", 0);
@@ -1789,8 +1880,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);
@@ -1798,6 +1897,104 @@ factory_reset (void)
 }
 
 
+#define USER_PIN_DEFAULT "123456"
+#define ADMIN_PIN_DEFAULT "12345678"
+#define KDF_DATA_LENGTH 110
+
+/* Generate KDF data.  */
+static gpg_error_t
+gen_kdf_data (unsigned char *data)
+{
+  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;
+  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 (void)
+{
+  struct agent_card_info_s info;
+  gpg_error_t err;
+  unsigned char kdf_data[KDF_DATA_LENGTH];
+
+  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;
+    }
+
+  if (!(err = gen_kdf_data (kdf_data))
+      && !(err = agent_scd_setattr ("KDF", kdf_data, KDF_DATA_LENGTH, NULL)))
+    err = agent_scd_getattr ("KDF", &info);
+
+  if (err)
+    log_error (_("error for setup KDF: %s\n"), gpg_strerror (err));
+
+ leave:
+  agent_release_card_info (&info);
+}
 \f
 /* Data used by the command parser.  This needs to be outside of the
    function scope to allow readline based command completion.  */
@@ -1807,7 +2004,7 @@ 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,
     cmdINVCMD
   };
 
@@ -1840,6 +2037,7 @@ static struct
     { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
     { "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")},
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
     { "readcert", cmdREADCERT, 0, NULL },
@@ -1925,16 +2123,18 @@ card_edit (ctrl_t ctrl, strlist_t commands)
       int cmd_admin_only;
 
       tty_printf("\n");
-      if (redisplay )
+      if (redisplay)
         {
           if (opt.with_colons)
             {
-              card_status (es_stdout, serialnobuf, DIM (serialnobuf));
+              current_card_status (ctrl, es_stdout,
+                                   serialnobuf, DIM (serialnobuf));
               fflush (stdout);
             }
           else
             {
-              card_status (NULL, serialnobuf, DIM (serialnobuf));
+              current_card_status (ctrl, NULL,
+                                   serialnobuf, DIM (serialnobuf));
               tty_printf("\n");
             }
           redisplay = 0;
@@ -2121,6 +2321,10 @@ card_edit (ctrl_t ctrl, strlist_t commands)
           factory_reset ();
           break;
 
+        case cmdKDFSETUP:
+          kdf_setup ();
+          break;
+
         case cmdQUIT:
           goto leave;