scd: better handling of extended APDU.
[gnupg.git] / scd / app-p15.c
index d2ed15a..8322617 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,40 +48,54 @@ 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. */
+#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 */
-  { 19, "\x3B\x6F\x00\xFF\x00\x68\xD2\x76\x00\x00\x28\xFF\x05\x1E\x31\x80"
-        "\x00\x90\x00",
+  { 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, "\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",
+  { 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 */
+/* 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,
@@ -259,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
@@ -274,6 +300,10 @@ struct app_local_s
     unsigned short auth_objects;
   } 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;
   /* Information on all trusted certificates. */
@@ -361,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;
     }
@@ -404,26 +435,44 @@ 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;
 }
 
@@ -469,7 +518,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;
@@ -584,12 +633,13 @@ read_ef_odf (app_t app, unsigned short odf_fid)
         }
       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
@@ -640,7 +690,8 @@ 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;
@@ -1078,14 +1129,14 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
                               + 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;
         }
@@ -1096,7 +1147,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;
@@ -1364,14 +1415,14 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
                             + 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;
         }
@@ -2083,7 +2134,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;
 
@@ -2156,12 +2207,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
@@ -2172,11 +2292,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;
@@ -2229,11 +2363,10 @@ 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)
         {
@@ -2241,11 +2374,7 @@ send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
           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),
@@ -2324,11 +2453,11 @@ 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)
         {
@@ -2336,11 +2465,7 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo)
           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)
@@ -2366,18 +2491,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);
 
@@ -2409,7 +2540,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;
@@ -2505,7 +2636,6 @@ 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
@@ -2513,7 +2643,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
    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 */
+static gpg_error_t 
 do_readcert (app_t app, const char *certid,
              unsigned char **r_cert, size_t *r_certlen)
 {
@@ -2529,6 +2659,90 @@ do_readcert (app_t app, const char *certid,
 }
 
 
+
+/* 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)
+            {
+              sprintf (p, "-%04hX", (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
@@ -2629,9 +2843,9 @@ micardo_mse (app_t app, unsigned short fid)
    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 in
+static gpg_error_
 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 )
@@ -2645,14 +2859,17 @@ 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);
@@ -2692,7 +2909,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)
@@ -2711,6 +2928,38 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       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)
@@ -2719,8 +2968,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));
@@ -2762,8 +3015,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++)
             ;
@@ -2792,14 +3043,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_errno (errno);
+              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_syserror ();
               xfree (pinvalue);
               return err;
             }
@@ -2830,7 +3113,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
@@ -2857,7 +3147,9 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     }
 
   /* 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. */
@@ -2887,51 +3179,170 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       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);
+}
+
+
+\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.  */
+/* 
+   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)
@@ -2941,7 +3352,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;
         }
 
@@ -2956,6 +3367,9 @@ app_select_p15 (app_t app)
          the common APP structure. */
       app->app_local->card_type = card_type;
 
+      /* 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);
@@ -2987,17 +3401,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;
@@ -3009,5 +3421,3 @@ app_select_p15 (app_t app)
 
   return rc;
 }
-
-