aboutsummaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog11
-rw-r--r--scd/Makefile.am2
-rw-r--r--scd/app-common.h3
-rw-r--r--scd/app-geldkarte.c337
-rw-r--r--scd/app.c21
5 files changed, 371 insertions, 3 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index b42c569f1..755019693 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,14 @@
+2009-01-27 Werner Koch <[email protected]>
+
+ * app.c (app_munge_serialno): Add case for no serialno.
+ (app_get_serial_and_stamp): Ditto.
+
+2009-01-26 Werner Koch <[email protected]>
+
+ * app-geldkarte.c: New.
+ * Makefile.am (card_apps): Add new file.
+ * app.c (select_application): Test for geldkarte.
+
2009-01-12 Werner Koch <[email protected]>
* command.c (send_client_notifications) [HAVE_W32_SYSTEM]: Fix
diff --git a/scd/Makefile.am b/scd/Makefile.am
index e9da5b492..86dbff897 100644
--- a/scd/Makefile.am
+++ b/scd/Makefile.am
@@ -30,7 +30,7 @@ AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
$(KSBA_CFLAGS) $(LIBASSUAN_PTH_CFLAGS) $(PTH_CFLAGS)
-card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c
+card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c app-geldkarte.c
scdaemon_SOURCES = \
scdaemon.c scdaemon.h \
diff --git a/scd/app-common.h b/scd/app-common.h
index fe98bf832..5a45fa05b 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -207,6 +207,9 @@ gpg_error_t app_select_dinsig (app_t app);
/*-- app-p15.c --*/
gpg_error_t app_select_p15 (app_t app);
+/*-- app-geldkarte.c --*/
+gpg_error_t app_select_geldkarte (app_t app);
+
#endif
diff --git a/scd/app-geldkarte.c b/scd/app-geldkarte.c
new file mode 100644
index 000000000..3db42bf95
--- /dev/null
+++ b/scd/app-geldkarte.c
@@ -0,0 +1,337 @@
+/* app-geldkarte.c - The German Geldkarte application
+ * Copyright (C) 2004 g10 Code GmbH
+ * Copyright (C) 2009 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/* This is a read-only application to quickly dump information of a
+ German Geldkarte (debit card for small amounts).
+
+ Because this application does no use an AID it is best to test for
+ it after the test for other applications.
+*/
+
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "scdaemon.h"
+
+#include "i18n.h"
+#include "iso7816.h"
+#include "app-common.h"
+#include "tlv.h"
+
+
+
+/* Object with application (i.e. Geldkarte) specific data. */
+struct app_local_s
+{
+ char kblz[2+1+4+1];
+ const char *banktype;
+ char *cardno;
+ char expires[7+1];
+ char validfrom[10+1];
+ char *country;
+ char currency[3+1];
+ unsigned int currency_mult100;
+ unsigned char chipid;
+ unsigned char osvers;
+};
+
+
+
+
+/* Deconstructor. */
+static void
+do_deinit (app_t app)
+{
+ if (app && app->app_local)
+ {
+ xfree (app->app_local->cardno);
+ xfree (app->app_local->country);
+ xfree (app->app_local);
+ app->app_local = NULL;
+ }
+}
+
+
+static gpg_error_t
+send_one_string (ctrl_t ctrl, const char *name, const char *string)
+{
+ if (!name || !string)
+ return 0;
+ send_status_info (ctrl, name, string, strlen (string), NULL, 0);
+ return 0;
+}
+
+/* Implement the GETATTR command. This is similar to the LEARN
+ command but returns just one value via the status interface. */
+static gpg_error_t
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ gpg_error_t err;
+ struct app_local_s *ld = app->app_local;
+ char numbuf[100];
+
+ if (!strcmp (name, "X-KBLZ"))
+ err = send_one_string (ctrl, name, ld->kblz);
+ else if (!strcmp (name, "X-BANKINFO"))
+ err = send_one_string (ctrl, name, ld->banktype);
+ else if (!strcmp (name, "X-CARDNO"))
+ err = send_one_string (ctrl, name, ld->cardno);
+ else if (!strcmp (name, "X-EXPIRES"))
+ err = send_one_string (ctrl, name, ld->expires);
+ else if (!strcmp (name, "X-VALIDFROM"))
+ err = send_one_string (ctrl, name, ld->validfrom);
+ else if (!strcmp (name, "X-COUNTRY"))
+ err = send_one_string (ctrl, name, ld->country);
+ else if (!strcmp (name, "X-CURRENCY"))
+ err = send_one_string (ctrl, name, ld->currency);
+ else if (!strcmp (name, "X-CRNCMULT"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%u", ld->currency_mult100);
+ err = send_one_string (ctrl, name, numbuf);
+ }
+ else if (!strcmp (name, "X-ZKACHIPID"))
+ {
+ snprintf (numbuf, sizeof numbuf, "0x%02X", ld->chipid);
+ err = send_one_string (ctrl, name, numbuf);
+ }
+ else if (!strcmp (name, "X-OSVERSION"))
+ {
+ snprintf (numbuf, sizeof numbuf, "0x%02X", ld->osvers);
+ err = send_one_string (ctrl, name, numbuf);
+ }
+ else
+ err = gpg_error (GPG_ERR_INV_NAME);
+
+ return err;
+}
+
+
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl)
+{
+ static const char *names[] = {
+ "X-KBLZ",
+ "X-BANKINFO",
+ "X-CARDNO",
+ "X-EXPIRES",
+ "X-VALIDFROM",
+ "X-COUNTRY",
+ "X-CURRENCY",
+ "X-CRNCMULT",
+ "X-ZKACHIPID",
+ "X-OSVERSION",
+ NULL
+ };
+ gpg_error_t err = 0;
+ int idx;
+
+ for (idx=0; names[idx] && !err; idx++)
+ err = do_getattr (app, ctrl, names[idx]);
+ return err;
+}
+
+
+static char *
+copy_bcd (const unsigned char *string, size_t length)
+{
+ const unsigned char *s;
+ size_t n;
+ size_t needed;
+ char *buffer, *dst;
+
+ if (!length)
+ return xtrystrdup ("");
+
+ /* Skip leading zeroes. */
+ for (; length && !*string; length--, string++)
+ ;
+ s = string;
+ n = length;
+ needed = 0;
+ for (; n ; n--, s++)
+ {
+ if (!needed && !(*s & 0xf0))
+ ; /* Skip the leading zero in the first nibble. */
+ else
+ {
+ if ( ((*s >> 4) & 0x0f) > 9 )
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ needed++;
+ }
+ if ( n == 1 && (*s & 0x0f) > 9 )
+ ; /* Ignore the last digit if it has the sign. */
+ else
+ {
+ needed++;
+ if ( (*s & 0x0f) > 9 )
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ }
+
+ }
+ if (!needed) /* If it is all zero, print a "0". */
+ needed++;
+
+ buffer = dst = xtrymalloc (needed+1);
+ if (!buffer)
+ return NULL;
+
+ s = string;
+ n = length;
+ needed = 0;
+ for (; n ; n--, s++)
+ {
+ if (!needed && !(*s & 0xf0))
+ ; /* Skip the leading zero in the first nibble. */
+ else
+ {
+ *dst++ = '0' + ((*s >> 4) & 0x0f);
+ needed++;
+ }
+
+ if ( n == 1 && (*s & 0x0f) > 9 )
+ ; /* Ignore the last digit if it has the sign. */
+ else
+ {
+ *dst++ = '0' + (*s & 0x0f);
+ needed++;
+ }
+ }
+ if (!needed)
+ *dst++ = '0';
+ *dst = 0;
+
+ return buffer;
+}
+
+
+
+
+/* Select the Geldkarte application. */
+gpg_error_t
+app_select_geldkarte (app_t app)
+{
+ gpg_error_t err;
+ int slot = app->slot;
+ unsigned char *result = NULL;
+ size_t resultlen;
+ struct app_local_s *ld;
+ const char *banktype;
+
+ err = iso7816_select_file (slot, 0x3f00, 1, NULL, NULL);
+ if (err)
+ goto leave; /* Oops. */
+
+ /* Read short EF 0xbc. We require this record to be at least 24
+ bytes with the the first byte 0x67 and a correct the filler
+ byte. */
+ err = iso7816_read_record (slot, 1, 1, 0xbc, &result, &resultlen);
+ if (err)
+ goto leave; /* No such record or other error - not a Geldkarte. */
+ if (resultlen < 24 || *result != 0x67 || result[22])
+ {
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+
+ /* The short Bankleitzahl consists of 3 bytes at offset 1. */
+ switch (result[1])
+ {
+ case 0x21: banktype = "Oeffentlich-rechtliche oder private Bank"; break;
+ case 0x22: banktype = "Privat- oder Geschaeftsbank"; break;
+ case 0x25: banktype = "Sparkasse"; break;
+ case 0x26:
+ case 0x29: banktype = "Genossenschaftsbank"; break;
+ default:
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave; /* Probably not a Geldkarte. */
+ }
+
+ app->apptype = "GELDKARTE";
+ app->fnc.deinit = do_deinit;
+
+ app->app_local = ld = xtrycalloc (1, sizeof *app->app_local);
+ if (!app->app_local)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ snprintf (ld->kblz, sizeof ld->kblz, "%02X-%02X%02X",
+ result[1], result[2], result[3]);
+ ld->banktype = banktype;
+ ld->cardno = copy_bcd (result+4, 5);
+ if (!ld->cardno)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ snprintf (ld->expires, sizeof ld->expires, "20%02X-%02X",
+ result[10], result[11]);
+ snprintf (ld->validfrom, sizeof ld->validfrom, "20%02X-%02X-%02X",
+ result[12], result[13], result[14]);
+
+ ld->country = copy_bcd (result+15, 2);
+ if (!ld->country)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ snprintf (ld->currency, sizeof ld->currency, "%c%c%c",
+ isascii (result[17])? result[17]:' ',
+ isascii (result[18])? result[18]:' ',
+ isascii (result[19])? result[19]:' ');
+
+ ld->currency_mult100 = (result[20] == 0x01? 1:
+ result[20] == 0x02? 10:
+ result[20] == 0x04? 100:
+ result[20] == 0x08? 1000:
+ result[20] == 0x10? 10000:
+ result[20] == 0x20? 100000:0);
+
+ ld->chipid = result[21];
+ ld->osvers = result[23];
+
+ /* Setup the rest of the methods. */
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.getattr = do_getattr;
+
+
+ leave:
+ xfree (result);
+ if (err)
+ do_deinit (app);
+ return err;
+}
diff --git a/scd/app.c b/scd/app.c
index 731f98326..4034fa64c 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -352,6 +352,9 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
err = app_select_p15 (app);
if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
err = app_select_dinsig (app);
+ if (err && is_app_allowed ("geldkarte")
+ && (!name || !strcmp (name, "geldkarte")))
+ err = app_select_geldkarte (app);
if (err && name)
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
@@ -440,6 +443,7 @@ release_application (app_t app)
FF 00 00 = For serial numbers starting with an FF
FF 01 00 = Some german p15 cards return an empty serial number so the
serial number from the EF(TokenInfo) is used instead.
+ FF 7F 00 = No serialno.
All other serial number not starting with FF are used as they are.
*/
@@ -452,13 +456,23 @@ app_munge_serialno (app_t app)
requires that we put our default prefix "FF0000" in front. */
unsigned char *p = xtrymalloc (app->serialnolen + 3);
if (!p)
- return gpg_error (gpg_err_code_from_errno (errno));
+ return gpg_error_from_syserror ();
memcpy (p, "\xff\0", 3);
memcpy (p+3, app->serialno, app->serialnolen);
app->serialnolen += 3;
xfree (app->serialno);
app->serialno = p;
}
+ else if (!app->serialnolen)
+ {
+ unsigned char *p = xtrymalloc (3);
+ if (!p)
+ return gpg_error_from_syserror ();
+ memcpy (p, "\xff\x7f", 3);
+ app->serialnolen = 3;
+ xfree (app->serialno);
+ app->serialno = p;
+ }
return 0;
}
@@ -482,7 +496,10 @@ app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
if (stamp)
*stamp = 0; /* not available */
- buf = bin2hex (app->serialno, app->serialnolen, NULL);
+ if (!app->serialnolen)
+ buf = xtrystrdup ("FF7F00");
+ else
+ buf = bin2hex (app->serialno, app->serialnolen, NULL);
if (!buf)
return gpg_error_from_syserror ();