First set of changes to backport the new card code from 2.0.
authorWerner Koch <wk@gnupg.org>
Tue, 21 Jul 2009 14:30:13 +0000 (14:30 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 21 Jul 2009 14:30:13 +0000 (14:30 +0000)
For compatibility reasons a few new files had to be added.
Also added estream-printf as this is now used in app-openpgp.c and provides
a better and generic asprintf implementation than the hack we used for the
W32 code in ttyio.c.  Card code is not yet finished.

37 files changed:
AUTHORS
ChangeLog
NEWS
configure.ac
g10/ChangeLog
g10/apdu.c
g10/apdu.h
g10/app-common.h
g10/app-openpgp.c
g10/card-util.c
g10/cardglue.c
g10/cardglue.h
g10/ccid-driver.c
g10/ccid-driver.h
g10/gpg.c
g10/iso7816.c
g10/iso7816.h
g10/keyedit.c
g10/keygen.c
g10/main.h
g10/options.h
include/ChangeLog
include/distfiles
include/estream-printf.h [new file with mode: 0644]
include/memory.h
include/types.h
include/util.h
m4/ChangeLog
m4/Makefile.am
m4/estream.m4 [new file with mode: 0644]
util/ChangeLog
util/Makefile.am
util/convert.c [new file with mode: 0644]
util/estream-printf.c [new file with mode: 0644]
util/memory.c
util/strgutil.c
util/ttyio.c

diff --git a/AUTHORS b/AUTHORS
index 31466ed..742608e 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,6 +1,6 @@
 Program: GnuPG
 Maintainer:  Werner Koch <wk@gnupg.org>
-Bug reports: <bug-gnupg@gnu.org>
+Bug reports: http://bugs.gnupg.org
 Security related bug reports: <security@gnupg.org>
 License: GPLv3+
 
index 6db7d83..ba5d0ed 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-07-21  Werner Koch  <wk@g10code.com>
+
+       * configure.ac (AH_BOTTOM): Add macros for estream-printf.
+       (estream_PRINTF_INIT): Add it.
+
 2009-06-05  David Shaw  <dshaw@jabberwocky.com>
 
        * configure.ac: Remove Camellia restriction.
diff --git a/NEWS b/NEWS
index d66a33a..6441452 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,10 @@ Noteworthy changes in version 1.4.10 (unreleased)
 
     * Fixed a memory leak which made imports of many keys very slow.
 
+    * Support v2 OpenPGP cards.
+
+    * FIXME:  Anything else?
+
 
 Noteworthy changes in version 1.4.9 (2008-03-26)
 ------------------------------------------------
index b017040..9183675 100644 (file)
@@ -499,6 +499,11 @@ is intended for making fat binary builds on OS X. */
 #define SAFE_VERSION_DOT  '.'
 #define SAFE_VERSION_DASH '-'
 
+/* We want to use our memory allocator for estream-printf.  */
+#define _ESTREAM_PRINTF_MALLOC        xtrymalloc
+#define _ESTREAM_PRINTF_FREE          xfree
+#define _ESTREAM_PRINTF_EXTRA_INCLUDE "memory.h"
+
 #endif /*GNUPG_CONFIG_H_INCLUDED*/
 ])
 
@@ -1049,6 +1054,12 @@ fi
 GNUPG_CHECK_MLOCK
 GNUPG_FUNC_MKDIR_TAKES_ONE_ARG
 
+#
+# Prepare building of estream-printf
+#
+estream_PRINTF_INIT
+
+
 dnl
 dnl Check whether we can use Linux capabilities as requested
 dnl
index 68cf2d9..b5ed29a 100644 (file)
@@ -1,3 +1,31 @@
+2009-07-21  Werner Koch  <wk@g10code.com>
+
+       * app-common.h, app-openpgp.c, iso7816.c, iso7816.h, apdu.c,
+       * apdu.h, ccid-driver.c, ccid-driver.h, card-util.c: Update from
+       GnuPG 2.0 SVN revision 5084.
+
+       * cardglue.h (GCRY_MD_SHA256): Add more GCRY_MD constants.
+       (gcry_handler_progress_t): Add definition.
+       (struct agent_card_info_s): Add fields apptype, is_v2, key_attr.
+       * cardglue.c (learn_status_cb): Set them.
+       (agent_release_card_info): Release APPTYPE.
+       (unescape_status_string, send_status_direct): New.
+       (gcry_mpi_release, gcry_mpi_set_opaque): New.
+       (gcry_md_algo_name): New.
+       (open_card): s/initialized/ref_count/.
+       (agent_learn): Pass new new flag arg to learn_status.
+       (agent_scd_genkey): Add new arg createtime.
+       * keygen.c (gen_card_key, gen_card_key_with_backup): Add new arg
+       TIMESTAMP.
+       (write_direct_sig, write_selfsigs, write_keybinding)
+       (make_backsig): Ditto.
+       (do_generate_keypair): Pass timestamp to all signing functions. 
+       (generate_card_subkeypair): Ditto.
+       * keyedit.c (menu_backsign): Pass a new timestamp to all backsisg.
+
+       * gpg.c (main): Disable keypad support.
+       * options.h (struct): Add field disable_keypad.
+
 2009-07-17  Werner Koch  <wk@g10code.com>
 
        * keyring.c (keyring_rebuild_cache): Replace the assert by a
index d29676b..d114b6e 100644 (file)
@@ -1,5 +1,5 @@
 /* apdu.c - ISO 7816 APDU functions and low level I/O
- *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -31,9 +31,9 @@
 #include <assert.h>
 #include <signal.h>
 #ifdef USE_GNU_PTH
-# include <pth.h>
 # include <unistd.h>
 # include <fcntl.h>
+# include <pth.h>
 #endif
 
 
 #include "memory.h"
 #include "util.h"
 #include "i18n.h"
+#include "dynload.h"
 #include "cardglue.h"
 #else /* GNUPG_MAJOR_VERSION != 1 */
 #include "scdaemon.h"
+#include "exechelp.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
 
 #include "apdu.h"
-#include "dynload.h"
 #include "ccid-driver.h"
 
 
 #define DLSTDCALL
 #endif
 
-#ifdef _POSIX_OPEN_MAX
-#define MAX_OPEN_FDS _POSIX_OPEN_MAX
-#else
-#define MAX_OPEN_FDS 20
-#endif
 
-/* Helper to pass patrameters related to keypad based operations. */
+/* Helper to pass parameters related to keypad based operations. */
 struct pininfo_s
 {
   int mode;
@@ -104,6 +100,8 @@ struct reader_table_s {
   unsigned short port; /* Port number:  0 = unused, 1 - dev/tty */
 
   /* Function pointers intialized to the various backends.  */
+  int (*connect_card)(int);
+  int (*disconnect_card)(int);
   int (*close_reader)(int);
   int (*shutdown_reader)(int);
   int (*reset_reader)(int);
@@ -112,6 +110,7 @@ struct reader_table_s {
                           unsigned char *, size_t *, struct pininfo_s *);
   int (*check_keypad)(int, int, int, int, int, int);
   void (*dump_status_reader)(int);
+  int (*set_progress_cb)(int, gcry_handler_progress_t, void*);
 
   struct {
     ccid_driver_t handle;
@@ -132,6 +131,7 @@ struct reader_table_s {
   } rapdu;
 #endif /*USE_G10CODE_RAPDU*/
   char *rdrname;     /* Name of the connected reader or NULL if unknown. */
+  int any_status;    /* True if we have seen any status.  */
   int last_status;
   int status;
   int is_t0;         /* True if we know that we are running T=0. */
@@ -220,6 +220,11 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_E_READER_UNAVAILABLE      0x80100017
 #define PCSC_W_REMOVED_CARD            0x80100069
 
+/* The PC/SC error is defined as a long as per specs.  Due to left
+   shifts bit 31 will get sign extended.  We use this mask to fix
+   it. */
+#define PCSC_ERR_MASK(a)  ((a) & 0xffffffff)
+
 
 struct pcsc_io_request_s
 {
@@ -272,7 +277,8 @@ long (* DLSTDCALL pcsc_status) (unsigned long card,
                                 unsigned long *r_protocol,
                                 unsigned char *atr, unsigned long *atrlen);
 long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card);
-long (* DLSTDCALL pcsc_end_transaction) (unsigned long card);
+long (* DLSTDCALL pcsc_end_transaction) (unsigned long card,
+                                         unsigned long disposition);
 long (* DLSTDCALL pcsc_transmit) (unsigned long card,
                                   const pcsc_io_request_t send_pci,
                                   const unsigned char *send_buffer,
@@ -286,6 +292,10 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
 
 /*  Prototypes.  */
 static int pcsc_get_status (int slot, unsigned int *status);
+static int reset_pcsc_reader (int slot);
+static int apdu_get_status_internal (int slot, int hang, int no_atr_reset,
+                                     unsigned int *status,
+                                     unsigned int *changed);
 
 
 \f
@@ -322,6 +332,8 @@ new_reader_slot (void)
       reader_table[reader].lock_initialized = 1;
     }
 #endif /*USE_GNU_PTH*/
+  reader_table[reader].connect_card = NULL;
+  reader_table[reader].disconnect_card = NULL;
   reader_table[reader].close_reader = NULL;
   reader_table[reader].shutdown_reader = NULL;
   reader_table[reader].reset_reader = NULL;
@@ -329,8 +341,10 @@ new_reader_slot (void)
   reader_table[reader].send_apdu_reader = NULL;
   reader_table[reader].check_keypad = NULL;
   reader_table[reader].dump_status_reader = NULL;
+  reader_table[reader].set_progress_cb = NULL;
 
-  reader_table[reader].used = 1;
+  reader_table[reader].used = 1;  
+  reader_table[reader].any_status = 0;
   reader_table[reader].last_status = 0;
   reader_table[reader].is_t0 = 1;
 #ifdef NEED_PCSC_WRAPPER
@@ -381,6 +395,7 @@ host_sw_string (long err)
     case SW_HOST_NO_READER: return "no reader";
     case SW_HOST_ABORTED: return "aborted";
     case SW_HOST_NO_KEYPAD: return "no keypad"; 
+    case SW_HOST_ALREADY_CONNECTED: return "already connected"; 
     default: return "unknown host status error";
     }
 }
@@ -402,6 +417,7 @@ apdu_strerror (int rc)
     case SW_FILE_NOT_FOUND : return "file not found";
     case SW_RECORD_NOT_FOUND:return "record not found";
     case SW_REF_NOT_FOUND  : return "reference not found";
+    case SW_BAD_LC         : return "bad Lc";
     case SW_BAD_P0_P1      : return "bad P0 or P1";
     case SW_INS_NOT_SUP    : return "instruction not supported";
     case SW_CLA_NOT_SUP    : return "class not supported";
@@ -531,10 +547,11 @@ reset_ct_reader (int slot)
 static int
 ct_get_status (int slot, unsigned int *status)
 {
-  *status = 1|2|4;  /* FIXME */
+  (void)slot;
+  /* The status we returned is wrong but we don't care becuase ctAPI
+     is not anymore required.  */
+  *status = APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE;
   return 0;
-
-  return SW_HOST_NOT_SUPPORTED;
 }
 
 /* Actually send the APDU of length APDULEN to SLOT and return a
@@ -548,6 +565,8 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   unsigned char dad[1], sad[1];
   unsigned short ctbuflen;
 
+  (void)pininfo;
+
   /* If we don't have an ATR, we need to reset the reader first. */
   if (!reader_table[slot].atrlen
       && (rc = reset_ct_reader (slot)))
@@ -655,6 +674,9 @@ readn (int fd, void *buf, size_t buflen, size_t *nread)
   while (nleft > 0)
     {
 #ifdef USE_GNU_PTH
+# ifdef HAVE_W32_SYSTEM
+#  error Cannot use pth_read here because it expects a system HANDLE.
+# endif
       n = pth_read (fd, buf, nleft);
 #else
       n = read (fd, buf, nleft);
@@ -736,7 +758,7 @@ pcsc_error_to_sw (long ec)
 {
   int rc;
 
-  switch (ec)
+  switch ( PCSC_ERR_MASK (ec) )
     {
     case 0:  rc = 0; break;
 
@@ -762,177 +784,87 @@ pcsc_error_to_sw (long ec)
 static void
 dump_pcsc_reader_status (int slot)
 {
-  log_info ("reader slot %d: active protocol:", slot);
-  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
-    log_printf (" T0");
-  else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
-    log_printf (" T1");
-  else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
-    log_printf (" raw");
-  log_printf ("\n");
+  if (reader_table[slot].pcsc.card)
+    {
+      log_info ("reader slot %d: active protocol:", slot);
+      if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
+        log_printf (" T0");
+      else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+        log_printf (" T1");
+      else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
+        log_printf (" raw");
+      log_printf ("\n");
+    }
+  else
+    log_info ("reader slot %d: not connected\n", slot);
 }
 
 
-/* Send an PC/SC reset command and return a status word on error or 0
-   on success. */
+#ifndef NEED_PCSC_WRAPPER
 static int
-reset_pcsc_reader (int slot)
+pcsc_get_status_direct (int slot, unsigned int *status)
 {
-#ifdef NEED_PCSC_WRAPPER
   long err;
-  reader_table_t slotp;
-  size_t len;
-  int i, n;
-  unsigned char msgbuf[9];
-  unsigned int dummy_status;
-  int sw = SW_HOST_CARD_IO_ERROR;
-
-  slotp = reader_table + slot;
-
-  if (slotp->pcsc.req_fd == -1
-      || slotp->pcsc.rsp_fd == -1
-      || slotp->pcsc.pid == (pid_t)(-1) )
-    {
-      log_error ("pcsc_get_status: pcsc-wrapper not running\n");
-      return sw;
-    }
-
-  msgbuf[0] = 0x05; /* RESET command. */
-  len = 0;
-  msgbuf[1] = (len >> 24);
-  msgbuf[2] = (len >> 16);
-  msgbuf[3] = (len >>  8);
-  msgbuf[4] = (len      );
-  if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
-    {
-      log_error ("error sending PC/SC RESET request: %s\n",
-                 strerror (errno));
-      goto command_failed;
-    }
+  struct pcsc_readerstate_s rdrstates[1];
 
-  /* Read the response. */
-  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
-    {
-      log_error ("error receiving PC/SC RESET response: %s\n",
-                 i? strerror (errno) : "premature EOF");
-      goto command_failed;
-    }
-  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
-  if (msgbuf[0] != 0x81 || len < 4)
-    {
-      log_error ("invalid response header from PC/SC received\n");
-      goto command_failed;
-    }
-  len -= 4; /* Already read the error code. */
-  if (len > DIM (slotp->atr))
-    {
-      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
-                 (unsigned long)len);
-      sw = SW_HOST_GENERAL_ERROR;
-      goto command_failed;
-    }
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  memset (rdrstates, 0, sizeof *rdrstates);
+  rdrstates[0].reader = reader_table[slot].rdrname;
+  rdrstates[0].current_state = PCSC_STATE_UNAWARE;
+  err = pcsc_get_status_change (reader_table[slot].pcsc.context,
+                                0,
+                                rdrstates, 1);
+  if (err == PCSC_E_TIMEOUT)
+    err = 0; /* Timeout is no error error here. */
   if (err)
     {
-      log_error ("PC/SC RESET failed: %s (0x%lx)\n",
+      log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
                  pcsc_error_string (err), err);
-      /* If the error code is no smart card, we should not considere
-         this a major error and close the wrapper.  */
-      sw = pcsc_error_to_sw (err);
-      if (err == PCSC_E_NO_SMARTCARD)
-        return sw;
-      goto command_failed;
-    }
-
-  /* The open function may return a zero for the ATR length to
-     indicate that no card is present.  */
-  n = len;
-  if (n)
-    {
-      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
-        {
-          log_error ("error receiving PC/SC RESET response: %s\n",
-                     i? strerror (errno) : "premature EOF");
-          goto command_failed;
-        }
-    }
-  slotp->atrlen = len;
-
-  /* Read the status so that IS_T0 will be set. */
-  pcsc_get_status (slot, &dummy_status);
-
-  return 0;
-
- command_failed:
-  close (slotp->pcsc.req_fd);
-  close (slotp->pcsc.rsp_fd);
-  slotp->pcsc.req_fd = -1;
-  slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
-  slotp->pcsc.pid = (pid_t)(-1);
-  slotp->used = 0;
-  return sw;
-
-#else /* !NEED_PCSC_WRAPPER */
-  long err;
-  char reader[250];
-  unsigned long nreader, atrlen;
-  unsigned long card_state, card_protocol;
-
-  if (reader_table[slot].pcsc.card)
-    {
-      err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
-      if (err)
-        {
-          log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
-                     pcsc_error_string (err), err);
-          return SW_HOST_CARD_IO_ERROR;
-        }
-      reader_table[slot].pcsc.card = 0;
-    }
-
-  err = pcsc_connect (reader_table[slot].pcsc.context,
-                      reader_table[slot].rdrname,
-                      PCSC_SHARE_EXCLUSIVE,
-                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
-                      &reader_table[slot].pcsc.card,
-                      &reader_table[slot].pcsc.protocol);
-  if (err)
-    {
-      log_error ("pcsc_connect failed: %s (0x%lx)\n",
-                  pcsc_error_string (err), err);
-      reader_table[slot].pcsc.card = 0;
       return pcsc_error_to_sw (err);
     }
 
+  /*   log_debug  */
+  /*     ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
 
-  atrlen = 33;
-  nreader = sizeof reader - 1;
-  err = pcsc_status (reader_table[slot].pcsc.card,
-                     reader, &nreader,
-                     &card_state, &card_protocol,
-                     reader_table[slot].atr, &atrlen);
-  if (err)
-    {
-      log_error ("pcsc_status failed: %s (0x%lx)\n",
-                  pcsc_error_string (err), err);
-      reader_table[slot].atrlen = 0;
-      return pcsc_error_to_sw (err);
-    }
-  if (atrlen >= DIM (reader_table[0].atr))
-    log_bug ("ATR returned by pcsc_status is too large\n");
-  reader_table[slot].atrlen = atrlen;
-  reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+  *status = 0;
+  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+    *status |= APDU_CARD_PRESENT;
+  if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+    *status |= APDU_CARD_ACTIVE;
+#ifndef HAVE_W32_SYSTEM
+  /* We indicate a useful card if it is not in use by another
+     application.  This is because we only use exclusive access
+     mode.  */
+  if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+       == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)
+       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+    *status |= APDU_CARD_USABLE;
+#else
+  /* Some winscard drivers may set EXCLUSIVE and INUSE at the same
+     time when we are the only user (SCM SCR335) under Windows.  */
+  if ((*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+      == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+    *status |= APDU_CARD_USABLE;
+#endif
 
   return 0;
-#endif /* !NEED_PCSC_WRAPPER */
 }
+#endif /*!NEED_PCSC_WRAPPER*/
 
 
+#ifdef NEED_PCSC_WRAPPER
 static int
-pcsc_get_status (int slot, unsigned int *status)
+pcsc_get_status_wrapped (int slot, unsigned int *status)
 {
-#ifdef NEED_PCSC_WRAPPER
   long err;
   reader_table_t slotp;
   size_t len, full_len;
@@ -978,7 +910,8 @@ pcsc_get_status (int slot, unsigned int *status)
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     {
       log_error ("pcsc_status failed: %s (0x%lx)\n",
@@ -1023,7 +956,6 @@ pcsc_get_status (int slot, unsigned int *status)
   /* We are lucky: The wrapper already returns the data in the
      required format. */
   *status = buffer[3];
-
   return 0;
 
  command_failed:
@@ -1035,67 +967,63 @@ pcsc_get_status (int slot, unsigned int *status)
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return sw;
+}
+#endif /*NEED_PCSC_WRAPPER*/
 
-#else /*!NEED_PCSC_WRAPPER*/
 
-  long err;
-  struct pcsc_readerstate_s rdrstates[1];
+static int
+pcsc_get_status (int slot, unsigned int *status)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return pcsc_get_status_wrapped (slot, status);
+#else
+  return pcsc_get_status_direct (slot, status);
+#endif
+}
 
-  memset (rdrstates, 0, sizeof *rdrstates);
-  rdrstates[0].reader = reader_table[slot].rdrname;
-  rdrstates[0].current_state = PCSC_STATE_UNAWARE;
-  err = pcsc_get_status_change (reader_table[slot].pcsc.context,
-                                0,
-                                rdrstates, 1);
-  if (err == PCSC_E_TIMEOUT)
-    err = 0; /* Timeout is no error error here. */
-  if (err)
-    {
-      log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
-                 pcsc_error_string (err), err);
-      return pcsc_error_to_sw (err);
-    }
 
+#ifndef NEED_PCSC_WRAPPER
+static int
+pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
+                       unsigned char *buffer, size_t *buflen, 
+                       struct pininfo_s *pininfo)
+{
+  long err;
+  struct pcsc_io_request_s send_pci;
+  unsigned long recv_len;
 
-  /*   log_debug  */
-  /*     ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
+  if (!reader_table[slot].atrlen
+      && (err = reset_pcsc_reader (slot)))
+    return err;
 
-  *status = 0;
-  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
-    *status |= 2;
-  if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
-    *status |= 4;
-  /* We indicate a useful card if it is not in use by another
-     application.  This is because we only use exclusive access
-     mode.  */
-  if ( (*status & 6) == 6
-       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
-    *status |= 1;
+  if (DBG_CARD_IO)
+    log_printhex ("  PCSC_data:", apdu, apdulen);
 
-  return 0;
-#endif /*!NEED_PCSC_WRAPPER*/
+  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+      send_pci.protocol = PCSC_PROTOCOL_T1;
+  else
+      send_pci.protocol = PCSC_PROTOCOL_T0;
+  send_pci.pci_len = sizeof send_pci;
+  recv_len = *buflen;
+  err = pcsc_transmit (reader_table[slot].pcsc.card,
+                       &send_pci, apdu, apdulen,
+                       NULL, buffer, &recv_len);
+  *buflen = recv_len;
+  if (err)
+    log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+
+  return pcsc_error_to_sw (err);
 }
+#endif /*!NEED_PCSC_WRAPPER*/
 
 
-/* Actually send the APDU of length APDULEN to SLOT and return a
-   maximum of *BUFLEN data in BUFFER, the actual returned size will be
-   set to BUFLEN.  Returns: CT API error code. */
+#ifdef NEED_PCSC_WRAPPER
 static int
-pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen, 
-                struct pininfo_s *pininfo)
+pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
+                        unsigned char *buffer, size_t *buflen, 
+                        struct pininfo_s *pininfo)
 {
-#ifdef NEED_PCSC_WRAPPER
   long err;
   reader_table_t slotp;
   size_t len, full_len;
@@ -1103,6 +1031,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   unsigned char msgbuf[9];
   int sw = SW_HOST_CARD_IO_ERROR;
 
+  (void)pininfo;
+
   if (!reader_table[slot].atrlen
       && (err = reset_pcsc_reader (slot)))
     return err;
@@ -1148,7 +1078,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     {
       log_error ("pcsc_transmit failed: %s (0x%lx)\n",
@@ -1174,7 +1105,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
        err = SW_HOST_INV_VALUE;
      }
    /* We need to read any rest of the response, to keep the
-      protocol runnng. */
+      protocol running.  */
    while (full_len)
      {
        unsigned char dummybuf[128];
@@ -1200,43 +1131,43 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return sw;
+}
+#endif /*NEED_PCSC_WRAPPER*/
 
-#else /*!NEED_PCSC_WRAPPER*/
-
-  long err;
-  struct pcsc_io_request_s send_pci;
-  unsigned long recv_len;
 
-  if (!reader_table[slot].atrlen
-      && (err = reset_pcsc_reader (slot)))
-    return err;
-
-  if (DBG_CARD_IO)
-    log_printhex ("  PCSC_data:", apdu, apdulen);
+/* Send the APDU of length APDULEN to SLOT and return a maximum of
+   *BUFLEN data in BUFFER, the actual returned size will be stored at
+   BUFLEN.  Returns: A status word. */
+static int
+pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+                unsigned char *buffer, size_t *buflen, 
+                struct pininfo_s *pininfo)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo);
+#else
+  return pcsc_send_apdu_direct (slot, apdu, apdulen, buffer, buflen, pininfo);
+#endif
+}
 
-  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
-      send_pci.protocol = PCSC_PROTOCOL_T1;
-  else
-      send_pci.protocol = PCSC_PROTOCOL_T0;
-  send_pci.pci_len = sizeof send_pci;
-  recv_len = *buflen;
-  err = pcsc_transmit (reader_table[slot].pcsc.card,
-                       &send_pci, apdu, apdulen,
-                       NULL, buffer, &recv_len);
-  *buflen = recv_len;
-  if (err)
-    log_error ("pcsc_transmit failed: %s (0x%lx)\n",
-               pcsc_error_string (err), err);
 
-  return pcsc_error_to_sw (err);
-#endif /*!NEED_PCSC_WRAPPER*/
+#ifndef NEED_PCSC_WRAPPER
+static int
+close_pcsc_reader_direct (int slot)
+{
+  pcsc_release_context (reader_table[slot].pcsc.context);
+  xfree (reader_table[slot].rdrname);
+  reader_table[slot].rdrname = NULL;
+  reader_table[slot].used = 0;
+  return 0;
 }
+#endif /*!NEED_PCSC_WRAPPER*/
 
 
+#ifdef NEED_PCSC_WRAPPER
 static int
-close_pcsc_reader (int slot)
+close_pcsc_reader_wrapped (int slot)
 {
-#ifdef NEED_PCSC_WRAPPER
   long err;
   reader_table_t slotp;
   size_t len;
@@ -1280,7 +1211,8 @@ close_pcsc_reader (int slot)
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     log_error ("pcsc_close failed: %s (0x%lx)\n",
                pcsc_error_string (err), err);
@@ -1297,152 +1229,165 @@ close_pcsc_reader (int slot)
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return 0;
+}
+#endif /*NEED_PCSC_WRAPPER*/
 
-#else /*!NEED_PCSC_WRAPPER*/
 
-  pcsc_release_context (reader_table[slot].pcsc.context);
-  xfree (reader_table[slot].rdrname);
-  reader_table[slot].rdrname = NULL;
-  reader_table[slot].used = 0;
-  return 0;
-#endif /*!NEED_PCSC_WRAPPER*/
+static int
+close_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return close_pcsc_reader_wrapped (slot);
+#else
+  return close_pcsc_reader_direct (slot);
+#endif
 }
 
-/* Note:  It is a pitty that we can't return proper error codes.  */
+
+/* Connect a PC/SC card.  */
+#ifndef NEED_PCSC_WRAPPER
 static int
-open_pcsc_reader (const char *portstr)
+connect_pcsc_card (int slot)
 {
-#ifdef NEED_PCSC_WRAPPER
-/* Open the PC/SC reader using the pcsc_wrapper program.  This is
-   needed to cope with different thread models and other peculiarities
-   of libpcsclite. */
-  int slot;
-  reader_table_t slotp;
-  int fd, rp[2], wp[2];
-  int n, i;
-  pid_t pid;
-  size_t len;
-  unsigned char msgbuf[9];
-  int err;
-  unsigned int dummy_status;
-  int sw = SW_HOST_CARD_IO_ERROR;
+  long err;
 
-  slot = new_reader_slot ();
-  if (slot == -1)
-    return -1;
-  slotp = reader_table + slot;
+  assert (slot >= 0 && slot < MAX_READER);
 
-  /* Fire up the pcsc wrapper.  We don't use any fork/exec code from
-     the common directy but implement it direclty so that this file
-     may still be source copied. */
+  if (reader_table[slot].pcsc.card)
+    return SW_HOST_ALREADY_CONNECTED;
 
-  if (pipe (rp) == -1)
+  reader_table[slot].atrlen = 0;
+  reader_table[slot].last_status = 0;
+  reader_table[slot].is_t0 = 0;
+
+  err = pcsc_connect (reader_table[slot].pcsc.context,
+                      reader_table[slot].rdrname,
+                      PCSC_SHARE_EXCLUSIVE,
+                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+                      &reader_table[slot].pcsc.card,
+                      &reader_table[slot].pcsc.protocol);
+  if (err)
     {
-      log_error ("error creating a pipe: %s\n", strerror (errno));
-      slotp->used = 0;
-      return -1;
+      reader_table[slot].pcsc.card = 0;
+      if (err != PCSC_E_NO_SMARTCARD) 
+        log_error ("pcsc_connect failed: %s (0x%lx)\n",
+                   pcsc_error_string (err), err);
     }
-  if (pipe (wp) == -1)
+  else
     {
-      log_error ("error creating a pipe: %s\n", strerror (errno));
-      close (rp[0]);
-      close (rp[1]);
-      slotp->used = 0;
-      return -1;
+      char reader[250];
+      unsigned long readerlen, atrlen;
+      unsigned long card_state, card_protocol;
+
+      atrlen = DIM (reader_table[0].atr);
+      readerlen = sizeof reader -1 ;
+      err = pcsc_status (reader_table[slot].pcsc.card,
+                         reader, &readerlen,
+                         &card_state, &card_protocol,
+                         reader_table[slot].atr, &atrlen);
+      if (err)
+        log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
+                   pcsc_error_string (err), err, readerlen);
+      else
+        {
+          if (atrlen > DIM (reader_table[0].atr))
+            log_bug ("ATR returned by pcsc_status is too large\n");
+          reader_table[slot].atrlen = atrlen;
+          /* If we got to here we know that a card is present
+             and usable.  Remember this.  */
+          reader_table[slot].last_status = (   APDU_CARD_USABLE
+                                             | APDU_CARD_PRESENT
+                                             | APDU_CARD_ACTIVE);
+          reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+        }
     }
 
-  pid = fork ();
-  if (pid == -1)
+  dump_reader_status (slot);
+  return pcsc_error_to_sw (err);
+}
+#endif /*!NEED_PCSC_WRAPPER*/
+
+
+/* Disconnect a PC/SC card.  Note that this succeeds even if the card
+   is not connected.  */
+#ifndef NEED_PCSC_WRAPPER
+static int
+disconnect_pcsc_card (int slot)
+{
+  long err;
+
+  assert (slot >= 0 && slot < MAX_READER);
+
+  if (!reader_table[slot].pcsc.card)
+    return 0; 
+
+  err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
+  if (err)
     {
-      log_error ("error forking process: %s\n", strerror (errno));
-      close (rp[0]);
-      close (rp[1]);
-      close (wp[0]);
-      close (wp[1]);
-      slotp->used = 0;
-      return -1;
+      log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return SW_HOST_CARD_IO_ERROR;
     }
-  slotp->pcsc.pid = pid;
+  reader_table[slot].pcsc.card = 0;
+  return 0;
+}
+#endif /*!NEED_PCSC_WRAPPER*/
 
-  if (!pid)
-    { /*
-         === Child ===
-       */
 
-      /* Double fork. */
-      pid = fork ();
-      if (pid == -1)
-        _exit (31);
-      if (pid)
-        _exit (0); /* Immediate exit this parent, so that the child
-                      gets cleaned up by the init process. */
+#ifndef NEED_PCSC_WRAPPER
+static int
+reset_pcsc_reader_direct (int slot)
+{
+  int sw;
 
-      /* Connect our pipes. */
-      if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
-        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
-      if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
-        log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
+  sw = disconnect_pcsc_card (slot);
+  if (!sw)
+    sw = connect_pcsc_card (slot);
 
-      /* Send stderr to the bit bucket. */
-      fd = open ("/dev/null", O_WRONLY);
-      if (fd == -1)
-        log_fatal ("can't open `/dev/null': %s", strerror (errno));
-      if (fd != 2 && dup2 (fd, 2) == -1)
-        log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
+  return sw;
+}
+#endif /*NEED_PCSC_WRAPPER*/
 
-      /* Close all other files. */
-      n = sysconf (_SC_OPEN_MAX);
-      if (n < 0)
-        n = MAX_OPEN_FDS;
-      for (i=3; i < n; i++)
-        close(i);
-      errno = 0;
 
-      execl (GNUPG_LIBDIR "/pcsc-wrapper",
-             "pcsc-wrapper",
-             "--",
-             "1", /* API version */
-             opt.pcsc_driver, /* Name of the PC/SC library. */
-              NULL);
-      _exit (31);
-    }
+#ifdef NEED_PCSC_WRAPPER
+static int
+reset_pcsc_reader_wrapped (int slot)
+{
+  long err;
+  reader_table_t slotp;
+  size_t len;
+  int i, n;
+  unsigned char msgbuf[9];
+  unsigned int dummy_status;
+  int sw = SW_HOST_CARD_IO_ERROR;
 
-  /*
-     === Parent ===
-   */
-  close (wp[0]);
-  close (rp[1]);
-  slotp->pcsc.req_fd = wp[1];
-  slotp->pcsc.rsp_fd = rp[0];
+  slotp = reader_table + slot;
 
-  /* Wait for the intermediate child to terminate. */
-#ifdef USE_GNU_PTH
-#define WAIT pth_waitpid
-#else
-#define WAIT waitpid
-#endif
-  while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
-    ;
-#undef X
+  if (slotp->pcsc.req_fd == -1
+      || slotp->pcsc.rsp_fd == -1
+      || slotp->pcsc.pid == (pid_t)(-1) )
+    {
+      log_error ("pcsc_get_status: pcsc-wrapper not running\n");
+      return sw;
+    }
 
-  /* Now send the open request. */
-  msgbuf[0] = 0x01; /* OPEN command. */
-  len = portstr? strlen (portstr):0;
+  msgbuf[0] = 0x05; /* RESET command. */
+  len = 0;
   msgbuf[1] = (len >> 24);
   msgbuf[2] = (len >> 16);
   msgbuf[3] = (len >>  8);
   msgbuf[4] = (len      );
-  if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
-       || (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
     {
-      log_error ("error sending PC/SC OPEN request: %s\n",
+      log_error ("error sending PC/SC RESET request: %s\n",
                  strerror (errno));
       goto command_failed;
     }
+
   /* Read the response. */
   if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
     {
-      log_error ("error receiving PC/SC OPEN response: %s\n",
+      log_error ("error receiving PC/SC RESET response: %s\n",
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
@@ -1457,47 +1402,41 @@ open_pcsc_reader (const char *portstr)
     {
       log_error ("PC/SC returned a too large ATR (len=%lx)\n",
                  (unsigned long)len);
+      sw = SW_HOST_GENERAL_ERROR;
       goto command_failed;
     }
-  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
   if (err)
     {
-      log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
+      log_error ("PC/SC RESET failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      /* If the error code is no smart card, we should not considere
+         this a major error and close the wrapper.  */
       sw = pcsc_error_to_sw (err);
+      if (err == PCSC_E_NO_SMARTCARD)
+        return sw;
       goto command_failed;
     }
 
-  slotp->last_status = 0;
-
-  /* The open request may return a zero for the ATR length to
+  /* The open function may return a zero for the ATR length to
      indicate that no card is present.  */
   n = len;
   if (n)
     {
       if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
         {
-          log_error ("error receiving PC/SC OPEN response: %s\n",
+          log_error ("error receiving PC/SC RESET response: %s\n",
                      i? strerror (errno) : "premature EOF");
           goto command_failed;
         }
-      /* If we got to here we know that a card is present
-         and usable.  Thus remember this.  */
-      slotp->last_status = (1|2|4| 0x8000);
     }
   slotp->atrlen = len;
 
-  reader_table[slot].close_reader = close_pcsc_reader;
-  reader_table[slot].reset_reader = reset_pcsc_reader;
-  reader_table[slot].get_status_reader = pcsc_get_status;
-  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
-  reader_table[slot].check_keypad = NULL;
-  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
-
   /* Read the status so that IS_T0 will be set. */
   pcsc_get_status (slot, &dummy_status);
 
-  dump_reader_status (slot);
-  return slot;
+  return 0;
 
  command_failed:
   close (slotp->pcsc.req_fd);
@@ -1507,21 +1446,42 @@ open_pcsc_reader (const char *portstr)
   kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
-  /* There is no way to return SW. */
-  return -1;
+  return sw;
+}
+#endif /* !NEED_PCSC_WRAPPER */
+
+
+/* Send an PC/SC reset command and return a status word on error or 0
+   on success. */
+static int
+reset_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return reset_pcsc_reader_wrapped (slot);
+#else
+  return reset_pcsc_reader_direct (slot);
+#endif
+}
+
 
-#else /*!NEED_PCSC_WRAPPER */
+/* Open the PC/SC reader without using the wrapper.  Returns -1 on
+   error or a slot number for the reader.  */
+#ifndef NEED_PCSC_WRAPPER
+static int
+open_pcsc_reader_direct (const char *portstr)
+{
   long err;
   int slot;
   char *list = NULL;
-  unsigned long nreader, listlen, atrlen;
+  unsigned long nreader, listlen;
   char *p;
-  unsigned long card_state, card_protocol;
 
   slot = new_reader_slot ();
   if (slot == -1)
     return -1;
 
+  /* Fixme: Allocating a context for each slot is not required.  One
+     global context should be sufficient.  */
   err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
                                 &reader_table[slot].pcsc.context);
   if (err)
@@ -1554,7 +1514,7 @@ open_pcsc_reader (const char *portstr)
       pcsc_release_context (reader_table[slot].pcsc.context);
       reader_table[slot].used = 0;
       xfree (list);
-      return -1 /*pcsc_error_to_sw (err)*/;
+      return -1;
     }
 
   listlen = nreader;
@@ -1577,78 +1537,255 @@ open_pcsc_reader (const char *portstr)
   reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
   if (!reader_table[slot].rdrname)
     {
-      log_error ("error allocating memory for reader name\n");
-      pcsc_release_context (reader_table[slot].pcsc.context);
-      reader_table[slot].used = 0;
-      return -1 /*SW_HOST_OUT_OF_CORE*/;
+      log_error ("error allocating memory for reader name\n");
+      pcsc_release_context (reader_table[slot].pcsc.context);
+      reader_table[slot].used = 0;
+      return -1;
+    }
+  strcpy (reader_table[slot].rdrname, portstr? portstr : list);
+  xfree (list);
+  list = NULL;
+
+  reader_table[slot].pcsc.card = 0;
+  reader_table[slot].atrlen = 0;
+  reader_table[slot].last_status = 0;
+
+  reader_table[slot].connect_card = connect_pcsc_card;
+  reader_table[slot].disconnect_card = disconnect_pcsc_card;
+  reader_table[slot].close_reader = close_pcsc_reader;
+  reader_table[slot].reset_reader = reset_pcsc_reader;
+  reader_table[slot].get_status_reader = pcsc_get_status;
+  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
+  dump_reader_status (slot);
+  return slot;
+}
+#endif /*!NEED_PCSC_WRAPPER */
+
+
+/* Open the PC/SC reader using the pcsc_wrapper program.  This is
+   needed to cope with different thread models and other peculiarities
+   of libpcsclite. */
+#ifdef NEED_PCSC_WRAPPER
+static int
+open_pcsc_reader_wrapped (const char *portstr)
+{
+  int slot;
+  reader_table_t slotp;
+  int fd, rp[2], wp[2];
+  int n, i;
+  pid_t pid;
+  size_t len;
+  unsigned char msgbuf[9];
+  int err;
+  unsigned int dummy_status;
+  int sw = SW_HOST_CARD_IO_ERROR;
+  /* Note that we use the constant and not the fucntion because this
+     code won't be be used under Windows.  */
+  const char *wrapperpgm = GNUPG_LIBEXECDIR "/gnupg-pcsc-wrapper";
+
+  if (access (wrapperpgm, X_OK))
+    {
+      log_error ("can't run PC/SC access module `%s': %s\n",
+                 wrapperpgm, strerror (errno));
+      return -1;
+    }
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
+
+  /* Fire up the PC/SCc wrapper.  We don't use any fork/exec code from
+     the common directy but implement it directly so that this file
+     may still be source copied. */
+
+  if (pipe (rp) == -1)
+    {
+      log_error ("error creating a pipe: %s\n", strerror (errno));
+      slotp->used = 0;
+      return -1;
+    }
+  if (pipe (wp) == -1)
+    {
+      log_error ("error creating a pipe: %s\n", strerror (errno));
+      close (rp[0]);
+      close (rp[1]);
+      slotp->used = 0;
+      return -1;
+    }
+
+  pid = fork ();
+  if (pid == -1)
+    {
+      log_error ("error forking process: %s\n", strerror (errno));
+      close (rp[0]);
+      close (rp[1]);
+      close (wp[0]);
+      close (wp[1]);
+      slotp->used = 0;
+      return -1;
+    }
+  slotp->pcsc.pid = pid;
+
+  if (!pid)
+    { /*
+         === Child ===
+       */
+
+      /* Double fork. */
+      pid = fork ();
+      if (pid == -1)
+        _exit (31);
+      if (pid)
+        _exit (0); /* Immediate exit this parent, so that the child
+                      gets cleaned up by the init process. */
+
+      /* Connect our pipes. */
+      if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
+        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
+      if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
+        log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
+
+      /* Send stderr to the bit bucket. */
+      fd = open ("/dev/null", O_WRONLY);
+      if (fd == -1)
+        log_fatal ("can't open `/dev/null': %s", strerror (errno));
+      if (fd != 2 && dup2 (fd, 2) == -1)
+        log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
+
+      /* Close all other files. */
+      close_all_fds (3, NULL);
+
+      execl (wrapperpgm,
+             "pcsc-wrapper",
+             "--",
+             "1", /* API version */
+             opt.pcsc_driver, /* Name of the PC/SC library. */
+              NULL);
+      _exit (31);
+    }
+
+  /*
+     === Parent ===
+   */
+  close (wp[0]);
+  close (rp[1]);
+  slotp->pcsc.req_fd = wp[1];
+  slotp->pcsc.rsp_fd = rp[0];
+
+  /* Wait for the intermediate child to terminate. */
+#ifdef USE_GNU_PTH
+#define WAIT pth_waitpid
+#else
+#define WAIT waitpid
+#endif
+  while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
+    ;
+#undef WAIT
+
+  /* Now send the open request. */
+  msgbuf[0] = 0x01; /* OPEN command. */
+  len = portstr? strlen (portstr):0;
+  msgbuf[1] = (len >> 24);
+  msgbuf[2] = (len >> 16);
+  msgbuf[3] = (len >>  8);
+  msgbuf[4] = (len      );
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
+       || (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
+    {
+      log_error ("error sending PC/SC OPEN request: %s\n",
+                 strerror (errno));
+      goto command_failed;
+    }
+  /* Read the response. */
+  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+    {
+      log_error ("error receiving PC/SC OPEN response: %s\n",
+                 i? strerror (errno) : "premature EOF");
+      goto command_failed;
+    }
+  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  if (msgbuf[0] != 0x81 || len < 4)
+    {
+      log_error ("invalid response header from PC/SC received\n");
+      goto command_failed;
+    }
+  len -= 4; /* Already read the error code. */
+  if (len > DIM (slotp->atr))
+    {
+      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
+                 (unsigned long)len);
+      goto command_failed;
     }
-  strcpy (reader_table[slot].rdrname, portstr? portstr : list);
-  xfree (list);
-  list = NULL;
-
-  err = pcsc_connect (reader_table[slot].pcsc.context,
-                      reader_table[slot].rdrname,
-                      PCSC_SHARE_EXCLUSIVE,
-                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
-                      &reader_table[slot].pcsc.card,
-                      &reader_table[slot].pcsc.protocol);
-  if (err == PCSC_E_NO_SMARTCARD) 
-    reader_table[slot].pcsc.card = 0;
-  else if (err)
+  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+                       | (msgbuf[7] << 8 ) | msgbuf[8]);
+  if (err)
     {
-      log_error ("pcsc_connect failed: %s (0x%lx)\n",
-                  pcsc_error_string (err), err);
-      pcsc_release_context (reader_table[slot].pcsc.context);
-      xfree (reader_table[slot].rdrname);
-      reader_table[slot].rdrname = NULL;
-      reader_table[slot].used = 0;
-      return -1 /*pcsc_error_to_sw (err)*/;
+      log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
+      sw = pcsc_error_to_sw (err);
+      goto command_failed;
     }
 
-  reader_table[slot].atrlen = 0;
-  reader_table[slot].last_status = 0;
-  if (!err)
-    {
-      char reader[250];
-      unsigned long readerlen;
+  slotp->last_status = 0;
 
-      atrlen = 32;
-      readerlen = sizeof reader -1 ;
-      err = pcsc_status (reader_table[slot].pcsc.card,
-                         reader, &readerlen,
-                         &card_state, &card_protocol,
-                         reader_table[slot].atr, &atrlen);
-      if (err)
-        log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
-                   pcsc_error_string (err), err, readerlen);
-      else
+  /* The open request may return a zero for the ATR length to
+     indicate that no card is present.  */
+  n = len;
+  if (n)
+    {
+      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
         {
-          if (atrlen >= DIM (reader_table[0].atr))
-            log_bug ("ATR returned by pcsc_status is too large\n");
-          reader_table[slot].atrlen = atrlen;
-          /* If we got to here we know that a card is present
-             and usable.  Thus remember this.  */
-          reader_table[slot].last_status = (1|2|4| 0x8000);
-          reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+          log_error ("error receiving PC/SC OPEN response: %s\n",
+                     i? strerror (errno) : "premature EOF");
+          goto command_failed;
         }
+      /* If we got to here we know that a card is present
+         and usable.  Thus remember this.  */
+      slotp->last_status = (  APDU_CARD_USABLE
+                            | APDU_CARD_PRESENT
+                            | APDU_CARD_ACTIVE);
     }
+  slotp->atrlen = len;
 
   reader_table[slot].close_reader = close_pcsc_reader;
   reader_table[slot].reset_reader = reset_pcsc_reader;
   reader_table[slot].get_status_reader = pcsc_get_status;
   reader_table[slot].send_apdu_reader = pcsc_send_apdu;
-  reader_table[slot].check_keypad = NULL;
   reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
 
-/*   log_debug ("state    from pcsc_status: 0x%lx\n", card_state); */
-/*   log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */
+  /* Read the status so that IS_T0 will be set. */
+  pcsc_get_status (slot, &dummy_status);
 
   dump_reader_status (slot);
   return slot;
-#endif /*!NEED_PCSC_WRAPPER */
+
+ command_failed:
+  close (slotp->pcsc.req_fd);
+  close (slotp->pcsc.rsp_fd);
+  slotp->pcsc.req_fd = -1;
+  slotp->pcsc.rsp_fd = -1;
+  kill (slotp->pcsc.pid, SIGTERM);
+  slotp->pcsc.pid = (pid_t)(-1);
+  slotp->used = 0;
+  /* There is no way to return SW. */
+  return -1;
+
 }
+#endif /*NEED_PCSC_WRAPPER*/
 
 
+static int
+open_pcsc_reader (const char *portstr)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return open_pcsc_reader_wrapped (portstr);
+#else
+  return open_pcsc_reader_direct (portstr);
+#endif
+}
+
 
 \f
 #ifdef HAVE_LIBUSB
@@ -1701,6 +1838,15 @@ reset_ccid_reader (int slot)
 
 
 static int
+set_progress_cb_ccid_reader (int slot, gcry_handler_progress_t cb, void *cb_arg)
+{
+  reader_table_t slotp = reader_table + slot;
+
+  return ccid_set_progress_cb (slotp->ccid.handle, cb, cb_arg);
+}
+
+
+static int
 get_status_ccid (int slot, unsigned int *status)
 {
   int rc;
@@ -1708,12 +1854,12 @@ get_status_ccid (int slot, unsigned int *status)
 
   rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
   if (rc)
-    return -1;
+    return rc;
 
   if (bits == 0)
-    *status = 1|2|4;
+    *status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE);
   else if (bits == 1)
-    *status = 2;
+    *status = APDU_CARD_PRESENT;
   else
     *status = 0;
 
@@ -1738,7 +1884,7 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
     return err;
 
   if (DBG_CARD_IO)
-    log_printhex ("  APDU_data:", apdu, apdulen);
+    log_printhex (" raw apdu:", apdu, apdulen);
 
   maxbuflen = *buflen;
   if (pininfo)
@@ -1809,7 +1955,9 @@ open_ccid_reader (const char *portstr)
     {
       /* If we got to here we know that a card is present
          and usable.  Thus remember this.  */
-      reader_table[slot].last_status = (1|2|4| 0x8000);
+      reader_table[slot].last_status = (APDU_CARD_USABLE
+                                        | APDU_CARD_PRESENT
+                                        | APDU_CARD_ACTIVE);
     }
 
   reader_table[slot].close_reader = close_ccid_reader;
@@ -1819,6 +1967,10 @@ open_ccid_reader (const char *portstr)
   reader_table[slot].send_apdu_reader = send_apdu_ccid;
   reader_table[slot].check_keypad = check_ccid_keypad;
   reader_table[slot].dump_status_reader = dump_ccid_reader_status;
+  reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader;
+  /* Our CCID reader code does not support T=0 at all, thus reset the
+     flag.  */
+  reader_table[slot].is_t0 = 0;
 
   dump_reader_status (slot);
   return slot;
@@ -1913,7 +2065,7 @@ reset_rapdu_reader (int slot)
       rapdu_msg_release (msg);
       return sw;
     }
-  if (msg->datalen >= DIM (slotp->atr))
+  if (msg->datalen > DIM (slotp->atr))
     {
       log_error ("ATR returned by the RAPDU layer is too large\n");
       rapdu_msg_release (msg);
@@ -2094,7 +2246,7 @@ open_rapdu_reader (int portno,
                  rapdu_strerror (msg->cmd));
       goto failure;
     }
-  if (msg->datalen >= DIM (slotp->atr))
+  if (msg->datalen > DIM (slotp->atr))
     {
       log_error ("ATR returned by the RAPDU layer is too large\n");
       goto failure;
@@ -2338,6 +2490,15 @@ apdu_open_remote_reader (const char *portstr,
                             writefnc, writefnc_value,
                             closefnc, closefnc_value);
 #else
+  (void)portstr;
+  (void)cookie;
+  (void)length;
+  (void)readfnc;
+  (void)readfnc_value;
+  (void)writefnc;
+  (void)writefnc_value;
+  (void)closefnc;
+  (void)closefnc_value;
 #ifdef _WIN32
   errno = ENOENT;
 #else
@@ -2351,21 +2512,58 @@ apdu_open_remote_reader (const char *portstr,
 int
 apdu_close_reader (int slot)
 {
+  int sw;
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
+  sw = apdu_disconnect (slot);
+  if (sw)
+    return sw;
   if (reader_table[slot].close_reader)
     return reader_table[slot].close_reader (slot);
   return SW_HOST_NOT_SUPPORTED;
 }
 
+
+/* Function suitable for a cleanup function to close all reader.  It
+   should not be used if the reader will be opened again.  The reason
+   for implementing this to properly close USB devices so that they
+   will startup the next time without error. */
+void
+apdu_prepare_exit (void)
+{
+  static int sentinel;
+  int slot;
+
+  if (!sentinel)
+    {
+      sentinel = 1;
+      for (slot = 0; slot < MAX_READER; slot++)
+        if (reader_table[slot].used)
+          {
+            apdu_disconnect (slot);
+            if (reader_table[slot].close_reader)
+              reader_table[slot].close_reader (slot);
+            reader_table[slot].used = 0;
+          }
+      sentinel = 0;
+    }
+}
+
+
 /* Shutdown a reader; that is basically the same as a close but keeps
-   the handle ready for later use. A apdu_reset_header should be used
-   to get it active again. */
+   the handle ready for later use. A apdu_reset_reader or apdu_connect
+   should be used to get it active again. */
 int
 apdu_shutdown_reader (int slot)
 {
+  int sw;
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
+  sw = apdu_disconnect (slot);
+  if (sw)
+    return sw;
   if (reader_table[slot].shutdown_reader)
     return reader_table[slot].shutdown_reader (slot);
   return SW_HOST_NOT_SUPPORTED;
@@ -2383,6 +2581,91 @@ apdu_enum_reader (int slot, int *used)
   return 0;
 }
 
+
+/* Connect a card.  This is used to power up the card and make sure
+   that an ATR is available.  */
+int
+apdu_connect (int slot)
+{
+  int sw;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  /* Only if the access method provides a connect function we use it.
+     If not, we expect that the card has been implicitly connected by
+     apdu_open_reader.  */
+  if (reader_table[slot].connect_card)
+    {
+      sw = lock_slot (slot);
+      if (!sw)
+        {
+          sw = reader_table[slot].connect_card (slot);
+          unlock_slot (slot);
+        }
+    }
+  else
+    sw = 0;
+  
+  /* We need to call apdu_get_status_internal, so that the last-status
+     machinery gets setup properly even if a card is inserted while
+     scdaemon is fired up and apdu_get_status has not yet been called.
+     Without that we would force a reset of the card with the next
+     call to apdu_get_status.  */
+  apdu_get_status_internal (slot, 1, 1, NULL, NULL);
+
+  return sw;
+}
+
+
+int
+apdu_disconnect (int slot)
+{
+  int sw;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  if (reader_table[slot].disconnect_card)
+    {
+      sw = lock_slot (slot);
+      if (!sw)
+        {
+          sw = reader_table[slot].disconnect_card (slot);
+          unlock_slot (slot);
+        }
+    }
+  else
+    sw = 0;
+  return sw;
+}
+
+
+/* Set the progress callback of SLOT to CB and its args to CB_ARG.  If
+   CB is NULL the progress callback is removed.  */
+int
+apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg)
+{
+  int sw;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  if (reader_table[slot].set_progress_cb)
+    {
+      sw = lock_slot (slot);
+      if (!sw)
+        {
+          sw = reader_table[slot].set_progress_cb (slot, cb, cb_arg);
+          unlock_slot (slot);
+        }
+    }
+  else
+    sw = 0;
+  return sw;
+}
+
+
 /* Do a reset for the card in reader at SLOT. */
 int
 apdu_reset (int slot)
@@ -2403,7 +2686,9 @@ apdu_reset (int slot)
     {
       /* If we got to here we know that a card is present
          and usable.  Thus remember this.  */
-      reader_table[slot].last_status = (1|2|4| 0x8000);
+      reader_table[slot].last_status = (APDU_CARD_USABLE
+                                        | APDU_CARD_PRESENT
+                                        | APDU_CARD_ACTIVE);
     }
 
   unlock_slot (slot);
@@ -2447,7 +2732,9 @@ apdu_activate (int slot)
                 {
                   /* If we got to here we know that a card is present
                      and usable.  Thus remember this.  */
-                  reader_table[slot].last_status = (1|2|4| 0x8000);
+                  reader_table[slot].last_status = (APDU_CARD_USABLE
+                                                    | APDU_CARD_PRESENT
+                                                    | APDU_CARD_ACTIVE);
                 }
             }
         }
@@ -2458,7 +2745,6 @@ apdu_activate (int slot)
 }
 
 
-
 unsigned char *
 apdu_get_atr (int slot, size_t *atrlen)
 {
@@ -2466,7 +2752,8 @@ apdu_get_atr (int slot, size_t *atrlen)
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return NULL;
-
+  if (!reader_table[slot].atrlen)
+    return NULL;
   buf = xtrymalloc (reader_table[slot].atrlen);
   if (!buf)
     return NULL;
@@ -2481,20 +2768,20 @@ apdu_get_atr (int slot, size_t *atrlen)
    card to become available if HANG is set to true. On success the
    bits in STATUS will be set to
 
-     bit 0 = card present and usable
-     bit 1 = card present
-     bit 2 = card active
-     bit 3 = card access locked [not yet implemented]
+     APDU_CARD_USABLE  (bit 0) = card present and usable
+     APDU_CARD_PRESENT (bit 1) = card present
+     APDU_CARD_ACTIVE  (bit 2) = card active
+                       (bit 3) = card access locked [not yet implemented]
 
-   For must application, testing bit 0 is sufficient.
+   For must applications, testing bit 0 is sufficient.
 
    CHANGED will receive the value of the counter tracking the number
    of card insertions.  This value may be used to detect a card
    change.
 */
-int
-apdu_get_status (int slot, int hang,
-                 unsigned int *status, unsigned int *changed)
+static int
+apdu_get_status_internal (int slot, int hang, int no_atr_reset,
+                          unsigned int *status, unsigned int *changed)
 {
   int sw;
   unsigned int s;
@@ -2516,17 +2803,18 @@ apdu_get_status (int slot, int hang,
       return sw;
     }
 
-  /* Keep track of changes.  We use one extra bit to test whether we
-     have checked the status at least once. */
-  if ( s != (reader_table[slot].last_status & 0x07ff)
-       || !reader_table[slot].last_status )
+  /* Keep track of changes.  */
+  if (s != reader_table[slot].last_status
+      || !reader_table[slot].any_status )
     {
       reader_table[slot].change_counter++;
-      /* Make sure that the ATR is invalid so that a reset will be by
-         activate.  */
-      reader_table[slot].atrlen = 0;
+      /* Make sure that the ATR is invalid so that a reset will be
+         triggered by apdu_activate.  */
+      if (!no_atr_reset)
+        reader_table[slot].atrlen = 0;
     }
-  reader_table[slot].last_status = (s | 0x8000);
+  reader_table[slot].any_status = 1;
+  reader_table[slot].last_status = s;
 
   if (status)
     *status = s;
@@ -2536,6 +2824,15 @@ apdu_get_status (int slot, int hang,
 }
 
 
+/* See above for a description.  */
+int
+apdu_get_status (int slot, int hang,
+                 unsigned int *status, unsigned int *changed)
+{
+  return apdu_get_status_internal (slot, hang, 0, status, changed);
+}
+
+
 /* Check whether the reader supports the ISO command code COMMAND on
    the keypad.  Return 0 on success.  For a description of the pin
    parameters, see ccid-driver.c */
@@ -2567,88 +2864,245 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   if (reader_table[slot].send_apdu_reader)
     return reader_table[slot].send_apdu_reader (slot,
                                                 apdu, apdulen,
-                                                buffer, buflen, pininfo);
+                                                buffer, buflen, 
+                                                pininfo);
   else
     return SW_HOST_NOT_SUPPORTED;
 }
 
 
-/* Core APDU trabceiver function. Parameters are described at
+/* Core APDU tranceiver function. Parameters are described at
    apdu_send_le with the exception of PININFO which indicates keypad
-   related operations if not NULL. */
+   related operations if not NULL.  If EXTENDED_MODE is not 0
+   command chaining or extended length will be used according to these
+   values:
+       n < 0 := Use command chaining with the data part limited to -n
+                in each chunk.  If -1 is used a default value is used.
+      n == 0 := No extended mode or command chaining.
+      n == 1 := Use extended length for input and output without a
+                length limit.
+       n > 1 := Use extended length with up to N bytes.
+
+*/
 static int
 send_le (int slot, int class, int ins, int p0, int p1,
          int lc, const char *data, int le,
          unsigned char **retbuf, size_t *retbuflen,
-         struct pininfo_s *pininfo)
+         struct pininfo_s *pininfo, int extended_mode)
 {
-#define RESULTLEN 256
-  unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
-                                         the driver. */
+#define SHORT_RESULT_BUFFER_SIZE 258
+  /* We allocate 8 extra bytes as a safety margin towards a driver bug.  */
+  unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10]; 
+  unsigned char *result_buffer = NULL;
+  size_t result_buffer_size;
+  unsigned char *result;
   size_t resultlen;
-  unsigned char apdu[5+256+1];
+  unsigned char short_apdu_buffer[5+256+1];
+  unsigned char *apdu_buffer = NULL;
+  size_t apdu_buffer_size;
+  unsigned char *apdu;
   size_t apdulen;
   int sw;
-  long rc; /* we need a long here due to PC/SC. */
+  long rc; /* We need a long here due to PC/SC. */
+  int did_exact_length_hack = 0;
+  int use_chaining = 0;
+  int use_extended_length = 0;
+  int lc_chunk;
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
   if (DBG_CARD_IO)
-    log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
-               class, ins, p0, p1, lc, le);
+    log_debug ("send apdu: c=%02X i=%02X p1=%02X p2=%02X lc=%d le=%d em=%d\n",
+               class, ins, p0, p1, lc, le, extended_mode);
 
   if (lc != -1 && (lc > 255 || lc < 0))
-    return SW_WRONG_LENGTH;
-  if (le != -1 && (le > 256 || le < 0))
-    return SW_WRONG_LENGTH;
+    {
+      /* Data does not fit into an APDU.  What we do now depends on
+         the EXTENDED_MODE parameter.  */
+      if (!extended_mode)
+        return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
+      else if (extended_mode > 0)
+        use_extended_length = 1;
+      else if (extended_mode < 0)
+        {
+          /* Send APDU using chaining mode.  */
+          if (lc > 16384)
+            return SW_WRONG_LENGTH;   /* Sanity check.  */
+          if ((class&0xf0) != 0)
+            return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0.  */ 
+          use_chaining = extended_mode == -1? 255 : -extended_mode;  
+          use_chaining &= 0xff;
+        }
+      else 
+        return SW_HOST_INV_VALUE;
+    }
+  else if (lc == -1 && extended_mode > 0)
+    use_extended_length = 1;
+    
+  if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0))
+    {
+      /* Expected Data does not fit into an APDU.  What we do now
+         depends on the EXTENDED_MODE parameter.  Note that a check
+         for command chaining does not make sense because we are
+         looking at Le.  */
+      if (!extended_mode)
+        return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
+      else if (use_extended_length)
+        ; /* We are already using extended length.  */
+      else if (extended_mode > 0)
+        use_extended_length = 1;
+      else 
+        return SW_HOST_INV_VALUE;
+    }
+
   if ((!data && lc != -1) || (data && lc == -1))
     return SW_HOST_INV_VALUE;
 
+  if (use_extended_length)
+    {
+      if (reader_table[slot].is_t0)
+        return SW_HOST_NOT_SUPPORTED;
+
+      /* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le.  */
+      apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2;
+      apdu_buffer = xtrymalloc (apdu_buffer_size + 10);
+      if (!apdu_buffer)
+        return SW_HOST_OUT_OF_CORE;
+      apdu = apdu_buffer;
+    }
+  else
+    {
+      apdu_buffer_size = sizeof short_apdu_buffer;
+      apdu = short_apdu_buffer;
+    }
+
+  if (use_extended_length && (le > 256 || le < 0))
+    {
+      result_buffer_size = le < 0? 4096 : le;
+      result_buffer = xtrymalloc (result_buffer_size + 10);
+      if (!result_buffer)
+        {
+          xfree (apdu_buffer);
+          return SW_HOST_OUT_OF_CORE;
+        }
+      result = result_buffer;
+    }
+  else
+    {
+      result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+      result = short_result_buffer;
+    }
+#undef SHORT_RESULT_BUFFER_SIZE
+
   if ((sw = lock_slot (slot)))
-    return sw;
+    {
+      xfree (apdu_buffer);
+      xfree (result_buffer);
+      return sw;
+    }
 
-  apdulen = 0;
-  apdu[apdulen++] = class;
-  apdu[apdulen++] = ins;
-  apdu[apdulen++] = p0;
-  apdu[apdulen++] = p1;
-  if (lc != -1)
-    {
-      apdu[apdulen++] = lc;
-      memcpy (apdu+apdulen, data, lc);
-      apdulen += lc;
-      /* T=0 does not allow the use of Lc together with Le; thus
-         disable Le in this case. */
-      if (reader_table[slot].is_t0)
-        le = -1;
-    }
-  if (le != -1)
-    apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
-  assert (sizeof (apdu) >= apdulen);
-  /* As safeguard don't pass any garbage from the stack to the driver. */
-  memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
-  resultlen = RESULTLEN;
-  rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
-  if (rc || resultlen < 2)
+  do
     {
-      /* We use log_info here so that in case of a transient error, and
-         if this module is used by gpg standalone, the error counter
-         isn't incremented.  */
-      log_info ("apdu_send_simple(%d) failed: %s\n",
-                slot, apdu_strerror (rc));
-      unlock_slot (slot);
-      return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+      if (use_extended_length)
+        {
+          use_chaining = 0;
+          apdulen = 0;
+          apdu[apdulen++] = class;
+          apdu[apdulen++] = ins;
+          apdu[apdulen++] = p0;
+          apdu[apdulen++] = p1;
+          apdu[apdulen++] = 0;  /* Z byte: Extended length marker.  */
+          if (lc >= 0)
+            {
+              apdu[apdulen++] = ((lc >> 8) & 0xff);
+              apdu[apdulen++] = (lc & 0xff);
+              memcpy (apdu+apdulen, data, lc);
+              data += lc;
+              apdulen += lc;
+            }
+          if (le != -1)
+            {
+              apdu[apdulen++] = ((le >> 8) & 0xff); 
+              apdu[apdulen++] = (le & 0xff); 
+            }
+        }
+      else
+        {
+          apdulen = 0;
+          apdu[apdulen] = class;
+          if (use_chaining && lc > 255)
+            {
+              apdu[apdulen] |= 0x10;
+              assert (use_chaining < 256);
+              lc_chunk = use_chaining;
+              lc -= use_chaining;
+            }
+          else
+            {
+              use_chaining = 0;
+              lc_chunk = lc;
+            }
+          apdulen++;
+          apdu[apdulen++] = ins;
+          apdu[apdulen++] = p0;
+          apdu[apdulen++] = p1;
+          if (lc_chunk != -1)
+            {
+              apdu[apdulen++] = lc_chunk;
+              memcpy (apdu+apdulen, data, lc_chunk);
+              data += lc_chunk;
+              apdulen += lc_chunk;
+              /* T=0 does not allow the use of Lc together with Le;
+                 thus disable Le in this case.  */
+              if (reader_table[slot].is_t0)
+                le = -1;
+            }
+          if (le != -1 && !use_chaining)
+            apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */
+        }
+
+    exact_length_hack:
+      /* As a safeguard don't pass any garbage to the driver.  */
+      assert (apdulen <= apdu_buffer_size);
+      memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
+      resultlen = result_buffer_size;
+      rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
+      if (rc || resultlen < 2)
+        {
+          log_info ("apdu_send_simple(%d) failed: %s\n",
+                    slot, apdu_strerror (rc));
+          unlock_slot (slot);
+          xfree (apdu_buffer);
+          xfree (result_buffer);
+          return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+        }
+      sw = (result[resultlen-2] << 8) | result[resultlen-1];
+      if (!use_extended_length 
+          && !did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
+        {
+          apdu[apdulen-1] = (sw & 0x00ff);
+          did_exact_length_hack = 1;
+          goto exact_length_hack;
+        }
     }
-  sw = (result[resultlen-2] << 8) | result[resultlen-1];
-  /* store away the returned data but strip the statusword. */
+  while (use_chaining && sw == SW_SUCCESS);
+
+  if (apdu_buffer)
+    {
+      xfree (apdu_buffer);
+      apdu_buffer = NULL;
+      apdu_buffer_size = 0;
+    }
+  
+  /* Store away the returned data but strip the statusword. */
   resultlen -= 2;
   if (DBG_CARD_IO)
     {
       log_debug (" response: sw=%04X  datalen=%d\n",
                  sw, (unsigned int)resultlen);
       if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
-        log_printhex ("     dump: ", result, resultlen);
+        log_printhex ("    dump: ", result, resultlen);
     }
 
   if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
@@ -2659,6 +3113,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
           if (!*retbuf)
             {
               unlock_slot (slot);
+              xfree (result_buffer);
               return SW_HOST_OUT_OF_CORE;
             }
           *retbuflen = resultlen;
@@ -2678,6 +3133,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
           if (!*retbuf)
             {
               unlock_slot (slot);
+              xfree (result_buffer);
               return SW_HOST_OUT_OF_CORE;
             }
           assert (resultlen < bufsize);
@@ -2692,20 +3148,24 @@ send_le (int slot, int class, int ins, int p0, int p1,
           if (DBG_CARD_IO)
             log_debug ("apdu_send_simple(%d): %d more bytes available\n",
                        slot, len);
+          apdu_buffer_size = sizeof short_apdu_buffer;
+          apdu = short_apdu_buffer;
           apdulen = 0;
           apdu[apdulen++] = class;
           apdu[apdulen++] = 0xC0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = len;
-          memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
-          resultlen = RESULTLEN;
+          assert (apdulen <= apdu_buffer_size);
+          memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
+          resultlen = result_buffer_size;
           rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_simple(%d) for get response failed: %s\n",
                          slot, apdu_strerror (rc));
               unlock_slot (slot);
+              xfree (result_buffer);
               return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
             }
           sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -2731,6 +3191,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
                       if (!tmp)
                         {
                           unlock_slot (slot);
+                          xfree (result_buffer);
                           return SW_HOST_OUT_OF_CORE;
                         }
                       p = tmp + (p - *retbuf);
@@ -2757,63 +3218,70 @@ send_le (int slot, int class, int ins, int p0, int p1,
     }
 
   unlock_slot (slot);
+  xfree (result_buffer);
 
   if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
     log_printhex ("      dump: ", *retbuf, *retbuflen);
 
   return sw;
-#undef RESULTLEN
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA, LE.  A value of -1
    for LC won't sent this field and the data field; in this case DATA
-   must also be passed as NULL.  The return value is the status word
-   or -1 for an invalid SLOT or other non card related error.  If
-   RETBUF is not NULL, it will receive an allocated buffer with the
-   returned data.  The length of that data will be put into
-   *RETBUFLEN.  The caller is reponsible for releasing the buffer even
-   in case of errors.  */
+   must also be passed as NULL.  If EXTENDED_MODE is not 0 command
+   chaining or extended length will be used; see send_le for details.
+   The return value is the status word or -1 for an invalid SLOT or
+   other non card related error.  If RETBUF is not NULL, it will
+   receive an allocated buffer with the returned data.  The length of
+   that data will be put into *RETBUFLEN.  The caller is reponsible
+   for releasing the buffer even in case of errors.  */
 int
-apdu_send_le(int slot, int class, int ins, int p0, int p1,
+apdu_send_le(int slot, int extended_mode, 
+             int class, int ins, int p0, int p1,
              int lc, const char *data, int le,
              unsigned char **retbuf, size_t *retbuflen)
 {
   return send_le (slot, class, ins, p0, p1,
                   lc, data, le,
                   retbuf, retbuflen,
-                  NULL);
+                  NULL, extended_mode);
 }
 
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
    LC won't sent this field and the data field; in this case DATA must
-   also be passed as NULL. The return value is the status word or -1
-   for an invalid SLOT or other non card related error.  If RETBUF is
-   not NULL, it will receive an allocated buffer with the returned
-   data.  The length of that data will be put into *RETBUFLEN.  The
-   caller is reponsible for releasing the buffer even in case of
-   errors.  */
+   also be passed as NULL.  If EXTENDED_MODE is not 0 command chaining
+   or extended length will be used; see send_le for details.  The
+   return value is the status word or -1 for an invalid SLOT or other
+   non card related error.  If RETBUF is not NULL, it will receive an
+   allocated buffer with the returned data.  The length of that data
+   will be put into *RETBUFLEN.  The caller is reponsible for
+   releasing the buffer even in case of errors.  */
 int
-apdu_send (int slot, int class, int ins, int p0, int p1,
+apdu_send (int slot, int extended_mode,
+           int class, int ins, int p0, int p1,
            int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
 {
   return send_le (slot, class, ins, p0, p1, lc, data, 256,
-                  retbuf, retbuflen, NULL);
+                  retbuf, retbuflen, NULL, extended_mode);
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
    LC won't sent this field and the data field; in this case DATA must
-   also be passed as NULL. The return value is the status word or -1
-   for an invalid SLOT or other non card related error.  No data will be
-   returned. */
+   also be passed as NULL.  If EXTENDED_MODE is not 0 command chaining
+   or extended length will be used; see send_le for details.  The
+   return value is the status word or -1 for an invalid SLOT or other
+   non card related error.  No data will be returned.  */
 int
-apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+apdu_send_simple (int slot, int extended_mode,
+                  int class, int ins, int p0, int p1,
                   int lc, const char *data)
 {
-  return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL);
+  return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL, 
+                  extended_mode);
 }
 
 
@@ -2831,29 +3299,36 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
   pininfo.maxlen = pinlen_max;
   pininfo.padlen = pin_padlen;
   return send_le (slot, class, ins, p0, p1, lc, data, -1,
-                  NULL, NULL, &pininfo);
+                  NULL, NULL, &pininfo, 0);
 }
 
 
 /* This is a more generic version of the apdu sending routine.  It
    takes an already formatted APDU in APDUDATA or length APDUDATALEN
-   and returns the with the APDU including the status word.  With
+   and returns with an APDU including the status word.  With
    HANDLE_MORE set to true this function will handle the MORE DATA
    status and return all APDUs concatenated with one status word at
-   the end.  The function does not return a regular status word but 0
-   on success.  If the slot is locked, the fucntion returns
-   immediately.*/
+   the end.  If EXTENDED_LENGTH is != 0 extended lengths are allowed
+   with a max. result data length of EXTENDED_LENGTH bytes.  The
+   function does not return a regular status word but 0 on success.
+   If the slot is locked, the function returns immediately with an
+   error.  */
 int
-apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
+apdu_send_direct (int slot, size_t extended_length,
+                  const unsigned char *apdudata, size_t apdudatalen,
                   int handle_more,
                   unsigned char **retbuf, size_t *retbuflen)
 {
-#define RESULTLEN 256
-  unsigned char apdu[5+256+1];
-  size_t apdulen;
-  unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
-                                         the driver. */
+#define SHORT_RESULT_BUFFER_SIZE 258
+  unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10]; 
+  unsigned char *result_buffer = NULL;
+  size_t result_buffer_size;
+  unsigned char *result;
   size_t resultlen;
+  unsigned char short_apdu_buffer[5+256+10];
+  unsigned char *apdu_buffer = NULL;
+  unsigned char *apdu;
+  size_t apdulen;
   int sw;
   long rc; /* we need a long here due to PC/SC. */
   int class;
@@ -2861,23 +3336,59 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
-  if ((sw = trylock_slot (slot)))
-    return sw;
+  if (apdudatalen > 65535)
+    return SW_HOST_INV_VALUE;
 
-  /* We simply trunctate a too long APDU.  */
-  if (apdudatalen > sizeof apdu)
-    apdudatalen = sizeof apdu;
+  if (apdudatalen > sizeof short_apdu_buffer - 5)
+    {
+      apdu_buffer = xtrymalloc (apdudatalen + 5);
+      if (!apdu_buffer)
+        return SW_HOST_OUT_OF_CORE;
+      apdu = apdu_buffer;
+    }
+  else
+    {
+      apdu = short_apdu_buffer;
+    }
   apdulen = apdudatalen;
   memcpy (apdu, apdudata, apdudatalen);
   class = apdulen? *apdu : 0;
 
-  resultlen = RESULTLEN;
+  if (extended_length >= 256 && extended_length <= 65536)
+    {
+      result_buffer_size = extended_length;
+      result_buffer = xtrymalloc (result_buffer_size + 10);
+      if (!result_buffer)
+        {
+          xfree (apdu_buffer);
+          return SW_HOST_OUT_OF_CORE;
+        }
+      result = result_buffer;
+    }
+  else
+    {
+      result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+      result = short_result_buffer;
+    }
+#undef SHORT_RESULT_BUFFER_SIZE
+
+  if ((sw = trylock_slot (slot)))
+    {
+      xfree (apdu_buffer);
+      xfree (result_buffer);
+      return sw;
+    }
+
+  resultlen = result_buffer_size;
   rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+  xfree (apdu_buffer);
+  apdu_buffer = NULL;
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_direct(%d) failed: %s\n",
                  slot, apdu_strerror (rc));
       unlock_slot (slot);
+      xfree (result_buffer);
       return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
     }
   sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -2904,6 +3415,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           if (!*retbuf)
             {
               unlock_slot (slot);
+              xfree (result_buffer);
               return SW_HOST_OUT_OF_CORE;
             }
           assert (resultlen < bufsize);
@@ -2918,20 +3430,22 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           if (DBG_CARD_IO)
             log_debug ("apdu_send_direct(%d): %d more bytes available\n",
                        slot, len);
+          apdu = short_apdu_buffer;
           apdulen = 0;
           apdu[apdulen++] = class;
           apdu[apdulen++] = 0xC0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = len;
-          memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
-          resultlen = RESULTLEN;
+          memset (apdu+apdulen, 0, sizeof (short_apdu_buffer) - apdulen);
+          resultlen = result_buffer_size;
           rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_direct(%d) for get response failed: %s\n",
                          slot, apdu_strerror (rc));
               unlock_slot (slot);
+              xfree (result_buffer);
               return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
             }
           sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -2957,6 +3471,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
                       if (!tmp)
                         {
                           unlock_slot (slot);
+                          xfree (result_buffer);
                           return SW_HOST_OUT_OF_CORE;
                         }
                       p = tmp + (p - *retbuf);
@@ -2967,7 +3482,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
                 }
             }
           else
-            log_info ("apdu_send_sdirect(%d) "
+            log_info ("apdu_send_direct(%d) "
                       "got unexpected status %04X from get response\n",
                       slot, sw);
         }
@@ -2989,6 +3504,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           if (!*retbuf)
             {
               unlock_slot (slot);
+              xfree (result_buffer);
               return SW_HOST_OUT_OF_CORE;
             }
           *retbuflen = resultlen;
@@ -2997,9 +3513,10 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
     }
 
   unlock_slot (slot);
+  xfree (result_buffer);
 
-  /* Append the status word - we reseved the two extra bytes while
-     allocating the buffer. */
+  /* Append the status word.  Note that we reserved the two extra
+     bytes while allocating the buffer.  */
   if (retbuf)
     {
       (*retbuf)[(*retbuflen)++] = (sw >> 8);
@@ -3010,5 +3527,4 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
     log_printhex ("      dump: ", *retbuf, *retbuflen);
 
   return 0;
-#undef RESULTLEN
 }
index 92d0738..c47dea8 100644 (file)
@@ -1,5 +1,5 @@
 /* apdu.h - ISO 7816 APDU functions and low level I/O
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -29,8 +29,11 @@ enum {
   SW_MORE_DATA      = 0x6100, /* Note: that the low byte must be
                                  masked of.*/
   SW_EOF_REACHED    = 0x6282,
+  SW_TERM_STATE     = 0x6285, /* Selected file is in termination state.  */
   SW_EEPROM_FAILURE = 0x6581,
   SW_WRONG_LENGTH   = 0x6700,
+  SW_SM_NOT_SUP     = 0x6882, /* Secure Messaging is not supported.  */
+  SW_CC_NOT_SUP     = 0x6884, /* Command Chaining is not supported.  */
   SW_CHV_WRONG      = 0x6982,
   SW_CHV_BLOCKED    = 0x6983,
   SW_USE_CONDITIONS = 0x6985,
@@ -38,6 +41,7 @@ enum {
   SW_NOT_SUPPORTED  = 0x6a81,
   SW_FILE_NOT_FOUND = 0x6a82,
   SW_RECORD_NOT_FOUND = 0x6a83,
+  SW_BAD_LC         = 0x6a87, /* Lc does not match command or p1/p2.  */
   SW_REF_NOT_FOUND  = 0x6a88,
   SW_BAD_P0_P1      = 0x6b00,
   SW_EXACT_LENGTH   = 0x6c00,
@@ -62,13 +66,20 @@ enum {
   SW_HOST_GENERAL_ERROR = 0x1000b,
   SW_HOST_NO_READER     = 0x1000c,
   SW_HOST_ABORTED       = 0x1000d,
-  SW_HOST_NO_KEYPAD     = 0x1000e
+  SW_HOST_NO_KEYPAD     = 0x1000e,
+  SW_HOST_ALREADY_CONNECTED = 0x1000f
 };
 
 
 #define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH)
 
 
+/* Bit flags for the card status.  */
+#define APDU_CARD_USABLE   (1)    /* Card is present and ready for use.  */
+#define APDU_CARD_PRESENT  (2)    /* Card is just present.  */
+#define APDU_CARD_ACTIVE   (4)    /* Card is active.  */
+
+
 /* Note , that apdu_open_reader returns no status word but -1 on error. */
 int apdu_open_reader (const char *portstr);
 int apdu_open_remote_reader (const char *portstr,
@@ -83,13 +94,19 @@ int apdu_open_remote_reader (const char *portstr,
                              void *closefnc_value);
 int apdu_shutdown_reader (int slot);
 int apdu_close_reader (int slot);
+void apdu_prepare_exit (void);
 int apdu_enum_reader (int slot, int *used);
 unsigned char *apdu_get_atr (int slot, size_t *atrlen);
 
 const char *apdu_strerror (int rc);
 
 
-/* These apdu functions do return status words. */
+/* These APDU functions return status words. */
+
+int apdu_connect (int slot);
+int apdu_disconnect (int slot);
+
+int apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg);
 
 int apdu_activate (int slot);
 int apdu_reset (int slot);
@@ -97,19 +114,21 @@ int apdu_get_status (int slot, int hang,
                      unsigned int *status, unsigned int *changed);
 int apdu_check_keypad (int slot, int command, int pin_mode,
                        int pinlen_min, int pinlen_max, int pin_padlen);
-int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+int apdu_send_simple (int slot, int extended_mode,
+                      int class, int ins, int p0, int p1,
                       int lc, const char *data);
 int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
                          int lc, const char *data,  
                          int pin_mode,
                          int pinlen_min, int pinlen_max, int pin_padlen);
-int apdu_send (int slot, int class, int ins, int p0, int p1,
-               int lc, const char *data,
+int apdu_send (int slot, int extended_mode, 
+               int class, int ins, int p0, int p1, int lc, const char *data,
                unsigned char **retbuf, size_t *retbuflen);
-int apdu_send_le (int slot, int class, int ins, int p0, int p1,
+int apdu_send_le (int slot, int extended_mode, 
+                  int class, int ins, int p0, int p1,
                   int lc, const char *data, int le,
                   unsigned char **retbuf, size_t *retbuflen);
-int apdu_send_direct (int slot,
+int apdu_send_direct (int slot, size_t extended_length,
                       const unsigned char *apdudata, size_t apdudatalen,
                       int handle_more,
                       unsigned char **retbuf, size_t *retbuflen);
index 0e28487..4b2e13e 100644 (file)
@@ -1,5 +1,5 @@
 /* app-common.h - Common declarations for all card applications
- *     Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2005, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #endif
 
 
+#define APP_CHANGE_FLAG_RESET    1
+#define APP_CHANGE_FLAG_NULLPIN  2
+
+
 struct app_local_s;  /* Defined by all app-*.c.  */
 
 struct app_ctx_s {
-  int initialized;  /* The application has been initialied and the
-                       function pointers may be used.  Note that for
-                       unsupported operations the particular
-                       function pointer is set to NULL */
-  int slot;         /* Used reader. */
+  /* Number of connections currently using this application context.
+     If this is not 0 the application has been initialized and the
+     function pointers may be used.  Note that for unsupported
+     operations the particular function pointer is set to NULL */
+  unsigned int ref_count;
+
+  /* Flag indicating that a reset has been done for that application
+     and that this context is merely lingering and just should not be
+     reused.  */
+  int no_reuse;            
+
+  /* Used reader slot. */
+  int slot;     
 
   /* If this is used by GnuPG 1.4 we need to know the assuan context
      in case we need to divert the operation to an already running
@@ -59,7 +71,7 @@ struct app_ctx_s {
   struct app_local_s *app_local;  /* Local to the application. */
   struct {
     void (*deinit) (app_t app);
-    gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl);
+    gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags);
     gpg_error_t (*readcert) (app_t app, const char *certid,
                      unsigned char **cert, size_t *certlen);
     gpg_error_t (*readkey) (app_t app, const char *certid,
@@ -85,17 +97,23 @@ struct app_ctx_s {
                      void *pincb_arg,
                      const void *indata, size_t indatalen,
                      unsigned char **outdata, size_t *outdatalen);
+    gpg_error_t (*writecert) (app_t app, ctrl_t ctrl,
+                              const char *certid,
+                              gpg_error_t (*pincb)(void*,const char *,char **),
+                              void *pincb_arg,
+                              const unsigned char *data, size_t datalen);
     gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
-                             const char *certid, unsigned int flags,
+                             const char *keyid, unsigned int flags,
                              gpg_error_t (*pincb)(void*,const char *,char **),
                              void *pincb_arg,
                              const unsigned char *pk, size_t pklen);
     gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
-                   const char *keynostr, unsigned int flags,
-                   gpg_error_t (*pincb)(void*, const char *, char **),
-                   void *pincb_arg);
+                           const char *keynostr, unsigned int flags,
+                           time_t createtime,
+                           gpg_error_t (*pincb)(void*, const char *, char **),
+                           void *pincb_arg);
     gpg_error_t (*change_pin) (app_t app, ctrl_t ctrl,
-                       const char *chvnostr, int reset_mode,
+                       const char *chvnostr, unsigned int flags,
                        gpg_error_t (*pincb)(void*, const char *, char **),
                        void *pincb_arg);
     gpg_error_t (*check_pin) (app_t app, const char *keyidstr,
@@ -117,17 +135,23 @@ gpg_error_t app_openpgp_storekey (app_t app, int keyno,
                           void *pincb_arg);
 #else
 /*-- app-help.c --*/
+unsigned int app_help_count_bits (const unsigned char *a, size_t len);
 gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
 size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
 
 
 /*-- app.c --*/
+void app_dump_state (void);
+void application_notify_card_reset (int slot);
+gpg_error_t check_application_conflict (ctrl_t ctrl, const char *name);
 gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name,
                                 app_t *r_app);
+char *get_supported_applications (void);
 void release_application (app_t app);
 gpg_error_t app_munge_serialno (app_t app);
 gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
-gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl);
+gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl, 
+                                    unsigned int flags);
 gpg_error_t app_readcert (app_t app, const char *certid,
                   unsigned char **cert, size_t *certlen);
 gpg_error_t app_readkey (app_t app, const char *keyid,
@@ -152,15 +176,21 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr,
                   void *pincb_arg,
                   const void *indata, size_t indatalen,
                   unsigned char **outdata, size_t *outdatalen );
+gpg_error_t app_writecert (app_t app, ctrl_t ctrl,
+                           const char *certidstr,
+                           gpg_error_t (*pincb)(void*, const char *, char **),
+                           void *pincb_arg,
+                           const unsigned char *keydata, size_t keydatalen);
 gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
                           const char *keyidstr, unsigned int flags,
                           gpg_error_t (*pincb)(void*, const char *, char **),
                           void *pincb_arg,
                           const unsigned char *keydata, size_t keydatalen);
 gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
-                const char *keynostr, unsigned int flags,
-                gpg_error_t (*pincb)(void*, const char *, char **),
-                void *pincb_arg);
+                        const char *keynostr, unsigned int flags,
+                        time_t createtime,
+                        gpg_error_t (*pincb)(void*, const char *, char **),
+                        void *pincb_arg);
 gpg_error_t app_get_challenge (app_t app, size_t nbytes,
                                unsigned char *buffer);
 gpg_error_t app_change_pin (app_t app, ctrl_t ctrl,
@@ -184,6 +214,9 @@ gpg_error_t app_select_dinsig (app_t app);
 /*-- app-p15.c --*/
 gpg_error_t app_select_p15 (app_t app);
 
+/*-- app-geldkarte.c --*/
+gpg_error_t app_select_geldkarte (app_t app);
+
 
 #endif
 
index 9c949f3..d2b2bdd 100644 (file)
@@ -1,5 +1,6 @@
 /* app-openpgp.c - The OpenPGP card application.
- *     Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2005, 2007, 2008,
+ *               2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * $Id$
  */
 
+/* Some notes:
+
+   CHV means Card Holder Verification and is nothing else than a PIN
+   or password.  That term seems to have been used originally with GSM
+   cards.  Version v2 of the specs changes the term to the clearer
+   term PW for password.  We use the terms here interchangeable
+   because we do not want to change existing strings i18n wise.
+
+   Version 2 of the specs also drops the separate PW2 which was
+   required in v1 due to ISO requirements.  It is now possible to have
+   one physical PW but two reference to it so that they can be
+   individually be verified (e.g. to implement a forced verification
+   for one key).  Thus you will noticed the use of PW2 with the verify
+   command but not with change_reference_data because the latter
+   operates directly on the physical PW.
+
+   The Reset Code (RC) as implemented by v2 cards uses the same error
+   counter as the PW2 of v1 cards.  By default no RC is set and thus
+   that error counter is set to 0.  After setting the RC the error
+   counter will be initialized to 3.
+
+ */
+
 #include <config.h>
 #include <errno.h>
 #include <stdio.h>
 #include "tlv.h"
 
 
+/* A table describing the DOs of the card.  */
 static struct {
   int tag;
   int constructed;
   int get_from;  /* Constructed DO with this DO or 0 for direct access. */
-  int binary;
-  int dont_cache;
-  int flush_on_error;
-  int get_immediate_in_v11; /* Enable a hack to bypass the cache of
-                               this data object if it is used in 1.1
-                               and later versions of the card.  This
-                               does not work with composite DO and is
-                               currently only useful for the CHV
-                               status bytes. */
+  int binary:1;
+  int dont_cache:1;
+  int flush_on_error:1;
+  int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of
+                                 this data object if it is used in 1.1
+                                 and later versions of the card.  This
+                                 does not work with composite DO and
+                                 is currently only useful for the CHV
+                                 status bytes. */
+  int try_extlen:1;           /* Large object; try to use an extended
+                                 length APDU.  */
   char *desc;
 } data_objects[] = {
-  { 0x005E, 0,    0, 1, 0, 0, 0, "Login Data" },
-  { 0x5F50, 0,    0, 0, 0, 0, 0, "URL" },
-  { 0x0065, 1,    0, 1, 0, 0, 0, "Cardholder Related Data"},
-  { 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" },
-  { 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" },
-  { 0x5F35, 0, 0x65, 0, 0, 0, 0, "Sex" },
-  { 0x006E, 1,    0, 1, 0, 0, 0, "Application Related Data" },
-  { 0x004F, 0, 0x6E, 1, 0, 0, 0, "AID" },
-  { 0x0073, 1,    0, 1, 0, 0, 0, "Discretionary Data Objects" },
-  { 0x0047, 0, 0x6E, 1, 1, 0, 0, "Card Capabilities" },
-  { 0x00C0, 0, 0x6E, 1, 1, 0, 0, "Extended Card Capabilities" },
-  { 0x00C1, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Signature" },
-  { 0x00C2, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Decryption" },
-  { 0x00C3, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Authentication" },
-  { 0x00C4, 0, 0x6E, 1, 0, 1, 1, "CHV Status Bytes" },
-  { 0x00C5, 0, 0x6E, 1, 0, 0, 0, "Fingerprints" },
-  { 0x00C6, 0, 0x6E, 1, 0, 0, 0, "CA Fingerprints" },
-  { 0x00CD, 0, 0x6E, 1, 0, 0, 0, "Generation time" },
-  { 0x007A, 1,    0, 1, 0, 0, 0, "Security Support Template" },
-  { 0x0093, 0, 0x7A, 1, 1, 0, 0, "Digital Signature Counter" },
-  { 0x0101, 0,    0, 0, 0, 0, 0, "Private DO 1"},
-  { 0x0102, 0,    0, 0, 0, 0, 0, "Private DO 2"},
-  { 0x0103, 0,    0, 0, 0, 0, 0, "Private DO 3"},
-  { 0x0104, 0,    0, 0, 0, 0, 0, "Private DO 4"},
+  { 0x005E, 0,    0, 1, 0, 0, 0, 0, "Login Data" },
+  { 0x5F50, 0,    0, 0, 0, 0, 0, 0, "URL" },
+  { 0x5F52, 0,    0, 1, 0, 0, 0, 0, "Historical Bytes" },
+  { 0x0065, 1,    0, 1, 0, 0, 0, 0, "Cardholder Related Data"},
+  { 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" },
+  { 0x5F2D, 0, 0x65, 0, 0, 0, 0, 0, "Language preferences" },
+  { 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Sex" },
+  { 0x006E, 1,    0, 1, 0, 0, 0, 0, "Application Related Data" },
+  { 0x004F, 0, 0x6E, 1, 0, 0, 0, 0, "AID" },
+  { 0x0073, 1,    0, 1, 0, 0, 0, 0, "Discretionary Data Objects" },
+  { 0x0047, 0, 0x6E, 1, 1, 0, 0, 0, "Card Capabilities" },
+  { 0x00C0, 0, 0x6E, 1, 1, 0, 0, 0, "Extended Card Capabilities" },
+  { 0x00C1, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Signature" },
+  { 0x00C2, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Decryption" },
+  { 0x00C3, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Authentication" },
+  { 0x00C4, 0, 0x6E, 1, 0, 1, 1, 0, "CHV Status Bytes" },
+  { 0x00C5, 0, 0x6E, 1, 0, 0, 0, 0, "Fingerprints" },
+  { 0x00C6, 0, 0x6E, 1, 0, 0, 0, 0, "CA Fingerprints" },
+  { 0x00CD, 0, 0x6E, 1, 0, 0, 0, 0, "Generation time" },
+  { 0x007A, 1,    0, 1, 0, 0, 0, 0, "Security Support Template" },
+  { 0x0093, 0, 0x7A, 1, 1, 0, 0, 0, "Digital Signature Counter" },
+  { 0x0101, 0,    0, 0, 0, 0, 0, 0, "Private DO 1"},
+  { 0x0102, 0,    0, 0, 0, 0, 0, 0, "Private DO 2"},
+  { 0x0103, 0,    0, 0, 0, 0, 0, 0, "Private DO 3"},
+  { 0x0104, 0,    0, 0, 0, 0, 0, 0, "Private DO 4"},
+  { 0x7F21, 1,    0, 1, 0, 0, 0, 1, "Cardholder certificate"},
   { 0 }
 };
 
 
+/* The format of RSA private keys.  */
+typedef enum
+  { 
+    RSA_UNKNOWN_FMT,
+    RSA_STD,
+    RSA_STD_N,
+    RSA_CRT,
+    RSA_CRT_N
+  } 
+rsa_key_format_t;
+
+
 /* One cache item for DOs.  */
 struct cache_s {
   struct cache_s *next;
@@ -111,19 +152,36 @@ struct app_local_s {
                            encoded S-expression encoding a public
                            key. Might be NULL if key is not
                            available.  */
-    size_t keylen;      /* The length of the above S-expression.  Thsi
-                           is usullay only required for corss checks
+    size_t keylen;      /* The length of the above S-expression.  This
+                           is usually only required for cross checks
                            because the length of an S-expression is
                            implicitly available.  */
   } pk[3];
 
-  /* Keep track of card capabilities.  */
+  unsigned char status_indicator; /* The card status indicator.  */
+
+  /* Keep track of the ISO card capabilities.  */
+  struct
+  {
+    unsigned int cmd_chaining:1;  /* Command chaining is supported.  */
+    unsigned int ext_lc_le:1;     /* Extended Lc and Le are supported.  */
+  } cardcap;
+
+  /* Keep track of extended card capabilities.  */
   struct 
   {
+    unsigned int is_v2:1;              /* This is a v2.0 compatible card.  */
     unsigned int get_challenge:1;
     unsigned int key_import:1;
     unsigned int change_force_chv:1;
     unsigned int private_dos:1;
+    unsigned int algo_attr_change:1;   /* Algorithm attributes changeable.  */
+    unsigned int sm_supported:1;       /* Secure Messaging is supported.  */
+    unsigned int sm_aes128:1;          /* Use AES-128 for SM.  */
+    unsigned int max_certlen_3:16;
+    unsigned int max_get_challenge:16; /* Maximum size for get_challenge.  */
+    unsigned int max_cmd_data:16;      /* Maximum data size for a command.  */
+    unsigned int max_rsp_data:16;      /* Maximum size of a response.  */
   } extcap;
 
   /* Flags used to control the application.  */
@@ -132,6 +190,16 @@ struct app_local_s {
     unsigned int no_sync:1;   /* Do not sync CHV1 and CHV2 */
     unsigned int def_chv2:1;  /* Use 123456 for CHV2.  */
   } flags;
+
+  struct
+  {
+    unsigned int n_bits;     /* Size of the modulus in bits.  The rest
+                                of this strucuire is only valid if
+                                this is not 0.  */
+    unsigned int e_bits;     /* Size of the public exponent in bits.  */
+    rsa_key_format_t format;  
+  } keyattr[3];
+
 };
 
 
@@ -140,6 +208,12 @@ struct app_local_s {
 static unsigned long convert_sig_counter_value (const unsigned char *value,
                                                 size_t valuelen);
 static unsigned long get_sig_counter (app_t app);
+static gpg_error_t do_auth (app_t app, const char *keyidstr,
+                            gpg_error_t (*pincb)(void*, const char *, char **),
+                            void *pincb_arg,
+                            const void *indata, size_t indatalen,
+                            unsigned char **outdata, size_t *outdatalen);
+static void parse_algorithm_attribute (app_t app, int keyno);
 
 
 
@@ -173,17 +247,19 @@ do_deinit (app_t app)
 
 /* Wrapper around iso7816_get_data which first tries to get the data
    from the cache.  With GET_IMMEDIATE passed as true, the cache is
-   bypassed. */
+   bypassed.  With TRY_EXTLEN extended lengths APDUs are use if
+   supported by the card.  */
 static gpg_error_t
 get_cached_data (app_t app, int tag, 
                  unsigned char **result, size_t *resultlen,
-                 int get_immediate)
+                 int get_immediate, int try_extlen)
 {
   gpg_error_t err;
   int i;
   unsigned char *p;
   size_t len;
   struct cache_s *c;
+  int exmode;
 
   *result = NULL;
   *resultlen = 0;
@@ -208,7 +284,12 @@ get_cached_data (app_t app, int tag,
           }
     }
   
-  err = iso7816_get_data (app->slot, tag, &p, &len);
+  if (try_extlen && app->app_local->cardcap.ext_lc_le)
+    exmode = app->app_local->extcap.max_rsp_data;
+  else
+    exmode = 0;
+
+  err = iso7816_get_data (app->slot, exmode, tag, &p, &len);
   if (err)
     return err;
   *result = p;
@@ -321,6 +402,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
   unsigned char *value;
   size_t valuelen;
   int dummyrc;
+  int exmode;
 
   if (!r_rc)
     r_rc = &dummyrc;
@@ -333,7 +415,11 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
 
   if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
     {
-      rc = iso7816_get_data (app->slot, tag, &buffer, &buflen);
+      if (data_objects[i].try_extlen && app->app_local->cardcap.ext_lc_le)
+        exmode = app->app_local->extcap.max_rsp_data;
+      else
+        exmode = 0;
+      rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen);
       if (rc)
         {
           *r_rc = rc;
@@ -351,7 +437,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
       rc = get_cached_data (app, data_objects[i].get_from,
                             &buffer, &buflen,
                             (data_objects[i].dont_cache 
-                             || data_objects[i].get_immediate_in_v11));
+                             || data_objects[i].get_immediate_in_v11),
+                            data_objects[i].try_extlen);
       if (!rc)
         {
           const unsigned char *s;
@@ -374,7 +461,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
     {
       rc = get_cached_data (app, tag, &buffer, &buflen,
                             (data_objects[i].dont_cache 
-                             || data_objects[i].get_immediate_in_v11));
+                             || data_objects[i].get_immediate_in_v11),
+                            data_objects[i].try_extlen);
       if (!rc)
         {
           value = buffer;
@@ -405,7 +493,9 @@ dump_all_do (int slot)
       if (data_objects[i].get_from)
         continue;
 
-      rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen);
+      /* We don't try extended length APDU because such large DO would
+         be pretty useless in a log file.  */
+      rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen);
       if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
         ;
       else if (rc) 
@@ -443,7 +533,10 @@ dump_all_do (int slot)
                       if (data_objects[j].binary)
                         {
                           log_info ("DO `%s': ", data_objects[j].desc);
-                          log_printhex ("", value, valuelen);
+                          if (valuelen > 200)
+                            log_info ("[%u]\n", (unsigned int)valuelen);
+                          else
+                            log_printhex ("", value, valuelen);
                         }
                       else
                         log_info ("DO `%s': `%.*s'\n",
@@ -476,14 +569,14 @@ count_bits (const unsigned char *a, size_t len)
   return n;
 }
 
-/* GnuPG makes special use of the login-data DO, this fucntion parses
+/* GnuPG makes special use of the login-data DO, this function parses
    the login data to store the flags for later use.  It may be called
    at any time and should be called after changing the login-data DO.
 
    Everything up to a LF is considered a mailbox or account name.  If
    the first LF is followed by DC4 (0x14) control sequence are
    expected up to the next LF.  Control sequences are separated by FS
-   (0x28) and consist of key=value pairs.  There is one key defined:
+   (0x18) and consist of key=value pairs.  There is one key defined:
 
     F=<flags>
 
@@ -547,13 +640,14 @@ parse_login_data (app_t app)
 
 /* Note, that FPR must be at least 20 bytes. */
 static gpg_error_t 
-store_fpr (int slot, int keynumber, u32 timestamp,
+store_fpr (app_t app, int keynumber, u32 timestamp,
            const unsigned char *m, size_t mlen,
            const unsigned char *e, size_t elen, 
            unsigned char *fpr, unsigned int card_version)
 {
   unsigned int n, nbits;
   unsigned char *buffer, *p;
+  int tag, tag2;
   int rc;
   
   for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
@@ -564,7 +658,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
   n = 6 + 2 + mlen + 2 + elen;
   p = buffer = xtrymalloc (3 + n);
   if (!buffer)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   
   *p++ = 0x99;     /* ctb */
   *p++ = n >> 8;   /* 2 byte length header */
@@ -588,8 +682,12 @@ store_fpr (int slot, int keynumber, u32 timestamp,
 
   xfree (buffer);
 
-  rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6)
-                               + keynumber, fpr, 20);
+  tag = (card_version > 0x0007? 0xC7 : 0xC6) + keynumber;
+  flush_cache_item (app, tag);
+  tag2 = 0xCE + keynumber;
+  flush_cache_item (app, tag2);
+
+  rc = iso7816_put_data (app->slot, 0, tag, fpr, 20);
   if (rc)
     log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
 
@@ -602,7 +700,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
       buf[2] = timestamp >>  8;
       buf[3] = timestamp;
 
-      rc = iso7816_put_data (slot, 0xCE + keynumber, buf, 4);
+      rc = iso7816_put_data (app->slot, 0, tag2, buf, 4);
       if (rc)
         log_error (_("failed to store the creation date: %s\n"),
                    gpg_strerror (rc));
@@ -624,8 +722,7 @@ send_fpr_if_not_null (ctrl_t ctrl, const char *keyword,
     ;
   if (i==20)
     return; /* All zero. */
-  for (i=0; i< 20; i++)
-    sprintf (buf+2*i, "%02X", fpr[i]);
+  bin2hex (fpr, 20, buf);
   if (number == -1)
     *numbuf = 0; /* Don't print the key number */
   else
@@ -656,10 +753,14 @@ static void
 send_key_data (ctrl_t ctrl, const char *name, 
                const unsigned char *a, size_t alen)
 {
-  char *p, *buf = xmalloc (alen*2+1);
+  char *buf;
   
-  for (p=buf; alen; a++, alen--, p += 2)
-    sprintf (p, "%02X", *a);
+  buf = bin2hex (a, alen, NULL);
+  if (!buf)
+    {
+      log_error ("memory allocation error in send_key_data\n");
+      return;
+    }
 
   send_status_info (ctrl, "KEY-DATA",
                     name, (size_t)strlen(name), 
@@ -668,6 +769,24 @@ send_key_data (ctrl_t ctrl, const char *name,
   xfree (buf);
 }
 
+
+static void
+send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
+{                      
+  char buffer[200];
+
+  assert (number >=0 && number < DIM(app->app_local->keyattr));
+
+  /* We only support RSA thus the algo identifier is fixed to 1.  */
+  snprintf (buffer, sizeof buffer, "%d 1 %u %u %d",
+            number+1,
+            app->app_local->keyattr[number].n_bits,
+            app->app_local->keyattr[number].e_bits,
+            app->app_local->keyattr[number].format);
+  send_status_direct (ctrl, keyword, buffer);
+}
+
+
 /* Implement the GETATTR command.  This is similar to the LEARN
    command but returns just one value via the status interface. */
 static gpg_error_t 
@@ -685,6 +804,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     { "PUBKEY-URL",   0x5F50 },
     { "KEY-FPR",      0x00C5, 3 },
     { "KEY-TIME",     0x00CD, 4 },
+    { "KEY-ATTR",     0x0000, -5 },
     { "CA-FPR",       0x00C6, 3 },
     { "CHV-STATUS",   0x00C4, 1 }, 
     { "SIG-COUNTER",  0x0093, 2 },
@@ -695,6 +815,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     { "PRIVATE-DO-2", 0x0102 },
     { "PRIVATE-DO-3", 0x0103 },
     { "PRIVATE-DO-4", 0x0104 },
+    { "$AUTHKEYID",   0x0000, -3 },
+    { "$DISPSERIALNO",0x0000, -4 },
     { NULL, 0 }
   };
   int idx, i, rc;
@@ -731,16 +853,51 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     }
   if (table[idx].special == -2)
     {
-      char tmp[50];
-
-      sprintf (tmp, "gc=%d ki=%d fc=%d pd=%d", 
-               app->app_local->extcap.get_challenge,
-               app->app_local->extcap.key_import,
-               app->app_local->extcap.change_force_chv,
-               app->app_local->extcap.private_dos);
+      char tmp[100];
+
+      snprintf (tmp, sizeof tmp,
+                "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d", 
+                app->app_local->extcap.get_challenge,
+                app->app_local->extcap.key_import,
+                app->app_local->extcap.change_force_chv,
+                app->app_local->extcap.private_dos,
+                app->app_local->extcap.max_certlen_3,
+                app->app_local->extcap.algo_attr_change,
+                (app->app_local->extcap.sm_supported
+                 ? (app->app_local->extcap.sm_aes128? 7 : 2)
+                 : 0));
+      send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+      return 0;
+    }
+  if (table[idx].special == -3)
+    {
+      char const tmp[] = "OPENPGP.3";
       send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
       return 0;
     }
+  if (table[idx].special == -4)
+    {
+      char *serial;
+      time_t stamp;
+    
+      if (!app_get_serial_and_stamp (app, &serial, &stamp))
+        {
+          if (strlen (serial) > 16+12)
+            {
+              send_status_info (ctrl, table[idx].name, serial+16, 12, NULL, 0);
+              xfree (serial);
+              return 0;
+            }
+          xfree (serial);
+        }
+      return gpg_error (GPG_ERR_INV_NAME); 
+    }
+  if (table[idx].special == -5)
+    {
+      for (i=0; i < 3; i++)
+        send_key_attr (ctrl, app, table[idx].name, i);
+      return 0;
+    }
 
   relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc);
   if (relptr)
@@ -794,16 +951,12 @@ retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
   void *relptr;
   unsigned char *value;
   size_t valuelen;
-  int i;
 
   assert (keyno >=0 && keyno <= 2);
 
   relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
   if (relptr && valuelen >= 60)
-    {
-      for (i = 0; i < 20; i++)
-        sprintf (fpr + (i * 2), "%02X", value[(keyno*20)+i]);
-    }
+    bin2hex (value+keyno*20, 20, fpr);
   else
     err = gpg_error (GPG_ERR_NOT_FOUND);
   xfree (relptr);
@@ -834,7 +987,7 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
   size_t e_new_n = 0;
 
   /* Loop over all records until we have found the subkey
-     corresponsing to the fingerprint. Inm general the first record
+     corresponding to the fingerprint. Inm general the first record
      should be the pub record, but we don't rely on that.  Given that
      we only need to look at one key, it is sufficient to compare the
      keyid so that we don't need to look at "fpr" records. */
@@ -853,7 +1006,7 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
         break; /* EOF. */
       if (i < 0)
        {
-         err = gpg_error_from_errno (errno);
+         err = gpg_error_from_syserror ();
          goto leave; /* Error. */
        }
       if (!max_length)
@@ -947,8 +1100,8 @@ get_public_key (app_t app, int keyno)
   size_t buflen, keydatalen, mlen, elen;
   unsigned char *mbuf = NULL;
   unsigned char *ebuf = NULL;
-  unsigned char *keybuf = NULL;
-  unsigned char *keybuf_p;
+  char *keybuf = NULL;
+  char *keybuf_p;
 
   if (keyno < 1 || keyno > 3)
     return gpg_error (GPG_ERR_INV_ID);
@@ -962,14 +1115,30 @@ get_public_key (app_t app, int keyno)
   app->app_local->pk[keyno].key = NULL;
   app->app_local->pk[keyno].keylen = 0;
 
+  m = e = NULL; /* (avoid cc warning) */
+
   if (app->card_version > 0x0100)
     {
+      int exmode, le_value;
+
       /* We may simply read the public key out of these cards.  */
-      err = iso7816_read_public_key (app->slot, 
-                                    keyno == 0? "\xB6" :
-                                    keyno == 1? "\xB8" : "\xA4",
-                                    2,  
-                                    &buffer, &buflen);
+      if (app->app_local->cardcap.ext_lc_le)
+        {
+          exmode = 1;    /* Use extended length.  */
+          le_value = app->app_local->extcap.max_rsp_data;
+        }
+      else
+        {
+          exmode = 0;
+          le_value = 256; /* Use legacy value. */
+        }
+
+      err = iso7816_read_public_key 
+        (app->slot, exmode,
+         (const unsigned char*)(keyno == 0? "\xB6" :
+                                keyno == 1? "\xB8" : "\xA4"), 2,  
+         le_value,
+         &buffer, &buflen);
       if (err)
         {
           log_error (_("reading public key failed: %s\n"), gpg_strerror (err));
@@ -1007,7 +1176,7 @@ get_public_key (app_t app, int keyno)
           mbuf = xtrymalloc ( mlen + 1);
           if (!mbuf)
             {
-              err = gpg_error_from_errno (errno);
+              err = gpg_error_from_syserror ();
               goto leave;
             }
           *mbuf = 0;
@@ -1020,7 +1189,7 @@ get_public_key (app_t app, int keyno)
           ebuf = xtrymalloc ( elen + 1);
           if (!ebuf)
             {
-              err = gpg_error_from_errno (errno);
+              err = gpg_error_from_syserror ();
               goto leave;
             }
           *ebuf = 0;
@@ -1057,20 +1226,20 @@ get_public_key (app_t app, int keyno)
        }
       hexkeyid = fpr + 24;
 
-      ret = asprintf (&command,
-                     "gpg --list-keys --with-colons --with-key-data '%s'",
-                     fpr);
+      ret = estream_asprintf (&command,
+                             "gpg --list-keys --with-colons --with-key-data '%s'",
+                             fpr);
       if (ret < 0)
        {
-         err = gpg_error_from_errno (errno);
+         err = gpg_error_from_syserror ();
          goto leave;
        }
 
       fp = popen (command, "r");
-      free (command);
+      xfree (command);
       if (!fp)
        {
-         err = gpg_error_from_errno (errno);
+         err = gpg_error_from_syserror ();
          log_error ("running gpg failed: %s\n", gpg_strerror (err));
          goto leave;
        }
@@ -1091,7 +1260,7 @@ get_public_key (app_t app, int keyno)
   keybuf = xtrymalloc (50 + 2*35 + mlen + elen + 1);
   if (!keybuf)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
   
@@ -1106,7 +1275,7 @@ get_public_key (app_t app, int keyno)
   strcpy (keybuf_p, ")))");
   keybuf_p += strlen (keybuf_p);
   
-  app->app_local->pk[keyno].key = keybuf;
+  app->app_local->pk[keyno].key = (unsigned char*)keybuf;
   app->app_local->pk[keyno].keylen = (keybuf_p - keybuf);
 
  leave:
@@ -1134,7 +1303,6 @@ send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
   unsigned char grip[20];
   char gripstr[41];
   char idbuf[50];
-  int i;
 
   err = get_public_key (app, keyno);
   if (err)
@@ -1150,8 +1318,7 @@ send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
   if (err)
     goto leave;
   
-  for (i=0; i < 20; i++)
-    sprintf (gripstr+i*2, "%02X", grip[i]);
+  bin2hex (grip, 20, gripstr);
 
   sprintf (idbuf, "OPENPGP.%d", keyno);
   send_status_info (ctrl, "KEYPAIRINFO", 
@@ -1168,8 +1335,10 @@ send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
 
 /* Handle the LEARN command for OpenPGP.  */
 static gpg_error_t
-do_learn_status (app_t app, ctrl_t ctrl)
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
 {
+  (void)flags;
+  
   do_getattr (app, ctrl, "EXTCAP");
   do_getattr (app, ctrl, "DISP-NAME");
   do_getattr (app, ctrl, "DISP-LANG");
@@ -1194,6 +1363,8 @@ do_learn_status (app_t app, ctrl_t ctrl)
   send_keypair_info (app, ctrl, 1);
   send_keypair_info (app, ctrl, 2);
   send_keypair_info (app, ctrl, 3);
+  /* Note: We do not send the Cardholder Certificate, because that is
+     relativly long and for OpenPGP applications not really needed.  */
   return 0;
 }
 
@@ -1227,73 +1398,283 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
   buf = app->app_local->pk[keyno-1].key;
   if (!buf)
     return gpg_error (GPG_ERR_NO_PUBKEY);
-  *pk = buf;
   *pklen = app->app_local->pk[keyno-1].keylen;;
+  *pk = xtrymalloc (*pklen);
+  if (!*pk)
+    {
+      err = gpg_error_from_syserror ();
+      *pklen = 0;
+      return err;
+    }
+  memcpy (*pk, buf, *pklen);
   return 0;
 #else
   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 #endif
 }
 
+/* Read the standard certificate of an OpenPGP v2 card.  It is
+   returned in a freshly allocated buffer with that address stored at
+   CERT and the length of the certificate stored at CERTLEN.  CERTID
+   needs to be set to "OPENPGP.3".  */
+static gpg_error_t
+do_readcert (app_t app, const char *certid,
+             unsigned char **cert, size_t *certlen)
+{
+#if GNUPG_MAJOR_VERSION > 1
+  gpg_error_t err;
+  unsigned char *buffer;
+  size_t buflen;
+  void *relptr;
+
+  *cert = NULL;
+  *certlen = 0;
+  if (strcmp (certid, "OPENPGP.3"))
+    return gpg_error (GPG_ERR_INV_ID);
+  if (!app->app_local->extcap.is_v2)
+    return gpg_error (GPG_ERR_NOT_FOUND);
 
+  relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL);
+  if (!relptr)
+    return gpg_error (GPG_ERR_NOT_FOUND);
 
-/* Verify CHV2 if required.  Depending on the configuration of the
-   card CHV1 will also be verified. */
+  if (!buflen)
+    err = gpg_error (GPG_ERR_NOT_FOUND);
+  else if (!(*cert = xtrymalloc (buflen)))
+    err = gpg_error_from_syserror ();
+  else
+    {
+      memcpy (*cert, buffer, buflen);
+      *certlen = buflen;
+      err  = 0;
+    }
+  xfree (relptr);
+  return err;
+#else
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+/* Verify a CHV either using using the pinentry or if possibile by
+   using a keypad.  PINCB and PINCB_ARG describe the usual callback
+   for the pinentry.  CHVNO must be either 1 or 2. SIGCOUNT is only
+   used with CHV1.  PINVALUE is the address of a pointer which will
+   receive a newly allocated block with the actual PIN (this is useful
+   in case that PIN shall be used for another verify operation).  The
+   caller needs to free this value.  If the function returns with
+   success and NULL is stored at PINVALUE, the caller should take this
+   as an indication that the keypad has been used.
+   */
 static gpg_error_t
-verify_chv2 (app_t app,
-             gpg_error_t (*pincb)(void*, const char *, char **),
-             void *pincb_arg)
+verify_a_chv (app_t app,
+              gpg_error_t (*pincb)(void*, const char *, char **),
+              void *pincb_arg,
+              int chvno, unsigned long sigcount, char **pinvalue)
 {
   int rc = 0;
+  char *prompt_buffer = NULL;
+  const char *prompt;
+  iso7816_pininfo_t pininfo;
+  int minlen = 6;
 
-  if (!app->did_chv2) 
+  assert (chvno == 1 || chvno == 2);
+
+  *pinvalue = NULL;
+
+  if (chvno == 2 && app->app_local->flags.def_chv2)
     {
-      char *pinvalue;
+      /* Special case for def_chv2 mechanism. */
+      if (opt.verbose)
+        log_info (_("using default PIN as %s\n"), "CHV2");
+      rc = iso7816_verify (app->slot, 0x82, "123456", 6);
+      if (rc)
+        {
+          /* Verification of CHV2 with the default PIN failed,
+             although the card pretends to have the default PIN set as
+             CHV2.  We better disable the def_chv2 flag now. */
+          log_info (_("failed to use default PIN as %s: %s"
+                      " - disabling further default use\n"),
+                    "CHV2", gpg_strerror (rc));
+          app->app_local->flags.def_chv2 = 0;
+        }
+      return rc;
+    }
+
+  memset (&pininfo, 0, sizeof pininfo);
+  pininfo.mode = 1;
+  pininfo.minlen = minlen;
+
+
+  if (chvno == 1)
+    {
+#define PROMPTSTRING  _("||Please enter the PIN%%0A[sigs done: %lu]")
+      size_t promptsize = strlen (PROMPTSTRING) + 50;
+
+      prompt_buffer = xtrymalloc (promptsize);
+      if (!prompt_buffer)
+        return gpg_error_from_syserror ();
+      snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount);
+      prompt = prompt_buffer;
+#undef PROMPTSTRING
+    }
+  else
+    prompt = _("||Please enter the PIN");
 
-      rc = pincb (pincb_arg, "PIN", &pinvalue); 
+  
+  if (!opt.disable_keypad
+      && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+    {
+      /* The reader supports the verify command through the keypad.
+         Note that the pincb appends a text to the prompt telling the
+         user to use the keypad. */
+      rc = pincb (pincb_arg, prompt, NULL); 
+      prompt = NULL;
+      xfree (prompt_buffer); 
+      prompt_buffer = NULL;
       if (rc)
         {
-          log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
+          log_info (_("PIN callback returned error: %s\n"),
+                    gpg_strerror (rc));
           return rc;
         }
+      rc = iso7816_verify_kp (app->slot, 0x80+chvno, "", 0, &pininfo); 
+      /* Dismiss the prompt. */
+      pincb (pincb_arg, NULL, NULL);
 
-      if (strlen (pinvalue) < 6)
+      assert (!*pinvalue);
+    }
+  else
+    {
+      /* The reader has no keypad or we don't want to use it. */
+      rc = pincb (pincb_arg, prompt, pinvalue); 
+      prompt = NULL;
+      xfree (prompt_buffer); 
+      prompt_buffer = NULL;
+      if (rc)
+        {
+          log_info (_("PIN callback returned error: %s\n"),
+                    gpg_strerror (rc));
+          return rc;
+        }
+      
+      if (strlen (*pinvalue) < minlen)
         {
           log_error (_("PIN for CHV%d is too short;"
-                       " minimum length is %d\n"), 2, 6);
-          xfree (pinvalue);
+                       " minimum length is %d\n"), chvno, minlen);
+          xfree (*pinvalue);
+          *pinvalue = NULL;
           return gpg_error (GPG_ERR_BAD_PIN);
         }
 
-      rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
+      rc = iso7816_verify (app->slot, 0x80+chvno,
+                           *pinvalue, strlen (*pinvalue));
+    }
+  
+  if (rc)
+    {
+      log_error (_("verify CHV%d failed: %s\n"), chvno, gpg_strerror (rc));
+      xfree (*pinvalue);
+      *pinvalue = NULL;
+      flush_cache_after_error (app);
+    }
+
+  return rc;
+}
+
+
+/* Verify CHV2 if required.  Depending on the configuration of the
+   card CHV1 will also be verified. */
+static gpg_error_t
+verify_chv2 (app_t app,
+             gpg_error_t (*pincb)(void*, const char *, char **),
+             void *pincb_arg)
+{
+  int rc;
+  char *pinvalue;
+
+  if (app->did_chv2) 
+    return 0;  /* We already verified CHV2.  */
+
+  rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue);
+  if (rc)
+    return rc;
+  app->did_chv2 = 1;
+  
+  if (!app->did_chv1 && !app->force_chv1 && pinvalue)
+    {
+      /* For convenience we verify CHV1 here too.  We do this only if
+         the card is not configured to require a verification before
+         each CHV1 controlled operation (force_chv1) and if we are not
+         using the keypad (PINVALUE == NULL). */
+      rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+      if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
+        rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
       if (rc)
         {
-          log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
-          xfree (pinvalue);
+          log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
           flush_cache_after_error (app);
-          return rc;
-        }
-      app->did_chv2 = 1;
-
-      if (!app->did_chv1 && !app->force_chv1)
-        {
-          rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
-          if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
-            rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
-          if (rc)
-            {
-              log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
-              xfree (pinvalue);
-              flush_cache_after_error (app);
-              return rc;
-            }
-          app->did_chv1 = 1;
         }
-      xfree (pinvalue);
+      else
+        app->did_chv1 = 1;
     }
+
+  xfree (pinvalue);
+
   return rc;
 }
 
+
+/* Build the prompt to enter the Admin PIN.  The prompt depends on the
+   current sdtate of the card.  */
+static gpg_error_t 
+build_enter_admin_pin_prompt (app_t app, char **r_prompt)
+{
+  void *relptr;
+  unsigned char *value;
+  size_t valuelen;
+  int remaining;
+  char *prompt;
+
+  *r_prompt = NULL;
+
+  relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+  if (!relptr || valuelen < 7)
+    {
+      log_error (_("error retrieving CHV status from card\n"));
+      xfree (relptr);
+      return gpg_error (GPG_ERR_CARD);
+    }
+  if (value[6] == 0)
+    {
+      log_info (_("card is permanently locked!\n"));
+      xfree (relptr);
+      return gpg_error (GPG_ERR_BAD_PIN);
+    }
+  remaining = value[6];
+  xfree (relptr);
+  
+  log_info(_("%d Admin PIN attempts remaining before card"
+             " is permanently locked\n"), remaining);
+
+  if (remaining < 3)
+    {
+      /* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
+         the start of the string.  Use %%0A to force a linefeed.  */
+      prompt = xtryasprintf (_("|A|Please enter the Admin PIN%%0A"
+                               "[remaining attempts: %d]"), remaining);
+    }
+  else
+    prompt = xtrystrdup (_("|A|Please enter the Admin PIN"));
+  
+  if (!prompt)
+    return gpg_error_from_syserror ();
+  
+  *r_prompt = prompt;
+  return 0;
+}
+
+
 /* Verify CHV3 if required. */
 static gpg_error_t
 verify_chv3 (app_t app,
@@ -1312,49 +1693,61 @@ verify_chv3 (app_t app,
       
   if (!app->did_chv3) 
     {
-      char *pinvalue;
-      void *relptr;
-      unsigned char *value;
-      size_t valuelen;
-
-      relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
-      if (!relptr || valuelen < 7)
-        {
-          log_error (_("error retrieving CHV status from card\n"));
-          xfree (relptr);
-          return gpg_error (GPG_ERR_CARD);
-        }
-      if (value[6] == 0)
-        {
-          log_info (_("card is permanently locked!\n"));
-          xfree (relptr);
-          return gpg_error (GPG_ERR_BAD_PIN);
-        }
+      iso7816_pininfo_t pininfo;
+      int minlen = 8;
+      char *prompt;
 
-      log_info(_("%d Admin PIN attempts remaining before card"
-                 " is permanently locked\n"), value[6]);
-      xfree (relptr);
+      memset (&pininfo, 0, sizeof pininfo);
+      pininfo.mode = 1;
+      pininfo.minlen = minlen;
 
-      /* TRANSLATORS: Do not translate the "|A|" prefix but
-         keep it at the start of the string.  We need this elsewhere
-         to get some infos on the string. */
-      rc = pincb (pincb_arg, _("|A|Admin PIN"), &pinvalue); 
+      rc = build_enter_admin_pin_prompt (app, &prompt);
       if (rc)
+        return rc;
+
+      if (!opt.disable_keypad
+          && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
         {
-          log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
-          return rc;
+          /* The reader supports the verify command through the keypad. */
+          rc = pincb (pincb_arg, prompt, NULL); 
+          xfree (prompt);
+          prompt = NULL;
+          if (rc)
+            {
+              log_info (_("PIN callback returned error: %s\n"),
+                        gpg_strerror (rc));
+              return rc;
+            }
+          rc = iso7816_verify_kp (app->slot, 0x83, "", 0, &pininfo); 
+          /* Dismiss the prompt. */
+          pincb (pincb_arg, NULL, NULL);
         }
-
-      if (strlen (pinvalue) < 8)
+      else
         {
-          log_error (_("PIN for CHV%d is too short;"
-                       " minimum length is %d\n"), 3, 8);
+          char *pinvalue;
+
+          rc = pincb (pincb_arg, prompt, &pinvalue); 
+          xfree (prompt);
+          prompt = NULL;
+          if (rc)
+            {
+              log_info (_("PIN callback returned error: %s\n"),
+                        gpg_strerror (rc));
+              return rc;
+            }
+          
+          if (strlen (pinvalue) < minlen)
+            {
+              log_error (_("PIN for CHV%d is too short;"
+                           " minimum length is %d\n"), 3, minlen);
+              xfree (pinvalue);
+              return gpg_error (GPG_ERR_BAD_PIN);
+            }
+          
+          rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
           xfree (pinvalue);
-          return gpg_error (GPG_ERR_BAD_PIN);
         }
-
-      rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
-      xfree (pinvalue);
+      
       if (rc)
         {
           log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc));
@@ -1382,6 +1775,7 @@ do_setattr (app_t app, const char *name,
     int tag;
     int need_chv;
     int special;
+    unsigned int need_v2:1;
   } table[] = {
     { "DISP-NAME",    0x005B, 3 },
     { "LOGIN-DATA",   0x005E, 3, 2 },
@@ -1396,14 +1790,19 @@ do_setattr (app_t app, const char *name,
     { "PRIVATE-DO-2", 0x0102, 3 },
     { "PRIVATE-DO-3", 0x0103, 2 },
     { "PRIVATE-DO-4", 0x0104, 3 },
+    { "CERT-3",       0x7F21, 3, 0, 1 },
+    { "SM-KEY-ENC",   0x00D1, 3, 0, 1 },
+    { "SM-KEY-MAC",   0x00D2, 3, 0, 1 },
     { NULL, 0 }
   };
-
+  int exmode;
 
   for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
     ;
   if (!table[idx].name)
     return gpg_error (GPG_ERR_INV_NAME); 
+  if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported.  */
 
   switch (table[idx].need_chv)
     {
@@ -1423,7 +1822,14 @@ do_setattr (app_t app, const char *name,
      will reread the data from the card and thus get synced in case of
      errors (e.g. data truncated by the card). */
   flush_cache_item (app, table[idx].tag);
-  rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen);
+
+  if (app->app_local->cardcap.ext_lc_le && valuelen > 254)
+    exmode = 1;    /* Use extended length w/o a limit.  */
+  else if (app->app_local->cardcap.cmd_chaining && valuelen > 254)
+    exmode = -254; /* Command chaining with max. 254 bytes.  */
+  else
+    exmode = 0;
+  rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen);
   if (rc)
     log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc));
 
@@ -1436,47 +1842,201 @@ do_setattr (app_t app, const char *name,
 }
 
 
-/* Handle the PASSWD command. */
+/* Handle the WRITECERT command for OpenPGP.  This rites the standard
+   certifciate to the card; CERTID needs to be set to "OPENPGP.3".
+   PINCB and PINCB_ARG are the usual arguments for the pinentry
+   callback.  */
+static gpg_error_t
+do_writecert (app_t app, ctrl_t ctrl,
+              const char *certidstr, 
+              gpg_error_t (*pincb)(void*, const char *, char **),
+              void *pincb_arg,
+              const unsigned char *certdata, size_t certdatalen)
+{
+  (void)ctrl;
+#if GNUPG_MAJOR_VERSION > 1
+  if (strcmp (certidstr, "OPENPGP.3"))
+    return gpg_error (GPG_ERR_INV_ID);
+  if (!certdata || !certdatalen)
+    return gpg_error (GPG_ERR_INV_ARG);
+  if (!app->app_local->extcap.is_v2)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+  if (certdatalen > app->app_local->extcap.max_certlen_3)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+  return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen);
+#else
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+
+/* Handle the PASSWD command.  The following combinations are
+   possible:
+
+    Flags  CHVNO Vers.  Description
+    RESET    1   1      Verify CHV3 and set a new CHV1 and CHV2
+    RESET    1   2      Verify PW3 and set a new PW1.
+    RESET    2   1      Verify CHV3 and set a new CHV1 and CHV2.
+    RESET    2   2      Verify PW3 and set a new Reset Code.
+    RESET    3   any    Returns GPG_ERR_INV_ID.
+     -       1   1      Verify CHV2 and set a new CHV1 and CHV2.
+     -       1   2      Verify PW1 and set a new PW1.
+     -       2   1      Verify CHV2 and set a new CHV1 and CHV2.
+     -       2   2      Verify Reset Code and set a new PW1.
+     -       3   any    Verify CHV3/PW3 and set a new CHV3/PW3.
+ */
 static gpg_error_t 
-do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, int reset_mode,
+do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, 
+               unsigned int flags,
                gpg_error_t (*pincb)(void*, const char *, char **),
                void *pincb_arg)
 {
   int rc = 0;
   int chvno = atoi (chvnostr);
+  char *resetcode = NULL;
+  char *oldpinvalue = NULL;
   char *pinvalue;
+  int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
+  int set_resetcode = 0;
+
+  (void)ctrl;
 
   if (reset_mode && chvno == 3)
     {
       rc = gpg_error (GPG_ERR_INV_ID);
       goto leave;
     }
-  else if (reset_mode || chvno == 3)
+
+  if (!app->app_local->extcap.is_v2)
     {
-      /* we always require that the PIN is entered. */
-      app->did_chv3 = 0;
-      rc = verify_chv3 (app, pincb, pincb_arg);
-      if (rc)
-        goto leave;
-    }
-  else if (chvno == 1 || chvno == 2)
-    {
-      /* CHV1 and CVH2 should always have the same value, thus we
-         enforce it here.  */
-      int save_force = app->force_chv1;
+      /* Version 1 cards.  */
 
-      app->force_chv1 = 0;
-      app->did_chv1 = 0;
-      app->did_chv2 = 0;
-      rc = verify_chv2 (app, pincb, pincb_arg);
-      app->force_chv1 = save_force;
-      if (rc)
-        goto leave;
+      if (reset_mode || chvno == 3)
+        {
+          /* We always require that the PIN is entered. */
+          app->did_chv3 = 0;
+          rc = verify_chv3 (app, pincb, pincb_arg);
+          if (rc)
+            goto leave;
+        }
+      else if (chvno == 1 || chvno == 2)
+        {
+          /* On a v1.x card CHV1 and CVH2 should always have the same
+             value, thus we enforce it here.  */
+          int save_force = app->force_chv1;
+          
+          app->force_chv1 = 0;
+          app->did_chv1 = 0;
+          app->did_chv2 = 0;
+          rc = verify_chv2 (app, pincb, pincb_arg);
+          app->force_chv1 = save_force;
+          if (rc)
+            goto leave;
+        }
+      else
+        {
+          rc = gpg_error (GPG_ERR_INV_ID);
+          goto leave;
+        }
     }
   else
     {
-      rc = gpg_error (GPG_ERR_INV_ID);
-      goto leave;
+      /* Version 2 cards.  */
+
+      if (reset_mode)
+        {
+          /* To reset a PIN the Admin PIN is required. */
+          app->did_chv3 = 0;
+          rc = verify_chv3 (app, pincb, pincb_arg);
+          if (rc)
+            goto leave;
+          
+          if (chvno == 2)
+            set_resetcode = 1;
+        }
+      else if (chvno == 1 || chvno == 3)
+        {
+          int minlen = (chvno ==3)? 8 : 6;
+          char *promptbuf = NULL;
+          const char *prompt;
+
+          if (chvno == 3)
+            {
+              rc = build_enter_admin_pin_prompt (app, &promptbuf);
+              if (rc)
+                goto leave;
+              prompt = promptbuf;
+            }
+          else
+            prompt = _("||Please enter the PIN");
+          rc = pincb (pincb_arg, prompt, &oldpinvalue);
+          xfree (promptbuf);
+          promptbuf = NULL;
+          if (rc)
+            {
+              log_info (_("PIN callback returned error: %s\n"),
+                        gpg_strerror (rc));
+              goto leave;
+            }
+
+          if (strlen (oldpinvalue) < minlen)
+            {
+              log_info (_("PIN for CHV%d is too short;"
+                          " minimum length is %d\n"), chvno, minlen);
+              rc = gpg_error (GPG_ERR_BAD_PIN);
+              goto leave;
+            }
+        }
+      else if (chvno == 2)
+        {
+          /* There is no PW2 for v2 cards.  We use this condition to
+             allow a PW reset using the Reset Code.  */
+          void *relptr;
+          unsigned char *value;
+          size_t valuelen;
+          int remaining;
+          int minlen = 8;
+
+          relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+          if (!relptr || valuelen < 7)
+            {
+              log_error (_("error retrieving CHV status from card\n"));
+              xfree (relptr);
+              rc = gpg_error (GPG_ERR_CARD);
+              goto leave;
+            }
+          remaining = value[5];
+          xfree (relptr);
+          if (!remaining)
+            {
+              log_error (_("Reset Code not or not anymore available\n"));
+              rc = gpg_error (GPG_ERR_BAD_PIN);
+              goto leave;
+            }          
+          
+          rc = pincb (pincb_arg,
+                      _("||Please enter the Reset Code for the card"),
+                      &resetcode); 
+          if (rc)
+            {
+              log_info (_("PIN callback returned error: %s\n"), 
+                        gpg_strerror (rc));
+              goto leave;
+            }
+          if (strlen (resetcode) < minlen)
+            {
+              log_info (_("Reset Code is too short; minimum length is %d\n"),
+                        minlen);
+              rc = gpg_error (GPG_ERR_BAD_PIN);
+              goto leave;
+            }
+        }
+      else
+        {
+          rc = gpg_error (GPG_ERR_INV_ID);
+          goto leave;
+        }
     }
 
   if (chvno == 3)
@@ -1487,7 +2047,9 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, int reset_mode,
   /* TRANSLATORS: Do not translate the "|*|" prefixes but
      keep it at the start of the string.  We need this elsewhere
      to get some infos on the string. */
-  rc = pincb (pincb_arg, chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"), 
+  rc = pincb (pincb_arg, 
+              set_resetcode? _("|RN|New Reset Code") :
+              chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"), 
               &pinvalue); 
   if (rc)
     {
@@ -1495,16 +2057,45 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, int reset_mode,
       goto leave;
     }
 
-  if (reset_mode)
+
+  if (resetcode)
+    {
+      char *buffer;
+
+      buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
+      if (!buffer)
+        rc = gpg_error_from_syserror ();
+      else
+        {
+          strcpy (stpcpy (buffer, resetcode), pinvalue);
+          rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
+                                                    buffer, strlen (buffer));
+          wipememory (buffer, strlen (buffer));
+          xfree (buffer);
+        }
+    }
+  else if (set_resetcode)
+    {
+      if (strlen (pinvalue) < 8)
+        {
+          log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
+          rc = gpg_error (GPG_ERR_BAD_PIN);
+        }
+      else
+        rc = iso7816_put_data (app->slot, 0, 0xD3,
+                               pinvalue, strlen (pinvalue));
+    }
+  else if (reset_mode)
     {
       rc = iso7816_reset_retry_counter (app->slot, 0x81,
                                         pinvalue, strlen (pinvalue));
-      if (!rc)
+      if (!rc && !app->app_local->extcap.is_v2)
         rc = iso7816_reset_retry_counter (app->slot, 0x82,
                                           pinvalue, strlen (pinvalue));
     }
-  else
+  else if (!app->app_local->extcap.is_v2)
     {
+      /* Version 1 cards.  */
       if (chvno == 1 || chvno == 2)
         {
           rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0,
@@ -1513,24 +2104,51 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, int reset_mode,
             rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0,
                                                 pinvalue, strlen (pinvalue));
         }
-      else
-        rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
-                                            pinvalue, strlen (pinvalue));
+      else /* CHVNO == 3 */
+        {
+          rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
+                                              pinvalue, strlen (pinvalue));
+        }
+    }
+  else
+    {
+      /* Version 2 cards.  */
+      assert (chvno == 1 || chvno == 3);
+      
+      rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
+                                          oldpinvalue, strlen (oldpinvalue),
+                                          pinvalue, strlen (pinvalue));
+    }
+
+  if (pinvalue)
+    {
+      wipememory (pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
     }
-  xfree (pinvalue);
   if (rc)
     flush_cache_after_error (app);
 
  leave:
+  if (resetcode)
+    {
+      wipememory (resetcode, strlen (resetcode));
+      xfree (resetcode);
+    }
+  if (oldpinvalue)
+    {
+      wipememory (oldpinvalue, strlen (oldpinvalue));
+      xfree (oldpinvalue);
+    }
   return rc;
 }
 
 
 /* Check whether a key already exists.  KEYIDX is the index of the key
    (0..2).  If FORCE is TRUE a diagnositic will be printed but no
-   error returned if the key already exists. */
+   error returned if the key already exists.  The flag GENERATING is
+   only used to print correct messages. */
 static gpg_error_t
-does_key_exist (app_t app, int keyidx, int force)
+does_key_exist (app_t app, int keyidx, int generating, int force)
 {
   const unsigned char *fpr;
   unsigned char *buffer;
@@ -1539,7 +2157,7 @@ does_key_exist (app_t app, int keyidx, int force)
 
   assert (keyidx >=0 && keyidx <= 2);
 
-  if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen))
+  if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen))
     {
       log_error (_("error reading application data\n"));
       return gpg_error (GPG_ERR_GENERAL);
@@ -1562,12 +2180,230 @@ does_key_exist (app_t app, int keyidx, int force)
     }
   else if (i!=20)
     log_info (_("existing key will be replaced\n"));
-  else
+  else if (generating)
     log_info (_("generating new key\n"));
+  else
+    log_info (_("writing new key\n"));
+  return 0;
+}
+
+
+/* Create a TLV tag and value and store it at BUFFER.  Return the length
+   of tag and length.  A LENGTH greater than 65535 is truncated. */
+static size_t
+add_tlv (unsigned char *buffer, unsigned int tag, size_t length)
+{ 
+  unsigned char *p = buffer;
+
+  assert (tag <= 0xffff);
+  if ( tag > 0xff )
+    *p++ = tag >> 8;
+  *p++ = tag;
+  if (length < 128)
+    *p++ = length;
+  else if (length < 256)
+    {
+      *p++ = 0x81;
+      *p++ = length;
+    }
+  else
+    {
+      if (length > 0xffff)
+        length = 0xffff;
+      *p++ = 0x82;
+      *p++ = length >> 8;
+      *p++ = length;
+    }
+
+  return p - buffer;
+}
+
+
+/* Build the private key template as specified in the OpenPGP specs
+   v2.0 section 4.3.3.7.  */
+static gpg_error_t
+build_privkey_template (app_t app, int keyno,
+                        const unsigned char *rsa_n, size_t rsa_n_len,
+                        const unsigned char *rsa_e, size_t rsa_e_len,
+                        const unsigned char *rsa_p, size_t rsa_p_len,
+                        const unsigned char *rsa_q, size_t rsa_q_len,
+                        unsigned char **result, size_t *resultlen)
+{
+  size_t rsa_e_reqlen;
+  unsigned char privkey[7*(1+3)];
+  size_t privkey_len;
+  unsigned char exthdr[2+2+3];
+  size_t exthdr_len;
+  unsigned char suffix[2+3];
+  size_t suffix_len;
+  unsigned char *tp;
+  size_t datalen;
+  unsigned char *template;
+  size_t template_size;
+
+  *result = NULL;
+  *resultlen = 0;
+
+  switch (app->app_local->keyattr[keyno].format)
+    {
+    case RSA_STD:
+    case RSA_STD_N:
+      break;
+    case RSA_CRT:
+    case RSA_CRT_N:
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+    default:
+      return gpg_error (GPG_ERR_INV_VALUE);
+    }
+
+  /* Get the required length for E.  */
+  rsa_e_reqlen = app->app_local->keyattr[keyno].e_bits/8;
+  assert (rsa_e_len <= rsa_e_reqlen);
+
+  /* Build the 7f48 cardholder private key template.  */
+  datalen = 0;
+  tp = privkey;
+
+  tp += add_tlv (tp, 0x91, rsa_e_reqlen);
+  datalen += rsa_e_reqlen;
+
+  tp += add_tlv (tp, 0x92, rsa_p_len);
+  datalen += rsa_p_len;
+
+  tp += add_tlv (tp, 0x93, rsa_q_len);
+  datalen += rsa_q_len;
+
+  if (app->app_local->keyattr[keyno].format == RSA_STD_N
+      || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+    {
+      tp += add_tlv (tp, 0x97, rsa_n_len);
+      datalen += rsa_n_len;
+    }
+  privkey_len = tp - privkey;
+
+  /* Build the extended header list without the private key template.  */
+  tp = exthdr;
+  *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
+  *tp++ = 0;
+  tp += add_tlv (tp, 0x7f48, privkey_len);
+  exthdr_len = tp - exthdr;
+
+  /* Build the 5f48 suffix of the data.  */
+  tp = suffix;
+  tp += add_tlv (tp, 0x5f48, datalen);
+  suffix_len = tp - suffix;
+
+  /* Now concatenate everything.  */
+  template_size = (1 + 3   /* 0x4d and len. */
+                   + exthdr_len
+                   + privkey_len
+                   + suffix_len
+                   + datalen);
+  tp = template = xtrymalloc_secure (template_size);
+  if (!template)
+    return gpg_error_from_syserror ();
+
+  tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
+  memcpy (tp, exthdr, exthdr_len);
+  tp += exthdr_len;
+  memcpy (tp, privkey, privkey_len);
+  tp += privkey_len;
+  memcpy (tp, suffix, suffix_len);
+  tp += suffix_len;
+
+  memcpy (tp, rsa_e, rsa_e_len);
+  if (rsa_e_len < rsa_e_reqlen)
+    {
+      /* Right justify E. */
+      memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len);
+      memset (tp, 0, rsa_e_reqlen - rsa_e_len);
+    }                 
+  tp += rsa_e_reqlen;
+      
+  memcpy (tp, rsa_p, rsa_p_len);
+  tp += rsa_p_len;
+      
+  memcpy (tp, rsa_q, rsa_q_len);
+  tp += rsa_q_len;
+  
+  if (app->app_local->keyattr[keyno].format == RSA_STD_N
+      || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+    {
+      memcpy (tp, rsa_n, rsa_n_len);
+      tp += rsa_n_len;
+    }
+
+  /* Sanity check.  We don't know the exact length because we
+     allocated 3 bytes for the first length header.  */
+  assert (tp - template <= template_size);
+
+  *result = template;
+  *resultlen = tp - template;
   return 0;
 }
 
 
+/* Helper for do_writekley to change the size of a key.  Not ethat
+   this deletes the entire key without asking.  */
+static gpg_error_t
+change_keyattr (app_t app, int keyno, unsigned int nbits,
+                gpg_error_t (*pincb)(void*, const char *, char **),
+                void *pincb_arg)
+{
+  gpg_error_t err;
+  unsigned char *buffer;
+  size_t buflen;
+  void *relptr;
+
+  assert (keyno >=0 && keyno <= 2);
+
+  if (nbits > 3072)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+
+  /* Read the current attributes into a buffer.  */
+  relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
+  if (!relptr)
+    return gpg_error (GPG_ERR_CARD);
+  if (buflen < 6 || buffer[0] != 1)
+    {
+      /* Attriutes too short or not an RSA key.  */
+      xfree (relptr);
+      return gpg_error (GPG_ERR_CARD);
+    }
+  
+  /* We only change n_bits and don't touch anything else.  Before we
+     do so, we round up NBITS to a sensible way in the same way as
+     gpg's key generation does it.  This may help to sort out problems
+     with a few bits too short keys.  */
+  nbits = ((nbits + 31) / 32) * 32;
+  buffer[1] = (nbits >> 8);
+  buffer[2] = nbits;
+
+  /* Prepare for storing the key.  */
+  err = verify_chv3 (app, pincb, pincb_arg);
+  if (err)
+    {
+      xfree (relptr);
+      return err;
+    }
+
+  /* Change the attribute.  */
+  err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buffer, buflen);
+  xfree (relptr);
+  if (err)
+    log_error ("error changing size of key %d to %u bits\n", keyno+1, nbits);
+  else
+    log_info ("size of key %d changed to %u bits\n", keyno+1, nbits);
+  flush_cache (app);
+  parse_algorithm_attribute (app, keyno);
+  app->did_chv1 = 0;
+  app->did_chv2 = 0;
+  app->did_chv3 = 0;
+  return err;
+}
+
+
 
 /* Handle the WRITEKEY command for OpenPGP.  This function expects a
    canonical encoded S-expression with the secret key in KEYDATA and
@@ -1595,12 +2431,15 @@ do_writekey (app_t app, ctrl_t ctrl,
   const unsigned char *rsa_q = NULL;
   size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
   unsigned int nbits;
+  unsigned int maxbits;
   unsigned char *template = NULL;
   unsigned char *tp;
   size_t template_len;
   unsigned char fprbuf[20];
   u32 created_at = 0;
 
+  (void)ctrl;
+
   if (!strcmp (keyid, "OPENPGP.1"))
     keyno = 0;
   else if (!strcmp (keyid, "OPENPGP.2"))
@@ -1610,7 +2449,7 @@ do_writekey (app_t app, ctrl_t ctrl,
   else
     return gpg_error (GPG_ERR_INV_ID);
   
-  err = does_key_exist (app, keyno, force);
+  err = does_key_exist (app, keyno, 0, force);
   if (err)
     return err;
 
@@ -1734,99 +2573,153 @@ do_writekey (app_t app, ctrl_t ctrl,
       err = gpg_error (GPG_ERR_INV_VALUE);
       goto leave;
     }
+
+  maxbits = app->app_local->keyattr[keyno].n_bits;
   nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
-  if (nbits != 1024)
+  if (opt.verbose)
+    log_info ("RSA modulus size is %u bits (%u bytes)\n", 
+              nbits, (unsigned int)rsa_n_len);
+  if (nbits && nbits != maxbits
+      && app->app_local->extcap.algo_attr_change)
     {
-      log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
+      /* Try to switch the key to a new length.  */
+      err = change_keyattr (app, keyno, nbits, pincb, pincb_arg);
+      if (!err)
+        maxbits = app->app_local->keyattr[keyno].n_bits;
+    }
+  if (nbits != maxbits)
+    {
+      log_error (_("RSA modulus missing or not of size %d bits\n"), 
+                 (int)maxbits);
       err = gpg_error (GPG_ERR_BAD_SECKEY);
       goto leave;
     }
+
+  maxbits = app->app_local->keyattr[keyno].e_bits;
+  if (maxbits > 32 && !app->app_local->extcap.is_v2)
+    maxbits = 32; /* Our code for v1 does only support 32 bits.  */
   nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
-  if (nbits < 2 || nbits > 32)
+  if (nbits < 2 || nbits > maxbits)
     {
       log_error (_("RSA public exponent missing or larger than %d bits\n"),
-                 32);
+                 (int)maxbits);
       err = gpg_error (GPG_ERR_BAD_SECKEY);
       goto leave;
     }
+
+  maxbits = app->app_local->keyattr[keyno].n_bits/2;
   nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
-  if (nbits != 512)
+  if (nbits != maxbits)
     {
-      log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512);
+      log_error (_("RSA prime %s missing or not of size %d bits\n"), 
+                 "P", (int)maxbits);
       err = gpg_error (GPG_ERR_BAD_SECKEY);
       goto leave;
     }
   nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
-  if (nbits != 512)
+  if (nbits != maxbits)
     {
-      log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512);
+      log_error (_("RSA prime %s missing or not of size %d bits\n"), 
+                 "Q", (int)maxbits);
       err = gpg_error (GPG_ERR_BAD_SECKEY);
       goto leave;
     }
   
-
-  /* Build the private key template as described in section 4.3.3.6 of
-     the OpenPGP card specs:
-         0xC0   <length> public exponent
-         0xC1   <length> prime p 
-         0xC2   <length> prime q 
-  */
-  assert (rsa_e_len <= 4);
-  template_len = (1 + 1 + 4
-                  + 1 + 1 + rsa_p_len
-                  + 1 + 1 + rsa_q_len);
-  template = tp = xtrymalloc_secure (template_len);
-  if (!template)
-    {
-      err = gpg_error_from_errno (errno);
-      goto leave;
-    }
-  *tp++ = 0xC0;
-  *tp++ = 4;
-  memcpy (tp, rsa_e, rsa_e_len);
-  if (rsa_e_len < 4)
-    {
-      /* Right justify E. */
-      memmove (tp+4-rsa_e_len, tp, rsa_e_len);
-      memset (tp, 0, 4-rsa_e_len);
-    }                 
-  tp += 4;
-
-  *tp++ = 0xC1;
-  *tp++ = rsa_p_len;
-  memcpy (tp, rsa_p, rsa_p_len);
-  tp += rsa_p_len;
-
-  *tp++ = 0xC2;
-  *tp++ = rsa_q_len;
-  memcpy (tp, rsa_q, rsa_q_len);
-  tp += rsa_q_len;
-
-  assert (tp - template == template_len);
-
-
-  /* Obviously we need to remove the cached public key.  */
+  /* We need to remove the cached public key.  */
   xfree (app->app_local->pk[keyno].key);
   app->app_local->pk[keyno].key = NULL;
   app->app_local->pk[keyno].keylen = 0;
   app->app_local->pk[keyno].read_done = 0;
 
-  /* Prepare for storing the key.  */
-  err = verify_chv3 (app, pincb, pincb_arg);
-  if (err)
-    goto leave;
 
-  /* Store the key. */
-  err = iso7816_put_data (app->slot,
-                         (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
-                         template, template_len);
+  if (app->app_local->extcap.is_v2)
+    {
+      /* Build the private key template as described in section 4.3.3.7 of
+         the OpenPGP card specs version 2.0.  */
+      int exmode;
+      err = build_privkey_template (app, keyno,
+                                    rsa_n, rsa_n_len,
+                                    rsa_e, rsa_e_len,
+                                    rsa_p, rsa_p_len,
+                                    rsa_q, rsa_q_len,
+                                    &template, &template_len);
+      if (err)
+        goto leave;
+
+      /* Prepare for storing the key.  */
+      err = verify_chv3 (app, pincb, pincb_arg);
+      if (err)
+        goto leave;
+
+      /* Store the key. */
+      if (app->app_local->cardcap.ext_lc_le && template_len > 254)
+        exmode = 1;    /* Use extended length w/o a limit.  */
+      else if (app->app_local->cardcap.cmd_chaining && template_len > 254)
+        exmode = -254;
+      else
+        exmode = 0;
+      err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
+                                  template, template_len);
+    }
+  else
+    {
+      /* Build the private key template as described in section 4.3.3.6 of
+         the OpenPGP card specs version 1.1:
+         0xC0   <length> public exponent
+         0xC1   <length> prime p 
+         0xC2   <length> prime q 
+      */
+      assert (rsa_e_len <= 4);
+      template_len = (1 + 1 + 4
+                      + 1 + 1 + rsa_p_len
+                      + 1 + 1 + rsa_q_len);
+      template = tp = xtrymalloc_secure (template_len);
+      if (!template)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+      *tp++ = 0xC0;
+      *tp++ = 4;
+      memcpy (tp, rsa_e, rsa_e_len);
+      if (rsa_e_len < 4)
+        {
+          /* Right justify E. */
+          memmove (tp+4-rsa_e_len, tp, rsa_e_len);
+          memset (tp, 0, 4-rsa_e_len);
+        }                 
+      tp += 4;
+      
+      *tp++ = 0xC1;
+      *tp++ = rsa_p_len;
+      memcpy (tp, rsa_p, rsa_p_len);
+      tp += rsa_p_len;
+      
+      *tp++ = 0xC2;
+      *tp++ = rsa_q_len;
+      memcpy (tp, rsa_q, rsa_q_len);
+      tp += rsa_q_len;
+      
+      assert (tp - template == template_len);
+      
+      /* Prepare for storing the key.  */
+      err = verify_chv3 (app, pincb, pincb_arg);
+      if (err)
+        goto leave;
+
+      /* Store the key. */
+      err = iso7816_put_data (app->slot, 0,
+                              (app->card_version > 0x0007? 0xE0:0xE9)+keyno,
+                              template, template_len);
+    }
   if (err)
     {
       log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
       goto leave;
     }
  
-  err = store_fpr (app->slot, keyno, created_at,
+  err = store_fpr (app, keyno, created_at,
                   rsa_n, rsa_n_len, rsa_e, rsa_e_len,
                   fprbuf, app->card_version);
   if (err)
@@ -1842,8 +2735,9 @@ do_writekey (app_t app, ctrl_t ctrl,
 /* Handle the GENKEY command. */
 static gpg_error_t 
 do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
-          gpg_error_t (*pincb)(void*, const char *, char **),
-          void *pincb_arg)
+           time_t createtime,
+           gpg_error_t (*pincb)(void*, const char *, char **),
+           void *pincb_arg)
 {
   int rc;
   char numbuf[30];
@@ -1855,6 +2749,9 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   int keyno = atoi (keynostr);
   int force = (flags & 1);
   time_t start_at;
+  int exmode;
+  int le_value;
+  unsigned int keybits; 
 
   if (keyno < 1 || keyno > 3)
     return gpg_error (GPG_ERR_INV_ID);
@@ -1871,28 +2768,49 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   app->app_local->pk[keyno].read_done = 0;
 
   /* Check whether a key already exists.  */
-  rc = does_key_exist (app, keyno, force);
+  rc = does_key_exist (app, keyno, 1, force);
   if (rc)
     return rc;
 
-  /* Prepare for key generation by verifying the ADmin PIN.  */
+  /* Because we send the key parameter back via status lines we need
+     to put a limit on the max. allowed keysize.  2048 bit will
+     already lead to a 527 byte long status line and thus a 4096 bit
+     key would exceed the Assuan line length limit.  */ 
+  keybits = app->app_local->keyattr[keyno].n_bits;
+  if (keybits > 3072)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+
+  /* Prepare for key generation by verifying the Admin PIN.  */
   rc = verify_chv3 (app, pincb, pincb_arg);
   if (rc)
     goto leave;
-   
-#if 1
+
+  /* Test whether we will need extended length mode.  (1900 is an
+     arbitrary length which for sure fits into a short apdu.)  */
+  if (app->app_local->cardcap.ext_lc_le && keybits > 1900)
+    {
+      exmode = 1;    /* Use extended length w/o a limit.  */
+      le_value = app->app_local->extcap.max_rsp_data;
+      /* No need to check le_value because it comes from a 16 bit
+         value and thus can't create an overflow on a 32 bit
+         system.  */ 
+    }
+  else
+    {
+      exmode = 0;
+      le_value = 256; /* Use legacy value. */
+    }
+
   log_info (_("please wait while key is being generated ...\n"));
   start_at = time (NULL);
   rc = iso7816_generate_keypair 
-#else
-#warning key generation temporary replaced by reading an existing key.
-  rc = iso7816_read_public_key
-#endif
-                              (app->slot, 
-                                 keyno == 0? "\xB6" :
-                                 keyno == 1? "\xB8" : "\xA4",
-                                 2,
-                                 &buffer, &buflen);
+/* # warning key generation temporary replaced by reading an existing key. */
+/*   rc = iso7816_read_public_key */
+    (app->slot, exmode, 
+     (const unsigned char*)(keyno == 0? "\xB6" :
+                            keyno == 1? "\xB8" : "\xA4"), 2,
+     le_value,
+     &buffer, &buflen);
   if (rc)
     {
       rc = gpg_error (GPG_ERR_CARD);
@@ -1901,6 +2819,7 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
     }
   log_info (_("key generation completed (%d seconds)\n"),
             (int)(time (NULL) - start_at));
+
   keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
   if (!keydata)
     {
@@ -1916,7 +2835,7 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
       log_error (_("response does not contain the RSA modulus\n"));
       goto leave;
     }
-/*    log_printhex ("RSA n:", m, mlen); */
+  /* log_printhex ("RSA n:", m, mlen); */
   send_key_data (ctrl, "n", m, mlen);
 
   e = find_tlv (keydata, keydatalen, 0x0082, &elen);
@@ -1926,15 +2845,15 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
       log_error (_("response does not contain the RSA public exponent\n"));
       goto leave;
     }
-/*    log_printhex ("RSA e:", e, elen); */
+  /* log_printhex ("RSA e:", e, elen); */
   send_key_data (ctrl, "e", e, elen);
 
-  created_at = gnupg_get_time ();
+  created_at = createtime? createtime : gnupg_get_time ();
   sprintf (numbuf, "%lu", (unsigned long)created_at);
   send_status_info (ctrl, "KEY-CREATED-AT",
                     numbuf, (size_t)strlen(numbuf), NULL, 0);
 
-  rc = store_fpr (app->slot, keyno, (u32)created_at,
+  rc = store_fpr (app, keyno, (u32)created_at,
                   m, mlen, e, elen, fprbuf, app->card_version);
   if (rc)
     goto leave;
@@ -1988,7 +2907,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
   
   assert (keyno >= 1 && keyno <= 3);
 
-  rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0);
+  rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0);
   if (rc)
     {
       log_error (_("error reading application data\n"));
@@ -2006,6 +2925,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
     if (sha1fpr[i] != fpr[i])
       {
         xfree (buffer);
+        log_info (_("fingerprint on card does not match requested one\n"));
         return gpg_error (GPG_ERR_WRONG_SECKEY);
       }
   xfree (buffer);
@@ -2013,11 +2933,11 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
 }
 
 
-  /* If a fingerprint has been specified check it against the one on
-     the card.  This is allows for a meaningful error message in case
-     the key on the card has been replaced but the shadow information
-     known to gpg was not updated.  If there is no fingerprint we
-     assume that this is okay. */
+/* If a fingerprint has been specified check it against the one on the
+   card.  This allows for a meaningful error message in case the key
+   on the card has been replaced but the shadow information known to
+   gpg has not been updated.  If there is no fingerprint we assume
+   that this is okay. */
 static gpg_error_t
 check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
 {
@@ -2045,10 +2965,14 @@ check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
    raw message digest. For this application the KEYIDSTR consists of
    the serialnumber and the fingerprint delimited by a slash.
 
-   Note that this fucntion may return the error code
+   Note that this function may return the error code
    GPG_ERR_WRONG_CARD to indicate that the card currently present does
    not match the one required for the requested action (e.g. the
-   serial number does not match). */
+   serial number does not match). 
+   
+   As a special feature a KEYIDSTR of "OPENPGP.3" redirects the
+   operation to the auth command.
+*/
 static gpg_error_t 
 do_sign (app_t app, const char *keyidstr, int hashalgo,
          gpg_error_t (*pincb)(void*, const char *, char **),
@@ -2056,59 +2980,102 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
          const void *indata, size_t indatalen,
          unsigned char **outdata, size_t *outdatalen )
 {
-  static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
-  { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
-    0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
   static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
-  { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
-    0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+    { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+      0x02, 0x01, 0x05, 0x00, 0x04, 0x14  };
+  static unsigned char sha1_prefix[15] =   /* (1.3.14.3.2.26) */
+    { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+      0x02, 0x1a, 0x05, 0x00, 0x04, 0x14  };
+  static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */
+    { 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+      0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04,
+      0x1C  };
+  static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */
+    { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+      0x00, 0x04, 0x20  };
+  static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */
+    { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
+      0x00, 0x04, 0x30  };
+  static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */
+    { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+      0x00, 0x04, 0x40  };
   int rc;
-  unsigned char data[35];
-  unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
+  unsigned char data[19+64];
+  size_t datalen;
+  unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */
   const char *s;
   int n;
   const char *fpr = NULL;
   unsigned long sigcount;
+  int use_auth = 0;
+  int exmode, le_value;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* Strip off known prefixes.  */
+#define X(a,b,c,d) \
+  if (hashalgo == GCRY_MD_ ## a                               \
+      && (d)                                                  \
+      && indatalen == sizeof b ## _prefix + (c)               \
+      && !memcmp (indata, b ## _prefix, sizeof b ## _prefix)) \
+    {                                                         \
+      indata = (const char*)indata + sizeof b ## _prefix;     \
+      indatalen -= sizeof b ## _prefix;                       \
+    }                                                         
+
   if (indatalen == 20)
-    ;
-  else if (indatalen == (15 + 20) && hashalgo == GCRY_MD_SHA1
-           && !memcmp (indata, sha1_prefix, 15))
-    ;
-  else if (indatalen == (15 + 20) && hashalgo == GCRY_MD_RMD160
-           && !memcmp (indata, rmd160_prefix, 15))
-    ;
+    ;  /* Assume a plain SHA-1 or RMD160 digest has been given.  */
+  else X(SHA1,   sha1,   20, 1)
+  else X(RMD160, rmd160, 20, 1)
+  else X(SHA224, sha224, 28, app->app_local->extcap.is_v2)
+  else X(SHA256, sha256, 32, app->app_local->extcap.is_v2)
+  else X(SHA384, sha384, 48, app->app_local->extcap.is_v2)
+  else X(SHA512, sha512, 64, app->app_local->extcap.is_v2)
+  else if ((indatalen == 28 || indatalen == 32 
+            || indatalen == 48 || indatalen ==64)
+           && app->app_local->extcap.is_v2)
+    ;  /* Assume a plain SHA-3 digest has been given.  */
   else
     {
-      log_error(_("card does not support digest algorithm %s\n"),
-               digest_algo_to_string(hashalgo));
+      log_error (_("card does not support digest algorithm %s\n"),
+                 gcry_md_algo_name (hashalgo));
+      /* Or the supplied digest length does not match an algorithm.  */
       return gpg_error (GPG_ERR_INV_VALUE);
     }
+#undef X
 
   /* Check whether an OpenPGP card of any version has been requested. */
-  if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
-    return gpg_error (GPG_ERR_INV_ID);
-  
-  for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+  if (!strcmp (keyidstr, "OPENPGP.1"))
     ;
-  if (n != 32)
+  else if (!strcmp (keyidstr, "OPENPGP.3"))
+    use_auth = 1;
+  else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
     return gpg_error (GPG_ERR_INV_ID);
-  else if (!*s)
-    ; /* no fingerprint given: we allow this for now. */
-  else if (*s == '/')
-    fpr = s + 1; 
   else
-    return gpg_error (GPG_ERR_INV_ID);
+    {
+      for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+       ;
+      if (n != 32)
+       return gpg_error (GPG_ERR_INV_ID);
+      else if (!*s)
+       ; /* no fingerprint given: we allow this for now. */
+      else if (*s == '/')
+       fpr = s + 1; 
+      else
+       return gpg_error (GPG_ERR_INV_ID);
 
-  for (s=keyidstr, n=0; n < 16; s += 2, n++)
-    tmp_sn[n] = xtoi_2 (s);
+      for (s=keyidstr, n=0; n < 16; s += 2, n++)
+       tmp_sn[n] = xtoi_2 (s);
 
-  if (app->serialnolen != 16)
-    return gpg_error (GPG_ERR_INV_CARD);
-  if (memcmp (app->serialno, tmp_sn, 16))
-    return gpg_error (GPG_ERR_WRONG_CARD);
+      if (app->serialnolen != 16)
+       return gpg_error (GPG_ERR_INV_CARD);
+      if (memcmp (app->serialno, tmp_sn, 16))
+       return gpg_error (GPG_ERR_WRONG_CARD);
+    }
 
   /* If a fingerprint has been specified check it against the one on
      the card.  This is allows for a meaningful error message in case
@@ -2120,59 +3087,56 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   if (rc)
     return rc;
 
-  if (hashalgo == GCRY_MD_SHA1)
-    memcpy (data, sha1_prefix, 15);
-  else if (hashalgo == GCRY_MD_RMD160)
-    memcpy (data, rmd160_prefix, 15);
+  /* Concatenate prefix and digest.  */
+#define X(a,b,d) \
+  if (hashalgo == GCRY_MD_ ## a && (d) )                      \
+    {                                                         \
+      datalen = sizeof b ## _prefix + indatalen;              \
+      assert (datalen <= sizeof data);                        \
+      memcpy (data, b ## _prefix, sizeof b ## _prefix);       \
+      memcpy (data + sizeof b ## _prefix, indata, indatalen); \
+    }                                                         
+
+  X(SHA1,   sha1,   1)
+  else X(RMD160, rmd160, 1)
+  else X(SHA224, sha224, app->app_local->extcap.is_v2)
+  else X(SHA256, sha256, app->app_local->extcap.is_v2)
+  else X(SHA384, sha384, app->app_local->extcap.is_v2)
+  else X(SHA512, sha512, app->app_local->extcap.is_v2)
   else 
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
-  memcpy (data+15, indata, indatalen);
+#undef X
 
+  /* Redirect to the AUTH command if asked to. */
+  if (use_auth)
+    {
+      return do_auth (app, "OPENPGP.3", pincb, pincb_arg,
+                      data, datalen,
+                      outdata, outdatalen);
+    }
+
+  /* Show the number of signature done using this key.  */
   sigcount = get_sig_counter (app);
   log_info (_("signatures created so far: %lu\n"), sigcount);
 
+  /* Check CHV if needed.  */
   if (!app->did_chv1 || app->force_chv1 ) 
     {
       char *pinvalue;
 
-      {
-        char *prompt;
-#define PROMPTSTRING  _("||Please enter the PIN%%0A[sigs done: %lu]")
-
-        prompt = malloc (strlen (PROMPTSTRING) + 50);
-        if (!prompt)
-          return gpg_error_from_errno (errno);
-        sprintf (prompt, PROMPTSTRING, sigcount);
-        rc = pincb (pincb_arg, prompt, &pinvalue); 
-        free (prompt);
-#undef PROMPTSTRING
-      }
+      rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue);
       if (rc)
-        {
-          log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
-          return rc;
-        }
+        return rc;
 
-      if (strlen (pinvalue) < 6)
-        {
-          log_error (_("PIN for CHV%d is too short;"
-                       " minimum length is %d\n"), 1, 6);
-          xfree (pinvalue);
-          return gpg_error (GPG_ERR_BAD_PIN);
-        }
-
-      rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
-      if (rc)
-        {
-          log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
-          xfree (pinvalue);
-          flush_cache_after_error (app);
-          return rc;
-        }
       app->did_chv1 = 1;
-      if (!app->did_chv2)
+
+      /* For cards with versions < 2 we want to keep CHV1 and CHV2 in
+         sync, thus we verify CHV2 here using the given PIN.  Cards
+         with version2 to not have the need for a separate CHV2 and
+         internally use just one.  Obviously we can't do that if the
+         keypad has been used. */
+      if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2)
         {
-          /* We should also verify CHV2. */
           rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
           if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
             rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
@@ -2188,7 +3152,19 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       xfree (pinvalue);
     }
 
-  rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+
+  if (app->app_local->cardcap.ext_lc_le)
+    {
+      exmode = 1;    /* Use extended length.  */
+      le_value = app->app_local->extcap.max_rsp_data;
+    }
+  else
+    {
+      exmode = 0;
+      le_value = 0; 
+    }
+  rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value,
+                           outdata, outdatalen);
   return rc;
 }
 
@@ -2198,7 +3174,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
    fingerprint delimited by a slash.  Optionally the id OPENPGP.3 may
    be given.
 
-   Note that this fucntion may return the error code
+   Note that this function may return the error code
    GPG_ERR_WRONG_CARD to indicate that the card currently present does
    not match the one required for the requested action (e.g. the
    serial number does not match). */
@@ -2210,14 +3186,14 @@ do_auth (app_t app, const char *keyidstr,
          unsigned char **outdata, size_t *outdatalen )
 {
   int rc;
-  unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
+  unsigned char tmp_sn[20]; /* Actually 16 but we use it also for the fpr. */
   const char *s;
   int n;
   const char *fpr = NULL;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (indatalen > 50) /* For a 1024 bit key. */
+  if (indatalen > 101) /* For a 2048 bit key. */
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* Check whether an OpenPGP card of any version has been requested. */
@@ -2259,8 +3235,23 @@ do_auth (app_t app, const char *keyidstr,
 
   rc = verify_chv2 (app, pincb, pincb_arg);
   if (!rc)
-    rc = iso7816_internal_authenticate (app->slot, indata, indatalen,
-                                        outdata, outdatalen);
+    {
+      int exmode, le_value;
+
+      if (app->app_local->cardcap.ext_lc_le)
+        {
+          exmode = 1;    /* Use extended length.  */
+          le_value = app->app_local->extcap.max_rsp_data;
+        }
+      else
+        {
+          exmode = 0;
+          le_value = 0; 
+        }
+      rc = iso7816_internal_authenticate (app->slot, exmode,
+                                          indata, indatalen, le_value,
+                                          outdata, outdatalen);
+    }
   return rc;
 }
 
@@ -2277,38 +3268,43 @@ do_decipher (app_t app, const char *keyidstr,
   const char *s;
   int n;
   const char *fpr = NULL;
+  int exmode;
 
   if (!keyidstr || !*keyidstr || !indatalen)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* Check whether an OpenPGP card of any version has been requested. */
-  if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
-    return gpg_error (GPG_ERR_INV_ID);
-  
-  for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+  if (!strcmp (keyidstr, "OPENPGP.2"))
     ;
-  if (n != 32)
+  else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
     return gpg_error (GPG_ERR_INV_ID);
-  else if (!*s)
-    ; /* no fingerprint given: we allow this for now. */
-  else if (*s == '/')
-    fpr = s + 1; 
   else
-    return gpg_error (GPG_ERR_INV_ID);
-
-  for (s=keyidstr, n=0; n < 16; s += 2, n++)
-    tmp_sn[n] = xtoi_2 (s);
-
-  if (app->serialnolen != 16)
-    return gpg_error (GPG_ERR_INV_CARD);
-  if (memcmp (app->serialno, tmp_sn, 16))
-    return gpg_error (GPG_ERR_WRONG_CARD);
+    {
+      for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+       ;
+      if (n != 32)
+       return gpg_error (GPG_ERR_INV_ID);
+      else if (!*s)
+       ; /* no fingerprint given: we allow this for now. */
+      else if (*s == '/')
+       fpr = s + 1; 
+      else
+       return gpg_error (GPG_ERR_INV_ID);
+      
+      for (s=keyidstr, n=0; n < 16; s += 2, n++)
+       tmp_sn[n] = xtoi_2 (s);
+      
+      if (app->serialnolen != 16)
+       return gpg_error (GPG_ERR_INV_CARD);
+      if (memcmp (app->serialno, tmp_sn, 16))
+       return gpg_error (GPG_ERR_WRONG_CARD);
+    }
 
   /* If a fingerprint has been specified check it against the one on
      the card.  This is allows for a meaningful error message in case
      the key on the card has been replaced but the shadow information
      known to gpg was not updated.  If there is no fingerprint, the
-     decryption will won't produce the right plaintext anyway. */
+     decryption won't produce the right plaintext anyway. */
   rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0;
   if (rc)
     return rc;
@@ -2317,6 +3313,8 @@ do_decipher (app_t app, const char *keyidstr,
   if (!rc)
     {
       size_t fixuplen;
+      unsigned char *fixbuf = NULL;
+      int padind = 0;
 
       /* We might encounter a couple of leading zeroes in the
          cryptogram.  Due to internal use of MPIs thease leading
@@ -2327,35 +3325,44 @@ do_decipher (app_t app, const char *keyidstr,
          probability anyway broken.  */
       if (indatalen >= (128-16) && indatalen < 128)      /* 1024 bit key.  */
         fixuplen = 128 - indatalen;
-      else if (indatalen >= (256-16) && indatalen < 256) /* 2048 bit key.  */
-        fixuplen = 256 - indatalen;
       else if (indatalen >= (192-16) && indatalen < 192) /* 1536 bit key.  */
         fixuplen = 192 - indatalen;
+      else if (indatalen >= (256-16) && indatalen < 256) /* 2048 bit key.  */
+        fixuplen = 256 - indatalen;
+      else if (indatalen >= (384-16) && indatalen < 384) /* 3072 bit key.  */
+        fixuplen = 384 - indatalen;
       else
         fixuplen = 0;
+
       if (fixuplen)
         {
-          unsigned char *fixbuf;
-
           /* While we have to prepend stuff anyway, we can also
              include the padding byte here so that iso1816_decipher
-             does not need to do yet another data mangling.  */
+             does not need to do another data mangling.  */
           fixuplen++;
+
           fixbuf = xtrymalloc (fixuplen + indatalen);
           if (!fixbuf)
-            rc = gpg_error_from_syserror ();
-          else
-            {
-              memset (fixbuf, 0, fixuplen);
-              memcpy (fixbuf+fixuplen, indata, indatalen);
-              rc = iso7816_decipher (app->slot, fixbuf, fixuplen+indatalen, -1,
-                                     outdata, outdatalen);
-              xfree (fixbuf);
-            }
+            return gpg_error_from_syserror ();
+          
+          memset (fixbuf, 0, fixuplen);
+          memcpy (fixbuf+fixuplen, indata, indatalen);
+          indata = fixbuf;
+          indatalen = fixuplen + indatalen;
+          padind = -1; /* Already padded.  */
         }
+      
+      if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
+        exmode = 1;    /* Extended length w/o a limit.  */
+      else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
+        exmode = -254; /* Command chaining with max. 254 bytes.  */
       else
-        rc = iso7816_decipher (app->slot, indata, indatalen, 0,
-                               outdata, outdatalen);
+        exmode = 0;    
+
+      rc = iso7816_decipher (app->slot, exmode, 
+                             indata, indatalen, padind,
+                             outdata, outdatalen);
+      xfree (fixbuf);
     }
 
   return rc;
@@ -2452,8 +3459,142 @@ do_check_pin (app_t app, const char *keyidstr,
 }
 
 
+/* Show information about card capabilities.  */
+static void
+show_caps (struct app_local_s *s)
+{
+  log_info ("Version-2 ......: %s\n", s->extcap.is_v2? "yes":"no");
+  log_info ("Get-Challenge ..: %s", s->extcap.get_challenge? "yes":"no");
+  if (s->extcap.get_challenge)
+    log_printf (" (%u bytes max)", s->extcap.max_get_challenge);
+  log_info ("Key-Import .....: %s\n", s->extcap.key_import? "yes":"no");
+  log_info ("Change-Force-PW1: %s\n", s->extcap.change_force_chv? "yes":"no");
+  log_info ("Private-DOs ....: %s\n", s->extcap.private_dos? "yes":"no");
+  log_info ("Algo-Attr-Change: %s\n", s->extcap.algo_attr_change? "yes":"no");
+  log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no");
+  if (s->extcap.sm_supported)
+    log_printf (" (%s)", s->extcap.sm_aes128? "AES-128":"3DES");
+  log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3);
+  log_info ("Max-Cmd-Data ...: %u\n", s->extcap.max_cmd_data);
+  log_info ("Max-Rsp-Data ...: %u\n", s->extcap.max_rsp_data);
+  log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no");
+  log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no");
+  log_info ("Status Indicator: %02X\n", s->status_indicator);
+
+  log_info ("GnuPG-No-Sync ..: %s\n",  s->flags.no_sync? "yes":"no");
+  log_info ("GnuPG-Def-PW2 ..: %s\n",  s->flags.def_chv2? "yes":"no");
+}
 
 
+/* Parse the historical bytes in BUFFER of BUFLEN and store them in
+   APPLOC.  */
+static void
+parse_historical (struct app_local_s *apploc, 
+                  const unsigned char * buffer, size_t buflen)
+{
+  /* Example buffer: 00 31 C5 73 C0 01 80 00 90 00  */
+  if (buflen < 4)
+    {
+      log_error ("warning: historical bytes are too short\n");
+      return; /* Too short.  */
+    }
+  if (*buffer)
+    {
+      log_error ("warning: bad category indicator in historical bytes\n");
+      return; 
+    }
+  
+  /* Skip category indicator.  */
+  buffer++;
+  buflen--;
+
+  /* Get the status indicator.  */
+  apploc->status_indicator = buffer[buflen-3];
+  buflen -= 3;
+
+  /* Parse the compact TLV.  */
+  while (buflen)
+    {
+      unsigned int tag = (*buffer & 0xf0) >> 4;
+      unsigned int len = (*buffer & 0x0f);
+      if (len+1 > buflen)
+        {
+          log_error ("warning: bad Compact-TLV in historical bytes\n");
+          return; /* Error.  */
+        }
+      buffer++;
+      buflen--;
+      if (tag == 7 && len == 3)
+        {
+          /* Card capabilities.  */
+          apploc->cardcap.cmd_chaining = !!(buffer[2] & 0x80);
+          apploc->cardcap.ext_lc_le    = !!(buffer[2] & 0x40);
+        }
+      buffer += len;
+      buflen -= len;
+    }
+}
+
+
+/* Parse and optionally show the algorithm attributes for KEYNO.
+   KEYNO must be in the range 0..2.  */
+static void 
+parse_algorithm_attribute (app_t app, int keyno)
+{ 
+  unsigned char *buffer;
+  size_t buflen;
+  void *relptr;
+  const char const desc[3][5] = {"sign", "encr", "auth"};
+
+  assert (keyno >=0 && keyno <= 2);
+
+  app->app_local->keyattr[keyno].n_bits = 0;
+      
+  relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
+  if (!relptr)
+    {
+      log_error ("error reading DO 0x%02X\n", 0xc1+keyno);
+      return;
+    }
+  if (buflen < 1)
+    {
+      log_error ("error reading DO 0x%02X\n", 0xc1+keyno);
+      xfree (relptr);
+      return;
+    }
+
+  if (opt.verbose)
+    log_info ("Key-Attr-%s ..: ", desc[keyno]);
+  if (*buffer == 1 && (buflen == 5 || buflen == 6))
+    {
+      app->app_local->keyattr[keyno].n_bits = (buffer[1]<<8 | buffer[2]);
+      app->app_local->keyattr[keyno].e_bits = (buffer[3]<<8 | buffer[4]);
+      app->app_local->keyattr[keyno].format = 0;
+      if (buflen < 6)
+        app->app_local->keyattr[keyno].format = RSA_STD;
+      else
+        app->app_local->keyattr[keyno].format = (buffer[5] == 0? RSA_STD   :
+                                                 buffer[5] == 1? RSA_STD_N :
+                                                 buffer[5] == 2? RSA_CRT   :
+                                                 buffer[5] == 3? RSA_CRT_N : 
+                                                 RSA_UNKNOWN_FMT);
+
+      if (opt.verbose)
+        log_printf
+          ("RSA, n=%u, e=%u, fmt=%s\n",
+           app->app_local->keyattr[keyno].n_bits,
+           app->app_local->keyattr[keyno].e_bits,
+           app->app_local->keyattr[keyno].format == RSA_STD?  "std"  :
+           app->app_local->keyattr[keyno].format == RSA_STD_N?"std+n":
+           app->app_local->keyattr[keyno].format == RSA_CRT?  "crt"  :
+           app->app_local->keyattr[keyno].format == RSA_CRT_N?"crt+n":"?");
+    }
+  else if (opt.verbose)
+    log_printhex ("", buffer, buflen);
+
+  xfree (relptr);
+}
+
 /* Select the OpenPGP application on the card in SLOT.  This function
    must be used before any other OpenPGP application functions. */
 gpg_error_t
@@ -2466,7 +3607,9 @@ app_select_openpgp (app_t app)
   size_t buflen;
   void *relptr;
   
-  rc = iso7816_select_application (slot, aid, sizeof aid);
+  /* Note that the card can't cope with P2=0xCO, thus we need to pass a
+     special flag value. */
+  rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001);
   if (!rc)
     {
       unsigned int manufacturer;
@@ -2483,7 +3626,7 @@ app_select_openpgp (app_t app)
          replace a possibly already set one from a EF.GDO with this
          one.  Note, that for current OpenPGP cards, no EF.GDO exists
          and thus it won't matter at all. */
-      rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
+      rc = iso7816_get_data (slot, 0, 0x004F, &buffer, &buflen);
       if (rc)
         goto leave;
       if (opt.verbose)
@@ -2507,6 +3650,24 @@ app_select_openpgp (app_t app)
           goto leave;
         }
 
+      if (app->card_version >= 0x0200)
+        app->app_local->extcap.is_v2 = 1;
+
+
+      /* Read the historical bytes.  */
+      relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL);
+      if (relptr)
+        {
+          if (opt.verbose)
+            {
+              log_info ("Historical Bytes: ");
+              log_printhex ("", buffer, buflen);
+            }
+          parse_historical (app->app_local, buffer, buflen);
+          xfree (relptr);
+        }
+
+      /* Read the force-chv1 flag.  */
       relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
       if (!relptr)
         {
@@ -2517,6 +3678,7 @@ app_select_openpgp (app_t app)
       app->force_chv1 = (buflen && *buffer == 0);
       xfree (relptr);
 
+      /* Read the extended capabilities.  */
       relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL);
       if (!relptr)
         {
@@ -2526,13 +3688,25 @@ app_select_openpgp (app_t app)
         }
       if (buflen)
         {
+          app->app_local->extcap.sm_supported     = !!(*buffer & 0x80);
           app->app_local->extcap.get_challenge    = !!(*buffer & 0x40);
           app->app_local->extcap.key_import       = !!(*buffer & 0x20);
           app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
           app->app_local->extcap.private_dos      = !!(*buffer & 0x08);
+          app->app_local->extcap.algo_attr_change = !!(*buffer & 0x04);
+        }
+      if (buflen >= 10)
+        {
+          /* Available with v2 cards.  */
+          app->app_local->extcap.sm_aes128     = (buffer[1] == 1);
+          app->app_local->extcap.max_get_challenge 
+                                               = (buffer[2] << 8 | buffer[3]);
+          app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
+          app->app_local->extcap.max_cmd_data  = (buffer[6] << 8 | buffer[7]);
+          app->app_local->extcap.max_rsp_data  = (buffer[8] << 8 | buffer[9]);
         }
       xfree (relptr);
-      
+
       /* Some of the first cards accidently don't set the
          CHANGE_FORCE_CHV bit but allow it anyway. */
       if (app->card_version <= 0x0100 && manufacturer == 1)
@@ -2540,14 +3714,23 @@ app_select_openpgp (app_t app)
 
       parse_login_data (app);
 
+      if (opt.verbose)
+        show_caps (app->app_local);
+
+      parse_algorithm_attribute (app, 0);
+      parse_algorithm_attribute (app, 1);
+      parse_algorithm_attribute (app, 2);
+      
       if (opt.verbose > 1)
         dump_all_do (slot);
 
       app->fnc.deinit = do_deinit;
       app->fnc.learn_status = do_learn_status;
+      app->fnc.readcert = do_readcert;
       app->fnc.readkey = do_readkey;
       app->fnc.getattr = do_getattr;
       app->fnc.setattr = do_setattr;
+      app->fnc.writecert = do_writecert;
       app->fnc.writekey = do_writekey;
       app->fnc.genkey = do_genkey;
       app->fnc.sign = do_sign;
index 07d8dda..8c402a0 100644 (file)
@@ -1,5 +1,5 @@
 /* card-util.c - Utility functions for the OpenPGP card.
- *     Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2005, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -25,7 +25,7 @@
 #include <assert.h>
 
 #if GNUPG_MAJOR_VERSION != 1
-#include "gpg.h"
+# include "gpg.h"
 #endif /*GNUPG_MAJOR_VERSION != 1*/
 #include "util.h"
 #include "i18n.h"
 #include "main.h"
 #include "keyserver-internal.h"
 #if GNUPG_MAJOR_VERSION == 1
-#ifdef HAVE_LIBREADLINE
-#include <stdio.h>
-#include <readline/readline.h>
-#endif /*HAVE_LIBREADLINE*/
-#include "cardglue.h"
+# 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 "call-agent.h"
 #endif /*GNUPG_MAJOR_VERSION!=1*/
 
 #define CONTROL_D ('D' - 'A' + 1)
 
 
+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:
+      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 a an OpenPGP card.  This is an interactive
    function. */
 void
-change_pin (int chvno, int allow_admin)
+change_pin (int unblock_v2, int allow_admin)
 {
   struct agent_card_info_s info;
   int rc;
@@ -75,16 +99,31 @@ change_pin (int chvno, int allow_admin)
       return;
     }
 
-  if(!allow_admin)
+
+  if (unblock_v2)
+    {
+      if (!info.is_v2)
+        log_error (_("This command is only available for version 2 cards\n"));
+      else if (!info.chvretry[1])
+        log_error (_("Reset Code not or not anymore available\n"));
+      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
+            tty_printf ("PIN changed.\n");
+        }
+    }
+  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
-        {
-          write_status (STATUS_SC_OP_SUCCESS);
-          tty_printf ("PIN changed.\n");
-        }
+        tty_printf ("PIN changed.\n");
     }
   else
     for (;;)
@@ -95,6 +134,7 @@ change_pin (int chvno, int allow_admin)
        tty_printf ("1 - change PIN\n"
                    "2 - unblock PIN\n"
                    "3 - change Admin PIN\n"
+                    "4 - set the Reset Code\n"
                    "Q - quit\n");
        tty_printf ("\n");
 
@@ -106,36 +146,44 @@ change_pin (int chvno, int allow_admin)
        rc = 0;
        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
-              {
-                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);
+            write_sc_op_status (rc);
            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);
+            write_sc_op_status (rc);
            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);
+            write_sc_op_status (rc);
+           if (rc)
+             tty_printf ("Error setting the Reset Code: %s\n", 
+                          gpg_strerror (rc));
+           else
+              tty_printf ("Reset Code set.\n");
          }
        else if (*answer == 'q' || *answer == 'Q')
          {
@@ -156,11 +204,13 @@ get_manufacturer (unsigned int no)
     case 0x0002: return "Prism";
     case 0x0003: return "OpenFortress";
     case 0x0004: return "Wewid AB";
+    case 0x0005: return "ZeitControl";
 
+    case 0x002A: return "Magrathea";
       /* 0x00000 and 0xFFFF are defined as test cards per spec,
          0xFFF00 to 0xFFFE are assigned for use with randomly created
          serial numbers.  */
-    case 0:
+    case 0x0000:
     case 0xffff: return "test card";
     default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown";
     }
@@ -287,6 +337,18 @@ fpr_is_zero (const char *fpr)
 }
 
 
+/* Return true if the SHA1 fingerprint FPR consists only of 0xFF. */
+static int
+fpr_is_ff (const char *fpr)
+{
+  int i;
+
+  for (i=0; i < 20 && fpr[i] == '\xff'; i++)
+    ;
+  return (i == 20);
+}
+
+
 /* Print all available information about the current card. */
 void
 card_status (FILE *fp, char *serialno, size_t serialnobuflen)
@@ -320,8 +382,35 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
   if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) 
       || strlen (info.serialno) != 32 )
     {
-      if (opt.with_colons)
-        fputs ("unknown:\n", fp);
+      if (info.apptype && !strcmp (info.apptype, "NKS"))
+        {
+          if (opt.with_colons)
+            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)
+            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)
+            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)
+            fputs ("geldkarte-card:\n", fp);
+          log_info ("this is a Geldkarte compliant card\n");
+        }
+      else
+        {
+          if (opt.with_colons)
+            fputs ("unknown:\n", fp);
+        }
       log_info ("not an OpenPGP card\n");
       agent_release_card_info (&info);
       xfree (pk);
@@ -367,6 +456,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
       fputs (":\n", fp);
 
       fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
+      for (i=0; i < DIM (info.key_attr); i++)
+        if (info.key_attr[0].algo)
+          fprintf (fp, "keyattr:%d:%d:%u:\n", i+1,
+                   info.key_attr[i].algo, info.key_attr[i].nbits);
       fprintf (fp, "maxpinlen:%d:%d:%d:\n",
                    info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
       fprintf (fp, "pinretry:%d:%d:%d:\n",
@@ -442,6 +535,16 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
         }
       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++)
+            tty_fprintf (fp, " %u%c",
+                         info.key_attr[i].nbits,
+                         info.key_attr[i].algo == 1? 'R':
+                         info.key_attr[i].algo == 17? 'D': '?');
+          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",
@@ -466,7 +569,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
 
       thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 : 
                 info.fpr3valid? info.fpr3 : NULL);
-      if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20))
+      /* 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))
         {
           KBNODE keyblock = NULL;
 
@@ -599,6 +705,7 @@ change_url (void)
   if (rc)
     log_error ("error setting URL: %s\n", gpg_strerror (rc));
   xfree (url);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -608,7 +715,6 @@ change_url (void)
 static int
 fetch_url(void)
 {
-#if GNUPG_MAJOR_VERSION == 1
   int rc;
   struct agent_card_info_s info;
 
@@ -648,9 +754,91 @@ fetch_url(void)
     }
 
   return rc;
-#else
-  return 0;
+}
+
+
+/* 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. */
+static int
+get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
+{
+  FILE *fp;
+  char *data;
+  int n;
+  
+  *r_buffer = NULL;
+
+  fp = fopen (fname, "rb");
+#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 open `%s': %s\n"), fname, strerror (errno));
+      return -1;
+    }
+          
+  data = xtrymalloc (maxlen? maxlen:1);
+  if (!data)
+    {
+      tty_printf (_("error allocating enough memory: %s\n"), strerror (errno));
+      fclose (fp);
+      return -1;
+    }
+
+  if (maxlen)
+    n = fread (data, 1, maxlen, fp);
+  else
+    n = 0;
+  fclose (fp);
+  if (n < 0)
+    {
+      tty_printf (_("error reading `%s': %s\n"), fname, strerror (errno));
+      xfree (data);
+      return -1;
+    }
+  *r_buffer = data;
+  return n;
+}
+
+
+/* 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)
+{
+  FILE *fp;
+  
+  fp = 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 && fwrite (buffer, length, 1, fp) != 1)
+    {
+      tty_printf (_("error writing `%s': %s\n"), fname, strerror (errno));
+      fclose (fp);
+      return -1;
+    }
+  fclose (fp);
+  return 0;
 }
 
 
@@ -663,34 +851,11 @@ change_login (const char *args)
 
   if (args && *args == '<')  /* Read it from a file */
     {
-      FILE *fp;
-
       for (args++; spacep (args); args++)
         ;
-      fp = fopen (args, "rb");
-#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 open `%s': %s\n"), args, strerror (errno));
-          return -1;
-        }
-          
-      data = xmalloc (254);
-      n = fread (data, 1, 254, fp);
-      fclose (fp);
+      n = get_data_from_file (args, 254, &data);
       if (n < 0)
-        {
-          tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
-          xfree (data);
-          return -1;
-        }
+        return -1;
     }
   else
     {
@@ -715,6 +880,7 @@ change_login (const char *args)
   if (rc)
     log_error ("error setting login data: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -731,35 +897,11 @@ change_private_do (const char *args, int nr)
 
   if (args && (args = strchr (args, '<')))  /* Read it from a file */
     {
-      FILE *fp;
-
-      /* Fixme: Factor this duplicated code out. */
       for (args++; spacep (args); args++)
         ;
-      fp = fopen (args, "rb");
-#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 open `%s': %s\n"), args, strerror (errno));
-          return -1;
-        }
-          
-      data = xmalloc (254);
-      n = fread (data, 1, 254, fp);
-      fclose (fp);
+      n = get_data_from_file (args, 254, &data);
       if (n < 0)
-        {
-          tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
-          xfree (data);
-          return -1;
-        }
+        return -1;
     }
   else
     {
@@ -784,9 +926,74 @@ change_private_do (const char *args, int nr)
   if (rc)
     log_error ("error setting private DO: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
+  return rc;
+}
+
+
+static int
+change_cert (const char *args)
+{
+  char *data;
+  int n;
+  int rc;
+
+  if (args && *args == '<')  /* Read it from a file */
+    {
+      for (args++; spacep (args); args++)
+        ;
+      n = get_data_from_file (args, 16384, &data);
+      if (n < 0)
+        return -1;
+    }
+  else
+    {
+      tty_printf ("usage error: redirectrion to file required\n");
+      return -1;
+    }
+
+#warning need to implement this fucntion
+  rc = -1; /*agent_scd_writecert ("OPENPGP.3", data, n);*/
+  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: redirectrion to file required\n");
+      return -1;
+    }
+
+#warning need to implement this fucntion
+  rc = -1; /*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;
+}
+
+
 static int
 change_lang (void)
 {
@@ -820,6 +1027,7 @@ change_lang (void)
   if (rc)
     log_error ("error setting lang: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -855,6 +1063,7 @@ change_sex (void)
   if (rc)
     log_error ("error setting sex: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -899,6 +1108,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));
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -924,6 +1134,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));
+  write_sc_op_status (rc);
 }
 
 
@@ -963,7 +1174,7 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
 
   *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);
@@ -981,8 +1192,11 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
          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;
 }
 
@@ -1003,7 +1217,7 @@ restore_forced_chv1 (int *forced_chv1)
     }
 }
 
-#if GNUPG_MAJOR_VERSION == 1
+
 /* Helper for the key generation/edit functions.  */
 static void
 show_card_key_info (struct agent_card_info_s *info)
@@ -1016,9 +1230,8 @@ show_card_key_info (struct agent_card_info_s *info)
   print_sha1_fpr (NULL, info->fpr3valid? info->fpr3:NULL);
   tty_printf ("\n");
 }
-#endif
 
-#if GNUPG_MAJOR_VERSION == 1
+
 /* Helper for the key generation/edit functions.  */
 static int
 replace_existing_key_p (struct agent_card_info_s *info, int keyno)
@@ -1038,11 +1251,10 @@ replace_existing_key_p (struct agent_card_info_s *info, int keyno)
     }
   return 0;
 }
-#endif
 
 
 static void
-generate_card_keys (const char *serialno)
+generate_card_keys (void)
 {
   struct agent_card_info_s info;
   int forced_chv1;
@@ -1094,12 +1306,8 @@ generate_card_keys (const char *serialno)
   if (check_pin_for_key_operation (&info, &forced_chv1))
     goto leave;
   
-#if GNUPG_MAJOR_VERSION == 1
   generate_keypair (NULL, info.serialno,
                     want_backup? opt.homedir:NULL);
-#else
-  generate_keypair (NULL, info.serialno);
-#endif
 
  leave:
   agent_release_card_info (&info);
@@ -1112,7 +1320,6 @@ generate_card_keys (const char *serialno)
 int
 card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
 {
-#if GNUPG_MAJOR_VERSION == 1
   struct agent_card_info_s info;
   int okay = 0;
   int forced_chv1 = 0;
@@ -1159,9 +1366,6 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
   agent_release_card_info (&info);
   restore_forced_chv1 (&forced_chv1);
   return okay;
-#else
-  return 0;
-#endif
 }
 
 
@@ -1172,7 +1376,6 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
 int 
 card_store_subkey (KBNODE node, int use)
 {
-#if GNUPG_MAJOR_VERSION == 1
   struct agent_card_info_s info;
   int okay = 0;
   int rc;
@@ -1192,7 +1395,8 @@ card_store_subkey (KBNODE node, int use)
 
   show_card_key_info (&info);
 
-  if (!is_RSA (sk->pubkey_algo) || nbits_from_sk (sk) != 1024 )
+  if (!is_RSA (sk->pubkey_algo) 
+      || (!info.is_v2 && nbits_from_sk (sk) != 1024) )
     {
       tty_printf ("You may only store a 1024 bit RSA key on the card\n");
       tty_printf ("\n");
@@ -1260,7 +1464,10 @@ card_store_subkey (KBNODE node, int use)
 
   rc = save_unprotected_key_to_card (sk, keyno);
   if (rc)
-    goto leave;
+    {
+      log_error (_("error writing key to card: %s\n"), gpg_strerror (rc));
+      goto leave;
+    }
 
   /* Get back to the maybe protected original secret key.  */
   if (copied_sk)
@@ -1274,11 +1481,11 @@ card_store_subkey (KBNODE node, int use)
   n = pubkey_get_nskey (sk->pubkey_algo);
   for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++)
     {
-      mpi_free (sk->skey[i]);
+      gcry_mpi_release (sk->skey[i]);
       sk->skey[i] = NULL;
     }
   i = pubkey_get_npkey (sk->pubkey_algo);
-  sk->skey[i] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10);
+  sk->skey[i] = gcry_mpi_set_opaque (NULL, xstrdup ("dummydata"), 10*8);
   sk->is_protected = 1;
   sk->protect.s2k.mode = 1002;
   s = info.serialno;
@@ -1293,9 +1500,6 @@ card_store_subkey (KBNODE node, int use)
     free_secret_key (copied_sk);
   agent_release_card_info (&info);
   return okay;
-#else
-  return 0;
-#endif
 }
 
 
@@ -1307,7 +1511,8 @@ enum cmdids
     cmdNOP = 0,
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
-    cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO,
+    cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
+    cmdREADCERT, cmdUNBLOCK,
     cmdINVCMD
   };
 
@@ -1338,8 +1543,11 @@ static struct
     { "generate", cmdGENERATE, 1, N_("generate new keys")},
     { "passwd"  , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
     { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
-    /* Note, that we do not announce this command yet. */
+    { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
+    /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
+    { "readcert", cmdREADCERT, 0, NULL },
+    { "writecert", cmdWRITECERT, 1, NULL },
     { NULL, cmdINVCMD, 0, NULL } 
   };
 
@@ -1392,13 +1600,13 @@ card_edit_completion(const char *text, int start, int end)
 /* Menu to edit all user changeable values on an OpenPGP card.  Only
    Key creation is not handled here. */
 void
-card_edit (STRLIST commands)
+card_edit (strlist_t commands)
 {
   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];
 
 
@@ -1414,6 +1622,7 @@ card_edit (STRLIST commands)
     {
       int arg_number;
       const char *arg_string = "";
+      const char *arg_rest = "";
       char *p;
       int i;
       int cmd_admin_only;
@@ -1482,6 +1691,11 @@ card_edit (STRLIST commands)
               trim_spaces (p);
               arg_number = atoi(p);
               arg_string = p;
+              arg_rest = p;
+              while (digitp (arg_rest))
+                arg_rest++;
+              while (spacep (arg_rest))
+                arg_rest++;
             }
           
           for (i=0; cmds[i].name; i++ )
@@ -1580,17 +1794,34 @@ card_edit (STRLIST commands)
             change_private_do (arg_string, arg_number);
           break;
 
+        case cmdWRITECERT:
+          if ( arg_number != 3 )
+            tty_printf ("usage: writecert 3 < FILE\n");
+          else
+            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:
-          generate_card_keys (serialnobuf);
+          generate_card_keys ();
           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 cmdQUIT:
index b09aec3..0833d60 100644 (file)
@@ -190,6 +190,37 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
   va_end (arg_ptr);
 }
 
+/* Send a ready formatted status line via assuan.  */
+void
+send_status_direct (ctrl_t ctrl, const char *keyword, const char *args)
+{
+  char buf[950];
+
+  if (strchr (args, '\n'))
+    log_error ("error: LF detected in status line - not sending\n");
+  else
+    {
+      snprintf (buf, sizeof buf, "%s%s%s",
+                keyword, args? " ":"", args? args:"");
+      if (ctrl && ctrl->status_cb)
+        ctrl->status_cb (ctrl->status_cb_arg, buf);
+    }
+}
+
+
+
+void
+gcry_mpi_release (MPI a)
+{
+  mpi_free (a);
+}
+
+MPI 
+gcry_mpi_set_opaque (MPI a, void *p, unsigned int len)
+{
+  return mpi_set_opaque (a, p, len);
+}
+
 
 /* Replacement function of the Libgcrypt onewhich is used in gnupg
    1.9.  Thus function computes the digest of ALGO from the data in
@@ -208,6 +239,17 @@ gcry_md_hash_buffer (int algo, void *digest,
 }
 
 
+/* This function simply returns the name of the algorithm or some
+   constant string when there is no algo.  It will never return
+   NULL.  */
+const char *
+gcry_md_algo_name (int algorithm)
+{
+  const char *s = digest_algo_to_string (algorithm);
+  return s ? s : "?";
+}
+
+
 /* This is a limited version of the one in 1.9 but it should be
    sufficient here. */
 void
@@ -297,6 +339,7 @@ agent_release_card_info (struct agent_card_info_s *info)
     return;
 
   xfree (info->serialno); info->serialno = NULL;
+  xfree (info->apptype); info->apptype = NULL;
   xfree (info->disp_name); info->disp_name = NULL;
   xfree (info->disp_lang); info->disp_lang = NULL;
   xfree (info->pubkey_url); info->pubkey_url = NULL;
@@ -448,7 +491,7 @@ open_card (void)
     }
 
  ready:
-  app->initialized = 1;
+  app->ref_count = 1;
   current_app = app;
   if (is_status_enabled () )
     {
@@ -629,6 +672,15 @@ store_serialno (const char *line)
   return p;
 }
 
+/* Return a new malloced string by unescaping the string S.  Escaping
+   is percent escaping and '+'/space mapping.  A binary nul will
+   silently be replaced by a 0xFF.  Function returns NULL to indicate
+   an out of memory status. */
+static char *
+unescape_status_string (const unsigned char *s)
+{
+  return unescape_percent_string (s);
+}
 
 
 static assuan_error_t
@@ -649,6 +701,13 @@ learn_status_cb (void *opaque, const char *line)
     {
       xfree (parm->serialno);
       parm->serialno = store_serialno (line);
+      parm->is_v2 = (strlen (parm->serialno) >= 16 
+                     && xtoi_2 (parm->serialno+12) >= 2 );
+    }
+  else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen))
+    {
+      xfree (parm->apptype);
+      parm->apptype = unescape_status_string (line);
     }
   else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
     {
@@ -761,6 +820,18 @@ learn_status_cb (void *opaque, const char *line)
       xfree (parm->private_do[no]);
       parm->private_do[no] = unescape_percent_string (line);
     }
+  else if (keywordlen == 8 && !memcmp (keyword, "KEY-ATTR", keywordlen))
+    {
+      int keyno, algo, nbits;
+
+      sscanf (line, "%d %d %d", &keyno, &algo, &nbits);
+      keyno--;
+      if (keyno >= 0 && keyno < DIM (parm->key_attr))
+        {
+          parm->key_attr[keyno].algo = algo;
+          parm->key_attr[keyno].nbits = nbits;
+        }
+    }
  
   return 0;
 }
@@ -801,7 +872,7 @@ agent_learn (struct agent_card_info_s *info)
           send_status_info (&ctrl, "SERIALNO",
                             serial, strlen(serial), NULL, 0);
           xfree (serial);
-          rc = app->fnc.learn_status (app, &ctrl);
+          rc = app->fnc.learn_status (app, &ctrl, 0);
         }
     }
 
@@ -1132,7 +1203,7 @@ genkey_status_cb (void *opaque, const char *line)
 /* Send a GENKEY command to the SCdaemon. */
 int 
 agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
-                  const char *serialno)
+                  const char *serialno, u32 *createtime)
 {
   app_t app;
   char line[ASSUAN_LINELENGTH];
@@ -1166,6 +1237,7 @@ agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
       ctrl.status_cb_arg = info;
       rc = app->fnc.genkey (app, &ctrl, line,
                             force? 1:0,
+                            *createtime,
                             pin_cb, &parm);
     }
 
index e6a11c3..f1b51c8 100644 (file)
 
 #ifdef ENABLE_CARD_SUPPORT
 /* 
-   Note, that most card related code has been taken from 1.9.x branch
+   Note, that most card related code has been taken from 2.x branch
    and is maintained over there if at all possible.  Thus, if you make
    changes here, please check that a similar change has been commited
-   to the 1.9.x branch.
+   to the 2.x branch.
 */
 
+/* We don't use libgcrypt but the shared codes uses a function type
+   from libgcrypt.  Thus we have to provide this type here.  */
+typedef void (*gcry_handler_progress_t) (void *, const char *, int, int, int);
 
+
+/* Object to hold all info about the card.  */
 struct agent_card_info_s {
   int error;         /* private. */
+  char *apptype;     /* Malloced application type string.  */
   char *serialno;    /* malloced hex string. */
   char *disp_name;   /* malloced. */
   char *disp_lang;   /* malloced. */
@@ -56,8 +62,13 @@ struct agent_card_info_s {
   int chv1_cached;   /* True if a PIN is not required for each
                         signing.  Note that the gpg-agent might cache
                         it anyway. */
+  int is_v2;         /* True if this is a v2 card.  */
   int chvmaxlen[3];  /* Maximum allowed length of a CHV. */
   int chvretry[3];   /* Allowed retries for the CHV; 0 = blocked. */
+  struct {           /* Array with key attributes.  */
+    int algo;              /* Algorithm identifier.  */
+    unsigned int nbits;    /* Supported keysize.  */
+  } key_attr[3];      
 };
 
 struct agent_card_genkey_s {
@@ -147,14 +158,24 @@ void card_set_reader_port (const char *portstr);
 char *serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
                                 PKT_secret_key *sk);
 void send_status_info (ctrl_t ctrl, const char *keyword, ...);
+void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args);
 void gcry_md_hash_buffer (int algo, void *digest,
                          const void *buffer, size_t length);
+const char *gcry_md_algo_name (int algorithm);
 void log_printf (const char *fmt, ...);
 void log_printhex (const char *text, const void *buffer, size_t length);
 
 
 #define GCRY_MD_SHA1 DIGEST_ALGO_SHA1
 #define GCRY_MD_RMD160 DIGEST_ALGO_RMD160
+#define GCRY_MD_SHA256 DIGEST_ALGO_SHA256
+#define GCRY_MD_SHA384 DIGEST_ALGO_SHA384
+#define GCRY_MD_SHA512 DIGEST_ALGO_SHA512
+#define GCRY_MD_SHA224 DIGEST_ALGO_SHA224
+
+void gcry_mpi_release (MPI a);
+MPI  gcry_mpi_set_opaque (MPI a, void *p, unsigned int len);
+
 
 void card_close (void);
 
@@ -183,7 +204,7 @@ int agent_scd_writekey (int keyno, const char *serialno,
 
 /* Send a GENKEY command to the SCdaemon. */
 int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
-                      const char *serialno);
+                      const char *serialno, u32 *createtime);
 
 /* Send a PKSIGN command to the SCdaemon. */
 int agent_scd_pksign (const char *keyid, int hashalgo,
index 97e7bf9..b71c43c 100644 (file)
@@ -1,6 +1,7 @@
 /* ccid-driver.c - USB ChipCardInterfaceDevices driver
- *     Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
- *      Written by Werner Koch.
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007
+ *               2008, 2009  Free Software Foundation, Inc.
+ * Written by Werner Koch.
  *
  * This file is part of GnuPG.
  *
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_PTH
+# include <pth.h>
+#endif /*HAVE_PTH*/
 
 #include <usb.h>
 
 #endif /* This source not used by scdaemon. */
 
 
+#ifndef EAGAIN
+#define EAGAIN  EWOULDBLOCK
+#endif
+
+
 
 enum {
   RDR_to_PC_NotifySlotChange= 0x50,
@@ -198,7 +208,8 @@ enum {
   VENDOR_CHERRY = 0x046a,
   VENDOR_SCM    = 0x04e6,
   VENDOR_OMNIKEY= 0x076b,
-  VENDOR_GEMPC  = 0x08e6
+  VENDOR_GEMPC  = 0x08e6,
+  VENDOR_KAAN   = 0x0d46
 };
 
 /* A list and a table with special transport descriptions. */
@@ -237,13 +248,24 @@ struct ccid_driver_s
   int seqno;
   unsigned char t1_ns;
   unsigned char t1_nr;
-  int nonnull_nad;
-  int auto_ifsd;
+  unsigned char nonnull_nad;
   int max_ifsd;
   int ifsd;
-  int powered_off;
-  int has_pinpad;
-  int apdu_level;     /* Reader supports short APDU level exchange.  */
+  int ifsc;
+  unsigned char apdu_level:2;     /* Reader supports short APDU level
+                                     exchange.  With a value of 2 short
+                                     and extended level is supported.*/
+  unsigned int auto_ifsd:1;
+  unsigned int powered_off:1;
+  unsigned int has_pinpad:2;
+  unsigned int enodev_seen:1;
+
+  time_t last_progress; /* Last time we sent progress line.  */
+
+  /* The progress callback and its first arg as supplied to
+     ccid_set_progress_cb.  */
+  void (*progress_cb)(void *, const char *, int, int, int);
+  void *progress_cb_arg;
 };
 
 
@@ -251,16 +273,19 @@ static int initialized_usb; /* Tracks whether USB has been initialized. */
 static int debug_level;     /* Flag to control the debug output. 
                                0 = No debugging
                                1 = USB I/O info
-                               2 = T=1 protocol tracing
+                               2 = Level 1 + T=1 protocol tracing
+                               3 = Level 2 + USB/I/O tracing of SlotStatus.
                               */
 
 
 static unsigned int compute_edc (const unsigned char *data, size_t datalen,
                                  int use_crc);
-static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
+static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
+                     int no_debug);
 static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
                     size_t *nread, int expected_type, int seqno, int timeout,
                     int no_debug);
+static int abort_cmd (ccid_driver_t handle, int seqno);
 
 /* Convert a little endian stored 4 byte value into an unsigned
    integer. */
@@ -270,6 +295,15 @@ convert_le_u32 (const unsigned char *buf)
   return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); 
 }
 
+
+/* Convert a little endian stored 2 byte value into an unsigned
+   integer. */
+static unsigned int 
+convert_le_u16 (const unsigned char *buf)
+{
+  return buf[0] | (buf[1] << 8); 
+}
+
 static void
 set_msg_len (unsigned char *msg, unsigned int length)
 {
@@ -280,8 +314,49 @@ set_msg_len (unsigned char *msg, unsigned int length)
 }
 
 
+static void
+my_sleep (int seconds)
+{
+#ifdef HAVE_PTH
+  /* With Pth we also call the standard sleep(0) so that the process
+     may give up its timeslot.  */
+  if (!seconds)
+    {
+# ifdef HAVE_W32_SYSTEM    
+      Sleep (0);
+# else
+      sleep (0);
+# endif
+    }
+  pth_sleep (seconds);
+#else
+# ifdef HAVE_W32_SYSTEM    
+  Sleep (seconds*1000);
+# else
+  sleep (seconds);
+# endif
+#endif
+}
+
+static void
+print_progress (ccid_driver_t handle)
+{
+  time_t ct = time (NULL);
+
+  /* We don't want to print progress lines too often. */
+  if (ct == handle->last_progress)
+    return;
+
+  if (handle->progress_cb)
+    handle->progress_cb (handle->progress_cb_arg, "card_busy", 'w', 0, 0);
+
+  handle->last_progress = ct;
+}
+
+
+
 /* Pint an error message for a failed CCID command including a textual
-   error code.  MSG is shall be the CCID message of at least 10 bytes. */
+   error code.  MSG shall be the CCID message at a minimum of 10 bytes. */
 static void
 print_command_failed (const unsigned char *msg)
 {
@@ -325,8 +400,318 @@ print_command_failed (const unsigned char *msg)
     }
   DEBUGOUT_1 ("CCID command failed: %s\n", t);
 }
+
+
+static void
+print_pr_data (const unsigned char *data, size_t datalen, size_t off)
+{
+  int any = 0;
+
+  for (; off < datalen; off++)
+    {
+      if (!any || !(off % 16))
+        {
+          if (any)
+            DEBUGOUT_LF ();
+          DEBUGOUT_1 ("  [%04d] ", off);
+        }
+      DEBUGOUT_CONT_1 (" %02X", data[off]);
+      any = 1;
+    }
+  if (any && (off % 16))
+    DEBUGOUT_LF ();
+}
+
+static void
+print_p2r_header (const char *name, const unsigned char *msg, size_t msglen)
+{
+  DEBUGOUT_1 ("%s:\n", name);
+  if (msglen < 7)
+    return;
+  DEBUGOUT_1 ("  dwLength ..........: %u\n", convert_le_u32 (msg+1));
+  DEBUGOUT_1 ("  bSlot .............: %u\n", msg[5]);
+  DEBUGOUT_1 ("  bSeq ..............: %u\n", msg[6]);
+}
+
+
+static void
+print_p2r_iccpoweron (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_2 ("  bPowerSelect ......: 0x%02x (%s)\n", msg[7],
+              msg[7] == 0? "auto":
+              msg[7] == 1? "5.0 V":
+              msg[7] == 2? "3.0 V":
+              msg[7] == 3? "1.8 V":"");
+  print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_getslotstatus (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_xfrblock (const unsigned char *msg, size_t msglen)
+{
+  unsigned int val;
+
+  print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bBWI ..............: 0x%02x\n", msg[7]);
+  val = convert_le_u16 (msg+8);
+  DEBUGOUT_2 ("  wLevelParameter ...: 0x%04x%s\n", val,
+              val == 1? " (continued)":
+              val == 2? " (continues+ends)":
+              val == 3? " (continues+continued)":
+              val == 16? " (DataBlock-expected)":"");
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_getparameters (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_resetparameters (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_setparameters (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bProtocolNum ......: 0x%02x\n", msg[7]);
+  print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_escape (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_Escape", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_iccclock (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_IccClock", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bClockCommand .....: 0x%02x\n", msg[7]);
+  print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_to0apdu (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bmChanges .........: 0x%02x\n", msg[7]);
+  DEBUGOUT_1 ("  bClassGetResponse .: 0x%02x\n", msg[8]);
+  DEBUGOUT_1 ("  bClassEnvelope ....: 0x%02x\n", msg[9]);
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_secure (const unsigned char *msg, size_t msglen)
+{
+  unsigned int val;
+
+  print_p2r_header ("PC_to_RDR_Secure", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bBMI ..............: 0x%02x\n", msg[7]);
+  val = convert_le_u16 (msg+8);
+  DEBUGOUT_2 ("  wLevelParameter ...: 0x%04x%s\n", val,
+              val == 1? " (continued)":
+              val == 2? " (continues+ends)":
+              val == 3? " (continues+continued)":
+              val == 16? " (DataBlock-expected)":"");
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_mechanical (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bFunction .........: 0x%02x\n", msg[7]);
+  print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_abort (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_Abort", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_setdatarate (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen);
+  if (msglen < 10)
+    return;
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_unknown (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("Unknown PC_to_RDR command", msg, msglen);
+  if (msglen < 10)
+    return;
+  print_pr_data (msg, msglen, 0);
+}
+
+
+static void
+print_r2p_header (const char *name, const unsigned char *msg, size_t msglen)
+{
+  DEBUGOUT_1 ("%s:\n", name);
+  if (msglen < 9)
+    return;
+  DEBUGOUT_1 ("  dwLength ..........: %u\n", convert_le_u32 (msg+1));
+  DEBUGOUT_1 ("  bSlot .............: %u\n", msg[5]);
+  DEBUGOUT_1 ("  bSeq ..............: %u\n", msg[6]);
+  DEBUGOUT_1 ("  bStatus ...........: %u\n", msg[7]);
+  if (msg[8])
+    DEBUGOUT_1 ("  bError ............: %u\n", msg[8]);
+}
+
+
+static void
+print_r2p_datablock (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen);
+  if (msglen < 10)
+    return;
+  if (msg[9])
+    DEBUGOUT_2 ("  bChainParameter ...: 0x%02x%s\n", msg[9],
+                msg[9] == 1? " (continued)":
+                msg[9] == 2? " (continues+ends)":
+                msg[9] == 3? " (continues+continued)":
+                msg[9] == 16? " (XferBlock-expected)":"");
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_slotstatus (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_2 ("  bClockStatus ......: 0x%02x%s\n", msg[9],
+              msg[9] == 0? " (running)":
+              msg[9] == 1? " (stopped-L)":
+              msg[9] == 2? " (stopped-H)":
+              msg[9] == 3? " (stopped)":"");
+  print_pr_data (msg, msglen, 10);
+}
   
 
+static void
+print_r2p_parameters (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_Parameters", msg, msglen);
+  if (msglen < 10)
+    return;
+
+  DEBUGOUT_1 ("  protocol ..........: T=%d\n", msg[9]);
+  if (msglen == 17 && msg[9] == 1)
+    {
+      /* Protocol T=1.  */
+      DEBUGOUT_1 ("  bmFindexDindex ....: %02X\n", msg[10]);
+      DEBUGOUT_1 ("  bmTCCKST1 .........: %02X\n", msg[11]);
+      DEBUGOUT_1 ("  bGuardTimeT1 ......: %02X\n", msg[12]);
+      DEBUGOUT_1 ("  bmWaitingIntegersT1: %02X\n", msg[13]);
+      DEBUGOUT_1 ("  bClockStop ........: %02X\n", msg[14]);
+      DEBUGOUT_1 ("  bIFSC .............: %d\n", msg[15]);
+      DEBUGOUT_1 ("  bNadValue .........: %d\n", msg[16]);
+    }
+  else
+    print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_escape (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_Escape", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  buffer[9] .........: %02X\n", msg[9]);
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_datarate (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_DataRate", msg, msglen);
+  if (msglen < 10)
+    return;
+  if (msglen >= 18)
+    {
+      DEBUGOUT_1 ("  dwClockFrequency ..: %u\n", convert_le_u32 (msg+10));
+      DEBUGOUT_1 ("  dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14));
+      print_pr_data (msg, msglen, 18);
+    }
+  else
+    print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_unknown (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("Unknown RDR_to_PC command", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bMessageType ......: %02X\n", msg[0]);
+  DEBUGOUT_1 ("  buffer[9] .........: %02X\n", msg[9]);
+  print_pr_data (msg, msglen, 10);
+}
+
+
 /* Given a handle used for special transport prepare it for use.  In
    particular setup all information in way that resembles what
    parse_cccid_descriptor does. */
@@ -492,7 +877,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
   else if ((us & 0x00040000))
     {
       DEBUGOUT ("    Short and extended APDU level exchange\n");
-      handle->apdu_level = 1;
+      handle->apdu_level = 2;
     }
   else if ((us & 0x00070000))
     DEBUGOUT ("    WARNING: conflicting exchange levels\n");
@@ -986,13 +1371,24 @@ scan_or_find_devices (int readerno, const char *readerid,
       char *rid, *p;
 
       fd = open (transports[i].name, O_RDWR);
-      if (fd == -1)
-        continue;
+      if (fd == -1 && scan_mode && errno == EBUSY)
+        {
+          /* Ignore this error in scan mode because it indicates that
+             the device exists but is already open (most likely by us)
+             and thus in general suitable as a reader.  */
+        }
+      else if (fd == -1)
+        {
+          DEBUGOUT_2 ("failed to open `%s': %s\n",
+                     transports[i].name, strerror (errno));
+          continue;
+        }
 
       rid = malloc (strlen (transports[i].name) + 30 + 10);
       if (!rid)
         {
-          close (fd);
+          if (fd != -1)
+            close (fd);
           free (rid_list);
           return -1; /* Error. */
         }
@@ -1003,7 +1399,8 @@ scan_or_find_devices (int readerno, const char *readerid,
           p = malloc ((rid_list? strlen (rid_list):0) + 1 + strlen (rid) + 1);
           if (!p)
             {
-              close (fd);
+              if (fd != -1)
+                close (fd);
               free (rid_list);
               free (rid);
               return -1; /* Error. */
@@ -1039,7 +1436,8 @@ scan_or_find_devices (int readerno, const char *readerid,
             --readerno;
         }
       free (rid);
-      close (fd);
+      if (fd != -1)
+        close (fd);
     }
 
   if (scan_mode)
@@ -1055,7 +1453,11 @@ scan_or_find_devices (int readerno, const char *readerid,
 /* Set the level of debugging to LEVEL and return the old level.  -1
    just returns the old level.  A level of 0 disables debugging, 1
    enables debugging, 2 enables additional tracing of the T=1
-   protocol, other values are not yet defined. */
+   protocol, 3 additionally enables debugging for GetSlotStatus, other
+   values are not yet defined.
+
+   Note that libusb may provide its own debugging feature which is
+   enabled by setting the envvar USB_DEBUG.  */
 int
 ccid_set_debug_level (int level)
 {
@@ -1228,7 +1630,7 @@ do_close_reader (ccid_driver_t handle)
       set_msg_len (msg, 0);
       msglen = 10;
       
-      rc = bulk_out (handle, msg, msglen);
+      rc = bulk_out (handle, msg, msglen, 0);
       if (!rc)
         bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
                  seqno, 2000, 0);
@@ -1321,6 +1723,20 @@ ccid_shutdown_reader (ccid_driver_t handle)
 }
 
 
+int 
+ccid_set_progress_cb (ccid_driver_t handle, 
+                      void (*cb)(void *, const char *, int, int, int),
+                      void *cb_arg)
+{
+  if (!handle || !handle->rid)
+    return CCID_DRIVER_ERR_INV_VALUE;
+
+  handle->progress_cb = cb;
+  handle->progress_cb_arg = cb_arg;
+  return 0;
+}
+
+
 /* Close the reader HANDLE. */
 int 
 ccid_close_reader (ccid_driver_t handle)
@@ -1339,7 +1755,7 @@ ccid_close_reader (ccid_driver_t handle)
 int
 ccid_check_card_presence (ccid_driver_t handle)
 {
-
+  (void)handle;  /* Not yet implemented.  */
   return -1;
 }
 
@@ -1372,20 +1788,97 @@ writen (int fd, const void *buf, size_t nbytes)
 /* Write a MSG of length MSGLEN to the designated bulk out endpoint.
    Returns 0 on success. */
 static int
-bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
+bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
+          int no_debug)
 {
   int rc;
 
+  /* No need to continue and clutter the log with USB write error
+     messages after we got the first ENODEV.  */
+  if (handle->enodev_seen)
+    return CCID_DRIVER_ERR_NO_READER;
+
+  if (debug_level && (!no_debug || debug_level >= 3))
+    {
+      switch (msglen? msg[0]:0)
+        {
+        case PC_to_RDR_IccPowerOn:
+          print_p2r_iccpoweron (msg, msglen);
+          break;
+        case PC_to_RDR_IccPowerOff:
+          print_p2r_iccpoweroff (msg, msglen);
+          break;
+       case PC_to_RDR_GetSlotStatus:
+          print_p2r_getslotstatus (msg, msglen);
+          break;
+        case PC_to_RDR_XfrBlock:
+          print_p2r_xfrblock (msg, msglen);
+          break;
+       case PC_to_RDR_GetParameters:
+          print_p2r_getparameters (msg, msglen);
+          break;
+        case PC_to_RDR_ResetParameters:
+          print_p2r_resetparameters (msg, msglen);
+          break;
+       case PC_to_RDR_SetParameters:
+          print_p2r_setparameters (msg, msglen);
+          break;
+        case PC_to_RDR_Escape:
+          print_p2r_escape (msg, msglen);
+          break;
+        case PC_to_RDR_IccClock:
+          print_p2r_iccclock (msg, msglen);
+          break;
+       case PC_to_RDR_T0APDU:
+          print_p2r_to0apdu (msg, msglen);
+          break;
+        case PC_to_RDR_Secure:
+          print_p2r_secure (msg, msglen);
+          break;
+        case PC_to_RDR_Mechanical:
+          print_p2r_mechanical (msg, msglen);
+          break;
+        case PC_to_RDR_Abort:
+          print_p2r_abort (msg, msglen);
+          break;
+        case PC_to_RDR_SetDataRate:
+          print_p2r_setdatarate (msg, msglen);
+          break;
+        default:
+          print_p2r_unknown (msg, msglen);
+          break;
+        }
+    }
+  
   if (handle->idev)
     {
       rc = usb_bulk_write (handle->idev, 
                            handle->ep_bulk_out,
                            (char*)msg, msglen,
-                           1000 /* ms timeout */);
+                           5000 /* ms timeout */);
       if (rc == msglen)
         return 0;
+#ifdef ENODEV
+      if (rc == -(ENODEV))
+        {
+          /* The Linux libusb returns a negative error value.  Catch
+             the most important one.  */
+          errno = ENODEV;
+          rc = -1;
+        }
+#endif /*ENODEV*/
+
       if (rc == -1)
-        DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
+        {
+          DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
+#ifdef ENODEV
+          if (errno == ENODEV)
+            {
+              handle->enodev_seen = 1;
+              return CCID_DRIVER_ERR_NO_READER;
+            }
+#endif /*ENODEV*/
+        }
       else
         DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc);
     }
@@ -1407,14 +1900,16 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
    is the sequence number used to send the request and EXPECTED_TYPE
    the type of message we expect. Does checks on the ccid
    header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to
-   avoid debug messages in case of no error. Returns 0 on success. */
+   avoid debug messages in case of no error; this can be overriden
+   with a glibal debug level of at least 3. Returns 0 on success. */
 static int
 bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
          size_t *nread, int expected_type, int seqno, int timeout,
          int no_debug)
 {
-  int i, rc;
+  int rc;
   size_t msglen;
+  int eagain_retries = 0;
 
   /* Fixme: The next line for the current Valgrind without support
      for USB IOCTLs. */
@@ -1428,7 +1923,13 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
                           timeout);
       if (rc < 0)
         {
-          DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno));
+          rc = errno;
+          DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (rc));
+          if (rc == EAGAIN && eagain_retries++ < 3)
+            {
+              my_sleep (1);
+              goto retry;
+            }
           return CCID_DRIVER_ERR_CARD_IO_ERROR;
         }
       *nread = msglen = rc;
@@ -1438,22 +1939,24 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       rc = read (handle->dev_fd, buffer, length);
       if (rc < 0)
         {
+          rc = errno;
           DEBUGOUT_2 ("read from %d failed: %s\n",
-                      handle->dev_fd, strerror (errno));
+                      handle->dev_fd, strerror (rc));
+          if (rc == EAGAIN && eagain_retries++ < 5)
+            {
+              my_sleep (1);
+              goto retry;
+            }
           return CCID_DRIVER_ERR_CARD_IO_ERROR;
         }
       *nread = msglen = rc;
     }
-
+  eagain_retries = 0;
 
   if (msglen < 10)
     {
       DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
-      return CCID_DRIVER_ERR_INV_VALUE;
-    }
-  if (buffer[0] != expected_type)
-    {
-      DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
+      abort_cmd (handle, seqno);
       return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[5] != 0)    
@@ -1465,9 +1968,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
     {
       DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
                   seqno, buffer[6]);
-      return CCID_DRIVER_ERR_INV_VALUE;
+      /* Retry until we are synced again.  */
+      goto retry;
     }
 
+  /* We need to handle the time extension request before we check that
+     we got the expected message type.  This is in particular required
+     for the Cherry keyboard which sends a time extension request for
+     each key hit.  */
   if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
     { 
       /* Card present and active, time extension requested. */
@@ -1476,13 +1984,36 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       goto retry;
     }
 
-  if (!no_debug)
+  if (buffer[0] != expected_type)
     {
-      DEBUGOUT_3 ("status: %02X  error: %02X  octet[9]: %02X\n"
-                  "               data:",  buffer[7], buffer[8], buffer[9] );
-      for (i=10; i < msglen; i++)
-        DEBUGOUT_CONT_1 (" %02X", buffer[i]);
-      DEBUGOUT_LF ();
+      DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
+      abort_cmd (handle, seqno);
+      return CCID_DRIVER_ERR_INV_VALUE;
+    }
+
+  if (debug_level && (!no_debug || debug_level >= 3))
+    {
+      switch (buffer[0])
+        {
+        case RDR_to_PC_DataBlock:
+          print_r2p_datablock (buffer, msglen);
+          break;
+        case RDR_to_PC_SlotStatus:
+          print_r2p_slotstatus (buffer, msglen);
+          break;
+        case RDR_to_PC_Parameters:
+          print_r2p_parameters (buffer, msglen);
+          break;
+        case RDR_to_PC_Escape:
+          print_r2p_escape (buffer, msglen);
+          break;
+        case RDR_to_PC_DataRate:
+          print_r2p_datarate (buffer, msglen);
+          break;
+        default:
+          print_r2p_unknown (buffer, msglen);
+          break;
+        }
     }
   if (CCID_COMMAND_FAILED (buffer))
     print_command_failed (buffer);
@@ -1501,6 +2032,110 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
 }
 
 
+
+/* Send an abort sequence and wait until everything settled.  */
+static int
+abort_cmd (ccid_driver_t handle, int seqno)
+{
+  int rc;
+  char dummybuf[8];
+  unsigned char msg[100];
+  size_t msglen;
+
+  if (!handle->idev)
+    {
+      /* I don't know how to send an abort to non-USB devices.  */
+      rc = CCID_DRIVER_ERR_NOT_SUPPORTED;
+    }
+  
+  seqno &= 0xff;
+  DEBUGOUT_1 ("sending abort sequence for seqno %d\n", seqno);
+  /* Send the abort command to the control pipe.  Note that we don't
+     need to keep track of sent abort commands because there should
+     never be another thread using the same slot concurrently.  */
+  rc = usb_control_msg (handle->idev, 
+                        0x21,/* bmRequestType: host-to-device,
+                                class specific, to interface.  */
+                        1,   /* ABORT */
+                        (seqno << 8 | 0 /* slot */),
+                        handle->ifc_no,
+                        dummybuf, 0,
+                        1000 /* ms timeout */);
+  if (rc < 0)
+    {
+      DEBUGOUT_1 ("usb_control_msg error: %s\n", strerror (errno));
+      return CCID_DRIVER_ERR_CARD_IO_ERROR;
+    }
+
+  /* Now send the abort command to the bulk out pipe using the same
+     SEQNO and SLOT.  Do this in a loop to so that all seqno are
+     tried.  */
+  seqno--;  /* Adjust for next increment.  */
+  do
+    {
+      seqno++; 
+      msg[0] = PC_to_RDR_Abort;
+      msg[5] = 0; /* slot */
+      msg[6] = seqno;
+      msg[7] = 0; /* RFU */
+      msg[8] = 0; /* RFU */
+      msg[9] = 0; /* RFU */
+      msglen = 10;
+      set_msg_len (msg, 0);
+
+      rc = usb_bulk_write (handle->idev, 
+                           handle->ep_bulk_out,
+                           (char*)msg, msglen,
+                           5000 /* ms timeout */);
+      if (rc == msglen)
+        rc = 0;
+      else if (rc == -1)
+        DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n", 
+                    strerror (errno));
+      else
+        DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc);
+
+      if (rc)
+        return rc;
+      
+      rc = usb_bulk_read (handle->idev, 
+                          handle->ep_bulk_in,
+                          (char*)msg, sizeof msg, 
+                          5000 /*ms timeout*/);
+      if (rc < 0)
+        {
+          DEBUGOUT_1 ("usb_bulk_read error in abort_cmd: %s\n",
+                      strerror (errno));
+          return CCID_DRIVER_ERR_CARD_IO_ERROR;
+        }
+      msglen = rc;
+
+      if (msglen < 10)
+        {
+          DEBUGOUT_1 ("bulk-in msg in abort_cmd too short (%u)\n",
+                      (unsigned int)msglen);
+          return CCID_DRIVER_ERR_INV_VALUE;
+        }
+      if (msg[5] != 0)    
+        {
+          DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]);
+          return CCID_DRIVER_ERR_INV_VALUE;
+        }
+
+      DEBUGOUT_3 ("status: %02X  error: %02X  octet[9]: %02X\n",
+                  msg[7], msg[8], msg[9]);
+      if (CCID_COMMAND_FAILED (msg))
+        print_command_failed (msg);
+    }
+  while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno);
+
+  handle->seqno = ((seqno + 1) & 0xff);
+  DEBUGOUT ("sending abort sequence succeeded\n");
+
+  return 0;
+}
+
+
 /* Note that this function won't return the error codes NO_CARD or
    CARD_INACTIVE.  IF RESULT is not NULL, the result from the
    operation will get returned in RESULT and its length in RESULTLEN.
@@ -1511,7 +2146,7 @@ send_escape_cmd (ccid_driver_t handle,
                  const unsigned char *data, size_t datalen,
                  unsigned char *result, size_t resultmax, size_t *resultlen)
 {
-  int i, rc;
+  int rc;
   unsigned char msg[100];
   size_t msglen;
   unsigned char seqno;
@@ -1532,11 +2167,7 @@ send_escape_cmd (ccid_driver_t handle,
   msglen = 10 + datalen;
   set_msg_len (msg, datalen);
 
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape,
@@ -1637,7 +2268,7 @@ ccid_poll (ccid_driver_t handle)
 }
 
 
-/* Note that this fucntion won't return the error codes NO_CARD or
+/* Note that this function won't return the error codes NO_CARD or
    CARD_INACTIVE */
 int 
 ccid_slot_status (ccid_driver_t handle, int *statusbits)
@@ -1657,7 +2288,7 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits)
   msg[9] = 0; /* RFU */
   set_msg_len (msg, 0);
 
-  rc = bulk_out (handle, msg, 10);
+  rc = bulk_out (handle, msg, 10, 1);
   if (rc)
     return rc;
   /* Note that we set the NO_DEBUG flag here, so that the logs won't
@@ -1687,6 +2318,8 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits)
 }
 
 
+/* Return the ATR of the card.  This is not a cached value and thus an
+   actual reset is done.  */
 int 
 ccid_get_atr (ccid_driver_t handle,
               unsigned char *atr, size_t maxatrlen, size_t *atrlen)
@@ -1699,7 +2332,6 @@ ccid_get_atr (ccid_driver_t handle,
   unsigned char seqno;
   int use_crc = 0;
   unsigned int edc;
-  int i;
   int tried_iso = 0;
   int got_param;
 
@@ -1710,7 +2342,6 @@ ccid_get_atr (ccid_driver_t handle,
   if (statusbits == 2)
     return CCID_DRIVER_ERR_NO_CARD;
 
-    
   /* For an inactive and also for an active card, issue the PowerOn
      command to get the ATR.  */
  again:
@@ -1723,7 +2354,7 @@ ccid_get_atr (ccid_driver_t handle,
   set_msg_len (msg, 0);
   msglen = 10;
 
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
@@ -1768,34 +2399,14 @@ ccid_get_atr (ccid_driver_t handle,
   msg[9] = 0; /* RFU */
   set_msg_len (msg, 0);
   msglen = 10;
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (!rc)
     rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
                   seqno, 2000, 0);
   if (rc)
     DEBUGOUT ("GetParameters failed\n");
-  else
-    {
-      DEBUGOUT ("GetParametes returned");
-      for (i=0; i < msglen; i++)
-        DEBUGOUT_CONT_1 (" %02X", msg[i]);
-      DEBUGOUT_LF ();
-      if (msglen >= 10)
-        {
-          DEBUGOUT_1 ("  protocol ..........: T=%d\n", msg[9]);
-          if (msglen == 17 && msg[9] == 1)
-            {
-              DEBUGOUT_1 ("  bmFindexDindex ....: %02X\n", msg[10]);
-              DEBUGOUT_1 ("  bmTCCKST1 .........: %02X\n", msg[11]);
-              DEBUGOUT_1 ("  bGuardTimeT1 ......: %02X\n", msg[12]);
-              DEBUGOUT_1 ("  bmWaitingIntegersT1: %02X\n", msg[13]);
-              DEBUGOUT_1 ("  bClockStop ........: %02X\n", msg[14]);
-              DEBUGOUT_1 ("  bIFSC .............: %d\n", msg[15]);
-              DEBUGOUT_1 ("  bNadValue .........: %d\n", msg[16]);
-              got_param = 1;
-            }
-        }
-    }
+  else if (msglen == 17 && msg[9] == 1)
+    got_param = 1;
 
   /* Setup parameters to select T=1. */
   msg[0] = PC_to_RDR_SetParameters;
@@ -1819,12 +2430,7 @@ ccid_get_atr (ccid_driver_t handle,
   set_msg_len (msg, 7);
   msglen = 10 + 7;
 
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
@@ -1832,6 +2438,11 @@ ccid_get_atr (ccid_driver_t handle,
   if (rc)
     DEBUGOUT ("SetParameters failed (ignored)\n");
 
+  if (!rc && msglen > 15 && msg[15] >= 16 && msg[15] <= 254 )
+    handle->ifsc = msg[15];
+  else
+    handle->ifsc = 128; /* Something went wrong, assume 128 bytes.  */
+
   handle->t1_ns = 0;
   handle->t1_nr = 0;
 
@@ -1859,11 +2470,6 @@ ccid_get_atr (ccid_driver_t handle,
       set_msg_len (msg, tpdulen);
       msglen = 10 + tpdulen;
 
-      DEBUGOUT ("sending");
-      for (i=0; i < msglen; i++)
-        DEBUGOUT_CONT_1 (" %02X", msg[i]);
-      DEBUGOUT_LF ();
-
       if (debug_level > 1)
         DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
                       ((msg[11] & 0xc0) == 0x80)? 'R' :
@@ -1872,7 +2478,7 @@ ccid_get_atr (ccid_driver_t handle,
                                        : !!(msg[11] & 0x40)),
                     (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
 
-      rc = bulk_out (handle, msg, msglen);
+      rc = bulk_out (handle, msg, msglen, 0);
       if (rc)
         return rc;
 
@@ -1929,6 +2535,16 @@ compute_edc (const unsigned char *data, size_t datalen, int use_crc)
 }
 
 
+/* Return true if APDU is an extended length one.  */
+static int
+is_exlen_apdu (const unsigned char *apdu, size_t apdulen)
+{
+  if (apdulen < 7 || apdu[4])
+    return 0;  /* Too short or no Z byte.  */
+  return 1;
+}
+
+
 /* Helper for ccid_transceive used for APDU level exchanges.  */
 static int
 ccid_transceive_apdu_level (ccid_driver_t handle,
@@ -1937,13 +2553,13 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
                             size_t *nresp)
 {
   int rc;
-  unsigned char send_buffer[10+259], recv_buffer[10+259];
+  unsigned char send_buffer[10+261+300], recv_buffer[10+261+300];
   const unsigned char *apdu;
   size_t apdulen;
   unsigned char *msg;
   size_t msglen;
   unsigned char seqno;
-  int i;
+  int bwi = 4;
 
   msg = send_buffer;
 
@@ -1951,25 +2567,23 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
   apdulen = apdu_buflen;
   assert (apdulen);
 
-  if (apdulen > 254)
+  /* The maximum length for a short APDU T=1 block is 261.  For an
+     extended APDU T=1 block the maximum length 65544; however
+     extended APDU exchange level is not yet supported.  */
+  if (apdulen > 261)
     return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
-
+  
   msg[0] = PC_to_RDR_XfrBlock;
   msg[5] = 0; /* slot */
   msg[6] = seqno = handle->seqno++;
-  msg[7] = 4; /* bBWI */
+  msg[7] = bwi; /* bBWI */
   msg[8] = 0; /* RFU */
   msg[9] = 0; /* RFU */
   memcpy (msg+10, apdu, apdulen);
   set_msg_len (msg, apdulen);
   msglen = 10 + apdulen;
 
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-  
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
 
@@ -2059,19 +2673,24 @@ ccid_transceive (ccid_driver_t handle,
                  unsigned char *resp, size_t maxresplen, size_t *nresp)
 {
   int rc;
-  unsigned char send_buffer[10+259], recv_buffer[10+259];
+  /* The size of the buffer used to be 10+259.  For the via_escape
+     hack we need one extra byte, thus 11+259.  */
+  unsigned char send_buffer[11+259], recv_buffer[11+259];
   const unsigned char *apdu;
   size_t apdulen;
   unsigned char *msg, *tpdu, *p;
   size_t msglen, tpdulen, last_tpdulen, n;
   unsigned char seqno;
-  int i;
   unsigned int edc;
   int use_crc = 0;
+  int hdrlen, pcboff;
   size_t dummy_nresp;
+  int via_escape = 0;
   int next_chunk = 1;
   int sending = 1;
   int retries = 0;
+  int resyncing = 0;
+  int nad_byte;
 
   if (!nresp)
     nresp = &dummy_nresp;
@@ -2079,13 +2698,37 @@ ccid_transceive (ccid_driver_t handle,
 
   /* Smarter readers allow to send APDUs directly; divert here. */
   if (handle->apdu_level)
-    return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen,
-                                       resp, maxresplen, nresp);
+    {
+      /* We employ a hack for Omnikey readers which are able to send
+         TPDUs using an escape sequence.  There is no documentation
+         but the Windows driver does it this way.  Tested using a
+         CM6121.  This method works also for the Cherry XX44
+         keyboards; however there are problems with the
+         ccid_tranceive_secure which leads to a loss of sync on the
+         CCID level.  If Cherry wants to make their keyboard work
+         again, they should hand over some docs. */
+      if ((handle->id_vendor == VENDOR_OMNIKEY
+           || (!handle->idev && handle->id_product == TRANSPORT_CM4040))
+          && handle->apdu_level < 2
+          && is_exlen_apdu (apdu_buf, apdu_buflen))
+        via_escape = 1;
+      else
+        return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen,
+                                           resp, maxresplen, nresp);
+    }
 
   /* The other readers we support require sending TPDUs.  */
 
   tpdulen = 0; /* Avoid compiler warning about no initialization. */
   msg = send_buffer;
+  hdrlen = via_escape? 11 : 10;
+
+  /* NAD: DAD=1, SAD=0 */
+  nad_byte = handle->nonnull_nad? ((1 << 4) | 0): 0;
+  if (via_escape)
+    nad_byte = 0;
+
+  last_tpdulen = 0;  /* Avoid gcc warning (controlled by RESYNCING). */
   for (;;)
     {
       if (next_chunk)
@@ -2097,18 +2740,14 @@ ccid_transceive (ccid_driver_t handle,
           assert (apdulen);
 
           /* Construct an I-Block. */
-          if (apdulen > 254)
-            return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
-
-          tpdu = msg+10;
-          /* NAD: DAD=1, SAD=0 */
-          tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+          tpdu = msg + hdrlen;
+          tpdu[0] = nad_byte;
           tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
-          if (apdulen > 128 /* fixme: replace by ifsc */)
+          if (apdulen > handle->ifsc )
             {
-              apdulen = 128;
-              apdu_buf += 128;  
-              apdu_buflen -= 128;
+              apdulen = handle->ifsc;
+              apdu_buf += handle->ifsc;  
+              apdu_buflen -= handle->ifsc;
               tpdu[1] |= (1 << 5); /* Set more bit. */
             }
           tpdu[2] = apdulen;
@@ -2120,42 +2759,56 @@ ccid_transceive (ccid_driver_t handle,
           tpdu[tpdulen++] = edc;
         }
 
-      msg[0] = PC_to_RDR_XfrBlock;
-      msg[5] = 0; /* slot */
-      msg[6] = seqno = handle->seqno++;
-      msg[7] = 4; /* bBWI */
-      msg[8] = 0; /* RFU */
-      msg[9] = 0; /* RFU */
-      set_msg_len (msg, tpdulen);
-      msglen = 10 + tpdulen;
-      last_tpdulen = tpdulen;
-
-      DEBUGOUT ("sending");
-      for (i=0; i < msglen; i++)
-        DEBUGOUT_CONT_1 (" %02X", msg[i]);
-      DEBUGOUT_LF ();
+      if (via_escape)
+        {
+          msg[0] = PC_to_RDR_Escape;
+          msg[5] = 0; /* slot */
+          msg[6] = seqno = handle->seqno++;
+          msg[7] = 0; /* RFU */
+          msg[8] = 0; /* RFU */
+          msg[9] = 0; /* RFU */
+          msg[10] = 0x1a; /* Omnikey command to send a TPDU.  */
+          set_msg_len (msg, 1 + tpdulen);
+        }
+      else
+        {
+          msg[0] = PC_to_RDR_XfrBlock;
+          msg[5] = 0; /* slot */
+          msg[6] = seqno = handle->seqno++;
+          msg[7] = 4; /* bBWI */
+          msg[8] = 0; /* RFU */
+          msg[9] = 0; /* RFU */
+          set_msg_len (msg, tpdulen);
+        }
+      msglen = hdrlen + tpdulen;
+      if (!resyncing)
+        last_tpdulen = tpdulen;
+      pcboff = hdrlen+1;
 
       if (debug_level > 1)
-          DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
-                      ((msg[11] & 0xc0) == 0x80)? 'R' :
-                                (msg[11] & 0x80)? 'S' : 'I',
-                      ((msg[11] & 0x80)? !!(msg[11]& 0x10)
-                                       : !!(msg[11] & 0x40)),
-                      (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
-
-      rc = bulk_out (handle, msg, msglen);
+        DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
+                    ((msg[pcboff] & 0xc0) == 0x80)? 'R' :
+                    (msg[pcboff] & 0x80)? 'S' : 'I',
+                    ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10)
+                     : !!(msg[pcboff] & 0x40)),
+                    (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
+                     " [more]":""));
+      
+      rc = bulk_out (handle, msg, msglen, 0);
       if (rc)
         return rc;
 
       msg = recv_buffer;
       rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
-                    RDR_to_PC_DataBlock, seqno, 5000, 0);
+                    via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock, 
+                    seqno, 5000, 0);
       if (rc)
         return rc;
-      
-      tpdu = msg + 10;
-      tpdulen = msglen - 10;
-      
+
+      tpdu = msg + hdrlen;
+      tpdulen = msglen - hdrlen;
+      resyncing = 0;
+            
       if (tpdulen < 4) 
         {
           usb_clear_halt (handle->idev, handle->ep_bulk_in);
@@ -2164,11 +2817,13 @@ ccid_transceive (ccid_driver_t handle,
 
       if (debug_level > 1)
         DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
-                    ((msg[11] & 0xc0) == 0x80)? 'R' :
-                              (msg[11] & 0x80)? 'S' : 'I',
-                    ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
-                    ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
-                    (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+                    ((msg[pcboff] & 0xc0) == 0x80)? 'R' :
+                              (msg[pcboff] & 0x80)? 'S' : 'I',
+                    ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10)
+                     : !!(msg[pcboff] & 0x40)),
+                    ((msg[pcboff] & 0xc0) == 0x80)? (msg[pcboff] & 0x0f) : 0,
+                    (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
+                     " [more]":""));
 
       if (!(tpdu[1] & 0x80))
         { /* This is an I-block. */
@@ -2182,9 +2837,8 @@ ccid_transceive (ccid_driver_t handle,
           if (!!(tpdu[1] & 0x40) != handle->t1_nr)
             { /* Reponse does not match our sequence number. */
               msg = send_buffer;
-              tpdu = msg+10;
-              /* NAD: DAD=1, SAD=0 */
-              tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+              tpdu = msg + hdrlen;
+              tpdu[0] = nad_byte;
               tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
               tpdu[2] = 0;
               tpdulen = 3;
@@ -2221,9 +2875,8 @@ ccid_transceive (ccid_driver_t handle,
             return 0; /* No chaining requested - ready. */
           
           msg = send_buffer;
-          tpdu = msg+10;
-          /* NAD: DAD=1, SAD=0 */
-          tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+          tpdu = msg + hdrlen;
+          tpdu[0] = nad_byte;
           tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
           tpdu[2] = 0;
           tpdulen = 3;
@@ -2235,14 +2888,37 @@ ccid_transceive (ccid_driver_t handle,
       else if ((tpdu[1] & 0xc0) == 0x80)
         { /* This is a R-block. */
           if ( (tpdu[1] & 0x0f)) 
-            { /* Error: repeat last block */
-              if (++retries > 3)
+            { 
+              retries++;
+              if (via_escape && retries == 1 && (msg[pcboff] & 0x0f))
+                {
+                  /* Error probably due to switching to TPDU.  Send a
+                     resync request.  We use the recv_buffer so that
+                     we don't corrupt the send_buffer.  */
+                  msg = recv_buffer;
+                  tpdu = msg + hdrlen;
+                  tpdu[0] = nad_byte;
+                  tpdu[1] = 0xc0; /* S-block resync request. */
+                  tpdu[2] = 0;
+                  tpdulen = 3;
+                  edc = compute_edc (tpdu, tpdulen, use_crc);
+                  if (use_crc)
+                    tpdu[tpdulen++] = (edc >> 8);
+                  tpdu[tpdulen++] = edc;
+                  resyncing = 1;
+                  DEBUGOUT ("T=1: requesting resync\n");
+                }
+              else if (retries > 3)
                 {
-                  DEBUGOUT ("3 failed retries\n");
+                  DEBUGOUT ("T=1: 3 failed retries\n");
                   return CCID_DRIVER_ERR_CARD_IO_ERROR;
                 }
-              msg = send_buffer;
-              tpdulen = last_tpdulen;
+              else
+                {
+                  /* Error: repeat last block */
+                  msg = send_buffer;
+                  tpdulen = last_tpdulen;
+                }
             }
           else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns)
             { /* Response does not match our sequence number. */
@@ -2265,16 +2941,37 @@ ccid_transceive (ccid_driver_t handle,
       else 
         { /* This is a S-block. */
           retries = 0;
-          DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n",
+          DEBUGOUT_2 ("T=1: S-block %s received cmd=%d\n",
                       (tpdu[1] & 0x20)? "response": "request",
                       (tpdu[1] & 0x1f));
-          if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
-            { /* Wait time extension request. */
+          if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 1 && tpdu[2] == 1)
+            {
+              /* Information field size request.  */
+              unsigned char ifsc = tpdu[3];
+
+              if (ifsc < 16 || ifsc > 254)
+                return CCID_DRIVER_ERR_CARD_IO_ERROR;
+
+              msg = send_buffer;
+              tpdu = msg + hdrlen;
+              tpdu[0] = nad_byte;
+              tpdu[1] = (0xc0 | 0x20 | 1); /* S-block response */
+              tpdu[2] = 1;
+              tpdu[3] = ifsc;
+              tpdulen = 4;
+              edc = compute_edc (tpdu, tpdulen, use_crc);
+              if (use_crc)
+                tpdu[tpdulen++] = (edc >> 8);
+              tpdu[tpdulen++] = edc;
+              DEBUGOUT_1 ("T=1: requesting an ifsc=%d\n", ifsc);
+            }
+          else if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
+            {
+              /* Wait time extension request. */
               unsigned char bwi = tpdu[3];
               msg = send_buffer;
-              tpdu = msg+10;
-              /* NAD: DAD=1, SAD=0 */
-              tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+              tpdu = msg + hdrlen;
+              tpdu[0] = nad_byte;
               tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
               tpdu[2] = 1;
               tpdu[3] = bwi;
@@ -2283,7 +2980,15 @@ ccid_transceive (ccid_driver_t handle,
               if (use_crc)
                 tpdu[tpdulen++] = (edc >> 8);
               tpdu[tpdulen++] = edc;
-              DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi);
+              DEBUGOUT_1 ("T=1: waittime extension of bwi=%d\n", bwi);
+              print_progress (handle);
+            }
+          else if ( (tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 0 && !tpdu[2])
+            {
+              DEBUGOUT ("T=1: resync ack from reader\n");
+              /* Repeat previous block.  */
+              msg = send_buffer;
+              tpdulen = last_tpdulen;
             }
           else
             return CCID_DRIVER_ERR_CARD_IO_ERROR;
@@ -2320,9 +3025,9 @@ ccid_transceive_secure (ccid_driver_t handle,
   unsigned char *msg, *tpdu, *p;
   size_t msglen, tpdulen, n;
   unsigned char seqno;
-  int i;
   size_t dummy_nresp;
   int testmode;
+  int cherry_mode = 0;
 
   testmode = !resp && !nresp;
 
@@ -2354,10 +3059,26 @@ ccid_transceive_secure (ccid_driver_t handle,
       || pinlen_min > pinlen_max)
     return CCID_DRIVER_ERR_INV_VALUE;
 
-  /* We have only tested this with an SCM reader so better don't risk
-     anything and do not allow the use with other readers. */
-  if (handle->id_vendor != VENDOR_SCM)
-    return CCID_DRIVER_ERR_NOT_SUPPORTED;
+  /* We have only tested a few readers so better don't risk anything
+     and do not allow the use with other readers. */
+  switch (handle->id_vendor)
+    {
+    case VENDOR_SCM:  /* Tested with SPR 532. */
+    case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */
+      break;
+    case VENDOR_CHERRY:
+      /* The CHERRY XX44 keyboard echos an asterisk for each entered
+         character on the keyboard channel.  We use a special variant
+         of PC_to_RDR_Secure which directs these characters to the
+         smart card's bulk-in channel.  We also need to append a zero
+         Lc byte to the APDU.  It seems that it will be replaced with
+         the actual length instead of being appended before the APDU
+         is send to the card. */
+      cherry_mode = 1;
+      break;
+    default:
+     return CCID_DRIVER_ERR_NOT_SUPPORTED;
+    }
 
   if (testmode)
     return 0; /* Success */
@@ -2372,10 +3093,10 @@ ccid_transceive_secure (ccid_driver_t handle,
         return rc;
     }
 
-  msg[0] = PC_to_RDR_Secure;
+  msg[0] = cherry_mode? 0x89 : PC_to_RDR_Secure;
   msg[5] = 0; /* slot */
   msg[6] = seqno = handle->seqno++;
-  msg[7] = 4; /* bBWI */
+  msg[7] = 0; /* bBWI */
   msg[8] = 0; /* RFU */
   msg[9] = 0; /* RFU */
   msg[10] = 0; /* Perform PIN verification. */
@@ -2384,7 +3105,7 @@ ccid_transceive_secure (ccid_driver_t handle,
   if (handle->id_vendor == VENDOR_SCM)
     {
       /* For the SPR532 the next 2 bytes need to be zero.  We do this
-         for all SCM product. Kudos to Martin Paljak for this
+         for all SCM products.  Kudos to Martin Paljak for this
          hint.  */
       msg[13] = msg[14] = 0;
     }
@@ -2396,8 +3117,11 @@ ccid_transceive_secure (ccid_driver_t handle,
       msg[14] = 0x00; /* bmPINLengthFormat:
                          Units are bytes, position is 0. */
     }
-  msg[15] = pinlen_min;   /* wPINMaxExtraDigit-Minimum.  */
-  msg[16] = pinlen_max;   /* wPINMaxExtraDigit-Maximum.  */
+
+  /* The following is a little endian word. */
+  msg[15] = pinlen_max;   /* wPINMaxExtraDigit-Maximum.  */
+  msg[16] = pinlen_min;   /* wPINMaxExtraDigit-Minimum.  */
+
   msg[17] = 0x02; /* bEntryValidationCondition:
                      Validation key pressed */
   if (pinlen_min && pinlen_max && pinlen_min == pinlen_max)
@@ -2409,32 +3133,48 @@ ccid_transceive_secure (ccid_driver_t handle,
   /* bTeoProlog follows: */
   msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0;
   msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */
-  msg[24] = 4; /* apdulen.  */
+  msg[24] = 0; /* The apdulen will be filled in by the reader.  */
   /* APDU follows:  */
   msg[25] = apdu_buf[0]; /* CLA */
   msg[26] = apdu_buf[1]; /* INS */
   msg[27] = apdu_buf[2]; /* P1 */
   msg[28] = apdu_buf[3]; /* P2 */
   msglen = 29;
+  if (cherry_mode)
+    msg[msglen++] = 0;
+  /* An EDC is not required. */
   set_msg_len (msg, msglen - 10);
 
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-  
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
   
   msg = recv_buffer;
   rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
-                RDR_to_PC_DataBlock, seqno, 5000, 0);
+                RDR_to_PC_DataBlock, seqno, 30000, 0);
   if (rc)
     return rc;
   
   tpdu = msg + 10;
   tpdulen = msglen - 10;
+
+  if (handle->apdu_level)
+    {
+      if (resp)
+        {
+          if (tpdulen > maxresplen)
+            {
+              DEBUGOUT_2 ("provided buffer too short for received data "
+                          "(%u/%u)\n",
+                          (unsigned int)tpdulen, (unsigned int)maxresplen);
+              return CCID_DRIVER_ERR_INV_VALUE;
+            }
+          
+          memcpy (resp, tpdu, tpdulen); 
+          *nresp = tpdulen;
+        }
+      return 0;
+    }
   
   if (tpdulen < 4) 
     {
@@ -2507,7 +3247,7 @@ ccid_transceive_secure (ccid_driver_t handle,
     }
   else 
     { /* This is a S-block. */
-      DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n",
+      DEBUGOUT_2 ("T=1: S-block %s received cmd=%d for Secure operation\n",
                   (tpdu[1] & 0x20)? "response": "request",
                   (tpdu[1] & 0x1f));
       return CCID_DRIVER_ERR_CARD_IO_ERROR;
@@ -2548,6 +3288,7 @@ print_error (int err)
   fprintf (stderr, "operation failed: %s\n", p);
 }
 
+
 static void
 print_data (const unsigned char *data, size_t length)
 {
@@ -2580,7 +3321,7 @@ main (int argc, char **argv)
 {
   int rc;
   ccid_driver_t ccid;
-  unsigned int slotstat;
+  int slotstat;
   unsigned char result[512];
   size_t resultlen;
   int no_pinpad = 0;
@@ -2608,7 +3349,7 @@ main (int argc, char **argv)
         }
       else if ( !strcmp (*argv, "--debug"))
         {
-          ccid_set_debug_level (1);
+          ccid_set_debug_level (ccid_set_debug_level (-1)+1);
           argc--; argv++;
         }
       else if ( !strcmp (*argv, "--no-poll"))
index 341876b..6bb1913 100644 (file)
@@ -80,6 +80,9 @@ typedef struct ccid_driver_s *ccid_driver_t;
 int ccid_set_debug_level (int level);
 char *ccid_get_reader_list (void);
 int ccid_open_reader (ccid_driver_t *handle, const char *readerid);
+int ccid_set_progress_cb (ccid_driver_t handle, 
+                          void (*cb)(void *, const char *, int, int, int),
+                          void *cb_arg);
 int ccid_shutdown_reader (ccid_driver_t handle);
 int ccid_close_reader (ccid_driver_t handle);
 int ccid_get_atr (ccid_driver_t handle,
index 302b068..0d9122f 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1932,6 +1932,7 @@ main (int argc, char **argv )
 #else
     opt.pcsc_driver = "libpcsclite.so"; 
 #endif
+    opt.disable_keypad = 1;  /* No keypad support; use gpg2 instead.  */
 #endif /*ENABLE_CARD_SUPPORT*/
 
     /* check whether we have a config file on the commandline */
index 8c6af04..f1ee0da 100644 (file)
@@ -1,5 +1,5 @@
 /* iso7816.c - ISO 7816 commands
- *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -45,9 +45,9 @@
 
 
 #define CMD_SELECT_FILE 0xA4
-#define CMD_VERIFY      0x20
-#define CMD_CHANGE_REFERENCE_DATA 0x24
-#define CMD_RESET_RETRY_COUNTER   0x2C
+#define CMD_VERIFY                ISO7816_VERIFY
+#define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA
+#define CMD_RESET_RETRY_COUNTER   ISO7816_RESET_RETRY_COUNTER
 #define CMD_GET_DATA    0xCA
 #define CMD_PUT_DATA    0xDA
 #define CMD_MSE         0x22
@@ -66,7 +66,10 @@ map_sw (int sw)
   switch (sw)
     {
     case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
+    case SW_TERM_STATE:     ec = GPG_ERR_CARD; break;
     case SW_WRONG_LENGTH:   ec = GPG_ERR_INV_VALUE; break;
+    case SW_SM_NOT_SUP:     ec = GPG_ERR_NOT_SUPPORTED; break;
+    case SW_CC_NOT_SUP:     ec = GPG_ERR_NOT_SUPPORTED; break;
     case SW_CHV_WRONG:      ec = GPG_ERR_BAD_PIN; break;
     case SW_CHV_BLOCKED:    ec = GPG_ERR_PIN_BLOCKED; break;
     case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
@@ -93,6 +96,7 @@ map_sw (int sw)
     case SW_HOST_GENERAL_ERROR:  ec = GPG_ERR_GENERAL; break;
     case SW_HOST_NO_READER:      ec = GPG_ERR_ENODEV; break;
     case SW_HOST_ABORTED:        ec = GPG_ERR_CANCELED; break;
+    case SW_HOST_NO_KEYPAD:      ec = GPG_ERR_NOT_SUPPORTED; break;
 
     default:
       if ((sw & 0x010000))
@@ -122,12 +126,15 @@ iso7816_map_sw (int sw)
    requested application ID.  The function can't be used to enumerate
    AIDs and won't return the AID on success.  The return value is 0
    for okay or a GPG error code.  Note that ISO error codes are
-   internally mapped. */
+   internally mapped.  Bit 0 of FLAGS should be set if the card does
+   not understand P2=0xC0. */
 gpg_error_t
-iso7816_select_application (int slot, const char *aid, size_t aidlen)
+iso7816_select_application (int slot, const char *aid, size_t aidlen,
+                            unsigned int flags)
 {
   int sw;
-  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 4,
+                         (flags&1)? 0 :0x0c, aidlen, aid);
   return map_sw (sw);
 }
 
@@ -152,7 +159,7 @@ iso7816_select_file (int slot, int tag, int is_dir,
     {
       p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
       p1 = 0x0c; /* No FC return. */
-      sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+      sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
                              p0, p1, 2, (char*)tagbuf );
       return map_sw (sw);
     }
@@ -188,7 +195,7 @@ iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
 
   p0 = 0x08;
   p1 = 0x0c; /* No FC return. */
-  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
                          p0, p1, buflen, (char*)buffer );
   return map_sw (sw);
 }
@@ -206,7 +213,7 @@ iso7816_list_directory (int slot, int list_dirs,
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
+  sw = apdu_send (slot, 0, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
                   result, resultlen);
   if (sw != SW_SUCCESS)
     {
@@ -219,27 +226,101 @@ iso7816_list_directory (int slot, int list_dirs,
 }
 
 
+/* This funcion sends an already formatted APDU to the card.  With
+   HANDLE_MORE set to true a MORE DATA status will be handled
+   internally.  The return value is a gpg error code (i.e. a mapped
+   status word).  This is basica