[glib-networking/wip/tingping/pkcs11] WIP: Re-add pkcs11 support



commit d3ba6eba23aa0b2178811ffe986ebf8b7de3bfc7
Author: Patrick Griffis <pgriffis igalia com>
Date:   Fri Apr 26 12:13:11 2019 -0700

    WIP: Re-add pkcs11 support

 meson.build                             |   12 +
 meson_options.txt                       |    1 +
 po/POTFILES.in                          |    2 +
 tls/gnutls/gtlsbackend-gnutls.c         |   36 +
 tls/gnutls/gtlscertificate-gnutls.c     |  107 ++-
 tls/gnutls/gtlscertificate-gnutls.h     |   12 +
 tls/gnutls/gtlsconnection-gnutls.c      |   76 ++
 tls/gnutls/gtlsdatabase-gnutls-pkcs11.c | 1139 +++++++++++++++++++++++
 tls/gnutls/gtlsdatabase-gnutls-pkcs11.h |   44 +
 tls/gnutls/meson.build                  |   10 +
 tls/pkcs11/gpkcs11array.c               |  282 ++++++
 tls/pkcs11/gpkcs11array.h               |  107 +++
 tls/pkcs11/gpkcs11pin.c                 |  159 ++++
 tls/pkcs11/gpkcs11pin.h                 |   46 +
 tls/pkcs11/gpkcs11slot.c                |  618 ++++++++++++
 tls/pkcs11/gpkcs11slot.h                |   73 ++
 tls/pkcs11/gpkcs11util.c                |   63 ++
 tls/pkcs11/gpkcs11util.h                |   51 +
 tls/pkcs11/meson.build                  |   28 +
 tls/pkcs11/pkcs11-trust-assertions.h    |   59 ++
 tls/tests/meson.build                   |   14 +
 tls/tests/mock-pkcs11.c                 | 1547 +++++++++++++++++++++++++++++++
 tls/tests/mock-pkcs11.h                 |  396 ++++++++
 tls/tests/pkcs11-array.c                |  288 ++++++
 tls/tests/pkcs11-pin.c                  |  152 +++
 tls/tests/pkcs11-slot.c                 |  526 +++++++++++
 tls/tests/pkcs11-util.c                 |   63 ++
 27 files changed, 5910 insertions(+), 1 deletion(-)
---
diff --git a/meson.build b/meson.build
index 8d01f52..5c03f6e 100644
--- a/meson.build
+++ b/meson.build
@@ -75,6 +75,14 @@ gnutls_dep = dependency('gnutls', version: '>= 3.4.6', required: get_option('gnu
 
 if gnutls_dep.found()
   backends += ['gnutls']
+
+  # *** Checks for p11-kit  ***
+  pkcs11_dep = dependency('p11-kit-1', version: '>= 0.20', required: get_option('pkcs11'))
+
+  if pkcs11_dep.found()
+    config_h.set('HAVE_PKCS11', 1,
+                 description: 'Building with PKCS#11 support')
+  endif
 endif
 
 # *** Checks for OpenSSL    ***
@@ -172,6 +180,9 @@ if libproxy_dep.found() or gsettings_desktop_schemas_dep.found()
 endif
 
 subdir('tls/base')
+if pkcs11_dep.found()
+  subdir('tls/pkcs11')
+endif
 
 if gnutls_dep.found()
   subdir('tls/gnutls')
@@ -194,4 +205,5 @@ output += '  gnutls support:      ' + backends.contains('gnutls').to_string() +
 output += '  openssl support:     ' + backends.contains('openssl').to_string() + '\n'
 output += '  libproxy support:    ' + libproxy_dep.found().to_string() + '\n'
 output += '  GNOME proxy support: ' + gsettings_desktop_schemas_dep.found().to_string() + '\n'
+output += '  PKCS#11 support:     ' + pkcs11_dep.found().to_string() + '\n'
 message(output)
diff --git a/meson_options.txt b/meson_options.txt
index 3a525dd..04706be 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -2,5 +2,6 @@ option('gnutls', type: 'feature', value: 'auto', description: 'support for GnuTL
 option('openssl', type: 'feature', value: 'disabled', description: 'support for OpenSSL networking 
configration')
 option('libproxy', type: 'feature', value: 'auto', description: 'support for libproxy proxy configration')
 option('gnome_proxy', type: 'feature', value: 'auto', description: 'support for GNOME desktop proxy 
configuration')
+option('pkcs11', type: 'feature', value: 'disabled', description: 'support for PKCS#11 using p11-kit')
 option('installed_tests', type: 'boolean', value: false, description: 'enable installed tests')
 option('static_modules', type: 'boolean', value: false, description: 'build static modules')
diff --git a/po/POTFILES.in b/po/POTFILES.in
index dffd37e..0308cc4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -13,3 +13,5 @@ tls/openssl/gtlscertificate-openssl.c
 tls/openssl/gtlsclientconnection-openssl.c
 tls/openssl/gtlsconnection-openssl.c
 tls/openssl/gtlsserverconnection-openssl.c
+tls/pkcs11/gpkcs11pin.c
+tls/pkcs11/gpkcs11slot.c
diff --git a/tls/gnutls/gtlsbackend-gnutls.c b/tls/gnutls/gtlsbackend-gnutls.c
index c955327..23278cd 100644
--- a/tls/gnutls/gtlsbackend-gnutls.c
+++ b/tls/gnutls/gtlsbackend-gnutls.c
@@ -35,6 +35,7 @@
 #include "gtlsclientconnection-gnutls.h"
 #include "gtlsfiledatabase-gnutls.h"
 #include "gtlsserverconnection-gnutls.h"
+#include "gtlsdatabase-gnutls-pkcs11.h"
 
 struct _GTlsBackendGnutls
 {
@@ -42,6 +43,7 @@ struct _GTlsBackendGnutls
 
   GMutex mutex;
   GTlsDatabase *default_database;
+  GTlsDatabase *pkcs11_database;
 };
 
 static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
@@ -153,6 +155,39 @@ g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
   return result;
 }
 
+static GTlsDatabase*
+g_tls_backend_gnutls_get_pkcs11_database (GTlsBackend *backend)
+{
+  GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
+  GTlsDatabase *result;
+  GError *error = NULL;
+
+  g_mutex_lock (&self->mutex);
+
+  if (self->pkcs11_database)
+    {
+      result = g_object_ref (self->pkcs11_database);
+    }
+  else
+    {
+      result = G_TLS_DATABASE (g_tls_database_gnutls_pkcs11_new (&error));
+      if (error)
+        {
+          g_warning ("Failed to load PKCS11 TLS database: %s", error->message);
+          g_clear_error (&error);
+        }
+      else
+        {
+          g_assert (result);
+          self->pkcs11_database = g_object_ref (result);
+        }
+    }
+
+  g_mutex_unlock (&self->mutex);
+
+  return result;
+}
+
 static void
 g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
 {
@@ -161,6 +196,7 @@ g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
   iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
   iface->get_file_database_type =     g_tls_file_database_gnutls_get_type;
   iface->get_default_database =       g_tls_backend_gnutls_get_default_database;
+  iface->get_pkcs11_database =        g_tls_backend_gnutls_get_pkcs11_database;
   iface->get_dtls_client_connection_type = g_tls_client_connection_gnutls_get_type;
   iface->get_dtls_server_connection_type = g_tls_server_connection_gnutls_get_type;
 }
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index ff18c46..8023e91 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -39,7 +39,9 @@ enum
   PROP_CERTIFICATE_PEM,
   PROP_PRIVATE_KEY,
   PROP_PRIVATE_KEY_PEM,
-  PROP_ISSUER
+  PROP_ISSUER,
+  PROP_CERTIFICATE_URI,
+  PROP_PRIVATE_KEY_URI,
 };
 
 struct _GTlsCertificateGnutls
@@ -53,6 +55,9 @@ struct _GTlsCertificateGnutls
 
   GError *construct_error;
 
+  gchar *certificate_uri;
+  gchar *private_key_uri;
+
   guint have_cert : 1;
   guint have_key  : 1;
 };
@@ -141,6 +146,14 @@ g_tls_certificate_gnutls_get_property (GObject    *object,
       g_value_set_object (value, gnutls->issuer);
       break;
 
+    case PROP_CERTIFICATE_URI:
+      g_value_set_string (value, gnutls->certificate_uri);
+      break;
+
+    case PROP_PRIVATE_KEY_URI:
+      g_value_set_string (value, gnutls->private_key_uri);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -267,6 +280,16 @@ g_tls_certificate_gnutls_set_property (GObject      *object,
       gnutls->issuer = g_value_dup_object (value);
       break;
 
+    case PROP_CERTIFICATE_URI:
+      g_free (gnutls->certificate_uri);
+      gnutls->certificate_uri = g_value_dup_string (value);
+      break;
+
+    case PROP_PRIVATE_KEY_URI:
+      g_free (gnutls->private_key_uri);
+      gnutls->private_key_uri = g_value_dup_string (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -376,6 +399,17 @@ g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
   g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
+
+  g_object_class_install_property (gobject_class, PROP_CERTIFICATE_URI,
+                  g_param_spec_string ("certificate-uri", "Certificate URI",
+                                       "PKCS#11 URI of Certificate", NULL,
+                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY_URI,
+                  g_param_spec_string ("private-key-uri", "Private Key URI",
+                                       "PKCS#11 URI of Private Key", NULL,
+                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
 }
 
 static void
@@ -398,6 +432,33 @@ g_tls_certificate_gnutls_new (const gnutls_datum_t *datum,
   return G_TLS_CERTIFICATE (gnutls);
 }
 
+GTlsCertificate *
+g_tls_certificate_gnutls_pkcs11_new (gpointer certificate_data,
+                                     gsize certificate_data_length,
+                                     const gchar *certificate_uri,
+                                     const gchar *private_key_uri,
+                                     GTlsCertificate    *issuer)
+{
+  GTlsCertificate *certificate;
+  gnutls_datum_t datum;
+
+  g_return_val_if_fail (certificate_data, NULL);
+  g_return_val_if_fail (certificate_uri, NULL);
+
+  datum.data = certificate_data;
+  datum.size = certificate_data_length;
+
+  certificate = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
+                              "issuer", issuer,
+                              "certificate-uri", certificate_uri,
+                              "private-key-uri", private_key_uri,
+                              NULL);
+
+  g_tls_certificate_gnutls_set_data (G_TLS_CERTIFICATE_GNUTLS (certificate), &datum);
+
+  return certificate;
+}
+
 void
 g_tls_certificate_gnutls_set_data (GTlsCertificateGnutls *gnutls,
                                    const gnutls_datum_t  *datum)
@@ -489,6 +550,26 @@ g_tls_certificate_gnutls_copy  (GTlsCertificateGnutls  *gnutls,
       {
         *pkey = NULL;
       }
+
+  // if (*pkey == NULL)
+  //   {
+  //     gchar *uri = g_tls_certificate_gnutls_pkcs11_build_private_key_uri (chain, interaction_id);
+  //     g_message("%s", uri);
+  //     if (uri != NULL)
+  //       {
+  //         gnutls_pkcs11_privkey_t pkcs11_privkey;
+  //         gnutls_privkey_t privkey;
+
+  //         gnutls_pkcs11_privkey_init (&pkcs11_privkey);
+  //         gnutls_pkcs11_privkey_import_url (pkcs11_privkey, uri, GNUTLS_PKCS11_URL_GENERIC);
+  //         g_free (uri);
+
+  //         gnutls_privkey_init (&privkey);
+  //         gnutls_privkey_import_pkcs11 (privkey, pkcs11_privkey, GNUTLS_PRIVKEY_IMPORT_COPY);
+  //         *pkey = privkey;
+  //         gnutls_pkcs11_privkey_deinit (pkcs11_privkey);
+  //       }
+  //   }
 }
 
 void
@@ -761,3 +842,27 @@ g_tls_certificate_gnutls_build_chain (const gnutls_datum_t  *certs,
 
   return result;
 }
+
+gchar *
+g_tls_certificate_gnutls_pkcs11_build_certificate_uri (GTlsCertificateGnutls *self,
+                                                       const gchar *interaction_id)
+{
+  if (self->certificate_uri == NULL)
+    return NULL;
+  else if (interaction_id)
+    return g_strdup_printf ("%s;pinfile=%s", self->certificate_uri, interaction_id);
+  else
+    return g_strdup (self->certificate_uri);
+}
+
+gchar *
+g_tls_certificate_gnutls_pkcs11_build_private_key_uri (GTlsCertificateGnutls *self,
+                                                       const gchar *interaction_id)
+{
+  if (self->private_key_uri == NULL)
+    return NULL;
+  else if (interaction_id)
+    return g_strdup_printf ("%s;pinfile=%s", self->private_key_uri, interaction_id);
+  else
+    return g_strdup (self->private_key_uri);
+}
\ No newline at end of file
diff --git a/tls/gnutls/gtlscertificate-gnutls.h b/tls/gnutls/gtlscertificate-gnutls.h
index c5aff43..3c7a4e9 100644
--- a/tls/gnutls/gtlscertificate-gnutls.h
+++ b/tls/gnutls/gtlscertificate-gnutls.h
@@ -70,6 +70,18 @@ GTlsCertificateGnutls*       g_tls_certificate_gnutls_build_chain     (const gnu
                                                                        guint                  num_certs,
                                                                        gnutls_x509_crt_fmt_t  format);
 
+GTlsCertificate *  g_tls_certificate_gnutls_pkcs11_new                   (gpointer        certificate_der,
+                                                                          gsize           
certificate_der_length,
+                                                                          const gchar     *certificate_uri,
+                                                                          const gchar     *private_key_uri,
+                                                                          GTlsCertificate *issuer);
+
+gchar *            g_tls_certificate_gnutls_pkcs11_build_certificate_uri (GTlsCertificateGnutls *self,
+                                                                          const gchar *interaction_id);
+
+gchar *            g_tls_certificate_gnutls_pkcs11_build_private_key_uri (GTlsCertificateGnutls *self,
+                                                                          const gchar *interaction_id);
+
 G_END_DECLS
 
 #endif /* __G_TLS_CERTIFICATE_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 774d668..4eb1e36 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -40,6 +40,11 @@
 #include "gtlsoutputstream-gnutls.h"
 #include "gtlsserverconnection-gnutls.h"
 
+#ifdef HAVE_PKCS11
+#include <p11-kit/pin.h>
+#include "pkcs11/gpkcs11pin.h"
+#endif
+
 #ifdef G_OS_WIN32
 #include <winsock2.h>
 #include <winerror.h>
@@ -103,6 +108,14 @@ static gboolean g_tls_connection_gnutls_initable_init       (GInitable       *in
 static void     g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
 static void     g_tls_connection_gnutls_datagram_based_iface_init  (GDatagramBasedInterface  *iface);
 
+#ifdef HAVE_PKCS11
+static P11KitPin*    on_pin_prompt_callback  (const char     *pinfile,
+                                              P11KitUri      *pin_uri,
+                                              const char     *pin_description,
+                                              P11KitPinFlags  pin_flags,
+                                              void           *callback_data);
+#endif
+
 static void g_tls_connection_gnutls_init_priorities (void);
 
 static int verify_certificate_cb (gnutls_session_t session);
@@ -269,6 +282,11 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
   unique_id = g_atomic_int_add (&unique_interaction_id, 1);
   priv->interaction_id = g_strdup_printf ("gtls:%d", unique_id);
 
+#ifdef HAVE_PKCS11
+  p11_kit_pin_register_callback (priv->interaction_id,
+                                 on_pin_prompt_callback, gnutls, NULL);
+#endif
+
   priv->waiting_for_op = g_cancellable_new ();
   g_cancellable_cancel (priv->waiting_for_op);
   g_mutex_init (&priv->op_mutex);
@@ -462,6 +480,10 @@ g_tls_connection_gnutls_finalize (GObject *object)
 
   g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
 
+#ifdef HAVE_PKCS11
+  p11_kit_pin_unregister_callback (priv->interaction_id,
+                                   on_pin_prompt_callback, gnutls);
+#endif
   g_free (priv->interaction_id);
   g_clear_object (&priv->interaction);
 
@@ -3117,6 +3139,60 @@ g_tls_connection_gnutls_dtls_get_negotiated_protocol (GDtlsConnection *conn)
 }
 #endif
 
+#ifdef HAVE_PKCS11
+
+static P11KitPin*
+on_pin_prompt_callback (const char     *pinfile,
+                        P11KitUri      *pin_uri,
+                        const char     *pin_description,
+                        P11KitPinFlags  pin_flags,
+                        void           *callback_data)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (callback_data);
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  GTlsInteractionResult result;
+  GTlsPasswordFlags flags = 0;
+  GTlsPassword *password;
+  P11KitPin *pin = NULL;
+  GError *error = NULL;
+
+  if (!priv->interaction)
+    return NULL;
+
+  if (pin_flags & P11_KIT_PIN_FLAGS_RETRY)
+    flags |= G_TLS_PASSWORD_RETRY;
+  if (pin_flags & P11_KIT_PIN_FLAGS_MANY_TRIES)
+    flags |= G_TLS_PASSWORD_MANY_TRIES;
+  if (pin_flags & P11_KIT_PIN_FLAGS_FINAL_TRY)
+    flags |= G_TLS_PASSWORD_FINAL_TRY;
+
+  password = g_pkcs11_pin_new (flags, pin_description);
+
+  result = g_tls_interaction_ask_password (priv->interaction, password,
+                                           g_cancellable_get_current (), &error);
+
+  switch (result)
+    {
+    case G_TLS_INTERACTION_FAILED:
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("couldn't ask for password: %s", error->message);
+      pin = NULL;
+      break;
+    case G_TLS_INTERACTION_UNHANDLED:
+    default:
+      pin = NULL;
+      break;
+    case G_TLS_INTERACTION_HANDLED:
+      pin = g_pkcs11_pin_steal_internal (G_PKCS11_PIN (password));
+      break;
+    }
+
+  g_object_unref (password);
+  return pin;
+}
+
+#endif /* HAVE_PKCS11 */
+
 static void
 g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
 {
diff --git a/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
new file mode 100644
index 0000000..6a647d3
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
@@ -0,0 +1,1139 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gtlsdatabase-gnutls-pkcs11.h"
+#include "gtlscertificate-gnutls.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <gnutls/x509.h>
+
+#include <p11-kit/p11-kit.h>
+#include <stdlib.h>
+
+#include "pkcs11/gpkcs11pin.h"
+#include "pkcs11/gpkcs11slot.h"
+#include "pkcs11/gpkcs11util.h"
+#include "pkcs11/pkcs11-trust-assertions.h"
+
+typedef enum {
+  G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE = 1,
+  G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE = 2,
+} GTlsDatabaseGnutlsAssertion;
+
+static const CK_ATTRIBUTE_TYPE CERTIFICATE_ATTRIBUTE_TYPES[] = {
+    CKA_ID, CKA_LABEL, CKA_CLASS, CKA_VALUE
+};
+
+static const CK_ATTRIBUTE_TYPE KEY_ATTRIBUTE_TYPES[] = {
+    CKA_ID, CKA_LABEL, CKA_CLASS, CKA_KEY_TYPE
+};
+
+static void g_tls_database_gnutls_pkcs11_initable_iface_init (GInitableIface *iface);
+
+struct _GTlsDatabaseGnutlsPkcs11
+{
+  GTlsDatabase parent_instance;
+
+  /* no changes after construction */
+  CK_FUNCTION_LIST **modules;
+  GList *pkcs11_slots;
+  GList *trust_uris;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GTlsDatabaseGnutlsPkcs11, g_tls_database_gnutls_pkcs11,
+                         G_TYPE_TLS_DATABASE,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                g_tls_database_gnutls_pkcs11_initable_iface_init));
+
+static gboolean
+discover_module_slots_and_options (GTlsDatabaseGnutlsPkcs11   *self,
+                                   CK_FUNCTION_LIST_PTR        module,
+                                   GError                    **error)
+{
+  CK_ULONG i, count = 0;
+  CK_SLOT_ID *list;
+  GPkcs11Slot *slot;
+  P11KitUri *uri;
+  char *string;
+  guint uri_type;
+  int ret;
+  CK_RV rv;
+
+  /*
+   * Ask module for the number of slots. We include slots without tokens
+   * since we want to be able to use them if the user inserts a token
+   * later.
+   */
+
+  rv = (module->C_GetSlotList) (CK_FALSE, NULL, &count);
+  if (rv != CKR_OK)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                   "Couldn't load list of slots in PKCS#11 module: %s",
+                   p11_kit_strerror (rv));
+      return FALSE;
+    }
+
+  if (count == 0)
+    return TRUE;
+
+  /* Actually retrieve the slot ids */
+  list = g_new0 (CK_SLOT_ID, count);
+  rv = (module->C_GetSlotList) (CK_FALSE, list, &count);
+  if (rv != CKR_OK)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                   "Couldn't load list of slots in PKCS#11 module: %s",
+                   p11_kit_strerror (rv));
+      g_free (list);
+      return FALSE;
+    }
+
+  for (i = 0; i < count; ++i)
+    {
+      slot = g_object_new (G_TYPE_PKCS11_SLOT,
+                           "slot-id", list[i],
+                           "module", module,
+                           NULL);
+      self->pkcs11_slots = g_list_append (self->pkcs11_slots, slot);
+    }
+
+  /*
+   * Load up relevant options. We use the x-trust-lookup option to determine
+   * which slots we can use for looking up trust assertionts.
+   */
+
+  string = p11_kit_config_option (module, "x-trust-lookup");
+  if (string != NULL)
+    {
+      uri = p11_kit_uri_new ();
+      uri_type = P11_KIT_URI_FOR_TOKEN | P11_KIT_URI_FOR_MODULE_WITH_VERSION;
+      ret = p11_kit_uri_parse (string, uri_type, uri);
+
+      if (ret < 0)
+        {
+          g_message ("couldn't parse configured uri for trust lookups: %s: %s",
+                     string, p11_kit_uri_message (ret));
+          p11_kit_uri_free (uri);
+        }
+      else
+        {
+          self->trust_uris = g_list_append (self->trust_uris, uri);
+        }
+
+      free (string);
+    }
+
+  return TRUE;
+}
+
+static GTlsCertificate *
+create_database_pkcs11_certificate (GPkcs11Slot  *slot,
+                                    GPkcs11Array *certificate_attrs,
+                                    GPkcs11Array *private_key_attrs)
+{
+  GTlsCertificate *certificate;
+  gchar *certificate_uri = NULL;
+  gchar *private_key_uri = NULL;
+  const CK_ATTRIBUTE *value_attr;
+  P11KitUri *uri;
+  int ret;
+
+  value_attr = g_pkcs11_array_find (certificate_attrs, CKA_VALUE);
+  if (value_attr == NULL)
+    return NULL;
+
+  uri = p11_kit_uri_new ();
+
+  /*
+   * The PKCS#11 URIs we create for certificates and keys are not bound to
+   * the module. They are bound to the token.
+   *
+   * For example the user could have keys on a smart card token. He could insert
+   * this smart card into a different slot, or perhaps change the driver
+   * (through an OS upgrade). So the key and certificate should still be
+   * referenceable through the URI.
+   *
+   * We also set a 'pinfile' prompting id, so that users of p11-kit like
+   * gnutls can call our callback.
+   */
+
+  if (!g_pkcs11_slot_get_token_info (slot, p11_kit_uri_get_token_info (uri)))
+    g_return_val_if_reached (NULL);
+
+  ret = p11_kit_uri_set_attributes (uri, certificate_attrs->attrs,
+                                    certificate_attrs->count);
+  g_return_val_if_fail (ret == P11_KIT_URI_OK, NULL);
+
+  ret = p11_kit_uri_format (uri, P11_KIT_URI_FOR_OBJECT_ON_TOKEN, &certificate_uri);
+  g_return_val_if_fail (ret == P11_KIT_URI_OK, NULL);
+
+  if (private_key_attrs != NULL)
+    {
+
+      /* The URI will keep the token info above, so we just change attributes */
+
+      ret = p11_kit_uri_set_attributes (uri, private_key_attrs->attrs,
+                                        private_key_attrs->count);
+      g_return_val_if_fail (ret == P11_KIT_URI_OK, NULL);
+
+      ret = p11_kit_uri_format (uri, P11_KIT_URI_FOR_OBJECT_ON_TOKEN, &private_key_uri);
+      g_return_val_if_fail (ret == P11_KIT_URI_OK, NULL);
+    }
+
+  certificate = g_tls_certificate_gnutls_pkcs11_new (value_attr->pValue,
+                                                     value_attr->ulValueLen,
+                                                     certificate_uri,
+                                                     private_key_uri,
+                                                     NULL);
+
+  p11_kit_uri_free (uri);
+  g_free (certificate_uri);
+  g_free (private_key_uri);
+
+  return certificate;
+}
+
+static const gchar *
+calculate_peer_for_identity (GSocketConnectable *identity)
+{
+  const char *peer;
+
+  if (G_IS_NETWORK_ADDRESS (identity))
+    peer = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
+  else if (G_IS_NETWORK_SERVICE (identity))
+    peer = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
+  else
+    peer = NULL;
+
+  return peer;
+}
+
+static void
+g_tls_database_gnutls_pkcs11_finalize (GObject *object)
+{
+  GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (object);
+  GList *l;
+
+  for (l = self->pkcs11_slots; l; l = g_list_next (l))
+      g_object_unref (l->data);
+  g_list_free (self->pkcs11_slots);
+
+  for (l = self->trust_uris; l; l = g_list_next (l))
+    p11_kit_uri_free (l->data);
+  g_list_free (self->trust_uris);
+
+  if (self->modules)
+    p11_kit_modules_release (self->modules);
+
+  G_OBJECT_CLASS (g_tls_database_gnutls_pkcs11_parent_class)->finalize (object);
+}
+
+static void
+g_tls_database_gnutls_pkcs11_init (GTlsDatabaseGnutlsPkcs11 *self)
+{
+}
+
+static gboolean
+accumulate_stop (gpointer result,
+                 gpointer user_data)
+{
+  return FALSE; /* stop enumeration */
+}
+
+static gboolean
+accumulate_exists (gpointer result,
+                   gpointer user_data)
+{
+  gboolean *exists = (gboolean *)user_data;
+  *exists = TRUE;
+  return FALSE; /* stop enumeration */
+}
+
+static gboolean
+accumulate_first_attributes (gpointer result,
+                             gpointer user_data)
+{
+  GPkcs11Array **attributes = (GPkcs11Array **)user_data;
+  g_assert (attributes);
+  *attributes = g_pkcs11_array_ref (result);
+  return FALSE; /* stop enumeration */
+}
+
+static gboolean
+accumulate_list_attributes (gpointer result,
+                            gpointer user_data)
+{
+  GList **results = (GList **)user_data;
+  g_assert (results);
+  *results = g_list_append (*results, g_pkcs11_array_ref (result));
+  return TRUE; /* continue enumeration */
+}
+
+static gboolean
+accumulate_first_object (gpointer result,
+                         gpointer user_data)
+{
+  GObject **object = (GObject **)user_data;
+  g_assert (object);
+  *object = g_object_ref (result);
+  return FALSE; /* stop enumeration */
+}
+
+static gboolean
+accumulate_list_objects (gpointer result,
+                         gpointer user_data)
+{
+  GList **results = (GList **)user_data;
+  g_assert (results);
+  *results = g_list_append (*results, g_object_ref (result));
+  return TRUE; /* continue enumeration */
+}
+
+static GPkcs11EnumerateState
+enumerate_call_accumulator (GPkcs11Accumulator accumulator,
+                            gpointer           result,
+                            gpointer           user_data)
+{
+  g_assert (accumulator);
+
+  if (!(accumulator) (result, user_data))
+    return G_PKCS11_ENUMERATE_STOP;
+
+  return G_PKCS11_ENUMERATE_CONTINUE;
+}
+
+static GPkcs11EnumerateState
+enumerate_assertion_exists_in_slot (GPkcs11Slot         *slot,
+                                    GTlsInteraction     *interaction,
+                                    GPkcs11Array        *match,
+                                    GPkcs11Accumulator   accumulator,
+                                    gpointer             user_data,
+                                    GCancellable        *cancellable,
+                                    GError             **error)
+{
+  GPkcs11EnumerateState state;
+
+  state = g_pkcs11_slot_enumerate (slot, interaction, match->attrs, match->count,
+                                   FALSE, NULL, 0, accumulate_stop, NULL,
+                                   cancellable, error);
+
+  /* A stop means that something matched */
+  if (state == G_PKCS11_ENUMERATE_STOP)
+    return enumerate_call_accumulator (accumulator, NULL, user_data);
+
+  return state;
+}
+
+static GPkcs11EnumerateState
+enumerate_assertion_exists_in_database (GTlsDatabaseGnutlsPkcs11   *self,
+                                        GTlsInteraction            *interaction,
+                                        GPkcs11Array               *match,
+                                        GPkcs11Accumulator          accumulator,
+                                        gpointer                    user_data,
+                                        GCancellable               *cancellable,
+                                        GError                    **error)
+{
+  GPkcs11EnumerateState state = G_PKCS11_ENUMERATE_CONTINUE;
+  gboolean slot_matched;
+  GPkcs11Slot *slot;
+  GList *l, *t;
+
+  for (l = self->pkcs11_slots; l != NULL; l = g_list_next (l))
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        return G_PKCS11_ENUMERATE_FAILED;
+
+      slot = l->data;
+
+      /* We only search for assertions on slots that match the trust-lookup uris */
+      slot_matched = FALSE;
+      for (t = self->trust_uris; !slot_matched && t != NULL; t = g_list_next (t))
+          slot_matched = g_pkcs11_slot_matches_uri (slot, t->data);
+      if (!slot_matched)
+        continue;
+
+      state = enumerate_assertion_exists_in_slot (slot, interaction, match, accumulator,
+                                                  user_data, cancellable, error);
+      if (state != G_PKCS11_ENUMERATE_CONTINUE)
+        break;
+  }
+
+  return state;
+}
+
+static gboolean
+g_tls_database_gnutls_pkcs11_lookup_assertion (GTlsDatabaseGnutlsPkcs11     *self,
+                                               GTlsCertificateGnutls        *certificate,
+                                               GTlsDatabaseGnutlsAssertion   assertion,
+                                               const gchar                  *purpose,
+                                               GSocketConnectable           *identity,
+                                               GCancellable                 *cancellable,
+                                               GError                      **error)
+{
+  GByteArray *der = NULL;
+  gboolean found, ready;
+  GPkcs11Array *match;
+  const gchar *peer;
+
+  ready = FALSE;
+  found = FALSE;
+  match = g_pkcs11_array_new ();
+
+  if (assertion == G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE ||
+      assertion == G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE)
+    {
+      g_object_get (certificate, "certificate", &der, NULL);
+      g_return_val_if_fail (der, FALSE);
+      g_pkcs11_array_add_value (match, CKA_X_CERTIFICATE_VALUE, der->data, der->len);
+      g_byte_array_unref (der);
+
+      g_pkcs11_array_add_value (match, CKA_X_PURPOSE, purpose, -1);
+
+      if (assertion == G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE)
+        {
+          g_pkcs11_array_add_ulong (match, CKA_X_ASSERTION_TYPE, CKT_X_ANCHORED_CERTIFICATE);
+          ready = TRUE;
+        }
+      else if (assertion == G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE)
+        {
+          g_pkcs11_array_add_ulong (match, CKA_X_ASSERTION_TYPE, CKT_X_PINNED_CERTIFICATE);
+          peer = calculate_peer_for_identity (identity);
+          if (peer)
+            {
+              g_pkcs11_array_add_value (match, CKA_X_PEER, peer, -1);
+              ready = TRUE;
+            }
+        }
+    }
+
+  if (ready == TRUE)
+      enumerate_assertion_exists_in_database (self, NULL, match, accumulate_exists,
+                                              &found, cancellable, error);
+
+  g_pkcs11_array_unref (match);
+  return found;
+}
+
+static GPkcs11EnumerateState
+enumerate_keypair_for_certificate (GPkcs11Slot         *slot,
+                                   GTlsInteraction     *interaction,
+                                   GPkcs11Array        *match_certificate,
+                                   GPkcs11Accumulator   accumulator,
+                                   gpointer             user_data,
+                                   GCancellable        *cancellable,
+                                   GError             **error)
+{
+  static CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY;
+  GPkcs11Array *private_key_attrs = NULL;
+  const CK_ATTRIBUTE *id_attribute;
+  CK_ATTRIBUTE match[2];
+  GTlsCertificate *certificate;
+  GPkcs11EnumerateState state;
+
+  /*
+   * We need to find a private key that matches the certificate.
+   *
+   * The PKCS#11 standard strongly suggests the norm that matching certificates
+   * and keys have the same CKA_ID. This is how we lookup the key that matches
+   * a certificate.
+   */
+
+  id_attribute = g_pkcs11_array_find (match_certificate, CKA_ID);
+  if (id_attribute == NULL)
+    return TRUE;
+
+  match[0].type = CKA_ID;
+  match[0].pValue = id_attribute->pValue;
+  match[0].ulValueLen = id_attribute->ulValueLen;
+  match[1].type = CKA_CLASS;
+  match[1].pValue = &key_class;
+  match[1].ulValueLen = sizeof (key_class);
+
+  g_assert (private_key_attrs == NULL);
+  state = g_pkcs11_slot_enumerate (slot, interaction, match, G_N_ELEMENTS (match), TRUE,
+                                   KEY_ATTRIBUTE_TYPES, G_N_ELEMENTS (KEY_ATTRIBUTE_TYPES),
+                                   accumulate_first_attributes, &private_key_attrs,
+                                   cancellable, error);
+
+  if (state == G_PKCS11_ENUMERATE_FAILED)
+    return state;
+
+  state = G_PKCS11_ENUMERATE_CONTINUE;
+  if (private_key_attrs)
+    {
+      /* We searched for public key (see above) so change attributes to look like private */
+      g_pkcs11_array_set_ulong (private_key_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+      certificate = create_database_pkcs11_certificate (slot, match_certificate,
+                                                        private_key_attrs);
+      g_pkcs11_array_unref (private_key_attrs);
+
+      if (certificate)
+        {
+          state = enumerate_call_accumulator (accumulator, certificate, user_data);
+          g_object_unref (certificate);
+        }
+    }
+
+  return state;
+}
+
+static GPkcs11EnumerateState
+enumerate_keypairs_in_slot (GPkcs11Slot         *slot,
+                            GTlsInteraction     *interaction,
+                            CK_ATTRIBUTE_PTR     match,
+                            CK_ULONG             match_count,
+                            GPkcs11Accumulator   accumulator,
+                            gpointer             user_data,
+                            GCancellable        *cancellable,
+                            GError             **error)
+{
+  GPkcs11EnumerateState state;
+  GList *results = NULL;
+  GList *l;
+
+  /*
+   * Find all the certificates that match for this slot, and then below
+   * we lookup to see if there's a private key for any of them.
+   *
+   * Note that we shouldn't be doing two find operations at once, because
+   * this may use too many sessions on smart cards and fragile drivers. So
+   * that's why we list all certificates, complete that find operation, and
+   * then do more find ops looking for private keys.
+   */
+
+  state = g_pkcs11_slot_enumerate (slot, interaction, match, match_count, FALSE,
+                                   CERTIFICATE_ATTRIBUTE_TYPES,
+                                   G_N_ELEMENTS (CERTIFICATE_ATTRIBUTE_TYPES),
+                                   accumulate_list_attributes, &results,
+                                   cancellable, error);
+  if (state == G_PKCS11_ENUMERATE_CONTINUE)
+    {
+      for (l = results; l != NULL; l = g_list_next (l))
+        {
+          state = enumerate_keypair_for_certificate (slot, interaction, l->data, accumulator,
+                                                     user_data, cancellable, error);
+          if (state != G_PKCS11_ENUMERATE_CONTINUE)
+            break;
+        }
+    }
+
+  for (l = results; l != NULL; l = g_list_next (l))
+    g_pkcs11_array_unref (l->data);
+  g_list_free (results);
+
+  return state;
+}
+
+typedef struct {
+  GPkcs11Accumulator accumulator;
+  gpointer user_data;
+  GPkcs11Slot *slot;
+} enumerate_certificates_closure;
+
+static gboolean
+accumulate_wrap_into_certificate (gpointer result,
+                                  gpointer user_data)
+{
+  GPkcs11EnumerateState state = G_PKCS11_ENUMERATE_CONTINUE;
+  enumerate_certificates_closure *closure = user_data;
+  GTlsCertificate *certificate;
+
+  certificate = create_database_pkcs11_certificate (closure->slot,
+                                                    result, NULL);
+  if (certificate)
+    {
+      state = enumerate_call_accumulator (closure->accumulator, certificate,
+                                          closure->user_data);
+      g_object_unref (certificate);
+    }
+
+  return (state == G_PKCS11_ENUMERATE_CONTINUE);
+}
+
+static GPkcs11EnumerateState
+enumerate_certificates_in_slot (GPkcs11Slot         *slot,
+                                GTlsInteraction     *interaction,
+                                CK_ATTRIBUTE_PTR     match,
+                                CK_ULONG             match_count,
+                                GPkcs11Accumulator   accumulator,
+                                gpointer             user_data,
+                                GCancellable        *cancellable,
+                                GError             **error)
+{
+  enumerate_certificates_closure closure = { accumulator, user_data, slot };
+
+  /*
+   * We create the certificates inline, so we can stop the enumeration early
+   * if only one certificate is necessary, but a whole bunch match. We provide
+   * our own accumulator here, turning the attributes into certificates and
+   * then calling the original accumulator.
+   */
+
+  return g_pkcs11_slot_enumerate (slot, interaction, match, match_count, FALSE,
+                                  CERTIFICATE_ATTRIBUTE_TYPES,
+                                  G_N_ELEMENTS (CERTIFICATE_ATTRIBUTE_TYPES),
+                                  accumulate_wrap_into_certificate,
+                                  &closure, cancellable, error);
+}
+
+static GPkcs11EnumerateState
+enumerate_certificates_in_database (GTlsDatabaseGnutlsPkcs11  *self,
+                                    GTlsInteraction           *interaction,
+                                    GTlsDatabaseLookupFlags    flags,
+                                    CK_ATTRIBUTE_PTR           match,
+                                    CK_ULONG                   match_count,
+                                    P11KitUri                 *match_slot_to_uri,
+                                    GPkcs11Accumulator         accumulator,
+                                    gpointer                   user_data,
+                                    GCancellable              *cancellable,
+                                    GError                   **error)
+{
+  GPkcs11EnumerateState state = G_PKCS11_ENUMERATE_CONTINUE;
+  GPkcs11Slot *slot;
+  GList *l;
+
+  /* These are the flags we support */
+  if (flags & ~(G_TLS_DATABASE_LOOKUP_KEYPAIR))
+    return G_PKCS11_ENUMERATE_CONTINUE;
+
+  for (l = self->pkcs11_slots; l; l = g_list_next (l))
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        return G_PKCS11_ENUMERATE_FAILED;
+
+      slot = l->data;
+
+      /* If the slot doesn't match the URI (when one is present) nothing matches */
+      if (match_slot_to_uri && !g_pkcs11_slot_matches_uri (slot, match_slot_to_uri))
+        continue;
+
+      if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
+        {
+          state = enumerate_keypairs_in_slot (slot, interaction, match,
+                                              match_count, accumulator, user_data,
+                                              cancellable, error);
+
+        }
+      else
+        {
+          state = enumerate_certificates_in_slot (slot, interaction, match,
+                                                  match_count, accumulator,
+                                                  user_data, cancellable, error);
+        }
+
+      if (state != G_PKCS11_ENUMERATE_CONTINUE)
+        break;
+    }
+
+  return state;
+}
+
+static GTlsCertificate *
+g_tls_database_gnutls_pkcs11_lookup_certificate_issuer (GTlsDatabase             *database,
+                                                        GTlsCertificate          *certificate,
+                                                        GTlsInteraction          *interaction,
+                                                        GTlsDatabaseLookupFlags   flags,
+                                                        GCancellable             *cancellable,
+                                                        GError                  **error)
+{
+  GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+  GTlsCertificate *result = NULL;
+  GPkcs11Array *match = NULL;
+  gnutls_x509_crt_t cert;
+  gnutls_datum_t dn;
+  int gerr;
+
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), NULL);
+
+  /* Dig out the issuer of this certificate */
+  cert = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (certificate));
+  gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
+  if (gerr < 0)
+    {
+      g_warning ("failed to get issuer of certificate: %s", gnutls_strerror (gerr));
+      return NULL;
+    }
+
+  match = g_pkcs11_array_new ();
+  g_pkcs11_array_add_ulong (match, CKA_CLASS, CKO_CERTIFICATE);
+  g_pkcs11_array_add_ulong (match, CKA_CERTIFICATE_TYPE, CKC_X_509);
+  g_pkcs11_array_add_value (match, CKA_SUBJECT, dn.data, dn.size);
+  gnutls_free (dn.data);
+
+  enumerate_certificates_in_database (self, interaction, flags, match->attrs,
+                                      match->count, NULL, accumulate_first_object,
+                                      &result, cancellable, error);
+  g_pkcs11_array_unref (match);
+  return result;
+}
+
+static GList *
+g_tls_database_gnutls_pkcs11_lookup_certificates_issued_by (GTlsDatabase             *database,
+                                                            GByteArray               *issuer_subject,
+                                                            GTlsInteraction          *interaction,
+                                                            GTlsDatabaseLookupFlags   flags,
+                                                            GCancellable             *cancellable,
+                                                            GError                  **error)
+{
+  GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+  GList *l, *results = NULL;
+  GPkcs11Array *match = NULL;
+  GPkcs11EnumerateState state;
+
+  g_return_val_if_fail (issuer_subject, NULL);
+
+  match = g_pkcs11_array_new ();
+  g_pkcs11_array_add_ulong (match, CKA_CLASS, CKO_CERTIFICATE);
+  g_pkcs11_array_add_ulong (match, CKA_CERTIFICATE_TYPE, CKC_X_509);
+  g_pkcs11_array_add_value (match, CKA_ISSUER, issuer_subject->data, issuer_subject->len);
+
+  state = enumerate_certificates_in_database (self, interaction, flags, match->attrs,
+                                              match->count, NULL, accumulate_list_objects,
+                                              &results, cancellable, error);
+
+  /* Could have had partial success, don't leak memory */
+  if (state == G_PKCS11_ENUMERATE_FAILED)
+    {
+      for (l = results; l != NULL; l = g_list_next (l))
+        g_object_unref (l->data);
+      g_list_free (results);
+      results = NULL;
+    }
+
+  g_pkcs11_array_unref (match);
+  return results;
+}
+
+static gchar *
+g_tls_database_gnutls_pkcs11_create_certificate_handle (GTlsDatabase    *database,
+                                                        GTlsCertificate *certificate)
+{
+  return g_tls_certificate_gnutls_pkcs11_build_certificate_uri (G_TLS_CERTIFICATE_GNUTLS (certificate), 
NULL);
+}
+
+static GTlsCertificate *
+g_tls_database_gnutls_pkcs11_lookup_certificate_for_handle (GTlsDatabase             *database,
+                                                            const gchar              *handle,
+                                                            GTlsInteraction          *interaction,
+                                                            GTlsDatabaseLookupFlags   flags,
+                                                            GCancellable             *cancellable,
+                                                            GError                  **error)
+{
+  GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+  GTlsCertificate *result = NULL;
+  P11KitUri *uri;
+  CK_ATTRIBUTE_PTR match;
+  CK_ULONG match_count;
+  int ret;
+
+  /* The handle is a PKCS#11 URI */
+
+  /* These are the flags we support */
+  if (flags & ~(G_TLS_DATABASE_LOOKUP_KEYPAIR))
+    return NULL;
+
+  uri = p11_kit_uri_new ();
+  if (uri == NULL)
+    g_error ("out of memory in p11_kit_uri_new()");
+
+  ret = p11_kit_uri_parse (handle, P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE |
+                           P11_KIT_URI_FOR_MODULE_WITH_VERSION, uri);
+  if (ret == P11_KIT_URI_NO_MEMORY)
+    {
+      g_error ("out of memory in p11_kit_uri_parse()");
+    }
+  else if (ret != P11_KIT_URI_OK)
+    {
+      p11_kit_uri_free (uri);
+      g_set_error (error, G_PKCS11_ERROR, G_PKCS11_ERROR_BAD_URI,
+                   "Invalid PKCS#11 URI: %s", handle);
+      return NULL;
+    }
+
+  match = p11_kit_uri_get_attributes (uri, &match_count);
+  enumerate_certificates_in_database (self, interaction, flags, match, match_count,
+                                      uri, accumulate_first_object, &result,
+                                      cancellable, error);
+
+  p11_kit_uri_free (uri);
+  return result;
+}
+
+#define BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT 10
+
+enum {
+  STATUS_FAILURE,
+  STATUS_INCOMPLETE,
+  STATUS_SELFSIGNED,
+  STATUS_ANCHORED,
+  STATUS_RECURSION_LIMIT_REACHED
+};
+
+static gboolean
+is_self_signed (GTlsCertificateGnutls *certificate)
+{
+  const gnutls_x509_crt_t cert = g_tls_certificate_gnutls_get_cert (certificate);
+  return (gnutls_x509_crt_check_issuer (cert, cert) > 0);
+}
+
+static gint
+build_certificate_chain (GTlsDatabaseGnutlsPkcs11  *self,
+                         GTlsCertificateGnutls     *certificate,
+                         GTlsCertificateGnutls     *previous,
+                         gboolean                   certificate_is_from_db,
+                         guint                      recursion_depth,
+                         const gchar               *purpose,
+                         GSocketConnectable        *identity,
+                         GTlsInteraction           *interaction,
+                         GCancellable              *cancellable,
+                         GTlsCertificateGnutls    **anchor,
+                         GError                   **error)
+{
+  GTlsCertificate *issuer;
+  gint status;
+
+  if (recursion_depth++ > BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT)
+    return STATUS_RECURSION_LIMIT_REACHED;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return STATUS_FAILURE;
+
+  /* Look up whether this certificate is an anchor */
+  if (g_tls_database_gnutls_pkcs11_lookup_assertion (self, certificate,
+                                                     G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE,
+                                                     purpose, identity, cancellable, error))
+    {
+      g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+      *anchor = certificate;
+      return STATUS_ANCHORED;
+    }
+  else if (*error)
+    {
+      return STATUS_FAILURE;
+    }
+
+  /* Is it self-signed? */
+  if (is_self_signed (certificate))
+    {
+      /*
+       * Since at this point we would fail with 'self-signed', can we replace
+       * this certificate with one from the database and do better?
+       */
+      if (previous && !certificate_is_from_db)
+        {
+          issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+                                                             G_TLS_CERTIFICATE (previous),
+                                                             interaction,
+                                                             G_TLS_DATABASE_LOOKUP_NONE,
+                                                             cancellable, error);
+          if (*error)
+            {
+              return STATUS_FAILURE;
+            }
+          else if (issuer)
+            {
+              /* Replaced with certificate in the db, restart step again with this certificate */
+              g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+              certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+              g_tls_certificate_gnutls_set_issuer (previous, certificate);
+              g_object_unref (issuer);
+
+              return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
+                                              purpose, identity, interaction, cancellable, anchor, error);
+            }
+        }
+
+      g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+      return STATUS_SELFSIGNED;
+    }
+
+  previous = certificate;
+
+  /* Bring over the next certificate in the chain */
+  issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (certificate));
+  if (issuer)
+    {
+      g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+      certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+
+      status = build_certificate_chain (self, certificate, previous, FALSE, recursion_depth,
+                                        purpose, identity, interaction, cancellable, anchor, error);
+      if (status != STATUS_INCOMPLETE)
+        {
+          return status;
+        }
+    }
+
+  /* Search for the next certificate in chain */
+  issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+                                                     G_TLS_CERTIFICATE (certificate),
+                                                     interaction,
+                                                     G_TLS_DATABASE_LOOKUP_NONE,
+                                                     cancellable, error);
+  if (*error)
+    return STATUS_FAILURE;
+
+  if (!issuer)
+    return STATUS_INCOMPLETE;
+
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+  g_tls_certificate_gnutls_set_issuer (certificate, G_TLS_CERTIFICATE_GNUTLS (issuer));
+  certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+  g_object_unref (issuer);
+
+  return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
+                                  purpose, identity, interaction, cancellable, anchor, error);
+}
+
+static GTlsCertificateFlags
+double_check_before_after_dates (GTlsCertificateGnutls *chain)
+{
+  GTlsCertificateFlags gtls_flags = 0;
+  gnutls_x509_crt_t cert;
+  time_t t, now;
+
+  now = time (NULL);
+  while (chain)
+    {
+      cert = g_tls_certificate_gnutls_get_cert (chain);
+      t = gnutls_x509_crt_get_activation_time (cert);
+      if (t == (time_t) -1 || t > now)
+        gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+
+      t = gnutls_x509_crt_get_expiration_time (cert);
+      if (t == (time_t) -1 || t < now)
+        gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
+
+      chain = G_TLS_CERTIFICATE_GNUTLS (g_tls_certificate_get_issuer
+                                        (G_TLS_CERTIFICATE (chain)));
+    }
+
+  return gtls_flags;
+}
+
+static void
+convert_certificate_chain_to_gnutls (GTlsCertificateGnutls  *chain,
+                                     gnutls_x509_crt_t     **gnutls_chain,
+                                     guint                  *gnutls_chain_length)
+{
+  GTlsCertificate *cert;
+  guint i;
+
+  g_assert (gnutls_chain);
+  g_assert (gnutls_chain_length);
+
+  for (*gnutls_chain_length = 0, cert = G_TLS_CERTIFICATE (chain);
+       cert; cert = g_tls_certificate_get_issuer (cert))
+    ++(*gnutls_chain_length);
+
+  *gnutls_chain = g_new0 (gnutls_x509_crt_t, *gnutls_chain_length);
+
+  for (i = 0, cert = G_TLS_CERTIFICATE (chain);
+       cert; cert = g_tls_certificate_get_issuer (cert), ++i)
+    (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
+
+  g_assert (i == *gnutls_chain_length);
+}
+
+static GTlsCertificateFlags
+g_tls_database_gnutls_pkcs11_verify_chain (GTlsDatabase             *database,
+                                           GTlsCertificate          *chain,
+                                           const gchar              *purpose,
+                                           GSocketConnectable       *identity,
+                                           GTlsInteraction          *interaction,
+                                           GTlsDatabaseVerifyFlags   flags,
+                                           GCancellable             *cancellable,
+                                           GError                  **error)
+{
+  GTlsDatabaseGnutlsPkcs11 *self;
+  GTlsCertificateFlags result;
+  GTlsCertificateGnutls *certificate;
+  GError *err = NULL;
+  GTlsCertificateGnutls *anchor;
+  guint gnutls_result;
+  gnutls_x509_crt_t *certs, *anchors;
+  guint certs_length, anchors_length;
+  gint status, gerr;
+  guint recursion_depth = 0;
+
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (chain),
+                        G_TLS_CERTIFICATE_GENERIC_ERROR);
+  g_assert (purpose);
+
+  self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+  certificate = G_TLS_CERTIFICATE_GNUTLS (chain);
+
+  /* First check for pinned certificate */
+  if (g_tls_database_gnutls_pkcs11_lookup_assertion (self, certificate,
+                                                     G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE,
+                                                     purpose, identity, cancellable, &err))
+    {
+      /*
+       * A pinned certificate is verified on its own, without any further
+       * verification.
+       */
+      g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+      return 0;
+    }
+
+  if (err)
+    {
+      g_propagate_error (error, err);
+      return G_TLS_CERTIFICATE_GENERIC_ERROR;
+    }
+
+  anchor = NULL;
+  status = build_certificate_chain (self, certificate, NULL, FALSE, recursion_depth,
+                                    purpose, identity, interaction, cancellable, &anchor, &err);
+  if (status == STATUS_FAILURE)
+    {
+      g_propagate_error (error, err);
+      return G_TLS_CERTIFICATE_GENERIC_ERROR;
+    }
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+  convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
+                                       &certs, &certs_length);
+
+  if (anchor)
+    {
+      g_assert (g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (anchor)) == NULL);
+      convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (anchor),
+                                           &anchors, &anchors_length);
+    }
+  else
+    {
+      anchors = NULL;
+      anchors_length = 0;
+    }
+
+  gerr = gnutls_x509_crt_list_verify (certs, certs_length,
+                                      anchors, anchors_length,
+                                      NULL, 0, 0,
+                                      &gnutls_result);
+
+  g_free (certs);
+  g_free (anchors);
+
+  if (gerr != 0)
+    return G_TLS_CERTIFICATE_GENERIC_ERROR;
+  else if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+  result = g_tls_certificate_gnutls_convert_flags (gnutls_result);
+
+  /*
+   * We have to check these ourselves since gnutls_x509_crt_list_verify
+   * won't bother if it gets an UNKNOWN_CA.
+   */
+  result |= double_check_before_after_dates (G_TLS_CERTIFICATE_GNUTLS (chain));
+
+  if (identity)
+    result |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (chain),
+                                                        identity);
+
+  return result;
+}
+
+static void
+g_tls_database_gnutls_pkcs11_class_init (GTlsDatabaseGnutlsPkcs11Class *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+
+  gobject_class->finalize = g_tls_database_gnutls_pkcs11_finalize;
+
+  database_class->create_certificate_handle = g_tls_database_gnutls_pkcs11_create_certificate_handle;
+  database_class->lookup_certificate_issuer = g_tls_database_gnutls_pkcs11_lookup_certificate_issuer;
+  database_class->lookup_certificates_issued_by = g_tls_database_gnutls_pkcs11_lookup_certificates_issued_by;
+  database_class->lookup_certificate_for_handle = g_tls_database_gnutls_pkcs11_lookup_certificate_for_handle;
+  database_class->verify_chain = g_tls_database_gnutls_pkcs11_verify_chain;
+}
+
+static gboolean
+g_tls_database_gnutls_pkcs11_initable_init (GInitable     *initable,
+                                            GCancellable  *cancellable,
+                                            GError       **error)
+{
+  GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (initable);
+  GError *err = NULL;
+  gboolean any_success = FALSE;
+  gboolean any_failure = FALSE;
+  guint i;
+
+  g_return_val_if_fail (!self->modules, FALSE);
+
+  self->modules = p11_kit_modules_load (NULL, 0);
+  if (self->modules == NULL) {
+    g_set_error_literal (error, G_PKCS11_ERROR, CKR_FUNCTION_FAILED, p11_kit_message ());
+    return FALSE;
+  }
+
+  for (i = 0; self->modules[i] != NULL; i++)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        {
+          any_failure = TRUE;
+          any_success = FALSE;
+          break;
+        }
+
+      if (discover_module_slots_and_options (self, self->modules[i], &err))
+        {
+          /* A module was setup correctly */
+          any_success = TRUE;
+          g_clear_error (error);
+        }
+      else
+        {
+          /* No module success, first module failure */
+          if (!any_success && !any_failure)
+            g_propagate_error (error, err);
+          any_failure = TRUE;
+        }
+    }
+
+  return (any_failure && !any_success) ? FALSE : TRUE;
+}
+
+static void
+g_tls_database_gnutls_pkcs11_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = g_tls_database_gnutls_pkcs11_initable_init;
+}
+
+GTlsDatabase *
+g_tls_database_gnutls_pkcs11_new (GError **error)
+{
+  g_return_val_if_fail (!error || !*error, NULL);
+  return g_initable_new (G_TYPE_TLS_DATABASE_GNUTLS_PKCS11, NULL, error, NULL);
+}
diff --git a/tls/gnutls/gtlsdatabase-gnutls-pkcs11.h b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.h
new file mode 100644
index 0000000..cd7a52d
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_TLS_DATABASE_GNUTLS_PKCS11_H__
+#define __G_TLS_DATABASE_GNUTLS_PKCS11_H__
+
+#include <gio/gio.h>
+
+#include "gtlsdatabase-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_DATABASE_GNUTLS_PKCS11            (g_tls_database_gnutls_pkcs11_get_type ())
+
+G_DECLARE_FINAL_TYPE (GTlsDatabaseGnutlsPkcs11, g_tls_database_gnutls_pkcs11, G, TLS_DATABASE_GNUTLS_PKCS11, 
GTlsDatabase)
+
+GTlsDatabase*                g_tls_database_gnutls_pkcs11_new                   (GError **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_DATABASE_GNUTLS_PKCS11_H___ */
diff --git a/tls/gnutls/meson.build b/tls/gnutls/meson.build
index 4ff127e..4aff069 100644
--- a/tls/gnutls/meson.build
+++ b/tls/gnutls/meson.build
@@ -21,6 +21,16 @@ deps = [
   gnutls_dep
 ]
 
+if pkcs11_dep.found()
+  sources += files(
+    'gtlsdatabase-gnutls-pkcs11.c',
+  )
+
+  incs += tls_inc
+
+  deps += libgiopkcs11_dep
+endif
+
 module = shared_module(
   'giognutls',
   sources: sources,
diff --git a/tls/pkcs11/gpkcs11array.c b/tls/pkcs11/gpkcs11array.c
new file mode 100644
index 0000000..c3627f4
--- /dev/null
+++ b/tls/pkcs11/gpkcs11array.c
@@ -0,0 +1,282 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gpkcs11array.h"
+
+#include <string.h>
+
+G_DEFINE_BOXED_TYPE (GPkcs11Array, g_pkcs11_array, g_pkcs11_array_ref, g_pkcs11_array_unref);
+
+typedef struct _GRealPkcs11Array
+{
+  CK_ATTRIBUTE *attrs;
+  CK_ULONG len;
+  volatile gint ref_count;
+} GRealPkcs11Array;
+
+GPkcs11Array*
+g_pkcs11_array_new (void)
+{
+  GRealPkcs11Array *array = g_slice_new (GRealPkcs11Array);
+
+  array->attrs = NULL;
+  array->len = 0;
+  array->ref_count = 1;
+
+  return (GPkcs11Array *)array;
+}
+
+void
+g_pkcs11_array_add (GPkcs11Array *array,
+                    CK_ATTRIBUTE *attr)
+{
+  GRealPkcs11Array *rarray = (GRealPkcs11Array *)array;
+
+  g_return_if_fail (array);
+  g_return_if_fail (attr);
+  g_return_if_fail (attr->ulValueLen != (CK_ATTRIBUTE_TYPE)-1 || !attr->pValue);
+  g_return_if_fail (attr->pValue || !attr->ulValueLen);
+
+  rarray->attrs = g_renew (CK_ATTRIBUTE, rarray->attrs, rarray->len + 1);
+  memcpy (rarray->attrs + rarray->len, attr, sizeof (CK_ATTRIBUTE));
+  if (attr->pValue)
+    rarray->attrs[rarray->len].pValue = g_memdup (attr->pValue, attr->ulValueLen);
+  rarray->len++;
+}
+
+void
+g_pkcs11_array_add_value (GPkcs11Array      *array,
+                          CK_ATTRIBUTE_TYPE  type,
+                          gconstpointer      value,
+                          gssize              length)
+{
+  CK_ATTRIBUTE attr;
+
+  g_return_if_fail (array);
+
+  if (length < 0)
+    length = strlen (value);
+
+  attr.type = type;
+  attr.pValue = (gpointer)value;
+  attr.ulValueLen = length;
+  g_pkcs11_array_add (array, &attr);
+}
+
+void
+g_pkcs11_array_add_boolean (GPkcs11Array      *array,
+                            CK_ATTRIBUTE_TYPE  attr_type,
+                            gboolean           value)
+{
+  CK_ATTRIBUTE attr;
+  CK_BBOOL bval;
+
+  g_return_if_fail (array);
+
+  bval = value ? CK_TRUE : CK_FALSE;
+  attr.type = attr_type;
+  attr.pValue = &bval;
+  attr.ulValueLen = sizeof (bval);
+  g_pkcs11_array_add (array, &attr);
+}
+
+void
+g_pkcs11_array_add_ulong (GPkcs11Array      *array,
+                          CK_ATTRIBUTE_TYPE  type,
+                          gulong             value)
+{
+  CK_ATTRIBUTE attr;
+  CK_ULONG uval;
+
+  g_return_if_fail (array);
+
+  uval = value;
+  attr.type = type;
+  attr.pValue = &uval;
+  attr.ulValueLen = sizeof (uval);
+  g_pkcs11_array_add (array, &attr);
+}
+
+void
+g_pkcs11_array_set (GPkcs11Array *array,
+                    CK_ATTRIBUTE *attr)
+{
+  CK_ATTRIBUTE *previous;
+
+  g_return_if_fail (array);
+  g_return_if_fail (attr);
+  g_return_if_fail (attr->ulValueLen != (CK_ATTRIBUTE_TYPE)-1 || !attr->pValue);
+  g_return_if_fail (attr->pValue || !attr->ulValueLen);
+
+  previous = (CK_ATTRIBUTE *)g_pkcs11_array_find (array, attr->type);
+  if (previous == NULL)
+    {
+      g_pkcs11_array_add (array, attr);
+    }
+  else
+    {
+      g_free (previous->pValue);
+      previous->pValue = g_memdup (attr->pValue, attr->ulValueLen);
+      previous->ulValueLen = attr->ulValueLen;
+    }
+}
+
+void
+g_pkcs11_array_set_value (GPkcs11Array      *array,
+                          CK_ATTRIBUTE_TYPE  type,
+                          gconstpointer      value,
+                          gssize              length)
+{
+  CK_ATTRIBUTE attr;
+
+  g_return_if_fail (array);
+
+  if (length < 0)
+    length = strlen (value);
+
+  attr.type = type;
+  attr.pValue = (gpointer)value;
+  attr.ulValueLen = length;
+  g_pkcs11_array_set (array, &attr);
+}
+
+void
+g_pkcs11_array_set_boolean (GPkcs11Array      *array,
+                            CK_ATTRIBUTE_TYPE  attr_type,
+                            gboolean           value)
+{
+  CK_ATTRIBUTE attr;
+  CK_BBOOL bval;
+
+  g_return_if_fail (array);
+
+  bval = value ? CK_TRUE : CK_FALSE;
+  attr.type = attr_type;
+  attr.pValue = &bval;
+  attr.ulValueLen = sizeof (bval);
+  g_pkcs11_array_set (array, &attr);
+}
+
+void
+g_pkcs11_array_set_ulong (GPkcs11Array      *array,
+                          CK_ATTRIBUTE_TYPE  type,
+                          gulong             value)
+{
+  CK_ATTRIBUTE attr;
+  CK_ULONG uval;
+
+  g_return_if_fail (array);
+
+  uval = value;
+  attr.type = type;
+  attr.pValue = &uval;
+  attr.ulValueLen = sizeof (uval);
+  g_pkcs11_array_set (array, &attr);
+}
+
+
+const CK_ATTRIBUTE*
+g_pkcs11_array_find (GPkcs11Array         *array,
+                     CK_ATTRIBUTE_TYPE     type)
+{
+    const CK_ATTRIBUTE* attr;
+    guint i;
+
+    g_return_val_if_fail (array, NULL);
+
+    for (i = 0; i < array->count; ++i)
+      {
+        attr = &g_pkcs11_array_index (array, i);
+        if (attr->type == type)
+          return attr;
+      }
+
+    return NULL;
+}
+
+gboolean
+g_pkcs11_array_find_boolean (GPkcs11Array         *array,
+                             CK_ATTRIBUTE_TYPE     type,
+                             gboolean             *value)
+{
+  const CK_ATTRIBUTE* attr;
+
+  g_return_val_if_fail (array, FALSE);
+  g_return_val_if_fail (value, FALSE);
+
+  attr = g_pkcs11_array_find (array, type);
+  if (!attr || !attr->pValue || attr->ulValueLen != sizeof (CK_BBOOL))
+    return FALSE;
+  *value = *((CK_BBOOL *)attr->pValue) ? TRUE : FALSE;
+  return TRUE;
+}
+
+gboolean
+g_pkcs11_array_find_ulong (GPkcs11Array         *array,
+                           CK_ATTRIBUTE_TYPE     type,
+                           gulong               *value)
+{
+  const CK_ATTRIBUTE* attr;
+
+  g_return_val_if_fail (array, FALSE);
+  g_return_val_if_fail (value, FALSE);
+
+  attr = g_pkcs11_array_find (array, type);
+  if (!attr || !attr->pValue || attr->ulValueLen != sizeof (CK_ULONG))
+    return FALSE;
+  *value = *((CK_ULONG *)attr->pValue);
+  return TRUE;
+}
+
+GPkcs11Array*
+g_pkcs11_array_ref (GPkcs11Array *array)
+{
+  GRealPkcs11Array *rarray = (GRealPkcs11Array *)array;
+
+  g_return_val_if_fail (array, NULL);
+  g_return_val_if_fail (g_atomic_int_get (&rarray->ref_count) > 0, array);
+  g_atomic_int_inc (&rarray->ref_count);
+  return array;
+}
+
+void
+g_pkcs11_array_unref (GPkcs11Array *array)
+{
+  GRealPkcs11Array *rarray = (GRealPkcs11Array *)array;
+  CK_ULONG i;
+
+  g_return_if_fail (array);
+  g_return_if_fail (g_atomic_int_get (&rarray->ref_count) > 0);
+  if (g_atomic_int_dec_and_test (&rarray->ref_count))
+    {
+      for (i = 0; i < rarray->len; ++i)
+        g_free (rarray->attrs[i].pValue);
+      g_free (rarray->attrs);
+      g_slice_free1 (sizeof (GRealPkcs11Array), array);
+    }
+}
diff --git a/tls/pkcs11/gpkcs11array.h b/tls/pkcs11/gpkcs11array.h
new file mode 100644
index 0000000..04d1a66
--- /dev/null
+++ b/tls/pkcs11/gpkcs11array.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_PKCS11_ARRAY_H__
+#define __G_PKCS11_ARRAY_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <p11-kit/pkcs11.h>
+
+#include <p11-kit/uri.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GPkcs11Array       GPkcs11Array;
+
+struct _GPkcs11Array
+{
+  CK_ATTRIBUTE *attrs;
+  CK_ULONG      count;
+};
+
+#define             G_TYPE_PKCS11_ARRAY                     (g_pkcs11_array_get_type ())
+
+GType               g_pkcs11_array_get_type                 (void) G_GNUC_CONST;
+
+GPkcs11Array*       g_pkcs11_array_new                      (void);
+
+#define             g_pkcs11_array_index(array,index_)      ((array)->attrs)[index_]
+
+void                g_pkcs11_array_add                      (GPkcs11Array        *array,
+                                                             CK_ATTRIBUTE        *attr);
+
+void                g_pkcs11_array_add_value                (GPkcs11Array        *array,
+                                                             CK_ATTRIBUTE_TYPE    type,
+                                                             gconstpointer        value,
+                                                             gssize               length);
+
+void                g_pkcs11_array_add_boolean              (GPkcs11Array         *array,
+                                                             CK_ATTRIBUTE_TYPE     type,
+                                                             gboolean              value);
+
+void                g_pkcs11_array_add_ulong                (GPkcs11Array         *array,
+                                                             CK_ATTRIBUTE_TYPE     type,
+                                                             gulong                value);
+
+void                g_pkcs11_array_set                      (GPkcs11Array        *array,
+                                                             CK_ATTRIBUTE        *attr);
+
+void                g_pkcs11_array_set_value                (GPkcs11Array        *array,
+                                                             CK_ATTRIBUTE_TYPE    type,
+                                                             gconstpointer        value,
+                                                             gssize               length);
+
+void                g_pkcs11_array_set_boolean              (GPkcs11Array         *array,
+                                                             CK_ATTRIBUTE_TYPE     type,
+                                                             gboolean              value);
+
+void                g_pkcs11_array_set_ulong                (GPkcs11Array         *array,
+                                                             CK_ATTRIBUTE_TYPE     type,
+                                                             gulong                value);
+
+const CK_ATTRIBUTE* g_pkcs11_array_find                     (GPkcs11Array         *array,
+                                                             CK_ATTRIBUTE_TYPE     type);
+
+const CK_ATTRIBUTE* g_pkcs11_array_find_valid               (GPkcs11Array         *array,
+                                                             CK_ATTRIBUTE_TYPE     type);
+
+gboolean            g_pkcs11_array_find_boolean             (GPkcs11Array         *array,
+                                                             CK_ATTRIBUTE_TYPE     type,
+                                                             gboolean             *value);
+
+gboolean            g_pkcs11_array_find_ulong               (GPkcs11Array         *array,
+                                                             CK_ATTRIBUTE_TYPE     type,
+                                                             gulong               *value);
+
+GPkcs11Array*       g_pkcs11_array_ref                      (GPkcs11Array         *array);
+
+void                g_pkcs11_array_unref                    (GPkcs11Array         *array);
+
+G_END_DECLS
+
+#endif /* __G_PKCS11_ARRAY_H___ */
diff --git a/tls/pkcs11/gpkcs11pin.c b/tls/pkcs11/gpkcs11pin.c
new file mode 100644
index 0000000..6578bf4
--- /dev/null
+++ b/tls/pkcs11/gpkcs11pin.c
@@ -0,0 +1,159 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gpkcs11pin.h"
+#include <glib/gi18n-lib.h>
+
+enum
+{
+  PROP_0,
+
+  PROP_FLAGS,
+  PROP_DESCRIPTION
+};
+
+struct _GPkcs11Pin
+{
+  GTlsPassword parent_instance;
+
+  P11KitPin *pin;
+};
+
+G_DEFINE_TYPE (GPkcs11Pin, g_pkcs11_pin, G_TYPE_TLS_PASSWORD);
+
+static void
+g_pkcs11_pin_init (GPkcs11Pin *self)
+{
+}
+
+static void
+g_pkcs11_pin_finalize (GObject *object)
+{
+  GPkcs11Pin *self = G_PKCS11_PIN (object);
+
+  if (self->pin)
+    p11_kit_pin_unref (self->pin);
+
+  G_OBJECT_CLASS (g_pkcs11_pin_parent_class)->finalize (object);
+}
+
+static const guchar *
+g_pkcs11_pin_get_value (GTlsPassword  *password,
+                        gsize         *length)
+{
+  GPkcs11Pin *self = G_PKCS11_PIN (password);
+
+  if (!self->pin)
+    {
+      if (length)
+        *length = 0;
+      return NULL;
+    }
+
+  return p11_kit_pin_get_value (self->pin, length);
+}
+
+static void
+g_pkcs11_pin_set_value (GTlsPassword  *password,
+                        guchar        *value,
+                        gssize         length,
+                        GDestroyNotify destroy)
+{
+  GPkcs11Pin *self = G_PKCS11_PIN (password);
+
+  if (self->pin)
+    {
+      p11_kit_pin_unref (self->pin);
+      self->pin = NULL;
+    }
+
+  if (length < 0)
+    length = strlen ((gchar *)value);
+
+  self->pin = p11_kit_pin_new_for_buffer (value, length, destroy);
+}
+
+static const gchar *
+g_pkcs11_pin_get_default_warning (GTlsPassword  *password)
+{
+  GTlsPasswordFlags flags;
+
+  flags = g_tls_password_get_flags (password);
+
+  if (flags & G_TLS_PASSWORD_FINAL_TRY)
+    return _("This is the last chance to enter the PIN correctly before the token is locked.");
+  if (flags & G_TLS_PASSWORD_MANY_TRIES)
+    return _("Several PIN attempts have been incorrect, and the token will be locked after further 
failures.");
+  if (flags & G_TLS_PASSWORD_RETRY)
+    return _("The PIN entered is incorrect.");
+
+  return NULL;
+}
+
+
+static void
+g_pkcs11_pin_class_init (GPkcs11PinClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsPasswordClass *password_class = G_TLS_PASSWORD_CLASS (klass);
+
+  password_class->get_value = g_pkcs11_pin_get_value;
+  password_class->set_value = g_pkcs11_pin_set_value;
+  password_class->get_default_warning = g_pkcs11_pin_get_default_warning;
+
+  gobject_class->finalize     = g_pkcs11_pin_finalize;
+}
+
+GTlsPassword *
+g_pkcs11_pin_new (GTlsPasswordFlags  flags,
+                  const gchar       *description)
+{
+  GPkcs11Pin *self;
+
+  self = g_object_new (G_TYPE_PKCS11_PIN,
+                       "flags", flags,
+                       "description", description,
+                       NULL);
+
+  return G_TLS_PASSWORD (self);
+}
+
+
+P11KitPin *
+g_pkcs11_pin_steal_internal (GPkcs11Pin  *self)
+{
+  P11KitPin *pin;
+
+  g_return_val_if_fail (G_IS_PKCS11_PIN (self), NULL);
+
+  pin = self->pin;
+  self->pin = NULL;
+  return pin;
+}
diff --git a/tls/pkcs11/gpkcs11pin.h b/tls/pkcs11/gpkcs11pin.h
new file mode 100644
index 0000000..5fbb662
--- /dev/null
+++ b/tls/pkcs11/gpkcs11pin.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Pin, Output and Pkcs11ing Library
+ *
+ * Copyright © 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_PKCS11_PIN_H__
+#define __G_PKCS11_PIN_H__
+
+#include <gio/gio.h>
+#include <p11-kit/pin.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_PKCS11_PIN            (g_pkcs11_pin_get_type ())
+
+G_DECLARE_FINAL_TYPE (GPkcs11Pin, g_pkcs11_pin, G, PKCS11_PIN, GTlsPassword)
+
+GTlsPassword *          g_pkcs11_pin_new             (GTlsPasswordFlags  flags,
+                                                      const gchar       *description);
+
+P11KitPin *             g_pkcs11_pin_steal_internal  (GPkcs11Pin  *self);
+
+G_END_DECLS
+
+#endif /* __G_PKCS11_PIN_H___ */
diff --git a/tls/pkcs11/gpkcs11slot.c b/tls/pkcs11/gpkcs11slot.c
new file mode 100644
index 0000000..9b24dc0
--- /dev/null
+++ b/tls/pkcs11/gpkcs11slot.c
@@ -0,0 +1,618 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gpkcs11slot.h"
+
+#include "gpkcs11array.h"
+#include "gpkcs11pin.h"
+#include "gpkcs11util.h"
+
+#include <glib/gi18n.h>
+
+#include <p11-kit/p11-kit.h>
+#include <p11-kit/pin.h>
+
+#include <stdlib.h>
+
+enum {
+  PROP_0,
+  PROP_MODULE,
+  PROP_SLOT_ID
+};
+
+struct _GPkcs11Slot
+{
+  GObject parent_instance;
+
+  /* read-only after construct */
+  CK_FUNCTION_LIST_PTR module;
+  CK_SLOT_ID slot_id;
+
+  /* protected by mutex */
+  GMutex mutex;
+  CK_SESSION_HANDLE last_session;
+};
+
+G_DEFINE_TYPE (GPkcs11Slot, g_pkcs11_slot, G_TYPE_OBJECT);
+
+static gboolean
+check_if_session_logged_in (GPkcs11Slot        *self,
+                            CK_SESSION_HANDLE   session)
+{
+  CK_SESSION_INFO session_info;
+  CK_RV rv;
+
+  rv = (self->module->C_GetSessionInfo) (session, &session_info);
+  if (rv != CKR_OK)
+    return FALSE;
+
+  /* Already logged in */
+  if (session_info.state == CKS_RO_USER_FUNCTIONS ||
+      session_info.state == CKS_RW_USER_FUNCTIONS)
+    return TRUE;
+
+  return FALSE;
+}
+
+static gboolean
+session_login_protected_auth_path (GPkcs11Slot       *self,
+                                   CK_SESSION_HANDLE  session,
+                                   GError           **error)
+{
+  CK_RV rv;
+
+  rv = (self->module->C_Login) (session, CKU_USER, NULL, 0);
+  if (rv == CKR_USER_ALREADY_LOGGED_IN)
+    rv = CKR_OK;
+  if (g_pkcs11_propagate_error (error, rv))
+    return FALSE;
+  return TRUE;
+}
+
+static gboolean
+session_login_with_pin (GPkcs11Slot          *self,
+                        GTlsInteraction      *interaction,
+                        CK_SESSION_HANDLE     session,
+                        CK_TOKEN_INFO        *token_info,
+                        GTlsPasswordFlags     flags,
+                        GCancellable         *cancellable,
+                        GError              **error)
+{
+  GTlsInteractionResult result = G_TLS_INTERACTION_UNHANDLED;
+  GTlsPassword *password = NULL;
+  const guchar *value;
+  gsize length;
+  CK_RV rv;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+
+  else if (interaction != NULL)
+    {
+      gchar *description = p11_kit_space_strdup (token_info->label,
+                                                 sizeof (token_info->label));
+      password = g_tls_password_new (flags, description);
+      free (description);
+
+      result = g_tls_interaction_ask_password (interaction, password, cancellable, error);
+    }
+
+  switch (result)
+    {
+    case G_TLS_INTERACTION_UNHANDLED:
+      g_clear_object (&password);
+      g_message ("no pin is available to log in, or the user cancelled pin entry");
+      return TRUE;
+    case G_TLS_INTERACTION_FAILED:
+      g_clear_object (&password);
+      return FALSE;
+    case G_TLS_INTERACTION_HANDLED:
+      break;
+    }
+
+  g_assert (interaction != NULL && password != NULL);
+  value = g_tls_password_get_value (password, &length);
+  rv = (self->module->C_Login) (session, CKU_USER, (CK_UTF8CHAR_PTR)value, length);
+  g_object_unref (password);
+
+  if (rv == CKR_USER_ALREADY_LOGGED_IN)
+    rv = CKR_OK;
+  if (g_pkcs11_propagate_error (error, rv))
+    return FALSE;
+  return TRUE;
+}
+
+static gboolean
+session_login_if_necessary (GPkcs11Slot        *self,
+                            GTlsInteraction    *interaction,
+                            CK_SESSION_HANDLE   session,
+                            GCancellable       *cancellable,
+                            GError            **error)
+{
+  CK_TOKEN_INFO token_info;
+  GTlsPasswordFlags flags = 0;
+  GError *err = NULL;
+  CK_RV rv;
+
+  for (;;)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        return FALSE;
+
+      /* Do we actually need to login? */
+      if (check_if_session_logged_in (self, session))
+        return TRUE;
+
+      /* Get the token information, this can change between login attempts */
+      rv = (self->module->C_GetTokenInfo) (self->slot_id, &token_info);
+      if (g_pkcs11_propagate_error (error, rv))
+        return FALSE;
+
+      if (!(token_info.flags & CKF_LOGIN_REQUIRED))
+        return TRUE;
+
+      /* Login is not initialized on token, don't try to login */
+      if (!(token_info.flags & CKF_USER_PIN_INITIALIZED))
+        return TRUE;
+
+      /* Protected auth path, only call login once, and let token prompt user */
+      if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+        return session_login_protected_auth_path (self, session, error);
+
+      /* Normal authentication path, ask p11-kit to call any callbacks */
+      else
+        {
+
+          if (token_info.flags & CKF_SO_PIN_COUNT_LOW)
+            flags |= G_TLS_PASSWORD_MANY_TRIES;
+          if (token_info.flags & CKF_SO_PIN_FINAL_TRY)
+            flags |= G_TLS_PASSWORD_FINAL_TRY;
+
+          if (session_login_with_pin (self, interaction, session, &token_info,
+                                      flags, cancellable, &err))
+            return TRUE;
+
+          /* User cancelled, don't try to log in */
+          if (err == NULL)
+            return TRUE;
+
+          if (!g_error_matches (err, G_PKCS11_ERROR, CKR_PIN_INCORRECT))
+            {
+              g_propagate_error (error, err);
+              return FALSE;
+            }
+
+          /* Try again */
+          g_clear_error (&err);
+          flags |= G_TLS_PASSWORD_RETRY;
+        }
+    }
+}
+
+static CK_SESSION_HANDLE
+session_checkout_or_open (GPkcs11Slot     *self,
+                          GTlsInteraction *interaction,
+                          gboolean         login,
+                          GCancellable    *cancellable,
+                          GError         **error)
+{
+  CK_SESSION_HANDLE session = 0;
+  CK_RV rv;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return 0;
+
+  g_mutex_lock (&self->mutex);
+
+  if (self->last_session)
+    {
+      session = self->last_session;
+      self->last_session = 0;
+    }
+
+  g_mutex_unlock (&self->mutex);
+
+  if (!session)
+    {
+      rv = (self->module->C_OpenSession) (self->slot_id, CKF_SERIAL_SESSION,
+                                          NULL, NULL, &session);
+      if (g_pkcs11_propagate_error (error, rv))
+        return 0;
+    }
+
+  if (login)
+    {
+      if (!session_login_if_necessary (self, interaction, session, cancellable, error))
+        {
+          (self->module->C_CloseSession) (session);
+          return 0;
+        }
+    }
+
+  return session;
+}
+
+static void
+session_close (GPkcs11Slot       *self,
+               CK_SESSION_HANDLE   session)
+{
+  CK_RV rv;
+
+  g_assert (session != 0);
+
+  rv = (self->module->C_CloseSession) (session);
+  if (rv != CKR_OK)
+    g_warning ("couldn't close pkcs11 session: %s",
+               p11_kit_strerror (rv));
+}
+
+static void
+session_checkin_or_close (GPkcs11Slot      *self,
+                          CK_SESSION_HANDLE  session)
+{
+  g_assert (session != 0);
+
+  g_mutex_lock (&self->mutex);
+
+  if (self->last_session == 0)
+    {
+      self->last_session = session;
+      session = 0;
+    }
+
+  g_mutex_unlock (&self->mutex);
+
+  if (session != 0)
+    session_close (self, session);
+}
+
+static GPkcs11Array*
+retrieve_object_attributes (GPkcs11Slot              *self,
+                            CK_SESSION_HANDLE         session,
+                            CK_OBJECT_HANDLE          object,
+                            const CK_ATTRIBUTE_TYPE  *attr_types,
+                            guint                     attr_types_length,
+                            GError                  **error)
+{
+  GPkcs11Array *result;
+  CK_ATTRIBUTE_PTR attr;
+  CK_ATTRIBUTE blank;
+  CK_RV rv;
+  guint i;
+
+  result = g_pkcs11_array_new ();
+  memset (&blank, 0, sizeof (blank));
+  for (i = 0; i < attr_types_length; ++i)
+    {
+      blank.type = attr_types[i];
+      g_pkcs11_array_add (result, &blank);
+    }
+
+  /* Get all the required buffer sizes */
+  rv = (self->module->C_GetAttributeValue) (session, object,
+                                            result->attrs, result->count);
+  if (rv == CKR_ATTRIBUTE_SENSITIVE ||
+      rv == CKR_ATTRIBUTE_TYPE_INVALID)
+    rv = CKR_OK;
+  if (g_pkcs11_propagate_error (error, rv))
+    {
+      g_pkcs11_array_unref (result);
+      return NULL;
+    }
+
+  /* Now allocate memory for them all */
+  for (i = 0; i < attr_types_length; ++i)
+    {
+      attr = &g_pkcs11_array_index (result, i);
+      if (attr->ulValueLen != (CK_ULONG)-1 && attr->ulValueLen)
+          attr->pValue = g_malloc0 (attr->ulValueLen);
+    }
+
+  /* And finally get all the values */
+  rv = (self->module->C_GetAttributeValue) (session, object,
+                                            result->attrs, result->count);
+  if (rv == CKR_ATTRIBUTE_SENSITIVE ||
+      rv == CKR_ATTRIBUTE_TYPE_INVALID ||
+      rv == CKR_BUFFER_TOO_SMALL)
+    rv = CKR_OK;
+  if (g_pkcs11_propagate_error (error, rv))
+    {
+      g_pkcs11_array_unref (result);
+      return NULL;
+    }
+
+  return result;
+}
+
+static void
+g_pkcs11_slot_init (GPkcs11Slot *self)
+{
+  g_mutex_init (&self->mutex);
+}
+
+static void
+g_pkcs11_slot_dispose (GObject *object)
+{
+  GPkcs11Slot *self = G_PKCS11_SLOT (object);
+  CK_SESSION_HANDLE session = 0;
+
+  g_mutex_lock (&self->mutex);
+
+  session = self->last_session;
+  self->last_session = 0;
+
+  g_mutex_unlock (&self->mutex);
+
+  if (session)
+    session_close (self, session);
+
+  G_OBJECT_CLASS (g_pkcs11_slot_parent_class)->dispose (object);
+}
+
+static void
+g_pkcs11_slot_finalize (GObject *object)
+{
+  GPkcs11Slot *self = G_PKCS11_SLOT (object);
+
+  g_assert (self->last_session == 0);
+  g_mutex_clear (&self->mutex);
+
+  G_OBJECT_CLASS (g_pkcs11_slot_parent_class)->finalize (object);
+}
+
+static void
+g_pkcs11_slot_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GPkcs11Slot *self = G_PKCS11_SLOT (object);
+
+  switch (prop_id)
+    {
+    case PROP_MODULE:
+      g_value_set_pointer (value, self->module);
+      break;
+
+    case PROP_SLOT_ID:
+      g_value_set_ulong (value, self->slot_id);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_pkcs11_slot_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GPkcs11Slot *self = G_PKCS11_SLOT (object);
+
+  switch (prop_id)
+    {
+    case PROP_MODULE:
+      self->module = g_value_get_pointer (value);
+      g_assert (self->module);
+      break;
+
+    case PROP_SLOT_ID:
+      self->slot_id = g_value_get_ulong (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_pkcs11_slot_class_init (GPkcs11SlotClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->get_property = g_pkcs11_slot_get_property;
+  gobject_class->set_property = g_pkcs11_slot_set_property;
+  gobject_class->dispose      = g_pkcs11_slot_dispose;
+  gobject_class->finalize     = g_pkcs11_slot_finalize;
+
+  g_object_class_install_property (gobject_class, PROP_MODULE,
+                                   g_param_spec_pointer ("module",
+                                                         N_("Module"),
+                                                         N_("PKCS#11 Module Pointer"),
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT |
+                                                         G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SLOT_ID,
+                                   g_param_spec_ulong ("slot-id",
+                                                         N_("Slot ID"),
+                                                         N_("PKCS#11 Slot Identifier"),
+                                                         0,
+                                                         G_MAXULONG,
+                                                         G_MAXULONG,
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT |
+                                                         G_PARAM_STATIC_STRINGS));
+}
+
+GPkcs11EnumerateState
+g_pkcs11_slot_enumerate (GPkcs11Slot             *self,
+                         GTlsInteraction         *interaction,
+                         CK_ATTRIBUTE_PTR         match,
+                         CK_ULONG                 match_count,
+                         gboolean                 match_private,
+                         const CK_ATTRIBUTE_TYPE *attr_types,
+                         guint                    attr_types_length,
+                         GPkcs11Accumulator       accumulator,
+                         gpointer                 user_data,
+                         GCancellable            *cancellable,
+                         GError                 **error)
+{
+  GPkcs11EnumerateState state = G_PKCS11_ENUMERATE_CONTINUE;
+  CK_OBJECT_HANDLE objects[256];
+  CK_SESSION_HANDLE session;
+  GPkcs11Array *attrs;
+  GError *err = NULL;
+  CK_ULONG count, i;
+  CK_RV rv;
+
+  g_return_val_if_fail (G_IS_PKCS11_SLOT (self), FALSE);
+  g_return_val_if_fail (accumulator, FALSE);
+  g_return_val_if_fail (!error || !*error, FALSE);
+
+  session = session_checkout_or_open (self, interaction, match_private,
+                                      cancellable, &err);
+  if (err != NULL)
+    {
+      /* If the slot isn't present, then nothing to match :) */
+      if (g_error_matches (err, G_PKCS11_ERROR, CKR_TOKEN_NOT_PRESENT))
+        {
+          g_clear_error (&err);
+          return G_PKCS11_ENUMERATE_CONTINUE;
+        }
+
+      g_propagate_error (error, err);
+      return G_PKCS11_ENUMERATE_FAILED;
+    }
+
+  rv = (self->module->C_FindObjectsInit) (session, match, match_count);
+
+  while (state == G_PKCS11_ENUMERATE_CONTINUE && rv == CKR_OK &&
+         !g_cancellable_is_cancelled (cancellable))
+    {
+      count = 0;
+      rv = (self->module->C_FindObjects) (session, objects,
+                                          G_N_ELEMENTS (objects), &count);
+      if (rv == CKR_OK)
+        {
+          if (count == 0)
+            break;
+
+          for (i = 0; state == G_PKCS11_ENUMERATE_CONTINUE && i < count; ++i)
+            {
+              if (attr_types_length)
+                {
+                  attrs = retrieve_object_attributes (self, session, objects[i],
+                                                  attr_types, attr_types_length, error);
+                  if (attrs == NULL)
+                      state = G_PKCS11_ENUMERATE_FAILED;
+                }
+              else
+                {
+                  attrs = NULL;
+                }
+
+              if (state == G_PKCS11_ENUMERATE_CONTINUE)
+                {
+                  if (!(accumulator) (attrs, user_data))
+                    state = G_PKCS11_ENUMERATE_STOP;
+                }
+
+              if (attrs)
+                g_pkcs11_array_unref (attrs);
+
+              if (g_cancellable_is_cancelled (cancellable))
+                break;
+            }
+        }
+    }
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    {
+      state = G_PKCS11_ENUMERATE_FAILED;
+    }
+  else if (rv != CKR_OK && rv != CKR_TOKEN_NOT_PRESENT)
+    {
+      g_pkcs11_propagate_error (error, rv);
+      state = G_PKCS11_ENUMERATE_FAILED;
+    }
+
+  rv = (self->module->C_FindObjectsFinal) (session);
+  if (rv == CKR_OK)
+    session_checkin_or_close (self, session);
+  else
+    session_close (self, session);
+
+  return state;
+}
+
+gboolean
+g_pkcs11_slot_get_token_info (GPkcs11Slot       *self,
+                              CK_TOKEN_INFO_PTR  token_info)
+{
+  CK_RV rv;
+
+  g_return_val_if_fail (G_IS_PKCS11_SLOT (self), FALSE);
+  g_return_val_if_fail (token_info, FALSE);
+
+  memset (token_info, 0, sizeof (CK_TOKEN_INFO));
+  rv = (self->module->C_GetTokenInfo) (self->slot_id, token_info);
+  if (rv == CKR_TOKEN_NOT_PRESENT)
+    return FALSE;
+
+  if (rv != CKR_OK)
+    {
+      g_warning ("call to C_GetTokenInfo on PKCS#11 module failed: %s",
+                 p11_kit_strerror (rv));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+g_pkcs11_slot_matches_uri (GPkcs11Slot            *self,
+                           P11KitUri              *uri)
+{
+  CK_INFO library;
+  CK_TOKEN_INFO token;
+  CK_RV rv;
+
+  g_return_val_if_fail (G_IS_PKCS11_SLOT (self), FALSE);
+  g_return_val_if_fail (uri, FALSE);
+
+  memset (&library, 0, sizeof (library));
+  rv = (self->module->C_GetInfo) (&library);
+  if (rv != CKR_OK)
+    {
+      g_warning ("call to C_GetInfo on PKCS#11 module failed: %s",
+                 p11_kit_strerror (rv));
+      return FALSE;
+    }
+
+  if (!p11_kit_uri_match_module_info (uri, &library))
+    return FALSE;
+
+  memset (&token, 0, sizeof (token));
+  if (!g_pkcs11_slot_get_token_info (self, &token))
+    return FALSE;
+
+  return p11_kit_uri_match_token_info (uri, &token);
+}
diff --git a/tls/pkcs11/gpkcs11slot.h b/tls/pkcs11/gpkcs11slot.h
new file mode 100644
index 0000000..a57c2a6
--- /dev/null
+++ b/tls/pkcs11/gpkcs11slot.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_PKCS11_SLOT_H__
+#define __G_PKCS11_SLOT_H__
+
+#include <gio/gio.h>
+
+#include "gpkcs11array.h"
+
+#include <p11-kit/pkcs11.h>
+#include <p11-kit/uri.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+  G_PKCS11_ENUMERATE_FAILED,
+  G_PKCS11_ENUMERATE_STOP,
+  G_PKCS11_ENUMERATE_CONTINUE
+} GPkcs11EnumerateState;
+
+#define G_TYPE_PKCS11_SLOT            (g_pkcs11_slot_get_type ())
+
+G_DECLARE_FINAL_TYPE (GPkcs11Slot, g_pkcs11_slot, G, PKCS11_SLOT, GObject)
+
+typedef gboolean             (*GPkcs11Accumulator)            (gpointer result,
+                                                               gpointer user_data);
+
+GPkcs11EnumerateState        g_pkcs11_slot_enumerate          (GPkcs11Slot             *self,
+                                                               GTlsInteraction         *interaction,
+                                                               CK_ATTRIBUTE_PTR         match,
+                                                               CK_ULONG                 match_count,
+                                                               gboolean                 match_private,
+                                                               const CK_ATTRIBUTE_TYPE *attr_types,
+                                                               guint                    attr_types_length,
+                                                               GPkcs11Accumulator       accumulator,
+                                                               gpointer                 user_data,
+                                                               GCancellable            *cancellable,
+                                                               GError                 **error);
+
+gboolean                     g_pkcs11_slot_get_token_info     (GPkcs11Slot             *self,
+                                                               CK_TOKEN_INFO_PTR        token_info);
+
+gboolean                     g_pkcs11_slot_matches_uri        (GPkcs11Slot             *self,
+                                                               P11KitUri               *uri);
+
+G_END_DECLS
+
+#endif /* __G_PKCS11_SLOT_H___ */
diff --git a/tls/pkcs11/gpkcs11util.c b/tls/pkcs11/gpkcs11util.c
new file mode 100644
index 0000000..6ffe18f
--- /dev/null
+++ b/tls/pkcs11/gpkcs11util.c
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gpkcs11util.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include <p11-kit/p11-kit.h>
+
+GQuark
+g_pkcs11_get_error_domain (void)
+{
+  static GQuark domain = 0;
+  static volatile gsize quark_inited = 0;
+
+  if (g_once_init_enter (&quark_inited))
+    {
+      domain = g_quark_from_static_string ("g-pkcs11-error");
+      g_once_init_leave (&quark_inited, 1);
+    }
+
+  return domain;
+}
+
+gboolean
+g_pkcs11_propagate_error (GError **error, CK_RV rv)
+{
+  if (rv == CKR_OK)
+    return FALSE;
+  if (rv == CKR_CANCEL)
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+                           p11_kit_strerror (rv));
+  else
+    g_set_error_literal (error, G_PKCS11_ERROR, (gint)rv,
+                         p11_kit_strerror (rv));
+  return TRUE;
+}
diff --git a/tls/pkcs11/gpkcs11util.h b/tls/pkcs11/gpkcs11util.h
new file mode 100644
index 0000000..9368a78
--- /dev/null
+++ b/tls/pkcs11/gpkcs11util.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_PKCS11_UTIL_H__
+#define __G_PKCS11_UTIL_H__
+
+#include <glib.h>
+
+#include <p11-kit/pkcs11.h>
+
+G_BEGIN_DECLS
+
+#define                G_PKCS11_VENDOR_CODE               0x47000000 /* G000 */
+
+enum {
+  G_PKCS11_ERROR_BAD_URI = (CKR_VENDOR_DEFINED | (G_PKCS11_VENDOR_CODE + 1)),
+};
+
+#define                G_PKCS11_ERROR                     (g_pkcs11_get_error_domain ())
+
+GQuark                 g_pkcs11_get_error_domain          (void) G_GNUC_CONST;
+
+gboolean               g_pkcs11_propagate_error           (GError **error,
+                                                           CK_RV rv);
+
+G_END_DECLS
+
+#endif /* __G_PKCS11_UTIL_H___ */
diff --git a/tls/pkcs11/meson.build b/tls/pkcs11/meson.build
new file mode 100644
index 0000000..94f55d9
--- /dev/null
+++ b/tls/pkcs11/meson.build
@@ -0,0 +1,28 @@
+sources = files(
+  'gpkcs11array.c',
+  'gpkcs11pin.c',
+  'gpkcs11slot.c',
+  'gpkcs11util.c'
+)
+
+deps = [
+  glib_dep,
+  gio_dep,
+  gmodule_dep,
+  gobject_dep,
+  pkcs11_dep
+]
+
+libgiopkcs11 = static_library(
+  'giopkcs11',
+  sources: sources,
+  include_directories: top_inc,
+  dependencies: deps,
+  install: get_option('static_modules')
+)
+
+libgiopkcs11_dep = declare_dependency(
+  link_with: libgiopkcs11,
+  include_directories: include_directories('.'),
+  dependencies: deps
+)
diff --git a/tls/pkcs11/pkcs11-trust-assertions.h b/tls/pkcs11/pkcs11-trust-assertions.h
new file mode 100644
index 0000000..cfc916b
--- /dev/null
+++ b/tls/pkcs11/pkcs11-trust-assertions.h
@@ -0,0 +1,59 @@
+/*
+ * pkcs11x.h
+ *  Copyright 2010 Collabora, Ltd
+ *
+ * This file is free software; as a special exception the author gives
+ * unlimited permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+/*
+ * The latest version of this file is at:
+ *
+ * git://thewalter.net/git/pkcs11-trust-assertions
+ *
+ * or viewable on the web at:
+ *
+ * http://thewalter.net/git/cgit.cgi/pkcs11-trust-assertions/tree/pkcs11-trust-assertions.h
+ *
+ */
+
+#ifndef PKCS11_TRUST_ASSERTIONS_H
+#define PKCS11_TRUST_ASSERTIONS_H
+
+#include <p11-kit/pkcs11.h>
+
+#define CKA_XDG   (CKA_VENDOR_DEFINED | 0x58444700UL /* XDG0 */ )
+#define CKO_XDG   (CKA_VENDOR_DEFINED | 0x58444700UL /* XDG0 */ )
+
+/* -------------------------------------------------------------------
+ * TRUST ASSERTIONS
+ */
+
+#define CKO_X_TRUST_ASSERTION                    (CKO_XDG + 100)
+
+#define CKA_X_ASSERTION_TYPE                     (CKA_XDG + 1)
+
+#define CKA_X_CERTIFICATE_VALUE                  (CKA_XDG + 2)
+
+#define CKA_X_PURPOSE                            (CKA_XDG + 3)
+
+#define CKA_X_PEER                               (CKA_XDG + 4)
+
+typedef CK_ULONG CK_X_ASSERTION_TYPE;
+
+#define CKT_X_UNTRUSTED_CERTIFICATE              1UL
+
+#define CKT_X_PINNED_CERTIFICATE                 2UL
+
+#define CKT_X_ANCHORED_CERTIFICATE               3UL
+
+#endif /* PKCS11_TRUST_ASSERTIONS_H */
diff --git a/tls/tests/meson.build b/tls/tests/meson.build
index fc73a4b..1aee911 100644
--- a/tls/tests/meson.build
+++ b/tls/tests/meson.build
@@ -29,6 +29,20 @@ test_programs = [
 #  ['dtls-connection', ['mock-interaction.c'], deps, ['openssl']],
 ]
 
+if pkcs11_dep.found()
+  pkcs11_deps = deps + [
+    libgiopkcs11_dep,
+    pkcs11_dep
+  ]
+
+  test_programs += [
+    ['pkcs11-util', [], pkcs11_deps, []],
+    ['pkcs11-array', [], pkcs11_deps, []],
+    ['pkcs11-pin', [], pkcs11_deps, []],
+    ['pkcs11-slot', ['mock-interaction.c', 'mock-pkcs11.c'], pkcs11_deps, []]
+  ]
+endif
+
 foreach backend: backends
   foreach program: test_programs
     if not program[3].contains(backend)
diff --git a/tls/tests/mock-pkcs11.c b/tls/tests/mock-pkcs11.c
new file mode 100644
index 0000000..75a6874
--- /dev/null
+++ b/tls/tests/mock-pkcs11.c
@@ -0,0 +1,1547 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (C) 2010 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General  License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General  License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+
+#include "mock-pkcs11.h"
+
+#include <p11-kit/pkcs11.h>
+
+#include <glib.h>
+
+#include <string.h>
+
+/*
+ * This is *NOT* how you'd want to implement a PKCS#11 module. This
+ * fake module simply provides enough for gnutls-pkcs11 backend to test against.
+ * It doesn't pass any tests, or behave as expected from a PKCS#11 module.
+ */
+
+static gboolean initialized = FALSE;
+static gchar *the_pin = NULL;
+static gulong n_the_pin = 0;
+
+static gboolean logged_in = FALSE;
+static CK_USER_TYPE user_type = 0;
+static CK_FUNCTION_LIST functionList;
+
+typedef enum
+{
+  OP_FIND = 1,
+  OP_CRYPTO
+} Operation;
+
+typedef struct
+{
+  CK_SESSION_HANDLE handle;
+  CK_SESSION_INFO info;
+  GHashTable *objects;
+
+  Operation operation;
+
+  /* For find operations */
+  GList *matches;
+
+  /* For crypto operations */
+  CK_OBJECT_HANDLE crypto_key;
+  CK_ATTRIBUTE_TYPE crypto_method;
+  CK_MECHANISM_TYPE crypto_mechanism;
+  CK_BBOOL want_context_login;
+} Session;
+
+static guint unique_identifier = 100;
+static GHashTable *the_sessions = NULL;
+static GHashTable *the_objects = NULL;
+
+static void
+free_session (gpointer data)
+{
+  Session *sess = (Session *)data;
+  if (sess)
+    g_hash_table_destroy (sess->objects);
+  g_free (sess);
+}
+
+static GPkcs11Array *
+lookup_object (Session *session,
+               CK_OBJECT_HANDLE hObject)
+{
+  GPkcs11Array *attrs;
+  attrs = g_hash_table_lookup (the_objects, GUINT_TO_POINTER (hObject));
+  if (!attrs)
+    attrs = g_hash_table_lookup (session->objects, GUINT_TO_POINTER (hObject));
+  return attrs;
+}
+
+CK_OBJECT_HANDLE
+mock_module_take_object (GPkcs11Array *attrs)
+{
+  gboolean token;
+  guint handle;
+
+  g_return_val_if_fail (the_objects, 0);
+
+  if (g_pkcs11_array_find_boolean (attrs, CKA_TOKEN, &token))
+    g_return_val_if_fail (token == TRUE, 0);
+
+  handle = ++unique_identifier;
+  g_pkcs11_array_add_boolean (attrs, CKA_TOKEN, TRUE);
+  g_hash_table_insert (the_objects, GUINT_TO_POINTER (handle), attrs);
+  return handle;
+}
+
+void
+mock_module_enumerate_objects (CK_SESSION_HANDLE handle,
+                               MockEnumerator func,
+                               gpointer user_data)
+{
+  GHashTableIter iter;
+  gpointer key;
+  gpointer value;
+  Session *session;
+  gboolean private;
+
+  g_assert (the_objects);
+  g_assert (func);
+
+  /* Token objects */
+  g_hash_table_iter_init (&iter, the_objects);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      /* Don't include private objects when not logged in */
+      if (!logged_in)
+        {
+          if (g_pkcs11_array_find_boolean (value, CKA_PRIVATE, &private) && private == TRUE)
+            continue;
+        }
+
+      if (!(func) (GPOINTER_TO_UINT (key), value, user_data))
+        return;
+    }
+
+  /* session objects */
+  if (handle)
+    {
+      session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (handle));
+      if (session)
+        {
+          g_hash_table_iter_init (&iter, session->objects);
+          while (g_hash_table_iter_next (&iter, &key, &value))
+            {
+              /* Don't include private objects when not logged in */
+              if (!logged_in)
+                {
+                  if (g_pkcs11_array_find_boolean (value, CKA_PRIVATE, &private) && private == TRUE)
+                    continue;
+                }
+
+              if (!(func) (GPOINTER_TO_UINT (key), value, user_data))
+                return;
+            }
+        }
+    }
+}
+
+typedef struct {
+  CK_ATTRIBUTE_PTR attrs;
+  CK_ULONG n_attrs;
+  CK_OBJECT_HANDLE object;
+} FindObject;
+
+static gboolean
+enumerate_and_find_object (CK_OBJECT_HANDLE object,
+                           GPkcs11Array *attrs,
+                           gpointer user_data)
+{
+  FindObject *ctx = user_data;
+  const CK_ATTRIBUTE *match;
+  const CK_ATTRIBUTE *attr;
+  CK_ULONG i;
+
+  for (i = 0; i < ctx->n_attrs; ++i)
+    {
+      match = ctx->attrs + i;
+      attr = g_pkcs11_array_find (attrs, match->type);
+      if (!attr)
+        return TRUE; /* Continue */
+
+      if (attr->ulValueLen != match->ulValueLen ||
+          memcmp (attr->pValue, match->pValue, attr->ulValueLen) != 0)
+        return TRUE; /* Continue */
+    }
+
+  ctx->object = object;
+  return FALSE; /* Stop iteration */
+}
+
+CK_OBJECT_HANDLE
+mock_module_find_object (CK_SESSION_HANDLE session,
+                         CK_ATTRIBUTE_PTR attrs,
+                         CK_ULONG n_attrs)
+{
+  FindObject ctx;
+
+  ctx.attrs = attrs;
+  ctx.n_attrs = n_attrs;
+  ctx.object = 0;
+
+  mock_module_enumerate_objects (session, enumerate_and_find_object, &ctx);
+  return ctx.object;
+}
+
+static gboolean
+enumerate_and_count_objects (CK_OBJECT_HANDLE object,
+                             GPkcs11Array *attrs,
+                             gpointer user_data)
+{
+  guint *n_objects = user_data;
+  ++(*n_objects);
+  return TRUE; /* Continue */
+}
+
+guint
+mock_module_count_objects (CK_SESSION_HANDLE session)
+{
+  guint n_objects = 0;
+  mock_module_enumerate_objects (session, enumerate_and_count_objects, &n_objects);
+  return n_objects;
+}
+
+void
+mock_module_set_object (CK_OBJECT_HANDLE object,
+                        CK_ATTRIBUTE_PTR attrs,
+                        CK_ULONG n_attrs)
+{
+  CK_ULONG i;
+  GPkcs11Array *atts;
+
+  g_return_if_fail (object != 0);
+  g_return_if_fail (the_objects);
+
+  atts = g_hash_table_lookup (the_objects, GUINT_TO_POINTER (object));
+  g_return_if_fail (atts);
+
+  for (i = 0; i < n_attrs; ++i)
+    g_pkcs11_array_set (atts, &attrs[i]);
+}
+
+void
+mock_module_set_pin (const gchar *password)
+{
+  g_free (the_pin);
+  the_pin = g_strdup (password);
+  n_the_pin = strlen (password);
+}
+
+CK_RV
+mock_C_Initialize (CK_VOID_PTR pInitArgs)
+{
+  GPkcs11Array *attrs;
+  CK_C_INITIALIZE_ARGS_PTR args;
+
+  g_return_val_if_fail (initialized == FALSE, CKR_CRYPTOKI_ALREADY_INITIALIZED);
+
+  args = (CK_C_INITIALIZE_ARGS_PTR)pInitArgs;
+  if (args)
+    {
+      g_return_val_if_fail(
+          (args->CreateMutex == NULL && args->DestroyMutex == NULL &&
+           args->LockMutex == NULL && args->UnlockMutex == NULL) ||
+          (args->CreateMutex != NULL && args->DestroyMutex != NULL &&
+           args->LockMutex != NULL && args->UnlockMutex != NULL),
+          CKR_ARGUMENTS_BAD);
+
+      /* Flags should allow OS locking and os threads */
+      g_return_val_if_fail ((args->flags & CKF_OS_LOCKING_OK), CKR_CANT_LOCK);
+      g_return_val_if_fail ((args->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) == 0, 
CKR_NEED_TO_CREATE_THREADS);
+    }
+
+  the_pin = g_strdup (MOCK_SLOT_ONE_PIN);
+  n_the_pin = strlen (the_pin);
+  the_sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, free_session);
+  the_objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
(GDestroyNotify)g_pkcs11_array_unref);
+
+  /* Our first token object */
+  attrs = g_pkcs11_array_new ();
+  g_pkcs11_array_add_ulong (attrs, CKA_CLASS, CKO_DATA);
+  g_pkcs11_array_add_value (attrs, CKA_LABEL, "TEST LABEL", -1);
+  g_pkcs11_array_add_boolean (attrs, CKA_TOKEN, TRUE);
+  g_hash_table_insert (the_objects, GUINT_TO_POINTER (2), attrs);
+
+  /* Our second token object */
+  attrs = g_pkcs11_array_new ();
+  g_pkcs11_array_add_ulong (attrs, CKA_CLASS, CKO_DATA);
+  g_pkcs11_array_add_value (attrs, CKA_LABEL, "LABEL TWO", -1);
+  g_pkcs11_array_add_boolean (attrs, CKA_TOKEN, TRUE);
+  g_hash_table_insert (the_objects, GUINT_TO_POINTER (3), attrs);
+
+  /* A private object */
+  attrs = g_pkcs11_array_new ();
+  g_pkcs11_array_add_ulong (attrs, CKA_CLASS, CKO_DATA);
+  g_pkcs11_array_add_value (attrs, CKA_LABEL, "PRIVATE", -1);
+  g_pkcs11_array_add_boolean (attrs, CKA_PRIVATE, TRUE);
+  g_pkcs11_array_add_boolean (attrs, CKA_TOKEN, TRUE);
+  g_hash_table_insert (the_objects, GUINT_TO_POINTER (4), attrs);
+
+  initialized = TRUE;
+  return CKR_OK;
+}
+
+CK_RV
+mock_validate_and_C_Initialize (CK_VOID_PTR pInitArgs)
+{
+  CK_C_INITIALIZE_ARGS_PTR args;
+  void *mutex;
+  CK_RV rv;
+
+  args = (CK_C_INITIALIZE_ARGS_PTR)pInitArgs;
+  if (args)
+    {
+      g_assert ((args->CreateMutex) (NULL) == CKR_ARGUMENTS_BAD && "CreateMutex succeeded wrong");
+      g_assert ((args->DestroyMutex) (NULL) == CKR_MUTEX_BAD && "DestroyMutex succeeded wrong");
+      g_assert ((args->LockMutex) (NULL) == CKR_MUTEX_BAD && "LockMutex succeeded wrong");
+      g_assert ((args->UnlockMutex) (NULL) == CKR_MUTEX_BAD && "UnlockMutex succeeded wrong");
+
+      /* Try to create an actual mutex */
+      rv = (args->CreateMutex) (&mutex);
+      g_assert (rv == CKR_OK && "CreateMutex g_assert_not_reacheded");
+      g_assert (mutex != NULL && "CreateMutex created null mutex");
+
+      /* Try and lock the mutex */
+      rv = (args->LockMutex) (mutex);
+      g_assert (rv == CKR_OK && "LockMutex g_assert_not_reacheded");
+
+      /* Try and unlock the mutex */
+      rv = (args->UnlockMutex) (mutex);
+      g_assert (rv == CKR_OK && "UnlockMutex g_assert_not_reacheded");
+
+      /* Try and destroy the mutex */
+      rv = (args->DestroyMutex) (mutex);
+      g_assert (rv == CKR_OK && "DestroyMutex g_assert_not_reacheded");
+    }
+
+  return mock_C_Initialize (pInitArgs);
+}
+
+CK_RV
+mock_C_Finalize (CK_VOID_PTR pReserved)
+{
+  g_return_val_if_fail (pReserved == NULL, CKR_ARGUMENTS_BAD);
+  g_return_val_if_fail (initialized == TRUE, CKR_CRYPTOKI_NOT_INITIALIZED);
+
+  initialized = FALSE;
+  logged_in = FALSE;
+  g_hash_table_destroy (the_objects);
+  the_objects = NULL;
+
+  g_hash_table_destroy (the_sessions);
+  the_sessions = NULL;
+
+  g_free (the_pin);
+  return CKR_OK;
+}
+
+static const CK_INFO TEST_INFO = {
+  { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },
+  "TEST MANUFACTURER              ",
+  0,
+  "TEST LIBRARY                   ",
+  { 45, 145 }
+};
+
+CK_RV
+mock_C_GetInfo (CK_INFO_PTR pInfo)
+{
+  g_return_val_if_fail (pInfo, CKR_ARGUMENTS_BAD);
+  memcpy (pInfo, &TEST_INFO, sizeof (*pInfo));
+  return CKR_OK;
+}
+
+CK_RV
+mock_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+  g_return_val_if_fail (list, CKR_ARGUMENTS_BAD);
+  *list = &functionList;
+  return CKR_OK;
+}
+
+/*
+ * Two slots
+ *  ONE: token present
+ *  TWO: token not present
+ */
+
+CK_RV
+mock_C_GetSlotList (CK_BBOOL tokenPresent,
+                    CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount)
+{
+  CK_ULONG count;
+
+  g_return_val_if_fail (pulCount, CKR_ARGUMENTS_BAD);
+
+  count = tokenPresent ? 1 : 2;
+
+  /* Application only wants to know the number of slots. */
+  if (pSlotList == NULL)
+    {
+      *pulCount = count;
+      return CKR_OK;
+    }
+
+  if (*pulCount < count)
+    g_return_val_if_reached (CKR_BUFFER_TOO_SMALL);
+
+  *pulCount = count;
+  pSlotList[0] = MOCK_SLOT_ONE_ID;
+  if (!tokenPresent)
+    pSlotList[1] = MOCK_SLOT_TWO_ID;
+
+  return CKR_OK;
+}
+
+/* Update mock-pkcs11.h URIs when updating this */
+
+static const CK_SLOT_INFO TEST_INFO_ONE = {
+  "TEST SLOT                                                       ",
+  "TEST MANUFACTURER              ",
+  CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE,
+  { 55, 155 },
+  { 65, 165 },
+};
+
+/* Update mock-pkcs11.h URIs when updating this */
+
+static const CK_SLOT_INFO TEST_INFO_TWO = {
+  "TEST SLOT                                                       ",
+  "TEST MANUFACTURER              ",
+  CKF_REMOVABLE_DEVICE,
+  { 55, 155 },
+  { 65, 165 },
+};
+
+CK_RV
+mock_C_GetSlotInfo (CK_SLOT_ID slotID,
+                    CK_SLOT_INFO_PTR pInfo)
+{
+  g_return_val_if_fail (pInfo, CKR_ARGUMENTS_BAD);
+
+  if (slotID == MOCK_SLOT_ONE_ID)
+    {
+      memcpy (pInfo, &TEST_INFO_ONE, sizeof (*pInfo));
+      return CKR_OK;
+    }
+  else if (slotID == MOCK_SLOT_TWO_ID)
+    {
+      memcpy (pInfo, &TEST_INFO_TWO, sizeof (*pInfo));
+      return CKR_OK;
+    }
+  else
+    {
+      g_return_val_if_reached (CKR_SLOT_ID_INVALID);
+    }
+}
+
+/* Update mock-pkcs11.h URIs when updating this */
+
+static const CK_TOKEN_INFO TEST_TOKEN_ONE = {
+  "TEST LABEL                      ",
+  "TEST MANUFACTURER               ",
+  "TEST MODEL      ",
+  "TEST SERIAL     ",
+  CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_CLOCK_ON_TOKEN | CKF_TOKEN_INITIALIZED,
+  1,
+  2,
+  3,
+  4,
+  5,
+  6,
+  7,
+  8,
+  9,
+  10,
+  { 75, 175 },
+  { 85, 185 },
+  { '1', '9', '9', '9', '0', '5', '2', '5', '0', '9', '1', '9', '5', '9', '0', '0' }
+};
+
+CK_RV
+mock_C_GetTokenInfo (CK_SLOT_ID slotID,
+                     CK_TOKEN_INFO_PTR pInfo)
+{
+  g_return_val_if_fail (pInfo != NULL, CKR_ARGUMENTS_BAD);
+
+  if (slotID == MOCK_SLOT_ONE_ID)
+    {
+      memcpy (pInfo, &TEST_TOKEN_ONE, sizeof (*pInfo));
+      return CKR_OK;
+    }
+  else if (slotID == MOCK_SLOT_TWO_ID)
+    {
+      return CKR_TOKEN_NOT_PRESENT;
+    }
+  else
+    {
+      g_return_val_if_reached (CKR_SLOT_ID_INVALID);
+    }
+}
+
+CK_RV
+mock_fail_C_GetTokenInfo (CK_SLOT_ID slotID,
+                          CK_TOKEN_INFO_PTR pInfo)
+{
+  return CKR_GENERAL_ERROR;
+}
+
+/*
+ * TWO mechanisms:
+ *  CKM_MOCK_CAPITALIZE
+ *  CKM_MOCK_PREFIX
+ */
+
+CK_RV
+mock_C_GetMechanismList (CK_SLOT_ID slotID,
+                         CK_MECHANISM_TYPE_PTR pMechanismList,
+                         CK_ULONG_PTR pulCount)
+{
+  g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID, CKR_SLOT_ID_INVALID);
+  g_return_val_if_fail (pulCount, CKR_ARGUMENTS_BAD);
+
+  /* Application only wants to know the number of slots. */
+  if (pMechanismList == NULL)
+    {
+      *pulCount = 0;
+      return CKR_OK;
+    }
+
+  return CKR_OK;
+}
+
+CK_RV
+mock_C_GetMechanismInfo (CK_SLOT_ID slotID,
+                         CK_MECHANISM_TYPE type,
+                         CK_MECHANISM_INFO_PTR pInfo)
+{
+  g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID, CKR_SLOT_ID_INVALID);
+  g_return_val_if_fail (pInfo, CKR_ARGUMENTS_BAD);
+
+  g_return_val_if_reached (CKR_MECHANISM_INVALID);
+}
+
+CK_RV
+mock_specific_args_C_InitToken (CK_SLOT_ID slotID,
+                                CK_UTF8CHAR_PTR pPin,
+                                CK_ULONG ulPinLen,
+                                CK_UTF8CHAR_PTR pLabel)
+{
+  g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID, CKR_SLOT_ID_INVALID);
+
+  g_return_val_if_fail (pPin, CKR_PIN_INVALID);
+  g_return_val_if_fail (strlen ("TEST PIN") == ulPinLen, CKR_PIN_INVALID);
+  g_return_val_if_fail (strncmp ((gchar *)pPin, "TEST PIN", ulPinLen) == 0, CKR_PIN_INVALID);
+  g_return_val_if_fail (pLabel != NULL, CKR_PIN_INVALID);
+  g_return_val_if_fail (strcmp ((gchar *)pPin, "TEST LABEL") == 0, CKR_PIN_INVALID);
+
+  g_free (the_pin);
+  the_pin = g_strndup ((gchar *)pPin, ulPinLen);
+  n_the_pin = ulPinLen;
+  return CKR_OK;
+}
+
+CK_RV
+mock_unsupported_C_WaitForSlotEvent (CK_FLAGS flags,
+                                     CK_SLOT_ID_PTR pSlot,
+                                     CK_VOID_PTR pReserved)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_C_OpenSession (CK_SLOT_ID slotID,
+                    CK_FLAGS flags,
+                    CK_VOID_PTR pApplication,
+                    CK_NOTIFY Notify,
+                    CK_SESSION_HANDLE_PTR phSession)
+{
+  Session *sess;
+
+  g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID || slotID == MOCK_SLOT_TWO_ID, CKR_SLOT_ID_INVALID);
+  g_return_val_if_fail (phSession != NULL, CKR_ARGUMENTS_BAD);
+  g_return_val_if_fail ((flags & CKF_SERIAL_SESSION) == CKF_SERIAL_SESSION, 
CKR_SESSION_PARALLEL_NOT_SUPPORTED);
+
+  if (slotID == MOCK_SLOT_TWO_ID)
+    return CKR_TOKEN_NOT_PRESENT;
+
+  sess = g_new0 (Session, 1);
+  sess->handle = ++unique_identifier;
+  sess->info.flags = flags;
+  sess->info.slotID = slotID;
+  sess->info.state = 0;
+  sess->info.ulDeviceError = 1414;
+  sess->objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
(GDestroyNotify)g_pkcs11_array_unref);
+  *phSession = sess->handle;
+
+  g_hash_table_replace (the_sessions, GUINT_TO_POINTER (sess->handle), sess);
+  return CKR_OK;
+}
+
+CK_RV
+mock_fail_C_OpenSession (CK_SLOT_ID slotID,
+                         CK_FLAGS flags,
+                         CK_VOID_PTR pApplication,
+                         CK_NOTIFY Notify,
+                         CK_SESSION_HANDLE_PTR phSession)
+{
+  return CKR_GENERAL_ERROR;
+}
+
+CK_RV
+mock_C_CloseSession (CK_SESSION_HANDLE hSession)
+{
+  Session *session;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+  g_hash_table_remove (the_sessions, GUINT_TO_POINTER (hSession));
+  return CKR_OK;
+}
+
+CK_RV
+mock_C_CloseAllSessions (CK_SLOT_ID slotID)
+{
+  g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID, CKR_SLOT_ID_INVALID);
+
+  g_hash_table_remove_all (the_sessions);
+  return CKR_OK;
+}
+
+CK_RV
+mock_C_GetFunctionStatus (CK_SESSION_HANDLE hSession)
+{
+  return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+CK_RV
+mock_C_CancelFunction (CK_SESSION_HANDLE hSession)
+{
+  return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+CK_RV
+mock_C_GetSessionInfo (CK_SESSION_HANDLE hSession,
+                       CK_SESSION_INFO_PTR pInfo)
+{
+  Session *session;
+
+  g_return_val_if_fail (pInfo != NULL, CKR_ARGUMENTS_BAD);
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_assert (session != NULL && "No such session found");
+  if (!session)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  if (logged_in)
+    {
+      if (session->info.flags & CKF_RW_SESSION)
+        session->info.state = CKS_RW_USER_FUNCTIONS;
+      else
+        session->info.state = CKS_RO_USER_FUNCTIONS;
+    }
+  else
+    {
+      if (session->info.flags & CKF_RW_SESSION)
+        session->info.state = CKS_RW_PUBLIC_SESSION;
+      else
+        session->info.state = CKS_RO_PUBLIC_SESSION;
+    }
+
+  memcpy (pInfo, &session->info, sizeof (*pInfo));
+  return CKR_OK;
+}
+
+CK_RV
+mock_fail_C_GetSessionInfo (CK_SESSION_HANDLE hSession,
+                            CK_SESSION_INFO_PTR pInfo)
+{
+  return CKR_GENERAL_ERROR;
+}
+
+CK_RV
+mock_C_InitPIN (CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin,
+                    CK_ULONG ulPinLen)
+{
+  Session *session;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+  g_free (the_pin);
+  the_pin = g_strndup ((gchar *)pPin, ulPinLen);
+  n_the_pin = ulPinLen;
+  return CKR_OK;
+}
+
+CK_RV
+mock_C_SetPIN (CK_SESSION_HANDLE hSession,
+               CK_UTF8CHAR_PTR pOldPin,
+               CK_ULONG ulOldLen,
+               CK_UTF8CHAR_PTR pNewPin,
+               CK_ULONG ulNewLen)
+{
+  Session *session;
+  gchar *old;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+  old = g_strndup ((gchar *)pOldPin, ulOldLen);
+  if (!old || !g_str_equal (old, the_pin))
+    return CKR_PIN_INCORRECT;
+
+  g_free (the_pin);
+  the_pin = g_strndup ((gchar *)pNewPin, ulNewLen);
+  n_the_pin = ulNewLen;
+  return CKR_OK;
+}
+
+CK_RV
+mock_unsupported_C_GetOperationState (CK_SESSION_HANDLE hSession,
+                                      CK_BYTE_PTR pOperationState,
+                                      CK_ULONG_PTR pulOperationStateLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SetOperationState (CK_SESSION_HANDLE hSession,
+                                      CK_BYTE_PTR pOperationState,
+                                      CK_ULONG ulOperationStateLen,
+                                      CK_OBJECT_HANDLE hEncryptionKey,
+                                      CK_OBJECT_HANDLE hAuthenticationKey)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_C_Login (CK_SESSION_HANDLE hSession,
+              CK_USER_TYPE userType,
+              CK_UTF8CHAR_PTR pPin,
+              CK_ULONG pPinLen)
+{
+  Session *session;
+
+  g_return_val_if_fail (userType == CKU_SO ||
+                        userType == CKU_USER ||
+                        userType == CKU_CONTEXT_SPECIFIC,
+                        CKR_USER_TYPE_INVALID);
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+  g_return_val_if_fail (logged_in == FALSE, CKR_USER_ALREADY_LOGGED_IN);
+
+  if (!pPin)
+    return CKR_PIN_INCORRECT;
+
+  if (pPinLen != strlen (the_pin))
+    return CKR_PIN_INCORRECT;
+  if (strncmp ((gchar *)pPin, the_pin, pPinLen) != 0)
+    return CKR_PIN_INCORRECT;
+
+  if (userType == CKU_CONTEXT_SPECIFIC)
+    {
+      g_return_val_if_fail (session->want_context_login == TRUE, CKR_OPERATION_NOT_INITIALIZED);
+      session->want_context_login = CK_FALSE;
+    }
+  else
+    {
+      logged_in = TRUE;
+      user_type = userType;
+    }
+
+  return CKR_OK;
+}
+
+CK_RV
+mock_C_Logout (CK_SESSION_HANDLE hSession)
+{
+  Session *session;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_assert (session != NULL && "No such session found");
+  if (!session)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  g_assert (logged_in && "Not logged in");
+  logged_in = FALSE;
+  user_type = 0;
+  return CKR_OK;
+}
+
+CK_RV
+mock_C_CreateObject (CK_SESSION_HANDLE hSession,
+                     CK_ATTRIBUTE_PTR pTemplate,
+                     CK_ULONG ulCount,
+                     CK_OBJECT_HANDLE_PTR phObject)
+{
+  GPkcs11Array *attrs;
+  Session *session;
+  gboolean token, priv;
+  CK_ULONG i;
+
+  g_return_val_if_fail (phObject, CKR_ARGUMENTS_BAD);
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+  attrs = g_pkcs11_array_new ();
+  for (i = 0; i < ulCount; ++i)
+    g_pkcs11_array_add_value (attrs, pTemplate[i].type, pTemplate[i].pValue, pTemplate[i].ulValueLen);
+
+  if (g_pkcs11_array_find_boolean (attrs, CKA_PRIVATE, &priv) && priv)
+    {
+      if (!logged_in)
+        {
+          g_pkcs11_array_unref (attrs);
+          return CKR_USER_NOT_LOGGED_IN;
+        }
+    }
+
+  *phObject = ++unique_identifier;
+  if (g_pkcs11_array_find_boolean (attrs, CKA_TOKEN, &token) && token)
+    g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phObject), attrs);
+  else
+    g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phObject), attrs);
+
+  return CKR_OK;
+}
+
+CK_RV
+mock_fail_C_CreateObject (CK_SESSION_HANDLE hSession,
+                          CK_ATTRIBUTE_PTR pTemplate,
+                          CK_ULONG ulCount,
+                          CK_OBJECT_HANDLE_PTR phObject)
+{
+  /* Always fails */
+  return CKR_FUNCTION_FAILED;
+}
+
+CK_RV
+mock_unsupported_C_CopyObject (CK_SESSION_HANDLE hSession,
+                               CK_OBJECT_HANDLE hObject,
+                               CK_ATTRIBUTE_PTR pTemplate,
+                               CK_ULONG ulCount,
+                               CK_OBJECT_HANDLE_PTR phNewObject)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_C_DestroyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
+{
+  GPkcs11Array *attrs;
+  Session *session;
+  gboolean priv;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+  attrs = lookup_object (session, hObject);
+  g_return_val_if_fail (attrs, CKR_OBJECT_HANDLE_INVALID);
+
+  if (g_pkcs11_array_find_boolean (attrs, CKA_PRIVATE, &priv) && priv)
+    {
+      if (!logged_in)
+        return CKR_USER_NOT_LOGGED_IN;
+    }
+
+  g_hash_table_remove (the_objects, GUINT_TO_POINTER (hObject));
+  g_hash_table_remove (session->objects, GUINT_TO_POINTER (hObject));
+
+  return CKR_OK;
+}
+
+CK_RV
+mock_unsupported_C_GetObjectSize (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+                                      CK_ULONG_PTR pulSize)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_C_GetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+                              CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+  CK_ATTRIBUTE_PTR result;
+  CK_RV ret = CKR_OK;
+  GPkcs11Array *attrs;
+  const CK_ATTRIBUTE *attr;
+  Session *session;
+  CK_ULONG i;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+  attrs = lookup_object (session, hObject);
+  if (!attrs)
+    {
+      g_assert_not_reached (); /* "invalid object handle passed" */
+      return CKR_OBJECT_HANDLE_INVALID;
+    }
+
+  for (i = 0; i < ulCount; ++i)
+    {
+      result = pTemplate + i;
+      attr = g_pkcs11_array_find (attrs, result->type);
+      if (!attr)
+        {
+          result->ulValueLen = (CK_ULONG)-1;
+          ret = CKR_ATTRIBUTE_TYPE_INVALID;
+          continue;
+        }
+
+      if (!result->pValue)
+        {
+          result->ulValueLen = attr->ulValueLen;
+          continue;
+        }
+
+      if (result->ulValueLen >= attr->ulValueLen)
+        {
+          memcpy (result->pValue, attr->pValue, attr->ulValueLen);
+          continue;
+        }
+
+      result->ulValueLen = (CK_ULONG)-1;
+      ret = CKR_BUFFER_TOO_SMALL;
+    }
+
+  return ret;
+}
+
+CK_RV
+mock_fail_C_GetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+                                   CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+  return CKR_FUNCTION_FAILED;
+}
+
+CK_RV
+mock_C_SetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+                              CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+  Session *session;
+  GPkcs11Array *attrs;
+  CK_ULONG i;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+  attrs = lookup_object (session, hObject);
+  g_return_val_if_fail (attrs, CKR_OBJECT_HANDLE_INVALID);
+
+  for (i = 0; i < ulCount; ++i)
+    g_pkcs11_array_set (attrs, pTemplate + i);
+
+  return CKR_OK;
+}
+
+typedef struct
+{
+  CK_ATTRIBUTE_PTR template;
+  CK_ULONG count;
+  Session *session;
+} FindObjects;
+
+static gboolean
+enumerate_and_find_objects (CK_OBJECT_HANDLE object,
+                            GPkcs11Array *attrs,
+                            gpointer user_data)
+{
+  FindObjects *ctx = user_data;
+  CK_ATTRIBUTE_PTR match;
+  const CK_ATTRIBUTE *attr;
+  CK_ULONG i;
+
+  for (i = 0; i < ctx->count; ++i)
+    {
+      match = ctx->template + i;
+      attr = g_pkcs11_array_find (attrs, match->type);
+      if (!attr)
+        return TRUE; /* Continue */
+
+      if (attr->ulValueLen != match->ulValueLen ||
+          memcmp (attr->pValue, match->pValue, attr->ulValueLen) != 0)
+        return TRUE; /* Continue */
+    }
+
+  ctx->session->matches = g_list_prepend (ctx->session->matches, GUINT_TO_POINTER (object));
+  return TRUE; /* Continue */
+}
+
+CK_RV
+mock_C_FindObjectsInit (CK_SESSION_HANDLE hSession,
+                        CK_ATTRIBUTE_PTR pTemplate,
+                        CK_ULONG ulCount)
+{
+  Session *session;
+  FindObjects ctx;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+  /* Starting an operation, cancels any previous one */
+  if (session->operation != 0)
+    session->operation = 0;
+
+  session->operation = OP_FIND;
+
+  ctx.template = pTemplate;
+  ctx.count = ulCount;
+  ctx.session = session;
+
+  mock_module_enumerate_objects (hSession, enumerate_and_find_objects, &ctx);
+  return CKR_OK;
+}
+
+CK_RV
+mock_fail_C_FindObjects (CK_SESSION_HANDLE hSession,
+                         CK_OBJECT_HANDLE_PTR phObject,
+                         CK_ULONG ulMaxObjectCount,
+                         CK_ULONG_PTR pulObjectCount)
+{
+  /* Always fails */
+  return CKR_FUNCTION_FAILED;
+}
+
+CK_RV
+mock_C_FindObjects (CK_SESSION_HANDLE hSession,
+                    CK_OBJECT_HANDLE_PTR phObject,
+                    CK_ULONG ulMaxObjectCount,
+                    CK_ULONG_PTR pulObjectCount)
+{
+  Session *session;
+
+  g_return_val_if_fail (phObject, CKR_ARGUMENTS_BAD);
+  g_return_val_if_fail (pulObjectCount, CKR_ARGUMENTS_BAD);
+  g_return_val_if_fail (ulMaxObjectCount != 0, CKR_ARGUMENTS_BAD);
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+  g_return_val_if_fail (session->operation == OP_FIND, CKR_OPERATION_NOT_INITIALIZED);
+
+  *pulObjectCount = 0;
+  while (ulMaxObjectCount > 0 && session->matches)
+    {
+      *phObject = GPOINTER_TO_UINT (session->matches->data);
+      ++phObject;
+      --ulMaxObjectCount;
+      ++(*pulObjectCount);
+      session->matches = g_list_remove (session->matches, session->matches->data);
+    }
+
+  return CKR_OK;
+}
+
+CK_RV
+mock_C_FindObjectsFinal (CK_SESSION_HANDLE hSession)
+{
+  Session *session;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+  g_return_val_if_fail (session->operation == OP_FIND, CKR_OPERATION_NOT_INITIALIZED);
+
+  session->operation = 0;
+  g_list_free (session->matches);
+  session->matches = NULL;
+
+  return CKR_OK;
+}
+
+CK_RV
+mock_no_mechanisms_C_EncryptInit (CK_SESSION_HANDLE hSession,
+                                  CK_MECHANISM_PTR pMechanism,
+                                  CK_OBJECT_HANDLE hKey)
+{
+  Session *session;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+  return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_not_initialized_C_Encrypt (CK_SESSION_HANDLE hSession,
+                                CK_BYTE_PTR pData,
+                                CK_ULONG ulDataLen,
+                                CK_BYTE_PTR pEncryptedData,
+                                CK_ULONG_PTR pulEncryptedDataLen)
+{
+  return CKR_OPERATION_NOT_INITIALIZED;
+}
+
+CK_RV
+mock_unsupported_C_EncryptUpdate (CK_SESSION_HANDLE hSession,
+                                  CK_BYTE_PTR pPart,
+                                  CK_ULONG ulPartLen,
+                                  CK_BYTE_PTR pEncryptedPart,
+                                  CK_ULONG_PTR pulEncryptedPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_EncryptFinal (CK_SESSION_HANDLE hSession,
+                                 CK_BYTE_PTR pLastEncryptedPart,
+                                 CK_ULONG_PTR pulLastEncryptedPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_no_mechanisms_C_DecryptInit (CK_SESSION_HANDLE hSession,
+                                  CK_MECHANISM_PTR pMechanism,
+                                  CK_OBJECT_HANDLE hKey)
+{
+  Session *session;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+  return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_not_initialized_C_Decrypt (CK_SESSION_HANDLE hSession,
+                                CK_BYTE_PTR pEncryptedData,
+                                CK_ULONG ulEncryptedDataLen,
+                                CK_BYTE_PTR pData,
+                                CK_ULONG_PTR pulDataLen)
+{
+  return CKR_OPERATION_NOT_INITIALIZED;
+}
+
+CK_RV
+mock_unsupported_C_DecryptUpdate (CK_SESSION_HANDLE hSession,
+                                  CK_BYTE_PTR pEncryptedPart,
+                                  CK_ULONG ulEncryptedPartLen,
+                                  CK_BYTE_PTR pPart,
+                                  CK_ULONG_PTR pulPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DecryptFinal (CK_SESSION_HANDLE hSession,
+                                 CK_BYTE_PTR pLastPart,
+                                 CK_ULONG_PTR pulLastPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestInit (CK_SESSION_HANDLE hSession,
+                               CK_MECHANISM_PTR pMechanism)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_Digest (CK_SESSION_HANDLE hSession,
+                           CK_BYTE_PTR pData,
+                           CK_ULONG ulDataLen,
+                           CK_BYTE_PTR pDigest,
+                           CK_ULONG_PTR pulDigestLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestUpdate (CK_SESSION_HANDLE hSession,
+                                 CK_BYTE_PTR pPart,
+                                 CK_ULONG ulPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestKey (CK_SESSION_HANDLE hSession,
+                              CK_OBJECT_HANDLE hKey)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestFinal (CK_SESSION_HANDLE hSession,
+                                CK_BYTE_PTR pDigest,
+                                CK_ULONG_PTR pulDigestLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_no_mechanisms_C_SignInit (CK_SESSION_HANDLE hSession,
+                               CK_MECHANISM_PTR pMechanism,
+                               CK_OBJECT_HANDLE hKey)
+{
+  Session *session;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+  return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_not_initialized_C_Sign (CK_SESSION_HANDLE hSession,
+                             CK_BYTE_PTR pData,
+                             CK_ULONG ulDataLen,
+                             CK_BYTE_PTR pSignature,
+                             CK_ULONG_PTR pulSignatureLen)
+{
+  return CKR_OPERATION_NOT_INITIALIZED;
+}
+
+CK_RV
+mock_unsupported_C_SignUpdate (CK_SESSION_HANDLE hSession,
+                               CK_BYTE_PTR pPart,
+                               CK_ULONG ulPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SignFinal (CK_SESSION_HANDLE hSession,
+                              CK_BYTE_PTR pSignature,
+                              CK_ULONG_PTR pulSignatureLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SignRecoverInit (CK_SESSION_HANDLE hSession,
+                                    CK_MECHANISM_PTR pMechanism,
+                                    CK_OBJECT_HANDLE hKey)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SignRecover (CK_SESSION_HANDLE hSession,
+                                CK_BYTE_PTR pData,
+                                CK_ULONG ulDataLen,
+                                CK_BYTE_PTR pSignature,
+                                CK_ULONG_PTR pulSignatureLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_no_mechanisms_C_VerifyInit (CK_SESSION_HANDLE hSession,
+                                 CK_MECHANISM_PTR pMechanism,
+                                 CK_OBJECT_HANDLE hKey)
+{
+  Session *session;
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+  return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_not_initialized_C_Verify (CK_SESSION_HANDLE hSession,
+                               CK_BYTE_PTR pData,
+                               CK_ULONG ulDataLen,
+                               CK_BYTE_PTR pSignature,
+                               CK_ULONG ulSignatureLen)
+{
+  return CKR_OPERATION_NOT_INITIALIZED;
+}
+
+CK_RV
+mock_unsupported_C_VerifyUpdate (CK_SESSION_HANDLE hSession,
+                                 CK_BYTE_PTR pPart,
+                                 CK_ULONG ulPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_VerifyFinal (CK_SESSION_HANDLE hSession,
+                                CK_BYTE_PTR pSignature,
+                                CK_ULONG pulSignatureLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_VerifyRecoverInit (CK_SESSION_HANDLE hSession,
+                                      CK_MECHANISM_PTR pMechanism,
+                                      CK_OBJECT_HANDLE hKey)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_VerifyRecover (CK_SESSION_HANDLE hSession,
+                                  CK_BYTE_PTR pSignature,
+                                  CK_ULONG pulSignatureLen,
+                                  CK_BYTE_PTR pData,
+                                  CK_ULONG_PTR pulDataLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestEncryptUpdate (CK_SESSION_HANDLE hSession,
+                                        CK_BYTE_PTR pPart,
+                                        CK_ULONG ulPartLen,
+                                        CK_BYTE_PTR pEncryptedPart,
+                                        CK_ULONG_PTR ulEncryptedPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DecryptDigestUpdate (CK_SESSION_HANDLE hSession,
+                                        CK_BYTE_PTR pEncryptedPart,
+                                        CK_ULONG ulEncryptedPartLen,
+                                        CK_BYTE_PTR pPart,
+                                        CK_ULONG_PTR pulPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SignEncryptUpdate (CK_SESSION_HANDLE hSession,
+                                      CK_BYTE_PTR pPart,
+                                      CK_ULONG ulPartLen,
+                                      CK_BYTE_PTR pEncryptedPart,
+                                      CK_ULONG_PTR ulEncryptedPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DecryptVerifyUpdate (CK_SESSION_HANDLE hSession,
+                                        CK_BYTE_PTR pEncryptedPart,
+                                        CK_ULONG ulEncryptedPartLen,
+                                        CK_BYTE_PTR pPart,
+                                        CK_ULONG_PTR pulPartLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_GenerateKey (CK_SESSION_HANDLE hSession,
+                                CK_MECHANISM_PTR pMechanism,
+                                CK_ATTRIBUTE_PTR pTemplate,
+                                CK_ULONG ulCount,
+                                CK_OBJECT_HANDLE_PTR phKey)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_no_mechanisms_C_GenerateKeyPair (CK_SESSION_HANDLE hSession,
+                                      CK_MECHANISM_PTR pMechanism,
+                                      CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+                                      CK_ULONG ulPublicKeyAttributeCount,
+                                      CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+                                      CK_ULONG ulPrivateKeyAttributeCount,
+                                      CK_OBJECT_HANDLE_PTR phPublicKey,
+                                      CK_OBJECT_HANDLE_PTR phPrivateKey)
+{
+  Session *session;
+
+  g_return_val_if_fail (pMechanism, CKR_MECHANISM_INVALID);
+  g_return_val_if_fail (pPublicKeyTemplate, CKR_TEMPLATE_INCOMPLETE);
+  g_return_val_if_fail (ulPublicKeyAttributeCount, CKR_TEMPLATE_INCOMPLETE);
+  g_return_val_if_fail (pPrivateKeyTemplate, CKR_TEMPLATE_INCOMPLETE);
+  g_return_val_if_fail (ulPrivateKeyAttributeCount, CKR_TEMPLATE_INCOMPLETE);
+  g_return_val_if_fail (phPublicKey, CKR_ARGUMENTS_BAD);
+  g_return_val_if_fail (phPrivateKey, CKR_ARGUMENTS_BAD);
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+  return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_no_mechanisms_C_WrapKey (CK_SESSION_HANDLE hSession,
+                              CK_MECHANISM_PTR pMechanism,
+                              CK_OBJECT_HANDLE hWrappingKey,
+                              CK_OBJECT_HANDLE hKey,
+                              CK_BYTE_PTR pWrappedKey,
+                              CK_ULONG_PTR pulWrappedKeyLen)
+{
+  Session *session;
+
+  g_return_val_if_fail (pMechanism, CKR_MECHANISM_INVALID);
+  g_return_val_if_fail (hWrappingKey, CKR_OBJECT_HANDLE_INVALID);
+  g_return_val_if_fail (hKey, CKR_OBJECT_HANDLE_INVALID);
+  g_return_val_if_fail (pulWrappedKeyLen, CKR_WRAPPED_KEY_LEN_RANGE);
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+  return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_no_mechanisms_C_UnwrapKey (CK_SESSION_HANDLE hSession,
+                                CK_MECHANISM_PTR pMechanism,
+                                CK_OBJECT_HANDLE hUnwrappingKey,
+                                CK_BYTE_PTR pWrappedKey,
+                                CK_ULONG ulWrappedKeyLen,
+                                CK_ATTRIBUTE_PTR pTemplate,
+                                CK_ULONG ulCount,
+                                CK_OBJECT_HANDLE_PTR phKey)
+{
+  Session *session;
+
+  g_return_val_if_fail (pMechanism, CKR_MECHANISM_INVALID);
+  g_return_val_if_fail (hUnwrappingKey, CKR_WRAPPING_KEY_HANDLE_INVALID);
+  g_return_val_if_fail (pWrappedKey, CKR_WRAPPED_KEY_INVALID);
+  g_return_val_if_fail (ulWrappedKeyLen, CKR_WRAPPED_KEY_LEN_RANGE);
+  g_return_val_if_fail (phKey, CKR_ARGUMENTS_BAD);
+  g_return_val_if_fail (pTemplate, CKR_TEMPLATE_INCOMPLETE);
+  g_return_val_if_fail (ulCount, CKR_TEMPLATE_INCONSISTENT);
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+  return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_no_mechanisms_C_DeriveKey (CK_SESSION_HANDLE hSession,
+                                CK_MECHANISM_PTR pMechanism,
+                                CK_OBJECT_HANDLE hBaseKey,
+                                CK_ATTRIBUTE_PTR pTemplate,
+                                CK_ULONG ulCount,
+                                CK_OBJECT_HANDLE_PTR phKey)
+{
+  Session *session;
+
+  g_return_val_if_fail (pMechanism, CKR_MECHANISM_INVALID);
+  g_return_val_if_fail (ulCount, CKR_TEMPLATE_INCOMPLETE);
+  g_return_val_if_fail (pTemplate, CKR_TEMPLATE_INCOMPLETE);
+  g_return_val_if_fail (phKey, CKR_ARGUMENTS_BAD);
+
+  session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+  g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+  return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_unsupported_C_SeedRandom (CK_SESSION_HANDLE hSession,
+                               CK_BYTE_PTR pSeed,
+                               CK_ULONG ulSeedLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_GenerateRandom (CK_SESSION_HANDLE hSession,
+                                   CK_BYTE_PTR pRandomData,
+                                   CK_ULONG ulRandomLen)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_FUNCTION_LIST mock_default_functions = {
+  { 2, 11 },        /* version */
+  mock_validate_and_C_Initialize,
+  mock_C_Finalize,
+  mock_C_GetInfo,
+  mock_C_GetFunctionList,
+  mock_C_GetSlotList,
+  mock_C_GetSlotInfo,
+  mock_C_GetTokenInfo,
+  mock_C_GetMechanismList,
+  mock_C_GetMechanismInfo,
+  mock_specific_args_C_InitToken,
+  mock_C_InitPIN,
+  mock_C_SetPIN,
+  mock_C_OpenSession,
+  mock_C_CloseSession,
+  mock_C_CloseAllSessions,
+  mock_C_GetSessionInfo,
+  mock_unsupported_C_GetOperationState,
+  mock_unsupported_C_SetOperationState,
+  mock_C_Login,
+  mock_C_Logout,
+  mock_C_CreateObject,
+  mock_unsupported_C_CopyObject,
+  mock_C_DestroyObject,
+  mock_unsupported_C_GetObjectSize,
+  mock_C_GetAttributeValue,
+  mock_C_SetAttributeValue,
+  mock_C_FindObjectsInit,
+  mock_C_FindObjects,
+  mock_C_FindObjectsFinal,
+  mock_no_mechanisms_C_EncryptInit,
+  mock_not_initialized_C_Encrypt,
+  mock_unsupported_C_EncryptUpdate,
+  mock_unsupported_C_EncryptFinal,
+  mock_no_mechanisms_C_DecryptInit,
+  mock_not_initialized_C_Decrypt,
+  mock_unsupported_C_DecryptUpdate,
+  mock_unsupported_C_DecryptFinal,
+  mock_unsupported_C_DigestInit,
+  mock_unsupported_C_Digest,
+  mock_unsupported_C_DigestUpdate,
+  mock_unsupported_C_DigestKey,
+  mock_unsupported_C_DigestFinal,
+  mock_no_mechanisms_C_SignInit,
+  mock_not_initialized_C_Sign,
+  mock_unsupported_C_SignUpdate,
+  mock_unsupported_C_SignFinal,
+  mock_unsupported_C_SignRecoverInit,
+  mock_unsupported_C_SignRecover,
+  mock_no_mechanisms_C_VerifyInit,
+  mock_not_initialized_C_Verify,
+  mock_unsupported_C_VerifyUpdate,
+  mock_unsupported_C_VerifyFinal,
+  mock_unsupported_C_VerifyRecoverInit,
+  mock_unsupported_C_VerifyRecover,
+  mock_unsupported_C_DigestEncryptUpdate,
+  mock_unsupported_C_DecryptDigestUpdate,
+  mock_unsupported_C_SignEncryptUpdate,
+  mock_unsupported_C_DecryptVerifyUpdate,
+  mock_unsupported_C_GenerateKey,
+  mock_no_mechanisms_C_GenerateKeyPair,
+  mock_no_mechanisms_C_WrapKey,
+  mock_no_mechanisms_C_UnwrapKey,
+  mock_no_mechanisms_C_DeriveKey,
+  mock_unsupported_C_SeedRandom,
+  mock_unsupported_C_GenerateRandom,
+  mock_C_GetFunctionStatus,
+  mock_C_CancelFunction,
+  mock_unsupported_C_WaitForSlotEvent
+};
diff --git a/tls/tests/mock-pkcs11.h b/tls/tests/mock-pkcs11.h
new file mode 100644
index 0000000..a6bfa27
--- /dev/null
+++ b/tls/tests/mock-pkcs11.h
@@ -0,0 +1,396 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (C) 2010 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include <glib.h>
+
+#include <p11-kit/pkcs11.h>
+
+#include "pkcs11/gpkcs11array.h"
+
+#ifndef MOCK_MODULE_H
+#define MOCK_MODULE_H
+
+extern CK_FUNCTION_LIST mock_default_functions;
+
+CK_RV               mock_C_Initialize                          (CK_VOID_PTR pInitArgs);
+
+CK_RV               mock_validate_and_C_Initialize             (CK_VOID_PTR pInitArgs);
+
+CK_RV               mock_C_Finalize                            (CK_VOID_PTR pReserved);
+
+CK_RV               mock_C_GetInfo                             (CK_INFO_PTR pInfo);
+
+CK_RV               mock_C_GetFunctionList                     (CK_FUNCTION_LIST_PTR_PTR list);
+
+CK_RV               mock_C_GetSlotList                         (CK_BBOOL tokenPresent,
+                                                                CK_SLOT_ID_PTR pSlotList,
+                                                                CK_ULONG_PTR pulCount);
+
+CK_RV               mock_C_GetSlotInfo                         (CK_SLOT_ID slotID,
+                                                                CK_SLOT_INFO_PTR pInfo);
+
+CK_RV               mock_C_GetTokenInfo                        (CK_SLOT_ID slotID,
+                                                                CK_TOKEN_INFO_PTR pInfo);
+
+CK_RV               mock_fail_C_GetTokenInfo                   (CK_SLOT_ID slotID,
+                                                                CK_TOKEN_INFO_PTR pInfo);
+
+CK_RV               mock_C_GetMechanismList                    (CK_SLOT_ID slotID,
+                                                                CK_MECHANISM_TYPE_PTR pMechanismList,
+                                                                CK_ULONG_PTR pulCount);
+
+CK_RV               mock_C_GetMechanismInfo                    (CK_SLOT_ID slotID,
+                                                                CK_MECHANISM_TYPE type,
+                                                                CK_MECHANISM_INFO_PTR pInfo);
+
+CK_RV               mock_specific_args_C_InitToken             (CK_SLOT_ID slotID,
+                                                                CK_UTF8CHAR_PTR pPin,
+                                                                CK_ULONG ulPinLen,
+                                                                CK_UTF8CHAR_PTR pLabel);
+
+CK_RV               mock_unsupported_C_WaitForSlotEvent        (CK_FLAGS flags,
+                                                                CK_SLOT_ID_PTR pSlot,
+                                                                CK_VOID_PTR pReserved);
+
+CK_RV               mock_C_OpenSession                         (CK_SLOT_ID slotID,
+                                                                CK_FLAGS flags,
+                                                                CK_VOID_PTR pApplication,
+                                                                CK_NOTIFY Notify,
+                                                                CK_SESSION_HANDLE_PTR phSession);
+
+CK_RV               mock_fail_C_OpenSession                    (CK_SLOT_ID slotID,
+                                                                CK_FLAGS flags,
+                                                                CK_VOID_PTR pApplication,
+                                                                CK_NOTIFY Notify,
+                                                                CK_SESSION_HANDLE_PTR phSession);
+
+CK_RV               mock_C_CloseSession                        (CK_SESSION_HANDLE hSession);
+
+CK_RV               mock_C_CloseAllSessions                    (CK_SLOT_ID slotID);
+
+CK_RV               mock_C_GetFunctionStatus                   (CK_SESSION_HANDLE hSession);
+
+CK_RV               mock_C_CancelFunction                      (CK_SESSION_HANDLE hSession);
+
+CK_RV               mock_C_GetSessionInfo                      (CK_SESSION_HANDLE hSession,
+                                                                CK_SESSION_INFO_PTR pInfo);
+
+CK_RV               mock_fail_C_GetSessionInfo                 (CK_SESSION_HANDLE hSession,
+                                                                CK_SESSION_INFO_PTR pInfo);
+
+CK_RV               mock_C_InitPIN                             (CK_SESSION_HANDLE hSession,
+                                                                CK_UTF8CHAR_PTR pPin,
+                                                                CK_ULONG ulPinLen);
+
+CK_RV               mock_C_SetPIN                              (CK_SESSION_HANDLE hSession,
+                                                                CK_UTF8CHAR_PTR pOldPin,
+                                                                CK_ULONG ulOldLen,
+                                                                CK_UTF8CHAR_PTR pNewPin,
+                                                                CK_ULONG ulNewLen);
+
+CK_RV               mock_unsupported_C_GetOperationState       (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pOperationState,
+                                                                CK_ULONG_PTR pulOperationStateLen);
+
+CK_RV               mock_unsupported_C_SetOperationState       (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pOperationState,
+                                                                CK_ULONG ulOperationStateLen,
+                                                                CK_OBJECT_HANDLE hEncryptionKey,
+                                                                CK_OBJECT_HANDLE hAuthenticationKey);
+
+CK_RV               mock_C_Login                               (CK_SESSION_HANDLE hSession,
+                                                                CK_USER_TYPE userType,
+                                                                CK_UTF8CHAR_PTR pPin,
+                                                                CK_ULONG pPinLen);
+
+CK_RV               mock_C_Logout                              (CK_SESSION_HANDLE hSession);
+
+CK_RV               mock_C_CreateObject                        (CK_SESSION_HANDLE hSession,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount,
+                                                                CK_OBJECT_HANDLE_PTR phObject);
+
+CK_RV               mock_fail_C_CreateObject                   (CK_SESSION_HANDLE hSession,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount,
+                                                                CK_OBJECT_HANDLE_PTR phObject);
+
+CK_RV               mock_unsupported_C_CopyObject              (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE hObject,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount,
+                                                                CK_OBJECT_HANDLE_PTR phNewObject);
+
+CK_RV               mock_C_DestroyObject                       (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE hObject);
+
+CK_RV               mock_unsupported_C_GetObjectSize           (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE hObject,
+                                                                CK_ULONG_PTR pulSize);
+
+CK_RV               mock_C_GetAttributeValue                   (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE hObject,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount);
+
+CK_RV               mock_fail_C_GetAttributeValue              (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE hObject,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount);
+
+CK_RV               mock_C_SetAttributeValue                   (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE hObject,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount);
+
+CK_RV               mock_C_FindObjectsInit                     (CK_SESSION_HANDLE hSession,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount);
+
+CK_RV               mock_C_FindObjects                         (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE_PTR phObject,
+                                                                CK_ULONG ulMaxObjectCount,
+                                                                CK_ULONG_PTR pulObjectCount);
+
+CK_RV               mock_fail_C_FindObjects                    (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE_PTR phObject,
+                                                                CK_ULONG ulMaxObjectCount,
+                                                                CK_ULONG_PTR pulObjectCount);
+
+CK_RV               mock_C_FindObjectsFinal                    (CK_SESSION_HANDLE hSession);
+
+CK_RV               mock_no_mechanisms_C_EncryptInit           (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE hKey);
+
+CK_RV               mock_not_initialized_C_Encrypt             (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pData,
+                                                                CK_ULONG ulDataLen,
+                                                                CK_BYTE_PTR pEncryptedData,
+                                                                CK_ULONG_PTR pulEncryptedDataLen);
+
+CK_RV               mock_unsupported_C_EncryptUpdate           (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG ulPartLen,
+                                                                CK_BYTE_PTR pEncryptedPart,
+                                                                CK_ULONG_PTR pulEncryptedPartLen);
+
+CK_RV               mock_unsupported_C_EncryptFinal            (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pLastEncryptedPart,
+                                                                CK_ULONG_PTR pulLastEncryptedPartLen);
+
+CK_RV               mock_no_mechanisms_C_DecryptInit           (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE hKey);
+
+CK_RV               mock_not_initialized_C_Decrypt             (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pEncryptedData,
+                                                                CK_ULONG ulEncryptedDataLen,
+                                                                CK_BYTE_PTR pData,
+                                                                CK_ULONG_PTR pulDataLen);
+
+CK_RV               mock_unsupported_C_DecryptUpdate           (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pEncryptedPart,
+                                                                CK_ULONG ulEncryptedPartLen,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG_PTR pulPartLen);
+
+CK_RV               mock_unsupported_C_DecryptFinal            (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pLastPart,
+                                                                CK_ULONG_PTR pulLastPartLen);
+
+CK_RV               mock_unsupported_C_DigestInit              (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism);
+
+CK_RV               mock_unsupported_C_Digest                  (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pData,
+                                                                CK_ULONG ulDataLen,
+                                                                CK_BYTE_PTR pDigest,
+                                                                CK_ULONG_PTR pulDigestLen);
+
+CK_RV               mock_unsupported_C_DigestUpdate            (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG ulPartLen);
+
+CK_RV               mock_unsupported_C_DigestKey               (CK_SESSION_HANDLE hSession,
+                                                                CK_OBJECT_HANDLE hKey);
+
+CK_RV               mock_unsupported_C_DigestFinal             (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pDigest,
+                                                                CK_ULONG_PTR pulDigestLen);
+
+CK_RV               mock_no_mechanisms_C_SignInit              (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE hKey);
+
+CK_RV               mock_not_initialized_C_Sign                (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pData,
+                                                                CK_ULONG ulDataLen,
+                                                                CK_BYTE_PTR pSignature,
+                                                                CK_ULONG_PTR pulSignatureLen);
+
+CK_RV               mock_unsupported_C_SignUpdate              (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG ulPartLen);
+
+CK_RV               mock_unsupported_C_SignFinal               (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pSignature,
+                                                                CK_ULONG_PTR pulSignatureLen);
+
+CK_RV               mock_unsupported_C_SignRecoverInit         (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE hKey);
+
+CK_RV               mock_unsupported_C_SignRecover             (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pData,
+                                                                CK_ULONG ulDataLen,
+                                                                CK_BYTE_PTR pSignature,
+                                                                CK_ULONG_PTR pulSignatureLen);
+
+CK_RV               mock_no_mechanisms_C_VerifyInit            (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE hKey);
+
+CK_RV               mock_not_initialized_C_Verify              (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pData,
+                                                                CK_ULONG ulDataLen,
+                                                                CK_BYTE_PTR pSignature,
+                                                                CK_ULONG ulSignatureLen);
+
+CK_RV               mock_unsupported_C_VerifyUpdate            (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG ulPartLen);
+
+CK_RV               mock_unsupported_C_VerifyFinal             (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pSignature,
+                                                                CK_ULONG pulSignatureLen);
+
+CK_RV               mock_unsupported_C_VerifyRecoverInit       (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE hKey);
+
+CK_RV               mock_unsupported_C_VerifyRecover           (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pSignature,
+                                                                CK_ULONG pulSignatureLen,
+                                                                CK_BYTE_PTR pData,
+                                                                CK_ULONG_PTR pulDataLen);
+
+CK_RV               mock_unsupported_C_DigestEncryptUpdate     (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG ulPartLen,
+                                                                CK_BYTE_PTR pEncryptedPart,
+                                                                CK_ULONG_PTR ulEncryptedPartLen);
+
+CK_RV               mock_unsupported_C_DecryptDigestUpdate     (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pEncryptedPart,
+                                                                CK_ULONG ulEncryptedPartLen,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG_PTR pulPartLen);
+
+CK_RV               mock_unsupported_C_SignEncryptUpdate       (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG ulPartLen,
+                                                                CK_BYTE_PTR pEncryptedPart,
+                                                                CK_ULONG_PTR ulEncryptedPartLen);
+
+CK_RV               mock_unsupported_C_DecryptVerifyUpdate     (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pEncryptedPart,
+                                                                CK_ULONG ulEncryptedPartLen,
+                                                                CK_BYTE_PTR pPart,
+                                                                CK_ULONG_PTR pulPartLen);
+
+CK_RV               mock_unsupported_C_GenerateKey             (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount,
+                                                                CK_OBJECT_HANDLE_PTR phKey);
+
+CK_RV               mock_no_mechanisms_C_GenerateKeyPair         (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+                                                                CK_ULONG ulPublicKeyAttributeCount,
+                                                                CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+                                                                CK_ULONG ulPrivateKeyAttributeCount,
+                                                                CK_OBJECT_HANDLE_PTR phPublicKey,
+                                                                CK_OBJECT_HANDLE_PTR phPrivateKey);
+
+CK_RV               mock_no_mechanisms_C_WrapKey                 (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE hWrappingKey,
+                                                                CK_OBJECT_HANDLE hKey,
+                                                                CK_BYTE_PTR pWrappedKey,
+                                                                CK_ULONG_PTR pulWrappedKeyLen);
+
+CK_RV               mock_no_mechanisms_C_UnwrapKey               (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE pUnwrappingKey,
+                                                                CK_BYTE_PTR pWrappedKey,
+                                                                CK_ULONG pulWrappedKeyLen,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount,
+                                                                CK_OBJECT_HANDLE_PTR phKey);
+
+CK_RV               mock_no_mechanisms_C_DeriveKey               (CK_SESSION_HANDLE hSession,
+                                                                CK_MECHANISM_PTR pMechanism,
+                                                                CK_OBJECT_HANDLE hBaseKey,
+                                                                CK_ATTRIBUTE_PTR pTemplate,
+                                                                CK_ULONG ulCount,
+                                                                CK_OBJECT_HANDLE_PTR phKey);
+
+CK_RV               mock_unsupported_C_SeedRandom              (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pSeed,
+                                                                CK_ULONG ulSeedLen);
+
+CK_RV               mock_unsupported_C_GenerateRandom          (CK_SESSION_HANDLE hSession,
+                                                                CK_BYTE_PTR pRandomData,
+                                                                CK_ULONG ulRandomLen);
+
+CK_OBJECT_HANDLE    mock_module_find_object                    (CK_SESSION_HANDLE session,
+                                                                CK_ATTRIBUTE_PTR attrs,
+                                                                CK_ULONG n_attrs);
+
+guint               mock_module_count_objects                  (CK_SESSION_HANDLE session);
+
+typedef gboolean    (*MockEnumerator)                          (CK_OBJECT_HANDLE handle,
+                                                                GPkcs11Array *attrs,
+                                                                gpointer user_data);
+
+void                mock_module_enumerate_objects              (CK_SESSION_HANDLE session,
+                                                                MockEnumerator func,
+                                                                gpointer user_data);
+
+CK_OBJECT_HANDLE    mock_module_take_object                    (GPkcs11Array *attrs);
+
+void                mock_module_set_object                     (CK_OBJECT_HANDLE object,
+                                                                CK_ATTRIBUTE_PTR attrs,
+                                                                CK_ULONG n_attrs);
+
+void                mock_module_set_pin                        (const gchar *password);
+
+#define MOCK_SLOT_ONE_ID  52
+#define MOCK_SLOT_TWO_ID  134
+
+#define MOCK_SLOT_ONE_PIN "booo"
+#define MOCK_SLOT_ONE_URI "pkcs11:manufacturer=TEST%20MANUFACTURER;serial=TEST%20SERIAL"
+
+#endif /* MOCK_MODULE_H */
diff --git a/tls/tests/pkcs11-array.c b/tls/tests/pkcs11-array.c
new file mode 100644
index 0000000..c3c512d
--- /dev/null
+++ b/tls/tests/pkcs11-array.c
@@ -0,0 +1,288 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "pkcs11/gpkcs11array.h"
+
+typedef struct {
+  GPkcs11Array *array;
+} TestArray;
+
+static void
+setup_array (TestArray          *test,
+             gconstpointer       unused)
+{
+  test->array = g_pkcs11_array_new ();
+  g_assert (test->array);
+}
+
+static void
+teardown_array (TestArray       *test,
+                gconstpointer    unused)
+{
+  g_pkcs11_array_unref (test->array);
+}
+
+static void
+test_add_find (TestArray      *test,
+               gconstpointer   data)
+{
+  CK_ATTRIBUTE attr;
+  const CK_ATTRIBUTE *check;
+  const gchar *value = "test";
+
+  attr.type = CKA_LABEL;
+  attr.ulValueLen = strlen (value) + 1;
+  attr.pValue = (gpointer)value;
+  g_pkcs11_array_add (test->array, &attr);
+  memset (&attr, 0, sizeof (attr));
+
+  check = g_pkcs11_array_find (test->array, CKA_LABEL);
+  g_assert (check != NULL);
+  g_assert_cmpuint ((guint)check->ulValueLen, ==, strlen (value) + 1);
+  g_assert_cmpstr (check->pValue, ==, value);
+  g_assert (check->pValue != value);
+
+  /* Should be copied properly, and be independent from stack value */
+  g_assert (check != &attr);
+
+  check = g_pkcs11_array_find (test->array, CKA_ID);
+  g_assert (check == NULL);
+  g_assert_cmpuint (test->array->count, ==, 1);
+
+  /* Adding a second value of same type, should add a duplicate */
+  attr.type = CKA_LABEL;
+  attr.ulValueLen = 3;
+  attr.pValue = "bye";
+  g_pkcs11_array_add (test->array, &attr);
+  g_assert_cmpuint (test->array->count, ==, 2);
+}
+
+static void
+test_set_find (TestArray      *test,
+               gconstpointer   data)
+{
+  CK_ATTRIBUTE attr;
+  const CK_ATTRIBUTE *check;
+  const gchar *value = "test";
+
+  attr.type = CKA_LABEL;
+  attr.ulValueLen = strlen (value) + 1;
+  attr.pValue = (gpointer)value;
+  g_pkcs11_array_set (test->array, &attr);
+  memset (&attr, 0, sizeof (attr));
+
+  check = g_pkcs11_array_find (test->array, CKA_LABEL);
+  g_assert (check != NULL);
+  g_assert_cmpuint ((guint)check->ulValueLen, ==, strlen (value) + 1);
+  g_assert_cmpstr (check->pValue, ==, value);
+  g_assert (check->pValue != value);
+
+  /* Should be copied properly, and be independent from stack value */
+  g_assert (check != &attr);
+
+  /* Adding a second value of same type should override */
+  attr.type = CKA_LABEL;
+  attr.ulValueLen = 3;
+  attr.pValue = "bye";
+  g_pkcs11_array_set (test->array, &attr);
+  g_assert_cmpuint (test->array->count, ==, 1);
+}
+
+static void
+test_value (TestArray      *test,
+            gconstpointer   data)
+{
+  const CK_ATTRIBUTE *check;
+  const gchar *value = "test";
+
+  /* Add with null termiator */
+  g_pkcs11_array_add_value (test->array, CKA_LABEL, value, -1);
+  check = g_pkcs11_array_find (test->array, CKA_LABEL);
+  g_assert (check != NULL);
+  g_assert_cmpuint ((guint)check->ulValueLen, ==, strlen (value));
+  g_assert (memcmp (check->pValue, value, check->ulValueLen) == 0);
+  g_assert (check->pValue != value);
+
+  /* Add with value length */
+  g_pkcs11_array_add_value (test->array, CKA_ID, value, 3);
+  check = g_pkcs11_array_find (test->array, CKA_ID);
+  g_assert (check != NULL);
+  g_assert_cmpuint ((guint)check->ulValueLen, ==, 3);
+  g_assert (memcmp (check->pValue, value, check->ulValueLen) == 0);
+  g_assert (check->pValue != value);
+  g_assert_cmpuint (test->array->count, ==, 2);
+
+  /* Set should override */
+  g_pkcs11_array_set_value (test->array, CKA_LABEL, "boring", 6);
+  check = g_pkcs11_array_find (test->array, CKA_LABEL);
+  g_assert (check != NULL);
+  g_assert_cmpuint ((guint)check->ulValueLen, ==, 6);
+  g_assert (memcmp (check->pValue, "boring", check->ulValueLen) == 0);
+  g_assert_cmpuint (test->array->count, ==, 2);
+
+  /* Override with calculated length */
+  g_pkcs11_array_set_value (test->array, CKA_LABEL, "boring", -1);
+  check = g_pkcs11_array_find (test->array, CKA_LABEL);
+  g_assert (check != NULL);
+  g_assert_cmpuint ((guint)check->ulValueLen, ==, 6);
+  g_assert (memcmp (check->pValue, "boring", check->ulValueLen) == 0);
+  g_assert_cmpuint (test->array->count, ==, 2);
+
+}
+
+static void
+test_boolean (TestArray      *test,
+              gconstpointer   data)
+{
+  const CK_ATTRIBUTE *check;
+  gboolean bval = FALSE;
+
+  g_pkcs11_array_add_boolean (test->array, CKA_TOKEN, TRUE);
+  if (!g_pkcs11_array_find_boolean (test->array, CKA_TOKEN, &bval))
+    g_assert_not_reached ();
+  g_assert (bval == TRUE);
+
+  /* Check that it's actually formatted right */
+  check = g_pkcs11_array_find (test->array, CKA_TOKEN);
+  g_assert (check != NULL);
+  g_assert_cmpuint (check->ulValueLen, ==, sizeof (CK_BBOOL));
+  g_assert (check->pValue != NULL);
+  g_assert (*((CK_BBOOL*)check->pValue) == CK_TRUE);
+
+  /* Check FALSE */
+  g_pkcs11_array_add_boolean (test->array, CKA_ENCRYPT, FALSE);
+
+  /* Check that it's actually formatted right */
+  check = g_pkcs11_array_find (test->array, CKA_ENCRYPT);
+  g_assert (check != NULL);
+  g_assert_cmpuint (check->ulValueLen, ==, sizeof (CK_BBOOL));
+  g_assert (check->pValue != NULL);
+  g_assert (*((CK_BBOOL*)check->pValue) == CK_FALSE);
+  g_assert_cmpuint (test->array->count, ==, 2);
+
+  /* Add a non boolean value */
+  g_pkcs11_array_add_value (test->array, CKA_LABEL, "label", -1);
+
+  /* Shouldn't work to find boolean on that */
+  if (g_pkcs11_array_find_boolean (test->array, CKA_LABEL, &bval))
+    g_assert_not_reached ();
+  g_assert_cmpuint (test->array->count, ==, 3);
+
+  /* Set should override */
+  g_pkcs11_array_set_boolean (test->array, CKA_TOKEN, FALSE);
+  if (!g_pkcs11_array_find_boolean (test->array, CKA_TOKEN, &bval))
+    g_assert_not_reached ();
+  g_assert (bval == FALSE);
+  g_assert_cmpuint (test->array->count, ==, 3);
+}
+
+static void
+test_ulong (TestArray      *test,
+            gconstpointer   data)
+{
+  const CK_ATTRIBUTE *check;
+  gulong uval = FALSE;
+
+  g_pkcs11_array_add_ulong (test->array, CKA_PIXEL_X, 38938);
+  if (!g_pkcs11_array_find_ulong (test->array, CKA_PIXEL_X, &uval))
+    g_assert_not_reached ();
+  g_assert (uval == 38938UL);
+  g_assert_cmpuint (test->array->count, ==, 1);
+
+  /* Check that it's actually formatted right */
+  check = g_pkcs11_array_find (test->array, CKA_PIXEL_X);
+  g_assert (check != NULL);
+  g_assert_cmpuint (check->ulValueLen, ==, sizeof (CK_ULONG));
+  g_assert (check->pValue != NULL);
+  g_assert (*((CK_ULONG*)check->pValue) == 38938UL);
+
+  /* Check -1, since this is used regularly */
+  g_pkcs11_array_add_ulong (test->array, CKA_MODULUS_BITS, (gulong)-1);
+
+  /* Check that it's actually formatted right */
+  check = g_pkcs11_array_find (test->array, CKA_MODULUS_BITS);
+  g_assert (check != NULL);
+  g_assert_cmpuint (check->ulValueLen, ==, sizeof (CK_ULONG));
+  g_assert (check->pValue != NULL);
+  g_assert (*((CK_ULONG*)check->pValue) == (CK_ULONG)-1);
+  g_assert_cmpuint (test->array->count, ==, 2);
+
+  /* Add a non ulong length value */
+  g_pkcs11_array_add_value (test->array, CKA_LABEL, "label", -1);
+  g_assert_cmpuint (test->array->count, ==, 3);
+
+  /* Shouldn't work to find ulong on that */
+  if (g_pkcs11_array_find_ulong (test->array, CKA_LABEL, &uval))
+    g_assert_not_reached ();
+
+  /* Set should override */
+  g_pkcs11_array_set_ulong (test->array, CKA_PIXEL_X, 48);
+  if (!g_pkcs11_array_find_ulong (test->array, CKA_PIXEL_X, &uval))
+    g_assert_not_reached ();
+  g_assert (uval == 48UL);
+  g_assert_cmpuint (test->array->count, ==, 3);
+}
+
+static void
+test_boxed (TestArray      *test,
+            gconstpointer   data)
+{
+  GPkcs11Array *array;
+
+  /* Should reference */
+  array = g_boxed_copy (G_TYPE_PKCS11_ARRAY, test->array);
+  g_assert (array == test->array);
+
+  /* Should unreference */
+  g_boxed_free (G_TYPE_PKCS11_ARRAY, array);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add ("/pkcs11/array/add-find", TestArray, NULL,
+              setup_array, test_add_find, teardown_array);
+  g_test_add ("/pkcs11/array/set-find", TestArray, NULL,
+              setup_array, test_set_find, teardown_array);
+  g_test_add ("/pkcs11/array/value", TestArray, NULL,
+              setup_array, test_value, teardown_array);
+  g_test_add ("/pkcs11/array/boolean", TestArray, NULL,
+              setup_array, test_boolean, teardown_array);
+  g_test_add ("/pkcs11/array/ulong", TestArray, NULL,
+              setup_array, test_ulong, teardown_array);
+  g_test_add ("/pkcs11/array/boxed", TestArray, NULL,
+              setup_array, test_boxed, teardown_array);
+
+  return g_test_run();
+}
diff --git a/tls/tests/pkcs11-pin.c b/tls/tests/pkcs11-pin.c
new file mode 100644
index 0000000..79a34dd
--- /dev/null
+++ b/tls/tests/pkcs11-pin.c
@@ -0,0 +1,152 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "pkcs11/gpkcs11pin.h"
+
+typedef struct {
+  GTlsPassword *pin;
+} TestPin;
+
+static void
+setup_pin (TestPin          *test,
+           gconstpointer     unused)
+{
+  test->pin = g_pkcs11_pin_new (G_TLS_PASSWORD_RETRY, "Test description");
+  g_assert (G_IS_PKCS11_PIN (test->pin));
+  g_assert (G_IS_TLS_PASSWORD (test->pin));
+}
+
+static void
+teardown_pin (TestPin       *test,
+              gconstpointer  unused)
+{
+  g_assert_cmpint (G_OBJECT (test->pin)->ref_count, ==, 1);
+  g_object_unref (test->pin);
+}
+
+static void
+test_attributes (TestPin        *test,
+                 gconstpointer   data)
+{
+  GTlsPasswordFlags flags;
+  const gchar *description;
+
+  flags = g_tls_password_get_flags (test->pin);
+  g_assert_cmpuint (flags, ==, G_TLS_PASSWORD_RETRY);
+
+  description = g_tls_password_get_description (test->pin);
+  g_assert_cmpstr (description, ==, "Test description");
+}
+
+static void
+test_warnings (TestPin        *test,
+               gconstpointer   data)
+{
+  const gchar *warning;
+
+  g_tls_password_set_flags (test->pin, G_TLS_PASSWORD_RETRY);
+  warning = g_tls_password_get_warning (test->pin);
+  g_assert (warning != NULL);
+
+  g_tls_password_set_flags (test->pin, G_TLS_PASSWORD_FINAL_TRY);
+  warning = g_tls_password_get_warning (test->pin);
+  g_assert (warning != NULL);
+
+  g_tls_password_set_flags (test->pin, G_TLS_PASSWORD_MANY_TRIES);
+  warning = g_tls_password_get_warning (test->pin);
+  g_assert (warning != NULL);
+
+  g_tls_password_set_flags (test->pin, (GTlsPasswordFlags)0x10000000);
+  warning = g_tls_password_get_warning (test->pin);
+  g_assert (warning == NULL);
+
+}
+
+static void
+test_set_get_value (TestPin        *test,
+                    gconstpointer   data)
+{
+  const guchar *value;
+  gsize n_value = G_MAXSIZE;
+
+  value = g_tls_password_get_value (test->pin, &n_value);
+  g_assert_cmpuint (n_value, ==, 0);
+  g_assert (value == NULL);
+
+  g_tls_password_set_value (test->pin, (const guchar *)"secret", -1);
+
+  value = g_tls_password_get_value (test->pin, &n_value);
+  g_assert_cmpuint (n_value, ==, 6);
+  g_assert (!strncmp ((const gchar *)value, "secret", n_value));
+
+  g_tls_password_set_value (test->pin, (const guchar *)"other", 5);
+
+  value = g_tls_password_get_value (test->pin, &n_value);
+  g_assert_cmpuint (n_value, ==, 5);
+  g_assert (!strncmp ((const gchar *)value, "other", n_value));
+}
+
+static void
+test_internal_pin (TestPin        *test,
+                   gconstpointer   data)
+{
+  P11KitPin *pin;
+  const unsigned char *value;
+  size_t n_value;
+
+  g_tls_password_set_value (test->pin, (const guchar *)"secret", -1);
+
+  pin = g_pkcs11_pin_steal_internal (G_PKCS11_PIN (test->pin));
+
+  value = p11_kit_pin_get_value (pin, &n_value);
+  g_assert_cmpuint (n_value, ==, 6);
+  g_assert (!strncmp ((const gchar *)value, "secret", n_value));
+
+  p11_kit_pin_unref (pin);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add ("/pkcs11/pin/attributes", TestPin, NULL,
+              setup_pin, test_attributes, teardown_pin);
+  g_test_add ("/pkcs11/pin/warnings", TestPin, NULL,
+              setup_pin, test_warnings, teardown_pin);
+  g_test_add ("/pkcs11/pin/set-get-value", TestPin, NULL,
+              setup_pin, test_set_get_value, teardown_pin);
+  g_test_add ("/pkcs11/pin/internal-pin", TestPin, NULL,
+              setup_pin, test_internal_pin, teardown_pin);
+
+  return g_test_run();
+}
diff --git a/tls/tests/pkcs11-slot.c b/tls/tests/pkcs11-slot.c
new file mode 100644
index 0000000..0fcd44b
--- /dev/null
+++ b/tls/tests/pkcs11-slot.c
@@ -0,0 +1,526 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "pkcs11/gpkcs11slot.h"
+#include "pkcs11/gpkcs11util.h"
+
+#include "mock-pkcs11.h"
+#include "mock-interaction.h"
+
+#include <p11-kit/p11-kit.h>
+
+#include <stdlib.h>
+
+typedef struct {
+  CK_FUNCTION_LIST funcs;
+  GPkcs11Slot *slot;
+  GPkcs11Slot *not_present;
+} TestSlot;
+
+static void
+setup_slot (TestSlot        *test,
+            gconstpointer    unused)
+{
+  CK_RV rv;
+
+  /* Copy this so we can replace certain functions in our tests */
+  memcpy (&test->funcs, &mock_default_functions, sizeof (test->funcs));
+
+  rv = p11_kit_module_initialize (&test->funcs);
+  g_assert (rv == CKR_OK);
+
+  test->slot = g_object_new (G_TYPE_PKCS11_SLOT,
+                             "slot-id", MOCK_SLOT_ONE_ID,
+                             "module", &test->funcs,
+                             NULL);
+  g_assert (G_IS_PKCS11_SLOT (test->slot));
+
+  test->not_present = g_object_new (G_TYPE_PKCS11_SLOT,
+                                    "slot-id", MOCK_SLOT_TWO_ID,
+                                    "module", &test->funcs,
+                                    NULL);
+  g_assert (G_IS_PKCS11_SLOT (test->not_present));
+}
+
+static void
+teardown_slot (TestSlot     *test,
+               gconstpointer unused)
+{
+  CK_RV rv;
+
+  g_assert_cmpint (G_OBJECT (test->slot)->ref_count, ==, 1);
+  g_object_unref (test->slot);
+
+  g_assert_cmpint (G_OBJECT (test->not_present)->ref_count, ==, 1);
+  g_object_unref (test->not_present);
+
+  rv = p11_kit_module_finalize (&test->funcs);
+  g_assert (rv == CKR_OK);
+}
+
+static void
+test_properties (TestSlot       *test,
+                 gconstpointer   unused)
+{
+  CK_SLOT_ID id;
+  CK_FUNCTION_LIST_PTR module;
+
+  g_object_get (test->slot, "slot-id", &id, "module", &module, NULL);
+  g_assert_cmpuint (id, ==, MOCK_SLOT_ONE_ID);
+  g_assert (module == &test->funcs);
+}
+
+static void
+test_token_info (TestSlot       *test,
+                 gconstpointer   unused)
+{
+  CK_TOKEN_INFO token_info;
+  char *label;
+
+  if (!g_pkcs11_slot_get_token_info (test->slot, &token_info))
+    g_assert_not_reached ();
+
+  label = p11_kit_space_strdup (token_info.label, sizeof (token_info.label));
+  g_assert_cmpstr (label, ==, "TEST LABEL");
+  free (label);
+}
+
+static void
+test_token_info_not_present (TestSlot       *test,
+                             gconstpointer   unused)
+{
+  CK_TOKEN_INFO token_info;
+  char *label;
+
+  if (!g_pkcs11_slot_get_token_info (test->slot, &token_info))
+    g_assert_not_reached ();
+
+  label = p11_kit_space_strdup (token_info.label, sizeof (token_info.label));
+  g_assert_cmpstr (label, ==, "TEST LABEL");
+  free (label);
+}
+
+static void
+test_matches_uri (TestSlot       *test,
+                  gconstpointer   unused)
+{
+  P11KitUri *uri;
+
+  uri = p11_kit_uri_new ();
+  if (p11_kit_uri_parse (MOCK_SLOT_ONE_URI, P11_KIT_URI_FOR_TOKEN, uri) != 0)
+    g_assert_not_reached ();
+  g_assert (!p11_kit_uri_any_unrecognized (uri));
+
+  if (!g_pkcs11_slot_matches_uri (test->slot, uri))
+    g_assert_not_reached();
+
+  if (g_pkcs11_slot_matches_uri (test->not_present, uri))
+    g_assert_not_reached ();
+
+  p11_kit_uri_free (uri);
+}
+
+
+static gboolean
+accumulate_check_not_called (gpointer result,
+                             gpointer user_data)
+{
+  g_assert_not_reached ();
+  return FALSE;
+}
+
+static void
+test_enumerate_no_match (TestSlot     *test,
+                         gconstpointer unused)
+{
+  GPkcs11EnumerateState state;
+  CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+  GError *error = NULL;
+  GPkcs11Array *match;
+
+  match = g_pkcs11_array_new ();
+  g_pkcs11_array_add_value (match, CKA_LABEL, "Non existant", -1);
+  g_pkcs11_array_add_value (match, CKA_ID, "Bad ID", -1);
+
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   match->attrs, match->count, FALSE,
+                                   types, G_N_ELEMENTS (types),
+                                   accumulate_check_not_called, NULL,
+                                   NULL, &error);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+  g_assert_no_error (error);
+
+  g_pkcs11_array_unref (match);
+}
+
+static void
+test_enumerate_not_present (TestSlot      *test,
+                            gconstpointer  unused)
+{
+  GPkcs11EnumerateState state;
+  CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+  GError *error = NULL;
+  GPkcs11Array *match;
+
+  /* Empty match should match anything ... */
+  match = g_pkcs11_array_new ();
+
+  /* ... but token is not present, so nothing */
+  state = g_pkcs11_slot_enumerate (test->not_present, NULL,
+                                   match->attrs, match->count, FALSE,
+                                   types, G_N_ELEMENTS (types),
+                                   accumulate_check_not_called, NULL,
+                                   NULL, &error);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+  g_assert_no_error (error);
+
+  g_pkcs11_array_unref (match);
+}
+
+static gboolean
+accumulate_results (gpointer result,
+                    gpointer user_data)
+{
+  GPtrArray *results = user_data;
+  GPkcs11Array *attrs = result;
+
+  g_assert (results);
+  g_assert (attrs);
+
+  g_ptr_array_add (results, g_pkcs11_array_ref (attrs));
+  return TRUE;
+}
+
+static void
+test_enumerate_all (TestSlot     *test,
+                    gconstpointer unused)
+{
+  GPkcs11EnumerateState state;
+  CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+  GError *error = NULL;
+  GPkcs11Array *match;
+  GPkcs11Array *attrs;
+  GPtrArray *results;
+  const CK_ATTRIBUTE *attr;
+  guint i;
+
+  /* Match anything */
+  match = g_pkcs11_array_new ();
+
+  results = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pkcs11_array_unref);
+
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   match->attrs, match->count, FALSE,
+                                   types, G_N_ELEMENTS (types),
+                                   accumulate_results, results,
+                                   NULL, &error);
+
+  g_pkcs11_array_unref (match);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+  g_assert_no_error (error);
+
+  g_assert_cmpuint (results->len, >, 1);
+
+  for (i = 0; i < results->len; i++)
+    {
+      attrs = results->pdata[i];
+      attr = g_pkcs11_array_find (attrs, CKA_LABEL);
+      g_assert (attr != NULL);
+      g_assert (g_utf8_validate (attr->pValue, attr->ulValueLen, NULL));
+    }
+
+  g_ptr_array_free (results, TRUE);
+}
+
+static gboolean
+accumulate_first (gpointer result,
+                  gpointer user_data)
+{
+  GPtrArray *results = user_data;
+  GPkcs11Array *attrs = result;
+
+  g_assert (results);
+  g_assert (attrs);
+  g_assert_cmpuint (results->len, ==, 0);
+
+  g_ptr_array_add (results, g_pkcs11_array_ref (attrs));
+  return FALSE; /* Don't call again */
+}
+
+static void
+test_enumerate_first (TestSlot     *test,
+                      gconstpointer unused)
+{
+  GPkcs11EnumerateState state;
+  CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+  GError *error = NULL;
+  GPkcs11Array *match;
+  GPkcs11Array *attrs;
+  GPtrArray *results;
+  const CK_ATTRIBUTE *attr;
+
+  /* Match anything */
+  match = g_pkcs11_array_new ();
+
+  results = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pkcs11_array_unref);
+
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   match->attrs, match->count, FALSE,
+                                   types, G_N_ELEMENTS (types),
+                                   accumulate_first, results,
+                                   NULL, &error);
+
+  g_pkcs11_array_unref (match);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_STOP);
+  g_assert_no_error (error);
+
+  g_assert_cmpuint (results->len, ==, 1);
+  attrs = results->pdata[0];
+  attr = g_pkcs11_array_find (attrs, CKA_LABEL);
+  g_assert (attr != NULL);
+  g_assert (g_utf8_validate (attr->pValue, attr->ulValueLen, NULL));
+
+  g_ptr_array_free (results, TRUE);
+}
+
+static gboolean
+accumulate_check_null_result (gpointer result,
+                              gpointer user_data)
+{
+  GPkcs11Array *attrs = result;
+  g_assert (attrs == NULL);
+  return TRUE; /* call again */
+}
+
+static void
+test_enumerate_no_attrs (TestSlot     *test,
+                         gconstpointer unused)
+{
+  GPkcs11EnumerateState state;
+  GError *error = NULL;
+  GPkcs11Array *match;
+
+  /* Match anything */
+  match = g_pkcs11_array_new ();
+
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   match->attrs, match->count, FALSE,
+                                   NULL, 0,
+                                   accumulate_check_null_result, NULL,
+                                   NULL, &error);
+
+  g_pkcs11_array_unref (match);
+
+  /* Didn't find anything, so continue */
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+  g_assert_no_error (error);
+}
+
+static void
+test_enumerate_fail_session (TestSlot     *test,
+                             gconstpointer unused)
+{
+  GPkcs11EnumerateState state;
+  GError *error = NULL;
+
+  /* Make opening a session fail */
+  test->funcs.C_OpenSession = mock_fail_C_OpenSession;
+
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   NULL, 0, FALSE,
+                                   NULL, 0,
+                                   accumulate_check_not_called, NULL,
+                                   NULL, &error);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_FAILED);
+  g_assert_error (error, G_PKCS11_ERROR, CKR_GENERAL_ERROR);
+  g_error_free (error);
+}
+
+static void
+test_enumerate_fail_attributes (TestSlot     *test,
+                                gconstpointer unused)
+{
+  GPkcs11EnumerateState state;
+  GError *error = NULL;
+  CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+
+  /* Make retrieving object attrs fail */
+  test->funcs.C_GetAttributeValue = mock_fail_C_GetAttributeValue;
+
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   NULL, 0, FALSE,
+                                   types, G_N_ELEMENTS (types),
+                                   accumulate_check_not_called, NULL,
+                                   NULL, &error);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_FAILED);
+  g_assert_error (error, G_PKCS11_ERROR, CKR_FUNCTION_FAILED);
+  g_error_free (error);
+}
+
+static gboolean
+accumulate_cancel_on_first (gpointer result,
+                            gpointer user_data)
+{
+  GCancellable *cancellable = G_CANCELLABLE (user_data);
+  g_assert (!g_cancellable_is_cancelled (cancellable));
+  g_cancellable_cancel (cancellable);
+  return TRUE; /* call again, except that above cancellation should stop */
+}
+
+static void
+test_enumerate_cancel (TestSlot     *test,
+                       gconstpointer unused)
+{
+  GPkcs11EnumerateState state;
+  GError *error = NULL;
+  GPkcs11Array *match;
+  GCancellable *cancellable;
+
+  cancellable = g_cancellable_new ();
+
+  /* Match anything */
+  match = g_pkcs11_array_new ();
+
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   match->attrs, match->count, FALSE,
+                                   NULL, 0,
+                                   accumulate_cancel_on_first, cancellable,
+                                   cancellable, &error);
+
+  g_pkcs11_array_unref (match);
+  g_object_unref (cancellable);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_FAILED);
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+  g_error_free (error);
+}
+
+static void
+test_enumerate_private (TestSlot     *test,
+                        gconstpointer unused)
+{
+  CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID, CKA_PRIVATE };
+  GPkcs11EnumerateState state;
+  GError *error = NULL;
+  GPkcs11Array *match;
+  GPtrArray *results;
+  gboolean bval;
+  GTlsInteraction *interaction;
+
+  /* Match label of private object, see mock*/
+  match = g_pkcs11_array_new ();
+  g_pkcs11_array_add_value (match, CKA_LABEL, "PRIVATE", -1);
+
+  /* Shouldn't match anything, since not logged in */
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   match->attrs, match->count, FALSE,
+                                   types, G_N_ELEMENTS (types),
+                                   accumulate_check_not_called, NULL,
+                                   NULL, &error);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+  g_assert_no_error (error);
+
+  /* This time we try to log in but no interaction is set */
+  state = g_pkcs11_slot_enumerate (test->slot, NULL,
+                                   match->attrs, match->count, TRUE, /* match privates */
+                                   types, G_N_ELEMENTS (types),
+                                   accumulate_check_not_called, NULL,
+                                   NULL, &error);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+  g_assert_no_error (error);
+
+  /* This time we log in, and should have a match */
+  results = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pkcs11_array_unref);
+  interaction = mock_interaction_new_static_password (MOCK_SLOT_ONE_PIN);
+
+  state = g_pkcs11_slot_enumerate (test->slot, interaction,
+                                   match->attrs, match->count, TRUE,
+                                   types, G_N_ELEMENTS (types),
+                                   accumulate_results, results,
+                                   NULL, &error);
+
+  g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+  g_assert_no_error (error);
+
+  /* One private object, with following info */
+  g_assert_cmpuint (results->len, ==, 1);
+  if (!g_pkcs11_array_find_boolean (results->pdata[0], CKA_PRIVATE, &bval))
+    g_assert_not_reached ();
+  g_assert (bval == TRUE);
+
+  g_object_unref (interaction);
+  g_pkcs11_array_unref (match);
+  g_ptr_array_free (results, TRUE);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add ("/pkcs11/slot/properties", TestSlot, NULL,
+              setup_slot, test_properties, teardown_slot);
+  g_test_add ("/pkcs11/slot/token-info", TestSlot, NULL,
+              setup_slot, test_token_info, teardown_slot);
+  g_test_add ("/pkcs11/slot/token-not-present", TestSlot, NULL,
+              setup_slot, test_token_info_not_present, teardown_slot);
+  g_test_add ("/pkcs11/slot/matches-uri", TestSlot, NULL,
+              setup_slot, test_matches_uri, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-no-match", TestSlot, NULL,
+              setup_slot, test_enumerate_no_match, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-not-present", TestSlot, NULL,
+              setup_slot, test_enumerate_not_present, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-all", TestSlot, NULL,
+              setup_slot, test_enumerate_all, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-first", TestSlot, NULL,
+              setup_slot, test_enumerate_first, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-no-attrs", TestSlot, NULL,
+              setup_slot, test_enumerate_no_attrs, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-fail-session", TestSlot, NULL,
+              setup_slot, test_enumerate_fail_session, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-fail-attributes", TestSlot, NULL,
+              setup_slot, test_enumerate_fail_attributes, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-cancel", TestSlot, NULL,
+              setup_slot, test_enumerate_cancel, teardown_slot);
+  g_test_add ("/pkcs11/slot/enumerate-private", TestSlot, NULL,
+              setup_slot, test_enumerate_private, teardown_slot);
+
+  return g_test_run();
+}
diff --git a/tls/tests/pkcs11-util.c b/tls/tests/pkcs11-util.c
new file mode 100644
index 0000000..b432651
--- /dev/null
+++ b/tls/tests/pkcs11-util.c
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "pkcs11/gpkcs11util.h"
+
+static void
+test_propagate_error (void)
+{
+  GError *error = NULL;
+
+  if (!g_pkcs11_propagate_error (&error, CKR_BUFFER_TOO_SMALL))
+    g_assert_not_reached ();
+  g_assert_error (error, G_PKCS11_ERROR, (gint)CKR_BUFFER_TOO_SMALL);
+  g_clear_error (&error);
+
+  if (g_pkcs11_propagate_error (&error, CKR_OK))
+    g_assert_not_reached ();
+  g_assert_no_error (error);
+
+  if (!g_pkcs11_propagate_error (&error, CKR_CANCEL))
+    g_assert_not_reached ();
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+  g_clear_error (&error);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/pkcs11/util/propagate-error", test_propagate_error);
+
+  return g_test_run();
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]