Basically made Belgian EID cards work.
authorWerner Koch <wk@gnupg.org>
Mon, 5 Sep 2005 14:36:36 +0000 (14:36 +0000)
committerWerner Koch <wk@gnupg.org>
Mon, 5 Sep 2005 14:36:36 +0000 (14:36 +0000)
Signature creation has not yet been tested.
Also other changes to better cope with T=0 cards.

scd/ChangeLog
scd/apdu.c
scd/apdu.h
scd/app-p15.c
scd/iso7816.c
scd/iso7816.h
scd/pcsc-wrapper.c

index 970335a..3e8292d 100644 (file)
@@ -1,3 +1,30 @@
+2005-09-05  Werner Koch  <wk@g10code.com>
+
+       * iso7816.c (iso7816_select_path): New.
+       * app-p15.c (select_ef_by_path): Allow for direct path selection.
+       (app_select_p15): Try using the beigian variant of pkcs#15.
+       (read_home_df): New.
+       (read_ef_odf): Generalized.
+       (read_ef_tokeninfo): New.
+       (read_p15_info): Set serialnumber from TokenInfo.
+       (app_select_p15): Don't munge serialNumber - that must be done
+       only once.
+
+       * iso7816.c (iso7816_read_binary): Use Le=0 when reading all
+       data.  Handle 6C00 error and take 6B00 as indication for EOF.
+       * apdu.h (SW_EXACT_LENGTH_P): New.
+       * apdu.c (new_reader_slot, reset_pcsc_reader, pcsc_get_status) 
+       (open_pcsc_reader): Set new reader state IS_T0.
+       (apdu_send_le): When doing T=0 make sure not to send Lc and Le.
+       Problem reported by Carl Meijer.
+       (apdu_send_direct): Initialize RESULTLEN.
+       * pcsc-wrapper.c (handle_status): Return the current protocol as
+       a new third word.
+
+2005-08-05  Werner Koch  <wk@g10code.com>
+
+       * apdu.c (open_rapdu_reader): Set the reader number.
+
 2005-07-05  Werner Koch  <wk@g10code.com>
 
        * app-openpgp.c (do_readkey): Return a mallcoed copy of the key as
index 79022f2..678ea12 100644 (file)
@@ -15,7 +15,8 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  *
  * $Id$
  */
@@ -126,6 +127,7 @@ struct reader_table_s {
   char *rdrname;     /* Name of the connected reader or NULL if unknown. */
   int last_status;
   int status;
+  int is_t0;         /* True if we know that we are running T=0. */
   unsigned char atr[33];
   size_t atrlen;           /* A zero length indicates that the ATR has
                               not yet been read; i.e. the card is not
@@ -275,6 +277,9 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
                                      unsigned long timeout);
 
 
+/*  Prototypes.  */
+static int pcsc_get_status (int slot, unsigned int *status);
+
 
 \f
 /*
@@ -319,6 +324,7 @@ new_reader_slot (void)
 
   reader_table[reader].used = 1;
   reader_table[reader].last_status = 0;
+  reader_table[reader].is_t0 = 1;
 #ifdef NEED_PCSC_WRAPPER
   reader_table[reader].pcsc.req_fd = -1;
   reader_table[reader].pcsc.rsp_fd = -1;
@@ -768,6 +774,7 @@ reset_pcsc_reader (int slot)
   size_t len;
   int i, n;
   unsigned char msgbuf[9];
+  unsigned int dummy_status;
   int sw = SW_HOST_CARD_IO_ERROR;
 
   slotp = reader_table + slot;
@@ -841,6 +848,9 @@ reset_pcsc_reader (int slot)
     }
   slotp->atrlen = len;
 
+  /* Read the status so that IS_T0 will be set. */
+  pcsc_get_status (slot, &dummy_status);
+
   return 0;
 
  command_failed:
@@ -902,6 +912,7 @@ reset_pcsc_reader (int slot)
   if (atrlen >= DIM (reader_table[0].atr))
     log_bug ("ATR returned by pcsc_status is too large\n");
   reader_table[slot].atrlen = atrlen;
+  reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
 
   return 0;
 #endif /* !NEED_PCSC_WRAPPER */
@@ -917,7 +928,7 @@ pcsc_get_status (int slot, unsigned int *status)
   size_t len, full_len;
   int i, n;
   unsigned char msgbuf[9];
-  unsigned char buffer[12];
+  unsigned char buffer[16];
   int sw = SW_HOST_CARD_IO_ERROR;
 
   slotp = reader_table + slot;
@@ -968,14 +979,20 @@ pcsc_get_status (int slot, unsigned int *status)
 
   full_len = len;
 
-  n = 8 < len ? 8 : len;
-  if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != 8)
+  /* The current version returns 3 words but we allow also for old
+     versions returning only 2 words. */
+  n = 12 < len ? 12 : len;
+  if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len))
+      || (len != 8 && len != 12))
     {
       log_error ("error receiving PC/SC STATUS response: %s\n",
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
 
+  slotp->is_t0 = (len == 12 && !!(buffer[11] & PCSC_PROTOCOL_T0));
+
+
   full_len -= len;
   /* Newer versions of the wrapper might send more status bytes.
      Read them. */
@@ -1296,6 +1313,7 @@ open_pcsc_reader (const char *portstr)
   size_t len;
   unsigned char msgbuf[9];
   int err;
+  unsigned int dummy_status;
   int sw = SW_HOST_CARD_IO_ERROR;
 
   slot = new_reader_slot ();
@@ -1440,7 +1458,7 @@ open_pcsc_reader (const char *portstr)
 
   slotp->last_status = 0;
 
-  /* The open fucntion may return a zero for the ATR length to
+  /* The open request may return a zero for the ATR length to
      indicate that no card is present.  */
   n = len;
   if (n)
@@ -1463,6 +1481,9 @@ open_pcsc_reader (const char *portstr)
   reader_table[slot].send_apdu_reader = pcsc_send_apdu;
   reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
 
+  /* Read the status so that IS_T0 will be set. */
+  pcsc_get_status (slot, &dummy_status);
+
   dump_reader_status (slot);
   return slot;
 
@@ -1596,6 +1617,7 @@ open_pcsc_reader (const char *portstr)
           /* If we got to here we know that a card is present
              and usable.  Thus remember this.  */
           reader_table[slot].last_status = (1|2|4| 0x8000);
+          reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
         }
     }
 
@@ -1997,6 +2019,7 @@ open_rapdu_reader (int portno,
       return -1;
     }
 
+  rapdu_set_reader (slotp->rapdu.handle, portno);
 
   rapdu_set_iofunc (slotp->rapdu.handle,
                     readfnc, readfnc_value,
@@ -2518,7 +2541,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
 
   if (lc != -1 && (lc > 255 || lc < 0))
     return SW_WRONG_LENGTH;
-  if (le != -1 && (le > 256 || le < 1))
+  if (le != -1 && (le > 256 || le < 0))
     return SW_WRONG_LENGTH;
   if ((!data && lc != -1) || (data && lc == -1))
     return SW_HOST_INV_VALUE;
@@ -2536,9 +2559,13 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
       apdu[apdulen++] = lc;
       memcpy (apdu+apdulen, data, lc);
       apdulen += lc;
+      /* T=0 does not allow the use of Lc together with Le; thus
+         disable Le in this case. */
+      if (reader_table[slot].is_t0)
+        le = -1;
     }
   if (le != -1)
-    apdu[apdulen++] = le; /* Truncation is okay becuase 0 means 256. */
+    apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
   assert (sizeof (apdu) >= apdulen);
   /* As safeguard don't pass any garbage from the stack to the driver. */
   memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
@@ -2736,14 +2763,14 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
   if ((sw = trylock_slot (slot)))
     return sw;
 
-  /* We simply trucntate a too long APDU.  */
+  /* We simply trunctate a too long APDU.  */
   if (apdudatalen > sizeof apdu)
     apdudatalen = sizeof apdu;
   apdulen = apdudatalen;
   memcpy (apdu, apdudata, apdudatalen);
   class = apdulen? *apdu : 0;
 
-
+  resultlen = RESULTLEN;
   rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
   if (rc || resultlen < 2)
     {
index e0f50b7..45388fd 100644 (file)
@@ -15,7 +15,8 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  *
  * $Id$
  */
@@ -41,6 +42,7 @@ enum {
   SW_RECORD_NOT_FOUND = 0x6a83,
   SW_REF_NOT_FOUND  = 0x6a88,
   SW_BAD_P0_P1      = 0x6b00,
+  SW_EXACT_LENGTH   = 0x6c00,
   SW_INS_NOT_SUP    = 0x6d00,
   SW_CLA_NOT_SUP    = 0x6e00,
   SW_SUCCESS        = 0x9000,
@@ -65,6 +67,8 @@ enum {
 };
 
 
+#define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH)
+
 
 /* Note , that apdu_open_reader returns no status word but -1 on error. */
 int apdu_open_reader (const char *portstr);
index f03e5d5..c8d3885 100644 (file)
@@ -74,7 +74,17 @@ static struct
 #undef X
 
 
-/* The Pin Types as defined in pkcs#15 v1.1 */
+/* The AID of PKCS15. */
+static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63,
+                                   0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
+
+/* The Belgian eID variant - they didn't understood why a shared AID
+   is useful for a standard.  Oh well. */
+static char const pkcs15be_aid[] = { 0xA0, 0, 0, 0x01, 0x77,
+                                   0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
+
+
+/* The PIN types as defined in pkcs#15 v1.1 */
 typedef enum 
   {
     PIN_TYPE_BCD = 0,
@@ -261,6 +271,9 @@ struct app_local_s
   /* The type of the card. */
   card_type_t card_type;
 
+  /* Flag indicating whether we may use direct path selection. */
+  int direct_path_selection;
+
   /* Structure with the EFIDs of the objects described in the ODF
      file. */
   struct
@@ -276,6 +289,10 @@ struct app_local_s
     unsigned short auth_objects;
   } odf;  
 
+  /* The PKCS#15 serialnumber from EF(TokeiNFo) or NULL.  Malloced. */
+  unsigned char *serialno;
+  size_t serialnolen;
+
   /* Information on all certificates. */
   cdf_object_t certificate_info;
   /* Information on all trusted certificates. */
@@ -363,6 +380,7 @@ do_deinit (app_t app)
       release_cdflist (app->app_local->useful_certificate_info);
       release_prkdflist (app->app_local->private_key_info);
       release_aodflist (app->app_local->auth_object_info);
+      xfree (app->app_local->serialno);
       xfree (app->app_local);
       app->app_local = NULL;
     }
@@ -406,26 +424,44 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
   gpg_error_t err;
   int i, j;
 
-  /* FIXME: Need code to remember the last PATH so that we can decide
-     what select commands to send in case the path does not start off
-     with 3F00.  We might also want to use direct path selection if
-     supported by the card. */
+  if (!pathlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
   if (pathlen && *path != 0x3f00 )
     log_debug ("WARNING: relative path selection not yet implemented\n");
-
-  for (i=0; i < pathlen; i++)
+      
+  if (app->app_local->direct_path_selection)
     {
-      err = iso7816_select_file (app->slot, path[i],
-                                 !(i+1 == pathlen), NULL, NULL);
+      err = iso7816_select_path (app->slot, path+1, pathlen-1, NULL, NULL);
       if (err)
         {
-          log_error ("error selecting part %d from path ", i);
+          log_error ("error selecting path ");
           for (j=0; j < pathlen; j++)
             log_printf ("%04hX", path[j]);
           log_printf (": %s\n", gpg_strerror (err));
           return err;
         }
     }
+  else
+    {
+      /* FIXME: Need code to remember the last PATH so that we can decide
+         what select commands to send in case the path does not start off
+         with 3F00.  We might also want to use direct path selection if
+         supported by the card. */
+      for (i=0; i < pathlen; i++)
+        {
+          err = iso7816_select_file (app->slot, path[i],
+                                     !(i+1 == pathlen), NULL, NULL);
+          if (err)
+            {
+              log_error ("error selecting part %d from path ", i);
+              for (j=0; j < pathlen; j++)
+                log_printf ("%04hX", path[j]);
+              log_printf (": %s\n", gpg_strerror (err));
+              return err;
+            }
+        }
+    }
   return 0;
 }
 
@@ -586,12 +622,13 @@ read_ef_odf (app_t app, unsigned short odf_fid)
         }
       else if ( buflen >= 12 
                 && (p[0] & 0xf0) == 0xA0
-                && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00\x50\x15", 9)
-                && app->app_local->home_df == 0x5015 )
+                && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7)
+                && app->app_local->home_df == ((p[8]<<8)|p[9]) )
         {
-          /* This format using a full path is used by a self-created
-             test card of mine.  I have not checked whether this is
-             legal.  We assume a home DF of 0x5015 here. */
+          /* We only allow a full path if all files are at the same
+             level and below the home directory.  The extend this we
+             would need to make use of new data type capable of
+             keeping a full path. */
           offset = 10;
         }
       else
@@ -2158,12 +2195,81 @@ TokenFlags ::= BIT STRING {
 
 
  */
-/* static gpg_error_t */
-/* read_ef_tokeninfo (app_t app) */
-/* { */
-/*   unsigned short efid = 0x5032; */
-/*   return 0; */
-/* } */
+static gpg_error_t
+read_ef_tokeninfo (app_t app)
+{
+  gpg_error_t err;
+  unsigned char *buffer = NULL;
+  size_t buflen;
+  const unsigned char *p;
+  size_t n, objlen, hdrlen;
+  int class, tag, constructed, ndef;
+  unsigned long ul;
+  
+  err = select_and_read_binary (app->slot, 0x5032, "TokenInfo",
+                                &buffer, &buflen);
+  if (err)
+    return err;
+  
+  p = buffer;
+  n = buflen;
+
+  err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > n || tag != TAG_SEQUENCE))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    {
+      log_error ("error parsing TokenInfo: %s\n", gpg_strerror (err));
+      goto leave;
+    }
+
+  n = objlen;
+
+  /* Version.  */
+  err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > n || tag != TAG_INTEGER))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    goto leave;
+
+  for (ul=0; objlen; objlen--)
+    {
+      ul <<= 8;
+      ul |= (*p++) & 0xff; 
+      n--;
+    }
+  if (ul)
+    {
+      log_error ("invalid version %lu in TokenInfo\n", ul);
+      err = gpg_error (GPG_ERR_INV_OBJ);
+      goto leave;
+    }
+
+  /* serialNumber.  */
+  err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > n || tag != TAG_OCTET_STRING || !objlen))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    goto leave;
+  
+  xfree (app->app_local->serialno);
+  app->app_local->serialno = xtrymalloc (objlen);
+  if (!app->app_local->serialno)
+    {
+      err = gpg_error_from_errno (errno);
+      goto leave;
+    }
+  memcpy (app->app_local->serialno, p, objlen);
+  app->app_local->serialnolen = objlen;
+  log_printhex ("Serialnumber from EF(TokenInfo) is:", p, objlen);
+
+ leave:
+  xfree (buffer);
+  return err;
+}
 
 
 /* Get all the basic information from the pkcs#15 card, check the
@@ -2174,11 +2280,25 @@ read_p15_info (app_t app)
 {
   gpg_error_t err;
 
-  /* Fixme: We might need to read the tokeninfo to get a non-standard
-     ODF FID.  */
-
+  if (!read_ef_tokeninfo (app))
+    {
+      /* If we don't have a serial number yet but the TokenInfo provides
+         one, use that. */
+      if (!app->serialno && app->app_local->serialno)
+        {
+          app->serialno = app->app_local->serialno;
+          app->serialnolen = app->app_local->serialnolen;
+          app->app_local->serialno = NULL;
+          app->app_local->serialnolen = 0;
+          err = app_munge_serialno (app);
+          if (err)
+            return err;
+        }
+    }
+  
   /* Read the ODF so that we know the location of all directory
      files. */
+  /* Fixme: We might need to get a non-standard ODF FID from TokenInfo. */
   err = read_ef_odf (app, 0x5031);
   if (err)
     return err;
@@ -2895,22 +3015,88 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
 }
 
 
+\f
+/* Assume that EF(DIR) has been selected.  Read its content and figure
+   out the home EF of pkcs#15.  Return that home DF or 0 if not
+   found. */
+static unsigned short
+read_home_df (int slot)
+{
+  gpg_error_t err;
+  unsigned char *buffer;
+  const unsigned char *p, *pp;
+  size_t buflen, n, nn;
+  unsigned short result = 0;
+
+  err = iso7816_read_binary (slot, 0, 0, &buffer, &buflen);
+  if (err)
+    {
+      log_error ("error reading EF{DIR}: %s\n", gpg_strerror (err));
+      return 0;
+    }
+
+  /* FIXME: We need to scan all records. */
+  p = find_tlv (buffer, buflen, 0x61, &n);
+  if (p && n)
+    {
+      pp = find_tlv (p, n, 0x4f, &nn);
+      if (pp 
+          && ((nn == sizeof pkcs15_aid && !memcmp (pp, pkcs15_aid, nn))
+              ||(nn == sizeof pkcs15be_aid && !memcmp (pp, pkcs15be_aid, nn))))
+        {
+          pp = find_tlv (p, n, 0x50, &nn);
+          if (pp) /* fixme: Filter log value? */
+            log_info ("pkcs#15 application label from EF(DIR) is `%.*s'\n",
+                      (int)nn, pp);
+          pp = find_tlv (p, n, 0x51, &nn);
+          if (pp && nn == 4 && *pp == 0x3f && !pp[1])
+            {
+              result = ((pp[2] << 8) | pp[3]);
+              log_info ("pkcs#15 application directory is 0x%04hX\n", result);
+            }
+        }
+    }
+  xfree (buffer);
+  return result;
+}
 
 
-/* Select the PKCS#15 application on the card in SLOT.  */
+/* 
+   Select the PKCS#15 application on the card in SLOT. 
+ */
 gpg_error_t
 app_select_p15 (app_t app)
 {
-  static char const aid[] = { 0xA0, 0, 0, 0, 0x63,
-                              0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
   int slot = app->slot;
   int rc;
   unsigned short def_home_df = 0;
   card_type_t card_type = CARD_TYPE_UNKNOWN;
+  int direct = 0;
   
-  rc = iso7816_select_application (slot, aid, sizeof aid);
+  rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid);
   if (rc)
-    {
+    rc = iso7816_select_application (slot, pkcs15be_aid, sizeof pkcs15be_aid);
+  if (rc)
+    { /* Not found: Try to locate it from 2F00.  We use direct path
+         selection here because it seems that the Belgian eID card
+         does only allow for that.  Many other cards supports this
+         selection method too. */
+      unsigned short path[1] = { 0x2f00 };
+
+      rc = iso7816_select_path (app->slot, path, 1, NULL, NULL);
+      if (!rc)
+        {
+          direct = 1;
+          def_home_df = read_home_df (slot);
+          if (def_home_df)
+            {
+              path[0] = def_home_df;
+              rc = iso7816_select_path (app->slot, path, 1, NULL, NULL);
+            }
+        }
+    }
+  if (rc)
+    { /* Still not found:  Try the default DF. */
       def_home_df = 0x5015;
       rc = iso7816_select_file (slot, def_home_df, 1, NULL, NULL);
     }
@@ -2958,6 +3144,9 @@ app_select_p15 (app_t app)
          the common APP structure. */
       app->app_local->card_type = card_type;
 
+      /* Store whether we may and should use direct path selection. */
+      app->app_local->direct_path_selection = direct;
+
       /* Read basic information and thus check whether this is a real
          card.  */
       rc = read_p15_info (app);
@@ -2989,8 +3178,6 @@ app_select_p15 (app_t app)
               app->serialno = p;
             }
         }
-      else /* Use standard munging code. */
-        rc = app_munge_serialno (app);
 
       app->fnc.deinit = do_deinit;
       app->fnc.learn_status = do_learn_status;
index 742ed94..4849062 100644 (file)
@@ -77,6 +77,7 @@ map_sw (int sw)
     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_EXACT_LENGTH:   ec = GPG_ERR_INV_VALUE; break;
     case SW_INS_NOT_SUP:    ec = GPG_ERR_CARD; break;
     case SW_CLA_NOT_SUP:    ec = GPG_ERR_CARD; break;
     case SW_SUCCESS:        ec = 0; break;
@@ -161,6 +162,39 @@ iso7816_select_file (int slot, int tag, int is_dir,
 }
 
 
+/* Do a select file command with a direct path. */
+gpg_error_t
+iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
+                     unsigned char **result, size_t *resultlen)
+{
+  int sw, p0, p1;
+  unsigned char buffer[100];
+  int buflen;
+  
+  if (result || resultlen)
+    {
+      *result = NULL;
+      *resultlen = 0;
+      return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
+  
+  if (pathlen/2 >= sizeof buffer)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+  
+  for (buflen = 0; pathlen; pathlen--, path++)
+    {
+      buffer[buflen++] = (*path >> 8);
+      buffer[buflen++] = *path;
+    }
+
+  p0 = 0x08;
+  p1 = 0x0c; /* No FC return. */
+  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+                         p0, p1, buflen, (char*)buffer );
+  return map_sw (sw);
+}
+
+
 /* This is a private command currently only working for TCOS cards. */
 gpg_error_t
 iso7816_list_directory (int slot, int list_dirs,
@@ -524,8 +558,10 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
     {
       buffer = NULL;
       bufferlen = 0;
-      /* Fixme: Either the ccid driver or the TCOS cards have problems
-         with an Le of 0. */
+      /* Note, that we to set N to 254 due to problems either with the
+         ccid driver or some TCOS cards.  It actually should be 0
+         which is the official ISO value to read a variable length
+         object. */
       if (read_all || nmax > 254)
         n = 254;
       else
@@ -533,6 +569,21 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
       sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
                          ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
                          n, &buffer, &bufferlen);
+      if ( SW_EXACT_LENGTH_P(sw) )
+        {
+          n = (sw & 0x00ff);
+          sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
+                             ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+                             n, &buffer, &bufferlen);
+        }
+
+      if (*result && sw == SW_BAD_P0_P1)
+        {
+          /* Bad Parameter means that the offset is outside of the
+             EF. When reading all data we take this as an indication
+             for EOF.  */
+          break;
+        }
 
       if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
         {
index b9ba180..828fabb 100644 (file)
@@ -33,6 +33,9 @@ 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_select_path (int slot,
+                                 const unsigned short *path, size_t pathlen,
+                                 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,
index 21af16f..f149e78 100644 (file)
@@ -15,7 +15,8 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 /*
@@ -587,6 +588,11 @@ handle_status (unsigned char *argbuf, size_t arglen)
   buf[5] = (rdrstates[0].event_state >> 16);
   buf[6] = (rdrstates[0].event_state >>  8);
   buf[7] = (rdrstates[0].event_state >>  0);
+  /* The third word is the protocol. */
+  buf[8]  = (pcsc_protocol >> 24);
+  buf[9]  = (pcsc_protocol >> 16);
+  buf[10] = (pcsc_protocol >> 8);
+  buf[11] = (pcsc_protocol);
 
   request_succeeded (buf, 8);
 }