scd: Support KDF DO setup.
[gnupg.git] / g10 / card-util.c
index e25427f..d78e9bd 100644 (file)
@@ -1,5 +1,6 @@
 /* card-util.c - Utility functions for the OpenPGP card.
 /* card-util.c - Utility functions for the OpenPGP card.
- *     Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2003-2005, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2003-2005, 2009 Werner Koch
  *
  * This file is part of GnuPG.
  *
  *
  * This file is part of GnuPG.
  *
@@ -14,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * 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>
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
+#ifdef HAVE_LIBREADLINE
+# define GNUPG_LIBREADLINE_H_INCLUDED
+# include <readline/readline.h>
+#endif /*HAVE_LIBREADLINE*/
 
 #if GNUPG_MAJOR_VERSION != 1
 # include "gpg.h"
 #endif /*GNUPG_MAJOR_VERSION != 1*/
 
 #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"
 #include "options.h"
 #include "main.h"
 #include "keyserver-internal.h"
+
 #if GNUPG_MAJOR_VERSION == 1
 #if GNUPG_MAJOR_VERSION == 1
-# ifdef HAVE_LIBREADLINE
-# define GNUPG_LIBREADLINE_H_INCLUDED
-# include <stdio.h>
-# include <readline/readline.h>
-# endif /*HAVE_LIBREADLINE*/
 # include "cardglue.h"
 #else /*GNUPG_MAJOR_VERSION!=1*/
 # include "call-agent.h"
 # include "cardglue.h"
 #else /*GNUPG_MAJOR_VERSION!=1*/
 # include "call-agent.h"
 #define CONTROL_D ('D' - 'A' + 1)
 
 
 #define CONTROL_D ('D' - 'A' + 1)
 
 
-/* Change the PIN of a an OpenPGP card.  This is an interactive
+static void
+write_sc_op_status (gpg_error_t err)
+{
+  switch (gpg_err_code (err))
+    {
+    case 0:
+      write_status (STATUS_SC_OP_SUCCESS);
+      break;
+#if GNUPG_MAJOR_VERSION != 1
+    case GPG_ERR_CANCELED:
+    case GPG_ERR_FULLY_CANCELED:
+      write_status_text (STATUS_SC_OP_FAILURE, "1");
+      break;
+    case GPG_ERR_BAD_PIN:
+      write_status_text (STATUS_SC_OP_FAILURE, "2");
+      break;
+    default:
+      write_status (STATUS_SC_OP_FAILURE);
+      break;
+#endif /* GNUPG_MAJOR_VERSION != 1 */
+    }
+}
+
+
+/* Change the PIN of an OpenPGP card.  This is an interactive
    function. */
 void
 change_pin (int unblock_v2, int allow_admin)
    function. */
 void
 change_pin (int unblock_v2, int allow_admin)
@@ -56,14 +80,14 @@ change_pin (int unblock_v2, int allow_admin)
   struct agent_card_info_s info;
   int rc;
 
   struct agent_card_info_s info;
   int rc;
 
-  rc = agent_learn (&info);
+  rc = agent_scd_learn (&info, 0);
   if (rc)
     {
       log_error (_("OpenPGP card not available: %s\n"),
                   gpg_strerror (rc));
       return;
     }
   if (rc)
     {
       log_error (_("OpenPGP card not available: %s\n"),
                   gpg_strerror (rc));
       return;
     }
-  
+
   log_info (_("OpenPGP card no. %s detected\n"),
               info.serialno? info.serialno : "[none]");
 
   log_info (_("OpenPGP card no. %s detected\n"),
               info.serialno? info.serialno : "[none]");
 
@@ -86,25 +110,21 @@ change_pin (int unblock_v2, int allow_admin)
       else
         {
           rc = agent_scd_change_pin (2, info.serialno);
       else
         {
           rc = agent_scd_change_pin (2, info.serialno);
+          write_sc_op_status (rc);
           if (rc)
             tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
           else
           if (rc)
             tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
           else
-            {
-              write_status (STATUS_SC_OP_SUCCESS);
-              tty_printf ("PIN changed.\n");
-            }
+            tty_printf ("PIN changed.\n");
         }
     }
   else if (!allow_admin)
     {
       rc = agent_scd_change_pin (1, info.serialno);
         }
     }
   else if (!allow_admin)
     {
       rc = agent_scd_change_pin (1, info.serialno);
+      write_sc_op_status (rc);
       if (rc)
        tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
       else
       if (rc)
        tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
       else
-        {
-          write_status (STATUS_SC_OP_SUCCESS);
-          tty_printf ("PIN changed.\n");
-        }
+        tty_printf ("PIN changed.\n");
     }
   else
     for (;;)
     }
   else
     for (;;)
@@ -124,55 +144,46 @@ change_pin (int unblock_v2, int allow_admin)
        if (strlen (answer) != 1)
          continue;
 
        if (strlen (answer) != 1)
          continue;
 
-       rc = 0;
        if (*answer == '1')
          {
             /* Change PIN.  */
            rc = agent_scd_change_pin (1, info.serialno);
        if (*answer == '1')
          {
             /* Change PIN.  */
            rc = agent_scd_change_pin (1, info.serialno);
+            write_sc_op_status (rc);
            if (rc)
              tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
            else
            if (rc)
              tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
            else
-              {
-                write_status (STATUS_SC_OP_SUCCESS);
-                tty_printf ("PIN changed.\n");
-              }
+              tty_printf ("PIN changed.\n");
          }
        else if (*answer == '2')
          {
             /* Unblock PIN.  */
            rc = agent_scd_change_pin (101, info.serialno);
          }
        else if (*answer == '2')
          {
             /* Unblock PIN.  */
            rc = agent_scd_change_pin (101, info.serialno);
+            write_sc_op_status (rc);
            if (rc)
              tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
            else
            if (rc)
              tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
            else
-              {
-                write_status (STATUS_SC_OP_SUCCESS);
-                tty_printf ("PIN unblocked and new PIN set.\n");
-              }
+              tty_printf ("PIN unblocked and new PIN set.\n");
           }
        else if (*answer == '3')
          {
             /* Change Admin PIN.  */
            rc = agent_scd_change_pin (3, info.serialno);
           }
        else if (*answer == '3')
          {
             /* Change Admin PIN.  */
            rc = agent_scd_change_pin (3, info.serialno);
+            write_sc_op_status (rc);
            if (rc)
              tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
            else
            if (rc)
              tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
            else
-              {
-                write_status (STATUS_SC_OP_SUCCESS);
-                tty_printf ("PIN changed.\n");
-              }
+              tty_printf ("PIN changed.\n");
          }
        else if (*answer == '4')
          {
             /* Set a new Reset Code.  */
            rc = agent_scd_change_pin (102, info.serialno);
          }
        else if (*answer == '4')
          {
             /* Set a new Reset Code.  */
            rc = agent_scd_change_pin (102, info.serialno);
+            write_sc_op_status (rc);
            if (rc)
            if (rc)
-             tty_printf ("Error setting the Reset Code: %s\n", 
+             tty_printf ("Error setting the Reset Code: %s\n",
                           gpg_strerror (rc));
            else
                           gpg_strerror (rc));
            else
-              {
-                write_status (STATUS_SC_OP_SUCCESS);
-                tty_printf ("Reset Code set.\n");
-              }
+              tty_printf ("Reset Code set.\n");
          }
        else if (*answer == 'q' || *answer == 'Q')
          {
          }
        else if (*answer == 'q' || *answer == 'Q')
          {
@@ -192,10 +203,24 @@ get_manufacturer (unsigned int no)
     case 0x0001: return "PPC Card Systems";
     case 0x0002: return "Prism";
     case 0x0003: return "OpenFortress";
     case 0x0001: return "PPC Card Systems";
     case 0x0002: return "Prism";
     case 0x0003: return "OpenFortress";
-    case 0x0004: return "Wewid AB";
-
-      /* 0x00000 and 0xFFFF are defined as test cards per spec,
-         0xFFF00 to 0xFFFE are assigned for use with randomly created
+    case 0x0004: return "Wewid";
+    case 0x0005: return "ZeitControl";
+    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,
+         0xFF00 to 0xFFFE are assigned for use with randomly created
          serial numbers.  */
     case 0x0000:
     case 0xffff: return "test card";
          serial numbers.  */
     case 0x0000:
     case 0xffff: return "test card";
@@ -205,7 +230,7 @@ get_manufacturer (unsigned int no)
 
 
 static void
 
 
 static void
-print_sha1_fpr (FILE *fp, const unsigned char *fpr)
+print_sha1_fpr (estream_t fp, const unsigned char *fpr)
 {
   int i;
 
 {
   int i;
 
@@ -225,21 +250,36 @@ print_sha1_fpr (FILE *fp, const unsigned char *fpr)
 
 
 static void
 
 
 static void
-print_sha1_fpr_colon (FILE *fp, const unsigned char *fpr)
+print_sha1_fpr_colon (estream_t fp, const unsigned char *fpr)
 {
   int i;
 
   if (fpr)
     {
       for (i=0; i < 20 ; i++, fpr++)
 {
   int i;
 
   if (fpr)
     {
       for (i=0; i < 20 ; i++, fpr++)
-        fprintf (fp, "%02X", *fpr);
+        es_fprintf (fp, "%02X", *fpr);
+    }
+  es_putc (':', fp);
+}
+
+
+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");
     }
     }
-  putc (':', fp);
 }
 
 
 static void
 }
 
 
 static void
-print_name (FILE *fp, const char *text, const char *name)
+print_name (estream_t fp, const char *text, const char *name)
 {
   tty_fprintf (fp, "%s", text);
 
 {
   tty_fprintf (fp, "%s", text);
 
@@ -248,9 +288,9 @@ print_name (FILE *fp, const char *text, const char *name)
   if (name && *name)
     {
       if (fp)
   if (name && *name)
     {
       if (fp)
-        print_utf8_string2 (fp, name, strlen (name), '\n');
+        print_utf8_buffer2 (fp, name, strlen (name), '\n');
       else
       else
-        tty_print_utf8_string2 (name, strlen (name), 0);
+        tty_print_utf8_string2 (NULL, name, strlen (name), 0);
     }
   else
     tty_fprintf (fp, _("[not set]"));
     }
   else
     tty_fprintf (fp, _("[not set]"));
@@ -258,10 +298,11 @@ print_name (FILE *fp, const char *text, const char *name)
 }
 
 static void
 }
 
 static void
-print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
+print_isoname (estream_t fp, const char *text,
+               const char *tag, const char *name)
 {
   if (opt.with_colons)
 {
   if (opt.with_colons)
-    fprintf (fp, "%s:", tag);
+    es_fprintf (fp, "%s:", tag);
   else
     tty_fprintf (fp, "%s", text);
 
   else
     tty_fprintf (fp, "%s", text);
 
@@ -278,36 +319,36 @@ print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
           *given = 0;
           given += 2;
           if (opt.with_colons)
           *given = 0;
           given += 2;
           if (opt.with_colons)
-            print_string (fp, given, strlen (given), ':');
+            es_write_sanitized (fp, given, strlen (given), ":", NULL);
           else if (fp)
           else if (fp)
-            print_utf8_string2 (fp, given, strlen (given), '\n');
+            print_utf8_buffer2 (fp, given, strlen (given), '\n');
           else
           else
-            tty_print_utf8_string2 (given, strlen (given), 0);
+            tty_print_utf8_string2 (NULL, given, strlen (given), 0);
 
           if (opt.with_colons)
 
           if (opt.with_colons)
-            putc (':', fp);
+            es_putc (':', fp);
           else if (*buf)
             tty_fprintf (fp, " ");
         }
 
       if (opt.with_colons)
           else if (*buf)
             tty_fprintf (fp, " ");
         }
 
       if (opt.with_colons)
-        print_string (fp, buf, strlen (buf), ':');
+        es_write_sanitized (fp, buf, strlen (buf), ":", NULL);
       else if (fp)
       else if (fp)
-        print_utf8_string2 (fp, buf, strlen (buf), '\n');
+        print_utf8_buffer2 (fp, buf, strlen (buf), '\n');
       else
       else
-        tty_print_utf8_string2 (buf, strlen (buf), 0);
+        tty_print_utf8_string2 (NULL, buf, strlen (buf), 0);
       xfree (buf);
     }
   else
     {
       if (opt.with_colons)
       xfree (buf);
     }
   else
     {
       if (opt.with_colons)
-        putc (':', fp);
+        es_putc (':', fp);
       else
         tty_fprintf (fp, _("[not set]"));
     }
 
   if (opt.with_colons)
       else
         tty_fprintf (fp, _("[not set]"));
     }
 
   if (opt.with_colons)
-    fputs (":\n", fp);
+    es_fputs (":\n", fp);
   else
     tty_fprintf (fp, "\n");
 }
   else
     tty_fprintf (fp, "\n");
 }
@@ -337,11 +378,13 @@ fpr_is_ff (const char *fpr)
 
 
 /* Print all available information about the current card. */
 
 
 /* Print all available information about the current card. */
-void
-card_status (FILE *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);
 {
   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;
   int rc;
   unsigned int uval;
   const unsigned char *thefpr;
@@ -350,27 +393,58 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
   if (serialno && serialnobuflen)
     *serialno = 0;
 
   if (serialno && serialnobuflen)
     *serialno = 0;
 
-  rc = agent_learn (&info);
+  rc = agent_scd_learn (&info, 0);
   if (rc)
     {
       if (opt.with_colons)
   if (rc)
     {
       if (opt.with_colons)
-        fputs ("AID:::\n", fp);
-      log_error (_("OpenPGP card not available: %s\n"),
-                  gpg_strerror (rc));
+        es_fputs ("AID:::\n", fp);
+      log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (rc));
       xfree (pk);
       return;
     }
 
   if (opt.with_colons)
       xfree (pk);
       return;
     }
 
   if (opt.with_colons)
-    fprintf (fp, "AID:%s:", info.serialno? info.serialno : "");
+    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",
                  info.serialno? info.serialno : "[none]");
   else
     tty_fprintf (fp, "Application ID ...: %s\n",
                  info.serialno? info.serialno : "[none]");
-  if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) 
+  if (!info.serialno || strncmp (info.serialno, "D27600012401", 12)
       || strlen (info.serialno) != 32 )
     {
       || strlen (info.serialno) != 32 )
     {
-      if (opt.with_colons)
-        fputs ("unknown:\n", fp);
+      if (info.apptype && !strcmp (info.apptype, "NKS"))
+        {
+          if (opt.with_colons)
+            es_fputs ("netkey-card:\n", fp);
+          log_info ("this is a NetKey card\n");
+        }
+      else if (info.apptype && !strcmp (info.apptype, "DINSIG"))
+        {
+          if (opt.with_colons)
+            es_fputs ("dinsig-card:\n", fp);
+          log_info ("this is a DINSIG compliant card\n");
+        }
+      else if (info.apptype && !strcmp (info.apptype, "P15"))
+        {
+          if (opt.with_colons)
+            es_fputs ("pkcs15-card:\n", fp);
+          log_info ("this is a PKCS#15 compliant card\n");
+        }
+      else if (info.apptype && !strcmp (info.apptype, "GELDKARTE"))
+        {
+          if (opt.with_colons)
+            es_fputs ("geldkarte-card:\n", fp);
+          log_info ("this is a Geldkarte compliant card\n");
+        }
+      else
+        {
+          if (opt.with_colons)
+            es_fputs ("unknown:\n", fp);
+        }
       log_info ("not an OpenPGP card\n");
       agent_release_card_info (&info);
       xfree (pk);
       log_info ("not an OpenPGP card\n");
       agent_release_card_info (&info);
       xfree (pk);
@@ -379,86 +453,103 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
 
   if (!serialno)
     ;
 
   if (!serialno)
     ;
-  else if (strlen (serialno)+1 > serialnobuflen)
+  else if (strlen (info.serialno)+1 > serialnobuflen)
     log_error ("serial number longer than expected\n");
     log_error ("serial number longer than expected\n");
-  else 
+  else
     strcpy (serialno, info.serialno);
 
   if (opt.with_colons)
     strcpy (serialno, info.serialno);
 
   if (opt.with_colons)
-    fputs ("openpgp-card:\n", fp);
+    es_fputs ("openpgp-card:\n", fp);
 
 
   if (opt.with_colons)
     {
 
 
   if (opt.with_colons)
     {
-      fprintf (fp, "version:%.4s:\n", info.serialno+12);
+      es_fprintf (fp, "version:%.4s:\n", info.serialno+12);
       uval = xtoi_2(info.serialno+16)*256 + xtoi_2 (info.serialno+18);
       uval = xtoi_2(info.serialno+16)*256 + xtoi_2 (info.serialno+18);
-      fprintf (fp, "vendor:%04x:%s:\n", uval, get_manufacturer (uval));
-      fprintf (fp, "serial:%.8s:\n", info.serialno+20);
-      
+      es_fprintf (fp, "vendor:%04x:%s:\n", uval, get_manufacturer (uval));
+      es_fprintf (fp, "serial:%.8s:\n", info.serialno+20);
+
       print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
 
       print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
 
-      fputs ("lang:", fp);
+      es_fputs ("lang:", fp);
       if (info.disp_lang)
       if (info.disp_lang)
-        print_string (fp, info.disp_lang, strlen (info.disp_lang), ':');
-      fputs (":\n", fp);
+        es_write_sanitized (fp, info.disp_lang, strlen (info.disp_lang),
+                            ":", NULL);
+      es_fputs (":\n", fp);
 
 
-      fprintf (fp, "sex:%c:\n", (info.disp_sex == 1? 'm':
+      es_fprintf (fp, "sex:%c:\n", (info.disp_sex == 1? 'm':
                                  info.disp_sex == 2? 'f' : 'u'));
 
                                  info.disp_sex == 2? 'f' : 'u'));
 
-      fputs ("url:", fp);
+      es_fputs ("url:", fp);
       if (info.pubkey_url)
       if (info.pubkey_url)
-        print_string (fp, info.pubkey_url, strlen (info.pubkey_url), ':');
-      fputs (":\n", fp);
+        es_write_sanitized (fp, info.pubkey_url, strlen (info.pubkey_url),
+                            ":", NULL);
+      es_fputs (":\n", fp);
 
 
-      fputs ("login:", fp);
+      es_fputs ("login:", fp);
       if (info.login_data)
       if (info.login_data)
-        print_string (fp, info.login_data, strlen (info.login_data), ':');
-      fputs (":\n", fp);
-
-      fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
-      fprintf (fp, "maxpinlen:%d:%d:%d:\n",
-                   info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
-      fprintf (fp, "pinretry:%d:%d:%d:\n",
-                   info.chvretry[0], info.chvretry[1], info.chvretry[2]);
-      fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
+        es_write_sanitized (fp, info.login_data, strlen (info.login_data),
+                            ":", NULL);
+      es_fputs (":\n", fp);
+
+      es_fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
+      for (i=0; i < DIM (info.key_attr); i++)
+        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",
+                  info.chvretry[0], info.chvretry[1], info.chvretry[2]);
+      es_fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
 
       for (i=0; i < 4; i++)
         {
           if (info.private_do[i])
             {
 
       for (i=0; i < 4; i++)
         {
           if (info.private_do[i])
             {
-              fprintf (fp, "private_do:%d:", i+1);
-              print_string (fp, info.private_do[i],
-                            strlen (info.private_do[i]), ':');
-              fputs (":\n", fp);
+              es_fprintf (fp, "private_do:%d:", i+1);
+              es_write_sanitized (fp, info.private_do[i],
+                                  strlen (info.private_do[i]), ":", NULL);
+              es_fputs (":\n", fp);
             }
         }
 
             }
         }
 
-      fputs ("cafpr:", 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_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);
-      putc ('\n', fp);
-      fputs ("fpr:", fp);
+      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_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);
-      putc ('\n', fp);
-      fprintf (fp, "fprtime:%lu:%lu:%lu:\n",
+      es_putc ('\n', fp);
+      es_fprintf (fp, "fprtime:%lu:%lu:%lu:\n",
                (unsigned long)info.fpr1time, (unsigned long)info.fpr2time,
                (unsigned long)info.fpr3time);
                (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 
+  else
     {
       tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n",
                    info.serialno[12] == '0'?"":info.serialno+12,
                    info.serialno[13],
                    info.serialno[14] == '0'?"":info.serialno+14,
                    info.serialno[15]);
     {
       tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n",
                    info.serialno[12] == '0'?"":info.serialno+12,
                    info.serialno[13],
                    info.serialno[14] == '0'?"":info.serialno+14,
                    info.serialno[15]);
-      tty_fprintf (fp, "Manufacturer .....: %s\n", 
+      tty_fprintf (fp, "Manufacturer .....: %s\n",
                    get_manufacturer (xtoi_2(info.serialno+16)*256
                                      + xtoi_2 (info.serialno+18)));
       tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20);
                    get_manufacturer (xtoi_2(info.serialno+16)*256
                                      + xtoi_2 (info.serialno+18)));
       tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20);
-      
+
       print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
       print_name (fp, "Language prefs ...: ", info.disp_lang);
       tty_fprintf (fp,    "Sex ..............: %s\n",
       print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
       print_name (fp, "Language prefs ...: ", info.disp_lang);
       tty_fprintf (fp,    "Sex ..............: %s\n",
@@ -491,6 +582,29 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
         }
       tty_fprintf (fp,    "Signature PIN ....: %s\n",
                    info.chv1_cached? _("not forced"): _("forced"));
         }
       tty_fprintf (fp,    "Signature PIN ....: %s\n",
                    info.chv1_cached? _("not forced"): _("forced"));
+      if (info.key_attr[0].algo)
+        {
+          tty_fprintf (fp,    "Key attributes ...:");
+          for (i=0; i < DIM (info.key_attr); i++)
+            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",
                    info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
       tty_fprintf (fp,    "PIN retry counter : %d %d %d\n",
       tty_fprintf (fp,    "Max. PIN lengths .: %d %d %d\n",
                    info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
       tty_fprintf (fp,    "PIN retry counter : %d %d %d\n",
@@ -499,59 +613,114 @@ card_status (FILE *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, "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, "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, "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, "General key info..: "); 
+        {
+          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 : 
+      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
          OpenPGP certificate.  */
                 info.fpr3valid? info.fpr3 : NULL);
       /* 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))
+      if ( thefpr && !fpr_is_ff (thefpr)
+           && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, 20))
         {
         {
-          KBNODE keyblock = NULL;
-
-          print_pubkey_info (fp, pk);
-
-          if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
+          print_pubkey_info (ctrl, fp, pk);
+          if (keyblock)
             print_card_key_info (fp, 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);
-                }
-            }
-
-          release_kbnode (keyblock);
         }
       else
         tty_fprintf (fp, "[none]\n");
     }
         }
       else
         tty_fprintf (fp, "[none]\n");
     }
-      
+
+  release_kbnode (keyblock);
   free_public_key (pk);
   agent_release_card_info (&info);
 }
 
 
   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)
 {
 static char *
 get_one_name (const char *prompt1, const char *prompt2)
 {
@@ -575,7 +744,7 @@ get_one_name (const char *prompt1, const char *prompt2)
       else if (strchr (name, '<'))
         tty_printf (_("Error: The \"<\" character may not be used.\n"));
       else if (strstr (name, "  "))
       else if (strchr (name, '<'))
         tty_printf (_("Error: The \"<\" character may not be used.\n"));
       else if (strstr (name, "  "))
-        tty_printf (_("Error: Double spaces are not allowed.\n"));    
+        tty_printf (_("Error: Double spaces are not allowed.\n"));
       else
         return name;
       xfree (name);
       else
         return name;
       xfree (name);
@@ -613,7 +782,7 @@ change_name (void)
   if (strlen (isoname) > 39 )
     {
       tty_printf (_("Error: Combined name too long "
   if (strlen (isoname) > 39 )
     {
       tty_printf (_("Error: Combined name too long "
-                    "(limit is %d characters).\n"), 39);    
+                    "(limit is %d characters).\n"), 39);
       xfree (isoname);
       return -1;
     }
       xfree (isoname);
       return -1;
     }
@@ -639,18 +808,11 @@ change_url (void)
   trim_spaces (url);
   cpr_kill_prompt ();
 
   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));
   xfree (url);
   rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url), NULL );
   if (rc)
     log_error ("error setting URL: %s\n", gpg_strerror (rc));
   xfree (url);
+  write_sc_op_status (rc);
   return rc;
 }
 
   return rc;
 }
 
@@ -658,9 +820,8 @@ change_url (void)
 /* Fetch the key from the URL given on the card or try to get it from
    the default keyserver.  */
 static int
 /* Fetch the key from the URL given on the card or try to get it from
    the default keyserver.  */
 static int
-fetch_url(void)
+fetch_url (ctrl_t ctrl)
 {
 {
-#if GNUPG_MAJOR_VERSION == 1
   int rc;
   struct agent_card_info_s info;
 
   int rc;
   struct agent_card_info_s info;
 
@@ -671,55 +832,44 @@ fetch_url(void)
     log_error("error retrieving URL from card: %s\n",gpg_strerror(rc));
   else
     {
     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)
       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,NULL,0);
-         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(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)
        {
       else if (info.fpr1valid)
        {
-          rc = keyserver_import_fprint (info.fpr1, 20, opt.keyserver);
+          rc = keyserver_import_fprint (ctrl, info.fpr1, 20, opt.keyserver, 0);
        }
     }
 
   return rc;
        }
     }
 
   return rc;
-#else
-  return 0;
-#endif
 }
 
 
 }
 
 
-/* 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
 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)
 {
 {
-  FILE *fp;
+  estream_t fp;
   char *data;
   int n;
   char *data;
   int n;
-  
+
   *r_buffer = NULL;
 
   *r_buffer = NULL;
 
-  fp = fopen (fname, "rb");
+  fp = es_fopen (fname, "rb");
 #if GNUPG_MAJOR_VERSION == 1
   if (fp && is_secured_file (fileno (fp)))
     {
 #if GNUPG_MAJOR_VERSION == 1
   if (fp && is_secured_file (fileno (fp)))
     {
@@ -730,26 +880,23 @@ get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
 #endif
   if (!fp)
     {
 #endif
   if (!fp)
     {
-      tty_printf (_("can't open `%s': %s\n"), fname, strerror (errno));
+      tty_printf (_("can't open '%s': %s\n"), fname, strerror (errno));
       return -1;
     }
       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));
   if (!data)
     {
       tty_printf (_("error allocating enough memory: %s\n"), strerror (errno));
-      fclose (fp);
+      es_fclose (fp);
       return -1;
     }
 
       return -1;
     }
 
-  if (maxlen)
-    n = fread (data, 1, maxlen, fp);
-  else
-    n = 0;
-  fclose (fp);
+  n = es_fread (data, 1, MAX_GET_DATA_FROM_FILE, fp);
+  es_fclose (fp);
   if (n < 0)
     {
   if (n < 0)
     {
-      tty_printf (_("error reading `%s': %s\n"), fname, strerror (errno));
+      tty_printf (_("error reading '%s': %s\n"), fname, strerror (errno));
       xfree (data);
       return -1;
     }
       xfree (data);
       return -1;
     }
@@ -758,6 +905,39 @@ get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
 }
 
 
 }
 
 
+/* Write LENGTH bytes from BUFFER to file FNAME.  Return 0 on
+   success.  */
+static int
+put_data_to_file (const char *fname, const void *buffer, size_t length)
+{
+  estream_t fp;
+
+  fp = es_fopen (fname, "wb");
+#if GNUPG_MAJOR_VERSION == 1
+  if (fp && is_secured_file (fileno (fp)))
+    {
+      fclose (fp);
+      fp = NULL;
+      errno = EPERM;
+    }
+#endif
+  if (!fp)
+    {
+      tty_printf (_("can't create '%s': %s\n"), fname, strerror (errno));
+      return -1;
+    }
+
+  if (length && es_fwrite (buffer, length, 1, fp) != 1)
+    {
+      tty_printf (_("error writing '%s': %s\n"), fname, strerror (errno));
+      es_fclose (fp);
+      return -1;
+    }
+  es_fclose (fp);
+  return 0;
+}
+
+
 static int
 change_login (const char *args)
 {
 static int
 change_login (const char *args)
 {
@@ -769,7 +949,7 @@ change_login (const char *args)
     {
       for (args++; spacep (args); 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;
     }
       if (n < 0)
         return -1;
     }
@@ -784,18 +964,11 @@ change_login (const char *args)
       n = strlen (data);
     }
 
       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));
   xfree (data);
   rc = agent_scd_setattr ("LOGIN-DATA", data, n, NULL );
   if (rc)
     log_error ("error setting login data: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
   return rc;
 }
 
@@ -805,16 +978,16 @@ change_private_do (const char *args, int nr)
   char do_name[] = "PRIVATE-DO-X";
   char *data;
   int n;
   char do_name[] = "PRIVATE-DO-X";
   char *data;
   int n;
-  int rc; 
+  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++)
         ;
   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;
     }
       if (n < 0)
         return -1;
     }
@@ -829,18 +1002,11 @@ change_private_do (const char *args, int nr)
       n = strlen (data);
     }
 
       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));
   xfree (data);
   rc = agent_scd_setattr (do_name, data, n, NULL );
   if (rc)
     log_error ("error setting private DO: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
   return rc;
 }
 
@@ -856,13 +1022,13 @@ change_cert (const char *args)
     {
       for (args++; spacep (args); 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;
     }
   else
     {
       if (n < 0)
         return -1;
     }
   else
     {
-      tty_printf ("usage error: redirectrion to file required\n");
+      tty_printf ("usage error: redirection to file required\n");
       return -1;
     }
 
       return -1;
     }
 
@@ -870,6 +1036,38 @@ change_cert (const char *args)
   if (rc)
     log_error ("error writing certificate to card: %s\n", gpg_strerror (rc));
   xfree (data);
   if (rc)
     log_error ("error writing certificate to card: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
+  return rc;
+}
+
+
+static int
+read_cert (const char *args)
+{
+  const char *fname;
+  void *buffer;
+  size_t length;
+  int rc;
+
+  if (args && *args == '>')  /* Write it to a file */
+    {
+      for (args++; spacep (args); args++)
+        ;
+      fname = args;
+    }
+  else
+    {
+      tty_printf ("usage error: redirection to file required\n");
+      return -1;
+    }
+
+  rc = agent_scd_readcert ("OPENPGP.3", &buffer, &length);
+  if (rc)
+    log_error ("error reading certificate from card: %s\n", gpg_strerror (rc));
+  else
+    rc = put_data_to_file (fname, buffer, length);
+  xfree (buffer);
+  write_sc_op_status (rc);
   return rc;
 }
 
   return rc;
 }
 
@@ -907,6 +1105,7 @@ change_lang (void)
   if (rc)
     log_error ("error setting lang: %s\n", gpg_strerror (rc));
   xfree (data);
   if (rc)
     log_error ("error setting lang: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
   return rc;
 }
 
@@ -931,17 +1130,18 @@ change_sex (void)
     str = "1";
   else if ((*data == 'F' || *data == 'f') && !data[1])
     str = "2";
     str = "1";
   else if ((*data == 'F' || *data == 'f') && !data[1])
     str = "2";
-  else 
+  else
     {
       tty_printf (_("Error: invalid response.\n"));
       xfree (data);
       return -1;
     }
     {
       tty_printf (_("Error: invalid response.\n"));
       xfree (data);
       return -1;
     }
-     
+
   rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL );
   if (rc)
     log_error ("error setting sex: %s\n", gpg_strerror (rc));
   xfree (data);
   rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL );
   if (rc)
     log_error ("error setting sex: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
   return rc;
 }
 
@@ -986,6 +1186,7 @@ change_cafpr (int fprno)
                           fprno==3?"CA-FPR-3":"x", fpr, 20, NULL );
   if (rc)
     log_error ("error setting cafpr: %s\n", gpg_strerror (rc));
                           fprno==3?"CA-FPR-3":"x", fpr, 20, NULL );
   if (rc)
     log_error ("error setting cafpr: %s\n", gpg_strerror (rc));
+  write_sc_op_status (rc);
   return rc;
 }
 
   return rc;
 }
 
@@ -1011,6 +1212,7 @@ toggle_forcesig (void)
   rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL);
   if (rc)
     log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc));
   rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL);
   if (rc)
     log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc));
+  write_sc_op_status (rc);
 }
 
 
 }
 
 
@@ -1022,7 +1224,7 @@ get_info_for_key_operation (struct agent_card_info_s *info)
 
   memset (info, 0, sizeof *info);
   rc = agent_scd_getattr ("SERIALNO", info);
 
   memset (info, 0, sizeof *info);
   rc = agent_scd_getattr ("SERIALNO", info);
-  if (rc || !info->serialno || strncmp (info->serialno, "D27600012401", 12) 
+  if (rc || !info->serialno || strncmp (info->serialno, "D27600012401", 12)
       || strlen (info->serialno) != 32 )
     {
       log_error (_("key operation not possible: %s\n"),
       || strlen (info->serialno) != 32 )
     {
       log_error (_("key operation not possible: %s\n"),
@@ -1034,6 +1236,10 @@ get_info_for_key_operation (struct agent_card_info_s *info)
     rc = agent_scd_getattr ("CHV-STATUS", info);
   if (!rc)
     rc = agent_scd_getattr ("DISP-NAME", info);
     rc = agent_scd_getattr ("CHV-STATUS", info);
   if (!rc)
     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;
   if (rc)
     log_error (_("error getting current key info: %s\n"), gpg_strerror (rc));
   return rc;
@@ -1043,14 +1249,14 @@ get_info_for_key_operation (struct agent_card_info_s *info)
 /* Helper for the key generation/edit functions.  */
 static int
 check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
 /* Helper for the key generation/edit functions.  */
 static int
 check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
-{     
+{
   int rc = 0;
 
   agent_clear_pin_cache (info->serialno);
 
   *forced_chv1 = !info->chv1_cached;
   if (*forced_chv1)
   int rc = 0;
 
   agent_clear_pin_cache (info->serialno);
 
   *forced_chv1 = !info->chv1_cached;
   if (*forced_chv1)
-    { /* Switch of the forced mode so that during key generation we
+    { /* Switch off the forced mode so that during key generation we
          don't get bothered with PIN queries for each
          self-signature. */
       rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno);
          don't get bothered with PIN queries for each
          self-signature. */
       rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno);
@@ -1068,13 +1274,16 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
          binding signature. */
       rc = agent_scd_checkpin (info->serialno);
       if (rc)
          binding signature. */
       rc = agent_scd_checkpin (info->serialno);
       if (rc)
-        log_error ("error checking the PIN: %s\n", gpg_strerror (rc));
-    }
+        {
+          log_error ("error checking the PIN: %s\n", gpg_strerror (rc));
+          write_sc_op_status (rc);
+        }
+  }
   return rc;
 }
 
 /* Helper for the key generation/edit functions.  */
   return rc;
 }
 
 /* Helper for the key generation/edit functions.  */
-static void 
+static void
 restore_forced_chv1 (int *forced_chv1)
 {
   int rc;
 restore_forced_chv1 (int *forced_chv1)
 {
   int rc;
@@ -1109,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)
 {
 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)
 
   if ((keyno == 1 && info->fpr1valid)
       || (keyno == 2 && info->fpr2valid)
@@ -1121,56 +1330,169 @@ replace_existing_key_p (struct agent_card_info_s *info, int keyno)
       if ( !cpr_get_answer_is_yes( "cardedit.genkeys.replace_key",
                                   _("Replace existing key? (y/N) ")))
         return -1;
       if ( !cpr_get_answer_is_yes( "cardedit.genkeys.replace_key",
                                   _("Replace existing key? (y/N) ")))
         return -1;
+      return 1;
     }
   return 0;
 }
 
 
 static void
     }
   return 0;
 }
 
 
 static void
-generate_card_keys (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_keyattr (int keyno, unsigned int nbits)
+{
+  unsigned int min_nbits = 1024;
+  unsigned int max_nbits = 4096;
+  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 == 25519)
+        {
+          if (req_nbits == nbits)
+            return 0;  /* Use default.  */
+
+          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
+        {
+          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.  Using the magic value 25519 for NBITS
+ * switches to ed25519 or cv25519 depending on the KEYNO.  */
+static gpg_error_t
+do_change_keyattr (int keyno, unsigned int nbits)
+{
+  gpg_error_t err;
+  char args[100];
+
+  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"),
+               keyno+1, nbits, gpg_strerror (err));
+  return err;
+}
+
+
+static void
+generate_card_keys (ctrl_t ctrl)
 {
   struct agent_card_info_s info;
   int forced_chv1;
   int want_backup;
 {
   struct agent_card_info_s info;
   int forced_chv1;
   int want_backup;
+  int keyno;
 
   if (get_info_for_key_operation (&info))
     return;
 
 
   if (get_info_for_key_operation (&info))
     return;
 
-#if GNUPG_MAJOR_VERSION == 1
-  {
-    char *answer=cpr_get("cardedit.genkeys.backup_enc",
-                        _("Make off-card backup of encryption key? (Y/n) "));
+  if (info.extcap.ki)
+    {
+      char *answer;
 
 
-    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
+      /* FIXME: Should be something like cpr_get_bool so that a status
+         GET_BOOL will be emitted.  */
+      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/*(default to Yes)*/);
+      cpr_kill_prompt ();
+      xfree (answer);
+    }
+  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)))
     {
       tty_printf ("\n");
 
   if ( (info.fpr1valid && !fpr_is_zero (info.fpr1))
        || (info.fpr2valid && !fpr_is_zero (info.fpr2))
        || (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");
       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;
         }
     }
         {
           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"
     {
       tty_printf ("\n");
       tty_printf (_("Please note that the factory settings of the PINs are\n"
-                    "   PIN = `%s'     Admin PIN = `%s'\n"
+                    "   PIN = '%s'     Admin PIN = '%s'\n"
                     "You should change them using the command --change-pin\n"),
                   "123456", "12345678");
       tty_printf ("\n");
                     "You should change them using the command --change-pin\n"),
                   "123456", "12345678");
       tty_printf ("\n");
@@ -1178,9 +1500,40 @@ generate_card_keys (void)
 
   if (check_pin_for_key_operation (&info, &forced_chv1))
     goto leave;
 
   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++)
+        {
+          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)
+            {
+              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
+         the serialnumber and thus it won't harm.  */
+    }
+
+  generate_keypair (ctrl, 1, NULL, info.serialno, want_backup);
 
  leave:
   agent_release_card_info (&info);
 
  leave:
   agent_release_card_info (&info);
@@ -1190,16 +1543,17 @@ generate_card_keys (void)
 
 /* This function is used by the key edit menu to generate an arbitrary
    subkey. */
 
 /* This function is used by the key edit menu to generate an arbitrary
    subkey. */
-int
-card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
+gpg_error_t
+card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock)
 {
 {
+  gpg_error_t err;
   struct agent_card_info_s info;
   struct agent_card_info_s info;
-  int okay = 0;
   int forced_chv1 = 0;
   int keyno;
 
   int forced_chv1 = 0;
   int keyno;
 
-  if (get_info_for_key_operation (&info))
-    return 0;
+  err = get_info_for_key_operation (&info);
+  if (err)
+    return err;
 
   show_card_key_info (&info);
 
 
   show_card_key_info (&info);
 
@@ -1209,7 +1563,7 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
   tty_printf (_("   (2) Encryption key\n"));
   tty_printf (_("   (3) Authentication key\n"));
 
   tty_printf (_("   (2) Encryption key\n"));
   tty_printf (_("   (3) Authentication key\n"));
 
-  for (;;) 
+  for (;;)
     {
       char *answer = cpr_get ("cardedit.genkeys.subkeytype",
                               _("Your selection? "));
     {
       char *answer = cpr_get ("cardedit.genkeys.subkeytype",
                               _("Your selection? "));
@@ -1217,6 +1571,7 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
       if (*answer == CONTROL_D)
         {
           xfree (answer);
       if (*answer == CONTROL_D)
         {
           xfree (answer);
+          err = gpg_error (GPG_ERR_CANCELED);
           goto leave;
         }
       keyno = *answer? atoi(answer): 0;
           goto leave;
         }
       keyno = *answer? atoi(answer): 0;
@@ -1226,19 +1581,52 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
       tty_printf(_("Invalid selection.\n"));
     }
 
       tty_printf(_("Invalid selection.\n"));
     }
 
-  if (replace_existing_key_p (&info, keyno))
-    goto leave;
+  if (replace_existing_key_p (&info, keyno) < 0)
+    {
+      err = gpg_error (GPG_ERR_CANCELED);
+      goto leave;
+    }
 
 
-  if (check_pin_for_key_operation (&info, &forced_chv1))
+  err = check_pin_for_key_operation (&info, &forced_chv1);
+  if (err)
     goto leave;
 
     goto leave;
 
-  okay = generate_card_subkeypair (pub_keyblock, sec_keyblock,
-                                   keyno, info.serialno);
+  /* 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
+          || info.key_attr[keyno].algo == PUBKEY_ALGO_ECDH
+          || info.key_attr[keyno].algo == PUBKEY_ALGO_EDDSA)
+        {
+          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 (ctrl, pub_keyblock, keyno, info.serialno);
 
  leave:
   agent_release_card_info (&info);
   restore_forced_chv1 (&forced_chv1);
 
  leave:
   agent_release_card_info (&info);
   restore_forced_chv1 (&forced_chv1);
-  return okay;
+  return err;
 }
 
 
 }
 
 
@@ -1246,36 +1634,45 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
    carry the serialno stuff instead of the actual secret key
    parameters.  USE is the usage for that key; 0 means any
    usage. */
    carry the serialno stuff instead of the actual secret key
    parameters.  USE is the usage for that key; 0 means any
    usage. */
-int 
+int
 card_store_subkey (KBNODE node, int use)
 {
   struct agent_card_info_s info;
   int okay = 0;
 card_store_subkey (KBNODE node, int use)
 {
   struct agent_card_info_s info;
   int okay = 0;
-  int rc;
-  int keyno, i;
-  PKT_secret_key *copied_sk = NULL;
-  PKT_secret_key *sk;
-  size_t n;
-  const char *s;
+  unsigned int nbits;
   int allow_keyno[3];
   int allow_keyno[3];
+  int  keyno;
+  PKT_public_key *pk;
+  gpg_error_t err;
+  char *hexgrip;
+  int rc;
+  gnupg_isotime_t timebuf;
+
+  log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY
+              || node->pkt->pkttype == PKT_PUBLIC_SUBKEY);
 
 
-  assert (node->pkt->pkttype == PKT_SECRET_KEY
-          || node->pkt->pkttype == PKT_SECRET_SUBKEY);
-  sk = node->pkt->pkt.secret_key;
+  pk = node->pkt->pkt.public_key;
 
   if (get_info_for_key_operation (&info))
     return 0;
 
 
   if (get_info_for_key_operation (&info))
     return 0;
 
-  show_card_key_info (&info);
+  if (!info.extcap.ki)
+    {
+      tty_printf ("The card does not support the import of keys\n");
+      tty_printf ("\n");
+      goto leave;
+    }
+
+  nbits = nbits_from_pk (pk);
 
 
-  if (!is_RSA (sk->pubkey_algo) || nbits_from_sk (sk) != 1024 )
+  if (!info.is_v2 && nbits != 1024)
     {
       tty_printf ("You may only store a 1024 bit RSA key on the card\n");
       tty_printf ("\n");
       goto leave;
     }
 
     {
       tty_printf ("You may only store a 1024 bit RSA key on the card\n");
       tty_printf ("\n");
       goto leave;
     }
 
-  allow_keyno[0] = (!use || (use & (PUBKEY_USAGE_SIG)));
+  allow_keyno[0] = (!use || (use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT)));
   allow_keyno[1] = (!use || (use & (PUBKEY_USAGE_ENC)));
   allow_keyno[2] = (!use || (use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH)));
 
   allow_keyno[1] = (!use || (use & (PUBKEY_USAGE_ENC)));
   allow_keyno[2] = (!use || (use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH)));
 
@@ -1288,7 +1685,7 @@ card_store_subkey (KBNODE node, int use)
   if (allow_keyno[2])
     tty_printf (_("   (3) Authentication key\n"));
 
   if (allow_keyno[2])
     tty_printf (_("   (3) Authentication key\n"));
 
-  for (;;) 
+  for (;;)
     {
       char *answer = cpr_get ("cardedit.genkeys.storekeytype",
                               _("Your selection? "));
     {
       char *answer = cpr_get ("cardedit.genkeys.storekeytype",
                               _("Your selection? "));
@@ -1301,77 +1698,303 @@ card_store_subkey (KBNODE node, int use)
       keyno = *answer? atoi(answer): 0;
       xfree(answer);
       if (keyno >= 1 && keyno <= 3 && allow_keyno[keyno-1])
       keyno = *answer? atoi(answer): 0;
       xfree(answer);
       if (keyno >= 1 && keyno <= 3 && allow_keyno[keyno-1])
-        break; /* Okay. */
-      tty_printf(_("Invalid selection.\n"));
+        {
+          if (info.is_v2 && !info.extcap.aac
+              && info.key_attr[keyno-1].nbits != nbits)
+            {
+              tty_printf ("Key does not match the card's capability.\n");
+            }
+          else
+            break; /* Okay. */
+        }
+      else
+        tty_printf(_("Invalid selection.\n"));
     }
 
     }
 
-  if (replace_existing_key_p (&info, keyno))
+  if ((rc = replace_existing_key_p (&info, keyno)) < 0)
     goto leave;
 
     goto leave;
 
-  /* Unprotect key.  */
-  switch (is_secret_key_protected (sk) )
+  err = hexkeygrip_from_pk (pk, &hexgrip);
+  if (err)
+    goto leave;
+
+  epoch2isotime (timebuf, (time_t)pk->timestamp);
+  rc = agent_keytocard (hexgrip, keyno, rc, info.serialno, timebuf);
+
+  if (rc)
+    log_error (_("KEYTOCARD failed: %s\n"), gpg_strerror (rc));
+  else
+    okay = 1;
+  xfree (hexgrip);
+
+ leave:
+  agent_release_card_info (&info);
+  return okay;
+}
+
+
+
+/* Direct sending of an hex encoded APDU with error printing.  */
+static gpg_error_t
+send_apdu (const char *hexapdu, const char *desc, unsigned int ignore)
+{
+  gpg_error_t err;
+  unsigned int sw;
+
+  err = agent_scd_apdu (hexapdu, &sw);
+  if (err)
+    tty_printf ("sending card command %s failed: %s\n", desc,
+                gpg_strerror (err));
+  else if (!hexapdu || !strcmp (hexapdu, "undefined"))
+    ;
+  else if (ignore == 0xffff)
+    ; /* Ignore all status words.  */
+  else if (sw != 0x9000)
     {
     {
-    case 0: /* Not protected. */
-      break;
-    case -1:
-      log_error (_("unknown key protection algorithm\n"));
-      goto leave;
-    default:
-      if (sk->protect.s2k.mode == 1001)
+      switch (sw)
         {
         {
-          log_error (_("secret parts of key are not available\n"));
-          goto leave;
-       }
-      if (sk->protect.s2k.mode == 1002)
+        case 0x6285: err = gpg_error (GPG_ERR_OBJ_TERM_STATE); break;
+        case 0x6982: err = gpg_error (GPG_ERR_BAD_PIN); break;
+        case 0x6985: err = gpg_error (GPG_ERR_USE_CONDITIONS); break;
+        default: err = gpg_error (GPG_ERR_CARD);
+        }
+      if (!(ignore && ignore == sw))
+        tty_printf ("card command %s failed: %s (0x%04x)\n", desc,
+                    gpg_strerror (err),  sw);
+    }
+  return err;
+}
+
+
+/* Do a factory reset after confirmation.  */
+static void
+factory_reset (void)
+{
+  struct agent_card_info_s info;
+  gpg_error_t err;
+  char *answer = NULL;
+  int termstate = 0;
+  int i;
+
+  /*  The code below basically does the same what this
+      gpg-connect-agent script does:
+
+        scd reset
+        scd serialno undefined
+        scd apdu 00 A4 04 00 06 D2 76 00 01 24 01
+        scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+        scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+        scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+        scd apdu 00 20 00 81 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 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 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.
+   */
+
+  err = agent_scd_learn (&info, 0);
+  if (gpg_err_code (err) == GPG_ERR_OBJ_TERM_STATE
+      && gpg_err_source (err) == GPG_ERR_SOURCE_SCD)
+    termstate = 1;
+  else if (err)
+    {
+      log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+
+  if (!termstate)
+    {
+      log_info (_("OpenPGP card no. %s detected\n"),
+                info.serialno? info.serialno : "[none]");
+      if (!(info.status_indicator == 3 || info.status_indicator == 5))
         {
         {
-          log_error (_("secret key already stored on a card\n"));
+          /* Note: We won't see status-indicator 3 here because it is not
+             possible to select a card application in termination state.  */
+          log_error (_("This command is not supported by this card\n"));
           goto leave;
           goto leave;
-       }
-      /* We better copy the key before we unprotect it.  */
-      copied_sk = sk = copy_secret_key (NULL, sk);
-      rc = check_secret_key (sk, 0);
-      if (rc)
+        }
+
+      tty_printf ("\n");
+      log_info (_("Note: This command destroys all keys stored on the card!\n"));
+      tty_printf ("\n");
+      if (!cpr_get_answer_is_yes ("cardedit.factory-reset.proceed",
+                                  _("Continue? (y/N) ")))
+        goto leave;
+
+
+      answer = cpr_get ("cardedit.factory-reset.really",
+                        _("Really do a factory reset? (enter \"yes\") "));
+      cpr_kill_prompt ();
+      trim_spaces (answer);
+      if (strcmp (answer, "yes"))
+        goto leave;
+
+      /* We need to select a card application before we can send APDUs
+         to the card without scdaemon doing anything on its own.  */
+      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.  */
+      err = send_apdu ("00A4040006D27600012401", "SELECT AID", 0);
+      if (err)
+        goto leave;
+
+      /* Do some dummy verifies with wrong PINs to set the retry
+         counter to zero.  We can't easily use the card version 2.1
+         feature of presenting the admin PIN to allow the terminate
+         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 ("0020008120"
+                   "40404040404040404040404040404040"
+                   "40404040404040404040404040404040", "VERIFY", 0xffff);
+      for (i=0; i < 4; i++)
+        send_apdu ("0020008320"
+                   "40404040404040404040404040404040"
+                   "40404040404040404040404040404040", "VERIFY", 0xffff);
+
+      /* Send terminate datafile command.  */
+      err = send_apdu ("00e60000", "TERMINATE DF", 0x6985);
+      if (err)
         goto leave;
     }
 
         goto leave;
     }
 
-  rc = save_unprotected_key_to_card (sk, keyno);
-  if (rc)
+  /* Send activate datafile command.  This is used without
+     confirmation if the card is already in termination state.  */
+  err = send_apdu ("00440000", "ACTIVATE DF", 0);
+  if (err)
     goto leave;
 
     goto leave;
 
-  /* Get back to the maybe protected original secret key.  */
-  if (copied_sk)
+  /* Finally we reset the card reader once more.  */
+  err = send_apdu (NULL, "RESET", 0);
+
+  /* Then, connect the card again.  */
+  if (!err)
     {
     {
-      free_secret_key (copied_sk);
-      copied_sk = NULL; 
+      char *serialno0;
+
+      err = agent_scd_serialno (&serialno0, NULL);
+      if (!err)
+        xfree (serialno0);
     }
     }
-  sk = node->pkt->pkt.secret_key;
 
 
-  /* Get rid of the secret key parameters and store the serial numer. */
-  n = pubkey_get_nskey (sk->pubkey_algo);
-  for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++)
+ leave:
+  xfree (answer);
+  agent_release_card_info (&info);
+}
+
+
+#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)
     {
     {
-      gcry_mpi_release (sk->skey[i]);
-      sk->skey[i] = NULL;
+      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);
     }
     }
-  i = pubkey_get_npkey (sk->pubkey_algo);
-  sk->skey[i] = gcry_mpi_set_opaque (NULL, xstrdup ("dummydata"), 10*8);
-  sk->is_protected = 1;
-  sk->protect.s2k.mode = 1002;
-  s = info.serialno;
-  for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
-       sk->protect.ivlen++, s += 2)
-    sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s);
 
 
-  okay = 1;
+  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:
 
  leave:
-  if (copied_sk)
-    free_secret_key (copied_sk);
   agent_release_card_info (&info);
   agent_release_card_info (&info);
-  return okay;
 }
 }
-
-
 \f
 /* Data used by the command parser.  This needs to be outside of the
    function scope to allow readline based command completion.  */
 \f
 /* Data used by the command parser.  This needs to be outside of the
    function scope to allow readline based command completion.  */
@@ -1381,7 +2004,7 @@ enum cmdids
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
-    cmdUNBLOCK,
+    cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP,
     cmdINVCMD
   };
 
     cmdINVCMD
   };
 
@@ -1413,14 +2036,17 @@ static struct
     { "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") },
     { "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") },
+    { "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 },
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
+    { "readcert", cmdREADCERT, 0, NULL },
     { "writecert", cmdWRITECERT, 1, NULL },
     { "writecert", cmdWRITECERT, 1, NULL },
-    { NULL, cmdINVCMD, 0, NULL } 
+    { NULL, cmdINVCMD, 0, NULL }
   };
 
 
   };
 
 
-#if GNUPG_MAJOR_VERSION == 1 && defined (HAVE_LIBREADLINE)
+#ifdef HAVE_LIBREADLINE
 
 /* These two functions are used by readline for command completion. */
 
 
 /* These two functions are used by readline for command completion. */
 
@@ -1453,6 +2079,7 @@ command_generator(const char *text,int state)
 static char **
 card_edit_completion(const char *text, int start, int end)
 {
 static char **
 card_edit_completion(const char *text, int start, int end)
 {
+  (void)end;
   /* If we are at the start of a line, we try and command-complete.
      If not, just do nothing for now. */
 
   /* If we are at the start of a line, we try and command-complete.
      If not, just do nothing for now. */
 
@@ -1463,18 +2090,18 @@ card_edit_completion(const char *text, int start, int end)
 
   return NULL;
 }
 
   return NULL;
 }
-#endif /* GNUPG_MAJOR_VERSION == 1 && HAVE_LIBREADLINE */
+#endif /*HAVE_LIBREADLINE*/
 
 /* Menu to edit all user changeable values on an OpenPGP card.  Only
    Key creation is not handled here. */
 void
 
 /* Menu to edit all user changeable values on an OpenPGP card.  Only
    Key creation is not handled here. */
 void
-card_edit (strlist_t commands)
+card_edit (ctrl_t ctrl, strlist_t commands)
 {
   enum cmdids cmd = cmdNOP;
   int have_commands = !!commands;
   int redisplay = 1;
   char *answer = NULL;
 {
   enum cmdids cmd = cmdNOP;
   int have_commands = !!commands;
   int redisplay = 1;
   char *answer = NULL;
-  int did_checkpin = 0, allow_admin=0;
+  int allow_admin=0;
   char serialnobuf[50];
 
 
   char serialnobuf[50];
 
 
@@ -1494,18 +2121,20 @@ card_edit (strlist_t commands)
       char *p;
       int i;
       int cmd_admin_only;
       char *p;
       int i;
       int cmd_admin_only;
-      
+
       tty_printf("\n");
       tty_printf("\n");
-      if (redisplay )
+      if (redisplay)
         {
           if (opt.with_colons)
             {
         {
           if (opt.with_colons)
             {
-              card_status (stdout, serialnobuf, DIM (serialnobuf));
+              current_card_status (ctrl, es_stdout,
+                                   serialnobuf, DIM (serialnobuf));
               fflush (stdout);
             }
           else
             {
               fflush (stdout);
             }
           else
             {
-              card_status (NULL, serialnobuf, DIM (serialnobuf));
+              current_card_status (ctrl, NULL,
+                                   serialnobuf, DIM (serialnobuf));
               tty_printf("\n");
             }
           redisplay = 0;
               tty_printf("\n");
             }
           redisplay = 0;
@@ -1531,15 +2160,11 @@ card_edit (strlist_t commands)
 
            if (!have_commands)
               {
 
            if (!have_commands)
               {
-#if GNUPG_MAJOR_VERSION == 1
                tty_enable_completion (card_edit_completion);
                tty_enable_completion (card_edit_completion);
-#endif
-               answer = cpr_get_no_help("cardedit.prompt", _("Command> "));
+               answer = cpr_get_no_help("cardedit.prompt", _("gpg/card> "));
                cpr_kill_prompt();
                cpr_kill_prompt();
-#if GNUPG_MAJOR_VERSION == 1
                tty_disable_completion ();
                tty_disable_completion ();
-#endif
-           }
+              }
            trim_spaces(answer);
        }
       while ( *answer == '#' );
            trim_spaces(answer);
        }
       while ( *answer == '#' );
@@ -1550,7 +2175,7 @@ card_edit (strlist_t commands)
         cmd = cmdLIST; /* Default to the list command */
       else if (*answer == CONTROL_D)
         cmd = cmdQUIT;
         cmd = cmdLIST; /* Default to the list command */
       else if (*answer == CONTROL_D)
         cmd = cmdQUIT;
-      else 
+      else
         {
           if ((p=strchr (answer,' ')))
             {
         {
           if ((p=strchr (answer,' ')))
             {
@@ -1565,7 +2190,7 @@ card_edit (strlist_t commands)
               while (spacep (arg_rest))
                 arg_rest++;
             }
               while (spacep (arg_rest))
                 arg_rest++;
             }
-          
+
           for (i=0; cmds[i].name; i++ )
             if (!ascii_strcasecmp (answer, cmds[i].name ))
               break;
           for (i=0; cmds[i].name; i++ )
             if (!ascii_strcasecmp (answer, cmds[i].name ))
               break;
@@ -1587,7 +2212,7 @@ card_edit (strlist_t commands)
           for (i=0; cmds[i].name; i++ )
             if(cmds[i].desc
               && (!cmds[i].admin_only || (cmds[i].admin_only && allow_admin)))
           for (i=0; cmds[i].name; i++ )
             if(cmds[i].desc
               && (!cmds[i].admin_only || (cmds[i].admin_only && allow_admin)))
-              tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
+              tty_printf("%-14s %s\n", cmds[i].name, _(cmds[i].desc) );
           break;
 
        case cmdADMIN:
           break;
 
        case cmdADMIN:
@@ -1631,7 +2256,7 @@ card_edit (strlist_t commands)
           break;
 
        case cmdFETCH:
           break;
 
        case cmdFETCH:
-         fetch_url();
+         fetch_url (ctrl);
          break;
 
         case cmdLOGIN:
          break;
 
         case cmdLOGIN:
@@ -1669,22 +2294,35 @@ card_edit (strlist_t commands)
             change_cert (arg_rest);
           break;
 
             change_cert (arg_rest);
           break;
 
+        case cmdREADCERT:
+          if ( arg_number != 3 )
+            tty_printf ("usage: readcert 3 > FILE\n");
+          else
+            read_cert (arg_rest);
+          break;
+
         case cmdFORCESIG:
           toggle_forcesig ();
           break;
 
         case cmdGENERATE:
         case cmdFORCESIG:
           toggle_forcesig ();
           break;
 
         case cmdGENERATE:
-          generate_card_keys ();
+          generate_card_keys (ctrl);
           break;
 
         case cmdPASSWD:
           change_pin (0, allow_admin);
           break;
 
         case cmdPASSWD:
           change_pin (0, allow_admin);
-          did_checkpin = 0; /* Need to reset it of course. */
           break;
 
         case cmdUNBLOCK:
           change_pin (1, allow_admin);
           break;
 
         case cmdUNBLOCK:
           change_pin (1, allow_admin);
-          did_checkpin = 0; /* Need to reset it of course. */
+          break;
+
+        case cmdFACTORYRESET:
+          factory_reset ();
+          break;
+
+        case cmdKDFSETUP:
+          kdf_setup ();
           break;
 
         case cmdQUIT:
           break;
 
         case cmdQUIT:
@@ -1704,4 +2342,3 @@ card_edit (strlist_t commands)
  leave:
   xfree (answer);
 }
  leave:
   xfree (answer);
 }
-