* g10.c (main): New commands --card-edit, --card-status and
authorWerner Koch <wk@gnupg.org>
Sun, 28 Sep 2003 13:41:58 +0000 (13:41 +0000)
committerWerner Koch <wk@gnupg.org>
Sun, 28 Sep 2003 13:41:58 +0000 (13:41 +0000)
--change-pin.  New options --ctapi-driver, --pcsc-driver and
--disable-ccid
* options.h (DBG_CARD_IO): New.
* cardglue.c, cardclue.h: Enhanced.
* card-util.c: New. Taken from current the gnupg 1.9 branch.
* app-common.h, app-openpgp.c, iso7816.c, iso7816.h, apdu.c
* apdu.h, ccid-driver.c, ccid-driver.h: New.  Takem from the current
gnupg 1.9 branch withy minor changes to include directives.
* Makefile.am: Added these files.

15 files changed:
g10/ChangeLog
g10/Makefile.am
g10/apdu.c [new file with mode: 0644]
g10/apdu.h [new file with mode: 0644]
g10/app-common.h [new file with mode: 0644]
g10/app-openpgp.c [new file with mode: 0644]
g10/card-util.c [new file with mode: 0644]
g10/cardglue.c
g10/cardglue.h
g10/ccid-driver.c [new file with mode: 0644]
g10/ccid-driver.h [new file with mode: 0644]
g10/g10.c
g10/iso7816.c [new file with mode: 0644]
g10/iso7816.h [new file with mode: 0644]
g10/options.h

index 4c71b2e..a0905ed 100644 (file)
@@ -1,10 +1,22 @@
+2003-09-28  Werner Koch  <wk@gnupg.org>
+
+       * g10.c (main): New commands --card-edit, --card-status and
+       --change-pin.  New options --ctapi-driver, --pcsc-driver and
+       --disable-ccid
+       * options.h (DBG_CARD_IO): New.
+       * cardglue.c, cardclue.h: Enhanced.
+       * card-util.c: New. Taken from current the gnupg 1.9 branch.
+       * app-common.h, app-openpgp.c, iso7816.c, iso7816.h, apdu.c
+       * apdu.h, ccid-driver.c, ccid-driver.h: New.  Takem from the current
+       gnupg 1.9 branch withy minor changes to include directives.
+       * Makefile.am: Added these files.
+       
 2003-09-27  Werner Koch  <wk@gnupg.org>
 
        * sign.c (do_sign) [ENABLE_CARD_SUPPORT]: Divert to card.
        * cardglue.c, cardglue.h: New.
        * Makefile.am (gpg_LDADD): Added.
        (card_support_sources): New.
-
        
 2003-09-25  David Shaw  <dshaw@jabberwocky.com>
 
index 4105a7e..2be9e35 100644 (file)
@@ -64,7 +64,13 @@ common_source =  \
              signal.c
 
 card_support_source = \
-              cardglue.c cardclue.h
+       cardglue.c cardclue.h \
+       card-util.c \
+       app-common.h \
+       app-openpgp.c \
+       iso7816.c iso7816.h \
+       apdu.c apdu.h \
+       ccid-driver.c ccid-driver.h
 
 
 gpg_SOURCES  = g10.c           \
diff --git a/g10/apdu.c b/g10/apdu.c
new file mode 100644 (file)
index 0000000..0689498
--- /dev/null
@@ -0,0 +1,1193 @@
+/* apdu.c - ISO 7816 APDU functions and low level I/O
+ *     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
+ */
+
+#include <config.h>
+#ifdef ENABLE_CARD_SUPPORT
+/* 
+   Note, that most of this code has been taken from 1.9.x branch
+   and is maintained over there if at all possible.  Thus, if you make
+   changes here, please check that a similar change has been commited
+   to the 1.9.x branch.
+*/
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#ifdef HAVE_OPENSC
+# include <opensc/opensc.h>
+#endif
+
+#include "options.h"
+#include "errors.h"
+#include "memory.h"
+#include "util.h"
+#include "i18n.h"
+#include "cardglue.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
+                                  insertion of the card (1 = don't wait). */
+
+
+/* A structure to collect information pertaining to one reader
+   slot. */
+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;
+    unsigned long card;
+    unsigned long protocol;
+  } pcsc;
+#ifdef HAVE_OPENSC
+  int is_osc;          /* We are using the OpenSC driver layer. */
+  struct {
+    struct sc_context *ctx;
+    struct sc_card *scard;
+  } osc;
+#endif /*HAVE_OPENSC*/
+  int status;
+  unsigned char atr[33];
+  size_t atrlen;
+};
+typedef struct reader_table_s *reader_table_t;
+
+/* A global table to keep track of active readers. */
+static struct reader_table_s reader_table[MAX_READER];
+
+
+/* ct API function pointer. */
+static char (*CT_init) (unsigned short ctn, unsigned short Pn);
+static char (*CT_data) (unsigned short ctn, unsigned char *dad,
+                        unsigned char *sad, unsigned short lc,
+                        unsigned char *cmd, unsigned short *lr,
+                        unsigned char *rsp);
+static char (*CT_close) (unsigned short ctn);
+
+/* PC/SC constants and function pointer. */
+#define PCSC_SCOPE_USER      0 
+#define PCSC_SCOPE_TERMINAL  1 
+#define PCSC_SCOPE_SYSTEM    2 
+#define PCSC_SCOPE_GLOBAL    3 
+
+#define PCSC_PROTOCOL_T0     1 
+#define PCSC_PROTOCOL_T1     2 
+#define PCSC_PROTOCOL_RAW    4 
+
+#define PCSC_SHARE_EXCLUSIVE 1
+#define PCSC_SHARE_SHARED    2
+#define PCSC_SHARE_DIRECT    3
+
+#define PCSC_LEAVE_CARD      0
+#define PCSC_RESET_CARD      1
+#define PCSC_UNPOWER_CARD    2
+#define PCSC_EJECT_CARD      3
+
+struct pcsc_io_request_s {
+  unsigned long protocol; 
+  unsigned long pci_len;
+};
+
+typedef struct pcsc_io_request_s *pcsc_io_request_t;
+
+long (*pcsc_establish_context) (unsigned long scope,
+                                const void *reserved1,
+                                const void *reserved2,
+                                unsigned long *r_context);
+long (*pcsc_release_context) (unsigned long context);
+long (*pcsc_list_readers) (unsigned long context, const char *groups,
+                        char *readers, unsigned long *readerslen);
+long (*pcsc_connect) (unsigned long context,
+                      const char *reader,
+                      unsigned long share_mode,
+                      unsigned long preferred_protocols,
+                      unsigned long *r_card,
+                      unsigned long *r_active_protocol);
+long (*pcsc_disconnect) (unsigned long card, unsigned long disposition);
+long (*pcsc_status) (unsigned long card,
+                     char *reader, unsigned long *readerlen,
+                     unsigned long *r_state, unsigned long *r_protocol,
+                     unsigned char *atr, unsigned long *atrlen);
+long (*pcsc_begin_transaction) (unsigned long card);
+long (*pcsc_end_transaction) (unsigned long card);
+long (*pcsc_transmit) (unsigned long card,
+                       const pcsc_io_request_t send_pci,
+                       const unsigned char *send_buffer,
+                       unsigned long send_len,
+                       pcsc_io_request_t recv_pci,
+                       unsigned char *recv_buffer,
+                       unsigned long *recv_len);
+long (*pcsc_set_timeout) (unsigned long context, unsigned long timeout);
+
+
+
+
+\f
+/* 
+      Helper
+ */
+
+/* Find an unused reader slot for PORTSTR and put it into the reader
+   table.  Return -1 on error or the index into the reader table. */
+static int 
+new_reader_slot (void)    
+{
+  int i, reader = -1;
+
+  for (i=0; i < MAX_READER; i++)
+    {
+      if (!reader_table[i].used && reader == -1)
+        reader = i;
+    }
+  if (reader == -1)
+    {
+      log_error ("new_reader_slot: out of slots\n");
+      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;
+#endif
+  return reader;
+}
+
+
+static void
+dump_reader_status (int reader)
+{
+  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" :
+                reader_table[reader].status == 0? "Memory ICC present" :
+                "ICC not present" );
+    }
+  else
+    {
+      log_info ("reader slot %d: active protocol:", reader);
+      if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_T0))
+        log_printf (" T0");
+      else if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_T1))
+        log_printf (" T1");
+      else if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_RAW))
+        log_printf (" raw");
+      log_printf ("\n");
+    }
+
+  if (reader_table[reader].status != -1)
+    {
+      log_info ("reader %d: ATR=", reader);
+      log_printhex ("", reader_table[reader].atr,
+                    reader_table[reader].atrlen);
+    }
+}
+
+
+\f
+/* 
+       ct API Interface 
+ */
+
+static const char *
+ct_error_string (long err)
+{
+  switch (err)
+    {
+    case 0: return "okay";
+    case -1: return "invalid data";
+    case -8: return "ct error";
+    case -10: return "transmission error";
+    case -11: return "memory allocation error";
+    case -128: return "HTSI error";
+    default: return "unknown CT-API error";
+    }
+}
+
+/* Wait for the card in READER and activate it.  Return -1 on error or
+   0 on success. */
+static int
+ct_activate_card (int reader)
+{
+  int rc, count;
+
+  for (count = 0; count < CARD_CONNECT_TIMEOUT; count++)
+    {
+      unsigned char dad[1], sad[1], cmd[11], buf[256];
+      unsigned short buflen;
+
+      if (count)
+        ; /* FIXME: we should use a more reliable timer than sleep. */
+
+      /* Check whether card has been inserted. */
+      dad[0] = 1;     /* Destination address: CT. */    
+      sad[0] = 2;     /* Source address: Host. */
+
+      cmd[0] = 0x20;  /* Class byte. */
+      cmd[1] = 0x13;  /* Request status. */
+      cmd[2] = 0x00;  /* From kernel. */
+      cmd[3] = 0x80;  /* Return card's DO. */
+      cmd[4] = 0x00;
+
+      buflen = DIM(buf);
+
+      rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf);
+      if (rc || buflen < 2 || buf[buflen-2] != 0x90)
+        {
+          log_error ("ct_activate_card: can't get status of reader %d: %s\n",
+                     reader, ct_error_string (rc));
+          return -1;
+        }
+
+      /* Connected, now activate the card. */           
+      dad[0] = 1;    /* Destination address: CT. */    
+      sad[0] = 2;    /* Source address: Host. */
+
+      cmd[0] = 0x20;  /* Class byte. */
+      cmd[1] = 0x12;  /* Request ICC. */
+      cmd[2] = 0x01;  /* From first interface. */
+      cmd[3] = 0x01;  /* Return card's ATR. */
+      cmd[4] = 0x00;
+
+      buflen = DIM(buf);
+
+      rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf);
+      if (rc || buflen < 2 || buf[buflen-2] != 0x90)
+        {
+          log_error ("ct_activate_card(%d): activation failed: %s\n",
+                     reader, ct_error_string (rc));
+          return -1;
+        }
+
+      /* Store the type and the ATR. */
+      if (buflen - 2 > DIM (reader_table[0].atr))
+        {
+          log_error ("ct_activate_card(%d): ATR too long\n", reader);
+          return -1;
+        }
+
+      reader_table[reader].status = buf[buflen - 1];
+      memcpy (reader_table[reader].atr, buf, buflen - 2);
+      reader_table[reader].atrlen = buflen - 2;
+      return 0;
+    }
+  log_info ("ct_activate_card(%d): timeout waiting for card\n", reader);
+  return -1;
+}
+
+
+/* Open a reader and return an internal handle for it.  PORT is a
+   non-negative value with the port number of the reader. USB readers
+   do have port numbers starting at 32769. */
+static int
+open_ct_reader (int port)
+{
+  int rc, reader;
+
+  if (port < 0 || port > 0xffff)
+    {
+      log_error ("open_ct_reader: invalid port %d requested\n", port);
+      return -1;
+    }
+  reader = new_reader_slot ();
+  if (reader == -1)
+    return reader;
+  reader_table[reader].port = port;
+
+  rc = CT_init (reader, (unsigned short)port);
+  if (rc)
+    {
+      log_error ("apdu_open_ct_reader failed on port %d: %s\n",
+                 port, ct_error_string (rc));
+      reader_table[reader].used = 0;
+      return -1;
+    }
+
+  rc = ct_activate_card (reader);
+  if (rc)
+    {
+      reader_table[reader].used = 0;
+      return -1;
+    }
+
+  reader_table[reader].is_ctapi = 1;
+  dump_reader_status (reader);
+  return reader;
+}
+
+
+/* Actually send the APDU of length APDULEN to SLOT and return a
+   maximum of *BUFLEN data in BUFFER, the actual retruned size will be
+   set to BUFLEN.  Returns: CT API error code. */
+static int
+ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+              unsigned char *buffer, size_t *buflen)
+{
+  int rc;
+  unsigned char dad[1], sad[1];
+  unsigned short ctbuflen;
+  
+  dad[0] = 0;     /* Destination address: Card. */    
+  sad[0] = 2;     /* Source address: Host. */
+  ctbuflen = *buflen;
+  if (DBG_CARD_IO)
+    log_printhex ("  CT_data:", apdu, apdulen);
+  rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer);
+  *buflen = ctbuflen;
+
+  /* FIXME: map the errorcodes to GNUPG ones, so that they can be
+     shared between CTAPI and PCSC. */
+  return rc;
+}
+
+
+\f
+static const char *
+pcsc_error_string (long err)
+{
+  const char *s;
+
+  if (!err)
+    return "okay";
+  if ((err & 0x80100000) != 0x80100000)
+    return "invalid PC/SC error code";
+  err &= 0xffff;
+  switch (err)
+    {
+    case 0x0002: s = "cancelled"; break;
+    case 0x000e: s = "can't dispose"; break;
+    case 0x0008: s = "insufficient buffer"; break;   
+    case 0x0015: s = "invalid ATR"; break;
+    case 0x0003: s = "invalid handle"; break;
+    case 0x0004: s = "invalid parameter"; break; 
+    case 0x0005: s = "invalid target"; break;
+    case 0x0011: s = "invalid value"; break; 
+    case 0x0006: s = "no memory"; break;  
+    case 0x0013: s = "comm error"; break;      
+    case 0x0001: s = "internal error"; break;     
+    case 0x0014: s = "unknown error"; break; 
+    case 0x0007: s = "waited too long"; break;  
+    case 0x0009: s = "unknown reader"; break;
+    case 0x000a: s = "timeout"; break; 
+    case 0x000b: s = "sharing violation"; break;       
+    case 0x000c: s = "no smartcard"; break;
+    case 0x000d: s = "unknown card"; break;   
+    case 0x000f: s = "proto mismatch"; break;          
+    case 0x0010: s = "not ready"; break;               
+    case 0x0012: s = "system cancelled"; break;        
+    case 0x0016: s = "not transacted"; break;
+    case 0x0017: s = "reader unavailable"; break; 
+    case 0x0065: s = "unsupported card"; break;        
+    case 0x0066: s = "unresponsive card"; break;       
+    case 0x0067: s = "unpowered card"; break;          
+    case 0x0068: s = "reset card"; break;              
+    case 0x0069: s = "removed card"; break;            
+    case 0x006a: s = "inserted card"; break;           
+    case 0x001f: s = "unsupported feature"; break;     
+    case 0x0019: s = "PCI too small"; break;           
+    case 0x001a: s = "reader unsupported"; break;      
+    case 0x001b: s = "duplicate reader"; break;        
+    case 0x001c: s = "card unsupported"; break;        
+    case 0x001d: s = "no service"; break;              
+    case 0x001e: s = "service stopped"; break;      
+    default:     s = "unknown PC/SC error code"; break;
+    }
+  return s;
+}
+
+/* 
+       PC/SC Interface
+ */
+
+static int
+open_pcsc_reader (const char *portstr)
+{
+  long err;
+  int slot;
+  char *list = NULL;
+  unsigned long nreader, listlen, atrlen;
+  char *p;
+  unsigned long card_state, card_protocol;
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+
+  err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
+                                &reader_table[slot].pcsc.context);
+  if (err)
+    {
+      log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      reader_table[slot].used = 0;
+      return -1;
+    }
+  
+  err = pcsc_list_readers (reader_table[slot].pcsc.context,
+                           NULL, NULL, &nreader);
+  if (!err)
+    {
+      list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */
+      if (!list)
+        {
+          log_error ("error allocating memory for reader list\n");
+          pcsc_release_context (reader_table[slot].pcsc.context);
+          reader_table[slot].used = 0;
+          return -1;
+        }
+      err = pcsc_list_readers (reader_table[slot].pcsc.context,
+                               NULL, list, &nreader);
+    }
+  if (err)
+    {
+      log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      pcsc_release_context (reader_table[slot].pcsc.context);
+      reader_table[slot].used = 0;
+      xfree (list);
+      return -1;
+    }
+
+  listlen = nreader;
+  p = list;
+  while (nreader)
+    {
+      if (!*p && !p[1])
+        break;
+      log_info ("detected reader `%s'\n", p);
+      if (nreader < (strlen (p)+1))
+        {
+          log_error ("invalid response from pcsc_list_readers\n");
+          break;
+        }
+      nreader -= strlen (p)+1;
+      p += strlen (p) + 1;
+    }
+
+  err = pcsc_connect (reader_table[slot].pcsc.context,
+                      portstr? portstr : list,
+                      PCSC_SHARE_EXCLUSIVE,
+                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+                      &reader_table[slot].pcsc.card,
+                      &reader_table[slot].pcsc.protocol);
+  if (err)
+    {
+      log_error ("pcsc_connect failed: %s (0x%lx)\n",
+                  pcsc_error_string (err), err);
+      pcsc_release_context (reader_table[slot].pcsc.context);
+      reader_table[slot].used = 0;
+      xfree (list);
+      return -1;
+    }      
+  
+  atrlen = 32;
+  /* (We need to pass a dummy buffer.  We use LIST because it ought to
+     be large enough.) */
+  err = pcsc_status (reader_table[slot].pcsc.card,
+                     list, &listlen,
+                     &card_state, &card_protocol,
+                     reader_table[slot].atr, &atrlen);
+  xfree (list);
+  if (err)
+    {
+      log_error ("pcsc_status failed: %s (0x%lx)\n",
+                  pcsc_error_string (err), err);
+      pcsc_release_context (reader_table[slot].pcsc.context);
+      reader_table[slot].used = 0;
+      return -1;
+    }
+  if (atrlen >= DIM (reader_table[0].atr))
+    log_bug ("ATR returned by pcsc_status is too large\n");
+  reader_table[slot].atrlen = atrlen;
+/*   log_debug ("state    from pcsc_status: 0x%lx\n", card_state); */
+/*   log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */
+
+  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: CT API error code. */
+static int
+pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+                unsigned char *buffer, size_t *buflen)
+{
+  long err;
+  struct pcsc_io_request_s send_pci;
+  unsigned long recv_len;
+  
+  if (DBG_CARD_IO)
+    log_printhex ("  PCSC_data:", apdu, apdulen);
+
+  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+      send_pci.protocol = PCSC_PROTOCOL_T1;
+  else
+      send_pci.protocol = PCSC_PROTOCOL_T0;
+  send_pci.pci_len = sizeof send_pci;
+  recv_len = *buflen;
+  err = pcsc_transmit (reader_table[slot].pcsc.card,
+                       &send_pci, apdu, apdulen,
+                       NULL, buffer, &recv_len);
+  *buflen = recv_len;
+  if (err)
+    log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+  
+  return err? -1:0; /* FIXME: Return appropriate error code. */
+}
+
+\f
+#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 */
+
+
+\f
+#ifdef HAVE_OPENSC
+/* 
+     OpenSC Interface.
+
+     This uses the OpenSC primitives to send APDUs.  We need this
+     because we can't mix OpenSC and native (i.e. ctAPI or PC/SC)
+     access to a card for resource conflict reasons.
+ */
+
+static int
+open_osc_reader (int portno)
+{
+  int err;
+  int slot;
+  reader_table_t slotp;
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
+
+  err = sc_establish_context (&slotp->osc.ctx, "scdaemon");
+  if (err)
+    {
+      log_error ("failed to establish SC context: %s\n", sc_strerror (err));
+      slotp->used = 0;
+      return -1;
+    }
+  if (portno < 0 || portno >= slotp->osc.ctx->reader_count)
+    {
+      log_error ("no card reader available\n");
+      sc_release_context (slotp->osc.ctx);
+      slotp->used = 0;
+      return -1;
+    }
+
+  /* Redirect to our logging facility. */
+  slotp->osc.ctx->error_file = log_get_stream ();
+  slotp->osc.ctx->debug = opt.debug_sc;
+  slotp->osc.ctx->debug_file = log_get_stream ();
+
+  if (sc_detect_card_presence (slotp->osc.ctx->reader[portno], 0) != 1)
+    {
+      log_error ("no card present\n");
+      sc_release_context (slotp->osc.ctx);
+      slotp->used = 0;
+      return -1;
+    }
+  
+  /* We want the standard ISO driver. */
+  /*FIXME: OpenSC does not like "iso7816", so we use EMV for now. */
+  err = sc_set_card_driver(slotp->osc.ctx, "emv");
+  if (err)
+    {
+      log_error ("failed to select the iso7816 driver: %s\n",
+                 sc_strerror (err));
+      sc_release_context (slotp->osc.ctx);
+      slotp->used = 0;
+      return -1;
+    }
+
+  /* Now connect the card and hope that OpenSC won't try to be too
+     smart. */
+  err = sc_connect_card (slotp->osc.ctx->reader[portno], 0,
+                         &slotp->osc.scard);
+  if (err)
+    {
+      log_error ("failed to connect card in reader %d: %s\n",
+                 portno, sc_strerror (err));
+      sc_release_context (slotp->osc.ctx);
+      slotp->used = 0;
+      return -1;
+    }
+  if (opt.verbose)
+    log_info ("connected to card in opensc reader %d using driver `%s'\n",
+              portno, slotp->osc.scard->driver->name);
+
+  err = sc_lock (slotp->osc.scard);
+  if (err)
+    {
+      log_error ("can't lock card in reader %d: %s\n",
+                 portno, sc_strerror (err));
+      sc_disconnect_card (slotp->osc.scard, 0);
+      sc_release_context (slotp->osc.ctx);
+      slotp->used = 0;
+      return -1;
+    }
+
+  if (slotp->osc.scard->atr_len >= DIM (slotp->atr))
+    log_bug ("ATR returned by opensc is too large\n");
+  slotp->atrlen = slotp->osc.scard->atr_len;
+  memcpy (slotp->atr, slotp->osc.scard->atr, slotp->atrlen);
+
+  slotp->is_osc = 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: OpenSC error code. */
+static int
+osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+                unsigned char *buffer, size_t *buflen)
+{
+  long err;
+  struct sc_apdu a;
+  unsigned char data[SC_MAX_APDU_BUFFER_SIZE];
+  unsigned char result[SC_MAX_APDU_BUFFER_SIZE];
+
+  if (DBG_CARD_IO)
+    log_printhex ("  APDU_data:", apdu, apdulen);
+
+  if (apdulen < 4)
+    {
+      log_error ("osc_send_apdu: APDU is too short\n");
+      return SC_ERROR_CMD_TOO_SHORT;
+    }
+
+  memset(&a, 0, sizeof a);
+  a.cla = *apdu++;
+  a.ins = *apdu++;
+  a.p1 = *apdu++;
+  a.p2 = *apdu++;
+  apdulen -= 4;
+
+  if (!apdulen)
+    a.cse = SC_APDU_CASE_1;
+  else if (apdulen == 1) 
+    {
+      a.le = *apdu? *apdu : 256;
+      apdu++; apdulen--;
+      a.cse = SC_APDU_CASE_2_SHORT;
+    }
+  else
+    {
+      a.lc = *apdu++; apdulen--;
+      if (apdulen < a.lc)
+        {
+          log_error ("osc_send_apdu: APDU shorter than specified in Lc\n");
+          return SC_ERROR_CMD_TOO_SHORT;
+
+        }
+      memcpy(data, apdu, a.lc);
+      apdu += a.lc; apdulen -= a.lc;
+
+      a.data = data;
+      a.datalen = a.lc;
+      
+      if (!apdulen)
+        a.cse = SC_APDU_CASE_3_SHORT;
+      else
+        {
+          a.le = *apdu? *apdu : 256;
+          apdu++; apdulen--;
+          if (apdulen)
+            {
+              log_error ("osc_send_apdu: APDU larger than specified\n");
+              return SC_ERROR_CMD_TOO_LONG;
+            }
+          a.cse = SC_APDU_CASE_4_SHORT;
+        }
+    }
+
+  a.resp = result;
+  a.resplen = DIM(result);
+
+  err = sc_transmit_apdu (reader_table[slot].osc.scard, &a);
+  if (err)
+    {
+      log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err));
+      return err;
+    }
+
+  if (*buflen < 2 || a.resplen > *buflen - 2)
+    {
+      log_error ("osc_send_apdu: provided buffer too short to store result\n");
+      return SC_ERROR_BUFFER_TOO_SMALL;
+    }
+  memcpy (buffer, a.resp, a.resplen);
+  buffer[a.resplen] = a.sw1;
+  buffer[a.resplen+1] = a.sw2;
+  *buflen = a.resplen + 2;
+  return 0;
+}
+
+#endif /* HAVE_OPENSC */
+
+
+\f
+/* 
+       Driver Access
+ */
+
+/* Open the reader and return an internal slot number or -1 on
+   error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
+   the first USB reader.  For PC/SC the first listed reader).  If
+   OpenSC support is compiled in, we first try to use OpenSC. */
+int
+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 /* HAVE_LIBUSB */
+
+#ifdef HAVE_OPENSC
+  if (!opt.disable_opensc)
+    {
+      int port = portstr? atoi (portstr) : 0;
+
+      return open_osc_reader (port);
+    }
+#endif /* HAVE_OPENSC */  
+
+
+  if (opt.ctapi_driver && *opt.ctapi_driver)
+    {
+      int port = portstr? atoi (portstr) : 32768;
+
+      if (!ct_api_loaded)
+        {
+          void *handle;
+          
+          handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
+          if (!handle)
+            {
+              log_error ("apdu_open_reader: failed to open driver: %s",
+                         dlerror ());
+              return -1;
+            }
+          CT_init = dlsym (handle, "CT_init");
+          CT_data = dlsym (handle, "CT_data");
+          CT_close = dlsym (handle, "CT_close");
+          if (!CT_init || !CT_data || !CT_close)
+            {
+              log_error ("apdu_open_reader: invalid ctAPI driver\n");
+              dlclose (handle);
+              return -1;
+            }
+          ct_api_loaded = 1;
+        }
+      return open_ct_reader (port);
+    }
+
+  
+  /* No ctAPI configured, so lets try the PC/SC API */
+  if (!pcsc_api_loaded)
+    {
+      void *handle;
+
+      handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
+      if (!handle)
+        {
+          log_error ("apdu_open_reader: failed to open driver `%s': %s",
+                     opt.pcsc_driver, dlerror ());
+          return -1;
+        }
+
+      pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
+      pcsc_release_context   = dlsym (handle, "SCardReleaseContext");
+      pcsc_list_readers      = dlsym (handle, "SCardListReaders");
+      pcsc_connect           = dlsym (handle, "SCardConnect");
+      pcsc_disconnect        = dlsym (handle, "SCardDisconnect");
+      pcsc_status            = dlsym (handle, "SCardStatus");
+      pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
+      pcsc_end_transaction   = dlsym (handle, "SCardEndTransaction");
+      pcsc_transmit          = dlsym (handle, "SCardTransmit");
+      pcsc_set_timeout       = dlsym (handle, "SCardSetTimeout");
+
+      if (!pcsc_establish_context
+          || !pcsc_release_context  
+          || !pcsc_list_readers     
+          || !pcsc_connect          
+          || !pcsc_disconnect
+          || !pcsc_status
+          || !pcsc_begin_transaction
+          || !pcsc_end_transaction
+          || !pcsc_transmit         
+          || !pcsc_set_timeout)
+        {
+          log_error ("apdu_open_reader: invalid PC/SC driver\n");
+          dlclose (handle);
+          return -1;
+        }
+      pcsc_api_loaded = 1;
+    }
+  
+  return open_pcsc_reader (portstr);
+}
+
+
+unsigned char *
+apdu_get_atr (int slot, size_t *atrlen)
+{
+  char *buf;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return NULL;
+  
+  buf = xtrymalloc (reader_table[slot].atrlen);
+  if (!buf)
+    return NULL;
+  memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
+  *atrlen = reader_table[slot].atrlen;
+  return buf;
+}
+  
+    
+static const char *
+error_string (int slot, long rc)
+{
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    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);
+#endif
+  else
+    return pcsc_error_string (rc);
+}
+
+
+/* Dispatcher for the actual send_apdu fucntion. */
+static int
+send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+           unsigned char *buffer, size_t *buflen)
+{
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    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);
+#endif
+  else
+    return pcsc_send_apdu (slot, apdu, apdulen, buffer, buflen);
+}
+
+/* Send an APDU to the card in SLOT.  The APDU is created from all
+   given parameters: CLASS, INS, P0, P1, LC, DATA, LE.  A value of -1
+   for LC won't sent this field and the data field; in this case DATA
+   must also be passed as NULL.  The return value is the status word
+   or -1 for an invalid SLOT or other non card related error.  If
+   RETBUF is not NULL, it will receive an allocated buffer with the
+   returned data.  The length of that data will be put into
+   *RETBUFLEN.  The caller is reponsible for releasing the buffer even
+   in case of errors.  */
+int 
+apdu_send_le(int slot, int class, int ins, int p0, int p1,
+             int lc, const char *data, int le,
+             unsigned char **retbuf, size_t *retbuflen)
+{
+  unsigned char result[256+10]; /* 10 extra in case of bugs in the driver. */
+  size_t resultlen = 256;
+  unsigned char apdu[5+256+1];
+  size_t apdulen;
+  int sw;
+  long rc; /* we need a long here due to PC/SC. */
+
+  if (DBG_CARD_IO)
+    log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
+               class, ins, p0, p1, lc, le);
+
+  if (lc != -1 && (lc > 255 || lc < 0))
+    return SW_WRONG_LENGTH; 
+  if (le != -1 && (le > 256 || le < 1))
+    return SW_WRONG_LENGTH; 
+  if ((!data && lc != -1) || (data && lc == -1))
+    return SW_HOST_INV_VALUE;
+
+  apdulen = 0;
+  apdu[apdulen++] = class;
+  apdu[apdulen++] = ins;
+  apdu[apdulen++] = p0;
+  apdu[apdulen++] = p1;
+  if (lc != -1)
+    {
+      apdu[apdulen++] = lc;
+      memcpy (apdu+apdulen, data, lc);
+      apdulen += lc;
+    }
+  if (le != -1)
+    apdu[apdulen++] = le; /* Truncation is okay becuase 0 means 256. */
+  assert (sizeof (apdu) >= apdulen);
+  /* As safeguard don't pass any garbage from the stack to the driver. */
+  memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+  rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+  if (rc || resultlen < 2)
+    {
+      log_error ("apdu_send_simple(%d) failed: %s\n",
+                 slot, error_string (slot, rc));
+      return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    }
+  sw = (result[resultlen-2] << 8) | result[resultlen-1];
+  /* store away the returned data but strip the statusword. */
+  resultlen -= 2;
+  if (DBG_CARD_IO)
+    {
+      log_debug (" response: sw=%04X  datalen=%d\n", sw, resultlen);
+      if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
+        log_printhex ("     dump: ", result, resultlen);
+    }
+
+  if (sw == SW_SUCCESS)
+    {
+      if (retbuf)
+        {
+          *retbuf = xtrymalloc (resultlen? resultlen : 1);
+          if (!*retbuf)
+            return SW_HOST_OUT_OF_CORE;
+          *retbuflen = resultlen;
+          memcpy (*retbuf, result, resultlen);
+        }
+    }
+  else if ((sw & 0xff00) == SW_MORE_DATA)
+    {
+      unsigned char *p = NULL, *tmp;
+      size_t bufsize = 4096;
+
+      /* It is likely that we need to return much more data, so we
+         start off with a large buffer. */
+      if (retbuf)
+        {
+          *retbuf = p = xtrymalloc (bufsize);
+          if (!*retbuf)
+            return SW_HOST_OUT_OF_CORE;
+          assert (resultlen < bufsize);
+          memcpy (p, result, resultlen);
+          p += resultlen;
+        }
+
+      do
+        {
+          int len = (sw & 0x00ff);
+          
+          log_debug ("apdu_send_simple(%d): %d more bytes available\n",
+                     slot, len);
+          apdulen = 0;
+          apdu[apdulen++] = class;
+          apdu[apdulen++] = 0xC0;
+          apdu[apdulen++] = 0;
+          apdu[apdulen++] = 0;
+          apdu[apdulen++] = 64; /* that is 256 bytes for Le */
+          memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+          rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+          if (rc || resultlen < 2)
+            {
+              log_error ("apdu_send_simple(%d) for get response failed: %s\n",
+                         slot, error_string (slot, rc));
+              return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+            }
+          sw = (result[resultlen-2] << 8) | result[resultlen-1];
+          resultlen -= 2;
+          if (DBG_CARD_IO)
+            {
+              log_debug ("     more: sw=%04X  datalen=%d\n", sw, resultlen);
+              if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
+                log_printhex ("     dump: ", result, resultlen);
+            }
+
+          if ((sw & 0xff00) == SW_MORE_DATA || sw == SW_SUCCESS)
+            {
+              if (retbuf)
+                {
+                  if (p - *retbuf + resultlen > bufsize)
+                    {
+                      bufsize += resultlen > 4096? resultlen: 4096;
+                      tmp = xtryrealloc (*retbuf, bufsize);
+                      if (!tmp)
+                        return SW_HOST_OUT_OF_CORE;
+                      p = tmp + (p - *retbuf);
+                      *retbuf = tmp;
+                    }
+                  memcpy (p, result, resultlen);
+                  p += resultlen;
+                }
+            }
+          else
+            log_info ("apdu_send_simple(%d) "
+                      "got unexpected status %04X from get response\n",
+                      slot, sw);
+        }
+      while ((sw & 0xff00) == SW_MORE_DATA);
+      
+      if (retbuf)
+        {
+          *retbuflen = p - *retbuf;
+          tmp = xtryrealloc (*retbuf, *retbuflen);
+          if (tmp)
+            *retbuf = tmp;
+        }
+    }
+  if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
+    log_printhex ("      dump: ", *retbuf, *retbuflen);
+  return sw;
+}
+
+/* Send an APDU to the card in SLOT.  The APDU is created from all
+   given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
+   LC won't sent this field and the data field; in this case DATA must
+   also be passed as NULL. The return value is the status word or -1
+   for an invalid SLOT or other non card related error.  If RETBUF is
+   not NULL, it will receive an allocated buffer with the returned
+   data.  The length of that data will be put into *RETBUFLEN.  The
+   caller is reponsible for releasing the buffer even in case of
+   errors.  */
+int 
+apdu_send (int slot, int class, int ins, int p0, int p1,
+           int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
+{
+  return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256, 
+                       retbuf, retbuflen);
+}
+
+/* Send an APDU to the card in SLOT.  The APDU is created from all
+   given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
+   LC won't sent this field and the data field; in this case DATA must
+   also be passed as NULL. The return value is the status word or -1
+   for an invalid SLOT or other non card related error.  No data will be
+   returned. */
+int 
+apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+                  int lc, const char *data)
+{
+  return apdu_send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL);
+}
+
+
+
+#endif /*ENABLE_CARD_SUPPORT*/
diff --git a/g10/apdu.h b/g10/apdu.h
new file mode 100644 (file)
index 0000000..6e4244b
--- /dev/null
@@ -0,0 +1,74 @@
+/* apdu.h - ISO 7816 APDU functions and low level I/O
+ *     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 APDU_H
+#define APDU_H
+
+/* ISO 7816 values for the statusword are defined here because they
+   should not be visible to the users of the actual ISO command
+   API. */
+enum {
+  SW_MORE_DATA      = 0x6100, /* Note: that the low byte must be
+                                 masked of.*/
+  SW_EEPROM_FAILURE = 0x6581,
+  SW_WRONG_LENGTH   = 0x6700,
+  SW_CHV_WRONG      = 0x6982,
+  SW_CHV_BLOCKED    = 0x6983,
+  SW_USE_CONDITIONS = 0x6985,
+  SW_NOT_SUPPORTED  = 0x6a81,
+  SW_BAD_PARAMETER  = 0x6a80, /* (in the data field) */
+  SW_REF_NOT_FOUND  = 0x6a88,
+  SW_BAD_P0_P1      = 0x6b00,
+  SW_INS_NOT_SUP    = 0x6d00,
+  SW_CLA_NOT_SUP    = 0x6e00,
+  SW_SUCCESS        = 0x9000,
+
+  /* The follwoing statuswords are no real ones but used to map host
+     OS errors into status words.  A status word is 16 bit so that
+     those values can't be issued by a card. */
+  SW_HOST_OUT_OF_CORE = 0x10001,  /* No way yet to differentiate
+                                     between errnos on a failed malloc. */
+  SW_HOST_INV_VALUE   = 0x10002,
+  SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
+  SW_HOST_NO_DRIVER   = 0x10004
+};
+
+
+
+/* Note , that apdu_open_reader returns no status word but -1 on error. */
+int apdu_open_reader (const char *portstr);
+unsigned char *apdu_get_atr (int slot, size_t *atrlen);
+
+
+/* The apdu send functions do return status words. */
+int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+                      int lc, const char *data);
+int apdu_send (int slot, int class, int ins, int p0, int p1,
+               int lc, const char *data,
+               unsigned char **retbuf, size_t *retbuflen);
+int apdu_send_le (int slot, int class, int ins, int p0, int p1,
+                  int lc, const char *data, int le,
+                  unsigned char **retbuf, size_t *retbuflen);
+
+
+#endif /*APDU_H*/
+
+
+
diff --git a/g10/app-common.h b/g10/app-common.h
new file mode 100644 (file)
index 0000000..26666e7
--- /dev/null
@@ -0,0 +1,76 @@
+/* app-common.h 
+ *     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 APP_COMMON_H
+#define APP_COMMON_H
+
+struct app_ctx_s {
+  int initialized;  /* The application has been initialied and the
+                       function pointers may be used.  Note that for
+                       unsupported operations the particular
+                       function pointer is set to NULL */
+  int slot;         /* Used reader. */
+  unsigned char *serialno; /* Serialnumber in raw form, allocated. */
+  size_t serialnolen;      /* Length in octets of serialnumber. */
+  unsigned int card_version;
+  int did_chv1;
+  int did_chv2;
+  int did_chv3;
+  struct {
+    int (*learn_status) (APP app, CTRL ctrl);
+    int (*setattr) (APP app, const char *name,
+                    int (*pincb)(void*, const char *, char **),
+                    void *pincb_arg,
+                    const unsigned char *value, size_t valuelen);
+    int (*sign) (APP app,
+                 const char *keyidstr, int hashalgo,
+                 int (pincb)(void*, const char *, char **),
+                 void *pincb_arg,
+                 const void *indata, size_t indatalen,
+                 unsigned char **outdata, size_t *outdatalen );
+    int (*auth) (APP app, const char *keyidstr,
+                 int (*pincb)(void*, const char *, char **),
+                 void *pincb_arg,
+                 const void *indata, size_t indatalen,
+                 unsigned char **outdata, size_t *outdatalen);
+    int (*decipher) (APP app, const char *keyidstr,
+                     int (pincb)(void*, const char *, char **),
+                     void *pincb_arg,
+                     const void *indata, size_t indatalen,
+                     unsigned char **outdata, size_t *outdatalen);
+    int (*genkey) (APP app, CTRL ctrl,
+                   const char *keynostr, unsigned int flags,
+                   int (*pincb)(void*, const char *, char **),
+                   void *pincb_arg);
+    int (*change_pin) (APP app, CTRL ctrl,
+                       const char *chvnostr, int reset_mode,
+                       int (*pincb)(void*, const char *, char **),
+                       void *pincb_arg);
+  } fnc;
+};
+
+
+int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
+int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
+
+#endif /*APP_COMMON_H*/
+
+
+
diff --git a/g10/app-openpgp.c b/g10/app-openpgp.c
new file mode 100644 (file)
index 0000000..5469a09
--- /dev/null
@@ -0,0 +1,1430 @@
+/* app-openpgp.c - The OpenPGP card application.
+ *     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
+ */
+
+#include <config.h>
+#ifdef ENABLE_CARD_SUPPORT
+/* 
+   Note, that most of this code has been taken from 1.9.x branch
+   and is maintained over there if at all possible.  Thus, if you make
+   changes here, please check that a similar change has been commited
+   to the 1.9.x branch.
+*/
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+
+#include "options.h"
+#include "errors.h"
+#include "memory.h"
+#include "util.h"
+#include "i18n.h"
+
+#include "iso7816.h"
+#include "cardglue.h"
+#include "app-common.h"
+
+static struct {
+  int tag;
+  int constructed;
+  int get_from;  /* Constructed DO with this DO or 0 for direct access. */
+  int binary;
+  char *desc;
+} data_objects[] = {
+  { 0x005E, 0,    0, 1, "Login Data" },
+  { 0x5F50, 0,    0, 0, "URL" },
+  { 0x0065, 1,    0, 1, "Cardholder Related Data"},
+  { 0x005B, 0, 0x65, 0, "Name" },
+  { 0x5F2D, 0, 0x65, 0, "Language preferences" },
+  { 0x5F35, 0, 0x65, 0, "Sex" },
+  { 0x006E, 1,    0, 1, "Application Related Data" },
+  { 0x004F, 0, 0x6E, 1, "AID" },
+  { 0x0073, 1,    0, 1, "Discretionary Data Objects" },
+  { 0x0047, 0, 0x6E, 1, "Card Capabilities" },
+  { 0x00C0, 0, 0x6E, 1, "Extended Card Capabilities" },
+  { 0x00C1, 0, 0x6E, 1, "Algorithm Attributes Signature" },
+  { 0x00C2, 0, 0x6E, 1, "Algorithm Attributes Decryption" },
+  { 0x00C3, 0, 0x6E, 1, "Algorithm Attributes Authentication" },
+  { 0x00C4, 0, 0x6E, 1, "CHV Status Bytes" },
+  { 0x00C5, 0, 0x6E, 1, "Fingerprints" },
+  { 0x00C6, 0, 0x6E, 1, "CA Fingerprints" },
+  { 0x007A, 1,    0, 1, "Security Support Template" },
+  { 0x0093, 0, 0x7A, 1, "Digital Signature Counter" },
+  { 0 }
+};
+
+
+static unsigned long get_sig_counter (APP app);
+
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and
+   return a pointer to value as well as its length in NBYTES.  Return
+   NULL if it was not found.  Note, that the function does not check
+   whether the value fits into the provided buffer. 
+
+   FIXME: Move this to an extra file, it is mostly duplicated from card.c.
+*/
+static const unsigned char *
+find_tlv (const unsigned char *buffer, size_t length,
+          int tag, size_t *nbytes, int nestlevel)
+{
+  const unsigned char *s = buffer;
+  size_t n = length;
+  size_t len;
+  int this_tag;
+  int composite;
+    
+  for (;;)
+    {
+      buffer = s;
+      if (n < 2)
+        return NULL; /* buffer definitely too short for tag and length. */
+      if (!*s || *s == 0xff)
+        { /* Skip optional filler between TLV objects. */
+          s++;
+          n--;
+          continue;
+        }
+      composite = !!(*s & 0x20);
+      if ((*s & 0x1f) == 0x1f)
+        { /* more tag bytes to follow */
+          s++;
+          n--;
+          if (n < 2)
+            return NULL; /* buffer definitely too short for tag and length. */
+          if ((*s & 0x1f) == 0x1f)
+            return NULL; /* We support only up to 2 bytes. */
+          this_tag = (s[-1] << 8) | (s[0] & 0x7f);
+        }
+      else
+        this_tag = s[0];
+      len = s[1];
+      s += 2; n -= 2;
+      if (len < 0x80)
+        ;
+      else if (len == 0x81)
+        { /* One byte length follows. */
+          if (!n)
+            return NULL; /* we expected 1 more bytes with the length. */
+          len = s[0];
+          s++; n--;
+        }
+      else if (len == 0x82)
+        { /* Two byte length follows. */
+          if (n < 2)
+            return NULL; /* we expected 2 more bytes with the length. */
+          len = (s[0] << 8) | s[1];
+          s += 2; n -= 2;
+        }
+      else
+        return NULL; /* APDU limit is 65535, thus it does not make
+                        sense to assume longer length fields. */
+
+      if (composite && nestlevel < 100)
+        { /* Dive into this composite DO after checking for too deep
+             nesting. */
+          const unsigned char *tmp_s;
+          size_t tmp_len;
+          
+          tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1);
+          if (tmp_s)
+            {
+              *nbytes = tmp_len;
+              return tmp_s;
+            }
+        }
+
+      if (this_tag == tag)
+        {
+          *nbytes = len;
+          return s;
+        }
+      if (len > n)
+        return NULL; /* buffer too short to skip to the next tag. */
+      s += len; n -= len;
+    }
+}
+
+
+/* Get the DO identified by TAG from the card in SLOT and return a
+   buffer with its content in RESULT and NBYTES.  The return value is
+   NULL if not found or a pointer which must be used to release the
+   buffer holding value. */
+static void *
+get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
+{
+  int rc, i;
+  unsigned char *buffer;
+  size_t buflen;
+  unsigned char *value;
+  size_t valuelen;
+
+  *result = NULL;
+  *nbytes = 0;
+  for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
+    ;
+
+  value = NULL;
+  rc = -1;
+  if (data_objects[i].tag && data_objects[i].get_from)
+    {
+      rc = iso7816_get_data (slot, data_objects[i].get_from,
+                             &buffer, &buflen);
+      if (!rc)
+        {
+          const unsigned char *s;
+
+          s = find_tlv (buffer, buflen, tag, &valuelen, 0);
+          if (!s)
+            value = NULL; /* not found */
+          else if (valuelen > buflen - (s - buffer))
+            {
+              log_error ("warning: constructed DO too short\n");
+              value = NULL;
+              xfree (buffer); buffer = NULL;
+            }
+          else
+            value = buffer + (s - buffer);
+        }
+    }
+
+  if (!value) /* Not in a constructed DO, try simple. */
+    {
+      rc = iso7816_get_data (slot, tag, &buffer, &buflen);
+      if (!rc)
+        {
+          value = buffer;
+          valuelen = buflen;
+        }
+    }
+
+  if (!rc)
+    {
+      *nbytes = valuelen;
+      *result = value;
+      return buffer;
+    }
+  return NULL;
+}
+
+
+static void
+dump_all_do (int slot)
+{
+  int rc, i, j;
+  unsigned char *buffer;
+  size_t buflen;
+  
+  for (i=0; data_objects[i].tag; i++)
+    {
+      if (data_objects[i].get_from)
+        continue;
+
+      rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen);
+      if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
+        ;
+      else if (rc) 
+        log_info ("DO `%s' not available: %s\n",
+                  data_objects[i].desc, gpg_strerror (rc));
+      else
+        {
+          if (data_objects[i].binary)
+            {
+              log_info ("DO `%s': ", data_objects[i].desc);
+              log_printhex ("", buffer, buflen);
+            }
+          else
+            log_info ("DO `%s': `%.*s'\n",
+                      data_objects[i].desc,
+                      (int)buflen, buffer); /* FIXME: sanitize */
+
+          if (data_objects[i].constructed)
+            {
+              for (j=0; data_objects[j].tag; j++)
+                {
+                  const unsigned char *value;
+                  size_t valuelen;
+                  
+                  if (j==i || data_objects[i].tag != data_objects[j].get_from)
+                    continue;
+                  value = find_tlv (buffer, buflen,
+                                    data_objects[j].tag, &valuelen, 0);
+                  if (!value)
+                    ; /* not found */
+                  else if (valuelen > buflen - (value - buffer))
+                    log_error ("warning: constructed DO too short\n");
+                  else
+                    {
+                      if (data_objects[j].binary)
+                        {
+                          log_info ("DO `%s': ", data_objects[j].desc);
+                          log_printhex ("", value, valuelen);
+                        }
+                      else
+                        log_info ("DO `%s': `%.*s'\n",
+                                  data_objects[j].desc,
+                                  (int)valuelen, value); /* FIXME: sanitize */
+                    }
+                }
+            }
+        }
+      xfree (buffer); buffer = NULL;
+    }
+}
+
+
+/* Count the number of bits, assuming the A represents an unsigned big
+   integer of length LEN bytes. */
+static unsigned int
+count_bits (const unsigned char *a, size_t len)
+{
+  unsigned int n = len * 8;
+  int i;
+
+  for (; len && !*a; len--, a++, n -=8)
+    ;
+  if (len)
+    {
+      for (i=7; i && !(*a & (1<<i)); i--)
+        n--;
+    }
+  return n;
+}
+
+/* Note, that FPR must be at least 20 bytes. */
+static int 
+store_fpr (int slot, int keynumber, u32 timestamp,
+           const unsigned char *m, size_t mlen,
+           const unsigned char *e, size_t elen, 
+           unsigned char *fpr, unsigned int card_version)
+{
+  unsigned int n, nbits;
+  unsigned char *buffer, *p;
+  int rc;
+  
+  for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
+    ;
+  for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+    ;
+
+  n = 6 + 2 + mlen + 2 + elen;
+  p = buffer = xtrymalloc (3 + n);
+  if (!buffer)
+    return out_of_core ();
+  
+  *p++ = 0x99;     /* ctb */
+  *p++ = n >> 8;   /* 2 byte length header */
+  *p++ = n;
+  *p++ = 4;        /* key packet version */
+  *p++ = timestamp >> 24;
+  *p++ = timestamp >> 16;
+  *p++ = timestamp >>  8;
+  *p++ = timestamp;
+  *p++ = 1; /* RSA */
+  nbits = count_bits (m, mlen);
+  *p++ = nbits >> 8;
+  *p++ = nbits;
+  memcpy (p, m, mlen); p += mlen;
+  nbits = count_bits (e, elen);
+  *p++ = nbits >> 8;
+  *p++ = nbits;
+  memcpy (p, e, elen); p += elen;
+    
+  log_printhex ("fprbuf:", buffer, n+3);
+  gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
+
+  xfree (buffer);
+
+  rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6)
+                               + keynumber, fpr, 20);
+  if (rc)
+    log_error ("failed to store the fingerprint: %s\n",gpg_strerror (rc));
+
+  return rc;
+}
+
+       
+static void
+send_fpr_if_not_null (CTRL ctrl, const char *keyword,
+                      int number, const unsigned char *fpr)
+{                      
+  int i;
+  char buf[41];
+  char numbuf[25];
+
+  for (i=0; i < 20 && !fpr[i]; i++)
+    ;
+  if (i==20)
+    return; /* All zero. */
+  for (i=0; i< 20; i++)
+    sprintf (buf+2*i, "%02X", fpr[i]);
+  if (number == -1)
+    *numbuf = 0; /* Don't print the key number */
+  else
+    sprintf (numbuf, "%d", number);
+  send_status_info (ctrl, keyword,
+                    numbuf, (size_t)strlen(numbuf),
+                    buf, (size_t)strlen (buf), NULL, 0);
+}
+
+static void
+send_key_data (CTRL ctrl, const char *name, 
+               const unsigned char *a, size_t alen)
+{
+  char *p, *buf = xmalloc (alen*2+1);
+  
+  for (p=buf; alen; a++, alen--, p += 2)
+    sprintf (p, "%02X", *a);
+
+  send_status_info (ctrl, "KEY-DATA",
+                    name, (size_t)strlen(name), 
+                    buf, (size_t)strlen (buf),
+                    NULL, 0);
+  xfree (buf);
+}
+
+
+
+static int
+do_learn_status (APP app, CTRL ctrl)
+{
+  void *relptr;
+  unsigned char *value;
+  size_t valuelen;
+  int i;
+
+  relptr = get_one_do (app->slot, 0x005B, &value, &valuelen);
+  if (relptr)
+    {
+      send_status_info (ctrl, "DISP-NAME", value, valuelen, NULL, 0);
+      xfree (relptr);
+    }
+  relptr = get_one_do (app->slot, 0x5F2D, &value, &valuelen);
+  if (relptr)
+    {
+      send_status_info (ctrl, "DISP-LANG", value, valuelen, NULL, 0);
+      xfree (relptr);
+    }
+  relptr = get_one_do (app->slot, 0x5F35, &value, &valuelen);
+  if (relptr)
+    {
+      send_status_info (ctrl, "DISP-SEX", value, valuelen, NULL, 0);
+      xfree (relptr);
+    }
+  relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen);
+  if (relptr)
+    {
+      send_status_info (ctrl, "PUBKEY-URL", value, valuelen, NULL, 0);
+      xfree (relptr);
+    }
+  relptr = get_one_do (app->slot, 0x005E, &value, &valuelen);
+  if (relptr)
+    {
+      send_status_info (ctrl, "LOGIN-DATA", value, valuelen, NULL, 0);
+      xfree (relptr);
+    }
+
+  relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen);
+  if (relptr && valuelen >= 60)
+    {
+      for (i=0; i < 3; i++)
+        send_fpr_if_not_null (ctrl, "KEY-FPR", i+1, value+i*20);
+    }
+  xfree (relptr);
+  relptr = get_one_do (app->slot, 0x00C6, &value, &valuelen);
+  if (relptr && valuelen >= 60)
+    {
+      for (i=0; i < 3; i++)
+        send_fpr_if_not_null (ctrl, "CA-FPR", i+1, value+i*20);
+    }
+  xfree (relptr);
+  relptr = get_one_do (app->slot, 0x00C4, &value, &valuelen);
+  if (relptr)
+    {
+      char numbuf[7*23];
+
+      for (i=0,*numbuf=0; i < valuelen && i < 7; i++)
+        sprintf (numbuf+strlen (numbuf), " %d", value[i]); 
+      send_status_info (ctrl, "CHV-STATUS", numbuf, strlen (numbuf), NULL, 0);
+      xfree (relptr);
+    }
+
+  {
+    unsigned long ul = get_sig_counter (app);
+    char numbuf[23];
+
+    sprintf (numbuf, "%lu", ul);
+    send_status_info (ctrl, "SIG-COUNTER", numbuf, strlen (numbuf), NULL, 0);
+  }
+  return 0;
+}
+
+
+/* Handle the SETATTR operation. All arguments are already basically
+   checked. */
+static int 
+do_setattr (APP app, const char *name,
+            int (*pincb)(void*, const char *, char **),
+            void *pincb_arg,
+            const unsigned char *value, size_t valuelen)
+{
+  gpg_error_t rc;
+  int idx;
+  static struct {
+    const char *name;
+    int tag;
+  } table[] = {
+    { "DISP-NAME",    0x005B },
+    { "LOGIN-DATA",   0x005E },
+    { "DISP-LANG",    0x5F2D },
+    { "DISP-SEX",     0x5F35 },
+    { "PUBKEY-URL",   0x5F50 },
+    { "CHV-STATUS-1", 0x00C4 },
+    { "CA-FPR-1",     0x00CA },
+    { "CA-FPR-2",     0x00CB },
+    { "CA-FPR-3",     0x00CC },
+    { NULL, 0 }
+  };
+
+
+  for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
+    ;
+  if (!table[idx].name)
+    return gpg_error (GPG_ERR_INV_NAME); 
+
+  if (!app->did_chv3)
+    {
+      char *pinvalue;
+
+      rc = pincb (pincb_arg, "Admin PIN (CHV3)",
+                  &pinvalue);
+/*        pinvalue = xstrdup ("12345678"); */
+/*        rc = 0; */
+      if (rc)
+        {
+          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+          return rc;
+        }
+
+      rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
+      if (rc)
+        {
+          log_error ("verify CHV3 failed: %s\n", gpg_strerror (rc));
+          rc = gpg_error (GPG_ERR_GENERAL);
+          return rc;
+        }
+      app->did_chv3 = 1;
+    }
+
+  rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen);
+  if (rc)
+    log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc));
+  /* FIXME: If this fails we should *once* try again after
+     doing a verify command, so that in case of a problem with
+     tracking the verify operation we have a fallback. */
+
+  return rc;
+}
+
+/* Handle the PASSWD command. */
+static int 
+do_change_pin (APP app, CTRL ctrl,  const char *chvnostr, int reset_mode,
+               int (*pincb)(void*, const char *, char **),
+               void *pincb_arg)
+{
+  int rc = 0;
+  int chvno = atoi (chvnostr);
+  char *pinvalue;
+
+  if (reset_mode && chvno == 3)
+    {
+      rc = gpg_error (GPG_ERR_INV_ID);
+      goto leave;
+    }
+  else if (reset_mode || chvno == 3)
+    {
+      rc = pincb (pincb_arg, "Admin PIN", &pinvalue); 
+      if (rc)
+        {
+          log_error ("error getting PIN: %s\n", gpg_strerror (rc));
+          goto leave;
+        }
+      rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
+      if (rc)
+        {
+          log_error ("verify CHV3 failed: rc=%s\n", gpg_strerror (rc));
+          goto leave;
+        }
+    }
+  else if (chvno == 1)
+    {
+      rc = pincb (pincb_arg, "Signature PIN", &pinvalue); 
+      if (rc)
+        {
+          log_error ("error getting PIN: %s\n", gpg_strerror (rc));
+          goto leave;
+        }
+      rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
+      if (rc)
+        {
+          log_error ("verify CHV1 failed: rc=%s\n", gpg_strerror (rc));
+          goto leave;
+        }
+    }
+  else if (chvno == 2)
+    {
+      rc = pincb (pincb_arg, "Decryption PIN", &pinvalue); 
+      if (rc)
+        {
+          log_error ("error getting PIN: %s\n", gpg_strerror (rc));
+          goto leave;
+        }
+      rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
+      if (rc)
+        {
+          log_error ("verify CHV2 failed: rc=%s\n", gpg_strerror (rc));
+          goto leave;
+        }
+    }
+  else
+    {
+      rc = gpg_error (GPG_ERR_INV_ID);
+      goto leave;
+    }
+
+  
+  rc = pincb (pincb_arg, chvno == 1? "New Signature PIN" :
+                         chvno == 2? "New Decryption PIN" :
+                         chvno == 3? "New Admin PIN" : "?", &pinvalue); 
+  if (rc)
+    {
+      log_error ("error getting new PIN: %s\n", gpg_strerror (rc));
+      goto leave;
+    }
+
+  if (reset_mode)
+    rc = iso7816_reset_retry_counter (app->slot, 0x80 + chvno,
+                                      pinvalue, strlen (pinvalue));
+  else
+    rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
+                                        NULL, 0,
+                                        pinvalue, strlen (pinvalue));
+  xfree (pinvalue);
+
+
+ leave:
+  return rc;
+}
+
+
+
+/* Handle the GENKEY command. */
+static int 
+do_genkey (APP app, CTRL ctrl,  const char *keynostr, unsigned int flags,
+          int (*pincb)(void*, const char *, char **),
+          void *pincb_arg)
+{
+  int rc;
+  int i;
+  char numbuf[30];
+  unsigned char fprbuf[20];
+  const unsigned char *fpr;
+  const unsigned char *keydata, *m, *e;
+  unsigned char *buffer;
+  size_t buflen, keydatalen, n, mlen, elen;
+  time_t created_at;
+  int keyno = atoi (keynostr);
+  int force = (flags & 1);
+  time_t start_at;
+
+  if (keyno < 1 || keyno > 3)
+    return gpg_error (GPG_ERR_INV_ID);
+  keyno--;
+
+  rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
+  if (rc)
+    {
+      log_error ("error reading application data\n");
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
+  if (!fpr || n != 60)
+    {
+      rc = gpg_error (GPG_ERR_GENERAL);
+      log_error ("error reading fingerprint DO\n");
+      goto leave;
+    }
+  fpr += 20*keyno;
+  for (i=0; i < 20 && !fpr[i]; i++)
+    ;
+  if (i!=20 && !force)
+    {
+      rc = gpg_error (GPG_ERR_EEXIST);
+      log_error ("key already exists\n");
+      goto leave;
+    }
+  else if (i!=20)
+    log_info ("existing key will be replaced\n");
+  else
+    log_info ("generating new key\n");
+
+  {
+    char *pinvalue;
+    rc = pincb (pincb_arg, "Admin PIN", &pinvalue); 
+    if (rc)
+      {
+        log_error ("error getting PIN: %s\n", gpg_strerror (rc));
+        return rc;
+      }
+    rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
+    xfree (pinvalue);
+  }
+  if (rc)
+    {
+      log_error ("verify CHV3 failed: rc=%s\n", gpg_strerror (rc));
+      goto leave;
+    }
+
+  xfree (buffer); buffer = NULL;
+#if 1
+  log_info ("please wait while key is being generated ...\n");
+  start_at = time (NULL);
+  rc = iso7816_generate_keypair 
+#else
+#warning key generation temporary replaced by reading an existing key.
+  rc = iso7816_read_public_key
+#endif
+                              (app->slot, 
+                                 keyno == 0? "\xB6" :
+                                 keyno == 1? "\xB8" : "\xA4",
+                                 2,
+                                 &buffer, &buflen);
+  if (rc)
+    {
+      rc = gpg_error (GPG_ERR_CARD);
+      log_error ("generating key failed\n");
+      goto leave;
+    }
+  log_info ("key generation completed (%d seconds)\n",
+            (int)(time (NULL) - start_at));
+  keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
+  if (!keydata)
+    {
+      rc = gpg_error (GPG_ERR_CARD);
+      log_error ("response does not contain the public key data\n");
+      goto leave;
+    }
+  m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
+  if (!m)
+    {
+      rc = gpg_error (GPG_ERR_CARD);
+      log_error ("response does not contain the RSA modulus\n");
+      goto leave;
+    }
+/*    log_printhex ("RSA n:", m, mlen); */
+  send_key_data (ctrl, "n", m, mlen);
+
+  e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
+  if (!e)
+    {
+      rc = gpg_error (GPG_ERR_CARD);
+      log_error ("response does not contain the RSA public exponent\n");
+      goto leave;
+    }
+/*    log_printhex ("RSA e:", e, elen); */
+  send_key_data (ctrl, "e", e, elen);
+
+  created_at = gnupg_get_time ();
+  sprintf (numbuf, "%lu", (unsigned long)created_at);
+  send_status_info (ctrl, "KEY-CREATED-AT",
+                    numbuf, (size_t)strlen(numbuf), NULL, 0);
+
+  rc = store_fpr (app->slot, keyno, (u32)created_at,
+                  m, mlen, e, elen, fprbuf, app->card_version);
+  if (rc)
+    goto leave;
+  send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
+
+
+ leave:
+  xfree (buffer);
+  return rc;
+}
+
+
+static unsigned long
+get_sig_counter (APP app)
+{
+  void *relptr;
+  unsigned char *value;
+  size_t valuelen;
+  unsigned long ul;
+
+  relptr = get_one_do (app->slot, 0x0093, &value, &valuelen);
+  if (!relptr)
+    return 0;
+  if (valuelen == 3 )
+    ul = (value[0] << 16) | (value[1] << 8) | value[2];
+  else
+    {
+      log_error ("invalid structure of OpenPGP card (DO 0x93)\n");
+      ul = 0;
+    }
+  xfree (relptr);
+  return ul;
+}
+
+static int
+compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
+{
+  const unsigned char *fpr;
+  unsigned char *buffer;
+  size_t buflen, n;
+  int rc, i;
+  
+  assert (keyno >= 1 && keyno <= 3);
+
+  rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
+  if (rc)
+    {
+      log_error ("error reading application data\n");
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
+  if (!fpr || n != 60)
+    {
+      xfree (buffer);
+      log_error ("error reading fingerprint DO\n");
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  fpr += (keyno-1)*20;
+  for (i=0; i < 20; i++)
+    if (sha1fpr[i] != fpr[i])
+      {
+        xfree (buffer);
+        return gpg_error (GPG_ERR_WRONG_SECKEY);
+      }
+  xfree (buffer);
+  return 0;
+}
+
+
+
+/* Compute a digital signature on INDATA which is expected to be the
+   raw message digest. For this application the KEYIDSTR consists of
+   the serialnumber and the fingerprint delimited by a slash.
+
+   Note that this fucntion may return the error code
+   GPG_ERR_WRONG_CARD to indicate that the card currently present does
+   not match the one required for the requested action (e.g. the
+   serial number does not match). */
+static int 
+do_sign (APP app, const char *keyidstr, int hashalgo,
+         int (*pincb)(void*, const char *, char **),
+         void *pincb_arg,
+         const void *indata, size_t indatalen,
+         unsigned char **outdata, size_t *outdatalen )
+{
+  static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
+  { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+    0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+  static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
+  { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+    0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+  int rc;
+  unsigned char data[35];
+  unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
+  const char *s;
+  int n;
+  const char *fpr = NULL;
+  unsigned long sigcount;
+
+  if (!keyidstr || !*keyidstr)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (indatalen != 20)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* Check whether an OpenPGP card of any version has been requested. */
+  if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
+    return gpg_error (GPG_ERR_INV_ID);
+  
+  for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+    ;
+  if (n != 32)
+    return gpg_error (GPG_ERR_INV_ID);
+  else if (!*s)
+    ; /* no fingerprint given: we allow this for now. */
+  else if (*s == '/')
+    fpr = s + 1; 
+  else
+    return gpg_error (GPG_ERR_INV_ID);
+
+  for (s=keyidstr, n=0; n < 16; s += 2, n++)
+    tmp_sn[n] = xtoi_2 (s);
+
+  if (app->serialnolen != 16)
+    return gpg_error (GPG_ERR_INV_CARD);
+  if (memcmp (app->serialno, tmp_sn, 16))
+    return gpg_error (GPG_ERR_WRONG_CARD);
+
+  /* If a fingerprint has been specified check it against the one on
+     the card.  This is allows for a meaningful error message in case
+     the key on the card has been replaced but the shadow information
+     known to gpg was not updated.  If there is no fingerprint, gpg
+     will detect a bogus signature anyway due to the
+     verify-after-signing feature. */
+  if (fpr)
+    {
+      for (s=fpr, n=0; hexdigitp (s); s++, n++)
+        ;
+      if (n != 40)
+        return gpg_error (GPG_ERR_INV_ID);
+      else if (!*s)
+        ; /* okay */
+      else
+        return gpg_error (GPG_ERR_INV_ID);
+
+      for (s=fpr, n=0; n < 20; s += 2, n++)
+        tmp_sn[n] = xtoi_2 (s);
+      rc = compare_fingerprint (app, 1, tmp_sn);
+      if (rc)
+        return rc;
+    }
+
+  if (hashalgo == GCRY_MD_SHA1)
+    memcpy (data, sha1_prefix, 15);
+  else if (hashalgo == GCRY_MD_RMD160)
+    memcpy (data, rmd160_prefix, 15);
+  else 
+    return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+  memcpy (data+15, indata, indatalen);
+
+  sigcount = get_sig_counter (app);
+  log_info ("signatures created so far: %lu\n", sigcount);
+
+  /* FIXME: Check whether we are really required to enter the PIN for
+     each signature. There is a DO for this. */
+  if (!app->did_chv1 || 1) 
+    {
+      char *pinvalue;
+
+      {
+        char *prompt;
+        if (asprintf (&prompt, "Signature PIN [sigs done: %lu]", sigcount) < 0)
+          return gpg_error_from_errno (errno);
+        rc = pincb (pincb_arg, prompt, &pinvalue); 
+        free (prompt);
+      }
+/*        pinvalue = xstrdup ("123456"); */
+/*        rc = 0; */
+      if (rc)
+        {
+          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+          return rc;
+        }
+
+      rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
+      if (rc)
+        {
+          log_error ("verify CHV1 failed\n");
+          rc = gpg_error (GPG_ERR_GENERAL);
+          return rc;
+        }
+      app->did_chv1 = 1;
+    }
+
+  rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+  return rc;
+}
+
+/* Compute a digital signature using the INTERNAL AUTHENTICATE command
+   on INDATA which is expected to be the raw message digest. For this
+   application the KEYIDSTR consists of the serialnumber and the
+   fingerprint delimited by a slash.
+
+   Note that this fucntion may return the error code
+   GPG_ERR_WRONG_CARD to indicate that the card currently present does
+   not match the one required for the requested action (e.g. the
+   serial number does not match). */
+static int 
+do_auth (APP app, const char *keyidstr,
+         int (*pincb)(void*, const char *, char **),
+         void *pincb_arg,
+         const void *indata, size_t indatalen,
+         unsigned char **outdata, size_t *outdatalen )
+{
+  int rc;
+  unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
+  const char *s;
+  int n;
+  const char *fpr = NULL;
+
+  if (!keyidstr || !*keyidstr)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (indatalen > 50) /* For a 1024 bit key. */
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* Check whether an OpenPGP card of any version has been requested. */
+  if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
+    return gpg_error (GPG_ERR_INV_ID);
+  
+  for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+    ;
+  if (n != 32)
+    return gpg_error (GPG_ERR_INV_ID);
+  else if (!*s)
+    ; /* no fingerprint given: we allow this for now. */
+  else if (*s == '/')
+    fpr = s + 1; 
+  else
+    return gpg_error (GPG_ERR_INV_ID);
+
+  for (s=keyidstr, n=0; n < 16; s += 2, n++)
+    tmp_sn[n] = xtoi_2 (s);
+
+  if (app->serialnolen != 16)
+    return gpg_error (GPG_ERR_INV_CARD);
+  if (memcmp (app->serialno, tmp_sn, 16))
+    return gpg_error (GPG_ERR_WRONG_CARD);
+
+  /* If a fingerprint has been specified check it against the one on
+     the card.  This is allows for a meaningful error message in case
+     the key on the card has been replaced but the shadow information
+     known to gpg was not updated.  If there is no fingerprint, gpg
+     will detect a bogus signature anyway due to the
+     verify-after-signing feature. */
+  if (fpr)
+    {
+      for (s=fpr, n=0; hexdigitp (s); s++, n++)
+        ;
+      if (n != 40)
+        return gpg_error (GPG_ERR_INV_ID);
+      else if (!*s)
+        ; /* okay */
+      else
+        return gpg_error (GPG_ERR_INV_ID);
+
+      for (s=fpr, n=0; n < 20; s += 2, n++)
+        tmp_sn[n] = xtoi_2 (s);
+      rc = compare_fingerprint (app, 3, tmp_sn);
+      if (rc)
+        return rc;
+    }
+
+  if (!app->did_chv2) 
+    {
+      char *pinvalue;
+
+      rc = pincb (pincb_arg, "Authentication/Decryption PIN", &pinvalue); 
+      if (rc)
+        {
+          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+          return rc;
+        }
+
+      rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
+      if (rc)
+        {
+          log_error ("verify CHV2 failed\n");
+          rc = gpg_error (GPG_ERR_GENERAL);
+          return rc;
+        }
+      app->did_chv2 = 1;
+    }
+
+  rc = iso7816_internal_authenticate (app->slot, indata, indatalen,
+                                      outdata, outdatalen);
+  return rc;
+}
+
+
+static int 
+do_decipher (APP app, const char *keyidstr,
+             int (pincb)(void*, const char *, char **),
+             void *pincb_arg,
+             const void *indata, size_t indatalen,
+             unsigned char **outdata, size_t *outdatalen )
+{
+  int rc;
+  unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
+  const char *s;
+  int n;
+  const char *fpr = NULL;
+
+  if (!keyidstr || !*keyidstr || !indatalen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* Check whether an OpenPGP card of any version has been requested. */
+  if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
+    return gpg_error (GPG_ERR_INV_ID);
+  
+  for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+    ;
+  if (n != 32)
+    return gpg_error (GPG_ERR_INV_ID);
+  else if (!*s)
+    ; /* no fingerprint given: we allow this for now. */
+  else if (*s == '/')
+    fpr = s + 1; 
+  else
+    return gpg_error (GPG_ERR_INV_ID);
+
+  for (s=keyidstr, n=0; n < 16; s += 2, n++)
+    tmp_sn[n] = xtoi_2 (s);
+
+  if (app->serialnolen != 16)
+    return gpg_error (GPG_ERR_INV_CARD);
+  if (memcmp (app->serialno, tmp_sn, 16))
+    return gpg_error (GPG_ERR_WRONG_CARD);
+
+  /* If a fingerprint has been specified check it against the one on
+     the card.  This is allows for a meaningful error message in case
+     the key on the card has been replaced but the shadow information
+     known to gpg was not updated.  If there is no fingerprint, the
+     decryption will won't produce the right plaintext anyway. */
+  if (fpr)
+    {
+      for (s=fpr, n=0; hexdigitp (s); s++, n++)
+        ;
+      if (n != 40)
+        return gpg_error (GPG_ERR_INV_ID);
+      else if (!*s)
+        ; /* okay */
+      else
+        return gpg_error (GPG_ERR_INV_ID);
+
+      for (s=fpr, n=0; n < 20; s += 2, n++)
+        tmp_sn[n] = xtoi_2 (s);
+      rc = compare_fingerprint (app, 2, tmp_sn);
+      if (rc)
+        return rc;
+    }
+
+  if (!app->did_chv2) 
+    {
+      char *pinvalue;
+
+      rc = pincb (pincb_arg, "Decryption PIN", &pinvalue); 
+/*        pinvalue = xstrdup ("123456"); */
+/*        rc = 0; */
+      if (rc)
+        {
+          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+          return rc;
+        }
+
+      rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
+      if (rc)
+        {
+          log_error ("verify CHV2 failed\n");
+          rc = gpg_error (GPG_ERR_GENERAL);
+          return rc;
+        }
+      app->did_chv2 = 1;
+    }
+  
+  rc = iso7816_decipher (app->slot, indata, indatalen, outdata, outdatalen);
+  return rc;
+}
+
+
+
+
+/* Select the OpenPGP application on the card in SLOT.  This function
+   must be used before any other OpenPGP application functions. */
+int
+app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
+{
+  static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
+  int slot = app->slot;
+  int rc;
+  unsigned char *buffer;
+  size_t buflen;
+  
+  rc = iso7816_select_application (slot, aid, sizeof aid);
+  if (!rc)
+    {
+      rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
+      if (rc)
+        goto leave;
+      if (opt.verbose)
+        {
+          log_info ("got AID: ");
+          log_printhex ("", buffer, buflen);
+        }
+
+      if (sn)
+        {
+          *sn = buffer;
+          *snlen = buflen;
+          app->card_version = buffer[6] << 8;
+          app->card_version |= buffer[7];
+        }
+      else
+        xfree (buffer);
+
+      if (opt.verbose > 1)
+        dump_all_do (slot);
+
+      app->fnc.learn_status = do_learn_status;
+      app->fnc.setattr = do_setattr;
+      app->fnc.genkey = do_genkey;
+      app->fnc.sign = do_sign;
+      app->fnc.auth = do_auth;
+      app->fnc.decipher = do_decipher;
+      app->fnc.change_pin = do_change_pin;
+   }
+
+leave:
+  return rc;
+}
+
+
+
+/* This function is a hack to retrieve essential information about the
+   card to be displayed by simple tools.  It mostly resembles what the
+   LEARN command returns. All parameters return allocated strings or
+   buffers or NULL if the data object is not available.  All returned
+   values are sanitized. */
+int
+app_openpgp_cardinfo (APP app,
+                      char **serialno,
+                      char **disp_name,
+                      char **pubkey_url,
+                      unsigned char **fpr1,
+                      unsigned char **fpr2,
+                      unsigned char **fpr3)
+{
+  int rc;
+  void *relptr;
+  unsigned char *value;
+  size_t valuelen;
+
+  if (serialno)
+    {
+      time_t dummy;
+
+      *serialno = NULL;
+      rc = app_get_serial_and_stamp (app, serialno, &dummy);
+      if (rc)
+        {
+          log_error ("error getting serial number: %s\n", gpg_strerror (rc));
+          return rc;
+        }
+    }
+      
+  if (disp_name)
+    {
+      *disp_name = NULL;
+      relptr = get_one_do (app->slot, 0x005B, &value, &valuelen);
+      if (relptr)
+        {
+          *disp_name = make_printable_string (value, valuelen, 0);
+          xfree (relptr);
+        }
+    }
+
+  if (pubkey_url)
+    {
+      *pubkey_url = NULL;
+      relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen);
+      if (relptr)
+        {
+          *pubkey_url = make_printable_string (value, valuelen, 0);
+          xfree (relptr);
+        }
+    }
+
+  if (fpr1)
+    *fpr1 = NULL;
+  if (fpr2)
+    *fpr2 = NULL;
+  if (fpr3)
+    *fpr3 = NULL;
+  relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen);
+  if (relptr && valuelen >= 60)
+    {
+      if (fpr1)
+        {
+          *fpr1 = xmalloc (20);
+          memcpy (*fpr1, value +  0, 20);
+        }
+      if (fpr2)
+        {
+          *fpr2 = xmalloc (20);
+          memcpy (*fpr2, value + 20, 20);
+        }
+      if (fpr3)
+        {
+          *fpr3 = xmalloc (20);
+          memcpy (*fpr3, value + 40, 20);
+        }
+    }
+  xfree (relptr);
+
+  return 0;
+}
+
+
+
+/* This function is currently only used by the sc-copykeys program to
+   store a key on the smartcard.  APP ist the application handle,
+   KEYNO is the number of the key and PINCB, PINCB_ARG are used to ask
+   for the SO PIN.  TEMPLATE and TEMPLATE_LEN describe a buffer with
+   the key template to store. CREATED_AT is the timestamp used to
+   create the fingerprint. M, MLEN is the RSA modulus and E, ELEN the
+   RSA public exponent. This function silently overwrites an existing
+   key.*/
+int 
+app_openpgp_storekey (APP app, int keyno,
+                      unsigned char *template, size_t template_len,
+                      time_t created_at,
+                      const unsigned char *m, size_t mlen,
+                      const unsigned char *e, size_t elen,
+                      int (*pincb)(void*, const char *, char **),
+                      void *pincb_arg)
+{
+  int rc;
+  unsigned char fprbuf[20];
+
+  if (keyno < 1 || keyno > 3)
+    return gpg_error (GPG_ERR_INV_ID);
+  keyno--;
+
+  {
+    char *pinvalue;
+    rc = pincb (pincb_arg, "Admin PIN", &pinvalue); 
+    if (rc)
+      {
+        log_error ("error getting PIN: %s\n", gpg_strerror (rc));
+        return rc;
+      }
+    rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
+    xfree (pinvalue);
+  }
+  if (rc)
+    {
+      log_error ("verify CHV3 failed: rc=%s\n", gpg_strerror (rc));
+      goto leave;
+    }
+
+  rc = iso7816_put_data (app->slot,
+                         (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
+                         template, template_len);
+  if (rc)
+    {
+      log_error ("failed to store the key: rc=%s\n", gpg_strerror (rc));
+      rc = gpg_error (GPG_ERR_CARD);
+      goto leave;
+    }
+/*    log_printhex ("RSA n:", m, mlen);  */
+/*    log_printhex ("RSA e:", e, elen);  */
+
+  rc = store_fpr (app->slot, keyno, (u32)created_at,
+                  m, mlen, e, elen, fprbuf, app->card_version);
+
+ leave:
+  return rc;
+}
+
+
+/* Utility function for external tools: Read the public RSA key at
+   KEYNO and return modulus and exponent in (M,MLEN) and (E,ELEN). */
+int 
+app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
+                     unsigned char **e, size_t *elen)
+{
+  int rc;
+  const unsigned char *keydata, *a;
+  unsigned char *buffer;
+  size_t buflen, keydatalen, alen;
+
+  *m = NULL;
+  *e = NULL;
+
+  if (keyno < 1 || keyno > 3)
+    return gpg_error (GPG_ERR_INV_ID);
+  keyno--;
+
+  rc = iso7816_read_public_key(app->slot, 
+                               keyno == 0? "\xB6" :
+                               keyno == 1? "\xB8" : "\xA4",
+                               2,
+                               &buffer, &buflen);
+  if (rc)
+    {
+      rc = gpg_error (GPG_ERR_CARD);
+      log_error ("reading key failed\n");
+      goto leave;
+    }
+
+  keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
+  if (!keydata)
+    {
+      log_error ("response does not contain the public key data\n");
+      rc = gpg_error (GPG_ERR_CARD);
+      goto leave;
+    }
+  a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0);
+  if (!a)
+    {
+      log_error ("response does not contain the RSA modulus\n");
+      rc = gpg_error (GPG_ERR_CARD);
+      goto leave;
+    }
+  *mlen = alen;
+  *m = xmalloc (alen);
+  memcpy (*m, a, alen);
+  
+  a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0);
+  if (!e)
+    {
+      log_error ("response does not contain the RSA public exponent\n");
+      rc = gpg_error (GPG_ERR_CARD);
+      goto leave;
+    }
+  *elen = alen;
+  *e = xmalloc (alen);
+  memcpy (*e, a, alen);
+
+ leave:
+  xfree (buffer);
+  if (rc)
+    { 
+      xfree (*m); *m = NULL;
+      xfree (*e); *e = NULL;
+    }
+  return rc;
+}
+
+#endif /*ENABLE_CARD_SUPPORT*/
diff --git a/g10/card-util.c b/g10/card-util.c
new file mode 100644 (file)
index 0000000..901ce92
--- /dev/null
@@ -0,0 +1,720 @@
+/* card-util.c - Utility functions for the OpenPGP card.
+ *     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
+ */
+
+#include <config.h>
+#ifdef ENABLE_CARD_SUPPORT
+/* 
+   Note, that most card related code has been taken from 1.9.x branch
+   and is maintained over there if at all possible.  Thus, if you make
+   changes here, please check that a similar change has been commited
+   to the 1.9.x branch.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "util.h"
+#include "i18n.h"
+#include "ttyio.h"
+#include "status.h"
+#include "options.h"
+#include "main.h"
+
+#include "cardglue.h"
+
+#define CONTROL_D ('D' - 'A' + 1)
+
+
+/* Change the PIN of a an OpenPGP card.  This is an interactive
+   function. */
+void
+change_pin (int chvno)
+{
+  struct agent_card_info_s info;
+  int rc;
+  int reset_mode = 0;
+
+  rc = agent_learn (&info);
+  if (rc)
+    {
+      log_error (_("OpenPGP card not available: %s\n"),
+                  gpg_strerror (rc));
+      return;
+    }
+  
+  log_info (_("OpenPGP card no. %s detected\n"),
+              info.serialno? info.serialno : "[none]");
+
+  agent_release_card_info (&info);
+
+  if (opt.batch)
+    {
+      log_error (_("sorry, can't do this in batch mode\n"));
+      return;
+    }
+
+  for (;;)
+    {
+      char *answer;
+
+      tty_printf ("\n");
+      tty_printf ("1 - change signature PIN\n"
+                  "2 - change decryption and authentication PIN\n"
+                  "3 - change Admin's PIN\n"
+                  "R - toggle reset retry counter mode\n"
+                  "Q - quit\n");
+      tty_printf ("\n");
+      if (reset_mode)
+        {
+          tty_printf ("Reset Retry Counter mode active\n");
+          tty_printf ("\n");
+        }
+
+      answer = cpr_get("cardutil.change_pin.menu",_("Your selection? "));
+      cpr_kill_prompt();
+      if (strlen (answer) != 1)
+        continue;
+
+      rc = 0;
+      if (reset_mode && *answer == '3')
+        {
+          tty_printf ("Sorry, reset of the Admin PIN's retry counter "
+                      "is not possible.\n");
+        }
+      else if (*answer == '1'  || *answer == '2' || *answer == '3')
+        {
+          rc = agent_scd_change_pin (*answer - '0' + (reset_mode?100:0));
+          if (rc)
+            tty_printf ("Error changing/resetting the PIN: %s\n",
+                        gpg_strerror (rc));
+          else
+            tty_printf ("New PIN successfully set.\n");
+        }
+      else if (*answer == 'r' || *answer == 'R')
+        {
+          reset_mode = !reset_mode;
+        }
+      else if (*answer == 'q' || *answer == 'Q')
+        {
+          break;
+        }
+    }
+
+}
+
+static const char *
+get_manufacturer (unsigned int no)
+{
+  /* Note:  Make sure that there is no colon or linefeed in the string. */
+  switch (no)
+    {
+    case 0:
+    case 0xffff: return "test card";
+    case 0x0001: return "PPC Card Systems";
+    default: return "unknown";
+    }
+}
+
+
+static void
+print_sha1_fpr (FILE *fp, const unsigned char *fpr)
+{
+  int i;
+
+  if (fpr)
+    {
+      for (i=0; i < 20 ; i+=2, fpr += 2 )
+        {
+          if (i == 10 )
+            tty_fprintf (fp, " ");
+          tty_fprintf (fp, " %02X%02X", *fpr, fpr[1]);
+        }
+    }
+  else
+    tty_fprintf (fp, " [none]");
+  tty_fprintf (fp, "\n");
+}
+
+
+static void
+print_sha1_fpr_colon (FILE *fp, const unsigned char *fpr)
+{
+  int i;
+
+  if (fpr)
+    {
+      for (i=0; i < 20 ; i++, fpr++)
+        fprintf (fp, "%02X", *fpr);
+    }
+  putc (':', fp);
+}
+
+
+static void
+print_name (FILE *fp, const char *text, const char *name)
+{
+  tty_fprintf (fp, text);
+
+
+  /* FIXME: tty_printf_utf8_string2 eats everything after and
+     including an @ - e.g. when printing an url. */
+  if (name && *name)
+    {
+      if (fp)
+        print_utf8_string2 (fp, name, strlen (name), '\n');
+      else
+        tty_print_utf8_string2 (name, strlen (name), '\n');
+    }
+  else
+    tty_fprintf (fp, _("[not set]"));
+  tty_fprintf (fp, "\n");
+}
+
+static void
+print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
+{
+  if (opt.with_colons)
+    fprintf (fp, "%s:", tag);
+  else
+    tty_fprintf (fp, text);
+
+  if (name && *name)
+    {
+      char *p, *given, *buf = xstrdup (name);
+
+      given = strstr (buf, "<<");
+      for (p=buf; *p; p++)
+        if (*p == '<')
+          *p = ' ';
+      if (given && given[2])
+        {
+          *given = 0;
+          given += 2;
+          if (opt.with_colons)
+            print_string (fp, given, strlen (given), ':');
+          else if (fp)
+            print_utf8_string2 (fp, given, strlen (given), '\n');
+          else
+            tty_print_utf8_string2 (given, strlen (given), '\n');
+
+          if (opt.with_colons)
+            putc (':', fp);
+          else if (*buf)
+            tty_fprintf (fp, " ");
+        }
+
+      if (opt.with_colons)
+        print_string (fp, buf, strlen (buf), ':');
+      else if (fp)
+        print_utf8_string2 (fp, buf, strlen (buf), '\n');
+      else
+        tty_print_utf8_string2 (buf, strlen (buf), '\n');
+      xfree (buf);
+    }
+  else
+    {
+      if (opt.with_colons)
+        putc (':', fp);
+      else
+        tty_fprintf (fp, _("[not set]"));
+    }
+
+  if (opt.with_colons)
+    fputs (":\n", fp);
+  else
+    tty_fprintf (fp, "\n");
+}
+
+
+/* Print all available information about the current card. */
+void
+card_status (FILE *fp)
+{
+  struct agent_card_info_s info;
+  PKT_public_key *pk = xcalloc (1, sizeof *pk);
+  int rc;
+  unsigned int uval;
+
+  rc = agent_learn (&info);
+  if (rc)
+    {
+      if (opt.with_colons)
+        fputs ("AID:::\n", fp);
+      log_error (_("OpenPGP card not available: %s\n"),
+                  gpg_strerror (rc));
+      xfree (pk);
+      return;
+    }
+
+  if (opt.with_colons)
+    fprintf (fp, "AID:%s:", info.serialno? info.serialno : "");
+  else
+    tty_fprintf (fp, "Application ID ...: %s\n",
+                 info.serialno? info.serialno : "[none]");
+  if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) 
+      || strlen (info.serialno) != 32 )
+    {
+      if (opt.with_colons)
+        fputs ("unknown:\n", fp);
+      log_info ("not an OpenPGP card\n");
+      agent_release_card_info (&info);
+      xfree (pk);
+      return;
+    }
+
+  if (opt.with_colons)
+    fputs ("openpgp-card:\n", fp);
+
+
+  if (opt.with_colons)
+    {
+      fprintf (fp, "version:%.4s:\n", info.serialno+12);
+      uval = xtoi_2(info.serialno+16)*256 + xtoi_2 (info.serialno+18);
+      fprintf (fp, "vendor:%04x:%s:\n", uval, get_manufacturer (uval));
+      fprintf (fp, "serial:%.8s:\n", info.serialno+20);
+      
+      print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
+
+      fputs ("lang:", fp);
+      if (info.disp_lang)
+        print_string (fp, info.disp_lang, strlen (info.disp_lang), ':');
+      fputs (":\n", fp);
+
+      fprintf (fp, "sex:%c:\n", (info.disp_sex == 1? 'm':
+                                 info.disp_sex == 2? 'f' : 'u'));
+
+      fputs ("url:", fp);
+      if (info.pubkey_url)
+        print_string (fp, info.pubkey_url, strlen (info.pubkey_url), ':');
+      fputs (":\n", fp);
+
+      fputs ("login:", fp);
+      if (info.login_data)
+        print_string (fp, info.login_data, strlen (info.login_data), ':');
+      fputs (":\n", fp);
+
+      fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
+      fprintf (fp, "maxpinlen:%d:%d:%d:\n",
+                   info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
+      fprintf (fp, "pinretry:%d:%d:%d:\n",
+                   info.chvretry[0], info.chvretry[1], info.chvretry[2]);
+      fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
+
+      fputs ("fpr:", fp);
+      print_sha1_fpr_colon (fp, info.fpr1valid? info.fpr1:NULL);
+      print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL);
+      print_sha1_fpr_colon (fp, info.fpr3valid? info.fpr3:NULL);
+      putc ('\n', fp);
+
+    }
+  else 
+    {
+      tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n",
+                   info.serialno[12] == '0'?"":info.serialno+12,
+                   info.serialno[13],
+                   info.serialno[14] == '0'?"":info.serialno+14,
+                   info.serialno[15]);
+      tty_fprintf (fp, "Manufacturer .....: %s\n", 
+                   get_manufacturer (xtoi_2(info.serialno+16)*256
+                                     + xtoi_2 (info.serialno+18)));
+      tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20);
+      
+      print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
+      print_name (fp, "Language prefs ...: ", info.disp_lang);
+      tty_fprintf (fp,    "Sex ..............: %s\n",
+                   info.disp_sex == 1? _("male"):
+                   info.disp_sex == 2? _("female") : _("unspecified"));
+      print_name (fp, "URL of public key : ", info.pubkey_url);
+      print_name (fp, "Login data .......: ", info.login_data);
+      tty_fprintf (fp,    "Signature PIN ....: %s\n",
+                   info.chv1_cached? _("cached"): _("not cached"));
+      tty_fprintf (fp,    "Max. PIN lengths .: %d %d %d\n",
+                   info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
+      tty_fprintf (fp,    "PIN retry counter : %d %d %d\n",
+                   info.chvretry[0], info.chvretry[1], info.chvretry[2]);
+      tty_fprintf (fp,    "Signature counter : %lu\n", info.sig_counter);
+      tty_fprintf (fp, "Signature key ....:");
+      print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL);
+      tty_fprintf (fp, "Encryption key....:");
+      print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
+      tty_fprintf (fp, "Authentication key:");
+      print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
+/*       tty_fprintf (fp, "General key info..: ");  */
+/*       if (info.fpr1valid && !get_pubkey_byfprint (pk, info.fpr1, 20)) */
+/*         print_pubkey_info (fp, pk); */
+/*       else */
+/*         tty_fprintf (fp, "[none]\n"); */
+    }
+      
+  free_public_key (pk);
+  agent_release_card_info (&info);
+}
+
+
+static char *
+get_one_name (const char *prompt1, const char *prompt2)
+{
+  char *name;
+  int i;
+
+  for (;;)
+    {
+      name = cpr_get (prompt1, prompt2);
+      if (!name)
+        return NULL;
+      trim_spaces (name);
+      cpr_kill_prompt ();
+      for (i=0; name[i] && name[i] >= ' ' && name[i] <= 126; i++)
+        ;
+
+      /* The name must be in Latin-1 and not UTF-8 - lacking the code
+         to ensure this we restrict it to ASCII. */
+      if (name[i])
+        tty_printf (_("Error: Only plain ASCII is currently allowed.\n"));
+      else if (strchr (name, '<'))
+        tty_printf (_("Error: The \"<\" character may not be used.\n"));
+      else if (strstr (name, "  "))
+        tty_printf (_("Error: Double spaces are not allowed.\n"));    
+      else
+        return name;
+      xfree (name);
+    }
+}
+
+
+
+static int
+change_name (void)
+{
+  char *surname = NULL, *givenname = NULL;
+  char *isoname, *p;
+  int rc;
+
+  surname = get_one_name ("keygen.smartcard.surname",
+                                    _("Cardholder's surname: "));
+  givenname = get_one_name ("keygen.smartcard.givenname",
+                                       _("Cardholder's given name: "));
+  if (!surname || !givenname || (!*surname && !*givenname))
+    {
+      xfree (surname);
+      xfree (givenname);
+      return -1; /*canceled*/
+    }
+
+  isoname = xmalloc ( strlen (surname) + 2 + strlen (givenname) + 1);
+  strcpy (stpcpy (stpcpy (isoname, surname), "<<"), givenname);
+  xfree (surname);
+  xfree (givenname);
+  for (p=isoname; *p; p++)
+    if (*p == ' ')
+      *p = '<';
+
+  log_debug ("setting Name to `%s'\n", isoname);
+  rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) );
+  if (rc)
+    log_error ("error setting Name: %s\n", gpg_strerror (rc));
+
+  xfree (isoname);
+  return rc;
+}
+
+
+static int
+change_url (void)
+{
+  char *url;
+  int rc;
+
+  url = cpr_get ("cardedit.change_url", _("URL to retrieve public key: "));
+  if (!url)
+    return -1;
+  trim_spaces (url);
+  cpr_kill_prompt ();
+
+  rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) );
+  if (rc)
+    log_error ("error setting URL: %s\n", gpg_strerror (rc));
+  xfree (url);
+  return rc;
+}
+
+static int
+change_login (void)
+{
+  char *data;
+  int rc;
+
+  data = cpr_get ("cardedit.change_login",
+                  _("Login data (account name): "));
+  if (!data)
+    return -1;
+  trim_spaces (data);
+  cpr_kill_prompt ();
+
+  rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) );
+  if (rc)
+    log_error ("error setting login data: %s\n", gpg_strerror (rc));
+  xfree (data);
+  return rc;
+}
+
+static int
+change_lang (void)
+{
+  char *data, *p;
+  int rc;
+
+  data = cpr_get ("cardedit.change_lang",
+                  _("Language preferences: "));
+  if (!data)
+    return -1;
+  trim_spaces (data);
+  cpr_kill_prompt ();
+
+  if (strlen (data) > 8 || (strlen (data) & 1))
+    {
+      tty_printf (_("Error: invalid length of preference string.\n"));
+      xfree (data);
+      return -1;
+    }
+
+  for (p=data; *p && *p >= 'a' && *p <= 'z'; p++)
+    ;
+  if (*p)
+    {
+      tty_printf (_("Error: invalid characters in preference string.\n"));
+      xfree (data);
+      return -1;
+    }
+
+  rc = agent_scd_setattr ("DISP-LANG", data, strlen (data) );
+  if (rc)
+    log_error ("error setting lang: %s\n", gpg_strerror (rc));
+  xfree (data);
+  return rc;
+}
+
+
+static int
+change_sex (void)
+{
+  char *data;
+  const char *str;
+  int rc;
+
+  data = cpr_get ("cardedit.change_sex",
+                  _("Sex ((M)ale, (F)emale or space): "));
+  if (!data)
+    return -1;
+  trim_spaces (data);
+  cpr_kill_prompt ();
+
+  if (!*data)
+    str = "9";
+  else if ((*data == 'M' || *data == 'm') && !data[1])
+    str = "1";
+  else if ((*data == 'F' || *data == 'f') && !data[1])
+    str = "2";
+  else 
+    {
+      tty_printf (_("Error: invalid response.\n"));
+      xfree (data);
+      return -1;
+    }
+     
+  rc = agent_scd_setattr ("DISP-SEX", str, 1 );
+  if (rc)
+    log_error ("error setting sex: %s\n", gpg_strerror (rc));
+  xfree (data);
+  return rc;
+}
+
+
+/* Menu to edit all user changeable values on an OpenPGP card.  Only
+   Key creation is not handled here. */
+void
+card_edit (STRLIST commands)
+{
+  enum cmdids {
+    cmdNOP = 0,
+    cmdQUIT, cmdHELP, cmdLIST, cmdDEBUG,
+    cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX,
+
+    cmdINVCMD
+  };
+
+  static struct {
+    const char *name;
+    enum cmdids id;
+    const char *desc;
+  } cmds[] = {
+    { N_("quit")  , cmdQUIT  , N_("quit this menu") },
+    { N_("q")     , cmdQUIT  , NULL   },
+    { N_("help")  , cmdHELP  , N_("show this help") },
+    {    "?"      , cmdHELP  , NULL   },
+    { N_("list")  , cmdLIST  , N_("list all available data") },
+    { N_("l")     , cmdLIST  , NULL   },
+    { N_("debug") , cmdDEBUG , NULL },
+    { N_("name")  , cmdNAME  , N_("change card holder's name") },
+    { N_("url")   , cmdURL   , N_("change URL to retrieve key") },
+    { N_("login") , cmdLOGIN , N_("change the login name") },
+    { N_("lang")  , cmdLANG  , N_("change the language preferences") },
+    { N_("sex")   , cmdSEX   , N_("change card holder's sex") },
+    { NULL, cmdINVCMD } 
+  };
+
+  enum cmdids cmd = cmdNOP;
+  int have_commands = !!commands;
+  int redisplay = 1;
+  char *answer = NULL;
+
+  if (opt.command_fd != -1)
+    ;
+  else if (opt.batch && !have_commands)
+    {
+      log_error(_("can't do that in batchmode\n"));
+      goto leave;
+    }
+
+  for (;;)
+    {
+      int arg_number;
+      const char *arg_string = "";
+      char *p;
+      int i;
+
+      tty_printf("\n");
+      if (redisplay )
+        {
+          if (opt.with_colons)
+            {
+              card_status (stdout);
+              fflush (stdout);
+            }
+          else
+            {
+              card_status (NULL);
+              tty_printf("\n");
+            }
+          redisplay = 0;
+       }
+
+      do
+        {
+          xfree (answer);
+          if (have_commands)
+            {
+              if (commands)
+                {
+                  answer = xstrdup (commands->d);
+                  commands = commands->next;
+               }
+              else if (opt.batch)
+                {
+                  answer = xstrdup ("quit");
+               }
+              else
+                have_commands = 0;
+           }
+
+           if (!have_commands)
+              {
+               answer = cpr_get_no_help("cardedit.prompt", _("Command> "));
+               cpr_kill_prompt();
+           }
+           trim_spaces(answer);
+       }
+      while( *answer == '#' );
+
+      arg_number = 0; /* Yes, here is the init which egcc complains about */
+      if (!*answer)
+        cmd = cmdLIST; /* Default to the list command */
+      else if (*answer == CONTROL_D)
+        cmd = cmdQUIT;
+      else {
+        if ((p=strchr (answer,' ')))
+          {
+            *p++ = 0;
+            trim_spaces (answer);
+            trim_spaces (p);
+            arg_number = atoi(p);
+            arg_string = p;
+          }
+
+        for (i=0; cmds[i].name; i++ )
+          if (!ascii_strcasecmp (answer, cmds[i].name ))
+            break;
+
+        cmd = cmds[i].id;
+      }
+
+      switch (cmd)
+        {
+        case cmdHELP:
+          for (i=0; cmds[i].name; i++ )
+            if (cmds[i].desc)
+              tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
+          break;
+
+        case cmdLIST:
+          redisplay = 1;
+          break;
+
+        case cmdNAME:
+          change_name ();
+          break;
+
+        case cmdURL:
+          change_url ();
+          break;
+
+        case cmdLOGIN:
+          change_login ();
+          break;
+
+        case cmdLANG:
+          change_lang ();
+          break;
+
+        case cmdSEX:
+          change_sex ();
+          break;
+
+        case cmdQUIT:
+          goto leave;
+
+        case cmdNOP:
+          break;
+
+        case cmdINVCMD:
+        default:
+          tty_printf ("\n");
+          tty_printf (_("Invalid command  (try \"help\")\n"));
+          break;
+        } /* End command switch. */
+    } /* End of main menu loop. */
+
+ leave:
+  xfree (answer);
+}
+
+#endif /*ENABLE_CARD_SUPPORT*/
index ca47cef..8924889 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <stdarg.h>
 #include <assert.h>
 
 #include "options.h"
 #include "i18n.h"
 
 #include "cardglue.h"
+#include "apdu.h"
+#include "app-common.h"
+
+struct ctrl_ctx_s {
+  int (*status_cb)(void *opaque, const char *line);
+  void *status_cb_arg;
+};
+
+
+static char *default_reader_port;
+
 
 
 
@@ -70,11 +82,432 @@ serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
 }
 
 
+/* Send a line with status information via assuan and escape all given
+   buffers. The variable elements are pairs of (char *, size_t),
+   terminated with a (NULL, 0). */
+void
+send_status_info (CTRL ctrl, const char *keyword, ...)
+{
+  va_list arg_ptr;
+  const unsigned char *value;
+  size_t valuelen;
+  char buf[950], *p;
+  size_t n;
+  
+  va_start (arg_ptr, keyword);
+
+  p = buf; 
+  n = 0;
+  valuelen = strlen (keyword);
+  for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, keyword++)
+    *p++ = *keyword;
+
+  while ( (value = va_arg (arg_ptr, const unsigned char *)) )
+    {
+      valuelen = va_arg (arg_ptr, size_t);
+      if (!valuelen)
+        continue; /* empty buffer */
+      if (n)
+        {
+          *p++ = ' ';
+          n++;
+        }
+      for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
+        {
+          if (*value < ' ' || *value == '+')
+            {
+              sprintf (p, "%%%02X", *value);
+              p += 3;
+            }
+          else if (*value == ' ')
+            *p++ = '+';
+          else
+            *p++ = *value;
+        }
+    }
+  *p = 0;
+  ctrl->status_cb (ctrl->status_cb_arg, buf);
+
+  va_end (arg_ptr);
+}
 
 
+void gcry_md_hash_buffer (int algo, void *digest,
+                         const void *buffer, size_t length)
+{
+  MD_HANDLE h = md_open (algo, 0);
+  if (!h)
+    BUG();
+  md_write (h, (byte *) buffer, length);
+  md_final (h);
+  memcpy (digest, md_read (h, algo), md_digest_length (algo));
+  md_close (h);
+}
+
+
+/* This is a limited version of the one in 1.9 but it should be
+   sufficient here. */
+void
+log_printf (const char *fmt, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, fmt);
+  vfprintf (log_stream (), fmt, arg_ptr);
+  va_end (arg_ptr);
+}
+
+
+
+/* Print a hexdump of BUFFER.  With TEXT of NULL print just the raw
+   dump, with TEXT just an empty string, print a trailing linefeed,
+   otherwise print an entire debug line. */
+void
+log_printhex (const char *text, const void *buffer, size_t length)
+{
+  if (text && *text)
+    log_debug ("%s ", text);
+  if (length)
+    {
+      const unsigned char *p = buffer;
+      log_printf ("%02X", *p);
+      for (length--, p++; length--; p++)
+        log_printf (" %02X", *p);
+    }
+  if (text)
+    log_printf ("\n");
+}
+
+
+
+void
+app_set_default_reader_port (const char *portstr)
+{
+  xfree (default_reader_port);
+  default_reader_port = portstr? xstrdup (portstr): NULL;
+}
+
+
+/* Retrieve the serial number and the time of the last update of the
+   card.  The serial number is returned as a malloced string (hex
+   encoded) in SERIAL and the time of update is returned in STAMP.  If
+   no update time is available the returned value is 0.  Caller must
+   free SERIAL unless the function returns an error. */
+int 
+app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
+{
+  unsigned char *buf, *p;
+  int i;
+
+  if (!app || !serial || !stamp)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  *serial = NULL;
+  *stamp = 0; /* not available */
+
+  buf = xtrymalloc (app->serialnolen * 2 + 1);
+  if (!buf)
+    return gpg_error_from_errno (errno);
+  for (p=buf, i=0; i < app->serialnolen; p +=2, i++)
+    sprintf (p, "%02X", app->serialno[i]);
+  *p = 0;
+  *serial = buf;
+  return 0;
+}
+
+
+
+
+
+
+/* Release the card info structure. */
+void 
+agent_release_card_info (struct agent_card_info_s *info)
+{
+  if (!info)
+    return;
+
+  xfree (info->serialno); info->serialno = NULL;
+  xfree (info->disp_name); info->disp_name = NULL;
+  xfree (info->disp_lang); info->disp_lang = NULL;
+  xfree (info->pubkey_url); info->pubkey_url = NULL;
+  xfree (info->login_data); info->login_data = NULL;
+  info->fpr1valid = info->fpr2valid = info->fpr3valid = 0;
+}
+
+
+/* Open the current card and select the openpgp application.  Return
+   an APP context handle to be used for further procesing or NULL on
+   error or if no OpenPGP application exists.*/
+static APP
+open_card (void)
+{
+  int slot;
+  int rc;
+  APP app;
+
+  slot = apdu_open_reader (default_reader_port);
+  if (slot == -1)
+    {
+      log_error ("card reader not available\n");
+      return NULL;
+    }
+
+  app = xcalloc (1, sizeof *app);
+  app->slot = slot;
+  rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
+  if (rc)
+    {
+/*        apdu_close_reader (slot); */
+      log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+      xfree (app);
+      return NULL;
+    }
+
+  app->initialized = 1;
+  return app;
+}
 
 
 
+/* Return a new malloced string by unescaping the string S.  Escaping
+   is percent escaping and '+'/space mapping.  A binary nul will
+   silently be replaced by a 0xFF.  Function returns NULL to indicate
+   an out of memory status. */
+static char *
+unescape_status_string (const unsigned char *s)
+{
+  char *buffer, *d;
+
+  buffer = d = xmalloc (strlen (s)+1);
+  while (*s)
+    {
+      if (*s == '%' && s[1] && s[2])
+        { 
+          s++;
+          *d = xtoi_2 (s);
+          if (!*d)
+            *d = '\xff';
+          d++;
+          s += 2;
+        }
+      else if (*s == '+')
+        {
+          *d++ = ' ';
+          s++;
+        }
+      else
+        *d++ = *s++;
+    }
+  *d = 0; 
+  return buffer;
+}
+
+/* Take a 20 byte hexencoded string and put it into the the provided
+   20 byte buffer FPR in binary format. */
+static int
+unhexify_fpr (const char *hexstr, unsigned char *fpr)
+{
+  const char *s;
+  int n;
+
+  for (s=hexstr, n=0; hexdigitp (s); s++, n++)
+    ;
+  if (*s || (n != 40))
+    return 0; /* no fingerprint (invalid or wrong length). */
+  n /= 2;
+  for (s=hexstr, n=0; *s; s += 2, n++)
+    fpr[n] = xtoi_2 (s);
+  return 1; /* okay */
+}
+
+/* Take the serial number from LINE and return it verbatim in a newly
+   allocated string.  We make sure that only hex characters are
+   returned. */
+static char *
+store_serialno (const char *line)
+{
+  const char *s;
+  char *p;
+
+  for (s=line; hexdigitp (s); s++)
+    ;
+  p = xmalloc (s + 1 - line);
+  memcpy (p, line, s-line);
+  p[s-line] = 0;
+  return p;
+}
+
+
+
+static int
+learn_status_cb (void *opaque, const char *line)
+{
+  struct agent_card_info_s *parm = opaque;
+  const char *keyword = line;
+  int keywordlen;
+  int i;
+
+  for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+    ;
+  while (spacep (line))
+    line++;
+
+  if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
+    {
+      parm->serialno = store_serialno (line);
+    }
+  else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
+    {
+      parm->disp_name = unescape_status_string (line);
+    }
+  else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen))
+    {
+      parm->disp_lang = unescape_status_string (line);
+    }
+  else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen))
+    {
+      parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0;
+    }
+  else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen))
+    {
+      parm->pubkey_url = unescape_status_string (line);
+    }
+  else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen))
+    {
+      parm->login_data = unescape_status_string (line);
+    }
+  else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen))
+    {
+      parm->sig_counter = strtoul (line, NULL, 0);
+    }
+  else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen))
+    {
+      char *p, *buf;
+
+      buf = p = unescape_status_string (line);
+      if (buf)
+        {
+          while (spacep (p))
+            p++;
+          parm->chv1_cached = atoi (p);
+          while (!spacep (p))
+            p++;
+          while (spacep (p))
+            p++;
+          for (i=0; *p && i < 3; i++)
+            {
+              parm->chvmaxlen[i] = atoi (p);
+              while (!spacep (p))
+                p++;
+              while (spacep (p))
+                p++;
+            }
+          for (i=0; *p && i < 3; i++)
+            {
+              parm->chvretry[i] = atoi (p);
+              while (!spacep (p))
+                p++;
+              while (spacep (p))
+                p++;
+            }
+          xfree (buf);
+        }
+    }
+  else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
+    {
+      int no = atoi (line);
+      while (!spacep (line))
+        line++;
+      while (spacep (line))
+        line++;
+      if (no == 1)
+        parm->fpr1valid = unhexify_fpr (line, parm->fpr1);
+      else if (no == 2)
+        parm->fpr2valid = unhexify_fpr (line, parm->fpr2);
+      else if (no == 3)
+        parm->fpr3valid = unhexify_fpr (line, parm->fpr3);
+    }
+  
+  return 0;
+}
+
+
+/* Return card info. */
+int 
+agent_learn (struct agent_card_info_s *info)
+{
+  APP app;
+  int rc;
+  struct ctrl_ctx_s ctrl;
+  time_t stamp;
+  char *serial;
+  
+  app = open_card ();
+  if (!app)
+    return gpg_error (GPG_ERR_CARD);
+
+  memset (&ctrl, 0, sizeof ctrl);
+  ctrl.status_cb = learn_status_cb;
+  ctrl.status_cb_arg = info;
+
+  rc = app_get_serial_and_stamp (app, &serial, &stamp);
+  if (!rc)
+    {
+      send_status_info (&ctrl, "SERIALNO", serial, strlen(serial), NULL, 0);
+      xfree (serial);
+      rc = app->fnc.learn_status (app, &ctrl);
+    }
+
+  return rc;
+}
+
+/* Send a SETATTR command to the SCdaemon. */
+int 
+agent_scd_setattr (const char *name,
+                   const unsigned char *value, size_t valuelen)
+{
+
+  return gpg_error (GPG_ERR_CARD);
+}
+
+/* Send a GENKEY command to the SCdaemon. */
+int 
+agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force)
+{
+
+  return gpg_error (GPG_ERR_CARD);
+}
+
+/* Send a PKSIGN command to the SCdaemon. */
+int 
+agent_scd_pksign (const char *keyid, int hashalgo,
+                  const unsigned char *indata, size_t indatalen,
+                  char **r_buf, size_t *r_buflen)
+{
+
+  return gpg_error (GPG_ERR_CARD);
+}
+
+
+/* Send a PKDECRYPT command to the SCdaemon. */
+int 
+agent_scd_pkdecrypt (const char *serialno,
+                     const unsigned char *indata, size_t indatalen,
+                     char **r_buf, size_t *r_buflen)
+{
+
+  return gpg_error (GPG_ERR_CARD);
+}
+
+/* Change the PIN of an OpenPGP card or reset the retry counter. */
+int 
+agent_scd_change_pin (int chvno)
+{
+
+  return gpg_error (GPG_ERR_CARD);
+}
+
 
 
 
index 03b40a0..0768498 100644 (file)
@@ -60,13 +60,64 @@ struct agent_card_genkey_s {
 };
 
 
+struct app_ctx_s;
+struct ctrl_ctx_s;
 
+typedef struct app_ctx_s *APP;
+typedef struct ctrl_ctx_s *CTRL;
+
+
+#define GPG_ERR_BAD_PIN           G10ERR_BAD_PASS
+#define GPG_ERR_CARD              G10ERR_GENERAL
+#define GPG_ERR_EEXIST            G10ERR_FILE_EXISTS
+#define GPG_ERR_ENOMEM            G10ERR_RESOURCE_LIMIT
+#define GPG_ERR_GENERAL           G10ERR_GENERAL
+#define GPG_ERR_HARDWARE          G10ERR_GENERAL
+#define GPG_ERR_INV_CARD          G10ERR_GENERAL
+#define GPG_ERR_INV_ID            G10ERR_GENERAL
+#define GPG_ERR_INV_NAME          G10ERR_GENERAL
+#define GPG_ERR_INV_VALUE         G10ERR_INV_ARG
+#define GPG_ERR_NOT_SUPPORTED     G10ERR_UNSUPPORTED
+#define GPG_ERR_NO_OBJ            G10ERR_GENERAL
+#define GPG_ERR_PIN_BLOCKED       G10ERR_PASSPHRASE
+#define GPG_ERR_UNSUPPORTED_ALGORITHM G10ERR_PUBKEY_ALGO 
+#define GPG_ERR_USE_CONDITIONS    G10ERR_GENERAL
+#define GPG_ERR_WRONG_CARD        G10ERR_GENERAL
+#define GPG_ERR_WRONG_SECKEY      G10ERR_WRONG_SECKEY
+
+
+typedef int gpg_error_t;
+typedef int gpg_err_code_t;
+
+#define gpg_error(n) (n)
+#define gpg_err_code(n) (n)
+#define gpg_strerror(n) g10_errstr ((n))
+#define gpg_error_from_errno(n) (G10ERR_GENERAL) /*FIXME*/
+
+
+/* We are not using it in a library, so we even let xtrymalloc
+   abort. Because we won't never return from these malloc functions,
+   we also don't need the out_of_core function, we simply define it to
+   return -1 */
+#define xtrymalloc(n)    xmalloc((n))
+#define xtrycalloc(n,m)  xcalloc((n),(m))
+#define xtryrealloc(n,m) xrealloc((n),(m))
+#define out_of_core()    (-1) 
+
+#define gnupg_get_time() make_timestamp ()
 
 
 char *serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
                                 PKT_secret_key *sk);
+void send_status_info (CTRL ctrl, const char *keyword, ...);
+void gcry_md_hash_buffer (int algo, void *digest,
+                         const void *buffer, size_t length);
+void log_printf (const char *fmt, ...);
+void log_printhex (const char *text, const void *buffer, size_t length);
 
 
+#define GCRY_MD_SHA1 DIGEST_ALGO_SHA1
+#define GCRY_MD_RMD160 DIGEST_ALGO_RMD160
 
 
 /* Release the card info structure. */
diff --git a/g10/ccid-driver.c b/g10/ccid-driver.c
new file mode 100644 (file)
index 0000000..de8f870
--- /dev/null
@@ -0,0 +1,990 @@
+/* ccid-driver.c - USB ChipCardInterfaceDevices driver
+ *     Copyright (C) 2003 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., 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.
+ */
+
+
+/* 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 <config.h>
+#endif
+
+#if defined(HAVE_LIBUSB) || defined(TEST)
+
+#define  GNUPG_DEFAULT_SCDAEMON 1 /* Hack for 1.3 */
+
+#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: "
+
+
+#ifdef GNUPG_DEFAULT_SCDAEMON /* This source is used within the
+                                 gnupg>=1.9 source tree. */
+# include "options.h"
+# include "util.h"
+# include "memory.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(t)    do { if (DBG_CARD_IO) \
+                                  log_printf (t); } while (0)
+# define DEBUGOUT_CONT_1(t,a)  do { if (DBG_CARD_IO) \
+                                  log_printf (t,(a)); } while (0)
+# define DEBUGOUT_CONT_2(t,a,b)   do { if (DBG_CARD_IO) \
+                                  log_printf (t,(a),(b)); } 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(t)     fprintf (stderr, t)
+# define DEBUGOUT_CONT_1(t,a)      fprintf (stderr, t, (a))
+# define DEBUGOUT_CONT_2(t,a,b)    fprintf (stderr, t, (a), (b))
+# 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,
+
+  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_ns;
+  unsigned char t1_nr;
+};
+
+
+/* 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); 
+}
+
+
+
+/* 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 (const unsigned char *buf, size_t buflen)
+{
+  unsigned int i;
+  unsigned int us;
+  int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
+
+
+  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);
+
+  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 accpeted\n");
+  if ((us & 0x0400))
+    DEBUGOUT ("    Auto IFSD exchange\n");
+
+  if ((us & 0x00010000))
+    {
+      DEBUGOUT ("    TPDU level exchange\n");
+      have_tpdu = 1;
+    } 
+  else if ((us & 0x00020000))
+    DEBUGOUT ("    Short APDU level exchange\n");
+  else if ((us & 0x00040000))
+    DEBUGOUT ("    Short and extended APDU level exchange\n");
+  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_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");
+  if ((buf[52] & 2))
+    DEBUGOUT_CONT ( " modification");
+  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 || !have_auto_conf)
+    {
+      DEBUGOUT ("this drivers requires that the reader supports T=1, "
+                "TPDU level exchange and auto configuration - "
+                "this is not available\n");
+      return -1;
+    }
+  else
+    return 0;
+}
+
+
+/* Read the device information, return all required data and check
+   that the device is usable for us.  Returns 0 on success or an error
+   code. */
+static int
+read_device_info (struct usb_device *dev)
+{
+  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;
+
+      for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
+        {
+          struct usb_interface *interface = config->interface + ifc_no;
+          int set_no;
+
+          for (set_no=0; set_no < interface->num_altsetting; set_no++)
+            {
+              struct usb_interface_descriptor *ifcdesc
+                = interface->altsetting + set_no;
+              
+              if (ifcdesc->bInterfaceClass == 11
+                  && ifcdesc->bInterfaceSubClass == 0
+                  && ifcdesc->bInterfaceProtocol == 0)
+                {
+                  if (ifcdesc->extra)
+                    {
+                      if (!parse_ccid_descriptor (ifcdesc->extra,
+                                                 ifcdesc->extralen))
+                        return 0; /* okay. we can use it. */
+                    }
+                }
+            }
+        }
+    }
+  return -1; /* No suitable device found. */
+}
+
+
+/* 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)
+    {
+      DEBUGOUT_1 ("usb_create_match failed: %d\n", rc);
+      return -1;
+    }
+
+  while (usb_find_device(match, dev, &dev) >= 0) 
+    {
+      DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename,
+                  dev->descriptor->idVendor, dev->descriptor->idProduct);
+      if (!readerno)
+        {
+          rc = read_device_info (dev);
+          if (rc)
+            {
+              DEBUGOUT ("device not supported\n");
+              goto leave;
+            }
+
+          rc = usb_open (dev, &idev);
+          if (rc)
+            {
+              DEBUGOUT_1 ("usb_open failed: %d\n", rc);
+              goto leave;
+            }
+
+
+          /* fixme: Do we need to claim and set the interface as
+             determined by read_device_info ()? */
+          rc = usb_claim_interface (idev, 0);
+          if (rc)
+            {
+              DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+              goto leave;
+            }
+
+          *handle = calloc (1, sizeof **handle);
+          if (!*handle)
+            {
+              DEBUGOUT ("out of memory\n");
+              rc = -1;
+              goto leave;
+            }
+          (*handle)->idev = idev;
+          idev = NULL;
+          /* FIXME: Do we need to get the endpoint addresses from the
+             structure and store them with the handle? */
+
+          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)
+    DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
+  else
+    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. 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, int expected_type, int seqno)
+{
+  int i, rc;
+  size_t msglen;
+
+  rc = usb_bulk_read (handle->idev, 
+                      0x82,
+                      buffer, length,
+                      10000 /* ms timeout */ );
+  /* Fixme: instead of using a 10 second timeout we should better
+     handle the timeout here and retry if appropriate.  */
+  if (rc < 0)
+    {
+      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;
+    }
+
+  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;
+}
+
+
+/* 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)
+    {
+      DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno));
+      return -1;
+    }
+
+  msglen = rc;
+  rc = 0;
+
+  if (msglen < 1)
+    {
+      DEBUGOUT ("intr-in msg too short\n");
+      return -1;
+    }
+
+  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;
+}
+
+
+
+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, RDR_to_PC_SlotStatus, seqno);
+  if (rc)
+    return rc;
+
+  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;
+
+  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);
+  if (rc)
+    return rc;
+  
+  if (atr)
+    {
+      size_t n = msglen - 10;
+
+      if (n > maxatrlen)
+        n = maxatrlen;
+      memcpy (atr, msg+10, n);
+      *atrlen = n;
+    }
+
+  return 0;
+}
+
+
+\f
+/*
+  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, size_t apdulen,
+                 unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+  int rc;
+  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_ns & 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;
+
+  tpdulen = 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 blocks!\n");
+          return -1; 
+        }
+
+/*       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;
+            }
+
+          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;
+            }
+
+          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 ("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;
+          
+        }
+      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;
+}
+
+
+
+
+#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/g10/ccid-driver.h b/g10/ccid-driver.h
new file mode 100644 (file)
index 0000000..4d12d88
--- /dev/null
@@ -0,0 +1,74 @@
+/* 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
+ *
+ * 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.
+ */
+
+#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*/
+
+
+
index a280779..f50bbe8 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -128,6 +128,9 @@ enum cmd_and_opt_values { aNull = 0,
     aPipeMode,
     aRebuildKeydbCaches,
     aRefreshKeys,
+    aCardStatus,
+    aCardEdit,
+    aChangePIN,
 
     oTextmode,
     oNoTextmode,
@@ -318,6 +321,12 @@ enum cmd_and_opt_values { aNull = 0,
     oNoMangleDosFilenames,
     oEnableProgressFilter,
     oMultifile,
+
+    oReaderPort,
+    octapiDriver,
+    opcscDriver,
+    oDisableCCID,
+
 aTest };
 
 
@@ -365,6 +374,11 @@ static ARGPARSE_OPTS opts[] = {
     { aExportSecretSub, "export-secret-subkeys" , 256, "@" },
     { aImport, "import",      256     , N_("import/merge keys")},
     { aFastImport, "fast-import",  256 , "@"},
+#ifdef ENABLE_CARD_SUPPORT
+    { aCardStatus,  "card-status", 256, N_("print the card status")},
+    { aCardEdit,   "card-edit",  256, N_("change data on a card")},
+    { aChangePIN,  "change-pin", 256, N_("change a card's PIN")},
+#endif
     { aListPackets, "list-packets",256, "@"},
     { aExportOwnerTrust, "export-ownertrust", 256, "@"},
     { aImportOwnerTrust, "import-ownertrust", 256, "@"},
@@ -622,6 +636,13 @@ static ARGPARSE_OPTS opts[] = {
     { oNoMangleDosFilenames, "no-mangle-dos-filenames", 0, "@" },
     { oEnableProgressFilter, "enable-progress-filter", 0, "@" },
     { oMultifile, "multifile", 0, "@" },
+
+    { oReaderPort, "reader-port",    2, "@"},
+    { octapiDriver, "ctapi-driver",  2, "@"},
+    { opcscDriver, "pcsc-driver",    2, "@"},
+    { oDisableCCID, "disable-ccidc", 0, "@"},
+
+
 {0} };
 
 
@@ -1392,6 +1413,18 @@ main( int argc, char **argv )
           case aPipeMode: set_cmd( &cmd, aPipeMode); break;
           case aRebuildKeydbCaches: set_cmd( &cmd, aRebuildKeydbCaches); break;
 
+#ifdef ENABLE_CARD_SUPPORT
+          case aCardStatus: set_cmd (&cmd, aCardStatus); break;
+          case aCardEdit: set_cmd (&cmd, aCardEdit); break;
+          case aChangePIN: set_cmd (&cmd, aChangePIN); break;
+          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;
+#endif /* ENABLE_CARD_SUPPORT*/
+
          case oArmor: opt.armor = 1; opt.no_armor=0; break;
          case oOutput: opt.outfile = pargs.r.ret_str; break;
          case oQuiet: opt.quiet = 1; break;
@@ -2828,6 +2861,36 @@ main( int argc, char **argv )
         keydb_rebuild_caches ();
         break;
 
+#ifdef ENABLE_CARD_SUPPORT
+      case aCardStatus:
+        if (argc)
+            wrong_args ("--card-status");
+        card_status (stdout);
+        break;
+
+      case aCardEdit:
+        if (argc) {
+            sl = NULL;
+            for (argc--, argv++ ; argc; argc--, argv++)
+                append_to_strlist (&sl, *argv);
+            card_edit (sl);
+            free_strlist (sl);
+       }
+        else
+            card_edit (NULL);
+        break;
+
+      case aChangePIN:
+        if (!argc)
+            change_pin (0);
+        else if (argc == 1)
+            change_pin ( atoi (*argv));
+        else
+        wrong_args ("--change-pin [no]");
+        break;
+#endif /* ENABLE_CARD_SUPPORT*/
+
+
       case aListPackets:
        opt.list_packets=2;
       default:
diff --git a/g10/iso7816.c b/g10/iso7816.c
new file mode 100644 (file)
index 0000000..f4308f5
--- /dev/null
@@ -0,0 +1,385 @@
+/* iso7816.c - ISO 7816 commands
+ *     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
+ */
+
+#include <config.h>
+#ifdef ENABLE_CARD_SUPPORT
+/* 
+   Note, that most of this code has been taken from 1.9.x branch
+   and is maintained over there if at all possible.  Thus, if you make
+   changes here, please check that a similar change has been commited
+   to the 1.9.x branch.
+*/
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "options.h"
+#include "errors.h"
+#include "memory.h"
+#include "util.h"
+#include "i18n.h"
+
+#include "iso7816.h"
+#include "apdu.h"
+
+
+#define CMD_SELECT_FILE 0xA4
+#define CMD_VERIFY      0x20
+#define CMD_CHANGE_REFERENCE_DATA 0x24
+#define CMD_RESET_RETRY_COUNTER   0x2C
+#define CMD_GET_DATA    0xCA
+#define CMD_PUT_DATA    0xDA
+#define CMD_PSO         0x2A
+#define CMD_INTERNAL_AUTHENTICATE 0x88
+#define CMD_GENERATE_KEYPAIR      0x47
+#define CMD_GET_CHALLENGE         0x84
+
+static gpg_error_t
+map_sw (int sw)
+{
+  gpg_err_code_t ec;
+
+  switch (sw)
+    {
+    case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
+    case SW_WRONG_LENGTH:   ec = GPG_ERR_INV_VALUE; break;
+    case SW_CHV_WRONG:      ec = GPG_ERR_BAD_PIN; break;
+    case SW_CHV_BLOCKED:    ec = GPG_ERR_PIN_BLOCKED; break;
+    case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
+    case SW_NOT_SUPPORTED:  ec = GPG_ERR_NOT_SUPPORTED; break;
+    case SW_BAD_PARAMETER:  ec = GPG_ERR_INV_VALUE; break;
+    case SW_REF_NOT_FOUND:  ec = GPG_ERR_NO_OBJ; break;
+    case SW_BAD_P0_P1:      ec = GPG_ERR_INV_VALUE; break;
+    case SW_INS_NOT_SUP:    ec = GPG_ERR_CARD; break;
+    case SW_CLA_NOT_SUP:    ec = GPG_ERR_CARD; break;
+    case SW_SUCCESS:        ec = 0; break;
+
+    case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
+    case SW_HOST_INV_VALUE:   ec = GPG_ERR_INV_VALUE; break;
+    case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
+    default:
+      if ((sw & 0x010000))
+        ec = GPG_ERR_GENERAL; /* Should not happen. */
+      else if ((sw & 0xff00) == SW_MORE_DATA)
+        ec = 0; /* This should actually never been seen here. */
+      else
+        ec = GPG_ERR_CARD;
+    }
+  return gpg_error (ec);
+}
+
+/* This function is specialized version of the SELECT FILE command.
+   SLOT is the card and reader as created for example by
+   apdu_open_reader (), AID is a buffer of size AIDLEN holding the
+   requested application ID.  The function can't be used to enumerate
+   AIDs and won't return the AID on success.  The return value is 0
+   for okay or GNUPG error code.  Note that ISO error codes are
+   internally mapped. */
+gpg_error_t
+iso7816_select_application (int slot, const char *aid, size_t aidlen)
+{
+  int sw;
+
+  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
+  return map_sw (sw);
+}
+
+
+/* Perform a VERIFY command on SLOT using the card holder verification
+   vector CHVNO with a CHV of lenght CHVLEN.  Returns 0 on success. */
+gpg_error_t
+iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
+{
+  int sw;
+
+  sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+  return map_sw (sw);
+}
+
+/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
+   verification vector CHVNO.  If the OLDCHV is NULL (and OLDCHVLEN
+   0), a "change reference data" is done, otherwise an "exchange
+   reference data".  The new reference data is expected in NEWCHV of
+   length NEWCHVLEN.  */
+gpg_error_t
+iso7816_change_reference_data (int slot, int chvno,
+                               const char *oldchv, size_t oldchvlen,
+                               const char *newchv, size_t newchvlen)
+{
+  int sw;
+  char *buf;
+
+  if ((!oldchv && oldchvlen)
+      || (oldchv && !oldchvlen)
+      || !newchv || !newchvlen )
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  buf = xtrymalloc (oldchvlen + newchvlen);
+  if (!buf)
+    return out_of_core ();
+  if (oldchvlen)
+    memcpy (buf, oldchv, oldchvlen);
+  memcpy (buf+oldchvlen, newchv, newchvlen);
+
+  sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
+                         oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
+  xfree (buf);
+  return map_sw (sw);
+
+}
+
+gpg_error_t
+iso7816_reset_retry_counter (int slot, int chvno,
+                             const char *newchv, size_t newchvlen)
+{
+  int sw;
+
+  if (!newchv || !newchvlen )
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER,
+                         2, chvno, newchvlen, newchv);
+  return map_sw (sw);
+}
+
+
+/* Perform a GET DATA command requesting TAG and storing the result in
+   a newly allocated buffer at the address passed by RESULT.  Return
+   the length of this data at the address of RESULTLEN. */
+gpg_error_t
+iso7816_get_data (int slot, int tag,
+                  unsigned char **result, size_t *resultlen)
+{
+  int sw;
+
+  if (!result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  sw = apdu_send (slot, 0x00, CMD_GET_DATA,
+                  ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
+                  result, resultlen);
+  if (sw != SW_SUCCESS)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+      return map_sw (sw);
+    }
+
+  return 0;
+}
+
+
+/* Perform a PUT DATA command on card in SLOT.  Write DATA of length
+   DATALEN to TAG. */
+gpg_error_t
+iso7816_put_data (int slot, int tag,
+                  const unsigned char *data, size_t datalen)
+{
+  int sw;
+
+  sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
+                         ((tag >> 8) & 0xff), (tag & 0xff),
+                         datalen, data);
+  return map_sw (sw);
+}
+
+
+/* Perform the security operation COMPUTE DIGITAL SIGANTURE.  On
+   success 0 is returned and the data is availavle in a newly
+   allocated buffer stored at RESULT with its length stored at
+   RESULTLEN. */
+gpg_error_t
+iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
+                    unsigned char **result, size_t *resultlen)
+{
+  int sw;
+
+  if (!data || !datalen || !result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, data,
+                  result, resultlen);
+  if (sw != SW_SUCCESS)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+      return map_sw (sw);
+    }
+
+  return 0;
+}
+
+
+/* Perform the security operation DECIPHER.  On
+   success 0 is returned and the plaintext is available in a newly
+   allocated buffer stored at RESULT with its length stored at
+   RESULTLEN. */
+gpg_error_t
+iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
+                  unsigned char **result, size_t *resultlen)
+{
+  int sw;
+  unsigned char *buf;
+
+  if (!data || !datalen || !result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  /* We need to prepend the padding indicator. */
+  buf = xtrymalloc (datalen + 1);
+  if (!buf)
+    return out_of_core ();
+  *buf = 0; /* Padding indicator. */
+  memcpy (buf+1, data, datalen);
+  sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
+                  result, resultlen);
+  xfree (buf);
+  if (sw != SW_SUCCESS)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+      return map_sw (sw);
+    }
+
+  return 0;
+}
+
+
+gpg_error_t
+iso7816_internal_authenticate (int slot,
+                               const unsigned char *data, size_t datalen,
+                               unsigned char **result, size_t *resultlen)
+{
+  int sw;
+
+  if (!data || !datalen || !result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
+                  datalen, data,  result, resultlen);
+  if (sw != SW_SUCCESS)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+      return map_sw (sw);
+    }
+
+  return 0;
+}
+
+
+static gpg_error_t
+do_generate_keypair (int slot, int readonly,
+                  const unsigned char *data, size_t datalen,
+                  unsigned char **result, size_t *resultlen)
+{
+  int sw;
+
+  if (!data || !datalen || !result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
+                  datalen, data,  result, resultlen);
+  if (sw != SW_SUCCESS)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+      return map_sw (sw);
+    }
+
+  return 0;
+}
+
+
+gpg_error_t
+iso7816_generate_keypair (int slot,
+                          const unsigned char *data, size_t datalen,
+                          unsigned char **result, size_t *resultlen)
+{
+  return do_generate_keypair (slot, 0, data, datalen, result, resultlen);
+}
+
+
+gpg_error_t
+iso7816_read_public_key (int slot,
+                          const unsigned char *data, size_t datalen,
+                          unsigned char **result, size_t *resultlen)
+{
+  return do_generate_keypair (slot, 1, data, datalen, result, resultlen);
+}
+
+
+
+gpg_error_t
+iso7816_get_challenge (int slot, int length, unsigned char *buffer)
+{
+  int sw;
+  unsigned char *result;
+  size_t resultlen, n;
+
+  if (!buffer || length < 1)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  do
+    {
+      result = NULL;
+      n = length > 254? 254 : length;
+      sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL,
+                         n,
+                         &result, &resultlen);
+      if (sw != SW_SUCCESS)
+        {
+          /* Make sure that pending buffers are released. */
+          xfree (result);
+          return map_sw (sw);
+        }
+      if (resultlen > n)
+        resultlen = n;
+      memcpy (buffer, result, resultlen);
+      buffer += resultlen;
+      length -= resultlen;
+      xfree (result);
+    }
+  while (length > 0);
+
+  return 0;
+}
+
+#endif /*ENABLE_CARD_SUPPORT*/
diff --git a/g10/iso7816.h b/g10/iso7816.h
new file mode 100644 (file)
index 0000000..99c4a8e
--- /dev/null
@@ -0,0 +1,58 @@
+/* iso7816.h - ISO 7816 commands
+ *     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 ISO7816_H
+#define ISO7816_H
+
+#include "cardglue.h"
+
+gpg_error_t iso7816_select_application (int slot,
+                                        const char *aid, size_t aidlen);
+gpg_error_t iso7816_verify (int slot,
+                            int chvno, const char *chv, size_t chvlen);
+gpg_error_t iso7816_change_reference_data (int slot, int chvno,
+                               const char *oldchv, size_t oldchvlen,
+                               const char *newchv, size_t newchvlen);
+gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
+                                         const char *newchv, size_t newchvlen);
+gpg_error_t iso7816_get_data (int slot, int tag,
+                              unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_put_data (int slot, int tag,
+                              const unsigned char *data, size_t datalen);
+gpg_error_t iso7816_compute_ds (int slot,
+                                const unsigned char *data, size_t datalen,
+                                unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_decipher (int slot,
+                              const unsigned char *data, size_t datalen,
+                              unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_internal_authenticate (int slot,
+                                   const unsigned char *data, size_t datalen,
+                                   unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_generate_keypair (int slot,
+                                   const unsigned char *data, size_t datalen,
+                                   unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_read_public_key (int slot,
+                                   const unsigned char *data, size_t datalen,
+                                   unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_get_challenge (int slot,
+                                   int length, unsigned char *buffer);
+
+
+#endif /*ISO7816_H*/
index 2ac5680..c4fd89d 100644 (file)
@@ -182,6 +182,13 @@ struct {
     int strict;
     int mangle_dos_filenames; 
     int enable_progress_filter;
+
+#ifdef ENABLE_CARD_SUPPORT
+    const char *ctapi_driver; /* Library to access the ctAPI. */
+    const char *pcsc_driver;  /* Library to access the PC/SC system. */
+    int disable_ccid;    /* Disable the use of the internal CCID driver. */
+#endif /*ENABLE_CARD_SUPPORT*/
+
 } opt;
 
 
@@ -199,6 +206,7 @@ struct {
 #define DBG_TRUST_VALUE   256  /* debug the trustdb */
 #define DBG_HASHING_VALUE 512  /* debug hashing operations */
 #define DBG_EXTPROG_VALUE 1024  /* debug external program calls */
+#define DBG_CARD_IO_VALUE 2048
 
 
 #define DBG_PACKET (opt.debug & DBG_PACKET_VALUE)
@@ -207,6 +215,7 @@ struct {
 #define DBG_TRUST  (opt.debug & DBG_TRUST_VALUE)
 #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
 #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE)
+#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
 
 #define GNUPG   (opt.compliance==CO_GNUPG)
 #define RFC1991 (opt.compliance==CO_RFC1991 || opt.compliance==CO_PGP2)