aboutsummaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorRepo Admin <[email protected]>2003-08-05 17:11:04 +0000
committerRepo Admin <[email protected]>2003-08-05 17:11:04 +0000
commit9ca4830a5b8392bc7bb01211407c876fd40b49d4 (patch)
tree5b5eb60b48cea2f86d01b8d9a458a2fa4fc0a2b2 /scd
parentminor changes to make make distcheck happy (diff)
downloadgnupg-9ca4830a5b8392bc7bb01211407c876fd40b49d4.tar.gz
gnupg-9ca4830a5b8392bc7bb01211407c876fd40b49d4.zip
This commit was manufactured by cvs2svn to create branch
'GNUPG-1-9-BRANCH'.
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog242
-rw-r--r--scd/Makefile.am72
-rw-r--r--scd/apdu.c558
-rw-r--r--scd/apdu.h73
-rw-r--r--scd/app-common.h128
-rw-r--r--scd/app-openpgp.c1482
-rw-r--r--scd/app.c278
-rw-r--r--scd/card-common.h73
-rw-r--r--scd/card-p15.c502
-rw-r--r--scd/card.c564
-rw-r--r--scd/command.c1034
-rw-r--r--scd/iso7816.c371
-rw-r--r--scd/iso7816.h56
-rw-r--r--scd/sc-copykeys.c731
-rw-r--r--scd/sc-investigate.c209
-rw-r--r--scd/scdaemon.c638
-rw-r--r--scd/scdaemon.h127
17 files changed, 7138 insertions, 0 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
new file mode 100644
index 000000000..ad4b0518c
--- /dev/null
+++ b/scd/ChangeLog
@@ -0,0 +1,242 @@
+2003-07-31 Werner Koch <[email protected]>
+
+ * Makefile.am (scdaemon_LDADD): Added INTLLIBS.
+
+2003-07-28 Werner Koch <[email protected]>
+
+ * app-openpgp.c (do_setattr): Change implementation. Allow all
+ useful DOs.
+
+2003-07-27 Werner Koch <[email protected]>
+
+ Adjusted for gcry_mpi_print and gcry_mpi_scan API change.
+
+2003-07-24 Werner Koch <[email protected]>
+
+ * app-openpgp.c (do_learn_status): Print more status information.
+ (app_select_openpgp): Store the card version.
+ (store_fpr): Add argument card_version and fix DOs for old cards.
+ (app_openpgp_storekey): Likewise.
+
+2003-07-23 Werner Koch <[email protected]>
+
+ * command.c (cmd_pkauth): New.
+ (cmd_setdata): Check whether data was given at all to avoid
+ passing 0 to malloc.
+
+ * app.c (app_auth): New.
+ * app-openpgp.c (do_auth): New.
+
+2003-07-22 Werner Koch <[email protected]>
+
+ * command.c (cmd_passwd): New.
+ * app.c (app_change_pin): New.
+ * app-openpgp.c (do_change_pin): New.
+ * iso7816.c (iso7816_reset_retry_counter): Implemented.
+
+ * sc-investigate.c (main): New option --gen-random.
+ * iso7816.c (iso7816_get_challenge): Don't create APDUs with a
+ length larger than 255.
+
+2003-07-17 Werner Koch <[email protected]>
+
+ * command.c (cmd_random): New command RANDOM.
+
+ * iso7816.c (map_sw): New. Use it in this file to return
+ meaningful error messages. Changed all public fucntions to return
+ a gpg_error_t.
+ (iso7816_change_reference_data): New.
+ * apdu.c (apdu_open_reader): Use faked status words for soem
+ system errors.
+
+2003-07-16 Werner Koch <[email protected]>
+
+ * apdu.c (apdu_send_simple): Use apdu_send_le so that we can
+ specify not to send Le as it should be.
+
+2003-07-15 Werner Koch <[email protected]>
+
+ * Makefile.am: Add sc-copykeys program.
+ * sc-copykeys.c: New.
+ * app-openpgp.c (app_openpgp_storekey): New.
+ (app_openpgp_cardinfo): New.
+ (count_bits): New.
+ (store_fpr): And use it here to get the actual length in bit.
+
+2003-07-03 Werner Koch <[email protected]>
+
+ * app-openpgp.c (do_setattr): Add setting of the URL.
+ (app_select_openpgp): Dump card data only in very verbose mode.
+ (do_decipher): New.
+
+2003-07-02 Werner Koch <[email protected]>
+
+ * app-openpgp.c (get_sig_counter): New.
+ (do_sign): Print the signature counter and enable the PIN callback.
+ (do_genkey): Implement the PIN callback.
+
+2003-07-01 Werner Koch <[email protected]>
+
+ * app-openpgp.c (store_fpr): Fixed fingerprint calculation.
+
+2003-06-26 Werner Koch <[email protected]>
+
+ * app-openpgp.c (find_tlv): Fixed length header parsing.
+
+ * app.c (app_genkey): New.
+ * command.c (cmd_genkey): New.
+
+2003-06-25 Werner Koch <[email protected]>
+
+ * command.c (percent_plus_unescape): New.
+ (cmd_setattr): New.
+
+2003-06-24 Werner Koch <[email protected]>
+
+ * command.c (send_status_info): New.
+
+ * app-openpgp.c (app_select_openpgp): Replace SLOT arg by APP arg
+ and setup the function pointers in APP on success. Changed callers.
+ * app.c: New.
+ * app-common.h: New.
+ * scdaemon.h (APP): New type to handle applications.
+ (server_control_s): Add an APP context field.
+
+ * command.c (cmd_serialno): Handle applications.
+ (cmd_pksign): Ditto.
+ (cmd_pkdecrypt): Ditto.
+ (reset_notify): Ditto.
+ (cmd_learn): For now return error for application contexts.
+ (cmd_readcert): Ditto.
+ (cmd_readkey): Ditto.
+
+2003-06-04 Werner Koch <[email protected]>
+
+ * card.c (map_sc_err): Renamed gpg_make_err to gpg_err_make.
+
+ Renamed error codes from INVALID to INV and removed _ERROR suffixes.
+
+2003-06-03 Werner Koch <[email protected]>
+
+ Changed all error codes in all files to the new libgpg-error scheme.
+
+ * scdaemon.h: Include gpg-error.h and errno.h
+ * card.c (map_sc_err): Use unknown for the error source.
+ * Makefile.am: Link with libgpg-error
+
+2003-05-14 Werner Koch <[email protected]>
+
+ * atr.c, atr.h: New.
+ * sc-investigate.c: Dump the ATR in a human readable format.
+
+2003-05-08 Werner Koch <[email protected]>
+
+ * scdaemon.h (DBG_CARD_IO_VALUE): New.
+
+ * sc-investigate.c: New.
+ * scdaemon.c (main): Removed --print-atr option.
+
+ * iso7816.c, iso7816.h, app-openpgp.c: New.
+
+2003-04-29 Werner Koch <[email protected]>
+
+ * scdaemon.c: New options --print-atr and --reader-port
+ * apdu.c, apdu.h: New
+
+ * card.c, card-p15.c, card-dinsig.c: Allow build without OpenSC.
+
+ * Makefile.am (LDFLAGS): Removed.
+
+ * command.c (register_commands): Adjusted for new Assuan semantics.
+
+2002-08-21 Werner Koch <[email protected]>
+
+ * scdaemon.c (main): New option --daemon so that the program is
+ not accidently started in the background.
+
+2002-08-16 Werner Koch <[email protected]>
+
+ * scdaemon.c: Include i18n.h.
+
+ * card-common.h (struct p15_private_s): Forward declaration. Add
+ it to card_ctx_s.
+ * card.c (card_close): Make sure private data is released.
+ (card_enum_certs): New.
+ * card-p15.c (p15_release_private_data): New.
+ (init_private_data): New to work around an OpenSC weirdness.
+ (p15_enum_keypairs): Do an OpenSC get_objects only once.
+ (p15_enum_certs): New.
+ (card_p15_bind): Bind new function.
+ * command.c (cmd_learn): Return information about the certificates.
+
+2002-08-09 Werner Koch <[email protected]>
+
+ * card.c (card_get_serial_and_stamp): Use the tokeinfo serial
+ number as a fallback. Add a special prefix for serial numbers.
+
+2002-07-30 Werner Koch <[email protected]>
+
+ Changes to cope with OpenSC 0.7.0:
+
+ * card.c: Removed the check for the packed opensc version.
+ Changed include file names of opensc.
+ (map_sc_err): Adjusted error codes for new opensc version.
+ * card-p15.c: Changed include filename of opensc.
+ * card-dinsig.c: Ditto.
+
+ * card-p15.c (p15_decipher): Add flags argument to OpenSC call.
+
+2002-07-24 Werner Koch <[email protected]>
+
+ * card.c (find_simple_tlv, find_iccsn): New.
+ (card_get_serial_and_stamp): Improved serial number parser.
+
+2002-06-27 Werner Koch <[email protected]>
+
+ * scdaemon.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.
+
+2002-06-15 Werner Koch <[email protected]>
+
+ * card-dinsig.c: Documented some stuff from the DIN norm.
+
+2002-04-15 Werner Koch <[email protected]>
+
+ * command.c (cmd_pksign, cmd_pkdecrypt): Use a copy of the key ID.
+
+2002-04-12 Werner Koch <[email protected]>
+
+ * scdaemon.c: New option --debug-sc N.
+ * card.c (card_open): set it here.
+
+ * card-p15.c (p15_prepare_key): Factored out common code from ...
+ (p15_sign, p15_decipher): here and made the decryption work the
+ regular way.
+
+2002-04-10 Werner Koch <[email protected]>
+
+ * card.c (card_open): Return immediately when no reader is available.
+
+2002-03-27 Werner Koch <[email protected]>
+
+ * card.c (card_open, card_close): Adjusted for changes in OpenSC.
+
+2002-03-10 Werner Koch <[email protected]>
+
+ * card-p15.c, card-dinsig.c, card-common.h: New.
+ * card.c: Factored most code out to the new modules, so that we
+ can better support different types of card applications.
+
+2002-01-26 Werner Koch <[email protected]>
+
+ * scdaemon.c scdaemon.h, command.c: New. Based on the code from
+ the gpg-agent.
+
+ Copyright 2002 Free Software Foundation, Inc.
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/scd/Makefile.am b/scd/Makefile.am
new file mode 100644
index 000000000..0771beb60
--- /dev/null
+++ b/scd/Makefile.am
@@ -0,0 +1,72 @@
+# Copyright (C) 2002, 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
+
+## Process this file with automake to produce Makefile.in
+
+localedir = $(datadir)/locale
+INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\"
+
+bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
+
+AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
+ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
+
+scdaemon_SOURCES = \
+ scdaemon.c scdaemon.h \
+ command.c card.c \
+ card-common.h \
+ card-p15.c card-dinsig.c \
+ apdu.c apdu.h \
+ iso7816.c iso7816.h \
+ app.c app-common.h \
+ app-openpgp.c
+
+scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
+ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
+ -lgpg-error @INTLLIBS@ -ldl
+
+sc_investigate_SOURCES = \
+ sc-investigate.c scdaemon.h \
+ apdu.c apdu.h \
+ iso7816.c iso7816.h \
+ app.c app-common.h \
+ app-openpgp.c \
+ atr.c atr.h
+
+sc_investigate_LDADD = \
+ ../jnlib/libjnlib.a ../common/libcommon.a \
+ $(LIBGCRYPT_LIBS) @INTLLIBS@ -lgpg-error -ldl
+
+
+sc_copykeys_SOURCES = \
+ sc-copykeys.c scdaemon.h \
+ apdu.c apdu.h \
+ iso7816.c iso7816.h \
+ app.c app-common.h \
+ app-openpgp.c \
+ atr.c atr.h
+
+sc_copykeys_LDADD = \
+ ../jnlib/libjnlib.a ../common/libcommon.a \
+ ../common/libsimple-pwquery.a \
+ $(LIBGCRYPT_LIBS) -lgpg-error @INTLLIBS@ -ldl
+
+
+
+
+
diff --git a/scd/apdu.c b/scd/apdu.c
new file mode 100644
index 000000000..6fec584b9
--- /dev/null
+++ b/scd/apdu.c
@@ -0,0 +1,558 @@
+/* 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>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <assert.h>
+
+#include "scdaemon.h"
+#include "apdu.h"
+
+#define HAVE_CTAPI 1
+
+#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 global table to keep track of active readers. */
+static struct {
+ int used; /* True if slot is used. */
+ unsigned short port; /* port number0 = unused, 1 - dev/tty */
+ int status;
+ unsigned char atr[33];
+ size_t atrlen;
+} 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);
+
+
+
+
+
+/*
+ Helper
+ */
+
+
+/* Find an unused reader slot for PORT and put it into the reader
+ table. Return -1 on error or the index into the reader table. */
+static int
+new_reader_slot (int port)
+{
+ int i, reader = -1;
+
+ if (port < 0 || port > 0xffff)
+ {
+ log_error ("new_reader_slot: invalid port %d requested\n", port);
+ return -1;
+ }
+
+ for (i=0; i < MAX_READER; i++)
+ {
+ if (reader_table[i].used && reader_table[i].port == port)
+ {
+ log_error ("new_reader_slot: requested port %d already in use\n",
+ reader);
+ return -1;
+ }
+ else 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].port = port;
+ return reader;
+}
+
+
+static void
+dump_reader_status (int reader)
+{
+ log_info ("reader %d: %s\n", reader,
+ reader_table[reader].status == 1? "Processor ICC present" :
+ reader_table[reader].status == 0? "Memory ICC present" :
+ "ICC not present" );
+
+ if (reader_table[reader].status != -1)
+ {
+ log_info ("reader %d: ATR=", reader);
+ log_printhex ("", reader_table[reader].atr,
+ reader_table[reader].atrlen);
+ }
+}
+
+
+
+#ifdef HAVE_CTAPI
+/*
+ ct API Interface
+ */
+
+static const char *
+ct_error_string (int 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)
+ sleep (1); /* FIXME: we should use a more reliable timer. */
+
+ /* 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;
+ }
+
+ if (buf[0] == 0x05)
+ { /* 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;
+
+ reader = new_reader_slot (port);
+ if (reader == -1)
+ return reader;
+
+ 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;
+ }
+
+ 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;
+}
+
+
+#endif /*HAVE_CTAPI*/
+
+
+#ifdef HAVE_PCSC
+/*
+ PC/SC Interface
+ */
+
+
+#endif /*HAVE_PCSC*/
+
+
+/*
+ Driver Access
+ */
+
+/* Open the reader and return an internal slot number or -1 on
+ error. */
+int
+apdu_open_reader (int port)
+{
+ static int ct_api_loaded;
+
+ if (!ct_api_loaded)
+ {
+ void *handle;
+
+ handle = dlopen ("libtowitoko.so", 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 driver\n");
+ dlclose (handle);
+ return -1;
+ }
+ ct_api_loaded = 1;
+ }
+ return open_ct_reader (port);
+}
+
+
+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, int rc)
+{
+#ifdef HAVE_CTAPI
+ return ct_error_string (rc);
+#elif defined(HAVE_PCSC)
+ return "?";
+#else
+ return "?";
+#endif
+}
+
+
+/* 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)
+{
+#ifdef HAVE_CTAPI
+ return ct_send_apdu (slot, apdu, apdulen, buffer, buflen);
+#elif defined(HAVE_PCSC)
+ return SW_HOST_NO_DRIVER;
+#else
+ return SW_HOST_NO_DRIVER;
+#endif
+}
+
+/* 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 rc, sw;
+
+ 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);
+}
+
+
+
+
diff --git a/scd/apdu.h b/scd/apdu.h
new file mode 100644
index 000000000..44166a3fe
--- /dev/null
+++ b/scd/apdu.h
@@ -0,0 +1,73 @@
+/* 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,
+};
+
+
+
+/* Note , that apdu_open_reader returns no status word but -1 on error. */
+int apdu_open_reader (int port);
+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/scd/app-common.h b/scd/app-common.h
new file mode 100644
index 000000000..282f82715
--- /dev/null
+++ b/scd/app-common.h
@@ -0,0 +1,128 @@
+/* app-common.h - Common declarations for all card applications
+ * 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 GNUPG_SCD_APP_COMMON_H
+#define GNUPG_SCD_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;
+
+
+};
+
+/*-- app.c --*/
+APP select_application (void);
+int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
+int app_write_learn_status (APP app, CTRL ctrl);
+int app_setattr (APP app, const char *name,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen);
+int app_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 app_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 app_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 app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer);
+int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+
+
+/*-- app-openpgp.c --*/
+int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
+
+int app_openpgp_cardinfo (APP app,
+ char **serialno,
+ char **disp_name,
+ char **pubkey_url,
+ unsigned char **fpr1,
+ unsigned char **fpr2,
+ unsigned char **fpr3);
+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 app_openpgp_readkey (APP app, int keyno,
+ unsigned char **m, size_t *mlen,
+ unsigned char **e, size_t *elen);
+
+
+#endif /*GNUPG_SCD_APP_COMMON_H*/
+
+
+
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
new file mode 100644
index 000000000..09a19699d
--- /dev/null
+++ b/scd/app-openpgp.c
@@ -0,0 +1,1482 @@
+/* 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>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "scdaemon.h"
+#include "app-common.h"
+#include "iso7816.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;
+}
+
+#if 0 /* not used */
+static void
+dump_one_do (int slot, int tag)
+{
+ int rc, i;
+ unsigned char *buffer;
+ size_t buflen;
+ const char *desc;
+ int binary;
+ const unsigned char *value;
+ size_t valuelen;
+
+ for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
+ ;
+ desc = data_objects[i].tag? data_objects[i].desc : "?";
+ binary = data_objects[i].tag? data_objects[i].binary : 1;
+
+ 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)
+ {
+ value = find_tlv (buffer, buflen, tag, &valuelen, 0);
+ if (!value)
+ ; /* not found */
+ else if (valuelen > buflen - (value - buffer))
+ {
+ log_error ("warning: constructed DO too short\n");
+ value = NULL;
+ xfree (buffer); buffer = NULL;
+ }
+ }
+ }
+
+ 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 == 0x6a88)
+ log_info ("DO `%s' not available\n", desc);
+ else if (rc)
+ log_info ("DO `%s' not available (rc=%04X)\n", desc, rc);
+ else
+ {
+ if (binary)
+ {
+ log_info ("DO `%s': ", desc);
+ log_printhex ("", value, valuelen);
+ }
+ else
+ log_info ("DO `%s': `%.*s'\n",
+ desc, (int)valuelen, value); /* FIXME: sanitize */
+ xfree (buffer);
+ }
+}
+#endif /*not used*/
+
+
+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 (rc == 0x6a88)
+ ;
+ else if (rc)
+ log_info ("DO `%s' not available (rc=%04X)\n",
+ data_objects[i].desc, 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: rc=%04X\n", 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\n");
+ 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=%04X\n", 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=%04X\n", 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=%04X\n", 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=%04X\n", 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)
+ {
+ /* fixme: get the full AID and check that the version is okay
+ with us. */
+ 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=%04X\n", 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=%04X\n", 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;
+}
diff --git a/scd/app.c b/scd/app.c
new file mode 100644
index 000000000..7a85df336
--- /dev/null
+++ b/scd/app.c
@@ -0,0 +1,278 @@
+/* app.c - Application selection.
+ * 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>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include "scdaemon.h"
+#include "app-common.h"
+#include "apdu.h"
+#include "iso7816.h"
+
+/* The select the best fitting application and return a context.
+ Returns NULL if no application was found or no card is present. */
+APP
+select_application (void)
+{
+ int reader_port = 32768; /* First USB reader. */
+ int slot;
+ int rc;
+ APP app;
+
+ slot = apdu_open_reader (reader_port);
+ if (slot == -1)
+ {
+ log_error ("card reader not available\n");
+ return NULL;
+ }
+
+ app = xtrycalloc (1, sizeof *app);
+ if (!app)
+ {
+ rc = out_of_core ();
+ log_info ("error allocating context: %s\n", gpg_strerror (rc));
+ /*apdu_close_reader (slot);*/
+ return NULL;
+ }
+
+ 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;
+}
+
+
+
+/* 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;
+}
+
+
+/* Write out the application specifig status lines for the LEARN
+ command. */
+int
+app_write_learn_status (APP app, CTRL ctrl)
+{
+ if (!app)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.learn_status)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ return app->fnc.learn_status (app, ctrl);
+}
+
+
+/* Perform a SETATTR operation. */
+int
+app_setattr (APP app, const char *name,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen)
+{
+ if (!app || !name || !*name || !value)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.setattr)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ return app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen);
+}
+
+/* Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+int
+app_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 rc;
+
+ if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.sign)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = app->fnc.sign (app, keyidstr, hashalgo,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ if (opt.verbose)
+ log_info ("operation sign result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+/* Create the signature using the INTERNAL AUTHENTICATE command and
+ return the allocated result in OUTDATA. If a PIN is required the
+ PINCB will be used to ask for the PIN; it should return the PIN in
+ an allocated buffer and put it into PIN. */
+int
+app_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;
+
+ if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.auth)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = app->fnc.auth (app, keyidstr,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ if (opt.verbose)
+ log_info ("operation auth result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+int
+app_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;
+
+ if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.decipher)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = app->fnc.decipher (app, keyidstr,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ if (opt.verbose)
+ log_info ("operation decipher result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Perform a SETATTR operation. */
+int
+app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc;
+
+ if (!app || !keynostr || !*keynostr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.genkey)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg);
+ if (opt.verbose)
+ log_info ("operation genkey result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Perform a GET CHALLENGE operation. This fucntion is special as it
+ directly accesses the card without any application specific
+ wrapper. */
+int
+app_get_challenge (APP app, size_t nbytes, unsigned char *buffer)
+{
+ if (!app || !nbytes || !buffer)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ return iso7816_get_challenge (app->slot, nbytes, buffer);
+}
+
+
+
+/* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */
+int
+app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc;
+
+ if (!app || !chvnostr || !*chvnostr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.change_pin)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode, pincb, pincb_arg);
+ if (opt.verbose)
+ log_info ("operation change_pin result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+
+
+
+
diff --git a/scd/card-common.h b/scd/card-common.h
new file mode 100644
index 000000000..31f0dfe8f
--- /dev/null
+++ b/scd/card-common.h
@@ -0,0 +1,73 @@
+/* card-common.h - Common declarations for all card types
+ * Copyright (C) 2001, 2002 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 CARD_COMMON_H
+#define CARD_COMMON_H
+
+/* Declaration of private data structure used by card-p15.c */
+struct p15private_s;
+
+
+struct card_ctx_s {
+ int reader; /* used reader */
+ struct sc_context *ctx;
+ struct sc_card *scard;
+ struct sc_pkcs15_card *p15card; /* only if there is a pkcs15 application */
+ struct p15private_s *p15priv; /* private data used by card-p15.c */
+
+ struct {
+ int initialized; /* the card has been initialied and the function
+ pointers may be used. However for
+ unsupported operations the particular
+ function pointer is set to NULL */
+
+ int (*enum_keypairs) (CARD card, int idx,
+ unsigned char *keygrip, char **keyid);
+ int (*enum_certs) (CARD card, int idx, char **certid, int *certtype);
+ int (*read_cert) (CARD card, const char *certidstr,
+ unsigned char **cert, size_t *ncert);
+ int (*sign) (CARD card,
+ 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 (*decipher) (CARD card, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen);
+ } fnc;
+
+};
+
+/*-- card.c --*/
+gpg_error_t map_sc_err (int rc);
+int card_help_get_keygrip (KsbaCert cert, unsigned char *array);
+
+/*-- card-15.c --*/
+void p15_release_private_data (CARD card);
+
+/* constructors */
+void card_p15_bind (CARD card);
+void card_dinsig_bind (CARD card);
+
+
+#endif /*CARD_COMMON_H*/
diff --git a/scd/card-p15.c b/scd/card-p15.c
new file mode 100644
index 000000000..3cf4ba519
--- /dev/null
+++ b/scd/card-p15.c
@@ -0,0 +1,502 @@
+/* card-p15.c - PKCS-15 based card access
+ * Copyright (C) 2002 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>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef HAVE_OPENSC
+#include <opensc/pkcs15.h>
+#include <ksba.h>
+
+#include "scdaemon.h"
+#include "card-common.h"
+
+
+struct p15private_s {
+ int n_prkey_rsa_objs;
+ struct sc_pkcs15_object *prkey_rsa_objs[32];
+ int n_cert_objs;
+ struct sc_pkcs15_object *cert_objs[32];
+};
+
+
+/* Allocate private data. */
+static int
+init_private_data (CARD card)
+{
+ struct p15private_s *priv;
+ int rc;
+
+ if (card->p15priv)
+ return 0; /* already done. */
+
+ priv = xtrycalloc (1, sizeof *priv);
+ if (!priv)
+ return out_of_core ();
+
+ /* OpenSC (0.7.0) is a bit strange in that the get_objects functions
+ tries to be a bit too clever and implicitly does an enumeration
+ which eventually leads to the fact that every call to this
+ fucntion returns one more macthing object. The old code in
+ p15_enum_keypairs assume that it would alwyas return the same
+ numer of objects and used this to figure out what the last object
+ enumerated is. We now do an enum_objects just once and keep it
+ in the private data. */
+ rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_PRKEY_RSA,
+ priv->prkey_rsa_objs,
+ DIM (priv->prkey_rsa_objs));
+ if (rc < 0)
+ {
+ log_error ("private keys enumeration failed: %s\n", sc_strerror (rc));
+ xfree (priv);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ priv->n_prkey_rsa_objs = rc;
+
+ /* Read all certificate objects. */
+ rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_CERT_X509,
+ priv->cert_objs,
+ DIM (priv->cert_objs));
+ if (rc < 0)
+ {
+ log_error ("private keys enumeration failed: %s\n", sc_strerror (rc));
+ xfree (priv);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ priv->n_cert_objs = rc;
+
+ card->p15priv = priv;
+ return 0;
+}
+
+
+/* Release private data used in this module. */
+void
+p15_release_private_data (CARD card)
+{
+ if (!card->p15priv)
+ return;
+ xfree (card->p15priv);
+ card->p15priv = NULL;
+}
+
+
+
+/* See card.c for interface description */
+static int
+p15_enum_keypairs (CARD card, int idx,
+ unsigned char *keygrip, char **keyid)
+{
+ int rc;
+ KsbaError krc;
+ struct p15private_s *priv;
+ struct sc_pkcs15_object *tmpobj;
+ int nobjs;
+ struct sc_pkcs15_prkey_info *pinfo;
+ struct sc_pkcs15_cert_info *certinfo;
+ struct sc_pkcs15_cert *certder;
+ KsbaCert cert;
+
+ rc = init_private_data (card);
+ if (rc)
+ return rc;
+ priv = card->p15priv;
+ nobjs = priv->n_prkey_rsa_objs;
+ rc = 0;
+ if (idx >= nobjs)
+ return -1;
+ pinfo = priv->prkey_rsa_objs[idx]->data;
+
+ /* now we need to read the certificate so that we can calculate the
+ keygrip */
+ rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &tmpobj);
+ if (rc)
+ {
+ log_info ("certificate for private key %d not found: %s\n",
+ idx, sc_strerror (rc));
+ /* note, that we return the ID anyway */
+ rc = gpg_error (GPG_ERR_MISSING_CERTIFICATE);
+ goto return_keyid;
+ }
+ certinfo = tmpobj->data;
+ rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
+ if (rc)
+ {
+ log_info ("failed to read certificate for private key %d: %s\n",
+ idx, sc_strerror (rc));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ cert = ksba_cert_new ();
+ if (!cert)
+ {
+ gpg_error_t tmperr = out_of_core ();
+ sc_pkcs15_free_certificate (certder);
+ return tmperr;
+ }
+ krc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len);
+ sc_pkcs15_free_certificate (certder);
+ if (krc)
+ {
+ log_error ("failed to parse the certificate for private key %d: %s\n",
+ idx, ksba_strerror (krc));
+ ksba_cert_release (cert);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ if (card_help_get_keygrip (cert, keygrip))
+ {
+ log_error ("failed to calculate the keygrip of private key %d\n", idx);
+ ksba_cert_release (cert);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ ksba_cert_release (cert);
+
+ rc = 0;
+ return_keyid:
+ if (keyid)
+ {
+ char *p;
+ int i;
+
+ *keyid = p = xtrymalloc (9+pinfo->id.len*2+1);
+ if (!*keyid)
+ return out_of_core ();
+ p = stpcpy (p, "P15-5015.");
+ for (i=0; i < pinfo->id.len; i++, p += 2)
+ sprintf (p, "%02X", pinfo->id.value[i]);
+ *p = 0;
+ }
+
+ return rc;
+}
+
+/* See card.c for interface description */
+static int
+p15_enum_certs (CARD card, int idx, char **certid, int *type)
+{
+ int rc;
+ struct p15private_s *priv;
+ struct sc_pkcs15_object *obj;
+ struct sc_pkcs15_cert_info *cinfo;
+ int nobjs;
+
+ rc = init_private_data (card);
+ if (rc)
+ return rc;
+ priv = card->p15priv;
+ nobjs = priv->n_cert_objs;
+ rc = 0;
+ if (idx >= nobjs)
+ return -1;
+ obj = priv->cert_objs[idx];
+ cinfo = obj->data;
+
+ if (certid)
+ {
+ char *p;
+ int i;
+
+ *certid = p = xtrymalloc (9+cinfo->id.len*2+1);
+ if (!*certid)
+ return out_of_core ();
+ p = stpcpy (p, "P15-5015.");
+ for (i=0; i < cinfo->id.len; i++, p += 2)
+ sprintf (p, "%02X", cinfo->id.value[i]);
+ *p = 0;
+ }
+ if (type)
+ {
+ if (!obj->df)
+ *type = 0; /* unknown */
+ else if (obj->df->type == SC_PKCS15_CDF)
+ *type = 100;
+ else if (obj->df->type == SC_PKCS15_CDF_TRUSTED)
+ *type = 101;
+ else if (obj->df->type == SC_PKCS15_CDF_USEFUL)
+ *type = 102;
+ else
+ *type = 0; /* error -> unknown */
+ }
+
+ return rc;
+}
+
+
+
+static int
+idstr_to_id (const char *idstr, struct sc_pkcs15_id *id)
+{
+ const char *s;
+ int n;
+
+ /* For now we only support the standard DF */
+ if (strncmp (idstr, "P15-5015.", 9) )
+ return gpg_error (GPG_ERR_INV_ID);
+ for (s=idstr+9, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (*s || (n&1))
+ return gpg_error (GPG_ERR_INV_ID); /*invalid or odd number of digits*/
+ n /= 2;
+ if (!n || n > SC_PKCS15_MAX_ID_SIZE)
+ return gpg_error (GPG_ERR_INV_ID); /* empty or too large */
+ for (s=idstr+9, n=0; *s; s += 2, n++)
+ id->value[n] = xtoi_2 (s);
+ id->len = n;
+ return 0;
+}
+
+
+/* See card.c for interface description */
+static int
+p15_read_cert (CARD card, const char *certidstr,
+ unsigned char **cert, size_t *ncert)
+{
+ struct sc_pkcs15_object *tmpobj;
+ struct sc_pkcs15_id certid;
+ struct sc_pkcs15_cert_info *certinfo;
+ struct sc_pkcs15_cert *certder;
+ int rc;
+
+ if (!card || !certidstr || !cert || !ncert)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!card->p15card)
+ return gpg_error (GPG_ERR_NO_PKCS15_APP);
+
+ rc = idstr_to_id (certidstr, &certid);
+ if (rc)
+ return rc;
+
+ rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &tmpobj);
+ if (rc)
+ {
+ log_info ("certificate '%s' not found: %s\n",
+ certidstr, sc_strerror (rc));
+ return -1;
+ }
+ certinfo = tmpobj->data;
+ rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
+ if (rc)
+ {
+ log_info ("failed to read certificate '%s': %s\n",
+ certidstr, sc_strerror (rc));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ *cert = xtrymalloc (certder->data_len);
+ if (!*cert)
+ {
+ gpg_error_t tmperr = out_of_core ();
+ sc_pkcs15_free_certificate (certder);
+ return tmperr;
+ }
+ memcpy (*cert, certder->data, certder->data_len);
+ *ncert = certder->data_len;
+ sc_pkcs15_free_certificate (certder);
+ return 0;
+}
+
+
+
+
+
+static int
+p15_prepare_key (CARD card, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg, struct sc_pkcs15_object **r_keyobj)
+{
+ struct sc_pkcs15_id keyid;
+ struct sc_pkcs15_pin_info *pin;
+ struct sc_pkcs15_object *keyobj, *pinobj;
+ char *pinvalue;
+ int rc;
+
+ rc = idstr_to_id (keyidstr, &keyid);
+ if (rc)
+ return rc;
+
+ rc = sc_pkcs15_find_prkey_by_id (card->p15card, &keyid, &keyobj);
+ if (rc < 0)
+ {
+ log_error ("private key not found: %s\n", sc_strerror(rc));
+ return gpg_error (GPG_ERR_NO_SECRET_KEY);
+ }
+
+ rc = sc_pkcs15_find_pin_by_auth_id (card->p15card,
+ &keyobj->auth_id, &pinobj);
+ if (rc)
+ {
+ log_error ("failed to find PIN by auth ID: %s\n", sc_strerror (rc));
+ return gpg_error (GPG_ERR_BAD_PIN_METHOD);
+ }
+ pin = pinobj->data;
+
+ /* Fixme: pack this into a verification loop */
+ /* Fixme: we might want to pass pin->min_length and
+ pin->stored_length */
+ rc = pincb (pincb_arg, pinobj->label, &pinvalue);
+ if (rc)
+ {
+ log_info ("PIN callback returned error: %s\n", gnupg_strerror (rc));
+ return rc;
+ }
+
+ rc = sc_pkcs15_verify_pin (card->p15card, pin,
+ pinvalue, strlen (pinvalue));
+ xfree (pinvalue);
+ if (rc)
+ {
+ log_info ("PIN verification failed: %s\n", sc_strerror (rc));
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ /* fixme: check wheter we need to release KEYOBJ in case of an error */
+ *r_keyobj = keyobj;
+ return 0;
+}
+
+
+/* See card.c for interface description */
+static int
+p15_sign (CARD card, 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 )
+{
+ unsigned int cryptflags;
+ struct sc_pkcs15_object *keyobj;
+ int rc;
+ unsigned char *outbuf = NULL;
+ size_t outbuflen;
+
+ if (hashalgo != GCRY_MD_SHA1)
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+
+ rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj);
+ if (rc)
+ return rc;
+
+ cryptflags = SC_ALGORITHM_RSA_PAD_PKCS1;
+
+ outbuflen = 1024;
+ outbuf = xtrymalloc (outbuflen);
+ if (!outbuf)
+ return out_of_core ();
+
+ rc = sc_pkcs15_compute_signature (card->p15card, keyobj,
+ cryptflags,
+ indata, indatalen,
+ outbuf, outbuflen );
+ if (rc < 0)
+ {
+ log_error ("failed to create signature: %s\n", sc_strerror (rc));
+ rc = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ *outdatalen = rc;
+ *outdata = outbuf;
+ outbuf = NULL;
+ rc = 0;
+ }
+
+ xfree (outbuf);
+ return rc;
+}
+
+
+/* See card.c for description */
+static int
+p15_decipher (CARD card, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ struct sc_pkcs15_object *keyobj;
+ int rc;
+ unsigned char *outbuf = NULL;
+ size_t outbuflen;
+
+ rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj);
+ if (rc)
+ return rc;
+
+ if (card && card->scard && card->scard->driver
+ && !strcasecmp (card->scard->driver->short_name, "tcos"))
+ {
+ /* very ugly hack to force the use of a local key. We need this
+ until we have fixed the initialization code for TCOS cards */
+ struct sc_pkcs15_prkey_info *prkey = keyobj->data;
+ if ( !(prkey->key_reference & 0x80))
+ {
+ prkey->key_reference |= 0x80;
+ log_debug ("using TCOS hack to force the use of local keys\n");
+ }
+ if (*keyidstr && keyidstr[strlen(keyidstr)-1] == '6')
+ {
+ prkey->key_reference |= 1;
+ log_debug ("warning: using even more TCOS hacks\n");
+ }
+ }
+
+ outbuflen = indatalen < 256? 256 : indatalen;
+ outbuf = xtrymalloc (outbuflen);
+ if (!outbuf)
+ return out_of_core ();
+
+ rc = sc_pkcs15_decipher (card->p15card, keyobj,
+ 0,
+ indata, indatalen,
+ outbuf, outbuflen);
+ if (rc < 0)
+ {
+ log_error ("failed to decipher the data: %s\n", sc_strerror (rc));
+ rc = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ *outdatalen = rc;
+ *outdata = outbuf;
+ outbuf = NULL;
+ rc = 0;
+ }
+
+ xfree (outbuf);
+ return rc;
+}
+
+
+
+/* Bind our operations to the card */
+void
+card_p15_bind (CARD card)
+{
+ card->fnc.enum_keypairs = p15_enum_keypairs;
+ card->fnc.enum_certs = p15_enum_certs;
+ card->fnc.read_cert = p15_read_cert;
+ card->fnc.sign = p15_sign;
+ card->fnc.decipher = p15_decipher;
+}
+#endif /*HAVE_OPENSC*/
diff --git a/scd/card.c b/scd/card.c
new file mode 100644
index 000000000..02b7bfdbf
--- /dev/null
+++ b/scd/card.c
@@ -0,0 +1,564 @@
+/* card.c - SCdaemon card functions
+ * Copyright (C) 2002 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>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef HAVE_OPENSC
+#include <opensc/pkcs15.h>
+#endif
+#include <ksba.h>
+
+#include "scdaemon.h"
+#include "card-common.h"
+
+/* Map the SC error codes to the GNUPG ones */
+gpg_error_t
+map_sc_err (int rc)
+{
+ gpg_err_code_t e;
+
+ switch (rc)
+ {
+ case 0: e = 0; break;
+#ifdef HAVE_OPENSC
+ case SC_ERROR_NOT_SUPPORTED: e = GPG_ERR_NOT_SUPPORTED; break;
+ case SC_ERROR_PKCS15_APP_NOT_FOUND: e = GPG_ERR_NO_PKCS15_APP; break;
+ case SC_ERROR_OUT_OF_MEMORY: e = GPG_ERR_ENOMEM; break;
+ case SC_ERROR_CARD_NOT_PRESENT: e = GPG_ERR_CARD_NOT_PRESENT; break;
+ case SC_ERROR_CARD_REMOVED: e = GPG_ERR_CARD_REMOVED; break;
+ case SC_ERROR_INVALID_CARD: e = GPG_ERR_INV_CARD; break;
+#endif
+ default: e = GPG_ERR_CARD; break;
+ }
+ return gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, e);
+}
+
+/* Get the keygrip from CERT, return 0 on success */
+int
+card_help_get_keygrip (KsbaCert cert, unsigned char *array)
+{
+ gcry_sexp_t s_pkey;
+ int rc;
+ KsbaSexp p;
+ size_t n;
+
+ p = ksba_cert_get_public_key (cert);
+ if (!p)
+ return -1; /* oops */
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ return -1; /* libksba did not return a proper S-expression */
+ rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
+ xfree (p);
+ if (rc)
+ return -1; /* can't parse that S-expression */
+ array = gcry_pk_get_keygrip (s_pkey, array);
+ gcry_sexp_release (s_pkey);
+ if (!array)
+ return -1; /* failed to calculate the keygrip */
+ return 0;
+}
+
+
+
+
+
+
+
+/* Create a new context for the card and figures out some basic
+ information of the card. Detects whgether a PKCS_15 application is
+ stored.
+
+ Common errors: GPG_ERR_CARD_NOT_PRESENT */
+int
+card_open (CARD *rcard)
+{
+#ifdef HAVE_OPENSC
+ CARD card;
+ int rc;
+
+ card = xtrycalloc (1, sizeof *card);
+ if (!card)
+ return out_of_core ();
+ card->reader = 0;
+
+ rc = sc_establish_context (&card->ctx, "scdaemon");
+ if (rc)
+ {
+ log_error ("failed to establish SC context: %s\n", sc_strerror (rc));
+ rc = map_sc_err (rc);
+ goto leave;
+ }
+ if (card->reader >= card->ctx->reader_count)
+ {
+ log_error ("no card reader available\n");
+ rc = gpg_error (GPG_ERR_CARD);
+ goto leave;
+ }
+ card->ctx->error_file = log_get_stream ();
+ card->ctx->debug = opt.debug_sc;
+ card->ctx->debug_file = log_get_stream ();
+
+ if (sc_detect_card_presence (card->ctx->reader[card->reader], 0) != 1)
+ {
+ rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+ goto leave;
+ }
+
+ rc = sc_connect_card (card->ctx->reader[card->reader], 0, &card->scard);
+ if (rc)
+ {
+ log_error ("failed to connect card in reader %d: %s\n",
+ card->reader, sc_strerror (rc));
+ rc = map_sc_err (rc);
+ goto leave;
+ }
+ if (opt.verbose)
+ log_info ("connected to card in reader %d using driver `%s'\n",
+ card->reader, card->scard->driver->name);
+
+ rc = sc_lock (card->scard);
+ if (rc)
+ {
+ log_error ("can't lock card in reader %d: %s\n",
+ card->reader, sc_strerror (rc));
+ rc = map_sc_err (rc);
+ goto leave;
+ }
+
+
+ leave:
+ if (rc)
+ card_close (card);
+ else
+ *rcard = card;
+
+ return rc;
+#else
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+#endif
+}
+
+
+/* Close a card and release all resources */
+void
+card_close (CARD card)
+{
+ if (card)
+ {
+#ifdef HAVE_OPENSC
+ if (card->p15card)
+ {
+ sc_pkcs15_unbind (card->p15card);
+ card->p15card = NULL;
+ }
+ if (card->p15priv)
+ p15_release_private_data (card);
+ if (card->scard)
+ {
+ sc_unlock (card->scard);
+ sc_disconnect_card (card->scard, 0);
+ card->scard = NULL;
+ }
+ if (card->ctx)
+ {
+ sc_release_context (card->ctx);
+ card->ctx = NULL;
+ }
+#endif
+ xfree (card);
+ }
+}
+
+/* Locate a simple 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. */
+#ifdef HAVE_OPENSC
+static const char *
+find_simple_tlv (const unsigned char *buffer, size_t length,
+ int tag, size_t *nbytes)
+{
+ const char *s = buffer;
+ size_t n = length;
+ size_t len;
+
+ for (;;)
+ {
+ buffer = s;
+ if (n < 2)
+ return NULL; /* buffer too short for tag and length. */
+ len = s[1];
+ s += 2; n -= 2;
+ if (len == 255)
+ {
+ if (n < 2)
+ return NULL; /* we expected 2 more bytes with the length. */
+ len = (s[0] << 8) | s[1];
+ s += 2; n -= 2;
+ }
+ if (*buffer == tag)
+ {
+ *nbytes = len;
+ return s;
+ }
+ if (len > n)
+ return NULL; /* buffer too short to skip to the next tag. */
+ s += len; n -= len;
+ }
+}
+#endif /*HAVE_OPENSC*/
+
+/* Find the ICC Serial Number within the provided BUFFER of LENGTH
+ (which should contain the GDO file) and return it as a hex encoded
+ string and allocated string in SERIAL. Return an error code when
+ the ICCSN was not found. */
+#ifdef HAVE_OPENSC
+static int
+find_iccsn (const unsigned char *buffer, size_t length, char **serial)
+{
+ size_t n;
+ const unsigned char *s;
+ char *p;
+
+ s = find_simple_tlv (buffer, length, 0x5A, &n);
+ if (!s)
+ return gpg_error (GPG_ERR_CARD);
+ length -= s - buffer;
+ if (n > length)
+ {
+ /* Oops, it does not fit into the buffer. This is an invalid
+ encoding (or the buffer is too short. However, I have some
+ test cards with such an invalid encoding and therefore I use
+ this ugly workaround to return something I can further
+ experiment with. */
+ if (n == 0x0D && length+1 == n)
+ {
+ log_debug ("enabling BMI testcard workaround\n");
+ n--;
+ }
+ else
+ return gpg_error (GPG_ERR_CARD); /* Bad encoding; does
+ not fit into buffer. */
+ }
+ if (!n)
+ return gpg_error (GPG_ERR_CARD); /* Well, that is too short. */
+
+ *serial = p = xtrymalloc (2*n+1);
+ if (!*serial)
+ return out_of_core ();
+ for (; n; n--, p += 2, s++)
+ sprintf (p, "%02X", *s);
+ *p = 0;
+ return 0;
+}
+#endif /*HAVE_OPENSC*/
+
+/* 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. The serial
+ is mandatory for a PKCS_15 application and an error will be
+ returned if this value is not availbale. For non-PKCS-15 cards a
+ serial number is constructed by other means. Caller must free
+ SERIAL unless the function returns an error. */
+int
+card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
+{
+#ifdef HAVE_OPENSC
+ int rc;
+ struct sc_path path;
+ struct sc_file *file;
+ unsigned char buf[256];
+ int buflen;
+#endif
+
+ if (!card || !serial || !stamp)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ *serial = NULL;
+ *stamp = 0; /* not available */
+
+#ifdef HAVE_OPENSC
+ if (!card->fnc.initialized)
+ {
+ card->fnc.initialized = 1;
+ /* The first use of this card tries to figure out the type of the card
+ and sets up the function pointers. */
+ rc = sc_pkcs15_bind (card->scard, &card->p15card);
+ if (rc)
+ {
+ if (rc != SC_ERROR_PKCS15_APP_NOT_FOUND)
+ log_error ("binding of existing PKCS-15 failed in reader %d: %s\n",
+ card->reader, sc_strerror (rc));
+ card->p15card = NULL;
+ rc = 0;
+ }
+ if (card->p15card)
+ card_p15_bind (card);
+ else
+ card_dinsig_bind (card);
+ card->fnc.initialized = 1;
+ }
+
+
+ /* We should lookup the iso 7812-1 and 8583-3 - argh ISO
+ practice is suppressing innovation - IETF rules! So we
+ always get the serialnumber from the 2F02 GDO file. */
+ /* FIXME: in case we can't parse the 2F02 EF and we have a P15 card,
+ we should get the serial number from the respective P15 file */
+ sc_format_path ("3F002F02", &path);
+ rc = sc_select_file (card->scard, &path, &file);
+ if (rc)
+ {
+ log_error ("sc_select_file failed: %s\n", sc_strerror (rc));
+ return gpg_error (GPG_ERR_CARD);
+ }
+ if (file->type != SC_FILE_TYPE_WORKING_EF
+ || file->ef_structure != SC_FILE_EF_TRANSPARENT)
+ {
+ log_error ("wrong type or structure of GDO file\n");
+ sc_file_free (file);
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ if (!file->size || file->size >= DIM(buf) )
+ { /* FIXME: Use a real parser */
+ log_error ("unsupported size of GDO file (%d)\n", file->size);
+ sc_file_free (file);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ buflen = file->size;
+
+ rc = sc_read_binary (card->scard, 0, buf, buflen, 0);
+ sc_file_free (file);
+ if (rc < 0)
+ {
+ log_error ("error reading GDO file: %s\n", sc_strerror (rc));
+ return gpg_error (GPG_ERR_CARD);
+ }
+ if (rc != buflen)
+ {
+ log_error ("short read on GDO file\n");
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ rc = find_iccsn (buf, buflen, serial);
+ if (gpg_err_code (rc) == GPG_ERR_CARD)
+ log_error ("invalid structure of GDO file\n");
+ if (!rc && card->p15card && !strcmp (*serial, "D27600000000000000000000"))
+ { /* This is a German card with a silly serial number. Try to get
+ the serial number from the EF(TokenInfo). We indicate such a
+ serial number by the using the prefix: "FF0100". */
+ const char *efser = card->p15card->serial_number;
+ char *p;
+
+ if (!efser)
+ efser = "";
+
+ xfree (*serial);
+ *serial = NULL;
+ p = xtrymalloc (strlen (efser) + 7);
+ if (!p)
+ rc = out_of_core ();
+ else
+ {
+ strcpy (p, "FF0100");
+ strcpy (p+6, efser);
+ *serial = p;
+ }
+ }
+ else if (!rc && **serial == 'F' && (*serial)[1] == 'F')
+ { /* The serial number starts with our special prefix. This
+ requires that we put our default prefix "FF0000" in front. */
+ char *p = xtrymalloc (strlen (*serial) + 7);
+ if (!p)
+ {
+ xfree (*serial);
+ *serial = NULL;
+ rc = out_of_core ();
+ }
+ else
+ {
+ strcpy (p, "FF0000");
+ strcpy (p+6, *serial);
+ xfree (*serial);
+ *serial = p;
+ }
+ }
+ return rc;
+#else
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+#endif
+}
+
+
+/* Enumerate all keypairs on the card and return the Keygrip as well
+ as the internal identification of the key. KEYGRIP must be a
+ caller provided buffer with a size of 20 bytes which will receive
+ the KEYGRIP of the keypair. If KEYID is not NULL, it returns the
+ ID field of the key in allocated memory; this is a string without
+ spaces. The function returns -1 when all keys have been
+ enumerated. Note that the error GPG_ERR_MISSING_CERTIFICATE may be
+ returned if there is just the private key but no public key (ie.e a
+ certificate) available. Applications might want to continue
+ enumerating after this error.*/
+int
+card_enum_keypairs (CARD card, int idx,
+ unsigned char *keygrip,
+ char **keyid)
+{
+ int rc;
+
+ if (keyid)
+ *keyid = NULL;
+
+ if (!card || !keygrip)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (idx < 0)
+ return gpg_error (GPG_ERR_INV_INDEX);
+ if (!card->fnc.initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!card->fnc.enum_keypairs)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = card->fnc.enum_keypairs (card, idx, keygrip, keyid);
+ if (opt.verbose)
+ log_info ("card operation enum_keypairs result: %s\n",
+ gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Enumerate all trusted certificates available on the card, return
+ their ID in CERT and the type in CERTTYPE. Types of certificates
+ are:
+ 0 := Unknown
+ 100 := Regular X.509 cert
+ 101 := Trusted X.509 cert
+ 102 := Useful X.509 cert
+ */
+int
+card_enum_certs (CARD card, int idx, char **certid, int *certtype)
+{
+ int rc;
+
+ if (certid)
+ *certid = NULL;
+
+ if (!card)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (idx < 0)
+ return gpg_error (GPG_ERR_INV_INDEX);
+ if (!card->fnc.initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!card->fnc.enum_certs)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = card->fnc.enum_certs (card, idx, certid, certtype);
+ if (opt.verbose)
+ log_info ("card operation enum_certs result: %s\n",
+ gpg_strerror (rc));
+ return rc;
+}
+
+
+
+/* Read the certificate identified by CERTIDSTR which is the
+ hexadecimal encoded ID of the certificate, prefixed with the string
+ "3F005015.". The certificate is return in DER encoded form in CERT
+ and NCERT. */
+int
+card_read_cert (CARD card, const char *certidstr,
+ unsigned char **cert, size_t *ncert)
+{
+ int rc;
+
+ if (!card || !certidstr || !cert || !ncert)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!card->fnc.initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!card->fnc.read_cert)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = card->fnc.read_cert (card, certidstr, cert, ncert);
+ if (opt.verbose)
+ log_info ("card operation read_cert result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+int
+card_sign (CARD card, 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 rc;
+
+ if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!card->fnc.initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!card->fnc.sign)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = card->fnc.sign (card, keyidstr, hashalgo,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ if (opt.verbose)
+ log_info ("card operation sign result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+int
+card_decipher (CARD card, 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;
+
+ if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!card->fnc.initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!card->fnc.decipher)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = card->fnc.decipher (card, keyidstr,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ if (opt.verbose)
+ log_info ("card operation decipher result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
diff --git a/scd/command.c b/scd/command.c
new file mode 100644
index 000000000..c53af84f9
--- /dev/null
+++ b/scd/command.c
@@ -0,0 +1,1034 @@
+/* command.c - SCdaemon command handler
+ * Copyright (C) 2001, 2002, 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>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <ksba.h>
+
+#include <assuan.h>
+
+#include "scdaemon.h"
+#include "app-common.h"
+
+/* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */
+#define MAXLEN_PIN 100
+
+#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
+
+/* Data used to associate an Assuan context with local server data */
+struct server_local_s {
+ ASSUAN_CONTEXT assuan_ctx;
+};
+
+
+/* Check whether the option NAME appears in LINE */
+static int
+has_option (const char *line, const char *name)
+{
+ const char *s;
+ int n = strlen (name);
+
+ s = strstr (line, name);
+ return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
+}
+
+
+
+
+/* Note, that this reset_notify is also used for cleanup purposes. */
+static void
+reset_notify (ASSUAN_CONTEXT ctx)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ if (ctrl->card_ctx)
+ {
+ card_close (ctrl->card_ctx);
+ ctrl->card_ctx = NULL;
+ xfree (ctrl->in_data.value);
+ ctrl->in_data.value = NULL;
+ }
+ if (ctrl->app_ctx)
+ {
+ /* FIXME: close the application. */
+ xfree (ctrl->app_ctx);
+ ctrl->app_ctx = NULL;
+ }
+}
+
+
+static int
+option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
+{
+ return 0;
+}
+
+
+/* If the card has not yet been opened, do it. Note that this
+ function returns an Assuan error, so don't map the error a second
+ time */
+static AssuanError
+open_card (CTRL ctrl)
+{
+ if (ctrl->app_ctx)
+ return 0; /* Already initialized for one specific application. */
+ if (ctrl->card_ctx)
+ return 0; /* Already initialized using a card context. */
+
+ ctrl->app_ctx = select_application ();
+ if (!ctrl->app_ctx)
+ { /* No application found - fall back to old mode. */
+ int rc = card_open (&ctrl->card_ctx);
+ if (rc)
+ return map_to_assuan_status (rc);
+ }
+ return 0;
+}
+
+
+/* Do the percent and plus/space unescaping in place and return tghe
+ length of the valid buffer. */
+static size_t
+percent_plus_unescape (unsigned char *string)
+{
+ unsigned char *p = string;
+ size_t n = 0;
+
+ while (*string)
+ {
+ if (*string == '%' && string[1] && string[2])
+ {
+ string++;
+ *p++ = xtoi_2 (string);
+ n++;
+ string+= 2;
+ }
+ else if (*string == '+')
+ {
+ *p++ = ' ';
+ n++;
+ string++;
+ }
+ else
+ {
+ *p++ = *string++;
+ n++;
+ }
+ }
+
+ return n;
+}
+
+
+
+/* SERIALNO
+
+ Return the serial number of the card using a status reponse. This
+ functon should be used to check for the presence of a card.
+
+ This function is special in that it can be used to reset the card.
+ Most other functions will return an error when a card change has
+ been detected and the use of this function is therefore required.
+
+ Background: We want to keep the client clear of handling card
+ changes between operations; i.e. the client can assume that all
+ operations are done on the same card unless he calls this function.
+ */
+static int
+cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc = 0;
+ char *serial_and_stamp;
+ char *serial;
+ time_t stamp;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (ctrl->app_ctx)
+ rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
+ else
+ rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
+ if (rc)
+ return map_to_assuan_status (rc);
+ rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
+ xfree (serial);
+ if (rc < 0)
+ return ASSUAN_Out_Of_Core;
+ rc = 0;
+ assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
+ free (serial_and_stamp);
+ return 0;
+}
+
+
+
+
+/* LEARN [--force]
+
+ Learn all useful information of the currently inserted card. When
+ used without the force options, the command might do an INQUIRE
+ like this:
+
+ INQUIRE KNOWNCARDP <hexstring_with_serialNumber> <timestamp>
+
+ The client should just send an "END" if the processing should go on
+ or a "CANCEL" to force the function to terminate with a Cancel
+ error message. The response of this command is a list of status
+ lines formatted as this:
+
+ S APPTYPE <apptype>
+
+ This returns the type of the application, currently the strings:
+
+ P15 = PKCS-15 structure used
+ DINSIG = DIN SIG
+ OPENPGP = OpenPGP card
+
+ are implemented. These strings are aliases for the AID
+
+ S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>
+
+ If there is no certificate yet stored on the card a single "X" is
+ returned as the keygrip. In addition to the keypair info, information
+ about all certificates stored on the card is also returned:
+
+ S CERTINFO <certtype> <hexstring_with_id>
+
+ Where CERTTYPE is a number indicating the type of certificate:
+ 0 := Unknown
+ 100 := Regular X.509 cert
+ 101 := Trusted X.509 cert
+ 102 := Useful X.509 cert
+
+ For certain cards, more information will be returned:
+
+ S KEY-FPR <no> <hexstring>
+
+ For OpenPGP cards this returns the stored fingerprints of the
+ keys. This can be used check whether a key is available on the
+ card. NO may be 1, 2 or 3.
+
+ S CA-FPR <no> <hexstring>
+
+ Similar to above, these are the fingerprints of keys assumed to be
+ ultimately trusted.
+
+ S DISP-NAME <name_of_card_holder>
+
+ The name of the card holder as stored on the card; percent
+ aescaping takes place, spaces are encoded as '+'
+
+ S PUBKEY-URL <url>
+
+ The URL to be used for locating the entire public key.
+
+*/
+static int
+cmd_learn (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc = 0;
+ int idx;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ /* Unless the force option is used we try a shortcut by identifying
+ the card using a serial number and inquiring the client with
+ that. The client may choose to cancel the operation if he already
+ knows about this card */
+ {
+ char *serial_and_stamp;
+ char *serial;
+ time_t stamp;
+
+ if (ctrl->app_ctx)
+ rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
+ else
+ rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
+ if (rc)
+ return map_to_assuan_status (rc);
+ rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
+ xfree (serial);
+ if (rc < 0)
+ return ASSUAN_Out_Of_Core;
+ rc = 0;
+ assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
+
+ if (!has_option (line, "--force"))
+ {
+ char *command;
+
+ rc = asprintf (&command, "KNOWNCARDP %s", serial_and_stamp);
+ if (rc < 0)
+ {
+ free (serial_and_stamp);
+ return ASSUAN_Out_Of_Core;
+ }
+ rc = 0;
+ rc = assuan_inquire (ctx, command, NULL, NULL, 0);
+ free (command); /* (must use standard free here) */
+ if (rc)
+ {
+ if (rc != ASSUAN_Canceled)
+ log_error ("inquire KNOWNCARDP failed: %s\n",
+ assuan_strerror (rc));
+ free (serial_and_stamp);
+ return rc;
+ }
+ /* not canceled, so we have to proceeed */
+ }
+ free (serial_and_stamp);
+ }
+
+ /* Return information about the certificates. */
+ if (ctrl->app_ctx)
+ rc = -1; /* This information is not yet available for applications. */
+ for (idx=0; !rc; idx++)
+ {
+ char *certid;
+ int certtype;
+
+ rc = card_enum_certs (ctrl->card_ctx, idx, &certid, &certtype);
+ if (!rc)
+ {
+ char *buf;
+
+ buf = xtrymalloc (40 + 1 + strlen (certid) + 1);
+ if (!buf)
+ rc = out_of_core ();
+ else
+ {
+ sprintf (buf, "%d %s", certtype, certid);
+ assuan_write_status (ctx, "CERTINFO", buf);
+ xfree (buf);
+ }
+ }
+ xfree (certid);
+ }
+ if (rc == -1)
+ rc = 0;
+
+
+ /* Return information about the keys. */
+ if (ctrl->app_ctx)
+ rc = -1; /* This information is not yet available for applications. */
+ for (idx=0; !rc; idx++)
+ {
+ unsigned char keygrip[20];
+ char *keyid;
+ int no_cert = 0;
+
+ rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid);
+ if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT && keyid)
+ {
+ /* this does happen with an incomplete personalized
+ card; i.e. during the time we have stored the key on the
+ card but not stored the certificate; probably becuase it
+ has not yet been received back from the CA. Note that we
+ must release KEYID in this case. */
+ rc = 0;
+ no_cert = 1;
+ }
+ if (!rc)
+ {
+ char *buf, *p;
+
+ buf = p = xtrymalloc (40 + 1 + strlen (keyid) + 1);
+ if (!buf)
+ rc = out_of_core ();
+ else
+ {
+ int i;
+
+ if (no_cert)
+ *p++ = 'X';
+ else
+ {
+ for (i=0; i < 20; i++, p += 2)
+ sprintf (p, "%02X", keygrip[i]);
+ }
+ *p++ = ' ';
+ strcpy (p, keyid);
+ assuan_write_status (ctx, "KEYPAIRINFO", buf);
+ xfree (buf);
+ }
+ }
+ xfree (keyid);
+ }
+ if (rc == -1)
+ rc = 0;
+
+ if (!rc && ctrl->app_ctx)
+ rc = app_write_learn_status (ctrl->app_ctx, ctrl);
+
+
+ return map_to_assuan_status (rc);
+}
+
+
+
+/* READCERT <hexified_certid>
+
+ */
+static int
+cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *cert;
+ size_t ncert;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
+ if (rc)
+ {
+ log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
+ }
+ if (!rc)
+ {
+ rc = assuan_send_data (ctx, cert, ncert);
+ xfree (cert);
+ if (rc)
+ return rc;
+ }
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* READKEY <hexified_certid>
+
+ Return the public key for the given cert or key ID as an standard
+ S-Expression. */
+static int
+cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *cert = NULL;
+ size_t ncert, n;
+ KsbaCert kc = NULL;
+ KsbaSexp p;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
+ if (rc)
+ {
+ log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ kc = ksba_cert_new ();
+ if (!kc)
+ {
+ rc = out_of_core ();
+ xfree (cert);
+ goto leave;
+ }
+ rc = ksba_cert_init_from_mem (kc, cert, ncert);
+ if (rc)
+ {
+ log_error ("failed to parse the certificate: %s\n", ksba_strerror (rc));
+ rc = map_ksba_err (rc);
+ goto leave;
+ }
+
+ p = ksba_cert_get_public_key (kc);
+ if (!p)
+ {
+ rc = gpg_error (GPG_ERR_NO_PUBKEY);
+ goto leave;
+ }
+
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ rc = assuan_send_data (ctx, p, n);
+ rc = map_assuan_err (rc);
+ xfree (p);
+
+
+ leave:
+ ksba_cert_release (kc);
+ xfree (cert);
+ return map_to_assuan_status (rc);
+}
+
+
+
+
+/* SETDATA <hexstring>
+
+ The client should use this command to tell us the data he want to
+ sign. */
+static int
+cmd_setdata (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int n;
+ char *p;
+ unsigned char *buf;
+
+ /* parse the hexstring */
+ for (p=line,n=0; hexdigitp (p); p++, n++)
+ ;
+ if (*p)
+ return set_error (Parameter_Error, "invalid hexstring");
+ if (!n)
+ return set_error (Parameter_Error, "no data given");
+ if ((n&1))
+ return set_error (Parameter_Error, "odd number of digits");
+ n /= 2;
+ buf = xtrymalloc (n);
+ if (!buf)
+ return ASSUAN_Out_Of_Core;
+
+ ctrl->in_data.value = buf;
+ ctrl->in_data.valuelen = n;
+ for (p=line, n=0; n < ctrl->in_data.valuelen; p += 2, n++)
+ buf[n] = xtoi_2 (p);
+ return 0;
+}
+
+
+
+static int
+pin_cb (void *opaque, const char *info, char **retstr)
+{
+ ASSUAN_CONTEXT ctx = opaque;
+ char *command;
+ int rc;
+ char *value;
+ size_t valuelen;
+
+ *retstr = NULL;
+ log_debug ("asking for PIN '%s'\n", info);
+
+ rc = asprintf (&command, "NEEDPIN %s", info);
+ if (rc < 0)
+ return out_of_core ();
+
+ /* FIXME: Write an inquire function which returns the result in
+ secure memory */
+ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+ free (command);
+ if (rc)
+ return map_assuan_err (rc);
+
+ if (!valuelen || value[valuelen-1])
+ {
+ /* We require that the returned value is an UTF-8 string */
+ xfree (value);
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+ *retstr = value;
+ return 0;
+}
+
+
+/* PKSIGN <hexified_id>
+
+ */
+static int
+cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *outdata;
+ size_t outdatalen;
+ char *keyidstr;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ /* We have to use a copy of the key ID because the function may use
+ the pin_cb which in turn uses the assuan line buffer and thus
+ overwriting the original line with the keyid */
+ keyidstr = strdup (line);
+ if (!keyidstr)
+ return ASSUAN_Out_Of_Core;
+
+ if (ctrl->app_ctx)
+ rc = app_sign (ctrl->app_ctx,
+ keyidstr, GCRY_MD_SHA1,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ else
+ rc = card_sign (ctrl->card_ctx,
+ keyidstr, GCRY_MD_SHA1,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ free (keyidstr);
+ if (rc)
+ {
+ log_error ("card_sign failed: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ rc = assuan_send_data (ctx, outdata, outdatalen);
+ xfree (outdata);
+ if (rc)
+ return rc; /* that is already an assuan error code */
+ }
+
+ return map_to_assuan_status (rc);
+}
+
+/* PKAUTH <hexified_id>
+
+ */
+static int
+cmd_pkauth (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *outdata;
+ size_t outdatalen;
+ char *keyidstr;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ /* We have to use a copy of the key ID because the function may use
+ the pin_cb which in turn uses the assuan line buffer and thus
+ overwriting the original line with the keyid */
+ keyidstr = strdup (line);
+ if (!keyidstr)
+ return ASSUAN_Out_Of_Core;
+
+ rc = app_auth (ctrl->app_ctx,
+ keyidstr,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ free (keyidstr);
+ if (rc)
+ {
+ log_error ("app_auth_sign failed: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ rc = assuan_send_data (ctx, outdata, outdatalen);
+ xfree (outdata);
+ if (rc)
+ return rc; /* that is already an assuan error code */
+ }
+
+ return map_to_assuan_status (rc);
+}
+
+/* PKDECRYPT <hexified_id>
+
+ */
+static int
+cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *outdata;
+ size_t outdatalen;
+ char *keyidstr;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ keyidstr = strdup (line);
+ if (!keyidstr)
+ return ASSUAN_Out_Of_Core;
+ if (ctrl->app_ctx)
+ rc = app_decipher (ctrl->app_ctx,
+ keyidstr,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ else
+ rc = card_decipher (ctrl->card_ctx,
+ keyidstr,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ free (keyidstr);
+ if (rc)
+ {
+ log_error ("card_create_signature failed: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ rc = assuan_send_data (ctx, outdata, outdatalen);
+ xfree (outdata);
+ if (rc)
+ return rc; /* that is already an assuan error code */
+ }
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* SETATTR <name> <value>
+
+ This command is used to store data on a a smartcard. The allowed
+ names and values are depend on the currently selected smartcard
+ application. NAME and VALUE must be percent and '+' escaped.
+
+ However, the curent implementation assumes that Name is not escaped;
+ this works as long as noone uses arbitrary escaping.
+
+ A PIN will be requested for most NAMEs. See the corresponding
+ setattr function of the actually used application (app-*.c) for
+ details. */
+static int
+cmd_setattr (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyword;
+ int keywordlen;
+ size_t nbytes;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ keyword = line;
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ if (*line)
+ *line++ = 0;
+ while (spacep (line))
+ line++;
+ nbytes = percent_plus_unescape (line);
+
+ rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes);
+
+ return map_to_assuan_status (rc);
+}
+
+/* GENKEY [--force] <no>
+
+ Generate a key on-card identified by NO, which is application
+ specific. Return values are application specific. For OpenPGP
+ cards 2 status lines are returned:
+
+ S KEY-FPR <hexstring>
+ S KEY-CREATED-AT <seconds_since_epoch>
+ S KEY-DATA [p|n] <hexdata>
+
+
+ --force is required to overwriet an already existing key. The
+ KEY-CREATED-AT is required for further processing because it is
+ part of the hashed key material for the fingerprint.
+
+ The public part of the key can also later be retrieved using the
+ READKEY command.
+
+ */
+static int
+cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyno;
+ int force = has_option (line, "--force");
+
+ /* Skip over options. */
+ while ( *line == '-' && line[1] == '-' )
+ {
+ while (!spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ if (!*line)
+ return set_error (Parameter_Error, "no key number given");
+ keyno = line;
+ while (!spacep (line))
+ line++;
+ *line = 0;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* RANDOM <nbytes>
+
+ Get NBYTES of random from the card and send them back as data.
+*/
+static int
+cmd_random (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ size_t nbytes;
+ unsigned char *buffer;
+
+ if (!*line)
+ return set_error (Parameter_Error, "number of requested bytes missing");
+ nbytes = strtoul (line, NULL, 0);
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ buffer = xtrymalloc (nbytes);
+ if (!buffer)
+ return ASSUAN_Out_Of_Core;
+
+ rc = app_get_challenge (ctrl->app_ctx, nbytes, buffer);
+ if (!rc)
+ {
+ rc = assuan_send_data (ctx, buffer, nbytes);
+ xfree (buffer);
+ return rc; /* that is already an assuan error code */
+ }
+ xfree (buffer);
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* PASSWD [--reset] <chvno>
+
+ Change the PIN or reset thye retry counter of the card holder
+ verfication vector CHVNO. */
+static int
+cmd_passwd (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *chvnostr;
+ int reset_mode = has_option (line, "--reset");
+
+ /* Skip over options. */
+ while (*line == '-' && line[1] == '-')
+ {
+ while (!spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ if (!*line)
+ return set_error (Parameter_Error, "no CHV number given");
+ chvnostr = line;
+ while (!spacep (line))
+ line++;
+ *line = 0;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = app_change_pin (ctrl->app_ctx, ctrl, chvnostr, reset_mode, pin_cb, ctx
+);
+ if (rc)
+ log_error ("command passwd failed: %s\n", gpg_strerror (rc));
+ return map_to_assuan_status (rc);
+}
+
+
+
+
+/* Tell the assuan library about our commands */
+static int
+register_commands (ASSUAN_CONTEXT ctx)
+{
+ static struct {
+ const char *name;
+ int (*handler)(ASSUAN_CONTEXT, char *line);
+ } table[] = {
+ { "SERIALNO", cmd_serialno },
+ { "LEARN", cmd_learn },
+ { "READCERT", cmd_readcert },
+ { "READKEY", cmd_readkey },
+ { "SETDATA", cmd_setdata },
+ { "PKSIGN", cmd_pksign },
+ { "PKAUTH", cmd_pkauth },
+ { "PKDECRYPT", cmd_pkdecrypt },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "SETATTR", cmd_setattr },
+ { "GENKEY", cmd_genkey },
+ { "RANDOM", cmd_random },
+ { "PASSWD", cmd_passwd },
+ { NULL }
+ };
+ int i, rc;
+
+ for (i=0; table[i].name; i++)
+ {
+ rc = assuan_register_command (ctx, table[i].name, table[i].handler);
+ if (rc)
+ return rc;
+ }
+ assuan_set_hello_line (ctx, "GNU Privacy Guard's Smartcard server ready");
+
+ assuan_register_reset_notify (ctx, reset_notify);
+ assuan_register_option_handler (ctx, option_handler);
+ return 0;
+}
+
+
+/* Startup the server. If LISTEN_FD is given as -1, this is simple
+ piper server, otherwise it is a regular server */
+void
+scd_command_handler (int listen_fd)
+{
+ int rc;
+ ASSUAN_CONTEXT ctx;
+ struct server_control_s ctrl;
+
+ memset (&ctrl, 0, sizeof ctrl);
+ scd_init_default_ctrl (&ctrl);
+
+ if (listen_fd == -1)
+ {
+ int filedes[2];
+
+ filedes[0] = 0;
+ filedes[1] = 1;
+ rc = assuan_init_pipe_server (&ctx, filedes);
+ }
+ else
+ {
+ rc = assuan_init_socket_server (&ctx, listen_fd);
+ }
+ if (rc)
+ {
+ log_error ("failed to initialize the server: %s\n",
+ assuan_strerror(rc));
+ scd_exit (2);
+ }
+ rc = register_commands (ctx);
+ if (rc)
+ {
+ log_error ("failed to register commands with Assuan: %s\n",
+ assuan_strerror(rc));
+ scd_exit (2);
+ }
+ assuan_set_pointer (ctx, &ctrl);
+ ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
+ ctrl.server_local->assuan_ctx = ctx;
+
+ if (DBG_ASSUAN)
+ assuan_set_log_stream (ctx, log_get_stream ());
+
+ for (;;)
+ {
+ rc = assuan_accept (ctx);
+ if (rc == -1)
+ {
+ break;
+ }
+ else if (rc)
+ {
+ log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
+ break;
+ }
+
+ rc = assuan_process (ctx);
+ if (rc)
+ {
+ log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
+ continue;
+ }
+ }
+ reset_notify (ctx); /* used for cleanup */
+
+ assuan_deinit_server (ctx);
+}
+
+
+/* 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;
+ ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
+
+ va_start (arg_ptr, keyword);
+
+ p = buf;
+ n = 0;
+ 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;
+ assuan_write_status (ctx, keyword, buf);
+
+ va_end (arg_ptr);
+}
+
diff --git a/scd/iso7816.c b/scd/iso7816.c
new file mode 100644
index 000000000..8903d8a5c
--- /dev/null
+++ b/scd/iso7816.c
@@ -0,0 +1,371 @@
+/* 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>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include "scdaemon.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
+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 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 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;
+}
diff --git a/scd/iso7816.h b/scd/iso7816.h
new file mode 100644
index 000000000..d7e77a101
--- /dev/null
+++ b/scd/iso7816.h
@@ -0,0 +1,56 @@
+/* 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
+
+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*/
diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c
new file mode 100644
index 000000000..9caf39a8a
--- /dev/null
+++ b/scd/sc-copykeys.c
@@ -0,0 +1,731 @@
+/* sc-copykeys.c - A tool to store keys on a smartcard.
+ * 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>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define JNLIB_NEED_LOG_LOGV
+#include "scdaemon.h"
+#include <gcrypt.h>
+
+#include "../common/ttyio.h"
+#include "../common/simple-pwquery.h"
+#include "apdu.h" /* for open_reader */
+#include "atr.h"
+#include "app-common.h"
+
+#define _(a) (a)
+
+
+enum cmd_and_opt_values
+{ oVerbose = 'v',
+ oReaderPort = 500,
+ oDebug,
+ oDebugAll,
+
+aTest };
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ { 301, NULL, 0, "@Options:\n " },
+
+ { oVerbose, "verbose", 0, "verbose" },
+ { oReaderPort, "reader-port", 1, "|N|connect to reader at port N"},
+ { oDebug, "debug" ,4|16, "set debugging flags"},
+ { oDebugAll, "debug-all" ,0, "enable full debugging"},
+ {0}
+};
+
+
+static void copykeys (APP app, const char *fname);
+
+
+static const char *
+my_strusage (int level)
+{
+ const char *p;
+ switch (level)
+ {
+ case 11: p = "sc-copykeys (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+ break;
+ case 1:
+ case 40: p = _("Usage: sc-copykeys [options] (-h for help)\n");
+ break;
+ case 41: p = _("Syntax: sc-copykeys [options] "
+ "file-with-key\n"
+ "Copy keys to a smartcards\n");
+ break;
+
+ default: p = NULL;
+ }
+ return p;
+}
+
+/* Used by gcry for logging */
+static void
+my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
+{
+ /* translate the log levels */
+ switch (level)
+ {
+ case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
+ case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
+ case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
+ case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
+ case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
+ case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
+ case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
+ default: level = JNLIB_LOG_ERROR; break;
+ }
+ log_logv (level, fmt, arg_ptr);
+}
+
+
+int
+main (int argc, char **argv )
+{
+ ARGPARSE_ARGS pargs;
+ int slot, rc;
+ int reader_port = 32768; /* First USB reader. */
+ struct app_ctx_s appbuf;
+
+ memset (&appbuf, 0, sizeof appbuf);
+
+ set_strusage (my_strusage);
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+ log_set_prefix ("sc-copykeys", 1);
+
+ /* check that the libraries are suitable. Do it here because
+ the option parsing may need services of the library */
+ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
+ {
+ log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
+ NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
+ }
+
+ gcry_set_log_handler (my_gcry_logger, NULL);
+ gcry_control (GCRYCTL_DISABLE_SECMEM, 0); /* FIXME - we want to use it */
+ /* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
+
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1; /* do not remove the args */
+ while (arg_parse (&pargs, opts) )
+ {
+ switch (pargs.r_opt)
+ {
+ case oVerbose: opt.verbose++; break;
+ case oDebug: opt.debug |= pargs.r.ret_ulong; break;
+ case oDebugAll: opt.debug = ~0; break;
+ default : pargs.err = 2; break;
+ }
+ }
+ if (log_get_errorcount(0))
+ exit(2);
+
+ if (argc != 1)
+ usage (1);
+
+ slot = apdu_open_reader (reader_port);
+ if (slot == -1)
+ exit (1);
+
+ /* FIXME: Use select_application. */
+ appbuf.slot = slot;
+ rc = app_select_openpgp (&appbuf, &appbuf.serialno, &appbuf.serialnolen);
+ if (rc)
+ {
+ log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ exit (1);
+ }
+ appbuf.initialized = 1;
+ log_info ("openpgp application selected\n");
+
+ copykeys (&appbuf, *argv);
+
+
+ return 0;
+}
+
+
+
+void
+send_status_info (CTRL ctrl, const char *keyword, ...)
+{
+ /* DUMMY */
+}
+
+
+
+static char *
+read_file (const char *fname, size_t *r_length)
+{
+ FILE *fp;
+ struct stat st;
+ char *buf;
+ size_t buflen;
+
+ fp = fname? fopen (fname, "rb") : stdin;
+ if (!fp)
+ {
+ log_error ("can't open `%s': %s\n",
+ fname? fname: "[stdin]", strerror (errno));
+ return NULL;
+ }
+
+ if (fstat (fileno(fp), &st))
+ {
+ log_error ("can't stat `%s': %s\n",
+ fname? fname: "[stdin]", strerror (errno));
+ if (fname)
+ fclose (fp);
+ return NULL;
+ }
+
+ buflen = st.st_size;
+ buf = xmalloc (buflen+1);
+ if (fread (buf, buflen, 1, fp) != 1)
+ {
+ log_error ("error reading `%s': %s\n",
+ fname? fname: "[stdin]", strerror (errno));
+ if (fname)
+ fclose (fp);
+ xfree (buf);
+ return NULL;
+ }
+ if (fname)
+ fclose (fp);
+
+ *r_length = buflen;
+ return buf;
+}
+
+
+static gcry_sexp_t
+read_key (const char *fname)
+{
+ char *buf;
+ size_t buflen;
+ gcry_sexp_t private;
+ int rc;
+
+ buf = read_file (fname, &buflen);
+ if (!buf)
+ return NULL;
+
+ rc = gcry_sexp_new (&private, buf, buflen, 1);
+ if (rc)
+ {
+ log_error ("gcry_sexp_new failed: %s\n", gpg_strerror (rc));
+ return NULL;
+ }
+ xfree (buf);
+
+ return private;
+}
+
+
+
+static gcry_mpi_t *
+sexp_to_kparms (gcry_sexp_t sexp, unsigned long *created)
+{
+ gcry_sexp_t list, l2;
+ const char *name;
+ const char *s;
+ size_t n;
+ int i, idx;
+ const char *elems;
+ gcry_mpi_t *array;
+
+ *created = 0;
+ list = gcry_sexp_find_token (sexp, "private-key", 0 );
+ if(!list)
+ return NULL;
+
+ /* quick hack to get the creation time. */
+ l2 = gcry_sexp_find_token (list, "created", 0);
+ if (l2 && (name = gcry_sexp_nth_data (l2, 1, &n)))
+ {
+ char *tmp = xmalloc (n+1);
+ memcpy (tmp, name, n);
+ tmp[n] = 0;
+ *created = strtoul (tmp, NULL, 10);
+ xfree (tmp);
+ }
+ gcry_sexp_release (l2);
+ l2 = gcry_sexp_cadr (list);
+ gcry_sexp_release (list);
+ list = l2;
+ name = gcry_sexp_nth_data (list, 0, &n);
+ if(!name || n != 3 || memcmp (name, "rsa", 3))
+ {
+ gcry_sexp_release (list);
+ return NULL;
+ }
+
+ /* Parameter names used with RSA. */
+ elems = "nedpqu";
+ array = xcalloc (strlen(elems) + 1, sizeof *array);
+ for (idx=0, s=elems; *s; s++, idx++ )
+ {
+ l2 = gcry_sexp_find_token (list, s, 1);
+ if (!l2)
+ {
+ for (i=0; i<idx; i++)
+ gcry_mpi_release (array[i]);
+ xfree (array);
+ gcry_sexp_release (list);
+ return NULL; /* required parameter not found */
+ }
+ array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (l2);
+ if (!array[idx])
+ {
+ for (i=0; i<idx; i++)
+ gcry_mpi_release (array[i]);
+ xfree (array);
+ gcry_sexp_release (list);
+ return NULL; /* required parameter is invalid */
+ }
+ }
+
+ gcry_sexp_release (list);
+ return array;
+}
+
+
+/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
+static int
+fpr_is_zero (const char *fpr)
+{
+ int i;
+
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ return (i == 20);
+}
+
+
+static void
+show_sha1_fpr (const unsigned char *fpr)
+{
+ int i;
+
+ if (fpr)
+ {
+ for (i=0; i < 20 ; i+=2, fpr += 2 )
+ {
+ if (i == 10 )
+ tty_printf (" ");
+ tty_printf (" %02X%02X", *fpr, fpr[1]);
+ }
+ }
+ else
+ tty_printf (" [none]");
+ tty_printf ("\n");
+}
+
+/* Query the card, show a list of already stored keys and ask the user
+ where to store the key. Returns the key number or 0 for cancel
+ operation. */
+static int
+query_card (APP app)
+{
+ int keyno = 0;
+ char *serialno, *disp_name, *pubkey_url;
+ unsigned char *fpr1, *fpr2, *fpr3;
+
+
+ if (app_openpgp_cardinfo (app,
+ &serialno,
+ &disp_name,
+ &pubkey_url,
+ &fpr1, &fpr2, &fpr3))
+ return 0;
+
+
+ for (;;)
+ {
+ char *answer;
+
+ tty_printf ("\n");
+
+ tty_printf ("Serial number ....: %s\n",
+ serialno? serialno : "[none]");
+ tty_printf ("Name of cardholder: %s\n",
+ disp_name && *disp_name? disp_name : "[not set]");
+ tty_printf ("URL of public key : %s\n",
+ pubkey_url && *pubkey_url? pubkey_url : "[not set]");
+ tty_printf ("Signature key ....:");
+ show_sha1_fpr (fpr1);
+ tty_printf ("Encryption key....:");
+ show_sha1_fpr (fpr2);
+ tty_printf ("Authentication key:");
+ show_sha1_fpr (fpr3);
+
+ tty_printf ("\n"
+ "1 - store as signature key and reset usage counter\n"
+ "2 - store as encryption key\n"
+ "3 - store as authentication key\n"
+ "Q - quit\n"
+ "\n");
+
+ answer = tty_get("Your selection? ");
+ tty_kill_prompt();
+ if (strlen (answer) != 1)
+ ;
+ else if ( *answer == '1' )
+ {
+ if ( (fpr1 && !fpr_is_zero (fpr1)) )
+ {
+ tty_printf ("\n");
+ log_error ("WARNING: signature key does already exists!\n");
+ tty_printf ("\n");
+ if ( tty_get_answer_is_yes ("Replace existing key? ") )
+ {
+ keyno = 1;
+ break;
+ }
+ }
+ else
+ {
+ keyno = 1;
+ break;
+ }
+ }
+ else if ( *answer == '2' )
+ {
+ if ( (fpr2 && !fpr_is_zero (fpr2)) )
+ {
+ tty_printf ("\n");
+ log_error ("WARNING: encryption key does already exists!\n");
+ tty_printf ("\n");
+ if ( tty_get_answer_is_yes ("Replace existing key? ") )
+ {
+ keyno = 2;
+ break;
+ }
+ }
+ else
+ {
+ keyno = 2;
+ break;
+ }
+ }
+ else if ( *answer == '3' )
+ {
+ if ( (fpr3 && !fpr_is_zero (fpr3)) )
+ {
+ tty_printf ("\n");
+ log_error ("WARNING: authentication key does already exists!\n");
+ tty_printf ("\n");
+ if ( tty_get_answer_is_yes ("Replace existing key? ") )
+ {
+ keyno = 3;
+ break;
+ }
+ }
+ else
+ {
+ keyno = 3;
+ break;
+ }
+ }
+ else if ( *answer == 'q' || *answer == 'Q')
+ {
+ keyno = 0;
+ break;
+ }
+ }
+
+ xfree (serialno);
+ xfree (disp_name);
+ xfree (pubkey_url);
+ xfree (fpr1);
+ xfree (fpr2);
+ xfree (fpr3);
+
+ return keyno;
+}
+
+
+/* Callback function to ask for a PIN. */
+static int
+pincb (void *arg, const char *prompt, char **pinvalue)
+{
+ char *pin = xstrdup ("12345678");
+
+/* pin = simple_pwquery (NULL, NULL, prompt, */
+/* "We need the admin's PIN to store the key on the card", */
+/* NULL); */
+/* if (!pin) */
+/* return gpg_error (GPG_ERR_CANCELED); */
+
+
+
+ *pinvalue = pin;
+ return 0;
+}
+
+
+/* This function expects a file (or NULL for stdin) with the secret
+ and public key parameters. This file should consist of an
+ S-expression as used by gpg-agent. Only the unprotected format is
+ supported. Example:
+
+ (private-key
+ (rsa
+ (n #00e0ce9..[some bytes not shown]..51#)
+ (e #010001#)
+ (d #046129F..[some bytes not shown]..81#)
+ (p #00e861b..[some bytes not shown]..f1#)
+ (q #00f7a7c..[some bytes not shown]..61#)
+ (u #304559a..[some bytes not shown]..9b#))
+ (uri http://foo.bar x-foo:whatever_you_want))
+
+*/
+static void
+copykeys (APP app, const char *fname)
+{
+ int rc;
+ gcry_sexp_t private;
+ gcry_mpi_t *mpis, rsa_n, rsa_e, rsa_p, rsa_q;
+ unsigned int nbits;
+ size_t n;
+ unsigned char *template, *tp;
+ unsigned char m[128], e[4];
+ size_t mlen, elen;
+ unsigned long creation_date;
+ time_t created_at;
+ int keyno;
+
+ if (!strcmp (fname, "-"))
+ fname = NULL;
+
+ private = read_key (fname);
+ if (!private)
+ exit (1);
+
+ mpis = sexp_to_kparms (private, &creation_date);
+ if (!creation_date)
+ {
+ log_info ("no creation date found - assuming current date\n");
+ created_at = time (NULL);
+ }
+ else
+ created_at = creation_date;
+ gcry_sexp_release (private);
+ if (!mpis)
+ {
+ log_error ("invalid structure of key file or not RSA\n");
+ exit (1);
+ }
+ /* MPIS is now an array with the key parameters as defined by OpenPGP. */
+ rsa_n = mpis[0];
+ rsa_e = mpis[1];
+ gcry_mpi_release (mpis[2]);
+ rsa_p = mpis[3];
+ rsa_q = mpis[4];
+ gcry_mpi_release (mpis[5]);
+ xfree (mpis);
+
+ nbits = gcry_mpi_get_nbits (rsa_e);
+ if (nbits < 2 || nbits > 32)
+ {
+ log_error ("public exponent too large (more than 32 bits)\n");
+ goto failure;
+ }
+ nbits = gcry_mpi_get_nbits (rsa_p);
+ if (nbits != 512)
+ {
+ log_error ("length of first RSA prime is not 512\n");
+ goto failure;
+ }
+ nbits = gcry_mpi_get_nbits (rsa_q);
+ if (nbits != 512)
+ {
+ log_error ("length of second RSA prime is not 512\n");
+ goto failure;
+ }
+
+ nbits = gcry_mpi_get_nbits (rsa_n);
+ if (nbits != 1024)
+ {
+ log_error ("length of RSA modulus is not 1024\n");
+ goto failure;
+ }
+
+ keyno = query_card (app);
+ if (!keyno)
+ goto failure;
+
+ /* Build the private key template as described in section 4.3.3.6 of
+ the specs.
+ 0xC0 <length> public exponent
+ 0xC1 <length> prime p
+ 0xC2 <length> prime q */
+ template = tp = xmalloc (1+2 + 1+1+4 + 1+1+64 + 1+1+64);
+ *tp++ = 0xC0;
+ *tp++ = 4;
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 4, &n, rsa_e);
+ if (rc)
+ {
+ log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
+ goto failure;
+ }
+ assert (n <= 4);
+ memcpy (e, tp, n);
+ elen = n;
+ if (n != 4)
+ {
+ memmove (tp+4-n, tp, 4-n);
+ memset (tp, 0, 4-n);
+ }
+ tp += 4;
+
+ *tp++ = 0xC1;
+ *tp++ = 64;
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_p);
+ if (rc)
+ {
+ log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
+ goto failure;
+ }
+ assert (n == 64);
+ tp += 64;
+
+ *tp++ = 0xC2;
+ *tp++ = 64;
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_q);
+ if (rc)
+ {
+ log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
+ goto failure;
+ }
+ assert (n == 64);
+ tp += 64;
+ assert (tp - template == 138);
+
+ /* (we need the modulus to calculate the fingerprint) */
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG, m, 128, &n, rsa_n);
+ if (rc)
+ {
+ log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
+ goto failure;
+ }
+ assert (n == 128);
+ mlen = 128;
+
+
+ rc = app_openpgp_storekey (app, keyno,
+ template, tp - template,
+ created_at,
+ m, mlen,
+ e, elen,
+ pincb, NULL);
+
+ if (rc)
+ {
+ log_error ("error storing key: %s\n", gpg_strerror (rc));
+ goto failure;
+ }
+ log_info ("key successfully stored\n");
+ {
+ unsigned char *mm, *ee;
+ size_t mmlen, eelen;
+ int i;
+
+ rc = app_openpgp_readkey (app, keyno, &mm, &mmlen, &ee, &eelen);
+ if (rc)
+ {
+ log_error ("error reading key back: %s\n", gpg_strerror (rc));
+ goto failure;
+ }
+
+ /* Strip leading zeroes. */
+ for (i=0; i < mmlen && !mm[i]; i++)
+ ;
+ mmlen -= i;
+ memmove (mm, mm+i, mmlen);
+ for (i=0; i < eelen && !ee[i]; i++)
+ ;
+ eelen -= i;
+ memmove (ee, ee+i, eelen);
+
+ if (eelen != elen || mmlen != mlen)
+ {
+ log_error ("key parameter length mismatch (n=%u/%u, e=%u/%u)\n",
+ (unsigned int)mlen, (unsigned int)mmlen,
+ (unsigned int)elen, (unsigned int)eelen);
+ xfree (mm);
+ xfree (ee);
+ goto failure;
+ }
+
+ if (memcmp (m, mm, mlen))
+ {
+ log_error ("key parameter n mismatch\n");
+ log_printhex ("original n: ", m, mlen);
+ log_printhex (" copied n: ", mm, mlen);
+ xfree (mm);
+ xfree (ee);
+ goto failure;
+ }
+ if (memcmp (e, ee, elen))
+ {
+ log_error ("key parameter e mismatch\n");
+ log_printhex ("original e: ", e, elen);
+ log_printhex (" copied e: ", ee, elen);
+ xfree (mm);
+ xfree (ee);
+ goto failure;
+ }
+ xfree (mm);
+ xfree (ee);
+ }
+
+
+ gcry_mpi_release (rsa_e);
+ gcry_mpi_release (rsa_p);
+ gcry_mpi_release (rsa_q);
+ gcry_mpi_release (rsa_n);
+ return;
+
+ failure:
+ gcry_mpi_release (rsa_e);
+ gcry_mpi_release (rsa_p);
+ gcry_mpi_release (rsa_q);
+ gcry_mpi_release (rsa_n);
+ exit (1);
+}
+
+
diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c
new file mode 100644
index 000000000..e8f0eb83c
--- /dev/null
+++ b/scd/sc-investigate.c
@@ -0,0 +1,209 @@
+/* sc-investigate.c - A tool to look around on smartcards.
+ * 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>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define JNLIB_NEED_LOG_LOGV
+#include "scdaemon.h"
+#include <gcrypt.h>
+
+#include "apdu.h" /* for open_reader */
+#include "atr.h"
+#include "app-common.h"
+
+#define _(a) (a)
+
+
+enum cmd_and_opt_values
+{ oVerbose = 'v',
+ oReaderPort = 500,
+ oDebug,
+ oDebugAll,
+
+ oGenRandom,
+
+aTest };
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ { 301, NULL, 0, "@Options:\n " },
+
+ { oVerbose, "verbose", 0, "verbose" },
+ { oReaderPort, "reader-port", 1, "|N|connect to reader at port N"},
+ { oDebug, "debug" ,4|16, "set debugging flags"},
+ { oDebugAll, "debug-all" ,0, "enable full debugging"},
+ { oGenRandom, "gen-random", 4, "|N|generate N bytes of random"},
+ {0}
+};
+
+static const char *
+my_strusage (int level)
+{
+ const char *p;
+ switch (level)
+ {
+ case 11: p = "sc-investigate (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+ break;
+ case 1:
+ case 40: p = _("Usage: sc-investigate [options] (-h for help)\n");
+ break;
+ case 41: p = _("Syntax: sc-investigate [options] [args]]\n"
+ "Have a look at smartcards\n");
+ break;
+
+ default: p = NULL;
+ }
+ return p;
+}
+
+/* Used by gcry for logging */
+static void
+my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
+{
+ /* translate the log levels */
+ switch (level)
+ {
+ case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
+ case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
+ case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
+ case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
+ case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
+ case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
+ case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
+ default: level = JNLIB_LOG_ERROR; break;
+ }
+ log_logv (level, fmt, arg_ptr);
+}
+
+
+int
+main (int argc, char **argv )
+{
+ ARGPARSE_ARGS pargs;
+ int slot, rc;
+ int reader_port = 32768; /* First USB reader. */
+ struct app_ctx_s appbuf;
+ unsigned long gen_random = 0;
+
+ memset (&appbuf, 0, sizeof appbuf);
+
+ set_strusage (my_strusage);
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+ log_set_prefix ("sc-investigate", 1);
+
+ /* check that the libraries are suitable. Do it here because
+ the option parsing may need services of the library */
+ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
+ {
+ log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
+ NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
+ }
+
+ gcry_set_log_handler (my_gcry_logger, NULL);
+ /* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
+
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1; /* do not remove the args */
+ while (arg_parse (&pargs, opts) )
+ {
+ switch (pargs.r_opt)
+ {
+ case oVerbose: opt.verbose++; break;
+ case oDebug: opt.debug |= pargs.r.ret_ulong; break;
+ case oDebugAll: opt.debug = ~0; break;
+ case oGenRandom: gen_random = pargs.r.ret_ulong; break;
+ default : pargs.err = 2; break;
+ }
+ }
+ if (log_get_errorcount(0))
+ exit(2);
+
+ if (opt.verbose < 2)
+ opt.verbose = 2; /* hack to let select_openpgp print some info. */
+
+ if (argc)
+ usage (1);
+
+ slot = apdu_open_reader (reader_port);
+ if (slot == -1)
+ exit (1);
+
+ if (!gen_random)
+ {
+ rc = atr_dump (slot, stdout);
+ if (rc)
+ log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
+ }
+
+ appbuf.slot = slot;
+ rc = app_select_openpgp (&appbuf, NULL, NULL);
+ if (rc)
+ log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ else
+ {
+ appbuf.initialized = 1;
+ log_info ("openpgp application selected\n");
+
+ if (gen_random)
+ {
+ size_t nbytes;
+ unsigned char *buffer;
+
+ buffer = xmalloc (4096);
+ do
+ {
+ nbytes = gen_random > 4096? 4096 : gen_random;
+ rc = app_get_challenge (&appbuf, nbytes, buffer);
+ if (rc)
+ log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
+ else
+ {
+ if (fwrite (buffer, nbytes, 1, stdout) != 1)
+ log_error ("writing to stdout failed: %s\n",
+ strerror (errno));
+ gen_random -= nbytes;
+ }
+ }
+ while (gen_random && !log_get_errorcount (0));
+ xfree (buffer);
+ }
+ }
+
+ return log_get_errorcount (0)? 2:0;
+}
+
+
+
+void
+send_status_info (CTRL ctrl, const char *keyword, ...)
+{
+ /* DUMMY */
+}
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
new file mode 100644
index 000000000..8e0ef37c9
--- /dev/null
+++ b/scd/scdaemon.c
@@ -0,0 +1,638 @@
+/* scdaemon.c - The GnuPG Smartcard Daemon
+ * Copyright (C) 2001, 2002 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>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <signal.h>
+
+#define JNLIB_NEED_LOG_LOGV
+#include "scdaemon.h"
+#include <ksba.h>
+#include <gcrypt.h>
+
+#include <assuan.h> /* malloc hooks */
+
+#include "i18n.h"
+#include "sysutils.h"
+
+
+
+enum cmd_and_opt_values
+{ aNull = 0,
+ oCsh = 'c',
+ oQuiet = 'q',
+ oSh = 's',
+ oVerbose = 'v',
+
+ oNoVerbose = 500,
+ oOptions,
+ oDebug,
+ oDebugAll,
+ oDebugWait,
+ oDebugSC,
+ oNoGreeting,
+ oNoOptions,
+ oHomedir,
+ oNoDetach,
+ oNoGrab,
+ oLogFile,
+ oServer,
+ oDaemon,
+ oBatch,
+ oReaderPort,
+
+aTest };
+
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ { 301, NULL, 0, N_("@Options:\n ") },
+
+ { oServer, "server", 0, N_("run in server mode (foreground)") },
+ { oDaemon, "daemon", 0, N_("run in daemon mode (background)") },
+ { oVerbose, "verbose", 0, N_("verbose") },
+ { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
+ { oSh, "sh", 0, N_("sh-style command output") },
+ { oCsh, "csh", 0, N_("csh-style command output") },
+ { oOptions, "options" , 2, N_("read options from file")},
+ { oDebug, "debug" ,4|16, N_("set debugging flags")},
+ { oDebugAll, "debug-all" ,0, N_("enable full debugging")},
+ { oDebugWait,"debug-wait",1, "@"},
+ { oDebugSC, "debug-sc", 1, N_("|N|set OpenSC debug level to N")},
+ { oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
+ { oLogFile, "log-file" ,2, N_("use a log file for the server")},
+ { oReaderPort, "reader-port", 1, N_("|N|connect to reader at port N")},
+
+ {0}
+};
+
+
+static volatile int caught_fatal_sig = 0;
+
+/* It is possible that we are currently running under setuid permissions */
+static int maybe_setuid = 1;
+
+/* Name of the communication socket */
+static char socket_name[128];
+
+static const char *
+my_strusage (int level)
+{
+ const char *p;
+ switch (level)
+ {
+ case 11: p = "scdaemon (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+ break;
+ case 1:
+ case 40: p = _("Usage: scdaemon [options] (-h for help)");
+ break;
+ case 41: p = _("Syntax: scdaemon [options] [command [args]]\n"
+ "Smartcard daemon for GnuPG\n");
+ break;
+
+ default: p = NULL;
+ }
+ return p;
+}
+
+
+
+static void
+i18n_init (void)
+{
+#ifdef USE_SIMPLE_GETTEXT
+ set_gettext_file( PACKAGE );
+#else
+#ifdef ENABLE_NLS
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+#endif
+#endif
+}
+
+
+
+/* Used by gcry for logging */
+static void
+my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
+{
+ /* translate the log levels */
+ switch (level)
+ {
+ case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
+ case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
+ case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
+ case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
+ case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
+ case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
+ case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
+ default: level = JNLIB_LOG_ERROR; break;
+ }
+ log_logv (level, fmt, arg_ptr);
+}
+
+
+static void
+cleanup (void)
+{
+ if (*socket_name)
+ {
+ char *p;
+
+ remove (socket_name);
+ p = strrchr (socket_name, '/');
+ if (p)
+ {
+ *p = 0;
+ rmdir (socket_name);
+ *p = '/';
+ }
+ *socket_name = 0;
+ }
+}
+
+
+static RETSIGTYPE
+cleanup_sh (int sig)
+{
+ if (caught_fatal_sig)
+ raise (sig);
+ caught_fatal_sig = 1;
+
+ /* gcry_control( GCRYCTL_TERM_SECMEM );*/
+ cleanup ();
+
+#ifndef HAVE_DOSISH_SYSTEM
+ { /* reset action to default action and raise signal again */
+ struct sigaction nact;
+ nact.sa_handler = SIG_DFL;
+ sigemptyset( &nact.sa_mask );
+ nact.sa_flags = 0;
+ sigaction( sig, &nact, NULL);
+ }
+#endif
+ raise( sig );
+}
+
+int
+main (int argc, char **argv )
+{
+ ARGPARSE_ARGS pargs;
+ int orig_argc;
+ int may_coredump;
+ char **orig_argv;
+ FILE *configfp = NULL;
+ char *configname = NULL;
+ const char *shell;
+ unsigned configlineno;
+ int parse_debug = 0;
+ int default_config =1;
+ int greeting = 0;
+ int nogreeting = 0;
+ int pipe_server = 0;
+ int is_daemon = 0;
+ int nodetach = 0;
+ int csh_style = 0;
+ char *logfile = NULL;
+ int debug_wait = 0;
+ int reader_port = 32768; /* First USB reader. */
+
+ set_strusage (my_strusage);
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+ /* Please note that we may running SUID(ROOT), so be very CAREFUL
+ when adding any stuff between here and the call to INIT_SECMEM()
+ somewhere after the option parsing */
+ log_set_prefix ("scdaemon", 1|4);
+ i18n_init ();
+
+ /* check that the libraries are suitable. Do it here because
+ the option parsing may need services of the library */
+ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
+ {
+ log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
+ NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
+ }
+
+ ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+ assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+ gcry_set_log_handler (my_gcry_logger, NULL);
+ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+
+ may_coredump = disable_core_dumps ();
+
+ shell = getenv ("SHELL");
+ if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
+ csh_style = 1;
+
+ /* FIXME: Using this homedir option does only make sense when not
+ running as a system service. We might want to check for this by
+ looking at the uid or ebtter use an explict option for this */
+ opt.homedir = getenv("GNUPGHOME");
+ if (!opt.homedir || !*opt.homedir)
+ opt.homedir = GNUPG_DEFAULT_HOMEDIR;
+
+ /* check whether we have a config file on the commandline */
+ orig_argc = argc;
+ orig_argv = argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
+ while (arg_parse( &pargs, opts))
+ {
+ if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
+ parse_debug++;
+ else if (pargs.r_opt == oOptions)
+ { /* yes there is one, so we do not try the default one, but
+ read the option file when it is encountered at the
+ commandline */
+ default_config = 0;
+ }
+ else if (pargs.r_opt == oNoOptions)
+ default_config = 0; /* --no-options */
+ else if (pargs.r_opt == oHomedir)
+ opt.homedir = pargs.r.ret_str;
+ }
+
+ /* initialize the secure memory. */
+ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+ maybe_setuid = 0;
+
+ /*
+ Now we are working under our real uid
+ */
+
+
+ if (default_config)
+ configname = make_filename (opt.homedir, "scdaemon.conf", NULL );
+
+ argc = orig_argc;
+ argv = orig_argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1; /* do not remove the args */
+ next_pass:
+ if (configname)
+ {
+ configlineno = 0;
+ configfp = fopen (configname, "r");
+ if (!configfp)
+ {
+ if (default_config)
+ {
+ if( parse_debug )
+ log_info (_("NOTE: no default option file `%s'\n"),
+ configname );
+ }
+ else
+ {
+ log_error (_("option file `%s': %s\n"),
+ configname, strerror(errno) );
+ exit(2);
+ }
+ xfree (configname);
+ configname = NULL;
+ }
+ if (parse_debug && configname )
+ log_info (_("reading options from `%s'\n"), configname );
+ default_config = 0;
+ }
+
+ while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
+ {
+ switch (pargs.r_opt)
+ {
+ case oQuiet: opt.quiet = 1; break;
+ case oVerbose: opt.verbose++; break;
+ case oBatch: opt.batch=1; break;
+
+ case oDebug: opt.debug |= pargs.r.ret_ulong; break;
+ case oDebugAll: opt.debug = ~0; break;
+ case oDebugWait: debug_wait = pargs.r.ret_int; break;
+ case oDebugSC: opt.debug_sc = pargs.r.ret_int; break;
+
+ case oOptions:
+ /* config files may not be nested (silently ignore them) */
+ if (!configfp)
+ {
+ xfree(configname);
+ configname = xstrdup(pargs.r.ret_str);
+ goto next_pass;
+ }
+ break;
+ case oNoGreeting: nogreeting = 1; break;
+ case oNoVerbose: opt.verbose = 0; break;
+ case oNoOptions: break; /* no-options */
+ case oHomedir: opt.homedir = pargs.r.ret_str; break;
+ case oNoDetach: nodetach = 1; break;
+ case oLogFile: logfile = pargs.r.ret_str; break;
+ case oCsh: csh_style = 1; break;
+ case oSh: csh_style = 0; break;
+ case oServer: pipe_server = 1; break;
+ case oDaemon: is_daemon = 1; break;
+
+ case oReaderPort: reader_port = pargs.r.ret_int; break;
+
+ default : pargs.err = configfp? 1:2; break;
+ }
+ }
+ if (configfp)
+ {
+ fclose( configfp );
+ configfp = NULL;
+ xfree(configname);
+ configname = NULL;
+ goto next_pass;
+ }
+ xfree (configname);
+ configname = NULL;
+ if (log_get_errorcount(0))
+ exit(2);
+ if (nogreeting )
+ greeting = 0;
+
+ if (greeting)
+ {
+ fprintf (stderr, "%s %s; %s\n",
+ strusage(11), strusage(13), strusage(14) );
+ fprintf (stderr, "%s\n", strusage(15) );
+ }
+#ifdef IS_DEVELOPMENT_VERSION
+ log_info ("NOTE: this is a development version!\n");
+#endif
+
+
+ if (atexit (cleanup))
+ {
+ log_error ("atexit failed\n");
+ cleanup ();
+ exit (1);
+ }
+
+
+ if (debug_wait && pipe_server)
+ {
+ log_debug ("waiting for debugger - my pid is %u .....\n",
+ (unsigned int)getpid());
+ sleep (debug_wait);
+ log_debug ("... okay\n");
+ }
+
+ /* now start with logging to a file if this is desired */
+ if (logfile)
+ {
+ log_set_file (logfile);
+ log_set_prefix (NULL, 1|2|4);
+ }
+
+
+ if (pipe_server)
+ { /* this is the simple pipe based server */
+ scd_command_handler (-1);
+ }
+ else if (!is_daemon)
+ {
+ log_info (_("please use the option `--daemon'"
+ " to run the program in the background\n"));
+ }
+ else
+ { /* regular server mode */
+ int fd;
+ pid_t pid;
+ int i;
+ int len;
+ struct sockaddr_un serv_addr;
+ char *p;
+
+ /* fixme: if there is already a running gpg-agent we should
+ share the same directory - and vice versa */
+ *socket_name = 0;
+ snprintf (socket_name, DIM(socket_name)-1,
+ "/tmp/gpg-XXXXXX/S.scdaemon");
+ socket_name[DIM(socket_name)-1] = 0;
+ p = strrchr (socket_name, '/');
+ if (!p)
+ BUG ();
+ *p = 0;;
+ if (!mkdtemp(socket_name))
+ {
+ log_error ("can't create directory `%s': %s\n",
+ socket_name, strerror(errno) );
+ exit (1);
+ }
+ *p = '/';
+
+ if (strchr (socket_name, ':') )
+ {
+ log_error ("colons are not allowed in the socket name\n");
+ exit (1);
+ }
+ if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path )
+ {
+ log_error ("name of socket to long\n");
+ exit (1);
+ }
+
+
+ fd = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1)
+ {
+ log_error ("can't create socket: %s\n", strerror(errno) );
+ exit (1);
+ }
+
+ memset (&serv_addr, 0, sizeof serv_addr);
+ serv_addr.sun_family = AF_UNIX;
+ strcpy (serv_addr.sun_path, socket_name);
+ len = (offsetof (struct sockaddr_un, sun_path)
+ + strlen(serv_addr.sun_path) + 1);
+
+ if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1)
+ {
+ log_error ("error binding socket to `%s': %s\n",
+ serv_addr.sun_path, strerror (errno) );
+ close (fd);
+ exit (1);
+ }
+
+ if (listen (fd, 5 ) == -1)
+ {
+ log_error ("listen() failed: %s\n", strerror (errno));
+ close (fd);
+ exit (1);
+ }
+
+ if (opt.verbose)
+ log_info ("listening on socket `%s'\n", socket_name );
+
+
+ fflush (NULL);
+ pid = fork ();
+ if (pid == (pid_t)-1)
+ {
+ log_fatal ("fork failed: %s\n", strerror (errno) );
+ exit (1);
+ }
+ else if (pid)
+ { /* we are the parent */
+ char *infostr;
+
+ close (fd);
+
+ /* create the info string: <name>:<pid>:<protocol_version> */
+ if (asprintf (&infostr, "SCDAEMON_INFO=%s:%lu:1",
+ socket_name, (ulong)pid ) < 0)
+ {
+ log_error ("out of core\n");
+ kill (pid, SIGTERM);
+ exit (1);
+ }
+ *socket_name = 0; /* don't let cleanup() remove the socket -
+ the child should do this from now on */
+ if (argc)
+ { /* run the program given on the commandline */
+ if (putenv (infostr))
+ {
+ log_error ("failed to set environment: %s\n",
+ strerror (errno) );
+ kill (pid, SIGTERM );
+ exit (1);
+ }
+ execvp (argv[0], argv);
+ log_error ("failed to run the command: %s\n", strerror (errno));
+ kill (pid, SIGTERM);
+ exit (1);
+ }
+ else
+ {
+ /* print the environment string, so that the caller can use
+ shell's eval to set it */
+ if (csh_style)
+ {
+ *strchr (infostr, '=') = ' ';
+ printf ( "setenv %s\n", infostr);
+ }
+ else
+ {
+ printf ( "%s; export SCDAEMON_INFO;\n", infostr);
+ }
+ free (infostr);
+ exit (0);
+ }
+ /* NOTREACHED */
+ } /* end parent */
+
+ /* this is the child */
+
+ /* detach from tty and put process into a new session */
+ if (!nodetach )
+ { /* close stdin, stdout and stderr unless it is the log stream */
+ for (i=0; i <= 2; i++)
+ {
+ if ( log_get_fd () != i)
+ close (i);
+ }
+ if (setsid() == -1)
+ {
+ log_error ("setsid() failed: %s\n", strerror(errno) );
+ cleanup ();
+ exit (1);
+ }
+ }
+
+ /* setup signals */
+ {
+ struct sigaction oact, nact;
+
+ nact.sa_handler = cleanup_sh;
+ sigemptyset (&nact.sa_mask);
+ nact.sa_flags = 0;
+
+ sigaction (SIGHUP, NULL, &oact);
+ if (oact.sa_handler != SIG_IGN)
+ sigaction (SIGHUP, &nact, NULL);
+ sigaction( SIGTERM, NULL, &oact );
+ if (oact.sa_handler != SIG_IGN)
+ sigaction (SIGTERM, &nact, NULL);
+ nact.sa_handler = SIG_IGN;
+ sigaction (SIGPIPE, &nact, NULL);
+ sigaction (SIGINT, &nact, NULL);
+ }
+
+ if (chdir("/"))
+ {
+ log_error ("chdir to / failed: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ scd_command_handler (fd);
+
+ close (fd);
+ }
+
+ return 0;
+}
+
+void
+scd_exit (int rc)
+{
+ #if 0
+#warning no update_random_seed_file
+ update_random_seed_file();
+ #endif
+#if 0
+ /* at this time a bit annoying */
+ if (opt.debug & DBG_MEMSTAT_VALUE)
+ {
+ gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
+ gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
+ }
+ if (opt.debug)
+ gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
+#endif
+ gcry_control (GCRYCTL_TERM_SECMEM );
+ rc = rc? rc : log_get_errorcount(0)? 2 : 0;
+ exit (rc);
+}
+
+
+void
+scd_init_default_ctrl (CTRL ctrl)
+{
+
+}
+
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
new file mode 100644
index 000000000..b21e19f8c
--- /dev/null
+++ b/scd/scdaemon.h
@@ -0,0 +1,127 @@
+/* scdaemon.h - Global definitions for the SCdaemon
+ * Copyright (C) 2001, 2002, 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 SCDAEMON_H
+#define SCDAEMON_H
+
+#ifdef GPG_ERR_SOURCE_DEFAULT
+#error GPG_ERR_SOURCE_DEFAULT already defined
+#endif
+#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_SCD
+#include <gpg-error.h>
+#include <errno.h>
+
+#include <time.h>
+#include <gcrypt.h>
+#include "../common/util.h"
+#include "../common/errors.h"
+
+/* Convenience funcion to be used instead of returning the old
+ GNUPG_Out_Of_Core. */
+static __inline__ gpg_error_t
+out_of_core (void)
+{
+ return gpg_error (gpg_err_code_from_errno (errno));
+}
+
+
+#define MAX_DIGEST_LEN 24
+
+/* A large struct name "opt" to keep global flags */
+struct {
+ unsigned int debug; /* debug flags (DBG_foo_VALUE) */
+ int debug_sc; /* OpenSC debug level */
+ int verbose; /* verbosity level */
+ int quiet; /* be as quiet as possible */
+ int dry_run; /* don't change any persistent data */
+ int batch; /* batch mode */
+ const char *homedir; /* configuration directory name */
+} opt;
+
+
+#define DBG_COMMAND_VALUE 1 /* debug commands i/o */
+#define DBG_MPI_VALUE 2 /* debug mpi details */
+#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
+#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
+#define DBG_CACHE_VALUE 64 /* debug the caching */
+#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
+#define DBG_HASHING_VALUE 512 /* debug hashing operations */
+#define DBG_ASSUAN_VALUE 1024
+#define DBG_CARD_IO_VALUE 2048
+
+#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE)
+#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
+#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
+#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
+#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
+#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE)
+#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
+
+struct server_local_s;
+struct card_ctx_s;
+struct app_ctx_s;
+
+struct server_control_s {
+ struct server_local_s *server_local;
+ struct card_ctx_s *card_ctx;
+ struct app_ctx_s *app_ctx;
+ struct {
+ unsigned char *value;
+ int valuelen;
+ } in_data; /* helper to store the value we are going to sign */
+
+};
+
+typedef struct server_control_s *CTRL;
+typedef struct card_ctx_s *CARD;
+typedef struct app_ctx_s *APP;
+
+/*-- scdaemon.c --*/
+void scd_exit (int rc);
+void scd_init_default_ctrl (CTRL ctrl);
+
+/*-- command.c --*/
+void scd_command_handler (int);
+void send_status_info (CTRL ctrl, const char *keyword, ...);
+
+/*-- card.c --*/
+int card_open (CARD *rcard);
+void card_close (CARD card);
+int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp);
+int card_enum_keypairs (CARD card, int idx,
+ unsigned char *keygrip,
+ char **keyid);
+int card_enum_certs (CARD card, int idx, char **certid, int *certtype);
+int card_read_cert (CARD card, const char *certidstr,
+ unsigned char **cert, size_t *ncert);
+int card_sign (CARD card,
+ 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 card_decipher (CARD card, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen);
+
+
+#endif /*SCDAEMON_H*/