LCOV - code coverage report
Current view: top level - scd - app-geldkarte.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 179 0.0 %
Date: 2016-11-29 15:00:56 Functions: 0 7 0.0 %

          Line data    Source code
       1             : /* app-geldkarte.c - The German Geldkarte application
       2             :  * Copyright (C) 2004 g10 Code GmbH
       3             :  * Copyright (C) 2009 Free Software Foundation, Inc.
       4             :  *
       5             :  * This file is part of GnuPG.
       6             :  *
       7             :  * GnuPG is free software; you can redistribute it and/or modify
       8             :  * it under the terms of the GNU General Public License as published by
       9             :  * the Free Software Foundation; either version 3 of the License, or
      10             :  * (at your option) any later version.
      11             :  *
      12             :  * GnuPG is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :  * GNU General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU General Public License
      18             :  * along with this program; if not, see <https://www.gnu.org/licenses/>.
      19             :  */
      20             : 
      21             : 
      22             : /* This is a read-only application to quickly dump information of a
      23             :    German Geldkarte (debit card for small amounts).  We only support
      24             :    newer Geldkarte (with the AID DF_BOERSE_NEU) issued since 2000 or
      25             :    even earlier.
      26             : */
      27             : 
      28             : 
      29             : #include <config.h>
      30             : #include <errno.h>
      31             : #include <stdio.h>
      32             : #include <stdlib.h>
      33             : #include <string.h>
      34             : #include <assert.h>
      35             : #include <time.h>
      36             : #include <ctype.h>
      37             : 
      38             : #include "scdaemon.h"
      39             : 
      40             : #include "i18n.h"
      41             : #include "iso7816.h"
      42             : #include "app-common.h"
      43             : #include "tlv.h"
      44             : 
      45             : 
      46             : 
      47             : /* Object with application (i.e. Geldkarte) specific data.  */
      48             : struct app_local_s
      49             : {
      50             :   char kblz[2+1+4+1];
      51             :   const char *banktype;
      52             :   char *cardno;
      53             :   char expires[7+1];
      54             :   char validfrom[10+1];
      55             :   char *country;
      56             :   char currency[3+1];
      57             :   unsigned int currency_mult100;
      58             :   unsigned char chipid;
      59             :   unsigned char osvers;
      60             :   int balance;
      61             :   int maxamount;
      62             :   int maxamount1;
      63             : };
      64             : 
      65             : 
      66             : 
      67             : 
      68             : /* Deconstructor. */
      69             : static void
      70           0 : do_deinit (app_t app)
      71             : {
      72           0 :   if (app && app->app_local)
      73             :     {
      74           0 :       xfree (app->app_local->cardno);
      75           0 :       xfree (app->app_local->country);
      76           0 :       xfree (app->app_local);
      77           0 :       app->app_local = NULL;
      78             :     }
      79           0 : }
      80             : 
      81             : 
      82             : static gpg_error_t
      83           0 : send_one_string (ctrl_t ctrl, const char *name, const char *string)
      84             : {
      85           0 :   if (!name || !string)
      86           0 :     return 0;
      87           0 :   send_status_info (ctrl, name, string, strlen (string), NULL, 0);
      88           0 :   return 0;
      89             : }
      90             : 
      91             : /* Implement the GETATTR command.  This is similar to the LEARN
      92             :    command but returns just one value via the status interface. */
      93             : static gpg_error_t
      94           0 : do_getattr (app_t app, ctrl_t ctrl, const char *name)
      95             : {
      96             :   gpg_error_t err;
      97           0 :   struct app_local_s *ld = app->app_local;
      98             :   char numbuf[100];
      99             : 
     100           0 :   if (!strcmp (name, "X-KBLZ"))
     101           0 :     err = send_one_string (ctrl, name, ld->kblz);
     102           0 :   else if (!strcmp (name, "X-BANKINFO"))
     103           0 :     err = send_one_string (ctrl, name, ld->banktype);
     104           0 :   else if (!strcmp (name, "X-CARDNO"))
     105           0 :     err = send_one_string (ctrl, name, ld->cardno);
     106           0 :   else if (!strcmp (name, "X-EXPIRES"))
     107           0 :     err = send_one_string (ctrl, name, ld->expires);
     108           0 :   else if (!strcmp (name, "X-VALIDFROM"))
     109           0 :     err = send_one_string (ctrl, name, ld->validfrom);
     110           0 :   else if (!strcmp (name, "X-COUNTRY"))
     111           0 :     err = send_one_string (ctrl, name, ld->country);
     112           0 :   else if (!strcmp (name, "X-CURRENCY"))
     113           0 :     err = send_one_string (ctrl, name, ld->currency);
     114           0 :   else if (!strcmp (name, "X-ZKACHIPID"))
     115             :     {
     116           0 :       snprintf (numbuf, sizeof numbuf, "0x%02X", ld->chipid);
     117           0 :       err = send_one_string (ctrl, name, numbuf);
     118             :     }
     119           0 :   else if (!strcmp (name, "X-OSVERSION"))
     120             :     {
     121           0 :       snprintf (numbuf, sizeof numbuf, "0x%02X", ld->osvers);
     122           0 :       err = send_one_string (ctrl, name, numbuf);
     123             :     }
     124           0 :   else if (!strcmp (name, "X-BALANCE"))
     125             :     {
     126           0 :       snprintf (numbuf, sizeof numbuf, "%.2f",
     127           0 :                 (double)ld->balance / 100 * ld->currency_mult100);
     128           0 :       err = send_one_string (ctrl, name, numbuf);
     129             :     }
     130           0 :   else if (!strcmp (name, "X-MAXAMOUNT"))
     131             :     {
     132           0 :       snprintf (numbuf, sizeof numbuf, "%.2f",
     133           0 :                 (double)ld->maxamount / 100 * ld->currency_mult100);
     134           0 :       err = send_one_string (ctrl, name, numbuf);
     135             :     }
     136           0 :   else if (!strcmp (name, "X-MAXAMOUNT1"))
     137             :     {
     138           0 :       snprintf (numbuf, sizeof numbuf, "%.2f",
     139           0 :                 (double)ld->maxamount1 / 100 * ld->currency_mult100);
     140           0 :       err = send_one_string (ctrl, name, numbuf);
     141             :     }
     142             :   else
     143           0 :     err = gpg_error (GPG_ERR_INV_NAME);
     144             : 
     145           0 :   return err;
     146             : }
     147             : 
     148             : 
     149             : static gpg_error_t
     150           0 : do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
     151             : {
     152             :   static const char *names[] = {
     153             :     "X-KBLZ",
     154             :     "X-BANKINFO",
     155             :     "X-CARDNO",
     156             :     "X-EXPIRES",
     157             :     "X-VALIDFROM",
     158             :     "X-COUNTRY",
     159             :     "X-CURRENCY",
     160             :     "X-ZKACHIPID",
     161             :     "X-OSVERSION",
     162             :     "X-BALANCE",
     163             :     "X-MAXAMOUNT",
     164             :     "X-MAXAMOUNT1",
     165             :     NULL
     166             :   };
     167           0 :   gpg_error_t err = 0;
     168             :   int idx;
     169             : 
     170             :   (void)flags;
     171             : 
     172           0 :   for (idx=0; names[idx] && !err; idx++)
     173           0 :     err = do_getattr (app, ctrl, names[idx]);
     174           0 :   return err;
     175             : }
     176             : 
     177             : 
     178             : static char *
     179           0 : copy_bcd (const unsigned char *string, size_t length)
     180             : {
     181             :   const unsigned char *s;
     182             :   size_t n;
     183             :   size_t needed;
     184             :   char *buffer, *dst;
     185             : 
     186           0 :   if (!length)
     187           0 :     return xtrystrdup ("");
     188             : 
     189             :   /* Skip leading zeroes. */
     190           0 :   for (; length && !*string; length--, string++)
     191             :     ;
     192           0 :   s = string;
     193           0 :   n = length;
     194           0 :   needed = 0;
     195           0 :   for (; n ; n--, s++)
     196             :     {
     197           0 :       if (!needed && !(*s & 0xf0))
     198             :         ; /* Skip the leading zero in the first nibble.  */
     199             :       else
     200             :         {
     201           0 :           if ( ((*s >> 4) & 0x0f) > 9 )
     202             :             {
     203           0 :               errno = EINVAL;
     204           0 :               return NULL;
     205             :             }
     206           0 :           needed++;
     207             :         }
     208           0 :       if ( n == 1 && (*s & 0x0f) > 9 )
     209             :         ; /* Ignore the last digit if it has the sign.  */
     210             :       else
     211             :         {
     212           0 :           needed++;
     213           0 :           if ( (*s & 0x0f) > 9 )
     214             :             {
     215           0 :               errno = EINVAL;
     216           0 :               return NULL;
     217             :             }
     218             :         }
     219             : 
     220             :     }
     221           0 :   if (!needed) /* If it is all zero, print a "0". */
     222           0 :     needed++;
     223             : 
     224           0 :   buffer = dst = xtrymalloc (needed+1);
     225           0 :   if (!buffer)
     226           0 :     return NULL;
     227             : 
     228           0 :   s = string;
     229           0 :   n = length;
     230           0 :   needed = 0;
     231           0 :   for (; n ; n--, s++)
     232             :     {
     233           0 :       if (!needed && !(*s & 0xf0))
     234             :         ; /* Skip the leading zero in the first nibble.  */
     235             :       else
     236             :         {
     237           0 :           *dst++ = '0' + ((*s >> 4) & 0x0f);
     238           0 :           needed++;
     239             :         }
     240             : 
     241           0 :       if ( n == 1 && (*s & 0x0f) > 9 )
     242             :         ; /* Ignore the last digit if it has the sign.  */
     243             :       else
     244             :         {
     245           0 :           *dst++ = '0' + (*s & 0x0f);
     246           0 :           needed++;
     247             :         }
     248             :     }
     249           0 :   if (!needed)
     250           0 :     *dst++ = '0';
     251           0 :   *dst = 0;
     252             : 
     253           0 :   return buffer;
     254             : }
     255             : 
     256             : 
     257             : /* Convert the BCD number at STING of LENGTH into an integer and store
     258             :    that at RESULT.  Return 0 on success.  */
     259             : static gpg_error_t
     260           0 : bcd_to_int (const unsigned char *string, size_t length, int *result)
     261             : {
     262             :   char *tmp;
     263             : 
     264           0 :   tmp = copy_bcd (string, length);
     265           0 :   if (!tmp)
     266           0 :     return gpg_error (GPG_ERR_BAD_DATA);
     267           0 :   *result = strtol (tmp, NULL, 10);
     268           0 :   xfree (tmp);
     269           0 :   return 0;
     270             : }
     271             : 
     272             : 
     273             : /* Select the Geldkarte application.  */
     274             : gpg_error_t
     275           0 : app_select_geldkarte (app_t app)
     276             : {
     277             :   static char const aid[] =
     278             :     { 0xD2, 0x76, 0x00, 0x00, 0x25, 0x45, 0x50, 0x02, 0x00 };
     279             :   gpg_error_t err;
     280           0 :   int slot = app->slot;
     281           0 :   unsigned char *result = NULL;
     282             :   size_t resultlen;
     283             :   struct app_local_s *ld;
     284             :   const char *banktype;
     285             : 
     286           0 :   err = iso7816_select_application (slot, aid, sizeof aid, 0);
     287           0 :   if (err)
     288           0 :     goto leave;
     289             : 
     290             :   /* Read the first record of EF_ID (SFI=0x17).  We require this
     291             :      record to be at least 24 bytes with the the first byte 0x67 and a
     292             :      correct filler byte. */
     293           0 :   err = iso7816_read_record (slot, 1, 1, ((0x17 << 3)|4), &result, &resultlen);
     294           0 :   if (err)
     295           0 :     goto leave;  /* Oops - not a Geldkarte.  */
     296           0 :   if (resultlen < 24 || *result != 0x67 || result[22])
     297             :     {
     298           0 :       err = gpg_error (GPG_ERR_NOT_FOUND);
     299           0 :       goto leave;
     300             :     }
     301             : 
     302             :   /* The short Bankleitzahl consists of 3 bytes at offset 1.  */
     303           0 :   switch (result[1])
     304             :     {
     305           0 :     case 0x21: banktype = "Oeffentlich-rechtliche oder private Bank"; break;
     306           0 :     case 0x22: banktype = "Privat- oder Geschaeftsbank"; break;
     307           0 :     case 0x25: banktype = "Sparkasse"; break;
     308             :     case 0x26:
     309           0 :     case 0x29: banktype = "Genossenschaftsbank"; break;
     310             :     default:
     311           0 :       err = gpg_error (GPG_ERR_NOT_FOUND);
     312           0 :       goto leave; /* Probably not a Geldkarte. */
     313             :     }
     314             : 
     315           0 :   app->apptype = "GELDKARTE";
     316           0 :   app->fnc.deinit = do_deinit;
     317             : 
     318             :   /* If we don't have a serialno yet construct it from the EF_ID.  */
     319           0 :   if (!app->serialno)
     320             :     {
     321           0 :       app->serialno = xtrymalloc (10);
     322           0 :       if (!app->serialno)
     323             :         {
     324           0 :           err = gpg_error_from_syserror ();
     325           0 :           goto leave;
     326             :         }
     327           0 :       memcpy (app->serialno, result, 10);
     328           0 :       app->serialnolen = 10;
     329           0 :       err = app_munge_serialno (app);
     330           0 :       if (err)
     331           0 :         goto leave;
     332             :     }
     333             : 
     334             : 
     335           0 :   app->app_local = ld = xtrycalloc (1, sizeof *app->app_local);
     336           0 :   if (!app->app_local)
     337             :     {
     338           0 :       err = gpg_err_code_from_syserror ();
     339           0 :       goto leave;
     340             :     }
     341             : 
     342           0 :   snprintf (ld->kblz, sizeof ld->kblz, "%02X-%02X%02X",
     343           0 :             result[1], result[2], result[3]);
     344           0 :   ld->banktype = banktype;
     345           0 :   ld->cardno = copy_bcd (result+4, 5);
     346           0 :   if (!ld->cardno)
     347             :     {
     348           0 :       err = gpg_err_code_from_syserror ();
     349           0 :       goto leave;
     350             :     }
     351             : 
     352           0 :   snprintf (ld->expires, sizeof ld->expires, "20%02X-%02X",
     353           0 :             result[10], result[11]);
     354           0 :   snprintf (ld->validfrom, sizeof ld->validfrom, "20%02X-%02X-%02X",
     355           0 :             result[12], result[13], result[14]);
     356             : 
     357           0 :   ld->country = copy_bcd (result+15, 2);
     358           0 :   if (!ld->country)
     359             :     {
     360           0 :       err = gpg_err_code_from_syserror ();
     361           0 :       goto leave;
     362             :     }
     363             : 
     364           0 :   snprintf (ld->currency, sizeof ld->currency, "%c%c%c",
     365           0 :             isascii (result[17])? result[17]:' ',
     366           0 :             isascii (result[18])? result[18]:' ',
     367           0 :             isascii (result[19])? result[19]:' ');
     368             : 
     369           0 :   ld->currency_mult100 = (result[20] == 0x01? 1:
     370           0 :                           result[20] == 0x02? 10:
     371           0 :                           result[20] == 0x04? 100:
     372           0 :                           result[20] == 0x08? 1000:
     373           0 :                           result[20] == 0x10? 10000:
     374           0 :                           result[20] == 0x20? 100000:0);
     375             : 
     376           0 :   ld->chipid = result[21];
     377           0 :   ld->osvers = result[23];
     378             : 
     379             :   /* Read the first record of EF_BETRAG (SFI=0x18). */
     380           0 :   xfree (result);
     381           0 :   err = iso7816_read_record (slot, 1, 1, ((0x18 << 3)|4), &result, &resultlen);
     382           0 :   if (err)
     383           0 :     goto leave;  /* It does not make sense to continue.  */
     384           0 :   if (resultlen < 12)
     385             :     {
     386           0 :       err = gpg_error (GPG_ERR_NOT_FOUND);
     387           0 :       goto leave;
     388             :     }
     389           0 :   err = bcd_to_int (result+0, 3, &ld->balance);
     390           0 :   if (!err)
     391           0 :     err = bcd_to_int (result+3, 3, &ld->maxamount);
     392           0 :   if (!err)
     393           0 :     err = bcd_to_int (result+6, 3, &ld->maxamount1);
     394             :   /* The next 3 bytes are the maximum amount chargable without using a
     395             :      MAC.  This is usually 0.  */
     396           0 :   if (err)
     397           0 :     goto leave;
     398             : 
     399             :   /* Setup the rest of the methods.  */
     400           0 :   app->fnc.learn_status = do_learn_status;
     401           0 :   app->fnc.getattr = do_getattr;
     402             : 
     403             : 
     404             :  leave:
     405           0 :   xfree (result);
     406           0 :   if (err)
     407           0 :     do_deinit (app);
     408           0 :   return err;
     409             : }

Generated by: LCOV version 1.11