Add a Geldkarte gadget application.
authorWerner Koch <wk@gnupg.org>
Tue, 27 Jan 2009 11:30:02 +0000 (11:30 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 27 Jan 2009 11:30:02 +0000 (11:30 +0000)
Minor other changes.

12 files changed:
AUTHORS
g10/ChangeLog
g10/card-util.c
jnlib/ChangeLog
jnlib/t-support.c
keyserver/ChangeLog
keyserver/Makefile.am
scd/ChangeLog
scd/Makefile.am
scd/app-common.h
scd/app-geldkarte.c [new file with mode: 0644]
scd/app.c

diff --git a/AUTHORS b/AUTHORS
index a37d17a..6fb1fa8 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,5 +1,6 @@
 Program: GnuPG
-Maintainer:  Werner Koch <wk@gnupg.org>
+Homepage: http://www.gnupg.org
+Maintainer: Werner Koch <wk@gnupg.org>
 Bug reports: <bug-gnupg@gnu.org>
 Security related bug reports: <security@gnupg.org>
 License: GPLv3+
index a17c809..f7d7a62 100644 (file)
@@ -1,3 +1,7 @@
+2009-01-26  Werner Koch  <wk@g10code.com>
+
+       * card-util.c (card_status): Detect a Geldkarte. 
+
 2009-01-13  Werner Koch  <wk@g10code.com>
 
        * call-agent.c (dummy_data_cb): New.
index 24a7b09..1d26366 100644 (file)
@@ -387,6 +387,12 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
             fputs ("pkcs15-card:\n", fp);
           log_info ("this is a PKCS#15 compliant card\n");
         }
+      else if (info.apptype && !strcmp (info.apptype, "GELDKARTE"))
+        {
+          if (opt.with_colons)
+            fputs ("geldkarte-card:\n", fp);
+          log_info ("this is a Geldkarte compliant card\n");
+        }
       else
         {
           if (opt.with_colons)
index 4a42074..651ed79 100644 (file)
@@ -1,3 +1,8 @@
+2009-01-22  Werner Koch  <wk@g10code.com>
+
+       * t-support.c (gpg_err_code_from_errno) 
+       (gpg_err_code_from_syserror): New.
+
 2008-11-20  Werner Koch  <wk@g10code.com>
 
        * argparse.c (arg_parse): Fix last change.
index 9b84634..1261048 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 
 #include "t-support.h"
 
+\f
 /* Replacements for the malloc functions as used here. */
 
 static void
@@ -107,3 +109,31 @@ gcry_free (void *a)
   if (a)
     free (a);
 }
+
+
+\f
+/* Stubs for gpg-error functions required because some compilers do
+   not eliminate the supposed-to-be-unused-inline-functions and thus
+   require functions called from these inline fucntions.  Although we
+   do not use gpg-error, gpg-error.h may get included via gcrypt.h if
+   it happens to be used used in libjnlib-config.h.  */
+int
+gpg_err_code_from_errno (int err)
+{
+  assert (!"stub function");
+  return -1;
+}
+
+
+/* Retrieve the error code directly from the ERRNO variable.  This
+   returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped
+   (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */
+int
+gpg_err_code_from_syserror (void)
+{
+  assert (!"stub function");
+  return -1;
+}
+
+
+
index 0530d36..d9f5136 100644 (file)
@@ -1,3 +1,8 @@
+2009-01-22  Werner Koch  <wk@g10code.com>
+
+       * Makefile.am (gpg2keys_curl_LDADD, gpg2keys_hkp_LDADD): Add all
+       standard libs.
+
 2008-10-20  Werner Koch  <wk@g10code.com>
 
        * curl-shim.c (curl_global_init): Mark usused arg.
index b25c294..e97fe30 100644 (file)
@@ -1,4 +1,6 @@
-# Copyright (C) 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+# Makefile.am - Makefile for keyservers
+# Copyright (C) 2001, 2002, 2004, 2005, 2006, 
+#               2009 Free Software Foundation, Inc.
 #
 # This file is part of GnuPG.
 #
@@ -66,14 +68,17 @@ gpg2keys_hkp_CPPFLAGS = $(AM_CPPFLAGS)
 gpg2keys_hkp_LDADD = $(common_libs) $(GPG_ERROR_LIBS) $(NETLIBS) $(DNSLIBS) \
                     $(other_libs) 
 else
+# Note that we need to include all other libs here as well because
+# some compilers don't care about inline fucntions and insert
+# references to symbols used in unused inline functions.
 gpg2keys_curl_CPPFLAGS = $(LIBCURL_CPPFLAGS) $(AM_CPPFLAGS)
-gpg2keys_curl_LDADD = $(LIBCURL) $(GETOPT)
+gpg2keys_curl_LDADD = $(common_libs) $(GPG_ERROR_LIBS) $(NETLIBS) $(DNSLIBS) \
+                      $(other_libs) $(LIBCURL) $(GETOPT)
 gpg2keys_hkp_CPPFLAGS = $(LIBCURL_CPPFLAGS) $(AM_CPPFLAGS)
-gpg2keys_hkp_LDADD = $(LIBCURL) $(GETOPT)
+gpg2keys_hkp_LDADD =  $(common_libs) $(GPG_ERROR_LIBS) $(NETLIBS) $(DNSLIBS) \
+                      $(other_libs) $(LIBCURL) $(GETOPT)
 endif
 
 # Make sure that all libs are build before we use them.  This is
 # important for things like make -j2.
 $(PROGRAMS): $(common_libs)
-
-
index b42c569..7550196 100644 (file)
@@ -1,3 +1,14 @@
+2009-01-27  Werner Koch  <wk@g10code.com>
+
+       * app.c (app_munge_serialno): Add case for no serialno.
+       (app_get_serial_and_stamp): Ditto.
+
+2009-01-26  Werner Koch  <wk@g10code.com>
+
+       * app-geldkarte.c: New.
+       * Makefile.am (card_apps): Add new file.
+       * app.c (select_application): Test for geldkarte.
+
 2009-01-12  Werner Koch  <wk@g10code.com>
 
        * command.c (send_client_notifications) [HAVE_W32_SYSTEM]: Fix
index e9da5b4..86dbff8 100644 (file)
@@ -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 \
index fe98bf8..5a45fa0 100644 (file)
@@ -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 (file)
index 0000000..3db42bf
--- /dev/null
@@ -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;
+};
+
+
+
+\f
+/* 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;
+}
index 731f983..4034fa6 100644 (file)
--- 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 ();