scd: Make SPRx32 pinpad work with PC/SC on Windows.
[gnupg.git] / scd / card.c
index dbfbe53..a582c50 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,
@@ -14,8 +14,7 @@
  * 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/>.
  */
 
 #include <config.h>
 #include <string.h>
 #include <time.h>
 
-#include <opensc-pkcs15.h>
-#include <ksba.h>
+#ifdef HAVE_OPENSC
+#include <opensc/pkcs15.h>
+#endif
 
 #include "scdaemon.h"
+#include <ksba.h>
 
-
-
-struct card_ctx_s {
-  int reader;   /* used reader */
-  struct sc_context *ctx;
-  struct sc_card *scard;
-  struct sc_pkcs15_card *p15card; /* only if there is a pkcs15 application */
-  
-};
+#include "card-common.h"
 
 /* Map the SC error codes to the GNUPG ones */
-static int
+gpg_error_t
 map_sc_err (int rc)
 {
+  gpg_err_code_t e;
+
   switch (rc)
     {
-    case 0: rc = 0; break;
-    case SC_ERROR_CMD_TOO_SHORT:         rc = GNUPG_Card_Error; break;
-    case SC_ERROR_CMD_TOO_LONG:          rc = GNUPG_Card_Error; break;
-    case SC_ERROR_NOT_SUPPORTED:         rc = GNUPG_Not_Supported; break;
-    case SC_ERROR_TRANSMIT_FAILED:       rc = GNUPG_Card_Error; break;
-    case SC_ERROR_FILE_NOT_FOUND:        rc = GNUPG_Card_Error; break;
-    case SC_ERROR_INVALID_ARGUMENTS:     rc = GNUPG_Card_Error; break;
-    case SC_ERROR_PKCS15_APP_NOT_FOUND:  rc = GNUPG_No_PKCS15_App; break;
-    case SC_ERROR_REQUIRED_PARAMETER_NOT_FOUND: rc = GNUPG_Card_Error; break;
-    case SC_ERROR_OUT_OF_MEMORY:         rc = GNUPG_Out_Of_Core; break;
-    case SC_ERROR_NO_READERS_FOUND:      rc = GNUPG_Card_Error; break;
-    case SC_ERROR_OBJECT_NOT_VALID:      rc = GNUPG_Card_Error; break;
-    case SC_ERROR_ILLEGAL_RESPONSE:      rc = GNUPG_Card_Error; break;
-    case SC_ERROR_PIN_CODE_INCORRECT:    rc = GNUPG_Card_Error; break;
-    case SC_ERROR_SECURITY_STATUS_NOT_SATISFIED: rc = GNUPG_Card_Error; break;
-    case SC_ERROR_CONNECTING_TO_RES_MGR: rc = GNUPG_Card_Error; break;
-    case SC_ERROR_INVALID_ASN1_OBJECT:   rc = GNUPG_Card_Error; break;
-    case SC_ERROR_BUFFER_TOO_SMALL:      rc = GNUPG_Card_Error; break;
-    case SC_ERROR_CARD_NOT_PRESENT:      rc = GNUPG_Card_Not_Present; break;
-    case SC_ERROR_RESOURCE_MANAGER:      rc = GNUPG_Card_Error; break;
-    case SC_ERROR_CARD_REMOVED:          rc = GNUPG_Card_Removed; break;
-    case SC_ERROR_INVALID_PIN_LENGTH:    rc = GNUPG_Card_Error; break;
-    case SC_ERROR_UNKNOWN_SMARTCARD:     rc = GNUPG_Card_Error; break;
-    case SC_ERROR_UNKNOWN_REPLY:         rc = GNUPG_Card_Error; break;
-    case SC_ERROR_OBJECT_NOT_FOUND:      rc = GNUPG_Card_Error; break;
-    case SC_ERROR_CARD_RESET:            rc = GNUPG_Card_Reset; break;
-    case SC_ERROR_ASN1_OBJECT_NOT_FOUND: rc = GNUPG_Card_Error; break;
-    case SC_ERROR_ASN1_END_OF_CONTENTS:  rc = GNUPG_Card_Error; break;
-    case SC_ERROR_TOO_MANY_OBJECTS:      rc = GNUPG_Card_Error; break;
-    case SC_ERROR_INVALID_CARD:          rc = GNUPG_Invalid_Card; break;
-    case SC_ERROR_WRONG_LENGTH:          rc = GNUPG_Card_Error; break;
-    case SC_ERROR_RECORD_NOT_FOUND:      rc = GNUPG_Card_Error; break;
-    case SC_ERROR_INTERNAL:              rc = GNUPG_Card_Error; break;
-    default: rc = GNUPG_Card_Error; break;
+    case 0: e = 0; break;
+#ifdef HAVE_OPENSC
+    case SC_ERROR_NOT_SUPPORTED:         e = GPG_ERR_NOT_SUPPORTED; break;
+    case SC_ERROR_PKCS15_APP_NOT_FOUND:  e = GPG_ERR_NO_PKCS15_APP; break;
+    case SC_ERROR_OUT_OF_MEMORY:         e = GPG_ERR_ENOMEM; break;
+    case SC_ERROR_CARD_NOT_PRESENT:      e = GPG_ERR_CARD_NOT_PRESENT; break;
+    case SC_ERROR_CARD_REMOVED:          e = GPG_ERR_CARD_REMOVED; break;
+    case SC_ERROR_INVALID_CARD:          e = GPG_ERR_INV_CARD; break;
+#endif
+    default: e = GPG_ERR_CARD; break;
     }
-  return rc;
+  /* It does not make much sense to further distingusih the error
+     source between OpenSC and SCD.  Thus we use SCD as source
+     here. */
+  return gpg_err_make (GPG_ERR_SOURCE_SCD, e);
+}
+
+/* Get the keygrip from CERT, return 0 on success */
+int
+card_help_get_keygrip (ksba_cert_t cert, unsigned char *array)
+{
+  gcry_sexp_t s_pkey;
+  int rc;
+  ksba_sexp_t p;
+  size_t n;
+
+  p = ksba_cert_get_public_key (cert);
+  if (!p)
+    return -1; /* oops */
+  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+  if (!n)
+    return -1; /* libksba did not return a proper S-expression */
+  rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
+  xfree (p);
+  if (rc)
+    return -1; /* can't parse that S-expression */
+  array = gcry_pk_get_keygrip (s_pkey, array);
+  gcry_sexp_release (s_pkey);
+  if (!array)
+    return -1; /* failed to calculate the keygrip */
+  return 0;
 }
 
 
+
+
+
+
+\f
 /* Create a new context for the card and figures out some basic
    information of the card.  Detects whether a PKCS_15 application is
    stored.
 
-   Common errors: GNUPG_Card_Not_Present */
+   Common errors: GPG_ERR_CARD_NOT_PRESENT */
 int
 card_open (CARD *rcard)
 {
+#ifdef HAVE_OPENSC
   CARD card;
   int rc;
 
+  if (opt.disable_opensc)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
   card = xtrycalloc (1, sizeof *card);
   if (!card)
-    return GNUPG_Out_Of_Core;
+    return gpg_error (gpg_err_code_from_errno (errno));
   card->reader = 0;
-  
-  rc = sc_establish_context (&card->ctx);
+
+  rc = sc_establish_context (&card->ctx, "scdaemon");
   if (rc)
     {
       log_error ("failed to establish SC context: %s\n", sc_strerror (rc));
@@ -111,13 +120,16 @@ card_open (CARD *rcard)
   if (card->reader >= card->ctx->reader_count)
     {
       log_error ("no card reader available\n");
-      rc = GNUPG_Card_Error;
+      rc = gpg_error (GPG_ERR_CARD);
+      goto leave;
     }
   card->ctx->error_file = log_get_stream ();
+  card->ctx->debug = opt.debug_sc;
   card->ctx->debug_file = log_get_stream ();
+
   if (sc_detect_card_presence (card->ctx->reader[card->reader], 0) != 1)
     {
-      rc = GNUPG_Card_Not_Present;
+      rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
       goto leave;
     }
 
@@ -130,7 +142,7 @@ card_open (CARD *rcard)
       goto leave;
     }
   if (opt.verbose)
-    log_info ("connected to card in reader %d using driver `%s'\n",
+    log_info ("connected to card in reader %d using driver '%s'\n",
               card->reader, card->scard->driver->name);
 
   rc = sc_lock (card->scard);
@@ -142,23 +154,17 @@ card_open (CARD *rcard)
       goto leave;
     }
 
-  rc = sc_pkcs15_bind (card->scard, &card->p15card);
-  if (rc == SC_ERROR_PKCS15_APP_NOT_FOUND)
-    rc = 0; /* okay */
-  else if (rc)
-    {
-      log_error ("binding of existing PKCS-15 failed in reader %d: %s\n",
-                 card->reader, sc_strerror (rc));
-      rc = map_sc_err (rc);
-      goto leave;
-    }
-    
+
  leave:
   if (rc)
     card_close (card);
   else
     *rcard = card;
+
   return rc;
+#else
+  return gpg_error (GPG_ERR_NOT_SUPPORTED);
+#endif
 }
 
 
@@ -168,11 +174,14 @@ card_close (CARD card)
 {
   if (card)
     {
+#ifdef HAVE_OPENSC
       if (card->p15card)
         {
           sc_pkcs15_unbind (card->p15card);
           card->p15card = NULL;
         }
+      if (card->p15priv)
+        p15_release_private_data (card);
       if (card->scard)
         {
           sc_unlock (card->scard);
@@ -181,12 +190,97 @@ card_close (CARD card)
        }
       if (card->ctx)
         {
-          sc_destroy_context (card->ctx);
+          sc_release_context (card->ctx);
           card->ctx = NULL;
         }
+#endif
       xfree (card);
-    }      
+    }
+}
+
+/* Locate a simple TLV encoded data object in BUFFER of LENGTH and
+   return a pointer to value as well as its length in NBYTES.  Return
+   NULL if it was not found.  Note, that the function does not check
+   whether the value fits into the provided buffer. */
+#ifdef HAVE_OPENSC
+static const char *
+find_simple_tlv (const unsigned char *buffer, size_t length,
+                 int tag, size_t *nbytes)
+{
+  const char *s = buffer;
+  size_t n = length;
+  size_t len;
+
+  for (;;)
+    {
+      buffer = s;
+      if (n < 2)
+        return NULL; /* buffer too short for tag and length. */
+      len = s[1];
+      s += 2; n -= 2;
+      if (len == 255)
+        {
+          if (n < 2)
+            return NULL; /* we expected 2 more bytes with the length. */
+          len = (s[0] << 8) | s[1];
+          s += 2; n -= 2;
+        }
+      if (*buffer == tag)
+        {
+          *nbytes = len;
+          return s;
+        }
+      if (len > n)
+        return NULL; /* buffer too short to skip to the next tag. */
+      s += len; n -= len;
+    }
 }
+#endif /*HAVE_OPENSC*/
+
+/* Find the ICC Serial Number within the provided BUFFER of LENGTH
+   (which should contain the GDO file) and return it as a hex encoded
+   string and allocated string in SERIAL.  Return an error code when
+   the ICCSN was not found. */
+#ifdef HAVE_OPENSC
+static int
+find_iccsn (const unsigned char *buffer, size_t length, char **serial)
+{
+  size_t n;
+  const unsigned char *s;
+  char *p;
+
+  s = find_simple_tlv (buffer, length, 0x5A, &n);
+  if (!s)
+    return gpg_error (GPG_ERR_CARD);
+  length -= s - buffer;
+  if (n > length)
+    {
+      /* Oops, it does not fit into the buffer.  This is an invalid
+         encoding (or the buffer is too short.  However, I have some
+         test cards with such an invalid encoding and therefore I use
+         this ugly workaround to return something I can further
+         experiment with. */
+      if (n == 0x0D && length+1 == n)
+        {
+          log_debug ("enabling BMI testcard workaround\n");
+          n--;
+        }
+      else
+        return gpg_error (GPG_ERR_CARD); /* Bad encoding; does
+                                           not fit into buffer. */
+    }
+  if (!n)
+    return gpg_error (GPG_ERR_CARD); /* Well, that is too short. */
+
+  *serial = p = xtrymalloc (2*n+1);
+  if (!*serial)
+    return gpg_error (gpg_err_code_from_errno (errno));
+  for (; n; n--, p += 2, s++)
+    sprintf (p, "%02X", *s);
+  *p = 0;
+  return 0;
+}
+#endif /*HAVE_OPENSC*/
 
 /* Retrieve the serial number and the time of the last update of the
    card.  The serial number is returned as a malloced string (hex
@@ -195,170 +289,206 @@ card_close (CARD card)
    is mandatory for a PKCS_15 application and an error will be
    returned if this value is not availbale.  For non-PKCS-15 cards a
    serial number is constructed by other means. Caller must free
-   SERIAL unless the fucntion returns an error. */
-int 
+   SERIAL unless the function returns an error. */
+int
 card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
 {
-  char *s;
+#ifdef HAVE_OPENSC
+  int rc;
+  struct sc_path path;
+  struct sc_file *file;
+  unsigned char buf[256];
+  int buflen;
+#endif
 
   if (!card || !serial || !stamp)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   *serial = NULL;
   *stamp = 0; /* not available */
-  if (!card->p15card)
-    { /* fixme: construct a serial number */
-      /* We should lookup the iso 7812-1 and 8583-3 - argh ISO practice is
-         suppressing innovation - IETF rules! */
-      return GNUPG_No_PKCS15_App;
+
+#ifdef HAVE_OPENSC
+  if (!card->fnc.initialized)
+    {
+      card->fnc.initialized = 1;
+      /* The first use of this card tries to figure out the type of the card
+         and sets up the function pointers. */
+      rc = sc_pkcs15_bind (card->scard, &card->p15card);
+      if (rc)
+        {
+          if (rc != SC_ERROR_PKCS15_APP_NOT_FOUND)
+            log_error ("binding of existing PKCS-15 failed in reader %d: %s\n",
+                       card->reader, sc_strerror (rc));
+          card->p15card = NULL;
+          rc = 0;
+        }
+      if (card->p15card)
+        card_p15_bind (card);
+      card->fnc.initialized = 1;
     }
-  s = card->p15card->serial_number;
-  if (!s || !hexdigitp (s) )
-    return GNUPG_Invalid_Card; /* the serial number is mandatory */
-  *serial = xstrdup (s);
-  if (!*serial)
-    return GNUPG_Out_Of_Core;
-  return 0;
-}
 
 
-\f
-/* Get the keygrip from CERT, return 0 on success */
-static int
-get_keygrip (KsbaCert cert, unsigned char *array)
-{
-  GCRY_SEXP s_pkey;
-  int rc;
-  KsbaSexp p;
-  size_t n;
-  
-  p = ksba_cert_get_public_key (cert);
-  if (!p)
-    return -1; /* oops */
-  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
-  if (!n)
-    return -1; /* libksba did not return a proper S-expression */
-  rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
-  xfree (p);
+  /* We should lookup the iso 7812-1 and 8583-3 - argh ISO
+     practice is suppressing innovation - IETF rules!  So we
+     always get the serialnumber from the 2F02 GDO file.  */
+  /* FIXME: in case we can't parse the 2F02 EF and we have a P15 card,
+     we should get the serial number from the respective P15 file */
+  sc_format_path ("3F002F02", &path);
+  rc = sc_select_file (card->scard, &path, &file);
   if (rc)
-    return -1; /* can't parse that S-expression */
-  array = gcry_pk_get_keygrip (s_pkey, array);
-  gcry_sexp_release (s_pkey);
-  if (!array)
-    return -1; /* failed to calculate the keygrip */
-  return 0;
-}
+    {
+      log_error ("sc_select_file failed: %s\n", sc_strerror (rc));
+      return gpg_error (GPG_ERR_CARD);
+    }
+  if (file->type != SC_FILE_TYPE_WORKING_EF
+      || file->ef_structure != SC_FILE_EF_TRANSPARENT)
+    {
+      log_error ("wrong type or structure of GDO file\n");
+      sc_file_free (file);
+      return gpg_error (GPG_ERR_CARD);
+    }
 
+  if (!file->size || file->size >= DIM(buf) )
+    { /* FIXME: Use a real parser */
+      log_error ("unsupported size of GDO file (%d)\n", file->size);
+      sc_file_free (file);
+      return gpg_error (GPG_ERR_CARD);
+    }
+  buflen = file->size;
+
+  rc = sc_read_binary (card->scard, 0, buf, buflen, 0);
+  sc_file_free (file);
+  if (rc < 0)
+    {
+      log_error ("error reading GDO file: %s\n", sc_strerror (rc));
+      return gpg_error (GPG_ERR_CARD);
+    }
+  if (rc != buflen)
+    {
+      log_error ("short read on GDO file\n");
+      return gpg_error (GPG_ERR_CARD);
+    }
+
+  rc = find_iccsn (buf, buflen, serial);
+  if (gpg_err_code (rc) == GPG_ERR_CARD)
+    log_error ("invalid structure of GDO file\n");
+  if (!rc && card->p15card && !strcmp (*serial, "D27600000000000000000000"))
+    { /* This is a German card with a silly serial number.  Try to get
+         the serial number from the EF(TokenInfo). We indicate such a
+         serial number by the using the prefix: "FF0100". */
+      const char *efser = card->p15card->serial_number;
+      char *p;
+
+      if (!efser)
+        efser = "";
+
+      xfree (*serial);
+      *serial = NULL;
+      p = xtrymalloc (strlen (efser) + 7);
+      if (!p)
+          rc = gpg_error (gpg_err_code_from_errno (errno));
+      else
+        {
+          strcpy (p, "FF0100");
+          strcpy (p+6, efser);
+          *serial = p;
+        }
+    }
+  else if (!rc && **serial == 'F' && (*serial)[1] == 'F')
+    { /* The serial number starts with our special prefix.  This
+         requires that we put our default prefix "FF0000" in front. */
+      char *p = xtrymalloc (strlen (*serial) + 7);
+      if (!p)
+        {
+          xfree (*serial);
+          *serial = NULL;
+          rc = gpg_error (gpg_err_code_from_errno (errno));
+        }
+      else
+        {
+          strcpy (p, "FF0000");
+          strcpy (p+6, *serial);
+          xfree (*serial);
+          *serial = p;
+        }
+    }
+  return rc;
+#else
+  return gpg_error (GPG_ERR_NOT_SUPPORTED);
+#endif
+}
 
 
 /* Enumerate all keypairs on the card and return the Keygrip as well
    as the internal identification of the key.  KEYGRIP must be a
    caller provided buffer with a size of 20 bytes which will receive
    the KEYGRIP of the keypair.  If KEYID is not NULL, it returns the
-   ID field of the key in allocated memory, NKEYID will then receive
-   the length of it.  The function returns -1 when all keys have been
-   enumerated.  Note that the error GNUPG_Missing_Certificate may be
+   ID field of the key in allocated memory; this is a string without
+   spaces.  The function returns -1 when all keys have been
+   enumerated.  Note that the error GPG_ERR_MISSING_CERTIFICATE may be
    returned if there is just the private key but no public key (ie.e a
    certificate) available.  Applications might want to continue
    enumerating after this error.*/
 int
 card_enum_keypairs (CARD card, int idx,
                     unsigned char *keygrip,
-                    unsigned char **keyid, size_t *nkeyid)
+                    char **keyid)
 {
   int rc;
-  KsbaError krc;
-  struct sc_pkcs15_prkey_info *pinfo;
-  struct sc_pkcs15_cert_info *certinfo;
-  struct sc_pkcs15_cert      *certder;
-  KsbaCert cert;
 
   if (keyid)
     *keyid = NULL;
-  if (nkeyid)
-    *nkeyid = 0;
 
-  if (!card || !keygrip || !card->p15card)
-    return GNUPG_Invalid_Value;
+  if (!card || !keygrip)
+    return gpg_error (GPG_ERR_INV_VALUE);
   if (idx < 0)
-    return GNUPG_Invalid_Index;
-       
-  rc = sc_pkcs15_enum_private_keys (card->p15card);
-  if (rc < 0) 
-    {
-      log_error ("sc_pkcs15_enum_private_keys failed: %s\n", sc_strerror (rc));
-      return GNUPG_Card_Error;
-    }
-  if ( idx >= card->p15card->prkey_count)
-    return -1;
-  pinfo = card->p15card->prkey_info + idx;
-  
-  /* now we need to read the certificate so that we can calculate the
-     keygrip */
-  rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &certinfo);
-  if (rc)
-    {
-      log_info ("certificate for private key %d not found: %s\n",
-                idx, sc_strerror (rc));
-      /* but we should return the ID anyway */
-      if (keyid)
-        {
-          *keyid = xtrymalloc (pinfo->id.len);
-          if (!*keyid)
-            return GNUPG_Out_Of_Core;
-          memcpy (*keyid, pinfo->id.value, pinfo->id.len);
-        }
-      if (nkeyid)
-        *nkeyid = pinfo->id.len;
-      return GNUPG_Missing_Certificate;
-    }
-  rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
-  if (rc)
-    {
-      log_info ("failed to read certificate for private key %d: %s\n",
-                idx, sc_strerror (rc));
-      return GNUPG_Card_Error;
-    }
+    return gpg_error (GPG_ERR_INV_INDEX);
+  if (!card->fnc.initialized)
+    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+  if (!card->fnc.enum_keypairs)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+  rc = card->fnc.enum_keypairs (card, idx, keygrip, keyid);
+  if (opt.verbose)
+    log_info ("card operation enum_keypairs result: %s\n",
+              gpg_strerror (rc));
+  return rc;
+}
 
-  cert = ksba_cert_new ();
-  if (!cert)
-    {
-      sc_pkcs15_free_certificate (certder);
-      return GNUPG_Out_Of_Core;
-    }
-  krc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len);
-  sc_pkcs15_free_certificate (certder);
-  if (krc)
-    {
-      log_error ("failed to parse the certificate for private key %d: %s\n",
-                 idx, ksba_strerror (krc));
-      ksba_cert_release (cert);
-      return GNUPG_Card_Error;
-    }
-  if (get_keygrip (cert, keygrip))
-    {
-      log_error ("failed to calculate the keygrip of private key %d\n", idx);
-      ksba_cert_release (cert);
-      return GNUPG_Card_Error;
-    }      
-  ksba_cert_release (cert);
 
-  /* return the iD */
-  if (keyid)
-    {
-      *keyid = xtrymalloc (pinfo->id.len);
-      if (!*keyid)
-        return GNUPG_Out_Of_Core;
-      memcpy (*keyid, pinfo->id.value, pinfo->id.len);
-    }
-  if (nkeyid)
-    *nkeyid = pinfo->id.len;
-  
-  return 0;
+/* Enumerate all trusted certificates available on the card, return
+   their ID in CERT and the type in CERTTYPE.  Types of certificates
+   are:
+      0   := Unknown
+      100 := Regular X.509 cert
+      101 := Trusted X.509 cert
+      102 := Useful X.509 cert
+      110 := Root CA cert (DINSIG)
+ */
+int
+card_enum_certs (CARD card, int idx, char **certid, int *certtype)
+{
+  int rc;
+
+  if (certid)
+    *certid = NULL;
+
+  if (!card)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (idx < 0)
+    return gpg_error (GPG_ERR_INV_INDEX);
+  if (!card->fnc.initialized)
+    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+  if (!card->fnc.enum_certs)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+  rc = card->fnc.enum_certs (card, idx, certid, certtype);
+  if (opt.verbose)
+    log_info ("card operation enum_certs result: %s\n",
+              gpg_strerror (rc));
+  return rc;
 }
 
 
-\f
+
 /* Read the certificate identified by CERTIDSTR which is the
    hexadecimal encoded ID of the certificate, prefixed with the string
    "3F005015.". The certificate is return in DER encoded form in CERT
@@ -367,57 +497,72 @@ int
 card_read_cert (CARD card, const char *certidstr,
                 unsigned char **cert, size_t *ncert)
 {
-  struct sc_pkcs15_id certid;
-  struct sc_pkcs15_cert_info *certinfo;
-  struct sc_pkcs15_cert      *certder;
-  const char *s;
-  int rc, n;
-
-  if (!card || !certidstr || !card->p15card || !cert || !ncert)
-    return GNUPG_Invalid_Value;
-
-  /* For now we only support the standard DF */
-  if (strncmp (certidstr, "3F005015.", 9) ) 
-    return GNUPG_Invalid_Id;
-  for (s=certidstr+9, n=0; hexdigitp (s); s++, n++)
-    ;
-  if (*s || (n&1))
-    return GNUPG_Invalid_Id; /* invalid or odd number of digits */
-  n /= 2;
-  if (!n || n > SC_PKCS15_MAX_ID_SIZE)
-    return GNUPG_Invalid_Id; /* empty or too large */
-  for (s=certidstr+9, n=0; *s; s += 2, n++)
-    certid.value[n] = xtoi_2 (s);
-  certid.len = n;
-
-  rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &certinfo);
-  if (rc)
-    {
-      log_info ("certificate '%s' not found: %s\n", 
-                certidstr, sc_strerror (rc));
-      return -1;
-    }
-  rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
-  if (rc)
-    {
-      log_info ("failed to read certificate '%s': %s\n",
-                certidstr, sc_strerror (rc));
-      return GNUPG_Card_Error;
-    }
+  int rc;
 
-  *cert = xtrymalloc (certder->data_len);
-  if (!*cert)
-    {
-      sc_pkcs15_free_certificate (certder);
-      return GNUPG_Out_Of_Core;
-    }
-  memcpy (*cert, certder->data, certder->data_len);
-  *ncert = certder->data_len;
-  sc_pkcs15_free_certificate (certder);
-  return 0;
+  if (!card || !certidstr || !cert || !ncert)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (!card->fnc.initialized)
+    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+  if (!card->fnc.read_cert)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+  rc = card->fnc.read_cert (card, certidstr, cert, ncert);
+  if (opt.verbose)
+    log_info ("card operation read_cert result: %s\n", gpg_strerror (rc));
+  return rc;
 }
 
 
+/* 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; it
+   should return the PIN in an allocated buffer and put it into PIN.  */
+int
+card_sign (CARD card, const char *keyidstr, int hashalgo,
+           int (pincb)(void*, const char *, char **),
+           void *pincb_arg,
+           const void *indata, size_t indatalen,
+           unsigned char **outdata, size_t *outdatalen )
+{
+  int rc;
+
+  if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (!card->fnc.initialized)
+    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+  if (!card->fnc.sign)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+  rc =  card->fnc.sign (card, keyidstr, hashalgo,
+                        pincb, pincb_arg,
+                        indata, indatalen,
+                        outdata, outdatalen);
+  if (opt.verbose)
+    log_info ("card operation sign result: %s\n", gpg_strerror (rc));
+  return rc;
+}
 
 
+/* 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; it
+   should return the PIN in an allocated buffer and put it into PIN.  */
+int
+card_decipher (CARD card, const char *keyidstr,
+               int (pincb)(void*, const char *, char **),
+               void *pincb_arg,
+               const void *indata, size_t indatalen,
+               unsigned char **outdata, size_t *outdatalen )
+{
+  int rc;
 
+  if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (!card->fnc.initialized)
+    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+  if (!card->fnc.decipher)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+  rc =  card->fnc.decipher (card, keyidstr,
+                            pincb, pincb_arg,
+                            indata, indatalen,
+                            outdata, outdatalen);
+  if (opt.verbose)
+    log_info ("card operation decipher result: %s\n", gpg_strerror (rc));
+  return rc;
+}