* configure.ac (HAVE_LIBUSB): Added a simple test for libusb.
authorWerner Koch <wk@gnupg.org>
Tue, 2 Sep 2003 19:06:34 +0000 (19:06 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 2 Sep 2003 19:06:34 +0000 (19:06 +0000)
* scdaemon.c, scdaemon.h: New option --pcsc-ccid.
* ccid-driver.c, ccid-driver.h: New but far from being useful.
* Makefile.am: Add above.
* apdu.c: Add support for that ccid driver.

13 files changed:
ChangeLog
TODO
agent/pksign.c
configure.ac
g10/ChangeLog
g10/keygen.c
scd/ChangeLog
scd/Makefile.am
scd/apdu.c
scd/ccid-driver.c [new file with mode: 0644]
scd/ccid-driver.h [new file with mode: 0644]
scd/scdaemon.c
scd/scdaemon.h

index 45be0a5..6ac146e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2003-09-02  Werner Koch  <wk@gnupg.org>
+
+       * configure.ac (HAVE_LIBUSB): Added a simple test for libusb.
+
 2003-08-19  Marcus Brinkmann  <marcus@g10code.de>
 
        * configure.ac (AM_PATH_GPG_ERROR): Add missing comma in
diff --git a/TODO b/TODO
index c9b2d18..fe81d02 100644 (file)
--- a/TODO
+++ b/TODO
@@ -20,6 +20,9 @@ might want to have an agent context for each service request
 ** When a certificate chain was sucessfully verified, make ephemeral certs used  in this chain permanent.
 ** figure out how to auto retrieve a key by serialno+issuer.
    Dirmngr is currently not able to parse more than the CN.
+** Try all available root certs in case we have several of them in our keybox.
+ For example TC TrustCenter Class 1 CA certs are ambiguous becuase
+ user certs don't come with a authorityKeyIdentifier.
 
 * sm/decrypt.c
 ** replace leading zero in integer hack by a cleaner solution
index 200b6a2..3425821 100644 (file)
@@ -39,6 +39,10 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash)
   char * p, tmp[16];
   int i, rc;
 
+#warning I do do like that stuff - libgcrypt provides easier interfaces. -wk
+  /* FIXME: Either use the build function or create canonical encoded
+     S-expressions. */
+
   p = xmalloc (64 + 2 * mdlen);
   s = gcry_md_algo_name (algo);
   if (s && strlen (s) < 16)
index 7a7570e..591d5df 100644 (file)
@@ -376,6 +376,19 @@ AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_VERSION",
 #
 AM_PATH_KSBA("$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no)
 
+#
+# libusb allows us to use the integrated CCID smartcard reader driver.
+
+# Note, that we need the CVS version.  FIXME: libusb should have a
+# regular check as the other libraries do.
+
+AC_CHECK_LIB(usb, usb_find_device,
+              [ LIBUSB_LIBS="$LIBUSB_LIBS -lusb"
+                AC_DEFINE(HAVE_LIBUSB,1,
+                         [defined if libusb is available])
+             ])
+AC_SUBST(LIBUSB_LIBS)
+
 
 #
 # OpenSC is needed by the SCdaemon - if it is not availbale we can only
index 8eca012..2746372 100644 (file)
@@ -3,6 +3,10 @@
        * pkglue.c (mpi_from_sexp): New. Used to factor out
        some common code.
        
+2003-08-24  Werner Koch  <wk@gnupg.org>
+
+       * keygen.c (do_generate_keypair): Print a reminder to use --gen-revoke.
+
 2003-08-18  Timo Schulz  <twoaday@freakmail.de>
 
        * encode.c (encode_sesskey): Checked the code and removed
index 210bbc8..1840a58 100644 (file)
@@ -2655,6 +2655,15 @@ do_generate_keypair (struct para_data_s *para,
                            "the command \"--edit-key\" to generate a "
                            "secondary key for this purpose.\n"));
            }
+
+          if (!opt.batch && card)
+            {
+              tty_printf(_(
+"Please create a revocation certificate now, so that you are able\n"
+"to revoke the key if it ever happens that you lose your card or\n"
+"the card gets damaged.  Use the command \"--gen-revoke\".\n"
+                         ));
+            }
        }
     }
 
index ff407f8..28dc4a7 100644 (file)
@@ -1,3 +1,10 @@
+2003-09-02  Werner Koch  <wk@gnupg.org>
+
+       * scdaemon.c, scdaemon.h: New option --pcsc-ccid.
+       * ccid-driver.c, ccid-driver.h: New but far from being useful.
+       * Makefile.am: Add above.
+       * apdu.c: Add support for that ccid driver.
+       
 2003-08-26  Timo Schulz  <twoaday@freakmail.de>
 
        * apdu.c (new_reader_slot): Only set 'is_osc' when OpenSC
index 5ecadd2..a2ecd3a 100644 (file)
@@ -32,17 +32,19 @@ scdaemon_SOURCES = \
        card-common.h \
        card-p15.c card-dinsig.c \
        apdu.c apdu.h \
+       ccid-driver.c ccid-driver.h \
        iso7816.c iso7816.h \
        app.c app-common.h \
        app-openpgp.c
 
 scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
          $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
-        -lgpg-error @INTLLIBS@ -ldl
+         $(LIBUSB_LIBS) -lgpg-error @INTLLIBS@ -ldl
 
 sc_investigate_SOURCES = \
        sc-investigate.c scdaemon.h \
        apdu.c apdu.h \
+       ccid-driver.c ccid-driver.h \
        iso7816.c iso7816.h \
        app.c app-common.h \
        app-openpgp.c \
@@ -50,12 +52,14 @@ sc_investigate_SOURCES = \
 
 sc_investigate_LDADD = \
        ../jnlib/libjnlib.a ../common/libcommon.a \
-       $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) @INTLLIBS@ -lgpg-error -ldl
+       $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) @INTLLIBS@ \
+        -lgpg-error -ldl
 
 
 sc_copykeys_SOURCES = \
        sc-copykeys.c scdaemon.h \
        apdu.c apdu.h \
+       ccid-driver.c ccid-driver.h \
        iso7816.c iso7816.h \
        app.c app-common.h \
        app-openpgp.c \
@@ -64,7 +68,8 @@ sc_copykeys_SOURCES = \
 sc_copykeys_LDADD = \
        ../jnlib/libjnlib.a ../common/libcommon.a \
        ../common/libsimple-pwquery.a \
-       $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) -lgpg-error @INTLLIBS@ -ldl
+       $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) \
+        -lgpg-error @INTLLIBS@ -ldl
 
 
 
index 3809ef3..4867f10 100644 (file)
@@ -31,6 +31,7 @@
 #include "scdaemon.h"
 #include "apdu.h"
 #include "dynload.h"
+#include "ccid-driver.h"
 
 #define MAX_READER 4 /* Number of readers we support concurrently. */
 #define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
 struct reader_table_s {
   int used;            /* True if slot is used. */
   unsigned short port; /* Port number:  0 = unused, 1 - dev/tty */
+  int is_ccid;         /* Uses the internal CCID driver. */
+  struct {
+    ccid_driver_t handle;
+  } ccid;
   int is_ctapi;        /* This is a ctAPI driver. */
   struct {
     unsigned long context;
@@ -155,6 +160,7 @@ new_reader_slot (void)
       return -1;
     }
   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;
@@ -166,7 +172,9 @@ new_reader_slot (void)
 static void
 dump_reader_status (int reader)
 {
-  if (reader_table[reader].is_ctapi)
+  if (reader_table[reader].is_ccid)
+    log_info ("reader slot %d: using ccid driver\n", reader);
+  else if (reader_table[reader].is_ctapi)
     {
       log_info ("reader slot %d: %s\n", reader,
                 reader_table[reader].status == 1? "Processor ICC present" :
@@ -549,7 +557,74 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   return err? -1:0; /* FIXME: Return appropriate error code. */
 }
 
+\f
+#ifdef HAVE_LIBUSB
+/* 
+     Internal CCID driver interface.
+ */
+
+static int
+open_ccid_reader (void)
+{
+  int err;
+  int slot;
+  reader_table_t slotp;
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
+
+  err = ccid_open_reader (&slotp->ccid.handle, 0);
+  if (err)
+    {
+      slotp->used = 0;
+      return -1;
+    }
+
+  err = ccid_get_atr (slotp->ccid.handle,
+                      slotp->atr, sizeof slotp->atr, &slotp->atrlen);
+  if (err)
+    {
+      slotp->used = 0;
+      return -1;
+    }
+
+  slotp->is_ccid = 1;
+
+  dump_reader_status (slot); 
+  return slot;
+}
+
+
+/* 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: Internal CCID driver error code. */
+static int
+send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
+                unsigned char *buffer, size_t *buflen)
+{
+  long err;
+  size_t maxbuflen;
+
+  if (DBG_CARD_IO)
+    log_printhex ("  APDU_data:", apdu, apdulen);
+
+  maxbuflen = *buflen;
+  err = ccid_transceive (reader_table[slot].ccid.handle,
+                         apdu, apdulen,
+                         buffer, maxbuflen, buflen);
+  if (err)
+    log_error ("ccid_transceive failed: (0x%lx)\n",
+               err);
+  
+  return err? -1:0; /* FIXME: Return appropriate error code. */
+}
+
+#endif /* HAVE_LIBUSB */
+
 
+\f
 #ifdef HAVE_OPENSC
 /* 
      OpenSC Interface.
@@ -755,6 +830,17 @@ apdu_open_reader (const char *portstr)
 {
   static int pcsc_api_loaded, ct_api_loaded;
 
+#ifdef HAVE_LIBUSB
+  if (!opt.disable_ccid)
+    {
+      int slot;
+
+      slot = open_ccid_reader ();
+      if (slot != -1)
+        return slot; /* got one */
+    }
+#endif
+
 #ifdef HAVE_OPENSC
   if (!opt.disable_opensc)
     {
@@ -871,6 +957,10 @@ error_string (int slot, long rc)
     return "[invalid slot]";
   if (reader_table[slot].is_ctapi)
     return ct_error_string (rc);
+#ifdef HAVE_LIBUSB
+  else if (reader_table[slot].is_ccid)
+    return "no CCID driver error strings yet";
+#endif
 #ifdef HAVE_OPENSC
   else if (reader_table[slot].is_osc)
     return sc_strerror (rc);
@@ -889,6 +979,10 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
     return SW_HOST_NO_DRIVER;
   if (reader_table[slot].is_ctapi)
     return ct_send_apdu (slot, apdu, apdulen, buffer, buflen);
+#ifdef HAVE_LIBUSB
+  else if (reader_table[slot].is_ccid)
+    return send_apdu_ccid (slot, apdu, apdulen, buffer, buflen);
+#endif
 #ifdef HAVE_OPENSC
   else if (reader_table[slot].is_osc)
     return osc_send_apdu (slot, apdu, apdulen, buffer, buflen);
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
new file mode 100644 (file)
index 0000000..03c9bcb
--- /dev/null
@@ -0,0 +1,627 @@
+/* ccid-driver.c - USB ChipCardInterfaceDevices driver
+ *     Copyright (C) 2003 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
+ */
+
+
+/* CCID (ChipCardInterfaceDevices) is a specification for accessing
+   smartcard via a reader connected to the USB.  
+
+   This is a limited driver allowing to use some CCID drivers directly
+   without any other specila drivers. This is a fallback driver to be
+   used when nothing else works or the system should be kept minimal
+   for security reasons.  It makes use of the libusb library to gain
+   portable access to USB.
+
+   This driver has been tested with the SCM SCR335 smartcard reader
+   and requires that reader implements the TPDU level exchange and
+   does fully automatic initialization.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(HAVE_LIBUSB) || defined(TEST)
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <usb.h>
+
+
+#include "ccid-driver.h"
+
+enum {
+  RDR_to_PC_NotifySlotChange= 0x50,
+  RDR_to_PC_HardwareError   = 0x51,
+
+  PC_to_RDR_SetParameters   = 0x61,
+  PC_to_RDR_IccPowerOn      = 0x62,
+  PC_to_RDR_IccPowerOff     = 0x63,
+  PC_to_RDR_GetSlotStatus   = 0x65,
+  PC_to_RDR_Secure          = 0x69,
+  PC_to_RDR_T0APDU          = 0x6a,
+  PC_to_RDR_Escape          = 0x6b,
+  PC_to_RDR_GetParameters   = 0x6c,
+  PC_to_RDR_ResetParameters = 0x6d,
+  PC_to_RDR_IccClock        = 0x6e,
+  PC_to_RDR_XfrBlock        = 0x6f,
+  PC_to_RDR_Mechanical      = 0x71,
+  PC_to_RDR_Abort           = 0x72,
+  PC_to_RDR_SetDataRate     = 0x73,
+
+  RDR_to_PC_DataBlock       = 0x80,
+  RDR_to_PC_SlotStatus      = 0x81,
+  RDR_to_PC_Parameters      = 0x82,
+  RDR_to_PC_Escape          = 0x83,
+  RDR_to_PC_DataRate        = 0x84
+};
+
+
+/* Store information on the driver's state.  A pointer to such a
+   structure is used as handle for most functions. */
+struct ccid_driver_s {
+  usb_dev_handle *idev;
+  int seqno;
+  unsigned char t1_seqno;
+};
+
+
+
+
+/* Open the reader with the internal number READERNO and return a a
+   pointer to be used as handle in HANDLE.  Returns 0 on success. */
+int 
+ccid_open_reader (ccid_driver_t *handle, int readerno)
+{
+  static int initialized;
+
+  int rc;
+  usb_match_handle *match = NULL;
+  struct usb_device *dev = NULL;
+  usb_dev_handle *idev = NULL;
+
+  *handle = NULL;
+  if (!initialized)
+    {
+      usb_init ();
+      initialized = 1;
+    }
+  
+  rc = usb_create_match (&match, -1, -1, 11, -1, -1);
+  if (rc)
+    {
+      fprintf (stderr, "ccid-driver: usb_create_match failed: %d\n", rc);
+      return -1;
+    }
+
+  while (usb_find_device(match, dev, &dev) >= 0) 
+    {
+      fprintf(stderr, "ccid-driver: %-40s %04X/%04X\n", dev->filename,
+              dev->descriptor->idVendor, dev->descriptor->idProduct);
+      if (!readerno)
+        {
+          rc = usb_open (dev, &idev);
+          if (rc)
+            {
+              fprintf (stderr, "ccid-driver: usb_open failed: %d\n", rc);
+              goto leave;
+            }
+
+          rc = usb_claim_interface (idev, 0);
+          if (rc)
+            {
+              fprintf (stderr, "ccid-driver: usb_claim_interface failed: %d\n",
+                       rc);
+              goto leave;
+            }
+
+          *handle = calloc (1, sizeof **handle);
+          if (!*handle)
+            {
+              fprintf (stderr, "ccid-driver: out of memory\n");
+              rc = -1;
+              goto leave;
+            }
+          (*handle)->idev = idev;
+          idev = NULL;
+          break;
+        }
+      readerno--;
+    }
+
+
+ leave:
+  if (idev)
+    usb_close (idev);
+  /* fixme: Do we need to release dev or is it supposed to be a
+     shallow copy of the list created internally by usb_init ? */
+  usb_free_match (match);
+
+  return rc;
+}
+
+
+/* Return False if a card is present and powered. */
+int
+ccid_check_card_presence (ccid_driver_t handle)
+{
+
+  return -1;
+}
+
+
+static void
+set_msg_len (unsigned char *msg, unsigned int length)
+{
+  msg[1] = length;
+  msg[2] = length >> 8;
+  msg[3] = length >> 16;
+  msg[4] = length >> 24;
+}
+
+
+/* Write a MSG of length MSGLEN to the designated bulk out endpoint.
+   Returns 0 on success. */
+static int
+bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
+{
+  int rc;
+
+  rc = usb_bulk_write (handle->idev, 
+                       1, /*endpoint */
+                       msg, msglen,
+                       1000 /* ms timeout */);
+  if (rc == msglen)
+    return 0;
+
+  if (rc == -1)
+    fprintf (stderr, "ccid-driver: usb_bulk_write error: %s\n",
+             strerror (errno));
+  else
+    fprintf (stderr, "ccid-driver: usb_bulk_write failed: %d\n", rc);
+  return -1;
+}
+
+
+/* Read a maximum of LENGTH bytes from the bulk in endpoint into
+   BUFFER and return the actual read number if bytes in NREAD.
+   Returns 0 on success. */
+static int
+bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
+         size_t *nread)
+{
+  int rc;
+
+  rc = usb_bulk_read (handle->idev, 
+                      0x82,
+                      buffer, length,
+                      1000 /* ms timeout */ );
+  if (rc < 0)
+    {
+      fprintf (stderr, "ccid-driver: usb_bulk_read error: %s\n",
+               strerror (errno));
+      return -1;
+    }
+
+  *nread = rc;
+  return 0;
+}
+
+
+/* experimental */
+int
+ccid_poll (ccid_driver_t handle)
+{
+  int rc;
+  unsigned char msg[10];
+  size_t msglen;
+  int i, j;
+
+  rc = usb_bulk_read (handle->idev, 
+                      0x83,
+                      msg, sizeof msg,
+                      0 /* ms timeout */ );
+  if (rc < 0 && errno == ETIMEDOUT)
+    return 0;
+
+  if (rc < 0)
+    {
+        fprintf (stderr, "ccid-driver: usb_intr_read error: %s\n",
+                 strerror (errno));
+      return -1;
+    }
+
+  msglen = rc;
+  rc = 0;
+
+  if (msglen < 1)
+    {
+      fprintf (stderr, "ccid-driver: intr-in msg too short\n");
+      return -1;
+    }
+
+  if (msg[0] == RDR_to_PC_NotifySlotChange)
+    {
+      fprintf (stderr, "ccid-driver: notify slot change:");
+      for (i=1; i < msglen; i++)
+        for (j=0; j < 4; j++)
+          fprintf (stderr, " %d:%c%c",
+                   (i-1)*4+j, 
+                   (msg[i] & (1<<(j*2)))? 'p':'-',
+                   (msg[i] & (2<<(j*2)))? '*':' ');
+      putc ('\n', stderr);
+    }
+  else if (msg[0] == RDR_to_PC_HardwareError)    
+    {
+      fprintf (stderr, "ccid-driver: hardware error occured\n");
+    }
+  else
+    {
+      fprintf (stderr, "ccid-driver: unknown intr-in msg of type %02X\n",
+               msg[0]);
+    }
+
+  return 0;
+}
+
+
+
+int 
+ccid_slot_status (ccid_driver_t handle)
+{
+  int rc;
+  unsigned char msg[100];
+  size_t msglen;
+  unsigned char seqno;
+
+  msg[0] = PC_to_RDR_GetSlotStatus;
+  msg[5] = 0; /* slot */
+  msg[6] = seqno = handle->seqno++;
+  msg[7] = 0; /* RFU */
+  msg[8] = 0; /* RFU */
+  msg[9] = 0; /* RFU */
+  set_msg_len (msg, 0);
+
+  rc = bulk_out (handle, msg, 10);
+  if (rc)
+    return rc;
+  rc = bulk_in (handle, msg, sizeof msg, &msglen);
+  if (rc)
+    return rc;
+  if (msglen < 10)
+    {
+      fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n",
+               (unsigned int)msglen);
+      return -1;
+    }
+  if (msg[0] != RDR_to_PC_SlotStatus)
+    {
+      fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n",
+               msg[0]);
+      return -1;
+    }
+  if (msg[5] != 0)    
+    {
+      fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n",
+               msg[5]);
+      return -1;
+    }
+  if (msg[6] != seqno)    
+    {
+      fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n",
+               seqno, msg[6]);
+      return -1;
+    }
+
+  fprintf (stderr,
+           "ccid-driver: status: %02X  error: %02X clock-status: %02X\n",
+           msg[7], msg[8], msg[9] );
+
+  return 0;
+}
+
+
+int 
+ccid_get_atr (ccid_driver_t handle,
+              unsigned char *atr, size_t maxatrlen, size_t *atrlen)
+{
+  int rc;
+  unsigned char msg[100];
+  size_t msglen;
+  unsigned char seqno;
+  int i;
+
+  msg[0] = PC_to_RDR_IccPowerOn;
+  msg[5] = 0; /* slot */
+  msg[6] = seqno = handle->seqno++;
+  msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
+  msg[8] = 0; /* RFU */
+  msg[9] = 0; /* RFU */
+  set_msg_len (msg, 0);
+  msglen = 10;
+
+  rc = bulk_out (handle, msg, msglen);
+  if (rc)
+    return rc;
+  rc = bulk_in (handle, msg, sizeof msg, &msglen);
+  if (rc)
+    return rc;
+  if (msglen < 10)
+    {
+      fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n",
+               (unsigned int)msglen);
+      return -1;
+    }
+  if (msg[0] != RDR_to_PC_DataBlock)
+    {
+      fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n",
+               msg[0]);
+      return -1;
+    }
+  if (msg[5] != 0)    
+    {
+      fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n",
+               msg[5]);
+      return -1;
+    }
+  if (msg[6] != seqno)    
+    {
+      fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n",
+               seqno, msg[6]);
+      return -1;
+    }
+
+  fprintf (stderr,
+           "ccid-driver: status: %02X  error: %02X clock-status: %02X\n"
+           "               data:",  msg[7], msg[8], msg[9] );
+  for (i=10; i < msglen; i++)
+    fprintf (stderr, " %02X", msg[i]);
+  putc ('\n', stderr);
+  
+  if (atr)
+    {
+      size_t n = msglen - 10;
+
+      if (n > maxatrlen)
+        n = maxatrlen;
+      memcpy (atr, msg+10, n);
+      *atrlen = n;
+    }
+
+  return 0;
+}
+
+
+\f
+/*
+  Protocol T=1 overview
+
+  Block Structure:
+           Prologue Field:
+   1 byte     Node Address (NAD) 
+   1 byte     Protocol Control Byte (PCB)
+   1 byte     Length (LEN) 
+           Information Field:
+   0-254 byte APDU or Control Information (INF)
+           Epilogue Field:
+   1 byte     Error Detection Code (EDC)
+
+  NAD:  
+   bit 7     unused
+   bit 4..6  Destination Node Address (DAD)
+   bit 3     unused
+   bit 2..0  Source Node Address (SAD)
+
+   If node adresses are not used, SAD and DAD should be set to 0 on
+   the first block sent to the card.  If they are used they should
+   have different values (0 for one is okay); that first block sets up
+   the addresses of the node.
+
+  PCB:
+   Information Block (I-Block):
+      bit 7    0
+      bit 6    Sequence number (yep, that is modulo 2)
+      bit 5    Chaining flag 
+      bit 4..0 reserved
+   Received-Ready Block (R-Block):
+      bit 7    1
+      bit 6    0
+      bit 5    0
+      bit 4    Sequence number
+      bit 3..0  0 = no error
+                1 = EDC or parity error
+                2 = other error
+                other values are reserved
+   Supervisory Block (S-Block):
+      bit 7    1
+      bit 6    1
+      bit 5    clear=request,set=response
+      bit 4..0  0 = resyncronisation request
+                1 = information field size request
+                2 = abort request
+                3 = extension of BWT request
+                4 = VPP error
+                other values are reserved
+
+*/
+
+int
+ccid_transceive (ccid_driver_t handle,
+                 const unsigned char *apdu, size_t apdulen,
+                 unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+  int rc;
+  unsigned char msg[10+258], *tpdu, *p;
+  size_t msglen;
+  unsigned char seqno;
+  int i;
+  unsigned char crc;
+
+  
+  /* Construct an I-Block. */
+  if (apdulen > 254)
+    return -1; /* Invalid length. */
+
+  tpdu = msg+10;
+  tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
+  tpdu[1] = ((handle->t1_seqno & 1) << 6); /* I-block */
+  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;
+
+  handle->t1_seqno ^= 1;
+
+  msg[0] = PC_to_RDR_XfrBlock;
+  msg[5] = 0; /* slot */
+  msg[6] = seqno = handle->seqno++;
+  msg[7] = 4; /* bBWI */
+  msg[8] = 0; /* RFU */
+  msg[9] = 0; /* RFU */
+  set_msg_len (msg, apdulen+4);
+  msglen = 10 + apdulen + 4;
+
+  fprintf (stderr, "ccid-driver: sending");
+  for (i=0; i < msglen; i++)
+    fprintf (stderr, " %02X", msg[i]);
+  putc ('\n', stderr);
+
+  rc = bulk_out (handle, msg, msglen);
+  if (rc)
+    return rc;
+  rc = bulk_in (handle, msg, sizeof msg, &msglen);
+  if (rc)
+    return rc;
+  if (msglen < 10)
+    {
+      fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n",
+               (unsigned int)msglen);
+      return -1;
+    }
+  if (msg[0] != RDR_to_PC_DataBlock)
+    {
+      fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n",
+               msg[0]);
+      return -1;
+    }
+  if (msg[5] != 0)    
+    {
+      fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n",
+               msg[5]);
+      return -1;
+    }
+  if (msg[6] != seqno)    
+    {
+      fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n",
+               seqno, msg[6]);
+      return -1;
+    }
+
+  fprintf (stderr,
+           "ccid-driver: status: %02X  error: %02X clock-status: %02X\n"
+           "               data:",  msg[7], msg[8], msg[9] );
+  for (i=10; i < msglen; i++)
+    fprintf (stderr, " %02X", msg[i]);
+  putc ('\n', stderr);
+
+  if (resp)
+    {
+      size_t n = msglen - 10;
+      
+      if (n < 4)
+        n = 0; /* fixme: this is an empty I-block or some other block
+                  - we ignore it for now until we have implemented the
+                  T=1 machinery. */
+      else
+        {
+          p = msg + 10 + 3; /* Skip ccid header and prologue field. */
+          n -= 3;
+          n--; /* Strip the epilogue field. */
+          if (n > maxresplen)
+            n = maxresplen; /* fixme: return an error instead of truncating. */
+          memcpy (resp, p, n);
+        }
+      *nresp = n;
+    }
+
+  return 0;
+}
+
+
+
+
+#ifdef TEST
+int
+main (int argc, char **argv)
+{
+  int rc;
+  ccid_driver_t ccid;
+
+  rc = ccid_open_reader (&ccid, 0);
+  if (rc)
+    return 1;
+
+  ccid_poll (ccid);
+  fputs ("getting ATR ...\n", stderr);
+  rc = ccid_get_atr (ccid, NULL, 0, NULL);
+  if (rc)
+    return 1;
+
+  ccid_poll (ccid);
+  fputs ("getting slot status ...\n", stderr);
+  rc = ccid_slot_status (ccid);
+  if (rc)
+    return 1;
+
+  ccid_poll (ccid);
+
+  {
+    static unsigned char apdu[] = {
+      0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01};
+  rc = ccid_transceive (ccid,
+                        apdu, sizeof apdu,
+                        NULL, 0, NULL);
+  }
+  ccid_poll (ccid);
+
+  {
+    static unsigned char apdu[] = {
+      0, 0xCA, 0, 0x65, 254 };
+  rc = ccid_transceive (ccid,
+                        apdu, sizeof apdu,
+                        NULL, 0, NULL);
+  }
+  ccid_poll (ccid);
+
+
+  return 0;
+}
+
+/*
+ * Local Variables:
+ *  compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c"
+ * End:
+ */
+#endif /*TEST*/
+#endif /*HAVE_LIBUSB*/
diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h
new file mode 100644 (file)
index 0000000..8c2d1fe
--- /dev/null
@@ -0,0 +1,40 @@
+/* ccid-driver.c - USB ChipCardInterfaceDevices driver
+ *     Copyright (C) 2003 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 CCID_DRIVER_H
+#define CCID_DRIVER_H
+
+
+struct ccid_driver_s;
+typedef struct ccid_driver_s *ccid_driver_t;
+
+int ccid_open_reader (ccid_driver_t *handle, int readerno);
+int ccid_get_atr (ccid_driver_t handle,
+                  unsigned char *atr, size_t maxatrlen, size_t *atrlen);
+int ccid_transceive (ccid_driver_t handle,
+                     const unsigned char *apdu, size_t apdulen,
+                     unsigned char *resp, size_t maxresplen, size_t *nresp);
+
+
+
+#endif /*CCID_DRIVER_H*/
+
+
+
index 49036e0..fda0bed 100644 (file)
@@ -71,6 +71,7 @@ enum cmd_and_opt_values
   oReaderPort,
   octapiDriver,
   opcscDriver,
+  oDisableCCID,
   oDisableOpenSC,
 
 aTest };
@@ -97,11 +98,18 @@ static ARGPARSE_OPTS opts[] = {
   { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")},
   { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ct-API driver")},
   { opcscDriver, "pcsc-driver", 2, N_("NAME|use NAME as PC/SC driver")},
+  { oDisableCCID, "disable-ccidc", 0,
+#ifdef HAVE_LIBUSB
+                                    N_("do not use the internal CCID driver")
+#else
+                                    "@"
+#endif
+                                         /* end --disable-ccid */},
   { oDisableOpenSC, "disable-opensc", 0,
 #ifdef HAVE_OPENSC
-                                         N_("Do not use the OpenSC layer")
+                                    N_("do not use the OpenSC layer")
 #else
-                                         "@"
+                                    "@"
 #endif
                                          /* end --disable-opensc */},
 
@@ -387,6 +395,7 @@ main (int argc, char **argv )
         case oReaderPort: app_set_default_reader_port (pargs.r.ret_str); break;
         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 oDisableOpenSC: opt.disable_opensc = 1; break;
 
         default : pargs.err = configfp? 1:2; break;
index 3671c73..43c3189 100644 (file)
@@ -55,7 +55,8 @@ struct {
   const char *homedir; /* configuration directory name */
   const char *ctapi_driver; /* Library to access the ctAPI. */
   const char *pcsc_driver;  /* Library to access the PC/SC system. */
-  int disable_opensc;  /* Disable the sue of the OpenSC framework. */
+  int disable_opensc;  /* Disable the use of the OpenSC framework. */
+  int disable_ccid;    /* Disable the use of the internal CCID driver. */
 } opt;