summaryrefslogtreecommitdiffstats
path: root/scd/ccid-driver.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2004-09-30 16:34:34 +0200
committerWerner Koch <wk@gnupg.org>2004-09-30 16:34:34 +0200
commit335b5e4ac4d1ab56bb9db5addc4f7ca6ce8987f6 (patch)
treed5a581d9808c8efae6a0c6a1c9a1bd893f3633ed /scd/ccid-driver.c
parentThis commit was manufactured by cvs2svn to create branch (diff)
downloadgnupg2-335b5e4ac4d1ab56bb9db5addc4f7ca6ce8987f6.tar.xz
gnupg2-335b5e4ac4d1ab56bb9db5addc4f7ca6ce8987f6.zip
Preparing a new release. Updated gettext
Diffstat (limited to 'scd/ccid-driver.c')
-rw-r--r--scd/ccid-driver.c914
1 files changed, 663 insertions, 251 deletions
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 0fc168590..77fea944b 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -63,9 +63,9 @@
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.
+ This driver has been tested with the SCM SCR335 and SPR532
+ smartcard readers and requires that a reader implements the TPDU
+ level exchange and does fully automatic initialization.
*/
#ifdef HAVE_CONFIG_H
@@ -106,44 +106,49 @@
# include "scdaemon.h"
#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) \
+# define DEBUGOUT(t) do { if (debug_level) \
log_debug (DRVNAME t); } while (0)
-# define DEBUGOUT_1(t,a) do { if (DBG_CARD_IO) \
+# define DEBUGOUT_1(t,a) do { if (debug_level) \
log_debug (DRVNAME t,(a)); } while (0)
-# define DEBUGOUT_2(t,a,b) do { if (DBG_CARD_IO) \
+# define DEBUGOUT_2(t,a,b) do { if (debug_level) \
log_debug (DRVNAME t,(a),(b)); } while (0)
-# define DEBUGOUT_3(t,a,b,c) do { if (DBG_CARD_IO) \
+# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
log_debug (DRVNAME t,(a),(b),(c));} while (0)
-# define DEBUGOUT_CONT(t) do { if (DBG_CARD_IO) \
+# define DEBUGOUT_CONT(t) do { if (debug_level) \
log_printf (t); } while (0)
-# define DEBUGOUT_CONT_1(t,a) do { if (DBG_CARD_IO) \
+# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
log_printf (t,(a)); } while (0)
-# define DEBUGOUT_CONT_2(t,a,b) do { if (DBG_CARD_IO) \
+# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \
log_printf (t,(a),(b)); } while (0)
-# define DEBUGOUT_CONT_3(t,a,b,c) do { if (DBG_CARD_IO) \
+# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \
log_printf (t,(a),(b),(c)); } while (0)
-# define DEBUGOUT_LF() do { if (DBG_CARD_IO) \
+# define DEBUGOUT_LF() do { if (debug_level) \
log_printf ("\n"); } while (0)
#else /* Other usage of this source - don't use gnupg specifics. */
-# define DEBUGOUT(t) fprintf (stderr, DRVNAME t)
-# define DEBUGOUT_1(t,a) fprintf (stderr, DRVNAME t, (a))
-# define DEBUGOUT_2(t,a,b) fprintf (stderr, DRVNAME t, (a), (b))
-# define DEBUGOUT_3(t,a,b,c) fprintf (stderr, DRVNAME t, (a), (b), (c))
-# define DEBUGOUT_CONT(t) fprintf (stderr, t)
-# define DEBUGOUT_CONT_1(t,a) fprintf (stderr, t, (a))
-# define DEBUGOUT_CONT_2(t,a,b) fprintf (stderr, t, (a), (b))
-# define DEBUGOUT_CONT_3(t,a,b,c) fprintf (stderr, t, (a), (b), (c))
-# define DEBUGOUT_LF() putc ('\n', stderr)
+# define DEBUGOUT(t) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t); } while (0)
+# define DEBUGOUT_1(t,a) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a)); } while (0)
+# define DEBUGOUT_2(t,a,b) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b)); } while (0)
+# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0)
+# define DEBUGOUT_CONT(t) do { if (debug_level) \
+ fprintf (stderr, t); } while (0)
+# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
+ fprintf (stderr, t, (a)); } while (0)
+# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \
+ fprintf (stderr, t, (a), (b)); } while (0)
+# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \
+ fprintf (stderr, t, (a), (b), (c)); } while (0)
+# define DEBUGOUT_LF() do { if (debug_level) \
+ putc ('\n', stderr); } while (0)
#endif /* This source not used by scdaemon. */
@@ -180,8 +185,10 @@ enum {
/* Store information on the driver's state. A pointer to such a
structure is used as handle for most functions. */
-struct ccid_driver_s {
+struct ccid_driver_s
+{
usb_dev_handle *idev;
+ char *rid;
int seqno;
unsigned char t1_ns;
unsigned char t1_nr;
@@ -189,17 +196,21 @@ struct ccid_driver_s {
int auto_ifsd;
int max_ifsd;
int ifsd;
+ int powered_off;
+ int has_pinpad;
};
+static int initialized_usb; /* Tracks whether USB has been initialized. */
+static int debug_level; /* Flag to control the debug output. */
+
+
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);
-
-
/* Convert a little endian stored 4 byte value into an unsigned
integer. */
static unsigned int
@@ -239,6 +250,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
handle->auto_ifsd = 0;
handle->max_ifsd = 32;
handle->ifsd = 0;
+ handle->has_pinpad = 0;
if (buflen < 54 || buf[0] < 54)
{
DEBUGOUT ("CCID device descriptor is too short\n");
@@ -378,9 +390,15 @@ parse_ccid_descriptor (ccid_driver_t handle,
DEBUGOUT_1 (" bPINSupport %5u ", buf[52]);
if ((buf[52] & 1))
- DEBUGOUT_CONT ( " verification");
+ {
+ DEBUGOUT_CONT ( " verification");
+ handle->has_pinpad |= 1;
+ }
if ((buf[52] & 2))
- DEBUGOUT_CONT ( " modification");
+ {
+ DEBUGOUT_CONT ( " modification");
+ handle->has_pinpad |= 2;
+ }
DEBUGOUT_LF ();
DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]);
@@ -404,240 +422,541 @@ parse_ccid_descriptor (ccid_driver_t handle,
}
-/* Read the device information, return all required data and check
- that the device is usable for us. Returns 0 on success or an error
- code. */
-static int
-read_device_info (ccid_driver_t handle, struct usb_device *dev)
+static char *
+get_escaped_usb_string (usb_dev_handle *idev, int idx,
+ const char *prefix, const char *suffix)
+{
+ int rc;
+ unsigned char buf[280];
+ unsigned char *s;
+ unsigned int langid;
+ size_t i, n, len;
+ char *result;
+
+ if (!idx)
+ return NULL;
+
+ /* Fixme: The next line for the current Valgrid without support
+ for USB IOCTLs. */
+ memset (buf, 0, sizeof buf);
+
+ /* First get the list of supported languages and use the first one.
+ If we do don't find it we try to use English. Note that this is
+ all in a 2 bute Unicode encoding using little endian. */
+ rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8), 0,
+ buf, sizeof buf, 1000 /* ms timeout */);
+ if (rc < 4)
+ langid = 0x0409; /* English. */
+ else
+ langid = (buf[3] << 8) | buf[2];
+
+ rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8) + idx, langid,
+ buf, sizeof buf, 1000 /* ms timeout */);
+ if (rc < 2 || buf[1] != USB_DT_STRING)
+ return NULL; /* Error or not a string. */
+ len = buf[0];
+ if (len > rc)
+ return NULL; /* Larger than our buffer. */
+
+ for (s=buf+2, i=2, n=0; i+1 < len; i += 2, s += 2)
+ {
+ if (s[1])
+ n++; /* High byte set. */
+ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
+ n += 3 ;
+ else
+ n++;
+ }
+
+ result = malloc (strlen (prefix) + n + strlen (suffix) + 1);
+ if (!result)
+ return NULL;
+
+ strcpy (result, prefix);
+ n = strlen (prefix);
+ for (s=buf+2, i=2; i+1 < len; i += 2, s += 2)
+ {
+ if (s[1])
+ result[n++] = '\xff'; /* High byte set. */
+ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
+ {
+ sprintf (result+n, "%%%02X", *s);
+ n += 3;
+ }
+ else
+ result[n++] = *s;
+ }
+ strcpy (result+n, suffix);
+
+ return result;
+}
+
+/* This function creates an reader id to be used to find the same
+ physical reader after a reset. It returns an allocated and possibly
+ percent escaped string or NULL if not enough memory is available. */
+static char *
+make_reader_id (usb_dev_handle *idev,
+ unsigned int vendor, unsigned int product,
+ unsigned char serialno_index)
{
- int cfg_no;
+ char *rid;
+ char prefix[20];
- for (cfg_no=0; cfg_no <
-#ifdef HAVE_USB_CREATE_MATCH
- dev->descriptor->bNumConfigurations
+ sprintf (prefix, "%04X:%04X:", (vendor & 0xfff), (product & 0xffff));
+ rid = get_escaped_usb_string (idev, serialno_index, prefix, ":0");
+ if (!rid)
+ {
+ rid = malloc (strlen (prefix) + 3 + 1);
+ if (!rid)
+ return NULL;
+ strcpy (rid, prefix);
+ strcat (rid, "X:0");
+ }
+ return rid;
+}
+
+
+/* Combination function to either scan all CCID devices or to find and
+ open one specific device.
+
+ With READERNO = -1 and READERID is NULL, scan mode is used and
+ R_RID should be the address where to store the list of reader_ids
+ we found. If on return this list is empty, no CCID device has been
+ found; otherwise it points to an allocated linked list of reader
+ IDs. Note that in this mode the function always returns NULL.
+
+ With READERNO >= 0 or READERID is not NULL find mode is used. This
+ uses the same algorithm as the scan mode but stops and returns at
+ the entry number READERNO and return the handle for the the opened
+ USB device. If R_ID is not NULL it will receive the reader ID of
+ that device. If R_DEV is not NULL it will the device pointer of
+ that device. If IFCDESC_EXTRA is NOT NULL it will receive a
+ malloced copy of the interfaces "extra: data filed;
+ IFCDESC_EXTRA_LEN receive the lengtyh of this field. If there is
+ no reader with number READERNO or that reader is not usable by our
+ implementation NULL will be returned. The caller must close a
+ returned USB device handle and free (if not passed as NULL) the
+ returned reader ID info as well as the IFCDESC_EXTRA. On error
+ NULL will get stored at R_RID, R_DEV, IFCDESC_EXTRA and
+ IFCDESC_EXTRA_LEN. With READERID being -1 the function stops if
+ the READERID was found.
+
+ Note that the first entry of the returned reader ID list in scan mode
+ corresponds with a READERNO of 0 in find mode.
+*/
+static usb_dev_handle *
+scan_or_find_devices (int readerno, const char *readerid,
+ char **r_rid,
+ struct usb_device **r_dev,
+ unsigned char **ifcdesc_extra,
+ size_t *ifcdesc_extra_len)
+{
+ char *rid_list = NULL;
+ int count = 0;
+ struct usb_bus *busses, *bus;
+ struct usb_device *dev = NULL;
+ usb_dev_handle *idev = NULL;
+ int scan_mode = (readerno == -1 && !readerid);
+
+ /* Set return values to a default. */
+ if (r_rid)
+ *r_rid = NULL;
+ if (r_dev)
+ *r_dev = NULL;
+ if (ifcdesc_extra)
+ *ifcdesc_extra = NULL;
+ if (ifcdesc_extra_len)
+ *ifcdesc_extra_len = 0;
+
+ /* See whether we want scan or find mode. */
+ if (scan_mode)
+ {
+ assert (r_rid);
+ }
+
+ usb_find_busses();
+ usb_find_devices();
+
+#ifdef HAVE_USB_GET_BUSSES
+ busses = usb_get_busses();
#else
- dev->descriptor.bNumConfigurations
+ busses = usb_busses;
#endif
- ; cfg_no++)
- {
- struct usb_config_descriptor *config = dev->config + cfg_no;
- int ifc_no;
- for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
+ for (bus = busses; bus; bus = bus->next)
+ {
+ for (dev = bus->devices; dev; dev = dev->next)
{
- struct usb_interface *interface = config->interface + ifc_no;
- int set_no;
-
- for (set_no=0; set_no < interface->num_altsetting; set_no++)
+ int cfg_no;
+
+ for (cfg_no=0; cfg_no < dev->descriptor.bNumConfigurations; cfg_no++)
{
- struct usb_interface_descriptor *ifcdesc
- = interface->altsetting + set_no;
-
- if (ifcdesc->bInterfaceClass == 11
- && ifcdesc->bInterfaceSubClass == 0
- && ifcdesc->bInterfaceProtocol == 0)
+ struct usb_config_descriptor *config = dev->config + cfg_no;
+ int ifc_no;
+
+ if(!config)
+ continue;
+
+ for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
{
- if (ifcdesc->extra)
+ struct usb_interface *interface
+ = config->interface + ifc_no;
+ int set_no;
+
+ if (!interface)
+ continue;
+
+ for (set_no=0; set_no < interface->num_altsetting; set_no++)
{
- if (!parse_ccid_descriptor (handle,
- ifcdesc->extra,
- ifcdesc->extralen))
- return 0; /* okay. we can use it. */
+ struct usb_interface_descriptor *ifcdesc
+ = interface->altsetting + set_no;
+ char *rid;
+
+ /* The second condition is for some SCM Micro
+ SPR 532 which does not know about the
+ assigned CCID class. Instead of trying to
+ interpret the strings we simply look at the
+ product ID. */
+ if (ifcdesc && ifcdesc->extra
+ && ( (ifcdesc->bInterfaceClass == 11
+ && ifcdesc->bInterfaceSubClass == 0
+ && ifcdesc->bInterfaceProtocol == 0)
+ || (ifcdesc->bInterfaceClass == 255
+ && dev->descriptor.idVendor == 0x04e6
+ && dev->descriptor.idProduct == 0xe003
+ && ifcdesc->bInterfaceSubClass == 1
+ && ifcdesc->bInterfaceProtocol == 1)))
+ {
+ idev = usb_open (dev);
+ if (!idev)
+ {
+ DEBUGOUT_1 ("usb_open failed: %s\n",
+ strerror (errno));
+ continue;
+ }
+
+ rid = make_reader_id (idev,
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct,
+ dev->descriptor.iSerialNumber);
+ if (rid)
+ {
+ if (scan_mode)
+ {
+ char *p;
+
+ /* We are collecting infos about all
+ available CCID readers. Store
+ them and continue. */
+ DEBUGOUT_2 ("found CCID reader %d "
+ "(ID=%s)\n",
+ count, rid );
+ if ((p = malloc ((rid_list?
+ strlen (rid_list):0)
+ + 1 + strlen (rid)
+ + 1)))
+ {
+ *p = 0;
+ if (rid_list)
+ {
+ strcat (p, rid_list);
+ free (rid_list);
+ }
+ strcat (p, rid);
+ strcat (p, "\n");
+ rid_list = p;
+ }
+ else /* Out of memory. */
+ free (rid);
+ rid = NULL;
+ count++;
+ }
+ else if (!readerno
+ || (readerno < 0
+ && readerid
+ && !strcmp (readerid, rid)))
+ {
+ /* We found the requested reader. */
+ if (ifcdesc_extra && ifcdesc_extra_len)
+ {
+ *ifcdesc_extra = malloc (ifcdesc
+ ->extralen);
+ if (!*ifcdesc_extra)
+ {
+ usb_close (idev);
+ free (rid);
+ return NULL; /* Out of core. */
+ }
+ memcpy (*ifcdesc_extra, ifcdesc->extra,
+ ifcdesc->extralen);
+ *ifcdesc_extra_len = ifcdesc->extralen;
+ }
+
+ if (r_dev)
+ *r_dev = dev;
+ if (r_rid)
+ {
+ *r_rid = rid;
+ rid = NULL;
+ }
+ else
+ free (rid);
+ return idev; /* READY. */
+ }
+ else
+ {
+ /* This is not yet the reader we
+ want. fixme: We could avoid the
+ extra usb_open in this case. */
+ if (readerno >= 0)
+ readerno--;
+ }
+ free (rid);
+ }
+
+ usb_close (idev);
+ idev = NULL;
+ goto next_device;
+ }
}
}
}
+ next_device:
+ ;
}
}
- return CCID_DRIVER_ERR_NO_READER; /* No suitable device found. */
+
+ if (scan_mode)
+ *r_rid = rid_list;
+
+ return NULL;
+}
+
+
+/* Set the level of debugging to to usea dn return the old level. -1
+ just returns the old level. A level of 0 disables debugging, 1
+ enables debugging, other values are not yet defined. */
+int
+ccid_set_debug_level (int level)
+{
+ int old = debug_level;
+ if (level != -1)
+ debug_level = level;
+ return old;
+}
+
+
+char *
+ccid_get_reader_list (void)
+{
+ char *reader_list;
+
+ if (!initialized_usb)
+ {
+ usb_init ();
+ initialized_usb = 1;
+ }
+
+ scan_or_find_devices (-1, NULL, &reader_list, NULL, NULL, NULL);
+ return reader_list;
}
/* Open the reader with the internal number READERNO and return a
pointer to be used as handle in HANDLE. Returns 0 on success. */
int
-ccid_open_reader (ccid_driver_t *handle, int readerno)
+ccid_open_reader (ccid_driver_t *handle, const char *readerid)
{
-#ifdef HAVE_USB_CREATE_MATCH
- /* This is the development version of libusb. */
- static int initialized;
- int rc;
- usb_match_handle *match = NULL;
+ int rc = 0;
struct usb_device *dev = NULL;
usb_dev_handle *idev = NULL;
+ char *rid = NULL;
+ unsigned char *ifcdesc_extra = NULL;
+ size_t ifcdesc_extra_len;
+ int readerno;
*handle = NULL;
- if (!initialized)
+
+ if (!initialized_usb)
{
usb_init ();
- initialized = 1;
+ initialized_usb = 1;
}
-
- rc = usb_create_match (&match, -1, -1, 11, -1, -1);
- if (rc)
+
+ /* See whether we want to use the reader ID string or a reader
+ number. A readerno of -1 indicates that the reader ID string is
+ to be used. */
+ if (readerid && strchr (readerid, ':'))
+ readerno = -1; /* We want to use the readerid. */
+ else if (readerid)
{
- DEBUGOUT_1 ("usb_create_match failed: %d\n", rc);
- return CCID_DRIVER_ERR_NO_READER;
+ readerno = atoi (readerid);
+ if (readerno < 0)
+ {
+ DEBUGOUT ("no CCID readers found\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
}
-
- while (usb_find_device(match, dev, &dev) >= 0)
+ else
+ readerno = 0; /* Default. */
+
+ idev = scan_or_find_devices (readerno, readerid, &rid, &dev,
+ &ifcdesc_extra, &ifcdesc_extra_len);
+ if (!idev)
{
- DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename,
- dev->descriptor->idVendor, dev->descriptor->idProduct);
- if (!readerno)
- {
- *handle = calloc (1, sizeof **handle);
- if (!*handle)
- {
- DEBUGOUT ("out of memory\n");
- rc = CCID_DRIVER_ERR_OUT_OF_CORE;
- free (*handle);
- *handle = NULL;
- goto leave;
- }
+ if (readerno == -1)
+ DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid );
+ else
+ DEBUGOUT_1 ("no CCID reader with number %d\n", readerno );
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
- rc = read_device_info (*handle, dev);
- if (rc)
- {
- DEBUGOUT ("device not supported\n");
- free (*handle);
- *handle = NULL;
- goto leave;
- }
+ /* Okay, this is a CCID reader. */
+ *handle = calloc (1, sizeof **handle);
+ if (!*handle)
+ {
+ DEBUGOUT ("out of memory\n");
+ rc = CCID_DRIVER_ERR_OUT_OF_CORE;
+ goto leave;
+ }
+ (*handle)->idev = idev;
+ (*handle)->rid = rid;
- rc = usb_open (dev, &idev);
- if (rc)
- {
- DEBUGOUT_1 ("usb_open failed: %d\n", rc);
- free (*handle);
- *handle = NULL;
- rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
- goto leave;
- }
+ DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid );
- /* fixme: Do we need to claim and set the interface as
- determined by read_device_info ()? */
- rc = usb_claim_interface (idev, 0);
- if (rc)
- {
- DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
- free (*handle);
- *handle = NULL;
- rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
- goto leave;
- }
+ if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len))
+ {
+ DEBUGOUT ("device not supported\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
+
+ /* fixme: Do we need to claim and set the interface as
+ determined above? */
+ rc = usb_claim_interface (idev, 0);
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+ rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
+ goto leave;
+ }
+
+ /* FIXME: Do we need to get the endpoint addresses from the
+ structure and store them with the handle? */
+
+ leave:
+ free (ifcdesc_extra);
+ if (rc)
+ {
+ free (rid);
+ if (idev)
+ usb_close (idev);
+ free (*handle);
+ *handle = NULL;
+ }
- (*handle)->idev = idev;
- idev = NULL;
- /* FIXME: Do we need to get the endpoint addresses from the
- structure and store them with the handle? */
+ return rc;
+}
- break;
- }
- readerno--;
+
+static void
+do_close_reader (ccid_driver_t handle)
+{
+ int rc;
+ unsigned char msg[100];
+ size_t msglen;
+ unsigned char seqno;
+
+ if (!handle->powered_off)
+ {
+ msg[0] = PC_to_RDR_IccPowerOff;
+ 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);
+ msglen = 10;
+
+ rc = bulk_out (handle, msg, msglen);
+ if (!rc)
+ bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,seqno);
+ handle->powered_off = 1;
+ }
+ if (handle->idev)
+ {
+ usb_release_interface (handle->idev, 0);
+ usb_close (handle->idev);
+ handle->idev = NULL;
}
+}
- 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);
- if (!rc && !*handle)
- rc = -1; /* In case we didn't enter the while loop at all. */
+/* Reset a reader on HANDLE. This is useful in case a reader has been
+ plugged of and inserted at a different port. By resetting the
+ handle, the same reader will be get used. Note, that on error the
+ handle won't get released.
- return rc;
-#else /* Stable 0.1 version of libusb. */
- static int initialized;
+ This does not return an ATR, so ccid_get_atr should be called right
+ after this one.
+*/
+int
+ccid_shutdown_reader (ccid_driver_t handle)
+{
int rc = 0;
- struct usb_bus *busses, *bus;
struct usb_device *dev = NULL;
usb_dev_handle *idev = NULL;
+ unsigned char *ifcdesc_extra = NULL;
+ size_t ifcdesc_extra_len;
- *handle = NULL;
- if (!initialized)
- {
- usb_init ();
- initialized = 1;
- }
-
- usb_find_busses();
- usb_find_devices();
- busses = usb_get_busses();
-
- for (bus = busses; bus; bus = bus->next)
- {
- for (dev = bus->devices; dev; dev = dev->next)
- {
- DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename,
- dev->descriptor.idVendor, dev->descriptor.idProduct);
+ if (!handle || !handle->rid)
+ return CCID_DRIVER_ERR_INV_VALUE;
- if (!readerno)
- {
- *handle = calloc (1, sizeof **handle);
- if (!*handle)
- {
- DEBUGOUT ("out of memory\n");
- rc = CCID_DRIVER_ERR_OUT_OF_CORE;
- free (*handle);
- *handle = NULL;
- goto leave;
- }
+ do_close_reader (handle);
- rc = read_device_info (*handle, dev);
- if (rc)
- {
- DEBUGOUT ("device not supported\n");
- free (*handle);
- *handle = NULL;
- continue;
- }
+ idev = scan_or_find_devices (-1, handle->rid, NULL, &dev,
+ &ifcdesc_extra, &ifcdesc_extra_len);
+ if (!idev)
+ {
+ DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid);
+ return CCID_DRIVER_ERR_NO_READER;
+ }
- idev = usb_open (dev);
- if (!idev)
- {
- DEBUGOUT_1 ("usb_open failed: %s\n", strerror (errno));
- free (*handle);
- *handle = NULL;
- rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
- goto leave;
- }
- /* fixme: Do we need to claim and set the interface as
- determined by read_device_info ()? */
- rc = usb_claim_interface (idev, 0);
- if (rc)
- {
- DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
- free (*handle);
- *handle = NULL;
- rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
- goto leave;
- }
+ handle->idev = idev;
- (*handle)->idev = idev;
- idev = NULL;
- /* FIXME: Do we need to get the endpoint addresses from the
- structure and store them with the handle? */
-
- goto leave; /* ready. */
- }
- readerno--;
- }
+ if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len))
+ {
+ DEBUGOUT ("device not supported\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
}
-
+
+ /* fixme: Do we need to claim and set the interface as
+ determined above? */
+ rc = usb_claim_interface (idev, 0);
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+ rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
+ goto leave;
+ }
+
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 ? */
-
- if (!rc && !*handle)
- rc = -1; /* In case we didn't enter the while loop at all. */
+ free (ifcdesc_extra);
+ if (rc)
+ {
+ usb_close (handle->idev);
+ handle->idev = NULL;
+ }
return rc;
-#endif /* Stable version 0.1 of libusb. */
+
}
@@ -648,29 +967,8 @@ ccid_close_reader (ccid_driver_t handle)
if (!handle || !handle->idev)
return 0;
- {
- int rc;
- unsigned char msg[100];
- size_t msglen;
- unsigned char seqno;
-
- msg[0] = PC_to_RDR_IccPowerOff;
- 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);
- msglen = 10;
-
- rc = bulk_out (handle, msg, msglen);
- if (!rc)
- bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
- }
-
- usb_release_interface (handle->idev, 0);
- usb_close (handle->idev);
- handle->idev = NULL;
+ do_close_reader (handle);
+ free (handle->rid);
free (handle);
return 0;
}
@@ -719,6 +1017,9 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
int i, rc;
size_t msglen;
+ /* Fixme: The next line for the current Valgrind without support
+ for USB IOCTLs. */
+ memset (buffer, 0, length);
retry:
rc = usb_bulk_read (handle->idev,
0x82,
@@ -895,6 +1196,8 @@ ccid_get_atr (ccid_driver_t handle,
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, seqno);
if (rc)
return rc;
+
+ handle->powered_off = 0;
if (atr)
{
@@ -977,9 +1280,6 @@ ccid_get_atr (ccid_driver_t handle,
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);
@@ -1170,10 +1470,6 @@ ccid_transceive (ccid_driver_t handle,
if (rc)
return rc;
- /* Fixme: The next line for the current Valgrid without support
- for USB IOCTLs. */
- memset (recv_buffer, 0, sizeof recv_buffer);
-
msg = recv_buffer;
rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
RDR_to_PC_DataBlock, seqno);
@@ -1324,14 +1620,97 @@ ccid_transceive (ccid_driver_t handle,
#ifdef TEST
+
+static void
+print_error (int err)
+{
+ const char *p;
+ char buf[50];
+
+ switch (err)
+ {
+ case 0: p = "success";
+ case CCID_DRIVER_ERR_OUT_OF_CORE: p = "out of core"; break;
+ case CCID_DRIVER_ERR_INV_VALUE: p = "invalid value"; break;
+ case CCID_DRIVER_ERR_NO_DRIVER: p = "no driver"; break;
+ case CCID_DRIVER_ERR_NOT_SUPPORTED: p = "not supported"; break;
+ case CCID_DRIVER_ERR_LOCKING_FAILED: p = "locking failed"; break;
+ case CCID_DRIVER_ERR_BUSY: p = "busy"; break;
+ case CCID_DRIVER_ERR_NO_CARD: p = "no card"; break;
+ case CCID_DRIVER_ERR_CARD_INACTIVE: p = "card inactive"; break;
+ case CCID_DRIVER_ERR_CARD_IO_ERROR: p = "card I/O error"; break;
+ case CCID_DRIVER_ERR_GENERAL_ERROR: p = "general error"; break;
+ case CCID_DRIVER_ERR_NO_READER: p = "no reader"; break;
+ case CCID_DRIVER_ERR_ABORTED: p = "aborted"; break;
+ default: sprintf (buf, "0x%05x", err); p = buf; break;
+ }
+ fprintf (stderr, "operation failed: %s\n", p);
+}
+
+static void
+print_data (const unsigned char *data, size_t length)
+{
+ if (length >= 2)
+ {
+ fprintf (stderr, "operation status: %02X%02X\n",
+ data[length-2], data[length-1]);
+ length -= 2;
+ }
+ if (length)
+ {
+ fputs (" returned data:", stderr);
+ for (; length; length--, data++)
+ fprintf (stderr, " %02X", *data);
+ putc ('\n', stderr);
+ }
+}
+
+static void
+print_result (int rc, const unsigned char *data, size_t length)
+{
+ if (rc)
+ print_error (rc);
+ else if (data)
+ print_data (data, length);
+}
+
int
main (int argc, char **argv)
{
int rc;
ccid_driver_t ccid;
unsigned int slotstat;
+ unsigned char result[512];
+ size_t resultlen;
+
+ if (argc)
+ {
+ argc--;
+ argv++;
+ }
+
+ while (argc)
+ {
+ if ( !strcmp (*argv, "--list"))
+ {
+ char *p;
+ p = ccid_get_reader_list ();
+ if (!p)
+ return 1;
+ fputs (p, stderr);
+ free (p);
+ return 0;
+ }
+ else if ( !strcmp (*argv, "--debug"))
+ {
+ ccid_set_debug_level (1);
+ argc--; argv++;
+ }
+ else
+ break;
+ }
- rc = ccid_open_reader (&ccid, 0);
+ rc = ccid_open_reader (&ccid, argc? *argv:NULL);
if (rc)
return 1;
@@ -1339,34 +1718,67 @@ main (int argc, char **argv)
fputs ("getting ATR ...\n", stderr);
rc = ccid_get_atr (ccid, NULL, 0, NULL);
if (rc)
- return 1;
+ {
+ print_error (rc);
+ return 1;
+ }
ccid_poll (ccid);
fputs ("getting slot status ...\n", stderr);
rc = ccid_slot_status (ccid, &slotstat);
if (rc)
- return 1;
+ {
+ print_error (rc);
+ return 1;
+ }
ccid_poll (ccid);
+ fputs ("selecting application OpenPGP ....\n", stderr);
{
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);
+ rc = ccid_transceive (ccid,
+ apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (rc, result, resultlen);
}
+
+
ccid_poll (ccid);
+ fputs ("getting OpenPGP DO 0x65 ....\n", stderr);
{
- static unsigned char apdu[] = {
- 0, 0xCA, 0, 0x65, 254 };
- rc = ccid_transceive (ccid,
- apdu, sizeof apdu,
- NULL, 0, NULL);
+ static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 };
+ rc = ccid_transceive (ccid, apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (rc, result, resultlen);
}
+
ccid_poll (ccid);
+/* if (!ccid->has_pinpad) */
+/* { */
+/* fputs ("verifying that CHV1 is 123456....\n", stderr); */
+/* { */
+/* static unsigned char apdu[] = {0, 0x20, 0, 0x81, */
+/* 6, '1','2','3','4','5','6'}; */
+/* rc = ccid_transceive (ccid, apdu, sizeof apdu, */
+/* result, sizeof result, &resultlen); */
+/* print_result (rc, result, resultlen); */
+/* } */
+/* } */
+/* else */
+/* { */
+/* fputs ("verifying CHV1 using the PINPad ....\n", stderr); */
+/* { */
+/* rc = ccid_secure_transceive (ccid, */
+/* result, sizeof result, &resultlen); */
+/* print_result (rc, result, resultlen); */
+/* } */
+/* } */
+
+ ccid_close_reader (ccid);
return 0;
}