diff options
Diffstat (limited to 'ccid')
-rw-r--r-- | ccid/ccid-driver.c | 2448 | ||||
-rw-r--r-- | ccid/ccid-driver.h | 108 |
2 files changed, 0 insertions, 2556 deletions
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 <config.h> -#endif - -#if defined(HAVE_LIBUSB) || defined(TEST) - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include <usb.h> - -#include "ccid-driver.h" - -#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*/ - - - |