A bunch of changes for the openpgp card.
authorWerner Koch <wk@gnupg.org>
Tue, 27 Apr 2004 08:23:45 +0000 (08:23 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 27 Apr 2004 08:23:45 +0000 (08:23 +0000)
19 files changed:
g10/ChangeLog
g10/Makefile.am
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/g10.c
g10/iso7816.c
g10/iso7816.h
g10/options.h
g10/status.c
g10/status.h
g10/tlv.c [new file with mode: 0644]
g10/tlv.h [new file with mode: 0644]

index 518983b..07c57d8 100644 (file)
@@ -1,3 +1,63 @@
+2004-04-27  Werner Koch  <wk@gnupg.org>
+
+       * g10.c: New commands --allow-admin and --deny-admin.
+       * options.h (opt): Add member ALLOW_ADMIN.
+
+       * tlv.h, tlv.c: New.  Copied from gnupg-1.9. 
+       * cardglue.c (open_card): The serialno is now set internally by
+       app_select_openpgp; changed invocation.
+       * cardglue.h (app_t, ctrl_t): New.
+       (GPG_ERR_EBUSY, GPG_ERR_ENOENT, GPG_ERR_NOT_FOUND, GPG_ERR_BUG) 
+       (GPG_ERR_NOT_IMPLEMENTED, GPG_ERR_EACCESS): New.
+       (gpg_err_code_from_errno): New.
+
+       * app-common.h, app-openpgp.c, iso7816.c, iso7816.h
+       * apdu.c, apdu.h, ccid-driver.c, ccid-driver.h
+       * card-util.c: Updated from current gnupg-1.9.
+
+       Changes are:
+
+       * app-common.h: New members FNC.DEINIT and APP_LOCAL.
+       * app-openpgp.c (do_deinit): New.
+       (get_cached_data, flush_cache_item, flush_cache_after_error)
+       (flush_cache): New.
+       (get_one_do): Replaced arg SLOT by APP.  Make used of cached data.
+       (verify_chv2, verify_chv3): Flush some cache item after error.
+       (do_change_pin): Ditto.
+       (do_sign): Ditto.
+       (do_setattr): Flush cache item.
+       (do_genkey): Flush the entire cache.
+       (compare_fingerprint): Use cached data.
+
+       * apdu.c (apdu_send_le): Reinitialize RESULTLEN.  Handle
+       SW_EOF_REACHED like SW_SUCCESS.
+
+       * ccid-driver.c (parse_ccid_descriptor): Store some of the reader
+       features away.  New arg HANDLE
+       (read_device_info): New arg HANDLE. Changed caller.
+       (bulk_in): Handle time extension requests.
+       (ccid_get_atr): Setup parameters and the IFSD.
+       (compute_edc): New. Factored out code.
+       (ccid_transceive): Use default NADs when required.
+
+       * apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED,
+       SW_HOST_LOCKING_FAILED and SW_HOST_BUSY.
+       * iso7816.c (map_sw): Map it.
+       
+       * ccid-driver.c (ccid_slot_status): Add arg STATUSBITS.
+       * apdu.c (apdu_get_status): New.
+       (ct_get_status, pcsc_get_status, ocsc_get_status): New stubs.
+       (get_status_ccid): New.
+       (apdu_reset): New.
+       (reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs.
+       (reset_ccid_reader): New.
+       (apdu_enum_reader): New.
+       
+       * apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers.
+       (new_reader_slot) [USE_GNU_PTH]: Init mutex.
+       (apdu_reset, apdu_get_status, apdu_send_le): Run functions
+       in locked mode.
+       
 2004-04-25  David Shaw  <dshaw@jabberwocky.com>
 
        * getkey.c (get_seckey_byname2): Significantly simplify this
 
        * options.h: Encapsulate keyserver details.  Change all callers.
 
+2004-04-05  Werner Koch  <wk@gnupg.org>
+
+       * status.h (STATUS_NEWSIG): New.
+       * status.c (get_status_string): Add it. 
+
 2004-03-27  David Shaw  <dshaw@jabberwocky.com>
 
        * keyedit.c (keyedit_menu): Request a trustdb update when adding a
index 3f08d62..2c5d820 100644 (file)
@@ -78,7 +78,7 @@ card_support_source_scd = \
        iso7816.c iso7816.h \
        apdu.c apdu.h \
        ccid-driver.c ccid-driver.h
-card_support_source_local = cardglue.c cardglue.h
+card_support_source_local = cardglue.c cardglue.h tlv.c tlv.h
 else
 card_support_source_g10 =
 card_support_source_scd = 
index 27304c8..42b3372 100644 (file)
@@ -1,5 +1,5 @@
 /* apdu.c - ISO 7816 APDU functions and low level I/O
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#ifdef USE_GNU_PTH
+# include <pth.h>
+# include <unistd.h>
+# include <fcntl.h>
+#endif
 #ifdef HAVE_OPENSC
 # include <opensc/opensc.h>
 #endif
 
-#if GNUPG_MAJOR_VERSION == 1
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1
 /* This is used with GnuPG version < 1.9.  The code has been source
    copied from the current GnuPG >= 1.9  and is maintained over
    there. */
 #include "dynload.h"
 #include "ccid-driver.h"
 
+#ifdef USE_GNU_PTH
+#define NEED_PCSC_WRAPPER 1
+#endif
+
+
 #define MAX_READER 4 /* Number of readers we support concurrently. */
 #define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
                                   insertion of the card (1 = don't wait). */
 #define DLSTDCALL
 #endif
 
+#ifdef _POSIX_OPEN_MAX
+#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#else
+#define MAX_OPEN_FDS 20
+#endif
+
 
 /* A structure to collect information pertaining to one reader
    slot. */
@@ -72,6 +90,11 @@ struct reader_table_s {
     unsigned long context;
     unsigned long card;
     unsigned long protocol;
+#ifdef NEED_PCSC_WRAPPER
+    int req_fd;
+    int rsp_fd;
+    pid_t pid;
+#endif /*NEED_PCSC_WRAPPER*/
   } pcsc;
 #ifdef HAVE_OPENSC
   int is_osc;          /* We are using the OpenSC driver layer. */
@@ -83,6 +106,11 @@ struct reader_table_s {
   int status;
   unsigned char atr[33];
   size_t atrlen;
+  unsigned int change_counter;
+#ifdef USE_GNU_PTH
+  int lock_initialized;
+  pth_mutex_t lock;
+#endif
 };
 typedef struct reader_table_s *reader_table_t;
 
@@ -183,12 +211,28 @@ new_reader_slot (void)
       log_error ("new_reader_slot: out of slots\n");
       return -1;
     }
+#ifdef USE_GNU_PTH
+  if (!reader_table[reader].lock_initialized)
+    {
+      if (!pth_mutex_init (&reader_table[reader].lock))
+        {
+          log_error ("error initializing mutex: %s\n", strerror (errno));
+          return -1;
+        }
+      reader_table[reader].lock_initialized = 1;
+    }
+#endif /*USE_GNU_PTH*/
   reader_table[reader].used = 1;
   reader_table[reader].is_ccid = 0;
   reader_table[reader].is_ctapi = 0;
 #ifdef HAVE_OPENSC
   reader_table[reader].is_osc = 0;
 #endif
+#ifdef NEED_PCSC_WRAPPER
+  reader_table[reader].pcsc.req_fd = -1;
+  reader_table[reader].pcsc.rsp_fd = -1;
+  reader_table[reader].pcsc.pid = (pid_t)(-1);
+#endif
   return reader;
 }
 
@@ -368,6 +412,18 @@ close_ct_reader (int slot)
   return 0;
 }
 
+static int
+reset_ct_reader (int slot)
+{
+  return SW_HOST_NOT_SUPPORTED;
+}
+
+
+static int
+ct_get_status (int slot, unsigned int *status)
+{
+  return SW_HOST_NOT_SUPPORTED;
+}
 
 /* Actually send the APDU of length APDULEN to SLOT and return a
    maximum of *BUFLEN data in BUFFER, the actual retruned size will be
@@ -395,6 +451,66 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
 
 
 \f
+#ifdef NEED_PCSC_WRAPPER
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+  size_t nleft = nbytes;
+  int nwritten;
+
+/*   log_printhex (" writen:", buf, nbytes); */
+
+  while (nleft > 0)
+    {
+#ifdef USE_GNU_PTH
+      nwritten = pth_write (fd, buf, nleft);
+#else
+      nwritten = write (fd, buf, nleft);
+#endif
+      if (nwritten < 0 && errno == EINTR)
+        continue;
+      if (nwritten < 0)
+        return -1;
+      nleft -= nwritten;
+      buf = (const char*)buf + nwritten;
+    }
+  return 0;
+}
+
+/* Read up to BUFLEN bytes from FD and return the number of bytes
+   actually read in NREAD.  Returns -1 on error or 0 on success. */
+static int
+readn (int fd, void *buf, size_t buflen, size_t *nread)
+{
+  size_t nleft = buflen;
+  int n;
+/*   void *orig_buf = buf; */
+
+  while (nleft > 0)
+    {
+#ifdef USE_GNU_PTH
+      n = pth_read (fd, buf, nleft);
+#else
+      n = read (fd, buf, nleft);
+#endif
+      if (n < 0 && errno == EINTR) 
+        continue;
+      if (n < 0)
+        return -1; /* read error. */
+      if (!n)
+        break; /* EOF */
+      nleft -= n;
+      buf = (char*)buf + n;
+    }
+  if (nread)
+    *nread = buflen - nleft;
+
+/*   log_printhex ("  readn:", orig_buf, *nread); */
+    
+  return 0;
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
 static const char *
 pcsc_error_string (long err)
 {
@@ -455,6 +571,172 @@ pcsc_error_string (long err)
 static int
 open_pcsc_reader (const char *portstr)
 {
+#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;
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
+
+  /* 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 (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. */
+      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);
+    }
+
+  /* 
+     === 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. */
+  while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
+    ;
+
+  /* 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=%x)\n", len);
+      goto command_failed;
+    }
+  err = (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));
+      goto command_failed;
+    }
+  n = len;
+  if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+    {
+      log_error ("error receiving PC/SC OPEN response: %s\n",
+                 i? strerror (errno) : "premature EOF");
+      goto command_failed;
+    }
+  slotp->atrlen = len;
+
+  dump_reader_status (slot); 
+  return slot;
+
+ 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 -1;
+#else /*!NEED_PCSC_WRAPPER */
   long err;
   int slot;
   char *list = NULL;
@@ -557,9 +839,16 @@ open_pcsc_reader (const char *portstr)
 
   dump_reader_status (slot); 
   return slot;
+#endif /*!NEED_PCSC_WRAPPER */
 }
 
 
+static int
+pcsc_get_status (int slot, unsigned int *status)
+{
+  return SW_HOST_NOT_SUPPORTED;
+}
+
 /* 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. */
@@ -567,6 +856,109 @@ static int
 pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
                 unsigned char *buffer, size_t *buflen)
 {
+#ifdef NEED_PCSC_WRAPPER
+  long err;
+  reader_table_t slotp;
+  size_t len, full_len;
+  int i, n;
+  unsigned char msgbuf[9];
+
+  if (DBG_CARD_IO)
+    log_printhex ("  PCSC_data:", apdu, apdulen);
+
+  slotp = reader_table + slot;
+
+  if (slotp->pcsc.req_fd == -1 
+      || slotp->pcsc.rsp_fd == -1 
+      || slotp->pcsc.pid == (pid_t)(-1) )
+    {
+      log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
+      return -1;
+    }
+
+  msgbuf[0] = 0x03; /* TRANSMIT command. */
+  len = apdulen;
+  msgbuf[1] = (len >> 24);
+  msgbuf[2] = (len >> 16);
+  msgbuf[3] = (len >>  8);
+  msgbuf[4] = (len      );
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
+       || writen (slotp->pcsc.req_fd, apdu, len))
+    {
+      log_error ("error sending PC/SC TRANSMIT 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 TRANSMIT 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. */
+  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  if (err)
+    {
+      log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return -1;
+    }
+
+   full_len = len;
+   
+   n = *buflen < len ? *buflen : len;
+   if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
+     {
+       log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+                  i? strerror (errno) : "premature EOF");
+       goto command_failed;
+     }
+   *buflen = n;
+
+   full_len -= len;
+   if (full_len)
+     {
+       log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
+       err = -1; 
+     }
+   /* We need to read any rest of the response, to keep the
+      protocol runnng. */
+   while (full_len)
+     {
+       unsigned char dummybuf[128];
+
+       n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
+       if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
+         {
+           log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+                      i? strerror (errno) : "premature EOF");
+           goto command_failed;
+         }
+       full_len -= n;
+     }
+
+   return err;
+
+ 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 -1;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
   long err;
   struct pcsc_io_request_s send_pci;
   unsigned long recv_len;
@@ -589,14 +981,87 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
                pcsc_error_string (err), err);
   
   return err? -1:0; /* FIXME: Return appropriate error code. */
+#endif /*!NEED_PCSC_WRAPPER*/
 }
 
+
 static int
 close_pcsc_reader (int slot)
 {
+#ifdef NEED_PCSC_WRAPPER
+  long err;
+  reader_table_t slotp;
+  size_t len;
+  int i;
+  unsigned char msgbuf[9];
+
+  slotp = reader_table + slot;
+
+  if (slotp->pcsc.req_fd == -1 
+      || slotp->pcsc.rsp_fd == -1 
+      || slotp->pcsc.pid == (pid_t)(-1) )
+    {
+      log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
+      return 0;
+    }
+
+  msgbuf[0] = 0x02; /* CLOSE 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 CLOSE 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 CLOSE 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. */
+  err = (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);
+  
+  /* We will the wrapper in any case - errors are merely
+     informational. */
+  
+ 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 0;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
   pcsc_release_context (reader_table[slot].pcsc.context);
   reader_table[slot].used = 0;
   return 0;
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+static int
+reset_pcsc_reader (int slot)
+{
+  return SW_HOST_NOT_SUPPORTED;
 }
 
 
@@ -659,6 +1124,46 @@ close_ccid_reader (int slot)
 }                       
   
 
+static int
+reset_ccid_reader (int slot)
+{
+  int err;
+  reader_table_t slotp = reader_table + slot;
+  unsigned char atr[33];
+  size_t atrlen;
+
+  err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
+  if (err)
+    return -1;
+  /* If the reset was successful, update the ATR. */
+  assert (sizeof slotp->atr >= sizeof atr);
+  slotp->atrlen = atrlen;
+  memcpy (slotp->atr, atr, atrlen);
+  dump_reader_status (slot); 
+  return 0;
+}                       
+  
+
+static int
+get_status_ccid (int slot, unsigned int *status)
+{
+  int rc;
+  int bits;
+
+  rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
+  if (rc)
+    return -1;
+
+  if (bits == 0)
+    *status = 1|2|4;
+  else if (bits == 1)
+    *status = 2;
+  else 
+    *status = 0;
+
+  return 0;
+}
+
 
 /* Actually send the APDU of length APDULEN to SLOT and return a
    maximum of *BUFLEN data in BUFFER, the actual returned size will be
@@ -796,6 +1301,18 @@ close_osc_reader (int slot)
   return 0;
 }
 
+static int
+reset_osc_reader (int slot)
+{
+  return SW_HOST_NOT_SUPPORTED;
+}
+
+
+static int
+ocsc_get_status (int slot, unsigned int *status)
+{
+  return SW_HOST_NOT_SUPPORTED;
+}
 
 
 /* Actually send the APDU of length APDULEN to SLOT and return a
@@ -894,6 +1411,45 @@ osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
        Driver Access
  */
 
+
+static int
+lock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+  if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
+    {
+      log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+      return SW_HOST_LOCKING_FAILED;
+    }
+#endif /*USE_GNU_PTH*/
+  return 0;
+}
+
+static int
+trylock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+  if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
+    {
+      if (errno == EBUSY)
+        return SW_HOST_BUSY;
+      log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+      return SW_HOST_LOCKING_FAILED;
+    }
+#endif /*USE_GNU_PTH*/
+  return 0;
+}
+
+static void
+unlock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+  if (!pth_mutex_release (&reader_table[slot].lock))
+    log_error ("failed to release apdu lock: %s\n", strerror (errno));
+#endif /*USE_GNU_PTH*/
+}
+
+
 /* Open the reader and return an internal slot number or -1 on
    error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
    the first USB reader.  For PC/SC the first listed reader).  If
@@ -935,7 +1491,7 @@ apdu_open_reader (const char *portstr)
           handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
           if (!handle)
             {
-              log_error ("apdu_open_reader: failed to open driver: %s",
+              log_error ("apdu_open_reader: failed to open driver: %s\n",
                          dlerror ());
               return -1;
             }
@@ -957,12 +1513,13 @@ apdu_open_reader (const char *portstr)
   /* No ctAPI configured, so lets try the PC/SC API */
   if (!pcsc_api_loaded)
     {
+#ifndef NEED_PCSC_WRAPPER
       void *handle;
 
       handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
       if (!handle)
         {
-          log_error ("apdu_open_reader: failed to open driver `%s': %s",
+          log_error ("apdu_open_reader: failed to open driver `%s': %s\n",
                      opt.pcsc_driver, dlerror ());
           return -1;
         }
@@ -1018,9 +1575,10 @@ apdu_open_reader (const char *portstr)
           dlclose (handle);
           return -1;
         }
+#endif /*!NEED_PCSC_WRAPPER*/  
       pcsc_api_loaded = 1;
     }
-  
+
   return open_pcsc_reader (portstr);
 }
 
@@ -1044,6 +1602,47 @@ apdu_close_reader (int slot)
     return close_pcsc_reader (slot);
 }
 
+/* Enumerate all readers and return information on whether this reader
+   is in use.  The caller should start with SLOT set to 0 and
+   increment it with each call until an error is returned. */
+int
+apdu_enum_reader (int slot, int *used)
+{
+  if (slot < 0 || slot >= MAX_READER)
+    return SW_HOST_NO_DRIVER;
+  *used = reader_table[slot].used;
+  return 0;
+}
+
+/* Do a reset for the card in reader at SLOT. */
+int
+apdu_reset (int slot)
+{
+  int sw;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+  
+  if ((sw = lock_slot (slot)))
+    return sw;
+
+  if (reader_table[slot].is_ctapi)
+    sw = reset_ct_reader (slot);
+#ifdef HAVE_LIBUSB
+  else if (reader_table[slot].is_ccid)
+    sw = reset_ccid_reader (slot);
+#endif
+#ifdef HAVE_OPENSC
+  else if (reader_table[slot].is_osc)
+    sw = reset_osc_reader (slot);
+#endif
+  else
+    sw = reset_pcsc_reader (slot);
+
+  unlock_slot (slot);
+  return sw;
+}
+
 
 unsigned char *
 apdu_get_atr (int slot, size_t *atrlen)
@@ -1060,6 +1659,7 @@ apdu_get_atr (int slot, size_t *atrlen)
   *atrlen = reader_table[slot].atrlen;
   return buf;
 }
+
   
     
 static const char *
@@ -1082,7 +1682,62 @@ error_string (int slot, long rc)
 }
 
 
-/* Dispatcher for the actual send_apdu fucntion. */
+/* Retrieve the status for SLOT. The function does obnly wait fot the
+   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]
+
+   For must application, tetsing 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)
+{
+  int sw;
+  unsigned int s;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
+    return sw;
+
+  if (reader_table[slot].is_ctapi)
+    sw = ct_get_status (slot, &s);
+#ifdef HAVE_LIBUSB
+  else if (reader_table[slot].is_ccid)
+    sw = get_status_ccid (slot, &s);
+#endif
+#ifdef HAVE_OPENSC
+  else if (reader_table[slot].is_osc)
+    sw = osc_get_status (slot, &s);
+#endif
+  else
+    sw = pcsc_get_status (slot, &s);
+
+  unlock_slot (slot);
+
+  if (sw)
+    return sw;
+
+  if (status)
+    *status = s;
+  if (changed)
+    *changed = reader_table[slot].change_counter;
+  return 0;
+}
+
+
+/* Dispatcher for the actual send_apdu function. Note, that this
+   function should be called in locked state. */
 static int
 send_apdu (int slot, unsigned char *apdu, size_t apdulen,
            unsigned char *buffer, size_t *buflen)
@@ -1117,13 +1772,18 @@ apdu_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)
 {
-  unsigned char result[256+10]; /* 10 extra in case of bugs in the driver. */
-  size_t resultlen = 256;
+#define RESULTLEN 256
+  unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
+                                         the driver. */
+  size_t resultlen;
   unsigned char apdu[5+256+1];
   size_t apdulen;
   int sw;
   long rc; /* we need a long here due to PC/SC. */
 
+  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);
@@ -1135,6 +1795,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
   if ((!data && lc != -1) || (data && lc == -1))
     return SW_HOST_INV_VALUE;
 
+  if ((sw = lock_slot (slot)))
+    return sw;
+
   apdulen = 0;
   apdu[apdulen++] = class;
   apdu[apdulen++] = ins;
@@ -1151,11 +1814,13 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
   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);
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_simple(%d) failed: %s\n",
                  slot, error_string (slot, rc));
+      unlock_slot (slot);
       return SW_HOST_INCOMPLETE_CARD_RESPONSE;
     }
   sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -1168,13 +1833,16 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
         log_printhex ("     dump: ", result, resultlen);
     }
 
-  if (sw == SW_SUCCESS)
+  if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
     {
       if (retbuf)
         {
           *retbuf = xtrymalloc (resultlen? resultlen : 1);
           if (!*retbuf)
-            return SW_HOST_OUT_OF_CORE;
+            {
+              unlock_slot (slot);
+              return SW_HOST_OUT_OF_CORE;
+            }
           *retbuflen = resultlen;
           memcpy (*retbuf, result, resultlen);
         }
@@ -1190,7 +1858,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
         {
           *retbuf = p = xtrymalloc (bufsize);
           if (!*retbuf)
-            return SW_HOST_OUT_OF_CORE;
+            {
+              unlock_slot (slot);
+              return SW_HOST_OUT_OF_CORE;
+            }
           assert (resultlen < bufsize);
           memcpy (p, result, resultlen);
           p += resultlen;
@@ -1200,20 +1871,23 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
         {
           int len = (sw & 0x00ff);
           
-          log_debug ("apdu_send_simple(%d): %d more bytes available\n",
-                     slot, len);
+          if (DBG_CARD_IO)
+            log_debug ("apdu_send_simple(%d): %d more bytes available\n",
+                       slot, len);
           apdulen = 0;
           apdu[apdulen++] = class;
           apdu[apdulen++] = 0xC0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = 0;
-          apdu[apdulen++] = 64; /* that is 256 bytes for Le */
+          apdu[apdulen++] = len; 
           memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+          resultlen = RESULTLEN;
           rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_simple(%d) for get response failed: %s\n",
                          slot, error_string (slot, rc));
+              unlock_slot (slot);
               return SW_HOST_INCOMPLETE_CARD_RESPONSE;
             }
           sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -1225,16 +1899,21 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
                 log_printhex ("     dump: ", result, resultlen);
             }
 
-          if ((sw & 0xff00) == SW_MORE_DATA || sw == SW_SUCCESS)
+          if ((sw & 0xff00) == SW_MORE_DATA
+              || sw == SW_SUCCESS
+              || sw == SW_EOF_REACHED )
             {
-              if (retbuf)
+              if (retbuf && resultlen)
                 {
                   if (p - *retbuf + resultlen > bufsize)
                     {
                       bufsize += resultlen > 4096? resultlen: 4096;
                       tmp = xtryrealloc (*retbuf, bufsize);
                       if (!tmp)
-                        return SW_HOST_OUT_OF_CORE;
+                        {
+                          unlock_slot (slot);
+                          return SW_HOST_OUT_OF_CORE;
+                        }
                       p = tmp + (p - *retbuf);
                       *retbuf = tmp;
                     }
@@ -1257,10 +1936,14 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
             *retbuf = tmp;
         }
     }
+
+  unlock_slot (slot);
+
   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
index 21e2b98..f74bab7 100644 (file)
 enum {
   SW_MORE_DATA      = 0x6100, /* Note: that the low byte must be
                                  masked of.*/
+  SW_EOF_REACHED    = 0x6282,
   SW_EEPROM_FAILURE = 0x6581,
   SW_WRONG_LENGTH   = 0x6700,
   SW_CHV_WRONG      = 0x6982,
   SW_CHV_BLOCKED    = 0x6983,
   SW_USE_CONDITIONS = 0x6985,
-  SW_NOT_SUPPORTED  = 0x6a81,
   SW_BAD_PARAMETER  = 0x6a80, /* (in the data field) */
+  SW_NOT_SUPPORTED  = 0x6a81,
+  SW_FILE_NOT_FOUND = 0x6a82,
+  SW_RECORD_NOT_FOUND = 0x6a83,
   SW_REF_NOT_FOUND  = 0x6a88,
   SW_BAD_P0_P1      = 0x6b00,
   SW_INS_NOT_SUP    = 0x6d00,
@@ -45,9 +48,12 @@ enum {
      those values can't be issued by a card. */
   SW_HOST_OUT_OF_CORE = 0x10001,  /* No way yet to differentiate
                                      between errnos on a failed malloc. */
-  SW_HOST_INV_VALUE   = 0x10002,
+  SW_HOST_INV_VALUE     = 0x10002,
   SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
-  SW_HOST_NO_DRIVER   = 0x10004
+  SW_HOST_NO_DRIVER     = 0x10004,
+  SW_HOST_NOT_SUPPORTED = 0x10005,
+  SW_HOST_LOCKING_FAILED= 0x10006,
+  SW_HOST_BUSY          = 0x10007
 };
 
 
@@ -55,10 +61,14 @@ enum {
 /* Note , that apdu_open_reader returns no status word but -1 on error. */
 int apdu_open_reader (const char *portstr);
 int apdu_close_reader (int slot);
+int apdu_enum_reader (int slot, int *used);
 unsigned char *apdu_get_atr (int slot, size_t *atrlen);
 
 
 /* The apdu send functions do return status words. */
+int apdu_reset (int slot);
+int apdu_get_status (int slot, int hang,
+                     unsigned int *status, unsigned int *changed);
 int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
                       int lc, const char *data);
 int apdu_send (int slot, int class, int ins, int p0, int p1,
index de1e02c..c61bcca 100644 (file)
 #ifndef GNUPG_SCD_APP_COMMON_H
 #define GNUPG_SCD_APP_COMMON_H
 
+#if GNUPG_MAJOR_VERSION != 1
+#include <ksba.h>
+#endif
+
+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
@@ -29,111 +35,131 @@ struct app_ctx_s {
   int slot;         /* Used reader. */
   unsigned char *serialno; /* Serialnumber in raw form, allocated. */
   size_t serialnolen;      /* Length in octets of serialnumber. */
+  const char *apptype;
   unsigned int card_version;
   int did_chv1;
   int force_chv1;   /* True if the card does not cache CHV1. */
   int did_chv2;
   int did_chv3;
+  struct app_local_s *app_local;  /* Local to the application. */
   struct {
-    int (*learn_status) (APP app, CTRL ctrl);
-    int (*getattr) (APP app, CTRL ctrl, const char *name);
-    int (*setattr) (APP app, const char *name,
+    void (*deinit) (app_t app);
+    int (*learn_status) (app_t app, ctrl_t ctrl);
+    int (*readcert) (app_t app, const char *certid,
+                     unsigned char **cert, size_t *certlen);
+    int (*getattr) (app_t app, ctrl_t ctrl, const char *name);
+    int (*setattr) (app_t app, const char *name,
                     int (*pincb)(void*, const char *, char **),
                     void *pincb_arg,
                     const unsigned char *value, size_t valuelen);
-    int (*sign) (APP app,
+    int (*sign) (app_t app,
                  const char *keyidstr, int hashalgo,
                  int (pincb)(void*, const char *, char **),
                  void *pincb_arg,
                  const void *indata, size_t indatalen,
                  unsigned char **outdata, size_t *outdatalen );
-    int (*auth) (APP app, const char *keyidstr,
+    int (*auth) (app_t app, const char *keyidstr,
                  int (*pincb)(void*, const char *, char **),
                  void *pincb_arg,
                  const void *indata, size_t indatalen,
                  unsigned char **outdata, size_t *outdatalen);
-    int (*decipher) (APP app, const char *keyidstr,
+    int (*decipher) (app_t app, const char *keyidstr,
                      int (pincb)(void*, const char *, char **),
                      void *pincb_arg,
                      const void *indata, size_t indatalen,
                      unsigned char **outdata, size_t *outdatalen);
-    int (*genkey) (APP app, CTRL ctrl,
+    int (*genkey) (app_t app, ctrl_t ctrl,
                    const char *keynostr, unsigned int flags,
                    int (*pincb)(void*, const char *, char **),
                    void *pincb_arg);
-    int (*change_pin) (APP app, CTRL ctrl,
+    int (*change_pin) (app_t app, ctrl_t ctrl,
                        const char *chvnostr, int reset_mode,
                        int (*pincb)(void*, const char *, char **),
                        void *pincb_arg);
-    int (*check_pin) (APP app, const char *keyidstr,
+    int (*check_pin) (app_t app, const char *keyidstr,
                       int (pincb)(void*, const char *, char **),
                       void *pincb_arg);
   } fnc;
 
-
 };
 
 #if GNUPG_MAJOR_VERSION == 1
-int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
-int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
+int app_select_openpgp (app_t app);
+int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
 #else
+/*-- app-help.c --*/
+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_set_default_reader_port (const char *portstr);
-APP select_application (void);
-int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
-int app_write_learn_status (APP app, CTRL ctrl);
-int app_getattr (APP app, CTRL ctrl, const char *name);
-int app_setattr (APP app, const char *name,
+app_t select_application (ctrl_t ctrl, int slot, const char *name);
+void release_application (app_t app);
+int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
+int app_write_learn_status (app_t app, ctrl_t ctrl);
+int app_readcert (app_t app, const char *certid,
+                  unsigned char **cert, size_t *certlen);
+int app_getattr (app_t app, ctrl_t ctrl, const char *name);
+int app_setattr (app_t app, const char *name,
                  int (*pincb)(void*, const char *, char **),
                  void *pincb_arg,
                  const unsigned char *value, size_t valuelen);
-int app_sign (APP app, const char *keyidstr, int hashalgo,
+int app_sign (app_t app, const char *keyidstr, int hashalgo,
               int (pincb)(void*, const char *, char **),
               void *pincb_arg,
               const void *indata, size_t indatalen,
               unsigned char **outdata, size_t *outdatalen );
-int app_auth (APP app, const char *keyidstr,
+int app_auth (app_t app, const char *keyidstr,
               int (*pincb)(void*, const char *, char **),
               void *pincb_arg,
               const void *indata, size_t indatalen,
               unsigned char **outdata, size_t *outdatalen);
-int app_decipher (APP app, const char *keyidstr,
+int app_decipher (app_t app, const char *keyidstr,
                   int (pincb)(void*, const char *, char **),
                   void *pincb_arg,
                   const void *indata, size_t indatalen,
                   unsigned char **outdata, size_t *outdatalen );
-int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
+int app_genkey (app_t app, ctrl_t ctrl,
+                const char *keynostr, unsigned int flags,
                 int (*pincb)(void*, const char *, char **),
                 void *pincb_arg);
-int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer);
-int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
+int app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer);
+int app_change_pin (app_t app, ctrl_t ctrl,
+                    const char *chvnostr, int reset_mode,
                     int (*pincb)(void*, const char *, char **),
                     void *pincb_arg);
-int app_check_pin (APP app, const char *keyidstr,
+int app_check_pin (app_t app, const char *keyidstr,
                    int (*pincb)(void*, const char *, char **),
                    void *pincb_arg);
 
 
 /*-- app-openpgp.c --*/
-int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
+int app_select_openpgp (app_t app);
 
-int app_openpgp_cardinfo (APP app,
+int app_openpgp_cardinfo (app_t app,
                           char **serialno,
                           char **disp_name,
                           char **pubkey_url,
                           unsigned char **fpr1,
                           unsigned char **fpr2,
                           unsigned char **fpr3);
-int app_openpgp_storekey (APP app, int keyno,
+int app_openpgp_storekey (app_t app, int keyno,
                           unsigned char *template, size_t template_len,
                           time_t created_at,
                           const unsigned char *m, size_t mlen,
                           const unsigned char *e, size_t elen,
                           int (*pincb)(void*, const char *, char **),
                           void *pincb_arg);
-int app_openpgp_readkey (APP app, int keyno,
+int app_openpgp_readkey (app_t app, int keyno,
                          unsigned char **m, size_t *mlen,
                          unsigned char **e, size_t *elen);
+/*-- app-nks.c --*/
+int app_select_nks (app_t app);
+
+/*-- app-dinsig.c --*/
+int app_select_dinsig (app_t app);
+
+
 #endif
 
 
index 07abf9b..07420c6 100644 (file)
@@ -1,5 +1,5 @@
 /* app-openpgp.c - The OpenPGP card application.
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -42,7 +42,7 @@
 
 #include "iso7816.h"
 #include "app-common.h"
-
+#include "tlv.h"
 
 
 static struct {
@@ -50,121 +50,196 @@ static struct {
   int constructed;
   int get_from;  /* Constructed DO with this DO or 0 for direct access. */
   int binary;
+  int dont_cache;
+  int flush_on_error;
   char *desc;
 } data_objects[] = {
-  { 0x005E, 0,    0, 1, "Login Data" },
-  { 0x5F50, 0,    0, 0, "URL" },
-  { 0x0065, 1,    0, 1, "Cardholder Related Data"},
-  { 0x005B, 0, 0x65, 0, "Name" },
-  { 0x5F2D, 0, 0x65, 0, "Language preferences" },
-  { 0x5F35, 0, 0x65, 0, "Sex" },
-  { 0x006E, 1,    0, 1, "Application Related Data" },
-  { 0x004F, 0, 0x6E, 1, "AID" },
-  { 0x0073, 1,    0, 1, "Discretionary Data Objects" },
-  { 0x0047, 0, 0x6E, 1, "Card Capabilities" },
-  { 0x00C0, 0, 0x6E, 1, "Extended Card Capabilities" },
-  { 0x00C1, 0, 0x6E, 1, "Algorithm Attributes Signature" },
-  { 0x00C2, 0, 0x6E, 1, "Algorithm Attributes Decryption" },
-  { 0x00C3, 0, 0x6E, 1, "Algorithm Attributes Authentication" },
-  { 0x00C4, 0, 0x6E, 1, "CHV Status Bytes" },
-  { 0x00C5, 0, 0x6E, 1, "Fingerprints" },
-  { 0x00C6, 0, 0x6E, 1, "CA Fingerprints" },
-  { 0x007A, 1,    0, 1, "Security Support Template" },
-  { 0x0093, 0, 0x7A, 1, "Digital Signature Counter" },
+  { 0x005E, 0,    0, 1, 0, 0, "Login Data" },
+  { 0x5F50, 0,    0, 0, 0, 0, "URL" },
+  { 0x0065, 1,    0, 1, 0, 0, "Cardholder Related Data"},
+  { 0x005B, 0, 0x65, 0, 0, 0, "Name" },
+  { 0x5F2D, 0, 0x65, 0, 0, 0, "Language preferences" },
+  { 0x5F35, 0, 0x65, 0, 0, 0, "Sex" },
+  { 0x006E, 1,    0, 1, 0, 0, "Application Related Data" },
+  { 0x004F, 0, 0x6E, 1, 0, 0, "AID" },
+  { 0x0073, 1,    0, 1, 0, 0, "Discretionary Data Objects" },
+  { 0x0047, 0, 0x6E, 1, 0, 0, "Card Capabilities" },
+  { 0x00C0, 0, 0x6E, 1, 0, 0, "Extended Card Capabilities" },
+  { 0x00C1, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Signature" },
+  { 0x00C2, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Decryption" },
+  { 0x00C3, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Authentication" },
+  { 0x00C4, 0, 0x6E, 1, 0, 1, "CHV Status Bytes" },
+  { 0x00C5, 0, 0x6E, 1, 0, 0, "Fingerprints" },
+  { 0x00C6, 0, 0x6E, 1, 0, 0, "CA Fingerprints" },
+  { 0x007A, 1,    0, 1, 0, 0, "Security Support Template" },
+  { 0x0093, 0, 0x7A, 1, 1, 0, "Digital Signature Counter" },
   { 0 }
 };
 
 
+struct cache_s {
+  struct cache_s *next;
+  int tag;
+  size_t length;
+  unsigned char data[1];
+};
+
+struct app_local_s {
+  struct cache_s *cache;
+};
+
+
 static unsigned long convert_sig_counter_value (const unsigned char *value,
                                                 size_t valuelen);
 static unsigned long get_sig_counter (APP app);
 
+/* Deconstructor. */
+static void
+do_deinit (app_t app)
+{
+  if (app && app->app_local)
+    {
+      struct cache_s *c, *c2;
+
+      for (c = app->app_local->cache; c; c = c2)
+        {
+          c2 = c->next;
+          xfree (c);
+        }
+      xfree (app->app_local);
+      app->app_local = NULL;
+    }
+}
 
-/* Locate a TLV encoded data object in BUFFER of LENGTH and
-   return a pointer to value as well as its length in NBYTES.  Return
-   NULL if it was not found.  Note, that the function does not check
-   whether the value fits into the provided buffer. 
 
-   FIXME: Move this to an extra file, it is mostly duplicated from card.c.
-*/
-static const unsigned char *
-find_tlv (const unsigned char *buffer, size_t length,
-          int tag, size_t *nbytes, int nestlevel)
+/* Wrapper around iso7816_get_data which first tries to get the data
+   from the cache. */
+static gpg_error_t
+get_cached_data (app_t app, int tag, 
+                 unsigned char **result, size_t *resultlen)
 {
-  const unsigned char *s = buffer;
-  size_t n = length;
+  gpg_error_t err;
+  int i;
+  unsigned char *p;
   size_t len;
-  int this_tag;
-  int composite;
-    
-  for (;;)
+  struct cache_s *c;
+
+
+  *result = NULL;
+  *resultlen = 0;
+
+  if (app->app_local)
     {
-      buffer = s;
-      if (n < 2)
-        return NULL; /* buffer definitely too short for tag and length. */
-      if (!*s || *s == 0xff)
-        { /* Skip optional filler between TLV objects. */
-          s++;
-          n--;
-          continue;
-        }
-      composite = !!(*s & 0x20);
-      if ((*s & 0x1f) == 0x1f)
-        { /* more tag bytes to follow */
-          s++;
-          n--;
-          if (n < 2)
-            return NULL; /* buffer definitely too short for tag and length. */
-          if ((*s & 0x1f) == 0x1f)
-            return NULL; /* We support only up to 2 bytes. */
-          this_tag = (s[-1] << 8) | (s[0] & 0x7f);
-        }
-      else
-        this_tag = s[0];
-      len = s[1];
-      s += 2; n -= 2;
-      if (len < 0x80)
-        ;
-      else if (len == 0x81)
-        { /* One byte length follows. */
-          if (!n)
-            return NULL; /* we expected 1 more bytes with the length. */
-          len = s[0];
-          s++; n--;
-        }
-      else if (len == 0x82)
-        { /* Two byte length follows. */
-          if (n < 2)
-            return NULL; /* we expected 2 more bytes with the length. */
-          len = (s[0] << 8) | s[1];
-          s += 2; n -= 2;
-        }
-      else
-        return NULL; /* APDU limit is 65535, thus it does not make
-                        sense to assume longer length fields. */
-
-      if (composite && nestlevel < 100)
-        { /* Dive into this composite DO after checking for too deep
-             nesting. */
-          const unsigned char *tmp_s;
-          size_t tmp_len;
-          
-          tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1);
-          if (tmp_s)
-            {
-              *nbytes = tmp_len;
-              return tmp_s;
-            }
+      for (c=app->app_local->cache; c; c = c->next)
+        if (c->tag == tag)
+          {
+              p = xtrymalloc (c->length);
+              if (!p)
+                return gpg_error (gpg_err_code_from_errno (errno));
+              memcpy (p, c->data, c->length);
+              *resultlen = c->length;
+              *result = p;
+              return 0;
+          }
+    }
+  
+  err = iso7816_get_data (app->slot, tag, &p, &len);
+  if (err)
+    return err;
+  *result = p;
+  *resultlen = len;
+
+  /* Check whether we should cache this object. */
+  for (i=0; data_objects[i].tag; i++)
+    if (data_objects[i].tag == tag)
+      {
+        if (data_objects[i].dont_cache)
+          return 0;
+        break;
+      }
+
+  /* No, cache it. */
+  if (!app->app_local)
+    app->app_local = xtrycalloc (1, sizeof *app->app_local);
+
+  /* Note that we can safely ignore out of core errors. */
+  if (app->app_local)
+    {
+      for (c=app->app_local->cache; c; c = c->next)
+        assert (c->tag != tag);
+      
+      c = xtrymalloc (sizeof *c + len);
+      if (c)
+        {
+          memcpy (c->data, p, len);
+          c->length = len;
+          c->tag = tag;
+          c->next = app->app_local->cache;
+          app->app_local->cache = c;
         }
+    }
+
+  return 0;
+}
+
+/* Remove DO at TAG from the cache. */
+static void
+flush_cache_item (app_t app, int tag)
+{
+  struct cache_s *c, *cprev;
+  int i;
+
+  if (!app->app_local)
+    return;
+
+  for (c=app->app_local->cache, cprev=NULL; c ; cprev=c, c = c->next)
+    if (c->tag == tag)
+      {
+        if (cprev)
+          cprev->next = c->next;
+        else
+          app->app_local->cache = c->next;
+        xfree (c);
+
+        for (c=app->app_local->cache; c ; c = c->next)
+          assert (c->tag != tag); /* Oops: duplicated entry. */
+        return;
+      }
+
+  /* Try again if we have an outer tag. */
+  for (i=0; data_objects[i].tag; i++)
+    if (data_objects[i].tag == tag && data_objects[i].get_from
+        && data_objects[i].get_from != tag)
+      flush_cache_item (app, data_objects[i].get_from);
+}
+
+/* Flush all entries from the cache which might be out of sync after
+   an error. */
+static void
+flush_cache_after_error (app_t app)
+{
+  int i;
+
+  for (i=0; data_objects[i].tag; i++)
+    if (data_objects[i].flush_on_error)
+      flush_cache_item (app, data_objects[i].tag);
+}
+
 
-      if (this_tag == tag)
+/* Flush the entire cache. */
+static void
+flush_cache (app_t app)
+{
+  if (app && app->app_local)
+    {
+      struct cache_s *c, *c2;
+
+      for (c = app->app_local->cache; c; c = c2)
         {
-          *nbytes = len;
-          return s;
+          c2 = c->next;
+          xfree (c);
         }
-      if (len > n)
-        return NULL; /* buffer too short to skip to the next tag. */
-      s += len; n -= len;
+      app->app_local->cache = NULL;
     }
 }
 
@@ -174,7 +249,7 @@ find_tlv (const unsigned char *buffer, size_t length,
    NULL if not found or a pointer which must be used to release the
    buffer holding value. */
 static void *
-get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
+get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes)
 {
   int rc, i;
   unsigned char *buffer;
@@ -191,13 +266,13 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
   rc = -1;
   if (data_objects[i].tag && data_objects[i].get_from)
     {
-      rc = iso7816_get_data (slot, data_objects[i].get_from,
-                             &buffer, &buflen);
+      rc = get_cached_data (app, data_objects[i].get_from,
+                            &buffer, &buflen);
       if (!rc)
         {
           const unsigned char *s;
 
-          s = find_tlv (buffer, buflen, tag, &valuelen, 0);
+          s = find_tlv (buffer, buflen, tag, &valuelen);
           if (!s)
             value = NULL; /* not found */
           else if (valuelen > buflen - (s - buffer))
@@ -213,7 +288,7 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
 
   if (!value) /* Not in a constructed DO, try simple. */
     {
-      rc = iso7816_get_data (slot, tag, &buffer, &buflen);
+      rc = get_cached_data (app, tag, &buffer, &buflen);
       if (!rc)
         {
           value = buffer;
@@ -271,7 +346,7 @@ dump_all_do (int slot)
                   if (j==i || data_objects[i].tag != data_objects[j].get_from)
                     continue;
                   value = find_tlv (buffer, buflen,
-                                    data_objects[j].tag, &valuelen, 0);
+                                    data_objects[j].tag, &valuelen);
                   if (!value)
                     ; /* not found */
                   else if (valuelen > buflen - (value - buffer))
@@ -333,7 +408,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
   n = 6 + 2 + mlen + 2 + elen;
   p = buffer = xtrymalloc (3 + n);
   if (!buffer)
-    return out_of_core ();
+    return gpg_error (gpg_err_code_from_errno (errno));
   
   *p++ = 0x99;     /* ctb */
   *p++ = n >> 8;   /* 2 byte length header */
@@ -443,7 +518,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
     {
       /* The serial number is very special.  We could have used the
          AID DO to retrieve it, but we have it already in the app
-         context and the stanmp argument is required anyway which we
+         context and the stamp argument is required anyway which we
          can't by other means. The AID DO is available anyway but not
          hex formatted. */
       char *serial;
@@ -462,7 +537,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
       return 0;
     }
 
-  relptr = get_one_do (app->slot, table[idx].tag, &value, &valuelen);
+  relptr = get_one_do (app, table[idx].tag, &value, &valuelen);
   if (relptr)
     {
       if (table[idx].special == 1)
@@ -517,7 +592,7 @@ do_learn_status (APP app, CTRL ctrl)
 /* Verify CHV2 if required.  Depending on the configuration of the
    card CHV1 will also be verified. */
 static int
-verify_chv2 (APP app,
+verify_chv2 (app_t app,
              int (*pincb)(void*, const char *, char **),
              void *pincb_arg)
 {
@@ -534,11 +609,19 @@ verify_chv2 (APP app,
           return rc;
         }
 
+      if (strlen (pinvalue) < 6)
+        {
+          log_error ("prassphrase (CHV2) is too short; minimum length is 6\n");
+          xfree (pinvalue);
+          return gpg_error (GPG_ERR_BAD_PIN);
+        }
+
       rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
       if (rc)
         {
           log_error ("verify CHV2 failed: %s\n", gpg_strerror (rc));
           xfree (pinvalue);
+          flush_cache_after_error (app);
           return rc;
         }
       app->did_chv2 = 1;
@@ -552,6 +635,7 @@ verify_chv2 (APP app,
             {
               log_error ("verify CHV1 failed: %s\n", gpg_strerror (rc));
               xfree (pinvalue);
+              flush_cache_after_error (app);
               return rc;
             }
           app->did_chv1 = 1;
@@ -569,6 +653,12 @@ verify_chv3 (APP app,
 {
   int rc = 0;
 
+  if (!opt.allow_admin)
+    {
+      log_info ("access to admin commands is not configured\n");
+      return gpg_error (GPG_ERR_EACCES);
+    }
+      
   if (!app->did_chv3) 
     {
       char *pinvalue;
@@ -580,11 +670,19 @@ verify_chv3 (APP app,
           return rc;
         }
 
+      if (strlen (pinvalue) < 6)
+        {
+          log_error ("prassphrase (CHV3) is too short; minimum length is 6\n");
+          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 CHV3 failed: %s\n", gpg_strerror (rc));
+          flush_cache_after_error (app);
           return rc;
         }
       app->did_chv3 = 1;
@@ -629,6 +727,10 @@ do_setattr (APP app, const char *name,
   if (rc)
     return rc;
 
+  /* Flush the cache before writing it, so that the next get operation
+     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 (rc)
     log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc));
@@ -715,7 +817,8 @@ do_change_pin (APP app, CTRL ctrl,  const char *chvnostr, int reset_mode,
                                             pinvalue, strlen (pinvalue));
     }
   xfree (pinvalue);
-
+  if (rc)
+    flush_cache_after_error (app);
 
  leave:
   return rc;
@@ -746,13 +849,17 @@ do_genkey (APP app, CTRL ctrl,  const char *keynostr, unsigned int flags,
     return gpg_error (GPG_ERR_INV_ID);
   keyno--;
 
+  /* We flush the cache to increase the traffic before a key
+     generation.  This _might_ help a card to gather more entropy. */
+  flush_cache (app);
+
   rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
   if (rc)
     {
       log_error ("error reading application data\n");
       return gpg_error (GPG_ERR_GENERAL);
     }
-  fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
+  fpr = find_tlv (buffer, buflen, 0x00C5, &n);
   if (!fpr || n != 60)
     {
       rc = gpg_error (GPG_ERR_GENERAL);
@@ -779,6 +886,7 @@ do_genkey (APP app, CTRL ctrl,  const char *keynostr, unsigned int flags,
     goto leave;
 
   xfree (buffer); buffer = NULL;
+
 #if 1
   log_info ("please wait while key is being generated ...\n");
   start_at = time (NULL);
@@ -800,7 +908,7 @@ do_genkey (APP app, CTRL 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, 0);
+  keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
   if (!keydata)
     {
       rc = gpg_error (GPG_ERR_CARD);
@@ -808,7 +916,7 @@ do_genkey (APP app, CTRL ctrl,  const char *keynostr, unsigned int flags,
       goto leave;
     }
  
-  m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
+  m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
   if (!m)
     {
       rc = gpg_error (GPG_ERR_CARD);
@@ -818,7 +926,7 @@ do_genkey (APP app, CTRL ctrl,  const char *keynostr, unsigned int flags,
 /*    log_printhex ("RSA n:", m, mlen); */
   send_key_data (ctrl, "n", m, mlen);
 
-  e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
+  e = find_tlv (keydata, keydatalen, 0x0082, &elen);
   if (!e)
     {
       rc = gpg_error (GPG_ERR_CARD);
@@ -869,7 +977,7 @@ get_sig_counter (APP app)
   size_t valuelen;
   unsigned long ul;
 
-  relptr = get_one_do (app->slot, 0x0093, &value, &valuelen);
+  relptr = get_one_do (app, 0x0093, &value, &valuelen);
   if (!relptr)
     return 0;
   ul = convert_sig_counter_value (value, valuelen);
@@ -887,13 +995,13 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
   
   assert (keyno >= 1 && keyno <= 3);
 
-  rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
+  rc = get_cached_data (app, 0x006E, &buffer, &buflen);
   if (rc)
     {
       log_error ("error reading application data\n");
       return gpg_error (GPG_ERR_GENERAL);
     }
-  fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
+  fpr = find_tlv (buffer, buflen, 0x00C5, &n);
   if (!fpr || n != 60)
     {
       xfree (buffer);
@@ -1035,11 +1143,19 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
           return rc;
         }
 
+      if (strlen (pinvalue) < 6)
+        {
+          log_error ("prassphrase (CHV1) is too short; minimum length is 6\n");
+          xfree (pinvalue);
+          return gpg_error (GPG_ERR_BAD_PIN);
+        }
+
       rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
       if (rc)
         {
           log_error ("verify CHV1 failed\n");
           xfree (pinvalue);
+          flush_cache_after_error (app);
           return rc;
         }
       app->did_chv1 = 1;
@@ -1053,6 +1169,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
             {
               log_error ("verify CHV2 failed\n");
               xfree (pinvalue);
+              flush_cache_after_error (app);
               return rc;
             }
           app->did_chv2 = 1;
@@ -1182,7 +1299,8 @@ do_decipher (APP app, const char *keyidstr,
 
   rc = verify_chv2 (app, pincb, pincb_arg);
   if (!rc)
-    rc = iso7816_decipher (app->slot, indata, indatalen, outdata, outdatalen);
+    rc = iso7816_decipher (app->slot, indata, indatalen, 0,
+                           outdata, outdatalen);
   return rc;
 }
 
@@ -1241,7 +1359,7 @@ do_check_pin (APP app, const char *keyidstr,
 /* Select the OpenPGP application on the card in SLOT.  This function
    must be used before any other OpenPGP application functions. */
 int
-app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
+app_select_openpgp (APP app)
 {
   static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
   int slot = app->slot;
@@ -1253,10 +1371,17 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
   rc = iso7816_select_application (slot, aid, sizeof aid);
   if (!rc)
     {
+      app->apptype = "OPENPGP";
+
       app->did_chv1 = 0;
       app->did_chv2 = 0;
       app->did_chv3 = 0;
 
+      /* The OpenPGP card returns the serial number as part of the
+         AID; because we prefer to use OpenPGP serial numbers, we
+         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);
       if (rc)
         goto leave;
@@ -1266,17 +1391,14 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
           log_printhex ("", buffer, buflen);
         }
 
-      if (sn)
-        {
-          *sn = buffer;
-          *snlen = buflen;
-          app->card_version = buffer[6] << 8;
-          app->card_version |= buffer[7];
-        }
-      else
-        xfree (buffer);
+      app->card_version = buffer[6] << 8;
+      app->card_version |= buffer[7];
+      xfree (app->serialno);
+      app->serialno = buffer;
+      app->serialnolen = buflen;
+      buffer = NULL;
 
-      relptr = get_one_do (app->slot, 0x00C4, &buffer, &buflen);
+      relptr = get_one_do (app, 0x00C4, &buffer, &buflen);
       if (!relptr)
         {
           log_error ("can't access CHV Status Bytes - invalid OpenPGP card?\n");
@@ -1288,7 +1410,9 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
       if (opt.verbose > 1)
         dump_all_do (slot);
 
+      app->fnc.deinit = do_deinit;
       app->fnc.learn_status = do_learn_status;
+      app->fnc.readcert = NULL;
       app->fnc.getattr = do_getattr;
       app->fnc.setattr = do_setattr;
       app->fnc.genkey = do_genkey;
@@ -1340,7 +1464,7 @@ app_openpgp_cardinfo (APP app,
   if (disp_name)
     {
       *disp_name = NULL;
-      relptr = get_one_do (app->slot, 0x005B, &value, &valuelen);
+      relptr = get_one_do (app, 0x005B, &value, &valuelen);
       if (relptr)
         {
           *disp_name = make_printable_string (value, valuelen, 0);
@@ -1351,7 +1475,7 @@ app_openpgp_cardinfo (APP app,
   if (pubkey_url)
     {
       *pubkey_url = NULL;
-      relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen);
+      relptr = get_one_do (app, 0x5F50, &value, &valuelen);
       if (relptr)
         {
           *pubkey_url = make_printable_string (value, valuelen, 0);
@@ -1365,7 +1489,7 @@ app_openpgp_cardinfo (APP app,
     *fpr2 = NULL;
   if (fpr3)
     *fpr3 = NULL;
-  relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen);
+  relptr = get_one_do (app, 0x00C5, &value, &valuelen);
   if (relptr && valuelen >= 60)
     {
       if (fpr1)
@@ -1471,7 +1595,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
       goto leave;
     }
 
-  keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
+  keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
   if (!keydata)
     {
       log_error ("response does not contain the public key data\n");
@@ -1479,7 +1603,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
       goto leave;
     }
  
-  a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0);
+  a = find_tlv (keydata, keydatalen, 0x0081, &alen);
   if (!a)
     {
       log_error ("response does not contain the RSA modulus\n");
@@ -1490,7 +1614,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
   *m = xmalloc (alen);
   memcpy (*m, a, alen);
   
-  a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0);
+  a = find_tlv (keydata, keydatalen, 0x0082, &alen);
   if (!e)
     {
       log_error ("response does not contain the RSA public exponent\n");
index de445b7..c56ab24 100644 (file)
@@ -169,8 +169,7 @@ print_sha1_fpr_colon (FILE *fp, const unsigned char *fpr)
 static void
 print_name (FILE *fp, const char *text, const char *name)
 {
-  tty_fprintf (fp, text);
-
+  tty_fprintf (fp, "%s", text);
 
   /* FIXME: tty_printf_utf8_string2 eats everything after and
      including an @ - e.g. when printing an url. */
@@ -192,7 +191,7 @@ print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
   if (opt.with_colons)
     fprintf (fp, "%s:", tag);
   else
-    tty_fprintf (fp, text);
+    tty_fprintf (fp, "%s", text);
 
   if (name && *name)
     {
@@ -446,6 +445,14 @@ change_name (void)
     if (*p == ' ')
       *p = '<';
 
+  if (strlen (isoname) > 39 )
+    {
+      tty_printf (_("Error: Combined name too long "
+                    "(limit is %d characters).\n"), 39);    
+      xfree (isoname);
+      return -1;
+    }
+
   log_debug ("setting Name to `%s'\n", isoname);
   rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) );
   if (rc)
@@ -468,6 +475,14 @@ change_url (void)
   trim_spaces (url);
   cpr_kill_prompt ();
 
+  if (strlen (url) > 254 )
+    {
+      tty_printf (_("Error: URL too long "
+                    "(limit is %d characters).\n"), 254);    
+      xfree (url);
+      return -1;
+    }
+
   rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) );
   if (rc)
     log_error ("error setting URL: %s\n", gpg_strerror (rc));
@@ -488,6 +503,14 @@ change_login (void)
   trim_spaces (data);
   cpr_kill_prompt ();
 
+  if (strlen (data) > 254 )
+    {
+      tty_printf (_("Error: Login data too long "
+                    "(limit is %d characters).\n"), 254);    
+      xfree (data);
+      return -1;
+    }
+
   rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) );
   if (rc)
     log_error ("error setting login data: %s\n", gpg_strerror (rc));
index 55729bf..a192462 100644 (file)
@@ -262,7 +262,7 @@ open_card (void)
 
   app = xcalloc (1, sizeof *app);
   app->slot = slot;
-  rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
+  rc = app_select_openpgp (app);
   if (rc && !opt.batch)
     {
       write_status_text (STATUS_CARDCTRL, "1");
index b2f0205..454f8bf 100644 (file)
@@ -63,8 +63,10 @@ struct agent_card_genkey_s {
 struct app_ctx_s;
 struct ctrl_ctx_s;
 
-typedef struct app_ctx_s *APP;
-typedef struct ctrl_ctx_s *CTRL;
+typedef struct app_ctx_s *APP; /* deprecated. */
+typedef struct app_ctx_s *app_t;
+typedef struct ctrl_ctx_s *CTRL; /* deprecated. */
+typedef struct ctrl_ctx_s *ctrl_t;
 
 
 #define GPG_ERR_GENERAL           G10ERR_GENERAL
@@ -86,6 +88,15 @@ typedef struct ctrl_ctx_s *CTRL;
 #define GPG_ERR_WRONG_CARD        G10ERR_GENERAL
 #define GPG_ERR_WRONG_SECKEY      G10ERR_WRONG_SECKEY
 #define GPG_ERR_PIN_NOT_SYNCED    G10ERR_GENERAL
+#define GPG_ERR_NOT_FOUND         G10ERR_GENERAL
+#define GPG_ERR_BUG               G10ERR_GENERAL
+#define GPG_ERR_NOT_IMPLEMENTED   G10ERR_GENERAL
+#define GPG_ERR_BAD_BER           G10ERR_GENERAL
+#define GPG_ERR_EOF               (-1)
+
+#define GPG_ERR_EBUSY             G10ERR_GENERAL
+#define GPG_ERR_ENOENT            G10ERR_OPEN_FILE
+#define GPG_ERR_EACCES            G10ERR_UNSUPPORTED
 
 typedef int gpg_error_t;
 typedef int gpg_err_code_t;
@@ -94,7 +105,7 @@ typedef int gpg_err_code_t;
 #define gpg_err_code(n) (n)
 #define gpg_strerror(n) g10_errstr ((n))
 #define gpg_error_from_errno(n) (G10ERR_GENERAL) /*FIXME*/
-
+#define gpg_err_code_from_errno(n) (G10ERR_GENERAL)
 
 /* We are not using it in a library, so we even let xtrymalloc
    abort. Because we won't never return from these malloc functions,
index 099dae2..cd0bee6 100644 (file)
@@ -1,5 +1,5 @@
 /* ccid-driver.c - USB ChipCardInterfaceDevices driver
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
  *      Written by Werner Koch.
  *
  * This file is part of GnuPG.
 #define DRVNAME "ccid-driver: "
 
 
-#ifdef GNUPG_MAJOR_VERSION  /* This source is used within GnuPG. */
+/* Depending on how this source is used we either define our error
+   output to go to stderr or to the jnlib based logging functions.  We
+   use the latter when GNUPG_MAJOR_VERSION is defines or when both,
+   GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined.
+*/
+#if defined(GNUPG_MAJOR_VERSION) \
+    || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING))
 
-# if GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#  include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
 #  include "options.h"
 #  include "util.h"
 #  include "memory.h"
 #  include "cardglue.h"
 # else /* This is the modularized GnuPG 1.9 or later. */
 #  include "scdaemon.h"
-# endif
+#endif
+
+/* Disable all debugging output for now. */
+#undef DBG_CARD_IO
+#define DBG_CARD_IO 0
+
+/* Define to print information pertaining the T=1 protocol. */
+#undef DEBUG_T1 
+
 
 # define DEBUGOUT(t)         do { if (DBG_CARD_IO) \
                                   log_debug (DRVNAME t); } while (0)
 #endif /* This source not used by scdaemon. */
 
 
-/* Define to print information pertaining the T=1 protocol. */
-#undef DEBUG_T1 1
 
 
 
@@ -171,9 +185,15 @@ struct ccid_driver_s {
   int seqno;
   unsigned char t1_ns;
   unsigned char t1_nr;
+  int nonnull_nad;
+  int auto_ifsd;
+  int max_ifsd;
+  int ifsd;
 };
 
 
+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_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
                     size_t *nread, int expected_type, int seqno);
@@ -207,13 +227,18 @@ set_msg_len (unsigned char *msg, unsigned int length)
    Note, that this code is based on the one in lsusb.c of the
    usb-utils package, I wrote on 2003-09-01. -wk. */
 static int
-parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
+parse_ccid_descriptor (ccid_driver_t handle,
+                       const unsigned char *buf, size_t buflen)
 {
   unsigned int i;
   unsigned int us;
   int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
 
 
+  handle->nonnull_nad = 0;
+  handle->auto_ifsd = 0;
+  handle->max_ifsd = 32;
+  handle->ifsd = 0;
   if (buflen < 54 || buf[0] < 54)
     {
       DEBUGOUT ("CCID device descriptor is too short\n");
@@ -259,6 +284,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
         
   us = convert_le_u32(buf+28);
   DEBUGOUT_1 ("  dwMaxIFSD           %5u\n", us);
+  handle->max_ifsd = us;
 
   us = convert_le_u32(buf+32);
   DEBUGOUT_1 ("  dwSyncProtocols  %08X ", us);
@@ -307,9 +333,15 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
   if ((us & 0x0100))
     DEBUGOUT ("    CCID can set ICC in clock stop mode\n");
   if ((us & 0x0200))
-    DEBUGOUT ("    NAD value other than 0x00 accpeted\n");
+    {
+      DEBUGOUT ("    NAD value other than 0x00 accepted\n");
+      handle->nonnull_nad = 1;
+    }
   if ((us & 0x0400))
-    DEBUGOUT ("    Auto IFSD exchange\n");
+    {
+      DEBUGOUT ("    Auto IFSD exchange\n");
+      handle->auto_ifsd = 1;
+    }
 
   if ((us & 0x00010000))
     {
@@ -376,7 +408,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
    that the device is usable for us.  Returns 0 on success or an error
    code. */
 static int
-read_device_info (struct usb_device *dev)
+read_device_info (ccid_driver_t handle, struct usb_device *dev)
 {
   int cfg_no;
 
@@ -401,8 +433,9 @@ read_device_info (struct usb_device *dev)
                 {
                   if (ifcdesc->extra)
                     {
-                      if (!parse_ccid_descriptor (ifcdesc->extra,
-                                                 ifcdesc->extralen))
+                      if (!parse_ccid_descriptor (handle, 
+                                                  ifcdesc->extra,
+                                                  ifcdesc->extralen))
                         return 0; /* okay. we can use it. */
                     }
                 }
@@ -445,10 +478,22 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
                   dev->descriptor->idVendor, dev->descriptor->idProduct);
       if (!readerno)
         {
-          rc = read_device_info (dev);
+          *handle = calloc (1, sizeof **handle);
+          if (!*handle)
+            {
+              DEBUGOUT ("out of memory\n");
+              rc = -1;
+              free (*handle);
+              *handle = NULL;
+              goto leave;
+            }
+
+          rc = read_device_info (*handle, dev);
           if (rc)
             {
               DEBUGOUT ("device not supported\n");
+              free (*handle);
+              *handle = NULL;
               goto leave;
             }
 
@@ -456,6 +501,8 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
           if (rc)
             {
               DEBUGOUT_1 ("usb_open failed: %d\n", rc);
+              free (*handle);
+              *handle = NULL;
               goto leave;
             }
 
@@ -466,16 +513,11 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
           if (rc)
             {
               DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+              free (*handle);
+              *handle = NULL;
               goto leave;
             }
 
-          *handle = calloc (1, sizeof **handle);
-          if (!*handle)
-            {
-              DEBUGOUT ("out of memory\n");
-              rc = -1;
-              goto leave;
-            }
           (*handle)->idev = idev;
           idev = NULL;
           /* FIXME: Do we need to get the endpoint addresses from the
@@ -495,7 +537,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
   usb_free_match (match);
 
   if (!rc && !*handle)
-    rc = -1; /* In case we didn't enter the while lool at all. */
+    rc = -1; /* In case we didn't enter the while loop at all. */
 
   return rc;
 }
@@ -579,6 +621,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
   int i, rc;
   size_t msglen;
 
+ retry:
   rc = usb_bulk_read (handle->idev, 
                       0x82,
                       buffer, length,
@@ -615,6 +658,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       return -1;
     }
 
+  if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
+    { 
+      /* Card present and active, time extension requested. */
+      DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
+                  buffer[7], buffer[8]);
+      goto retry;
+    }
+  
   DEBUGOUT_3 ("status: %02X  error: %02X  octet[9]: %02X\n"
               "               data:",  buffer[7], buffer[8], buffer[9] );
   for (i=10; i < msglen; i++)
@@ -682,7 +733,7 @@ ccid_poll (ccid_driver_t handle)
 
 
 int 
-ccid_slot_status (ccid_driver_t handle)
+ccid_slot_status (ccid_driver_t handle, int *statusbits)
 {
   int rc;
   unsigned char msg[100];
@@ -703,6 +754,7 @@ ccid_slot_status (ccid_driver_t handle)
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
   if (rc)
     return rc;
+  *statusbits = (msg[7] & 3);
 
   return 0;
 }
@@ -714,8 +766,12 @@ ccid_get_atr (ccid_driver_t handle,
 {
   int rc;
   unsigned char msg[100];
-  size_t msglen;
+  unsigned char *tpdu;
+  size_t msglen, tpdulen;
   unsigned char seqno;
+  int use_crc = 0;
+  unsigned int edc;
+  int i;
 
   msg[0] = PC_to_RDR_IccPowerOn;
   msg[5] = 0; /* slot */
@@ -743,11 +799,135 @@ ccid_get_atr (ccid_driver_t handle,
       *atrlen = n;
     }
 
+  /* Setup parameters to select T=1. */
+  msg[0] = PC_to_RDR_SetParameters;
+  msg[5] = 0; /* slot */
+  msg[6] = seqno = handle->seqno++;
+  msg[7] = 1; /* Select T=1. */
+  msg[8] = 0; /* RFU */
+  msg[9] = 0; /* RFU */
+
+  /* FIXME: Get those values from the ATR. */
+  msg[10]= 0x01; /* Fi/Di */
+  msg[11]= 0x10; /* LRC, direct convention. */
+  msg[12]= 0;    /* Extra guardtime. */
+  msg[13]= 0x41; /* BWI/CWI */
+  msg[14]= 0;    /* No clock stoppping. */
+  msg[15]= 254;  /* IFSC */
+  msg[16]= 0;    /* Does not support non default NAD values. */
+  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);
+  if (rc)
+    return rc;
+  /* Note that we ignore the error code on purpose. */
+  bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno);
+
+
+  /* Send an S-Block with our maximun IFSD to the CCID.  */
+  if (!handle->auto_ifsd)
+    {
+      tpdu = msg+10;
+      /* NAD: DAD=1, SAD=0 */
+      tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+      tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
+      tpdu[2] = 1;
+      tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32; 
+      tpdulen = 4;
+      edc = compute_edc (tpdu, tpdulen, use_crc);
+      if (use_crc)
+        tpdu[tpdulen++] = (edc >> 8);
+      tpdu[tpdulen++] = edc;
+
+      msg[0] = PC_to_RDR_XfrBlock;
+      msg[5] = 0; /* slot */
+      msg[6] = seqno = handle->seqno++;
+      msg[7] = 0; 
+      msg[8] = 0; /* RFU */
+      msg[9] = 0; /* RFU */
+      set_msg_len (msg, tpdulen);
+      msglen = 10 + tpdulen;
+
+      DEBUGOUT ("sending");
+      for (i=0; i < msglen; i++)
+        DEBUGOUT_CONT_1 (" %02X", msg[i]);
+      DEBUGOUT_LF ();
+
+#ifdef DEBUG_T1      
+      fprintf (stderr, "T1: put %c-block seq=%d\n",
+               ((msg[11] & 0xc0) == 0x80)? 'R' :
+               (msg[11] & 0x80)? 'S' : 'I',
+               ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
+#endif  
+
+      rc = bulk_out (handle, msg, msglen);
+      if (rc)
+        return rc;
+
+      /* Fixme: The next line for the current Valgrid without support
+         for USB IOCTLs. */
+      memset (msg, 0, sizeof msg);
+
+      rc = bulk_in (handle, msg, sizeof msg, &msglen,
+                    RDR_to_PC_DataBlock, seqno);
+      if (rc)
+        return rc;
+      
+      tpdu = msg + 10;
+      tpdulen = msglen - 10;
+      
+      if (tpdulen < 4) 
+        {
+          DEBUGOUT ("cannot yet handle short blocks!\n");
+          return -1; 
+        }
+
+#ifdef DEBUG_T1
+      fprintf (stderr, "T1: got %c-block seq=%d err=%d\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
+               );
+#endif
+      if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
+        {
+          DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
+          return -1;
+        }
+      DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
+    }
+
   return 0;
 }
 
 
 \f
+
+static unsigned int 
+compute_edc (const unsigned char *data, size_t datalen, int use_crc)
+{
+  if (use_crc)
+    {
+      return 0x42; /* Not yet implemented. */
+    }
+  else
+    {
+      unsigned char crc = 0;
+      
+      for (; datalen; datalen--)
+        crc ^= *data++;
+      return crc;
+    }
+}
+
+
 /*
   Protocol T=1 overview
 
@@ -806,17 +986,19 @@ ccid_transceive (ccid_driver_t handle,
                  unsigned char *resp, size_t maxresplen, size_t *nresp)
 {
   int rc;
-  unsigned char send_buffer[10+258], recv_buffer[10+258];
+  unsigned char send_buffer[10+259], recv_buffer[10+259];
   const unsigned char *apdu;
   size_t apdulen;
   unsigned char *msg, *tpdu, *p;
-  size_t msglen, tpdulen, n;
+  size_t msglen, tpdulen, last_tpdulen, n;
   unsigned char seqno;
   int i;
-  unsigned char crc;
+  unsigned int edc;
+  int use_crc = 0;
   size_t dummy_nresp;
   int next_chunk = 1;
   int sending = 1;
+  int retries = 0;
 
   if (!nresp)
     nresp = &dummy_nresp;
@@ -839,7 +1021,8 @@ ccid_transceive (ccid_driver_t handle,
             return -1; /* Invalid length. */
 
           tpdu = msg+10;
-          tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+          /* NAD: DAD=1, SAD=0 */
+          tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
           tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
           if (apdulen > 128 /* fixme: replace by ifsc */)
             {
@@ -850,12 +1033,11 @@ ccid_transceive (ccid_driver_t handle,
             }
           tpdu[2] = apdulen;
           memcpy (tpdu+3, apdu, apdulen);
-          crc = 0;
-          for (i=0,p=tpdu; i < apdulen+3; i++)
-            crc ^= *p++;
-          tpdu[3+apdulen] = crc;
-          
-          tpdulen = apdulen + 4;
+          tpdulen = 3 + apdulen;
+          edc = compute_edc (tpdu, tpdulen, use_crc);
+          if (use_crc)
+            tpdu[tpdulen++] = (edc >> 8);
+          tpdu[tpdulen++] = edc;
         }
 
       msg[0] = PC_to_RDR_XfrBlock;
@@ -866,6 +1048,7 @@ ccid_transceive (ccid_driver_t handle,
       msg[9] = 0; /* RFU */
       set_msg_len (msg, tpdulen);
       msglen = 10 + tpdulen;
+      last_tpdulen = tpdulen;
 
       DEBUGOUT ("sending");
       for (i=0; i < msglen; i++)
@@ -913,7 +1096,7 @@ ccid_transceive (ccid_driver_t handle,
 
       if (!(tpdu[1] & 0x80))
         { /* This is an I-block. */
-          
+          retries = 0;
           if (sending)
             { /* last block sent was successful. */
               handle->t1_ns ^= 1;
@@ -924,13 +1107,15 @@ ccid_transceive (ccid_driver_t handle,
             { /* Reponse does not match our sequence number. */
               msg = send_buffer;
               tpdu = msg+10;
-              tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+              /* NAD: DAD=1, SAD=0 */
+              tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
               tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
               tpdu[2] = 0;
               tpdulen = 3;
-              for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
-                crc ^= *p++;
-              tpdu[tpdulen++] = crc;
+              edc = compute_edc (tpdu, tpdulen, use_crc);
+              if (use_crc)
+                tpdu[tpdulen++] = (edc >> 8);
+              tpdu[tpdulen++] = edc;
 
               continue;
             }
@@ -944,7 +1129,9 @@ ccid_transceive (ccid_driver_t handle,
             {
               if (n > maxresplen)
                 {
-                  DEBUGOUT ("provided buffer too short for received data\n");
+                  DEBUGOUT_2 ("provided buffer too short for received data "
+                              "(%u/%u)\n",
+                              (unsigned int)n, (unsigned int)maxresplen);
                   return -1;
                 }
               
@@ -959,20 +1146,27 @@ ccid_transceive (ccid_driver_t handle,
           
           msg = send_buffer;
           tpdu = msg+10;
-          tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+          /* NAD: DAD=1, SAD=0 */
+          tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
           tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
           tpdu[2] = 0;
           tpdulen = 3;
-          for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
-            crc ^= *p++;
-          tpdu[tpdulen++] = crc;
-          
+          edc = compute_edc (tpdu, tpdulen, use_crc);
+          if (use_crc)
+            tpdu[tpdulen++] = (edc >> 8);
+          tpdu[tpdulen++] = edc;
         }
       else if ((tpdu[1] & 0xc0) == 0x80)
         { /* This is a R-block. */
           if ( (tpdu[1] & 0x0f)) 
             { /* Error: repeat last block */
+              if (++retries > 3)
+                {
+                  DEBUGOUT ("3 failed retries\n");
+                  return -1;
+                }
               msg = send_buffer;
+              tpdulen = last_tpdulen;
             }
           else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
             { /* Reponse does not match our sequence number. */
@@ -981,6 +1175,7 @@ ccid_transceive (ccid_driver_t handle,
             }
           else if (sending)
             { /* Send next chunk. */
+              retries = 0;
               msg = send_buffer;
               next_chunk = 1;
               handle->t1_ns ^= 1;
@@ -993,6 +1188,7 @@ ccid_transceive (ccid_driver_t handle,
         }
       else 
         { /* This is a S-block. */
+          retries = 0;
           DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
                       (tpdu[1] & 0x20)? "response": "request",
                       (tpdu[1] & 0x1f));
@@ -1001,14 +1197,16 @@ ccid_transceive (ccid_driver_t handle,
               unsigned char bwi = tpdu[3];
               msg = send_buffer;
               tpdu = msg+10;
-              tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+              /* NAD: DAD=1, SAD=0 */
+              tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
               tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
               tpdu[2] = 1;
               tpdu[3] = bwi;
               tpdulen = 4;
-              for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
-                crc ^= *p++;
-              tpdu[tpdulen++] = crc;
+              edc = compute_edc (tpdu, tpdulen, use_crc);
+              if (use_crc)
+                tpdu[tpdulen++] = (edc >> 8);
+              tpdu[tpdulen++] = edc;
               DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
             }
           else
@@ -1028,6 +1226,7 @@ main (int argc, char **argv)
 {
   int rc;
   ccid_driver_t ccid;
+  unsigned int slotstat;
 
   rc = ccid_open_reader (&ccid, 0);
   if (rc)
@@ -1041,7 +1240,7 @@ main (int argc, char **argv)
 
   ccid_poll (ccid);
   fputs ("getting slot status ...\n", stderr);
-  rc = ccid_slot_status (ccid);
+  rc = ccid_slot_status (ccid, &slotstat);
   if (rc)
     return 1;
 
index e33be55..8b86eb1 100644 (file)
@@ -63,6 +63,7 @@ int ccid_open_reader (ccid_driver_t *handle, int readerno);
 int ccid_close_reader (ccid_driver_t handle);
 int ccid_get_atr (ccid_driver_t handle,
                   unsigned char *atr, size_t maxatrlen, size_t *atrlen);
+int ccid_slot_status (ccid_driver_t handle, int *statusbits);
 int ccid_transceive (ccid_driver_t handle,
                      const unsigned char *apdu, size_t apdulen,
                      unsigned char *resp, size_t maxresplen, size_t *nresp);
index 861d462..4281357 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -341,6 +341,8 @@ enum cmd_and_opt_values
     octapiDriver,
     opcscDriver,
     oDisableCCID,
+    oAllowAdmin,
+    oDenyAdmin,
 
     aTest
   };
@@ -521,6 +523,10 @@ static ARGPARSE_OPTS opts[] = {
     { oSetNotation,  "notation-data", 2, "@" }, /* Alias */
     { oSigNotation,  "sig-notation", 2, "@" },
     { oCertNotation, "cert-notation", 2, "@" },
+#ifdef ENABLE_CARD_SUPPORT
+    { oAllowAdmin, "allow-admin",0,N_("allow the use of admin card commands")},
+    { oDenyAdmin,  "deny-admin",0,"@"},
+#endif
 
     { 302, NULL, 0, N_(
   "@\n(See the man page for a complete listing of all commands and options)\n"
@@ -1699,6 +1705,8 @@ main( int argc, char **argv )
           case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
           case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
           case oDisableCCID: opt.disable_ccid = 1; break;
+          case oAllowAdmin: opt.allow_admin = 1; break;
+          case oDenyAdmin: opt.allow_admin = 0; break;
 #endif /* ENABLE_CARD_SUPPORT*/
 
          case oArmor: opt.armor = 1; opt.no_armor=0; break;
index f4aa18c..fd3f048 100644 (file)
@@ -1,5 +1,5 @@
 /* iso7816.c - ISO 7816 commands
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -24,7 +24,9 @@
 #include <stdlib.h>
 #include <string.h>
 
-#if GNUPG_MAJOR_VERSION == 1
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1
 /* This is used with GnuPG version < 1.9.  The code has been source
    copied from the current GnuPG >= 1.9  and is maintained over
    there. */
 #define CMD_RESET_RETRY_COUNTER   0x2C
 #define CMD_GET_DATA    0xCA
 #define CMD_PUT_DATA    0xDA
+#define CMD_MSE         0x22
 #define CMD_PSO         0x2A
 #define CMD_INTERNAL_AUTHENTICATE 0x88
 #define CMD_GENERATE_KEYPAIR      0x47
 #define CMD_GET_CHALLENGE         0x84
+#define CMD_READ_BINARY 0xB0
+#define CMD_READ_RECORD 0xB2
 
 static gpg_error_t
 map_sw (int sw)
@@ -66,6 +71,8 @@ map_sw (int sw)
     case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
     case SW_NOT_SUPPORTED:  ec = GPG_ERR_NOT_SUPPORTED; break;
     case SW_BAD_PARAMETER:  ec = GPG_ERR_INV_VALUE; break;
+    case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break;
+    case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
     case SW_REF_NOT_FOUND:  ec = GPG_ERR_NO_OBJ; break;
     case SW_BAD_P0_P1:      ec = GPG_ERR_INV_VALUE; break;
     case SW_INS_NOT_SUP:    ec = GPG_ERR_CARD; break;
@@ -75,6 +82,9 @@ map_sw (int sw)
     case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
     case SW_HOST_INV_VALUE:   ec = GPG_ERR_INV_VALUE; break;
     case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
+    case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
+    case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
+    case SW_HOST_BUSY:           ec = GPG_ERR_EBUSY; break;
     default:
       if ((sw & 0x010000))
         ec = GPG_ERR_GENERAL; /* Should not happen. */
@@ -91,18 +101,79 @@ map_sw (int sw)
    apdu_open_reader (), AID is a buffer of size AIDLEN holding the
    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 GNUPG error code.  Note that ISO error codes are
+   for okay or a GPG error code.  Note that ISO error codes are
    internally mapped. */
 gpg_error_t
 iso7816_select_application (int slot, const char *aid, size_t aidlen)
 {
+  static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
   int sw;
+  int p1 = 0x0C; /* No FCI to be returned. */
+  
+  if (aidlen == sizeof openpgp_aid
+      && !memcmp (aid, openpgp_aid, sizeof openpgp_aid))
+    p1 = 0; /* The current openpgp cards don't allow 0x0c. */
 
-  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
+  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid);
   return map_sw (sw);
 }
 
 
+gpg_error_t
+iso7816_select_file (int slot, int tag, int is_dir,
+                     unsigned char **result, size_t *resultlen)
+{
+  int sw, p0, p1;
+  unsigned char tagbuf[2];
+
+  tagbuf[0] = (tag >> 8) & 0xff;
+  tagbuf[1] = tag & 0xff;
+
+  if (result || resultlen)
+    {
+      *result = NULL;
+      *resultlen = 0;
+      return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
+  else
+    {
+      p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
+      p1 = 0x0c; /* No FC return. */
+      sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+                             p0, p1, 2, tagbuf );
+      return map_sw (sw);
+    }
+
+  return 0;
+}
+
+
+/* This is a private command currently only working for TCOS cards. */
+gpg_error_t
+iso7816_list_directory (int slot, int list_dirs,
+                        unsigned char **result, size_t *resultlen)
+{
+  int sw;
+
+  if (!result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
+                  result, resultlen);
+  if (sw != SW_SUCCESS)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+    }
+  return map_sw (sw);
+}
+
+
+
 /* Perform a VERIFY command on SLOT using the card holder verification
    vector CHVNO with a CHV of lenght CHVLEN.  Returns 0 on success. */
 gpg_error_t
@@ -134,7 +205,7 @@ iso7816_change_reference_data (int slot, int chvno,
 
   buf = xtrymalloc (oldchvlen + newchvlen);
   if (!buf)
-    return out_of_core ();
+    return gpg_error (gpg_err_code_from_errno (errno));
   if (oldchvlen)
     memcpy (buf, oldchv, oldchvlen);
   memcpy (buf+oldchvlen, newchv, newchvlen);
@@ -205,6 +276,23 @@ iso7816_put_data (int slot, int tag,
   return map_sw (sw);
 }
 
+/* Manage Security Environment.  This is a weird operation and there
+   is no easy abstraction for it.  Furthermore, some card seem to have
+   a different interpreation of 7816-8 and thus we resort to let the
+   caller decide what to do. */
+gpg_error_t
+iso7816_manage_security_env (int slot, int p1, int p2,
+                             const unsigned char *data, size_t datalen)
+{
+  int sw;
+
+  if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 || !data || !datalen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, datalen, data);
+  return map_sw (sw);
+}
+
 
 /* Perform the security operation COMPUTE DIGITAL SIGANTURE.  On
    success 0 is returned and the data is availavle in a newly
@@ -236,13 +324,14 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
 }
 
 
-/* Perform the security operation DECIPHER.  On
-   success 0 is returned and the plaintext is available in a newly
-   allocated buffer stored at RESULT with its length stored at
-   RESULTLEN. */
+/* Perform the security operation DECIPHER.  PADIND is the padding
+   indicator to be used.  It should be 0 if no padding is required, a
+   value of -1 suppresses the padding byte.  On success 0 is returned
+   and the plaintext is available in a newly allocated buffer stored
+   at RESULT with its length stored at RESULTLEN. */
 gpg_error_t
 iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
-                  unsigned char **result, size_t *resultlen)
+                  int padind, unsigned char **result, size_t *resultlen)
 {
   int sw;
   unsigned char *buf;
@@ -252,15 +341,24 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
   *result = NULL;
   *resultlen = 0;
 
-  /* We need to prepend the padding indicator. */
-  buf = xtrymalloc (datalen + 1);
-  if (!buf)
-    return out_of_core ();
-  *buf = 0; /* Padding indicator. */
-  memcpy (buf+1, data, datalen);
-  sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
-                  result, resultlen);
-  xfree (buf);
+  if (padind >= 0)
+    {
+      /* We need to prepend the padding indicator. */
+      buf = xtrymalloc (datalen + 1);
+      if (!buf)
+        return gpg_error (gpg_err_code_from_errno (errno));
+
+      *buf = padind; /* Padding indicator. */
+      memcpy (buf+1, data, datalen);
+      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
+                      result, resultlen);
+      xfree (buf);
+    }
+  else
+    {
+      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen, data,
+                      result, resultlen);
+    }
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
@@ -381,3 +479,139 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
 
   return 0;
 }
+
+/* Perform a READ BINARY command requesting a maximum of NMAX bytes
+   from OFFSET.  With NMAX = 0 the entire file is read. The result is
+   stored in a newly allocated buffer at the address passed by RESULT.
+   Returns the length of this data at the address of RESULTLEN. */
+gpg_error_t
+iso7816_read_binary (int slot, size_t offset, size_t nmax,
+                     unsigned char **result, size_t *resultlen)
+{
+  int sw;
+  unsigned char *buffer;
+  size_t bufferlen;
+  int read_all = !nmax;
+  size_t n;
+
+  if (!result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+     we check for this limit. */
+  if (offset > 32767)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  do
+    {
+      buffer = NULL;
+      bufferlen = 0;
+      /* Fixme: Either the ccid driver or the TCOS cards have problems
+         with an Le of 0. */
+      if (read_all || nmax > 254)
+        n = 254;
+      else
+        n = nmax;
+      sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
+                         ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+                         n, &buffer, &bufferlen);
+
+      if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
+        {
+          /* Make sure that pending buffers are released. */
+          xfree (buffer);
+          xfree (*result);
+          *result = NULL;
+          *resultlen = 0;
+          return map_sw (sw);
+        }
+      if (*result) /* Need to extend the buffer. */
+        {
+          unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
+          if (!p)
+            {
+              gpg_error_t err = gpg_error_from_errno (errno);
+              xfree (buffer);
+              xfree (*result);
+              *result = NULL;
+              *resultlen = 0;
+              return err;
+            }
+          *result = p;
+          memcpy (*result + *resultlen, buffer, bufferlen);
+          *resultlen += bufferlen;
+          xfree (buffer);
+          buffer = NULL;
+        }
+      else /* Transfer the buffer into our result. */
+        {
+          *result = buffer;
+          *resultlen = bufferlen;
+        }
+      offset += bufferlen;
+      if (offset > 32767)
+        break; /* We simply truncate the result for too large
+                  files. */
+      if (nmax > bufferlen)
+        nmax -= bufferlen;
+      else
+        nmax = 0;
+    }
+  while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax));
+  
+  return 0;
+}
+
+/* Perform a READ RECORD command. RECNO gives the record number to
+   read with 0 indicating the current record.  RECCOUNT must be 1 (not
+   all cards support reading of more than one record).  SHORT_EF
+   should be 0 to read the current EF or contain a short EF. The
+   result is stored in a newly allocated buffer at the address passed
+   by RESULT.  Returns the length of this data at the address of
+   RESULTLEN. */
+gpg_error_t
+iso7816_read_record (int slot, int recno, int reccount, int short_ef,
+                     unsigned char **result, size_t *resultlen)
+{
+  int sw;
+  unsigned char *buffer;
+  size_t bufferlen;
+
+  if (!result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+     we check for this limit. */
+  if (recno < 0 || recno > 255 || reccount != 1
+      || short_ef < 0 || short_ef > 254 )
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  buffer = NULL;
+  bufferlen = 0;
+  /* Fixme: Either the ccid driver of the TCOS cards have problems
+     with an Le of 0. */
+  sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
+                     recno, 
+                     short_ef? short_ef : 0x04,
+                     -1, NULL,
+                     254, &buffer, &bufferlen);
+
+  if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (buffer);
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+      return map_sw (sw);
+    }
+  *result = buffer;
+  *resultlen = bufferlen;
+  
+  return 0;
+}
+
index 26b8d6a..8f2b150 100644 (file)
 
 gpg_error_t iso7816_select_application (int slot,
                                         const char *aid, size_t aidlen);
+gpg_error_t iso7816_select_file (int slot, int tag, int is_dir,
+                                 unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_list_directory (int slot, int list_dirs,
+                                    unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_verify (int slot,
                             int chvno, const char *chv, size_t chvlen);
 gpg_error_t iso7816_change_reference_data (int slot, int chvno,
@@ -38,11 +42,15 @@ gpg_error_t iso7816_get_data (int slot, int tag,
                               unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_put_data (int slot, int tag,
                               const unsigned char *data, size_t datalen);
+gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
+                                         const unsigned char *data,
+                                         size_t datalen);
 gpg_error_t iso7816_compute_ds (int slot,
                                 const unsigned char *data, size_t datalen,
                                 unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_decipher (int slot,
                               const unsigned char *data, size_t datalen,
+                              int padind,
                               unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_internal_authenticate (int slot,
                                    const unsigned char *data, size_t datalen,
@@ -56,5 +64,10 @@ gpg_error_t iso7816_read_public_key (int slot,
 gpg_error_t iso7816_get_challenge (int slot,
                                    int length, unsigned char *buffer);
 
+gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
+                                 unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
+                                 int short_ef,
+                                 unsigned char **result, size_t *resultlen);
 
 #endif /*ISO7816_H*/
index 8a93a80..5da785a 100644 (file)
@@ -195,6 +195,7 @@ struct
   const char *ctapi_driver; /* Library to access the ctAPI. */
   const char *pcsc_driver;  /* Library to access the PC/SC system. */
   int disable_ccid;    /* Disable the use of the internal CCID driver. */
+  int allow_admin;     /* Allow the use of Admin commands. */
 #endif /*ENABLE_CARD_SUPPORT*/
 
 } opt;
index d39bc68..5506e25 100644 (file)
@@ -83,6 +83,7 @@ get_status_string ( int no )
       case STATUS_ENTER  : s = "ENTER"; break;
       case STATUS_LEAVE  : s = "LEAVE"; break;
       case STATUS_ABORT  : s = "ABORT"; break;
+      case STATUS_NEWSIG : s = "NEWSIG"; break;
       case STATUS_GOODSIG: s = "GOODSIG"; break;
       case STATUS_KEYEXPIRED: s = "KEYEXPIRED"; break;
       case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
index 73cc3f1..63f01b9 100644 (file)
 #define STATUS_IMPORT_CHECK     69
 #define STATUS_REVKEYSIG        70
 #define STATUS_CARDCTRL         71
+#define STATUS_NEWSIG           72
 
 /*-- status.c --*/
 void set_status_fd ( int fd );
diff --git a/g10/tlv.c b/g10/tlv.c
new file mode 100644 (file)
index 0000000..b7c8819
--- /dev/null
+++ b/g10/tlv.c
@@ -0,0 +1,209 @@
+/* tlv.c - Tag-Length-Value Utilities
+ *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "errors.h"
+#include "util.h"
+#include "packet.h"
+#include "tlv.h"
+
+static const unsigned char *
+do_find_tlv (const unsigned char *buffer, size_t length,
+             int tag, size_t *nbytes, int nestlevel)
+{
+  const unsigned char *s = buffer;
+  size_t n = length;
+  size_t len;
+  int this_tag;
+  int composite;
+    
+  for (;;)
+    {
+      buffer = s;
+      if (n < 2)
+        return NULL; /* Buffer definitely too short for tag and length. */
+      if (!*s || *s == 0xff)
+        { /* Skip optional filler between TLV objects. */
+          s++;
+          n--;
+          continue;
+        }
+      composite = !!(*s & 0x20);
+      if ((*s & 0x1f) == 0x1f)
+        { /* more tag bytes to follow */
+          s++;
+          n--;
+          if (n < 2)
+            return NULL; /* buffer definitely too short for tag and length. */
+          if ((*s & 0x1f) == 0x1f)
+            return NULL; /* We support only up to 2 bytes. */
+          this_tag = (s[-1] << 8) | (s[0] & 0x7f);
+        }
+      else
+        this_tag = s[0];
+      len = s[1];
+      s += 2; n -= 2;
+      if (len < 0x80)
+        ;
+      else if (len == 0x81)
+        { /* One byte length follows. */
+          if (!n)
+            return NULL; /* we expected 1 more bytes with the length. */
+          len = s[0];
+          s++; n--;
+        }
+      else if (len == 0x82)
+        { /* Two byte length follows. */
+          if (n < 2)
+            return NULL; /* We expected 2 more bytes with the length. */
+          len = (s[0] << 8) | s[1];
+          s += 2; n -= 2;
+        }
+      else
+        return NULL; /* APDU limit is 65535, thus it does not make
+                        sense to assume longer length fields. */
+
+      if (composite && nestlevel < 100)
+        { /* Dive into this composite DO after checking for a too deep
+             nesting. */
+          const unsigned char *tmp_s;
+          size_t tmp_len;
+          
+          tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
+          if (tmp_s)
+            {
+              *nbytes = tmp_len;
+              return tmp_s;
+            }
+        }
+
+      if (this_tag == tag)
+        {
+          *nbytes = len;
+          return s;
+        }
+      if (len > n)
+        return NULL; /* Buffer too short to skip to the next tag. */
+      s += len; n -= len;
+    }
+}
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and
+   return a pointer to value as well as its length in NBYTES.  Return
+   NULL if it was not found.  Note, that the function does not check
+   whether the value fits into the provided buffer. */
+const unsigned char *
+find_tlv (const unsigned char *buffer, size_t length,
+          int tag, size_t *nbytes)
+{
+  return do_find_tlv (buffer, length, tag, nbytes, 0);
+}
+
+
+
+
+/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
+   and the length part from the TLV triplet.  Update BUFFER and SIZE
+   on success. */
+gpg_error_t
+parse_ber_header (unsigned char const **buffer, size_t *size,
+                  int *r_class, int *r_tag, 
+                  int *r_constructed, int *r_ndef,
+                  size_t *r_length, size_t *r_nhdr)
+{
+  int c;
+  unsigned long tag;
+  const unsigned char *buf = *buffer;
+  size_t length = *size;
+
+  *r_ndef = 0;
+  *r_length = 0;
+  *r_nhdr = 0;
+
+  /* Get the tag. */
+  if (!length)
+    return gpg_error (GPG_ERR_EOF);
+  c = *buf++; length--; ++*r_nhdr;
+
+  *r_class = (c & 0xc0) >> 6;
+  *r_constructed = !!(c & 0x20);
+  tag = c & 0x1f;
+
+  if (tag == 0x1f)
+    {
+      tag = 0;
+      do
+        {
+          tag <<= 7;
+          if (!length)
+            return gpg_error (GPG_ERR_EOF);
+          c = *buf++; length--; ++*r_nhdr;
+          tag |= c & 0x7f;
+
+        }
+      while (c & 0x80);
+    }
+  *r_tag = tag;
+
+  /* Get the length. */
+  if (!length)
+    return gpg_error (GPG_ERR_EOF);
+  c = *buf++; length--; ++*r_nhdr;
+
+  if ( !(c & 0x80) )
+    *r_length = c;
+  else if (c == 0x80)
+    *r_ndef = 1;
+  else if (c == 0xff)
+    return gpg_error (GPG_ERR_BAD_BER);
+  else
+    {
+      unsigned long len = 0;
+      int count = c & 0x7f;
+
+      if (count > sizeof (len) || count > sizeof (size_t))
+        return gpg_error (GPG_ERR_BAD_BER);
+
+      for (; count; count--)
+        {
+          len <<= 8;
+          if (!length)
+            return gpg_error (GPG_ERR_EOF);
+          c = *buf++; length--; ++*r_nhdr;
+          len |= c & 0xff;
+        }
+      *r_length = len;
+    }
+  
+  /* Without this kludge some example certs can't be parsed. */
+  if (*r_class == CLASS_UNIVERSAL && !*r_tag)
+    *r_length = 0;
+  
+  *buffer = buf;
+  *size = length;
+  return 0;
+}
diff --git a/g10/tlv.h b/g10/tlv.h
new file mode 100644 (file)
index 0000000..f1ef134
--- /dev/null
+++ b/g10/tlv.h
@@ -0,0 +1,86 @@
+/* tlv.h - Tag-Length-Value Utilities
+ *     Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef SCD_TLV_H
+#define SCD_TLV_H 1
+
+#include "types.h"
+#include "cardglue.h"
+
+enum tlv_tag_class {
+  CLASS_UNIVERSAL = 0,
+  CLASS_APPLICATION = 1,
+  CLASS_CONTEXT = 2,
+  CLASS_PRIVATE =3
+};
+
+enum tlv_tag_type {
+  TAG_NONE = 0,
+  TAG_BOOLEAN = 1,
+  TAG_INTEGER = 2,
+  TAG_BIT_STRING = 3,
+  TAG_OCTET_STRING = 4,
+  TAG_NULL = 5,
+  TAG_OBJECT_ID = 6,
+  TAG_OBJECT_DESCRIPTOR = 7,
+  TAG_EXTERNAL = 8,
+  TAG_REAL = 9,
+  TAG_ENUMERATED = 10,
+  TAG_EMBEDDED_PDV = 11,
+  TAG_UTF8_STRING = 12,
+  TAG_REALTIVE_OID = 13,
+  TAG_SEQUENCE = 16,
+  TAG_SET = 17,
+  TAG_NUMERIC_STRING = 18,
+  TAG_PRINTABLE_STRING = 19,
+  TAG_TELETEX_STRING = 20,
+  TAG_VIDEOTEX_STRING = 21,
+  TAG_IA5_STRING = 22,
+  TAG_UTC_TIME = 23,
+  TAG_GENERALIZED_TIME = 24,
+  TAG_GRAPHIC_STRING = 25,
+  TAG_VISIBLE_STRING = 26,
+  TAG_GENERAL_STRING = 27,
+  TAG_UNIVERSAL_STRING = 28,
+  TAG_CHARACTER_STRING = 29,
+  TAG_BMP_STRING = 30
+};
+
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and return a
+   pointer to value as well as its length in NBYTES.  Return NULL if
+   it was not found.  Note, that the function does not check whether
+   the value fits into the provided buffer.*/
+const unsigned char *find_tlv (const unsigned char *buffer, size_t length,
+                               int tag, size_t *nbytes);
+
+
+/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
+   and the length part from the TLV triplet.  Update BUFFER and SIZE
+   on success. */
+gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
+                              int *r_class, int *r_tag, 
+                              int *r_constructed,
+                              int *r_ndef, size_t *r_length, size_t *r_nhdr);
+
+
+
+#endif /* SCD_TLV_H */