diff options
author | Werner Koch <wk@gnupg.org> | 2003-09-05 09:40:41 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2003-09-05 09:40:41 +0200 |
commit | 25430119e832a89cd141fb7c1a0edbfab237c243 (patch) | |
tree | 182f0eb5edf6f7fd7d4e3e5922c44326a44c0464 /scd/ccid-driver.c | |
parent | * keygen.c (do_add_key_flags, parse_parameter_usage) (diff) | |
download | gnupg2-25430119e832a89cd141fb7c1a0edbfab237c243.tar.xz gnupg2-25430119e832a89cd141fb7c1a0edbfab237c243.zip |
* ccid-driver.c: More work, data can now actually be retrieved.
* ccid-driver.c, ccid-driver.h: Alternativley allow use under BSD
conditions.
Diffstat (limited to 'scd/ccid-driver.c')
-rw-r--r-- | scd/ccid-driver.c | 444 |
1 files changed, 280 insertions, 164 deletions
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 03c9bcb95..b18b055b0 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -1,5 +1,6 @@ /* ccid-driver.c - USB ChipCardInterfaceDevices driver * Copyright (C) 2003 Free Software Foundation, Inc. + * Written by Werner Koch. * * This file is part of GnuPG. * @@ -16,6 +17,40 @@ * 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 + * + * 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. */ @@ -38,6 +73,7 @@ #endif #if defined(HAVE_LIBUSB) || defined(TEST) + #include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -46,9 +82,43 @@ #include <usb.h> - #include "ccid-driver.h" +#define DRVNAME "ccid-driver: " + + +#ifdef GNUPG_DEFAULT_SCDAEMON /* This source is used within the + gnupg>=1.9 source tree. */ +# include "scdaemon.h" + +# define DEBUGOUT(t) do { if (DBG_CARD_IO) \ + log_debug (DRVNAME t); } while (0) +# define DEBUGOUT_1(t,a) do { if (DBG_CARD_IO) \ + log_debug (DRVNAME t,(a)); } while (0) +# define DEBUGOUT_2(t,a,b) do { if (DBG_CARD_IO) \ + log_debug (DRVNAME t,(a),(b)); } while (0) +# define DEBUGOUT_3(t,a,b,c) do { if (DBG_CARD_IO) \ + log_debug (DRVNAME t,(a),(b),(c));} while (0) +# define DEBUGOUT_CONT_1(t,a) do { if (DBG_CARD_IO) \ + log_printf (t,(a)); } while (0) +# define DEBUGOUT_CONT_3(t,a,b,c) do { if (DBG_CARD_IO) \ + log_printf (t,(a),(b),(c)); } while (0) +# define DEBUGOUT_LF() do { if (DBG_CARD_IO) \ + log_printf ("\n"); } while (0) + +#else /* Other usage of this source - don't use gnupg specifics. */ + +# define DEBUGOUT(t) fprintf (stderr, DRVNAME t) +# define DEBUGOUT_1(t,a) fprintf (stderr, DRVNAME t, (a)) +# define DEBUGOUT_2(t,a,b) fprintf (stderr, DRVNAME t, (a), (b)) +# define DEBUGOUT_3(t,a,b,c) fprintf (stderr, DRVNAME t, (a), (b), (c)) +# define DEBUGOUT_CONT_1(t,a) fprintf (stderr, t, (a)) +# define DEBUGOUT_CONT_3(t,a,b,c) fprintf (stderr, t, (a), (b), (c)) +# define DEBUGOUT_LF() putc ('\n', stderr) + +#endif /* This source not used by scdaemon. */ + + enum { RDR_to_PC_NotifySlotChange= 0x50, RDR_to_PC_HardwareError = 0x51, @@ -81,7 +151,8 @@ enum { struct ccid_driver_s { usb_dev_handle *idev; int seqno; - unsigned char t1_seqno; + unsigned char t1_ns; + unsigned char t1_nr; }; @@ -109,35 +180,34 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) rc = usb_create_match (&match, -1, -1, 11, -1, -1); if (rc) { - fprintf (stderr, "ccid-driver: usb_create_match failed: %d\n", rc); + DEBUGOUT_1 ("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); + DEBUGOUT_3 ("%-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); + DEBUGOUT_1 ("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); + DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); goto leave; } *handle = calloc (1, sizeof **handle); if (!*handle) { - fprintf (stderr, "ccid-driver: out of memory\n"); + DEBUGOUT ("out of memory\n"); rc = -1; goto leave; } @@ -194,22 +264,24 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen) return 0; if (rc == -1) - fprintf (stderr, "ccid-driver: usb_bulk_write error: %s\n", - strerror (errno)); + DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); else - fprintf (stderr, "ccid-driver: usb_bulk_write failed: %d\n", rc); + DEBUGOUT_1 ("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. */ + 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. Returns 0 on success. */ static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, - size_t *nread) + size_t *nread, int expected_type, int seqno) { - int rc; + int i, rc; + size_t msglen; rc = usb_bulk_read (handle->idev, 0x82, @@ -217,12 +289,40 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, 1000 /* ms timeout */ ); if (rc < 0) { - fprintf (stderr, "ccid-driver: usb_bulk_read error: %s\n", - strerror (errno)); + DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno)); + return -1; + } + + *nread = msglen = rc; + + if (msglen < 10) + { + DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen); + return -1; + } + if (buffer[0] != expected_type) + { + DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]); + return -1; + } + if (buffer[5] != 0) + { + DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]); + return -1; + } + if (buffer[6] != seqno) + { + DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n", + seqno, buffer[6]); return -1; } - *nread = rc; + DEBUGOUT_3 ("status: %02X error: %02X clock-status: %02X\n" + " data:", buffer[7], buffer[8], buffer[9] ); + for (i=10; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", buffer[i]); + DEBUGOUT_LF (); + return 0; } @@ -245,8 +345,7 @@ ccid_poll (ccid_driver_t handle) if (rc < 0) { - fprintf (stderr, "ccid-driver: usb_intr_read error: %s\n", - strerror (errno)); + DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno)); return -1; } @@ -255,29 +354,28 @@ ccid_poll (ccid_driver_t handle) if (msglen < 1) { - fprintf (stderr, "ccid-driver: intr-in msg too short\n"); + DEBUGOUT ("intr-in msg too short\n"); return -1; } if (msg[0] == RDR_to_PC_NotifySlotChange) { - fprintf (stderr, "ccid-driver: notify slot change:"); + DEBUGOUT ("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); + 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) { - fprintf (stderr, "ccid-driver: hardware error occured\n"); + DEBUGOUT ("hardware error occured\n"); } else { - fprintf (stderr, "ccid-driver: unknown intr-in msg of type %02X\n", - msg[0]); + DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]); } return 0; @@ -304,37 +402,9 @@ ccid_slot_status (ccid_driver_t handle) rc = bulk_out (handle, msg, 10); if (rc) return rc; - rc = bulk_in (handle, msg, sizeof msg, &msglen); + rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno); 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; } @@ -348,7 +418,6 @@ ccid_get_atr (ccid_driver_t handle, unsigned char msg[100]; size_t msglen; unsigned char seqno; - int i; msg[0] = PC_to_RDR_IccPowerOn; msg[5] = 0; /* slot */ @@ -362,40 +431,9 @@ ccid_get_atr (ccid_driver_t handle, rc = bulk_out (handle, msg, msglen); if (rc) return rc; - rc = bulk_in (handle, msg, sizeof msg, &msglen); + rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, seqno); 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) { @@ -434,7 +472,7 @@ ccid_get_atr (ccid_driver_t handle, 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. + the addresses of the nodes. PCB: Information Block (I-Block): @@ -470,20 +508,29 @@ ccid_transceive (ccid_driver_t handle, unsigned char *resp, size_t maxresplen, size_t *nresp) { int rc; - unsigned char msg[10+258], *tpdu, *p; - size_t msglen; + unsigned char send_buffer[10+258], recv_buffer[10+258]; + unsigned char *msg, *tpdu, *p; + size_t msglen, tpdulen, n; unsigned char seqno; int i; unsigned char crc; + size_t dummy_nresp; + int sending = 1; + + if (!nresp) + nresp = &dummy_nresp; + *nresp = 0; /* Construct an I-Block. */ if (apdulen > 254) return -1; /* Invalid length. */ + msg = send_buffer; + tpdu = msg+10; tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ - tpdu[1] = ((handle->t1_seqno & 1) << 6); /* I-block */ + tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ tpdu[2] = apdulen; memcpy (tpdu+3, apdu, apdulen); crc = 0; @@ -491,79 +538,148 @@ ccid_transceive (ccid_driver_t handle, crc ^= *p++; tpdu[3+apdulen] = crc; - handle->t1_seqno ^= 1; + tpdulen = apdulen + 4; - 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; + for (;;) + { + 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; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + fprintf (stderr, "T1: put %c-block seq=%d\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40))); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + msg = recv_buffer; + rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, + RDR_to_PC_DataBlock, seqno); + if (rc) + return rc; + + tpdu = msg + 10; + tpdulen = msglen - 10; + + if (tpdulen < 4) + { + DEBUGOUT ("cannot yet handle short block!!\n"); + return -1; + } - fprintf (stderr, "ccid-driver: sending"); - for (i=0; i < msglen; i++) - fprintf (stderr, " %02X", msg[i]); - putc ('\n', stderr); + fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), + ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0 + ); + + if (!(tpdu[1] & 0x80)) + { /* This is an I-block. */ + + if (sending) + { /* last block sent was successful. */ + handle->t1_ns ^= 1; + sending = 0; + } - 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; - } + if (!!(tpdu[1] & 0x40) != handle->t1_nr) + { /* Reponse does not match our sequence number. */ + msg = send_buffer; + tpdu = msg+10; + tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ + tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */ + tpdu[2] = 0; + tpdulen = 3; + for (crc=0,i=0,p=tpdu; i < tpdulen; i++) + crc ^= *p++; + tpdu[tpdulen++] = crc; + + continue; + } - 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); + handle->t1_nr ^= 1; - 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); + 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 ("provided buffer too short for received data\n"); + return -1; + } + + 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; + tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ + tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */ + tpdu[2] = 0; + tpdulen = 3; + for (crc=0,i=0,p=tpdu; i < tpdulen; i++) + crc ^= *p++; + tpdu[tpdulen++] = crc; + } - *nresp = n; - } + else if ((tpdu[1] & 0xc0) == 0x80) + { /* This is a R-block. */ + if ( (tpdu[1] & 0x0f)) + { /* Error: repeat last block */ + msg = send_buffer; + } + else + { + DEBUGOUT ("unxpectec ACK R-block received\n"); + return -1; + } + } + else + { /* This is a S-block. */ + DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n", + (tpdu[1] & 0x20)? "response": "request", + (tpdu[1] & 0x1f)); + if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2]) + { /* Wait time extension request. */ + unsigned char bwi = tpdu[3]; + msg = send_buffer; + tpdu = msg+10; + tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ + tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */ + tpdu[2] = 1; + tpdu[3] = bwi; + tpdulen = 4; + for (crc=0,i=0,p=tpdu; i < tpdulen; i++) + crc ^= *p++; + tpdu[tpdulen++] = crc; + DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi); + } + else + return -1; + } + } /* end T=1 protocol loop. */ return 0; } |