diff --git a/.gitignore b/.gitignore
index 4c98074..8b773cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ Makefile
 *-marshal.[ch]
 
 # autofoo stuff here
+compile
 config.*
 configure
 depcomp
@@ -33,4 +34,12 @@ m4
 proxy/libproxy/glib-pacrunner
 proxy/libproxy/org.gtk.GLib.PACRunner.service
 
-/tls/tests/tls
+/tls/tests/pkcs11-array
+/tls/tests/pkcs11-pin
+/tls/tests/pkcs11-slot
+/tls/tests/pkcs11-util
+/tls/tests/tls-gnutls
+/tls/tests/tls-gnutls-pkcs11
+
+/tls/pkcs11/gpkcs11marshal.c
+/tls/pkcs11/gpkcs11marshal.h
diff --git a/Makefile.am b/Makefile.am
index 41c7a60..5c9a3c6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,6 +13,10 @@ if HAVE_GNOME_PROXY
 SUBDIRS += proxy/gnome
 endif
 
+if HAVE_PKCS11
+SUBDIRS += tls/pkcs11
+endif
+
 if HAVE_GNUTLS
 SUBDIRS += tls/gnutls
 endif
diff --git a/Makefile.decl b/Makefile.decl
index 72d0e15..b936d5c 100644
--- a/Makefile.decl
+++ b/Makefile.decl
@@ -78,6 +78,7 @@ lcov:
 genlcov:
 	$(LTP) --directory $(top_builddir) --capture --output-file glib-lcov.info --test-name GLIB_PERF --no-checksum --compat-libtool
 	LANG=C $(LTP_GENHTML) --prefix $(top_builddir) --output-directory glib-lcov --title "GLib Code Coverage" --legend --show-details glib-lcov.info
+	@echo "file://$(abs_top_builddir)/glib-lcov/index.html"
 
 lcov-clean:
 	-$(LTP) --directory $(top_builddir) -z
diff --git a/configure.ac b/configure.ac
index 8d8e41f..4040b85 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,6 +16,7 @@ dnl Checks for programs.
 AC_PROG_CC
 AM_PROG_CC_C_O
 AC_PROG_CPP
+AM_PROG_CC_C_O
 
 dnl Checks for libraries.
 
@@ -80,18 +81,24 @@ AC_SUBST(GSETTINGS_DESKTOP_SCHEMAS_CFLAGS)
 dnl *****************************
 dnl *** Checks for GNUTLS     ***
 dnl *****************************
+
+GNUTLS_MIN_REQUIRED=2.11.0
+GNUTLS_MIN_PKCS11=2.12.8
+
 AC_ARG_WITH(gnutls,
     [AC_HELP_STRING([--with-gnutls],
                     [support for GNUTLS @<:@default=yes@:>@])],
     [],
     [with_gnutls=yes])
-AS_IF([test "x$with_gnutls" != "xno"],
-    [PKG_CHECK_MODULES(GNUTLS, [gnutls >= 2.1.7],
-        [with_gnutls=yes
-	 tls_support=gnutls
-	 AM_PATH_LIBGCRYPT([])],
-        [AS_IF([test "x$with_gnutls" = "xyes"],
-               [AC_MSG_FAILURE("$GNUTLS_PKG_ERRORS")])])])
+if test "x$with_gnutls" != "xno"; then
+	PKG_CHECK_MODULES(GNUTLS,
+	                  [gnutls >= $GNUTLS_MIN_REQUIRED],
+	                  [with_gnutls=yes
+	                   tls_support="${tls_support}gnutls "
+	                   AM_PATH_LIBGCRYPT([])],
+	                  [AS_IF([test "x$with_gnutls" = "xyes"],
+	                         [AC_MSG_FAILURE("$GNUTLS_PKG_ERRORS")])])
+fi
 AM_CONDITIONAL(HAVE_GNUTLS, [test "x$with_gnutls" = "xyes"])
 AC_SUBST(GNUTLS_CFLAGS)
 AC_SUBST(GNUTLS_LIBS)
@@ -121,6 +128,30 @@ if test "x$with_gnutls" = "xyes"; then
     fi
 fi
 
+dnl *****************************
+dnl *** Checks for pkcs11    ***
+dnl *****************************
+
+AC_ARG_WITH(pkcs11,
+	[AC_HELP_STRING([--with-pkcs11],
+	                [support for pkcs11 @<:@default=check@:>@])],
+	                [],
+	                [with_pkcs11=check])
+if test "x$with_pkcs11" != "xno"; then
+	PKG_CHECK_MODULES(PKCS11,
+	                  [p11-kit-1 gnutls >= $GNUTLS_MIN_PKCS11],
+	                  [with_pkcs11=yes
+	                   pkcs11_support=p11-kit
+	                   tls_support="${tls_support}gnutls-pkcs11 "
+	                   AC_DEFINE_UNQUOTED([HAVE_PKCS11], [1], [Building with PKCS#11 support])],
+	                  [AS_IF([test "x$with_pkcs11" = "xyes"],
+	                         [AC_MSG_FAILURE("$PKCS11_PKG_ERRORS")])
+	                   pkcs11_support=no])
+fi
+AM_CONDITIONAL(HAVE_PKCS11, [test "x$with_pkcs11" = "xyes"])
+AC_SUBST(PKCS11_CFLAGS)
+AC_SUBST(PKCS11_LIBS)
+
 dnl *************************************
 dnl *** Warnings to show if using GCC ***
 dnl *************************************
@@ -216,15 +247,17 @@ AC_CONFIG_FILES([Makefile
                  proxy/libproxy/Makefile
                  proxy/gnome/Makefile
                  tls/gnutls/Makefile
+                 tls/pkcs11/Makefile
                  tls/tests/Makefile
                 ])
 AC_OUTPUT
 
 echo ""
-echo "  Proxy support: ${proxy_support:-no}"
-echo "  TLS support:   ${tls_support:-no}"
+echo     "  Proxy support:     ${proxy_support:-no}"
+echo     "  TLS support:       ${tls_support:-no}"
 if test "$tls_support" != "no"; then
-    echo "  TLS CA file:   ${with_ca_certificates:-(none)}"
+    echo "  PKCS#11 Support:   $pkcs11_support"
+    echo "  TLS CA file:       ${with_ca_certificates:-(none)}"
     if test -n "$with_ca_certificates"; then
 	if ! test -f "$with_ca_certificates"; then
 	    AC_MSG_WARN([Specified certificate authority file '$with_ca_certificates' does not exist])
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2ee69ac..4323e7f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -3,3 +3,5 @@ tls/gnutls/gtlscertificate-gnutls.c
 tls/gnutls/gtlsclientconnection-gnutls.c
 tls/gnutls/gtlsconnection-gnutls.c
 tls/gnutls/gtlsserverconnection-gnutls.c
+tls/pkcs11/gpkcs11pin.c
+tls/pkcs11/gpkcs11slot.c
diff --git a/tls/gnutls/Makefile.am b/tls/gnutls/Makefile.am
index 745f0ce..6a31acf 100644
--- a/tls/gnutls/Makefile.am
+++ b/tls/gnutls/Makefile.am
@@ -16,6 +16,28 @@ gnutls-marshal.c: gnutls-marshal.h gnutls-marshal.list
 	$(GLIB_GENMARSHAL) --prefix=_gnutls_marshal $(srcdir)/gnutls-marshal.list --body --internal) > $@.tmp && \
 	  mv $@.tmp $@
 
+if HAVE_PKCS11
+P11_SRCS = \
+	gtlsbackend-gnutls-pkcs11.c \
+	gtlsbackend-gnutls-pkcs11.h \
+	gtlscertificate-gnutls-pkcs11.c \
+	gtlscertificate-gnutls-pkcs11.h \
+	gtlsdatabase-gnutls-pkcs11.c	\
+	gtlsdatabase-gnutls-pkcs11.h	\
+	$(NULL)
+P11_LIBADD = \
+	$(top_srcdir)/tls/pkcs11/libgiopkcs11.la \
+	$(PKCS11_LIBS) \
+	$(NULL)
+P11_CFLAGS = \
+	$(PKCS11_CFLAGS) \
+	-DP11_KIT_API_SUBJECT_TO_CHANGE
+else
+P11_SRCS =
+P11_LIBADD =
+P11_CFLAGS =
+endif
+
 libgiognutls_la_SOURCES = 		\
 	gnutls-module.c			\
 	gnutls-marshal.c		\
@@ -38,6 +60,7 @@ libgiognutls_la_SOURCES = 		\
 	gtlsoutputstream-gnutls.h	\
 	gtlsserverconnection-gnutls.c	\
 	gtlsserverconnection-gnutls.h	\
+	$(P11_SRCS) 			\
 	$(NULL)
 
 $(libgiognutls_la_OBJECTS): gnutls-marshal.h
@@ -45,13 +68,17 @@ $(libgiognutls_la_OBJECTS): gnutls-marshal.h
 libgiognutls_la_CFLAGS = \
 	-DG_LOG_DOMAIN=\"GLib-Net\"	\
 	$(GLIB_CFLAGS)			\
+	$(P11_CFLAGS)			\
 	$(LIBGNUTLS_CFLAGS)		\
 	$(LIBGCRYPT_CFLAGS)		\
 	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"	\
-	-DG_DISABLE_DEPRECATED
+	-DG_DISABLE_DEPRECATED		\
+	-I$(top_srcdir)/tls/		\
+	$(NULL)
 
 libgiognutls_la_LDFLAGS = $(module_flags)
 libgiognutls_la_LIBADD =		\
+	$(P11_LIBADD)			\
 	$(GLIB_LIBS)			\
 	$(GNUTLS_LIBS)			\
 	$(LIBGCRYPT_LIBS)		\
diff --git a/tls/gnutls/gnutls-module.c b/tls/gnutls/gnutls-module.c
index 393757f..b243850 100644
--- a/tls/gnutls/gnutls-module.c
+++ b/tls/gnutls/gnutls-module.c
@@ -23,12 +23,14 @@
 #include <gio/gio.h>
 
 #include "gtlsbackend-gnutls.h"
+#include "gtlsbackend-gnutls-pkcs11.h"
 
 
 void
 g_io_module_load (GIOModule *module)
 {
   g_tls_backend_gnutls_register (module);
+  g_tls_backend_gnutls_pkcs11_register (module);
 }
 
 void
diff --git a/tls/gnutls/gtlsbackend-gnutls-pkcs11.c b/tls/gnutls/gtlsbackend-gnutls-pkcs11.c
new file mode 100644
index 0000000..4ae8f0d
--- /dev/null
+++ b/tls/gnutls/gtlsbackend-gnutls-pkcs11.c
@@ -0,0 +1,73 @@
+/* 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stef@collabora.co.uk>
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+
+#include <gnutls/gnutls.h>
+#include <gcrypt.h>
+#ifndef G_OS_WIN32
+#include <pthread.h>
+#endif
+
+#include "gtlsbackend-gnutls-pkcs11.h"
+#include "gtlsdatabase-gnutls-pkcs11.h"
+
+G_DEFINE_DYNAMIC_TYPE (GTlsBackendGnutlsPkcs11, g_tls_backend_gnutls_pkcs11, G_TYPE_TLS_BACKEND_GNUTLS);
+
+static void
+g_tls_backend_gnutls_pkcs11_init (GTlsBackendGnutlsPkcs11 *backend)
+{
+
+}
+
+static GTlsDatabase*
+g_tls_backend_gnutls_pkcs11_create_database (GTlsBackendGnutls  *backend,
+                                             GError            **error)
+{
+  return g_tls_database_gnutls_pkcs11_new (error);
+}
+
+static void
+g_tls_backend_gnutls_pkcs11_class_init (GTlsBackendGnutlsPkcs11Class *backend_class)
+{
+  GTlsBackendGnutlsClass *gnutls_class = G_TLS_BACKEND_GNUTLS_CLASS (backend_class);
+  gnutls_class->create_database = g_tls_backend_gnutls_pkcs11_create_database;
+}
+
+static void
+g_tls_backend_gnutls_pkcs11_class_finalize (GTlsBackendGnutlsPkcs11Class *backend_class)
+{
+
+}
+
+void
+g_tls_backend_gnutls_pkcs11_register (GIOModule *module)
+{
+  g_tls_backend_gnutls_pkcs11_register_type (G_TYPE_MODULE (module));
+  g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
+				  g_tls_backend_gnutls_pkcs11_get_type(),
+				  "gnutls-pkcs11",
+				  -5);
+}
diff --git a/tls/gnutls/gtlsbackend-gnutls-pkcs11.h b/tls/gnutls/gtlsbackend-gnutls-pkcs11.h
new file mode 100644
index 0000000..219a74c
--- /dev/null
+++ b/tls/gnutls/gtlsbackend-gnutls-pkcs11.h
@@ -0,0 +1,51 @@
+/* GIO - GLib Backend, Output and Gnutlsing Library
+ *
+ * Copyright © 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <stef@collabora.co.uk>
+ */
+
+#ifndef __G_TLS_BACKEND_GNUTLS_PKCS11_H__
+#define __G_TLS_BACKEND_GNUTLS_PKCS11_H__
+
+#include <gio/gio.h>
+#include <gnutls/gnutls.h>
+
+#include "gtlsbackend-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_BACKEND_GNUTLS_PKCS11            (g_tls_backend_gnutls_pkcs11get_type ())
+#define G_TLS_BACKEND_GNUTLS_PKCS11(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_BACKEND_GNUTLS_PKCS11, GTlsBackendGnutlsPkcs11))
+#define G_TLS_BACKEND_GNUTLS_PKCS11_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_BACKEND_GNUTLS_PKCS11, GTlsBackendGnutlsPkcs11Class))
+#define G_IS_TLS_BACKEND_GNUTLS_PKCS11(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_BACKEND_GNUTLS_PKCS11))
+#define G_IS_TLS_BACKEND_GNUTLS_PKCS11_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_BACKEND_GNUTLS_PKCS11))
+#define G_TLS_BACKEND_GNUTLS_PKCS11_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_BACKEND_GNUTLS_PKCS11, GTlsBackendGnutlsPkcs11Class))
+
+typedef struct _GTlsBackendGnutlsPkcs11        GTlsBackendGnutlsPkcs11;
+typedef struct _GTlsBackendGnutlsPkcs11Class   GTlsBackendGnutlsPkcs11Class;
+
+struct _GTlsBackendGnutlsPkcs11Class
+{
+  GTlsBackendGnutlsClass parent_class;
+};
+
+struct _GTlsBackendGnutlsPkcs11
+{
+  GTlsBackendGnutls parent_instance;
+};
+
+GType        g_tls_backend_gnutls_pkcs11_get_type           (void) G_GNUC_CONST;
+
+void         g_tls_backend_gnutls_pkcs11_register           (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __G_TLS_BACKEND_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsbackend-gnutls.c b/tls/gnutls/gtlsbackend-gnutls.c
index 75b3e93..be7cd16 100644
--- a/tls/gnutls/gtlsbackend-gnutls.c
+++ b/tls/gnutls/gtlsbackend-gnutls.c
@@ -144,11 +144,23 @@ g_tls_backend_gnutls_finalize (GObject *object)
   G_OBJECT_CLASS (g_tls_backend_gnutls_parent_class)->finalize (object);
 }
 
+static GTlsDatabase*
+g_tls_backend_gnutls_real_create_database (GTlsBackendGnutls  *self,
+                                           GError            **error)
+{
+  const gchar *anchor_file = NULL;
+#ifdef GTLS_SYSTEM_CA_FILE
+  anchor_file = GTLS_SYSTEM_CA_FILE;
+#endif
+  return g_tls_file_database_new (anchor_file, error);
+}
+
 static void
 g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
   gobject_class->finalize = g_tls_backend_gnutls_finalize;
+  backend_class->create_database = g_tls_backend_gnutls_real_create_database;
   g_type_class_add_private (backend_class, sizeof (GTlsBackendGnutlsPrivate));
 }
 
@@ -161,7 +173,6 @@ static GTlsDatabase*
 g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
 {
   GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
-  const gchar *anchor_file = NULL;
   GTlsDatabase *result;
   GError *error = NULL;
 
@@ -173,10 +184,8 @@ g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
     }
   else
     {
-#ifdef GTLS_SYSTEM_CA_FILE
-      anchor_file = GTLS_SYSTEM_CA_FILE;
-#endif
-      result = g_tls_file_database_new (anchor_file, &error);
+      g_assert (G_TLS_BACKEND_GNUTLS_GET_CLASS (self)->create_database);
+      result = G_TLS_BACKEND_GNUTLS_GET_CLASS (self)->create_database (self, &error);
       if (error)
         {
           g_warning ("couldn't load TLS file database: %s",
@@ -185,6 +194,7 @@ g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
         }
       else
         {
+          g_assert (result);
           self->priv->default_database = g_object_ref (result);
         }
     }
diff --git a/tls/gnutls/gtlsbackend-gnutls.h b/tls/gnutls/gtlsbackend-gnutls.h
index 6ab0344..f597a50 100644
--- a/tls/gnutls/gtlsbackend-gnutls.h
+++ b/tls/gnutls/gtlsbackend-gnutls.h
@@ -32,6 +32,9 @@ typedef struct _GTlsBackendGnutlsPrivate GTlsBackendGnutlsPrivate;
 struct _GTlsBackendGnutlsClass
 {
   GObjectClass parent_class;
+
+  GTlsDatabase*   (*create_database)      (GTlsBackendGnutls          *self,
+                                           GError                    **error);
 };
 
 struct _GTlsBackendGnutls
diff --git a/tls/gnutls/gtlscertificate-gnutls-pkcs11.c b/tls/gnutls/gtlscertificate-gnutls-pkcs11.c
new file mode 100644
index 0000000..c167b5a
--- /dev/null
+++ b/tls/gnutls/gtlscertificate-gnutls-pkcs11.c
@@ -0,0 +1,211 @@
+/* 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/pkcs11.h>
+#include <string.h>
+
+#include "gtlscertificate-gnutls.h"
+#include "gtlscertificate-gnutls-pkcs11.h"
+
+G_DEFINE_TYPE (GTlsCertificateGnutlsPkcs11, g_tls_certificate_gnutls_pkcs11,
+               G_TYPE_TLS_CERTIFICATE_GNUTLS);
+
+enum
+{
+  PROP_0,
+
+  PROP_CERTIFICATE_URI,
+  PROP_PRIVATE_KEY_URI
+};
+
+struct _GTlsCertificateGnutlsPkcs11Private
+{
+  gchar *certificate_uri;
+  gchar *private_key_uri;
+};
+
+static void
+g_tls_certificate_gnutls_pkcs11_finalize (GObject *object)
+{
+  GTlsCertificateGnutlsPkcs11 *self = G_TLS_CERTIFICATE_GNUTLS_PKCS11 (object);
+
+  g_free (self->priv->certificate_uri);
+  g_free (self->priv->private_key_uri);
+
+  G_OBJECT_CLASS (g_tls_certificate_gnutls_pkcs11_parent_class)->finalize (object);
+}
+
+static void
+g_tls_certificate_gnutls_pkcs11_get_property (GObject    *object,
+                                              guint       prop_id,
+                                              GValue     *value,
+                                              GParamSpec *pspec)
+{
+  GTlsCertificateGnutlsPkcs11 *self = G_TLS_CERTIFICATE_GNUTLS_PKCS11 (object);
+
+  switch (prop_id)
+    {
+    case PROP_CERTIFICATE_URI:
+      g_value_set_string (value, self->priv->certificate_uri);
+      break;
+    case PROP_PRIVATE_KEY_URI:
+      g_value_set_string (value, self->priv->private_key_uri);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_certificate_gnutls_pkcs11_set_property (GObject      *object,
+                                              guint         prop_id,
+                                              const GValue *value,
+                                              GParamSpec   *pspec)
+{
+  GTlsCertificateGnutlsPkcs11 *self = G_TLS_CERTIFICATE_GNUTLS_PKCS11 (object);
+
+  switch (prop_id)
+    {
+    case PROP_CERTIFICATE_URI:
+      g_free (self->priv->certificate_uri);
+      self->priv->certificate_uri = g_value_dup_string (value);
+      break;
+    case PROP_PRIVATE_KEY_URI:
+      g_free (self->priv->private_key_uri);
+      self->priv->private_key_uri = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_certificate_gnutls_pkcs11_init (GTlsCertificateGnutlsPkcs11 *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                            G_TYPE_TLS_CERTIFICATE_GNUTLS_PKCS11,
+                                            GTlsCertificateGnutlsPkcs11Private);
+}
+
+static gpointer
+g_tls_certificate_gnutls_pkcs11_copy_key (GTlsCertificateGnutls    *gnutls,
+                                          const gchar              *interaction_id,
+                                          gnutls_privkey_type_t    *key_type)
+{
+  GTlsCertificateGnutlsPkcs11 *self = G_TLS_CERTIFICATE_GNUTLS_PKCS11 (gnutls);
+  gnutls_pkcs11_privkey_t key;
+  gchar *uri;
+
+  uri = g_tls_certificate_gnutls_pkcs11_build_private_key_uri (self, interaction_id);
+
+  if (uri == NULL)
+    return G_TLS_CERTIFICATE_GNUTLS_CLASS (g_tls_certificate_gnutls_pkcs11_parent_class)->copy_key (gnutls,
+                                                                                                    interaction_id,
+                                                                                                    key_type);
+
+  gnutls_pkcs11_privkey_init (&key);
+  gnutls_pkcs11_privkey_import_url (key, uri, GNUTLS_PKCS11_URL_GENERIC);
+  *key_type = GNUTLS_PRIVKEY_PKCS11;
+  g_free (uri);
+
+  return key;
+}
+
+static void
+g_tls_certificate_gnutls_pkcs11_class_init (GTlsCertificateGnutlsPkcs11Class *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsCertificateGnutlsClass *gnutls_class = G_TLS_CERTIFICATE_GNUTLS_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsCertificateGnutlsPkcs11Private));
+
+  gobject_class->get_property = g_tls_certificate_gnutls_pkcs11_get_property;
+  gobject_class->set_property = g_tls_certificate_gnutls_pkcs11_set_property;
+  gobject_class->finalize     = g_tls_certificate_gnutls_pkcs11_finalize;
+
+  gnutls_class->copy_key = g_tls_certificate_gnutls_pkcs11_copy_key;
+
+  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));
+}
+
+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_PKCS11,
+                              "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;
+}
+
+gchar *
+g_tls_certificate_gnutls_pkcs11_build_certificate_uri (GTlsCertificateGnutlsPkcs11 *self,
+                                                       const gchar *interaction_id)
+{
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS_PKCS11 (self), NULL);
+  if (self->priv->certificate_uri == NULL)
+    return NULL;
+  else if (interaction_id)
+    return g_strdup_printf ("%s;pinfile=%s", self->priv->certificate_uri, interaction_id);
+  else
+    return g_strdup (self->priv->certificate_uri);
+}
+
+gchar *
+g_tls_certificate_gnutls_pkcs11_build_private_key_uri (GTlsCertificateGnutlsPkcs11 *self,
+                                                       const gchar *interaction_id)
+{
+  if (self->priv->private_key_uri == NULL)
+    return NULL;
+  else if (interaction_id)
+    return g_strdup_printf ("%s;pinfile=%s", self->priv->private_key_uri, interaction_id);
+  else
+    return g_strdup (self->priv->private_key_uri);
+}
diff --git a/tls/gnutls/gtlscertificate-gnutls-pkcs11.h b/tls/gnutls/gtlscertificate-gnutls-pkcs11.h
new file mode 100644
index 0000000..4e1df63
--- /dev/null
+++ b/tls/gnutls/gtlscertificate-gnutls-pkcs11.h
@@ -0,0 +1,61 @@
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#ifndef __G_TLS_CERTIFICATE_GNUTLS_PKCS11_H__
+#define __G_TLS_CERTIFICATE_GNUTLS_PKCS11_H__
+
+#include <gio/gio.h>
+#include <gnutls/gnutls.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CERTIFICATE_GNUTLS_PKCS11            (g_tls_certificate_gnutls_pkcs11_get_type ())
+#define G_TLS_CERTIFICATE_GNUTLS_PKCS11(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS_PKCS11, GTlsCertificateGnutlsPkcs11))
+#define G_TLS_CERTIFICATE_GNUTLS_PKCS11_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CERTIFICATE_GNUTLS_PKCS11, GTlsCertificateGnutlsPkcs11Class))
+#define G_IS_TLS_CERTIFICATE_GNUTLS_PKCS11(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS_PKCS11))
+#define G_IS_TLS_CERTIFICATE_GNUTLS_PKCS11_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CERTIFICATE_GNUTLS_PKCS11))
+#define G_TLS_CERTIFICATE_GNUTLS_PKCS11_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS_PKCS11, GTlsCertificateGnutlsPkcs11Class))
+
+typedef struct _GTlsCertificateGnutlsPkcs11Private                   GTlsCertificateGnutlsPkcs11Private;
+typedef struct _GTlsCertificateGnutlsPkcs11Class                     GTlsCertificateGnutlsPkcs11Class;
+typedef struct _GTlsCertificateGnutlsPkcs11                          GTlsCertificateGnutlsPkcs11;
+
+struct _GTlsCertificateGnutlsPkcs11Class
+{
+  GTlsCertificateGnutlsClass parent_class;
+};
+
+struct _GTlsCertificateGnutlsPkcs11
+{
+  GTlsCertificateGnutls parent_instance;
+  GTlsCertificateGnutlsPkcs11Private *priv;
+};
+
+GType              g_tls_certificate_gnutls_pkcs11_get_type              (void) G_GNUC_CONST;
+
+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 (GTlsCertificateGnutlsPkcs11 *self,
+                                                                          const gchar *interaction_id);
+
+gchar *            g_tls_certificate_gnutls_pkcs11_build_private_key_uri (GTlsCertificateGnutlsPkcs11 *self,
+                                                                          const gchar *interaction_id);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CERTIFICATE_GNUTLS_PKCS11_H___ */
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index 956235e..17a7224 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -63,7 +63,8 @@ g_tls_certificate_gnutls_finalize (GObject *object)
   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
 
   gnutls_x509_crt_deinit (gnutls->priv->cert);
-  gnutls_x509_privkey_deinit (gnutls->priv->key);
+  if (gnutls->priv->key)
+    gnutls_x509_privkey_deinit (gnutls->priv->key);
 
   if (gnutls->priv->issuer)
     g_object_unref (gnutls->priv->issuer);
@@ -203,6 +204,8 @@ g_tls_certificate_gnutls_set_property (GObject      *object,
       g_return_if_fail (gnutls->priv->have_key == FALSE);
       data.data = bytes->data;
       data.size = bytes->len;
+      if (!gnutls->priv->key)
+        gnutls_x509_privkey_init (&gnutls->priv->key);
       status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
 					   GNUTLS_X509_FMT_DER);
       if (status == 0)
@@ -223,8 +226,10 @@ g_tls_certificate_gnutls_set_property (GObject      *object,
       g_return_if_fail (gnutls->priv->have_key == FALSE);
       data.data = (void *)string;
       data.size = strlen (string);
+      if (!gnutls->priv->key)
+        gnutls_x509_privkey_init (&gnutls->priv->key);
       status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
-					   GNUTLS_X509_FMT_PEM);
+                                           GNUTLS_X509_FMT_PEM);
       if (status == 0)
 	gnutls->priv->have_key = TRUE;
       else if (!gnutls->priv->construct_error)
@@ -253,7 +258,6 @@ g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
 					      GTlsCertificateGnutlsPrivate);
 
   gnutls_x509_crt_init (&gnutls->priv->cert);
-  gnutls_x509_privkey_init (&gnutls->priv->key);
 }
 
 static gboolean
@@ -344,6 +348,22 @@ g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
   return gtls_flags;
 }
 
+static gpointer
+g_tls_certificate_gnutls_real_copy_key (GTlsCertificateGnutls    *gnutls,
+                                        const gchar              *interaction_id,
+                                        gnutls_privkey_type_t    *key_type)
+{
+  gnutls_x509_privkey_t key;
+
+  if (gnutls->priv->key == NULL)
+    return NULL;
+
+  gnutls_x509_privkey_init (&key);
+  gnutls_x509_privkey_cpy (key, gnutls->priv->key);
+  *key_type = GNUTLS_PRIVKEY_X509;
+  return key;
+}
+
 static void
 g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
 {
@@ -358,6 +378,8 @@ g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
 
   certificate_class->verify = g_tls_certificate_gnutls_verify;
 
+  klass->copy_key = g_tls_certificate_gnutls_real_copy_key;
+
   g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
   g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem");
   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
@@ -380,27 +402,32 @@ g_tls_certificate_gnutls_new (const gnutls_datum *datum,
   gnutls = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
 			 "issuer", issuer,
 			 NULL);
-  if (gnutls_x509_crt_import (gnutls->priv->cert, datum,
-			      GNUTLS_X509_FMT_DER) == 0)
-    gnutls->priv->have_cert = TRUE;
+  g_tls_certificate_gnutls_set_data (gnutls, datum);
 
   return G_TLS_CERTIFICATE (gnutls);
 }
 
-const gnutls_x509_crt_t
-g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
+void
+g_tls_certificate_gnutls_set_data (GTlsCertificateGnutls *gnutls,
+                                   const gnutls_datum *datum)
 {
-  return gnutls->priv->cert;
+  g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
+  g_return_if_fail (!gnutls->priv->have_cert);
+
+  if (gnutls_x509_crt_import (gnutls->priv->cert, datum,
+                              GNUTLS_X509_FMT_DER) == 0)
+    gnutls->priv->have_cert = TRUE;
 }
 
-const gnutls_x509_privkey_t
-g_tls_certificate_gnutls_get_key (GTlsCertificateGnutls *gnutls)
+const gnutls_x509_crt_t
+g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
 {
-  return gnutls->priv->key;
+  return gnutls->priv->cert;
 }
 
 gnutls_x509_crt_t
-g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls)
+g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls,
+                                    const gchar           *interaction_id)
 {
   gnutls_x509_crt_t cert;
   gnutls_datum data;
@@ -421,14 +448,15 @@ g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls)
   return cert;
 }
 
-gnutls_x509_privkey_t
-g_tls_certificate_gnutls_copy_key  (GTlsCertificateGnutls *gnutls)
+gpointer
+g_tls_certificate_gnutls_copy_key  (GTlsCertificateGnutls *gnutls,
+                                    const gchar           *interaction_id,
+                                    gnutls_privkey_type_t *key_type)
 {
-  gnutls_x509_privkey_t key;
-
-  gnutls_x509_privkey_init (&key);
-  gnutls_x509_privkey_cpy (key, gnutls->priv->key);
-  return key;
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls), NULL);
+  g_return_val_if_fail (key_type, NULL);
+  g_return_val_if_fail (G_TLS_CERTIFICATE_GNUTLS_GET_CLASS (gnutls)->copy_key, NULL);
+  return G_TLS_CERTIFICATE_GNUTLS_GET_CLASS (gnutls)->copy_key (gnutls, interaction_id, key_type);
 }
 
 static const struct {
diff --git a/tls/gnutls/gtlscertificate-gnutls.h b/tls/gnutls/gtlscertificate-gnutls.h
index 201dbb4..26e1b49 100644
--- a/tls/gnutls/gtlscertificate-gnutls.h
+++ b/tls/gnutls/gtlscertificate-gnutls.h
@@ -32,6 +32,10 @@ typedef struct _GTlsCertificateGnutls                          GTlsCertificateGn
 struct _GTlsCertificateGnutlsClass
 {
   GTlsCertificateClass parent_class;
+
+  gpointer          (*copy_key)               (GTlsCertificateGnutls    *gnutls,
+                                               const gchar              *interaction_id,
+                                               gnutls_privkey_type_t    *key_type);
 };
 
 struct _GTlsCertificateGnutls
@@ -43,13 +47,18 @@ struct _GTlsCertificateGnutls
 GType g_tls_certificate_gnutls_get_type (void) G_GNUC_CONST;
 
 GTlsCertificate *            g_tls_certificate_gnutls_new             (const gnutls_datum    *datum,
-								       GTlsCertificate       *issuer);
+                                                                       GTlsCertificate       *issuer);
+
+void                         g_tls_certificate_gnutls_set_data        (GTlsCertificateGnutls *gnutls,
+                                                                       const gnutls_datum *datum);
 
 const gnutls_x509_crt_t      g_tls_certificate_gnutls_get_cert        (GTlsCertificateGnutls *gnutls);
-const gnutls_x509_privkey_t  g_tls_certificate_gnutls_get_key         (GTlsCertificateGnutls *gnutls);
 
-gnutls_x509_crt_t            g_tls_certificate_gnutls_copy_cert       (GTlsCertificateGnutls *gnutls);
-gnutls_x509_privkey_t        g_tls_certificate_gnutls_copy_key        (GTlsCertificateGnutls *gnutls);
+gnutls_x509_crt_t            g_tls_certificate_gnutls_copy_cert       (GTlsCertificateGnutls *gnutls,
+                                                                       const gchar           *interaction_id);
+gpointer                     g_tls_certificate_gnutls_copy_key        (GTlsCertificateGnutls *gnutls,
+                                                                       const gchar           *interaction_id,
+                                                                       gnutls_privkey_type_t *key_type);
 
 GTlsCertificateFlags         g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls,
 								       GSocketConnectable    *identity);
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index aec001c..833ea14 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -66,7 +66,7 @@ static int g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
 							     int                          nreqs,
 							     const gnutls_pk_algorithm_t *pk_algos,
 							     int                          pk_algos_length,
-							     gnutls_retr_st              *st);
+							     gnutls_retr2_st              *st);
 
 G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionGnutls, g_tls_client_connection_gnutls, G_TYPE_TLS_CONNECTION_GNUTLS,
 			 G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION,
@@ -120,7 +120,7 @@ g_tls_client_connection_gnutls_init (GTlsClientConnectionGnutls *gnutls)
   gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutlsPrivate);
 
   creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
-  gnutls_certificate_client_set_retrieve_function (creds, g_tls_client_connection_gnutls_retrieve_function);
+  gnutls_certificate_set_retrieve_function (creds, g_tls_client_connection_gnutls_retrieve_function);
 }
 
 static void
@@ -274,7 +274,7 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t             s
 						  int                          nreqs,
 						  const gnutls_pk_algorithm_t *pk_algos,
 						  int                          pk_algos_length,
-						  gnutls_retr_st              *st)
+						  gnutls_retr2_st              *st)
 {
   GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
   GPtrArray *accepted_cas;
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index c1ede79..abe954f 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
+#include <p11-kit/pin.h>
 
 #include "gtlsconnection-gnutls.h"
 #include "gtlsbackend-gnutls.h"
@@ -32,6 +33,9 @@
 #include "gtlsoutputstream-gnutls.h"
 #include "gtlsserverconnection-gnutls.h"
 #include "gnutls-marshal.h"
+
+#include "pkcs11/gpkcs11pin.h"
+
 #include <glib/gi18n-lib.h>
 
 static void g_tls_connection_gnutls_get_property (GObject    *object,
@@ -83,6 +87,12 @@ static gboolean g_tls_connection_gnutls_initable_init       (GInitable       *in
 							     GCancellable    *cancellable,
 							     GError         **error);
 
+static P11KitPin*    on_pin_prompt_callback  (const char     *pinfile,
+                                              P11KitUri      *pin_uri,
+                                              const char     *pin_description,
+                                              P11KitPinFlags  pin_flags,
+                                              void           *callback_data);
+
 static void g_tls_connection_gnutls_init_priorities (void);
 
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION,
@@ -129,6 +139,7 @@ struct _GTlsConnectionGnutlsPrivate
   GOutputStream *tls_ostream;
 
   GTlsInteraction *interaction;
+  gchar *interaction_id;
 
   GError *error;
   GCancellable *cancellable;
@@ -136,6 +147,8 @@ struct _GTlsConnectionGnutlsPrivate
   GIOCondition internal_direction;
 };
 
+static gint unique_interaction_id = 0;
+
 static void
 g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
 {
@@ -179,6 +192,8 @@ g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
 static void
 g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
 {
+  gint unique_id;
+
   gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutlsPrivate);
 
   gnutls_certificate_allocate_credentials (&gnutls->priv->creds);
@@ -189,6 +204,12 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
 
   gnutls->priv->database_is_unset = TRUE;
   gnutls->priv->is_system_certdb = TRUE;
+
+  unique_id = g_atomic_int_add (&unique_interaction_id, 1);
+  gnutls->priv->interaction_id = g_strdup_printf ("gtls:%d", unique_id);
+
+  p11_kit_pin_register_callback (gnutls->priv->interaction_id,
+                                 on_pin_prompt_callback, gnutls, NULL);
 }
 
 static gnutls_priority_t priorities[2][2];
@@ -301,6 +322,10 @@ g_tls_connection_gnutls_finalize (GObject *object)
   if (connection->priv->error)
     g_error_free (connection->priv->error);
 
+  p11_kit_pin_unregister_callback (connection->priv->interaction_id,
+                                   on_pin_prompt_callback, connection);
+  g_free (connection->priv->interaction_id);
+
   G_OBJECT_CLASS (g_tls_connection_gnutls_parent_class)->finalize (object);
 }
 
@@ -469,21 +494,24 @@ g_tls_connection_gnutls_get_session (GTlsConnectionGnutls *gnutls)
 
 void
 g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
-					 gnutls_retr_st       *st)
+                                         gnutls_retr2_st       *st)
 {
   GTlsCertificate *cert;
 
   cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (gnutls));
 
-  st->type = GNUTLS_CRT_X509;
+  st->cert_type = GNUTLS_CRT_X509;
   if (cert)
     {
       GTlsCertificateGnutls *gnutlscert = G_TLS_CERTIFICATE_GNUTLS (cert);
 
       st->ncerts = 1;
       st->cert.x509 = gnutls_malloc (sizeof (gnutls_x509_crt_t));
-      st->cert.x509[0] = g_tls_certificate_gnutls_copy_cert (gnutlscert);
-      st->key.x509 = g_tls_certificate_gnutls_copy_key (gnutlscert);
+      st->cert.x509[0] = g_tls_certificate_gnutls_copy_cert (gnutlscert,
+                                                             gnutls->priv->interaction_id);
+      st->key.x509 = g_tls_certificate_gnutls_copy_key (gnutlscert,
+                                                        gnutls->priv->interaction_id,
+                                                        &st->key_type);
       st->deinit_all = TRUE;
     }
   else
@@ -498,6 +526,8 @@ begin_gnutls_io (GTlsConnectionGnutls  *gnutls,
   gnutls->priv->blocking = blocking;
   gnutls->priv->cancellable = cancellable;
   gnutls->priv->internal_direction = 0;
+  if (cancellable)
+    g_cancellable_push_current (cancellable);
   g_clear_error (&gnutls->priv->error);
 }
 
@@ -506,6 +536,8 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
 	       int                    status,
 	       GError               **error)
 {
+  if (gnutls->priv->cancellable)
+    g_cancellable_pop_current (gnutls->priv->cancellable);
   gnutls->priv->cancellable = NULL;
 
   if (status >= 0)
@@ -1290,3 +1322,51 @@ g_tls_connection_gnutls_close_finish (GIOStream           *stream,
 
   return g_simple_async_result_get_op_res_gboolean (simple);
 }
+
+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);
+  GTlsInteractionResult result;
+  GTlsPasswordFlags flags = 0;
+  GTlsPassword *password;
+  P11KitPin *pin = NULL;
+  GError *error = NULL;
+
+  if (!gnutls->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 (gnutls->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:
+      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;
+}
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index dd061c4..d10ab44 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -53,7 +53,7 @@ GType g_tls_connection_gnutls_get_type (void) G_GNUC_CONST;
 gnutls_certificate_credentials g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
 gnutls_session                 g_tls_connection_gnutls_get_session     (GTlsConnectionGnutls *connection);
 void                           g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
-									 gnutls_retr_st       *st);
+                                                                        gnutls_retr2_st       *st);
 
 gssize   g_tls_connection_gnutls_read          (GTlsConnectionGnutls  *gnutls,
 						void                  *buffer,
diff --git a/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
new file mode 100644
index 0000000..56a2fe1
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
@@ -0,0 +1,862 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include "gtlsdatabase-gnutls-pkcs11.h"
+#include "gtlscertificate-gnutls-pkcs11.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"
+
+const static CK_ATTRIBUTE_TYPE CERTIFICATE_ATTRIBUTE_TYPES[] = {
+    CKA_ID, CKA_LABEL, CKA_CLASS, CKA_VALUE
+};
+
+const static 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);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsDatabaseGnutlsPkcs11, g_tls_database_gnutls_pkcs11,
+                         G_TYPE_TLS_DATABASE_GNUTLS,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                g_tls_database_gnutls_pkcs11_initable_iface_init));
+
+struct _GTlsDatabaseGnutlsPkcs11Private
+{
+  /* no changes after construction */
+  GList *pkcs11_slots;
+  GList *trust_uris;
+  gboolean initialized_registered;
+};
+
+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->priv->pkcs11_slots = g_list_append (self->priv->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_registered_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->priv->trust_uris = g_list_append (self->priv->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->priv->pkcs11_slots; l; l = g_list_next (l))
+      g_object_unref (l->data);
+  g_list_free (self->priv->pkcs11_slots);
+
+  for (l = self->priv->trust_uris; l; l = g_list_next (l))
+    p11_kit_uri_free (l->data);
+  g_list_free (self->priv->trust_uris);
+
+  if (self->priv->initialized_registered)
+    p11_kit_finalize_registered ();
+
+  G_OBJECT_CLASS (g_tls_database_gnutls_pkcs11_parent_class)->finalize (object);
+}
+
+static void
+g_tls_database_gnutls_pkcs11_init (GTlsDatabaseGnutlsPkcs11 *self)
+{
+
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                            G_TYPE_TLS_DATABASE_GNUTLS_PKCS11,
+                                            GTlsDatabaseGnutlsPkcs11Private);
+
+}
+
+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->priv->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->priv->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 (GTlsDatabaseGnutls          *database,
+                                               GTlsCertificateGnutls       *certificate,
+                                               GTlsDatabaseGnutlsAssertion  assertion,
+                                               const gchar                 *purpose,
+                                               GSocketConnectable          *identity,
+                                               GCancellable                *cancellable,
+                                               GError                     **error)
+{
+  GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+  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->priv->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)
+{
+  GTlsCertificateGnutlsPkcs11 *pkcs11_cert;
+
+  if (!G_IS_TLS_CERTIFICATE_GNUTLS_PKCS11 (certificate))
+    return NULL;
+
+  pkcs11_cert = G_TLS_CERTIFICATE_GNUTLS_PKCS11 (certificate);
+  return g_tls_certificate_gnutls_pkcs11_build_certificate_uri (pkcs11_cert, 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;
+}
+
+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);
+  GTlsDatabaseGnutlsClass *gnutls_class = G_TLS_DATABASE_GNUTLS_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsDatabaseGnutlsPkcs11Private));
+
+  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;
+  gnutls_class->lookup_assertion = g_tls_database_gnutls_pkcs11_lookup_assertion;
+}
+
+static gboolean
+g_tls_database_gnutls_pkcs11_initable_init (GInitable     *initable,
+                                            GCancellable  *cancellable,
+                                            GError       **error)
+{
+  GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (initable);
+  CK_FUNCTION_LIST_PTR_PTR modules;
+  GError *err = NULL;
+  gboolean any_success = FALSE;
+  gboolean any_failure = FALSE;
+  CK_RV rv;
+  guint i;
+
+  g_return_val_if_fail (!self->priv->initialized_registered, FALSE);
+
+  rv = p11_kit_initialize_registered ();
+  if (g_pkcs11_propagate_error (error, rv))
+      return FALSE;
+
+  self->priv->initialized_registered = TRUE;
+
+  modules = p11_kit_registered_modules ();
+  for (i = 0; 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, 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..90c94ac
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.h
@@ -0,0 +1,52 @@
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * 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 ())
+#define G_TLS_DATABASE_GNUTLS_PKCS11(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_DATABASE_GNUTLS_PKCS11, GTlsDatabaseGnutlsPkcs11))
+#define G_TLS_DATABASE_GNUTLS_PKCS11_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_DATABASE_GNUTLS_PKCS11, GTlsDatabaseGnutlsPkcs11Class))
+#define G_IS_TLS_DATABASE_GNUTLS_PKCS11(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_DATABASE_GNUTLS_PKCS11))
+#define G_IS_TLS_DATABASE_GNUTLS_PKCS11_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_DATABASE_GNUTLS_PKCS11))
+#define G_TLS_DATABASE_GNUTLS_PKCS11_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_DATABASE_GNUTLS_PKCS11, GTlsDatabaseGnutlsPkcs11Class))
+
+typedef struct _GTlsDatabaseGnutlsPkcs11Private                   GTlsDatabaseGnutlsPkcs11Private;
+typedef struct _GTlsDatabaseGnutlsPkcs11Class                     GTlsDatabaseGnutlsPkcs11Class;
+typedef struct _GTlsDatabaseGnutlsPkcs11                          GTlsDatabaseGnutlsPkcs11;
+
+struct _GTlsDatabaseGnutlsPkcs11Class
+{
+  GTlsDatabaseGnutlsClass parent_class;
+};
+
+struct _GTlsDatabaseGnutlsPkcs11
+{
+  GTlsDatabaseGnutls parent_instance;
+  GTlsDatabaseGnutlsPkcs11Private *priv;
+};
+
+GType                        g_tls_database_gnutls_pkcs11_get_type              (void) G_GNUC_CONST;
+
+GTlsDatabase*                g_tls_database_gnutls_pkcs11_new                   (GError **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_DATABASE_GNUTLS_PKCS11_H___ */
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 2b688f0..1fc4e74 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -54,8 +54,12 @@ static void     g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionG
 
 static void g_tls_server_connection_gnutls_server_connection_interface_init (GTlsServerConnectionInterface *iface);
 
-static int g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t  session,
-							     gnutls_retr_st   *st);
+static int g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t             session,
+                                                             const gnutls_datum_t        *req_ca_rdn,
+                                                             int                          nreqs,
+                                                             const gnutls_pk_algorithm_t *pk_algos,
+                                                             int                          pk_algos_length,
+                                                             gnutls_retr2_st              *st);
 
 G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnutls, G_TYPE_TLS_CONNECTION_GNUTLS,
 			 G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION,
@@ -97,7 +101,7 @@ g_tls_server_connection_gnutls_init (GTlsServerConnectionGnutls *gnutls)
   gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutlsPrivate);
 
   creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
-  gnutls_certificate_server_set_retrieve_function (creds, g_tls_server_connection_gnutls_retrieve_function);
+  gnutls_certificate_set_retrieve_function (creds, g_tls_server_connection_gnutls_retrieve_function);
 }
 
 static void
@@ -139,8 +143,12 @@ g_tls_server_connection_gnutls_set_property (GObject      *object,
 }
 
 static int
-g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t  session,
-						  gnutls_retr_st   *st)
+g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t             session,
+                                                  const gnutls_datum_t        *req_ca_rdn,
+                                                  int                          nreqs,
+                                                  const gnutls_pk_algorithm_t *pk_algos,
+                                                  int                          pk_algos_length,
+                                                  gnutls_retr2_st              *st)
 {
   g_tls_connection_gnutls_get_certificate (gnutls_transport_get_ptr (session), st);
   return 0;
diff --git a/tls/pkcs11/Makefile.am b/tls/pkcs11/Makefile.am
new file mode 100644
index 0000000..2e6535a
--- /dev/null
+++ b/tls/pkcs11/Makefile.am
@@ -0,0 +1,29 @@
+include $(top_srcdir)/Makefile.decl
+
+NULL =
+
+noinst_LTLIBRARIES = \
+	libgiopkcs11.la
+
+libgiopkcs11_la_SOURCES = 		\
+	gpkcs11array.c				\
+	gpkcs11array.h				\
+	gpkcs11pin.c				\
+	gpkcs11pin.h				\
+	gpkcs11slot.c				\
+	gpkcs11slot.h				\
+	gpkcs11util.c				\
+	gpkcs11util.h				\
+	pkcs11.h					\
+	pkcs11-trust-assertions.h 	\
+	$(NULL)
+
+libgiopkcs11_la_CFLAGS = \
+	-DG_LOG_DOMAIN=\"GLib-Net\"	\
+	$(GLIB_CFLAGS)			\
+	$(PKCS11_CFLAGS)			\
+	-DG_DISABLE_DEPRECATED \
+	-DP11_KIT_API_SUBJECT_TO_CHANGE
+
+CLEANFILES = 			\
+	$(BUILT_SOURCES)
\ No newline at end of file
diff --git a/tls/pkcs11/gpkcs11array.c b/tls/pkcs11/gpkcs11array.c
new file mode 100644
index 0000000..1683784
--- /dev/null
+++ b/tls/pkcs11/gpkcs11array.c
@@ -0,0 +1,276 @@
+/* GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright © 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * 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_realloc (rarray->attrs,
+                             (rarray->len + 1) * sizeof (CK_ATTRIBUTE));
+  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..003e8d2
--- /dev/null
+++ b/tls/pkcs11/gpkcs11array.h
@@ -0,0 +1,95 @@
+/* GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright © 2010 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * 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..a16aa38
--- /dev/null
+++ b/tls/pkcs11/gpkcs11pin.c
@@ -0,0 +1,158 @@
+/* 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * 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
+};
+
+G_DEFINE_TYPE (GPkcs11Pin, g_pkcs11_pin, G_TYPE_TLS_PASSWORD);
+
+struct _GPkcs11PinPrivate
+{
+  P11KitPin *pin;
+};
+
+static void
+g_pkcs11_pin_init (GPkcs11Pin *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                            G_TYPE_PKCS11_PIN,
+                                            GPkcs11PinPrivate);
+}
+
+static void
+g_pkcs11_pin_finalize (GObject *object)
+{
+  GPkcs11Pin *self = G_PKCS11_PIN (object);
+
+  if (self->priv->pin)
+    p11_kit_pin_unref (self->priv->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->priv->pin)
+    {
+      if (length)
+        *length = 0;
+      return NULL;
+    }
+
+  return p11_kit_pin_get_value (self->priv->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->priv->pin)
+    {
+      p11_kit_pin_unref (self->priv->pin);
+      self->priv->pin = NULL;
+    }
+
+  if (length < 0)
+    length = strlen ((gchar *) value);
+
+  self->priv->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 ether 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;
+
+  g_type_class_add_private (klass, sizeof (GPkcs11PinPrivate));
+}
+
+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->priv->pin;
+  self->priv->pin = NULL;
+  return pin;
+}
diff --git a/tls/pkcs11/gpkcs11pin.h b/tls/pkcs11/gpkcs11pin.h
new file mode 100644
index 0000000..6012e82
--- /dev/null
+++ b/tls/pkcs11/gpkcs11pin.h
@@ -0,0 +1,54 @@
+/* GIO - GLib Pin, Output and Pkcs11ing Library
+ *
+ * Copyright © 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * 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 ())
+#define G_PKCS11_PIN(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_PKCS11_PIN, GPkcs11Pin))
+#define G_PKCS11_PIN_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_PKCS11_PIN, GPkcs11PinClass))
+#define G_IS_PKCS11_PIN(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_PKCS11_PIN))
+#define G_IS_PKCS11_PIN_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_PKCS11_PIN))
+#define G_PKCS11_PIN_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_PKCS11_PIN, GPkcs11PinClass))
+
+typedef struct _GPkcs11PinPrivate                   GPkcs11PinPrivate;
+typedef struct _GPkcs11PinClass                     GPkcs11PinClass;
+typedef struct _GPkcs11Pin                          GPkcs11Pin;
+
+struct _GPkcs11PinClass
+{
+  GTlsPasswordClass parent_class;
+};
+
+struct _GPkcs11Pin
+{
+  GTlsPassword parent_instance;
+  GPkcs11PinPrivate *priv;
+};
+
+GType                   g_pkcs11_pin_get_type        (void) G_GNUC_CONST;
+
+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..893bab8
--- /dev/null
+++ b/tls/pkcs11/gpkcs11slot.c
@@ -0,0 +1,616 @@
+/* GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright © 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * 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 _GPkcs11SlotPrivate
+{
+  /* 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->priv->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->priv->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->priv->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->priv->module->C_GetTokenInfo) (self->priv->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->priv->mutex);
+
+  if (self->priv->last_session)
+    {
+      session = self->priv->last_session;
+      self->priv->last_session = 0;
+    }
+
+  g_mutex_unlock (self->priv->mutex);
+
+  if (!session)
+    {
+      rv = (self->priv->module->C_OpenSession) (self->priv->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->priv->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->priv->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->priv->mutex);
+
+  if (self->priv->last_session == 0)
+    {
+      self->priv->last_session = session;
+      session = 0;
+    }
+
+  g_mutex_unlock (self->priv->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->priv->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->priv->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)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                            G_TYPE_PKCS11_SLOT,
+                                            GPkcs11SlotPrivate);
+  self->priv->mutex = g_mutex_new ();
+}
+
+static void
+g_pkcs11_slot_dispose (GObject *object)
+{
+  GPkcs11Slot *self = G_PKCS11_SLOT (object);
+  CK_SESSION_HANDLE session = 0;
+
+  g_mutex_lock (self->priv->mutex);
+
+  session = self->priv->last_session;
+  self->priv->last_session = 0;
+
+  g_mutex_unlock (self->priv->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->priv->last_session == 0);
+  g_mutex_free (self->priv->mutex);
+  self->priv->mutex = NULL;
+
+  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->priv->module);
+      break;
+
+    case PROP_SLOT_ID:
+      g_value_set_ulong (value, self->priv->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->priv->module = g_value_get_pointer (value);
+      g_assert (self->priv->module);
+      break;
+
+    case PROP_SLOT_ID:
+      self->priv->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);
+
+  g_type_class_add_private (klass, sizeof (GPkcs11SlotPrivate));
+
+  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->priv->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->priv->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->priv->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->priv->module->C_GetTokenInfo) (self->priv->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->priv->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..cd8c7fb
--- /dev/null
+++ b/tls/pkcs11/gpkcs11slot.h
@@ -0,0 +1,81 @@
+/* GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright © 2010 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * 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 ())
+#define G_PKCS11_SLOT(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_PKCS11_SLOT, GPkcs11Slot))
+#define G_PKCS11_SLOT_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_PKCS11_SLOT, GPkcs11SlotClass))
+#define G_IS_PKCS11_SLOT(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_PKCS11_SLOT))
+#define G_IS_PKCS11_SLOT_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_PKCS11_SLOT))
+#define G_PKCS11_SLOT_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_PKCS11_SLOT, GPkcs11SlotClass))
+
+typedef struct _GPkcs11SlotPrivate                   GPkcs11SlotPrivate;
+typedef struct _GPkcs11SlotClass                     GPkcs11SlotClass;
+typedef struct _GPkcs11Slot                          GPkcs11Slot;
+
+struct _GPkcs11SlotClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GPkcs11Slot
+{
+  GObject parent_instance;
+  GPkcs11SlotPrivate *priv;
+};
+
+typedef gboolean             (*GPkcs11Accumulator)           (gpointer result,
+                                                              gpointer user_data);
+
+GType                        g_pkcs11_slot_get_type           (void) G_GNUC_CONST;
+
+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..ccad945
--- /dev/null
+++ b/tls/pkcs11/gpkcs11util.c
@@ -0,0 +1,59 @@
+/* GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright © 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * 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..b1582e2
--- /dev/null
+++ b/tls/pkcs11/gpkcs11util.h
@@ -0,0 +1,39 @@
+/* GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright © 2010 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * 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/pkcs11-trust-assertions.h b/tls/pkcs11/pkcs11-trust-assertions.h
new file mode 100644
index 0000000..ed8bb6b
--- /dev/null
+++ b/tls/pkcs11/pkcs11-trust-assertions.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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/Makefile.am b/tls/tests/Makefile.am
index 6fd538b..950971c 100644
--- a/tls/tests/Makefile.am
+++ b/tls/tests/Makefile.am
@@ -17,12 +17,36 @@ TEST_ENVIRONMENT =   \
 	GIO_EXTRA_MODULES=$(top_builddir)/tls/gnutls/.libs
 
 TEST_PROGS +=         \
-	tls           \
+	tls-gnutls    \
 	$(NULL)
 
-EXTRA_DIST += \
-	files \
-	$(NULL)
+tls_gnutls_SOURCES = tls.c
+tls_gnutls_CFLAGS = -DGIO_USE_TLS=\"gnutls\"
+
+if HAVE_PKCS11
+
+TEST_PROGS +=              \
+	tls-gnutls-pkcs11  \
+	pkcs11-util        \
+	pkcs11-array       \
+	pkcs11-pin         \
+	pkcs11-slot
+
+INCLUDES += $(PKCS11_CFLAGS)
+LDADD += $(top_builddir)/tls/pkcs11/libgiopkcs11.la $(PKCS11_LIBS)
+
+tls_gnutls_pkcs11_SOURCES = tls.c
+tls_gnutls_pkcs11_CFLAGS = -DGIO_USE_TLS=\"gnutls-pkcs11\"
+
+pkcs11_slot_SOURCES = pkcs11-slot.c \
+	mock-pkcs11.c mock-pkcs11.h \
+	mock-interaction.c mock-interaction.h
+
+endif
 
 DISTCLEANFILES = \
 	$(NULL)
+
+EXTRA_DIST += \
+	files \
+	$(NULL)
\ No newline at end of file
diff --git a/tls/tests/mock-interaction.c b/tls/tests/mock-interaction.c
new file mode 100644
index 0000000..578106b
--- /dev/null
+++ b/tls/tests/mock-interaction.c
@@ -0,0 +1,134 @@
+/*
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gio/gio.h>
+
+#include "mock-interaction.h"
+
+G_DEFINE_TYPE (MockInteraction, mock_interaction, G_TYPE_TLS_INTERACTION);
+
+static void
+on_cancellable_cancelled (GCancellable *cancellable,
+                          gpointer user_data)
+{
+  GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+  GError *error = NULL;
+  if (!g_cancellable_set_error_if_cancelled (cancellable, &error))
+    g_assert_not_reached ();
+  g_simple_async_result_take_error (res, error);
+}
+
+static void
+mock_interaction_ask_password_async (GTlsInteraction    *interaction,
+                                     GTlsPassword       *password,
+                                     GCancellable       *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer            user_data)
+{
+  MockInteraction *self = MOCK_INTERACTION (interaction);
+  GSimpleAsyncResult *res;
+
+  res = g_simple_async_result_new (G_OBJECT (interaction), callback, user_data,
+                                   mock_interaction_ask_password_async);
+
+  if (cancellable)
+    g_cancellable_connect (cancellable,
+                           G_CALLBACK (on_cancellable_cancelled),
+                           g_object_ref (res),
+                           g_object_unref);
+
+  g_tls_password_set_value (password, (const guchar *)self->static_password, -1);
+  g_simple_async_result_complete_in_idle (res);
+}
+
+static GTlsInteractionResult
+mock_interaction_ask_password_finish (GTlsInteraction    *interaction,
+                                      GAsyncResult       *result,
+                                      GError            **error)
+{
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (interaction),
+                                                        mock_interaction_ask_password_async),
+                                                        G_TLS_INTERACTION_UNHANDLED);
+
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+    return G_TLS_INTERACTION_FAILED;
+
+  return G_TLS_INTERACTION_HANDLED;
+}
+
+static GTlsInteractionResult
+mock_interaction_ask_password (GTlsInteraction    *interaction,
+                               GTlsPassword       *password,
+                               GCancellable       *cancellable,
+                               GError            **error)
+{
+  MockInteraction *self = MOCK_INTERACTION (interaction);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return G_TLS_INTERACTION_FAILED;
+
+  g_tls_password_set_value (password, (const guchar *)self->static_password, -1);
+  return G_TLS_INTERACTION_HANDLED;
+}
+
+static void
+mock_interaction_init (MockInteraction *self)
+{
+
+}
+
+static void
+mock_interaction_finalize (GObject *object)
+{
+  MockInteraction *self = MOCK_INTERACTION (object);
+
+  g_free (self->static_password);
+
+  G_OBJECT_CLASS (mock_interaction_parent_class)->finalize (object);
+}
+
+static void
+mock_interaction_class_init (MockInteractionClass *klass)
+{
+  GObjectClass         *object_class = G_OBJECT_CLASS (klass);
+  GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
+
+  object_class->finalize     = mock_interaction_finalize;
+
+  interaction_class->ask_password = mock_interaction_ask_password;
+  interaction_class->ask_password_async = mock_interaction_ask_password_async;
+  interaction_class->ask_password_finish = mock_interaction_ask_password_finish;
+
+}
+
+GTlsInteraction *
+mock_interaction_new_static (const gchar *password)
+{
+  MockInteraction *self;
+
+  self = g_object_new (MOCK_TYPE_INTERACTION, NULL);
+
+  self->static_password = g_strdup (password);
+  return G_TLS_INTERACTION (self);
+}
diff --git a/tls/tests/mock-interaction.h b/tls/tests/mock-interaction.h
new file mode 100644
index 0000000..90668c7
--- /dev/null
+++ b/tls/tests/mock-interaction.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include <gio/gio.h>
+
+#ifndef __MOCK_INTERACTION_H__
+#define __MOCK_INTERACTION_H__
+
+G_BEGIN_DECLS
+
+#define MOCK_TYPE_INTERACTION         (mock_interaction_get_type ())
+#define MOCK_INTERACTION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), MOCK_TYPE_INTERACTION, MockInteraction))
+#define MOCK_INTERACTION_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), MOCK_TYPE_INTERACTION, MockInteractionClass))
+#define MOCK_IS_INTERACTION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MOCK_TYPE_INTERACTION))
+#define MOCK_IS_INTERACTION_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), MOCK_TYPE_INTERACTION))
+#define MOCK_INTERACTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MOCK_TYPE_INTERACTION, MockInteractionClass))
+
+typedef struct _MockInteraction         MockInteraction;
+typedef struct _MockInteractionClass    MockInteractionClass;
+
+struct _MockInteraction
+{
+  GTlsInteraction parent_instance;
+  gchar *static_password;
+};
+
+struct _MockInteractionClass
+{
+  GTlsInteractionClass parent_class;
+};
+
+
+GType            mock_interaction_get_type   (void);
+GTlsInteraction *mock_interaction_new_static       (const gchar *password);
+
+G_END_DECLS
+
+#endif /* __MOCK_INTERACTION_H__ */
diff --git a/tls/tests/mock-pkcs11.c b/tls/tests/mock-pkcs11.c
new file mode 100644
index 0000000..f76cd5f
--- /dev/null
+++ b/tls/tests/mock-pkcs11.c
@@ -0,0 +1,1548 @@
+/*
+ * gnome-keyring
+ *
+ * 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
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#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 gck library 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 gck-mock.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 gck-mock.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 gck-mock.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;
+    }
+
+  if (*pulCount < 0)
+    g_return_val_if_reached (CKR_BUFFER_TOO_SMALL);
+
+  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..540b782
--- /dev/null
+++ b/tls/tests/mock-pkcs11.h
@@ -0,0 +1,395 @@
+/*
+ * gnome-keyring
+ *
+ * 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <glib.h>
+
+#include <p11-kit/pkcs11.h>
+
+#include "tls/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..ebb9e10
--- /dev/null
+++ b/tls/tests/pkcs11-array.c
@@ -0,0 +1,285 @@
+/* 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * 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_type_init ();
+  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..d4051f9
--- /dev/null
+++ b/tls/tests/pkcs11-pin.c
@@ -0,0 +1,149 @@
+/* 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * 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_object_unref (test->pin);
+  g_assert (!G_IS_OBJECT (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_cmpstr ((const gchar *)value, ==, "secret");
+
+  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_cmpstr ((const gchar *)value, ==, "other");
+}
+
+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_cmpstr ((const gchar *)value, ==, "secret");
+
+  p11_kit_pin_unref (pin);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  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..81c49d4
--- /dev/null
+++ b/tls/tests/pkcs11-slot.c
@@ -0,0 +1,522 @@
+/* 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * 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_initialize_module (&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_object_unref (test->slot);
+  g_assert (!G_IS_OBJECT (test->slot));
+
+  g_object_unref (test->not_present);
+  g_assert (!G_IS_OBJECT (test->not_present));
+
+  rv = p11_kit_finalize_module (&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 (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_type_init ();
+  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..0864141
--- /dev/null
+++ b/tls/tests/pkcs11-util.c
@@ -0,0 +1,60 @@
+/* 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * 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_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/pkcs11/util/propagate-error", test_propagate_error);
+
+  return g_test_run();
+}
