From: Harald Welte Date: Sat, 27 May 2006 14:20:29 +0000 (+0000) Subject: move ccid driver to correct directory X-Git-Url: http://git.rot13.org/?a=commitdiff_plain;h=083e5aaf5dd60247cf918082837d6b529183ce0b;p=librfid move ccid driver to correct directory git-svn-id: https://svn.gnumonks.org/trunk/librfid@1795 e0336214-984f-0b4b-a45f-81c69e1f0ede --- diff --git a/ccid/ccid-driver.c b/ccid/ccid-driver.c deleted file mode 100644 index edca5ca..0000000 --- a/ccid/ccid-driver.c +++ /dev/null @@ -1,2448 +0,0 @@ -/* ccid-driver.c - USB ChipCardInterfaceDevices driver - * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. - * Written by Werner Koch. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * - * ALTERNATIVELY, this file may be distributed under the terms of the - * following license, in which case the provisions of this license are - * required INSTEAD OF the GNU General Public License. If you wish to - * allow use of your version of this file only under the terms of the - * GNU General Public License, and not to allow others to use your - * version of this file under the terms of the following license, - * indicate your decision by deleting this paragraph and the license - * below. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Date: 2005-09-09 13:18:08 +0200 (Fri, 09 Sep 2005) $ - */ - - -/* 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 and SPR532 - smartcard readers and requires that a reader implements the TPDU - level exchange and does fully automatic initialization. -*/ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#if defined(HAVE_LIBUSB) || defined(TEST) - -#include -#include -#include -#include -#include - -#include - -#include "ccid-driver.h" - -#define DRVNAME "ccid-driver: " - - -/* Depending on how this source is used we either define our error - output to go to stderr or to the jnlib based logging functions. We - use the latter when GNUPG_MAJOR_VERSION is defines or when both, - GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined. -*/ -#if defined(GNUPG_MAJOR_VERSION) \ - || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING)) - -#if defined(GNUPG_SCD_MAIN_HEADER) -# include GNUPG_SCD_MAIN_HEADER -#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */ -# include "options.h" -# include "util.h" -# include "memory.h" -# include "cardglue.h" -# else /* This is the modularized GnuPG 1.9 or later. */ -# include "scdaemon.h" -#endif - - -# define DEBUGOUT(t) do { if (debug_level) \ - log_debug (DRVNAME t); } while (0) -# define DEBUGOUT_1(t,a) do { if (debug_level) \ - log_debug (DRVNAME t,(a)); } while (0) -# 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 (debug_level) \ - log_debug (DRVNAME t,(a),(b),(c));} while (0) -# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ - log_debug (DRVNAME t,(a),(b),(c),(d));} while (0) -# define DEBUGOUT_CONT(t) do { if (debug_level) \ - log_printf (t); } while (0) -# 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 (debug_level) \ - log_printf (t,(a),(b)); } while (0) -# 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 (debug_level) \ - log_printf ("\n"); } while (0) - -#else /* Other usage of this source - don't use gnupg specifics. */ - -# 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_4(t,a,b,c,d) do { if (debug_level) \ - fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} 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. */ - - - -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 -}; - - -/* Two macro to detect whether a CCID command has failed and to get - the error code. These macros assume that we can access the - mandatory first 10 bytes of a CCID message in BUF. */ -#define CCID_COMMAND_FAILED(buf) ((buf)[7] & 0x40) -#define CCID_ERROR_CODE(buf) (((unsigned char *)(buf))[8]) - - -/* We need to know the vendor to do some hacks. */ -enum { - VENDOR_SCM = 0x04e6, - VENDOR_CHERRY = 0x046a, - VENDOR_OMNIKEY= 0x076b, - VENDOR_GEMPC = 0x08e6 -}; - - -/* 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; - char *rid; - unsigned short id_vendor; - unsigned short id_product; - unsigned short bcd_device; - int ifc_no; - int ep_bulk_out; - int ep_bulk_in; - int ep_intr; - int seqno; - unsigned char t1_ns; - unsigned char t1_nr; - int nonnull_nad; - int auto_ifsd; - int max_ifsd; - int ifsd; - int powered_off; - int has_pinpad; - int apdu_level; /* Reader supports short APDU level exchange. */ -}; - - -static int initialized_usb; /* Tracks whether USB has been initialized. */ -static int debug_level; /* Flag to control the debug output. - 0 = No debugging - 1 = USB I/O info - 2 = T=1 protocol tracing - */ - - -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, int timeout, - int no_debug); - -/* Convert a little endian stored 4 byte value into an unsigned - integer. */ -static unsigned int -convert_le_u32 (const unsigned char *buf) -{ - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); -} - -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; -} - - -/* Pint an error message for a failed CCID command including a textual - error code. MSG is shall be the CCID message of at least 10 bytes. */ -static void -print_command_failed (const unsigned char *msg) -{ - const char *t; - char buffer[100]; - int ec; - - if (!debug_level) - return; - - ec = CCID_ERROR_CODE (msg); - switch (ec) - { - case 0x00: t = "Command not supported"; break; - - case 0xE0: t = "Slot busy"; break; - case 0xEF: t = "PIN cancelled"; break; - case 0xF0: t = "PIN timeout"; break; - - case 0xF2: t = "Automatic sequence ongoing"; break; - case 0xF3: t = "Deactivated Protocol"; break; - case 0xF4: t = "Procedure byte conflict"; break; - case 0xF5: t = "ICC class not supported"; break; - case 0xF6: t = "ICC protocol not supported"; break; - case 0xF7: t = "Bad checksum in ATR"; break; - case 0xF8: t = "Bad TS in ATR"; break; - - case 0xFB: t = "An all inclusive hardware error occurred"; break; - case 0xFC: t = "Overrun error while talking to the ICC"; break; - case 0xFD: t = "Parity error while talking to the ICC"; break; - case 0xFE: t = "CCID timed out while talking to the ICC"; break; - case 0xFF: t = "Host aborted the current activity"; break; - - default: - if (ec > 0 && ec < 128) - sprintf (buffer, "Parameter error at offset %d", ec); - else - sprintf (buffer, "Error code %02X", ec); - t = buffer; - break; - } - DEBUGOUT_1 ("CCID command failed: %s\n", t); -} - - - - -/* Parse a CCID descriptor, optionally print all available features - and test whether this reader is usable by this driver. Returns 0 - if it is usable. - - Note, that this code is based on the one in lsusb.c of the - usb-utils package, I wrote on 2003-09-01. -wk. */ -static int -parse_ccid_descriptor (ccid_driver_t handle, - const unsigned char *buf, size_t buflen) -{ - unsigned int i; - unsigned int us; - int have_t1 = 0, have_tpdu=0, have_auto_conf = 0; - - - handle->nonnull_nad = 0; - handle->auto_ifsd = 0; - handle->max_ifsd = 32; - handle->ifsd = 0; - handle->has_pinpad = 0; - handle->apdu_level = 0; - DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n", - handle->id_vendor, handle->id_product, handle->bcd_device); - if (buflen < 54 || buf[0] < 54) - { - DEBUGOUT ("CCID device descriptor is too short\n"); - return -1; - } - - DEBUGOUT ("ChipCard Interface Descriptor:\n"); - DEBUGOUT_1 (" bLength %5u\n", buf[0]); - DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]); - DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]); - if (buf[3] != 1 || buf[2] != 0) - DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)"); - DEBUGOUT_LF (); - - DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]); - DEBUGOUT_2 (" bVoltageSupport %5u %s\n", - buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V" - : buf[5] == 3? "1.8V":"?")); - - us = convert_le_u32 (buf+6); - DEBUGOUT_1 (" dwProtocols %5u ", us); - if ((us & 1)) - DEBUGOUT_CONT (" T=0"); - if ((us & 2)) - { - DEBUGOUT_CONT (" T=1"); - have_t1 = 1; - } - if ((us & ~3)) - DEBUGOUT_CONT (" (Invalid values detected)"); - DEBUGOUT_LF (); - - us = convert_le_u32(buf+10); - DEBUGOUT_1 (" dwDefaultClock %5u\n", us); - us = convert_le_u32(buf+14); - DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us); - DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]); - us = convert_le_u32(buf+19); - DEBUGOUT_1 (" dwDataRate %7u bps\n", us); - us = convert_le_u32(buf+23); - DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us); - DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]); - - us = convert_le_u32(buf+28); - DEBUGOUT_1 (" dwMaxIFSD %5u\n", us); - handle->max_ifsd = us; - - us = convert_le_u32(buf+32); - DEBUGOUT_1 (" dwSyncProtocols %08X ", us); - if ((us&1)) - DEBUGOUT_CONT ( " 2-wire"); - if ((us&2)) - DEBUGOUT_CONT ( " 3-wire"); - if ((us&4)) - DEBUGOUT_CONT ( " I2C"); - DEBUGOUT_LF (); - - us = convert_le_u32(buf+36); - DEBUGOUT_1 (" dwMechanical %08X ", us); - if ((us & 1)) - DEBUGOUT_CONT (" accept"); - if ((us & 2)) - DEBUGOUT_CONT (" eject"); - if ((us & 4)) - DEBUGOUT_CONT (" capture"); - if ((us & 8)) - DEBUGOUT_CONT (" lock"); - DEBUGOUT_LF (); - - us = convert_le_u32(buf+40); - DEBUGOUT_1 (" dwFeatures %08X\n", us); - if ((us & 0x0002)) - { - DEBUGOUT (" Auto configuration based on ATR\n"); - have_auto_conf = 1; - } - if ((us & 0x0004)) - DEBUGOUT (" Auto activation on insert\n"); - if ((us & 0x0008)) - DEBUGOUT (" Auto voltage selection\n"); - if ((us & 0x0010)) - DEBUGOUT (" Auto clock change\n"); - if ((us & 0x0020)) - DEBUGOUT (" Auto baud rate change\n"); - if ((us & 0x0040)) - DEBUGOUT (" Auto parameter negotation made by CCID\n"); - else if ((us & 0x0080)) - DEBUGOUT (" Auto PPS made by CCID\n"); - else if ((us & (0x0040 | 0x0080))) - DEBUGOUT (" WARNING: conflicting negotation features\n"); - - if ((us & 0x0100)) - DEBUGOUT (" CCID can set ICC in clock stop mode\n"); - if ((us & 0x0200)) - { - DEBUGOUT (" NAD value other than 0x00 accepted\n"); - handle->nonnull_nad = 1; - } - if ((us & 0x0400)) - { - DEBUGOUT (" Auto IFSD exchange\n"); - handle->auto_ifsd = 1; - } - - if ((us & 0x00010000)) - { - DEBUGOUT (" TPDU level exchange\n"); - have_tpdu = 1; - } - else if ((us & 0x00020000)) - { - DEBUGOUT (" Short APDU level exchange\n"); - handle->apdu_level = 1; - } - else if ((us & 0x00040000)) - { - DEBUGOUT (" Short and extended APDU level exchange\n"); - handle->apdu_level = 1; - } - else if ((us & 0x00070000)) - DEBUGOUT (" WARNING: conflicting exchange levels\n"); - - us = convert_le_u32(buf+44); - DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us); - - DEBUGOUT ( " bClassGetResponse "); - if (buf[48] == 0xff) - DEBUGOUT_CONT ("echo\n"); - else - DEBUGOUT_CONT_1 (" %02X\n", buf[48]); - - DEBUGOUT ( " bClassEnvelope "); - if (buf[49] == 0xff) - DEBUGOUT_CONT ("echo\n"); - else - DEBUGOUT_CONT_1 (" %02X\n", buf[48]); - - DEBUGOUT ( " wlcdLayout "); - if (!buf[50] && !buf[51]) - DEBUGOUT_CONT ("none\n"); - else - DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]); - - DEBUGOUT_1 (" bPINSupport %5u ", buf[52]); - if ((buf[52] & 1)) - { - DEBUGOUT_CONT ( " verification"); - handle->has_pinpad |= 1; - } - if ((buf[52] & 2)) - { - DEBUGOUT_CONT ( " modification"); - handle->has_pinpad |= 2; - } - DEBUGOUT_LF (); - - DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]); - - if (buf[0] > 54) { - DEBUGOUT (" junk "); - for (i=54; i < buf[0]-54; i++) - DEBUGOUT_CONT_1 (" %02X", buf[i]); - DEBUGOUT_LF (); - } - - if (!have_t1 || !(have_tpdu || handle->apdu_level) || !have_auto_conf) - { - DEBUGOUT ("this drivers requires that the reader supports T=1, " - "TPDU or APDU level exchange and auto configuration - " - "this is not available\n"); - return -1; - } - - - /* SCM drivers get stuck in their internal USB stack if they try to - send a frame of n*wMaxPacketSize back to us. Given that - wMaxPacketSize is 64 for these readers we set the IFSD to a value - lower than that: - 64 - 10 CCID header - 4 T1frame - 2 reserved = 48 - Product Ids: - 0xe001 - SCR 331 - 0x5111 - SCR 331-DI - 0x5115 - SCR 335 - 0xe003 - SPR 532 - */ - if (handle->id_vendor == VENDOR_SCM - && handle->max_ifsd > 48 - && ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516) - ||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620) - ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0514) - ||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504) - )) - { - DEBUGOUT ("enabling workaround for buggy SCM readers\n"); - handle->max_ifsd = 48; - } - - - return 0; -} - - -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 is 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, - (char*)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, - (char*)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) -{ - 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) - { - rid = malloc (strlen (prefix) + 3 + 1); - if (!rid) - return NULL; - strcpy (rid, prefix); - strcat (rid, "X:0"); - } - return rid; -} - - -/* Helper to find the endpoint from an interface descriptor. */ -static int -find_endpoint (struct usb_interface_descriptor *ifcdesc, int mode) -{ - int no; - int want_bulk_in = 0; - - if (mode == 1) - want_bulk_in = 0x80; - for (no=0; no < ifcdesc->bNumEndpoints; no++) - { - struct usb_endpoint_descriptor *ep = ifcdesc->endpoint + no; - if (ep->bDescriptorType != USB_DT_ENDPOINT) - ; - else if (mode == 2 - && ((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) - == USB_ENDPOINT_TYPE_INTERRUPT) - && (ep->bEndpointAddress & 0x80)) - return (ep->bEndpointAddress & 0x0f); - else if (((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) - == USB_ENDPOINT_TYPE_BULK) - && (ep->bEndpointAddress & 0x80) == want_bulk_in) - return (ep->bEndpointAddress & 0x0f); - } - /* Should never happen. */ - return mode == 2? 0x83 : mode == 1? 0x82 :1; -} - - - -/* 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, - int *interface_number, - int *ep_bulk_out, int *ep_bulk_in, int *ep_intr) -{ - 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; - if (interface_number) - *interface_number = 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 - busses = usb_busses; -#endif - - for (bus = busses; bus; bus = bus->next) - { - for (dev = bus->devices; dev; dev = dev->next) - { - int cfg_no; - - for (cfg_no=0; cfg_no < dev->descriptor.bNumConfigurations; cfg_no++) - { - 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++) - { - 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++) - { - 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))) - { - 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 (interface_number) - *interface_number = (ifcdesc-> - bInterfaceNumber); - if (ep_bulk_out) - *ep_bulk_out = find_endpoint (ifcdesc, 0); - if (ep_bulk_in) - *ep_bulk_in = find_endpoint (ifcdesc, 1); - if (ep_intr) - *ep_intr = find_endpoint (ifcdesc, 2); - - - 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: - ; - } - } - - 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, 2 enables additional tracing of the T=1 - protocol, 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, NULL, - 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, const char *readerid) -{ - 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; - int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; - - *handle = NULL; - - if (!initialized_usb) - { - usb_init (); - initialized_usb = 1; - } - - /* 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) - { - 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, readerid, &rid, &dev, - &ifcdesc_extra, &ifcdesc_extra_len, - &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr); - if (!idev) - { - 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; - } - - /* 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; - (*handle)->id_vendor = dev->descriptor.idVendor; - (*handle)->id_product = dev->descriptor.idProduct; - (*handle)->bcd_device = dev->descriptor.bcdDevice; - (*handle)->ifc_no = ifc_no; - (*handle)->ep_bulk_out = ep_bulk_out; - (*handle)->ep_bulk_in = ep_bulk_in; - (*handle)->ep_intr = ep_intr; - - DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid ); - - - if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len)) - { - DEBUGOUT ("device not supported\n"); - rc = CCID_DRIVER_ERR_NO_READER; - goto leave; - } - - rc = usb_claim_interface (idev, ifc_no); - if (rc) - { - DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); - rc = CCID_DRIVER_ERR_CARD_IO_ERROR; - goto leave; - } - - leave: - free (ifcdesc_extra); - if (rc) - { - free (rid); - if (idev) - usb_close (idev); - free (*handle); - *handle = NULL; - } - - return rc; -} - - -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, 2000, 0); - handle->powered_off = 1; - } - if (handle->idev) - { - usb_release_interface (handle->idev, handle->ifc_no); - usb_close (handle->idev); - handle->idev = NULL; - } -} - - -/* 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. - - 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_device *dev = NULL; - usb_dev_handle *idev = NULL; - unsigned char *ifcdesc_extra = NULL; - size_t ifcdesc_extra_len; - int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; - - if (!handle || !handle->rid) - return CCID_DRIVER_ERR_INV_VALUE; - - do_close_reader (handle); - - idev = scan_or_find_devices (-1, handle->rid, NULL, &dev, - &ifcdesc_extra, &ifcdesc_extra_len, - &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr); - if (!idev) - { - DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid); - return CCID_DRIVER_ERR_NO_READER; - } - - - handle->idev = idev; - handle->ifc_no = ifc_no; - handle->ep_bulk_out = ep_bulk_out; - handle->ep_bulk_in = ep_bulk_in; - handle->ep_intr = ep_intr; - - if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len)) - { - DEBUGOUT ("device not supported\n"); - rc = CCID_DRIVER_ERR_NO_READER; - goto leave; - } - - rc = usb_claim_interface (idev, ifc_no); - if (rc) - { - DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); - rc = CCID_DRIVER_ERR_CARD_IO_ERROR; - goto leave; - } - - leave: - free (ifcdesc_extra); - if (rc) - { - usb_close (handle->idev); - handle->idev = NULL; - } - - return rc; - -} - - -/* Close the reader HANDLE. */ -int -ccid_close_reader (ccid_driver_t handle) -{ - if (!handle || !handle->idev) - return 0; - - do_close_reader (handle); - free (handle->rid); - free (handle); - return 0; -} - - -/* Return False if a card is present and powered. */ -int -ccid_check_card_presence (ccid_driver_t handle) -{ - - return -1; -} - - -/* 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, - handle->ep_bulk_out, - (char*)msg, msglen, - 1000 /* ms timeout */); - if (rc == msglen) - return 0; - - if (rc == -1) - DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); - else - DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc); - return CCID_DRIVER_ERR_CARD_IO_ERROR; -} - - -/* Read a maximum of LENGTH bytes from the bulk in endpoint into - BUFFER and return the actual read number if bytes in NREAD. SEQNO - is the sequence number used to send the request and EXPECTED_TYPE - the type of message we expect. Does checks on the ccid - header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to - avoid debug messages in case of no error. Returns 0 on success. */ -static int -bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, - size_t *nread, int expected_type, int seqno, int timeout, - int no_debug) -{ - 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, - handle->ep_bulk_in, - (char*)buffer, length, - timeout); - if (rc < 0) - { - DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno)); - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - - *nread = msglen = rc; - - if (msglen < 10) - { - DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen); - return CCID_DRIVER_ERR_INV_VALUE; - } - if (buffer[0] != expected_type) - { - DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]); - return CCID_DRIVER_ERR_INV_VALUE; - } - if (buffer[5] != 0) - { - DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]); - return CCID_DRIVER_ERR_INV_VALUE; - } - if (buffer[6] != seqno) - { - DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n", - seqno, buffer[6]); - return CCID_DRIVER_ERR_INV_VALUE; - } - - if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80) - { - /* Card present and active, time extension requested. */ - DEBUGOUT_2 ("time extension requested (%02X,%02X)\n", - buffer[7], buffer[8]); - goto retry; - } - - if (!no_debug) - { - DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n" - " data:", buffer[7], buffer[8], buffer[9] ); - for (i=10; i < msglen; i++) - DEBUGOUT_CONT_1 (" %02X", buffer[i]); - DEBUGOUT_LF (); - } - if (CCID_COMMAND_FAILED (buffer)) - print_command_failed (buffer); - - /* Check whether a card is at all available. Note: If you add new - error codes here, check whether they need to be ignored in - send_escape_cmd. */ - switch ((buffer[7] & 0x03)) - { - case 0: /* no error */ break; - case 1: return CCID_DRIVER_ERR_CARD_INACTIVE; - case 2: return CCID_DRIVER_ERR_NO_CARD; - case 3: /* RFU */ break; - } - return 0; -} - - -/* Note that this function won't return the error codes NO_CARD or - CARD_INACTIVE. IF RESULT is not NULL, the result from the - operation will get returned in RESULT and its length in RESULTLEN. - If the response is larger than RESULTMAX, an error is returned and - the required buffer length returned in RESULTLEN. */ -static int -send_escape_cmd (ccid_driver_t handle, - const unsigned char *data, size_t datalen, - unsigned char *result, size_t resultmax, size_t *resultlen) -{ - int i, rc; - unsigned char msg[100]; - size_t msglen; - unsigned char seqno; - - if (resultlen) - *resultlen = 0; - - if (datalen > sizeof msg - 10) - return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */ - - msg[0] = PC_to_RDR_Escape; - msg[5] = 0; /* slot */ - msg[6] = seqno = handle->seqno++; - msg[7] = 0; /* RFU */ - msg[8] = 0; /* RFU */ - msg[9] = 0; /* RFU */ - memcpy (msg+10, data, datalen); - msglen = 10 + datalen; - set_msg_len (msg, datalen); - - DEBUGOUT ("sending"); - for (i=0; i < msglen; i++) - DEBUGOUT_CONT_1 (" %02X", msg[i]); - DEBUGOUT_LF (); - rc = bulk_out (handle, msg, msglen); - if (rc) - return rc; - rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape, - seqno, 5000, 0); - if (result) - switch (rc) - { - /* We need to ignore certain errorcode here. */ - case 0: - case CCID_DRIVER_ERR_CARD_INACTIVE: - case CCID_DRIVER_ERR_NO_CARD: - { - if (msglen < 10 || (msglen-10) > resultmax ) - rc = CCID_DRIVER_ERR_INV_VALUE; /* Invalid length of response. */ - else - { - memcpy (result, msg+10, msglen-10); - *resultlen = msglen-10; - } - rc = 0; - } - break; - default: - break; - } - - return rc; -} - - -int -ccid_transceive_escape (ccid_driver_t handle, - const unsigned char *data, size_t datalen, - unsigned char *resp, size_t maxresplen, size_t *nresp) -{ - return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp); -} - - - -/* 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, - handle->ep_intr, - (char*)msg, sizeof msg, - 0 /* ms timeout */ ); - if (rc < 0 && errno == ETIMEDOUT) - return 0; - - if (rc < 0) - { - DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno)); - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - - msglen = rc; - rc = 0; - - if (msglen < 1) - { - DEBUGOUT ("intr-in msg too short\n"); - return CCID_DRIVER_ERR_INV_VALUE; - } - - if (msg[0] == RDR_to_PC_NotifySlotChange) - { - DEBUGOUT ("notify slot change:"); - for (i=1; i < msglen; i++) - for (j=0; j < 4; j++) - DEBUGOUT_CONT_3 (" %d:%c%c", - (i-1)*4+j, - (msg[i] & (1<<(j*2)))? 'p':'-', - (msg[i] & (2<<(j*2)))? '*':' '); - DEBUGOUT_LF (); - } - else if (msg[0] == RDR_to_PC_HardwareError) - { - DEBUGOUT ("hardware error occured\n"); - } - else - { - DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]); - } - - return 0; -} - - -/* Note that this fucntion won't return the error codes NO_CARD or - CARD_INACTIVE */ -int -ccid_slot_status (ccid_driver_t handle, int *statusbits) -{ - int rc; - unsigned char msg[100]; - size_t msglen; - unsigned char seqno; - int retries = 0; - - retry: - 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; - /* Note that we set the NO_DEBUG flag here, so that the logs won't - get cluttered up by a ticker function checking for the slot - status and debugging enabled. */ - rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, - seqno, retries? 1000 : 200, 1); - if (rc == CCID_DRIVER_ERR_CARD_IO_ERROR && retries < 3) - { - if (!retries) - { - DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n"); - usb_clear_halt (handle->idev, handle->ep_bulk_in); - usb_clear_halt (handle->idev, handle->ep_bulk_out); - } - else - DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n"); - retries++; - goto retry; - } - if (rc && rc != CCID_DRIVER_ERR_NO_CARD - && rc != CCID_DRIVER_ERR_CARD_INACTIVE) - return rc; - *statusbits = (msg[7] & 3); - - return 0; -} - - -int -ccid_get_atr (ccid_driver_t handle, - unsigned char *atr, size_t maxatrlen, size_t *atrlen) -{ - int rc; - int statusbits; - unsigned char msg[100]; - unsigned char *tpdu; - size_t msglen, tpdulen; - unsigned char seqno; - int use_crc = 0; - unsigned int edc; - int i; - int tried_iso = 0; - - /* First check whether a card is available. */ - rc = ccid_slot_status (handle, &statusbits); - if (rc) - return rc; - if (statusbits == 2) - return CCID_DRIVER_ERR_NO_CARD; - - /* For an inactive and also for an active card, issue the PowerOn - command to get the ATR. */ - again: - 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, RDR_to_PC_DataBlock, - seqno, 5000, 0); - if (rc) - return rc; - if (!tried_iso && CCID_COMMAND_FAILED (msg) && CCID_ERROR_CODE (msg) == 0xbb - && ((handle->id_vendor == VENDOR_CHERRY - && handle->id_product == 0x0005) - || (handle->id_vendor == VENDOR_GEMPC - && handle->id_product == 0x4433) - )) - { - tried_iso = 1; - /* Try switching to ISO mode. */ - if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2, - NULL, 0, NULL)) - goto again; - } - else if (CCID_COMMAND_FAILED (msg)) - return CCID_DRIVER_ERR_CARD_IO_ERROR; - - - handle->powered_off = 0; - - if (atr) - { - size_t n = msglen - 10; - - if (n > maxatrlen) - n = maxatrlen; - memcpy (atr, msg+10, n); - *atrlen = n; - } - - /* Setup parameters to select T=1. */ - msg[0] = PC_to_RDR_SetParameters; - msg[5] = 0; /* slot */ - msg[6] = seqno = handle->seqno++; - msg[7] = 1; /* Select T=1. */ - msg[8] = 0; /* RFU */ - msg[9] = 0; /* RFU */ - - /* FIXME: Get those values from the ATR. */ - msg[10]= 0x01; /* Fi/Di */ - msg[11]= 0x10; /* LRC, direct convention. */ - msg[12]= 0; /* Extra guardtime. */ - msg[13]= 0x41; /* BWI/CWI */ - msg[14]= 0; /* No clock stoppping. */ - msg[15]= 254; /* IFSC */ - msg[16]= 0; /* Does not support non default NAD values. */ - set_msg_len (msg, 7); - msglen = 10 + 7; - - DEBUGOUT ("sending"); - for (i=0; i < msglen; i++) - DEBUGOUT_CONT_1 (" %02X", msg[i]); - DEBUGOUT_LF (); - - rc = bulk_out (handle, msg, msglen); - if (rc) - return rc; - /* Note that we ignore the error code on purpose. */ - bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, - seqno, 5000, 0); - - handle->t1_ns = 0; - handle->t1_nr = 0; - - /* Send an S-Block with our maximun IFSD to the CCID. */ - if (!handle->auto_ifsd) - { - tpdu = msg+10; - /* NAD: DAD=1, SAD=0 */ - tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; - tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */ - tpdu[2] = 1; - tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32; - tpdulen = 4; - edc = compute_edc (tpdu, tpdulen, use_crc); - if (use_crc) - tpdu[tpdulen++] = (edc >> 8); - tpdu[tpdulen++] = edc; - - msg[0] = PC_to_RDR_XfrBlock; - msg[5] = 0; /* slot */ - msg[6] = seqno = handle->seqno++; - msg[7] = 0; - msg[8] = 0; /* RFU */ - msg[9] = 0; /* RFU */ - set_msg_len (msg, tpdulen); - msglen = 10 + tpdulen; - - DEBUGOUT ("sending"); - for (i=0; i < msglen; i++) - DEBUGOUT_CONT_1 (" %02X", msg[i]); - DEBUGOUT_LF (); - - if (debug_level > 1) - DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) - : !!(msg[11] & 0x40)), - (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); - - rc = bulk_out (handle, msg, msglen); - if (rc) - return rc; - - - rc = bulk_in (handle, msg, sizeof msg, &msglen, - RDR_to_PC_DataBlock, seqno, 5000, 0); - if (rc) - return rc; - - tpdu = msg + 10; - tpdulen = msglen - 10; - - if (tpdulen < 4) - return CCID_DRIVER_ERR_ABORTED; - - if (debug_level > 1) - DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\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, - (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); - - if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1) - { - DEBUGOUT ("invalid response for S-block (Change-IFSD)\n"); - return -1; - } - DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]); - } - - return 0; -} - - - - -static unsigned int -compute_edc (const unsigned char *data, size_t datalen, int use_crc) -{ - if (use_crc) - { - return 0x42; /* Not yet implemented. */ - } - else - { - unsigned char crc = 0; - - for (; datalen; datalen--) - crc ^= *data++; - return crc; - } -} - - -/* Helper for ccid_transceive used for APDU level exchanges. */ -static int -ccid_transceive_apdu_level (ccid_driver_t handle, - const unsigned char *apdu_buf, size_t apdu_buflen, - unsigned char *resp, size_t maxresplen, - size_t *nresp) -{ - int rc; - unsigned char send_buffer[10+259], recv_buffer[10+259]; - const unsigned char *apdu; - size_t apdulen; - unsigned char *msg; - size_t msglen; - unsigned char seqno; - int i; - - msg = send_buffer; - - apdu = apdu_buf; - apdulen = apdu_buflen; - assert (apdulen); - - if (apdulen > 254) - return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ - - 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 */ - memcpy (msg+10, apdu, apdulen); - set_msg_len (msg, apdulen); - msglen = 10 + apdulen; - - DEBUGOUT ("sending"); - for (i=0; i < msglen; i++) - DEBUGOUT_CONT_1 (" %02X", msg[i]); - DEBUGOUT_LF (); - - rc = bulk_out (handle, msg, msglen); - if (rc) - return rc; - - msg = recv_buffer; - rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, - RDR_to_PC_DataBlock, seqno, 5000, 0); - if (rc) - return rc; - - apdu = msg + 10; - apdulen = msglen - 10; - - if (resp) - { - if (apdulen > maxresplen) - { - DEBUGOUT_2 ("provided buffer too short for received data " - "(%u/%u)\n", - (unsigned int)apdulen, (unsigned int)maxresplen); - return CCID_DRIVER_ERR_INV_VALUE; - } - - memcpy (resp, apdu, apdulen); - *nresp = apdulen; - } - - return 0; -} - - - -/* - 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 nodes. - - 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_buf, size_t apdu_buflen, - unsigned char *resp, size_t maxresplen, size_t *nresp) -{ - int rc; - unsigned char send_buffer[10+259], recv_buffer[10+259]; - const unsigned char *apdu; - size_t apdulen; - unsigned char *msg, *tpdu, *p; - size_t msglen, tpdulen, last_tpdulen, n; - unsigned char seqno; - int i; - unsigned int edc; - int use_crc = 0; - size_t dummy_nresp; - int next_chunk = 1; - int sending = 1; - int retries = 0; - - if (!nresp) - nresp = &dummy_nresp; - *nresp = 0; - - /* Smarter readers allow to send APDUs directly; divert here. */ - if (handle->apdu_level) - return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen, - resp, maxresplen, nresp); - - /* The other readers we support require sending TPDUs. */ - - tpdulen = 0; /* Avoid compiler warning about no initialization. */ - msg = send_buffer; - for (;;) - { - if (next_chunk) - { - next_chunk = 0; - - apdu = apdu_buf; - apdulen = apdu_buflen; - assert (apdulen); - - /* Construct an I-Block. */ - if (apdulen > 254) - return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ - - tpdu = msg+10; - /* NAD: DAD=1, SAD=0 */ - tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; - tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ - if (apdulen > 128 /* fixme: replace by ifsc */) - { - apdulen = 128; - apdu_buf += 128; - apdu_buflen -= 128; - tpdu[1] |= (1 << 5); /* Set more bit. */ - } - tpdu[2] = apdulen; - memcpy (tpdu+3, apdu, apdulen); - tpdulen = 3 + apdulen; - edc = compute_edc (tpdu, tpdulen, use_crc); - if (use_crc) - tpdu[tpdulen++] = (edc >> 8); - tpdu[tpdulen++] = edc; - } - - msg[0] = PC_to_RDR_XfrBlock; - 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, tpdulen); - msglen = 10 + tpdulen; - last_tpdulen = tpdulen; - - DEBUGOUT ("sending"); - for (i=0; i < msglen; i++) - DEBUGOUT_CONT_1 (" %02X", msg[i]); - DEBUGOUT_LF (); - - if (debug_level > 1) - DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) - : !!(msg[11] & 0x40)), - (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); - - 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, 5000, 0); - if (rc) - return rc; - - tpdu = msg + 10; - tpdulen = msglen - 10; - - if (tpdulen < 4) - { - usb_clear_halt (handle->idev, handle->ep_bulk_in); - return CCID_DRIVER_ERR_ABORTED; - } - - if (debug_level > 1) - DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\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, - (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); - - 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, use_crc); - if (use_crc) - tpdu[tpdulen++] = (edc >> 8); - 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, use_crc); - if (use_crc) - tpdu[tpdulen++] = (edc >> 8); - tpdu[tpdulen++] = edc; - } - else if ((tpdu[1] & 0xc0) == 0x80) - { /* This is a R-block. */ - if ( (tpdu[1] & 0x0f)) - { /* Error: repeat last block */ - if (++retries > 3) - { - DEBUGOUT ("3 failed retries\n"); - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - msg = send_buffer; - tpdulen = last_tpdulen; - } - else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns) - { /* Response 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 ("T=1 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, use_crc); - if (use_crc) - tpdu[tpdulen++] = (edc >> 8); - tpdu[tpdulen++] = edc; - DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi); - } - else - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - } /* end T=1 protocol loop. */ - - return 0; -} - - -/* Send the CCID Secure command to the reader. APDU_BUF should - contain the APDU template. PIN_MODE defines how the pin gets - formatted: - - 1 := The PIN is ASCII encoded and of variable length. The - length of the PIN entered will be put into Lc by the reader. - The APDU should me made up of 4 bytes without Lc. - - PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0 - may be used t enable reasonable defaults. PIN_PADLEN should be 0. - - When called with RESP and NRESP set to NULL, the function will - merely check whether the reader supports the secure command for the - given APDU and PIN_MODE. */ -int -ccid_transceive_secure (ccid_driver_t handle, - const unsigned char *apdu_buf, size_t apdu_buflen, - int pin_mode, int pinlen_min, int pinlen_max, - int pin_padlen, - 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, n; - unsigned char seqno; - int i; - size_t dummy_nresp; - int testmode; - - testmode = !resp && !nresp; - - if (!nresp) - nresp = &dummy_nresp; - *nresp = 0; - - if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1)) - ; - else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2)) - return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */ - else - return CCID_DRIVER_ERR_NO_KEYPAD; - - if (pin_mode != 1) - return CCID_DRIVER_ERR_NOT_SUPPORTED; - - if (pin_padlen != 0) - return CCID_DRIVER_ERR_NOT_SUPPORTED; - - if (!pinlen_min) - pinlen_min = 1; - if (!pinlen_max) - pinlen_max = 25; - - /* Note that the 25 is the maximum value the SPR532 allows. */ - if (pinlen_min < 1 || pinlen_min > 25 - || pinlen_max < 1 || pinlen_max > 25 - || pinlen_min > pinlen_max) - return CCID_DRIVER_ERR_INV_VALUE; - - /* We have only tested this with an SCM reader so better don't risk - anything and do not allow the use with other readers. */ - if (handle->id_vendor != VENDOR_SCM) - return CCID_DRIVER_ERR_NOT_SUPPORTED; - - if (testmode) - return 0; /* Success */ - - msg = send_buffer; - if (handle->id_vendor == VENDOR_SCM) - { - DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n"); - rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3, - NULL, 0, NULL); - if (rc) - return rc; - } - - 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] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */ - if (handle->id_vendor == VENDOR_SCM) - { - /* For the SPR532 the next 2 bytes need to be zero. We do this - for all SCM product. Kudos to Martin Paljak for this - hint. */ - msg[13] = msg[14] = 0; - } - else - { - msg[13] = 0x00; /* bmPINBlockString: - 0 bits of pin length to insert. - 0 bytes of PIN block size. */ - msg[14] = 0x00; /* bmPINLengthFormat: - Units are bytes, position is 0. */ - } - msg[15] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */ - msg[16] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */ - msg[17] = 0x02; /* bEntryValidationCondition: - Validation key pressed */ - if (pinlen_min && pinlen_max && pinlen_min == pinlen_max) - msg[17] |= 0x01; /* Max size reached. */ - 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] = apdu_buf[0]; /* CLA */ - msg[26] = apdu_buf[1]; /* INS */ - msg[27] = apdu_buf[2]; /* P1 */ - msg[28] = apdu_buf[3]; /* P2 */ - msglen = 29; - set_msg_len (msg, msglen - 10); - - DEBUGOUT ("sending"); - for (i=0; i < msglen; i++) - DEBUGOUT_CONT_1 (" %02X", msg[i]); - DEBUGOUT_LF (); - - rc = bulk_out (handle, msg, msglen); - if (rc) - return rc; - - msg = recv_buffer; - rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, - RDR_to_PC_DataBlock, seqno, 5000, 0); - if (rc) - return rc; - - tpdu = msg + 10; - tpdulen = msglen - 10; - - if (tpdulen < 4) - { - usb_clear_halt (handle->idev, handle->ep_bulk_in); - return CCID_DRIVER_ERR_ABORTED; - } - if (debug_level > 1) - DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\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, - (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); - - if (!(tpdu[1] & 0x80)) - { /* This is an I-block. */ - /* Last block sent was successful. */ - handle->t1_ns ^= 1; - - if (!!(tpdu[1] & 0x40) != handle->t1_nr) - { /* Reponse does not match our sequence number. */ - DEBUGOUT ("I-block with wrong seqno received\n"); - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - - 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. */ - - DEBUGOUT ("chaining requested but not supported for Secure operation\n"); - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - else if ((tpdu[1] & 0xc0) == 0x80) - { /* This is a R-block. */ - if ( (tpdu[1] & 0x0f)) - { /* Error: repeat last block */ - DEBUGOUT ("No retries supported for Secure operation\n"); - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - else if (!!(tpdu[1] & 0x10) == 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 - { /* Send next chunk. */ - DEBUGOUT ("chaining not supported on Secure operation\n"); - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - } - else - { /* This is a S-block. */ - DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n", - (tpdu[1] & 0x20)? "response": "request", - (tpdu[1] & 0x1f)); - return CCID_DRIVER_ERR_CARD_IO_ERROR; - } - - 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; - int no_pinpad = 0; - int verify_123456 = 0; - int did_verify = 0; - int no_poll = 0; - - 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 if ( !strcmp (*argv, "--no-poll")) - { - no_poll = 1; - argc--; argv++; - } - else if ( !strcmp (*argv, "--no-pinpad")) - { - no_pinpad = 1; - argc--; argv++; - } - else if ( !strcmp (*argv, "--verify-123456")) - { - verify_123456 = 1; - argc--; argv++; - } - else - break; - } - - rc = ccid_open_reader (&ccid, argc? *argv:NULL); - if (rc) - return 1; - - if (!no_poll) - ccid_poll (ccid); - fputs ("getting ATR ...\n", stderr); - rc = ccid_get_atr (ccid, NULL, 0, NULL); - if (rc) - { - print_error (rc); - return 1; - } - - if (!no_poll) - ccid_poll (ccid); - fputs ("getting slot status ...\n", stderr); - rc = ccid_slot_status (ccid, &slotstat); - if (rc) - { - print_error (rc); - return 1; - } - - if (!no_poll) - 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, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); - } - - - if (!no_poll) - 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, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); - } - - if (!no_pinpad) - { - } - - if (!no_pinpad) - { - static unsigned char apdu[] = { 0, 0x20, 0, 0x81 }; - - - if (ccid_transceive_secure (ccid, - apdu, sizeof apdu, - 1, 0, 0, 0, - NULL, 0, NULL)) - fputs ("can't verify using a PIN-Pad reader\n", stderr); - else - { - fputs ("verifying CHV1 using the PINPad ....\n", stderr); - - rc = ccid_transceive_secure (ccid, - apdu, sizeof apdu, - 1, 0, 0, 0, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); - did_verify = 1; - } - } - - if (verify_123456 && !did_verify) - { - 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); - } - } - - if (!rc) - { - fputs ("getting OpenPGP DO 0x5E ....\n", stderr); - { - static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 }; - rc = ccid_transceive (ccid, apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); - } - } - - ccid_close_reader (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/ccid/ccid-driver.h b/ccid/ccid-driver.h deleted file mode 100644 index ae6b7fd..0000000 --- a/ccid/ccid-driver.h +++ /dev/null @@ -1,108 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * - * ALTERNATIVELY, this file may be distributed under the terms of the - * following license, in which case the provisions of this license are - * required INSTEAD OF the GNU General Public License. If you wish to - * allow use of your version of this file only under the terms of the - * GNU General Public License, and not to allow others to use your - * version of this file under the terms of the following license, - * indicate your decision by deleting this paragraph and the license - * below. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id: ccid-driver.h 3887 2005-09-09 11:18:08Z wk $ - */ - -#ifndef CCID_DRIVER_H -#define CCID_DRIVER_H - -/* The CID driver returns the same error codes as the status words - used by GnuPG's apdu.h. For ease of maintenance they should always - match. */ -#define CCID_DRIVER_ERR_OUT_OF_CORE 0x10001 -#define CCID_DRIVER_ERR_INV_VALUE 0x10002 -#define CCID_DRIVER_ERR_INCOMPLETE_CARD_RESPONSE = 0x10003 -#define CCID_DRIVER_ERR_NO_DRIVER 0x10004 -#define CCID_DRIVER_ERR_NOT_SUPPORTED 0x10005 -#define CCID_DRIVER_ERR_LOCKING_FAILED 0x10006 -#define CCID_DRIVER_ERR_BUSY 0x10007 -#define CCID_DRIVER_ERR_NO_CARD 0x10008 -#define CCID_DRIVER_ERR_CARD_INACTIVE 0x10009 -#define CCID_DRIVER_ERR_CARD_IO_ERROR 0x1000a -#define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b -#define CCID_DRIVER_ERR_NO_READER 0x1000c -#define CCID_DRIVER_ERR_ABORTED 0x1000d -#define CCID_DRIVER_ERR_NO_KEYPAD 0x1000e - -struct ccid_driver_s; -typedef struct ccid_driver_s *ccid_driver_t; - -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_shutdown_reader (ccid_driver_t handle); -int ccid_close_reader (ccid_driver_t handle); -int ccid_get_atr (ccid_driver_t handle, - unsigned char *atr, size_t maxatrlen, size_t *atrlen); -int ccid_slot_status (ccid_driver_t handle, int *statusbits); -int ccid_transceive (ccid_driver_t handle, - const unsigned char *apdu, size_t apdulen, - unsigned char *resp, size_t maxresplen, size_t *nresp); -int ccid_transceive_secure (ccid_driver_t handle, - const unsigned char *apdu, size_t apdulen, - int pin_mode, - int pinlen_min, int pinlen_max, int pin_padlen, - unsigned char *resp, size_t maxresplen, size_t *nresp); -int ccid_transceive_escape (ccid_driver_t handle, - const unsigned char *data, size_t datalen, - unsigned char *resp, size_t maxresplen, - size_t *nresp); - - - -#endif /*CCID_DRIVER_H*/ - - - diff --git a/src/ccid/ccid-driver.c b/src/ccid/ccid-driver.c new file mode 100644 index 0000000..edca5ca --- /dev/null +++ b/src/ccid/ccid-driver.c @@ -0,0 +1,2448 @@ +/* ccid-driver.c - USB ChipCardInterfaceDevices driver + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + * Written by Werner Koch. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU General Public License. If you wish to + * allow use of your version of this file only under the terms of the + * GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Date: 2005-09-09 13:18:08 +0200 (Fri, 09 Sep 2005) $ + */ + + +/* 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 and SPR532 + smartcard readers and requires that a reader implements the TPDU + level exchange and does fully automatic initialization. +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if defined(HAVE_LIBUSB) || defined(TEST) + +#include +#include +#include +#include +#include + +#include + +#include "ccid-driver.h" + +#define DRVNAME "ccid-driver: " + + +/* Depending on how this source is used we either define our error + output to go to stderr or to the jnlib based logging functions. We + use the latter when GNUPG_MAJOR_VERSION is defines or when both, + GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined. +*/ +#if defined(GNUPG_MAJOR_VERSION) \ + || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING)) + +#if defined(GNUPG_SCD_MAIN_HEADER) +# include GNUPG_SCD_MAIN_HEADER +#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */ +# include "options.h" +# include "util.h" +# include "memory.h" +# include "cardglue.h" +# else /* This is the modularized GnuPG 1.9 or later. */ +# include "scdaemon.h" +#endif + + +# define DEBUGOUT(t) do { if (debug_level) \ + log_debug (DRVNAME t); } while (0) +# define DEBUGOUT_1(t,a) do { if (debug_level) \ + log_debug (DRVNAME t,(a)); } while (0) +# 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 (debug_level) \ + log_debug (DRVNAME t,(a),(b),(c));} while (0) +# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ + log_debug (DRVNAME t,(a),(b),(c),(d));} while (0) +# define DEBUGOUT_CONT(t) do { if (debug_level) \ + log_printf (t); } while (0) +# 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 (debug_level) \ + log_printf (t,(a),(b)); } while (0) +# 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 (debug_level) \ + log_printf ("\n"); } while (0) + +#else /* Other usage of this source - don't use gnupg specifics. */ + +# 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_4(t,a,b,c,d) do { if (debug_level) \ + fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} 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. */ + + + +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 +}; + + +/* Two macro to detect whether a CCID command has failed and to get + the error code. These macros assume that we can access the + mandatory first 10 bytes of a CCID message in BUF. */ +#define CCID_COMMAND_FAILED(buf) ((buf)[7] & 0x40) +#define CCID_ERROR_CODE(buf) (((unsigned char *)(buf))[8]) + + +/* We need to know the vendor to do some hacks. */ +enum { + VENDOR_SCM = 0x04e6, + VENDOR_CHERRY = 0x046a, + VENDOR_OMNIKEY= 0x076b, + VENDOR_GEMPC = 0x08e6 +}; + + +/* 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; + char *rid; + unsigned short id_vendor; + unsigned short id_product; + unsigned short bcd_device; + int ifc_no; + int ep_bulk_out; + int ep_bulk_in; + int ep_intr; + int seqno; + unsigned char t1_ns; + unsigned char t1_nr; + int nonnull_nad; + int auto_ifsd; + int max_ifsd; + int ifsd; + int powered_off; + int has_pinpad; + int apdu_level; /* Reader supports short APDU level exchange. */ +}; + + +static int initialized_usb; /* Tracks whether USB has been initialized. */ +static int debug_level; /* Flag to control the debug output. + 0 = No debugging + 1 = USB I/O info + 2 = T=1 protocol tracing + */ + + +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, int timeout, + int no_debug); + +/* Convert a little endian stored 4 byte value into an unsigned + integer. */ +static unsigned int +convert_le_u32 (const unsigned char *buf) +{ + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + +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; +} + + +/* Pint an error message for a failed CCID command including a textual + error code. MSG is shall be the CCID message of at least 10 bytes. */ +static void +print_command_failed (const unsigned char *msg) +{ + const char *t; + char buffer[100]; + int ec; + + if (!debug_level) + return; + + ec = CCID_ERROR_CODE (msg); + switch (ec) + { + case 0x00: t = "Command not supported"; break; + + case 0xE0: t = "Slot busy"; break; + case 0xEF: t = "PIN cancelled"; break; + case 0xF0: t = "PIN timeout"; break; + + case 0xF2: t = "Automatic sequence ongoing"; break; + case 0xF3: t = "Deactivated Protocol"; break; + case 0xF4: t = "Procedure byte conflict"; break; + case 0xF5: t = "ICC class not supported"; break; + case 0xF6: t = "ICC protocol not supported"; break; + case 0xF7: t = "Bad checksum in ATR"; break; + case 0xF8: t = "Bad TS in ATR"; break; + + case 0xFB: t = "An all inclusive hardware error occurred"; break; + case 0xFC: t = "Overrun error while talking to the ICC"; break; + case 0xFD: t = "Parity error while talking to the ICC"; break; + case 0xFE: t = "CCID timed out while talking to the ICC"; break; + case 0xFF: t = "Host aborted the current activity"; break; + + default: + if (ec > 0 && ec < 128) + sprintf (buffer, "Parameter error at offset %d", ec); + else + sprintf (buffer, "Error code %02X", ec); + t = buffer; + break; + } + DEBUGOUT_1 ("CCID command failed: %s\n", t); +} + + + + +/* Parse a CCID descriptor, optionally print all available features + and test whether this reader is usable by this driver. Returns 0 + if it is usable. + + Note, that this code is based on the one in lsusb.c of the + usb-utils package, I wrote on 2003-09-01. -wk. */ +static int +parse_ccid_descriptor (ccid_driver_t handle, + const unsigned char *buf, size_t buflen) +{ + unsigned int i; + unsigned int us; + int have_t1 = 0, have_tpdu=0, have_auto_conf = 0; + + + handle->nonnull_nad = 0; + handle->auto_ifsd = 0; + handle->max_ifsd = 32; + handle->ifsd = 0; + handle->has_pinpad = 0; + handle->apdu_level = 0; + DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n", + handle->id_vendor, handle->id_product, handle->bcd_device); + if (buflen < 54 || buf[0] < 54) + { + DEBUGOUT ("CCID device descriptor is too short\n"); + return -1; + } + + DEBUGOUT ("ChipCard Interface Descriptor:\n"); + DEBUGOUT_1 (" bLength %5u\n", buf[0]); + DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]); + DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]); + if (buf[3] != 1 || buf[2] != 0) + DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)"); + DEBUGOUT_LF (); + + DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]); + DEBUGOUT_2 (" bVoltageSupport %5u %s\n", + buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V" + : buf[5] == 3? "1.8V":"?")); + + us = convert_le_u32 (buf+6); + DEBUGOUT_1 (" dwProtocols %5u ", us); + if ((us & 1)) + DEBUGOUT_CONT (" T=0"); + if ((us & 2)) + { + DEBUGOUT_CONT (" T=1"); + have_t1 = 1; + } + if ((us & ~3)) + DEBUGOUT_CONT (" (Invalid values detected)"); + DEBUGOUT_LF (); + + us = convert_le_u32(buf+10); + DEBUGOUT_1 (" dwDefaultClock %5u\n", us); + us = convert_le_u32(buf+14); + DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us); + DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]); + us = convert_le_u32(buf+19); + DEBUGOUT_1 (" dwDataRate %7u bps\n", us); + us = convert_le_u32(buf+23); + DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us); + DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]); + + us = convert_le_u32(buf+28); + DEBUGOUT_1 (" dwMaxIFSD %5u\n", us); + handle->max_ifsd = us; + + us = convert_le_u32(buf+32); + DEBUGOUT_1 (" dwSyncProtocols %08X ", us); + if ((us&1)) + DEBUGOUT_CONT ( " 2-wire"); + if ((us&2)) + DEBUGOUT_CONT ( " 3-wire"); + if ((us&4)) + DEBUGOUT_CONT ( " I2C"); + DEBUGOUT_LF (); + + us = convert_le_u32(buf+36); + DEBUGOUT_1 (" dwMechanical %08X ", us); + if ((us & 1)) + DEBUGOUT_CONT (" accept"); + if ((us & 2)) + DEBUGOUT_CONT (" eject"); + if ((us & 4)) + DEBUGOUT_CONT (" capture"); + if ((us & 8)) + DEBUGOUT_CONT (" lock"); + DEBUGOUT_LF (); + + us = convert_le_u32(buf+40); + DEBUGOUT_1 (" dwFeatures %08X\n", us); + if ((us & 0x0002)) + { + DEBUGOUT (" Auto configuration based on ATR\n"); + have_auto_conf = 1; + } + if ((us & 0x0004)) + DEBUGOUT (" Auto activation on insert\n"); + if ((us & 0x0008)) + DEBUGOUT (" Auto voltage selection\n"); + if ((us & 0x0010)) + DEBUGOUT (" Auto clock change\n"); + if ((us & 0x0020)) + DEBUGOUT (" Auto baud rate change\n"); + if ((us & 0x0040)) + DEBUGOUT (" Auto parameter negotation made by CCID\n"); + else if ((us & 0x0080)) + DEBUGOUT (" Auto PPS made by CCID\n"); + else if ((us & (0x0040 | 0x0080))) + DEBUGOUT (" WARNING: conflicting negotation features\n"); + + if ((us & 0x0100)) + DEBUGOUT (" CCID can set ICC in clock stop mode\n"); + if ((us & 0x0200)) + { + DEBUGOUT (" NAD value other than 0x00 accepted\n"); + handle->nonnull_nad = 1; + } + if ((us & 0x0400)) + { + DEBUGOUT (" Auto IFSD exchange\n"); + handle->auto_ifsd = 1; + } + + if ((us & 0x00010000)) + { + DEBUGOUT (" TPDU level exchange\n"); + have_tpdu = 1; + } + else if ((us & 0x00020000)) + { + DEBUGOUT (" Short APDU level exchange\n"); + handle->apdu_level = 1; + } + else if ((us & 0x00040000)) + { + DEBUGOUT (" Short and extended APDU level exchange\n"); + handle->apdu_level = 1; + } + else if ((us & 0x00070000)) + DEBUGOUT (" WARNING: conflicting exchange levels\n"); + + us = convert_le_u32(buf+44); + DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us); + + DEBUGOUT ( " bClassGetResponse "); + if (buf[48] == 0xff) + DEBUGOUT_CONT ("echo\n"); + else + DEBUGOUT_CONT_1 (" %02X\n", buf[48]); + + DEBUGOUT ( " bClassEnvelope "); + if (buf[49] == 0xff) + DEBUGOUT_CONT ("echo\n"); + else + DEBUGOUT_CONT_1 (" %02X\n", buf[48]); + + DEBUGOUT ( " wlcdLayout "); + if (!buf[50] && !buf[51]) + DEBUGOUT_CONT ("none\n"); + else + DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]); + + DEBUGOUT_1 (" bPINSupport %5u ", buf[52]); + if ((buf[52] & 1)) + { + DEBUGOUT_CONT ( " verification"); + handle->has_pinpad |= 1; + } + if ((buf[52] & 2)) + { + DEBUGOUT_CONT ( " modification"); + handle->has_pinpad |= 2; + } + DEBUGOUT_LF (); + + DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]); + + if (buf[0] > 54) { + DEBUGOUT (" junk "); + for (i=54; i < buf[0]-54; i++) + DEBUGOUT_CONT_1 (" %02X", buf[i]); + DEBUGOUT_LF (); + } + + if (!have_t1 || !(have_tpdu || handle->apdu_level) || !have_auto_conf) + { + DEBUGOUT ("this drivers requires that the reader supports T=1, " + "TPDU or APDU level exchange and auto configuration - " + "this is not available\n"); + return -1; + } + + + /* SCM drivers get stuck in their internal USB stack if they try to + send a frame of n*wMaxPacketSize back to us. Given that + wMaxPacketSize is 64 for these readers we set the IFSD to a value + lower than that: + 64 - 10 CCID header - 4 T1frame - 2 reserved = 48 + Product Ids: + 0xe001 - SCR 331 + 0x5111 - SCR 331-DI + 0x5115 - SCR 335 + 0xe003 - SPR 532 + */ + if (handle->id_vendor == VENDOR_SCM + && handle->max_ifsd > 48 + && ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516) + ||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620) + ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0514) + ||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504) + )) + { + DEBUGOUT ("enabling workaround for buggy SCM readers\n"); + handle->max_ifsd = 48; + } + + + return 0; +} + + +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 is 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, + (char*)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, + (char*)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) +{ + 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) + { + rid = malloc (strlen (prefix) + 3 + 1); + if (!rid) + return NULL; + strcpy (rid, prefix); + strcat (rid, "X:0"); + } + return rid; +} + + +/* Helper to find the endpoint from an interface descriptor. */ +static int +find_endpoint (struct usb_interface_descriptor *ifcdesc, int mode) +{ + int no; + int want_bulk_in = 0; + + if (mode == 1) + want_bulk_in = 0x80; + for (no=0; no < ifcdesc->bNumEndpoints; no++) + { + struct usb_endpoint_descriptor *ep = ifcdesc->endpoint + no; + if (ep->bDescriptorType != USB_DT_ENDPOINT) + ; + else if (mode == 2 + && ((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) + == USB_ENDPOINT_TYPE_INTERRUPT) + && (ep->bEndpointAddress & 0x80)) + return (ep->bEndpointAddress & 0x0f); + else if (((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) + == USB_ENDPOINT_TYPE_BULK) + && (ep->bEndpointAddress & 0x80) == want_bulk_in) + return (ep->bEndpointAddress & 0x0f); + } + /* Should never happen. */ + return mode == 2? 0x83 : mode == 1? 0x82 :1; +} + + + +/* 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, + int *interface_number, + int *ep_bulk_out, int *ep_bulk_in, int *ep_intr) +{ + 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; + if (interface_number) + *interface_number = 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 + busses = usb_busses; +#endif + + for (bus = busses; bus; bus = bus->next) + { + for (dev = bus->devices; dev; dev = dev->next) + { + int cfg_no; + + for (cfg_no=0; cfg_no < dev->descriptor.bNumConfigurations; cfg_no++) + { + 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++) + { + 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++) + { + 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))) + { + 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 (interface_number) + *interface_number = (ifcdesc-> + bInterfaceNumber); + if (ep_bulk_out) + *ep_bulk_out = find_endpoint (ifcdesc, 0); + if (ep_bulk_in) + *ep_bulk_in = find_endpoint (ifcdesc, 1); + if (ep_intr) + *ep_intr = find_endpoint (ifcdesc, 2); + + + 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: + ; + } + } + + 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, 2 enables additional tracing of the T=1 + protocol, 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, NULL, + 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, const char *readerid) +{ + 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; + int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; + + *handle = NULL; + + if (!initialized_usb) + { + usb_init (); + initialized_usb = 1; + } + + /* 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) + { + 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, readerid, &rid, &dev, + &ifcdesc_extra, &ifcdesc_extra_len, + &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr); + if (!idev) + { + 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; + } + + /* 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; + (*handle)->id_vendor = dev->descriptor.idVendor; + (*handle)->id_product = dev->descriptor.idProduct; + (*handle)->bcd_device = dev->descriptor.bcdDevice; + (*handle)->ifc_no = ifc_no; + (*handle)->ep_bulk_out = ep_bulk_out; + (*handle)->ep_bulk_in = ep_bulk_in; + (*handle)->ep_intr = ep_intr; + + DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid ); + + + if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len)) + { + DEBUGOUT ("device not supported\n"); + rc = CCID_DRIVER_ERR_NO_READER; + goto leave; + } + + rc = usb_claim_interface (idev, ifc_no); + if (rc) + { + DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; + goto leave; + } + + leave: + free (ifcdesc_extra); + if (rc) + { + free (rid); + if (idev) + usb_close (idev); + free (*handle); + *handle = NULL; + } + + return rc; +} + + +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, 2000, 0); + handle->powered_off = 1; + } + if (handle->idev) + { + usb_release_interface (handle->idev, handle->ifc_no); + usb_close (handle->idev); + handle->idev = NULL; + } +} + + +/* 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. + + 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_device *dev = NULL; + usb_dev_handle *idev = NULL; + unsigned char *ifcdesc_extra = NULL; + size_t ifcdesc_extra_len; + int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; + + if (!handle || !handle->rid) + return CCID_DRIVER_ERR_INV_VALUE; + + do_close_reader (handle); + + idev = scan_or_find_devices (-1, handle->rid, NULL, &dev, + &ifcdesc_extra, &ifcdesc_extra_len, + &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr); + if (!idev) + { + DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid); + return CCID_DRIVER_ERR_NO_READER; + } + + + handle->idev = idev; + handle->ifc_no = ifc_no; + handle->ep_bulk_out = ep_bulk_out; + handle->ep_bulk_in = ep_bulk_in; + handle->ep_intr = ep_intr; + + if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len)) + { + DEBUGOUT ("device not supported\n"); + rc = CCID_DRIVER_ERR_NO_READER; + goto leave; + } + + rc = usb_claim_interface (idev, ifc_no); + if (rc) + { + DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; + goto leave; + } + + leave: + free (ifcdesc_extra); + if (rc) + { + usb_close (handle->idev); + handle->idev = NULL; + } + + return rc; + +} + + +/* Close the reader HANDLE. */ +int +ccid_close_reader (ccid_driver_t handle) +{ + if (!handle || !handle->idev) + return 0; + + do_close_reader (handle); + free (handle->rid); + free (handle); + return 0; +} + + +/* Return False if a card is present and powered. */ +int +ccid_check_card_presence (ccid_driver_t handle) +{ + + return -1; +} + + +/* 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, + handle->ep_bulk_out, + (char*)msg, msglen, + 1000 /* ms timeout */); + if (rc == msglen) + return 0; + + if (rc == -1) + DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); + else + DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc); + return CCID_DRIVER_ERR_CARD_IO_ERROR; +} + + +/* Read a maximum of LENGTH bytes from the bulk in endpoint into + BUFFER and return the actual read number if bytes in NREAD. SEQNO + is the sequence number used to send the request and EXPECTED_TYPE + the type of message we expect. Does checks on the ccid + header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to + avoid debug messages in case of no error. Returns 0 on success. */ +static int +bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, + size_t *nread, int expected_type, int seqno, int timeout, + int no_debug) +{ + 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, + handle->ep_bulk_in, + (char*)buffer, length, + timeout); + if (rc < 0) + { + DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno)); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + + *nread = msglen = rc; + + if (msglen < 10) + { + DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen); + return CCID_DRIVER_ERR_INV_VALUE; + } + if (buffer[0] != expected_type) + { + DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]); + return CCID_DRIVER_ERR_INV_VALUE; + } + if (buffer[5] != 0) + { + DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]); + return CCID_DRIVER_ERR_INV_VALUE; + } + if (buffer[6] != seqno) + { + DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n", + seqno, buffer[6]); + return CCID_DRIVER_ERR_INV_VALUE; + } + + if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80) + { + /* Card present and active, time extension requested. */ + DEBUGOUT_2 ("time extension requested (%02X,%02X)\n", + buffer[7], buffer[8]); + goto retry; + } + + if (!no_debug) + { + DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n" + " data:", buffer[7], buffer[8], buffer[9] ); + for (i=10; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", buffer[i]); + DEBUGOUT_LF (); + } + if (CCID_COMMAND_FAILED (buffer)) + print_command_failed (buffer); + + /* Check whether a card is at all available. Note: If you add new + error codes here, check whether they need to be ignored in + send_escape_cmd. */ + switch ((buffer[7] & 0x03)) + { + case 0: /* no error */ break; + case 1: return CCID_DRIVER_ERR_CARD_INACTIVE; + case 2: return CCID_DRIVER_ERR_NO_CARD; + case 3: /* RFU */ break; + } + return 0; +} + + +/* Note that this function won't return the error codes NO_CARD or + CARD_INACTIVE. IF RESULT is not NULL, the result from the + operation will get returned in RESULT and its length in RESULTLEN. + If the response is larger than RESULTMAX, an error is returned and + the required buffer length returned in RESULTLEN. */ +static int +send_escape_cmd (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *result, size_t resultmax, size_t *resultlen) +{ + int i, rc; + unsigned char msg[100]; + size_t msglen; + unsigned char seqno; + + if (resultlen) + *resultlen = 0; + + if (datalen > sizeof msg - 10) + return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */ + + msg[0] = PC_to_RDR_Escape; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* RFU */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + memcpy (msg+10, data, datalen); + msglen = 10 + datalen; + set_msg_len (msg, datalen); + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape, + seqno, 5000, 0); + if (result) + switch (rc) + { + /* We need to ignore certain errorcode here. */ + case 0: + case CCID_DRIVER_ERR_CARD_INACTIVE: + case CCID_DRIVER_ERR_NO_CARD: + { + if (msglen < 10 || (msglen-10) > resultmax ) + rc = CCID_DRIVER_ERR_INV_VALUE; /* Invalid length of response. */ + else + { + memcpy (result, msg+10, msglen-10); + *resultlen = msglen-10; + } + rc = 0; + } + break; + default: + break; + } + + return rc; +} + + +int +ccid_transceive_escape (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *resp, size_t maxresplen, size_t *nresp) +{ + return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp); +} + + + +/* 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, + handle->ep_intr, + (char*)msg, sizeof msg, + 0 /* ms timeout */ ); + if (rc < 0 && errno == ETIMEDOUT) + return 0; + + if (rc < 0) + { + DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno)); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + + msglen = rc; + rc = 0; + + if (msglen < 1) + { + DEBUGOUT ("intr-in msg too short\n"); + return CCID_DRIVER_ERR_INV_VALUE; + } + + if (msg[0] == RDR_to_PC_NotifySlotChange) + { + DEBUGOUT ("notify slot change:"); + for (i=1; i < msglen; i++) + for (j=0; j < 4; j++) + DEBUGOUT_CONT_3 (" %d:%c%c", + (i-1)*4+j, + (msg[i] & (1<<(j*2)))? 'p':'-', + (msg[i] & (2<<(j*2)))? '*':' '); + DEBUGOUT_LF (); + } + else if (msg[0] == RDR_to_PC_HardwareError) + { + DEBUGOUT ("hardware error occured\n"); + } + else + { + DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]); + } + + return 0; +} + + +/* Note that this fucntion won't return the error codes NO_CARD or + CARD_INACTIVE */ +int +ccid_slot_status (ccid_driver_t handle, int *statusbits) +{ + int rc; + unsigned char msg[100]; + size_t msglen; + unsigned char seqno; + int retries = 0; + + retry: + 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; + /* Note that we set the NO_DEBUG flag here, so that the logs won't + get cluttered up by a ticker function checking for the slot + status and debugging enabled. */ + rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, + seqno, retries? 1000 : 200, 1); + if (rc == CCID_DRIVER_ERR_CARD_IO_ERROR && retries < 3) + { + if (!retries) + { + DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n"); + usb_clear_halt (handle->idev, handle->ep_bulk_in); + usb_clear_halt (handle->idev, handle->ep_bulk_out); + } + else + DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n"); + retries++; + goto retry; + } + if (rc && rc != CCID_DRIVER_ERR_NO_CARD + && rc != CCID_DRIVER_ERR_CARD_INACTIVE) + return rc; + *statusbits = (msg[7] & 3); + + return 0; +} + + +int +ccid_get_atr (ccid_driver_t handle, + unsigned char *atr, size_t maxatrlen, size_t *atrlen) +{ + int rc; + int statusbits; + unsigned char msg[100]; + unsigned char *tpdu; + size_t msglen, tpdulen; + unsigned char seqno; + int use_crc = 0; + unsigned int edc; + int i; + int tried_iso = 0; + + /* First check whether a card is available. */ + rc = ccid_slot_status (handle, &statusbits); + if (rc) + return rc; + if (statusbits == 2) + return CCID_DRIVER_ERR_NO_CARD; + + /* For an inactive and also for an active card, issue the PowerOn + command to get the ATR. */ + again: + 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, RDR_to_PC_DataBlock, + seqno, 5000, 0); + if (rc) + return rc; + if (!tried_iso && CCID_COMMAND_FAILED (msg) && CCID_ERROR_CODE (msg) == 0xbb + && ((handle->id_vendor == VENDOR_CHERRY + && handle->id_product == 0x0005) + || (handle->id_vendor == VENDOR_GEMPC + && handle->id_product == 0x4433) + )) + { + tried_iso = 1; + /* Try switching to ISO mode. */ + if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2, + NULL, 0, NULL)) + goto again; + } + else if (CCID_COMMAND_FAILED (msg)) + return CCID_DRIVER_ERR_CARD_IO_ERROR; + + + handle->powered_off = 0; + + if (atr) + { + size_t n = msglen - 10; + + if (n > maxatrlen) + n = maxatrlen; + memcpy (atr, msg+10, n); + *atrlen = n; + } + + /* Setup parameters to select T=1. */ + msg[0] = PC_to_RDR_SetParameters; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 1; /* Select T=1. */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + + /* FIXME: Get those values from the ATR. */ + msg[10]= 0x01; /* Fi/Di */ + msg[11]= 0x10; /* LRC, direct convention. */ + msg[12]= 0; /* Extra guardtime. */ + msg[13]= 0x41; /* BWI/CWI */ + msg[14]= 0; /* No clock stoppping. */ + msg[15]= 254; /* IFSC */ + msg[16]= 0; /* Does not support non default NAD values. */ + set_msg_len (msg, 7); + msglen = 10 + 7; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + /* Note that we ignore the error code on purpose. */ + bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, + seqno, 5000, 0); + + handle->t1_ns = 0; + handle->t1_nr = 0; + + /* Send an S-Block with our maximun IFSD to the CCID. */ + if (!handle->auto_ifsd) + { + tpdu = msg+10; + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; + tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */ + tpdu[2] = 1; + tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32; + tpdulen = 4; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + + msg[0] = PC_to_RDR_XfrBlock; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, tpdulen); + msglen = 10 + tpdulen; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + if (debug_level > 1) + DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) + : !!(msg[11] & 0x40)), + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + + rc = bulk_in (handle, msg, sizeof msg, &msglen, + RDR_to_PC_DataBlock, seqno, 5000, 0); + if (rc) + return rc; + + tpdu = msg + 10; + tpdulen = msglen - 10; + + if (tpdulen < 4) + return CCID_DRIVER_ERR_ABORTED; + + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\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, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1) + { + DEBUGOUT ("invalid response for S-block (Change-IFSD)\n"); + return -1; + } + DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]); + } + + return 0; +} + + + + +static unsigned int +compute_edc (const unsigned char *data, size_t datalen, int use_crc) +{ + if (use_crc) + { + return 0x42; /* Not yet implemented. */ + } + else + { + unsigned char crc = 0; + + for (; datalen; datalen--) + crc ^= *data++; + return crc; + } +} + + +/* Helper for ccid_transceive used for APDU level exchanges. */ +static int +ccid_transceive_apdu_level (ccid_driver_t handle, + const unsigned char *apdu_buf, size_t apdu_buflen, + unsigned char *resp, size_t maxresplen, + size_t *nresp) +{ + int rc; + unsigned char send_buffer[10+259], recv_buffer[10+259]; + const unsigned char *apdu; + size_t apdulen; + unsigned char *msg; + size_t msglen; + unsigned char seqno; + int i; + + msg = send_buffer; + + apdu = apdu_buf; + apdulen = apdu_buflen; + assert (apdulen); + + if (apdulen > 254) + return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ + + 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 */ + memcpy (msg+10, apdu, apdulen); + set_msg_len (msg, apdulen); + msglen = 10 + apdulen; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + msg = recv_buffer; + rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, + RDR_to_PC_DataBlock, seqno, 5000, 0); + if (rc) + return rc; + + apdu = msg + 10; + apdulen = msglen - 10; + + if (resp) + { + if (apdulen > maxresplen) + { + DEBUGOUT_2 ("provided buffer too short for received data " + "(%u/%u)\n", + (unsigned int)apdulen, (unsigned int)maxresplen); + return CCID_DRIVER_ERR_INV_VALUE; + } + + memcpy (resp, apdu, apdulen); + *nresp = apdulen; + } + + return 0; +} + + + +/* + 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 nodes. + + 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_buf, size_t apdu_buflen, + unsigned char *resp, size_t maxresplen, size_t *nresp) +{ + int rc; + unsigned char send_buffer[10+259], recv_buffer[10+259]; + const unsigned char *apdu; + size_t apdulen; + unsigned char *msg, *tpdu, *p; + size_t msglen, tpdulen, last_tpdulen, n; + unsigned char seqno; + int i; + unsigned int edc; + int use_crc = 0; + size_t dummy_nresp; + int next_chunk = 1; + int sending = 1; + int retries = 0; + + if (!nresp) + nresp = &dummy_nresp; + *nresp = 0; + + /* Smarter readers allow to send APDUs directly; divert here. */ + if (handle->apdu_level) + return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen, + resp, maxresplen, nresp); + + /* The other readers we support require sending TPDUs. */ + + tpdulen = 0; /* Avoid compiler warning about no initialization. */ + msg = send_buffer; + for (;;) + { + if (next_chunk) + { + next_chunk = 0; + + apdu = apdu_buf; + apdulen = apdu_buflen; + assert (apdulen); + + /* Construct an I-Block. */ + if (apdulen > 254) + return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ + + tpdu = msg+10; + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; + tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ + if (apdulen > 128 /* fixme: replace by ifsc */) + { + apdulen = 128; + apdu_buf += 128; + apdu_buflen -= 128; + tpdu[1] |= (1 << 5); /* Set more bit. */ + } + tpdu[2] = apdulen; + memcpy (tpdu+3, apdu, apdulen); + tpdulen = 3 + apdulen; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + } + + msg[0] = PC_to_RDR_XfrBlock; + 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, tpdulen); + msglen = 10 + tpdulen; + last_tpdulen = tpdulen; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + if (debug_level > 1) + DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) + : !!(msg[11] & 0x40)), + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + 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, 5000, 0); + if (rc) + return rc; + + tpdu = msg + 10; + tpdulen = msglen - 10; + + if (tpdulen < 4) + { + usb_clear_halt (handle->idev, handle->ep_bulk_in); + return CCID_DRIVER_ERR_ABORTED; + } + + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\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, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + 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, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + 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, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + } + else if ((tpdu[1] & 0xc0) == 0x80) + { /* This is a R-block. */ + if ( (tpdu[1] & 0x0f)) + { /* Error: repeat last block */ + if (++retries > 3) + { + DEBUGOUT ("3 failed retries\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + msg = send_buffer; + tpdulen = last_tpdulen; + } + else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns) + { /* Response 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 ("T=1 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, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi); + } + else + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + } /* end T=1 protocol loop. */ + + return 0; +} + + +/* Send the CCID Secure command to the reader. APDU_BUF should + contain the APDU template. PIN_MODE defines how the pin gets + formatted: + + 1 := The PIN is ASCII encoded and of variable length. The + length of the PIN entered will be put into Lc by the reader. + The APDU should me made up of 4 bytes without Lc. + + PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0 + may be used t enable reasonable defaults. PIN_PADLEN should be 0. + + When called with RESP and NRESP set to NULL, the function will + merely check whether the reader supports the secure command for the + given APDU and PIN_MODE. */ +int +ccid_transceive_secure (ccid_driver_t handle, + const unsigned char *apdu_buf, size_t apdu_buflen, + int pin_mode, int pinlen_min, int pinlen_max, + int pin_padlen, + 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, n; + unsigned char seqno; + int i; + size_t dummy_nresp; + int testmode; + + testmode = !resp && !nresp; + + if (!nresp) + nresp = &dummy_nresp; + *nresp = 0; + + if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1)) + ; + else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2)) + return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */ + else + return CCID_DRIVER_ERR_NO_KEYPAD; + + if (pin_mode != 1) + return CCID_DRIVER_ERR_NOT_SUPPORTED; + + if (pin_padlen != 0) + return CCID_DRIVER_ERR_NOT_SUPPORTED; + + if (!pinlen_min) + pinlen_min = 1; + if (!pinlen_max) + pinlen_max = 25; + + /* Note that the 25 is the maximum value the SPR532 allows. */ + if (pinlen_min < 1 || pinlen_min > 25 + || pinlen_max < 1 || pinlen_max > 25 + || pinlen_min > pinlen_max) + return CCID_DRIVER_ERR_INV_VALUE; + + /* We have only tested this with an SCM reader so better don't risk + anything and do not allow the use with other readers. */ + if (handle->id_vendor != VENDOR_SCM) + return CCID_DRIVER_ERR_NOT_SUPPORTED; + + if (testmode) + return 0; /* Success */ + + msg = send_buffer; + if (handle->id_vendor == VENDOR_SCM) + { + DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n"); + rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3, + NULL, 0, NULL); + if (rc) + return rc; + } + + 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] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */ + if (handle->id_vendor == VENDOR_SCM) + { + /* For the SPR532 the next 2 bytes need to be zero. We do this + for all SCM product. Kudos to Martin Paljak for this + hint. */ + msg[13] = msg[14] = 0; + } + else + { + msg[13] = 0x00; /* bmPINBlockString: + 0 bits of pin length to insert. + 0 bytes of PIN block size. */ + msg[14] = 0x00; /* bmPINLengthFormat: + Units are bytes, position is 0. */ + } + msg[15] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */ + msg[16] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */ + msg[17] = 0x02; /* bEntryValidationCondition: + Validation key pressed */ + if (pinlen_min && pinlen_max && pinlen_min == pinlen_max) + msg[17] |= 0x01; /* Max size reached. */ + 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] = apdu_buf[0]; /* CLA */ + msg[26] = apdu_buf[1]; /* INS */ + msg[27] = apdu_buf[2]; /* P1 */ + msg[28] = apdu_buf[3]; /* P2 */ + msglen = 29; + set_msg_len (msg, msglen - 10); + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + msg = recv_buffer; + rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, + RDR_to_PC_DataBlock, seqno, 5000, 0); + if (rc) + return rc; + + tpdu = msg + 10; + tpdulen = msglen - 10; + + if (tpdulen < 4) + { + usb_clear_halt (handle->idev, handle->ep_bulk_in); + return CCID_DRIVER_ERR_ABORTED; + } + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\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, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + + if (!(tpdu[1] & 0x80)) + { /* This is an I-block. */ + /* Last block sent was successful. */ + handle->t1_ns ^= 1; + + if (!!(tpdu[1] & 0x40) != handle->t1_nr) + { /* Reponse does not match our sequence number. */ + DEBUGOUT ("I-block with wrong seqno received\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + + 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. */ + + DEBUGOUT ("chaining requested but not supported for Secure operation\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + else if ((tpdu[1] & 0xc0) == 0x80) + { /* This is a R-block. */ + if ( (tpdu[1] & 0x0f)) + { /* Error: repeat last block */ + DEBUGOUT ("No retries supported for Secure operation\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + else if (!!(tpdu[1] & 0x10) == 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 + { /* Send next chunk. */ + DEBUGOUT ("chaining not supported on Secure operation\n"); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + } + else + { /* This is a S-block. */ + DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n", + (tpdu[1] & 0x20)? "response": "request", + (tpdu[1] & 0x1f)); + return CCID_DRIVER_ERR_CARD_IO_ERROR; + } + + 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; + int no_pinpad = 0; + int verify_123456 = 0; + int did_verify = 0; + int no_poll = 0; + + 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 if ( !strcmp (*argv, "--no-poll")) + { + no_poll = 1; + argc--; argv++; + } + else if ( !strcmp (*argv, "--no-pinpad")) + { + no_pinpad = 1; + argc--; argv++; + } + else if ( !strcmp (*argv, "--verify-123456")) + { + verify_123456 = 1; + argc--; argv++; + } + else + break; + } + + rc = ccid_open_reader (&ccid, argc? *argv:NULL); + if (rc) + return 1; + + if (!no_poll) + ccid_poll (ccid); + fputs ("getting ATR ...\n", stderr); + rc = ccid_get_atr (ccid, NULL, 0, NULL); + if (rc) + { + print_error (rc); + return 1; + } + + if (!no_poll) + ccid_poll (ccid); + fputs ("getting slot status ...\n", stderr); + rc = ccid_slot_status (ccid, &slotstat); + if (rc) + { + print_error (rc); + return 1; + } + + if (!no_poll) + 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, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + } + + + if (!no_poll) + 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, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + } + + if (!no_pinpad) + { + } + + if (!no_pinpad) + { + static unsigned char apdu[] = { 0, 0x20, 0, 0x81 }; + + + if (ccid_transceive_secure (ccid, + apdu, sizeof apdu, + 1, 0, 0, 0, + NULL, 0, NULL)) + fputs ("can't verify using a PIN-Pad reader\n", stderr); + else + { + fputs ("verifying CHV1 using the PINPad ....\n", stderr); + + rc = ccid_transceive_secure (ccid, + apdu, sizeof apdu, + 1, 0, 0, 0, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + did_verify = 1; + } + } + + if (verify_123456 && !did_verify) + { + 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); + } + } + + if (!rc) + { + fputs ("getting OpenPGP DO 0x5E ....\n", stderr); + { + static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 }; + rc = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (rc, result, resultlen); + } + } + + ccid_close_reader (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/src/ccid/ccid-driver.h b/src/ccid/ccid-driver.h new file mode 100644 index 0000000..ae6b7fd --- /dev/null +++ b/src/ccid/ccid-driver.h @@ -0,0 +1,108 @@ +/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU General Public License. If you wish to + * allow use of your version of this file only under the terms of the + * GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: ccid-driver.h 3887 2005-09-09 11:18:08Z wk $ + */ + +#ifndef CCID_DRIVER_H +#define CCID_DRIVER_H + +/* The CID driver returns the same error codes as the status words + used by GnuPG's apdu.h. For ease of maintenance they should always + match. */ +#define CCID_DRIVER_ERR_OUT_OF_CORE 0x10001 +#define CCID_DRIVER_ERR_INV_VALUE 0x10002 +#define CCID_DRIVER_ERR_INCOMPLETE_CARD_RESPONSE = 0x10003 +#define CCID_DRIVER_ERR_NO_DRIVER 0x10004 +#define CCID_DRIVER_ERR_NOT_SUPPORTED 0x10005 +#define CCID_DRIVER_ERR_LOCKING_FAILED 0x10006 +#define CCID_DRIVER_ERR_BUSY 0x10007 +#define CCID_DRIVER_ERR_NO_CARD 0x10008 +#define CCID_DRIVER_ERR_CARD_INACTIVE 0x10009 +#define CCID_DRIVER_ERR_CARD_IO_ERROR 0x1000a +#define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b +#define CCID_DRIVER_ERR_NO_READER 0x1000c +#define CCID_DRIVER_ERR_ABORTED 0x1000d +#define CCID_DRIVER_ERR_NO_KEYPAD 0x1000e + +struct ccid_driver_s; +typedef struct ccid_driver_s *ccid_driver_t; + +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_shutdown_reader (ccid_driver_t handle); +int ccid_close_reader (ccid_driver_t handle); +int ccid_get_atr (ccid_driver_t handle, + unsigned char *atr, size_t maxatrlen, size_t *atrlen); +int ccid_slot_status (ccid_driver_t handle, int *statusbits); +int ccid_transceive (ccid_driver_t handle, + const unsigned char *apdu, size_t apdulen, + unsigned char *resp, size_t maxresplen, size_t *nresp); +int ccid_transceive_secure (ccid_driver_t handle, + const unsigned char *apdu, size_t apdulen, + int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen, + unsigned char *resp, size_t maxresplen, size_t *nresp); +int ccid_transceive_escape (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *resp, size_t maxresplen, + size_t *nresp); + + + +#endif /*CCID_DRIVER_H*/ + + +