aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ChangeLog5
-rw-r--r--configure.ac2
-rw-r--r--g10/ChangeLog19
-rw-r--r--g10/apdu.c7
-rw-r--r--g10/ccid-driver.c872
-rw-r--r--g10/ccid-driver.h5
-rw-r--r--g10/g10.c16
7 files changed, 628 insertions, 298 deletions
diff --git a/ChangeLog b/ChangeLog
index 2f743a3ee..42b961b5c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2004-09-17 Werner Koch <[email protected]>
+
+ * configure.ac: Don't check for usb_create_match or
+ use_get_string_simple anymore.
+
2004-09-15 David Shaw <[email protected]>
* configure.ac: Give warning when using capabilities. Check for
diff --git a/configure.ac b/configure.ac
index eb4b9192e..818a980a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1097,9 +1097,7 @@ if test "$card_support" = yes; then
])
fi
AC_SUBST(LIBUSB_LIBS)
-AC_CHECK_FUNCS(usb_create_match)
AC_CHECK_FUNCS(usb_get_busses)
-AC_CHECK_FUNCS(usb_get_string_simple)
#
# Check for readline support
diff --git a/g10/ChangeLog b/g10/ChangeLog
index 03202da8d..678ccdb5a 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,22 @@
+2004-09-17 Werner Koch <[email protected]>
+
+ * g10.c (list_config): New config option ccid-reader-id.
+ (gpgconf_list): Add "reader-port".
+
+ * apdu.c (open_ccid_reader): New arg PORTSTR. Pass it to
+ ccid_open_reader.
+ (apdu_open_reader): Pass portstr to open_ccid_reader.
+
+ * ccid-driver.c (ccid_get_reader_list): New.
+ (ccid_open_reader): Changed API to take a string for the reader.
+ Removed al the cruft for the libusb development vesion which seems
+ not to be maintained anymore and there are no packages anyway.
+ The stable library works just fine.
+ (struct ccid_reader_id_s): Deleted and replaced everywhere by a
+ simple string.
+ (usb_get_string_simple): Removed.
+ (bulk_in): Do valgrind hack here and not just everywhere.
+
2004-09-16 David Shaw <[email protected]>
* keyedit.c (show_key_with_all_names, show_prefs): Show preferred
diff --git a/g10/apdu.c b/g10/apdu.c
index 5f800c983..00f149205 100644
--- a/g10/apdu.c
+++ b/g10/apdu.c
@@ -1273,7 +1273,7 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
/* Open the reader and try to read an ATR. */
static int
-open_ccid_reader (void)
+open_ccid_reader (const char *portstr)
{
int err;
int slot;
@@ -1284,7 +1284,7 @@ open_ccid_reader (void)
return -1;
slotp = reader_table + slot;
- err = ccid_open_reader (&slotp->ccid.handle, 0);
+ err = ccid_open_reader (&slotp->ccid.handle, portstr);
if (err)
{
slotp->used = 0;
@@ -1881,10 +1881,11 @@ apdu_open_reader (const char *portstr)
{
int slot;
- slot = open_ccid_reader ();
+ slot = open_ccid_reader (portstr);
if (slot != -1)
return slot; /* got one */
}
+
#endif /* HAVE_LIBUSB */
#ifdef HAVE_OPENSC
diff --git a/g10/ccid-driver.c b/g10/ccid-driver.c
index 7277ac20c..266aa4038 100644
--- a/g10/ccid-driver.c
+++ b/g10/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
@@ -183,19 +183,12 @@ enum {
};
-/* This structure is used to keep information about a specific reader. */
-struct ccid_reader_id_s
-{
- struct ccid_reader_id_s *next;
- char reader_id[1];
-};
-
/* 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;
- struct ccid_reader_id_s *rid;
+ char *rid;
int seqno;
unsigned char t1_ns;
unsigned char t1_nr;
@@ -203,10 +196,13 @@ struct ccid_driver_s
int auto_ifsd;
int max_ifsd;
int ifsd;
+ int has_pinpad;
};
-/* Flag to control whether we we want debug output. */
-static int debug_level;
+
+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);
@@ -214,64 +210,6 @@ 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);
-#ifndef HAVE_USB_GET_STRING_SIMPLE
-
-/* This function adapted from the libusb 0.1.8 sources. It's here so
- that systems with an older libusb can still use smartcards. */
-
-static int usb_get_string_simple(usb_dev_handle *dev, int indx, char *buf,
- size_t buflen)
-{
- char tbuf[256];
- int ret, langid, si, di;
-
- /*
- * Asking for the zero'th index is special - it returns a string
- * descriptor that contains all the language IDs supported by the
- * device. Typically there aren't many - often only one. The
- * language IDs are 16 bit numbers, and they start at the third byte
- * in the descriptor. See USB 2.0 specification, section 9.6.7, for
- * more information on this. */
-
- ret=usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
- (USB_DT_STRING << 8), 0, tbuf, sizeof(tbuf), 1000);
-
- if (ret < 0)
- return ret;
-
- if (ret < 4)
- return -EIO;
-
- langid = tbuf[2] | (tbuf[3] << 8);
-
- ret=usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
- (USB_DT_STRING << 8)+indx,langid,tbuf,sizeof(tbuf),1000);
- if (ret < 0)
- return ret;
-
- if (tbuf[1] != USB_DT_STRING)
- return -EIO;
-
- if (tbuf[0] > ret)
- return -EFBIG;
-
- for (di = 0, si = 2; si < tbuf[0]; si += 2) {
- if (di >= (buflen - 1))
- break;
-
- if (tbuf[si + 1]) /* high byte */
- buf[di++] = '?';
- else
- buf[di++] = tbuf[si];
- }
-
- buf[di] = 0;
-
- return di;
-}
-
-#endif /* !HAVE_USB_GET_STRING_SIMPLE */
-
/* Convert a little endian stored 4 byte value into an unsigned
integer. */
static unsigned int
@@ -311,6 +249,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");
@@ -450,9 +389,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]);
@@ -476,55 +421,98 @@ parse_ccid_descriptor (ccid_driver_t handle,
}
+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 in the reader_id element of the returned
- structure or NULL if not enough memory is available. */
-static struct ccid_reader_id_s *
+ 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)
{
- char serialno[257];
- unsigned char *s;
- size_t n;
- struct ccid_reader_id_s *rid;
- char *buf;
-
- /* NOTE: valgrind will complain here becuase it does not know about
- the USB IOCTLs. */
-
- if (!serialno_index)
- *serialno = 0;
- else if (usb_get_string_simple (idev, serialno_index,
- serialno, sizeof (serialno) -1) < 0)
- strcpy (serialno, "X"); /* Error. */
-
- for (n=0,s=serialno; *s; s++)
- if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
- n += 3;
- else
- n++;
-
- n += 4+1+4+1+2; /* (vendor : product : :0 ) */
- rid = malloc (sizeof *rid + n);
+ char *rid;
+ char prefix[20];
+
+ sprintf (prefix, "%04X:%04X:", (vendor & 0xfff), (product & 0xffff));
+ rid = get_escaped_usb_string (idev, serialno_index, prefix, ":0");
if (!rid)
- return NULL;
- rid->next = NULL;
- buf = rid->reader_id;
- sprintf (buf, "%04X:%04X:", (vendor & 0xfff), (product & 0xffff));
- for (n=strlen (buf),s=serialno; *s; s++)
- if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
- {
- sprintf (buf+n, "%%%02X", *s);
- n += 3;
- }
- else
- buf[n++] = *s;
- buf[n++] = ':';
- buf[n++] = '0'; /* Reserved. */
- buf[n] = 0;
-
+ {
+ rid = malloc (strlen (prefix) + 3 + 1);
+ if (!rid)
+ return NULL;
+ strcpy (rid, prefix);
+ strcat (rid, "X:0");
+ }
return rid;
}
@@ -532,42 +520,44 @@ make_reader_id (usb_dev_handle *idev,
/* Combination function to either scan all CCID devices or to find and
open one specific device.
- With READERNO = -1 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 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 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,
- struct ccid_reader_id_s **r_rid,
+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)
{
- struct ccid_reader_id_s *rid_list = NULL;
- struct ccid_reader_id_s **rid_tail = NULL;
+ 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)
@@ -580,10 +570,9 @@ scan_or_find_devices (int readerno,
*ifcdesc_extra_len = 0;
/* See whether we want scan or find mode. */
- if (readerno < 0) /* Scan mode. */
+ if (scan_mode)
{
assert (r_rid);
- rid_tail = &rid_list;
}
usb_find_busses();
@@ -622,13 +611,22 @@ scan_or_find_devices (int readerno,
{
struct usb_interface_descriptor *ifcdesc
= interface->altsetting + set_no;
- struct ccid_reader_id_s *rid;
+ char *rid;
- if (ifcdesc
- && ifcdesc->bInterfaceClass == 11
- && ifcdesc->bInterfaceSubClass == 0
- && ifcdesc->bInterfaceProtocol == 0
- && ifcdesc->extra)
+ /* 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)
@@ -644,19 +642,40 @@ scan_or_find_devices (int readerno,
dev->descriptor.iSerialNumber);
if (rid)
{
- if (readerno < 0)
+ 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->reader_id );
- *rid_tail = rid;
- rid_tail = &rid->next;
+ 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)
+ else if (!readerno
+ || (readerno < 0
+ && readerid
+ && !strcmp (readerid, rid)))
{
/* We found the requested reader. */
if (ifcdesc_extra && ifcdesc_extra_len)
@@ -690,7 +709,8 @@ scan_or_find_devices (int readerno,
/* This is not yet the reader we
want. fixme: We could avoid the
extra usb_open in this case. */
- readerno--;
+ if (readerno >= 0)
+ readerno--;
}
free (rid);
}
@@ -707,141 +727,89 @@ scan_or_find_devices (int readerno,
}
}
- if (readerno < 0)
+ if (scan_mode)
*r_rid = rid_list;
return NULL;
}
-/* 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)
-{
-#ifdef HAVE_USB_CREATE_MATCH
- /* This is the development version of libusb. */
- 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)
- {
- DEBUGOUT_1 ("usb_create_match failed: %d\n", rc);
- return CCID_DRIVER_ERR_NO_READER;
- }
-
- while (usb_find_device(match, dev, &dev) >= 0)
- {
- 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;
- }
-
- rc = read_device_info (*handle, dev);
- if (rc)
- {
- DEBUGOUT ("device not supported\n");
- free (*handle);
- *handle = NULL;
- goto leave;
- }
-
- 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;
- }
+/* 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;
+}
- /* 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;
- idev = NULL;
- /* FIXME: Do we need to get the endpoint addresses from the
- structure and store them with the handle? */
+char *
+ccid_get_reader_list (void)
+{
+ char *reader_list;
- break;
- }
- readerno--;
+ if (!initialized_usb)
+ {
+ usb_init ();
+ initialized_usb = 1;
}
- 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);
+ scan_or_find_devices (-1, NULL, &reader_list, NULL, NULL, NULL);
+ return reader_list;
+}
- if (!rc && !*handle)
- rc = -1; /* In case we didn't enter the while loop at all. */
- return rc;
-#else /* Stable 0.1 version of libusb. */
- static int initialized;
- static struct ccid_reader_id_s *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, const char *readerid)
+{
int rc = 0;
struct usb_device *dev = NULL;
usb_dev_handle *idev = NULL;
- struct ccid_reader_id_s *rid = 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 ();
- /* Now scan all CCID device. I am not sure whether this is
- really required, but anyway, it is good for debugging. */
- scan_or_find_devices (-1, &reader_list, NULL, NULL, NULL);
- initialized = 1;
+ initialized_usb = 1;
}
- if (!reader_list || readerno < 0)
+ /* 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 ("no CCID readers found\n");
- rc = CCID_DRIVER_ERR_NO_READER;
- goto leave;
+ readerno = atoi (readerid);
+ if (readerno < 0)
+ {
+ DEBUGOUT ("no CCID readers found\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
}
+ else
+ readerno = 0; /* Default. */
- idev = scan_or_find_devices (readerno, &rid, &dev,
+ idev = scan_or_find_devices (readerno, readerid, &rid, &dev,
&ifcdesc_extra, &ifcdesc_extra_len);
if (!idev)
{
- DEBUGOUT_1 ("no CCID reader with number %d\n", readerno );
+ 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;
}
@@ -857,9 +825,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
(*handle)->idev = idev;
(*handle)->rid = rid;
- DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n",
- readerno, rid->reader_id );
-
+ DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid );
if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len))
@@ -894,7 +860,6 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
}
return rc;
-#endif /* Stable version 0.1 of libusb. */
}
@@ -933,18 +898,6 @@ ccid_close_reader (ccid_driver_t handle)
return 0;
}
-/* 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;
-}
-
/* Return False if a card is present and powered. */
int
ccid_check_card_presence (ccid_driver_t handle)
@@ -988,6 +941,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,
@@ -1246,9 +1202,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);
@@ -1439,10 +1392,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);
@@ -1591,18 +1540,326 @@ ccid_transceive (ccid_driver_t handle,
+int
+ccid_secure_transceive (ccid_driver_t handle,
+ unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+ int rc;
+ unsigned char send_buffer[10+259], recv_buffer[10+259];
+ unsigned char *msg, *tpdu, *p;
+ size_t msglen, tpdulen, last_tpdulen, n;
+ unsigned char seqno;
+ int i;
+ unsigned int edc;
+ size_t dummy_nresp;
+ int next_chunk = 1;
+ int sending = 1;
+ int retries = 0;
+
+ if (!nresp)
+ nresp = &dummy_nresp;
+ *nresp = 0;
+
+ tpdulen = 0; /* Avoid compiler warning about no initialization. */
+ msg = send_buffer;
+ for (;;)
+ {
+ msg[0] = PC_to_RDR_Secure;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 4; /* bBWI */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ msg[10] = 0; /* Perform PIN verification. */
+ msg[11] = 0; /* Timeout in seconds. */
+ msg[12] = 0x8A; /* bmFormatString: Byte, pos=1, Ascii. */
+ msg[13] = 0x80; /* bmPINBlockString:
+ 8 bits of pin length to insert.
+ 0 bytes of PIN block size. */
+ msg[14] = 0x00; /* bmPINLengthFormat:
+ Units are bits, position is 0. */
+ msg[15] = 6; /* wPINMaxExtraDigit-Minimum. */
+ msg[16] = 8; /* wPINMaxExtraDigit-Maximum. */
+ msg[17] = 0x02; /* bEntryValidationCondition:
+ Validation key pressed
+ */
+ msg[18] = 0xff; /* bNumberMessage: Default. */
+ msg[19] = 0x04; /* wLangId-High. */
+ msg[20] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */
+ msg[21] = 0; /* bMsgIndex. */
+ /* bTeoProlog follows: */
+ msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */
+ msg[24] = 4; /* apdulen. */
+ /* APDU follows: */
+ msg[25] = 0; /* CLA */
+ msg[26] = 0x20; /* INS Verify */
+ msg[27] = 0; /* P1 */
+ msg[28] = 0x81; /* P2: OpenPGP CHV1 */
+ msg[29] = 0; /* L_c */
+ msg[30] = '0';
+ msg[31] = '0';
+ msg[32] = '0';
+ msg[33] = '0';
+ msg[34] = '0';
+ msg[35] = '0';
+ msg[36] = '0';
+ msg[37] = '0';
+ msglen = 29;
+ set_msg_len (msg, msglen - 10);
+ last_tpdulen = 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;
+
+ msg = recv_buffer;
+ rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
+ RDR_to_PC_DataBlock, seqno);
+ if (rc)
+ return rc;
+
+ tpdu = msg + 10;
+ tpdulen = msglen - 10;
+
+ if (tpdulen < 4)
+ {
+ usb_clear_halt (handle->idev, 0x82);
+ return CCID_DRIVER_ERR_ABORTED;
+ }
+#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] & 0x80))
+ { /* This is an I-block. */
+ retries = 0;
+ if (sending)
+ { /* last block sent was successful. */
+ handle->t1_ns ^= 1;
+ sending = 0;
+ }
+
+ if (!!(tpdu[1] & 0x40) != handle->t1_nr)
+ { /* Reponse does not match our sequence number. */
+ msg = send_buffer;
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
+ tpdu[2] = 0;
+ tpdulen = 3;
+ edc = compute_edc (tpdu, tpdulen, 0);
+ tpdu[tpdulen++] = edc;
+
+ continue;
+ }
+
+ handle->t1_nr ^= 1;
+
+ p = tpdu + 3; /* Skip the prologue field. */
+ n = tpdulen - 3 - 1; /* Strip the epilogue field. */
+ /* fixme: verify the checksum. */
+ if (resp)
+ {
+ if (n > maxresplen)
+ {
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)n, (unsigned int)maxresplen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ memcpy (resp, p, n);
+ resp += n;
+ *nresp += n;
+ maxresplen -= n;
+ }
+
+ if (!(tpdu[1] & 0x20))
+ return 0; /* No chaining requested - ready. */
+
+ msg = send_buffer;
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
+ tpdu[2] = 0;
+ tpdulen = 3;
+ edc = compute_edc (tpdu, tpdulen, 0);
+ 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 CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ msg = send_buffer;
+ tpdulen = last_tpdulen;
+ }
+ else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
+ { /* Reponse does not match our sequence number. */
+ DEBUGOUT ("R-block with wrong seqno received on more bit\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else if (sending)
+ { /* Send next chunk. */
+ retries = 0;
+ msg = send_buffer;
+ next_chunk = 1;
+ handle->t1_ns ^= 1;
+ }
+ else
+ {
+ DEBUGOUT ("unexpected ACK R-block received\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ }
+ 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));
+ if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
+ { /* Wait time extension request. */
+ unsigned char bwi = tpdu[3];
+ msg = send_buffer;
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
+ tpdu[2] = 1;
+ tpdu[3] = bwi;
+ tpdulen = 4;
+ edc = compute_edc (tpdu, tpdulen, 0);
+ tpdu[tpdulen++] = edc;
+ DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
+ }
+ else
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ } /* end T=1 protocol loop. */
+
+ return 0;
+}
+
+
+
#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;
- ccid_set_debug_level (1);
+ if (argc)
+ {
+ argc--;
+ argv++;
+ }
- rc = ccid_open_reader (&ccid, 0);
+ 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, argc? *argv:NULL);
if (rc)
return 1;
@@ -1610,34 +1867,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;
}
diff --git a/g10/ccid-driver.h b/g10/ccid-driver.h
index 6b9677833..23562b3b9 100644
--- a/g10/ccid-driver.h
+++ b/g10/ccid-driver.h
@@ -75,9 +75,10 @@
struct ccid_driver_s;
typedef struct ccid_driver_s *ccid_driver_t;
-int ccid_open_reader (ccid_driver_t *handle, int readerno);
-int ccid_close_reader (ccid_driver_t handle);
int ccid_set_debug_level (int level);
+char *ccid_get_reader_list (void);
+int ccid_open_reader (ccid_driver_t *handle, const char *readerid);
+int ccid_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);
diff --git a/g10/g10.c b/g10/g10.c
index 367aa4ee8..3fd6c9aad 100644
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -1338,6 +1338,21 @@ list_config(char *items)
any=1;
}
+ if(show_all || ascii_strcasecmp(name,"ccid-reader-id")==0)
+ {
+#if defined(ENABLE_CARD_SUPPORT) && defined(HAVE_LIBUSB)
+ char *p, *p2, *list = ccid_get_reader_list ();
+
+ for (p=list; p && (p2 = strchr (p, '\n')); p = p2+1)
+ {
+ *p2 = 0;
+ printf("cfg:ccid-reader-id:%s\n", p);
+ }
+ free (list);
+#endif
+ any=1;
+ }
+
if(show_all)
break;
@@ -1364,6 +1379,7 @@ gpgconf_list (const char *configfile)
printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE);
printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE);
printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE);
}