Use a unique capitalization for "Note:".
[gnupg.git] / scd / app-p15.c
index 7a92da1..eb074ef 100644 (file)
@@ -5,7 +5,7 @@
  *
  * 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 2 of the License, or
+ * 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,
  * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+/* Information pertaining to the BELPIC developer card samples:
+
+       Unblock PUK: "222222111111"
+       Reset PIN:   "333333111111")
+
+   e.g. the APDUs 00:20:00:02:08:2C:33:33:33:11:11:11:FF
+              and 00:24:01:01:08:24:12:34:FF:FF:FF:FF:FF
+   should change the PIN into 1234.
+*/
+
 #include <config.h>
 #include <errno.h>
 #include <stdio.h>
@@ -39,37 +48,55 @@ typedef enum
   {
     CARD_TYPE_UNKNOWN,
     CARD_TYPE_TCOS,
-    CARD_TYPE_MICARDO
-  } card_type_t;
+    CARD_TYPE_MICARDO,
+    CARD_TYPE_BELPIC   /* Belgian eID card specs. */
+  }
+card_type_t;
 
 /* A list card types with ATRs noticed with these cards. */
-static struct 
+#define X(a) ((unsigned char const *)(a))
+static struct
 {
   size_t atrlen;
-  unsigned char *atr;
+  unsigned char const *atr;
   card_type_t type;
 } card_atr_list[] = {
-  { 19, "\x3B\xBA\x13\x00\x81\x31\x86\x5D\x00\x64\x05\x0A\x02\x01\x31\x80"
-        "\x90\x00\x8B", 
+  { 19, X("\x3B\xBA\x13\x00\x81\x31\x86\x5D\x00\x64\x05\x0A\x02\x01\x31\x80"
+          "\x90\x00\x8B"),
     CARD_TYPE_TCOS },  /* SLE44 */
-  { 19, "\x3B\xBA\x14\x00\x81\x31\x86\x5D\x00\x64\x05\x14\x02\x02\x31\x80"
-        "\x90\x00\x91", 
+  { 19, X("\x3B\xBA\x14\x00\x81\x31\x86\x5D\x00\x64\x05\x14\x02\x02\x31\x80"
+          "\x90\x00\x91"),
     CARD_TYPE_TCOS }, /* SLE66S */
-  { 19, "\x3B\xBA\x96\x00\x81\x31\x86\x5D\x00\x64\x05\x60\x02\x03\x31\x80"
-        "\x90\x00\x66",
+  { 19, X("\x3B\xBA\x96\x00\x81\x31\x86\x5D\x00\x64\x05\x60\x02\x03\x31\x80"
+          "\x90\x00\x66"),
     CARD_TYPE_TCOS }, /* SLE66P */
-  { 27, "\x3B\xFF\x94\x00\xFF\x80\xB1\xFE\x45\x1F\x03\x00\x68\xD2\x76\x00"
-        "\x00\x28\xFF\x05\x1E\x31\x80\x00\x90\x00\x23", 
+  { 27, X("\x3B\xFF\x94\x00\xFF\x80\xB1\xFE\x45\x1F\x03\x00\x68\xD2\x76\x00"
+          "\x00\x28\xFF\x05\x1E\x31\x80\x00\x90\x00\x23"),
     CARD_TYPE_MICARDO }, /* German BMI card */
-  { 26, "\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49"
-        "\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43",
+  { 19, X("\x3B\x6F\x00\xFF\x00\x68\xD2\x76\x00\x00\x28\xFF\x05\x1E\x31\x80"
+          "\x00\x90\x00"),
+    CARD_TYPE_MICARDO }, /* German BMI card (ATR due to reader problem) */
+  { 26, X("\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49"
+          "\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43"),
     CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */
+
   { 0 }
 };
+#undef X
 
 
-/* The Pin Types as defined in pkcs#15 v1.1 */
-typedef enum 
+/* The AID of PKCS15. */
+static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63,
+                                   0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
+
+/* The Belgian eID variant - they didn't understood why a shared AID
+   is useful for a standard.  Oh well. */
+static char const pkcs15be_aid[] = { 0xA0, 0, 0, 0x01, 0x77,
+                                   0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
+
+
+/* The PIN types as defined in pkcs#15 v1.1 */
+typedef enum
   {
     PIN_TYPE_BCD = 0,
     PIN_TYPE_ASCII_NUMERIC = 1,
@@ -103,10 +130,10 @@ typedef struct keyusage_flags_s keyusage_flags_t;
    us. To keep memory management, simple we use a linked list of
    items; i.e. one such object represents one certificate and the list
    the entire CDF. */
-struct cdf_object_s 
+struct cdf_object_s
 {
   /* Link to next item when used in a linked list. */
-  struct cdf_object_s *next; 
+  struct cdf_object_s *next;
 
   /* Length and allocated buffer with the Id of this object. */
   size_t objidlen;
@@ -116,7 +143,7 @@ struct cdf_object_s
      allocated memory IMAGE of IMAGELEN. */
   size_t imagelen;
   unsigned char *image;
-  
+
   /* Set to true if a length and offset is available. */
   int have_off;
   /* The offset and length of the object.  They are only valid if
@@ -137,10 +164,10 @@ typedef struct cdf_object_s *cdf_object_t;
    by us. To keep memory management, simple we use a linked list of
    items; i.e. one such object represents one certificate and the list
    the entire PrKDF. */
-struct prkdf_object_s 
+struct prkdf_object_s
 {
   /* Link to next item when used in a linked list. */
-  struct prkdf_object_s *next; 
+  struct prkdf_object_s *next;
 
   /* Length and allocated buffer with the Id of this object. */
   size_t objidlen;
@@ -154,7 +181,7 @@ struct prkdf_object_s
   /* The key's usage flags. */
   keyusage_flags_t usageflags;
 
-  /* The keyReference and a flag telling whether it is valid. */ 
+  /* The keyReference and a flag telling whether it is valid. */
   unsigned long key_reference;
   int key_reference_valid;
 
@@ -177,10 +204,10 @@ typedef struct prkdf_object_s *prkdf_object_t;
    processing by us. To keep memory management, simple we use a linked
    list of items; i.e. one such object represents one authentication
    object and the list the entire AOKDF. */
-struct aodf_object_s 
+struct aodf_object_s
 {
   /* Link to next item when used in a linked list. */
-  struct aodf_object_s *next; 
+  struct aodf_object_s *next;
 
   /* Length and allocated buffer with the Id of this object. */
   size_t objidlen;
@@ -192,7 +219,7 @@ struct aodf_object_s
   unsigned char *authid;
 
   /* The PIN Flags. */
-  struct 
+  struct
   {
     unsigned int case_sensitive: 1;
     unsigned int local: 1;
@@ -221,7 +248,7 @@ struct aodf_object_s
   unsigned long max_length;
   int max_length_valid;
 
-  /* The pinReference and a flag telling whether it is valid. */ 
+  /* The pinReference and a flag telling whether it is valid. */
   unsigned long pin_reference;
   int pin_reference_valid;
 
@@ -246,7 +273,7 @@ typedef struct aodf_object_s *aodf_object_t;
 
 
 /* Context local to this application. */
-struct app_local_s 
+struct app_local_s
 {
   /* The home DF. Note, that we don't yet support a multilevel
      hierachy.  Thus we assume this is directly below the MF.  */
@@ -255,6 +282,9 @@ struct app_local_s
   /* The type of the card. */
   card_type_t card_type;
 
+  /* Flag indicating whether we may use direct path selection. */
+  int direct_path_selection;
+
   /* Structure with the EFIDs of the objects described in the ODF
      file. */
   struct
@@ -268,7 +298,11 @@ struct app_local_s
     unsigned short useful_certificates;
     unsigned short data_objects;
     unsigned short auth_objects;
-  } odf;  
+  } odf;
+
+  /* The PKCS#15 serialnumber from EF(TokeiNFo) or NULL.  Malloced. */
+  unsigned char *serialno;
+  size_t serialnolen;
 
   /* Information on all certificates. */
   cdf_object_t certificate_info;
@@ -357,6 +391,7 @@ do_deinit (app_t app)
       release_cdflist (app->app_local->useful_certificate_info);
       release_prkdflist (app->app_local->private_key_info);
       release_aodflist (app->app_local->auth_object_info);
+      xfree (app->app_local->serialno);
       xfree (app->app_local);
       app->app_local = NULL;
     }
@@ -368,7 +403,7 @@ do_deinit (app_t app)
    desctription of the EF to be used with error messages.  On success
    BUFFER and BUFLEN contain the entire content of the EF.  The caller
    must free BUFFER only on success. */
-static gpg_error_t 
+static gpg_error_t
 select_and_read_binary (int slot, unsigned short efid, const char *efid_desc,
                         unsigned char **buffer, size_t *buflen)
 {
@@ -392,34 +427,52 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc,
 }
 
 
-/* This function calls select file to read a file suing a complete
-   path which may or may not start at the master file (MF). */ 
+/* This function calls select file to read a file using a complete
+   path which may or may not start at the master file (MF). */
 static gpg_error_t
 select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
 {
   gpg_error_t err;
   int i, j;
 
-  /* FIXME: Need code to remember the last PATH so that we can decide
-     what select commands to send in case the path does not start off
-     with 3F00.  We might also want to use direct path selection if
-     supported by the card. */
+  if (!pathlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
   if (pathlen && *path != 0x3f00 )
     log_debug ("WARNING: relative path selection not yet implemented\n");
 
-  for (i=0; i < pathlen; i++)
+  if (app->app_local->direct_path_selection)
     {
-      err = iso7816_select_file (app->slot, path[i],
-                                 !(i+1 == pathlen), NULL, NULL);
+      err = iso7816_select_path (app->slot, path+1, pathlen-1, NULL, NULL);
       if (err)
         {
-          log_error ("error selecting part %d from path ", i);
+          log_error ("error selecting path ");
           for (j=0; j < pathlen; j++)
             log_printf ("%04hX", path[j]);
           log_printf (": %s\n", gpg_strerror (err));
           return err;
         }
     }
+  else
+    {
+      /* FIXME: Need code to remember the last PATH so that we can decide
+         what select commands to send in case the path does not start off
+         with 3F00.  We might also want to use direct path selection if
+         supported by the card. */
+      for (i=0; i < pathlen; i++)
+        {
+          err = iso7816_select_file (app->slot, path[i],
+                                     !(i+1 == pathlen), NULL, NULL);
+          if (err)
+            {
+              log_error ("error selecting part %d from path ", i);
+              for (j=0; j < pathlen; j++)
+                log_printf ("%04hX", path[j]);
+              log_printf (": %s\n", gpg_strerror (err));
+              return err;
+            }
+        }
+    }
   return 0;
 }
 
@@ -441,7 +494,8 @@ parse_certid (app_t app, const char *certid,
   *r_objidlen = 0;
 
   if (app->app_local->home_df)
-    sprintf (tmpbuf, "P15-%04hX.", (app->app_local->home_df & 0xffff));
+    snprintf (tmpbuf, sizeof tmpbuf,
+              "P15-%04X.", (unsigned int)(app->app_local->home_df & 0xffff));
   else
     strcpy (tmpbuf, "P15.");
   if (strncmp (certid, tmpbuf, strlen (tmpbuf)) )
@@ -465,7 +519,7 @@ parse_certid (app_t app, const char *certid,
   objidlen /= 2;
   objid = xtrymalloc (objidlen);
   if (!objid)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   for (s=certid, i=0; i < objidlen; i++, s+=2)
     objid[i] = xtoi_2 (s);
   *r_objid = objid;
@@ -541,11 +595,11 @@ prkdf_object_from_keyidstr (app_t app, const char *keyidstr,
    Example of such a file:
 
    A0 06 30 04 04 02 60 34  = Private Keys
-   A4 06 30 04 04 02 60 35  = Certificates 
+   A4 06 30 04 04 02 60 35  = Certificates
    A5 06 30 04 04 02 60 36  = TrustedCertificates
    A7 06 30 04 04 02 60 37  = DataObjects
    A8 06 30 04 04 02 60 38  = AuthObjects
-    
+
    These are all PathOrObjects using the path CHOICE element.  The
    paths are octet strings of length 2.  Using this Path CHOICE
    element is recommended, so we only implement that for now.
@@ -578,14 +632,15 @@ read_ef_odf (app_t app, unsigned short odf_fid)
         {
           offset = 6;
         }
-      else if ( buflen >= 12 
+      else if ( buflen >= 12
                 && (p[0] & 0xf0) == 0xA0
-                && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00\x50\x15", 9)
-                && app->app_local->home_df == 0x5015 )
+                && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7)
+                && app->app_local->home_df == ((p[8]<<8)|p[9]) )
         {
-          /* This format using a full path is used by a self-created
-             test card of mine.  I have not checked whether this is
-             legal.  We assume a home DF of 0x5015 here. */
+          /* We only allow a full path if all files are at the same
+             level and below the home directory.  The extend this we
+             would need to make use of new data type capable of
+             keeping a full path. */
           offset = 10;
         }
       else
@@ -624,7 +679,7 @@ read_ef_odf (app_t app, unsigned short odf_fid)
         case 6: app->app_local->odf.useful_certificates = value; break;
         case 7: app->app_local->odf.data_objects = value; break;
         case 8: app->app_local->odf.auth_objects = value; break;
-        default: 
+        default:
           log_error ("unknown object type %d in ODF ignored\n", (p[0]&0x0f));
         }
       offset += 2;
@@ -636,14 +691,15 @@ read_ef_odf (app_t app, unsigned short odf_fid)
     }
 
   if (buflen)
-    log_info ("warning: %u bytes of garbage detected at end of ODF\n", buflen);
+    log_info ("warning: %u bytes of garbage detected at end of ODF\n",
+              (unsigned int)buflen);
 
   xfree (buffer);
   return 0;
 }
 
 
-/* Parse the BIT STRING with the keyUsageFlags from teh
+/* Parse the BIT STRING with the keyUsageFlags from the
    CommonKeyAttributes. */
 static gpg_error_t
 parse_keyusage_flags (const unsigned char *der, size_t derlen,
@@ -655,7 +711,7 @@ parse_keyusage_flags (const unsigned char *der, size_t derlen,
   memset (usageflags, 0, sizeof *usageflags);
   if (!derlen)
     return gpg_error (GPG_ERR_INV_OBJ);
-          
+
   unused = *der++; derlen--;
   if ((!derlen && unused) || unused/8 > derlen)
     return gpg_error (GPG_ERR_ENCODING_PROBLEM);
@@ -664,28 +720,28 @@ parse_keyusage_flags (const unsigned char *der, size_t derlen,
   mask = 0;
   for (i=1; unused; i <<= 1, unused--)
     mask |= i;
-  
+
   /* First octet */
   if (derlen)
     {
       bits = *der++; derlen--;
       if (full)
         full--;
-      else 
+      else
         {
           bits &= ~mask;
-          mask = 0; 
+          mask = 0;
         }
     }
   else
     bits = 0;
   if ((bits & 0x80)) usageflags->encrypt = 1;
   if ((bits & 0x40)) usageflags->decrypt = 1;
-  if ((bits & 0x20)) usageflags->sign = 1;  
+  if ((bits & 0x20)) usageflags->sign = 1;
   if ((bits & 0x10)) usageflags->sign_recover = 1;
   if ((bits & 0x08)) usageflags->wrap = 1;
-  if ((bits & 0x04)) usageflags->unwrap = 1; 
-  if ((bits & 0x02)) usageflags->verify = 1;  
+  if ((bits & 0x04)) usageflags->unwrap = 1;
+  if ((bits & 0x02)) usageflags->verify = 1;
   if ((bits & 0x01)) usageflags->verify_recover = 1;
 
   /* Second octet. */
@@ -694,10 +750,10 @@ parse_keyusage_flags (const unsigned char *der, size_t derlen,
       bits = *der++; derlen--;
       if (full)
         full--;
-      else 
+      else
         {
           bits &= ~mask;
-          mask = 0; 
+          mask = 0;
         }
     }
   else
@@ -768,14 +824,14 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
   int class, tag, constructed, ndef;
   prkdf_object_t prkdflist = NULL;
   int i;
-  
+
   if (!fid)
     return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */
-  
+
   err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen);
   if (err)
     return err;
-  
+
   p = buffer;
   n = buflen;
 
@@ -940,7 +996,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
             /* Skip the native element. */
             ppp += objlen;
             nnn -= objlen;
-            
+
             err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
                                     &ndef, &objlen, &hdrlen);
             if (gpg_err_code (err) == GPG_ERR_EOF)
@@ -955,7 +1011,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
             /* Skip the accessFlags. */
             ppp += objlen;
             nnn -= objlen;
-            
+
             err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
                                     &ndef, &objlen, &hdrlen);
             if (gpg_err_code (err) == GPG_ERR_EOF)
@@ -971,7 +1027,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
             for (ul=0; objlen; objlen--)
               {
                 ul <<= 8;
-                ul |= (*ppp++) & 0xff; 
+                ul |= (*ppp++) & 0xff;
                 nnn--;
             }
             key_reference = ul;
@@ -995,7 +1051,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
         {
           pp += objlen;
           nn -= objlen;
-      
+
           where = __LINE__;
           err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
                                   &ndef, &objlen, &hdrlen);
@@ -1069,19 +1125,19 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
           goto parse_error;
         }
       /* Create a new PrKDF list item. */
-      prkdf = xtrycalloc (1, (sizeof *prkdf 
+      prkdf = xtrycalloc (1, (sizeof *prkdf
                               - sizeof(unsigned short)
                               + objlen/2 * sizeof(unsigned short)));
       if (!prkdf)
         {
-          err = gpg_error_from_errno (errno);
+          err = gpg_error_from_syserror ();
           goto leave;
         }
       prkdf->objidlen = objidlen;
       prkdf->objid = xtrymalloc (objidlen);
       if (!prkdf->objid)
         {
-          err = gpg_error_from_errno (errno);
+          err = gpg_error_from_syserror ();
           xfree (prkdf);
           goto leave;
         }
@@ -1092,7 +1148,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
           prkdf->authid = xtrymalloc (authidlen);
           if (!prkdf->authid)
             {
-              err = gpg_error_from_errno (errno);
+              err = gpg_error_from_syserror ();
               xfree (prkdf->objid);
               xfree (prkdf);
               goto leave;
@@ -1120,15 +1176,15 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
             err = gpg_error (GPG_ERR_INV_OBJ);
           if (err)
             goto parse_error;
-          
+
           for (ul=0; objlen; objlen--)
             {
               ul <<= 8;
-              ul |= (*pp++) & 0xff; 
+              ul |= (*pp++) & 0xff;
               nn--;
             }
           prkdf->off = ul;
-          
+
           where = __LINE__;
           err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
                                   &ndef, &objlen, &hdrlen);
@@ -1137,11 +1193,11 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
             err = gpg_error (GPG_ERR_INV_OBJ);
           if (err)
             goto parse_error;
-          
+
           for (ul=0; objlen; objlen--)
             {
               ul <<= 8;
-              ul |= (*pp++) & 0xff; 
+              ul |= (*pp++) & 0xff;
               nn--;
             }
           prkdf->len = ul;
@@ -1180,7 +1236,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
       if (prkdf->usageflags.non_repudiation)
         log_printf ("%snon_repudiation", s), s = ",";
       log_printf ("\n");
-      
+
       /* Put it into the list. */
       prkdf->next = prkdflist;
       prkdflist = prkdf;
@@ -1224,14 +1280,14 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
   int class, tag, constructed, ndef;
   cdf_object_t cdflist = NULL;
   int i;
-  
+
   if (!fid)
     return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */
-  
+
   err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen);
   if (err)
     return err;
-  
+
   p = buffer;
   n = buflen;
 
@@ -1248,7 +1304,7 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
       unsigned long ul;
       const unsigned char *objid;
       size_t objidlen;
-      
+
       err = parse_ber_header (&p, &n, &class, &tag, &constructed,
                               &ndef, &objlen, &hdrlen);
       if (!err && (objlen > n || tag != TAG_SEQUENCE))
@@ -1355,19 +1411,19 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
           goto parse_error;
         }
       /* Create a new CDF list item. */
-      cdf = xtrycalloc (1, (sizeof *cdf 
+      cdf = xtrycalloc (1, (sizeof *cdf
                             - sizeof(unsigned short)
                             + objlen/2 * sizeof(unsigned short)));
       if (!cdf)
         {
-          err = gpg_error_from_errno (errno);
+          err = gpg_error_from_syserror ();
           goto leave;
         }
       cdf->objidlen = objidlen;
       cdf->objid = xtrymalloc (objidlen);
       if (!cdf->objid)
         {
-          err = gpg_error_from_errno (errno);
+          err = gpg_error_from_syserror ();
           xfree (cdf);
           goto leave;
         }
@@ -1389,15 +1445,15 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
             err = gpg_error (GPG_ERR_INV_OBJ);
           if (err)
             goto parse_error;
-          
+
           for (ul=0; objlen; objlen--)
             {
               ul <<= 8;
-              ul |= (*pp++) & 0xff; 
+              ul |= (*pp++) & 0xff;
               nn--;
             }
           cdf->off = ul;
-          
+
           where = __LINE__;
           err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
                                   &ndef, &objlen, &hdrlen);
@@ -1406,11 +1462,11 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
             err = gpg_error (GPG_ERR_INV_OBJ);
           if (err)
             goto parse_error;
-          
+
           for (ul=0; objlen; objlen--)
             {
               ul <<= 8;
-              ul |= (*pp++) & 0xff; 
+              ul |= (*pp++) & 0xff;
               nn--;
             }
           cdf->len = ul;
@@ -1425,7 +1481,7 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
       if (cdf->have_off)
         log_printf ("[%lu/%lu]", cdf->off, cdf->len);
       log_printf ("\n");
-      
+
       /* Put it into the list. */
       cdf->next = cdflist;
       cdflist = cdf;
@@ -1497,14 +1553,14 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
   int class, tag, constructed, ndef;
   aodf_object_t aodflist = NULL;
   int i;
-  
+
   if (!fid)
     return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */
-  
+
   err = select_and_read_binary (app->slot, fid, "AODF", &buffer, &buflen);
   if (err)
     return err;
-  
+
   p = buffer;
   n = buflen;
 
@@ -1694,7 +1750,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
       {
         unsigned int bits, mask;
         int unused, full;
-        
+
         unused = *pp++; nn--; objlen--;
         if ((!objlen && unused) || unused/8 > objlen)
           {
@@ -1706,7 +1762,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
         mask = 0;
         for (i=1; unused; i <<= 1, unused--)
           mask |= i;
-      
+
         /* The first octet */
         bits = 0;
         if (objlen)
@@ -1714,27 +1770,27 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
             bits = *pp++; nn--; objlen--;
             if (full)
               full--;
-            else 
+            else
               {
                 bits &= ~mask;
-                mask = 0; 
+                mask = 0;
               }
           }
         if ((bits & 0x80)) /* ASN.1 bit 0. */
           aodf->pinflags.case_sensitive = 1;
         if ((bits & 0x40)) /* ASN.1 bit 1. */
           aodf->pinflags.local = 1;
-        if ((bits & 0x20)) 
+        if ((bits & 0x20))
           aodf->pinflags.change_disabled = 1;
-        if ((bits & 0x10)) 
+        if ((bits & 0x10))
           aodf->pinflags.unblock_disabled = 1;
-        if ((bits & 0x08)) 
+        if ((bits & 0x08))
           aodf->pinflags.initialized = 1;
-        if ((bits & 0x04)) 
+        if ((bits & 0x04))
           aodf->pinflags.needs_padding = 1;
-        if ((bits & 0x02)) 
+        if ((bits & 0x02))
           aodf->pinflags.unblocking_pin = 1;
-        if ((bits & 0x01)) 
+        if ((bits & 0x01))
           aodf->pinflags.so_pin = 1;
         /* The second octet. */
         bits = 0;
@@ -1743,19 +1799,19 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
             bits = *pp++; nn--; objlen--;
             if (full)
               full--;
-            else 
+            else
               {
                 bits &= ~mask;
-                mask = 0; 
+                mask = 0;
               }
           }
-        if ((bits & 0x80)) 
+        if ((bits & 0x80))
           aodf->pinflags.disable_allowed = 1;
-        if ((bits & 0x40)) 
+        if ((bits & 0x40))
           aodf->pinflags.integrity_protected = 1;
-        if ((bits & 0x20)) 
+        if ((bits & 0x20))
           aodf->pinflags.confidentiality_protected = 1;
-        if ((bits & 0x10)) 
+        if ((bits & 0x10))
           aodf->pinflags.exchange_ref_data = 1;
         /* Skip remaining bits. */
         pp += objlen;
@@ -1778,7 +1834,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
       for (ul=0; objlen; objlen--)
         {
           ul <<= 8;
-          ul |= (*pp++) & 0xff; 
+          ul |= (*pp++) & 0xff;
           nn--;
         }
       aodf->pintype = ul;
@@ -1798,7 +1854,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
       for (ul=0; objlen; objlen--)
         {
           ul <<= 8;
-          ul |= (*pp++) & 0xff; 
+          ul |= (*pp++) & 0xff;
           nn--;
         }
       aodf->min_length = ul;
@@ -1818,7 +1874,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
       for (ul=0; objlen; objlen--)
         {
           ul <<= 8;
-          ul |= (*pp++) & 0xff; 
+          ul |= (*pp++) & 0xff;
           nn--;
         }
       aodf->stored_length = ul;
@@ -1843,12 +1899,12 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
           for (ul=0; objlen; objlen--)
             {
               ul <<= 8;
-              ul |= (*pp++) & 0xff; 
+              ul |= (*pp++) & 0xff;
               nn--;
             }
           aodf->max_length = ul;
           aodf->max_length_valid = 1;
-          
+
           where = __LINE__;
           err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
                                   &ndef, &objlen, &hdrlen);
@@ -1871,12 +1927,12 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
           for (ul=0; objlen; objlen--)
             {
               ul <<= 8;
-              ul |= (*pp++) & 0xff; 
+              ul |= (*pp++) & 0xff;
               nn--;
             }
           aodf->pin_reference = ul;
           aodf->pin_reference_valid = 1;
-          
+
           where = __LINE__;
           err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
                                   &ndef, &objlen, &hdrlen);
@@ -1898,7 +1954,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
             }
           aodf->pad_char = *pp++; nn--;
           aodf->pad_char_valid = 1;
-          
+
           where = __LINE__;
           err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
                                   &ndef, &objlen, &hdrlen);
@@ -1915,7 +1971,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
         {
           pp += objlen;
           nn -= objlen;
-          
+
           where = __LINE__;
           err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
                                   &ndef, &objlen, &hdrlen);
@@ -1932,7 +1988,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
         {
           const unsigned char *ppp = pp;
           size_t nnn = objlen;
-          
+
           pp += objlen;
           nn -= objlen;
 
@@ -1959,7 +2015,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
             goto no_core;
           for (i=0; i < aodf->pathlen; i++, ppp += 2, nnn -= 2)
             aodf->path[i] = ((ppp[0] << 8) | ppp[1]);
-          
+
           if (nnn)
             {
               /* An index and length follows. */
@@ -1972,15 +2028,15 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
                 err = gpg_error (GPG_ERR_INV_OBJ);
               if (err)
                 goto parse_error;
-              
+
               for (ul=0; objlen; objlen--)
                 {
                   ul <<= 8;
-                  ul |= (*ppp++) & 0xff; 
+                  ul |= (*ppp++) & 0xff;
                   nnn--;
                 }
               aodf->off = ul;
-              
+
               where = __LINE__;
               err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
                                       &ndef, &objlen, &hdrlen);
@@ -1989,17 +2045,17 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
                 err = gpg_error (GPG_ERR_INV_OBJ);
               if (err)
                 goto parse_error;
-              
+
               for (ul=0; objlen; objlen--)
                 {
                   ul <<= 8;
-                  ul |= (*ppp++) & 0xff; 
+                  ul |= (*ppp++) & 0xff;
                   nnn--;
                 }
               aodf->len = ul;
             }
         }
-      
+
       /* Igonore further objects which might be there due to future
          extensions of pkcs#15. */
 
@@ -2071,7 +2127,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
             log_printf ("[%lu/%lu]", aodf->off, aodf->len);
         }
       log_printf ("\n");
-      
+
       /* Put it into the list. */
       aodf->next = aodflist;
       aodflist = aodf;
@@ -2079,7 +2135,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
       continue; /* Ready. */
 
     no_core:
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       release_aodf_object (aodf);
       goto leave;
 
@@ -2103,7 +2159,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
 
 
 
-/* Read and parse the EF(TokenInfo). 
+/* Read and parse the EF(TokenInfo).
 
 TokenInfo ::= SEQUENCE {
     version            INTEGER {v1(0)} (v1,...),
@@ -2119,11 +2175,11 @@ TokenInfo ::= SEQUENCE {
     holderId           [4] Label OPTIONAL,
     lastUpdate         [5] LastUpdate OPTIONAL,
     preferredLanguage  PrintableString OPTIONAL -- In accordance with
-    -- IETF RFC 1766 
+    -- IETF RFC 1766
 } (CONSTRAINED BY { -- Each AlgorithmInfo.reference value must be unique --})
 
 TokenFlags ::= BIT STRING {
-    readonly           (0),
+    readOnly           (0),
     loginRequired      (1),
     prnGeneration      (2),
     eidCompliant       (3)
@@ -2152,12 +2208,81 @@ TokenFlags ::= BIT STRING {
 
 
  */
-/* static gpg_error_t */
-/* read_ef_tokeninfo (app_t app) */
-/* { */
-/*   unsigned short efid = 0x5032; */
-/*   return 0; */
-/* } */
+static gpg_error_t
+read_ef_tokeninfo (app_t app)
+{
+  gpg_error_t err;
+  unsigned char *buffer = NULL;
+  size_t buflen;
+  const unsigned char *p;
+  size_t n, objlen, hdrlen;
+  int class, tag, constructed, ndef;
+  unsigned long ul;
+
+  err = select_and_read_binary (app->slot, 0x5032, "TokenInfo",
+                                &buffer, &buflen);
+  if (err)
+    return err;
+
+  p = buffer;
+  n = buflen;
+
+  err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > n || tag != TAG_SEQUENCE))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    {
+      log_error ("error parsing TokenInfo: %s\n", gpg_strerror (err));
+      goto leave;
+    }
+
+  n = objlen;
+
+  /* Version.  */
+  err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > n || tag != TAG_INTEGER))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    goto leave;
+
+  for (ul=0; objlen; objlen--)
+    {
+      ul <<= 8;
+      ul |= (*p++) & 0xff;
+      n--;
+    }
+  if (ul)
+    {
+      log_error ("invalid version %lu in TokenInfo\n", ul);
+      err = gpg_error (GPG_ERR_INV_OBJ);
+      goto leave;
+    }
+
+  /* serialNumber.  */
+  err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > n || tag != TAG_OCTET_STRING || !objlen))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    goto leave;
+
+  xfree (app->app_local->serialno);
+  app->app_local->serialno = xtrymalloc (objlen);
+  if (!app->app_local->serialno)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  memcpy (app->app_local->serialno, p, objlen);
+  app->app_local->serialnolen = objlen;
+  log_printhex ("Serialnumber from EF(TokenInfo) is:", p, objlen);
+
+ leave:
+  xfree (buffer);
+  return err;
+}
 
 
 /* Get all the basic information from the pkcs#15 card, check the
@@ -2168,11 +2293,25 @@ read_p15_info (app_t app)
 {
   gpg_error_t err;
 
-  /* Fixme: We might need to read the tokeninfo to get a non-standard
-     ODF FID.  */
+  if (!read_ef_tokeninfo (app))
+    {
+      /* If we don't have a serial number yet but the TokenInfo provides
+         one, use that. */
+      if (!app->serialno && app->app_local->serialno)
+        {
+          app->serialno = app->app_local->serialno;
+          app->serialnolen = app->app_local->serialnolen;
+          app->app_local->serialno = NULL;
+          app->app_local->serialnolen = 0;
+          err = app_munge_serialno (app);
+          if (err)
+            return err;
+        }
+    }
 
   /* Read the ODF so that we know the location of all directory
      files. */
+  /* Fixme: We might need to get a non-standard ODF FID from TokenInfo. */
   err = read_ef_odf (app, 0x5031);
   if (err)
     return err;
@@ -2181,13 +2320,13 @@ read_p15_info (app_t app)
   assert (!app->app_local->certificate_info);
   assert (!app->app_local->trusted_certificate_info);
   assert (!app->app_local->useful_certificate_info);
-  err = read_ef_cdf (app, app->app_local->odf.certificates, 
+  err = read_ef_cdf (app, app->app_local->odf.certificates,
                      &app->app_local->certificate_info);
   if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
-    err = read_ef_cdf (app, app->app_local->odf.trusted_certificates, 
+    err = read_ef_cdf (app, app->app_local->odf.trusted_certificates,
                        &app->app_local->trusted_certificate_info);
   if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
-    err = read_ef_cdf (app, app->app_local->odf.useful_certificates, 
+    err = read_ef_cdf (app, app->app_local->odf.useful_certificates,
                        &app->app_local->useful_certificate_info);
   if (gpg_err_code (err) == GPG_ERR_NO_DATA)
     err = 0;
@@ -2225,27 +2364,23 @@ send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
   for (; certinfo; certinfo = certinfo->next)
     {
       char *buf, *p;
-      int i;
 
       buf = xtrymalloc (9 + certinfo->objidlen*2 + 1);
       if (!buf)
-        return gpg_error_from_errno (errno);
+        return gpg_error_from_syserror ();
       p = stpcpy (buf, "P15");
       if (app->app_local->home_df)
         {
-          sprintf (p, "-%04hX", (app->app_local->home_df & 0xffff));
+          snprintf (p, 6, "-%04X",
+                    (unsigned int)(app->app_local->home_df & 0xffff));
           p += 5;
         }
       p = stpcpy (p, ".");
-      for (i=0; i < certinfo->objidlen; i++)
-        {
-          sprintf (p, "%02X", certinfo->objid[i]);
-          p += 2;
-        }
+      bin2hex (certinfo->objid, certinfo->objidlen, p);
 
       send_status_info (ctrl, "CERTINFO",
                         certtype, strlen (certtype),
-                        buf, strlen (buf), 
+                        buf, strlen (buf),
                         NULL, (size_t)0);
       xfree (buf);
     }
@@ -2272,7 +2407,7 @@ keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr)
      a matching certificate and extract the key from there. */
 
   /* Look for a matching certificate. A certificate matches if the Id
-     matches the obne of the private key info. */
+     matches the one of the private key info. */
   for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next)
     if (cdf->objidlen == prkdf->objidlen
         && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen))
@@ -2310,7 +2445,7 @@ keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr)
 
 /* Helper to do_learn_status: Send information about all known
    keypairs back.  FIXME: much code duplication from
-   send_sertinfo(). */
+   send_certinfo(). */
 static gpg_error_t
 send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo)
 {
@@ -2320,23 +2455,20 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo)
     {
       char gripstr[40+1];
       char *buf, *p;
-      int i, j;
+      int j;
 
       buf = xtrymalloc (9 + keyinfo->objidlen*2 + 1);
       if (!buf)
-        return gpg_error_from_errno (errno);
+        return gpg_error_from_syserror ();
       p = stpcpy (buf, "P15");
       if (app->app_local->home_df)
         {
-          sprintf (p, "-%04hX", (app->app_local->home_df & 0xffff));
+          snprintf (p, 6, "-%04hX",
+                    (unsigned int)(app->app_local->home_df & 0xffff));
           p += 5;
         }
       p = stpcpy (p, ".");
-      for (i=0; i < keyinfo->objidlen; i++)
-        {
-          sprintf (p, "%02X", keyinfo->objid[i]);
-          p += 2;
-        }
+      bin2hex (keyinfo->objid, keyinfo->objidlen, p);
 
       err = keygripstr_from_prkdf (app, keyinfo, gripstr);
       if (err)
@@ -2351,7 +2483,7 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo)
           assert (strlen (gripstr) == 40);
           send_status_info (ctrl, "KEYPAIRINFO",
                             gripstr, 40,
-                            buf, strlen (buf), 
+                            buf, strlen (buf),
                             NULL, (size_t)0);
         }
       xfree (buf);
@@ -2362,18 +2494,24 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo)
 
 
 /* This is the handler for the LEARN command.  */
-static int  /* FIXME: change this to gpg_error_t */
-do_learn_status (app_t app, ctrl_t ctrl)
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
 {
   gpg_error_t err;
 
-  err = send_certinfo (app, ctrl, "100", app->app_local->certificate_info);
-  if (!err)
-    err = send_certinfo (app, ctrl, "101",
-                         app->app_local->trusted_certificate_info);
-  if (!err)
-    err = send_certinfo (app, ctrl, "102",
-                         app->app_local->useful_certificate_info);
+  if ((flags & 1))
+    err = 0;
+  else
+    {
+      err = send_certinfo (app, ctrl, "100", app->app_local->certificate_info);
+      if (!err)
+        err = send_certinfo (app, ctrl, "101",
+                             app->app_local->trusted_certificate_info);
+      if (!err)
+        err = send_certinfo (app, ctrl, "102",
+                             app->app_local->useful_certificate_info);
+    }
+
   if (!err)
     err = send_keypairinfo (app, ctrl, app->app_local->private_key_info);
 
@@ -2405,7 +2543,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
     {
       *r_cert = xtrymalloc (cdf->imagelen);
       if (!*r_cert)
-        return gpg_error_from_errno (errno);
+        return gpg_error_from_syserror ();
       memcpy (*r_cert, cdf->image, cdf->imagelen);
       *r_certlen = cdf->imagelen;
       return 0;
@@ -2429,7 +2567,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
       log_printf (": %s\n", gpg_strerror (err));
       goto leave;
     }
-  
+
   /* Check whether this is really a certificate.  */
   p = buffer;
   n = buflen;
@@ -2454,7 +2592,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
                           &ndef, &objlen, &hdrlen);
   if (err)
     goto leave;
-  
+
   if (!rootca
       && class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
     {
@@ -2471,7 +2609,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
       save_p = p;
       err = parse_ber_header (&p, &n, &class, &tag, &constructed,
                               &ndef, &objlen, &hdrlen);
-      if (err) 
+      if (err)
         goto leave;
       if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
         {
@@ -2501,15 +2639,14 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
 }
 
 
-
 /* Handler for the READCERT command.
 
    Read the certificate with id CERTID (as returned by learn_status in
    the CERTINFO status lines) and return it in the freshly allocated
    buffer to be stored at R_CERT and its length at R_CERTLEN.  A error
    code will be returned on failure and R_CERT and R_CERTLEN will be
-   set to NULL/0. */
-static int /* FIXME: change this to gpg_error_t */
+   set to (NULL,0). */
+static gpg_error_t
 do_readcert (app_t app, const char *certid,
              unsigned char **r_cert, size_t *r_certlen)
 {
@@ -2520,21 +2657,199 @@ do_readcert (app_t app, const char *certid,
   *r_certlen = 0;
   err = cdf_object_from_certid (app, certid, &cdf);
   if (!err)
-    err =readcert_by_cdf (app, cdf, r_cert, r_certlen);
+    err = readcert_by_cdf (app, cdf, r_cert, r_certlen);
   return err;
 }
 
 
 
-/* Handler for the PKSIGN command. 
+/* 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;
+
+  if (!strcmp (name, "$AUTHKEYID"))
+    {
+      char *buf, *p;
+      prkdf_object_t prkdf;
+
+      /* We return the ID of the first private keycapable of
+         signing. */
+      for (prkdf = app->app_local->private_key_info; prkdf;
+           prkdf = prkdf->next)
+        if (prkdf->usageflags.sign)
+          break;
+      if (prkdf)
+        {
+          buf = xtrymalloc (9 + prkdf->objidlen*2 + 1);
+          if (!buf)
+            return gpg_error_from_syserror ();
+          p = stpcpy (buf, "P15");
+          if (app->app_local->home_df)
+            {
+              snprintf (p, 6, "-%04hX",
+                        (unsigned int)(app->app_local->home_df & 0xffff));
+              p += 5;
+            }
+          p = stpcpy (p, ".");
+          bin2hex (prkdf->objid, prkdf->objidlen, p);
+
+          send_status_info (ctrl, name, buf, strlen (buf), NULL, 0);
+          xfree (buf);
+          return 0;
+        }
+    }
+  else if (!strcmp (name, "$DISPSERIALNO"))
+    {
+      /* For certain cards we return special IDs.  There is no
+         general rule for it so we need to decide case by case. */
+      if (app->app_local->card_type == CARD_TYPE_BELPIC)
+        {
+          /* The eID card has a card number printed on the front matter
+             which seems to be a good indication. */
+          unsigned char *buffer;
+          const unsigned char *p;
+          size_t buflen, n;
+          unsigned short path[] = { 0x3F00, 0xDF01, 0x4031 };
+
+          err = select_ef_by_path (app, path, DIM(path) );
+          if (!err)
+            err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
+          if (err)
+            {
+              log_error ("error accessing EF(ID): %s\n", gpg_strerror (err));
+              return err;
+            }
+
+          p = find_tlv (buffer, buflen, 1, &n);
+          if (p && n == 12)
+            {
+              char tmp[12+2+1];
+              memcpy (tmp, p, 3);
+              tmp[3] = '-';
+              memcpy (tmp+4, p+3, 7);
+              tmp[11] = '-';
+              memcpy (tmp+12, p+10, 2);
+              tmp[14] = 0;
+              send_status_info (ctrl, name, tmp, strlen (tmp), NULL, 0);
+              xfree (buffer);
+              return 0;
+            }
+          xfree (buffer);
+        }
+
+    }
+  return gpg_error (GPG_ERR_INV_NAME);
+}
+
+
+
+
+/* Micardo cards require special treatment. This is a helper for the
+   crypto functions to manage the security environment.  We expect that
+   the key file has already been selected. FID is the one of the
+   selected key. */
+static gpg_error_t
+micardo_mse (app_t app, unsigned short fid)
+{
+  gpg_error_t err;
+  int recno;
+  unsigned short refdata = 0;
+  int se_num;
+  unsigned char msebuf[10];
+
+  /* Read the KeyD file containing extra information on keys. */
+  err = iso7816_select_file (app->slot, 0x0013, 0, NULL, NULL);
+  if (err)
+    {
+      log_error ("error reading EF_keyD: %s\n", gpg_strerror (err));
+      return err;
+    }
+
+  for (recno = 1, se_num = -1; ; recno++)
+    {
+      unsigned char *buffer;
+      size_t buflen;
+      size_t n, nn;
+      const unsigned char *p, *pp;
+
+      err = iso7816_read_record (app->slot, recno, 1, 0, &buffer, &buflen);
+      if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+        break; /* ready */
+      if (err)
+        {
+          log_error ("error reading EF_keyD record: %s\n",
+                     gpg_strerror (err));
+          return err;
+        }
+      log_printhex ("keyD record:", buffer, buflen);
+      p = find_tlv (buffer, buflen, 0x83, &n);
+      if (p && n == 4 && ((p[2]<<8)|p[3]) == fid)
+        {
+          refdata = ((p[0]<<8)|p[1]);
+          /* Locate the SE DO and the there included sec env number. */
+          p = find_tlv (buffer, buflen, 0x7b, &n);
+          if (p && n)
+            {
+              pp = find_tlv (p, n, 0x80, &nn);
+              if (pp && nn == 1)
+                {
+                  se_num = *pp;
+                  xfree (buffer);
+                  break; /* found. */
+                }
+            }
+        }
+      xfree (buffer);
+    }
+  if (se_num == -1)
+    {
+      log_error ("CRT for keyfile %04hX not found\n", fid);
+      return gpg_error (GPG_ERR_NOT_FOUND);
+    }
+
+
+  /* Restore the security environment to SE_NUM if needed */
+  if (se_num)
+    {
+      err = iso7816_manage_security_env (app->slot, 0xf3, se_num, NULL, 0);
+      if (err)
+        {
+          log_error ("restoring SE to %d failed: %s\n",
+                     se_num, gpg_strerror (err));
+          return err;
+        }
+    }
+
+  /* Set the DST reference data. */
+  msebuf[0] = 0x83;
+  msebuf[1] = 0x03;
+  msebuf[2] = 0x80;
+  msebuf[3] = (refdata >> 8);
+  msebuf[4] = refdata;
+  err = iso7816_manage_security_env (app->slot, 0x41, 0xb6, msebuf, 5);
+  if (err)
+    {
+      log_error ("setting SE to reference file %04hX failed: %s\n",
+                 refdata, gpg_strerror (err));
+      return err;
+    }
+  return 0;
+}
+
+
+
+/* Handler for the PKSIGN command.
 
    Create the signature and return the allocated result in OUTDATA.
    If a PIN is required, the PINCB will be used to ask for the PIN;
    that callback should return the PIN in an allocated buffer and
    store that as the 3rd argument.  */
-static int 
+static gpg_error_t
 do_sign (app_t app, const char *keyidstr, int hashalgo,
-         int (pincb)(void*, const char *, char **),
+         gpg_error_t (*pincb)(void*, const char *, char **),
          void *pincb_arg,
          const void *indata, size_t indatalen,
          unsigned char **outdata, size_t *outdatalen )
@@ -2548,19 +2863,29 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
 
   gpg_error_t err;
   int i;
-  unsigned char data[35];   /* Must be large enough for a SHA-1 digest
-                               + the largest OID prefix above. */
+  unsigned char data[36];   /* Must be large enough for a SHA-1 digest
+                               + the largest OID prefix above and also
+                               fit the 36 bytes of md5sha1.  */
   prkdf_object_t prkdf;    /* The private key object. */
   aodf_object_t aodf;      /* The associated authentication object. */
+  int no_data_padding = 0; /* True if the card want the data without padding.*/
+  int mse_done = 0;        /* Set to true if the MSE has been done. */
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (indatalen != 20 && indatalen != 16 && indatalen != 35)
+  if (indatalen != 20 && indatalen != 16 && indatalen != 35 && indatalen != 36)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
   if (err)
     return err;
+  if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover
+        ||prkdf->usageflags.non_repudiation))
+    {
+      log_error ("key %s may not be used for signing\n", keyidstr);
+      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+    }
+
   if (!prkdf->authid)
     {
       log_error ("no authentication object defined for %s\n", keyidstr);
@@ -2571,7 +2896,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
 
   /* Find the authentication object to this private key object. */
   for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
-    if (aodf->objidlen == prkdf->authidlen 
+    if (aodf->objidlen == prkdf->authidlen
         && !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen))
       break;
   if (!aodf)
@@ -2588,7 +2913,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   if (aodf->pinflags.integrity_protected
       || aodf->pinflags.confidentiality_protected)
     {
-      log_error ("PIN verification requires unsupported protecion method\n");
+      log_error ("PIN verification requires unsupported protection method\n");
       return gpg_error (GPG_ERR_BAD_PIN_METHOD);
     }
   if (!aodf->stored_length && aodf->pinflags.needs_padding)
@@ -2597,6 +2922,48 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       return gpg_error (GPG_ERR_INV_CARD);
     }
 
+  /* Select the key file.  Note that this may change the security
+     environment thus we do it before PIN verification. */
+  err = select_ef_by_path (app, prkdf->path, prkdf->pathlen);
+  if (err)
+    {
+      log_error ("error selecting file for key %s: %s\n",
+                 keyidstr, gpg_strerror (errno));
+      return err;
+    }
+
+
+  /* Due to the fact that the non-repudiation signature on a BELPIC
+     card requires a verify immediately before the DSO we set the
+     MSE before we do the verification.  Other cards might allow to do
+     this also but I don't want to break anything, thus we do it only
+     for the BELPIC card here. */
+  if (app->app_local->card_type == CARD_TYPE_BELPIC)
+    {
+      unsigned char mse[5];
+
+      mse[0] = 4;    /* Length of the template. */
+      mse[1] = 0x80; /* Algorithm reference tag. */
+      if (hashalgo == MD_USER_TLS_MD5SHA1)
+        mse[2] = 0x01; /* Let card do pkcs#1 0xFF padding. */
+      else
+        mse[2] = 0x02; /* RSASSA-PKCS1-v1.5 using SHA1. */
+      mse[3] = 0x84; /* Private key reference tag. */
+      mse[4] = prkdf->key_reference_valid? prkdf->key_reference : 0x82;
+
+      err = iso7816_manage_security_env (app->slot,
+                                         0x41, 0xB6,
+                                         mse, sizeof mse);
+      no_data_padding = 1;
+      mse_done = 1;
+    }
+  if (err)
+    {
+      log_error ("MSE failed: %s\n", gpg_strerror (err));
+      return err;
+    }
+
+
   /* Now that we have all the information available, prepare and run
      the PIN verification.*/
   if (1)
@@ -2605,8 +2972,12 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       size_t pinvaluelen;
       const char *errstr;
       const char *s;
-      
-      err = pincb (pincb_arg, "PIN", &pinvalue);
+
+      if (prkdf->usageflags.non_repudiation
+          && app->app_local->card_type == CARD_TYPE_BELPIC)
+        err = pincb (pincb_arg, "PIN (qualified signature!)", &pinvalue);
+      else
+        err = pincb (pincb_arg, "PIN", &pinvalue);
       if (err)
         {
           log_info ("PIN callback returned error: %s\n", gpg_strerror (err));
@@ -2620,7 +2991,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       if (strlen (pinvalue) < aodf->min_length)
         {
           log_error ("PIN is too short; minimum length is %lu\n",
-                     aodf->min_length);         
+                     aodf->min_length);
           err = gpg_error (GPG_ERR_BAD_PIN);
         }
       else if (aodf->stored_length && strlen (pinvalue) > aodf->stored_length)
@@ -2648,8 +3019,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       switch (aodf->pintype)
         {
         case PIN_TYPE_BCD:
-          errstr = "PIN type BCD is not supported"; 
-          break;
         case PIN_TYPE_ASCII_NUMERIC:
           for (s=pinvalue; digitp (s); s++)
             ;
@@ -2662,7 +3031,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
         case PIN_TYPE_UTF8:
           break;
         case PIN_TYPE_HALF_NIBBLE_BCD:
-          errstr = "PIN type Half-Nibble-BCD is not supported"; 
+          errstr = "PIN type Half-Nibble-BCD is not supported";
           break;
         case PIN_TYPE_ISO9564_1:
           errstr = "PIN type ISO9564-1 is not supported";
@@ -2678,14 +3047,46 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
           return err? err : gpg_error (GPG_ERR_BAD_PIN_METHOD);
         }
 
-      if (aodf->pinflags.needs_padding)
+
+      if (aodf->pintype == PIN_TYPE_BCD )
+        {
+          char *paddedpin;
+          int ndigits;
+
+          for (ndigits=0, s=pinvalue; *s; ndigits++, s++)
+            ;
+          paddedpin = xtrymalloc (aodf->stored_length+1);
+          if (!paddedpin)
+            {
+              err = gpg_error_from_syserror ();
+              xfree (pinvalue);
+              return err;
+            }
+
+          i = 0;
+          paddedpin[i++] = 0x20 | (ndigits & 0x0f);
+          for (s=pinvalue; i < aodf->stored_length && *s && s[1]; s = s+2 )
+            paddedpin[i++] = (((*s - '0') << 4) | ((s[1] - '0') & 0x0f));
+          if (i < aodf->stored_length && *s)
+            paddedpin[i++] = (((*s - '0') << 4)
+                              |((aodf->pad_char_valid?aodf->pad_char:0)&0x0f));
+
+          if (aodf->pinflags.needs_padding)
+            while (i < aodf->stored_length)
+              paddedpin[i++] = aodf->pad_char_valid? aodf->pad_char : 0;
+
+          xfree (pinvalue);
+          pinvalue = paddedpin;
+          pinvaluelen = i;
+        }
+      else if (aodf->pinflags.needs_padding)
         {
           char *paddedpin;
 
           paddedpin = xtrymalloc (aodf->stored_length+1);
           if (!paddedpin)
             {
-              err = gpg_error_from_errno (errno);
+              err = gpg_error_from_syserror ();
               xfree (pinvalue);
               return err;
             }
@@ -2716,7 +3117,14 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     }
 
   /* Prepare the DER object from INDATA. */
-  if (indatalen == 35)
+  if (indatalen == 36)
+    {
+      /* No ASN.1 container used. */
+      if (hashalgo != MD_USER_TLS_MD5SHA1)
+        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+      memcpy (data, indata, indatalen);
+    }
+  else if (indatalen == 35)
     {
       /* Alright, the caller was so kind to send us an already
          prepared DER object.  Check that it is what we want and that
@@ -2726,7 +3134,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       else if (hashalgo == GCRY_MD_RMD160
                && !memcmp (indata, rmd160_prefix, 15))
         ;
-      else 
+      else
         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
       memcpy (data, indata, indatalen);
     }
@@ -2737,91 +3145,208 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
         memcpy (data, sha1_prefix, 15);
       else if (hashalgo == GCRY_MD_RMD160)
         memcpy (data, rmd160_prefix, 15);
-      else 
+      else
         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
       memcpy (data+15, indata, indatalen);
     }
 
   /* Manage security environment needs to be weaked for certain cards. */
-  if (app->app_local->card_type == CARD_TYPE_TCOS)
+  if (mse_done)
+    err = 0;
+  else if (app->app_local->card_type == CARD_TYPE_TCOS)
     {
       /* TCOS creates signatures always using the local key 0.  MSE
          may not be used. */
     }
   else if (app->app_local->card_type == CARD_TYPE_MICARDO)
     {
-      /* Micardo cards are very special in that they need to restore a
-         security environment using a infomration from a special
-         file. */
-      log_error ("WARNING: support for MICARDO cards is not yet available\n");
+      if (!prkdf->pathlen)
+        err = gpg_error (GPG_ERR_BUG);
+      else
+        err = micardo_mse (app, prkdf->path[prkdf->pathlen-1]);
     }
   else if (prkdf->key_reference_valid)
     {
       unsigned char mse[3];
-      
+
       mse[0] = 0x84; /* Select asym. key. */
       mse[1] = 1;
       mse[2] = prkdf->key_reference;
 
-      err = iso7816_manage_security_env (app->slot, 
+      err = iso7816_manage_security_env (app->slot,
                                          0x41, 0xB6,
                                          mse, sizeof mse);
-      if (err)
-        {
-          log_error ("MSE failed: %s\n", gpg_strerror (err));
-          return err;
-        }
+    }
+  if (err)
+    {
+      log_error ("MSE failed: %s\n", gpg_strerror (err));
+      return err;
     }
 
-
-  err = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+  if (hashalgo == MD_USER_TLS_MD5SHA1)
+    err = iso7816_compute_ds (app->slot, 0, data, 36, 0, outdata, outdatalen);
+  else if (no_data_padding)
+    err = iso7816_compute_ds (app->slot, 0, data+15, 20, 0,outdata,outdatalen);
+  else
+    err = iso7816_compute_ds (app->slot, 0, data, 35, 0, outdata, outdatalen);
   return err;
 }
 
 
+/* Handler for the PKAUTH command.
 
+   This is basically the same as the PKSIGN command but we first check
+   that the requested key is suitable for authentication; that is, it
+   must match the criteria used for the attribute $AUTHKEYID.  See
+   do_sign for calling conventions; there is no HASHALGO, though. */
+static gpg_error_t
+do_auth (app_t app, const char *keyidstr,
+         gpg_error_t (*pincb)(void*, const char *, char **),
+         void *pincb_arg,
+         const void *indata, size_t indatalen,
+         unsigned char **outdata, size_t *outdatalen )
+{
+  gpg_error_t err;
+  prkdf_object_t prkdf;
+  int algo;
 
+  if (!keyidstr || !*keyidstr)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+  if (err)
+    return err;
+  if (!prkdf->usageflags.sign)
+    {
+      log_error ("key %s may not be used for authentication\n", keyidstr);
+      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+    }
 
+  algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1;
+  return do_sign (app, keyidstr, algo, pincb, pincb_arg,
+                  indata, indatalen, outdata, outdatalen);
+}
 
 
-/* Select the PKCS#15 application on the card in SLOT.  */
+\f
+/* Assume that EF(DIR) has been selected.  Read its content and figure
+   out the home EF of pkcs#15.  Return that home DF or 0 if not found
+   and the value at the address of BELPIC indicates whether it was
+   found by the belpic aid. */
+static unsigned short
+read_home_df (int slot, int *r_belpic)
+{
+  gpg_error_t err;
+  unsigned char *buffer;
+  const unsigned char *p, *pp;
+  size_t buflen, n, nn;
+  unsigned short result = 0;
+
+  *r_belpic = 0;
+
+  err = iso7816_read_binary (slot, 0, 0, &buffer, &buflen);
+  if (err)
+    {
+      log_error ("error reading EF{DIR}: %s\n", gpg_strerror (err));
+      return 0;
+    }
+
+  /* FIXME: We need to scan all records. */
+  p = find_tlv (buffer, buflen, 0x61, &n);
+  if (p && n)
+    {
+      pp = find_tlv (p, n, 0x4f, &nn);
+      if (pp && ((nn == sizeof pkcs15_aid && !memcmp (pp, pkcs15_aid, nn))
+                 || (*r_belpic = (nn == sizeof pkcs15be_aid
+                                  && !memcmp (pp, pkcs15be_aid, nn)))))
+        {
+          pp = find_tlv (p, n, 0x50, &nn);
+          if (pp) /* fixme: Filter log value? */
+            log_info ("pkcs#15 application label from EF(DIR) is '%.*s'\n",
+                      (int)nn, pp);
+          pp = find_tlv (p, n, 0x51, &nn);
+          if (pp && nn == 4 && *pp == 0x3f && !pp[1])
+            {
+              result = ((pp[2] << 8) | pp[3]);
+              log_info ("pkcs#15 application directory is 0x%04hX\n", result);
+            }
+        }
+    }
+  xfree (buffer);
+  return result;
+}
+
+
+/*
+   Select the PKCS#15 application on the card in SLOT.
+ */
 gpg_error_t
 app_select_p15 (app_t app)
 {
-  static char const aid[] = { 0xA0, 0, 0, 0, 0x63,
-                              0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
   int slot = app->slot;
   int rc;
   unsigned short def_home_df = 0;
   card_type_t card_type = CARD_TYPE_UNKNOWN;
-  
-  rc = iso7816_select_application (slot, aid, sizeof aid);
+  int direct = 0;
+  int is_belpic = 0;
+
+  rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid, 0);
   if (rc)
-    {
+    { /* Not found: Try to locate it from 2F00.  We use direct path
+         selection here because it seems that the Belgian eID card
+         does only allow for that.  Many other cards supports this
+         selection method too.  Note, that we don't use
+         select_application above for the Belgian card - the call
+         works but it seems that it did not switch to the correct DF.
+         Using the 2f02 just works. */
+      unsigned short path[1] = { 0x2f00 };
+
+      rc = iso7816_select_path (app->slot, path, 1, NULL, NULL);
+      if (!rc)
+        {
+          direct = 1;
+          def_home_df = read_home_df (slot, &is_belpic);
+          if (def_home_df)
+            {
+              path[0] = def_home_df;
+              rc = iso7816_select_path (app->slot, path, 1, NULL, NULL);
+            }
+        }
+    }
+  if (rc)
+    { /* Still not found:  Try the default DF. */
       def_home_df = 0x5015;
       rc = iso7816_select_file (slot, def_home_df, 1, NULL, NULL);
     }
   if (!rc)
     {
-      /* We need to know the ATR for tweaking some security operations. */
-      unsigned char *atr;
-      size_t atrlen;
-      int i;
-
-      atr = apdu_get_atr (app->slot, &atrlen);
-      if (!atr)
-        rc = gpg_error (GPG_ERR_INV_CARD);
+      /* Determine the type of the card.  The general case is to look
+         it up from the ATR table.  For the Belgian eID card we know
+         it instantly from the AID. */
+      if (is_belpic)
+        {
+          card_type = CARD_TYPE_BELPIC;
+        }
       else
         {
-          for (i=0; card_atr_list[i].atrlen; i++)
-            if (card_atr_list[i].atrlen == atrlen
-                && !memcmp (card_atr_list[i].atr, atr, atrlen))
-              {
-                card_type = card_atr_list[i].type;
-                break;
-              }
-          xfree (atr);
+          unsigned char *atr;
+          size_t atrlen;
+          int i;
+
+          atr = apdu_get_atr (app->slot, &atrlen);
+          if (!atr)
+            rc = gpg_error (GPG_ERR_INV_CARD);
+          else
+            {
+              for (i=0; card_atr_list[i].atrlen; i++)
+                if (card_atr_list[i].atrlen == atrlen
+                    && !memcmp (card_atr_list[i].atr, atr, atrlen))
+                  {
+                    card_type = card_atr_list[i].type;
+                    break;
+                  }
+              xfree (atr);
+            }
         }
     }
   if (!rc)
@@ -2831,7 +3356,7 @@ app_select_p15 (app_t app)
       app->app_local = xtrycalloc (1, sizeof *app->app_local);
       if (!app->app_local)
         {
-          rc = gpg_error_from_errno (errno);
+          rc = gpg_error_from_syserror ();
           goto leave;
         }
 
@@ -2846,7 +3371,10 @@ app_select_p15 (app_t app)
          the common APP structure. */
       app->app_local->card_type = card_type;
 
-      /* Read basic information and check whether this is a real
+      /* Store whether we may and should use direct path selection. */
+      app->app_local->direct_path_selection = direct;
+
+      /* Read basic information and thus check whether this is a real
          card.  */
       rc = read_p15_info (app);
       if (rc)
@@ -2864,7 +3392,7 @@ app_select_p15 (app_t app)
           unsigned char *p;
 
           /* FIXME: actually get it from EF(TokenInfo). */
-          
+
           p = xtrymalloc (3 + app->serialnolen);
           if (!p)
             rc = gpg_error (gpg_err_code_from_errno (errno));
@@ -2877,17 +3405,15 @@ app_select_p15 (app_t app)
               app->serialno = p;
             }
         }
-      else /* Use standard munging code. */
-        rc = app_munge_serialno (app);
 
       app->fnc.deinit = do_deinit;
       app->fnc.learn_status = do_learn_status;
       app->fnc.readcert = do_readcert;
-      app->fnc.getattr = NULL;
+      app->fnc.getattr = do_getattr;
       app->fnc.setattr = NULL;
       app->fnc.genkey = NULL;
       app->fnc.sign = do_sign;
-      app->fnc.auth = NULL;
+      app->fnc.auth = do_auth;
       app->fnc.decipher = NULL;
       app->fnc.change_pin = NULL;
       app->fnc.check_pin = NULL;
@@ -2899,5 +3425,3 @@ app_select_p15 (app_t app)
 
   return rc;
 }
-
-