~ubuntu-branches/ubuntu/precise/gnupg2/precise-proposed

« back to all changes in this revision

Viewing changes to scd/app-geldkarte.c

  • Committer: Bazaar Package Importer
  • Author(s): Eric Dorland
  • Date: 2009-03-08 22:46:47 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20090308224647-gq17gatcl71lrc2k
Tags: 2.0.11-1
* New upstream release. (Closes: #496663)
* debian/control: Make the description a little more distinctive than
  gnupg v1's. Thanks Jari Aalto. (Closes: #496323)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <http://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
do_deinit (app_t app)
 
71
{
 
72
  if (app && app->app_local)
 
73
    {
 
74
      xfree (app->app_local->cardno);
 
75
      xfree (app->app_local->country);
 
76
      xfree (app->app_local);
 
77
      app->app_local = NULL;
 
78
    }
 
79
}
 
80
 
 
81
 
 
82
static gpg_error_t
 
83
send_one_string (ctrl_t ctrl, const char *name, const char *string)
 
84
{
 
85
  if (!name || !string)
 
86
    return 0;
 
87
  send_status_info (ctrl, name, string, strlen (string), NULL, 0);
 
88
  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
do_getattr (app_t app, ctrl_t ctrl, const char *name)
 
95
{
 
96
  gpg_error_t err;
 
97
  struct app_local_s *ld = app->app_local;
 
98
  char numbuf[100];
 
99
  
 
100
  if (!strcmp (name, "X-KBLZ"))
 
101
    err = send_one_string (ctrl, name, ld->kblz);
 
102
  else if (!strcmp (name, "X-BANKINFO"))
 
103
    err = send_one_string (ctrl, name, ld->banktype);
 
104
  else if (!strcmp (name, "X-CARDNO"))
 
105
    err = send_one_string (ctrl, name, ld->cardno);
 
106
  else if (!strcmp (name, "X-EXPIRES"))
 
107
    err = send_one_string (ctrl, name, ld->expires);
 
108
  else if (!strcmp (name, "X-VALIDFROM"))
 
109
    err = send_one_string (ctrl, name, ld->validfrom);
 
110
  else if (!strcmp (name, "X-COUNTRY"))
 
111
    err = send_one_string (ctrl, name, ld->country);
 
112
  else if (!strcmp (name, "X-CURRENCY"))
 
113
    err = send_one_string (ctrl, name, ld->currency);
 
114
  else if (!strcmp (name, "X-ZKACHIPID"))
 
115
    {
 
116
      snprintf (numbuf, sizeof numbuf, "0x%02X", ld->chipid);
 
117
      err = send_one_string (ctrl, name, numbuf);
 
118
    }
 
119
  else if (!strcmp (name, "X-OSVERSION"))
 
120
    {
 
121
      snprintf (numbuf, sizeof numbuf, "0x%02X", ld->osvers);
 
122
      err = send_one_string (ctrl, name, numbuf);
 
123
    }
 
124
  else if (!strcmp (name, "X-BALANCE"))
 
125
    {
 
126
      snprintf (numbuf, sizeof numbuf, "%.2f", 
 
127
                (double)ld->balance / 100 * ld->currency_mult100);
 
128
      err = send_one_string (ctrl, name, numbuf);
 
129
    }
 
130
  else if (!strcmp (name, "X-MAXAMOUNT"))
 
131
    {
 
132
      snprintf (numbuf, sizeof numbuf, "%.2f", 
 
133
                (double)ld->maxamount / 100 * ld->currency_mult100);
 
134
      err = send_one_string (ctrl, name, numbuf);
 
135
    }
 
136
  else if (!strcmp (name, "X-MAXAMOUNT1"))
 
137
    {
 
138
      snprintf (numbuf, sizeof numbuf, "%.2f", 
 
139
                (double)ld->maxamount1 / 100 * ld->currency_mult100);
 
140
      err = send_one_string (ctrl, name, numbuf);
 
141
    }
 
142
  else
 
143
    err = gpg_error (GPG_ERR_INV_NAME); 
 
144
 
 
145
  return err;
 
146
}
 
147
 
 
148
 
 
149
static gpg_error_t
 
150
do_learn_status (app_t app, ctrl_t ctrl)
 
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
  gpg_error_t err = 0;
 
168
  int idx;
 
169
 
 
170
  for (idx=0; names[idx] && !err; idx++)
 
171
    err = do_getattr (app, ctrl, names[idx]);
 
172
  return err;
 
173
}
 
174
 
 
175
 
 
176
static char *
 
177
copy_bcd (const unsigned char *string, size_t length)
 
178
{
 
179
  const unsigned char *s;
 
180
  size_t n;
 
181
  size_t needed;
 
182
  char *buffer, *dst;
 
183
 
 
184
  if (!length)
 
185
    return xtrystrdup ("");
 
186
      
 
187
  /* Skip leading zeroes. */
 
188
  for (; length && !*string; length--, string++)
 
189
    ;
 
190
  s = string;
 
191
  n = length;
 
192
  needed = 0;
 
193
  for (; n ; n--, s++)
 
194
    {
 
195
      if (!needed && !(*s & 0xf0))
 
196
        ; /* Skip the leading zero in the first nibble.  */
 
197
      else 
 
198
        {
 
199
          if ( ((*s >> 4) & 0x0f) > 9 )
 
200
            {
 
201
              errno = EINVAL;
 
202
              return NULL;
 
203
            }
 
204
          needed++;
 
205
        }
 
206
      if ( n == 1 && (*s & 0x0f) > 9 )
 
207
        ; /* Ignore the last digit if it has the sign.  */
 
208
      else
 
209
        {
 
210
          needed++;
 
211
          if ( (*s & 0x0f) > 9 )
 
212
            {
 
213
              errno = EINVAL;
 
214
              return NULL;
 
215
            }
 
216
        }
 
217
      
 
218
    }
 
219
  if (!needed) /* If it is all zero, print a "0". */
 
220
    needed++;
 
221
 
 
222
  buffer = dst = xtrymalloc (needed+1);
 
223
  if (!buffer)
 
224
    return NULL;
 
225
 
 
226
  s = string;
 
227
  n = length;
 
228
  needed = 0;  
 
229
  for (; n ; n--, s++)
 
230
    {
 
231
      if (!needed && !(*s & 0xf0))
 
232
        ; /* Skip the leading zero in the first nibble.  */
 
233
      else 
 
234
        {
 
235
          *dst++ = '0' + ((*s >> 4) & 0x0f);
 
236
          needed++;
 
237
        }
 
238
 
 
239
      if ( n == 1 && (*s & 0x0f) > 9 )
 
240
        ; /* Ignore the last digit if it has the sign.  */
 
241
      else
 
242
        {
 
243
          *dst++ = '0' + (*s & 0x0f);
 
244
          needed++;
 
245
        }
 
246
    }
 
247
  if (!needed)
 
248
    *dst++ = '0';  
 
249
  *dst = 0;
 
250
 
 
251
  return buffer;
 
252
}
 
253
 
 
254
 
 
255
/* Convert the BCD number at STING of LENGTH into an integer and store
 
256
   that at RESULT.  Return 0 on success.  */
 
257
static gpg_error_t
 
258
bcd_to_int (const unsigned char *string, size_t length, int *result)
 
259
{
 
260
  char *tmp;
 
261
 
 
262
  tmp = copy_bcd (string, length);
 
263
  if (!tmp)
 
264
    return gpg_error (GPG_ERR_BAD_DATA);
 
265
  *result = strtol (tmp, NULL, 10);
 
266
  xfree (tmp);
 
267
  return 0;
 
268
}
 
269
 
 
270
 
 
271
/* Select the Geldkarte application.  */
 
272
gpg_error_t
 
273
app_select_geldkarte (app_t app)
 
274
{
 
275
  static unsigned char const aid[] =
 
276
    { 0xD2, 0x76, 0x00, 0x00, 0x25, 0x45, 0x50, 0x02, 0x00 };
 
277
  gpg_error_t err;
 
278
  int slot = app->slot;
 
279
  unsigned char *result = NULL;
 
280
  size_t resultlen;
 
281
  struct app_local_s *ld;
 
282
  const char *banktype;
 
283
  
 
284
  err = iso7816_select_application (slot, aid, sizeof aid, 0);
 
285
  if (err)
 
286
    goto leave; 
 
287
  
 
288
  /* Read the first record of EF_ID (SFI=0x17).  We require this
 
289
     record to be at least 24 bytes with the the first byte 0x67 and a
 
290
     correct filler byte. */
 
291
  err = iso7816_read_record (slot, 1, 1, ((0x17 << 3)|4), &result, &resultlen);
 
292
  if (err)
 
293
    goto leave;  /* Oops - not a Geldkarte.  */
 
294
  if (resultlen < 24 || *result != 0x67 || result[22])
 
295
    {
 
296
      err = gpg_error (GPG_ERR_NOT_FOUND);
 
297
      goto leave;
 
298
    }
 
299
  
 
300
  /* The short Bankleitzahl consists of 3 bytes at offset 1.  */
 
301
  switch (result[1])
 
302
    {
 
303
    case 0x21: banktype = "Oeffentlich-rechtliche oder private Bank"; break;
 
304
    case 0x22: banktype = "Privat- oder Geschaeftsbank"; break;
 
305
    case 0x25: banktype = "Sparkasse"; break;
 
306
    case 0x26:
 
307
    case 0x29: banktype = "Genossenschaftsbank"; break;
 
308
    default: 
 
309
      err = gpg_error (GPG_ERR_NOT_FOUND);
 
310
      goto leave; /* Probably not a Geldkarte. */
 
311
    }
 
312
  
 
313
  app->apptype = "GELDKARTE";
 
314
  app->fnc.deinit = do_deinit;
 
315
 
 
316
  /* If we don't have a serialno yet construct it from the EF_ID.  */
 
317
  if (!app->serialno)
 
318
    {
 
319
      app->serialno = xtrymalloc (10);
 
320
      if (!app->serialno)
 
321
        {
 
322
          err = gpg_error_from_syserror ();
 
323
          goto leave;
 
324
        }
 
325
      memcpy (app->serialno, result, 10);
 
326
      app->serialnolen = 10;
 
327
      err = app_munge_serialno (app);
 
328
      if (err)
 
329
        goto leave;
 
330
    }
 
331
 
 
332
 
 
333
  app->app_local = ld = xtrycalloc (1, sizeof *app->app_local);
 
334
  if (!app->app_local)
 
335
    {
 
336
      err = gpg_err_code_from_syserror ();
 
337
      goto leave;
 
338
    }
 
339
 
 
340
  snprintf (ld->kblz, sizeof ld->kblz, "%02X-%02X%02X", 
 
341
            result[1], result[2], result[3]);
 
342
  ld->banktype = banktype;
 
343
  ld->cardno = copy_bcd (result+4, 5);
 
344
  if (!ld->cardno)
 
345
    {
 
346
      err = gpg_err_code_from_syserror ();
 
347
      goto leave;
 
348
    }
 
349
  
 
350
  snprintf (ld->expires, sizeof ld->expires, "20%02X-%02X", 
 
351
            result[10], result[11]);
 
352
  snprintf (ld->validfrom, sizeof ld->validfrom, "20%02X-%02X-%02X",
 
353
            result[12], result[13], result[14]);
 
354
 
 
355
  ld->country = copy_bcd (result+15, 2);
 
356
  if (!ld->country)
 
357
    {
 
358
      err = gpg_err_code_from_syserror ();
 
359
      goto leave;
 
360
    }
 
361
 
 
362
  snprintf (ld->currency, sizeof ld->currency, "%c%c%c",
 
363
            isascii (result[17])? result[17]:' ',
 
364
            isascii (result[18])? result[18]:' ',
 
365
            isascii (result[19])? result[19]:' ');
 
366
 
 
367
  ld->currency_mult100 = (result[20] == 0x01? 1:
 
368
                          result[20] == 0x02? 10:
 
369
                          result[20] == 0x04? 100:
 
370
                          result[20] == 0x08? 1000:
 
371
                          result[20] == 0x10? 10000:
 
372
                          result[20] == 0x20? 100000:0);
 
373
 
 
374
  ld->chipid = result[21];
 
375
  ld->osvers = result[23];
 
376
 
 
377
  /* Read the first record of EF_BETRAG (SFI=0x18). */
 
378
  xfree (result);
 
379
  err = iso7816_read_record (slot, 1, 1, ((0x18 << 3)|4), &result, &resultlen);
 
380
  if (err)
 
381
    goto leave;  /* It does not make sense to continue.  */
 
382
  if (resultlen < 12)
 
383
    {
 
384
      err = gpg_error (GPG_ERR_NOT_FOUND);
 
385
      goto leave;
 
386
    }
 
387
  err = bcd_to_int (result+0, 3, &ld->balance);
 
388
  if (!err)
 
389
    err = bcd_to_int (result+3, 3, &ld->maxamount);
 
390
  if (!err)
 
391
    err = bcd_to_int (result+6, 3, &ld->maxamount1);
 
392
  /* The next 3 bytes are the maximum amount chargable without using a
 
393
     MAC.  This is usually 0.  */
 
394
  if (err)
 
395
    goto leave;
 
396
 
 
397
  /* Setup the rest of the methods.  */
 
398
  app->fnc.learn_status = do_learn_status;
 
399
  app->fnc.getattr = do_getattr;
 
400
 
 
401
 
 
402
 leave:
 
403
  xfree (result);
 
404
  if (err)
 
405
    do_deinit (app);
 
406
  return err;
 
407
}