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