* configure.ac (HAVE_LIBUSB): Added a simple test for libusb.
[gnupg.git] / scd / ccid-driver.c
1 /* ccid-driver.c - USB ChipCardInterfaceDevices driver
2  *      Copyright (C) 2003 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21
22 /* CCID (ChipCardInterfaceDevices) is a specification for accessing
23    smartcard via a reader connected to the USB.  
24
25    This is a limited driver allowing to use some CCID drivers directly
26    without any other specila drivers. This is a fallback driver to be
27    used when nothing else works or the system should be kept minimal
28    for security reasons.  It makes use of the libusb library to gain
29    portable access to USB.
30
31    This driver has been tested with the SCM SCR335 smartcard reader
32    and requires that reader implements the TPDU level exchange and
33    does fully automatic initialization.
34 */
35
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
39
40 #if defined(HAVE_LIBUSB) || defined(TEST)
41 #include <errno.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <assert.h>
46
47 #include <usb.h>
48
49
50 #include "ccid-driver.h"
51
52 enum {
53   RDR_to_PC_NotifySlotChange= 0x50,
54   RDR_to_PC_HardwareError   = 0x51,
55
56   PC_to_RDR_SetParameters   = 0x61,
57   PC_to_RDR_IccPowerOn      = 0x62,
58   PC_to_RDR_IccPowerOff     = 0x63,
59   PC_to_RDR_GetSlotStatus   = 0x65,
60   PC_to_RDR_Secure          = 0x69,
61   PC_to_RDR_T0APDU          = 0x6a,
62   PC_to_RDR_Escape          = 0x6b,
63   PC_to_RDR_GetParameters   = 0x6c,
64   PC_to_RDR_ResetParameters = 0x6d,
65   PC_to_RDR_IccClock        = 0x6e,
66   PC_to_RDR_XfrBlock        = 0x6f,
67   PC_to_RDR_Mechanical      = 0x71,
68   PC_to_RDR_Abort           = 0x72,
69   PC_to_RDR_SetDataRate     = 0x73,
70
71   RDR_to_PC_DataBlock       = 0x80,
72   RDR_to_PC_SlotStatus      = 0x81,
73   RDR_to_PC_Parameters      = 0x82,
74   RDR_to_PC_Escape          = 0x83,
75   RDR_to_PC_DataRate        = 0x84
76 };
77
78
79 /* Store information on the driver's state.  A pointer to such a
80    structure is used as handle for most functions. */
81 struct ccid_driver_s {
82   usb_dev_handle *idev;
83   int seqno;
84   unsigned char t1_seqno;
85 };
86
87
88
89
90 /* Open the reader with the internal number READERNO and return a a
91    pointer to be used as handle in HANDLE.  Returns 0 on success. */
92 int 
93 ccid_open_reader (ccid_driver_t *handle, int readerno)
94 {
95   static int initialized;
96
97   int rc;
98   usb_match_handle *match = NULL;
99   struct usb_device *dev = NULL;
100   usb_dev_handle *idev = NULL;
101
102   *handle = NULL;
103   if (!initialized)
104     {
105       usb_init ();
106       initialized = 1;
107     }
108   
109   rc = usb_create_match (&match, -1, -1, 11, -1, -1);
110   if (rc)
111     {
112       fprintf (stderr, "ccid-driver: usb_create_match failed: %d\n", rc);
113       return -1;
114     }
115
116   while (usb_find_device(match, dev, &dev) >= 0) 
117     {
118       fprintf(stderr, "ccid-driver: %-40s %04X/%04X\n", dev->filename,
119               dev->descriptor->idVendor, dev->descriptor->idProduct);
120       if (!readerno)
121         {
122           rc = usb_open (dev, &idev);
123           if (rc)
124             {
125               fprintf (stderr, "ccid-driver: usb_open failed: %d\n", rc);
126               goto leave;
127             }
128
129           rc = usb_claim_interface (idev, 0);
130           if (rc)
131             {
132               fprintf (stderr, "ccid-driver: usb_claim_interface failed: %d\n",
133                        rc);
134               goto leave;
135             }
136
137           *handle = calloc (1, sizeof **handle);
138           if (!*handle)
139             {
140               fprintf (stderr, "ccid-driver: out of memory\n");
141               rc = -1;
142               goto leave;
143             }
144           (*handle)->idev = idev;
145           idev = NULL;
146           break;
147         }
148       readerno--;
149     }
150
151
152  leave:
153   if (idev)
154     usb_close (idev);
155   /* fixme: Do we need to release dev or is it supposed to be a
156      shallow copy of the list created internally by usb_init ? */
157   usb_free_match (match);
158
159   return rc;
160 }
161
162
163 /* Return False if a card is present and powered. */
164 int
165 ccid_check_card_presence (ccid_driver_t handle)
166 {
167
168   return -1;
169 }
170
171
172 static void
173 set_msg_len (unsigned char *msg, unsigned int length)
174 {
175   msg[1] = length;
176   msg[2] = length >> 8;
177   msg[3] = length >> 16;
178   msg[4] = length >> 24;
179 }
180
181
182 /* Write a MSG of length MSGLEN to the designated bulk out endpoint.
183    Returns 0 on success. */
184 static int
185 bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
186 {
187   int rc;
188
189   rc = usb_bulk_write (handle->idev, 
190                        1, /*endpoint */
191                        msg, msglen,
192                        1000 /* ms timeout */);
193   if (rc == msglen)
194     return 0;
195
196   if (rc == -1)
197     fprintf (stderr, "ccid-driver: usb_bulk_write error: %s\n",
198              strerror (errno));
199   else
200     fprintf (stderr, "ccid-driver: usb_bulk_write failed: %d\n", rc);
201   return -1;
202 }
203
204
205 /* Read a maximum of LENGTH bytes from the bulk in endpoint into
206    BUFFER and return the actual read number if bytes in NREAD.
207    Returns 0 on success. */
208 static int
209 bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
210          size_t *nread)
211 {
212   int rc;
213
214   rc = usb_bulk_read (handle->idev, 
215                       0x82,
216                       buffer, length,
217                       1000 /* ms timeout */ );
218   if (rc < 0)
219     {
220       fprintf (stderr, "ccid-driver: usb_bulk_read error: %s\n",
221                strerror (errno));
222       return -1;
223     }
224
225   *nread = rc;
226   return 0;
227 }
228
229
230 /* experimental */
231 int
232 ccid_poll (ccid_driver_t handle)
233 {
234   int rc;
235   unsigned char msg[10];
236   size_t msglen;
237   int i, j;
238
239   rc = usb_bulk_read (handle->idev, 
240                       0x83,
241                       msg, sizeof msg,
242                       0 /* ms timeout */ );
243   if (rc < 0 && errno == ETIMEDOUT)
244     return 0;
245
246   if (rc < 0)
247     {
248         fprintf (stderr, "ccid-driver: usb_intr_read error: %s\n",
249                  strerror (errno));
250       return -1;
251     }
252
253   msglen = rc;
254   rc = 0;
255
256   if (msglen < 1)
257     {
258       fprintf (stderr, "ccid-driver: intr-in msg too short\n");
259       return -1;
260     }
261
262   if (msg[0] == RDR_to_PC_NotifySlotChange)
263     {
264       fprintf (stderr, "ccid-driver: notify slot change:");
265       for (i=1; i < msglen; i++)
266         for (j=0; j < 4; j++)
267           fprintf (stderr, " %d:%c%c",
268                    (i-1)*4+j, 
269                    (msg[i] & (1<<(j*2)))? 'p':'-',
270                    (msg[i] & (2<<(j*2)))? '*':' ');
271       putc ('\n', stderr);
272     }
273   else if (msg[0] == RDR_to_PC_HardwareError)    
274     {
275       fprintf (stderr, "ccid-driver: hardware error occured\n");
276     }
277   else
278     {
279       fprintf (stderr, "ccid-driver: unknown intr-in msg of type %02X\n",
280                msg[0]);
281     }
282
283   return 0;
284 }
285
286
287
288 int 
289 ccid_slot_status (ccid_driver_t handle)
290 {
291   int rc;
292   unsigned char msg[100];
293   size_t msglen;
294   unsigned char seqno;
295
296   msg[0] = PC_to_RDR_GetSlotStatus;
297   msg[5] = 0; /* slot */
298   msg[6] = seqno = handle->seqno++;
299   msg[7] = 0; /* RFU */
300   msg[8] = 0; /* RFU */
301   msg[9] = 0; /* RFU */
302   set_msg_len (msg, 0);
303
304   rc = bulk_out (handle, msg, 10);
305   if (rc)
306     return rc;
307   rc = bulk_in (handle, msg, sizeof msg, &msglen);
308   if (rc)
309     return rc;
310   if (msglen < 10)
311     {
312       fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n",
313                (unsigned int)msglen);
314       return -1;
315     }
316   if (msg[0] != RDR_to_PC_SlotStatus)
317     {
318       fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n",
319                msg[0]);
320       return -1;
321     }
322   if (msg[5] != 0)    
323     {
324       fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n",
325                msg[5]);
326       return -1;
327     }
328   if (msg[6] != seqno)    
329     {
330       fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n",
331                seqno, msg[6]);
332       return -1;
333     }
334
335   fprintf (stderr,
336            "ccid-driver: status: %02X  error: %02X clock-status: %02X\n",
337            msg[7], msg[8], msg[9] );
338
339   return 0;
340 }
341
342
343 int 
344 ccid_get_atr (ccid_driver_t handle,
345               unsigned char *atr, size_t maxatrlen, size_t *atrlen)
346 {
347   int rc;
348   unsigned char msg[100];
349   size_t msglen;
350   unsigned char seqno;
351   int i;
352
353   msg[0] = PC_to_RDR_IccPowerOn;
354   msg[5] = 0; /* slot */
355   msg[6] = seqno = handle->seqno++;
356   msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
357   msg[8] = 0; /* RFU */
358   msg[9] = 0; /* RFU */
359   set_msg_len (msg, 0);
360   msglen = 10;
361
362   rc = bulk_out (handle, msg, msglen);
363   if (rc)
364     return rc;
365   rc = bulk_in (handle, msg, sizeof msg, &msglen);
366   if (rc)
367     return rc;
368   if (msglen < 10)
369     {
370       fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n",
371                (unsigned int)msglen);
372       return -1;
373     }
374   if (msg[0] != RDR_to_PC_DataBlock)
375     {
376       fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n",
377                msg[0]);
378       return -1;
379     }
380   if (msg[5] != 0)    
381     {
382       fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n",
383                msg[5]);
384       return -1;
385     }
386   if (msg[6] != seqno)    
387     {
388       fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n",
389                seqno, msg[6]);
390       return -1;
391     }
392
393   fprintf (stderr,
394            "ccid-driver: status: %02X  error: %02X clock-status: %02X\n"
395            "               data:",  msg[7], msg[8], msg[9] );
396   for (i=10; i < msglen; i++)
397     fprintf (stderr, " %02X", msg[i]);
398   putc ('\n', stderr);
399   
400   if (atr)
401     {
402       size_t n = msglen - 10;
403
404       if (n > maxatrlen)
405         n = maxatrlen;
406       memcpy (atr, msg+10, n);
407       *atrlen = n;
408     }
409
410   return 0;
411 }
412
413
414 \f
415 /*
416   Protocol T=1 overview
417
418   Block Structure:
419            Prologue Field:
420    1 byte     Node Address (NAD) 
421    1 byte     Protocol Control Byte (PCB)
422    1 byte     Length (LEN) 
423            Information Field:
424    0-254 byte APDU or Control Information (INF)
425            Epilogue Field:
426    1 byte     Error Detection Code (EDC)
427
428   NAD:  
429    bit 7     unused
430    bit 4..6  Destination Node Address (DAD)
431    bit 3     unused
432    bit 2..0  Source Node Address (SAD)
433
434    If node adresses are not used, SAD and DAD should be set to 0 on
435    the first block sent to the card.  If they are used they should
436    have different values (0 for one is okay); that first block sets up
437    the addresses of the node.
438
439   PCB:
440    Information Block (I-Block):
441       bit 7    0
442       bit 6    Sequence number (yep, that is modulo 2)
443       bit 5    Chaining flag 
444       bit 4..0 reserved
445    Received-Ready Block (R-Block):
446       bit 7    1
447       bit 6    0
448       bit 5    0
449       bit 4    Sequence number
450       bit 3..0  0 = no error
451                 1 = EDC or parity error
452                 2 = other error
453                 other values are reserved
454    Supervisory Block (S-Block):
455       bit 7    1
456       bit 6    1
457       bit 5    clear=request,set=response
458       bit 4..0  0 = resyncronisation request
459                 1 = information field size request
460                 2 = abort request
461                 3 = extension of BWT request
462                 4 = VPP error
463                 other values are reserved
464
465 */
466
467 int
468 ccid_transceive (ccid_driver_t handle,
469                  const unsigned char *apdu, size_t apdulen,
470                  unsigned char *resp, size_t maxresplen, size_t *nresp)
471 {
472   int rc;
473   unsigned char msg[10+258], *tpdu, *p;
474   size_t msglen;
475   unsigned char seqno;
476   int i;
477   unsigned char crc;
478
479   
480   /* Construct an I-Block. */
481   if (apdulen > 254)
482     return -1; /* Invalid length. */
483
484   tpdu = msg+10;
485   tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
486   tpdu[1] = ((handle->t1_seqno & 1) << 6); /* I-block */
487   tpdu[2] = apdulen;
488   memcpy (tpdu+3, apdu, apdulen);
489   crc = 0;
490   for (i=0,p=tpdu; i < apdulen+3; i++)
491     crc ^= *p++;
492   tpdu[3+apdulen] = crc;
493
494   handle->t1_seqno ^= 1;
495
496   msg[0] = PC_to_RDR_XfrBlock;
497   msg[5] = 0; /* slot */
498   msg[6] = seqno = handle->seqno++;
499   msg[7] = 4; /* bBWI */
500   msg[8] = 0; /* RFU */
501   msg[9] = 0; /* RFU */
502   set_msg_len (msg, apdulen+4);
503   msglen = 10 + apdulen + 4;
504
505   fprintf (stderr, "ccid-driver: sending");
506   for (i=0; i < msglen; i++)
507     fprintf (stderr, " %02X", msg[i]);
508   putc ('\n', stderr);
509
510   rc = bulk_out (handle, msg, msglen);
511   if (rc)
512     return rc;
513   rc = bulk_in (handle, msg, sizeof msg, &msglen);
514   if (rc)
515     return rc;
516   if (msglen < 10)
517     {
518       fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n",
519                (unsigned int)msglen);
520       return -1;
521     }
522   if (msg[0] != RDR_to_PC_DataBlock)
523     {
524       fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n",
525                msg[0]);
526       return -1;
527     }
528   if (msg[5] != 0)    
529     {
530       fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n",
531                msg[5]);
532       return -1;
533     }
534   if (msg[6] != seqno)    
535     {
536       fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n",
537                seqno, msg[6]);
538       return -1;
539     }
540
541   fprintf (stderr,
542            "ccid-driver: status: %02X  error: %02X clock-status: %02X\n"
543            "               data:",  msg[7], msg[8], msg[9] );
544   for (i=10; i < msglen; i++)
545     fprintf (stderr, " %02X", msg[i]);
546   putc ('\n', stderr);
547
548   if (resp)
549     {
550       size_t n = msglen - 10;
551       
552       if (n < 4)
553         n = 0; /* fixme: this is an empty I-block or some other block
554                   - we ignore it for now until we have implemented the
555                   T=1 machinery. */
556       else
557         {
558           p = msg + 10 + 3; /* Skip ccid header and prologue field. */
559           n -= 3;
560           n--; /* Strip the epilogue field. */
561           if (n > maxresplen)
562             n = maxresplen; /* fixme: return an error instead of truncating. */
563           memcpy (resp, p, n);
564         }
565       *nresp = n;
566     }
567
568   return 0;
569 }
570
571
572
573
574 #ifdef TEST
575 int
576 main (int argc, char **argv)
577 {
578   int rc;
579   ccid_driver_t ccid;
580
581   rc = ccid_open_reader (&ccid, 0);
582   if (rc)
583     return 1;
584
585   ccid_poll (ccid);
586   fputs ("getting ATR ...\n", stderr);
587   rc = ccid_get_atr (ccid, NULL, 0, NULL);
588   if (rc)
589     return 1;
590
591   ccid_poll (ccid);
592   fputs ("getting slot status ...\n", stderr);
593   rc = ccid_slot_status (ccid);
594   if (rc)
595     return 1;
596
597   ccid_poll (ccid);
598
599   {
600     static unsigned char apdu[] = {
601       0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01};
602   rc = ccid_transceive (ccid,
603                         apdu, sizeof apdu,
604                         NULL, 0, NULL);
605   }
606   ccid_poll (ccid);
607
608   {
609     static unsigned char apdu[] = {
610       0, 0xCA, 0, 0x65, 254 };
611   rc = ccid_transceive (ccid,
612                         apdu, sizeof apdu,
613                         NULL, 0, NULL);
614   }
615   ccid_poll (ccid);
616
617
618   return 0;
619 }
620
621 /*
622  * Local Variables:
623  *  compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c"
624  * End:
625  */
626 #endif /*TEST*/
627 #endif /*HAVE_LIBUSB*/