g10: Don't skip legacy keys if the search mode is KEYDB_SEARCH_MODE_NEXT
[gnupg.git] / scd / app-geldkarte.c
index 3db42bf..f8ee9f6 100644 (file)
 
 
 /* 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.
+   German Geldkarte (debit card for small amounts).  We only support
+   newer Geldkarte (with the AID DF_BOERSE_NEU) issued since 2000 or
+   even earlier.
 */
 
 
@@ -58,6 +57,9 @@ struct app_local_s
   unsigned int currency_mult100;
   unsigned char chipid;
   unsigned char osvers;
+  int balance;
+  int maxamount;
+  int maxamount1;
 };
 
 
@@ -88,13 +90,13 @@ send_one_string (ctrl_t ctrl, const char *name, const char *string)
 
 /* Implement the GETATTR command.  This is similar to the LEARN
    command but returns just one value via the status interface. */
-static gpg_error_t 
+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"))
@@ -109,11 +111,6 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     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);
@@ -124,15 +121,33 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
       snprintf (numbuf, sizeof numbuf, "0x%02X", ld->osvers);
       err = send_one_string (ctrl, name, numbuf);
     }
+  else if (!strcmp (name, "X-BALANCE"))
+    {
+      snprintf (numbuf, sizeof numbuf, "%.2f",
+                (double)ld->balance / 100 * ld->currency_mult100);
+      err = send_one_string (ctrl, name, numbuf);
+    }
+  else if (!strcmp (name, "X-MAXAMOUNT"))
+    {
+      snprintf (numbuf, sizeof numbuf, "%.2f",
+                (double)ld->maxamount / 100 * ld->currency_mult100);
+      err = send_one_string (ctrl, name, numbuf);
+    }
+  else if (!strcmp (name, "X-MAXAMOUNT1"))
+    {
+      snprintf (numbuf, sizeof numbuf, "%.2f",
+                (double)ld->maxamount1 / 100 * ld->currency_mult100);
+      err = send_one_string (ctrl, name, numbuf);
+    }
   else
-    err = gpg_error (GPG_ERR_INV_NAME); 
+    err = gpg_error (GPG_ERR_INV_NAME);
 
   return err;
 }
 
 
 static gpg_error_t
-do_learn_status (app_t app, ctrl_t ctrl)
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
 {
   static const char *names[] = {
     "X-KBLZ",
@@ -142,14 +157,18 @@ do_learn_status (app_t app, ctrl_t ctrl)
     "X-VALIDFROM",
     "X-COUNTRY",
     "X-CURRENCY",
-    "X-CRNCMULT",
     "X-ZKACHIPID",
     "X-OSVERSION",
+    "X-BALANCE",
+    "X-MAXAMOUNT",
+    "X-MAXAMOUNT1",
     NULL
   };
   gpg_error_t err = 0;
   int idx;
 
+  (void)flags;
+
   for (idx=0; names[idx] && !err; idx++)
     err = do_getattr (app, ctrl, names[idx]);
   return err;
@@ -166,7 +185,7 @@ copy_bcd (const unsigned char *string, size_t length)
 
   if (!length)
     return xtrystrdup ("");
-      
+
   /* Skip leading zeroes. */
   for (; length && !*string; length--, string++)
     ;
@@ -177,7 +196,7 @@ copy_bcd (const unsigned char *string, size_t length)
     {
       if (!needed && !(*s & 0xf0))
         ; /* Skip the leading zero in the first nibble.  */
-      else 
+      else
         {
           if ( ((*s >> 4) & 0x0f) > 9 )
             {
@@ -197,7 +216,7 @@ copy_bcd (const unsigned char *string, size_t length)
               return NULL;
             }
         }
-      
+
     }
   if (!needed) /* If it is all zero, print a "0". */
     needed++;
@@ -208,12 +227,12 @@ copy_bcd (const unsigned char *string, size_t length)
 
   s = string;
   n = length;
-  needed = 0;  
+  needed = 0;
   for (; n ; n--, s++)
     {
       if (!needed && !(*s & 0xf0))
         ; /* Skip the leading zero in the first nibble.  */
-      else 
+      else
         {
           *dst++ = '0' + ((*s >> 4) & 0x0f);
           needed++;
@@ -228,42 +247,58 @@ copy_bcd (const unsigned char *string, size_t length)
         }
     }
   if (!needed)
-    *dst++ = '0';  
+    *dst++ = '0';
   *dst = 0;
 
   return buffer;
 }
 
 
+/* Convert the BCD number at STING of LENGTH into an integer and store
+   that at RESULT.  Return 0 on success.  */
+static gpg_error_t
+bcd_to_int (const unsigned char *string, size_t length, int *result)
+{
+  char *tmp;
+
+  tmp = copy_bcd (string, length);
+  if (!tmp)
+    return gpg_error (GPG_ERR_BAD_DATA);
+  *result = strtol (tmp, NULL, 10);
+  xfree (tmp);
+  return 0;
+}
 
 
 /* Select the Geldkarte application.  */
 gpg_error_t
 app_select_geldkarte (app_t app)
 {
+  static char const aid[] =
+    { 0xD2, 0x76, 0x00, 0x00, 0x25, 0x45, 0x50, 0x02, 0x00 };
   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);
+
+  err = iso7816_select_application (slot, aid, sizeof aid, 0);
   if (err)
-    goto leave;  /* Oops.  */
+    goto leave;
 
-  /* 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);
+  /* Read the first record of EF_ID (SFI=0x17).  We require this
+     record to be at least 24 bytes with the the first byte 0x67 and a
+     correct filler byte. */
+  err = iso7816_read_record (slot, 1, 1, ((0x17 << 3)|4), &result, &resultlen);
   if (err)
-    goto leave;  /* No such record or other error - not a Geldkarte.  */
+    goto leave;  /* Oops - 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])
     {
@@ -272,14 +307,31 @@ app_select_geldkarte (app_t app)
     case 0x25: banktype = "Sparkasse"; break;
     case 0x26:
     case 0x29: banktype = "Genossenschaftsbank"; break;
-    default: 
+    default:
       err = gpg_error (GPG_ERR_NOT_FOUND);
       goto leave; /* Probably not a Geldkarte. */
     }
-  
+
   app->apptype = "GELDKARTE";
   app->fnc.deinit = do_deinit;
 
+  /* If we don't have a serialno yet construct it from the EF_ID.  */
+  if (!app->serialno)
+    {
+      app->serialno = xtrymalloc (10);
+      if (!app->serialno)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+      memcpy (app->serialno, result, 10);
+      app->serialnolen = 10;
+      err = app_munge_serialno (app);
+      if (err)
+        goto leave;
+    }
+
+
   app->app_local = ld = xtrycalloc (1, sizeof *app->app_local);
   if (!app->app_local)
     {
@@ -287,7 +339,7 @@ app_select_geldkarte (app_t app)
       goto leave;
     }
 
-  snprintf (ld->kblz, sizeof ld->kblz, "%02X-%02X%02X", 
+  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);
@@ -296,8 +348,8 @@ app_select_geldkarte (app_t app)
       err = gpg_err_code_from_syserror ();
       goto leave;
     }
-  
-  snprintf (ld->expires, sizeof ld->expires, "20%02X-%02X", 
+
+  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]);
@@ -324,6 +376,26 @@ app_select_geldkarte (app_t app)
   ld->chipid = result[21];
   ld->osvers = result[23];
 
+  /* Read the first record of EF_BETRAG (SFI=0x18). */
+  xfree (result);
+  err = iso7816_read_record (slot, 1, 1, ((0x18 << 3)|4), &result, &resultlen);
+  if (err)
+    goto leave;  /* It does not make sense to continue.  */
+  if (resultlen < 12)
+    {
+      err = gpg_error (GPG_ERR_NOT_FOUND);
+      goto leave;
+    }
+  err = bcd_to_int (result+0, 3, &ld->balance);
+  if (!err)
+    err = bcd_to_int (result+3, 3, &ld->maxamount);
+  if (!err)
+    err = bcd_to_int (result+6, 3, &ld->maxamount1);
+  /* The next 3 bytes are the maximum amount chargable without using a
+     MAC.  This is usually 0.  */
+  if (err)
+    goto leave;
+
   /* Setup the rest of the methods.  */
   app->fnc.learn_status = do_learn_status;
   app->fnc.getattr = do_getattr;